updated help headers

updated help headers

--- src/hubot-tapsaff.coffee
+++ src/hubot-tapsaff.coffee
@@ -1,13 +1,12 @@
 # Description:
-#   hubot-tapsaff
-#
-# Configuration:
-#   HUBOT_TAPSAFF_TOON - optional default location for lookups
+#   Find out if its tapsaff weather in your location.
 #
 # Commands:
 #   tapsaff           - get the tapsaff status for the default location (HUBOT_TAPSAFF_TOON)
 #   tapsaff in <toon> - get the tapsaff status for the specified location
 #
+# Configuration:
+#   HUBOT_TAPSAFF_TOON - optional default location for lookups

 module.exports = (robot) ->
   robot.respond /tapsaff in (\w+)/i, (msg) ->


Dickbutt

Dickbutt

--- scripts/dees.coffee
+++ scripts/dees.coffee
@@ -2,7 +2,7 @@

 module.exports = (robot) ->
   robot.enter (msg) ->
-    msg.send inspect(msg)
+    msg.send require('util').inspect(msg)
     # If its dusya optionally follwed by ANYTHING
     # if /^dusya/.test(msg.user.name.toLowerCase())
     #  msg.send "YOUR NUTS"


hear regex adjusted to match respond regex indexes


Whitespace cleanup.

Whitespace cleanup.

--- scripts/loud.coffee
+++ scripts/loud.coffee
@@ -38,7 +38,7 @@
     action = res.match[1].trim()
     data = res.match[2]?.trim()

-    switch action
+    switch action
       when 'delete'
         index = robot.brain.get('louds').indexOf(data)
         if index != -1


Only work for koenbok/framer

Only work for koenbok/framer

--- scripts/site.coffee
+++ scripts/site.coffee
@@ -17,15 +17,17 @@
 	gzipExtensions: [".js", ".html", ".css", ".map"]
 	startAt: "2e97990"

-client = knox.createClient
-	key: process.env.AWS_ACCESS_KEY_ID
-	secret: process.env.AWS_SECRET_ACCESS_KEY
-	bucket: Config.bucket
-

 ###############################################################
 # Parse the command line
 main = ->
+	exec "git config --local --get remote.origin.url", (err, output) ->
+		output = output.trim()
+		if output != "git@github.com:koenbok/framer.git"
+			throw Error("Not the right repo!", output)
+		_main()
+
+_main = ->

 	COMMANDS =
 		"upload": -> uploadDir Config.output
@@ -55,6 +57,8 @@
 	exec "git log --pretty=format:\"%h\t%an\t%ad\t%s\" --first-parent master", (err, output) ->
 		throw err if err

+		console.log output
+
 		start = false

 		for line in output.split "\n"
@@ -77,6 +81,11 @@
 uploadFile = (path, remotePath="") ->

 	console.log "Upload #{path} -> #{remotePath}"
+
+	client = knox.createClient
+		key: process.env.AWS_ACCESS_KEY_ID
+		secret: process.env.AWS_SECRET_ACCESS_KEY
+		bucket: Config.bucket

 	buffer = fs.readFileSync path
 	headers =


hint for disabled keymap

hint for disabled keymap

--- keymaps/vim-mode-plus.cson
+++ keymaps/vim-mode-plus.cson
@@ -204,7 +204,7 @@

   # 'g s': 'vim-mode-plus:move-to-next-string'
   # 'g S': 'vim-mode-plus:move-to-previous-string'
-
+
   # 'g n': 'vim-mode-plus:move-to-next-number'
   # 'g N': 'vim-mode-plus:move-to-previous-number'

@@ -245,6 +245,7 @@
   # 'c s': 'vim-mode-plus:change-surround'
   # 'd s': 'vim-mode-plus:delete-surround-any-pair'
   # 'c s': 'vim-mode-plus:change-surround-any-pair'
+  # 'y m s':  'vim-mode-plus:map-surround'

 # operator-pending, visual
 # -------------------------
@@ -340,6 +341,8 @@
   'v':      'vim-mode-plus:activate-characterwise-visual-mode'
   'V':      'vim-mode-plus:activate-linewise-visual-mode'
   'ctrl-v': 'vim-mode-plus:activate-blockwise-visual-mode'
+  # 'ctrl-k': 'vim-mode-plus:move-line-up'
+  # 'ctrl-j': 'vim-mode-plus:move-line-down'

 # visual-blockwise
 # -------------------------


updates slightly pop-ups

updates slightly pop-ups

--- components/new_user_messages/index.coffee
+++ components/new_user_messages/index.coffee
@@ -10,13 +10,13 @@
     {
       id: 'help_message'
       title: "Welcome"
-      body: "If you need help check the FAQs or contact us. Also, feel free to give us any suggestions or report bugs in our feedback channel"
+      body: "If you need help check the FAQs. Also feel free to give us suggestions or report bugs in our feedback channel"
       type: 'announcement'
     },
     {
       id: 'channel_message'
       title: "Channels"
-      body: "Create a new channel, or explore existing ones"
+      body: "Create a new channel, explore recent activity or search for existing ones."
       type: 'announcement'
     },
     {


handleLinkClick

handleLinkClick

--- django/bb_chat/assets/scripts/views.coffee
+++ django/bb_chat/assets/scripts/views.coffee
@@ -98,18 +98,13 @@

 class AppView extends Backbone.View
   template: compileTemplate 'app'
+  events:
+    'click a[href]': 'handleLinkClick'

   initialize: (options) ->
     @mainView = null
     @navigationView = new NavigationView
     @render()
-    @$el.on 'click', 'a[href]', (event) ->
-      href = this.getAttribute 'href'
-      root = Backbone.history.root
-      if href.startsWith root
-        event.preventDefault()
-        Backbone.history.navigate href.substr(root.length), trigger: true
-      return

     router = options.router
     @listenTo router, 'route:conversation', @showConversation
@@ -125,6 +120,14 @@
       .render()
     this

+  handleLinkClick: (event) ->
+    href = event.target.getAttribute 'href'
+    root = Backbone.history.root
+    if href.startsWith root
+      event.preventDefault()
+      Backbone.history.navigate href.substr(root.length), trigger: true
+    return
+
   showConversation: (id) =>
     @mainView = new ChatView id: id
       .setElement @.$('.main')


Fixed issue with executables without extension causing all extensions to match

--- src/program-family.coffee
+++ src/program-family.coffee
@@ -7,14 +7,15 @@
 { attemptShell } = require './utils'

 module.exports = class ProgramFamily
-    @OPTIMIZED_PROGRAMS_SUFFIX: "-opt*"
+    @OPTIMIZED_PROGRAMS_SUFFIX: "-opt"

     guessOthers = ({ name, execExtension, srcExtension }, last) =>
-        executablesPattern = "#{name}#{@OPTIMIZED_PROGRAMS_SUFFIX}#{execExtension}"
-        executables = attemptShell("ls", executablesPattern)
-        sourcesPattern = "#{name}#{@OPTIMIZED_PROGRAMS_SUFFIX}#{srcExtension}"
+        pattern = "^#{name}#{@OPTIMIZED_PROGRAMS_SUFFIX}((?!\\.).)*"
+        executablesPattern = new RegExp(pattern + "#{execExtension.replace(".", "\\.")}$")
+        executables = attemptShell("ls").filter((s) -> s.match(executablesPattern))
+        sourcesPattern = new RegExp(pattern + "#{srcExtension.replace(".", "\\.")}$")
         sources =
-            for source in attemptShell("ls", sourcesPattern)
+            for source in attemptShell("ls").filter((s) -> s.match(sourcesPattern))
                 { dir, name } = path.parse source
                 "#{path.join(dir, name)}#{execExtension}"

@@ -39,7 +40,7 @@

         assert(@original instanceof Program, "original parameter is not a program nor string")

-        if @others.length is 0
+        if not @others? or @others.length is 0
             @others = guessOthers(@original, last)
         else
             @others[i] = new Program(v) for v, i in @others


Revert "remove unused code, tweak active node styling."

This reverts commit bcb1e338043f3202c891ffa20484b999d3577732.

--- scripts/bungie-data-helper.coffee
+++ scripts/bungie-data-helper.coffee
@@ -1,11 +1,11 @@
 request = require('request')

 class DataHelper
-  # 'fetchDefs': ->
-  #   @fetchStatDefs (error, response, body) =>
-  #     @statDefs = JSON.parse(body)
-  #   @fetchVendorDefs (error, response, body) =>
-  #     @vendorDefs = JSON.parse(body)
+  'fetchDefs': ->
+    @fetchStatDefs (error, response, body) =>
+      @statDefs = JSON.parse(body)
+    @fetchVendorDefs (error, response, body) =>
+      @vendorDefs = JSON.parse(body)

   'serializeFromApi': (response) ->
     damageColor =
@@ -33,8 +33,8 @@
     nodeDefs: response.definitions.talentGrids[item.talentGridHash].nodes
     damageType: damageTypeName

-  # 'parseItemsForAttachment': (items) ->
-  #   items.map (item) => @parseItemAttachment(item)
+  'parseItemsForAttachment': (items) ->
+    items.map (item) => @parseItemAttachment(item)

   'parseItemAttachment': (item) ->
     name = "#{item.itemName}"
@@ -76,16 +76,16 @@
     return orderedNodes

   # creates fields for perks and their descriptions
-  # 'buildFields': (nodes, nodeDefs) ->
-  #   displayNodes = nodes.map (node) ->
-  #     step = nodeDefs[node.nodeIndex].steps[node.stepIndex]
-  #     description = step.nodeStepDescription.replace(/(\r\n|\n|\r)/gm," ").replace("  "," ")
-  #
-  #     title: step.nodeStepName
-  #     value: description
-  #     short: true
-  #
-  #   displayNodes.filter (x) -> x
+  'buildFields': (nodes, nodeDefs) ->
+    displayNodes = nodes.map (node) ->
+      step = nodeDefs[node.nodeIndex].steps[node.stepIndex]
+      description = step.nodeStepDescription.replace(/(\r\n|\n|\r)/gm," ").replace("  "," ")
+
+      title: step.nodeStepName
+      value: description
+      short: true
+
+    displayNodes.filter (x) -> x

   'buildText': (nodes, nodeDefs) ->
     getName = (node) ->
@@ -98,27 +98,27 @@
       column = nodeDefs[node.nodeIndex].column
       name = step.nodeStepName
       if node.isActivated
-        name = "*#{step.nodeStepName}*"
+        name = '_*' + step.nodeStepName + '*_'
       text[column] = "" unless text[column]
       text[column]+= name + " | "

     setText node for node in nodes
     return text

-  # 'fetchVendorDefs': (callback) ->
-  #   options =
-  #     method: 'GET'
-  #     url: 'http://destiny.plumbing/raw/mobileWorldContent/en/DestinyStatDefinition.json'
-  #     gzip: true
-  #
-  #   request(options, callback)
-  #
-  # 'fetchStatDefs': (callback) ->
-  #   options =
-  #     method: 'GET'
-  #     url: 'http://destiny.plumbing/raw/mobileWorldContent/en/DestinyStatDefinition.json'
-  #     gzip: true
-  #
-  #   request(options, callback)
+  'fetchVendorDefs': (callback) ->
+    options =
+      method: 'GET'
+      url: 'http://destiny.plumbing/raw/mobileWorldContent/en/DestinyStatDefinition.json'
+      gzip: true
+
+    request(options, callback)
+
+  'fetchStatDefs': (callback) ->
+    options =
+      method: 'GET'
+      url: 'http://destiny.plumbing/raw/mobileWorldContent/en/DestinyStatDefinition.json'
+      gzip: true
+
+    request(options, callback)

 module.exports = DataHelper


UserImporter: add super-admin flags for all imported user in local development

--- scripts/user-importer/main.coffee
+++ scripts/user-importer/main.coffee
@@ -48,7 +48,11 @@

             return next()

-          account.update $set: type: 'registered', (err)->
+          account.update {
+            $set          :
+              type        : 'registered'
+              globalFlags : ['super-admin']
+          }, (err)->

             if err?
               console.log "  Failed to activate #{u.username}:", err


use core.argumentJoiner

use core.argumentJoiner

--- test/common/kozu/transformers_test.coffee
+++ test/common/kozu/transformers_test.coffee
@@ -5,10 +5,6 @@
 plus1 = (n) -> n+1
 times2 = (n) -> n*2
 exclaim = (x) -> "#{x}!"
-argumentJoiner = core.compose(
-  core.gathersArguments,
-  core.partial(core.partialRest, core.functionalize(Array::join))
-);

 describe "kozu.transformers.arrayTransformer(functionSchemaArray)", ->
   it "returns a function which takes an array and maps its items with corresponding functions", ->
@@ -29,13 +25,13 @@
 describe "kozu.transformers.objectTemplate(functionSchemaObject)", ->
   it "returns a function which applies function values of the given schema to its argument", ->
     tmpl = transformers.objectTemplate
-      name: core.extractsKeys('first', 'last')(argumentJoiner(' '))
+      name: core.extractsKeys('first', 'last')(core.argumentJoiner(' '))
     expect(tmpl(first: "Larry", last: "Zozo")).to.deep.equal(name: "Larry Zozo")
   it "treats non-function values as constants which end up untouched in the output", ->
     tmpl = transformers.objectTemplate
       type: "person"
       nothing: null
-      name: core.extractsKeys('first', 'last')(argumentJoiner(' '))
+      name: core.extractsKeys('first', 'last')(core.argumentJoiner(' '))
     expect(tmpl(first: "Larry", last: "Zozo")).to.deep.equal(type: "person", nothing: null, name: "Larry Zozo")

 describe "kozu.transformers.objectPropertyTemplate(functionSchemaObject)", ->


Additional tests for nested transactions.

--- test/integration/transactions-test.coffee
+++ test/integration/transactions-test.coffee
@@ -42,6 +42,31 @@

     @connection.execSqlBatch(request)

+  createProc: (callback) =>
+    request = new Request('''
+      CREATE PROCEDURE #proc
+      AS
+        SET NOCOUNT ON;
+
+        begin transaction
+        insert into #temp (id) values(1)
+        commit transaction
+      GO'''
+    , (err) =>
+      @test.ok(!err)
+      callback(err)
+    )
+
+    @connection.execSqlBatch(request)
+
+  execProc: (callback) =>
+    request = new Request('exec #proc', (err) =>
+      @test.ok(!err)
+      callback(err)
+    )
+
+    @connection.execSqlBatch(request)
+
   insert: (callback) =>
     request = new Request('insert into #temp (id) values(1)', (err) =>
       @test.ok(!err)
@@ -69,13 +94,19 @@
   selectExpectOneRow: (callback) =>
     @select(callback, 1)

-  beginTransaction: (callback) =>
+  beginTransaction: (callback, transactionName) =>
     @connection.beginTransaction((err, transactionDescriptor) =>
       @test.ok(!err)
       @test.ok(transactionDescriptor)

       callback(err)
-    , 'abc')
+    , transactionName)
+
+  beginTransaction1: (callback) =>
+    @beginTransaction(callback, 'one')
+
+  beginTransaction2: (callback) =>
+    @beginTransaction(callback, 'two')

   commitTransaction: (callback) =>
     @connection.commitTransaction((err) =>
@@ -99,28 +130,86 @@
       async.series(actions)
     )

-exports.beginCommit = (test) ->
+exports.transactionCommit = (test) ->
   test.expect(8)

   tester = new Tester(test)
   tester.run([
     tester.createTable
-    tester.beginTransaction
+    tester.beginTransaction1
     tester.insert
     tester.commitTransaction
     tester.selectExpectOneRow
     tester.close
   ])

-exports.beginRollback = (test) ->
+exports.transactionRollback = (test) ->
   test.expect(7)

   tester = new Tester(test)
   tester.run([
     tester.createTable
-    tester.beginTransaction
+    tester.beginTransaction1
     tester.insert
     tester.rollbackTransaction
     tester.selectExpectZeroRows
     tester.close
   ])
+
+exports.nestedTransactionCommit = (test) ->
+  test.expect(11)
+
+  tester = new Tester(test)
+  tester.run([
+    tester.createTable
+    tester.beginTransaction1
+    tester.beginTransaction2
+    tester.insert
+    tester.commitTransaction
+    tester.commitTransaction
+    tester.selectExpectOneRow
+    tester.close
+  ])
+
+exports.nestedTransactionRollbackOuter = (test) ->
+  test.expect(10)
+
+  tester = new Tester(test)
+  tester.run([
+    tester.createTable
+    tester.beginTransaction1
+    tester.beginTransaction2
+    tester.insert
+    tester.commitTransaction
+    tester.rollbackTransaction
+    tester.selectExpectZeroRows
+    tester.close
+  ])
+
+exports.nestedTransactionInProcCommit = (test) ->
+  test.expect(9)
+
+  tester = new Tester(test)
+  tester.run([
+    tester.createTable
+    tester.createProc
+    tester.beginTransaction1
+    tester.execProc
+    tester.commitTransaction
+    tester.selectExpectOneRow
+    tester.close
+  ])
+
+exports.nestedTransactionInProcRollbackOuter = (test) ->
+  test.expect(8)
+
+  tester = new Tester(test)
+  tester.run([
+    tester.createTable
+    tester.createProc
+    tester.beginTransaction1
+    tester.execProc
+    tester.rollbackTransaction
+    tester.selectExpectZeroRows
+    tester.close
+  ])


Indent all the things

Indent all the things

--- src/scripts/sheits.coffee
+++ src/scripts/sheits.coffee
@@ -1,18 +1,17 @@
 # Description:
-# When you get some bad news sometimes you got to let it out.
+#   When you get some bad news sometimes you got to let it out.
 #
 # Dependencies:
-# None
+#   None
 #
 # Configuration:
-# None
+#   None
 #
 # Commands:
-# sheeeit - Display an image or animated gif
+#   sheeeit - Display an image or animated gif
 #
 # Author:
-# danishkhan
-#
+#   danishkhan

 sheits = [
   "http://www.circlenoir.com/forums/attachment.php?attachmentid=478&stc=1#.jpg",


Return when an error is encountered

Return when an error is encountered

--- src/scripts/trello.coffee
+++ src/scripts/trello.coffee
@@ -37,4 +37,5 @@
   t.post "/1/cards", {name: cardName, idList: process.env.HUBOT_TRELLO_LIST}, (err, data) ->
     if err
       msg.send "There was an error creating the card"
+      return
     msg.send data.url


s/definition/meaning

s/definition/meaning

The markup on urbandictionary.com has changed so this needs an update.

--- src/scripts/whatis.coffee
+++ src/scripts/whatis.coffee
@@ -33,7 +33,7 @@
         $ = require('jquery').create(window)

         definitions = []
-        $(".definition").each (idx, item) ->
+        $(".meaning").each (idx, item) ->
           definitions.push $(item).text()

         msgText = if definitions.length==0 then "No definition found." else (msg.random definitions)


Enforce stricter requirements for Editor.getPane()

Previously it would return the closest '.pane' element
which would cause an editor inside of another editor
to report having a pane such as in the autocomplete view.

--- lib/bracket-matcher.coffee
+++ lib/bracket-matcher.coffee
@@ -41,7 +41,7 @@

   goToMatchingPair: (editor) ->
     return unless @pairHighlighted
-    return unless underlayer = editor.pane()?.find('.underlayer')
+    return unless underlayer = editor.getPane()?.find('.underlayer')

     position = editor.getCursorBufferPosition()
     previousPosition = position.translate([0, -1])
@@ -127,7 +127,7 @@
     startPairPosition

   updateMatch: (editor) ->
-    return unless underlayer = editor.pane()?.find('.underlayer')
+    return unless underlayer = editor.getPane()?.find('.underlayer')

     @hideHighlightViews(editor) if @pairHighlighted
     @pairHighlighted = false


Tweak behavior when running lines or selections.

--- lib/execute-as-ruby.coffee
+++ lib/execute-as-ruby.coffee
@@ -10,15 +10,15 @@
     cursor = editor.getCursor()
     selection = editor.getSelectedText()

-    insertResult = (result) ->
-        editor.moveCursorToEndOfLine()
-        editor.insertText(" " + result)
-
     if not selection
         line = cursor.getCurrentBufferLine()
-        result = @runRuby(line, insertResult)
+        @runRuby(line, (result) ->
+            editor.moveCursorToEndOfLine()
+            editor.insertText("\n" + result))
     else
-        result = @runRuby(selection, insertResult)
+        @runRuby(selection, (result) ->
+            editor.moveCursorToEndOfLine()
+            editor.insertText(" " + result))

   packagePath: ->
     packagePath = null


fix selection issue

fix selection issue

--- lib/functional-view.coffee
+++ lib/functional-view.coffee
@@ -6,7 +6,7 @@
   @content: ->
     @div class: "functional-view console", =>
       @div class: 'block', =>
-        @div class: 'btn-group', =>
+        @div class: 'btn-group functional-controls', =>
           @button class: 'btn selected', 'data-functional': 'console', 'Console'
           @button class: 'btn', 'data-functional': 'debug','Debug'
           @button class: 'btn', 'data-functional': 'frames','Frame'
@@ -22,7 +22,7 @@
     @on 'click', '[data-functional]', (e) => @toggleFunctional(e)

   toggleFunctional: (e) ->
-    $prevSelected = @find('.selected')
+    $prevSelected = @find('.functional-controls .selected')
     @removeClass($prevSelected.data('functional'))
     $prevSelected.removeClass('selected')


Pull lists remotes

Pull lists remotes

--- lib/models/git-pull.coffee
+++ lib/models/git-pull.coffee
@@ -1,13 +1,9 @@
-{BufferedProcess} = require 'atom'
-OutputView = require '../views/output-view'
 git = require '../git'
+RemoteListView = require '../views/remote-list-view'

 gitPull = ->
-  view = new OutputView()
   git.cmd
-    args: ['pull']
-    stdout: (data) -> view.addLine(data.toString())
-    stderr: (data) -> view.addLine(data.toString())
-    exit: (code) -> view.finish()
+    args: ['remote']
+    stdout: (data) -> new RemoteListView(data, 'pull')

 module.exports = gitPull


Fix initializing of InMemoryStore (wrong parameters, fixes #7)

--- src/store_inmemory.coffee
+++ src/store_inmemory.coffee
@@ -6,9 +6,9 @@
   _domainEvents: {}
   _projections: {}

-  initialize: (@_contextName, [options]..., callback) ->
-    @_domainEventsCollectionName = "#{@_contextName}.DomainEvents"
-    @_projectionCollectionName   = "#{@_contextName}.Projections"
+  initialize: (@_context, [options]..., callback) ->
+    @_domainEventsCollectionName = "#{@_context.name}.DomainEvents"
+    @_projectionCollectionName   = "#{@_context.name}.Projections"

     @_domainEvents[@_domainEventsCollectionName] = []
     callback()


Fix PostType fragment parsing when fragment is uri encoded

--- src/tent-client.js.coffee
+++ src/tent-client.js.coffee
@@ -30,6 +30,7 @@
     parseUri: (uri) =>
       if m = uri.match(TYPE_URI_REGEX)
         [m, @base, @version, @fragment] = m
+        @fragment = decodeURIComponent(@fragment) if @fragment
         @version = parseInt(@version)

     toString: =>


webpack: colorpicker images mysteriously did not load. now they are url-embedded (higher limit) and hence work

--- src/webpack.config.coffee
+++ src/webpack.config.coffee
@@ -269,9 +269,10 @@
                                     side_effects: true

-hashname    = '[path][name]-[sha256:hash:base64:10].[ext]'
-pngconfig   = "name=#{hashname}&limit=2000&mimetype=image/png"
-svgconfig   = "name=#{hashname}&limit=2000&mimetype=image/svg+xml"
+# don't use base64!
+hashname    = '[sha256:hash:base62:33].[ext]'
+pngconfig   = "name=#{hashname}&limit=16000&mimetype=image/png"
+svgconfig   = "name=#{hashname}&limit=16000&mimetype=image/svg+xml"
 icoconfig   = "name=#{hashname}&mimetype=image/x-icon"
 woffconfig  = "name=#{hashname}&mimetype=application/font-woff"


deleted unnecessary code

deleted unnecessary code

--- source/parrot.notification.coffee
+++ source/parrot.notification.coffee
@@ -32,7 +32,6 @@
           delete opts.title
           new Notification title, opts
         catch e
-          console.log e
           throw new Error "Notification API is not available."

       return _createNotification options unless name?


These should probably be here

These should probably be here

--- spec/atom-environment-spec.coffee
+++ spec/atom-environment-spec.coffee
@@ -340,9 +340,9 @@
           callback([tempDirectory])
         waitsForPromise ->
           atom.addProjectFolder()
-        # runs ->
-        #   expect(atom.project.getPaths()).toEqual([tempDirectory])
-        #   expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled()
+        runs ->
+          expect(atom.project.getPaths()).toEqual([tempDirectory])
+          expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled()

     describe "when there is saved state for the relevant directories", ->
       state = Symbol('savedState')


Add toBeInstanceOf and toHaveLength jasmine matchers

--- spec/markdown-preview-spec.coffee
+++ spec/markdown-preview-spec.coffee
@@ -1,14 +1,72 @@
 $ = require 'jquery'
 RootView = require 'root-view'
-MarkdownPreview = require 'markdown-preview/lib/markdown-preview-view'
+MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
 _ = require 'underscore'

-describe "MarkdownPreview", ->
+describe "MarkdownPreviewView", ->
   beforeEach ->
     project.setPath(project.resolve('markdown'))
     window.rootView = new RootView
     window.loadPackage("markdown-preview")
-    spyOn(MarkdownPreview.prototype, 'loadHtml')
+    spyOn(MarkdownPreviewView.prototype, 'loadHtml')
+
+  fdescribe "markdown-preview:show", ->
+    beforeEach ->
+      rootView.open("file.markdown")
+
+    describe "when the active item is an edit session", ->
+      beforeEach ->
+        rootView.attachToDom()
+
+      describe "when a preview item has not been created for the edit session's uri", ->
+        describe "when there is more than one pane", ->
+          it "shows a markdown preview for the current buffer on the next pane", ->
+            rootView.getActivePane().splitRight()
+            [pane1, pane2] = rootView.getPanes()
+            pane1.focus()
+
+            rootView.getActiveView().trigger 'markdown-preview:show'
+
+            preview = pane2.activeItem
+            expect(preview).toBeInstanceOf(MarkdownPreviewView)
+            expect(preview.buffer).toBe rootView.getActivePaneItem().buffer
+            expect(pane1).toMatchSelector(':has(:focus)')
+
+        describe "when there is only one pane", ->
+          it "splits the current pane to the right with a markdown preview for the current buffer", ->
+            expect(rootView.getPanes()).toHaveLength 1
+
+            rootView.getActiveView().trigger 'markdown-preview:show'
+
+            expect(rootView.getPanes()).toHaveLength 2
+            [pane1, pane2] = rootView.getPanes()
+
+            expect(pane2.items).toHaveLength 1
+            preview = pane2.activeItem
+            expect(preview).toBeInstanceOf(MarkdownPreviewView)
+            expect(preview.buffer).toBe rootView.getActivePaneItem().buffer
+            expect(pane1).toMatchSelector(':has(:focus)')
+
+      describe "when a preview item has already been created for the edit session's uri", ->
+        it "updates and shows the existing preview item if it isn't displayed", ->
+          rootView.getActiveView().trigger 'markdown-preview:show'
+          [pane1, pane2] = rootView.getPanes()
+          pane2.focus()
+          expect(rootView.getActivePane()).toBe pane2
+          preview = pane2.activeItem
+          expect(preview).toBeInstanceOf(MarkdownPreviewView)
+          rootView.open()
+          expect(pane2.activeItem).not.toBe preview
+          pane1.focus()
+
+          rootView.getActiveView().trigger 'markdown-preview:show'
+          expect(rootView.getPanes()).toHaveLength 2
+          expect(pane2.getItems()).toHaveLength 2
+          expect(pane2.activeItem).toBe preview
+          expect(pane1).toMatchSelector(':has(:focus)')
+
+    describe "when the active item is not an edit session ", ->
+      it "logs a warning to the console saying that it isn't possible to preview the item", ->

   describe "markdown-preview:toggle event", ->
     it "toggles on/off a preview for a .md file", ->
@@ -50,7 +108,7 @@
       expect(rootView.find('.markdown-preview')).not.toExist()
       editor.trigger('markdown-preview:toggle')
       expect(rootView.find('.markdown-preview')).not.toExist()
-      expect(MarkdownPreview.prototype.loadHtml).not.toHaveBeenCalled()
+      expect(MarkdownPreviewView.prototype.loadHtml).not.toHaveBeenCalled()

   describe "core:cancel event", ->
    it "removes markdown preview", ->


Fixes tests.

Fixes tests.

--- spec/multi-wrap-guide-spec.coffee
+++ spec/multi-wrap-guide-spec.coffee
@@ -43,69 +43,81 @@
         expect(atom.workspace.getPanes().length).toBe 1
         views = getWrapGuideViews()
         expect(views.length).toBe 1
-        guide = views[0].firstChild
-        expect(getLeftPosition(views[0].firstChild)).toBeGreaterThan(0)
-
+        group = views[0].firstChild
+        expect(group).toExist()
+        guide = group.firstChild
+        expect(guide).toExist()
+        expect(getLeftPosition(guide)).toBeGreaterThan(0)
         atom.workspace.getActivePane().splitRight(copyActiveItem: true)
         expect(atom.workspace.getPanes().length).toBe 2
         views = getWrapGuideViews()
         expect(views.length).toBe 2
-        expect(getLeftPosition(views[0].firstChild)).toBeGreaterThan(0)
-        expect(getLeftPosition(views[1].firstChild)).toBeGreaterThan(0)
+        expect(getLeftPosition(views[0].firstChild.firstChild)).toBeGreaterThan(0)
+        expect(getLeftPosition(views[1].firstChild.firstChild)).toBeGreaterThan(0)

     it "positions the guide at the configured column", ->
       width = editor.getDefaultCharWidth() * MultiWrapGuide.views[editor.id].getColumns()[0]
       expect(width).toBeGreaterThan(0)
-      expect(getLeftPosition(wrapGuideView.firstChild)).toBe(width)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      guide = wrapGuideView.firstChild.firstChild
+      expect(getLeftPosition(guide)).toBe(width)
+      expect(guide).toBeVisible()

   describe "when the font size changes", ->
     it "updates the wrap guide position", ->
-      initial = getLeftPosition(wrapGuideView.firstChild)
+      guide = wrapGuideView.firstChild.firstChild
+      initial = getLeftPosition(guide)
       expect(initial).toBeGreaterThan(0)
       fontSize = atom.config.get("editor.fontSize")
       atom.config.set("editor.fontSize", fontSize + 10)

       advanceClock(1)
-      expect(getLeftPosition(wrapGuideView.firstChild)).toBeGreaterThan(initial)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      guide = wrapGuideView.firstChild.firstChild
+      expect(getLeftPosition(guide)).toBeGreaterThan(initial)
+      expect(guide).toBeVisible()

   describe "when the column config changes", ->
     it "updates the wrap guide position", ->
-      initial = getLeftPosition(wrapGuideView.firstChild)
+      guide = wrapGuideView.firstChild.firstChild
+      initial = getLeftPosition(guide)
       expect(initial).toBeGreaterThan(0)
       column = atom.config.get("editor.preferredLineLength")
       atom.config.set("editor.preferredLineLength", column + 10)
-      expect(getLeftPosition(wrapGuideView.firstChild)).toBeGreaterThan(initial)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      guide = wrapGuideView.firstChild.firstChild
+      expect(getLeftPosition(guide)).toBeGreaterThan(initial)
+      expect(guide).toBeVisible()

       atom.config.set("editor.preferredLineLength", column - 10)
-      expect(getLeftPosition(wrapGuideView.firstChild)).toBeLessThan(initial)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      guide = wrapGuideView.firstChild.firstChild
+      expect(getLeftPosition(guide)).toBeLessThan(initial)
+      expect(guide).toBeVisible()

   describe "when the editor's scroll left changes", ->
     it "updates the wrap guide position to a relative position on screen", ->
       editor.setText("a long line which causes the editor to scroll")
       editor.setWidth(100)

-      initial = getLeftPosition(wrapGuideView.firstChild)
+      guide = wrapGuideView.firstChild.firstChild
+      initial = getLeftPosition(guide)
       expect(initial).toBeGreaterThan(0)

       editor.setScrollLeft(10)

-      expect(getLeftPosition(wrapGuideView.firstChild)).toBe(initial - 10)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      guide = wrapGuideView.firstChild.firstChild
+      expect(getLeftPosition(guide)).toBe(initial - 10)
+      expect(guide).toBeVisible()

   describe "when the editor's grammar changes", ->
     it "updates the wrap guide position", ->
       atom.config.set('editor.preferredLineLength', 20, scopeSelector: '.source.js')
-      initial = getLeftPosition(wrapGuideView.firstChild)
+      guide = wrapGuideView.firstChild.firstChild
+      initial = getLeftPosition(guide)
       expect(initial).toBeGreaterThan(0)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      expect(guide).toBeVisible()

       editor.setGrammar(atom.grammars.grammarForScopeName('text.plain.null-grammar'))
-      expect(getLeftPosition(wrapGuideView.firstChild)).toBeGreaterThan(initial)
-      expect(wrapGuideView.firstChild).toBeVisible()
+      guide = wrapGuideView.firstChild.firstChild
+      expect(getLeftPosition(guide)).toBeGreaterThan(initial)
+      expect(guide).toBeVisible()

   describe 'scoped config', ->
     it '::getDefaultColumn returns the scope-specific column value', ->


fix for issue where copying binary files didn't work.

--- lib/jsau.iced
+++ lib/jsau.iced
@@ -26,14 +26,9 @@

 transform_file = (src, project, target) ->

-  srcStream = fs.createReadStream src
-  targetStream = fs.createWriteStream target
-  srcStream.pipe targetStream
-
-  await srcStream.on 'end', defer err
-
-  srcStream.destroy()
-  targetStream.destroy()
+  # copy the file.
+  proc = child_process.spawn 'cp', [src, target], {'stdio' : 'ignore'}
+  await proc.on 'exit', defer code

   # replace each key with each replacement using sed
   args = ['-i', '']


Slim down selector

Slim down selector

--- src/trix/elements/trix_toolbar_element.coffee
+++ src/trix/elements/trix_toolbar_element.coffee
@@ -12,7 +12,7 @@
       display: block;
     }

-    %t [data-trix-dialog] [data-trix-input][data-trix-validate]:invalid {
+    %t [data-trix-dialog] [data-trix-validate]:invalid {
       background-color: #ffdddd;
     }
   """


Test for getValue with string input

Test for getValue with string input

--- test/integration/steps/widget_steps.coffee
+++ test/integration/steps/widget_steps.coffee
@@ -25,8 +25,11 @@
     })

     w.sendKeys(sent)
-    .then ->
-      w.getValue().should.eventually.eql(read)
+    .then =>
+      new @Widget({
+        root: "p"
+      })
+      .getValue(".inputbox").should.eventually.eql(read)

   @When /^I make sure inputbox is empty$/, ->
     new @Widget({root: ".inputbox"}).then (w) =>


check it it has geometry before adding it

--- src/leaflet.pouch.coffee
+++ src/leaflet.pouch.coffee
@@ -22,12 +22,13 @@
 					onChange : (c) =>
 						doc = c.doc
 						if parseInt(doc._rev.slice(0, 1)) is 1
-							@addData doc
+							@addData doc if "geometry" of doc
 						else if parseInt(doc._rev.slice(0, 1)) > 1
 							@eachLayer (f) =>
 								@removeLayer f  if f.feature._id is doc._id
-
-							@addData doc  unless doc._deleted
+
+							if "geometry" of doc
+								@addData doc  unless doc._deleted
 						true
 				)
 				if remoteDB


element mouse listeners calculate local x and y

--- source/lib/element.coffee
+++ source/lib/element.coffee
@@ -1,4 +1,8 @@
 Event = require './event'
+
+defaultMouseEventListener = (event) ->
+  event.localX = (if event.localX? then event.localX else event.x) - (@origin?.x or 0)
+  event.localY = (if event.localY? then event.localY else event.y) - (@origin?.y or 0)

 module.exports = class Element extends require './evented'
   constructor: (args = {}) ->
@@ -10,6 +14,15 @@
     for name in ['mousemove', 'mousedown', 'mouseup', 'update', 'render']
       do (name) => @addListener name, ((e) -> e.stop() unless @events?[name]), yes

+    for name in ['mousemove', 'mousedown', 'mouseup']
+      do (name) => @addListener name, this["#{name}Listener"], yes
+
+  mousemoveListener: defaultMouseEventListener
+
+  mousedownListener: defaultMouseEventListener
+
+  mouseupListener: defaultMouseEventListener
+
   update: (args) ->
     if @events?.update then @broadcastEvent new Event
       type:'update', target:this, args:args


Use _.sortBy for vertices sorting.

Use _.sortBy for vertices sorting.

--- 09-delaunay/src/delaunay.coffee
+++ 09-delaunay/src/delaunay.coffee
@@ -5,17 +5,6 @@
   constructor: (@x, @y) ->
   toArray: -> [@x, @y]
   atan2: (vertex) -> Math.atan2(@y - vertex.y, @x - vertex.x)
-
-  @compare: (v1, v2) ->
-    if v1.x == v2.x && v1.y == v2.y
-      0
-    else
-      if v1.x < v2.x || v1.x == v2.x && v1.y < v2.y then -1 else 1
-
-  @compareByAtan2: (v1, v2, vertext) ->
-    a1 = v1.atan2(vertex)
-    a2 = v2.atan2(vertex)
-    if a1 < a2 then -1 else if a1 > a2 then 1 else 0

 class Edge
@@ -71,7 +60,7 @@
     edges.concat(leftTriangulation).concat(rightTriangulation)

   # convert a list of points into a list of vertices sorted by (x, y)-coordinate
-  sortedVertices: (points) -> (new Vertex(p[0], p[1]) for p in points).sort Vertex.compare
+  sortedVertices: (points) -> _.sortBy (new Vertex(p[0], p[1]) for p in points), (v) -> v.toArray()

   # splits sorted by x-coordinate sequence of vertices in two
   splitVertices: (vertices) ->
@@ -113,8 +102,7 @@
   # returns vertices adjacent to the vertex in the triangulation, sorted clockwise
   adjacent: (triangulation, vertex) ->
     # TODO: remove full look-up by storing a reference to adjacent vertices (or incident edges) in the vertex
-    vertices = [edge.otherVertex(vertex) for edge in triangulation].filter (v) -> v?
-    vertices.sort (v1, v2) -> Vertex.compareByAtan2(v1, v2, vertex)
+    _.sortBy (edge.otherVertex(vertex) for edge in triangulation).filter((v) -> v?), (v) -> v.atan2(vertex)

   # returns true if d is within (a, b, c) triangle's circumcircle
   inCircle: (a, b, c, d) ->


change event name

change event name

--- app/assets/javascripts/modules/cms/tracked_download_links.js.coffee
+++ app/assets/javascripts/modules/cms/tracked_download_links.js.coffee
@@ -11,7 +11,7 @@

         link.addEventListener('click', ->
           if ga?
-            ga('send', 'event', "Downloads - OECM", 'click', label)
+            ga('send', 'event', "Downloads - CMS", 'click', label)
         )
       )
 )


持っている -> 持っていない

持っている -> 持っていない

--- test/calc-box.spec.coffee
+++ test/calc-box.spec.coffee
@@ -27,8 +27,7 @@
     should.not.exists canContain

 describe '1つの荷物を使ったテスト', ->
-  it '荷物は横幅・縦幅・奥行きを持っている', ->
-    # TODO
+  it '荷物は横幅・縦幅・奥行きを持っていない', ->
     box = new CalcBox
       width: 100
       height: 200
@@ -359,8 +358,7 @@

 describe 'pack', ->
   describe '1つの荷物を使ったテスト', ->
-    it '荷物は横幅・縦幅・奥行きを持っている', ->
-      # TODO
+    it '荷物は横幅・縦幅・奥行きを持っていない', ->
       box = new CalcBox
         width: 100
         height: 200


Added Two More Tests

Added Two More Tests

These are all sort of pseudo tests, but they are there for consistency.

--- test/deps/outdated.coffee
+++ test/deps/outdated.coffee
@@ -29,4 +29,24 @@
                                     # docs for reference.
           done()

+  describe 'on stub/devdeps', ->
+    it 'should callback true', (done) ->
+      outdatedNodeDev path.join(stubsdir, 'devdeps'),
+        (err, result, list) ->
+          should.not.exist err
+          result.should.equal true
+          list.should.have.length 0 # List should normally be > 0, see fn
+                                    # docs for reference.
+          done()

+  describe 'on stub/installdeps', ->
+    it 'should callback true', (done) ->
+      outdatedNodeDev path.join(stubsdir, 'installdeps'),
+        (err, result, list) ->
+          should.not.exist err
+          result.should.equal true
+          list.should.have.length 0 # List should normally be > 0, see fn
+                                    # docs for reference.
+          done()
+
+


Modify the callback tests

Modify the callback tests

--- tests/integration/tasksGatewayTests.coffee
+++ tests/integration/tasksGatewayTests.coffee
@@ -2,28 +2,24 @@

 describe 'TasksGateway', ->
   tasksGateway = null
+  callback = null

   beforeEach ->
     tasksGateway = new TasksGateway
+    callback = sinon.spy()

-  describe '#addListener', ->
-    it 'Should invoke callback when task is added', ->
-      callback = sinon.spy()
+  it 'Should invoke callback when task is added', ->
+    tasksGateway.addListener callback
+    addTask()

-      tasksGateway.addListener callback
-      addTask()
+    assert.isTrue callback.calledOnce

-      assert.isTrue callback.calledOnce
+  it 'Should invoke callback when task is added', ->
+    tasksGateway.addListener callback
+    tasksGateway.addListener callback
+    addTask()

-    it 'Should invoke callback when task is added', ->
-      firstCallback = ->
-      secondCallback = sinon.spy()
+    assert.isTrue callback.calledTwice

-      tasksGateway.addListener firstCallback
-      tasksGateway.addListener secondCallback
-      addTask()
-
-      assert.isTrue secondCallback.calledOnce
-
-    addTask = ->
-      tasksGateway.addTask 'new task'+  addTask = ->
+    tasksGateway.addTask 'new task'


Changing to a behavioural FSM so the FSM doesn't hold the state internally

--- tech-tests/primes-table/src/cli.coffee
+++ tech-tests/primes-table/src/cli.coffee
@@ -25,7 +25,7 @@

 program = new (machina.Fsm)(
     initialize: (options) ->
-        @data = {}
+
     namespace: 'primes-table'
     initialState: 'uninitialized'
     states:
@@ -33,7 +33,7 @@
             @deferUntilTransition()
             @transition 'welcome'
         welcome:
-            _onEnter: ->
+            _onEnter: (client) ->
                 console.log """
                 =================================================

@@ -41,22 +41,22 @@

                 =================================================
                 """
-                @transition 'promptForInput'
+                @transition( client, 'promptForInput' )
         promptForInput:
-            _onEnter: ->
+            _onEnter: (client) ->
                 inquirer.prompt [question1],(answers) =>
                     if answers.action == 'Exit'
-                        @transition 'exit'
+                        @transition( client, 'exit')
                     else
                         inquirer.prompt [tableSizeQuestion], (answers) =>
-                            @data.tableSize = answers.tableSize
-                            @transition 'drawTable'
+                            client.tableSize = answers.tableSize
+                            @transition( client, 'drawTable' )
         drawTable:
-            _onEnter: ->
-                printer.print @data.tableSize
-                @transition 'promptForInput'
+            _onEnter: (client) ->
+                printer.print client.tableSize
+                @transition( client, 'promptForInput' )
         exit:
-            _onEnter: ->
+            _onEnter: (client) ->
                 console.log """
                 =================================================

@@ -65,8 +65,9 @@
                 =================================================
                 """
                 process.exit(0)
-    welcome: ->
-        @handle 'welcome'
+    welcome: (client) ->
+        @handle( 'welcome', client )
 )

-program.welcome()
+client = {}
+program.welcome(client)


Add test for a url that caused deadlock

Add test for a url that caused deadlock

--- spec/UrlToTempFile.coffee
+++ spec/UrlToTempFile.coffee
@@ -72,6 +72,17 @@
         done()
       ins.send url

+  describe 'with image that caused deadlock', ->
+    return if noflo.isBrowser()
+    url = "https://s7.toryburch.com/is/image/ToryBurchLLC/TB_32164_001?$trb_grid_md$"
+    it 'should do a correct error', (done) ->
+      @timeout 0
+      error.once 'data', (data) ->
+        console.log JSON.stringify data
+        chai.expect(data.url).to.equal url
+        done()
+      ins.send url
+
   describe 'with data URL image', ->
     return if noflo.isBrowser()
     url = 'data:image/jpeg;base64,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'


Fix doc specs

Fix doc specs

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -297,7 +297,7 @@
   describe "API documentation", ->
     it "meets a minimum threshold for /app (with no errors)", ->
       docRunner = jasmine.createSpy("docRunner")
-      Exec "rake docs:app:stats", cwd: project.resolve('../..'), docRunner
+      Exec "./node_modules/.bin/coffee ./node_modules/.bin/biscotto -- --statsOnly src/app/", cwd: project.resolve('../..'), docRunner
       waitsFor ->
         docRunner.callCount > 0


Tests: Environments: Added turnOnNewPaidVM test.

--- tests/src/environments/environments.coffee
+++ tests/src/environments/environments.coffee
@@ -198,3 +198,22 @@
       .click                    '.env-modal.paid-plan button'
       .waitForElementVisible    'a[href="/IDE/koding-vm-1/my-workspace"]', 25000
       .end()
+
+
+  # this test depends addVM test.
+  turnOnNewPaidVM: (browser) ->
+
+    vmName     = 'koding-vm-1'
+    vmSelector = 'a[href="/IDE/' + vmName + '/my-workspace"]'
+
+    helpers.beginTest(browser)
+
+    browser
+      .waitForElementVisible vmSelector, 25000
+      .pause                 10000 # required, wait for IDE open.
+      .click                 vmSelector
+
+    helpers.waitForVMRunning(browser, vmName)
+
+    browser.end()
+


Refactor the yank method

Refactor the yank method

--- lib/transient-emacs.coffee
+++ lib/transient-emacs.coffee
@@ -168,15 +168,17 @@
     @isUserCommand = true

   yank: ->
+    @yankTexts @killring.top()
+
+  yankTexts: (texts)->
     editor = @getEditor()
     return unless editor
     editor.transact =>
       cursors = editor.getCursors()
-      top = @killring.top()
-      if cursors.length == top?.length
-        c.selection.insertText top[i] for c, i in cursors
-      else if top
-        c.selection.insertText top.join '\n' for c in cursors
+      if cursors.length == texts?.length
+        c.selection.insertText texts[i] for c, i in cursors
+      else if texts
+        c.selection.insertText texts.join '\n' for c in cursors

   enterControlCharacter: (code)->
     editor = @getEditor()


rotation now possible (using the handler) BlockView el has been resized, it now has the same dimension as selectR (its subcontainer), the difference between the dimension of those two was making the calculus of some vector tedious

--- assets/js/BlockView.coffee
+++ assets/js/BlockView.coffee
@@ -2,65 +2,102 @@
 class @BlockView extends Backbone.View

   events:
-    "mousedown .tracker" : "handlerMethod"
-    "mousedown .handle" : "handlerMethod"
-    "mousedown .dragbar" : "handlerMethod"
-    "mousedown .handleR" : "handlerMethod"
+    'mousedown .tracker' : 'handlerDispatcher'
+    'mousedown .handleR' : 'handlerDispatcher'
+    'mousedown .dragbar' : 'handlerDispatcher'
+    'mousedown .handle' : 'handlerDispatcher'

   initialize: ->
     @page = @options.page
     @$page = $(@page)

+    # We check what the rotation transformation name is
+    # in the browser
+    @transformName = null
+    for b in ['transform', 'webkitTransform', "MozTransform", 'msTransform', "OTransform"] when @el.style[b]?
+      @transformName = b
+

     #predefining a style for the el, adapted to the image for
     #the moment
     @$el.css(
-      position: "absolute",
-      "z-index": 600,
-      width: "256px",
+      position: 'absolute',
+      'z-index': 600,
+      width: '256px',
       # 175 + 25px for the rotation handler
-      height: "200px",
-      top: "116px",
-      left: "166px"
+      height: '175px',
+      top: '116px',
+      left: '166px'
     )
     @render()

   handlerDispatcher: (e) ->
-
-  beginMove: (e) =>
     origine =
-      ox: e.pageX
-      oy: e.pageY
-    @$page.on('mouseup', origine, @endmove)
-    @$page.on('mouseleave', origine, @endmove)
-    @$page.on('mousemove', origine, @move)
+      x: e.pageX
+      y: e.pageY
+
+    #if $(e.currentTarget).hasClass('tracker')
+
+    if $(e.currentTarget).hasClass('handleR')
+      offset = @$el.offset()
+      center =
+        x: offset.left + @$el.width()/2
+        y: offset.top  + @$el.height()/2 # dh=25px
+      console.log(center)
+      @$page.on('mouseup', center, @endRotate)
+      @$page.on('mouseleave', center, @endRotate)
+      return @$page.on('mousemove', center, @rotate)
+
+    #if $(e.currentTarget).hasClass('dragbar')
+
+    #if $(e.currentTarget).hasClass('handle')

   # NB: here we need to bind, because we haven't use the delegate
   # event method of backbone...
   move: (e)=>
     v =
-      x:e.pageX - e.data.ox
-      y:e.pageY - e.data.oy
-
-  endmove:(e) =>
+      x:e.pageX - e.data.x
+      y:e.pageY - e.data.y
+
+  endmove: (e)=>
     @$page.off('mouseup',@endmove)
     @$page.off('mouseleave',@endmove)
     @$page.off('mousemove',@move)
-    console.log("endmove")
-
-  f1: ->
-    console.log("handle!!!")
-
-  rotate:->
-    @$selectR.css("-webkit-transform":"rotate(30deg)")
+
+  rotate: (e)=>
+    # v is the vector from the center of the content to
+    # the pointer of the mouse
+    v =
+      x:e.pageX - e.data.x
+      y:e.pageY - e.data.y
+    #norm of v
+    n = Math.sqrt(v.x*v.x + v.y*v.y)
+    # vn is v normalized
+    vn =
+      x: v.x/n
+      y: v.y/n
+    # "sign" is the sign of v.x
+    sign = v.x/Math.abs(v.x)
+    # beta is the angle between v and the vector (0,-1)
+    beta = (Math.asin(vn.y) + Math.PI/2) * sign
+    betaDeg = beta * 360 /(2*Math.PI)
+    # preparing and changing css
+    style = {}
+    style[@transformName] = "rotate(#{betaDeg}deg)"
+    @$selectR.css(style)
+
+  endRotate:=>
+    @$page.off('mouseup',@endRotate)
+    @$page.off('mouseleave',@endRotate)
+    @$page.off('mousemove',@rotate)

   render: ->
     #cardinals that will be used throughout the rendering (maybe in other
     # functions too?)
-    cards = ["n", "s", "e", "w", "nw", "ne", "se", "sw"]
+    cards = ['n', 's', 'e', 'w', 'nw', 'ne', 'se', 'sw']
     # global subcontainer to which rotations will be applied
     @$selectR = $('

',{
-      style:"width: 100%; height: 100%; z-index: 600; position: absolute;"
+      style:'width: 100%; height: 100%; z-index: 600; position: absolute;'
     })
     @selectR = @$selectR[0]
     @$el.append(@selectR)
@@ -68,15 +105,15 @@
     # dCt : display container that holds the content and the borders of the
     # content
     @$dCt = $('

',{
-      style:"width: 100%; height: 85%; z-index: 310; position: absolute; overflow: hidden; bottom:0px;"
+      style:'width: 100%; height: 100%; z-index: 310; position: absolute; overflow: hidden; bottom:0px;'
     })
     @dCt = @$dCt[0]
     @$selectR.append(@dCt)

     # the content that we display
     @$content = $('',{
-      src: "assets/images/cat.jpg",
-      style:"border: none; visibility: visible; margin: 0px; padding: 0px; position: absolute; top: 0px; left: 0px; width: 256px; height: 175px;"
+      src: 'assets/images/cat.jpg',
+      style:'border: none; visibility: visible; margin: 0px; padding: 0px; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%;'
     })
     @content = @$content[0]
     @$dCt.append(@content)
@@ -85,9 +122,9 @@
     @$borders = []
     @borders = []

-    for str, i in ["jcrop-hline", "jcrop-hline bottom", "jcrop-vline", "jcrop-vline right"]
+    for str, i in ['jcrop-hline', 'jcrop-hline bottom', 'jcrop-vline', 'jcrop-vline right']
       e = $('

',{
-        style:"position:absolute; opacity:0.4;"
+        style:'position:absolute; opacity:0.4;'
       })
       e.addClass(str)
       @$borders.push(e)
@@ -100,7 +137,7 @@
     # hCt: Handler container that will hold all the handlers
     #
     @$hCt = $('

',{
-      style:"width: 100%; height: 85%; bottom: 0px; position: absolute; z-index: 320; display: block;"
+      style:'width: 100%; height: 100%; bottom: 0px; position: absolute; z-index: 320; display: block;'
     })
     @hCt = @$hCt[0]
     @$selectR.append(@hCt)
@@ -111,12 +148,12 @@
     @$dragbars = []
     for str, i in cards.slice(0, 4)
       e = $('

',{
-        style:"position:absolute;"
+        style:'position:absolute;'
       })
       style = {}
-      style.cursor = str + "-resize"
-      style["z-index"] = 370 + i
-      className = "ord-" + str + " jcrop-dragbar"
+      style.cursor = str + '-resize'
+      style['z-index'] = 370 + i
+      className = 'ord-' + str + ' jcrop-dragbar'
       e.css(style)
       e.addClass(className)
       e.addClass('dragbar')
@@ -131,12 +168,12 @@
     @$handles = []
     for str, i in cards
       e = $('

',{
-        style:"position:absolute;"
+        style:'position:absolute;'
       })
       style = {}
-      style.cursor = str + "-resize"
-      style["z-index"] = 374 + i
-      className = "ord-" + str + " jcrop-handle"
+      style.cursor = str + '-resize'
+      style['z-index'] = 374 + i
+      className = 'ord-' + str + ' jcrop-handle'
       e.css(style)
       e.addClass(className)
       e.addClass('handle')
@@ -148,20 +185,20 @@

     # the special handler responsible for the rotation
     @$handleR = $('

',{
-      style:"position:absolute; margin-left: -4px; margin-top: -4px; left: 50%; top:-25px; cursor:url(assets/images/rotate.png) 12 12, auto; z-index:382;"
-    })
-    @$handleR.addClass("jcrop-handle")
-    @$handleR.addClass("handleR")
+      style:'position:absolute; margin-left: -4px; margin-top: -4px; left: 50%; top:-25px; cursor:url(assets/images/rotate.png) 12 12, auto; z-index:382;'
+    })
+    @$handleR.addClass('jcrop-handle')
+    @$handleR.addClass('handleR')
     @handleR = @$handleR[0]

     @$hCt.append(@handleR)

     #This element is here to receive mouse events (clicks)
     @$tracker = $('

',{
-      style:"cursor: move; position: absolute; z-index: 360;"
+      style:'cursor: move; position: absolute; z-index: 360;'
     })
     # ADD A CROSS COMPATIBLE CSS PROPERTY TO MAKE IT UNSELECTABLE
-    @$tracker.css({"-webkit-user-select":"none"})
+    @$tracker.css({'-webkit-user-select':'none'})
     @$tracker.addClass('jcrop-tracker')
     @$tracker.addClass('tracker')
     @tracker = @$tracker[0]


Fix tests when run unfocused

Fix tests when run unfocused

It turns out the reason tests were failing when run as part of the
larger group was due to a dirty keymap. The reconfigured beforeEach block
now ensures its clean on each run.

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -1,11 +1,15 @@
 $ = require 'jquery'

 RootView = require 'root-view'
+Keymap = require 'keymap'

 describe "VimState", ->
-  [editor, vimState] = []
+  [editor, vimState, originalKeymap] = []

   beforeEach ->
+    originalKeymap = window.keymap
+    window.keymap = new Keymap
+
     window.rootView = new RootView
     rootView.open()
     rootView.simulateDomAttachment()
@@ -14,6 +18,9 @@
     editor = rootView.getActiveView()
     editor.enableKeymap()
     vimState = editor.vimState
+
+  afterEach ->
+    window.keymap = originalKeymap

   keydown = (key, {element, ctrl, shift, alt, meta}={}) ->
     dispatchKeyboardEvent = (target, eventArgs...) ->


showing vertretungsplan earlier

showing vertretungsplan earlier

--- lib/vertretungsplan.coffee
+++ lib/vertretungsplan.coffee
@@ -10,7 +10,7 @@
     date = new Date()
     hour = date.getHours()
     day = date.getDay()
-    return @days[day] if hour > 18
+    return @days[day] if hour > 16
     return @days[day-1] if hour < 9

   _url: (day) ->
@@ -29,4 +29,4 @@
     @_check url, (err, success) ->
       cb(err, if success then url else null)

-module.exports = Vertretungsplan+module.exports = Vertretungsplan


Use .click() method on <input type="file">

--- www/js/views/venues.coffee
+++ www/js/views/venues.coffee
@@ -278,8 +278,10 @@
         venue: @model
       $(@$el).html(html)

+    # Simply activate the file upload, because we stylize a button to make
+    # things seem pretty rather than rely on the <input type="file"> directly.
     activateUpload: () ->
-      $('#upload-photo').trigger 'click'
+      $('#upload-photo').click()

     checkIn: (event) ->
       # TODO: See why this is fired for old venue code... maybe it's not


Slightly updated debug information

Slightly updated debug information

--- lib/flash-loader.coffee
+++ lib/flash-loader.coffee
@@ -98,9 +98,9 @@

 load = ->
   flashPath = getPath()
-  dbg.extra('flashLoader').assert validatePath(flashPath), 'Path to Pepper Flash Player is invalid.'
-  dbg.ex.flashLoader.log "Loading flash player from:"
-  dbg.ex.flashLoader.log flashPath
+  dbg.ex.flashLoader?.assert validatePath(flashPath), 'No installed Pepper Flash Player found.'
+  dbg.ex.flashLoader?.log "Loading Pepper Flash Player from:"
+  dbg.ex.flashLoader?.log flashPath
   app.commandLine.appendSwitch 'ppapi-flash-path', flashPath


Fix a little bug that caused a blank line to be inserted if no line comment pattern is defined

--- lib/workspace-utils.coffee
+++ lib/workspace-utils.coffee
@@ -21,7 +21,7 @@
     # TODO toggle lines comments from grammar?
     if (lineCommentPattern = language.getLineCommentPattern())?
       fileHeader = lineCommentPattern.replace(/<commentText>/,fileHeader)
-    textEditor.getBuffer().insert(new Point(0,0),"#{fileHeader}\n")
+      textEditor.getBuffer().insert(new Point(0,0),"#{fileHeader}\n")

   deleteLanguageInformationFileHeader: (textEditor) ->
     textBuffer = textEditor.getBuffer()


Add the test for alias with spaces

Add the test for alias with spaces

--- test/alias_test.coffee
+++ test/alias_test.coffee
@@ -166,6 +166,17 @@
       adapter.receive new TextMessage(user, "hubot alias wow=super useful")
       expect(@brainSetSpy).to.have.been.calledWith sinon.match.string, {foo: 'goo', bar: 'par', wow: 'super useful'}

+    it 'sets multiple words alias name', (done)->
+      adapter.on "send", (envelope, strings)->
+        try
+          expect(strings).to.have.length(1)
+          expect(strings[0]).to.equal 'I made an alias foo bar for "cool".'
+          do done
+        catch e
+          done e
+      adapter.receive new TextMessage(user, "hubot alias foo bar=cool")
+      expect(@brainSetSpy).to.have.been.calledWith sinon.match.string, {foo: 'goo', bar: 'par', 'foo bar': 'cool'}
+
     it 'removes an alias', (done)->
       adapter.on "send", (envelope, strings)->
         try


Updated tests for #1216 and pull #1348

Updated tests for #1216 and pull #1348

--- test/assignment.coffee
+++ test/assignment.coffee
@@ -269,8 +269,20 @@
   d ?= nonce
   eq nonce, d

-test "#1348, #1216: existential assignment comilation", ->
+test "#1348, #1216: existential assignment compilation", ->
   nonce = {}
   a = nonce
   b = (a ?= 0)
   eq nonce, b
+  #the first ?= compiles into a statement; the second ?= compiles to a ternary expression
+  eq a ?= b ?= 1, nonce
+
+  e ?= f ?= g ?= 1
+  eq e + g, 2
+
+  #need to ensure the two vars are not defined, hence the strange names;
+  # broke earlier when using c ?= d ?= 1 because `d` is declared elsewhere
+  eq und1_1348 ?= und2_1348 ?= 1, 1
+
+  if a then a ?= 2 else a = 3
+  eq a, nonce