cleaning up some dev console.logs and adding if, again to avoid key error when segment.tracks not populated.

--- assets/front.coffee
+++ assets/front.coffee
@@ -241,22 +241,23 @@
 		console.log(segments)
 		window._track_id = 0 # Assume we'll never actually get a track ID of zero
 		for segment in segments
-			if segment.tracks[0].metadata.id != window._track_id
-				length = segment.tracks[0].metadata.length
-				minutes = Math.floor(length/60)
-				seconds = Math.floor(length%60)
-				if seconds < 10
-					seconds = "0" + seconds
-				if window._track_id
-					window._next_artist = segment.tracks[0].metadata.artist
-					window._next_length = minutes + ":" + seconds
-				else
-					document.getElementById('artist').innerHTML = window._next_artist = segment.tracks[0].metadata.artist
-					document.getElementById('length').innerHTML = window._next_length = minutes + ":" + seconds
-					window._track_id = segment.tracks[0].metadata.id
-			console.log("Segment" + window._count_getJSON + ": ")
-			console.log(segment.tracks[0].metadata.artist)
-			console.log(segment.tracks[0].metadata.id)
+			if segment.tracks?
+				if segment.tracks[0].metadata.id != window._track_id
+					length = segment.tracks[0].metadata.length
+					minutes = Math.floor(length/60)
+					seconds = Math.floor(length%60)
+					if seconds < 10
+						seconds = "0" + seconds
+					if window._track_id
+						window._next_artist = segment.tracks[0].metadata.artist
+						window._next_length = minutes + ":" + seconds
+					else
+						document.getElementById('artist').innerHTML = window._next_artist = segment.tracks[0].metadata.artist
+						document.getElementById('length').innerHTML = window._next_length = minutes + ":" + seconds
+						window._track_id = segment.tracks[0].metadata.id
+				console.log("Segment" + window._count_getJSON + ": ")
+			# console.log(segment.tracks[0].metadata.artist)
+			# console.log(segment.tracks[0].metadata.id)
 			window._count_getJSON++

 	getPing = ->
@@ -269,7 +270,6 @@

 	getTrackDetails = ->
-		console.log("You pressed me.")
 		s = io.connect ":8193/info.websocket"
 		s.on 'message', (data) ->
 			if typeof data is "string"
@@ -289,15 +289,16 @@
 						seconds = "0" + seconds
 					document.getElementById('length').innerHTML = minutes + ":" + seconds
 					return
-				if data.segment.tracks[0].metadata.id != window._track_id
-					window._track_id = data.segment.tracks[0].metadata.id
-					# TODO Be safe against embedded HTML tags
-					document.getElementById('artist').innerHTML = window._next_artist
-					window._next_artist = data.segment.tracks[0].metadata.artist
-					document.getElementById('artist_next').innerHTML = "Up next: " + window._next_artist
-				console.log("GetTrackDets Data " + window._count_TrackDeets + ": ")
-				console.log(data.segment.tracks[0].metadata.artist)
-				console.log(data.segment.tracks[0].metadata.id)
+				if data.segment.tracks?
+					if data.segment.tracks[0].metadata.id != window._track_id
+						window._track_id = data.segment.tracks[0].metadata.id
+						# TODO Be safe against embedded HTML tags
+						document.getElementById('artist').innerHTML = window._next_artist
+						window._next_artist = data.segment.tracks[0].metadata.artist
+						document.getElementById('artist_next').innerHTML = "Up next: " + window._next_artist
+					# console.log("GetTrackDets Data " + window._count_TrackDeets + ": ")
+					# console.log(data.segment.tracks[0].metadata.artist)
+					# console.log(data.segment.tracks[0].metadata.id)
 				window._count_TrackDeets++
 			if data.listener_count?
 				window._listeners = data.listener_count


remove unused window variable assignments.

--- assets/front.coffee
+++ assets/front.coffee
@@ -222,9 +222,6 @@
 	, 5000

 $(document).ready ->
-	window.rotate_h2()
-	window.__titular = new Titular
-	window.__heartbeat = $('#endpoint_link').attr('href').replace('all.mp3', 'heartbeat')

 	$('body').keyup (e) ->
 		s = window.soundManager.sounds.ui360Sound0


remove manual artwork impression tracking on feature

--- apps/feature/client/view.coffee
+++ apps/feature/client/view.coffee
@@ -1,6 +1,5 @@
 _ = require 'underscore'
 Backbone = require 'backbone'
-{ trackArtworkImpressions } = require '../../../components/analytics/impression_tracking.coffee'
 Artworks = require '../../../collections/artworks.coffee'
 ShareView = require '../../../components/share/view.coffee'
 SaleArtworkView = require '../../../components/artwork_item/views/sale_artwork.coffee'
@@ -53,14 +52,10 @@

   doneFetchingSaleArtworks: (saleFeaturedSet) =>
     artworks = saleFeaturedSet.get 'data'
-    @setupArtworkImpressionTracking artworks.models

   appendArtworks: (artworks) ->
     @artworkColumns.appendArtworks artworks.models
     @setupSaleArtworks artworks, @sale
-
-  setupArtworkImpressionTracking: (artworks) ->
-    trackArtworkImpressions artworks, @$el

   setupCurrentUser: ->
     @currentUser = CurrentUser.orNull()


Only require git if exluding ignored files

--- src/load-paths-handler.coffee
+++ src/load-paths-handler.coffee
@@ -1,11 +1,13 @@
 fs = require 'fs'
 _ = require 'underscore'
-Git = require 'git'

 module.exports =
   loadPaths: (rootPath, ignoredNames, excludeGitIgnoredPaths) ->
+    if excludeGitIgnoredPaths
+      Git = require 'git'
+      repo = Git.open(rootPath, refreshIndexOnFocus: false)
+
     paths = []
-    repo = Git.open(rootPath, refreshIndexOnFocus: false) if excludeGitIgnoredPaths
     isIgnored = (path) ->
       for segment in path.split('/')
         return true if _.contains(ignoredNames, segment)
@@ -15,5 +17,7 @@
     onDirectory = (path) ->
       not isIgnored(path)
     fs.traverseTree(rootPath, onFile, onDirectory)
+
     repo?.destroy()
+
     callTaskMethod('pathsLoaded', paths)


http server with coffee script

http server with coffee script

--- httpServer.coffee
+++ httpServer.coffee
@@ -8,4 +8,9 @@
         'Content-Length':   data.length
     res.end data

+io = require 'socket.io'
+io.sockets.on 'connection', (socket) ->
+    console.log('Initializing Socket');
+
+
 server.listen 8000


dynamic linking

dynamic linking

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -117,6 +117,9 @@
       c = f.add(@props, 'start_script')
       c = f.add(@props, 'os')

+    links: []
+
+
     #cyjs generates the json for this object
     cyjs: ->

@@ -146,6 +149,8 @@
       f.add(@props, 'capacity')
       f.add(@props, 'latency')

+    links: []
+
     #cyjs generates the json for this object
     cyjs: ->

@@ -173,6 +178,8 @@
       f.add(@props, 'capacity')
       f.add(@props, 'latency')

+    links: []
+
     #cyjs generates the json for this object
     cyjs: ->

@@ -184,12 +191,13 @@
       @ln.obj3d.userData = this
       @props = {
         name: "link0",
+        sys: "",
         capacity: 1000,
-        a_name: null,
-        a_sys: null,
-        b_name:null,
-        b_sys: null,
-        latency: null
+        latency: null,
+        endpoints: [
+          {name: "", sys: "", design: dsg, ifname: ""},
+          {name: "", sys: "", design: dsg, ifname: ""}
+        ]
       }
       @id = {
         name: "link0"
@@ -214,16 +222,25 @@
       @props.latency = 7

     setEndpointData: ->
+      ###
       @props.a_name = @endpoint[0].props.name
       @props.a_sys = @endpoint[0].props.sys
       @props.b_name = @endpoint[1].props.name
       @props.b_sys = @endpoint[1].props.sys
+      ###
+      @props.endpoints[0].name = @endpoint[0].props.name
+      @props.endpoints[0].sys = @endpoint[0].props.sys
+      #@props.endpoints[0].ifname = @endpoint[0].?
+      @props.endpoints[1].name = @endpoint[1].props.name
+      @props.endpoints[1].sys = @endpoint[1].props.sys
+      #@props.endpoints[1].ifname = @endpoint[1].?

     ifInternetToWanLink:  ->
       @applyWanProps() if @isInternet()

     showProps: (f) ->
       f.add(@props, 'name')
+      f.add(@props, 'sys')
       f.add(@props, 'capacity')
       if @isInternet()
         f.add(@props, 'latency')
@@ -558,26 +575,14 @@
 class Addie
   constructor: (@ve) ->

-  update: (x) ->
+  update: (x) =>
     console.log("updating object")
     console.log(x)

-    ###
-    msg = {
-      Computers: [],
-      Switches: []
-    }
-    ###
     msg = { Elements: [] }

     ido = { OID: x.id, Type: x.constructor.name, Element: x.props }
     msg.Elements.push(ido)
-    ###
-    switch
-      when x instanceof BaseElements.Computer then msg.Computers.push(ido)
-      when x instanceof BaseElements.Switch then msg.Switches.push(x.props)
-      else console.log('update not implemented for object')
-    ###

     console.log(msg)

@@ -586,6 +591,16 @@
       console.log(data)
       x.id.name = x.props.name
       x.id.sys = x.props.sys
+
+    updateLink = (l) =>
+      l.setEndpointData()
+      @update(l)
+
+
+    if x.links?
+      updateLink(l) for l in x.links
+
+    true

 #Mouse handler encapsulates the logic of dealing with mouse events
 class MouseHandler
@@ -672,6 +687,7 @@
       @ve.surface.elements.push(@placingLink)
       @placingLink.props.name = @ve.namemanager.getName("lnk")
       @placingLink.endpoint[0] = ixs[0].object.userData
+      @placingLink.endpoint[0].links.push(@placingLink)
       ixs[0].object.lines.push(@placingLink.ln)
       @ve.container.onmousemove = (eve) => @linkingMove1(eve)
       @ve.container.onmousedown = (eve) => @linkingDown1(eve)
@@ -688,19 +704,14 @@
       ixs[0].object.lines.push(@placingLink.ln)

       @placingLink.endpoint[1] = ixs[0].object.userData
+      @placingLink.endpoint[1].links.push(@placingLink)

       @ve.surface.updateLink(@placingLink.ln)
       @placingLink.ifInternetToWanLink()
       @placingLink.setEndpointData()

-      ###
-      if @placingLink.endpoint[0] instanceof BaseElements.Router and
-        @placingLink.endpoint[1] instanceof BaseElements.Router
-          @placingLink.props.latency = 7
-      else
-        @placingLink.props.capacity = 1000
-      ###
-
+      @ve.addie.update(@placingLink)
+

       @ve.container.onmousemove = null
       @ve.container.onmousedown = (eve) => @baseDown(eve)
@@ -709,9 +720,8 @@

   #onmouseuphandlers
   placingUp: (event) ->
-    console.log "plop"
-    #push that muffin to the bakery!
     @ve.addie.update(@placingObject)
+
     @ve.container.onmousemove = null
     @ve.container.onmousedown = (eve) => @baseDown(eve)
     @ve.container.onmouseup = null


no name change on multi-select

no name change on multi-select

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -794,6 +794,7 @@
       continue if k == 'position'
       continue if k == 'design'
       continue if k == 'endpoints'
+      continue if k == 'name' and @elements.length > 1
       @datgui.add(@cprops, k)
     true


reverse order of the hint characters in f-mode

--- coffee/mode.coffee
+++ coffee/mode.coffee
@@ -432,7 +432,7 @@
             j = @keyLength
             k = i
             while j--
-                key = @keys.charAt( k % @keys.length ) + key
+                key = key + @keys.charAt( k % @keys.length )
                 k /= @keys.length

             @hints[i]        = {}


Convert job init from "scrape people" to "scrape rep.."

--- jobs/index.coffee
+++ jobs/index.coffee
@@ -18,7 +18,7 @@
   jobs.process 'poll', (job, done) ->
     # Queue up jobs that should run on each poll.
     jobs.create('scrape sessions').save()
-    jobs.create('scrape people').save()
+    jobs.create('scrape representatives').save()
     #jobs.create('scrape committees').save()
     #jobs.create('scrape legislation').save()
     #jobs.create('scrape votes').save()


Save pickup time and instructions

Save pickup time and instructions

--- app/assets/javascripts/admin/order_cycles/controllers/simple.js.coffee
+++ app/assets/javascripts/admin/order_cycles/controllers/simple.js.coffee
@@ -3,6 +3,7 @@
     enterprise = enterprises[Object.keys(enterprises)[0]]
     OrderCycle.addSupplier enterprise.id
     OrderCycle.addDistributor enterprise.id
+    $scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]

     OrderCycle.setExchangeVariants(OrderCycle.order_cycle.incoming_exchanges[0],
       Enterprise.suppliedVariants(enterprise.id), true)


Disable chart tooltips

Disable chart tooltips

--- app/assets/javascripts/angular/controllers/result_controller.js.coffee
+++ app/assets/javascripts/angular/controllers/result_controller.js.coffee
@@ -7,6 +7,7 @@
       $scope.chartData = []
       $scope.chartOptions = {
         responsive: true
+        showTooltips: false
       }

       $scope.updateChart = ->


Avoid duplicated calls to model update when data attribute changes

--- app/frontend/javascripts/views/visualization-table-relations.js.coffee
+++ app/frontend/javascripts/views/visualization-table-relations.js.coffee
@@ -144,6 +144,9 @@
     index = change[0]
     key   = change[1]
     value = change[3]
+    # we don't need to update model for date column
+    if key == 'date'
+      return
     # Get model id in order to acced to model in Collection
     cell_id = @table.getDataAtRowProp(index, 'id')
     if cell_id
@@ -165,7 +168,6 @@
             obj.target_name = node[0].get('name')
       else
         obj[ key ] = value
-      console.log 'updateModel', obj, cell_model
       # Save model with updated attributes in order to delegate in Collection trigger 'change' events
       cell_model.save obj, {patch: true}

@@ -206,7 +208,7 @@

   # Function to show modal with date edit form
   showDateModal: (index) =>
-    console.log 'showDateModal', index
+    #console.log 'showDateModal', index
     $modal = $('#table-date-modal')
     # Load description edit form via ajax in modal
     $modal.find('.modal-body').load '/relations/'+@getIdAtRow(index)+'/edit/date/', () =>
@@ -235,7 +237,6 @@
   rowDateRenderer: (instance, td, row, col, prop, value, cellProperties) =>
     Handsontable.Dom.empty(td)
     if value
-      console.log 'rowDateRenderer', value
       link = document.createElement('DIV')
       link.innerHTML = value
       td.appendChild(link)


Update layout generator js

Update layout generator js

--- app/assets/javascripts/active_admin/cinematron.js.coffee
+++ app/assets/javascripts/active_admin/cinematron.js.coffee
@@ -22,4 +22,4 @@
       $(this).children().val(0)

   seatTemplate = (row_id) ->
-    "
<input name='layout[#{row_id}][]' type='hidden' value=1 class='seat_type' />
"
+    "
<input name='hall[layout[#{row_id}]][]' type='hidden' value=1 class='seat_type' />
"


Close now also have a checkbox + modal confirmation (only js is updated)

--- app/assets/javascripts/backend/financial_years.js.coffee
+++ app/assets/javascripts/backend/financial_years.js.coffee
@@ -1,7 +1,12 @@
 ((E, $) ->
-  $(document).on 'change', '#confirm-revised-accounts', ->
-    lock_btn = $('#lock-btn')
-    lock_btn.attr('disabled', !this.checked)
+  $(document).ready ->
+    $('.lock-table #confirm-revised-accounts').on 'change', ->
+      lock_btn = $('#lock-btn')
+      lock_btn.attr('disabled', !this.checked)
+
+    $('.close-table #confirm-revised-accounts').on 'change', ->
+      close_btn = $('#close-btn')
+      close_btn.attr('disabled', !this.checked)

   $(document).ready ->
     return unless $('.edit_financial_year').length


#181 Fix arbitrary cam distance to view the entire plane

--- app/assets/javascripts/oxalis/view/arbitrary_view.coffee
+++ app/assets/javascripts/oxalis/view/arbitrary_view.coffee
@@ -9,7 +9,10 @@

 class ArbitraryView

-  CAM_DISTANCE   : 128
+  # CAM_DISTANCE has to be calculates such that with cam
+  # angle 45°, the plane of width 128 fits exactly in the
+  # viewport.
+  CAM_DISTANCE   : 64 / Math.tan( Math.PI / 180 * 45 / 2 )
   DEFAULT_SCALE  : 2
   MAX_SCALE      : 3
   MIN_SCALE      : 1


Speedup download by decrease concurrency.

--- app/assets/javascripts/uploadcare/files/object.js.coffee
+++ app/assets/javascripts/uploadcare/files/object.js.coffee
@@ -9,7 +9,7 @@
   class ns.ObjectFile extends ns.BaseFile
     MP_MIN_SIZE: 25 * 1024 * 1024
     MP_PART_SIZE: 5 * 1024 * 1024
-    MP_CONCURRENCY: 8
+    MP_CONCURRENCY: 4

     constructor: (settings, @__file) ->
       super


add comment

add comment

--- chrome/src/bootstrap.coffee
+++ chrome/src/bootstrap.coffee
@@ -6,6 +6,7 @@
         test: !jQuery?
         yep: 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'
     yepnope [
+    	# 2013-03-01: Currenty, you can not use the .min version of jquery-bbq with jQuery 1.9.x
         chrome.extension.getURL "lib/jquery-bbq/jquery.ba-bbq.js"
         chrome.extension.getURL "lib/gmailr.js"
         chrome.extension.getURL "main.js"


Allow canvas/snapshot to capture images and windows.

--- application/chrome/content/wesabe/canvas/snapshot.coffee
+++ application/chrome/content/wesabe/canvas/snapshot.coffee
@@ -2,10 +2,36 @@

 TYPE = 'image/png'

-writeToFile = (window, path) ->
-  File.write path, serializeCanvas(canvasWithContentsOfWindow(window))
+imageDataForWindow = (window) ->
+  serializeCanvas canvasWithContentsOfWindow(window)
+
+imageDataForImage = (image) ->
+  serializeCanvas canvasWithImage(image)
+
+writeToFile = (windowOrImage, path) ->
+  data = if windowOrImage?.tagName?.toLowerCase() is 'img'
+    imageDataForImage windowOrImage
+  else
+    imageDataForWindow windowOrImage
+
+  File.write path, data

   return true
+
+canvasWithImage = (image) ->
+  document = image.ownerDocument
+  canvas   = document.createElement 'canvas'
+  width    = image.width
+  height   = image.height
+
+  canvas.setAttribute 'width', width
+  canvas.setAttribute 'height', height
+
+  context = canvas.getContext '2d'
+  context.drawImage image, 0, 0
+
+  return canvas
+

 canvasWithContentsOfWindow = (window) ->
   document = window.document
@@ -31,4 +57,4 @@
   return atob(dataurl.substring(13 + TYPE.length)) # hack off scheme

-module.exports = {writeToFile}
+module.exports = {writeToFile, imageDataForWindow, imageDataForImage}


Make alert/confirm/open callbacks actually work again.

--- application/chrome/content/wesabe/download/Player.coffee
+++ application/chrome/content/wesabe/download/Player.coffee
@@ -461,8 +461,8 @@
     module = @constructor.fid

     # log when alert and confirm are called
-    new Bridge page, ->
-      @evaluate ->
+    new Bridge page, (bridge) =>
+      bridge.evaluate ->
         # evaluated on the page
         window.alert = (message) ->
           callback 'alert', message
@@ -474,7 +474,7 @@
           callback 'open', url
           return false

-      , (data) ->
+      , (data) =>
         # evaluated here
           unless data
             logger.debug "Bridge connected"


JUser::authenticateClient use `guestuser` as username when a /^guest-/ requested

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -314,6 +314,8 @@
             callback createKodingError "no username found, logged out."

           return
+
+        username = 'guestuser'  if /^guest-/.test username

         JUser.one {username}, (err, user)=>


poll the element for the pdf viewer if it is zero size

avoid invisible pdf forever waiting for layout-ready event when
switching between full-screen and split-screen

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -249,15 +249,20 @@
 							spinner.remove(element)
 							ctrl.redraw(origposition)

+				checkElementReady = () ->
+					# if element is zero-sized keep checking until it is ready
+					if element.height() == 0 or element.width() == 0
+						$timeout () ->
+							checkElementReady()
+						, 250
+					else
+						scope.$broadcast 'layout-ready' if !scope.parentSize?
+
+				checkElementReady()
+
 				scope.$on 'layout-ready', () ->
 					# console.log 'GOT LAYOUT READY EVENT'
 					# console.log 'calling refresh'
-					if element.height() == 0 or element.width() == 0
-						# if element is zero-sized keep checking until it is ready
-						$timeout () ->
-							scope.$broadcast 'layout-ready'
-						, 250
-						return
 					updateContainer()
 					spinner.add(element)
 					layoutReady.resolve 'layout is ready'


fix pdf loadcount (no ?: operator in coffeescript)

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -25,7 +25,7 @@
 			# $scope.pages = []

 			$scope.document.destroy() if $scope.document?
-			$scope.loadCount = $scope.loadCount? ? $scope.loadCount + 1 : 1
+			$scope.loadCount = if $scope.loadCount? then $scope.loadCount + 1 else 1
 			# TODO need a proper url manipulation library to add to query string
 			$scope.document = new PDFRenderer($scope.pdfSrc + '&pdfng=true' , {
 				scale: 1,


Fix callback stub being defined in wrong scope

--- services/web/test/unit/coffee/Uploads/ArchiveManagerTests.coffee
+++ services/web/test/unit/coffee/Uploads/ArchiveManagerTests.coffee
@@ -26,12 +26,12 @@
 			"metrics-sharelatex": @metrics
 			"fs": @fs = {}
 			"fs-extra": @fse = {}
+		@callback = sinon.stub()

 	describe "extractZipArchive", ->
 		beforeEach ->
 			@source = "/path/to/zip/source.zip"
 			@destination = "/path/to/zip/destination"
-			@callback = sinon.stub()
 			@ArchiveManager._isZipTooLarge = sinon.stub().callsArgWith(1, null, false)

 		describe "successfully", ->


Fix changing args to ''

Fix changing args to ''

--- assets/javascripts/evaluator/structure_identifier.coffee
+++ assets/javascripts/evaluator/structure_identifier.coffee
@@ -4,7 +4,8 @@
   rootId = baseId + 'r'
   recursionLevel = (rootId.match(/r/g) || []).length - 1

-  observable.root.args ?= V.Roots[observable.root.type].getDefaultArgs?(recursionLevel)
+  if !observable.root.args?
+    observable.root.args = V.Roots[observable.root.type].getDefaultArgs?(recursionLevel)

   root: R.assoc('id', rootId, R.pick(['type', 'args'], observable.root))
   operators: R.mapIndexed(identifyOperator(rootId)(recursionLevel), observable.operators)
@@ -13,9 +14,10 @@
   id = rootId + Array(index + 2).join("o")
   definition = V.Operators[operator.type]

-  args = operator.args?  && operator.args || definition.getDefaultArgs?(recursionLevel)
+  if !operator.args?
+    operator.args = definition.getDefaultArgs?(recursionLevel)

-  newOperator = R.mixin(R.pick(['type'], operator), id: id, args: args, recursionType: definition.recursionType)
+  newOperator = R.mixin(R.pick(['type', 'args'], operator), id: id, recursionType: definition.recursionType)

   if V.Operators[operator.type].getDefaultObservable
     observable = operator.observable || definition.getDefaultObservable(recursionLevel)


Fixed the space optimization of substitutions but more importantly a crucial bug in unification by having the incorrect order of arguments to joinSubs

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -160,7 +160,7 @@
 subToObject = (sub) ->
   ob = {}
   for s, i in sub.vars when s
-    ob["#{i}"] = s
+    ob["#{i + sub.start}"] = s
   ob

@@ -1364,7 +1364,7 @@
       malformed typeParam, 'Data type parameter not used'
       throw new Error 'Data type parameter not used'

-  freshingSub = mapToSubstitution mapMap ((kind) -> ctx.freshTypeVariable kind), kinds
+  freshingSub = mapMap ((kind) -> ctx.freshTypeVariable kind), kinds

   dataKind = kindFnOfArgs (map ((name) -> lookupInMap kinds, name), varNames)...
   typeVars = map ((name) -> new TypeVariable name, (lookupInMap kinds, name)), varNames
@@ -1465,7 +1465,7 @@
       malformed param, 'A class paramater must occur in at least one method\'s type'
       false)
     return {}
-  freshingSub = mapToSubstitution mapMap ((kind) -> ctx.freshTypeVariable kind), kinds
+  freshingSub = mapMap ((kind) -> ctx.freshTypeVariable kind), kinds
   classParam = (param) ->
     substitute freshingSub, new TypeVariable param, (lookupInMap kinds, param)
   method = ({arity, type, def}) ->
@@ -3522,14 +3522,14 @@
   else if t1.TypeApp and t2.TypeApp
     s1 = mostGeneralUnifier t1.op, t2.op
     s2 = mostGeneralUnifier (substitute s1, t1.arg), (substitute s1, t2.arg)
-    joinSubs s1, s2
+    joinSubs s2, s1
   else if t1.Types and t2.Types
     if t1.types.length isnt t2.types.length
       unifyFail t1, t2
     else if _notEmpty t1.types
       s1 = mostGeneralUnifier t1.types[0], t2.types[0]
       s2 = mostGeneralUnifier (new Types t1.types[1...]), (new Types t2.types[1...])
-      joinSubs s1, s2
+      joinSubs s2, s1
     else
       emptySubstitution()
   else
@@ -3606,7 +3606,7 @@
   mapped.start = (subStart sub)
   mapped.fails = sub.fails
   for name in [(subStart sub)...(subLimit sub)] by 1 when v = (inSub sub, name)
-    mapped.vars[name] = fn v
+    setInSub mapped, name, fn v
   mapped

 subIntersection = (subA, subB) ->
@@ -3616,17 +3616,18 @@
 subUnion = (subA, subB) ->
   union = emptySubstitution()
   start = Math.min (subStart subA), (subStart subB)
+  end = Math.max (subLimit subA), (subLimit subB)
   union.start = start
-  for name in [start...Math.max (subLimit subA), (subLimit subB)] by 1
+  for name in [start...end] by 1
     type = (inSub subA, name) or (inSub subB, name)
     if type
-      union.vars[name] = type
+      setInSub union, name, type
   union.fails = [].concat subA.fails, subB.fails
   union

 newSubstitution = (name, type) ->
   sub = emptySubstitution()
-  sub.vars[name] = type
+  sub.vars[0] = type
   sub.start = name
   sub

@@ -3639,13 +3640,16 @@
   sub

 subLimit = (sub) ->
-  sub.vars.length
+  if sub.start is Infinity then -Infinity else sub.start + sub.vars.length

 subStart = (sub) ->
   sub.start

+setInSub = (sub, name, value) ->
+  sub.vars[name - sub.start] = value
+
 inSub = (sub, name) ->
-  sub.vars[name]
+  sub.vars[name - sub.start]

 emptySubstitution = ->
   start: Infinity
@@ -3655,8 +3659,9 @@
 # Unlike in Jones, we simply use substitute for both variables and quantifieds
 # - variables are strings, wheres quantifieds are ints
 substitute = (substitution, type) ->
-  if type.TypeVariable and substitution.vars
-    (inSub substitution, type.name) or type
+  if type.TypeVariable
+    substitution.vars and (inSub substitution, type.name) or
+      substitution.values and (lookupInMap substitution, type.name) or type
   else if type.QuantifiedVar
     substitution[type.var] or type
   else if type.TypeApp
@@ -3676,10 +3681,6 @@

 substituteList = (substitution, list) ->
   map ((t) -> substitute substitution, t), list
-
-# Only valid for substitute
-mapToSubstitution = (map) ->
-  vars: map.values

 findFree = (type) ->
   if type.TypeVariable
@@ -3876,7 +3877,7 @@
   polymorphicVars = filterMap ((name) -> inSet vars, name), findFree type
   kinds = mapToArray polymorphicVars
   varIndex = 0
-  quantifiedVars = mapToSubstitution mapMap (-> new QuantifiedVar varIndex++), polymorphicVars
+  quantifiedVars = mapMap (-> new QuantifiedVar varIndex++), polymorphicVars
   new ForAll kinds, (substitute quantifiedVars, type)

 star = '*'


Still broken the wheres compilation, shuffled back

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2348,7 +2348,7 @@
       jsNoop()

 findDefinitionsIncludingDeps = (ctx, names) ->
-  findDefinitions ctx, setToArray (findDeps ctx) names
+  findDefinitions ctx, reverse setToArray (findDeps ctx) names

 findDefinitions = (ctx, names) ->
   auxiliaries = ctx.auxiliaries()


Changed syntax of labels and added enum consts

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -134,7 +134,7 @@
       ['keyword', token in keywords]
       ['numerical', /^-?\d+/.test(token)]
       #['typename', /^[A-Z]/.test token]
-      ['label', /\w*:$/.test token]
+      ['label', /[:;]\w*$/.test token]
       ['string', /^["']/.test token]
       ['regex', /^\/[^ \/]/.test token]
       ['paren', token in ['(', ')']]


fixed dfn header regex

fixed dfn header regex


Close pleaseWait overlay, if page unload event is fired.

--- app/assets/javascripts/alchemy/alchemy.dirty.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.dirty.js.coffee
@@ -22,6 +22,7 @@
     window.onbeforeunload = @pageUnload

   pageUnload: ->
+    Alchemy.pleaseWaitOverlay(false)
     Alchemy._t('page_dirty_notice')

   setElementClean: (element) ->


sliders: removed unnecessary methods

sliders: removed unnecessary methods

--- app/assets/javascripts/lib/models/input_element.coffee
+++ app/assets/javascripts/lib/models/input_element.coffee
@@ -7,19 +7,6 @@
     @bind('change:user_value', @markDirty)
     @bind('change:user_value', @logUpdate)
     @bind('change:user_value', @update_collection)
-
-  set_min_value: (result) =>
-    @set({'min_value' : result})
-
-  set_max_value: (result) =>
-    @set({'max_value' : result})
-
-  set_label: (label) =>
-    return unless _.isObject(label)
-    @set({'label' : label})
-
-  set_start_value: (result) =>
-    @set({'start_value' : result})

   logUpdate: =>
     percent = 100 -
@@ -63,10 +50,11 @@
       if !values
         console.warn "Missing slider information! #{i.get 'key'}"
         return false
-      i.set_min_value(values.min)
-      i.set_max_value(values.max)
-      i.set_start_value(values.default)
-      i.set_label(values.label)
+      i.set
+        min_value: values.min
+        max_value: values.max
+        start_value: values.default
+        label: if _.isObject(values.label) then values.label else null

       v = +values.user
       def = if (v? && !_.isNaN(v)) then v else values.default
@@ -106,7 +94,7 @@
     inputElement.onscreen = true
     true

-  # Does a update request to update the values.
+  # Triggers a new API request on AppView
   handleUpdate: => @trigger("change")

   close_all_info_boxes: =>


Simplify the error checking logic

Simplify the error checking logic

--- bokehjs/src/coffee/models/transforms/interpolator.coffee
+++ bokehjs/src/coffee/models/transforms/interpolator.coffee
@@ -26,17 +26,7 @@
       throw Error('The parameters for x and y must be of the same type, either both strings which define a column in the data source or both arrays of the same length')
       return

-    if typeof(@get('x')) == 'object'
-      if @get('x').length != @get('y').length
-        throw Error('The arrays for x and y must be the same length')
-        return
     else
-      if @get('x').length == 0
-        return
-
-      if @get('y').length == 0
-        return
-
       if @get('data') == null
         throw Error('If the x and y parameters are not specified as an array, the data parameter is reqired.')
         return
@@ -66,6 +56,12 @@
       tsx = @get('x')
       tsy = @get('y')

+    if tsx.length != tsy.length
+      throw Error('The length for x and y do not match')
+
+    if tsx.length < 2
+      throw Error('x and y must have at least two elements to support interpolation')
+
     # The following sorting code is referenced from:
     # http://stackoverflow.com/questions/11499268/sort-two-arrays-the-same-way
     list = [];


Add comment

Add comment

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -1095,6 +1095,7 @@

       unless @isLocalSnapshotRestored

+        # Just resurrect snapshot for the host with/without a session.
         if snapshot and @amIHost
           @layoutManager.resurrectSnapshot snapshot
         else


Revert "IDE: use new resource state modal"

This reverts commit a198b5d7f7ff61a0afcac78f0fd95b8420a9e141.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -737,9 +737,11 @@
     container   ?= @getView()
     modalOptions = { state, container, initial }

-    if isTeamReactSide()
+    if checkFlag 'new-resource-state-modal'
       @machineStateModal = new ResourceStateModal modalOptions, machineItem
     else
+      if isTeamReactSide() and state is Stopping
+        modalOptions.cssClass = 'env-machine-state team full'
       @machineStateModal = new EnvironmentsMachineStateModal modalOptions, machineItem

     @machineStateModal.once 'KDObjectWillBeDestroyed', => @machineStateModal = null


Managed VMs default finder path

Managed VMs default finder path

This PR implements a default /root finder path for a Managed VM, as instructed via @gokmen

Related https://github.com/koding/finder/pull/27

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -399,6 +399,8 @@
     path ?= if owner = machineData.getOwner()
     then "/home/#{owner}"
     else '/'
+
+    path = '/root'  if machineData.isManaged()

     @workspace.ready ->
       filesPane.emit 'MachineMountRequested', machineData, path


home: fixed pricing copy

home: fixed pricing copy

--- client/introapp/main.coffee
+++ client/introapp/main.coffee
@@ -156,10 +156,10 @@
       slogan     : "Pricing"
       subSlogan  : """

-          When you hire someone, they can get up to speed in your development environment in 5 minutes—easily collaborating with others and contributing code.  All without sharing ssh keys or passwords.  Stop cc'ing your team; stop searching through old emails.
-

-

-          Koding is your new workspace.
+          You'll be able to buy more resources for your personal account or for accounts in your organization.
+

+

+          Coming soon.

       """


scroll to top of the page on page change

--- app/assets/javascripts/stores/questionnaire_store.cjsx
+++ app/assets/javascripts/stores/questionnaire_store.cjsx
@@ -12,10 +12,12 @@

   previousPage: ->
     currentPage -= 1
+    window.scrollTo(0, 0)
     @emit(CHANGE_EVENT)

   nextPage: ->
     currentPage += 1
+    window.scrollTo(0, 0)
     @emit(CHANGE_EVENT)

   load: (data) ->


Style changes of the previous commit

Style changes of the previous commit

--- lib/autocomplete-provider.coffee
+++ lib/autocomplete-provider.coffee
@@ -87,27 +87,22 @@
                         when '*' then '*'
                     longest_parameter_length = null
                     if value.type == 'function'
-                        params = value.text.match(/\w+\s+:=/g)
+                        params = value.text.match /\w+\s+:=/g
                         if params
-                            matches = params.map((x) ->
-                                return x.replace(/\s+:=/, '')
-                            )
-                            longest_parameter_length = Math.max.apply(null, matches.map((x) ->
-                                return x.length
-                            ))
+                            matches = params.map (x) -> return x.replace(/\s+:=/, '')
+                            longest_parameter_length = Math.max.apply(null, matches.map (x) -> return x.length)

-                    value.text = value.text.replace(/([(])([_])/g, '$1\n\t$2')
-                    value.text = value.text.replace(/([}])([)])/g, '$1\n$2')
-                    value.text = value.text.replace(/([,])\s+([_])/g, '$1\n\t$2')
+                    value.text = value.text.replace /([(])([_])/g   ,   '$1\n\t$2'
+                    value.text = value.text.replace /([}])([)])/g   ,   '$1\n$2'
+                    value.text = value.text.replace /([,])\s+([_])/g,   '$1\n\t$2'
                     markdownText = value.text
                     if longest_parameter_length
-                        replace_callback = (x) ->
+                        # Align parameters with spaces
+                        markdownText = value.text.replace /\w+\s+:=/g, (x) ->
                             y = x.replace(/\s+:=/g, '')
                             return x unless y
                             return y + ' '.repeat(longest_parameter_length - y.length + 1) + ':='
-                        markdownText = value.text.replace(/\w+\s+:=/g, replace_callback)
                     suggestion =
-                        className: 'pghoffsuggestionlist'
                         snippet: value.text
                         displayText: value.displayText
                         iconHTML: iconHTML


Clean up methods order in Selector prefixer

--- lib/autoprefixer/selector.coffee
+++ lib/autoprefixer/selector.coffee
@@ -6,15 +6,6 @@
   # Is rule selectors need to be prefixed
   check: (rule) ->
     rule.selector.indexOf(@name) != -1
-
-  # Clone and add prefixes for at-rule
-  add: (rule, prefix) ->
-    prefixed = @replace(rule.selector, prefix)
-
-    return if rule.parent.some (i) -> i.selector == prefixed
-
-    clone = rule.clone(selector: prefixed)
-    rule.parent.insertBefore(rule, clone)

   # Return prefixed version of selector
   prefixed: (prefix) ->
@@ -28,4 +19,13 @@
   replace: (selector, prefix) ->
     selector.replace(@regexp(), @prefixed(prefix))

+  # Clone and add prefixes for at-rule
+  add: (rule, prefix) ->
+    prefixed = @replace(rule.selector, prefix)
+
+    return if rule.parent.some (i) -> i.selector == prefixed
+
+    clone = rule.clone(selector: prefixed)
+    rule.parent.insertBefore(rule, clone)
+
 module.exports = Selector


refactoring

refactoring

--- lib/carte/client/views/list.cjsx
+++ lib/carte/client/views/list.cjsx
@@ -31,8 +31,7 @@
     @queryParam {tag: tag}, ['seed']

   queryParam: (param, deleteKeys)->
-    query = $.extend {}, @props.cards.query
-    query = $.extend query, param
+    query = $.extend {}, @props.cards.query, param
     for key in deleteKeys
       delete query[key]
     $.param(query)


Fix failing test

Fix failing test

--- test/property-filterer-test.coffee
+++ test/property-filterer-test.coffee
@@ -80,12 +80,12 @@
       propertiesString = "foo=hello\n\n\nbar=world"

       it 'should skip blank lines', ()->
-        filterer = PropertyFilterer.withString(propertiesString)
+        filterer = PropertyFilterer.withString(string: propertiesString)
         expect(filterer.properties).to.have.length(2)

       it 'should accept a delimiters option', ()->
         delimiters = ['%']
-        filterer = PropertyFilterer.withString(propertiesString, {delimiters: delimiters})
+        filterer = PropertyFilterer.withString(string: propertiesString, delimiters: delimiters)
         expect(filterer.delimiters).to.eql(delimiters)

     describe 'withStream', ()->


Added TODO.

Added TODO.

--- client/lib/annotator.coffee
+++ client/lib/annotator.coffee
@@ -33,6 +33,8 @@

   list

+# TODO: We should really move pages into their own objects so that we do not have to pass pageNumber everywhere around
+
 class @Annotator
   constructor: ->
     @_pages = []


removed old packages

removed old packages

--- client/routes/router.coffee
+++ client/routes/router.coffee
@@ -37,9 +37,6 @@
 , {
 	except: ['login']
 }
-
-subs = new SubsManager
-	expireIn: 0.5

 Router.route '/',
 	name: 'index'


Fixing mood poll (#49)

Fixing mood poll (#49)

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -225,11 +225,11 @@
         for achievement in JSON.parse body
           msg.send achievement.description

-  moodPoll = new CronJob '00 30 16 * * 1-5', ->
+  moodPoll = new CronJob '00 00 20 * * 1-5', ->
     for room, board of robot.brain.data._private.board
       buttonName = "mood_" + robot.brain.data._private.team[room]._id
       robot.emit 'slack.attachment',
-        message: msg.message
+        message: robot.messageRoom
         content:
           text: "How was your day today?"
           fallback: "Oh no! Something went horribly wrong!"


Remove unused test in smc-util

Remove unused test in smc-util

--- src/smc-util/test/misc-test.coffee
+++ src/smc-util/test/misc-test.coffee
@@ -1288,17 +1288,6 @@
     it "also works with weird corner cases", ->
         dup('asdf-').should.be.eql 'asdf--1'

-describe 'parsing function for argument names', ->
-    f0 = (a, b, c) => "none"
-    it 'works in the basic case', ->
-        expect misc.get_arg_names(f0)
-        .toEqual ['a', 'b', 'c']
-
-    f1 = (a=3, b=2, c=5) => "all"
-    it 'works with default values', ->
-        expect misc.get_arg_names(f1)
-        .toEqual ['a', 'b', 'c']
-
 describe 'top_sort', ->
     # Initialize DAG
     DAG =


add windows console key

add windows console key

--- keymaps/julia-client.cson
+++ keymaps/julia-client.cson
@@ -38,6 +38,7 @@
   'ctrl-j ctrl-r': 'julia-client:open-a-repl'
   'ctrl-alt-j ctrl-alt-r': 'julia-client:start-repl-client'
   'ctrl-j ctrl-s': 'julia-client:start-julia'
+  'ctrl-j ctrl-o': 'julia-client:open-console'
   'ctrl-j ctrl-c': 'julia-client:clear-console'
   'ctrl-j ctrl-l': 'julia-client:reset-loading-indicator'
   'ctrl-j ctrl-k': 'julia-client:kill-julia'


fix(test-name): changed client test name

--- src/spec/client/client.spec.coffee
+++ src/spec/client/client.spec.coffee
@@ -77,7 +77,7 @@
     expect(=> @client.setMaxParallel(0)).toThrow new Error 'MaxParallel must be a number between 1 and 100'
     expect(=> @client.setMaxParallel(101)).toThrow new Error 'MaxParallel must be a number between 1 and 100'

-  it 'should not repeat request on error',(done) ->
+  it 'should not repeat request on error if repeater is disabled',(done) ->
     client =  new SphereClient {config: Config, enableRepeater: false}
     callsMap = {
       0: { statusCode: 500, message: 'ETIMEDOUT' }


Changes for weird unhinged cards and periods

--- scripts/card-fetcher.coffee
+++ scripts/card-fetcher.coffee
@@ -11,15 +11,27 @@
 #   JacobGinsparg

 module.exports = (robot) ->
-  robot.hear /\[\[(.[\w|\,| |/|'|_|(|)]+)\]\]+/i, (msg) ->
+  robot.hear /\[\[(.[\w|\,| |/|'|_|(|)|.]+)\]\]+/i, (msg) ->
     url = "http://gatherer.wizards.com/Handlers/Image.ashx"
     card = msg.match[1]
-    cardCheckUrl = "https://api.deckbrew.com/mtg/cards?name=#{card}"
-    robot.http(cardCheckUrl)
-      .get() (err, res, body) ->
-        jsonBody = JSON.parse body
-        firstMatch = jsonBody[0]["name"] if jsonBody.length != 0
-        if firstMatch.toUpperCase() != card.toUpperCase()
-          msg.send "#{url}?type=card&name=Dismal%20Failure"
-        else
-          msg.send "#{url}?type=card&name=#{encodeURIComponent(card)}"
+    cardCheckUrl = ""
+    if card == "_____"
+      cardCheckUrl = "https://api.deckbrew.com/mtg/cards/#{card}"
+      robot.http(cardCheckUrl)
+        .get() (err, res, body) ->
+          jsonBody = JSON.parse body
+          firstMatch = jsonBody["name"]
+          if !firstMatch || firstMatch.toUpperCase() != card.toUpperCase()
+            msg.send "#{url}?type=card&name=Dismal%20Failure"
+          else
+            msg.send "#{url}?type=card&name=#{encodeURIComponent(card)}"
+    else
+      cardCheckUrl = "https://api.deckbrew.com/mtg/cards?name=#{card}"
+      robot.http(cardCheckUrl)
+        .get() (err, res, body) ->
+          jsonBody = JSON.parse body
+          firstMatch = jsonBody[0]["name"] if jsonBody.length != 0
+          if !firstMatch || firstMatch.toUpperCase() != card.toUpperCase()
+            msg.send "#{url}?type=card&name=Dismal%20Failure"
+          else
+            msg.send "#{url}?type=card&name=#{encodeURIComponent(card)}"


provide a hook to parent scope

provide a hook to parent scope

--- coffee/directives/fixtable.coffee
+++ coffee/directives/fixtable.coffee
@@ -33,6 +33,9 @@
 			scope.prevPage = ->
 				scope.pagingOptions.currentPage -= 1

+			# provide a hook to parent scope
+			scope.parent = scope.$parent
+
 		replace: true
 		restrict: 'E'
 		scope:


Move outerHTML into Chosen's Abstract class until we find it a permanent home with other dom utilities.

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -1,9 +1,3 @@
-outerHTML = (element) ->
-    return element.outerHTML if element.outerHTML
-    tmp = document.createElement("div")
-    tmp.appendChild(element)
-    tmp.innerHTML
-
 class AbstractChosen

   constructor: (@form_field, @options={}) ->
@@ -96,7 +90,7 @@
     option_el.setAttribute("data-option-array-index", option.array_index)
     option_el.innerHTML = option.search_text

-    outerHTML(option_el)
+    this.outerHTML(option_el)

   result_add_group: (group) ->
     return '' unless group.search_match || group.group_match
@@ -106,7 +100,7 @@
     group_el.className = "group-result"
     group_el.innerHTML = group.search_text

-    outerHTML(group_el)
+    this.outerHTML(group_el)

   results_update_field: ->
     this.set_default_text()
@@ -236,6 +230,12 @@

     return true

+  outerHTML: (element) ->
+    return element.outerHTML if element.outerHTML
+    tmp = document.createElement("div")
+    tmp.appendChild(element)
+    tmp.innerHTML
+
   # class methods and variables ============================================================

   @browser_is_supported: ->
@@ -250,3 +250,4 @@
   @default_multiple_text: "Select Some Options"
   @default_single_text: "Select an Option"
   @default_no_result_text: "No results match"
+


Use ensurePath rather than ensure in createDirectory operator

--- src/tasks/package/operators.coffee
+++ src/tasks/package/operators.coffee
@@ -1,7 +1,7 @@
 Neat = require '../../neat'

 {parallel} = Neat.require 'async'
-{ensure} = Neat.require 'utils/files'
+{ensurePath} = Neat.require 'utils/files'
 {writeFile} = require 'fs'
 {compile:coffee} = require 'coffee-script'
 {parser, uglify:pro} = require 'uglify-js'
@@ -120,7 +120,7 @@
 createDirectory = (buffer, conf, callback) ->
   newBuffer = {}
   path = "#{conf.dir}/#{conf.directory}"
-  ensure path, (err) ->
+  ensurePath path, (err) ->
     for p,c of buffer
       newBuffer[p.replace conf.dir, path] = c

@@ -192,7 +192,7 @@

 saveToFile = (buffer, conf, callback) ->
   gen = (path, content) -> (callback) ->
-    writeFile path, content, ->
+    writeFile path, content, (err) ->
       callback?()

   parallel (gen k,v for k,v of buffer), ->


callbacks

callbacks

--- src/tower/model/validations.coffee
+++ src/tower/model/validations.coffee
@@ -76,6 +76,19 @@
     # @param [Function] callback
     #
     # @return [void]
+    #
+    # @todo make sure the callbacks execute in this order
+    # - save
+    # - valid
+    # - before_validation
+    # - validate
+    # - after_validation
+    # - before_save
+    # - before_create
+    # - create
+    # - after_create
+    # - after_save
+    # - after_commit
     validate: (callback) ->
       success         = false


regenerating large swathes of the docs

regenerating large swathes of the docs

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -22,7 +22,7 @@
   helpers      = this.helpers

 # The current CoffeeScript version number.
-exports.VERSION = '0.7.2'
+exports.VERSION = '0.9.0'

 # Instantiate a Lexer for our use here.
 lexer = new Lexer


manually add the newline at EOF

manually add the newline at EOF

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -278,3 +278,4 @@
     "#{functionName} (#{fileLocation})"
   else
     fileLocation
+


Fix Conversation.coffee

Fix Conversation.coffee

--- scripts/conversation.coffee
+++ scripts/conversation.coffee
@@ -64,5 +64,5 @@

 module.exports = (robot) ->

-  robot.hear /(snowbit|sn0wbit)/i, (msg) ->
+  robot.hear /(snowbit)/i, (msg) ->
     msg.reply msg.random phrases


"Add step to rebuild raw_url of plunks

"Add step to rebuild raw_url of plunks

--- lib/plunker/stores/memory.coffee
+++ lib/plunker/stores/memory.coffee
@@ -17,18 +17,8 @@

 delay = (timeout, callback) -> setTimeout(callback, timeout)

-class Model extends Backbone.Model
-
 class Collection extends Backbone.Collection
   comparator: (model) -> -Cromag.parse(model.get("updated_at") or model.get("created_at"))
-  parse: (data) ->
-    _.map json, (json) ->
-      if matches = json.http_url.match(/^(http:\/\/[^\/]+)(.+)$/)
-        json.raw_url = "#{matches[0]}/raw#{matches[1]}"
-
-        for filename, file of json.files
-          file.raw_url = json.raw_url + filename
-      json

 class Store
@@ -75,7 +65,17 @@
     console.log "Attempting to restore data from: #{@filename}"
     fs.readFile @filename, "utf8", (err, data) ->
       if err then console.log "Failed to restore data: #{self.filename}"
-      else self.plunks.reset(JSON.parse(data)) and console.log "Restore succeeded from: #{self.filename}"
+      else
+        plunks = JSON.parse(data)
+        plunks = _.map plunks, (json) ->
+          if matches = json.html_url.match(/^(http:\/\/[^\/]+)(.+)$/)
+            json.raw_url = "#{matches[1]}/raw#{matches[2]}"
+
+            for filename, file of json.files
+              file.raw_url = json.raw_url + filename
+          json
+
+        self.plunks.reset(plunks) and console.log "Restore succeeded from: #{self.filename}"

   shrink: =>
     if @plunks.length > @options.size


Fix active setting

Fix active setting

--- bucky.coffee
+++ bucky.coffee
@@ -82,7 +82,7 @@
   setOptions = (opts) ->
     extend options, opts

-    if 'sample' of opts
+    if 'sample' of opts or 'active' of opts
       updateActive()

     options


Fixes moment().format error on userInfo

Fixes moment().format error on userInfo

--- client/views/app/userInfo.coffee
+++ client/views/app/userInfo.coffee
@@ -49,7 +49,8 @@
 		return Session.get('selfVideoUrl')

 	userTime: ->
-		return Template.instance().now?.get().utcOffset(@utcOffset).format('HH:mm')
+		if @utcOffset?
+			return Template.instance().now.get().utcOffset(@utcOffset).format('HH:mm')

 Template.userInfo.onCreated ->
 	@now = new ReactiveVar moment()


Update class template file

Update class template file


Fix bug where the form could be submitted with an item being edited

--- app/assets/javascripts/cocoon/iceberg.js.coffee
+++ app/assets/javascripts/cocoon/iceberg.js.coffee
@@ -12,12 +12,13 @@

       @toggleInputVisibility()

+      @setFormSubmitable()
+
       unless mode is "add" or @line.find('.error').length > 0
         @display.removeClass('hidden')
         @oldForm().remove()
         @_bindSelectorsInitialization()
         @newForm().addClass('hidden')
-        @setFormSubmitable()

     _bindButtons: (form) ->
       # console.log '_bindButtons:this', this


delete js in assets

delete js in assets

--- app/assets/javascripts/expenses/index.js.coffee
+++ app/assets/javascripts/expenses/index.js.coffee
@@ -1,3 +0,0 @@
-$ ->
-  $('#new_expense_button').on 'ajax:success', (data, res, xhr) ->
-    $('#modal').html(res)


Fix zoomlevel range.

Fix zoomlevel range.

--- app/assets/javascripts/views/map.view.js.coffee
+++ app/assets/javascripts/views/map.view.js.coffee
@@ -55,6 +55,8 @@
     that = @

     options =
+      maxZoom: 19
+      minZoom: 2
       unloadInvisibleTiles: true
       pointToLayer: $.proxy(@pointToLayer, @)


Switch preview detection methods @adamschwartz

--- coffee/installHelper.coffee
+++ coffee/installHelper.coffee
@@ -64,7 +64,7 @@

     ready ->
       if options.trigger is 'first-page-visit'
-        if location.href.match(/https:\/\/.+\.(p|s)\.eager\.works\//i)
+        if window.Eager?.installs?.preview?.appId is 'AalP5veMma6s'
           tour.start()

         else if window.localStorage?.eagerShepherdHasRun isnt 'true'


Tightened up a test now that I confirmed that MenuHelpers.merge() intentionally strips the created field.

--- spec/context-menu-manager-spec.coffee
+++ spec/context-menu-manager-spec.coffee
@@ -158,14 +158,13 @@
       expect(addError.message).toContain('<>')

     it "recursively applies templateForEvent on submenu items", ->
-      created = (event) -> @label = 'D'
       item = {
         label: 'A',
         command: 'B',
         submenu: [
           {
             label: 'C',
-            created,
+            created: (event) -> @label = 'D',
           }
         ]
       }


Removed width for staff badge size check

--- client/app/lib/commonviews/avatarviews/avatarview.coffee
+++ client/app/lib/commonviews/avatarviews/avatarview.coffee
@@ -128,7 +128,7 @@

     @cite.setClass flags

-    @cite.hide() if width and height < minAvatarSize
+    @cite.hide() if height < minAvatarSize

     kd.getSingleton("groupsController").ready =>
       {slug} = kd.getSingleton("groupsController").getCurrentGroup()


Renamed Lexer.tag() argument to new_tag, due to Rhino scope confusion

--- lexer.coffee
+++ lexer.coffee
@@ -399,9 +399,9 @@
     @tokens.push [tag, value, @line]

   # Peek at a tag in the current token stream.
-  tag: (index, tag) ->
+  tag: (index, new_tag) ->
     return unless tok: @prev index
-    return tok[0]: tag if tag?
+    return tok[0]: new_tag if new_tag?
     tok[0]

   # Peek at a value in the current token stream.


fix syntax error in xhtml-file.coffee

fix syntax error in xhtml-file.coffee

--- scripts/gh-book/xhtml-file.coffee
+++ scripts/gh-book/xhtml-file.coffee
@@ -120,7 +120,7 @@
       super(options, force) if not _.isEmpty(changed)

     # This promise is resolved once the file is parsed so we know which images to load
-    _imagesLoaded = new $.Deferred()
+    _imagesLoaded: new $.Deferred()
     _loadComplex: (originalPromise) ->
       return @_imagesLoaded


fix <tbody> being converted to <tprefix-body>

--- scripts/gh-book/xhtml-file.coffee
+++ scripts/gh-book/xhtml-file.coffee
@@ -138,7 +138,10 @@

       html = html.replace(/html>/g, "prefix-html>")
       html = html.replace(/<\/head>/g, "</prefix-head>")
-      html = html.replace(/body>/g, "prefix-body>")
+
+      # **Note:** There is a `<tbody>` element so we have to match start of the tag
+      html = html.replace(/<body/g, "<prefix-body")
+      html = html.replace(/<\/body/g, "</prefix-body")

       html = html.replace(/<html/g, "<prefix-html")
       # Search for the exact element `<head>` since otherwise it could be


scripts: silence rabbitmq client not existing errors

--- scripts/user-importer/main.coffee
+++ scripts/user-importer/main.coffee
@@ -63,8 +63,13 @@

             user.confirmEmail (err)->

-              if err then console.log "     Failed to verify: ", err
-              else        console.log "     Email verified."
+              if err
+                # RabbitMQ client is required to be initialized to send emails;
+                # however send welcome emails is not reuqired here, so we silence it.
+                if !/RabbitMQ client not found in Email/.test err
+                  console.log "     Failed to verify: ", err
+              else
+                console.log "     Email verified."

               console.log "\n   - Adding to group #{u.group} ..."


remove unless comments

remove unless comments

--- server/controllers/devices.coffee
+++ server/controllers/devices.coffee
@@ -1,9 +1,6 @@
 request = require("request-json")
 fs = require('fs')
 Device = require '../models/device'
-# Helpers
-
-

 module.exports =


Fix lint

Fix lint

--- server/controllers/sharing.coffee
+++ server/controllers/sharing.coffee
@@ -70,7 +70,7 @@
         return next err
     # ...and well formed
     for rule in share.rules
-         unless rule.docType and rule.id
+        unless rule.docType and rule.id
             err = new Error "Incorrect rule detected"
             err.status = 400
             return next err


Less verbose getEnv

Less verbose getEnv

--- server/initializers/config.coffee
+++ server/initializers/config.coffee
@@ -11,10 +11,7 @@
     throw new Error("No application config found, have you called AppConfig.initialize() first?")

 getEnv = ->
-  unless process.env.NODE_ENV?
-    return 'development'
-
-  return process.env.NODE_ENV
+  process.env.NODE_ENV || 'development'

 exports.initialize = ->
   env = getEnv()


Getters/setters caching has been removed

It occurs in bugs when two properties have been reclarated with the same name
but different options.

--- utils/item.coffee
+++ utils/item.coffee
@@ -229,8 +229,6 @@

 	Impl.DeepObject = DeepObject

-	funcCache = Object.create null
-
 	exports =
 	Object: UtilsObject
 	FixedObject: FixedObject
@@ -278,8 +276,7 @@
 		# getter
 		internalName = getPropInternalName name

-		propGetter = basicGetter =
-		funcCache["get-#{internalName}"] ?= Function "return this.#{internalName}"
+		propGetter = basicGetter = Function "return this.#{internalName}"

 		if valueConstructor
 			propGetter = ->
@@ -297,7 +294,7 @@
 			namespaceSignalName = "on#{utils.capitalize(namespace)}Change"
 			uniquePropName = namespace + utils.capitalize(name)

-			func = funcCache["set-deep-#{namespace}-#{internalName}-#{developmentSetter?}-#{implementation?}"] ?= do ->
+			func = do ->
 				funcStr = "return function(val){\n"
 				`//<development>`
 				if developmentSetter?
@@ -318,7 +315,7 @@
 				func = new Function 'impl', 'implValue', 'emitSignal', 'debug', funcStr
 			propSetter = basicSetter = func implementation, implementationValue, emitSignal, developmentSetter
 		else
-			func = funcCache["set-#{internalName}-#{developmentSetter?}-#{implementation?}"] ?= do ->
+			func = do ->
 				funcStr = "return function(val){\n"
 				`//<development>`
 				if developmentSetter?


Loosened the welcome-back detector

Loosened the welcome-back detector

On a line by itself triggers again

Issue #6

--- kills.coffee
+++ kills.coffee
@@ -83,7 +83,7 @@
     if (awake(robot))
       msg.send "Yeah, fuck those guys.  Rabble rabble."

-  robot.hear /^wb [a-z]+|welcome back/i, (msg) ->
+  robot.hear /^wb [a-z]+|^wb$|welcome back/i, (msg) ->
     if (awake(robot))
       msg.send "http://imagemacros.files.wordpress.com/2009/06/clowntrain.jpeg"


Add support for user-interface tdd|bdd

Add support for user-interface tdd|bdd

--- cakes.coffee
+++ cakes.coffee
@@ -2,6 +2,23 @@

 colors = require 'colors'
 _ = require 'underscore'
+{argv} = require 'optimist'
+
+
+interface =
+	bdd :
+		it : 'it'
+		describe : 'describe'
+	tdd :
+		it : 'test'
+		describe : 'suite'
+
+argv.ui = argv.ui ? argv.u  # mocha option --ui overwrites -u
+if argv.ui && (argv.ui == 'tdd' or _.last(argv.ui) == 'tdd')
+	ui = interface.tdd
+else
+	ui = interface.bdd  # Default
+

 exports.Feature = (feature, story..., callback)->
 	#  exp. Feature 'new feature', 'in order to do good', 'as a user', 'I want to do good', ->
@@ -10,8 +27,9 @@
 	message = "Feature: #{feature} \n\n".green.underline
 	(message += '\t'+part+'\n' for part in story)

-	describe(message, callback)
+	global[ui.describe](message, callback)
 	return
+

 dic = (type, label, args, options={})->  # Dictate to describe() or it()

@@ -33,10 +51,10 @@

 		fn = ->
-			global[type] label, cb
+			global[ui[type]] label, cb

 		if options.padding
-			describe '|'.green, ->
+			global[ui.describe] '|'.green, ->
 				fn()
 		else
 			fn()


NavigationMachineItem: use uid for machine links of reassigned machines

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -27,15 +27,20 @@

     @alias           = machine.slug or machine.label
     machineOwner     = machine.getOwner()
+    reassigned       = machine.jMachine.meta?.oldOwner?
+    oldOwner         = machine.jMachine.meta?.oldOwner ? machineOwner
     isMyMachine      = machine.isMine()
     machineRoutes    =
       own            : "/IDE/#{@alias}"
       collaboration  : "/IDE/#{@getChannelId data}"
       permanentShare : "/IDE/#{machine.uid}"
+      reassigned     : "/IDE/#{machine.uid}"

     machineType = 'own'

-    unless isMyMachine
+    if reassigned
+      machineType = 'reassigned'
+    else if not isMyMachine
       machineType = if machine.isPermanent() then 'permanentShare' else 'collaboration'

     @machineRoute = groupifyLink machineRoutes[machineType]
@@ -56,11 +61,11 @@
     { @machine } = @getData()
     labelPartial = machine.label or @alias

-    unless isMyMachine
+    if not isMyMachine or reassigned
       labelPartial = """
         #{labelPartial}
         <cite class='shared-by'>
-          (@#{htmlencode.htmlDecode machineOwner})
+          (@#{htmlencode.htmlDecode oldOwner})
         </cite>
       """


Handle errors from applications kite and show a proper modal for them

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -224,15 +224,10 @@
     if KDApps[name]
       callback null, KDApps[name]
     else
-
       @fetchCompiledApp manifest, (err, script)=>
         if err
-          @compileApp name, (err)=>
-            if err
-              new KDNotificationView type : "mini", title : "There was an error, please try again later!"
-              callback err
-            else
-              callback err, KDApps[name]
+          @compileApp name, (err)->
+            callback err, KDApps[name]
         else
           @defineApp name, script
           callback err, KDApps[name]
@@ -392,19 +387,47 @@
     compileOnServer = (app)=>
       return warn "#{name}: No such application!" unless app
       appPath = getAppPath app
+
+      loader = new KDNotificationView
+        duration : 18000
+        title    : "Compiling #{name}..."
+        type     : "mini"
+
       @kiteController.run
         kiteName  : "applications"
         method    : "compileApp"
-        withArgs  :
-          appPath : appPath
+        withArgs  : {appPath}
       , (err)=>
         if not err
+          loader.notificationSetTitle "Fetching compiled app..."
           @fetchCompiledApp app, (err, res)=>
-            if not err then @defineApp name, res
-            callback?()
-        else
-          warn "An error occured while compiling:", err
-          callback? yes
+            if not err
+              @defineApp name, res
+              loader.notificationSetTitle "App compiled successfully"
+              loader.notificationSetTimer 2000
+            callback? err
+        else
+          loader.destroy()
+
+          if err.details?.details?
+            details = """
ERROR: #{err.details.details}

+                              FILE : #{err.details.file}

"""
+          else if err.details?
+            details = "
#{err.details}
"
+          else
+            details = ""
+
+          new KDModalView
+            title   : "An error occured while compiling the App!"
+            width   : 500
+            overlay : yes
+            content : """
+

+
#{err.message}

+                        #{details}
+

+                      """
+          callback? err

     unless @constructor.manifests[name]
       @fetchApps (err, apps)->


We need this

We need this

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -1,3 +1,5 @@
+KDApps = {}
+
 class KodingAppsController extends KDController

   escapeFilePath = FSHelper.escapeFilePath


Use getManifest and getManifests helpers

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -52,8 +52,9 @@
   fetchApps:(callback)->

     @appStorage.ready =>
-      if Object.keys(@constructor.manifests).length isnt 0
-        callback null, @constructor.manifests
+      manifests = @getManifests()
+      if Object.keys(manifests).length > 0
+        callback null, manifests
       else
         @fetchAppsFromDb (err, apps)=>
           if err and not @_loadedOnce
@@ -66,7 +67,7 @@

   fetchAppFromFs:(appName, cb)->

-    manifests = @constructor.manifests
+    manifests = @getManifests()
     appsPath = "/home/#{KD.nick()}/Applications/"
     suffix   = ".kdapp/manifest.json"

@@ -109,7 +110,7 @@
         apps = @filterAppsFromFileList files
         @fetchAppsFromFsHelper apps, (result)=>
           for callback in @_fetchQueue
-            callback null, @constructor.manifests
+            callback null, @getManifests()
           @_fetchQueue = []
     , ->
       warn msg = "Timeout reached for kite request"
@@ -129,7 +130,7 @@

   syncAppStorageWithFS:(force=no, callback=noop)->

-    currentApps = Object.keys(@constructor.manifests)
+    currentApps = Object.keys(@getManifests())
     removedApps = []
     newApps     = []

@@ -206,7 +207,7 @@

   putAppsToAppStorage:(apps, callback = noop)->
     warn "calling putAppsToAppStorage:", apps
-    apps or= @constructor.manifests
+    apps or= @getManifests()
     @appStorage.setValue 'apps', apps, -> callback()

   invalidateDeletedApps:(deletedApps, force=no, callback)->
@@ -215,7 +216,7 @@
       @constructor.manifests = {}
       @putAppsToAppStorage manifests, callback

-    manifests = @constructor.manifests
+    manifests = @getManifests()
     delete manifests[app]  for app in deletedApps
     if deletedApps.length > 0
       @putAppsToAppStorage manifests, callback
@@ -362,7 +363,7 @@

     @getAppScript manifest, (appScript)=>

-      manifest   = @constructor.manifests[appName]
+      manifest   = @getManifest appName
       appPath    = @getAppPath manifest
       options    =
         method   : "app.publish"
@@ -481,11 +482,11 @@
                       """
           callback? err

-    unless @constructor.manifests[name]
+    unless @getManifest name
       @fetchApps (err, apps)->
         compileOnServer apps[name]
     else
-      compileOnServer @constructor.manifests[name]
+      compileOnServer @getManifest name

   getManifests:->
     @constructor.manifests


BW & XY: Fix Baton Passing stat stages.

BW & XY: Fix Baton Passing stat stages.

--- server/bw/attachment.coffee
+++ server/bw/attachment.coffee
@@ -1291,7 +1291,7 @@
       attachment.battle = pokemon.battle
       attachment.attached = true
       pokemon.attachments.attachments.push(attachment)
-    pokemon.boost(@stages)
+    pokemon.setBoosts(@stages)
     @team.unattach(@constructor)

 class @Attachment.FlashFire extends @VolatileAttachment


Minor changes in the archetype

Minor changes in the archetype

--- library/nucleus/archetype.coffee
+++ library/nucleus/archetype.coffee
@@ -28,7 +28,6 @@
 connect = require "connect"
 logger = require "winston"
 assert = require "assert"
-events = require "eventemitter2"
 colors = require "colors"
 nconf = require "nconf"
 https = require "https"
@@ -36,12 +35,14 @@
 util = require "util"
 url = require "url"

+{EventEmitter2} = require "eventemitter2"
+
 # An important base class that should be used when you need to inherit
 # from a clean hierarchy. Basically, whenever you need a clean, top level
 # root object to inherit - use this one, not Object and not EventEmitter.
 # This abstraction also aids help for the dynamic class composition system
 # that needs a common point of match in the class hierarchies of the peers.
-module.exports.Archetype = class Archetype extends events.EventEmitter2
+module.exports.Archetype = class Archetype extends EventEmitter2

     # This is a marker that indicates to some internal subsystems
     # that this class has to be considered abstract and therefore


get the not needed anymore imports out

get the not needed anymore imports out

--- library/semantic/dialogue.coffee
+++ library/semantic/dialogue.coffee
@@ -30,7 +30,6 @@
 {Widget} = require "./abstract"
 {Archetype} = require "../nucleus/arche"
 {remote, cc} = require "../membrane/remote"
-{GoogleFonts} = require "../fringes/fonting"

 # This frontend widget implements a simple dialogue, rendered as a
 # modal window. The HTML markup (and therefore the exterior looks)


add a alert

add a alert

--- lib/atom-chameleon.coffee
+++ lib/atom-chameleon.coffee
@@ -113,6 +113,9 @@
     bsv = new (require './build/build-state-view')()
     bsv.attach()

+  cmdCreateFile: ->
+    alert "This feature is in development."
+
   cmdCreateProject: ->

     ProjectWizardView = require './project/project-wizard-view'


Double quotes to avoid escaping

Double quotes to avoid escaping

--- lib/config.coffee
+++ lib/config.coffee
@@ -71,6 +71,6 @@
   quietUpdateCheck:
     type: 'boolean'
     default: false
-    description: 'Mute \'Latest backup is already applied\' message'
+    description: "Mute 'Latest backup is already applied' message"
     order: 15
 }


<model> Driver - Adding possibility to create from js or coffee file.

--- server/models/driver.coffee
+++ server/models/driver.coffee
@@ -113,7 +113,7 @@
 		superCreate = super

 		file = data.file
-		unless file.ext = path.extname(file.originalFilename) and file.ext in ['.zip', '.tar', '.tar.bz2', '.tar.gz']
+		unless file.ext = path.extname(file.originalFilename) and file.ext in ['.zip', '.tar', '.tar.bz2', '.tar.gz', '.js', '.coffee']
 			callback 'Unknown file extension', null
 			return cleanUp()
 		unless file.name = path.basename(file.originalFilename, file.ext)
@@ -127,14 +127,11 @@
 			else if drivers.length isnt 0 # Driver already exists.
 				callback 'Driver already added', drivers[0]
 			else
-				# Create directory for the driver's files:
+				# If zipped, unzip. Otherwise copy it to its own folder:
 				dirName = DRIVERS_DIR + file.name + "/"
-				mkdirp dirName, (err) ->
-					if err
-						callback 'Error creating the directory for the driver', null
-						return
-
-					# Unzip in it:
+
+				if file.ext in ['.zip', '.tar', '.tar.bz2', '.tar.gz']
+					# Unzip it:
 					plug = [
 						'.zip' : decompress.zip,
 						'.tar' : decompress.tar,
@@ -143,38 +140,47 @@

 					new decompress( mode: '755' )
 					 .src file.path
-					 .dest dirName
+					 .dest DRIVERS_DIR
 					 .use plug strip: 1
 					 .run (err) ->
 						if err
 							callback 'Error unzipping the driver', null
-							return
-
-						# Initialize the driver:
-						unless driverModule = require dirName + file.name
-							callback 'Couldn\'t find module', null
-						unless isActuator = driverModule.isActuator or isSensor = driverModule.isSensor # or (!isActuator and !isSensor)
-							callback 'Driver not defining if for Sensors and/or Actuators', null
-
-						unless driverModule.add and driverModule.remove and driver.update and driver.apply and (!isActuator or driver.init)
-							callback 'Driver not implementing the interface', null
-
-						driverModule.init (err) ->
-							if err
-								callback 'Error initializing the driver', null
-							else
-								# If everything worked, save the Driver instance in DB:
-								data =
-									name: file.name
-									isSensor: isSensor
-									isActuator: isActuator
-								superCreate data, (err, driver) ->
-									if err
-										callback 'Error saving the driver in DB', null
-									else
-										sensorsDrivers[driver.id] = driverModule if driver.isSensor
-										actuatorsDrivers[driver.id] = driverModule if driver.isActuator
-										callback null, driver
+						else
+							initializeDriver()
+				else
+					mkdirp dirName, (err) ->
+						if err
+							callback 'Error creating the directory for the driver', null
+						else
+							fs.createReadStream(file.path).pipe(fs.createWriteStream(dirName + file.name + file.ext))
+							initializeDriver()
+
+				# Initialize the driver:
+				initializeDriver = () ->
+					unless driverModule = require dirName + file.name
+						callback 'Couldn\'t find module', null
+					unless isActuator = driverModule.isActuator or isSensor = driverModule.isSensor # or (!isActuator and !isSensor)
+						callback 'Driver not defining if for Sensors and/or Actuators', null
+
+					unless driverModule.add and driverModule.remove and driver.update and driver.apply and (!isActuator or driver.init)
+						callback 'Driver not implementing the interface', null
+
+					driverModule.init (err) ->
+						if err
+							callback 'Error initializing the driver', null
+						else
+							# If everything worked, save the Driver instance in DB:
+							data =
+								name: file.name
+								isSensor: isSensor
+								isActuator: isActuator
+							superCreate data, (err, driver) ->
+								if err
+									callback 'Error saving the driver in DB', null
+								else
+									sensorsDrivers[driver.id] = driverModule if driver.isSensor
+									actuatorsDrivers[driver.id] = driverModule if driver.isActuator
+									callback null, driver

 	###
 	# reinit


Log invalid private key parsing

Log invalid private key parsing

--- dl/src/ecc/key_public.coffee
+++ dl/src/ecc/key_public.coffee
@@ -67,8 +67,9 @@
            new_checksum = new_checksum.slice 0, 4
            assert.deepEqual checksum, new_checksum, 'Checksum did not match'
            PublicKey.fromBuffer public_key
-        catch  e
-           null
+        catch e
+            console.error 'PublicKey.fromPublicKeyString', e
+            null

     toAddressString: ->


bugfix

bugfix

--- imports/startup/client/index.coffee
+++ imports/startup/client/index.coffee
@@ -57,7 +57,7 @@

 	Template.registerHelper 'isCordova', -> Platform.isCordova

-	Template.registerHelper 'platformClasses'. ->
+	Template.registerHelper 'platformClasses', ->
 		classes = []
 		classes.push 'platform-cordova' if Meteor.isCordova
 		classes.push 'platform-web' if Meteor.isClient


Add undefined check in idris-ide-mode.coffee

Fixes #69

--- lib/idris-ide-mode.coffee
+++ lib/idris-ide-mode.coffee
@@ -15,7 +15,7 @@
       pathToIdris = atom.config.get("language-idris.pathToIdris")

       pkgs =
-        if compilerOptions.pkgs.length
+        if compilerOptions.pkgs && compilerOptions.pkgs.length
           ["-p"].concat compilerOptions.pkgs
         else
           []


Rework #2223 (backspace).

Rework #2223 (backspace).

--- lib/keyboard_utils.coffee
+++ lib/keyboard_utils.coffee
@@ -53,16 +53,21 @@
       ""

   getKeyCharUsingKeyIdentifier: (event) ->
-    # Not a letter
-    if (event.keyIdentifier.slice(0, 2) != "U+")
-      return @keyNames[event.keyCode] if (@keyNames[event.keyCode])
-      # F-key
-      if (event.keyCode >= @keyCodes.f1 && event.keyCode <= @keyCodes.f12)
-        return "f" + (1 + event.keyCode - keyCodes.f1)
-      return ""
-    return "backspace" if event.keyIdentifier == "U+0008"
+    # Handle named keys.
+    keyCode = event.keyCode
+    if keyCode
+      if keyCode of @keyNames
+        return @keyNames[keyCode]
+      # Function keys.
+      if @keyCodes.f1 <= keyCode <= @keyCodes.f12
+        return "f" + (1 + keyCode - keyCodes.f1)

     keyIdentifier = event.keyIdentifier
+
+    # Not a letter.
+    if not keyIdentifier.startsWith "U+"
+      return ""
+
     # On Windows, the keyIdentifiers for non-letter keys are incorrect. See
     # https://bugs.webkit.org/show_bug.cgi?id=19906 for more details.
     if ((@platform == "Windows" || @platform == "Linux") && @keyIdentifierCorrectionMap[keyIdentifier])


:lipstick:

:lipstick:

--- lib/settings-panel.coffee
+++ lib/settings-panel.coffee
@@ -3,12 +3,11 @@
 _ = require 'underscore'
 async = require 'async'
 AtomPackage = require 'atom-package'
+Editor = require 'editor'

 ###
 # Internal #
 ###
-
-Editor = require 'editor'

 module.exports =
 class SettingsPanel extends View


⬆ particle-dev-cloud-variables 0.0.5

⬆ particle-dev-cloud-variables 0.0.5

--- build/tasks/install-unpublished-packages.coffee
+++ build/tasks/install-unpublished-packages.coffee
@@ -51,6 +51,6 @@
       installPackage 'spark', 'language-spark', '0.3.1', ->
         installPackage 'spark', 'exception-reporting', '0.36.0', ->
           installPackage 'spark', 'spark-dev-cloud-functions', '0.0.5', ->
-            installPackage 'spark', 'particle-dev-cloud-variables', '0.0.4', ->
+            installPackage 'spark', 'particle-dev-cloud-variables', '0.0.5', ->
               installPackage 'spark', 'metrics', '0.45.0', ->
                 done()


Cleanup code

Cleanup code

--- src/scripts/helpers/backbone/views/base.coffee
+++ src/scripts/helpers/backbone/views/base.coffee
@@ -51,8 +51,7 @@

     # Update page title
     updateTitle: () ->
-      if @pageTitle
-        document.title = settings.titlePrefix + @pageTitle
+      document.title = settings.titlePrefix + @pageTitle if @pageTitle

     _render: () ->
       data = @model?.toJSON() or @collection?.toJSON() or {}


Print more info on test error

Print more info on test error

--- src/spec/integration/products-sync.spec.coffee
+++ src/spec/integration/products-sync.spec.coffee
@@ -156,5 +156,5 @@
       expect(updated.variants[1].prices[0].value.centAmount).toBe 3000
       expect(updated.variants[1].prices[0].country).toBe 'FR'
       done()
-    .catch (error) -> done(_.prettify(error))
+    .catch (error) -> done(_.prettify(error.body))
   , 10000 # 10sec


fixbug

fixbug

--- core/plugin.coffee
+++ core/plugin.coffee
@@ -30,4 +30,4 @@
       throw err if err

       fs.unlink path, ->
-        callback
+        callback()


Tweak hint scoring for filtered hints.

Tweak hint scoring for filtered hints.

This tweaks the scoring of hints for filtered hints.

Specifically, it scores matches at the start of the text or the start of
a word higher than whole-word matches.

This is intended avoid the phenomenon whereby adding matching characters
to the filter causes the selected hint to change.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -571,9 +571,9 @@
               if position < 0
                 0 # No match.
               else if position == 0 and searchWord.length == linkWord.length
-                if idx == 0 then 8 else 6 # Whole-word match.
+                if idx == 0 then 8 else 4 # Whole-word match.
               else if position == 0
-                if idx == 0 then 4 else 2 # Match at the start of a word.
+                if idx == 0 then 6 else 2 # Match at the start of a word.
               else
                 1 # 0 < position; other match.
           Math.max linkWordScores...


Split filtered hints on non-word and hint characters.

This affects the scoring system for filtered link hits, and therefore
affects their usability.

Example link text:

    "We open all day", say 7Eleven

Previously:

    "We
    open
    all
    day",
    say
    7Eleven

With this PR:

    we
    open
    all
    day
    say
    Eleven

Previously: the typed text `we` and `day` would receive poor scores (not
the start of a word, and not a whole word.  Now, these get high scores,
so are more likely to be selected as the active link.  Also, `7Eleven`
cannot be typed (because `7` is a hint character).

With this PR, the typed text `we` `day` now get high scores, as they
should.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -479,6 +479,9 @@
     @linkTextKeystrokeQueue = []
     @labelMap = {}
     @activeHintMarker = null
+    # The regexp for splitting typed text and link texts.  We split on sequences of non-word characters and
+    # link-hint numbers.
+    @splitRegexp = new RegExp "[\\W#{Utils.escapeRegexSpecialCharacters @linkHintNumbers}]+"

   #
   # Generate a map of input element => label
@@ -602,10 +605,10 @@
   # Assign a score to a filter match (higher is better).  We assign a higher score for matches at the start of
   # a word, and a considerably higher score still for matches which are whole words.
   scoreLinkHint: (linkSearchString) ->
-    searchWords = linkSearchString.trim().split /\s+/
-    (linkMarker) ->
+    searchWords = linkSearchString.trim().split @splitRegexp
+    (linkMarker) =>
       text = linkMarker.linkText.trim()
-      linkWords = linkMarker.linkWords ?= text.toLowerCase().split /\s+/
+      linkWords = linkMarker.linkWords ?= text.toLowerCase().split @splitRegexp

       searchWordScores =
         for searchWord in searchWords


Tack on time query param to avoid cacheing cam images.

--- views/road.coffee
+++ views/road.coffee
@@ -18,6 +18,10 @@
                 for camera in @cameras
                     li class: 'ui-li ui-li-static ui-body-c', 'data-theme': 'c', ->
                         h3 class: 'ui-li-heading', -> camera.name
+                        if camera.url.indexOf('?') is 0
+                            param = "&t=#{Date.now()}"
+                        else
+                            param = "?t=#{Date.now()}"
                         # Paragraph avoids jQM adding a -has-thumb class to the
                         # li, which is not what is desired in this view.
-                        p -> img src: camera.url, alt: 'M.I.A.'
+                        p -> img src: "#{camera.url}#{param}", alt: 'M.I.A.'


Issue #1547 'use strict' octal literals prohibited

--- lexer.coffee
+++ lexer.coffee
@@ -134,6 +134,8 @@
     return 0 unless match = NUMBER.exec @chunk
     number = match[0]
     lexedLength = number.length
+    if octalLiteral = /^0[0-7]+$/.test number
+      @error "octal literals \"#{number}\" are not allowed"
     if binaryLiteral = /0b([01]+)/i.exec number
       number = (parseInt binaryLiteral[1], 2).toString()
     @token 'NUMBER', number


Kernel: Remove hardcoded ipython kernel

Kernel: Remove hardcoded ipython kernel

--- lib/kernel.coffee
+++ lib/kernel.coffee
@@ -15,7 +15,7 @@
         console.log 'Kernel spec:', @kernelSpec
         console.log 'Kernel configuration:', @config
         console.log 'Kernel configuration file path:', @configPath
-        @language = @kernelSpec.grammarLanguage
+        @language = @kernelSpec.language
         @kernelName = @kernelSpec.display_name
         @executionCallbacks = {}
         @watchCallbacks = []
@@ -32,27 +32,13 @@
             atom.notifications.addInfo 'Using custom kernel connection:',
                 detail: @configPath
         else
-            if @language is 'python' and not @kernelSpec.argv?
-                commandString = 'ipython'
-                args = [
-                    'kernel',
-                    '--no-secure',
-                    "--hb=#{@config.hb_port}",
-                    "--control=#{@config.control_port}",
-                    "--shell=#{@config.shell_port}",
-                    "--stdin=#{@config.stdin_port}",
-                    "--iopub=#{@config.iopub_port}",
-                    '--colors=NoColor'
-                    ]
-
-            else
-                commandString = _.head(@kernelSpec.argv)
-                args = _.tail(@kernelSpec.argv)
-                args = _.map args, (arg) =>
-                    if arg is '{connection_file}'
-                        return @configPath
-                    else
-                        return arg
+            commandString = _.head(@kernelSpec.argv)
+            args = _.tail(@kernelSpec.argv)
+            args = _.map args, (arg) =>
+                if arg is '{connection_file}'
+                    return @configPath
+                else
+                    return arg

             console.log 'Kernel: Spawning:', commandString, args
             @kernelProcess = child_process.spawn commandString, args,


Retrieving video information from YouTube using https instead of http.

--- common/lib/xmodule/xmodule/js/src/video/display.coffee
+++ common/lib/xmodule/xmodule/js/src/video/display.coffee
@@ -44,7 +44,7 @@
   fetchMetadata: (url) ->
     @metadata = {}
     $.each @videos, (speed, url) =>
-      $.get "http://gdata.youtube.com/feeds/api/videos/#{url}?v=2&alt=jsonc", ((data) => @metadata[data.data.id] = data.data) , 'jsonp'
+      $.get "https://gdata.youtube.com/feeds/api/videos/#{url}?v=2&alt=jsonc", ((data) => @metadata[data.data.id] = data.data) , 'jsonp'

   getDuration: ->
     @metadata[@youtubeId()].duration


Add a deprecation message in the Linter class.

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -2,6 +2,7 @@
 path = require 'path'
 {CompositeDisposable, Range, Point, BufferedProcess} = require 'atom'
 XRegExp = null
+Grim = require('grim')

 # These are NOOPs in linter-plus
 log = -> undefined
@@ -56,6 +57,10 @@

   # Public: Construct a linter passing it's base editor
   constructor: (@editor) ->
+    Grim.deprecate('AtomLinter v0.X.Y API has been deprecated.
+      Please refer to the Linter docs to update and the latest API:
+      https://github.com/AtomLinter/Linter/wiki/Migrating-to-the-new-API')
+
     @cwd = path.dirname(@editor.getPath())

     @subscriptions = new CompositeDisposable


check if config and project config is defined.

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -12,8 +12,8 @@

   lint: (textEditor) ->
     return new Promise (resolve, reject) =>
-      return resolve [] unless @manager.config.config.plugins.lint?.active
-      return resolve [] unless @manager.server and @manager.config.config
+      return resolve [] unless @manager.config?.config?.plugins.lint?.active
+      return resolve [] unless @manager.server

       messages = []


return empty array if not tern-lint in .tern-project

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -13,9 +13,9 @@
     super(editor)

   lintFile: (filePath, callback) ->
-    console.log @_manager
-    return unless @_manager.server and @_manager.useLint and @_manager.config.config
-    return unless @_manager.config.config.plugins.lint?.active
+    messages = []
+    return callback(messages) unless @_manager.server and @_manager.useLint and @_manager.config.config
+    return callback(messages) unless @_manager.config.config.plugins.lint?.active
     editor = atom.workspace.getActiveTextEditor()
     buffer = editor.getBuffer()
     URI = editor.getURI()
@@ -25,7 +25,6 @@
         return unless data.messages
         editor = atom.workspace.getActiveTextEditor()
         buffer = editor.getBuffer()
-        messages = []
         for message in data.messages
           positionFrom = buffer.positionForCharacterIndex(message.from)
           positionTo = buffer.positionForCharacterIndex(message.to)


Make it clearer that the cache is of metadata.

--- lib/loader.coffee
+++ lib/loader.coffee
@@ -43,10 +43,10 @@
     (r['lineage_']= ((base['lineage_']||[])).slice(0)).push(name)
   r

-cache= {}
+metacache= {}

 meta_lookup= (name,from)->
-  from||= cache
+  from||= metacache
   descend= (name)->
     if from[name]
       return from[name]
@@ -66,7 +66,7 @@
   loadDir= (dirname,superMeta,visited) ->   #recursive
     defaultModName = path.join(dirname,'_default.meta')
     meta= if isModuleSync(defaultModName)
-      cache[dirname+'/']= load(defaultModName,superMeta)
+      metacache[dirname+'/']= load(defaultModName,superMeta)
     else
       superMeta
     for filename in fs.readdirSync(dirname)
@@ -83,13 +83,13 @@
           if moduleSignature.test(filename)
             stripped= stripExtension(filename)
             if metafileSignature.test(filename)
-              cache[filename.replace(stripMetaExtension,'$1')]= load(stripped,meta)
+              metacache[filename.replace(stripMetaExtension,'$1')]= load(stripped,meta)
             else
               modules.unshift(stripped)
     return meta
   visited= {}
   visited[fs.lstatSync(rootdir).ino]= true
-  cache[rootdir+'/']= superMeta  # this will be overridden if _default_meta found
+  metacache[rootdir+'/']= superMeta  # this will be overridden if _default_meta found
   meta= loadDir(rootdir,superMeta,visited)
   preload(modules,meta,supersites)
   return meta
@@ -102,7 +102,7 @@
     m= require(modname)
     if m?.meta
       itsMeta= meta_lookup(modname)
-      cache[modname]= extend(itsMeta,m.meta,modname)
+      metacache[modname]= extend(itsMeta,m.meta,modname)
     if sitesModuleSignature.test(modname)
       siteroot= path.dirname(modname)
       siteLookupRoot ?= siteroot


fix logging level output and warn level

fix logging level output and warn level

--- lib/logger.coffee
+++ lib/logger.coffee
@@ -3,7 +3,7 @@
   constructor: (name) ->
     @levels =
       'ERROR': 40
-      'WARNING': 30
+      'WARN': 30
       'INFO': 20
       'DEBUG': 10
     @name = name
@@ -30,12 +30,12 @@
     origLine = @originalLine()
     if origLine[0]?
       msg = '[' + origLine[0] + ':' + origLine[1] + '] ' + msg
-    msg = '[' + @level + '] ' + msg
+    msg = '[' + level + '] ' + msg
     msg = '[' + @name + '] ' + msg
     switch level
       when 'DEBUG' then console.log msg
       when 'INFO' then console.log msg
-      when 'WARNING' then console.warn msg
+      when 'WARN' then console.warn msg
       when 'ERROR' then console.error msg

   originalLine: () ->


points awarded on alien kill, not bullet hitting alien


RootView talks about packages and packageModules, not extensions

Now you call `rootView.activatePackage`, etc

--- spec/markdown-preview-spec.coffee
+++ spec/markdown-preview-spec.coffee
@@ -7,7 +7,7 @@

   beforeEach ->
     rootView = new RootView(require.resolve('fixtures/markdown'))
-    rootView.activateExtension(MarkdownPreview)
+    atom.loadPackage("markdown-preview")
     markdownPreview = MarkdownPreview.instance
     rootView.attachToDom()


:art: Update message-registry specs to include specs about deactivated linters

--- spec/message-registry-spec.coffee
+++ spec/message-registry-spec.coffee
@@ -7,6 +7,8 @@
     size = 0
     size++ for value of obj
     return size
+  getMessage = (type, filePath) ->
+    return {type, text: "Some Message", filePath}
   getLinterRegistry = ->
     linterRegistry = new LinterRegistry
     editorLinter = new EditorLinter(atom.workspace.getActiveTextEditor())
@@ -18,7 +20,7 @@
       lint: -> return [{type: "Error", text: "Something"}]
     }
     linterRegistry.addLinter(linter)
-    return {linterRegistry, editorLinter}
+    return {linterRegistry, editorLinter, linter}

   beforeEach ->
     waitsForPromise ->
@@ -39,6 +41,19 @@
         linterRegistry.lint({onChange: false, editorLinter}).then ->
           expect(wasUpdated).toBe(true)
           linterRegistry.deactivate()
+    it 'ignores deactivated linters', ->
+      {linterRegistry, editorLinter, linter} = getLinterRegistry()
+      messageRegistry.set({linter, messages: [getMessage('Error'), getMessage('Warning')]})
+      messageRegistry.updatePublic()
+      expect(messageRegistry.publicMessages.length).toBe(2)
+      linter.deactivated = true
+      messageRegistry.set({linter, messages: [getMessage('Error')]})
+      messageRegistry.updatePublic()
+      expect(messageRegistry.publicMessages.length).toBe(2)
+      linter.deactivated = false
+      messageRegistry.set({linter, messages: [getMessage('Error')]})
+      messageRegistry.updatePublic()
+      expect(messageRegistry.publicMessages.length).toBe(1)

   describe '::onDidUpdateMessages', ->
     it 'is triggered asyncly with results', ->


:white_check_mark: #438 describe goalColumn respect

--- spec/visual-blockwise-spec.coffee
+++ spec/visual-blockwise-spec.coffee
@@ -404,57 +404,112 @@
       it 'case-7', -> set cursor: [5, 0]; ensureCharacterwiseWasRestored('v 5 l 3 k')

   describe "keep goalColumn", ->
-    beforeEach ->
-      set
-        text: """
-        012345678
-
-        ABCDEFGHI\n
-        """
-
-    it "when [reversed = false, headReversed = false]", ->
-      set cursor: [0, 3]
-      ensure "ctrl-v l l l", cursor: [[0, 7]], selectedTextOrdered: ["3456"]
-      ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: false
-
-      ensure "j", cursor: [[0, 0], [1, 0]], selectedTextOrdered: ["0123", ""]
-      ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: true
-
-      ensure "j", cursor: [[0, 7], [1, 0], [2, 7]], selectedTextOrdered: ["3456", "", "DEFG"]
-      ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: false
-
-    it "when [reversed = true, headReversed = true]", ->
-      set cursor: [2, 6]
-      ensure "ctrl-v h h h", cursor: [[2, 3]], selectedTextOrdered: ["DEFG"]
-      ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
-
-      ensure "k", cursor: [[1, 0], [2, 0]], selectedTextOrdered: ["", "ABCDEFG"]
-      ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
-
-      ensure "k", cursor: [[0, 3], [1, 0], [2, 3]], selectedTextOrdered: ["3456", "", "DEFG"]
-      ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
-
-    it "when [reversed = false, headReversed = true]", ->
-      set cursor: [0, 6]
-      ensure "ctrl-v h h h", cursor: [[0, 3]], selectedTextOrdered: ["3456"]
-      ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: true, headReversed: true
-
-      ensure "j", cursor: [[0, 0], [1, 0]], selectedTextOrdered: ["0123456", ""]
-      ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: true
-
-      ensure "j", cursor: [[0, 3], [1, 0], [2, 3]], selectedTextOrdered: ["3456", "", "DEFG"]
-      ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: true
-
-    it "when [reversed = true, headReversed = false]", ->
-      set cursor: [2, 3]
-      ensure "ctrl-v l l l", cursor: [[2, 7]], selectedTextOrdered: ["DEFG"]
-      ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: false, headReversed: false
-
-      ensure "k", cursor: [[1, 0], [2, 0]], selectedTextOrdered: ["", "ABCD"]
-      ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
-
-      ensure "k", cursor: [[0, 7], [1, 0], [2, 7]], selectedTextOrdered:  ["3456", "", "DEFG"]
-      ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: false
+    describe "when passing through blank row", ->
+      beforeEach ->
+        set
+          text: """
+          012345678
+
+          ABCDEFGHI\n
+          """
+
+      it "when [reversed = false, headReversed = false]", ->
+        set cursor: [0, 3]
+        ensure "ctrl-v l l l", cursor: [[0, 7]], selectedTextOrdered: ["3456"]
+        ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: false
+
+        ensure "j", cursor: [[0, 0], [1, 0]], selectedTextOrdered: ["0123", ""]
+        ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: true
+
+        ensure "j", cursor: [[0, 7], [1, 0], [2, 7]], selectedTextOrdered: ["3456", "", "DEFG"]
+        ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: false
+
+      it "when [reversed = true, headReversed = true]", ->
+        set cursor: [2, 6]
+        ensure "ctrl-v h h h", cursor: [[2, 3]], selectedTextOrdered: ["DEFG"]
+        ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
+
+        ensure "k", cursor: [[1, 0], [2, 0]], selectedTextOrdered: ["", "ABCDEFG"]
+        ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
+
+        ensure "k", cursor: [[0, 3], [1, 0], [2, 3]], selectedTextOrdered: ["3456", "", "DEFG"]
+        ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
+
+      it "when [reversed = false, headReversed = true]", ->
+        set cursor: [0, 6]
+        ensure "ctrl-v h h h", cursor: [[0, 3]], selectedTextOrdered: ["3456"]
+        ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: true, headReversed: true
+
+        ensure "j", cursor: [[0, 0], [1, 0]], selectedTextOrdered: ["0123456", ""]
+        ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: true
+
+        ensure "j", cursor: [[0, 3], [1, 0], [2, 3]], selectedTextOrdered: ["3456", "", "DEFG"]
+        ensureBlockwiseSelection head: 'bottom', tail: 'top', reversed: false, headReversed: true
+
+      it "when [reversed = true, headReversed = false]", ->
+        set cursor: [2, 3]
+        ensure "ctrl-v l l l", cursor: [[2, 7]], selectedTextOrdered: ["DEFG"]
+        ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: false, headReversed: false
+
+        ensure "k", cursor: [[1, 0], [2, 0]], selectedTextOrdered: ["", "ABCD"]
+        ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: true
+
+        ensure "k", cursor: [[0, 7], [1, 0], [2, 7]], selectedTextOrdered:  ["3456", "", "DEFG"]
+        ensureBlockwiseSelection head: 'top', tail: 'bottom', reversed: true, headReversed: false
+
+    describe "when head cursor position is less than original goal column", ->
+      beforeEach ->
+        set
+          text: """
+          012345678901234567890123
+                 xxx01234
+          012345678901234567890123\n
+          """
+
+      describe "[tailColumn < headColum], goalColumn isnt Infinity", ->
+        it "shrinks block till head column by keeping goalColumn", ->
+          set cursor: [0, 10] # j, k motion keep goalColumn so starting `10` column means goalColumn is 10.
+          ensure "ctrl-v 1 0 l", selectedTextOrdered: ["01234567890"], cursor: [[0, 21]]
+          ensure "j", selectedTextOrdered: ["012345", "01234"], cursor: [[0, 16], [1, 15]]
+          ensure "j", selectedTextOrdered: ["01234567890", "01234", "01234567890"], cursor: [[0, 21], [1, 15], [2, 21]]
+        it "shrinks block till head column by keeping goalColumn", ->
+          set cursor: [2, 10]
+          ensure "ctrl-v 1 0 l", selectedTextOrdered: ["01234567890"], cursor: [[2, 21]]
+          ensure "k", selectedTextOrdered: ["01234", "012345"], cursor: [[1, 15], [2, 16]]
+          ensure "k", selectedTextOrdered: ["01234567890", "01234", "01234567890"], cursor: [[0, 21], [1, 15], [2, 21]]
+      describe "[tailColumn < headColum], goalColumn is Infinity", ->
+        it "keep each member selection selected till end-of-line( No shrink )", ->
+          set cursor: [0, 10] # $ motion set goalColumn to Infinity
+          ensure "ctrl-v $", selectedTextOrdered: ["01234567890123"], cursor: [[0, 24]]
+          ensure "j", selectedTextOrdered: ["01234567890123", "01234"], cursor: [[0, 24], [1, 15]]
+          ensure "j", selectedTextOrdered: ["01234567890123", "01234", "01234567890123"], cursor: [[0, 24], [1, 15], [2, 24]]
+        it "keep each member selection selected till end-of-line( No shrink )", ->
+          set cursor: [2, 10]
+          ensure "ctrl-v $", selectedTextOrdered: ["01234567890123"], cursor: [[2, 24]]
+          ensure "k", selectedTextOrdered: ["01234", "01234567890123"], cursor: [[1, 15], [2, 24]]
+          ensure "k", selectedTextOrdered: ["01234567890123", "01234", "01234567890123"], cursor: [[0, 24], [1, 15], [2, 24]]
+      describe "[tailColumn > headColum], goalColumn isnt Infinity", ->
+        it "Respect actual head column over goalColumn", ->
+          set cursor: [0, 20] # j, k motion keep goalColumn so starting `10` column means goalColumn is 10.
+          ensure "ctrl-v l l", selectedTextOrdered: ["012"], cursor: [[0, 23]]
+          ensure "j", selectedTextOrdered: ["567890", ""], cursor: [[0, 15], [1, 15]]
+          ensure "j", selectedTextOrdered: ["012", "", "012"], cursor: [[0, 23], [1, 15], [2, 23]]
+        it "Respect actual head column over goalColumn", ->
+          set cursor: [2, 20] # j, k motion keep goalColumn so starting `10` column means goalColumn is 10.
+          ensure "ctrl-v l l", selectedTextOrdered: ["012"], cursor: [[2, 23]]
+          ensure "k", selectedTextOrdered: ["", "567890"], cursor: [[1, 15], [2, 15]]
+          ensure "k", selectedTextOrdered: ["012", "", "012"], cursor: [[0, 23], [1, 15], [2, 23]]
+      describe "[tailColumn > headColum], goalColumn is Infinity", ->
+        it "Respect actual head column over goalColumn", ->
+          set cursor: [0, 20] # j, k motion keep goalColumn so starting `10` column means goalColumn is 10.
+          ensure "ctrl-v $", selectedTextOrdered: ["0123"], cursor: [[0, 24]]
+          ensure "j", selectedTextOrdered: ["567890", ""], cursor: [[0, 15], [1, 15]]
+          ensure "j", selectedTextOrdered: ["0123", "", "0123"], cursor: [[0, 24], [1, 15], [2, 24]]
+        it "Respect actual head column over goalColumn", ->
+          set cursor: [2, 20] # j, k motion keep goalColumn so starting `10` column means goalColumn is 10.
+          ensure "ctrl-v $", selectedTextOrdered: ["0123"], cursor: [[2, 24]]
+          ensure "k", selectedTextOrdered: ["", "567890"], cursor: [[1, 15], [2, 15]]
+          ensure "k", selectedTextOrdered: ["0123", "", "0123"], cursor: [[0, 24], [1, 15], [2, 24]]

   # [FIXME] not appropriate put here, re-consider all spec file layout later.
   describe "gv feature", ->