refactor: Remove unnecessary asynchronicity

--- src/transaction-runner.coffee
+++ src/transaction-runner.coffee
@@ -36,16 +36,15 @@
     transactions = if @configuration.options['sorted'] then sortTransactions(transactions) else transactions

     logger.verbose('Configuring HTTP transactions')
-    async.mapSeries transactions, @configureTransaction.bind(@), (err, results) =>
-      transactions = results
-
-      # Remainings of functional approach, probs to be eradicated
-      logger.verbose('Reading hook files and registering hooks')
-      addHooks @, transactions, (addHooksError) =>
-        return callback addHooksError if addHooksError
-
-        logger.verbose('Executing HTTP transactions')
-        @executeAllTransactions(transactions, @hooks, callback)
+    transactions = transactions.map(@configureTransaction.bind(@))
+
+    # Remainings of functional approach, probs to be eradicated
+    logger.verbose('Reading hook files and registering hooks')
+    addHooks @, transactions, (addHooksError) =>
+      return callback addHooksError if addHooksError
+
+      logger.verbose('Executing HTTP transactions')
+      @executeAllTransactions(transactions, @hooks, callback)

   executeAllTransactions: (transactions, hooks, callback) ->
     # Warning: Following lines is "differently" performed by 'addHooks'
@@ -274,7 +273,7 @@
     if typeof(hook) == 'string'
       @runSandboxedHookFromString hook, data, callback

-  configureTransaction: (transaction, callback) =>
+  configureTransaction: (transaction) =>
     configuration = @configuration

     {origin, request, response} = transaction
@@ -333,7 +332,7 @@
       protocol: @parsedUrl.protocol
       skip: skip

-    return process.nextTick(() -> callback(null, configuredTransaction))
+    return configuredTransaction

   parseServerUrl: (serverUrl) ->
     unless serverUrl.match(/^https?:\/\//i)


only animate on typing

only animate on typing

--- src/ui/views/convlist.coffee
+++ src/ui/views/convlist.coffee
@@ -39,7 +39,10 @@
                                 image = "images/photo.jpg"
                             img src:fixlink(image)
                 if c.typing?.length > 0
-                    span class:'convtyping animate-growshrink', '⋮'
+                    anyTyping = c.typing.filter((t) -> t?.status == 'TYPING').length
+                    tclz = ['convtyping']
+                    tclz.push 'animate-growshrink' if anyTyping
+                    span class:tclz.join(' '), '⋮'
                 span class:'convname', name
                 if ur > 0 and not conv.isQuiet(c)
                     lbl = if ur >= conv.MAX_UNREAD then "#{conv.MAX_UNREAD}+" else ur + ''


bitbucket: make it possible to reopen issues when it reoccurs

--- plugins/bitbucket-issues/index.coffee
+++ plugins/bitbucket-issues/index.coffee
@@ -4,6 +4,35 @@

 class BitbucketIssue extends NotificationPlugin
   BASE_URL = "https://bitbucket.org"
+
+  @issuesUrl: (config) ->
+    "#{BASE_URL}/api/1.0/repositories/#{config.username}/#{config.repo}/issues"
+
+  @issueUrl: (config, issueId) ->
+    @issuesUrl(config) + "/" + issueId
+
+  @commentsUrl: (config, issueId) ->
+    @issueUrl(config, issueId) + "/comments"
+
+  @bitbucketRequest: (req, config) ->
+    req
+      .timeout(4000)
+      .auth(config.username, config.password)
+      .set('Accept', 'application/json')
+
+  @ensureIssueOpen: (config, issueId, callback) ->
+    @bitbucketRequest(@request.put(@issueUrl(config, issueId)), config)
+      .send(qs.stringify({status: "open"}))
+      .on "error", (err) ->
+        callback(err)
+      .end (res) ->
+        callback(res.error)
+
+  @addCommentToIssue: (config, issueId, comment) ->
+    @bitbucketRequest(@request.post(@commentsUrl(config, issueId)), config)
+      .send(qs.stringify({content: comment}))
+      .on("error", console.error)
+      .end()

   @openIssue: (config, event, callback) ->
     query_object =
@@ -13,11 +42,7 @@
       "priority": config.priority

     # Send the request
-    @request
-      .post(url.resolve(BASE_URL, "/api/1.0/repositories/#{config.username}/#{config.repo}/issues"))
-      .timeout(4000)
-      .auth(config.username, config.password)
-      .set('Accept', 'application/json')
+    @bitbucketRequest(@request.post(@issuesUrl(config)), config)
       .send(qs.stringify(query_object))
       .on "error", (err) ->
         callback(err)
@@ -26,7 +51,7 @@

         callback null,
           id: res.body.local_id
-          url: url.resolve(BASE_URL, "#{config.repo}/issue/#{res.body.local_id}")
+          url: url.resolve(BASE_URL, "#{config.username}/#{config.repo}/issue/#{res.body.local_id}")

   @receiveEvent: (config, event, callback) ->
     if event?.trigger?.type == "reopened"


Update ansi_to_html.coffee

Update ansi_to_html.coffee

Standardize `newline` throughout code, rather than `newLine`.

--- src/ansi_to_html.coffee
+++ src/ansi_to_html.coffee
@@ -70,7 +70,7 @@
 defaults =
 	fg: '#FFF'
 	bg: '#000'
-	newLine: false
+	newline: false
 	escapeXML: false
 	stream: false


add start and stop

add start and stop

--- lib/dobi.coffee
+++ lib/dobi.coffee
@@ -129,8 +129,8 @@

   # authenticate your user
   when 'login'
-    login true, (user) ->
-      exit JSON.stringify user, null, 2 if user
+    login true, (config) ->
+      exit JSON.stringify user, null, 2 if config.user
       exit 'not logged in. try "dobi login"'

   # open a site
@@ -146,11 +146,35 @@

   # daemonize a development server
   when 'start'
-    exit 'not available yet'
+    login (config) ->
+      ((next) ->
+        if config.pid
+          log "killing running server: #{config.pid}" if config.pid
+          try process.kill config.pid, 'SIGHUP'
+          config.pid = null
+          saveUserConfig config, next
+        else
+          next()
+      )( ->
+        log "starting process"
+        cp = require 'child_process'
+        child = cp.spawn 'coffee', ["#{__dirname}/dobi.coffee", 'run'], {
+          detached: true
+          stdio: [ 'ignore', 'ignore', 'ignore']
+        }
+        child.unref()
+        config.pid = child.pid
+        log "server running at: #{config.pid}"
+        saveUserConfig config, exit
+      )

   # daemonize a development server
   when 'stop'
-    exit 'not available yet'
+    login (config) ->
+      return exit() if not config.pid
+      try process.kill config.pid, 'SIGHUP'
+      config.pid = null
+      saveUserConfig config, exit

   # check your dobi version
   when 'version'
@@ -159,8 +183,8 @@

   # check your authentication status
   when 'whoami'
-    login (user) ->
-      exit JSON.stringify user, null, 2 if user
+    login (config) ->
+      exit JSON.stringify user, null, 2 if config.user
       exit 'not logged in. try "dobi login"'

   # invalid command


Handle recursive diff3 output.

Handle recursive diff3 output.

--- lib/conflict.coffee
+++ lib/conflict.coffee
@@ -23,15 +23,34 @@
 parseConflict = (state, editor, row) ->
   previousSide = null

-  d = (x) -> console.log "#{x} - #{editor.lineTextForBufferRow row}"
-
   # Mark and construct a Side that begins with a banner and description as its first line.
   markHeaderSide = (position, sideKlass) ->
     sideRowStart = row
     description = sideDescription()
-    advanceToBoundary()
+    row += 1
+    advanceToBoundary('|=')
     sideRowEnd = row

+    createHeaderSide(sideRowStart, sideRowEnd, description, sideKlass, position)
+
+  maybeMarkBase = ->
+    return null if isAtBoundary('=')
+
+    sideRowStart = row
+    description = sideDescription()
+    row += 1
+
+    b = advanceToBoundary('<=')
+    until b is '='
+      # Embedded recursive conflict within the base. Advance beyond it.
+      row = parseConflict(state, editor, row).endRow
+      b = advanceToBoundary('<=')
+
+    sideRowEnd = row
+
+    createHeaderSide(sideRowStart, sideRowEnd, description, BaseSide, BASE)
+
+  createHeaderSide = (sideRowStart, sideRowEnd, description, sideKlass, position) ->
     bannerMarker = editor.markBufferRange([[sideRowStart, 0], [sideRowStart + 1, 0]], options)
     previousSide.followingMarker = bannerMarker if previousSide?

@@ -41,12 +60,21 @@

     previousSide = new sideKlass(text, description, textMarker, bannerMarker, position)

+  markSeparator = ->
+    sepRowStart = row
+    row += 1
+    sepRowEnd = row
+
+    marker = editor.markBufferRange([[sepRowStart, 0], [sepRowEnd, 0]], options)
+    previousSide.followingMarker = marker
+    previousSide = new Navigator(marker)
+
   # Mark and construct a Side with a banner and description as its last line.
   markFooterSide = (position, sideKlass) ->
     sideRowStart = row
-    advanceToBoundary()
+    advanceToBoundary('>')
     description = sideDescription()
-    row += 1 # Advance past the boundary line.
+    row += 1
     sideRowEnd = row

     textRange = [[sideRowStart, 0], [sideRowEnd - 1, 0]]
@@ -60,27 +88,20 @@
     previousSide.followingMarker = bannerMarker
     previousSide

-  maybeMarkBase = -> if isAtSeparator() then null else markHeaderSide(BASE, BaseSide)
-
-  markSeparator = ->
-    sepRowStart = row
-    row += 1
-    sepRowEnd = row
-
-    marker = editor.markBufferRange([[sepRowStart, 0], [sepRowEnd, 0]], options)
-    previousSide.followingMarker = marker
-    previousSide = new Navigator(marker)
-
   sideDescription = -> editor.lineTextForBufferRow(row).match(/^[<|>]{7} (.*)$/)[1]

-  isAtBoundary = -> /^[<|=>]{7}/.test editor.lineTextForBufferRow(row)
-
-  isAtSeparator = -> /^={7}$/.test editor.lineTextForBufferRow(row)
-
-  advanceToBoundary = ->
-    row += 1
-    until isAtBoundary()
+  isAtBoundary = (boundaryKinds = '<|=>') ->
+    line = editor.lineTextForBufferRow(row)
+    for b in boundaryKinds
+      return b if line.startsWith(b.repeat(7))
+    null
+
+  advanceToBoundary = (boundaryKinds = '<|=>') ->
+    b = isAtBoundary(boundaryKinds)
+    until b
       row += 1
+      b = isAtBoundary(boundaryKinds)
+    b

   if state.isRebase
     theirs = markHeaderSide(TOP, TheirSide)


bad logging

bad logging

--- src/server.iced
+++ src/server.iced
@@ -30,9 +30,8 @@
       console.log "HTTP Launched, Listening on 4002"

     server.on 'upgrade', (request, socket, body) ->
-      console.log "upgrade on " + req.path
       if not websocket.isWebSocket request
-        return console.error "Bad Websocket Upgrade Request to Path: " + req.path
+        return console.error "Bad Websocket Upgrade"
       driver = websocket.http request
       driver.io.write body
       socket.pipe(driver.io).pipe(socket)


added readonly feature

added readonly feature

Lets you turn any session into a read only session controlled by you
only.

--- lib/devunity.coffee
+++ lib/devunity.coffee
@@ -27,6 +27,9 @@
         style: 'padding:10px;border-bottom:1px solid #ef4423'
         =>
           @div '[x] Stop session',id: 'stopCollab', class: ' pull-right',style:'cursor:pointer;color:#ef4423'
+          @div id: 'readonly', class: ' pull-right',style:'cursor:pointer;margin-right:4px;font-size:10px', =>
+            @label 'Read only ', =>
+              @input type: 'checkbox',id:'readonlycheckbox',style:'margin-right:3px;'
           @div id: 'users', class: ' pull-right'
           @div id: 'chat', class: ' pull-right', style:'max-width:300px;overflow:hidden;height:17px;width:100%'
           @div class: 'pull-left', =>
@@ -124,6 +127,7 @@
     @coderef = @ref.child('code');
     @userref = @ref.child('users');
     @chatref = @ref.child('chat');
+    @readonlyref = @ref.child('readonly');

     @coderef.once 'value', (snapshot) =>
         options = {sv_: Firebase.ServerValue.TIMESTAMP}
@@ -139,13 +143,23 @@
         @view = new ColabView(codekey[1],@getFilename())
         @view.show()

+        #stop collab button
         $('#stopCollab').on 'click', =>
           @stop()
+
         #@initStatistics
+
+        #read only button
+        $('#readonlycheckbox').on 'click', =>
+          console.log($('#readonlycheckbox')[0].checked)
+          @readonlyref.set($('#readonlycheckbox')[0].checked);

         #Lets add the comment stating where the collaboration is being done online
         #comment = @getColabComment(@getLanguage(),codekey[1])
         #editor.setText comment+editor.getText()
+        @readonlyref.on 'value',(snapshot) =>
+          $('#readonlycheckbox')[0].checked = snapshot.val()
+
         @chatref.on 'child_added', (snapshot) =>
           chattext = snapshot.val().text
           chatuser = snapshot.val().user
@@ -160,7 +174,12 @@
           $('#'+connectedusercolor.substr(1)).css('color',connectedusercolor);
           $('#'+connectedusercolor.substr(1)).text(connecteduser);

-
+        @userref.on 'child_removed', (snapshot) =>
+          console.log(snapshot)
+          user = snapshot.val();
+          connecteduser = snapshot.name()
+          connectedusercolor = snapshot.val().color;
+          $('#'+connectedusercolor.substr(1)).remove();


remove support for 'click to go to definition'

--- lib/init.coffee
+++ lib/init.coffee
@@ -126,25 +126,6 @@

 el.addEventListener('mouseover', handleEntityMouseOver.bind(this, null))

-
-el.addEventListener('click',
-    (e) ->
-        target = e.target
-
-        line = target.getAttribute('data-line')
-        path = e.target.getAttribute('data-path')
-        if path
-            console.log("lInIa", line)
-            window.lupaEmit('goToFile', [path, line])
-        else if line
-            pos = [~~line - 1, 0]
-            lastPos = pos
-            window.lupaGoToPos(pos)
-
-
-)
-
-
 currentFile = ''

 atom.workspace.addLeftPanel(item: el)


Fixed config to adhere to the API docs. Fixes #19

--- lib/init.coffee
+++ lib/init.coffee
@@ -1,11 +1,28 @@
 module.exports =
-  configDefaults:
-    executableDir: null
-    maxLineLength: 79
-    ignoreErrorCodes: []
-    maxComplexity: 10
-    hangClosing: false
-    selectErrors: null
+  config:
+    executableDir:
+      type: 'string'
+      default: 'None'
+    maxLineLength:
+      type: 'integer'
+      default: 79
+    ignoreErrorCodes:
+      type: 'array'
+      default: []
+      items:
+        type: 'string'
+    maxComplexity:
+      type: 'integer'
+      default: 10
+    hangClosing:
+      type: 'boolean'
+      default: false
+    selectErrors:
+      description: 'input "E, W" to include all errors/warnings'
+      type: 'array'
+      default: []
+      items:
+        type: 'string'

   activate: ->
     console.log 'activate linter-flake8'


remove trailing semicolons

remove trailing semicolons

--- lib/main.coffee
+++ lib/main.coffee
@@ -90,14 +90,14 @@
             {
               text: 'Open Settings'
               onDidClick: ->
-                atom.workspace.open("atom://config/packages/#{meta.name}");
-                notification.dismiss();
+                atom.workspace.open("atom://config/packages/#{meta.name}")
+                notification.dismiss()
             },
             {
               text: 'Ignore',
               onDidClick: ->
-                atom.config.set("#{meta.name}.mutePathWarning", true);
-                notification.dismiss();
+                atom.config.set("#{meta.name}.mutePathWarning", true)
+                notification.dismiss()
             }
           ]
         )


Made some minor improvements to the main module

--- lib/main.coffee
+++ lib/main.coffee
@@ -12,7 +12,7 @@

     pkgSubscription = atom.packages.onDidActivatePackage (pkg) =>
       if pkg.name == @pkgName
-        dummyGrammarPath = path.join @pkgLanguageDirPath, 'grammars', 'dummy.cson'
+        dummyGrammarPath = CSON.resolve path.join @pkgLanguageDirPath, 'grammars', 'dummy'
         @dummyGrammar = atom.grammars.readGrammarSync dummyGrammarPath

         if @consumedLevels
@@ -27,9 +27,8 @@
     @activated = false

   getExecutablePath: ->
-    executableDirPath = path.join @pkgLanguageDirPath, 'executables'
     executable = if process.platform == 'win32' then 'run.exe' else 'run'
-    return path.join executableDirPath, process.platform, executable
+    return path.join @pkgLanguageDirPath, 'executables', process.platform, executable

   consumeLevels: ({@languageRegistry}) ->
     if @activated


Get `executablePath` with observer

Get `executablePath` with observer

--- lib/main.coffee
+++ lib/main.coffee
@@ -13,9 +13,12 @@
       description: 'Lint JavaScript inside `<script>` blocks in HTML or PHP files.'

   activate: ->
+    @subscriptions = new CompositeDisposable
+    @subscriptions.add atom.config.observe 'linter-jshint.executablePath',
+      (executablePath) =>
+        @executablePath = executablePath
     scopeEmbedded = 'source.js.embedded.html'
     @scopes = ['source.js', 'source.js.jsx']
-    @subscriptions = new CompositeDisposable
     @subscriptions.add atom.config.observe 'linter-jshint.lintInlineJavaScript',
       (lintInlineJavaScript) =>
         if lintInlineJavaScript
@@ -34,14 +37,13 @@
       scope: 'file'
       lintOnFly: true
       lint: (textEditor) =>
-        executablePath = atom.config.get('linter-jshint.executablePath')
         filePath = textEditor.getPath()
         text = textEditor.getText()
         parameters = ['--reporter', reporter, '--filename', filePath]
         if textEditor.getGrammar().scopeName.indexOf('text.html') isnt -1 and 'source.js.embedded.html' in @scopes
           parameters.push('--extract', 'always')
         parameters.push('-')
-        return helpers.execNode(executablePath, parameters, {stdin: text}).then (output) ->
+        return helpers.execNode(@executablePath, parameters, {stdin: text}).then (output) ->
           try
             output = JSON.parse(output).result
           catch error


modify argument handling

modify argument handling

--- lib/main.coffee
+++ lib/main.coffee
@@ -57,11 +57,17 @@
           atom.notifications.addError("**language-bridlensis**: no valid `BridleNSIS.jar` specified in your config", dismissable: false)
           return

+        defaultArguments = ["java", "-jar", nslJar]
         customArguments = atom.config.get('language-bridlensis.customArguments').trim().split(" ")
-        customArguments.push("\"#{script}\"")
-        customArguments.join(" ")

-        exec "\"java\" -jar \"#{bridleJar}\" #{customArguments}", (error, stdout, stderr) ->
+        if os.platform() is 'win32'
+          customArguments.push("\"#{script}\"")
+        else
+          customArguments.push(script)
+
+        bridleCmd = defaultArguments.concat(customArguments)
+
+        exec bridleCmd.join(" "), (error, stdout, stderr) ->
           if error isnt null
             # bridleJar error from stdout, not error!
             atom.notifications.addError("**#{script}**", detail: error, dismissable: true)


Change how definition-lists do not get a new list-item

--- lib/main.coffee
+++ lib/main.coffee
@@ -102,8 +102,7 @@
                         if classes.indexOf('ordered') isnt -1
                           typeOfList = 'ordered'
                         if classes.indexOf('definition') isnt -1
-                          # Skip definition-lists
-                          isListItem = false
+                          typeOfList = 'definition'
                         break

                       else
@@ -115,7 +114,7 @@
                     else
                       isPunctuation = false

-                  if isListItem
+                  if isListItem and typeOfList isnt 'definition'
                     text = token.value

                     # increment ordered list-item


features browserified +1

features browserified +1

--- client/features/lib/index.coffee
+++ client/features/lib/index.coffee
@@ -1,9 +1,13 @@
-class FeaturesAppController extends KDViewController
+kd = require 'kd'
+KDViewController = kd.ViewController
+FeaturesView = require './featuresview'

-  KD.registerAppClass this,
+
+module.exports = class FeaturesAppController extends KDViewController
+
+  @options =
     name  : 'Features'
-    route : '/Features'
-
+    route : '/:name?/Features'

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

@@ -11,3 +15,5 @@
       cssClass : 'content-page features'

     super options, data
+
+


Team: Update team

Team: Update team

--- client/landing/About/team.coffee
+++ client/landing/About/team.coffee
@@ -16,11 +16,11 @@
         username  : 'gokmen'
         title     : 'Software Engineer'
       ,
+        username  : 'fatihacet'
+        title     : 'Front-End Engineer'
+      ,
         username  : 'arslan'
         title     : 'Software Engineer'
-      ,
-        username  : 'fatihacet'
-        title     : 'Front-End Developer'
       ,
         username  : 'sent-hil'
         title     : 'Software Engineer'
@@ -29,6 +29,9 @@
         title     : 'Software Engineer'
       ,
         username  : 'geraint'
+        title     : 'Director of Infrastructure'
+      ,
+        username  : 'leventyalcin'
         title     : 'System Administrator'
       ,
         username  : 'ybrs'
@@ -57,7 +60,27 @@
       ,
         username  : 'pablostanley'
         title     : 'Illustrator'
-
+      ,
+        username  : 'leeolayvar'
+        title     : 'Community Manager'
+      ,
+        username  : 'stefanbc'
+        title     : 'Community Relations'
+      ,
+        username  : 'dicle'
+        title     : 'Business Analysist'
+      ,
+        username  : 'niltuncer'
+        title     : 'Business Analysist'
+      ,
+        username  : 'zeynep'
+        title     : 'Finance Assistant'
+      ,
+        username  : 'nesli'
+        title     : 'Chef de cuisine'
+      ,
+        username  : 'aleksey-m'
+        title     : 'System Administrator'
       ]

     suspended     : []


remove tracking scroll events in frontend

--- client/landing/Intro/home.coffee
+++ client/landing/Intro/home.coffee
@@ -116,18 +116,6 @@
       offset    :
         top     : 25
         left    : 25
-
-    new MixpanelScrollTracker
-      attribute: 'section',
-      event: '/ scroll to',
-      markers: [
-        { position: 362,  value: 'Teamwork screenshot'    }
-        { position: 627,  value: 'Feature explanation'    }
-        { position: 1495, value: 'Activity screenshot'    }
-        { position: 1995, value: 'Enterprise explanation' }
-        { position: 2270, value: 'Enterprise contact'     }
-        { position: 2864, value: 'Scrolled to bottom'     }
-      ]

   pistachio:->


pricing: improve wording

pricing: improve wording

--- client/landing/Intro/home.coffee
+++ client/landing/Intro/home.coffee
@@ -11,7 +11,7 @@
     super options, data

     @pricingButton = new KDButtonView
-      title       : "Check our pricingfor you or your team"
+      title       : "Check out our pricingfor you or your team"
       cssClass    : 'solid green shadowed pricing'
       icon        : 'yes'
       iconClass   : 'dollar'


add line on mouse click

add line on mouse click

--- client/scripts/coffee/app.coffee
+++ client/scripts/coffee/app.coffee
@@ -14,7 +14,6 @@
         @scoreText = null

     captureBall: (ball) ->
-        console.log @score
         ball.kill()
         @score += 10
         @displayScore()
@@ -42,7 +41,8 @@
             '',
             preload:( -> self.preload()),
             create: ( -> self.create()),
-            update: (-> self.update())
+            update: (-> self.update()),
+            render: (-> self.render())
         )

@@ -91,27 +91,36 @@
       #cursors = @phaser.input.keyboard.createCursorKeys()
       @balls = @phaser.add.group()
       @balls.enableBody = true;
+
+      @new_line = new Phaser.Line 64, 64, 200, 300

     collectBalls: (plateform, ball) ->
       plateform.playerParent.captureBall(ball)

     update: () ->
       @phaser.physics.arcade.overlap @plateforms, @balls, @collectBalls, null, this
+      @phaser.physics.arcade.collide @balls, @new_line

-      @new_line = new Phaser.Line 64, 64, 200, 300
       @phaser.input.onDown.add(@click, this);

-      #@phaser.physics.overlap @balls, @collectStar, null, this
       @generate_fake_balls()

       if @dragging
           if @phaser.input.activePointer.isDown
-            @new_line.end.set(@phaser.input.activePointer.x, @phaser.input.activePointer.y)
+            @new_line.end.set @phaser.input.activePointer.x, @phaser.input.activePointer.y
+
           else
             @dragging = false
+            console.log 'push line !'

-    click: ->
-      console.log('click');
+    render: () ->
+      @phaser.debug.geom(@new_line);
+      @phaser.debug.rectangle(@new_line);
+
+
+    click: (pointer) ->
+      @dragging = true
+      @new_line.start.set pointer.x, pointer.y


typo fix

typo fix

--- src/app/common/tags.coffee
+++ src/app/common/tags.coffee
@@ -106,7 +106,7 @@
     {"name": "sailor moon", "regex": /sailor ?moon/},
     {"name": "naruto", "regex": /naruto/},
     {"name": "one piece", "regex": /one ?piece/},
-    {"name": "attack on titan", "regex": /(attack ?on ?titan)|(snk)|(shinkegi ?no ?kyojin)/}
+    {"name": "attack on titan", "regex": /(attack ?on ?titan)|(snk)|(shingeki ?no ?kyojin)/}
   ]
     # template: {"name": "", "regex": //}
 }


cleanup

cleanup

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -1159,36 +1159,34 @@
     super

   moveCursor: (cursor) ->
-    index = @getIndex(cursor.getBufferPosition())
-    if index?
-      offset = switch @direction
-        when 'next' then @getCount(-1)
-        when 'previous' then -@getCount(-1)
-      range = @ranges[getIndex(index + offset, @ranges)]
-      point = range.start
-
-      cursor.setBufferPosition(point, autoscroll: false)
-
-      if cursor.isLastCursor()
-        @editor.unfoldBufferRow(point.row)
-        smartScrollToBufferPosition(@editor, point)
-
-      if @getConfig('flashOnMoveToOccurrence')
-        @vimState.flash(range, type: 'search')
+    range = @ranges[getIndex(@getIndex(cursor.getBufferPosition()), @ranges)]
+    point = range.start
+    cursor.setBufferPosition(point, autoscroll: false)
+
+    if cursor.isLastCursor()
+      @editor.unfoldBufferRow(point.row)
+      smartScrollToBufferPosition(@editor, point)
+
+    if @getConfig('flashOnMoveToOccurrence')
+      @vimState.flash(range, type: 'search')

   getIndex: (fromPoint) ->
+    index = null
     for range, i in @ranges when range.start.isGreaterThan(fromPoint)
-      return i
-    0
+      index = i
+      break
+    (index ? 0) + @getCount(-1)

 class MoveToPreviousOccurrence extends MoveToNextOccurrence
   @extend()
   direction: 'previous'

   getIndex: (fromPoint) ->
+    index = null
     for range, i in @ranges by -1 when range.end.isLessThan(fromPoint)
-      return i
-    @ranges.length - 1
+      index = i
+      break
+    (index ? @ranges.length - 1) - @getCount(-1)

 # -------------------------
 # keymap: %


Fixed black screen when close a window in fullscreen mode

Fixes #523

--- lib/window.coffee
+++ lib/window.coffee
@@ -31,7 +31,11 @@
         e.preventDefault() unless forceClose
     else
       current.on 'close', (e) ->
-        current.hide()
+        if current.isFullScreen()
+          current.once 'leave-full-screen', current.hide.bind(current)
+          current.setFullScreen(false)
+        else
+          current.hide()
         e.preventDefault() unless forceClose
     # Draggable
     unless options.navigatable


ContextToDebug: handle library name with graphs

--- components/ContextToDebug.coffee
+++ components/ContextToDebug.coffee
@@ -19,26 +19,26 @@

   c.setRuntimeDebug = (enable) ->
     c.runtime.sendNetwork 'debug',
-      graph: c.graph.properties.id or c.graph.name
+      graph: @graphId
       enable: enable

   c.addListener = (runtime, graph) ->
     return unless runtime? and graph?
-    c.runtime = runtime
-    c.graph = graph
-    c.listener = (status) =>
+    @runtime = runtime
+    @graph = graph
+    @graphId = if graph.properties.library? then "#{graph.properties.library}/#{graph.properties.id}" else graph.properties.id
+    @listener = (status) =>
       return unless status.online
       c.setRuntimeDebug true
-    c.runtime.on 'status', c.listener
-    c.setRuntimeDebug true if c.runtime?.isConnected()
+    @runtime.on 'status', c.listener
+    @setRuntimeDebug true if @runtime.isConnected()

   c.removeListener = ->
-    return unless c.listener
-    return unless c.runtime
+    return unless @listener and @runtime
     # Disable debug on old runtime
-    c.setRuntimeDebug false if c.runtime.isConnected()
+    @setRuntimeDebug false if @runtime.isConnected()
     # Stop listening
-    c.runtime.removeListener 'status', c.listener
+    @runtime.removeListener 'status', @listener
     delete c.runtime
     delete c.graph


Extract set/clearValidity methods

Extract set/clearValidity methods

--- components/form/validator.coffee
+++ components/form/validator.coffee
@@ -32,6 +32,12 @@
   isEligible: ->
     _.isFunction @$form[0].checkValidity

+  setValidity: ($el, message) ->
+    $el[0].setCustomValidity? message
+
+  clearValidity: ($el) ->
+    $el[0].setCustomValidity? ''
+
   # Ensures fields with data-confirm='some_name' has a value matching the value
   # of the field called out in the data-confirm attribute
   confirm: ->
@@ -41,6 +47,6 @@
       $confirm = @$form.find("input[name='#{confirm}']")

       if $confirm.length and $el.val() isnt $confirm.val()
-        $el[0].setCustomValidity? "#{_s.humanize(confirm)} must match"
+        @setValidity $el, "#{_s.humanize(confirm)} must match"
       else
-        $el[0].setCustomValidity? ''
+        @clearValidity $el


config: add social api uri to sl-production

--- config/main.sl-production.coffee
+++ config/main.sl-production.coffee
@@ -169,6 +169,7 @@
       useNeo4j: yes
       logToExternal : yes
       resourceName: socialQueueName
+      socialApiUri: 'https://social.koding.com/xhr'
       suppressLogs: yes
       version   : version
       mainUri   : "http://koding.com"


Don't re run search when confirm in results-view

--- lib/project/results-view.coffee
+++ lib/project/results-view.coffee
@@ -23,6 +23,7 @@

     @on 'core:confirm', =>
       @find('.selected').view?().confirm?()
+      false

     @on 'mousedown', '.match-result, .path', (e) =>
       @find('.selected').removeClass('selected')


Handle mousedown events

Handle mousedown events

--- lib/project/results-view.coffee
+++ lib/project/results-view.coffee
@@ -25,10 +25,12 @@
     @on 'core:confirm', =>
       @find('.selected').view?().confirm?()

-    @on 'mousedown', (e) =>
+    @on 'mousedown', '.match-result, .path', (e) =>
       @find('.selected').removeClass('selected')
-      console.log e
-      # @addClass('selected')
+
+      if view = $(e.srcElement).view?()
+        view.addClass('selected')
+        view.confirm()

   beforeRemove: ->
     @clear()


decrease reconnect ratelimit for user activity

--- services/web/public/coffee/ide/connection/ConnectionManager.coffee
+++ services/web/public/coffee/ide/connection/ConnectionManager.coffee
@@ -226,8 +226,8 @@
 			@lastConnectionAttempt = new Date()
 			setTimeout (=> @startAutoReconnectCountdown() if !@connected), 2000

-		MIN_RETRY_INTERVAL: 1000 # ms
-		BACKGROUND_RETRY_INTERVAL : 30 * 1000 # ms
+		MIN_RETRY_INTERVAL: 1000 # ms, rate limit on reconnects for user clicking "try now"
+		BACKGROUND_RETRY_INTERVAL : 5 * 1000 # ms, rate limit on reconnects for other user activity (e.g. cursor moves)

 		tryReconnectWithRateLimit: (options) ->
 			# bail out if the reconnect is already in progress


Add fetch method in collections Fix bug in destroy method

--- app/assets/javascripts/action_cable_notifications/collection.coffee
+++ app/assets/javascripts/action_cable_notifications/collection.coffee
@@ -9,6 +9,9 @@

   # Public methods
   #######################################
+
+  fetch: () ->
+    @channel?.perform?('fetch') if @sync

   where: (selector={}) ->
     _.filter(@data, selector)
@@ -35,9 +38,10 @@
     @update(selector, fields, {upsert: true})

   destroy: (selector={}) ->
-    index = @findIndex(selector)
+    index = _.findIndex(@data, selector)
     if index < 0
       console.warn("Couldn't find a matching record: #{selector}")
     else
-      record = @data.splice(index, 1)
-      @channel?.perform?('remove', {id: record.id}) if @sync
+      record = @data[index]
+      @data.splice(index, 1)
+      @channel?.perform?('destroy', {id: record.id}) if @sync


head: canonical

head: canonical

--- src/components/head/index.coffee
+++ src/components/head/index.coffee
@@ -59,6 +59,7 @@
       # misc
       z 'meta', {name: 'theme-color', content: "#{themeColor}"}
       z 'link', {rel: 'shortcut icon', href: "#{favicon}"}
+      z 'link', {rel: 'canonical', href: 'https://zorium.org'}

       # fonts
       z 'style',


menu: close on second tap for links

menu: close on second tap for links

--- src/components/menu/index.coffee
+++ src/components/menu/index.coffee
@@ -158,12 +158,14 @@
         z '.menu',
           z 'a.logo',
             href: '/'
-            onclick: z.ev (e, $$el) ->
+            onclick: z.ev (e, $$el) =>
               if z.isSimpleClick e
                 e.preventDefault()
                 # Manually scroll because we don't want people to lose
                 # their scoll position on page refresh
                 window.scrollTo(0, 0)
+                unless isPermanent
+                  @toggle()
                 z.router.go $$el.href
             'Zorium'
           z '.break'
@@ -178,6 +180,8 @@
                     if z.isSimpleClick e
                       e.preventDefault()
                       if isSelected
+                        unless isPermanent
+                          @toggle()
                         z.router.go $$el.href
                       else
                         @state.set selected: link.select


fixed #1 issue ( full progress bug for acer )

--- src/mediaMonitor.coffee
+++ src/mediaMonitor.coffee
@@ -69,11 +69,18 @@
   onSeeking:( e )->
     @isEnablePlaying = false
     @clearLastStatus_()
+    @seekingTime_ = @el.currentTime

   #@event
   onSeeked:( e )->
     @isEnablePlaying = true
-    @resetStartTime_ @el.currentTime
+    #fixed a bug that current time is zero when progress is seeked to end
+    #test device [OS]( Android 4.4.2; B1-730HD Build/KOT49H ) [Browser]( Chrome 35.0.1916.141 ) [device builder]( acer )
+    currentTime = @el.currentTime
+    seekingTime = @seekingTime_
+    resetStartTime = if seekingTime != undefined && currentTime != seekingTime then seekingTime else currentTime
+
+    @resetStartTime_ resetStartTime

   #@event
   onLoadedmetadata:( e )->


Transitions can be space separated function names as strings, arrays of functions, functions or arrays of strings..

--- src/view.coffee
+++ src/view.coffee
@@ -234,10 +234,10 @@
         ###
         calcTransition: (from, to) ->
             # Look for a specific transition first
-            transition = @transitions[from + ' ' + to]
+            transitions = @transitions[from + ' ' + to]

             # Go through in order, looking for a wildcard transition to match.
-            unless transition
+            unless transitions
                 key = _.chain(@transitions)
                     .keys()
                     .filter((t) =>
@@ -248,9 +248,18 @@
                     .first()
                     .value()

-                transition = @transitions[key]
-
-            return @[transition]
+                transitions = @transitions[key]
+
+            # Allow functions, space separated strings pointing to functions on
+            # self, arrays of functions, and arrays of strings pointing to
+            # functions on self.
+            if _.isFunction(transitions) then return [transitions]
+            if _.isString(transitions)
+                return (@[t] for t in transitions.split(' '))
+            if _.isArray(transitions)
+                return _.map(transition, (t) ->
+                    return if _.isFunction(t) then t else @[t]
+                )

         ###
         Matches a state with a state rule. Exact matches are true, * is true,


Fixed outlets set to "true" instead of actual value

--- src/view.coffee
+++ src/view.coffee
@@ -19,7 +19,7 @@
 					if outlet.el.type is 'checkbox'
 						outlet.el.checked = if value then true else false
 					else
-						outlet.el.value = value? or ''
+						outlet.el.value = if value? then value or ''
 			img:
 				get: (outlet, view) ->
 					outlet.el.src
@@ -29,7 +29,7 @@
 				get: (outlet, view) ->
 					outlet.el.innerHTML
 				set: (outlet, view, value) ->
-					outlet.el.innerHTML = value? or ''
+					outlet.el.innerHTML = if value? then value or ''

 		@outletTypes.default = @outletTypes.html
 		@outletTypes.textarea = @outletTypes.input


Add the missing data field to the fluid api for autocomplete

--- modules/autocomplete/main/index.coffee
+++ modules/autocomplete/main/index.coffee
@@ -383,9 +383,9 @@
       _.cleanUp = true
     this

-hx.autoComplete = (options) ->
+hx.autoComplete = (data, options) ->
   selection = hx.detached('input')
-  new AutoComplete(selection.node(), options)
+  new AutoComplete(selection.node(), data, options)
   selection

-hx.AutoComplete = AutoComplete+hx.AutoComplete = AutoComplete


neo4j: remove white space from graph.coffee

--- server/lib/server/graph.coffee
+++ server/lib/server/graph.coffee
@@ -1,7 +1,7 @@
 neo4j = require "neo4j"
 {race} = require 'sinkrow'

-module.exports = class Graph
+module.exports = class Graph
   constructor:(config)->
     @db = new neo4j.GraphDatabase(config.host + ":" + config.port);

@@ -26,16 +26,16 @@
     query = [
       'start koding=node:koding(\'id:*\')'
       'where koding.name = "JTutorial"'
-      ' or koding.name = "JCodeSnip"'
-      ' or koding.name = "JDiscussion"'
-      ' or koding.name = "JStatusUpdate"'
+      ' or koding.name = "JCodeSnip"'
+      ' or koding.name = "JDiscussion"'
+      ' or koding.name = "JStatusUpdate"'
       'return *'
       'order by koding.`meta.createdAt` DESC'
-      'limit 4'
+      'limit 4'
     ].join('\n');

     params =
-      itemId : "515360d23af2fb6b6b000009"
+      itemId : "515360d23af2fb6b6b000009"

     @db.query query, params, (err, results)=>
 #      console.log results
@@ -56,11 +56,11 @@
         , ->
           console.log new Date().getTime() - start
           callback null, tempRes
-        resultData = ( result.koding.data for result in results)
+        resultData = ( result.koding.data for result in results)
         objectify resultData, (objecteds)->
           for objected in objecteds
             tempRes.push objected
-            collectRelations objected
+            collectRelations objected

   fecthRelatedItems:(itemId, callback)->
     query = [
@@ -73,7 +73,6 @@
     params =
       itemId : itemId

-
     @db.query query, params, (err, results) ->
       if err then throw err
       resultData = []


Don't kill web server processes

Don't kill web server processes

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -15,16 +15,17 @@
     name : "webServer on port #{webPort}"
     stats_id: "webserver." + process.pid
     interval : 30000
+    librato: KONFIG.librato
     limit_hard  :
       memory   : 300
       callback : ->
         console.log "[WEBSERVER #{webPort}] Using excessive memory, exiting."
         process.exit()
-    die :
-      after: "non-overlapping, random, 3 digits prime-number of minutes"
-      middleware : (name,callback) -> koding.disconnect callback
-      middlewareTimeout : 5000
-    librato: KONFIG.librato
+    # DISABLED TO TEST MEMORY LEAKS
+    # die :
+    #   after: "non-overlapping, random, 3 digits prime-number of minutes"
+    #   middleware : (name,callback) -> koding.disconnect callback
+    #   middlewareTimeout : 5000

   # Services (order is important here)
   services =


[Server] return if subpage is rendered allready

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -337,7 +337,7 @@

           if name is "Develop"
             options = {account, name, section, client, bongoModels}
-            JGroup.render[prefix].subPage options, serveSub
+            return JGroup.render[prefix].subPage options, serveSub

           JName.fetchModels "#{name}/#{section}", (err, models)->
             if err


added Channel endpoint

added Channel endpoint

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -44,12 +44,14 @@
   path += "&target=transformNull(summarize(stats.timers." + domain + ".Channels." + channelId + ".mean,'1day'))";
   var data = [];
   var raw = yield fetchData(path);
-
+  var i = 0;
   _.forEach(raw.data, function (item) {
     data.push({
       load: item[0],
+      avgResp: raw.data1[i][0],
       timestamp: moment.unix(item[1])
     });
+    i++;
   });
   this.body = data
 }`


Minor fix.

Minor fix.

--- src/xapi.coffee
+++ src/xapi.coffee
@@ -63,7 +63,7 @@
       catch error # Captures the error if it was thrown.

       # If it failed too much times, just stops.
-      throw error unless --@tries
+      throw error unless --tries

       # Gets the error code for transport errors and XAPI errors.
       code = error.code or error[0]


upd: doc

upd: doc

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -371,11 +371,10 @@
 	genResolver = (self, state) -> (value) ->
 		resolvePromise self, state, value

-	# AMD Support
+	# CMD & AMD Support
 	if typeof module == $object and typeof module.exports == $object
 		module.exports = Yaku
 	else
-		# CMD
 		if typeof define == $function and define.amd
 			define -> Yaku
 		else


make generic open orders balance list

make generic open orders balance list

--- app/js/services/wallet.coffee
+++ app/js/services/wallet.coffee
@@ -78,12 +78,14 @@
     refresh_open_order_balances: (name) ->
         if !@open_orders_balances[name]
             @open_orders_balances[name] = {}
-        @wallet_api.market_order_list("USD", "BTSX", 200, name).then (result) =>
+        @wallet_api.account_order_list(name).then (result) =>
             angular.forEach result, (order) =>
+                base = @blockchain.asset[order.market_index.order_price.base_asset_id]
+                quote = @blockchain.asset[order.market_index.order_price.quote_asset_id]
                 if order.type == "ask_order"
-                    @open_orders_balances[name]["BTSX"] = @utils.asset(order.state.balance, @blockchain.symbol2records["BTSX"])
+                    @open_orders_balances[name][base.symbol] = @utils.asset(order.state.balance, @blockchain.symbol2records[base.symbol])
                 if order.type == "bid_order" or order.type == "short_order"
-                    @open_orders_balances[name]["USD"] = @utils.asset(order.state.balance, @blockchain.symbol2records["USD"])
+                    @open_orders_balances[name][quote.symbol] = @utils.asset(order.state.balance, @blockchain.symbol2records[quote.symbol])
             console.log @open_orders_balances


Added the ability to query jira to see how many open issues for a project and a fixVersion

--- src/scripts/jira.coffee
+++ src/scripts/jira.coffee
@@ -18,6 +18,7 @@
 #   <Project Key>-<Issue ID> - Displays information about the JIRA ticket (if it exists)
 #   hubot show watchers for <Issue Key> - Shows watchers for the given JIRA issue
 #   hubot show comments for <Issue Key> - Shows the comments for the given JIRA issue
+#   hubot show open issues for <Issue Key> - Shows the open issues for the given JQL
 #   hubot search for <JQL> - Search JIRA with JQL
 #   hubot save filter <JQL> as <name> - Save JIRA JQL query as filter in the brain
 #   hubot use filter <name> - Use a JIRA filter from the brain
@@ -172,6 +173,19 @@
       else
         cb resultText + " (too many to list)"

+  openIssues = (msg, jql, cb) ->
+    get msg, "search/?jql=#{escape(jql)}AND%20status%20%21%3D%20%22Signed%20Off%22", (result) ->
+      if result.errors?
+        return
+
+      resultText = "I found #{result.total} issues for your search."
+      if result.issues.length <= maxlist
+        cb resultText
+        result.issues.forEach (issue) ->
+          cb issue.key + ": " + issue.fields.summary
+      else
+        cb resultText + " (too many to list)"
+
   robot.respond /(show )?watchers (for )?(\w+-[0-9]+)/i, (msg) ->
     if msg.message.user.id is robot.name
       return
@@ -191,6 +205,13 @@
       return

     search msg, msg.match[2], (text) ->
+      msg.reply text
+
+  robot.respond /show (open issues for )?(.*)/i, (msg) ->
+    if msg.message.user.id is robot.name
+      return
+
+    openIssues msg, msg.match[2], (text) ->
       msg.reply text

   robot.respond /([^\w\-]|^)(\w+-[0-9]+)(?=[^\w]|$)/ig, (msg) ->


Fixed small bug where messages were being marked as good punches while still returning a failure message

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -74,13 +74,7 @@
       res.send 'Punch could not be parsed :('
       return
     Organization.spreadsheet.enterPunch(punch, user)
-    .catch(
-      (err) ->
-        Logger.error err
-        robot.send {room: res.message.user.name},
-                  "Something is wrong with your punch, please don't punch dogs."
-    )
-    .done(
+    .then(
       () ->
         client = robot.adapter.client
         params = {
@@ -92,6 +86,13 @@
           if not response.ok
             res.send response.error
     )
+    .catch(
+      (err) ->
+        Logger.error err
+        robot.send {room: res.message.user.name},
+                  "Something is wrong with your punch, please don't punch dogs."
+    )
+    .done()

   # respond to mode


Update complex operator examples

Update complex operator examples

--- examples/complex_operator_examples.coffee
+++ examples/complex_operator_examples.coffee
@@ -6,35 +6,6 @@
     (error) -> console.log("error:", error, "\n")
     -> console.log("completed\n")
   )
-
-Rx.Observable.range(1,2)
-  .flatMap (x) ->
-    Rx.Observable.range(x * 10, 2)
-      .delay(x * 500)
-  .subscribe(createObserver())
-# 10 (at 500 ms)
-# 11 (at 500 ms)
-# 20 (at 1000 ms)
-# 21 (at 1000 ms)
-# completed (at 1000 ms)
-
-Rx.Observable.interval(500)
-  .takeUntil(Rx.Observable.timer(2000))
-  .subscribe(createObserver())
-# 0 (at 500 ms)
-# 1 (at 1000 ms)
-# 2 (at 1500 ms)
-# completed (at 2000 ms)
-
-Rx.Observable.interval(500)
-  .combineLatest(Rx.Observable.interval(700), (a, b) -> {a: a, b: b})
-  .take(4)
-  .subscribe(createObserver())
-# { a: 0, b: 0 } (at 700 ms)
-# { a: 1, b: 0 } (at 1000 ms)
-# { a: 1, b: 1 } (at 1400 ms)
-# { a: 2, b: 1 } (at 1500 ms)
-# completed (at 1500 ms)

 Rx.Observable.interval(300)
   .merge(Rx.Observable.interval(200))
@@ -48,12 +19,37 @@
 # 3 (at 800 ms)
 # completed (at 800 ms)

+Rx.Observable.timer(500)
+  .concat(Rx.Observable.timer(200))
+  .subscribe(createObserver())
+# 0 (at 500 ms)
+# 0 (at 700 ms)
+# completed (at 700 ms)
+
 Rx.Observable.interval(500)
-  .zip(Rx.Observable.interval(200), (a, b) -> {a: a, b: b})
-  .take(4)
+  .zip(Rx.Observable.interval(200), ((a, b) -> {a: a, b: b}))
+  .take(3)
   .subscribe(createObserver())
-# { a: 0, b: 0 } (at 500 ms)
+# { a: 0, b: 1 } (at 500 ms)
 # { a: 1, b: 1 } (at 1000 ms)
 # { a: 2, b: 2 } (at 1500 ms)
-# { a: 3, b: 3 } (at 2000 ms)
+# completed (at 1500 ms)
+
+Rx.Observable.interval(500)
+  .combineLatest(Rx.Observable.interval(200), ((a, b) -> {a: a, b: b}))
+  .take(5)
+  .subscribe(createObserver())
+# { a: 0, b: 1 } (at 500 ms)
+# { a: 0, b: 2 } (at 600 ms)
+# { a: 0, b: 3 } (at 800 ms)
+# { a: 0, b: 4 } (at 1000 ms)
+# { a: 1, b: 4 } (at 1000 ms)
+# completed (at 1000 ms)
+
+Rx.Observable.interval(500)
+  .takeUntil(Rx.Observable.timer(2000))
+  .subscribe(createObserver())
+# 0 (at 500 ms)
+# 1 (at 1000 ms)
+# 2 (at 1500 ms)
 # completed (at 2000 ms)


Fix afm glyph width when not defined

Fix afm glyph width when not defined

--- lib/font/afm.coffee
+++ lib/font/afm.coffee
@@ -104,7 +104,7 @@
     return characters[WIN_ANSI_MAP[character] or character] or '.notdef'

   widthOfGlyph: (glyph) ->
-    return @glyphWidths[glyph]
+    return @glyphWidths[glyph] or 0

   getKernPair: (left, right) ->
     return @kernPairs[left + '\0' + right] or 0


Use new event APIs

Use new event APIs

--- lib/git-view.coffee
+++ lib/git-view.coffee
@@ -22,15 +22,28 @@
     @gitStatus = @gitStatus.element
     @gitStatusIcon = @gitStatusIcon.element

-    @statusBar.subscribeToBuffer 'saved', @update
-    @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', @update
-    @projectPathSubscription = atom.project.on 'path-changed' => @subscribeToRepo()
+    @activeItemSubscription = atom.workspace.onDidChangeActivePaneItem =>
+      @subscribeToActiveItem()
+    @projectPathSubscription = atom.project.on 'path-changed', => @subscribeToRepo()
     @subscribeToRepo()
+    @subscribeToActiveItem()
+
+  subscribeToActiveItem: ->
+    activeItem = @getActiveItem()
+
+    @savedSubscription?.dispose()
+    @savedSubscription = activeItem?.onDidSave? => @update()
+
+    @update()

   destroy: ->
+    @activeItemSubscription.dispose()
+    @projectPathSubscription.off()
+
+    @savedSubscription?.dispose()
     @statusChangedSubscription?.dispose()
     @statusesChangedSubscription?.dispose()
-    @projectPathSubscription.off()
+
     @remove()

   afterAttach: ->


Remove private posts from live view. Fix #151

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -226,6 +226,7 @@
     group: group
     published: $ne: false
     deleted: $ne: true
+    private: $ne: true
   if group == wildGroup
     delete query.group
   query


Use isAtEndOfLine() method instead of homemade

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -133,7 +133,7 @@

       return if @isEndOfFile()

-      if @isEndOfLine()
+      if cursor.isAtEndOfLine()
         cursor.moveDown()
         cursor.moveToBeginningOfLine()
         cursor.skipLeadingWhitespace()
@@ -157,11 +157,6 @@
         @editor.selectToBeginningOfNextWord()

       true
-
-  isEndOfLine: ->
-    cur = @editor.getCursor().getBufferPosition()
-    end = @editor.getCursor().getCurrentLineBufferRange().end
-    cur.row is end.row and cur.column is end.column

   isEndOfFile: ->
     cur = @editor.getCursor().getBufferPosition()


Fixed bug to track which tests succeed and fail.

--- src/main/coffeescript/scxml/test/multi-process-browser/harness.coffee
+++ src/main/coffeescript/scxml/test/multi-process-browser/harness.coffee
@@ -205,8 +205,12 @@
 									response.writeHead 200,{"Content-Type":"text/plain"}
 									console.error "Matched expected configuration."
 									response.end()
-
+
+
+									#if there are no more expected configurations, mark test as succeeded, send the reset event
 									if not testData.expectedConfigurations.length
+										results.testsPassed.push testData.test
+
 										serverClientComm.sendResetEventToClient testData.sourceAddress
 								else
 									response.writeHead 500,{"Content-Type":"text/plain"}
@@ -216,8 +220,9 @@

 									console.error errMsg

-									results.testsFailed.push currentTest
-
+									results.testsFailed.push testData.test
+
+									#test has failed, so we send reset event
 									serverClientComm.sendResetEventToClient testData.sourceAddress
 							catch e
 								console.error e.message


Import just the parse method from espree and esprima.

--- test/es6.coffee
+++ test/es6.coffee
@@ -24,12 +24,12 @@
 'use strict'

 Dumper = require './dumper'
-harmony = require './third_party/esprima'
-espree = require 'espree'
+{parse: esprima} = require './third_party/esprima'
+{parse: espree} = require 'espree'
 {expect} = require 'chai'

 classDeclaration = ->
-    program = harmony.parse """
+    program = esprima """
         class Hello {
         };
     """
@@ -54,7 +54,7 @@

 describe 'class', ->
     it 'declaration#1', ->
-        tree = harmony.parse """
+        tree = esprima """
         class Hello extends World {
             method() {
             }
@@ -85,7 +85,7 @@
         """

     it 'declaration#2', ->
-        tree = harmony.parse """
+        tree = esprima """
         class Hello extends ok() {
             method() {
             }
@@ -238,7 +238,7 @@

 describe 'import', ->
     it 'default specifier #1', ->
-        tree = espree.parse """
+        tree = espree """
         import Cocoa from 'rabbit-house'
         """, {
             ecmaFeatures: {
@@ -260,7 +260,7 @@
         """

     it 'named specifier #1', ->
-        tree = espree.parse """
+        tree = espree """
         import {Cocoa, Cappuccino as Chino} from 'rabbit-house'
         """, {
             ecmaFeatures: {
@@ -290,7 +290,7 @@
         """

     it 'namespace specifier #1', ->
-        tree = espree.parse """
+        tree = espree """
         import * as RabbitHouse from 'rabbit-house'
         """, {
             ecmaFeatures: {


:gift: new InsertByMotion can insert after move, and repeatable.

This enhance operation repeatability greatly.
e.g. When you map `gi` to insert-by-motion, then `gi{\n…` insert new
line to each paragraph for total 4 time(first one plus three period)

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -776,10 +776,10 @@
   execute: ->
     if @isRepeated()
       return unless text = @getText()
-      unless @instanceof('Change') # Don't select twice!
+      unless @instanceof('Change')
         @flashTarget = @trackChange = true
-        @setTarget @new('NullMotion')
-        @selectTarget()
+        @observeSelectAction()
+        @emitDidSelect()
       @editor.transact =>
         for s in @editor.getSelections()
           @repeatInsert(s, text)
@@ -825,16 +825,26 @@
     @editor.moveToFirstCharacterOfLine()
     super

-class InsertAtPreviousFoldStart extends ActivateInsertMode
-  @extend()
-  motion: 'MoveToPreviousFoldStart'
-  execute: ->
-    @new(@motion).execute()
-    super
+class InsertByMotion extends ActivateInsertMode
+  @extend()
+  requireTarget: true
+  execute: ->
+    if @target.instanceof('Motion')
+      @target.execute()
+    if @instanceof('InsertAfterByMotion')
+      moveCursorRight(c) for c in @editor.getCursors()
+    super
+
+class InsertAfterByMotion extends InsertByMotion
+  @extend()
+
+class InsertAtPreviousFoldStart extends InsertByMotion
+  @extend()
+  target: 'MoveToPreviousFoldStart'

 class InsertAtNextFoldStart extends InsertAtPreviousFoldStart
   @extend()
-  motion: 'MoveToNextFoldStart'
+  target: 'MoveToNextFoldStart'

 # FIXME need support count
 class InsertAboveWithNewline extends ActivateInsertMode


:gift: trackChange(update mark [,]) when insert repeated

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -41,6 +41,9 @@
   needFlash: ->
     @flashTarget and settings.get('flashOnOperate') and not @instanceof('Select')

+  needTrackChange: ->
+    @trackChange
+
   needStay: ->
     param = if @instanceof('TransformString')
       "stayOnTransformString"
@@ -50,6 +53,9 @@

   constructor: ->
     super
+    # Guard when Repeated.
+    return if @instanceof("Repeat")
+
     @setTarget @new(@target) if @target?
     #  To support, `dd`, `cc` and a like.
     if @isSameOperatorRepeated()
@@ -62,17 +68,17 @@
       @onDidSelect =>
         @flash @editor.getSelectedBufferRanges()

-    if @trackChange
+    if @needTrackChange()
       changeMarker = null
       @onDidSelect =>
-        return if (selection = @editor.getLastSelection()).isEmpty()
-        changeMarker = @editor.markBufferRange selection.getBufferRange(),
+        range = @editor.getSelectedBufferRange()
+        changeMarker = @editor.markBufferRange range,
           invalidate: 'never'
           persistent: false

       @onDidOperationFinish =>
-        if range = changeMarker?.getBufferRange()
-          @setMarkForChange(range)
+        if newRange = changeMarker.getBufferRange()
+          @setMarkForChange(newRange)

   # target - TextObject or Motion to operate on.
   setTarget: (@target) ->
@@ -535,47 +541,53 @@
     {text, type} = @vimState.register.get()
     return unless text
     text = _.multiplyString(text, @getCount())
-    paste = switch
-      when type is 'linewise', @vimState.isMode('visual', 'linewise')
-        @pasteLinewise
-      when 'character'
-        @pasteCharacterwise
+    isLinewise = type is 'linewise' or @vimState.isMode('visual', 'linewise')

     @editor.transact =>
-      paste(s, text) for s in @editor.getSelections()
+      for s in @editor.getSelections()
+        {cursor} = s
+        if isLinewise
+          newRange = @pasteLinewise(s, text)
+          cursor.setBufferPosition(newRange.start)
+          cursor.moveToFirstCharacterOfLine()
+        else
+          newRange = @pasteCharacterwise(s, text)
+          cursor.setBufferPosition(newRange.end.translate([0, -1]))
+        @setMarkForChange(newRange)
+        @flash newRange
     @activate('normal')

-  pasteLinewise: (selection, text) => # fat
-    cursor = selection.cursor
+  # Return newRange
+  pasteLinewise: (selection, text) ->
+    {cursor} = selection
     if selection.isEmpty()
+      text = text.replace(/\n$/, '')
       if @location is 'before'
-        cursor.moveToBeginningOfLine()
-        selection.insertText("\n")
-        cursor.moveUp()
+        @insertTextAbove(selection, text)
       else
-        cursor.moveToEndOfLine()
-        selection.insertText("\n")
-      text = text.replace(/\n$/, '')
+        @insertTextBelow(selection, text)
     else
       if @vimState.isMode('visual', 'linewise')
         text += '\n' unless text.endsWith('\n')
       else
         selection.insertText("\n")
-    range = selection.insertText(text)
-    @setMarkForChange(range)
-    @flash range
-    cursor.setBufferPosition(range.start)
-    cursor.moveToFirstCharacterOfLine()
-
-  pasteCharacterwise: (selection, text) => # fat
-    cursor = selection.cursor
+      selection.insertText(text)
+
+  pasteCharacterwise: (selection, text) ->
     if @location is 'after' and selection.isEmpty()
-      cursor.moveRight()
-    range = selection.insertText(text)
-    @setMarkForChange(range)
-
-    @flash range
-    cursor.setBufferPosition(range.end.translate([0, -1]))
+      selection.cursor.moveRight()
+    selection.insertText(text)
+
+  insertTextAbove: (selection, text) ->
+    selection.cursor.moveToBeginningOfLine()
+    selection.insertText("\n")
+    selection.cursor.moveUp()
+    selection.insertText(text)
+
+  insertTextBelow: (selection, text) ->
+    selection.cursor.moveToEndOfLine()
+    selection.insertText("\n")
+    selection.insertText(text)

 class PutAfter extends PutBefore
   @extend()
@@ -656,17 +668,19 @@
     @vimState.register.getText('.')

   # called when repeated
-  insertText: (selection, text) ->
+  repeatInsert: (selection, text) ->
     selection.insertText(text, autoIndent: true)

   execute: ->
     if @isRepeated()
       return unless text = @getText()
+      @flashTarget = @trackChange = true
+      @observeSelectAction()
+      @emitDidSelect()
       @editor.transact =>
-        for selection in @editor.getSelections()
-          {cursor} = selection
-          @insertText(selection, text)
-          cursor.moveLeft() unless cursor.isAtBeginningOfLine()
+        for s in @editor.getSelections()
+          @repeatInsert(s, text)
+          s.cursor.moveLeft() unless s.cursor.isAtBeginningOfLine()
     else
       @setCheckpoint('insert')
       @vimState.activate('insert', @submode)
@@ -683,7 +697,7 @@
   @extend()
   submode: 'replace'

-  insertText: (selection, text) ->
+  repeatInsert: (selection, text) ->
     for char in text when char isnt "\n"
       break if selection.cursor.isAtEndOfLine()
       selection.selectRight()
@@ -717,7 +731,7 @@
   insertNewline: ->
     @editor.insertNewlineAbove()

-  insertText: (selection, text) ->
+  repeatInsert: (selection, text) ->
     selection.insertText(text.trimLeft(), autoIndent: true)

 class InsertBelowWithNewline extends InsertAboveWithNewline


Refactor specs

Refactor specs

--- spec/chart-d3-line-spec.coffee
+++ spec/chart-d3-line-spec.coffee
@@ -3,6 +3,8 @@

   args = {}
   instance = undefined
+  xScale = undefined
+  yScale = undefined

   before ->
     args.data =
@@ -24,22 +26,22 @@
       animationSteps: 60
       animationEasing: 'easeOutQuad'
     instance = new Chart.D3Line('#svg', args.data, args.options)
+    xScale = Chart.D3Line.xScale([0, 0], 0)
+    yScale = Chart.D3Line.yScale([], 0)

   after ->
-    args = {}
+    args = null
     instance = null
+    xScale = null
+    yScale = null

   describe '.area', ->
     it 'should return a function', ->
-      xScale = Chart.D3Line.xScale([0, 0], 0)
-      yScale = Chart.D3Line.yScale([], 0)
       actual = Chart.D3Line.area(xScale, yScale, args.data.labels, 0)
       expect(actual).to.be.a('function')

   describe '.line', ->
     it 'should return a function', ->
-      xScale = Chart.D3Line.xScale([0, 0], 0)
-      yScale = Chart.D3Line.yScale([], 0)
       actual = Chart.D3Line.line(xScale, yScale, args.data.labels)
       expect(actual).to.be.a('function')

@@ -71,15 +73,11 @@

   describe '::renderAreas', ->
     it 'should return an array', ->
-      xScale = Chart.D3Line.xScale([0, 0], 0)
-      yScale = Chart.D3Line.yScale([], 0)
       actual = instance.renderAreas([], [], xScale, yScale, 0, {})
       expect(actual).to.be.an(Array)

   describe '::renderLines', ->
     it 'should return an array', ->
-      xScale = Chart.D3Line.xScale([0, 0], 0)
-      yScale = Chart.D3Line.yScale([], 0)
       line = Chart.D3Line.line(xScale, yScale, {})
       actual = instance.renderLines(line, {}, {})
       expect(actual).to.be.an(Array)


filter out 'upgarde damage' nodes

filter out 'upgarde damage' nodes

--- scripts/bungie-data-helper.coffee
+++ scripts/bungie-data-helper.coffee
@@ -84,7 +84,8 @@
   'filterNodes': (nodes, nodeDefs) ->
     validNodes = []
     invalid = (node) ->
-      node.stateId is "Invalid" or node.hidden is true
+      name = nodeDefs[node.nodeIndex].steps[node.stepIndex].nodeStepName
+      node.stateId is "Invalid" or node.hidden is true or name is "Upgrade Damage"

     validNodes.push node for node in nodes when not invalid(node)


Fix hacks order

Fix hacks order

--- lib/prefixes.coffee
+++ lib/prefixes.coffee
@@ -21,6 +21,7 @@
 Declaration.hack require('./hacks/flex-wrap')
 Declaration.hack require('./hacks/align-self')
 Declaration.hack require('./hacks/flex-basis')
+Declaration.hack require('./hacks/mask-border')
 Declaration.hack require('./hacks/align-items')
 Declaration.hack require('./hacks/flex-shrink')
 Declaration.hack require('./hacks/break-props')
@@ -29,7 +30,6 @@
 Declaration.hack require('./hacks/align-content')
 Declaration.hack require('./hacks/border-radius')
 Declaration.hack require('./hacks/block-logical')
-Declaration.hack require('./hacks/mask-border')
 Declaration.hack require('./hacks/inline-logical')
 Declaration.hack require('./hacks/transform-decl')
 Declaration.hack require('./hacks/flex-direction')


quitting the redis client on testmode when it is idle

--- lib/r2gredis.coffee
+++ lib/r2gredis.coffee
@@ -5,5 +5,7 @@
     r = redis.createClient()
     if process.env.NODE_ENV == "test"
       r.select 15
+      r.on "idle", ->
+        r.quit()
     return r
 }


text-decoration: normal to none

text-decoration: normal to none

--- lib/psd/layer_info/typetool.coffee
+++ lib/psd/layer_info/typetool.coffee
@@ -86,7 +86,7 @@
       if f.Underline
         decoration = 'underline';
       else
-        decoration = 'normal';
+        decoration = 'none';
       return decoration

   sizes: ->


:white_check_mark: More specs

:white_check_mark: More specs

--- spec/sync-settings-spec.coffee
+++ spec/sync-settings-spec.coffee
@@ -33,8 +33,6 @@
     TOKEN_CONFIG = 'sync-settings.personalAccessToken'
     GIST_ID_CONFIG = 'sync-settings.gistId'

-    token = null
-    gistId = null
     SyncSettings.activate()

     beforeEach ->
@@ -42,20 +40,16 @@
       atom.config.set(TOKEN_CONFIG, @token)

       run (cb) ->
-        console.debug "Creating test gist..."
-        gist =
+        gistSettings =
           public: false
           description: "Test gist by Sync Settings for Atom https://github.com/Hackafe/atom-sync-settings"
-          files:
-            "README": {content: '# Generated by Sync Settings for Atom https://github.com/Hackafe/atom-sync-settings'}
-        SyncSettings.createClient().gists.create(gist, cb)
+          files: README: content: '# Generated by Sync Settings for Atom https://github.com/Hackafe/atom-sync-settings'
+        SyncSettings.createClient().gists.create(gistSettings, cb)
       , (err, res) =>
-        console.debug "Created test gist"
         expect(err).toBeNull()

         @gistId = res.id
         atom.config.set(GIST_ID_CONFIG, @gistId)
-

     afterEach ->
       run (cb) =>
@@ -74,6 +68,16 @@
           , (err, res) ->
             expect(res.files['settings.json']).toBeDefined()

+      it "don't back up the settings", ->
+        atom.config.set('sync-settings.syncSettings', false)
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(res.files['settings.json']).not.toBeDefined()
+
       it "back up the installed packages list", ->
         atom.config.set('sync-settings.syncPackages', true)
         run (cb) ->
@@ -84,6 +88,16 @@
           , (err, res) ->
             expect(res.files['packages.json']).toBeDefined()

+      it "don't back up the installed packages list", ->
+        atom.config.set('sync-settings.syncPackages', false)
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(res.files['packages.json']).not.toBeDefined()
+
       it "back up the user keymaps", ->
         atom.config.set('sync-settings.syncKeymap', true)
         run (cb) ->
@@ -94,6 +108,16 @@
           , (err, res) ->
             expect(res.files['keymap.cson']).toBeDefined()

+      it "don't back up the user keymaps", ->
+        atom.config.set('sync-settings.syncKeymap', false)
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(res.files['keymap.cson']).not.toBeDefined()
+
       it "back up the user styles", ->
         atom.config.set('sync-settings.syncStyles', true)
         run (cb) ->
@@ -104,6 +128,16 @@
           , (err, res) ->
             expect(res.files['styles.less']).toBeDefined()

+      it "don't back up the user styles", ->
+        atom.config.set('sync-settings.syncStyles', false)
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(res.files['styles.less']).not.toBeDefined()
+
       it "back up the user init.coffee file", ->
         atom.config.set('sync-settings.syncInit', true)
         run (cb) ->
@@ -114,6 +148,16 @@
           , (err, res) ->
             expect(res.files['init.coffee']).toBeDefined()

+      it "don't back up the user init.coffee file", ->
+        atom.config.set('sync-settings.syncInit', false)
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(res.files['init.coffee']).not.toBeDefined()
+
       it "back up the user snippets", ->
         atom.config.set('sync-settings.syncSnippets', true)
         run (cb) ->
@@ -124,6 +168,16 @@
           , (err, res) ->
             expect(res.files['snippets.cson']).toBeDefined()

+      it "don't back up the user snippets", ->
+        atom.config.set('sync-settings.syncSnippets', false)
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(res.files['snippets.cson']).not.toBeDefined()
+
       it "back up the files defined in config.extraFiles", ->
         atom.config.set 'sync-settings.extraFiles', ['test.tmp', 'test2.tmp']
         run (cb) ->
@@ -134,6 +188,16 @@
           , (err, res) ->
             for file in atom.config.get 'sync-settings.extraFiles'
               expect(res.files[file]).toBeDefined()
+
+      it "don't back up extra files defined in config.extraFiles", ->
+        atom.config.set 'sync-settings.extraFiles', undefined
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            expect(Object.keys(res.files).length).toBe(1)

     describe "::restore", ->
       it "updates settings", ->
@@ -148,6 +212,17 @@
           , ->
             expect(atom.config.get "some-dummy").toBeTruthy()

+      it "doesn't updates settings", ->
+        atom.config.set('sync-settings.syncSettings', false)
+        atom.config.set "some-dummy", true
+        run (cb) ->
+          SyncSettings.backup cb
+        , ->
+          run (cb) ->
+            SyncSettings.restore cb
+          , ->
+            expect(atom.config.get "some-dummy").toBeTruthy()
+
       it "overrides keymap.cson", ->
         atom.config.set('sync-settings.syncKeymap', true)
         original = SyncSettings.fileContent(atom.keymaps.getUserKeymapPath()) ? "# keymap file (not found)"


Make a check more coffeescript-y

Make a check more coffeescript-y

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -92,7 +92,7 @@

   # postUpdateHooks convenience wrapper
   performPostUpdateHook: (key, value) ->
-    @postUpdateHooks[key] value if @postUpdateHooks[key]
+    @postUpdateHooks[key]? value

   # Only ever called from asynchronous synced-storage callbacks (fetchAsync and handleStorageUpdate).
   storeAndPropagate: (key, value) ->


Streamlined the implementation of Sortable.

--- lib/sortable.coffee
+++ lib/sortable.coffee
@@ -1,7 +1,8 @@
 # --------------------------------------------------------------------
-# An implementation of Chris Okasaki's bottom-up-merge sortable.
+# An implementation of Chris Okasaki's shared bottom-up-merge
+# sortable.
 #
-# The recursive helper methods merge(), addSeg() and mergeAll() from
+# The recursive helper functions merge(), addSeg() and mergeAll() from
 # the original algorithm have been replaced with iterative
 # implementations to avoid stack overflow, and the new code for
 # mergeAll() then inserted into sort().
@@ -20,58 +21,47 @@
   suspend = pazy.suspend

-class List
-  constructor: (@first, rest) ->
-    @rest = if rest? then rest else null
+merge = (less, xs, ys) ->
+  [buf, ix, iy, lx, ly] = [[], 0, 0, xs.length, ys.length]

-  each: (func) ->
-    list = this
-    while list and list.first?
-      val = func(list.first)
-      list = list.rest
-    val
+  while ix < lx and iy < ly
+    if less(xs[ix], ys[iy])
+      buf.push(xs[ix])
+      ix += 1
+    else
+      buf.push(ys[iy])
+      iy += 1

-  toArray: ->
-    buffer = []
-    @each (x) -> buffer.push(x)
-    buffer
+  buf.concat(xs[ix..], ys[iy..])

-  toString: ->
-    data = "[#{a}]" for a in @toArray()
-    "List(#{data.join(", ")})"
+
+addSegment = (less, seg, segs, bits) ->
+  while bits % 2 > 0
+    [seg, segs, bits] = [merge(less, seg, segs[0]), segs[1], bits >> 1]
+  [seg, segs]

 class Sortable
   constructor: (@less, size, segments) ->
     [@size, @_segs] = if size? then [size, segments] else [0, -> null]

-  _merge: (xs, ys) ->
-    [buf, ix, iy, lx, ly] = [[], 0, 0, xs.length, ys.length]
-
-    while ix < lx and iy < ly
-      if @less(xs[ix], ys[iy])
-        buf.push(xs[ix])
-        ix += 1
-      else
-        buf.push(ys[iy])
-        iy += 1
-
-    buf.concat(xs[ix..], ys[iy..])
-
-  _addSeg: (seg, segs, bits) ->
-    while bits % 2 > 0
-      [seg, segs, bits] = [@_merge(seg, segs.first), segs.rest, bits >> 1]
-    new List(seg, segs)
-
   plus: (x) ->
-    new Sortable(@less, @size + 1, suspend(=> @_addSeg([x], @_segs(), @size)))
+    new Sortable(@less, @size + 1,
+                 suspend(=> addSegment(@less, [x], @_segs(), @size)))

   sort: ->
     [buf, segs] = [[], @_segs()]
     while segs?
-      [buf, segs] = [@_merge(buf, segs.first), segs.rest]
+      [buf, segs] = [merge(@less, buf, segs[0]), segs[1]]
     buf

+
+segsToString = (segs) ->
+  buf = []
+  while segs?
+    buf.push(segs[0])
+    segs = segs[1]
+  ("[#{a}]" for a in buf).join(", ")

 puts = (s) -> print s + '\n'

@@ -79,5 +69,5 @@
 b = a.plus(3).plus(4).plus(2).plus(5).plus(1).plus(7).plus(6)

 puts "b.size    = #{b.size}"
-puts "b._segs() = #{b._segs()}"
+puts "b._segs() = #{segsToString(b._segs())}"
 puts "b.sort()  = #{b.sort()}"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/title_screen.coffee
+++ src/title_screen.coffee
@@ -15,7 +15,7 @@
 ###

 ###*
-Goes to the next level on any user input.
+Draws the title text

 @name update
 @methodOf TitleScreen#


removed obsolete hook

removed obsolete hook

--- app/assets/javascripts/species/routes/taxon_concept_route.js.coffee
+++ app/assets/javascripts/species/routes/taxon_concept_route.js.coffee
@@ -2,15 +2,6 @@

   model: (params) ->
     Species.TaxonConcept.find(params.taxon_concept_id)
-
-
-  setupController: (controller, model) ->
-    # Call _super for default behavior (as of rc4)
-    this._super(controller, model)
-    # If the route is reached using a {{#linkTo route myObject}} or
-    # transitionTo(myObject) call then the passed object is used to call
-    # setupController directly and model is not called.
-    # We might need to revisit this when loading particular tabs.

   renderTemplate: ->
     taxonConceptController = @controllerFor('taxonConcept')


Don't keep repeating keyboard deprecation warning

--- src/core/bindEvents.coffee
+++ src/core/bindEvents.coffee
@@ -75,8 +75,8 @@
       lc.pointerMove(coordsForTouchEvent(canvas, e)...)

   if panWithKeyboard
+    console.warn("Keyboard panning is deprecated.")
     listener = (e) ->
-      console.warn("Keyboard panning is deprecated.")
       switch e.keyCode
         when 37 then lc.pan -10, 0
         when 38 then lc.pan 0, -10


Fixes saving (loading) for docs with videos

--- src/core/normalizer.coffee
+++ src/core/normalizer.coffee
@@ -134,7 +134,7 @@
   @pullBlocks: (lineNode) ->
     curNode = lineNode.firstChild
     while curNode?
-      if dom.BLOCK_TAGS[curNode.tagName]? and curNode.tagName != 'LI'
+      if dom.BLOCK_TAGS[curNode.tagName]? and curNode.tagName != 'LI' and curNode.tagName != 'IFRAME'
         dom(curNode).isolate(lineNode.parentNode)
         if (!dom.LIST_TAGS[curNode.tagName]? or !curNode.firstChild) and curNode.tagName != 'IFRAME'
           dom(curNode).unwrap()


retry sending heartbeat to wakatime-cli if executing process raises exception

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -324,10 +324,13 @@
     statusBarIcon?.setStatus('extracting python...')

     unzip(zipFile, __dirname + path.sep + 'python', ->
-      fs.unlink(zipFile)
-      log.debug 'Finished installing python.'
-      if callback?
-        callback()
+      fs.unlink(zipFile, (e) ->
+        if e?
+          log.warn e
+        log.debug 'Finished installing python.'
+        if callback?
+          callback()
+      )
     )
   )

@@ -443,9 +446,12 @@
     catch e
       log.warn e
     finally
-      fs.unlink(file)
-      if callback?
-        callback()
+      fs.unlink(file, (err) ->
+        if err?
+          log.warn err
+        if callback?
+          callback()
+      )

 sendHeartbeat = (file, lineno, isWrite) ->
   if (not file.path? or file.path is undefined) and (not file.getPath? or file.getPath() is undefined)
@@ -485,43 +491,59 @@
             args.push(path.basename(realPath))
             break

-      log.debug python + ' ' + args.join(' ')
-
-      proc = child_process.execFile(python, args, (error, stdout, stderr) ->
-        if error?
-          if stderr? and stderr != ''
-            log.warn stderr
-          if stdout? and stdout != ''
-            log.warn stdout
-          if proc.exitCode == 102
-            msg = null
-            status = null
-            title = 'WakaTime Offline, coding activity will sync when online.'
-          else if proc.exitCode == 103
-            msg = 'An error occured while parsing ~/.wakatime.cfg. Check ~/.wakatime.log for more info.'
-            status = 'Error'
-            title = msg
-          else if proc.exitCode == 104
-            msg = 'Invalid API Key. Make sure your API Key is correct!'
-            status = 'Error'
-            title = msg
-          else
-            msg = error
-            status = 'Error'
-            title = 'Unknown Error (' + proc.exitCode + '); Check your Dev Console and ~/.wakatime.log for more info.'
-
-          if msg?
-            log.warn msg
-          statusBarIcon?.setStatus(status)
-          statusBarIcon?.setTitle(title)
-
-        else
-          statusBarIcon?.setStatus()
-          today = new Date()
-          statusBarIcon?.setTitle('Last heartbeat sent ' + formatDate(today))
-      )
       lastHeartbeat = time
       lastFile = currentFile
+
+      log.debug python + ' ' + args.join(' ')
+      executeHeartbeatProcess python, args, 0
+
+executeHeartbeatProcess = (python, args, tries) ->
+  max_retries = 5
+  try
+    proc = child_process.execFile(python, args, (error, stdout, stderr) ->
+      if error?
+        if stderr? and stderr != ''
+          log.warn stderr
+        if stdout? and stdout != ''
+          log.warn stdout
+        if proc.exitCode == 102
+          msg = null
+          status = null
+          title = 'WakaTime Offline, coding activity will sync when online.'
+        else if proc.exitCode == 103
+          msg = 'An error occured while parsing ~/.wakatime.cfg. Check ~/.wakatime.log for more info.'
+          status = 'Error'
+          title = msg
+        else if proc.exitCode == 104
+          msg = 'Invalid API Key. Make sure your API Key is correct!'
+          status = 'Error'
+          title = msg
+        else
+          msg = error
+          status = 'Error'
+          title = 'Unknown Error (' + proc.exitCode + '); Check your Dev Console and ~/.wakatime.log for more info.'
+
+        if msg?
+          log.warn msg
+        statusBarIcon?.setStatus(status)
+        statusBarIcon?.setTitle(title)
+
+      else
+        statusBarIcon?.setStatus()
+        today = new Date()
+        statusBarIcon?.setTitle('Last heartbeat sent ' + formatDate(today))
+    )
+  catch e
+    tries++
+    retry_in = 2
+    if tries < max_retries
+      log.debug 'Failed to send heartbeat by executing wakatime-cli, will retry in ' + retry_in + ' seconds...'
+      setTimeout ->
+        executeHeartbeatProcess(python, args, tries)
+      , retry_in * 1000
+    else
+      log.error 'Failed to send heartbeat by executing wakatime-cli:'
+      log.error e

 fileIsIgnored = (file) ->
   if endsWith(file, 'COMMIT_EDITMSG') or endsWith(file, 'PULLREQ_EDITMSG') or endsWith(file, 'MERGE_MSG') or endsWith(file, 'TAG_EDITMSG')


re-raise original exception after retries exhausted

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -537,13 +537,13 @@
     tries++
     retry_in = 2
     if tries < max_retries
-      log.debug 'Failed to send heartbeat by executing wakatime-cli, will retry in ' + retry_in + ' seconds...'
+      log.debug 'Failed to send heartbeat when executing wakatime-cli background process, will retry in ' + retry_in + ' seconds...'
       setTimeout ->
         executeHeartbeatProcess(python, args, tries)
       , retry_in * 1000
     else
-      log.error 'Failed to send heartbeat by executing wakatime-cli:'
-      log.error e
+      log.error 'Failed to send heartbeat when executing wakatime-cli background process.'
+      throw e

 fileIsIgnored = (file) ->
   if endsWith(file, 'COMMIT_EDITMSG') or endsWith(file, 'PULLREQ_EDITMSG') or endsWith(file, 'MERGE_MSG') or endsWith(file, 'TAG_EDITMSG')


fix eof

fix eof

--- BackofficeBundle/Resources/public/coffee/contentTypeFormAddFieldListener.coffee
+++ BackofficeBundle/Resources/public/coffee/contentTypeFormAddFieldListener.coffee
@@ -8,4 +8,5 @@
     # display error
     $(form).prepend(errorMessage);
     return false
-  return true+  return true
+


game state

game state

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -64,7 +64,7 @@

     matcher = Engine.Selector.generate(selector)

-    I.objects.each (object) ->
+    self.objects().each (object) ->
       results.push object if matcher.match object

     Object.extend results, instanceMethods


Use customClass instead of looking for a class

--- packages/rocketchat-ui/views/app/spotlight/mobileMessageMenu.coffee
+++ packages/rocketchat-ui/views/app/spotlight/mobileMessageMenu.coffee
@@ -16,17 +16,7 @@
 			mobileMessageMenu.reportAbuse
 		]

-		context = 'message-mobile'
-		if $(e.currentTarget).hasClass('pinned')
-			context = 'pinned'
-		if $(e.currentTarget).hasClass('starred')
-			context = 'starred'
-		if $(e.currentTarget).hasClass('mentions')
-			context = 'mentions'
-		if $(e.currentTarget).hasClass('search')
-			context = 'search'
-
-		buttons = RocketChat.MessageAction.getButtons message, context
+		buttons = RocketChat.MessageAction.getButtons message, (message.customClass or 'message-mobile')
 		for button in buttons
 			if button.id is 'delete-message'
 				options.addDestructiveButtonWithLabel = TAPi18n.__(button.i18nLabel)


do not double visit on 'active' nav tests

- may prevent flake from visiting the guides twice in that first active
test.

--- cypress/integration/main_spec.coffee
+++ cypress/integration/main_spec.coffee
@@ -57,10 +57,10 @@
         cy.url().should("include", GUIDES_PATH)

   context "Navigation", ->
-    beforeEach ->
-      cy.visit("/")
+    it "displays links to pages", ->
+      beforeEach ->
+        cy.visit("/")

-    it "displays links to pages", ->
       cy.contains(".main-nav-link", "Guides")
         .should("have.attr", "href").and("include", GUIDES_PATH)

@@ -74,11 +74,13 @@
         .should("have.attr", "href").and("include", FAQ_PATH)

     it "displays link to github repo", ->
+      cy.visit("/")
       cy.get(".main-nav-link").find(".fa-github").parent()
         .should("have.attr", "href")
         .and("eq", "https://github.com/cypress-io/cypress")

     it "displays language dropdown", ->
+      cy.visit("/")
       cy.contains("select", "English").find("option").contains("English")

     describe "active nav", ->


Adjust initialization for this being the matched element's ElementWrapper

--- tapestry-core/src/main/coffeescript/META-INF/modules/core/zone.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/core/zone.coffee
@@ -20,17 +20,16 @@
   (spi, events) ->
     spi.domReady ->
       spi.body().on events.zone.update, (event) ->
-        zone = spi.wrap this

-        zone.trigger events.zone.willUpdate
+        this.trigger events.zone.willUpdate

         # TODO: purge existing children?

-        zone.update event.memo
+        this.update event.memo

-        zone.show() unless zone.visible()
+        this.show() unless this.visible()

-        zone.trigger events.zone.didUpdate
+        this.trigger events.zone.didUpdate

     # No meaningful value is returned.
-    true+    return


remove console.log

remove console.log

--- BackofficeBundle/Resources/public/coffee/tab/TabElementFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/tab/TabElementFormView.coffee
@@ -17,7 +17,6 @@
     @options.domContainer = @$el if !@options.submitted

   render: ->
-    console.log @options.response
     @options.domContainer.html @options.response
     @options.domContainer.append @renderTemplate('OpenOrchestraBackofficeBundle:BackOffice:Underscore/backToList',
         listUrl : @options.listUrl


Disable chart animation.

Disable chart animation.

--- src/main/webapp/coffeescripts-hidden/weeks-performance-graph.coffee
+++ src/main/webapp/coffeescripts-hidden/weeks-performance-graph.coffee
@@ -24,6 +24,7 @@
     stepWidth = ceilingValue / 10

     options = {
+      animation: false,
       scaleOverride: true,
       scaleSteps: 10,
       scaleStepWidth: stepWidth,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/string_extensions.coffee
+++ test/string_extensions.coffee
@@ -33,7 +33,7 @@

 test "#startsWith", ->
   ok "cool".startsWith("coo")
-  equals
+  equals f

 test "#titleize", ->
   equals "man from the boondocks".titleize(), "Man From The Boondocks"


add don't close dialog support to new card dialog

--- lib/carte/client/views/edit.cjsx
+++ lib/carte/client/views/edit.cjsx
@@ -4,6 +4,7 @@
 Modal = require('react-bootstrap/lib/Modal')
 Button = require('react-bootstrap/lib/Button')
 TagsInput = require('react-tagsinput')
+CardModel = require('../models/card')

 module.exports = React.createClass
   mixins: [React.addons.LinkedStateMixin]
@@ -17,12 +18,17 @@
     tags: @props.card.get('tags') || []
     errors: false
     shaking: false
+    dontCloseDialog: false
+    createSuccess: false

   onChangeTitle: (event)->
     @setState title: event.target.value

   onChangeContent: (event)->
     @setState content: event.target.value
+
+  onChangeDontCloseDialog: (event)->
+    @setState dontCloseDialog: !@state.dontCloseDialog

   onClickOk: (event)->
     event.preventDefault()
@@ -34,10 +40,19 @@
     @props.card.save attributes,
       success: ()=>
         @setState updating: false
-        @props.onRequestHide()
-        @props.card.set 'title', @state.title
         if @props.card.isNew()
-          location.hash = '/' + @state.title
+          if @state.dontCloseDialog
+            @setState
+              createSuccess: true
+              title: ''
+              content: ''
+              tags: []
+            @props.card = new CardModel()
+            @props.card._isNew = true
+          else
+            @props.onRequestHide()
+            @props.card.set 'title', @state.title
+            location.hash = '/' + @state.title
       error: (model, response, options)=>
         console.log response.responseJSON
         @setState errors: response.responseJSON.card.errors
@@ -59,6 +74,11 @@
               }

+          else if @state.createSuccess
+

+
+              You created a card successfully. Let's create next one.
+

         }

           Title
@@ -72,6 +92,14 @@
           Tags
           <TagsInput ref='tags' valueLink={this.linkState('tags')} />

+        {
+          if @props.card.isNew()
+

+
+                <input type="checkbox" checked={@state.dontCloseDialog} onChange={@onChangeDontCloseDialog} /> Don't Close Dialog
+
+

+        }

           <button className="btn btn-default pull-right" onClick={@onClickOk} disabled={@state.updating}>


JCredential: one: user nonplural fetcher, cleanup selector

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -124,15 +124,13 @@

     success: (client, publicKey, callback)->

-      options  = { limit : 1 }
-      selector = { publicKey }
+      options =
+        limit         : 1
+        targetOptions : selector : { publicKey }

       {delegate} = client.connection
-      delegate.fetchCredentials {},
-        targetOptions : {
-          selector, options
-        }, (err, res = [])->
-          callback err, res[0]
+      delegate.fetchCredential { }, options, (err, res)->
+        callback err, res

   @some$: permit 'list credentials',


Made the "Exp to next level" field read only. It doesn't make sense to modify this value

--- index.cjsx
+++ index.cjsx
@@ -183,8 +183,6 @@
       @handleExpChange _currentLevel, _nextExp, _goalLevel, @state.mapValue, @state.mapPercent
     handleCurrentLevelChange: (e) ->
       @handleExpChange e.target.value, @state.nextExp, @state.goalLevel, @state.mapValue, @state.mapPercent
-    handleNextExpChange: (e) ->
-      @handleExpChange @state.currentLevel, e.target.value, @state.goalLevel, @state.mapValue, @state.mapPercent
     handleGoalLevelChange: (e) ->
       @handleExpChange @state.currentLevel, @state.nextExp, e.target.value, @state.mapValue, @state.mapPercent
     handleExpMapChange: (e) ->
@@ -279,7 +277,7 @@
             <Input type="number" label={__("Actual level")} value={@state.currentLevel} onChange={@handleCurrentLevelChange} />
           </Col>
           <Col xs={row}>
-            <Input type="number" label={__("To next")} value={@state.nextExp} onChange={@handleNextExpChange} />
+            <Input type="number" label={__("To next")} value={@state.nextExp} readOnly />
           </Col>
           <Col xs={row}>
             <Input type="number" label={__("Goal")} value={@state.goalLevel} onChange={@handleGoalLevelChange} />


Revert "Fix external"

Revert "Fix external"

This reverts commit 666928fc4141ecee8ee579bc7eb08aec0b285a8a.

--- server/controllers/devices.coffee
+++ server/controllers/devices.coffee
@@ -74,13 +74,14 @@

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

-    # Add his credentials for CouchDB
-    if process.env.NODE_ENV is "production"
-        req.headers['authorization'] = getCredentialsHeader()
-    else
-        # Do not forward 'authorization' header in other environments
-        # in order to avoid wrong authentications in CouchDB
-        req.headers['authorization'] = null
+    if auth
+        # Add his creadentials for CouchDB
+        if process.env.NODE_ENV is "production"
+            req.headers['authorization'] = getCredentialsHeader()
+        else
+            # Do not forward 'authorization' header in other environments
+            # in order to avoid wrong authentications in CouchDB
+            req.headers['authorization'] = null

-    # Forward the request to couchdb
-    getProxy().web req, res, target: "http://localhost:5984"
+        # Forward the request to couchdb
+        getProxy().web req, res, target: "http://localhost:5984"


Token is not required when a sharing is denied

--- server/controllers/sharing.coffee
+++ server/controllers/sharing.coffee
@@ -283,14 +283,14 @@
 #   recipientUrl -> the url of the recipient's cozy
 #   accepted     -> boolean specifying if the share was accepted or not
 #   preToken     -> the token sent by the sharer to authenticate the receiver
-#   token        -> the token generated by the target, if accepted
+#   token        -> [conditionnal] token generated by the target, if accepted
 module.exports.validateTarget = (req, res, next) ->

     answer = req.body

     # Check the structure of the answer
     if utils.hasEmptyField answer, ["shareID", "recipientUrl", "accepted",\
-                                    "preToken", "token"]
+                                    "preToken"]
         err = new Error "Bad request: body is incomplete"
         err.status = 400
         return next err


Removed old sbory news

Removed old sbory news

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -8,10 +8,6 @@
         """),
         newsItem("Очных занятий в этом учебном году больше не будет (ни в лицее 40, ни в ННГУ)", String.raw"""
             В текущем учебном году занятий в лицее 40 и в ННГУ больше не будет. Занятия возобновятся в сентябре, следите за объявлениями на сайте.
-        """),
-        newsItem("Летние алгопрог-сборы", String.raw"""
-
Я рассматриваю возможность провести дистанционные сборы летом 2021 года длительностью примерно 7-10 дней. Сборы будут бесплатными для нижегородских школьников, и платными для любых других желающих (школьников, студентов и т.д.), стоимость примерно 5-8 тыс. рублей. (Безотносительно оплаты основного алгопрога.) Сборы будут рассчитаны на учеников, чей уровень на алгопроге примерно 3 и выше. По формату будет примерно похоже на зимние сборы к региону.

-
Если вам будет интересно участвовать, заполните анкету.

         """),
         newsItem("Летния стажировка на алгопроге", String.raw"""
             В этом году, как и в прошлые годы, летом будет проходить стажировка на алгопроге. Кто хочет участвовать — пишите.


code seems to imply it's nullable

code seems to imply it's nullable

--- resources/assets/coffee/_classes/fancy-chart.coffee
+++ resources/assets/coffee/_classes/fancy-chart.coffee
@@ -46,7 +46,7 @@
       .attr 'data-visibility', 'hidden'
       .attr 'r', 2

-    data = osu.parseJson area.dataset.src
+    data = osu.parseJsonNullable area.dataset.src
     @loadData data


Fixed safari issue with .position().top == 0 and "see more" feature.

--- app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee
@@ -178,6 +178,7 @@
     @shown = true

     maxHeight               = 560
+    minHeight               = 90
     bubbleContent           = @textBubbleContent
     bubbleOvervlowContainer = @textBubbleOverflowContainer

@@ -194,11 +195,20 @@
     # remember offset of "see more"
     offsetTop = bubbleContent.find('.js-signatureMarker').position()

+    # safari - workaround
+    # in safari somethimes the marker is directly on top via .top and inspector but it isn't
+    # in this case use the next element
+    if offsetTop && offsetTop.top is 0
+      offsetTop = bubbleContent.find('.js-signatureMarker').next('div, p').position()
+
     # remember bubble heigth
     heigth = bubbleContent.height()
     if offsetTop && heigth
+      newHeigth = offsetTop.top + 30
+      if newHeigth < minHeight
+        newHeigth = minHeight
       bubbleContent.attr('data-height', heigth)
-      bubbleContent.css('height', "#{offsetTop.top + 30}px")
+      bubbleContent.css('height', "#{newHeigth}px")
       bubbleOvervlowContainer.removeClass('hide')
     else if heigth > maxHeight
       bubbleContent.attr('data-height', heigth)


GitLab: Use token from config

GitLab: Use token from config

--- servers/lib/server/handlers/api/gitlab/utils.coffee
+++ servers/lib/server/handlers/api/gitlab/utils.coffee
@@ -1,6 +1,8 @@
+KONFIG = require 'koding-config-manager'
+
 utils = {}

-utils.GITLAB_TOKEN = 'dev-test-token'
+utils.GITLAB_TOKEN = KONFIG.gitlab.systemHookToken

 utils.log = (rest...) -> console.log '[GitLab]', rest...


Fix ITB-9569

Fix ITB-9569

--- imports/server/mqtt/docker/inspect.coffee
+++ imports/server/mqtt/docker/inspect.coffee
@@ -23,8 +23,9 @@
     if mappedState
       reconciler.updateServiceState mappedState, labels

-    if (hostname = msg.Config?.Hostname) and (domain = msg.Config.Domainname)
-      reconciler.updateServiceFQDN "#{hostname}.#{domain}", labels
+    if (msg.Config?.Hostname?) and (msg.Config?.Domainname?)
+      updatedFQDN = "#{msg.Config.Hostname}.#{msg.Config.Domainname}"
+      reconciler.updateServiceFQDN updatedFQDN, labels
     if ports = msg.Config?.ExposedPorts
       reconciler.updateServicePorts (_.keys ports), labels
     if name = msg.Name


Use jQuery "on" for event hook.

Use jQuery "on" for event hook.

--- app/assets/javascripts/spree/frontend/spree_multi_currency.js.coffee
+++ app/assets/javascripts/spree/frontend/spree_multi_currency.js.coffee
@@ -1,5 +1,5 @@
 $ ->
-  $('#currency').change ->
+  $('#currency').on 'change', ->
     $.ajax(
       type: 'POST'
       url: $(this).data('href')


Fix gesture event velocity

Fix gesture event velocity

--- framer/GestureInputRecognizer.coffee
+++ framer/GestureInputRecognizer.coffee
@@ -337,7 +337,7 @@
 			event.deltaTime = event.time - @session.lastEvent.time

 		if @session?.events
-			events = _.filter @session.events, (e) -> e.time > (event.time - GestureInputVelocityTime)
+			events = _.filter @session.events, (e) -> e.time > (event.time - (GestureInputVelocityTime * 1000))
 			event.velocity = @_getVelocity(events)

 		if event.fingers > 0


Adding loading message when creating enterprise

--- app/assets/javascripts/darkswarm/services/enterprise_creation_service.js.coffee
+++ app/assets/javascripts/darkswarm/services/enterprise_creation_service.js.coffee
@@ -7,6 +7,7 @@
       country: availableCountries[0]

     create: =>
+      # Loading.message = "Creating " + $scope.enterprise.name
       # $http(
       #   method: "POST"
       #   url: "/api/enterprises"
@@ -15,8 +16,10 @@
       #   params:
       #     token: SpreeApiKey
       # ).success((data) ->
+      #   Loading.clear()
       #   RegistrationService.select('about')
       # ).error((data) ->
+      #   Loading.clear()
       #   console.log angular.toJson(data)
       #   alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
       # )


Cleanup the optional fields specs

Cleanup the optional fields specs

--- spec/loaders/abstract-loader-shared-behavior.coffee
+++ spec/loaders/abstract-loader-shared-behavior.coffee
@@ -303,7 +303,7 @@
           loader.setup(opts)
           notFound(loader, opts)

-      context 'when optional fields have been requested but the fields arent on the tasks', ->
+      context 'when optional fields have been requested but the fields arent on all the tasks', ->
         beforeEach ->
           opts.optionalFields = ['test_field']
           taskOne.set('test_field', 'fake value')
@@ -314,8 +314,11 @@
           loader._checkCacheForData()
           expect(loader._onLoadSuccess).not.toHaveBeenCalled()

-      context 'when optional fields have been requested but the fields are there on the tasks', ->
-        beforeEach ->
+      context 'when optional fields have been requested and the fields are already on the tasks', ->
+        beforeEach ->
+          opts.optionalFields = ['test_field']
+          taskOne.set('test_field', 'fake value for one')
+          taskTwo.set('test_field', 'fake value for two')
           loader.storageManager.storage('tasks').add([taskOne, taskTwo])

         it 'returns true and does not calls #_onLoadSuccess', ->
@@ -367,6 +370,7 @@
             it 'returns false and does not call #_onLoadSuccess', ->
               opts.optionalFields = ['test_field']
               loader.setup(opts)
+              loader._checkCacheForData()
               expect(loader._onLoadSuccess).not.toHaveBeenCalled()

         context 'cache is invalid', ->


add tests

add tests

--- test/bacon.jquery.spec.coffee
+++ test/bacon.jquery.spec.coffee
@@ -1,7 +1,14 @@
 expect = require("chai").expect

-require "../src/bacon.jquery"
+bjq = require "../src/bacon.jquery"

 twice = (x) -> x * 2
 Bacon = require "baconjs"
+ModelBacon = require "bacon.model"
 model = Bacon.Model
+
+describe "Node.js specific", ->
+  it "exports original Bacon.$", ->
+    expect(bjq).to.equal(Bacon.$)
+  it "shares the same bacon with model", ->
+    expect(bjq).to.equal(ModelBacon.$)