Grab the first x-forwarded ip address

Grab the first x-forwarded ip address

--- app/controllers/device-controller.coffee
+++ app/controllers/device-controller.coffee
@@ -1,14 +1,19 @@
 PinController = require './pin-controller'
 debug = require('debug')('meshblu-pin-authenticator:device-controller')
+_ = require 'lodash'

 class DeviceController
   constructor: (uuid, meshblu) ->
     @pinController = new PinController uuid, meshblu: meshblu

+  ipAddress: (request) =>
+    return request.connection.remoteAddress unless request.headers['x-forwarded-for']?
+    _.first request.headers['x-forwarded-for'].split(',')
+
   create: (request, response) =>
     {device, pin} = request.body
     device ?= {}
-    device.ipAddress ?= request.headers['x-forwarded-for'] ? request.connection.remoteAddress
+    device.ipAddress ?= @ipAddress(request)
     @pinController.createDevice pin, device, (error, uuid) =>
       return response.status(500).send error: error.message if error?
       response.status(201).send uuid: uuid


Fix mruby compile error

Fix mruby compile error

--- src/engine/mrubyengine.coffee
+++ src/engine/mrubyengine.coffee
@@ -109,7 +109,7 @@
       src_data = data
       return mrbc.setup()
     ).then(=>
-      return mrbc.writeFile("/#{paths.rb}", paths.rb, RUBY_ENCODING)
+      return mrbc.writeFile("/#{paths.rb}", src_data, {encoding: RUBY_ENCODING})
     ).then(=>
       return mrbc.run("-o/#{paths.mrb}", "-g", (item.flags or [])...)
     ).then((status) =>


Fix quick subject comment

Fix quick subject comment

Make sure project is loaded correctly

Closes #1299

--- app/talk/quick-subject-comment-form.cjsx
+++ app/talk/quick-subject-comment-form.cjsx
@@ -40,7 +40,7 @@
     {name, owner} = @getParams() # of project, maybe better to pass in as prop later

     # get project
-    apiClient.type('projects').get({slug: name})
+    apiClient.type('projects').get({slug: owner + '/' + name})
       .then ([project]) =>
         section = projectSection(project)

@@ -62,7 +62,7 @@
                     talkClient.type('comments').create(comment).save()
                       .then (comment) =>
                         pageOfComment = getPageOfComment(comment, discussion, PAGE_SIZE)
-                        @transitionTo('project-talk-discussion', {owner: owner, name: project.slug, board: discussion.board_id, discussion: discussion.id}, {page: pageOfComment, comment: comment.id})
+                        @transitionTo('project-talk-discussion', {owner: owner, name: name, board: discussion.board_id, discussion: discussion.id}, {page: pageOfComment, comment: comment.id})

                   else
                     focus_id = +@props.subject?.id
@@ -81,7 +81,7 @@
                       }
                     talkClient.type('discussions').create(discussion).save()
                       .then (discussion) =>
-                        @transitionTo('project-talk-discussion', {owner: owner, name: project.slug, board: discussion.board_id, discussion: discussion.id})
+                        @transitionTo('project-talk-discussion', {owner: owner, name: name, board: discussion.board_id, discussion: discussion.id})

             else
               throw new Error("A board for subject comments has not been setup for this project yet.")


Change '==' to 'is'

Change '==' to 'is'

--- src/scripts/helpers/backbone/views/editable.coffee
+++ src/scripts/helpers/backbone/views/editable.coffee
@@ -139,7 +139,7 @@
             # Update the model if an event for this editable was triggered
             Aloha.bind 'aloha-smart-content-changed.updatemodel', (evt, d) =>
               # Ensures that all unwrapped text is wrapped in p tags
-              $('.media-body').contents().filter(-> @nodeType == 3).wrap '

'
+              $('.media-body').contents().filter(-> @nodeType is 3).wrap '

'
               # HACK - d is undefined because processing instructions
               # needs to trigger aloha-smart-content-change event
               if d is undefined or d.triggerType isnt 'blur'


NavigationLink: bypass KDWindowController’s ‘click’ trap by setting target as _self

--- client/Main/navigation/navigationlink.coffee
+++ client/Main/navigation/navigationlink.coffee
@@ -14,7 +14,7 @@
     options.draggable  = yes
       # axis             : 'xy'
       # containment      : 'parent' #KD.getSingleton('DockController').getView()
-    options.attributes = {href}
+    options.attributes = {href, target:'_self'}
     options.cssClass   = KD.utils.curry @utils.slugify(data.title), options.cssClass

     super options, data


activity: improved getembedsize helper

activity: improved getembedsize helper

--- client/activity/lib/util/getEmbedSize.coffee
+++ client/activity/lib/util/getEmbedSize.coffee
@@ -1,8 +1,8 @@
-module.exports = getEmbedSize = (embed) ->
+module.exports = getEmbedSize = (embed, maxWidth) ->

   { width, height } = embed
-  maxWidth          = 600
-  maxHeight         = 450
+  maxWidth          = Math.min (maxWidth or 600), 600
+  maxHeight         = 400
   ratio             = width/height
   isWideImage       = ratio > 1
   needsResize       = width > maxWidth or height > maxHeight
@@ -11,8 +11,15 @@
     if isWideImage
       width  = Math.floor Math.min maxWidth, width
       height = Math.floor width / ratio
+      if height > maxHeight
+        height = maxHeight
+        width  = Math.floor height * ratio
+
     else
       height = Math.floor Math.min maxHeight, height
       width  = Math.floor height * ratio
+      if width > maxWidth
+        width  = maxWidth
+        height = Math.floor width / ratio

   return { width, height }


Fix throwing error for editor pane.

Fix throwing error for editor pane.

--- client/ide/workspace/panes/editorpane.coffee
+++ client/ide/workspace/panes/editorpane.coffee
@@ -10,13 +10,12 @@

   createEditor: ->
     {file, content} = @getOptions()
-    isLocalFile     = no

     unless file instanceof FSFile
-      return new Error "File must be an instance of FSFile"
+      throw new TypeError "File must be an instance of FSFile"

     unless content
-      return new Error "You must pass file content to EditorPane"
+      throw new TypeError "You must pass file content to EditorPane"

     @aceView = new AceView delegate: @getDelegate(), file
     @aceView.ace.once "ace.ready", =>


Fix mapping state toggles

Fix mapping state toggles

--- app/assets/javascripts/admin/views/home/add_integration.js.coffee
+++ app/assets/javascripts/admin/views/home/add_integration.js.coffee
@@ -33,16 +33,19 @@
         on: 'Enabled',
         off: 'Disabled'
       },
-      on: true,
+      on: false,
       width: 90
     })

-    # Handle clicking on consumer state toggles
-    @$el.find('.integration-toggle').on 'toggle', (event, active) =>
+    # Handle clicking on consumer toggle
+    @$el.find('.integration-toggle').on 'toggle', (e, active) =>
+      consumerName = $(e.currentTarget).data('consumer-name')
       if active
-        @model.enableMappings()
+        @enabledMappings.push consumerName
       else
-        @model.disableMappings()
+        index = @enabledMappings.indexOf consumerName
+        if index != -1
+          @enabledMappings.splice(index, 1)

     @listClickHandlers()

@@ -78,13 +81,11 @@
         _($(value).find('.list-row')).each (element) ->
           key = $(element).find('input[name=key]').val()
           value = $(element).find('input[name=value]').val()
-          console.log key, value
           if key && value
             currentValue[key] = value
         finalValue.push currentValue
       finalValueJSON = JSON.stringify(finalValue)
       @$el.append("<input class='parameter_value' name='#{paramName}' type='hidden' value='#{finalValueJSON}' />")
-    console.log @$el.find('input.parameter_value')

   save: (event) ->
     event.preventDefault()
@@ -109,15 +110,6 @@
         else
           console.log 'missing'

-    # _(@$el.find('input.enabled')).each (enabled) ->
-    #   enabled = $(enabled)
-
-    # enabled = $(".enabled:checked").map ->
-    #   $(@).val()
-    #
-    for consumerName of @parametersByConsumer()
-      @enabledMappings.push consumerName
-
     @model.signup parameters, @enabledMappings, error: @displayErrors
     $.modal.close()


bug fix

bug fix

--- app/assets/javascripts/file_part_upload/file_part_uploader.coffee
+++ app/assets/javascripts/file_part_upload/file_part_uploader.coffee
@@ -112,14 +112,14 @@
           else
             @file_progress_class.uploader_error?(up, err, err_tip)

-        # 同时选择多个文件时才会触发
+        # 添加文件时就会触发
         FilesAdded: (up, files)=>
-          console.debug 'many files'
           plupload.each files, (file)=>
             if not @file_progress_instances[file.id]?
               @file_progress_instances[file.id] = new @file_progress_class(file, up)

         # 该方法在整个队列处理完毕后触发
+        # 目前这个钩子没什么用
         UploadComplete: =>
           # @qiniu_all_complete_callback()


More tests

More tests

--- spec/arrayMethods.coffee
+++ spec/arrayMethods.coffee
@@ -14,10 +14,13 @@

   describe 'filter', ->

-    it 'works', ->
+    it 'works given an array', ->
       arr = Z([{ a: 1 }, { a: 2 }, { a: 3 }])
       small = arr.filter (x) -> x.a <= 2
       small.should.become [{ a: 1 }, { a: 2 }]
+
+    it 'fails given an object', ->
+      Z({}).filter((x) -> x).should.be.rejected

@@ -44,8 +47,11 @@

   describe 'split', ->

-    it 'works', ->
+    it 'works given a string', ->
       Z("jakob mattsson").split('a').should.become ["j", "kob m", "ttsson"]
+
+    it 'fails given an object', ->
+      Z({}).split('a').should.be.rejected

@@ -69,3 +75,13 @@

     it 'returns the keys of an object', ->
       Z([{ a: 1, b: 2 }, {}, { x: 1 }]).omitEach('a').should.become [{ b: 2 }, {}, { x: 1 }]
+
+
+
+describe 'generic method', ->
+
+  describe 'toString', ->
+
+    it 'turns the value into a string', ->
+      Z(1).toString().should.become "1"
+


Testing the array method join

Testing the array method join

--- spec/arrayMethods.coffee
+++ spec/arrayMethods.coffee
@@ -21,6 +21,15 @@

     it 'fails given an object', ->
       Z({}).filter((x) -> x).should.be.rejected
+
+
+  describe 'join', ->
+
+    it 'works given an array', ->
+      Z(["abc", 56, {}]).join('').should.become "abc56[object Object]"
+
+    it 'fails given an object', ->
+      Z({}).join('').should.be.rejected


Use greater than check for folder size

Use greater than check for folder size

--- spec/fs-plus-spec.coffee
+++ spec/fs-plus-spec.coffee
@@ -156,6 +156,6 @@
       expect(fs.getSizeSync()).toBe -1
       expect(fs.getSizeSync('')).toBe -1
       expect(fs.getSizeSync(null)).toBe -1
-      expect(fs.getSizeSync(fixturesDir)).toBe 238
+      expect(fs.getSizeSync(fixturesDir)).toBeGreaterThan 0
       expect(fs.getSizeSync(path.join(fixturesDir, 'sample.js'))).toBe 408
       expect(fs.getSizeSync(path.join(fixturesDir, 'does.not.exist'))).toBe -1


Add incomplete keystroke

Add incomplete keystroke

--- spec/helpers-spec.coffee
+++ spec/helpers-spec.coffee
@@ -21,6 +21,7 @@
     expect(normalizeKeystrokes('cmd-a-b')).toBe false

     expect(-> normalizeKeystrokes('-a-b')).toThrow()
+    expect(-> normalizeKeystrokes('ctrl-')).toThrow()
     expect(-> normalizeKeystrokes('--')).toThrow()
     expect(-> normalizeKeystrokes('- ')).toThrow()
     expect(-> normalizeKeystrokes('a ')).toThrow()


Tests: Parametrice hetronenous participant

--- spec/heterogenous.coffee
+++ spec/heterogenous.coffee
@@ -8,6 +8,7 @@
 python = process.env.PYTHON or 'python'
 foreignParticipants =
   'PythonRepeat': [python, path.join __dirname, 'fixtures', './repeat.py']
+#  'CppRepeat': [python, path.join __dirname, 'fixtures', './repeat-cpp']

 startProcess = (args, callback) ->
   prog = args[0]
@@ -37,39 +38,34 @@
   args = foreignParticipants[name]
   return startProcess args, callback

-describe 'Heterogenous', ->
-  address = 'amqp://localhost'
-  broker = null
+testParticipant = (state, name) ->

-  beforeEach (done) ->
-    broker = msgflo.transport.getBroker address
-    broker.connect done
-  afterEach (done) ->
-    broker.disconnect done
-
-  # TODO: make parametric over the participants
-  describe 'PythonRepeat participant', ->
+  describe "#{name} participant", ->
     participant = null
     beforeEach (done) ->
       @timeout 4000
-      participant = startForeign 'PythonRepeat', done
+      participant = startForeign name, done
     afterEach (done) ->
       participant.kill()
       done()

     describe 'when started', ->
       it 'sends definition on fbp queue', (done) ->
+        broker = state.broker
+
         onDiscovery = (msg) ->
           def = msg.data
           chai.expect(def).to.be.an 'object'
           chai.expect(def).to.have.keys ['id', 'icon', 'component', 'label', 'inports', 'outports']
-          broker.ackMessage msg
-          return if def.component != 'PythonRepeat'
+          state.broker.ackMessage msg
+          return if def.component != name
           done()
-        broker.subscribeParticipantChange onDiscovery
+        state.broker.subscribeParticipantChange onDiscovery

     describe 'sending data on inport queue', ->
       it 'repeats the same data on outport queue', (done) ->
+        broker = state.broker
+
         input = { bar: 'foo' }
         onReceive = (msg) ->
           broker.ackMessage msg
@@ -94,3 +90,21 @@
               chai.expect(err).to.not.exist
               setTimeout send, 1000 # HACK: wait for inqueue to be setup

+
+
+describe 'Heterogenous', ->
+  address = 'amqp://localhost'
+  g =
+    broker: null
+
+  beforeEach (done) ->
+    g.broker = msgflo.transport.getBroker address
+    g.broker.connect done
+  afterEach (done) ->
+    g.broker.disconnect done
+
+  names = Object.keys foreignParticipants
+  names.forEach (name) ->
+    testParticipant g, name
+
+


remove blank line

remove blank line

--- client/app/views/app_view.coffee
+++ client/app/views/app_view.coffee
@@ -10,4 +10,3 @@
         konnectors = new Konnectors()
         konnectors.render()
         konnectors.fetch()
-


change describe() to it() so runs() is nested in an it()

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -205,7 +205,7 @@
     # be different than the atom.project used in a subsequent it(). To work
     # around this issue, we use runs() instead of it().

-    describe "there is no repository for /tmp even though there are RepositoryProviders", ->
+    it "there is no repository for /tmp even though there are RepositoryProviders", ->
       the_result = "dummy_value"

       waitsForPromise ->
@@ -218,7 +218,7 @@
         expect(the_result).toBe null
         expect(atom.project.repositoryPromisesByPath.size).toBe 0

-    describe "when Git repository for directory, promise resolves to Atom's GitRepository and is cached", ->
+    it "when Git repository for directory, promise resolves to Atom's GitRepository and is cached", ->
       the_result = "dummy_value"
       the_promise = null
       directory = new Directory(path.join(__dirname, '..'))


Changing missing message text to be a bit clearer

Now, instead of:
> 'info: package.json is near to be fine. Check the file and run again.'

It should be a bit clearer and say:
> 'info: package.json is almost fine. Check the file and run again.'

--- lib/Report.coffee
+++ lib/Report.coffee
@@ -39,7 +39,7 @@
     cb(true, data, @logger.messages)

   missingMessage: (cb, data) ->
-    @logger.info("#{chalk.bold(@filename)} is near to be #{chalk.bold('fine')}. Check the file and run again.")
+    @logger.info("#{chalk.bold(@filename)} is almost #{chalk.bold('fine')}. Check the file and run again.")
     cb(true, data, @logger.messages)

   alreadyMessage: (cb, data) ->


VMController: add getKite function into vm controller

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -30,6 +30,12 @@
         @kc.run options, (rest...) ->
           # log rest...
           callback rest...
+
+  getKite:(vmName, callback)->
+    @fetchRegion vmName, (region)=>
+      kiteName = "os-#{region}"
+      kite = @kc.getKite kiteName, vmName
+      return callback kite

   _runWrapper:(command, vm, callback)->
     [callback, vm] = [vm, callback]  if vm and 'string' isnt typeof vm


IDEView: When a new terminal is created set it's RTM.

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -374,6 +374,7 @@
         options.path = frontApp.workspaceData.rootPath

     terminalPane = new IDETerminalPane options
+    terminalPane.ready -> frontApp.setRealtimeManager terminalPane

     @createPane_ terminalPane, { name: 'Terminal' }


client: welcome: add docs link

client: welcome: add docs link

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -96,6 +96,11 @@
       title    : 'DELETE STACK TEMPLATE'
       click    : @bound 'deleteStack'

+    @secondaryActions.addSubView new CustomLinkView
+      cssClass : 'HomeAppView--button secondary fr'
+      title    : 'DOCUMENTATION'
+      href     : 'http://www.koding.com/docs'
+
     @tabView.unsetClass 'kdscrollview'

     @editorViews = {}


fixes #1150 buffer slice issue in JS

fixes #1150 buffer slice issue in JS

--- drivers/javascript/src/net.coffee
+++ drivers/javascript/src/net.coffee
@@ -64,7 +64,7 @@
             @_processResponse response

             # For some reason, Arraybuffer.slice is not in my version of node
-            @buffer = bufferSlice @buffer, (4 + responseLength)
+            @buffer = @buffer.slice(4 + responseLength)

     mkAtom = (response) -> DatumTerm::deconstruct response.getResponse 0

@@ -271,8 +271,8 @@
                     if b is 0
                         @rawSocket.removeListener('data', handshake_callback)

-                        status_buf = bufferSlice(@buffer, 0, i)
-                        @buffer = bufferSlice(@buffer, i)
+                        status_buf = @buffer.slice(0, i)
+                        @buffer = @buffer.slice(i + 1)
                         status_str = String.fromCharCode.apply(null, new Uint8Array status_buf)

                         handshake_complete = true
@@ -400,9 +400,6 @@
     view.set new Uint8Array(buf2), buf1.byteLength
     view.buffer

-bufferSlice = (buffer, offset, end) ->
-    return buffer.slice(offset, end)
-
 toArrayBuffer = (node_buffer) ->
     # Convert from node buffer to array buffer
     arr = new Uint8Array new ArrayBuffer node_buffer.length


test/teamshelpers: add create default stack template func

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -413,6 +413,14 @@
       .waitForElementVisible successModal, 40000
       .click closeButton
       .pause 5000, ->
+
+
+  createDefaultStackTemplate: (browser, done) ->
+
+    stackEditorUrl = "#{helpers.getUrl(yes)}/Home/stacks"
+
+    @createCredential browser, 'aws', 'draft-stack', yes, (res) ->
+      browser.url stackEditorUrl, ->
         done()


test/teamsHelper: replace old funcs with new ones

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -445,167 +445,97 @@
       .assert.containsText       emailList, userEmail

-  createStack: (browser, skipStackSetup = no) ->
-
-    modalSelector         = '.kdmodal-content .AppModal-content'
-    providerSelector      = "#{modalSelector} .stack-onboarding .provider-selection"
-    machineSelector       = "#{providerSelector} .providers"
-    stackPreview          = "#{modalSelector} .stack-preview"
-    codeSelector          = "#{stackPreview} .has-markdown"
-    footerSelector        = "#{modalSelector} .stacks .footer"
-    nextButtonSelector    = "#{footerSelector} button.next"
-    awsSelector           = "#{machineSelector} .aws"
-    configurationSelector = "#{modalSelector} .configuration .server-configuration"
-    inputSelector         = "#{configurationSelector} .Database"
-    mysqlSelector         = "#{inputSelector} .mysql input.checkbox + label"
-    postgresqlSelector    = "#{inputSelector} .postgresql input.checkbox + label"
-    server1PageSelector   = "#{modalSelector} .code-setup .server-1"
-    githubSelector        = "#{server1PageSelector} .box-wrapper .github"
-    bitbucketSelector     = "#{server1PageSelector} .box-wrapper .bitbucket"
-    editorSelector        = "#{modalSelector} .editor-main"
-    skipSetupSelector     = '.footer .skip-setup'
-    stackTemplateSelector = '.kdtabhandlecontainer.hide-close-icons .stack-template'
-    saveAndTestButton     = '.buttons button:nth-of-type(5)'
-
-    @openStackCatalog(browser)
-
-    if skipStackSetup
-      browser
-        .waitForElementVisible  skipSetupSelector, 20000
-        .click                  skipSetupSelector
-        .waitForElementVisible  stackTemplateSelector, 20000
-        .assert.containsText    stackTemplateSelector, 'Stack Template'
-        .assert.containsText    saveAndTestButton, 'SAVE & TEST'
-    else
-      browser
-        .waitForElementVisible  providerSelector, 20000
-        .waitForElementVisible  awsSelector, 20000
-        .waitForElementVisible  "#{machineSelector} .vagrant" , 20000 # Assertion
-        .click                  awsSelector
-        .waitForElementVisible  stackPreview, 20000
-        .waitForElementVisible  codeSelector, 20000
-        .assert.containsText    codeSelector, 'koding_group_slug'
-        .waitForElementVisible  footerSelector, 20000
-        .waitForElementVisible  nextButtonSelector, 20000
-        .pause                  2000 # wait for animation
-        .click                  nextButtonSelector
-        .waitForElementVisible  configurationSelector, 20000
-        .pause                  2000 # wait for animation
-        .waitForElementVisible  mysqlSelector, 20000
-        .click                  mysqlSelector
-        .pause                  2000 # wait for animation
-        .waitForElementVisible  postgresqlSelector, 20000
-        .click                  postgresqlSelector
-        .waitForElementVisible  stackPreview, 20000
-        .assert.containsText    codeSelector, 'mysql-server postgresql'
-        .waitForElementVisible  nextButtonSelector, 20000
-        .pause                  2000 # wait for animation
-        .click                  nextButtonSelector
-        .waitForElementVisible  server1PageSelector, 20000
-        .waitForElementVisible  githubSelector, 20000 # Assertion
-        .waitForElementVisible  bitbucketSelector, 20000 # Assertion
-        .waitForElementVisible  nextButtonSelector, 20000
-        .moveToElement          nextButtonSelector, 15, 10
-        .pause                  2000
-        .click                  nextButtonSelector
-        .waitForElementVisible  "#{modalSelector} .define-stack-view", 20000
-        .waitForElementVisible  editorSelector, 20000
-        .pause                  1000
-        .assert.containsText    editorSelector, 'aws_instance'
-
-
-  createCredential: (browser, show = no, remove = no, use = no) ->
-
-    credetialTabSelector     = '.team-stack-templates .kdtabhandle-tabs .credentials'
-    stackTabSelector         = '.team-stack-templates .kdtabhandle.stack-template.active'
-    credentialsPane          = '.credentials-form-view'
-    editorSelector           = '.editor-main'
-    saveButtonSelector       = '.add-credential-scroll .button-field button.green'
-    newCredential            = '.step-creds .listview-wrapper .credential-list .credential-item'
-    credentialName           = 'test credential'
-    showCredentialButton     = "#{newCredential} button.show"
-    deleteCredentialButton   = "#{newCredential} button.delete"
-    useCredentialButton      = "#{newCredential} button.verify"
-    inUseLabelSelector       = "#{newCredential} .custom-tag.inuse"
-    secretKeyInput           = "#{credentialsPane} .secret-key input"
-    destroyCredentialModal   = '.kdmodal[testpath=destroyCredentialModal]'
-    removeCredentialModal    = '.kdmodal[testpath=removeCredentialModal]'
-    destroyCredentialsButton = '.kdbutton[testpath=destroyAll]'
-    removeCredentialButton   = '.kdbutton[testpath=removeCredential]'
+  createCredential: (browser, provider, credentialName, addMore = no, done) ->
+
+    url = helpers.getUrl(yes)
+
+    stacksPageSelector = '.HomeAppView-Stacks--create'
+    newStackButton = "#{stacksPageSelector} .kdbutton.GenericButton.HomeAppView-Stacks--createButton"
+    stackOnboardingPage = '.kdview.stack-onboarding.main-content.get-started'
+    createStackButton = '.kdbutton.GenericButton.StackEditor-OnboardingModal--create'
+    providers = '.providers.box-wrapper'
+    providerSelector = "#{providers} .provider.box.#{provider}"
+    skipGuideButton = '.kdview.stack-onboarding.main-content a.custom-link-view.HomeAppView--button'
+    stackEditorTab = '.kdview.kdtabview.StackEditorTabs'
+    credentialsTabSelector = "#{stackEditorTab} div.kdtabhandle.credentials.notification"
+    checkForCredentials = ".kdview.kdlistitemview.kdlistitemview-default.StackEditor-CredentialItem.#{provider}"
+    createNewButton = '.kdview.stacks.step-creds .kdbutton.add-big-btn.with-icon'
+
+
+    browser
+      .url "#{url}/Home/stacks"
+      .waitForElementVisible stacksPageSelector, 20000
+      .click newStackButton
+      .pause 2000
+      .waitForElementVisible stackOnboardingPage, 20000
+      .waitForElementVisible createStackButton, 20000
+      .click createStackButton
+      .waitForElementVisible providers, 20000
+      .click providerSelector
+      .waitForElementVisible skipGuideButton, 20000
+      .click skipGuideButton
+      .pause 5000
+      .waitForElementVisible stackEditorTab, 20000
+      .click credentialsTabSelector
+    browser.element 'css selector', checkForCredentials, (result) =>
+      if result.status is -1
+        @fillCredentialsPage browser, credentialName, done
+      else
+        if addMore
+          browser
+            .waitForElementVisible createNewButton, 20000
+            .click createNewButton
+            .pause 2000, =>
+              @fillCredentialsPage browser, credentialName, done
+        else
+          done()
+
+
+  fillCredentialsPage: (browser, name, done) ->
+
+    newCredentialPage = '.kdview.stacks.stacks-v2'
+    saveButton = "#{newCredentialPage} button[type=submit]"

     { accessKeyId, secretAccessKey } = @getAwsKey()

-    keyPart    = accessKeyId.substr accessKeyId.length - 6
-    secretPart = secretAccessKey.substr secretAccessKey.length - 13
-
-    browser
-      .waitForElementVisible  credetialTabSelector, 20000
-      .moveToElement          credetialTabSelector, 50, 21
-      .click                  credetialTabSelector
-      .pause                  2000
-
-    browser.element 'css selector', newCredential, (result) ->
-      if result.status is -1
+    browser
+      .waitForElementVisible newCredentialPage, 20000
+      .scrollToElement saveButton
+      .setValue "#{newCredentialPage} .title input", name
+      .pause 1000
+      .setValue "#{newCredentialPage} .access-key input", accessKeyId
+      .pause 1000
+      .setValue "#{newCredentialPage} .secret-key input", secretAccessKey
+      .pause 1000
+      .click saveButton, -> done()
+
+
+  createStack: (browser, done) ->
+
+    url = helpers.getUrl(yes)
+
+    credentialSelector = '.kdview.kdlistitemview.kdlistitemview-default.StackEditor-CredentialItem'
+    useThisAndContinueButton = '.StackEditor-CredentialItem--buttons .kdbutton.solid.compact.outline.verify'
+    editorPaneSelector = '.kdview.pane.editor-pane.editor-view'
+    saveButtonSelector = '.StackEditorView--header .kdbutton.GenericButton.save-test'
+    successModal = '.kdmodal-inner .kdmodal-content'
+    closeButton = '.kdmodal-inner .kdview.kdmodal-buttons .kdbutton.solid.medium.gray'
+    browser
+      .pause 2000
+      .click useThisAndContinueButton
+      .waitForElementVisible editorPaneSelector, 20000
+      .click saveButtonSelector
+    browser.element 'css selector', successModal, (result) ->
+      if result.status is 0
         browser
-          .waitForElementVisible  credentialsPane, 20000
-          .setValue               "#{credentialsPane} .title input", credentialName
-          .pause                  500
-          .setValue               "#{credentialsPane} .access-key input", accessKeyId
-          .pause                  500
-          .scrollToElement        secretKeyInput
-          .setValue               secretKeyInput, secretAccessKey
-          .pause                  1000
-          .click                  '.credential-creation-intro'
-          .scrollToElement        saveButtonSelector
-          .click                  saveButtonSelector
-          .pause                  2000 # wait for loade next page
-          .waitForElementVisible  newCredential, 20000
-          .assert.containsText    newCredential, credentialName
-
-      browser
-        .waitForElementVisible    newCredential, 20000
-        .moveToElement            newCredential, 300, 20
-        .pause                    1000
-
-      if show
-        browser
-          .waitForElementVisible  showCredentialButton, 20000
-          .click                  showCredentialButton
-          .waitForElementVisible  '.credential-modal', 20000
-          .pause                  2000
-          .assert.containsText    '.credential-modal .kdmodal-title', 'test credential'
-          .assert.containsText    '.credential-modal .kdmodal-content', keyPart
-          .assert.containsText    '.credential-modal .kdmodal-content', secretPart
-
-      if remove
-        browser
-          .waitForElementVisible    deleteCredentialButton, 20000
-          .click                    deleteCredentialButton
-          .pause                    2000
-          .element                  'css selector', destroyCredentialModal, (result) ->
-            buttonSelector = removeCredentialButton
-            modalSelector  = removeCredentialModal
-
-            if result.status is 0
-              buttonSelector = destroyCredentialsButton
-              modalSelector  = destroyCredentialModal
-
-            browser
-              .assert.containsText      modalSelector, credentialName
-              .click                    buttonSelector
-              .waitForElementNotPresent modalSelector, 60000
-              .waitForElementNotPresent newCredential, 20000
-
-      if use
-        browser
-          .waitForElementVisible    useCredentialButton, 20000
-          .click                    useCredentialButton
-          .waitForElementVisible    stackTabSelector, 20000
-          .waitForElementVisible    editorSelector, 20000
-          .click                    credetialTabSelector
-          .pause                    1000
-          .waitForElementVisible    newCredential, 20000
-          .waitForElementVisible    inUseLabelSelector, 20000
+          .waitForElementVisible successModal, 40000
+          .click closeButton, ->
+            browser.end()
+            done()
+      else
+        browser.end()
+        done()
+

   clickSaveAndTestButton: (browser) ->


Safety for legacy graphs

Safety for legacy graphs

--- components/ConnectRuntime.coffee
+++ components/ConnectRuntime.coffee
@@ -66,7 +66,7 @@
       tests: component.tests

   sendGraph: (runtime, graph) ->
-    if graph.properties.environment.type
+    if graph.properties.environment?.type
       if graph.properties.environment.type isnt 'all' and graph.properties.environment.type isnt @runtime.definition.type
         return


don't set fixed cost collection

don't set fixed cost collection

--- src/node_modules/round-overview/round-overview-controller.coffee
+++ src/node_modules/round-overview/round-overview-controller.coffee
@@ -36,7 +36,6 @@

     FixedCostsStore.save(fixedCost).then (saved) ->
       debug("saved", saved)
-      round.fixedCosts.set(fixedCost)
     , (err) ->
       # pass error to xeditable as string
       "Oh noes! :( We had a problem saving, please refresh and try again."


include footer when printing

include footer when printing

--- app/assets/javascripts/effective_datatables/initialize.js.coffee
+++ app/assets/javascripts/effective_datatables/initialize.js.coffee
@@ -44,6 +44,7 @@
         },
         {
           extend: 'print',
+          footer: true,
           exportOptions:
             format:
               header: (str) -> $("<div>#{str}</div>").children('.filter-label').first().text()


Empty select before rendering.

Empty select before rendering.

--- app/assets/javascripts/rahani/views/sorting_rules/form.js.coffee
+++ app/assets/javascripts/rahani/views/sorting_rules/form.js.coffee
@@ -21,6 +21,7 @@
       @renderCategories()

     renderCategories: ->
+      @ui.category.empty()
       _.each @options.categories.groupBy('type'), (categories, label) =>
         $optgroup = $("<optgroup label='#{label}'></optgroup>")
         _.each categories, (category) =>


ENHANCE: Change name to teaching period to rollover

--- src/app/teaching-periods/states/edit/directives/teaching-period-units/teaching-period-units.coffee
+++ src/app/teaching-periods/states/edit/directives/teaching-period-units/teaching-period-units.coffee
@@ -17,7 +17,7 @@
     # Rollover
     $scope.rolloverTeachingPeriodModal = (teachingPeriod) ->
       # If we're given a user, show that user, else create a new one
-      teachingPeriodToShow = if teachingPeriod? then teachingPeriod else { }
-      RolloverTeachingPeriodModal.show teachingPeriodToShow
+      teachingPeriodToRollover = if teachingPeriod? then teachingPeriod else { }
+      RolloverTeachingPeriodModal.show teachingPeriodToRollover

 )


cloning a book keeps the overridden title (or original if it was not overridden)

--- scripts/gh-book/toc-node.coffee
+++ scripts/gh-book/toc-node.coffee
@@ -56,8 +56,11 @@
       modelRoot = model.getRoot?() # Case of dropping a book onto a folder... `or model`

       if root and modelRoot and root != modelRoot
+        # Use `.toJSON().title` instead of `.get('title')` to support
+        # TocPointerNodes which inherit their title if it is not overridden
+        title = model.toJSON().title or 'Untitled'
+
         # If it is a pointer then dereference it
-        title = model.get('title')
         realModel = model.dereferencePointer?() or model

         # At this point realModel can be a XhtmlFile or a TocNode (section).


winctrl: remove obsolete superizeCombos

winctrl: remove obsolete superizeCombos

--- lib/core/windowcontroller.coffee
+++ lib/core/windowcontroller.coffee
@@ -17,7 +17,6 @@

 module.exports = class KDWindowController extends KDController

-  superKey        = if navigator.userAgent.indexOf("Mac OS X") is -1 then "ctrl" else "command"
   addListener     = (eventName, listener, capturePhase = yes) ->
     window.addEventListener eventName, listener, capturePhase

@@ -187,17 +186,6 @@
       @setKeyView @oldKeyView

-  superizeCombos = (combos)->
-
-    safeCombos = {}
-    for own combo, cb of combos
-      if /\bsuper(\+|\s)/.test combo
-        combo = combo.replace /super/g, superKey
-      safeCombos[combo] = cb
-
-    return safeCombos
-
-
   viewHasKeyCombos:(view)->

     return unless view


Fix typo in failed preview message

Fix typo in failed preview message

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -243,7 +243,7 @@
   showError: (result) ->
     @element.textContent = ''
     h2 = document.createElement('h2')
-    h2.textContent = 'Prevewing Markdown Failed'
+    h2.textContent = 'Previewing Markdown Failed'
     @element.appendChild(h2)
     if failureMessage = result?.message
       h3 = document.createElement('h3')


Change default `defaultCompiledDir`

Change default `defaultCompiledDir`

--- lib/bundle.coffee
+++ lib/bundle.coffee
@@ -8,7 +8,7 @@
   constructor: (@options) ->
     @options.staticRoot = path.normalize(@options.staticRoot)
     @files = []
-    @defaultCompiledDir = 'c'
+    @defaultCompiledDir = 'min'
     @defaultNamespace = 'global'

   # Gets relative path from staticRoot. If the


rollbar log first & last entry in feed to debug duplicate entires in feed

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -169,21 +169,47 @@
         cache.overview.reverse()  if cache?.overview
         callback null, cache

+  getTimeFromItem: (item) ->
+    # memberbucket data has no serverside model it comes from cache
+    # so it has no meta, that's why we check its date by its overview
+    lastDate = if item.createdAtTimestamps
+      new Date item.createdAtTimestamps.first
+    else
+      new Date item.meta.createdAt
+
+    return lastDate
+
   continueLoadingTeasers:->

     unless isLoading
       if @listController.itemsOrdered.last
+
+        # DEBUG: duplicate entries in activity feed
         lastItemData = @listController.itemsOrdered.last.getData()
-        # memberbucket data has no serverside model it comes from cache
-        # so it has no meta, that's why we check its date by its overview
-        lastDate = if lastItemData.createdAtTimestamps
-          new Date lastItemData.createdAtTimestamps.first
-        else
-          new Date lastItemData.meta.createdAt
+        lastDate = @getTimeFromItem(lastItemData)
+        lastId = lastItemData._id
+
+        firstItemData = @listController.itemsOrdered.first.getData()
+        firstDate = @getTimeFromItem(firstItemData)
+        firstId = firstItemData._id
+
+        timeStamps =
+          top:
+            date:firstDate
+            id:lastId
+          bottom:
+            date:lastDate
+            id:lastId
+          correct:firstDate>lastDate
+
+        _rollbar.push(msg:"duplicate entries debug", timeStamps:timeStamps, level:"debug")
+        # DEBUG: duplicate entries in activity feed
       else
         lastDate = new Date
+        _rollbar.push(msg:"could not find date", date:lastDate)

       lastTimeStamp = lastDate.getTime()
+
       @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->


:art: adding support for background color

--- src/BrailleBuffer.coffee
+++ src/BrailleBuffer.coffee
@@ -19,10 +19,10 @@

   pixelBuffer: null
   charBuffer: null
-  colorBuffer: null
+  foregroundBuffer: null
+  backgroundBuffer: null

-  termReset: "\x1B[39m"
-  termColor: (color) -> "\x1B[38;5;#{color}m"
+  termReset: "\x1B[39;49m"

   constructor: (@width, @height) ->
     @pixelBuffer = new Buffer @width*@height/8
@@ -31,12 +31,16 @@
   clear: ->
     @pixelBuffer.fill 0
     @charBuffer = []
-    @colorBuffer = []
+    @foregroundBuffer = []
+    @backgroundBuffer = []
+
+  setBackground: (idx, color) ->
+    @backgroundBuffer[idx] = color

   setPixel: (x, y, color) ->
     @_locate x, y, (idx, mask) =>
       @pixelBuffer[idx] |= mask
-      @colorBuffer[idx] = @termColor color
+      @foregroundBuffer[idx] = color

   unsetPixel: (x, y) ->
     @_locate x, y, (idx, mask) =>
@@ -51,6 +55,16 @@
     mask = @characterMap[y&3][x&1]
     cb idx, mask

+  _termColor: (foreground, background) ->
+    if foreground and background
+      "\x1B[38;5;#{foreground};48;5;#{background}m"
+    else if foreground
+      "\x1B[49;38;5;#{foreground}m"
+    else if background
+      "\x1B[39;48;5;#{background}m"
+    else
+      @termReset
+
   frame: ->
     output = []
     currentColor = null
@@ -59,7 +73,7 @@
     for idx in [0...@pixelBuffer.length]
       output.push delimeter unless idx % (@width/2)

-      if currentColor isnt colorCode = @colorBuffer[idx] or @termReset
+      if currentColor isnt colorCode = @_termColor @foregroundBuffer[idx], @backgroundBuffer[idx]
         output.push currentColor = colorCode

       output.push if @charBuffer[idx]
@@ -76,7 +90,7 @@
     return unless 0 <= x < @width and 0 <= y < @height
     idx = @_project x, y
     @charBuffer[idx] = char
-    @colorBuffer[idx] = @termColor color
+    @foregroundBuffer[idx] = color

   writeText: (text, x, y, color, center = true) ->
     x -= text.length/2+1 if center


Fixed subtle bug in CSS advice.

Fixed subtle bug in CSS advice.

CSSOM's CSSStyleDeclaration treats itself like an array, with each index
containing a string, respresenting a property that has been set on the
object.  See CSSStyleDeclaration#setProperty.

The array indices are used by the cssText getter to iterate over the CSS
properties that have been set.  Our advice wasn't setting the array
index when a .style.property was set, which was causing cssText to miss
properties.  JSDOM uses cssText internally in the StyleAttr attribute
class, which means our serializer was missing CSS attributes.

See cssom's CSSStyleDeclaration.js and JSDOM's level2/style.js files for
more information.

--- lib/server/browser/advice.coffee
+++ lib/server/browser/advice.coffee
@@ -201,11 +201,27 @@
                 # internally, so we only want to emit instructions if there
                 # is a parent element pointer, meaning this CSSStyleDeclaration
                 # belongs to an element.
-                if this._parentElement && this._parentElement._attachedToDocument
+                parent = this._parentElement
+                prop = "_#{attr}"
+
+                # To explain this if statement, see cssom's
+                #    CSSStyleDeclaration.js.
+                # CSSStyleDeclaration treats itself like an array, with each
+                # index containing a string for a property that has been set
+                # on the object.  See CSSStyleDeclaration#setProperty.
+                # The array indices are used by the cssText getter to iterate
+                # over the CSS properties that have been set, so not doing this
+                # was causing cssText to miss properties, and therefore our
+                # serializer to miss CSS attributes (due to JSDOM's StyleAttr
+                # object, which uses cssText internally).
+                if !this[prop]
+                    this[this.length++] = attr
+                rv = this[prop] = val
+                if parent?._attachedToDocument
                     browser.emit 'DOMStyleChanged',
-                        target : this._parentElement
+                        target    : parent
                         attribute : attr
-                        value : val
-                return @["_#{attr}"] = val
+                        value     : val
+                return rv
             proto.__defineGetter__ attr, () ->
                 return @["_#{attr}"]


fix #791

fix #791

--- lib/config.coffee
+++ lib/config.coffee
@@ -54,7 +54,7 @@
         title: "Status-bar New Branch modifier key"
         type: "string"
         default: "alt"
-        description: "Holding this modifier key while clicking on the branch name in the status bar will trigger creatinga a new branch. Note that _[`meta`](#{meta.define})_ is #{meta.key}"
+        description: "Holding this modifier key while clicking on the branch name in the status bar will trigger creating a new branch. Note that _[`meta`](#{meta.define})_ is #{meta.key}"
         enum: ["alt", "shift", "meta", "ctrl"]
       showBranchInTreeView:
         order: 8


Refix dbg to please the build script

Refix dbg to please the build script

Sorry Travis, it wasn't your fault.

--- lib/config.coffee
+++ lib/config.coffee
@@ -4,7 +4,9 @@
 CSON = require 'cson'
 {log, warn, error} = require './utils'

-{ROOT, EXROOT, dbg} = global
+{ROOT, EXROOT} = global
+
+if not dbg? then dbg = require './debug'

 config = null
 configCache = {}


Fix spawning persistent remote entities

Fix spawning persistent remote entities

--- src/EntityManager.coffee
+++ src/EntityManager.coffee
@@ -90,7 +90,7 @@
   processIncoming:(data, remote)->
     console.debug(data) unless data.message == "update"
     if data.message == "initEntity"
-      if (!data.forLevel && !@level) || data.forLevel == @level
+      if !data.forLevel || data.forLevel == @level
         @spawnRemoteEntity(data.type, data.id, remote.peer, data.state)
     else if data.message == "update"
       entity = @entities[data.id]


Fix parameters

Fix parameters

--- lib/getETA.coffee
+++ lib/getETA.coffee
@@ -1,8 +1,8 @@
 Meteor.methods {
   getETA: (stop,route,direction)->
-    check opts.stop,String
-    check opts.route,String
-    check opts.direction,String
+    check stop,String
+    check route,String
+    check direction,String

     # Get data from backend
     #HTTP.get 'http://localhost:


a little more specific comment

a little more specific comment

--- library/semantic/abstract.coffee
+++ library/semantic/abstract.coffee
@@ -75,7 +75,7 @@
     # will be wrapped with the special class instance of `Widget`.
     # This is a shortcut for quick widget design and composition.
     @subwidget: (signature) ->
-        blueprint = this # save the class as blueprint
+        blueprint = this # save to use it from closure
         token = (try _.first(_.keys(signature))) or no
         value = (try _.first(_.values(signature))) or no
         invalid = "received invalid invocation signature"


:bug: Fix a typo

:bug: Fix a typo

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -47,7 +47,7 @@
     @subscriptions.add atom.workspace.observeTextEditors (editor) => @createEditorLinter(editor)
     @commands.onShouldLint =>
       @getActiveEditorLinter().lint()
-    @commands.onShouldToggleActiveEditor ->
+    @commands.onShouldToggleActiveEditor =>
       activeLinter = @getActiveEditorLinter()
       if activeLinter
         activeLinter.dispose()


:bug: Fix lintOnFly

:bug: Fix lintOnFly

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -57,7 +57,10 @@
     Promises = []

     @Linters.forEach (Linter) ->
-      return if (onChange and not Linter.lintOnFly) or onChange
+      if onChange
+        return unless Linter.lintOnFly
+      else
+        return if Linter.lintOnFly
       Matching = Scopes.filter (Entry) -> Linter.scopes.indexOf(Entry) isnt -1
       return unless Matching.length
       Promises.push Linter.lint(


lib/params: added `benchmark` command

lib/params: added `benchmark` command

--- lib/params.coffee
+++ lib/params.coffee
@@ -217,4 +217,17 @@
       name: 'fast', shortcut: 'f', type: 'boolean'
       description: 'Fast mode without dependency resolution'
     ]
+  ,
+    name: 'benchmark'
+    description: 'Run benchmark modules'
+    options: [
+      name: 'hosts', shortcut: 'h', type: 'array'
+      description: 'Limit to a list of server hostnames'
+    ,
+      name: 'modules', shortcut: 'm', type: 'array'
+      description: 'Limit to a list of modules'
+    ,
+      name: 'fast', shortcut: 'f', type: 'boolean'
+      description: 'Fast mode without dependency resolution'
+    ]
   ]


add range types

add range types

--- lib/ripper.coffee
+++ lib/ripper.coffee
@@ -90,6 +90,7 @@
         range
       else
         Ripper.locToRange binding.identifier.loc
+    declRange.type = 'decl'

     ranges = [declRange]

@@ -98,6 +99,7 @@

     ranges = ranges.concat refPaths.map (p) ->
       range = Ripper.locToRange p.node.loc
+      range.type = 'ref'
       if p.parentPath.isObjectProperty()
         { key, shorthand } = p.parentPath.node
         range.shorthand = shorthand
@@ -108,5 +110,6 @@
     ranges = ranges.concat binding.constantViolations.map (p) ->
       node = p.node.left || p.node
       range = Ripper.locToRange node.loc
+      range.type = 'mut'
       range
     ranges


remove unused invariant call

remove unused invariant call

--- lib/router.coffee
+++ lib/router.coffee
@@ -8,8 +8,6 @@
 class Router

   constructor: (obj = {}) ->
-    invariant _.isEmpty(obj),
-      'Router constructor: you have to provide memoryType.'
     @_activeComponent = null
     @_targets = []
     @_delimiter = obj.delimiter or '/'


WIP: Stupid fucking paginated GET /top/:page rig

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -22,6 +22,15 @@
       layout: null
       archive: archive.archive

+app.get '/top/:skip?', (request, response) ->
+  failHome = -> return response.redirect('/')
+  return failHome() if isNaN(skip = parseInt(request.params.skip || 0))
+
+  perPage = 10
+  skipTotal = (skip * perPage)
+  Comic.top perPage, skipTotal, (err, comics) =>
+    return response.status(400).send JSON.stringify err: err if err
+    response.send JSON.stringify comics: comics

 app.get '/archive/:start?', (request, response) ->
   Comic.archive request.params.start, 10, (err, archive) =>


is:published, is:deleted, is:minimized, is:private #29

--- lib/search.coffee
+++ lib/search.coffee
@@ -140,6 +140,16 @@
               wants.push root: $ne: null
             else
               wants.push root: null
+          when 'published'
+            if negate
+              wants.push published: false
+            else
+              wants.push published: $ne: false
+          when 'deleted', 'minimized', 'private'
+            if negate
+              wants.push "#{token}": $ne: true
+            else
+              wants.push "#{token}": true
           else
             console.warn "Unknown 'is:' specification '#{token}'"
   options =
@@ -234,6 +244,34 @@
       "root message"
     else
       "descendant of message #{root}"
+  else if _.isEqual keys, ['published']
+    if _.isEqual query.published, false
+      'unpublished'
+    else if _.isEqual query.published, {$ne: false}
+      'published'
+    else
+      "published: #{query.published}"
+  else if _.isEqual keys, ['deleted']
+    if _.isEqual query.deleted, true
+      'deleted'
+    else if _.isEqual query.deleted, {$ne: true}
+      'not deleted'
+    else
+      "deleted: #{query.deleted}"
+  else if _.isEqual keys, ['minimized']
+    if _.isEqual query.minimized, true
+      'minimized'
+    else if _.isEqual query.minimized, {$ne: true}
+      'not minimized'
+    else
+      "minimized: #{query.minimized}"
+  else if _.isEqual keys, ['private']
+    if _.isEqual query.private, true
+      'private'
+    else if _.isEqual query.private, {$ne: true}
+      'not private'
+    else
+      "private: #{query.private}"
   else if _.isRegExp query
     simplify = unbreakRegExp uncaseInsensitiveRegExp query.source
     if realRegExp simplify


Fixing remote port again

Fixing remote port again

--- lib/server.coffee
+++ lib/server.coffee
@@ -5,6 +5,8 @@
 Redis = require "redis"
 RedisStore = require("connect-redis")(express)

+####################
+
 Pass = require "passport"
 TwitterStrategy = require("passport-twitter").Strategy
 GithubStrategy = require("passport-github").Strategy
@@ -12,6 +14,10 @@
 MeetupStrategy = require("passport-meetup").Strategy
 GoogleStrategy = require("passport-google-oauth").Strategy

+####################
+
+CHANNEL = require "channel_names"
+
 ################################################################################

 redisClient = Redis.createClient()
@@ -20,6 +26,7 @@
 ## Faye Config

 faye = new Faye.NodeAdapter(mount: "/faye", timeout: 45)
+publish = (args...) -> faye.getClient().publish(args...)

 faye.asObservable = (event_type) ->
   subj = new Rx.Subject()
@@ -126,10 +133,11 @@
 ## Routes

 _slideshowRedirect = (service, req, resp) ->
-  console.log("====")
-  console.log("Authenticated Successfuly with", service)
-  console.log(req.user)
-  console.log("====")
+  publish(CHANNEL.clientAuthenticated,
+    provider: service,
+    displayName: req.user.displayName,
+    username: req.user.username,
+    id: req.user.id)
   resp.redirect("/slideshow/")
   resp.end()
   null
@@ -215,5 +223,5 @@
       resp.end()
     null)

-app = server.listen(8000)
+app = server.listen(8080)
 faye.attach(app)


rm SIGTERM handling as it does not work on heroku

--- lib/server.coffee
+++ lib/server.coffee
@@ -24,6 +24,3 @@
             handle req, res

         .listen port, undefined, -> console.log "listening on port #{port}"
-
-    # graceful shutdown
-    process.on 'SIGTERM', srv.close  # stop accepting connections, terminate once event queue empty


Fix linking sessions to sockets when using SSL

--- lib/server.coffee
+++ lib/server.coffee
@@ -82,11 +82,10 @@
       .match(/\-+BEGIN CERTIFICATE\-+[0-9a-zA-Z\+\-\/\=\s]+?\-+END CERTIFICATE\-+/g)

   server = require('https').createServer(options, listener)
+  server.on('secureConnection', onConnection) if onConnection?
 else
   server = require('http').createServer(listener)
-
-if onConnection?
-  server.on('connection', onConnection)
+  server.on('connection', onConnection) if onConnection?

 db.connect((err) ->
   throw err if err


Require HTTPS in production

Require HTTPS in production

--- lib/server.coffee
+++ lib/server.coffee
@@ -14,6 +14,19 @@
 loggingFormat = "#{LOG_PREFIX}:remote-addr \":method :url HTTP/:http-version\" :status :res[content-length] request-id=:req[X-Request-ID] \":referrer\" \":user-agent\""
 server.use morgan(loggingFormat)

+# require HTTPS
+server.use (req, res, next) ->
+  hostPort = req.get('Host')
+  host = /^(.+?)(:\d+)?$/.exec(hostPort)[1]
+  isSecure = req.secure or (req.get('x-forwarded-proto') is 'https')
+  isLocalhost = host is 'localhost'
+
+  if not isSecure and not isLocalhost
+    res.redirect 301, "https://#{hostPort}#{req.url}"
+
+  else
+    next()
+
 # enable gzip compression
 server.use compress()


Fix PDF window focus for non Windows platforms

Fixes #16

--- lib/viewer.coffee
+++ lib/viewer.coffee
@@ -50,7 +50,7 @@
       @latex.viewer.focusMain()

   focusViewer: ->
-    @window.focus() if @window? and !@window.isDestroyed()
+    @window.show() if @window? and !@window.isDestroyed()

   focusMain: ->
     @self.focus() if @self? and !@self.focused


spec: restore spy for window.confirm

spec: restore spy for window.confirm

Change-Id: I715b4c7e8b7475f412f8a1b555cb197fd72da7f0
Reviewed-on: https://gerrit.instructure.com/56502
Reviewed-by: Steven Burnett <a0b450117c5d20a22d5c4d52bb8ae13adee003d8@instructure.com>
Tested-by: Jenkins
Product-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>

--- spec/coffeescripts/views/conferences/ConferenceViewSpec.coffee
+++ spec/coffeescripts/views/conferences/ConferenceViewSpec.coffee
@@ -31,7 +31,7 @@
     ok view

   test 'delete calls screenreader', ->
-    sinon.stub(window, 'confirm').returns(true)
+    confirm_stub = sinon.stub(window, 'confirm').returns(true)
     ENV.context_asset_string = "course_1"
     server = sinon.fakeServer.create()
     server.respondWith('DELETE', '/api/v1/courses/1/conferences/1',
@@ -47,3 +47,4 @@
     view.delete(jQuery.Event( "click" ))
     server.respond()
     equal $.screenReaderFlashMessage.callCount, 1
+    confirm_stub.restore()


fix typo in test

fix typo in test

--- src/api/apps/articles/test/model/index/persistence.test.coffee
+++ src/api/apps/articles/test/model/index/persistence.test.coffee
@@ -861,7 +861,7 @@
         article.sponsor.partner_light_logo.should.equal 'https://media.artsy.net/partner_light_logo.jpg'
         article.sponsor.partner_condensed_logo.should.equal 'https://media.artsy.net/partner_condensed_logo.jpg'
         article.sponsor.partner_logo_link.should.equal 'https://partner.com'
-        article.sponsor.pixel_tracking_code.equal 'tracking_image.jpg'
+        article.sponsor.pixel_tracking_code.should.equal 'tracking_image.jpg'
         done()

     it 'saves a news_source', (done) ->


add number of new members to alert

add number of new members to alert

--- src/node_modules/group-members/group-members-controller.coffee
+++ src/node_modules/group-members/group-members-controller.coffee
@@ -31,7 +31,7 @@
       if modalRet.members.length > 0
         $scope.addAlert(
           type: "success"
-          msg: "Successfully added new members:
+          msg: "Successfully added #{ modalRet.members.length } new members:
             #{ (_.map modalRet.members, (m) -> m.name).join(", ") }"
         )


Fixed: text is actually selected when the badge is clicked

--- client/app/views/widgets/combobox.coffee
+++ client/app/views/widgets/combobox.coffee
@@ -47,7 +47,7 @@
         @$el.focus().val(@value()).autocomplete 'search', ''
         # when clicking on menu, auto selecting the input so that it
         # can be updated when typing
-        @$el.select()
+        @$el[0].setSelectionRange 0, @value().length

     setValue: (value) =>
         @$el.val value


use cid for key

use cid for key

--- client/lanes/components/grid/Header.cjsx
+++ client/lanes/components/grid/Header.cjsx
@@ -19,7 +19,7 @@
             desc:  sorted and not @props.query.sortAscending
             @props.cellStyles.props[i].className
         )
-        <div key={i}
+        <div key={f.cid}
             {...@props.cellStyles.props[i]}
             onClick={_.partial(@onColumnClick, f)}
             className={classNames}


wording.

wording.

--- client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
+++ client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
@@ -112,7 +112,7 @@

     @showAllLink = new KDCustomHTMLView
       tagName    : "a"
-      partial    : "show all"
+      partial    : "See All"
       cssClass   : "show-all-link"
       click      : (event) ->
         KD.singletons.router.handleRoute "/#{group.slug}/Members"


Disable unmute option for now

Disable unmute option for now

--- client/ide/lib/views/chat/idechatmessageparticipantavatar.coffee
+++ client/ide/lib/views/chat/idechatmessageparticipantavatar.coffee
@@ -66,12 +66,13 @@

     appManager.tell 'IDE', 'getCollaborationData', (data) =>
       appManager.tell 'IDE', 'hasParticipantWithAudio', @nickname, (hasAudio) =>
-        if hasAudio
-          items['Unmute'] =
-            title    : 'Unmute'
-            callback : =>
-              MENU?.destroy()
-              appManager.tell 'IDE', 'unmuteParticipant', @nickname
+        # disabled for now
+        # if hasAudio
+        #   items['Mute'] =
+        #     title    : 'Mute'
+        #     callback : =>
+        #       MENU?.destroy()
+        #       appManager.tell 'IDE', 'muteParticipant', @nickname

         if data.amIHost
           items['Kick'] =


More class conversion

More class conversion

--- resources/assets/coffee/react/profile-page/beatmaps.coffee
+++ resources/assets/coffee/react/profile-page/beatmaps.coffee
@@ -19,16 +19,16 @@
 {div, h2, h3, ul, li, a, p, pre, span} = React.DOM
 el = React.createElement

-ProfilePage.Beatmaps = React.createClass
-  mixins: [React.addons.PureRenderMixin]
+class ProfilePage.Beatmaps extends React.PureComponent
+  constructor: (props) ->
+    super props
+
+    @state =
+      visible_favourite: 6
+      visible_ranked_and_approved: 6

-  getInitialState: ->
-    visible_favourite: 6
-    visible_ranked_and_approved: 6
-
-
-  render: ->
+  render: =>
     allBeatmapsets =
       favourite: @props.favouriteBeatmapsets
       ranked_and_approved: @props.rankedAndApprovedBeatmapsets
@@ -63,7 +63,7 @@
             p className: 'page-extra-entries', osu.trans('users.show.extra.beatmaps.none')

-  _showMore: (e) ->
+  _showMore: (e) =>
     e.preventDefault()

     key = e.currentTarget.dataset.section


Update load-markdown-documents.coffee

Update load-markdown-documents.coffee

--- documentation/app/scripts/modules/load-markdown-documents.coffee
+++ documentation/app/scripts/modules/load-markdown-documents.coffee
@@ -16,7 +16,7 @@
   create_a_mv_example: require("../../documents/api/create-a-mv-example.md")
   create_a_mv: require("../../documents/api/create-a-mv.md")
   create_a_redeem_order: require("../../documents/api/create-a-redeem-order.md")
-  create_a_redeem_order_example: require("../../documents/api/create-a-redeem-order_example.md")
+  create_a_redeem_order_example: require("../../documents/api/create-a-redeem-order-example.md")
   create_a_report_example: require("../../documents/api/create-a-report-example.md")
   create_a_report_instance_example: require("../../documents/api/create-a-report-instance-example.md")
   create_a_report_instance: require("../../documents/api/create-a-report-instance.md")


removed extra spaces from enrollment code links

--- src/components/course-settings/period-enrollment-code.cjsx
+++ src/components/course-settings/period-enrollment-code.cjsx
@@ -21,6 +21,8 @@

   getInstructions: (code) ->
     {bookUrl, bookName} = @props
+    # appends :3 to skip book intro
+    url = bookUrl + ':3'

@@ -47,8 +49,8 @@

             1. Paste this link in your web browser to visit
-             your {bookName} textbook:
-
{bookUrl}:3

+             your{bookName}textbook:
+
{url}

             2.


Added a check for existance of the author JAccount.

--- server/lib/crawler/index.coffee
+++ server/lib/crawler/index.coffee
@@ -78,11 +78,21 @@
         if err
           console.error err
           callback null, err
-        fullName = acc.data.profile.firstName + " "
-        fullName += acc.data.profile.lastName
+
+        fullName = "A koding user"
+        if acc?.data?.profile?.firstName
+          fullName = acc.data.profile.firstName + " "
+
+        if acc?.data?.profile?.lastName
+          fullName += acc.data.profile.lastName
+
+        hash = ""
+        if acc?.data?.profile?.hash
+          hash = acc.data.profile.hash
+
         activityContent = {
           fullName : fullName
-          hash : acc.data.profile.hash
+          hash : hash
           title : if model.title then model.title else model.body or ""
           body : if model.body  then model.body  else ""
           codeSnippet : htmlEncode codeSnippet
@@ -107,7 +117,7 @@
     [slash, name, section] = slug.split("/")
     [firstLetter] = name

-    # if there is no firstLetter, request hits home
+    # if there is no firstLetter, serve homepage to bot.
     unless firstLetter
       content = kodinghome()
       return res.send 200, content


manyboxplots: clicked arrays in multiple colors

--- manyboxplots/manyboxplots.coffee
+++ manyboxplots/manyboxplots.coffee
@@ -141,8 +141,8 @@
        .attr("width", 2)
        .attr("id", d)
        .attr("height", yScale(data.quant[0][d]) - yScale(data.quant[nQuant-1][d]))
-       .attr("fill", "blue")
        .attr("opacity", 0)
+       .attr("stroke", "none")

   # vertical rectangles representing each array
   indRectGrp = svg.append("g")
@@ -269,6 +269,8 @@
        .attr("stroke-width", "2")

+  histColors = ["blue", "red", "green", "orange", "black"]
+
   lowsvg.append("text")
         .datum(randomInd)
         .attr("x", w/2)
@@ -280,7 +282,8 @@

   indRect
     .on("mouseover", (d) ->
-              d3.select(this).attr("opacity", "1")
+              d3.select(this)
+                 .attr("opacity", "1")
               d3.select("#histline")
                  .datum(data.counts[d])
                  .attr("d", histline)
@@ -295,13 +298,18 @@
               clickStatus[d] = 1 - clickStatus[d]
               svg.select("rect##{d}").attr("opacity", clickStatus[d])
               if clickStatus[d]
+                curcolor = histColors.shift()
+                histColors.push(curcolor)
+
                 d3.select(this).attr("opacity", "0")
+                svg.select("rect##{d}").attr("fill", curcolor)
+
                 lowsvg.append("path")
                       .datum(data.counts[d])
                       .attr("d", histline)
                       .attr("id", d)
                       .attr("fill", "none")
-                      .attr("stroke", "blue")
+                      .attr("stroke", curcolor)
                       .attr("stroke-width", "2")
               else
                 lowsvg.select("path##{d}").remove()


use prefer-english-operator consistently

--- src/RuleProcessor.coffee
+++ src/RuleProcessor.coffee
@@ -1,6 +1,6 @@
 class RuleProcessor
   rule:
-    name: 'prefer_english_operators'
+    name: 'prefer_english_operator'
     description: 'This rule encourages to use english style operators.'
     level: 'warn'
     message: 'Use English equivalent operators. i.e)is, isnt, and, or'


Attach trigger event to owner rather than parent view

--- src/scripts/helpers/backbone/views/attached/tooltip/tooltip.coffee
+++ src/scripts/helpers/backbone/views/attached/tooltip/tooltip.coffee
@@ -21,9 +21,7 @@
       @$owner = $(options.owner)

     onShow: () ->
-      events = @parent.events or {}
-      events[@trigger] = () => @toggle()
-      @parent.delegateEvents(events)
+      @$owner.on "#{@trigger}.#{@type}", () => @toggle()

     toggle: () ->
       @reposition()
@@ -34,16 +32,25 @@

       switch @placement
         when 'top'
-          console.log 'top'
+          $container.css
+            'top': @$owner.offset().top + @$owner.outerHeight()
+            'left': 'auto'
+            'right': $(document).outerWidth(true) - (@$owner.offset().left + @$owner.outerWidth())
         when 'right'
-          console.log 'right'
+          $container.css
+            'top': @$owner.offset().top + @$owner.outerHeight()
+            'left': 'auto'
+            'right': $(document).outerWidth(true) - (@$owner.offset().left + @$owner.outerWidth())
         when 'bottom'
           $container.css
             'top': @$owner.offset().top + @$owner.outerHeight()
             'left': 'auto'
             'right': $(document).outerWidth(true) - (@$owner.offset().left + @$owner.outerWidth())
         when 'left'
-          console.log 'left'
+          $container.css
+            'top': @$owner.offset().top + @$owner.outerHeight()
+            'left': 'auto'
+            'right': $(document).outerWidth(true) - (@$owner.offset().left + @$owner.outerWidth())

     onBeforeClose: () ->
-      @parent.delegateEvents(@parent.events)
+      @$owner.off "#{@trigger}.#{@type}"


Use collection to retrieve fact in opinionatersevidence

--- core/app/backbone/models/evidence.coffee
+++ core/app/backbone/models/evidence.coffee
@@ -4,8 +4,8 @@

   # TODO: eventually, fetching this model should populate
   #       the collection, not the other way around
-  initialize: (attributes, options) ->
-    @_wheel = options.fact.getFactWheel()
+  initialize: ->
+    @_wheel = @collection.fact.getFactWheel()
     @opinionaters().fetch()

     @_wheel.on 'change', =>


:airplane::u6709: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,4 +1,4 @@
-version: "0.1.0"
+version: "0.1.1"
 remoteDependencies: [
   "https://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.6.3/coffee-script.min.js"
 ]


Allow SMTP server with no login

Allow SMTP server with no login

Only require smtp host to be present. And include username and password if they are provided.

--- packages/rocketchat-lib/server/startup/settingsOnLoadSMTP.coffee
+++ packages/rocketchat-lib/server/startup/settingsOnLoadSMTP.coffee
@@ -1,7 +1,10 @@
 buildMailURL = _.debounce ->
 	console.log 'Updating process.env.MAIL_URL'
-	if RocketChat.settings.get('SMTP_Host') and RocketChat.settings.get('SMTP_Username') and RocketChat.settings.get('SMTP_Password')
-		process.env.MAIL_URL = "smtp://" + encodeURIComponent(RocketChat.settings.get('SMTP_Username')) + ':' + encodeURIComponent(RocketChat.settings.get('SMTP_Password')) + '@' + encodeURIComponent(RocketChat.settings.get('SMTP_Host'))
+	if RocketChat.settings.get('SMTP_Host')
+		process.env.MAIL_URL = "smtp://"
+		if RocketChat.settings.get('SMTP_Username') and RocketChat.settings.get('SMTP_Password')
+                        process.env.MAIL_URL += encodeURIComponent(RocketChat.settings.get('SMTP_Username')) + ':' + encodeURIComponent(RocketChat.settings.get('SMTP_Password')) + '@'
+                process.env.MAIL_URL += encodeURIComponent(RocketChat.settings.get('SMTP_Host'))
 		if RocketChat.settings.get('SMTP_Port')
 			process.env.MAIL_URL += ':' + parseInt(RocketChat.settings.get('SMTP_Port'))
 , 500


Fixed nasty uncertainty bug.

Fixed nasty uncertainty bug.

--- Src/coffeescript/cql-execution/src/datatypes/uncertainty.coffee
+++ Src/coffeescript/cql-execution/src/datatypes/uncertainty.coffee
@@ -6,7 +6,7 @@

   constructor: (@low = null, @high) ->
     gt = (a, b) -> if a.constructor.name is 'DateTime' then a.after b else a > b
-    if typeof high is 'undefined' then @high = @low
+    if typeof @high is 'undefined' then @high = @low
     if @low? and @high? and gt(@low, @high) then [@low, @high] = [@high, @low]

   isPoint: () ->


Replace this with @ where applicable

Replace this with @ where applicable

--- app/assets/javascripts/spree/backend/product_properties.js.coffee
+++ app/assets/javascripts/spree/backend/product_properties.js.coffee
@@ -2,16 +2,16 @@
   constructor: (@node, payload, @product_id) ->
     @category_editors = []

-    this.fetchCategories(payload)
+    @fetchCategories(payload)

     @node.on 'click', '.add-category', =>
-      this.addCategory({name: 'New Category'})
+      @addCategory({name: 'New Category'})

     @node.on 'click', '.save', =>
-      this.save()
+      @save()

     @node.on 'deleteCategory', (ev, category) =>
-      this.categoryDeleted(category)
+      @categoryDeleted(category)

   categoryDeleted: (category) ->
     category.node.remove()
@@ -19,13 +19,13 @@

   fetchCategories: (payload) ->
     $.each payload, (index, item) =>
-      this.addCategory(item)
+      @addCategory(item)

   addCategory: (category) ->
     @category_editors.push new CategoryEditor(@node, category)

   save: ->
-    payload = { product_id: @product_id, product_categories: this.serialize() }
+    payload = { product_id: @product_id, product_categories: @serialize() }
     $.ajax
       type: 'POST'
       dataType: 'json'
@@ -58,13 +58,13 @@

     if @category.properties
       $.each @category.properties, (index, item) =>
-        this.addProperty(item)
+        @addProperty(item)

     @property_rows.on 'deleteProperty', (ev, property) =>
-      this.propertyDeleted(property)
+      @propertyDeleted(property)

     @add_property_button.click =>
-      this.addProperty({key: '', value: ''})
+      @addProperty({key: '', value: ''})

     @delete_button.click =>
       @parent.trigger 'deleteCategory', this
@@ -81,7 +81,7 @@

   serialize: ->
     {
-      name: this.name(),
+      name: @name(),
       properties: @properties.map (p) -> p.serialize()
     }

@@ -103,4 +103,4 @@
     @node.find(".js-val").val()

   serialize: ->
-    { key: this.key(), value: this.value() }
+    { key: @key(), value: @value() }


Every time plus icon add to the parent's end.

--- client/app/lib/commonviews/applicationview/applicationtabhandleholder.coffee
+++ client/app/lib/commonviews/applicationview/applicationtabhandleholder.coffee
@@ -73,8 +73,11 @@

   repositionPlusHandle: (handles) ->
-    handlesLength = handles.length
-    @plusHandle?.$().insertAfter handles[handlesLength - 1].$() if handlesLength
+
+    return unless handles.length
+
+    @plusHandle?.$().appendTo @tabs.getDomElement()

   addHandle: (handle)->
+
     @tabs.addSubView handle


go to usual join if not logged in and you try to request an invite within group context

--- client/landing/app/Applications/Home.kdapplication/Views/homeloginbar.coffee
+++ client/landing/app/Applications/Home.kdapplication/Views/homeloginbar.coffee
@@ -45,7 +45,7 @@
       attributes  :
         href      : "/Join"
       click       : (event)=>
-        if entryPoint isnt ''
+        if entryPoint isnt '' and KD.isLoggedIn()
           @utils.stopDOMEvent event
           @getSingleton('mainController').emit "groupAccessRequested", @group, no
         else


added basic target rendering

added basic target rendering

--- src/mist-ninja-builder.coffee
+++ src/mist-ninja-builder.coffee
@@ -60,6 +60,19 @@
   prepareCommand: (command)->
     command.replace /\%([fo])/g, '${_$1}'

+  processInput: (input, vars)->
+    results = []
+    switch input.type
+      when 'glob'
+        input = @inlineVariables input.glob, vars
+        results.push MistGlobber.performGlob input, @rootDir
+      when 'group'
+        console.warn 'WARN: groups not yet implemented'
+      else
+        throw "unknown input type: #{input.type}\n" +
+          "  at #{@rootDir}/Mistfile:#{target.line}:#{target.column}"
+    return results
+
   compile: ->
     result = []

@@ -90,20 +103,7 @@
             commandHash = rules[command]

           console.log '\x1b[31m', target, '\x1b[0m'
-          aggInputs = []
-          for input in target.mainInputs
-            switch input.type
-              when 'glob'
-                input = @inlineVariables input.glob, vars
-                aggInputs.push MistGlobber.performGlob input, @rootDir
-              when 'group'
-                console.warn 'WARN: groups not yet implemented'
-              else
-                throw "unknown input type: #{input.type}\n" +
-                  "  at #{@rootDir}/Mistfile:#{target.line}:#{target.column}"
-            if input of targets
-              throw "duplicate input: #{input}\n" +
-                "  at #{@rootDir}/Mistfile:#{target.line}:#{target.column}"
+          aggInputs = target.mainInputs.map (i)=> @processInput i, vars

           targets = aggInputs.compile().map (input)=>
             # NOTE: do not flatten arrays here!


Use fs.resolveOnLoadPath() instead of require.resolve()

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -56,7 +56,7 @@
       true

   createPackageFiles: ->
-    templatePath = require.resolve(fs.join("package-generator", "template"))
+    templatePath = fs.resolveOnLoadPath(fs.join("package-generator", "template"))
     packageName = fs.base(@getPackagePath())

     for path in fs.listTree(templatePath)


:white_check_mark: g0, g^, g$ and allowMoveToOffScreenColumnOnScreenLineMotion config

--- spec/motion-general-spec.coffee
+++ spec/motion-general-spec.coffee
@@ -1,6 +1,12 @@
 {Point} = require 'atom'
 {getVimState, dispatch, TextData, getView} = require './spec-helper'
 settings = require '../lib/settings'
+
+setEditorWidthInCharacters = (editor, widthInCharacters) ->
+  component = editor.component
+  component.element.style.width =
+    component.getGutterContainerWidth() + widthInCharacters * component.measurements.baseCharacterWidth + "px"
+  return component.getNextUpdatePromise()

 describe "Motion general", ->
   [set, ensure, keystroke, editor, editorElement, vimState] = []
@@ -1104,6 +1110,126 @@
     describe "as a selection", ->
       it 'selects to the first column of the line', ->
         ensure 'd 0', text: 'cde', cursor: [0, 0]
+
+  describe "g 0, g ^ and g $", ->
+    enableSoftWrapAndEnsure = ->
+      editor.setSoftWrapped(true)
+      expect(editor.lineTextForScreenRow(0)).toBe(" 123456789")
+      expect(editor.lineTextForScreenRow(1)).toBe(" B12345678") # first space is softwrap indentation
+      expect(editor.lineTextForScreenRow(2)).toBe(" 9C1234567") # first space is softwrap indentation
+      expect(editor.lineTextForScreenRow(3)).toBe(" 89") # first space is softwrap indentation
+
+    beforeEach ->
+      set text_: """
+      _123456789B123456789C123456789
+      """
+      jasmine.attachToDOM(getView(atom.workspace))
+      waitsForPromise -> setEditorWidthInCharacters(editor, 10)
+
+    describe "the g 0 keybinding", ->
+      describe "allowMoveToOffScreenColumnOnScreenLineMotion = true(default)", ->
+        beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', true)
+
+        describe "softwrap = false, firstColumnIsVisible = true", ->
+          beforeEach -> set cursor: [0, 3];
+          it "move to column 0 of screen line", -> ensure "g 0", cursor: [0, 0]
+
+        describe "softwrap = false, firstColumnIsVisible = false", ->
+          beforeEach -> set cursor: [0, 15]; editor.setFirstVisibleScreenColumn(10)
+          it "move to column 0 of screen line", -> ensure "g 0", cursor: [0, 0]
+
+        describe "softwrap = true", ->
+          beforeEach -> enableSoftWrapAndEnsure()
+          it "move to column 0 of screen line", ->
+            set cursorScreen: [0, 3]; ensure "g 0", cursorScreen: [0, 0]
+            set cursorScreen: [1, 3]; ensure "g 0", cursorScreen: [1, 1] # skip softwrap indentation.
+
+      describe "allowMoveToOffScreenColumnOnScreenLineMotion = false", ->
+        beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', false)
+
+        describe "softwrap = false, firstColumnIsVisible = true", ->
+          beforeEach -> set cursor: [0, 3]
+          it "move to column 0 of screen line", -> ensure "g 0", cursor: [0, 0]
+
+        describe "softwrap = false, firstColumnIsVisible = false", ->
+          beforeEach -> set cursor: [0, 15]; editor.setFirstVisibleScreenColumn(10)
+          it "move to first visible colum of screen line", -> ensure "g 0", cursor: [0, 10]
+
+        describe "softwrap = true", ->
+          beforeEach -> enableSoftWrapAndEnsure()
+          it "move to column 0 of screen line", ->
+            set cursorScreen: [0, 3]; ensure "g 0", cursorScreen: [0, 0]
+            set cursorScreen: [1, 3]; ensure "g 0", cursorScreen: [1, 1] # skip softwrap indentation.
+
+    describe "the g ^ keybinding", ->
+      describe "allowMoveToOffScreenColumnOnScreenLineMotion = true(default)", ->
+        beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', true)
+
+        describe "softwrap = false, firstColumnIsVisible = true", ->
+          beforeEach -> set cursor: [0, 3]
+          it "move to first-char of screen line", -> ensure "g ^", cursor: [0, 1]
+
+        describe "softwrap = false, firstColumnIsVisible = false", ->
+          beforeEach -> set cursor: [0, 15]; editor.setFirstVisibleScreenColumn(10)
+          it "move to first-char of screen line", -> ensure "g ^", cursor: [0, 1]
+
+        describe "softwrap = true", ->
+          beforeEach -> enableSoftWrapAndEnsure()
+          it "move to first-char of screen line", ->
+            set cursorScreen: [0, 3]; ensure "g ^", cursorScreen: [0, 1]
+            set cursorScreen: [1, 3]; ensure "g ^", cursorScreen: [1, 1] # skip softwrap indentation.
+
+      describe "allowMoveToOffScreenColumnOnScreenLineMotion = false", ->
+        beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', false)
+
+        describe "softwrap = false, firstColumnIsVisible = true", ->
+          beforeEach -> set cursor: [0, 3]
+          it "move to first-char of screen line", -> ensure "g ^", cursor: [0, 1]
+
+        describe "softwrap = false, firstColumnIsVisible = false", ->
+          beforeEach -> set cursor: [0, 15]; editor.setFirstVisibleScreenColumn(10)
+          it "move to first-char of screen line", -> ensure "g ^", cursor: [0, 10]
+
+        describe "softwrap = true", ->
+          beforeEach -> enableSoftWrapAndEnsure()
+          it "move to first-char of screen line", ->
+            set cursorScreen: [0, 3]; ensure "g ^", cursorScreen: [0, 1]
+            set cursorScreen: [1, 3]; ensure "g ^", cursorScreen: [1, 1] # skip softwrap indentation.
+
+    describe "the g $ keybinding", ->
+      describe "allowMoveToOffScreenColumnOnScreenLineMotion = true(default)", ->
+        beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', true)
+
+        describe "softwrap = false, lastColumnIsVisible = true", ->
+          beforeEach -> set cursor: [0, 27]
+          it "move to last-char of screen line", -> ensure "g $", cursor: [0, 29]
+
+        describe "softwrap = false, lastColumnIsVisible = false", ->
+          beforeEach -> set cursor: [0, 15]; editor.setFirstVisibleScreenColumn(10)
+          it "move to last-char of screen line", -> ensure "g $", cursor: [0, 29]
+
+        describe "softwrap = true", ->
+          beforeEach -> enableSoftWrapAndEnsure()
+          it "move to last-char of screen line", ->
+            set cursorScreen: [0, 3]; ensure "g $", cursorScreen: [0, 9]
+            set cursorScreen: [1, 3]; ensure "g $", cursorScreen: [1, 9]
+
+      describe "allowMoveToOffScreenColumnOnScreenLineMotion = false", ->
+        beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', false)
+
+        describe "softwrap = false, lastColumnIsVisible = true", ->
+          beforeEach -> set cursor: [0, 27]
+          it "move to last-char of screen line", -> ensure "g $", cursor: [0, 29]
+
+        describe "softwrap = false, lastColumnIsVisible = false", ->
+          beforeEach -> set cursor: [0, 15]; editor.setFirstVisibleScreenColumn(10)
+          it "move to last-char in visible screen line", -> ensure "g $", cursor: [0, 20]
+
+        describe "softwrap = true", ->
+          beforeEach -> enableSoftWrapAndEnsure()
+          it "move to last-char of screen line", ->
+            set cursorScreen: [0, 3]; ensure "g $", cursorScreen: [0, 9]
+            set cursorScreen: [1, 3]; ensure "g $", cursorScreen: [1, 9]

   describe "the | keybinding", ->
     beforeEach ->


Test that date inputs are validated on blur (fixes #336)

--- spec/unpoly/form_spec.js.coffee
+++ spec/unpoly/form_spec.js.coffee
@@ -1459,7 +1459,7 @@

       describe 'when a selector is given', ->

-        it "submits the input's form with an 'X-Up-Validate' header and replaces the selector with the response", asyncSpec (next) ->
+        it "submits the input's form when the input is changed, adding an 'X-Up-Validate' header, and then replaces the selector", asyncSpec (next) ->
           $form = $fixture('form[action="/path/to"]')
           $group = $("""

@@ -1495,6 +1495,30 @@
             # Since there isn't anyone who could handle the rejection inside
             # the event handler, our handler mutes the rejection.
             expect(window).not.toHaveUnhandledRejections() if REJECTION_EVENTS_SUPPORTED
+
+        # https://github.com/unpoly/unpoly/issues/336
+        it 'validates a input[type=date] on blur instead of change, as browser date pickers often emit `change` when any date component changes', asyncSpec (next) ->
+          $form = $fixture('form[action="/path/to"]')
+          $group = $("""
+

+              <input name="birthday" type="date" up-validate=".field-group">
+

+          """).appendTo($form)
+          up.hello($form)
+
+          $input = $group.find('input')
+          $input.focus()
+          $input.val('2017-01-02')
+          Trigger.change($input)
+
+          next ->
+            expect(jasmine.Ajax.requests.count()).toBe(0)
+
+            up.emit($input, 'blur')
+
+          next ->
+            expect(jasmine.Ajax.requests.count()).toBe(1)
+            expect(jasmine.lastRequest().requestHeaders['X-Up-Validate']).toEqual('birthday')

         it 'does not reveal the updated fragment (bugfix)', asyncSpec (next) ->
           revealSpy = spyOn(up, 'reveal').and.returnValue(Promise.resolve())


Fix unnassigned in Firefox

Fix unnassigned in Firefox

--- app/modules/components/assigned/assigned-users.directive.coffee
+++ app/modules/components/assigned/assigned-users.directive.coffee
@@ -57,7 +57,6 @@

         $scope.unassign = (user) ->
             return if not isEditable()
-            target = angular.element(event.currentTarget)
             assignedUserId = user.id

             title = $translate.instant("COMMON.ASSIGNED_USERS.TITLE_LIGHTBOX_DELETE_ASSIGNED")


created fix so createDocument is lazy replaced with browser compatible version

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -89,9 +89,10 @@
   title = doc.querySelector 'title'
   [ title?.textContent, doc.body ]

-createDocument = do ->
+createDocument = (html) ->
   createDocumentUsingParser = (html) ->
-    (new DOMParser).parseFromString html, 'text/html'
+    try
+      (new DOMParser).parseFromString html, 'text/html'

   createDocumentUsingWrite = (html) ->
     doc = document.implementation.createHTMLDocument ''
@@ -104,9 +105,11 @@
     testDoc = createDocumentUsingParser '<html><body><p>test'

   if testDoc?.body?.childNodes.length is 1
-    createDocumentUsingParser
+    createDocument = createDocumentUsingParser
   else
-    createDocumentUsingWrite
+    createDocument = createDocumentUsingWrite
+
+  createDocument html

 handleClick = (event) ->
@@ -145,7 +148,7 @@

 ignoreClick = (event, link) ->
   samePageLink(link) or crossOriginLink(link) or anchoredLink(link) or
-  nonHtmlLink(link)  or remoteLink(link)      or noTurbolink(link)  or
+  nonHtmlLink(link)  or remoteLink(link)      or noTurbolink(link)  or
   newTabClick(event)


fix sentence splitting bug skipping final sent

--- client/controllers/incidentReports/suggestedIncidentsModal.coffee
+++ client/controllers/incidentReports/suggestedIncidentsModal.coffee
@@ -58,6 +58,8 @@
       sentStart = idx
     else
       idx++
+  if sentStart < idx
+    sents[sents.length] = text.slice(sentStart, idx)
   return sents

 # determines if the user should be prompted before leaving the current modal


Fixes data overriding before render

Fixes data overriding before render

--- src/client/widget.coffee
+++ src/client/widget.coffee
@@ -165,9 +165,9 @@
       ctx = @buildContext.call(@)
       ctx.fail (err)-> console.error("Error building context: ", err)
       ctx.then (ctx)=>
-        beforeCtx = _.extend(@beforeRender.call(@, ctx), data)
+        beforeCtx = @beforeRender.call(@, ctx)
         $.when(beforeCtx).done (dataAfterBefore)=>
-          data = dataAfterBefore || ctx
+          data = _.extend(dataAfterBefore || ctx, data)
           @doRender(tpl, data)
           _.defer(@afterRender.bind(@, data))
           _.defer((-> @sandbox.start(@$el)).bind(@))


add Daemon button #1

add Daemon button #1

--- lib/daemon-status-bar-container-view.coffee
+++ lib/daemon-status-bar-container-view.coffee
@@ -13,7 +13,9 @@
     @element = $("<div/>",
       class: "inline-block smart-daemon-control",
     )
-    @addScanButton()
+    if @daemonManagement.scanDeamons.thereIsScanDaemonForOs()
+      @addScanButton()
+    else @addAddDaemonButton()
     @regConfOnDidChange()

   regConfOnDidChange: ->
@@ -24,23 +26,31 @@

   initialize: (@statusBar) ->

+  addAddDaemonButton: () ->
+    @addButton = $("<span/>",
+      text: "Add Daemon"
+      class: "scan-button"
+    ).click ()=>
+      @addDaemonItem()
+      @addButton.remove()
+    @element.append @addButton

   addScanButton: () -> #on reset/first install
     @scanButton = $("<span/>",
-      text: "Scan Daemons now"
+      text: "Scan Daemons"
       class: "scan-button"
     ).click ()=>
       @daemonManagement.scanDeamons.run()
-      @removeScanButton()
+      @scanButton.remove()
     @element.append @scanButton
-  removeScanButton: () ->
-    @scanButton.remove()
+

   addDaemonItem: (daemonItem) ->
     item = new DaemonStatusBarItemView(@serializedState,daemonItem,@daemonManagement)
     @items[daemonItem.id] = item
     @element.append item.element
-    @removeScanButton()
+    @scanButton?.remove()
+    @addButton?.remove()
   removeDaemon: (daemonItem) ->
     @items[daemonItem.id].element.remove()
     delete @items[daemonItem.id]


See #717. Remove unsupported options for Coldfusion language

--- src/languages/coldfusion.coffee
+++ src/languages/coldfusion.coffee
@@ -28,10 +28,6 @@
   ]

   options:
-    indent_inner_html:
-      type: 'boolean'
-      default: false
-      description: "Indent <head> and <body> sections."
     indent_size:
       type: 'integer'
       default: defaultIndentSize
@@ -42,53 +38,13 @@
       default: defaultIndentChar
       minimum: 0
       description: "Indentation character"
-    brace_style:
-      type: 'string'
-      default: "collapse"
-      enum: ["collapse", "expand", "end-expand", "none"]
-      description: "[collapse|expand|end-expand|none]"
-    indent_scripts:
-      type: 'string'
-      default: "normal"
-      enum: ["keep", "separate", "normal"]
-      description: "[keep|separate|normal]"
     wrap_line_length:
       type: 'integer'
       default: 250
       description: "Maximum characters per line (0 disables)"
-    wrap_attributes:
-      type: 'string'
-      default: "auto"
-      enum: ["auto", "force"]
-      description: "Wrap attributes to new lines [auto|force]"
-    wrap_attributes_indent_size:
-      type: 'integer'
-      default: defaultIndentSize
-      minimum: 0
-      description: "Indent wrapped attributes to after N characters"
     preserve_newlines:
       type: 'boolean'
       default: true
       description: "Preserve line-breaks"
-    max_preserve_newlines:
-      type: 'integer'
-      default: 10
-      description: "Number of line-breaks to be preserved in one chunk"
-    unformatted:
-      type: 'array'
-      default: ['a', 'sub', 'sup', 'b', 'i', 'u']
-      items:
-        type: 'string'
-      description: "List of tags (defaults to inline) that should not be reformatted"
-    end_with_newline:
-      type: 'boolean'
-      default: false
-      description: "End output with newline"
-    extra_liners:
-      type: 'array'
-      default: ['head', 'body', '/html']
-      items:
-        type: 'string'
-      description: "List of tags (defaults to [head,body,/html] that should have an extra newline before them."

 }


Add Spaces to engine.coffee for Legibility

--- src/coffee/engine.coffee
+++ src/coffee/engine.coffee
@@ -1,4 +1,5 @@
 angular.module 'qbn.engine', ['qbn.quality', 'qbn.storylet']
+
   .factory 'instantiator', () ->
     # The immutable objects that make up the game itself serve as prototypes for the objects that
     # make up the mutable game state. This function takes a game definition object and makes a state
@@ -14,5 +15,6 @@
         newName = first.toLowerCase() + rest
         instance[newName] = value
       return instance
+
   .controller 'QbnEngine', ($scope, qualityLibrary, storyletLibrary, instantiator) ->
     return


Allow style sheet path to be set as an option. Add class "old-android" to body

* old-andorid class helps with theme styling.

--- src/coffee/flight.coffee
+++ src/coffee/flight.coffee
@@ -28,7 +28,9 @@
   constructor: (options = {}) ->
     #TODO: don't allow slide up transitions if old android
     @detectUserAgent()
-    @setupStyleSheets()
+
+    if options.stylesheetPath?
+      @stylesheetPath = options.stylesheetPath

     if options.transitionAnimation?
       @transitionAnimation = options.transitionAnimation
@@ -39,6 +41,8 @@
     @mainMenu = document.querySelector @mainMenu if typeof @mainMenu is "string"

     @transitionAnimation = false if @os.android and @os.version <= '2.1'
+
+    @setupStyleSheets()

     @hideUrlBar() if options.hideUrlbar

@@ -292,6 +296,8 @@
       styleSheets = document.styleSheets
       styleSheet.disabled = true for styleSheet in styleSheets when styleSheet.href.indexOf("flight.css") isnt -1

+      document.body.className = "old-android"
+
   # Private: Hide the URL bar in mobile browsers.
   #
   # Returns nothing.


Update ranges tracker.

Update ranges tracker.

--- services/web/public/coffee/ide/review-panel/RangesTracker.coffee
+++ services/web/public/coffee/ide/review-panel/RangesTracker.coffee
@@ -95,6 +95,20 @@
 					change = c
 					break
 			return change
+
+		getChanges: (change_ids) ->
+			changes_response = []
+			ids_map = {}
+
+			for change_id in change_ids
+				ids_map[change_id] = true
+
+			for change in @changes
+				if ids_map[change.id]
+					delete ids_map[change.id]
+					changes_response.push change
+
+			return changes_response

 		removeChangeId: (change_id) ->
 			change = @getChange(change_id)


:art:

:art:

--- src/lines-tile-component.coffee
+++ src/lines-tile-component.coffee
@@ -113,8 +113,8 @@
       while oldLineNode = oldLineNodes.shift()
         break if @screenRowForNode(newLineNode) < @screenRowForNode(oldLineNode)

-      lineId = @lineIdsByScreenRow[newLineNode.dataset.screenRow]
-      @lineNodesByLineId[lineId] = newLineNode
+      id = @lineIdsByScreenRow[newLineNode.dataset.screenRow]
+      @lineNodesByLineId[id] = newLineNode
       if oldLineNode?
         @domNode.insertBefore(newLineNode, oldLineNode)
       else


fixes

fixes

--- src/local/model_postgres.coffee
+++ src/local/model_postgres.coffee
@@ -125,7 +125,7 @@
         cb err, res?.rows?.map (row) -> row.node

-LOST_TRANSACTION_TIMEOUT = 10 * 1000
+LOST_TRANSACTION_TIMEOUT = 60 * 1000

 ##
 # Wraps the postgres-js transaction with our model operations.
@@ -294,16 +294,6 @@
         @db.query "SELECT \"user\", node, subscription FROM subscriptions WHERE \"user\"=$1 OR listener=$1 ORDER BY updated DESC", [ actor ], (err, res) ->
             cb err, res?.rows

-    getPingNodes: (user, cb) ->
-        db = @db
-        async.waterfall [(cb2) ->
-            db.query "SELECT node FROM affiliations WHERE affiliation = 'owner' AND \"user\" = $1 AND EXISTS (SELECT \"user\" FROM subscriptions WHERE subscription = 'pending' AND node = affiliations.node) ORDER BY updated DESC", [ user ], cb2
-        , (res, cb2) ->
-            cb2 null, res.rows.map((row) ->
-                row.node
-            )
-        ], cb
-
     getPending: (node, cb) ->
         db = @db
         async.waterfall [(cb2) ->
@@ -315,7 +305,11 @@
         ], cb

     getNodeListeners: (node, cb) ->
-        @db.query "SELECT DISTINCT listener FROM subscriptions WHERE node = $1 AND listener IS NOT NULL"
+        @db.query """SELECT DISTINCT listener
+                     FROM subscriptions
+                     WHERE node=$1
+                     AND subscription='subscribed'
+                     AND listener IS NOT NULL"""
         , [node]
         , (err, res) ->
             cb err, res?.rows?.map((row) -> row.listener)
@@ -328,7 +322,7 @@
                      AND EXISTS (SELECT affiliation
                                  FROM affiliations
                                  WHERE node=$1
-                                 AND (affiliation='owner' OR affiliation='moderator')"""
+                                 AND (affiliation='owner' OR affiliation='moderator'))"""
         , [node]
         , (err, res) ->
             cb err, res?.rows?.map((row) -> row.listener)


added ::signup() + renamed ::bootstrap() in bxSession

--- lib/services/bxSession/lib/bxSession.coffee
+++ lib/services/bxSession/lib/bxSession.coffee
@@ -56,7 +56,7 @@
     scope.$emit 'bxSession:' + emit, session

   @$get = () ->
-    bootStrap: (_rootScope, _http, _q) ->
+    bootstrap: (_rootScope, _http, _q) ->
       scope = $rootScope = _rootScope
       $http = _http
       $q = _q
@@ -189,7 +189,7 @@
       deferred.promise

   @$get = () ->
-    bootStrap: (_state, _q, _bxSession) ->
+    bootstrap: (_state, _q, _bxSession) ->
       bxSession = _bxSession
       $state = _state
       $q = _q
@@ -203,6 +203,6 @@
 .run [
   '$rootScope', '$state', '$http', '$q', 'bxAuth', 'bxSession'
   ($rootScope, $state, $http, $q, bxAuth, bxSession) ->
-    bxAuth.bootStrap $state, $q, bxSession
-    bxSession.bootStrap $rootScope, $http, $q
+    bxAuth.bootstrap $state, $q, bxSession
+    bxSession.bootstrap $rootScope, $http, $q
 ]


Typo fix

Typo fix

--- src/parser/java-parser.coffee
+++ src/parser/java-parser.coffee
@@ -203,7 +203,7 @@
   argumentdef: (line, convention, commitUrl) ->
     convention = {lang: this.lang} unless convention
     (convention.argumentdef =
-      title: "Arguements definition with one space vs. no space"
+      title: "Arguments definition with one space vs. no space"
       column: [
         {
           key: "onespace", display: "One space",


remove spurious console.log

remove spurious console.log

--- scripts/jquery/css_animation.coffee
+++ scripts/jquery/css_animation.coffee
@@ -59,7 +59,6 @@
   $(el).data({cssAnimations}).css(compileAnimations cssAnimations)

 $('*').live jQuery.cssAnimationEvents.animationEnd, (event) ->
-  console.log "animation ended for #{event.target}"
   removeAnimation this, event.originalEvent.animationName

 jQuery.fn.cssFadeOut = (speed, easing, callback) ->


check in the working client side part of localization toolkit

--- library/exposure/localized.coffee
+++ library/exposure/localized.coffee
@@ -56,6 +56,37 @@
     # Once inherited from, the inheritee is not abstract anymore.
     @abstract yes

+    # This block here defines a set of Bower dependencies that are
+    # going to be necessary no matter what sort of functionality is
+    # is going to be implemented. Most of these libraries required
+    # by the internal implementations of the various subcomponents.
+    # Refer to `BowerSupport` class implementation for information.
+    @bower "underscore.string", "dist/underscore.string.min.js"
+
+    # An automatically called external routine that will take care
+    # of setting up the client site part of the translation toolkit.
+    # This implementation requests the necessary translation tables
+    # off the server provider. Once is acknowledged, a client side
+    # routines are setup and associated with the language and table.
+    setupTranslationTools: @awaiting "booted", ->
+        unexpected = "received malformed translation"
+        unrecognized = "unrecognized language received"
+        noted = "loaded %s translation messages for %s"
+        sel = "using %s as the language selector for %s"
+        _.mixin _.string.exports() # set underscore.string
+        assert _.isFunction sprintf = _.sprintf # format
+        retrieve = (s) => @translationMessages[s] or s
+        logger.info "installing the translation tookit"
+        this.t = (s, a...) => sprintf retrieve(s), a...
+        @obtainTranslation 0, (language, translation) ->
+            assert _.isString language, unrecognized
+            assert _.isObject translation, unexpected
+            assert @translationMessages = translation
+            assert @translationLanguage = language
+            length = _.keys(translation).length
+            logger.info noted, length, @service
+            logger.info sel, language, @service
+
     # An out-of-the-box isolated provider that exposes translation
     # messages, provided by the service in the language requested.
     # If no language is explicitly asked, the compound will try to
@@ -77,7 +108,7 @@
         assert identify = try @constructor.identify()
         logger.debug banner, amount, identify, selector
         assert _.isFunction(callback), "invalid callback"
-        return callback cache[selector] or new Object()
+        return callback selector, cache[selector] or {}

     # Walk over all of the declared translation message locations
     # and try loading messages off the every declared location. All