Disable auto tabbing JS on date fields

Disable auto tabbing JS on date fields

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -529,26 +529,26 @@
     replaceEntryPeriodText()

   # Auto tab on date input entry
-  $(".date-input input").on 'keyup', (e) ->
-    this_label = $(this).closest("label")
-    new_input = ""
-    # if it isn't the year input accept 2 numbers (48-57) or moving right (39)
-    if this_label.index() != $(this).closest(".date-input").find("label:last-child").index()
-      if (e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105) || e.keyCode == 39
-        if $(this).val().length == 2
-          # focus on the next input
-          new_input = this_label.next().find("input")
-    # if it isn't the day input you can go backwards by backspacing (8) or moving left (37)
-    if this_label.index() != $(this).closest(".date-input").find("label:first-child").index()
-      if e.keyCode == 8 || e.keyCode == 37
-        if $(this).val().length == 0
-          # focus on the next input
-          new_input = this_label.prev().find("input")
-    if new_input != ""
-      new_input_text = new_input.val()
-      new_input.val("")
-      new_input.focus()
-      new_input.val(new_input_text)
+  #$(".date-input input").on 'keyup', (e) ->
+  #  this_label = $(this).closest("label")
+  #  new_input = ""
+  #  # if it isn't the year input accept 2 numbers (48-57) or moving right (39)
+  #  if this_label.index() != $(this).closest(".date-input").find("label:last-child").index()
+  #    if (e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105) || e.keyCode == 39
+  #      if $(this).val().length == 2
+  #        # focus on the next input
+  #        new_input = this_label.next().find("input")
+  #  # if it isn't the day input you can go backwards by backspacing (8) or moving left (37)
+  #  if this_label.index() != $(this).closest(".date-input").find("label:first-child").index()
+  #    if e.keyCode == 8 || e.keyCode == 37
+  #      if $(this).val().length == 0
+  #        # focus on the next input
+  #        new_input = this_label.prev().find("input")
+  #  if new_input != ""
+  #    new_input_text = new_input.val()
+  #    new_input.val("")
+  #    new_input.focus()
+  #    new_input.val(new_input_text)

   # only accept numbers(48-47), backspace(8), tab(9), cursor keys left(37) and right(39) and enter for submitting
   $(".date-input input").on 'keypress keydown keyup', (e) ->


fade out container instead of flash message

--- app/assets/javascripts/flash-messages.coffee
+++ app/assets/javascripts/flash-messages.coffee
@@ -1,2 +1,2 @@
 $('.js-flash-close').on 'click', ->
-  $(this).parent().fadeOut('slow')
+  $(this).parents().eq(1).fadeOut('slow')


ws for LiveEventBus.coffee

ws for LiveEventBus.coffee

--- packages/deft/src/coffee/event/LiveEventBus.coffee
+++ packages/deft/src/coffee/event/LiveEventBus.coffee
@@ -9,26 +9,26 @@
 ###
 Ext.define( 'Deft.event.LiveEventBus',
 	alternateClassName: [ 'Deft.LiveEventBus' ]
-	requires: [
+	requires: [
 		'Ext.Component'
 		'Ext.ComponentManager'
-
+
 		'Deft.event.LiveEventListener'
 	]
 	singleton: true
-
+
 	constructor: ->
 		@listeners = []
 		return
-
+
 	destroy: ->
 		for listener in @listeners
 			listener.destroy()
 		@listeners = null
 		return
-
+
 	addListener: ( container, selector, eventName, fn, scope, options ) ->
-		listener = Ext.create( 'Deft.event.LiveEventListener',
+		listener = Ext.create( 'Deft.event.LiveEventListener',
 			container: container
 			selector: selector
 			eventName: eventName
@@ -38,20 +38,20 @@
 		)
 		@listeners.push( listener )
 		return
-
+
 	removeListener: ( container, selector, eventName, fn, scope ) ->
 		listener = @findListener( container, selector, eventName, fn, scope )
 		if listener?
 			Ext.Array.remove( @listeners, listener )
 			listener.destroy()
 		return
-
+
 	on: ( container, selector, eventName, fn, scope, options ) ->
 		return @addListener( container, selector, eventName, fn, scope, options )
-
+
 	un: ( container, selector, eventName, fn, scope ) ->
 		return @removeListener( container, selector, eventName, fn, scope )
-
+
 	# @private
 	findListener: ( container, selector, eventName, fn, scope ) ->
 		for listener in @listeners
@@ -60,25 +60,25 @@
 			if listener.container is container and listener.selector is selector and listener.eventName is eventName and listener.fn is fn and listener.scope is scope
 				return listener
 		return null
-
+
 	# @private
 	register: ( component ) ->
 		component.on( 'added', @onComponentAdded, @ )
 		component.on( 'removed', @onComponentRemoved, @ )
 		return
-
+
 	# @private
 	unregister: ( component ) ->
 		component.un( 'added', @onComponentAdded, @ )
 		component.un( 'removed', @onComponentRemoved, @ )
 		return
-
+
 	# @private
 	onComponentAdded: ( component, container, eOpts ) ->
 		for listener in @listeners
 			listener.register( component )
 		return
-
+
 	# @private
 	onComponentRemoved: ( component, container, eOpts ) ->
 		for listener in @listeners
@@ -89,12 +89,12 @@
 		if Ext.getVersion( 'touch' )?
 			Ext.define( 'Deft.Component',
 				override: 'Ext.Component'
-
+
 				setParent: ( newParent ) ->
 					oldParent = @getParent()
-
+
 					result = @callParent( arguments )
-
+
 					if oldParent is null and newParent isnt null
 						@fireEvent( 'added', @, newParent )
 					else if oldParent isnt null and newParent isnt null
@@ -102,9 +102,9 @@
 						@fireEvent( 'added', @, newParent )
 					else if oldParent isnt null and newParent is null
 						@fireEvent( 'removed', @, oldParent )
-
+
 					return result
-
+
 				isDescendantOf: ( container ) ->
 					ancestor = @getParent()
 					while ancestor?
@@ -113,7 +113,7 @@
 						ancestor = ancestor.getParent()
 					return false
 			)
-
+
 		Ext.Function.interceptAfter(
 			Ext.ComponentManager,
 			'register',
@@ -121,7 +121,7 @@
 				Deft.event.LiveEventBus.register( component )
 				return
 		)
-
+
 		Ext.Function.interceptAfter(
 			Ext.ComponentManager,
 			'unregister',
@@ -129,6 +129,6 @@
 				Deft.event.LiveEventBus.unregister( component )
 				return
 		)
-
+
 		return
-)+)


Add specs from the text-editor-component

--- spec/text-editor-presenter-spec.coffee
+++ spec/text-editor-presenter-spec.coffee
@@ -1488,11 +1488,11 @@
           }

       describe ".overlays", ->
+        [item] = []
         stateForOverlay = (presenter, decoration) ->
           presenter.getState().content.overlays[decoration.id]

         it "contains state for overlay decorations both initially and when their markers move", ->
-          item = {}
           marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
           decoration = editor.decorateMarker(marker, {type: 'overlay', item})
           presenter = buildPresenter(explicitHeight: 30, scrollTop: 20)
@@ -1540,7 +1540,6 @@
           }

         it "updates when ::baseCharacterWidth changes", ->
-          item = {}
           scrollTop = 20
           marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
           decoration = editor.decorateMarker(marker, {type: 'overlay', item})
@@ -1559,7 +1558,6 @@
           }

         it "updates when ::lineHeight changes", ->
-          item = {}
           scrollTop = 20
           marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
           decoration = editor.decorateMarker(marker, {type: 'overlay', item})
@@ -1578,7 +1576,6 @@
           }

         it "honors the 'position' option on overlay decorations", ->
-          item = {}
           scrollTop = 20
           marker = editor.markBufferRange([[2, 13], [4, 14]], invalidate: 'touch')
           decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})
@@ -1589,7 +1586,6 @@
           }

         it "is empty until all of the required measurements are assigned", ->
-          item = {}
           marker = editor.markBufferRange([[2, 13], [4, 14]], invalidate: 'touch')
           decoration = editor.decorateMarker(marker, {type: 'overlay', position: 'tail', item})

@@ -1607,6 +1603,169 @@

           presenter.setBoundingClientRect({top: 0, left: 0, height: 100, width: 500})
           expect(presenter.getState().content.overlays).not.toEqual({})
+
+        describe "when the overlay has been measured", ->
+          [gutterWidth, windowWidth, windowHeight, itemWidth, itemHeight, contentMargin, boundingClientRect, contentFrameWidth] = []
+          beforeEach ->
+            item = {}
+            gutterWidth = 5 * 10 # 5 chars wide
+            contentFrameWidth = 30 * 10
+            windowWidth = gutterWidth + contentFrameWidth
+            windowHeight = 9 * 10
+
+            itemWidth = 4 * 10
+            itemHeight = 4 * 10
+            contentMargin = 0
+
+            boundingClientRect =
+              top: 0
+              left: 0,
+              width: windowWidth
+              height: windowHeight
+
+          it "slides horizontally left when near the right edge", ->
+            scrollLeft = 20
+            marker = editor.markBufferPosition([0, 26], invalidate: 'never')
+            decoration = editor.decorateMarker(marker, {type: 'overlay', item})
+
+            presenter = buildPresenter({scrollLeft, windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
+            expectStateUpdate presenter, ->
+              presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
+
+            expectValues stateForOverlay(presenter, decoration), {
+              item: item
+              pixelPosition: {top: 1 * 10, left: 26 * 10 + gutterWidth - scrollLeft}
+            }
+
+            expectStateUpdate presenter, -> editor.insertText('a')
+            expectValues stateForOverlay(presenter, decoration), {
+              item: item
+              pixelPosition: {top: 1 * 10, left: windowWidth - itemWidth}
+            }
+
+            expectStateUpdate presenter, -> editor.insertText('b')
+            expectValues stateForOverlay(presenter, decoration), {
+              item: item
+              pixelPosition: {top: 1 * 10, left: windowWidth - itemWidth}
+            }
+
+          it "flips vertically when near the bottom edge", ->
+            scrollTop = 10
+            marker = editor.markBufferPosition([5, 0], invalidate: 'never')
+            decoration = editor.decorateMarker(marker, {type: 'overlay', item})
+
+            presenter = buildPresenter({scrollTop, windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
+            expectStateUpdate presenter, ->
+              presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
+
+            expectValues stateForOverlay(presenter, decoration), {
+              item: item
+              pixelPosition: {top: 6 * 10 - scrollTop, left: gutterWidth}
+            }
+
+            expectStateUpdate presenter, ->
+              editor.insertNewline()
+              editor.setScrollTop(scrollTop) # I'm fighting the editor
+            expectValues stateForOverlay(presenter, decoration), {
+              item: item
+              pixelPosition: {top: 6 * 10 - scrollTop - itemHeight, left: gutterWidth}
+            }
+
+          describe "when the overlay item has a margin", ->
+            beforeEach ->
+              itemWidth = 12 * 10
+              contentMargin = -(gutterWidth + 2 * 10)
+
+            it "slides horizontally right when near the left edge with margin", ->
+              editor.setCursorBufferPosition([0, 3])
+              cursor = editor.getLastCursor()
+              marker = cursor.marker
+              decoration = editor.decorateMarker(marker, {type: 'overlay', item})
+
+              presenter = buildPresenter({windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
+              expectStateUpdate presenter, ->
+                presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
+
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 1 * 10, left: 3 * 10 + gutterWidth}
+              }
+
+              expectStateUpdate presenter, -> cursor.moveLeft()
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 1 * 10, left: -contentMargin}
+              }
+
+              expectStateUpdate presenter, -> cursor.moveLeft()
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 1 * 10, left: -contentMargin}
+              }
+
+          describe "when the editor is very small", ->
+            beforeEach ->
+              windowWidth = gutterWidth + 6 * 10
+              windowHeight = 6 * 10
+              contentFrameWidth = windowWidth - gutterWidth
+              boundingClientRect.width = windowWidth
+              boundingClientRect.height = windowHeight
+
+            it "does not flip vertically and force the overlay to have a negative top", ->
+              marker = editor.markBufferPosition([1, 0], invalidate: 'never')
+              decoration = editor.decorateMarker(marker, {type: 'overlay', item})
+
+              presenter = buildPresenter({windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
+              expectStateUpdate presenter, ->
+                presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
+
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 2 * 10, left: 0 * 10 + gutterWidth}
+              }
+
+              expectStateUpdate presenter, -> editor.insertNewline()
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 3 * 10, left: gutterWidth}
+              }
+
+            it "does not adjust horizontally and force the overlay to have a negative left", ->
+              itemWidth = 6 * 10
+
+              marker = editor.markBufferPosition([0, 0], invalidate: 'never')
+              decoration = editor.decorateMarker(marker, {type: 'overlay', item})
+
+              presenter = buildPresenter({windowWidth, windowHeight, contentFrameWidth, boundingClientRect})
+              expectStateUpdate presenter, ->
+                presenter.setOverlayDimensions(decoration.id, itemWidth, itemHeight, contentMargin)
+
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 10, left: gutterWidth}
+              }
+
+              windowWidth = gutterWidth + 5 * 10
+              expectStateUpdate presenter, -> presenter.setWindowSize(windowWidth, windowHeight)
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 10, left: windowWidth - itemWidth}
+              }
+
+              windowWidth = gutterWidth + 1 * 10
+              expectStateUpdate presenter, -> presenter.setWindowSize(windowWidth, windowHeight)
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 10, left: 0}
+              }
+
+              windowWidth = gutterWidth
+              expectStateUpdate presenter, -> presenter.setWindowSize(windowWidth, windowHeight)
+              expectValues stateForOverlay(presenter, decoration), {
+                item: item
+                pixelPosition: {top: 10, left: 0}
+              }
+

     describe ".gutter", ->
       describe ".scrollHeight", ->


Add checks for undefined objects

Add checks for undefined objects

--- app/assets/utensils/collapse/collapse.coffee
+++ app/assets/utensils/collapse/collapse.coffee
@@ -77,7 +77,7 @@
     scroll = if @dimension is 'width' then 'scrollWidth' else 'scrollHeight'
     @target[@dimension](0)
     @transition('addClass', 'show', 'shown')
-    utensils.Detect.hasTransition && @target[@dimension](@target[0][scroll])
+    utensils.Detect.hasTransition && @target[@dimension](@target[0]?[scroll])

   deactivated: (e) ->
@@ -96,7 +96,7 @@

   reset: (size) ->
     @setTarget() unless @target
-    @target.removeClass('collapse')[@dimension](size || 'auto')[0].offsetWidth
+    @target.removeClass('collapse')[@dimension](size || 'auto')[0]?.offsetWidth
     @target[(if size isnt null then 'addClass' else 'removeClass')]('collapse')
     return this


cleaning and commenting cake.coffee

cleaning and commenting cake.coffee


:bug: Override constructor in bottom-tab-file

--- lib/views/bottom-tab-file.coffee
+++ lib/views/bottom-tab-file.coffee
@@ -1,6 +1,8 @@
 BottomTab = require './bottom-tab'

 class BottomTabFile extends BottomTab
+  constructor: (linter)->
+    super("Current File", linter)
   onClick: ->

 module.exports = BottomTabFile = document.registerElement('linter-bottom-tab-file', {prototype: BottomTabFile.prototype})


minor changes to the Formular#validator

minor changes to the Formular#validator

--- library/semantic/formular.coffee
+++ library/semantic/formular.coffee
@@ -77,9 +77,9 @@
         assert _.isString(message), "got no fail message"
         object = try _.find data, identity: id.toString()
         assert _.isPlainObject(object), "no #{id} object"
-        conditions = -> (object.checked or false) is yes
-        functional = -> check.call object, object.value
-        expression = -> check.test object.value or ("")
+        conditions = -> (try object.checked or no) is yes
+        functional = -> try check.call object, object.value
+        expression = -> check.test object.value or String()
         sequential = -> (object.value or null) in check
         select = _.isBoolean(check) and not conditions()
         method = _.isFunction(check) and not functional()


Add embedded support for Crystal.

Add embedded support for Crystal.

--- grammars/fixtures/fenced-code.cson
+++ grammars/fixtures/fenced-code.cson
@@ -25,12 +25,14 @@
   { pattern:'c' }
   { pattern:'clojure' }
   { pattern:'cpp|c\\+\\+', include:'source.cpp' }
+  { pattern:'cr|crystal', include:'source.crystal'}
   { pattern:'cs|csharp', include:'source.cs' }
   { pattern:'css' }
   { pattern:'diff|patch|rej', include:'source.diff' }
   { pattern:'elixir' }
   { pattern:'elm' }

+  # { pattern:'ecr', include:'source.crystal', contentName:'text.html.ecr'}
   # { pattern:'erb', include:'source.ruby', contentName:'text.html.erb' }

   { pattern:'erlang' }


Specs for #scalePlaceholder

Specs for #scalePlaceholder

--- apps/artwork/test/client/view-in-room.coffee
+++ apps/artwork/test/client/view-in-room.coffee
@@ -82,3 +82,19 @@
         @view.$placeholder.width(200)
         @view.getArtworkDimensions = -> [20, 20]
         @view.artworkScalingFactor().should.equal 0.55
+
+    describe '#scalePlaceholder', ->
+      it 'scales the placeholder and sets it at eye level if the significant dimension is less than 100', ->
+        @view.$placeholder.css = sinon.stub()
+        @view.scalePlaceholder()
+        args = @view.$placeholder.css.args[0][0]
+        _.keys(args).should.eql ['bottom', 'marginBottom', 'marginLeft', 'transform']
+        args.bottom.should.equal "#{@view.eyeLevel()}px"
+
+      it 'scales the placeholder and sets it at ground level if the significant dimension is greater than 100', ->
+        @view.$placeholder.css = sinon.stub()
+        @view.artwork.dimensions = -> '101 x 20 x 30in'
+        @view.scalePlaceholder()
+        args = @view.$placeholder.css.args[0][0]
+        _.keys(args).should.eql ['bottom', 'marginLeft', 'transform', 'transformOrigin']
+        args.bottom.should.equal "#{@view.groundLevel()}px"


Remove unused file that has errors

Remove unused file that has errors

--- src/components/course-calendar/week.cjsx
+++ src/components/course-calendar/week.cjsx
@@ -1,3 +0,0 @@
-moment = require 'moment'
-React = require 'react'
-{Calendar, Month, Week, Day} = require './react-calendar'


minor updates to newsletter signup.

minor updates to newsletter signup.

--- assets/coffeescript/newsletter_signup.coffee
+++ assets/coffeescript/newsletter_signup.coffee
@@ -1,11 +1,11 @@
 clearForm = ()->
   $("#mce-EMAIL").val('')
-  $("#mce-EMAIL").attr("placeholder","Success! Thanks for signing up.")
+  # $("#mce-EMAIL").attr("placeholder","Thanks for signing up.")
   $('label[for="newsletter-form-submit"]').removeClass("loading")
-  $('label[for="newsletter-form-submit"]').attr('disabled',false)
-  $('label[for="newsletter-form-submit"]').text("Sign Up!")
+  # $('label[for="newsletter-form-submit"]').attr('disabled',false)
+  $('label[for="newsletter-form-submit"]').text("Success!")

-  $("#newsletter-form-submit").attr('disabled',false)
+  # $("#newsletter-form-submit").attr('disabled',false)

 newsletterSignup = ()->
   # bind jquery/ajax events here


Fix to the fix

Fix to the fix

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -186,7 +186,7 @@
             unless Object::toString.call(opts) is '[object Object]'
                 throw new err.RqlDriverError "First argument to two-argument `close` must be a dictionary."
             cb = callback
-        else if Object::toString.call(opts) is '[object Object]'
+        else if Object::toString.call(optsOrCallback) is '[object Object]'
             opts = optsOrCallback
             cb = null
         else
@@ -397,7 +397,7 @@
         if callback?
             opts = optsOrCallback
             cb = callback
-        else if Object::toString.call(opts) is '[object Object]'
+        else if Object::toString.call(optsOrCallback) is '[object Object]'
             opts = optsOrCallback
             cb = null
         else


Change ridiculous WebSocketRails.Event description comments.

--- lib/assets/javascripts/websocket_rails/event.js.coffee
+++ lib/assets/javascripts/websocket_rails/event.js.coffee
@@ -1,6 +1,5 @@
 ###
-The Event object stores all the releavant information for events
-that are being queued for sending to the server.
+The Event object stores all the relevant event information.
 ###

 class WebSocketRails.Event


customscroll: char code constants

customscroll: char code constants

--- lib/components/scrollview/customscrollviewinner.coffee
+++ lib/components/scrollview/customscrollviewinner.coffee
@@ -8,6 +8,11 @@

 module.exports = class KDCustomScrollViewWrapper extends KDScrollView

+  BACKSPACE = 32
+  PAGEUP    = 33
+  PAGEDOWN  = 34
+  END       = 35
+  HOME      = 36

   constructor: (options = {}, data) ->

@@ -150,14 +155,14 @@
     return yes  if @getScrollHeight() <= @verticalThumb.getTrackSize()

     shouldPropagate = no
-    if event.which is 32 and event.shiftKey
+    if event.which is BACKSPACE and event.shiftKey
       @pageUp()
     else
       switch event.which
-        when 33 then @pageUp()
-        when 32, 34 then @pageDown()
-        when 35 then @scrollToBottom()
-        when 36 then @scrollTo top : 0
+        when PAGEUP then @pageUp()
+        when BACKSPACE, PAGEDOWN then @pageDown()
+        when END then @scrollToBottom()
+        when HOME then @scrollTo top : 0
         else shouldPropagate = yes

     return shouldPropagate


Added problem

Added problem

--- server/materials/data/topics/games_cyclic.coffee
+++ server/materials/data/topics/games_cyclic.coffee
@@ -8,7 +8,7 @@
 export default games_cyclic = () ->
     return {
         topic: topic("Игры на циклических графах", "Задачи на игры на циклических графах", [
-            label("TODO"),
             problem(3390),
+            problem({testSystem: "codeforces", contest: "787", problem: "C"}),
         ]),
     }


fix coffeelint error on register.test.coffee

--- servers/lib/server/handlers/register.test.coffee
+++ servers/lib/server/handlers/register.test.coffee
@@ -147,7 +147,7 @@
     username  = username.replace /(.)/g, '$1.'
     email = "#{username}@#{host}"

-    registerParams = generateRegisterRequestParams body: {email}
+    registerParams = generateRegisterRequestParams { body: { email } }

     request.post registerParams, (err, res, body) ->
       expect(err)             .to.not.exist


Server: Tests: remove daisy usage

Server: Tests: remove daisy usage

--- servers/lib/server/handlers/validate.test.coffee
+++ servers/lib/server/handlers/validate.test.coffee
@@ -1,16 +1,12 @@
-Bongo                                     = require 'bongo'
-koding                                    = require './../bongo'
-request                                   = require 'request'
-querystring                               = require 'querystring'
-
-{ daisy }                                 = Bongo
-{ expect }                                = require 'chai'
-{ generateRandomEmail
+{ async
+  expect
+  request
+  querystring
+  generateRandomEmail
   generateRandomString
-  generateRandomUsername }                = require '../../../testhelper'
+  generateRandomUsername }        = require '../../../testhelper'
 { generateValidateRequestBody
-  generateValidateRequestParams }         = require '../../../testhelper/handler/validatehelper'
-
+  generateValidateRequestParams } = require '../../../testhelper/handler/validatehelper'

 # here we have actual tests
@@ -22,61 +18,51 @@
       body       :
         username : generateRandomUsername()

-    queue       = []
-    methods     = ['put', 'patch', 'delete']
+    queue   = []
+    methods = ['put', 'patch', 'delete']

-    addRequestToQueue = (queue, method) -> queue.push ->
+    addRequestToQueue = (queue, method) -> queue.push (next) ->
       validateRequestParams.method = method
       request validateRequestParams, (err, res, body) ->
-        expect(err)             .to.not.exist
-        expect(res.statusCode)  .to.be.equal 404
-        queue.next()
+        expect(err).to.not.exist
+        expect(res.statusCode).to.be.equal 404
+        next()

     for method in methods
       addRequestToQueue queue, method

-    queue.push -> done()
-
-    daisy queue
+    async.series queue, done

   it 'should send HTTP 400 if fields params is not set', (done) ->

-    validateRequestParams       = generateValidateRequestParams()
-    validateRequestParams.body  = null
+    validateRequestParams      = generateValidateRequestParams()
+    validateRequestParams.body = null

     request.post validateRequestParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 400
-      expect(body)            .to.be.equal 'Bad request'
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 400
+      expect(body).to.be.equal 'Bad request'
       done()

   it.skip 'should send HTTP 200 if both email and username is not in use', (done) ->

-    queue = [
+    validateRequestParams = generateValidateRequestParams()

-      ->
-        validateRequestParams      = generateValidateRequestParams()
+    # querystring fails encoding nested objects
+    stringifiedFields = JSON.stringify
+      email    : generateRandomEmail()
+      username : generateRandomUsername()

-        # querystring fails encoding nested objects
-        stringifiedFields          = JSON.stringify
-          email    : generateRandomEmail()
-          username : generateRandomUsername()
+    validateRequestParams.body = "fields=#{stringifiedFields}"

-        validateRequestParams.body = "fields=#{stringifiedFields}"
+    request.post validateRequestParams, (err, res, body) ->
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 400
+      expect(body).to.be.equal 'asd'
+      queue.next()

-        request.post validateRequestParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 400
-          expect(body)            .to.be.equal 'asd'
-          queue.next()
-
-      -> done()
-
-    ]
-
-    daisy queue

 runTests()


Scrollable pointer usage fixed

Scrollable pointer usage fixed

--- impl/base/level2/scrollable.coffee
+++ impl/base/level2/scrollable.coffee
@@ -4,6 +4,7 @@
 signal = require 'signal'

 WHEEL_DIVISOR = 8
+MIN_POINTER_DELTA = 5

 module.exports = (impl) ->
 	{Types} = impl
@@ -78,11 +79,13 @@
 			v = 100 * -val / (1 + elapsed);
 			velocity = 0.8 * v + 0.2 * velocity;

+	pointerUsed = false
 	usePointer = (item) ->
 		horizontalContinuous = createContinuous item, 'x'
 		verticalContinuous = createContinuous item, 'y'

 		focus = false
+		listen = false
 		x = y = 0

 		moveMovement = (e) ->
@@ -91,7 +94,7 @@
 			scroll item, dx, dy

 		item.onPointerPressed (e) ->
-			focus = true
+			listen = true

 			item._impl.globalScale = getItemGlobalScale item
 			horizontalContinuous.press()
@@ -101,8 +104,11 @@

 		listenOnWindowSignals = ->
 			impl.window.onPointerReleased (e) ->
+				listen = false
+
 				return unless focus
 				focus = false
+				pointerUsed = false

 				moveMovement e

@@ -112,14 +118,20 @@
 				x = y = 0

 			impl.window.onPointerMove (e) ->
-				return unless focus
+				return unless listen
+
+				if pointerUsed and not focus
+					return

 				horizontalContinuous.update e.x - x
 				verticalContinuous.update e.y - y

-				x = e.x; y = e.y
+				if focus or Math.abs(e.x - x) + Math.abs(e.y - y) > MIN_POINTER_DELTA
+					if moveMovement(e) is signal.STOP_PROPAGATION
+						focus = true
+						pointerUsed = true

-				moveMovement e
+					x = e.x; y = e.y

 		if impl.window?
 			listenOnWindowSignals()


Fix Menu Link editing error on jquery autocomplete

--- app/assets/javascripts/georgia/views/links/link_form_content.js.coffee
+++ app/assets/javascripts/georgia/views/links/link_form_content.js.coffee
@@ -19,7 +19,7 @@
         event.stopPropagation()
         @$('#text').val(ui.item.url)
       )
-    .data( "autocomplete" )._renderItem = ( ul, item ) ->
+    .data('ui-autocomplete')._renderItem = ( ul, item ) ->
       return $( "

" )
         .data( "item.autocomplete", item )
         .append( "" + item.url + "" )


better authenticity token for bulk actions

--- app/assets/javascripts/effective_datatables/bulk_actions.js.coffee
+++ app/assets/javascripts/effective_datatables/bulk_actions.js.coffee
@@ -53,7 +53,8 @@

   url = $bulkAction.attr('href')
   title = $bulkAction.text()
-  token = $bulkAction.parent('li').data('authenticity-token')
+  downloadToken = $bulkAction.parent('li').data('authenticity-token')
+  token = $table.data('authenticity-token')
   values = $.map($selected, (input) -> input.getAttribute('value'))
   method = $bulkAction.data('ajax-method')

@@ -72,10 +73,10 @@

   $table.dataTable().data('bulk-actions-restore-selected-values', values)

-  if token # This is a file download
+  if downloadToken # This is a file download
     $.fileDownload(url,
       httpMethod: 'POST',
-      data: { ids: values, authenticity_token: token }
+      data: { ids: values, authenticity_token: downloadToken }
       successCallback: ->
         success = "Successfully completed #{title} bulk action"
         $table.one 'draw.dt', (e) -> $(e.target).DataTable().flash(success, 'success')
@@ -91,7 +92,7 @@
     $.ajax(
       method: method,
       url: url,
-      data: { ids: values }
+      data: { ids: values, authenticity_token: token }
     ).done((response) ->
       success = response['message'] || "Successfully completed #{title} bulk action"
       $table.one 'draw.dt', (e) -> $(e.target).DataTable().flash(success, 'success') and restoreSelected($(e.target), values)


cl can now handle checklist report

cl can now handle checklist report

--- app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
@@ -321,7 +321,10 @@
       return $conditionElement.text().replace(/\↵/g,"").trim()
     # survey report preview
     if $conditionElement.is('td')
-      return $conditionElement.text().replace(/\↵/g, "").trim()
+      if $conditionElement.hasClass('checklist')
+        return $conditionElement.find('span.hidden-answer').text().replace(/\↵/g, '').trim()
+      else
+        return $conditionElement.text().replace(/\↵/g, '').trim()
     $conditionElement.val()

 $ ->


Small debug removed

Small debug removed

--- app/assets/javascripts/javascript_renderer/helpers/form_tag.coffee
+++ app/assets/javascripts/javascript_renderer/helpers/form_tag.coffee
@@ -156,5 +156,3 @@

 helper 'UTF8EnforcerTag', () ->
   H.tag 'input', type: 'hidden', name: 'utf8', value: "✓"
-
-# utf8_enforcer_tag


add flexibility for waiting for invisible elements

--- test-integration/helpers/utils/wait.coffee
+++ test-integration/helpers/utils/wait.coffee
@@ -6,6 +6,9 @@
   # TODO reduce the copy pasta between for and forMultiple
   forMultiple: (locator, ms = 60 * 1000) ->
     locator = @test.utils.toLocator(locator)
+    locator.shouldBeVisible ?= true
+    waitUntil = if locator.shouldBeVisible then 'elementIsVisible' else 'elementIsEnabled'
+
     @giveTime ms, =>
       @test.utils.verboseWrap "Waiting for multiple #{JSON.stringify(locator)}", =>
         @test.driver.wait(selenium.until.elementsLocated(locator))
@@ -14,7 +17,7 @@

         el.then (elements) =>
           @test.utils.verbose("Found #{elements.count}")
-          @test.driver.wait(selenium.until.elementIsVisible(elements[0]))
+          @test.driver.wait(selenium.until[waitUntil](elements[0]))

     el = @test.driver.findElements(locator)
     el
@@ -22,11 +25,14 @@
   # Waits for an element to be available and bumps up the timeout to be at least 60sec from now
   for: (locator, ms = 60 * 1000) ->
     locator = @test.utils.toLocator(locator)
+    locator.shouldBeVisible ?= true
+    waitUntil = if locator.shouldBeVisible then 'elementIsVisible' else 'elementIsEnabled'
+
     @giveTime ms, =>
       @test.driver.wait(selenium.until.elementLocated(locator))
       el = @test.driver.findElement(locator)
       # Because of animations an element might be in the DOM but not visible
-      @test.utils.verboseWrap "Waiting for #{JSON.stringify(locator)}", => @test.driver.wait(selenium.until.elementIsVisible(el))
+      @test.utils.verboseWrap "Waiting for #{JSON.stringify(locator)}", => @test.driver.wait(selenium.until[waitUntil](el))
     el = @test.driver.findElement(locator)
     el


componentWillUnmount misspelling meant that enter listener would never unbind

--- src/components/exercise/step-card.cjsx
+++ src/components/exercise/step-card.cjsx
@@ -68,7 +68,7 @@
   componentWillMount: ->
     keymaster('enter', @onContinue) if @props.allowKeyNext

-  componentWilUnmount: ->
+  componentWillUnmount: ->
     keymaster.unbind('enter') if @props.allowKeyNext

   shouldComponentUpdate: (nextProps, nextState) ->


disable enter key submit, was causing more troubles for now than was worth having

--- src/components/exercise/step-card.cjsx
+++ src/components/exercise/step-card.cjsx
@@ -60,17 +60,10 @@
     disabled: false
     isContinueEnabled: true
     footer: <ExerciseDefaultFooter/>
-    allowKeyNext: true
+    allowKeyNext: false

   getInitialState: ->
     stepState = @getStepState(@props)
-
-  componentWillMount: ->
-    @clearKeys()
-    @startKeys() if @props.allowKeyNext
-
-  componentWillUnmount: ->
-    @clearKeys()

   shouldComponentUpdate: (nextProps, nextState) ->
     not (_.isEqual(@props, nextProps) and


added EntityConstructorMap into EntityRepository for canHandle matching

--- assets/scripts/coffee/scribe/repositories/entity_repository.coffee
+++ assets/scripts/coffee/scribe/repositories/entity_repository.coffee
@@ -1,3 +1,4 @@
+EntityConstructorMap = require 'scribe/repositories/entity_constructor_map'
 #
 # @author - Tim Shelburne <tim@musiconelive.com>
 #
@@ -6,11 +7,10 @@
 class EntityRepository

   constructor: (@entityClass)->
-    @entityClassName = getClassName @entityClass
     @entityList = []

   canHandle: (entityCheck)->
-    @entityClass is entityCheck or @entityClassName is entityCheck or entityCheck.constructor is @entityClass
+    @entityClass is entityCheck or EntityConstructorMap.match(entityCheck, @entityClass) or entityCheck.constructor is @entityClass

   add: (entity)-> @entityList.push entity

@@ -35,10 +35,5 @@
   isMatch = (entity, criteria)->
     return false for prop, value of criteria when entity[prop] isnt value
     true
-
-  getClassName = (entityClass)->
-    funcNameRegex = /function (.{1,})\(/;
-    results = (funcNameRegex).exec(entityClass.toString());
-    if (results? and results.length > 1) then results[1] else ""

 return EntityRepository


Update aloha's selection object after cursor placement.

--- scripts/views/workspace/content/aloha-edit.coffee
+++ scripts/views/workspace/content/aloha-edit.coffee
@@ -98,4 +98,7 @@
           alohaEditable = Aloha.getEditableById @$el.attr('id')
           alohaEditable.activate()

+          # Update what Aloha think's is the select range after focussing.
+          Aloha.Selection.updateSelection()
+
       @initalRender.resolve()


Use new class modifier helper

Use new class modifier helper

--- resources/assets/coffee/react/beatmap-discussions/discussion.coffee
+++ resources/assets/coffee/react/beatmap-discussions/discussion.coffee
@@ -9,6 +9,7 @@
 import * as React from 'react'
 import { button, div, i, span, a } from 'react-dom-factories'
 import { UserAvatar } from 'user-avatar'
+import { classWithModifiers } from 'utils/css'

 el = React.createElement

@@ -45,25 +46,25 @@
     return null if !@isVisible(@props.discussion)
     return null if !@props.discussion.starting_post && (!@props.discussion.posts || @props.discussion.posts.length == 0)

-    topClasses = "#{bn} js-beatmap-discussion-jump"
-    topClasses += " #{bn}--highlighted" if @state.highlighted
-    topClasses += " #{bn}--deleted" if @props.discussion.deleted_at?
-    topClasses += " #{bn}--timeline" if @props.discussion.timestamp?
-    topClasses += " #{bn}--preview" if @props.preview
-    topClasses += " #{bn}--review" if @props.discussion.message_type == 'review'
-
-    lineClasses = "#{bn}__line"
-    lineClasses += " #{bn}__line--resolved" if @props.discussion.resolved
+    lineClasses = classWithModifiers "#{bn}__line",
+      resolved: @props.discussion.resolved

     lastResolvedState = false
     @_resolvedSystemPostId = null

     firstPost = @props.discussion.starting_post || @props.discussion.posts[0]

+    topClasses = classWithModifiers bn,
+      deleted: @props.discussion.deleted_at?
+      highlighted: @state.highlighted
+      preview: @props.preview
+      review: @props.discussion.message_type == 'review'
+      timeline: @props.discussion.timestamp?
+      unread: !_.includes(@props.readPostIds, firstPost.id) && !@isOwner(firstPost) && !@props.preview
+    topClasses += ' js-beatmap-discussion-jump'
+
     user = @props.users[@props.discussion.user_id]
     group = if user.id == @props.beatmapset.user_id then mapperGroup else user.groups[0]
-
-    topClasses += " #{bn}--unread" unless _.includes(@props.readPostIds, firstPost.id) || @isOwner(firstPost) || @props.preview

     div
       className: topClasses


Updated method name.

Updated method name.

--- client/app/Applications/Ace.kdapplication/acefindandreplaceview.coffee
+++ client/app/Applications/Ace.kdapplication/acefindandreplaceview.coffee
@@ -98,22 +98,22 @@
     regExp        : @selections.indexOf("regex") > -1
     backwards     : no

-  findNext: -> @find_ "next"
+  findNext: -> @findHelper "next"

-  findPrev: -> @find_ "prev"
+  findPrev: -> @findHelper "prev"

-  find_: (direction) ->
+  findHelper: (direction) ->
     keyword = @findInput.getValue()
     return unless keyword
     methodName = if direction is "prev" then "findPrevious" else "find"
     @getDelegate().ace.editor[methodName] @findInput.getValue(), @getSearchOptions()
     @findInput.focus()

-  replace:    -> @replace_ no
+  replace:    -> @replaceHelper no

-  replaceAll: -> @replace_ yes
+  replaceAll: -> @replaceHelper yes

-  replace_: (doReplaceAll) ->
+  replaceHelper: (doReplaceAll) ->
     findKeyword    = @findInput.getValue()
     replaceKeyword = @replaceInput.getValue()
     return unless findKeyword or replaceKeyword


Fix typo

Fix typo

--- lib/assets/javascripts/models/meta_profile.js.coffee
+++ lib/assets/javascripts/models/meta_profile.js.coffee
@@ -27,10 +27,10 @@
         avatar_digest: server_meta_post.attachments?[0]?.digest
       }, server_meta_post.content.profile || {})

-      existing_model = constructorFn.find(entity, fetch: false)
+      model = constructorFn.find(entity, fetch: false)

-      if existing_model
-        existing_model.parseAttributes(attrs)
+      if model
+        model.parseAttributes(attrs)
       else
         model = new constructorFn(attrs)


Workspace: Added a layer to save process to track save event.

--- client/CollaborativeWorkspace/panes/collaborativeeditorpane.coffee
+++ client/CollaborativeWorkspace/panes/collaborativeeditorpane.coffee
@@ -62,11 +62,15 @@
       scrollPastEnd   : yes
       mode            : "htmlmixed"
       extraKeys       :
-        "Cmd-S"       : @bound "save"
-        "Ctrl-S"      : @bound "save"
+        "Cmd-S"       : @bound "handleSave"
+        "Ctrl-S"      : @bound "handleSave"

     @setEditorTheme()
     @setEditorMode()
+
+  handleSave: ->
+    @save()
+    @workspace.addToHistory "$0 saved #{@getData().name}"

   setEditorTheme: ->
     if document.getElementById "codemirror-ambiance-style"


fiddled with the commentsByRange DB query to fetch the most current 3 instead of the first 3 comments

--- client/landing/app/Applications/Demos.kdapplication/AppView.coffee
+++ client/landing/app/Applications/Demos.kdapplication/AppView.coffee
@@ -2,34 +2,26 @@

   viewAppended:()->

+    # foo = new KDView
+    #       cssClass : "bar"
+    #     @addSubView foo
+    #     foo.$().css "background-color" : "pink"
+
     @addSubView form = new KDFormView
       callback : ->
         log arguments, "form submitted"
-
+        new KDNotificationView
+          title : "something"
+
     form.addSubView input = new KDInputView
-      name              : "kk"
-      validate          :
-        rules           :
-          required      : yes
-          # maxLength     : 20
-          # minLength     : 10
-          rangeLength   : [8,25]
-          # email         : yes
-          # creditCard    : yes
-
-    form.addSubView new KDButtonView
-      title: "disable range validation"
-      callback : ->
-        validation = input.getOptions().validate
-        delete validation.rules.rangeLength
-        input.setValidation validation
-
-    form.addSubView new KDButtonView
-      title: "enable range validation"
-      callback : ->
-        validation = input.getOptions().validate
-        validation.rules.rangeLength = [8,25]
-        input.setValidation validation
-
-
-
+     name              : "kk"
+     validate          :
+       rules           :
+         required      : yes
+         # maxLength     : 20
+         #          minLength     : 10
+         #          rangeLength   : [8,25]
+         #          email         : yes
+         #          creditCard    : yes
+       messages        :
+         required      : "test"


removed unnecessary object properties

removed unnecessary object properties

--- client/landing/app/CommonViews/opinions/opinionlistitemview.coffee
+++ client/landing/app/CommonViews/opinions/opinionlistitemview.coffee
@@ -260,20 +260,8 @@
     </div>
     """

-class OpinionBodyView extends KDView
-  constructor:(options,data)->
-    super options, data
-
-  viewAppended:->
-    @setTemplate @pistachio()
-    @template.update()
-
-  render:(force=no)->
-    if force
-      super
-    else
-      no
-
+class OpinionBodyView extends JView
+
   pistachio:->
     """
       {{@utils.expandUsernames(@utils.applyMarkdown(#(body)),"pre")}}


Update specs for upcoming nested binding changes

--- specs/examples/flattening_connection.specs.coffee
+++ specs/examples/flattening_connection.specs.coffee
@@ -13,61 +13,76 @@

 describe 'Flattening connection', ->

-  class NestedChannel extends Transmitter.Channels.CompositeChannel
-
-    constructor: (@nestedObject, @serializedVar) ->
-      @valueVarDynamic = [@nestedObject?.valueVar].filter (v) -> v?
-
-    @defineChannel ->
-      new Transmitter.Channels.SimpleChannel()
-        .inForwardDirection()
-        .fromDynamicSources @valueVarDynamic
-        .toTarget @serializedVar
-        .withTransform (valuePayloads) =>
-          valuePayloads.merge().map ([value]) =>
-            {name: @nestedObject?.name ? null, value: value ? null}
+  beforeEach ->
+    @define 'serializedVar', new Transmitter.Nodes.Variable()
+    @define 'flatVar', new Transmitter.Nodes.Variable()
+    @define 'nestedVar', new Transmitter.Nodes.Variable()
+    @define 'nestedBackwardChannelVar',
+      new Transmitter.ChannelNodes.ChannelVariable()
+    @define 'nestedForwardChannelVar',
+      new Transmitter.ChannelNodes.ChannelVariable()

-    @defineChannel ->
-      new Transmitter.Channels.SimpleChannel()
-        .inBackwardDirection()
-        .fromSource @serializedVar
-        .toDynamicTargets @valueVarDynamic
-        .withTransform (serializedPayload) ->
-          [serializedPayload.map (serialized) -> serialized?.value]
+    @createFlatChannel = =>
+      new Transmitter.Channels.CompositeChannel()
+        .defineChannel =>
+          new Transmitter.Channels.SimpleChannel()
+            .inForwardDirection()
+            .fromSources @flatVar, @nestedVar
+            .toTarget @serializedVar
+            .withTransform ([flatPayload, nestedPayload]) ->
+              flatPayload.merge(nestedPayload).map ([value, nestedObject]) ->
+                {name: nestedObject?.name ? null, value: value ? null}
+
+        .defineChannel =>
+          new Transmitter.Channels.SimpleChannel()
+            .inBackwardDirection()
+            .fromSource @serializedVar
+            .toTarget @nestedVar
+            .withTransform (payload) ->
+              payload.map (serialized) ->
+                if serialized?
+                  return new NestedObject(serialized.name)
+                else
+                  null

-  beforeEach ->
-    @define 'serializedVar', new Transmitter.Nodes.Variable()
-    @define 'nestedVar', new Transmitter.Nodes.Variable()
-    @define 'nestedChannelVar', new Transmitter.ChannelNodes.ChannelVariable()
+    @createNestedChannel = =>
+      new Transmitter.Channels.CompositeChannel()
+        .defineChannel =>
+          new Transmitter.Channels.SimpleChannel()
+            .fromSource @nestedVar
+            .toConnectionTarget @nestedBackwardChannelVar
+            .withTransform (payload) =>
+              payload.map (nestedObject) =>
+                valueVarDynamic = [nestedObject?.valueVar].filter (v) -> v?
+                new Transmitter.Channels.SimpleChannel()
+                  .inBackwardDirection()
+                  .fromSource @serializedVar
+                  .toDynamicTargets valueVarDynamic
+                  .withTransform (serializedPayload) ->
+                    [serializedPayload.map (serialized) -> serialized?.value]

-    @createBackwardChannel = =>
-      new Transmitter.Channels.SimpleChannel()
-        .inBackwardDirection()
-        .fromSource @serializedVar
-        .toTarget @nestedVar
-        .withTransform (payload) ->
-          payload.map (serialized) ->
-            if serialized?
-              return new NestedObject(serialized.name)
-            else
-              null
-
-    @createNestedChannel = =>
-      new Transmitter.Channels.SimpleChannel()
-        .fromSource @nestedVar
-        .toConnectionTarget @nestedChannelVar
-        .withTransform (payload) =>
-          payload.map (nestedObject) =>
-            new NestedChannel(nestedObject, @serializedVar)
+        .defineChannel =>
+          new Transmitter.Channels.SimpleChannel()
+            .fromSource @nestedVar
+            .toConnectionTarget @nestedForwardChannelVar
+            .withTransform (payload) =>
+              payload.map (nestedObject) =>
+                valueVarDynamic = [nestedObject?.valueVar].filter (v) -> v?
+                new Transmitter.Channels.SimpleChannel()
+                  .inForwardDirection()
+                  .fromDynamicSources valueVarDynamic
+                  .toTarget @flatVar
+                  .withTransform (valuePayloads) =>
+                    valuePayloads.merge().map ([value]) -> value

   describe 'initialization', ->

     specify 'has default const value after initialization', ->
       Transmitter.startTransmission (tr) =>
-        @createBackwardChannel().init(tr)
+        @createFlatChannel().init(tr)

       # Separate transmissions to test channel init querying
       Transmitter.startTransmission (tr) =>
@@ -81,7 +96,7 @@

     beforeEach ->
       Transmitter.startTransmission (tr) =>
-        @createBackwardChannel().init(tr)
+        @createFlatChannel().init(tr)
         @createNestedChannel().init(tr)

@@ -154,7 +169,7 @@

         Transmitter.startTransmission (tr) =>
           tr.reverseOrder = order is 'reverse'
-          @createBackwardChannel().init(tr)
+          @createFlatChannel().init(tr)
           @createNestedChannel().init(tr)
           @createDerivedChannel().init(tr)


FIX: Add sql type and vb type

FIX: Add sql type and vb type

--- src/app/common/file-uploader/file-uploader.coffee
+++ src/app/common/file-uploader/file-uploader.coffee
@@ -50,7 +50,8 @@
         name:       'CSV'
       code:
         extensions: ['pas', 'cpp', 'c', 'cs', 'h', 'java', 'py', 'js', 'html', 'coffee', 'rb', 'css',
-                    'scss', 'yaml', 'yml', 'xml', 'json', 'ts']
+                    'scss', 'yaml', 'yml', 'xml', 'json', 'ts',
+                    'vb', 'sql']
         icon:       'fa-file-code-o'
         name:       'code'
       image:


fix auth error response and use 401 for malformed header

GitOrigin-RevId: 6dc4d10c25db7710b3e4e8e876d425a1e84574c8

--- services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
+++ services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
@@ -194,10 +194,12 @@
 			response = new Oauth2Server.Response(res)
 			Oauth2Server.server.authenticate request, response, {}, (err, token) ->
 				if err?
+					# use a 401 status code for malformed header for git-bridge
+					err.code = 401 if err.code == 400 and err.message == 'Invalid request: malformed authorization header'
 					# fall back to v1 on invalid token
 					return AuthenticationController._requireOauthV1Fallback req, res, next if err.code == 401
-					# bubble up all other errors
-					return next(err)
+					# send all other errors
+					return res.status(err.code).json({error: err.name, error_description: err.message})
 				req.oauth =
 					access_token: token.accessToken
 				req.oauth_token = token


Improve appearance of tags in Solarized themes

--- grammars/csound-document.cson
+++ grammars/csound-document.cson
@@ -5,31 +5,55 @@

 patterns: [
   {
-    begin: "<(CsoundSynthesi[sz]er)>"
-    end:   "</(CsoundSynthesi[sz]er)>"
-    beginCaptures: 1: name: "entity.name.tag.csound-document"
-    endCaptures:   1: name: "entity.name.tag.csound-document"
+    begin: "(<)(CsoundSynthesi[sz]er)(>)"
+    end:   "(</)(CsoundSynthesi[sz]er)(>)"
+    beginCaptures:
+      1: name: "punctuation.definition.tag.begin.csound-document"
+      2: name: "entity.name.tag.begin.csound-document"
+      3: name: "punctuation.definition.tag.begin.csound-document"
+    endCaptures:
+      1: name: "punctuation.definition.tag.end.csound-document"
+      2: name: "entity.name.tag.end.csound-document"
+      3: name: "punctuation.definition.tag.end.csound-document"
     patterns: [
       {
         name:  "meta.orchestra.csound-document"
-        begin: "<(CsOptions)>"
-        end:   "</(CsOptions)>"
-        beginCaptures: 1: name: "entity.name.tag.csound-document"
-        endCaptures:   1: name: "entity.name.tag.csound-document"
+        begin: "(<)(CsOptions)(>)"
+        end:   "(</)(CsOptions)(>)"
+        beginCaptures:
+          1: name: "punctuation.definition.tag.begin.csound-document"
+          2: name: "entity.name.tag.begin.csound-document"
+          3: name: "punctuation.definition.tag.begin.csound-document"
+        endCaptures:
+          1: name: "punctuation.definition.tag.end.csound-document"
+          2: name: "entity.name.tag.end.csound-document"
+          3: name: "punctuation.definition.tag.end.csound-document"
         patterns: [include: "source.csound#comments"]
       },{
         name:  "meta.orchestra.csound-document"
-        begin: "<(CsInstruments)>"
-        end:   "</(CsInstruments)>"
-        beginCaptures: 1: name: "entity.name.tag.csound-document"
-        endCaptures:   1: name: "entity.name.tag.csound-document"
+        begin: "(<)(CsInstruments)(>)"
+        end:   "(</)(CsInstruments)(>)"
+        beginCaptures:
+          1: name: "punctuation.definition.tag.begin.csound-document"
+          2: name: "entity.name.tag.begin.csound-document"
+          3: name: "punctuation.definition.tag.begin.csound-document"
+        endCaptures:
+          1: name: "punctuation.definition.tag.end.csound-document"
+          2: name: "entity.name.tag.end.csound-document"
+          3: name: "punctuation.definition.tag.end.csound-document"
         patterns: [include: "source.csound"]
       },{
         name:  "meta.score.csound-document"
-        begin: "<(CsScore)>"
-        end:   "</(CsScore)>"
-        beginCaptures: 1: name: "entity.name.tag.csound-document"
-        endCaptures:   1: name: "entity.name.tag.csound-document"
+        begin: "(<)(CsScore)(>)"
+        end:   "(</)(CsScore)(>)"
+        beginCaptures:
+          1: name: "punctuation.definition.tag.begin.csound-document"
+          2: name: "entity.name.tag.begin.csound-document"
+          3: name: "punctuation.definition.tag.begin.csound-document"
+        endCaptures:
+          1: name: "punctuation.definition.tag.end.csound-document"
+          2: name: "entity.name.tag.end.csound-document"
+          3: name: "punctuation.definition.tag.end.csound-document"
         patterns: [include: "source.csound-score"]
       },{
         name:  "meta.html.csound-document"
@@ -88,9 +112,15 @@
   tags:
     patterns: [
       {
-        begin: "<([a-zA-Z0-9:-]+)>"
-        end:   "</([a-zA-Z0-9:-]+)>"
-        beginCaptures: 1: name: "entity.name.tag.csound-document"
-        endCaptures:   1: name: "entity.name.tag.csound-document"
+        begin: "(<)([a-zA-Z0-9:-]+)(>)"
+        end:   "(</)([a-zA-Z0-9:-]+)(>)"
+        beginCaptures:
+          1: name: "punctuation.definition.tag.begin.csound-document"
+          2: name: "entity.name.tag.begin.csound-document"
+          3: name: "punctuation.definition.tag.begin.csound-document"
+        endCaptures:
+          1: name: "punctuation.definition.tag.end.csound-document"
+          2: name: "entity.name.tag.end.csound-document"
+          3: name: "punctuation.definition.tag.end.csound-document"
       }
     ]


[refactor] Pulled out `onOrderChanged` (5m)

--- app/assets/javascripts/houston-scheduler/views/sequence_view.coffee
+++ app/assets/javascripts/houston-scheduler/views/sequence_view.coffee
@@ -10,7 +10,7 @@
     # send the sort order 800ms after the user has
     # stopped making changes to it.
     #
-    @delayedNotifier = new Lail.DelayedAction(_.bind(@updateOrder, @), delay: 800)
+    @delayedNotifier = new Lail.DelayedAction(_.bind(@saveTicketOrder, @), delay: 800)

     @sortedTickets = @tickets.sorted()
     @unsortedTickets = @tickets.unresolved().unsorted().sortBy (ticket)-> ticket.get('summary')
@@ -50,12 +50,10 @@

     @$el.find('#sequence_unsorted').on 'sortreceive', (event, ui)=>
       @clearSequenceOfTicketFor(ui.item)
-      @delayedNotifier.trigger()
-      @adjustVelocityIndicatorHeight()
+      @onOrderChanged()

     @$el.find('#sequence_sorted').on 'sortupdate', (event, ui)=>
-      @delayedNotifier.trigger()
-      @adjustVelocityIndicatorHeight()
+      @onOrderChanged()

     @adjustVelocityIndicatorHeight()

@@ -83,7 +81,11 @@

-  updateOrder: ->
+  onOrderChanged: ->
+    @delayedNotifier.trigger()
+    @adjustVelocityIndicatorHeight()
+
+  saveTicketOrder: ->
     $tickets = $('#sequence_sorted .sequence-ticket')
     ids = _.map $tickets, (el)-> $(el).attr('data-ticket-id')
     @setTicketSequence(ids[i], i + 1) for i in [0...ids.length]
@@ -129,8 +131,7 @@
         $ticket = $ticket.remove().insertBefore($target).data('multiselectable', true).pseudoHover()

       @clearSequenceOfTicketFor($ticket)
-      @delayedNotifier.trigger()
-      @adjustVelocityIndicatorHeight()
+      @onOrderChanged()

   moveUp: ->
     $ticket = $('.sequence-ticket.selected')
@@ -138,8 +139,7 @@
     if $prev.length > 0
       $ticket.remove().insertBefore($prev).data('multiselectable', true).pseudoHover()

-      @delayedNotifier.trigger()
-      @adjustVelocityIndicatorHeight()
+      @onOrderChanged()

   moveRight: ->
     $ticket = $('#sequence_unsorted .sequence-ticket.selected')
@@ -151,8 +151,7 @@
       else
         $ticket.remove().insertBefore($target).data('multiselectable', true).pseudoHover()

-      @delayedNotifier.trigger()
-      @adjustVelocityIndicatorHeight()
+      @onOrderChanged()

   moveDown: ->
     $ticket = $('.sequence-ticket.selected')
@@ -160,8 +159,7 @@
     if $next.length > 0
       $ticket.remove().insertAfter($next).data('multiselectable', true).pseudoHover()

-      @delayedNotifier.trigger()
-      @adjustVelocityIndicatorHeight()
+      @onOrderChanged()


記録ボタン: 記録後のflashメッセージに記録したエピソードのページへのリンクを表示する

--- app/assets/javascripts/v3/base/components/AnnRecordButton.js.coffee
+++ app/assets/javascripts/v3/base/components/AnnRecordButton.js.coffee
@@ -52,10 +52,10 @@
         $.ajax
           method: "GET"
           url: "/api/internal/works/#{latestStatus.work.id}/latest_status"
-        .done (latestStatus) =>
-          @$dispatch("AnnFlash:show", "記録しました")
-          index = @_getLatestStatusIndex(latestStatus)
-          @latestStatuses.$set(index, @_initLatestStatus(latestStatus))
+        .done (newLatestStatus) =>
+          @$dispatch("AnnFlash:show", @_flashMessage(latestStatus))
+          index = @_getLatestStatusIndex(newLatestStatus)
+          @latestStatuses.$set(index, @_initLatestStatus(newLatestStatus))
       .fail (data) =>
         latestStatus.record.isSaving = false
         @$dispatch("AnnFlash:show", data.responseJSON.message, "danger")
@@ -74,3 +74,11 @@
     _getLatestStatusIndex: (latestStatus) ->
       _.findIndex @latestStatuses, (ls) ->
         ls.id == latestStatus.id
+
+    _flashMessage: (latestStatus) ->
+      episodeLink = """
+        <a href='/works/#{latestStatus.work.id}/episodes/#{latestStatus.next_episode.id}'>
+          記録を見る
+        </a>
+      """
+      "記録しました。#{episodeLink}"


Fix CallHistoryTable to start with West as that
seems to be the standard display order.

Credit to keith meinelt for reporting the issue.

--- dist/gae/scripts/view.coffee
+++ dist/gae/scripts/view.coffee
@@ -843,6 +843,12 @@
 class CallHistoryTable extends HTMLTableElement
     constructor: (@board, @callHistory, @showLoadingSpinner) ->
         @className = 'history'
+        @positionsInDisplayOrder = [
+            model.Position.WEST,
+            model.Position.NORTH,
+            model.Position.EAST,
+            model.Position.SOUTH
+        ]
         @setupView()

     updateFromCallHistory: (@callHistory, @showLoadingSpinner) ->
@@ -852,7 +858,7 @@

     setupView: ->
         headerRow = @insertRow(-1)
-        for position in model.Position.POSITIONS
+        for position in @positionsInDisplayOrder
             positionHeader = document.createElement('th')
             headerRow.appendChild(positionHeader)
             positionHeader.textContent = position.displayName()
@@ -861,16 +867,17 @@

         # FIXME: This would probably be cleaner using a biddingRounds() and callForRound() like bidhistory.py uses.
         firstRow = @insertRow(-1)
-        for position in model.Position.POSITIONS
+        for position in @positionsInDisplayOrder
             if position.index() == @board.dealer.index()
                 break
             firstRow.insertCell(-1)  # Add empty cells for all players before the dealer.

+        firstDisplayPositionIndex = @positionsInDisplayOrder[0].index()
         currentRow = firstRow
         biddingComplete = @callHistory.isComplete()
         for call, callIndex in @callHistory.calls
             positionIndex = (@board.dealer.index() + callIndex) % model.Position.POSITIONS.length
-            if positionIndex == 0 and callIndex != 0
+            if positionIndex == firstDisplayPositionIndex and callIndex != 0
                 currentRow = @insertRow(-1)
             partialHistory = @callHistory.calls[0..callIndex]
             currentCell = currentRow.insertCell(-1)


fixed quiz creation client code

fixed quiz creation client code

--- assets/js/quiz.coffee
+++ assets/js/quiz.coffee
@@ -12,7 +12,7 @@
 			header: @header()
 			text: @text()
 		if @new
-			$.ajax "/quizes/create", data, (quiz)->
+			$.post "/quizes/create", data, (quiz)->
 				@new = false
 				@id = quiz._id
 			,"json"


Connect "view all my work" button to tabs

--- src/components/student-dashboard/dashboard.cjsx
+++ src/components/student-dashboard/dashboard.cjsx
@@ -1,7 +1,7 @@
 React  = require 'react'
 BS     = require 'react-bootstrap'
 moment = require 'moment'
-
+_ = require 'underscore'
 DontForgetPanel = require './dont-forget-panel'
 EmptyPanel      = require './empty-panel'
 UpcomingPanel   = require './upcoming-panel'
@@ -19,8 +19,15 @@
 module.exports = React.createClass
   displayName: 'StudentDashboard'

-  contextTypes:
-    router: React.PropTypes.func
+  propTypes:
+    courseId: React.PropTypes.any.isRequired
+    selectedTabIndex: React.PropTypes.number
+
+  getInitialState: ->
+    selectedTabIndex: 1
+
+  selectTab: (index) ->
+    this.setState(selectedTabIndex:index);

   render: ->
     courseId = @props.courseId
@@ -33,7 +40,10 @@
         <BS.Col xs={12} md={9}>

{longTitle}

-          <BS.TabbedArea animation={false}>
+          <BS.TabbedArea
+            activeKey = {@state.selectedTabIndex}
+            onSelect  = {@selectTab}
+            animation = {false}>

             <BS.TabPane eventKey={1} tab='This Week'>
               <ThisWeekPanel   courseId={courseId}/>
@@ -53,7 +63,7 @@

How am I doing?

             <BS.Button
               bsStyle="primary"
-              onClick={@viewWork}
+              onClick={_.partial(@selectTab, 2)}
               className="-view-my-work"
             >
               View All My Work


removed not-working scrollbar stuff

removed not-working scrollbar stuff

--- src/desktop/components/view_in_room/view.coffee
+++ src/desktop/components/view_in_room/view.coffee
@@ -1,6 +1,5 @@
 _ = require 'underscore'
 Backbone = require 'backbone'
-Scrollbar = require '../scrollbar/index.coffee'
 template = -> require('./index.jade') arguments...

 module.exports = class ViewInRoom extends Backbone.View
@@ -23,7 +22,6 @@
   # Should provide dimensions AND (img OR imgUrl, imgSelector, positionStyles)
   initialize: ({ @$img, @imgUrl, @imgSelector, @positionStyles, @dimensions }) ->
     $(window).on 'resize.view-in-room', _.throttle(@scale, 100)
-    @scrollbar = new Scrollbar
     @$sourceImage = $(@imgSelector)

   __render__: ->
@@ -32,7 +30,6 @@

   render: ->
     @__render__()
-    @scrollbar.disable()
     document.body.style.overflowY = 'hidden'
     @cacheSelectors()
     @injectImage()
@@ -167,7 +164,6 @@
           @$img.css visibility: 'visible'
         if @$sourceImage
           @$sourceImage.css visibility: 'visible'
-        @scrollbar.reenable()
         document.body.style.overflowY = 'visible'
         ViewInRoom.__super__.remove.apply this, arguments
         @trigger 'removed'


seekTo time from youtube url

seekTo time from youtube url

--- src/scripts/controllers/subreddit.coffee
+++ src/scripts/controllers/subreddit.coffee
@@ -48,6 +48,10 @@
         $scope.nextVideo()

     $scope.$on 'youtube.player.ready', ->
+        time = $youtube.getTimeFromURL $scope.currentPost.url
+        if time?
+            $youtube.player.seekTo time, true
+
         $youtube.player.playVideo()

     $scope.$on 'redditelly.post.change', ->


Add missing parameters to replyTo in toys

--- handlers/toys.coffee
+++ handlers/toys.coffee
@@ -40,10 +40,10 @@
 			sides = 6 if not sides? or sides is ''
 			dice = 1 if not dice? or dice is ''
 			if sides > 150
-				api.replyTo message, __("The maximum number of sides is 150.")
+				api.replyTo message, __("The maximum number of sides is 150."), no, callback
 				return
 			if dice > 50
-				api.replyTo message, __("The maximum number of dices is 50.")
+				api.replyTo message, __("The maximum number of dices is 50."), no, callback
 				return
 			api.sendMessage Random.dice(sides, dice)(mt).join(', '), yes, callback


Remove runPostAction method from NFinderTreeController

--- client/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -223,14 +223,12 @@
         callback  : (confirmation)=>
           @deleteFiles @selectedNodes if confirmation
           @setKeyView()
-          @runPostActions 'delete', extension
     else
       @beingEdited = nodeView
       nodeView.confirmDelete (confirmation)=>
         @deleteFiles [nodeView] if confirmation
         @setKeyView()
         @beingEdited = null
-        @runPostActions 'delete', extension

   deleteFiles:(nodes, callback)->

@@ -402,13 +400,6 @@
       folder.emit "fs.run.finished"
       callback?()

-  runPostActions:(action, extension)->
-    actions   =
-      delete  :
-        kdapp :
-          => @getSingleton("kodingAppsController").refreshApps()
-
-    actions[action]?[extension]?()

   cloneRepo:(nodeView)->


teams: removed unused var

teams: removed unused var

--- client/landing/site.landing/coffee/team/tabs/teamusernametab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamusernametab.coffee
@@ -19,9 +19,8 @@

     super options, data

-    teamData                = KD.utils.getTeamData()
-    { alreadyMember, join } = teamData.signup
-    { mainController }      = KD.singletons
+    teamData          = KD.utils.getTeamData()
+    { alreadyMember } = teamData.signup

     @header = new MainHeaderView
       cssClass : 'team'


newpie experiments

newpie experiments

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -259,6 +259,7 @@
             .attr('class', 'pointer')
             .style('fill', 'none')
             .style('stroke', 'black')
+            .attr('stroke-width', 3)
             .attr('marker-end', 'url(#circ)')
         pointers.exit().remove()
         pointers.transition().attr 'd', (d) ->


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -287,29 +287,38 @@
         #arcs.append("svg:path").attr("fill", (d, i) -> color i)
         #    .attr("fill-opacity", 0.4).attr("d", arc)

+        #path.transition()
+        #    .duration(1500)
+        #    #.attrTween("d", pieTween)
+        #    .attrTween("d", (d,i) ->
+        #        console.log("d",d)
+        #        console.log("i",i)
+        #        #i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
+        #        i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: 90, endAngle: 135})
+        #        return (t) ->
+        #            b = i(t)
+        #            return arc(b)
+        #    )
+
+        #path.exit()
+        #    .transition()
+        #    .duration(300)
+        #    .attrTween("d", (d,i) ->
+        #        i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
+        #        return (t) ->
+        #            b = i(t)
+        #            return arc(b)
+        #    )
+        #    .remove()
         path.transition()
             .duration(1500)
-            #.attrTween("d", pieTween)
-            .attrTween("d", (d,i) ->
-                console.log("d",d)
-                console.log("i",i)
-                #i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
-                i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: 90, endAngle: 135})
-                return (t) ->
-                    b = i(t)
-                    return arc(b)
-            )
+            .attrTween("d", @pieTween);

         path.exit()
             .transition()
             .duration(300)
-            .attrTween("d", (d,i) ->
-                i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
-                return (t) ->
-                    b = i(t)
-                    return arc(b)
-            )
-            .remove()
+            .attrTween("d", @removePieTween)
+            .remove();

         labels = labelGroup.selectAll("text")
             .data(piedata.sort((p1,p2) -> return p1.startAngle - p2.startAngle))


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -2,11 +2,8 @@

     @accessor 'data'

-    @pie = d3.layout.pie()
-    @arc = d3.svg.arc()
-
     #constructor: ->
-    ready: ->
+    #ready: ->
         #@oldPieData = ''

     onData: (data) ->
@@ -49,8 +46,8 @@
         # END CONFIG ZONE
         #

-        @pie = @pie.value((d) -> d.value).sort(null)
-        @arc = @arc.innerRadius(@radiusi).outerRadius(@radiuso)
+        @pie = d3.layout.pie().value((d) -> d.value).sort(null)
+        @arc = d3.svg.arc().innerRadius(@radiusi).outerRadius(@radiuso)

         @update(data.data)


do not display the duplicat error - more than one call is intentionally.

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -75,7 +75,7 @@
         proxy         : { mode: 'vm' }
         regYears      : 0
       domainObj.save (err)->
-        console.log err  if err?
+        console.log err  unless err?.code? is 11000
         unless err
           account.addDomain domainObj, (err)->
             console.log err  if err?


Fixes #681

Fixes #681

Changed how 'git whatchanged' result is parsed when generating commit amend

--- lib/models/git-commit-amend.coffee
+++ lib/models/git-commit-amend.coffee
@@ -46,16 +46,17 @@
   previousFiles.filter (p) -> p.path in currentPaths is false

 parse = (prevCommit) ->
-  lines = prevCommit.split(/\n/).filter (line) -> line isnt '/n'
-  statusRegex = /(([ MADRCU?!])\s(.*))/
-  indexOfStatus = lines.findIndex (line) -> statusRegex.test line
+  statusRegex = /\n{2,}((?:(?::\w{6} \w{6}(?: \w{7}\.{3}){2} [ MADRCU?!]\s+.+?\n?))*)$/
+  firstSpliting = statusRegex.exec prevCommit

-  prevMessage = lines.splice 0, indexOfStatus - 1
-  prevMessage.reverse()
-  prevMessage.shift() if prevMessage[0] is ''
-  prevMessage.reverse()
-  prevChangedFiles = lines.filter (line) -> line isnt ''
-  message = prevMessage.join('\n')
+  if firstSpliting?
+    message = prevCommit.substring 0, firstSpliting.index
+
+    replacerRegex = /^:\w{6} \w{6}(?: \w{7}\.{3}){2} ([ MADRCU?!].+)$/gm
+    prevChangedFiles = (firstSpliting[1].trim().replace replacerRegex, "$1").split '\n'
+  else
+    message = prevCommit.trim()
+    prevChangedFiles = []
   {message, prevChangedFiles}

 cleanupUnstagedText = (status) ->
@@ -130,7 +131,7 @@
   filePath = Path.join(repo.getPath(), 'COMMIT_EDITMSG')
   cwd = repo.getWorkingDirectory()
   commentChar = git.getConfig(repo, 'core.commentchar') ? '#'
-  git.cmd(['whatchanged', '-1', '--name-status', '--format=%B'], {cwd})
+  git.cmd(['whatchanged', '-1', '--format=%B'], {cwd})
   .then (amend) -> parse amend
   .then ({message, prevChangedFiles}) ->
     getStagedFiles(repo)


Dunno how this got here

Dunno how this got here

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -155,9 +155,6 @@
   # Returns 1
   directionIncrement: ->
     1
-
-  move: (count) ->
-    @editor.moveCursorDown(count)

   select: (count=1) ->
     _.times count, =>


sync2: output messages ordering

sync2: output messages ordering

--- src/smc-util/syncstring_evaluator.coffee
+++ src/smc-util/syncstring_evaluator.coffee
@@ -52,19 +52,38 @@
             id    : [@string._string_id, time, 0]
             input : misc.copy_without(opts, 'cb')
         if opts.cb?
-            # if we care about the output, listen for it until receving a mesg.done
+            # Listen for output until we receive a message with mesg.done true.
+            messages = {}
+            mesg_number = 0
             handle_output = (keys) =>
+                #console.log("handle_output #{misc.to_json(keys)}")
                 if @_closed
                     opts.cb?("closed")
                     return
                 for key in keys
                     t = misc.from_json(key)
-                    if t[1] - time == 0  # we call opts.cb on all output with the given timestamp
+                    if t[1] - time == 0  # we called opts.cb on output with the given timestamp; ignore any other output
                         mesg = @_outputs.get(key)?.get('output')?.toJS()
                         if mesg?
                             if mesg.done
                                 @_outputs.removeListener('change', handle_output)
-                            opts.cb?(mesg)
+                            # Message may arrive in somewhat random order -- RethinkDB doesn't guarantee
+                            # anything about the order of writes versus when changes get pushed out!  E.g. this
+                            # in a Sage worksheet:
+                            #    for i in range(20): print i; sys.stdout.flush()
+                            if t[2] == mesg_number     # t[2] is the sequence number of the message
+                                # Inform caller of result
+                                opts.cb(mesg)
+                                # Push out any messages that arrived earlier that are ready to send.
+                                mesg_number += 1
+                                while messages[mesg_number]?
+                                    opts.cb(messages[mesg_number])
+                                    delete messages[mesg_number]
+                                    mesg_number += 1
+                            else
+                                # Put message in the queue of messages that arrived too early
+                                messages[t[2]] = mesg
+
             @_outputs.on('change', handle_output)

     _execute_code_hook: (output_uuid) =>


only initialize server hooks in development

--- packages/tower-application/server/application.coffee
+++ packages/tower-application/server/application.coffee
@@ -207,7 +207,7 @@
         _console.info("Tower #{Tower.env} server listening on port #{Tower.port}")
         value.applySocketEventHandlers() for key, value of @ when key.match /(Controller)$/
         @watch() if Tower.watch
-        @initializeServerHooks()
+        @initializeServerHooks() if Tower.env == 'development'

   initializeSockets: ->
     unless @io


Properly encode the assemblyId of committees as a number.

--- jobs/import-committees.coffee
+++ jobs/import-committees.coffee
@@ -37,7 +37,7 @@
     sessionId: session._id,
     type: assemblyCommittee.Type.toLowerCase(),
     assemblyCode: assemblyCommittee.Code,
-    assemblyId: assemblyCommittee.Id,
+    assemblyId: Number(assemblyCommittee.Id),
     name: assemblyCommittee.Name,
     description: assemblyCommittee.Description,
     members: {}


Added locking to prevent adding entities multiple times in a graph.

--- js/binding/src/binding.coffee
+++ js/binding/src/binding.coffee
@@ -154,6 +154,26 @@
 				return render.entity(entity[rel_name]())
 	})

+	(()->
+		key = {}
+
+		lock = (entity) ->
+			if key[entity.id(true)]
+				return false
+			return key[entity.id(true)] = true;
+
+		unlock = (entity) ->
+			delete key[entity.id(true)]
+
+		r_e = render.entity
+		render.entity = (entity, view = "view") ->
+			e = $("");
+			if lock(entity)
+				e = r_e(entity, view)
+				unlock(entity)
+			return e
+	)()
+
 	init = (options) ->
 		_clear()
 		_.extend(true, settings, options)


add repo address

add repo address


removing debug code

removing debug code

--- lib/function-name-status-bar.coffee
+++ lib/function-name-status-bar.coffee
@@ -9,7 +9,6 @@
       me = this
       atom.workspaceView.eachEditorView (editor) ->
           editor.on 'keydown', (evt) =>
-              console.log atom.config, atom.config.get('function-name-in-status-bar.computeOnKeys')
               if evt.which in atom.config.get 'function-name-in-status-bar.computeOnKeys'
                   me.showFunctionNameInStatus(evt)


veamos

veamos

--- app/assets/javascripts/buttons_helper.js.coffee
+++ app/assets/javascripts/buttons_helper.js.coffee
@@ -30,6 +30,4 @@
 jQuery window.Helpers.ButtonsHelper.init
 $(document).on "page:load", window.Helpers.ButtonsHelper.init
 $(document).on "nested:fieldAdded", window.Helpers.ButtonsHelper.init
-$("#myModal").on "show.bs.modal", (e) ->
-  console.log "hola"
-  window.Helpers.ButtonsHelper.init+$("#myModal").on "show.bs.modal", console.log "hola"


Submit form button disabled when a reception item is being edited

--- app/assets/javascripts/cocoon/iceberg.js.coffee
+++ app/assets/javascripts/cocoon/iceberg.js.coffee
@@ -7,6 +7,7 @@
       @display = @line.find('.item-display')

       @_bindButtons(@newForm())
+      $('.form-actions .primary').attr("disabled",true)

       @line.find('*[data-edit="item-form"]').click =>
         @display.addClass('hidden')
@@ -16,6 +17,7 @@
         clone.trigger('cocoon:after-insert')
         clone.removeClass('hidden')
         @_bindButtons(@newForm())
+        $('.form-actions .primary').attr("disabled",true)

     _bindButtons: (form) ->
       # console.log '_bindButtons:this', this
@@ -45,11 +47,20 @@
       @display.removeClass('hidden')
       @oldForm().remove()
       @newForm().addClass('hidden')
+      @isFormSubmitable()

     cancel: ->
       # console.log 'cancel:this', this
       @display.removeClass('hidden')
       @newForm().remove()
+      @isFormSubmitable()
+
+    isFormSubmitable: ->
+      debugger
+      if $('.nested-item-form:visible').length >= 1
+        $('.form-actions .primary').attr("disabled",true)
+      else
+        $('.form-actions .primary').attr("disabled",null)

     oldForm: ->
       # console.log 'oldForm:this', this


Set @target to event.currentTarget each time UIGuider#show() is called (#more)

Basically allows to create 1 UIGuider that would show up on each of the multiple
elements specified by the :target option when an event is triggered.

--- app/assets/javascripts/lib/ui_guiders.js.coffee
+++ app/assets/javascripts/lib/ui_guiders.js.coffee
@@ -99,6 +99,7 @@

       options["side_arrow"]  = true                            if @block.hasClass("side_arrow")
       options["edge"]        = @block.attr("data-target-edge") if @block.attr("data-target-edge")
+      @target = options.target

       @place_arrow  @target_location(), options
       @place_guider @target_location(), options
@@ -134,7 +135,7 @@

         if g.block.attr("data-event-name") && g.block.attr("data-event-name").length > 0
           g.target.bind g.block.attr("data-event-name"), { guider: g }, (event) =>
-            event.data.guider.show() unless event.data.guider.state == "visible"
+            event.data.guider.show(target: $(event.currentTarget)) unless event.data.guider.state == "visible"

         g.block.find(".close").click () ->
           g.hide()


default product attributes for new product

--- app/assets/javascripts/routes/product.js.coffee
+++ app/assets/javascripts/routes/product.js.coffee
@@ -1,17 +1,20 @@
-setupController = (controller, model) ->
-  @_super controller, model
-  controller.set 'productTypes', @store.find 'product_type'
-  controller.set 'productPackings', @store.find 'product_packing'
-  controller.set 'categories', @store.find 'category'
-
 Stocks.ProductRoute = Ember.Route.extend
   model: (params) -> @store.fetchById 'product', params.id
-  setupController: setupController
+  setupController: (controller, model) ->
+    @_super controller, model
+    controller.set 'productTypes', @store.find 'product_type'
+    controller.set 'productPackings', @store.find 'product_packing'
+    controller.set 'categories', @store.find 'category'

-Stocks.ProductsNewRoute = Ember.Route.extend
+Stocks.ProductsNewRoute = Stocks.ProductRoute.extend
   model: (params) ->
     @store.find('category', params.category_id).then (category) =>
       @store.createRecord 'product', category: category
   templateName: 'product'
   controllerName: 'product'
-  setupController: setupController+  setupController: (controller, model) ->
+    @_super controller, model
+    controller.get('productTypes').then (items) ->
+      model.set 'product_type', items.objectAt(0)
+    controller.get('productPackings').then (items) ->
+      model.set 'product_packing', items.objectAt(0)


fix opening point details when drilled into results page

Former-commit-id: 6723f0567fc04c589a090f18daf652b1c67c5a48 [formerly 6fa454a675b0f7880e94429d5ec5fde196b71cc0] [formerly 1a77265c6130d9b66ac4bca9018fdc097923e898 [formerly 1a77265c6130d9b66ac4bca9018fdc097923e898 [formerly 26cd2a0c0acef31050325cf827d4591dc49a88ae]]]
Former-commit-id: f10f193819a6c11c4d5a3b9fb63b3d1cdb2387f0 [formerly bfd86e2ffb17ad74443a7331261b020cba02d17a]
Former-commit-id: 2d54173ff18bd9b014e9e0af950bdb07a3dcebd7
Former-commit-id: a0e74d6b31c20582bcf00eb2326a78ddf497d2c2

--- app/assets/javascripts/views/resultsview.coffee
+++ app/assets/javascripts/views/resultsview.coffee
@@ -315,8 +315,8 @@
       #######
       # when clicking outside of bar, close it
       if hard_select
-        $(document).click (ev) => @close_bar_click()
-        $(document).keyup (ev) => @close_bar_key(ev)
+        $(document).on 'click.histogram', (ev) => @close_bar_click(ev)
+        $(document).on 'keyup.histogram', (ev) => @close_bar_key(ev)
         ev.stopPropagation()
       #######

@@ -324,9 +324,7 @@

   close_bar_click : (ev) -> @deselect_bar() if $(ev.target).closest('.m-results-responders').length == 0

-  close_bar_key : (ev) ->
-    if ev.keyCode == 27 && $('#registration_overlay').length == 0 && $('.m-point-expanded').length == 0
-      @deselect_bar()
+  close_bar_key : (ev) -> @deselect_bar() if ev.keyCode == 27 && $('#registration_overlay').length == 0 && $('.m-point-expanded').length == 0

   deselect_bar : (ev) ->

@@ -360,8 +358,8 @@

     $selected_bar.removeClass('m-bar-is-selected m-bar-is-hard-selected m-bar-is-soft-selected')

-    $(document).unbind 'click', @close_bar_click
-    $(document).unbind 'keyup', @close_bar_key
+    $(document).off 'click.histogram'
+    $(document).off 'keyup.histogram'

   show_user_explanation : (ev) ->


optimize subIntervalsToDailyRates and fix potential DST bug

--- imports/incidentResolution/incidentResolution.coffee
+++ imports/incidentResolution/incidentResolution.coffee
@@ -580,22 +580,17 @@
   result = []
   while current < end
     result.push(new Date(current))
-    current.setDate(current.getDate() + 1)
+    current.setUTCDate(current.getUTCDate() + 1)
     current = new Date(current.toISOString().split('T')[0])
   return result

 subIntervalsToDailyRates = (subIntervals) ->
-  _.chain(subIntervals)
-    .map (subInterval)->
-      enumerateDateRange(subInterval.start, subInterval.end).map (date) ->
-        date: date
-        rate: subInterval.rate
-    .flatten()
-    .groupBy('date')
-    .pairs()
-    .map ([date, group]) -> [new Date(date), sum(group.map (x)-> x.rate)]
-    .sortBy (x) -> x[0]
-    .value()
+  dailyRates = {}
+  subIntervals.forEach (subInterval)->
+    enumerateDateRange(subInterval.start, subInterval.end).forEach (date) ->
+      day = date.toISOString().split('T')[0]
+      dailyRates[day] = (dailyRates[day] or 0) + subInterval.rate
+  _.sortBy(_.pairs(dailyRates), (x) -> x[0])

 dailyRatesToActiveCases = (dailyRates, dailyDecayRate) ->
   activeCases = 0


manyboxplots2: clean up and simplify

manyboxplots2: clean up and simplify

--- manyboxplots2/manyboxplots2.coffee
+++ manyboxplots2/manyboxplots2.coffee
@@ -7,7 +7,7 @@
 #   is show below; click for it to persist; click again to make it go away.
 #

-d3.json("data.json", (data) ->
+d3.json "data.json", (data) ->

   # dimensions of SVG
   w = 1000
@@ -54,8 +54,11 @@

   # x and y scales for top figure
   xScale = d3.scale.linear()
-             .domain([0, data.ind.length+1])
+             .domain([-1, data.ind.length])
              .range([pad.left, w-pad.right])
+
+  # width of rectangles in top panel
+  recWidth = xScale(1) - xScale(0)

   yScale = d3.scale.linear()
              .domain(topylim)
@@ -64,7 +67,7 @@
   # function to create quantile lines
   quline = (j) ->
     d3.svg.line()
-        .x((d) -> xScale(d+1))
+        .x((d) -> xScale(d))
         .y((d) -> yScale(data.quant[j][d]))

   svg = d3.select("div#plot").append("svg")
@@ -79,10 +82,11 @@
      .attr("width", w-pad.left-pad.right)
      .attr("stroke", "none")
      .attr("fill", d3.rgb(200, 200, 200))
+     .attr("pointer-events", "none")

   # axis on left
   LaxisData = yScale.ticks(6)
-  Laxis = svg.append("g")
+  Laxis = svg.append("g").attr("id", "Laxis")

   # axis: white lines
   Laxis.append("g").selectAll("empty")
@@ -96,6 +100,7 @@
      .attr("y1", (d) -> yScale(d))
      .attr("y2", (d) -> yScale(d))
      .attr("stroke", "white")
+     .attr("pointer-events", "none")

   # axis: labels
   Laxis.append("g").selectAll("empty")
@@ -109,10 +114,9 @@
      .attr("dominant-baseline", "middle")
      .attr("text-anchor", "end")

-
   # axis on bottom
   BaxisData = xScale.ticks(10)
-  Baxis = svg.append("g")
+  Baxis = svg.append("g").attr("id", "Baxis")

   # axis: white lines
   Baxis.append("g").selectAll("empty")
@@ -126,6 +130,7 @@
      .attr("x1", (d) -> xScale(d))
      .attr("x2", (d) -> xScale(d))
      .attr("stroke", "white")
+     .attr("pointer-events", "none")

   # axis: labels
   Baxis.append("g").selectAll("empty")
@@ -138,7 +143,6 @@
      .attr("x", (d) -> xScale(d))
      .attr("dominant-baseline", "middle")
      .attr("text-anchor", "middle")
-

   # colors for quantile curves
   colindex = d3.range((nQuant-1)/2)
@@ -151,7 +155,7 @@
     qucolors.push(tmp(j))

   # curves for quantiles
-  curves = svg.append("g")
+  curves = svg.append("g").attr("id", "curves")

   for j in [0...nQuant]
     curves.append("path")
@@ -159,33 +163,19 @@
        .attr("d", quline(j))
        .attr("class", "line")
        .attr("stroke", qucolors[j])
-
-  # special rectangles in the background
-  clickStatus = {}
-  index = {}
-  specialrects = svg.append("g")
-  for d in indindex
-    clickStatus[d] = 0
-    specialrects.append("rect")
-       .attr("x", xScale(d+0.5))
-       .attr("y", yScale(data.quant[nQuant-1][d]))
-       .attr("width", 2)
-       .attr("id", "rect#{data.ind[d]}")
-       .attr("height", yScale(data.quant[0][d]) - yScale(data.quant[nQuant-1][d]))
-       .attr("opacity", 0)
-       .attr("stroke", "none")
+       .attr("pointer-events", "none")

   # vertical rectangles representing each array
-  indRectGrp = svg.append("g")
+  indRectGrp = svg.append("g").attr("id", "indRect")

   indRect = indRectGrp.selectAll("empty")
                  .data(indindex)
                  .enter()
                  .append("rect")
-                 .attr("x", (d) -> xScale(d+0.5))
+                 .attr("x", (d) -> xScale(d) - recWidth/2)
                  .attr("y", (d) -> yScale(data.quant[nQuant-1][d]))
                  .attr("id", (d) -> "rect#{data.ind[d]}")
-                 .attr("width", 2)
+                 .attr("width", recWidth)
                  .attr("height", (d) ->
                     yScale(data.quant[0][d]) - yScale(data.quant[nQuant-1][d]))
                  .attr("fill", "purple")
@@ -193,7 +183,7 @@
                  .attr("opacity", "0")

   # label quantiles on right
-  rightAxis = svg.append("g")
+  rightAxis = svg.append("g").attr("id", "rightAxis")

   rightAxis.selectAll("empty")
        .data(data.qu)
@@ -244,7 +234,7 @@

   # axis on left
   lowBaxisData = lowxScale.ticks(8)
-  lowBaxis = lowsvg.append("g")
+  lowBaxis = lowsvg.append("g").attr("id", "lowBaxis")

   # axis: white lines
   lowBaxis.append("g").selectAll("empty")
@@ -271,13 +261,15 @@
      .attr("dominant-baseline", "middle")
      .attr("text-anchor", "middle")

-  grp4BkgdHist = lowsvg.append("g")
+  grp4BkgdHist = lowsvg.append("g").attr("id", "bkgdHist")

   histline = d3.svg.line()
         .x((d,i) -> lowxScale(br2[i]))
         .y((d) -> lowyScale(d))

   randomInd = indindex[Math.floor(Math.random()*data.ind.length)]
+  randomInd = 0
+  d3.select("rect#rect#{data.ind[randomInd]}").attr("opacity", "1")

   hist = lowsvg.append("path")
     .datum(data.counts[randomInd])
@@ -299,6 +291,10 @@
         .attr("text-anchor", "start")
         .attr("dominant-baseline", "middle")
         .attr("fill", "blue")
+
+  clickStatus = []
+  for d in indindex
+    clickStatus.push(0)

   indRect
     .on "mouseover", (d) ->
@@ -312,18 +308,18 @@
                  .text((d) -> data.ind[d])

     .on "mouseout", (d) ->
-              d3.select(this).attr("opacity", "0")
+              if !clickStatus[d]
+                d3.select(this).attr("opacity", "0")

     .on "click", (d) ->
-              console.log("Click: #{data.ind[d]}")
+              console.log("Click: #{data.ind[d]} (#{d})")
               clickStatus[d] = 1 - clickStatus[d]
-              svg.select("rect#rect#{data.ind[d]}").attr("opacity", clickStatus[d])
+              d3.select(this).attr("opacity", clickStatus[d])
               if clickStatus[d]
                 curcolor = histColors.shift()
                 histColors.push(curcolor)

-                d3.select(this).attr("opacity", "0")
-                svg.select("rect#rect#{data.ind[d]}").attr("fill", curcolor)
+                d3.select(this).attr("fill", curcolor)

                 grp4BkgdHist.append("path")
                       .datum(data.counts[d])
@@ -333,7 +329,7 @@
                       .attr("stroke", curcolor)
                       .attr("stroke-width", "2")
               else
-                grp4BkgdHist.select("path#path#{data.ind[d]}").remove()
+                d3.select("path#path#{data.ind[d]}").remove()

   # box around the outside
   lowsvg.append("rect")
@@ -385,5 +381,3 @@
   d3.select("div#legend").append("p")
     .text("Hover over a column in the top panel and the corresponding histogram is shown below; " +
           "click for it to persist; click again to make it go away.")
-
-)


manyboxplots2: remove two extraneous lines

--- manyboxplots2/manyboxplots2.coffee
+++ manyboxplots2/manyboxplots2.coffee
@@ -7,8 +7,6 @@
 #   is show below; click for it to persist; click again to make it go away.
 #

-# function that does all of the work
-# load json file and call draw function
 d3.json("data.json", (data) ->

   # dimensions of SVG


enabled comments #347

enabled comments #347

--- app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
+++ app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
@@ -15,7 +15,7 @@
   MODE_NORMAL          : 0     # draw every node and the complete tree
   MODE_NOCHAIN         : 1     # draw only decision points

-  RENDER_COMMENTS      : false  # draw comments into tree
+  RENDER_COMMENTS      : true  # draw comments into tree

   constructor : ($canvas, width, height) ->


Changed starting configuration for slider (6am - 11pm)

--- public/scripts/backbone-schedule/views/filter.coffee
+++ public/scripts/backbone-schedule/views/filter.coffee
@@ -46,41 +46,15 @@
 				maxHour = 12
 			@$el.find('.time').text minHour + ':' + minMin + ' - ' + maxHour + ':' + maxMin

-		formatTime: (values) ->
-			minHour = parseInt( (value[0] / 12) % 24)
-			minMin = parseInt( (value[0] - 12 * minHour) * 5 )
-			maxHour = parseInt( (value[1] / 12) % 24)
-			maxMin = parseInt( (value[1] - 12 * maxHour) * 5)
-			if minMin < 10
-				minMin = '0' + minMin
-			if maxMin < 10
-				maxMin = '0' + maxMin
-			if minHour < 12
-				minMin = minMin + ' am'
-			else
-				minHour = minHour - 12
-				minMin = minMin + ' pm'
-			if maxHour < 12
-				if maxHour == 0
-					maxHour = 12
-				maxMin = maxMin + ' am'
-			else
-				maxHour = maxHour - 12
-				maxMin = maxMin + ' pm'
-			if minHour == 0
-				minHour = 12
-			if maxHour == 0
-				maxHour = 12
-			@el.find('.time').text minHour + ':' + minMin + ' - ' + maxHour + ':' + maxMin
-
 		render: ->
 			@$el.append @filterTemplate()
 			# 5-minute increments [0 - 24*60/5 == 0-288]         range starts at 7am - 7pm
 			@$el.find('.slider').slider
 				range: true
-				min: 0
-				max: 288
+				min: 72
+				max: 276
 				values: [84, 228]
+			@$el.find('.time').text "7:00 am - 7:00 pm"

 	filterView
 )


Refactor crossover operations

Refactor crossover operations

--- lib/crossover_operator.coffee
+++ lib/crossover_operator.coffee
@@ -8,10 +8,10 @@
 # http://opensource.org/licenses/mit-license.php
 ###

-{randomInt} = require './utils'
+utils = require './utils'

 class CrossoverOperator
-  randomLocusOf = (c)-> randomInt c.length
+  randomLocusOf = (c)-> utils.randomInt c.length

   # N point crossover operation
   @point: (n = 1)-> Object.defineProperty(
@@ -46,36 +46,35 @@
     [c1, c2]
   , 'probability', value: probability

-  exchange = (c1, c2, pos)->
-    temp = c1[pos]; c1[pos] = c2[pos]; c2[pos] = temp
+  exchange = (array1, array2, p)->
+    temp = array1[p]
+    array1[p] = array2[p]
+    array2[p] = temp
+    undefined

   # Order crossover operation
-  @OX: @order = (n = 2)-> Object.defineProperty(
+  @OX: @order = (n)-> withRotation n, (c1, c2, p)->
+    (o = c1[...p]).push (utils.reject c2, o)...
+    o
+
+
+  withRotation = (n = 2, operator)-> Object.defineProperty(
     if 1 <= n <= 2
       (c1, c2)->
-        r = randomLocusOf c1 if n == 2
-        [c1, c2] = rotate c1, c2, r
+        if n == 2 and r = randomLocusOf c1
+          c1 = utils.rotate c1, r
+          c2 = utils.rotate c2, r
         p = randomLocusOf c1
-        o1 = order c1, c2, p
-        o2 = order c2, c1, p
-        rotate o1, o2, -r
+        o1 = operator c1, c2, p
+        o2 = operator c2, c1, p
+        if r
+          o1 = utils.rotate o1, -r
+          o2 = utils.rotate o2, -r
+        [o1, o2]
     else
       throw new Error "invalid number of crossover point: #{n}"
   , 'n', value: n)
-
-  order = (c1, c2, p)->
-    (o = c1[...p]).push (reject c2, o)...
-    o
-
-  reject = (array, excepts)->
-    array.filter (e)-> (excepts.indexOf e) < 0
-
-  rotate = (c1, c2, p)->
-    if p
-      c1 = c1[p...].concat c1[...p]
-      c2 = c2[p...].concat c2[...p]
-    [c1, c2]

   # Cycle crossover operation
@@ -90,36 +89,20 @@
         o2[p] = c2[p]
         p = c1.indexOf c2[p]
         throw new Error 'Invalid chromosome for cyclic crossover' if p < 0
-      break if o1.length == length == countElements(o1)
+      break if o1.length == length == utils.count o1
       p++ while o1[p]
       p = 0 if p == length
       [c1, c2] = [c2, c1]
     [o1, o2]

-  countElements = (array)->
-    array.reduce ((count)-> count + 1), 0
-

   # Partially-mapped crossover operation
-  @PMX: (n = 2)-> Object.defineProperty(
-    if 1 <= n <= 2
-      (c1, c2)->
-        r = randomLocusOf c1 if n == 2
-        [c1, c2] = rotate c1, c2, r
-        p = randomLocusOf c1
-        o1 = pmx c2, c1, p
-        o2 = pmx c1, c2, p
-        rotate o1, o2, -r
-    else
-      throw new Error "invalid number of crossover point: #{n}"
-  , 'n', value: n)
-
-  pmx = (c1, c2, p)->
-    o = c2[...p]
+  @PMX: (n)-> withRotation n, (c1, c2, p)->
+    o = c1[...p]
     for q in [p..c1.length - 1]
       o[q] ?= do ->
-        until (o.indexOf g = c1[q]) < 0
-          q = c2.indexOf g
+        while utils.contains o, (g = c2[q])
+          q = c1.indexOf g
         g
     o


workers: fix unncessary white space

workers: fix unncessary white space

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1524,7 +1524,7 @@
           account.save (err)=>
             return callback err  if err

-              {firstName} = account.profile
+            {firstName} = account.profile

             # send EmailChanged event
             @constructor.emit 'EmailChanged', {


JUser::fetchGuestUser: make sure there is a guestuser,remove configureNewAccount

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -673,6 +673,10 @@

       return callback err  if err?

+      unless user?
+        console.log "guestuser not found! FIXME @gokmen"
+        return callback new KodingError 'No Guest available!'
+
       username      = @createGuestUsername()

       user.username = username
@@ -684,7 +688,7 @@

         account.profile.nickname = username

-        @configureNewAcccount account, user, createId(), callback
+        callback null, { account, replacementToken: createId() }

   @createUser = (userInfo, callback)->


:lipstick:

:lipstick:

--- lib/markdown-preview.coffee
+++ lib/markdown-preview.coffee
@@ -1,10 +1,10 @@
 EditSession = require 'edit-session'
-MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
+MarkdownPreviewView = require './markdown-preview-view'

 module.exports =
   activate: ->
     rootView.command 'markdown-preview:show', '.editor', => @show()
-    rootView.on 'core:save', ".pane", => @show() if @previewExists()
+    rootView.on 'core:save', '.pane', => @show() if @previewExists()

   show: ->
     activePane = rootView.getActivePane()


cleanup

cleanup

--- lib/register-manager.coffee
+++ lib/register-manager.coffee
@@ -28,8 +28,6 @@
         type = @getCopyType(text)
       when '%'
         text = @editor.getURI()
-        console.log text
-        console.log @getCopyType(text)
         type = @getCopyType(text)
       when '_' # Blackhole always returns nothing
         text = ''


null check path when building a pdf download url

if there is an error just the domain is currently returned, empty string
is better for us

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -230,7 +230,7 @@

 		buildPdfDownloadUrl = (pdfDownloadDomain, path)->
 			 #we only download builds from compiles server for security reasons
-			if pdfDownloadDomain? and path.indexOf("build") != -1
+			if pdfDownloadDomain? and path? and path.indexOf("build") != -1
 				return "#{pdfDownloadDomain}#{path}"
 			else
 				return path


:art:

:art:

--- lib/spell-check-view.coffee
+++ lib/spell-check-view.coffee
@@ -127,7 +127,7 @@
           menuItem: atom.contextMenu.add({'atom-text-editor': [{type: 'separator'}]})
         })

-        for correction in @getCorrections(marker)
+        for correction in corrections
           contextMenuEntry = {}
           # Register new command for correction.
           contextMenuEntry.command = do (correction, contextMenuEntry) =>