add extra spec level (#1)

add extra spec level (#1)

--- spec/jasmine-spec-reporter.spec.coffee
+++ spec/jasmine-spec-reporter.spec.coffee
@@ -3,167 +3,169 @@
 describe 'spec reporter', ->
   addMatchers()

-  describe 'when spec', ->
+  describe 'with default options', ->

-    it 'should report success', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @it 'successfull spec', ->
-            @passed()
-      ).outputs)
-      .contains /✓ successfull spec/
+    describe 'when spec', ->
+
+      it 'should report success', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @it 'successfull spec', ->
+              @passed()
+        ).outputs)
+        .contains /✓ successfull spec/

-    it 'should report failure', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @it 'failed spec', ->
-            @failed()
-      ).outputs)
-      .contains /✗ failed spec/
+      it 'should report failure', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @it 'failed spec', ->
+              @failed()
+        ).outputs)
+        .contains /✗ failed spec/

-    it 'should not report skipped', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @xit 'skipped spec', ->
-      ).outputs)
-      .not.contains /skipped spec/
+      it 'should not report skipped', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @xit 'skipped spec', ->
+        ).outputs)
+        .not.contains /skipped spec/

-  describe 'when failed spec', ->
+    describe 'when failed spec', ->

-    it 'should display with error messages', ->
-      outputs = new Test(->
-        @describe 'suite', ->
-          @it 'failed spec', ->
-            @failed('first failed assertion')
-            @passed('passed assertion')
-            @failed('second failed assertion')
-      ).outputs
+      it 'should display with error messages', ->
+        outputs = new Test(->
+          @describe 'suite', ->
+            @it 'failed spec', ->
+              @failed('first failed assertion')
+              @passed('passed assertion')
+              @failed('second failed assertion')
+        ).outputs

-      expect(outputs).not.contains /passed assertion/
-      expect(outputs).contains [
-        '    ✗ failed spec'
-        '      Message:'
-        '        first failed assertion'
-        ''
-        '      Message:'
-        '        second failed assertion'
-        ''
-      ]
+        expect(outputs).not.contains /passed assertion/
+        expect(outputs).contains [
+          '    ✗ failed spec'
+          '      Message:'
+          '        first failed assertion'
+          ''
+          '      Message:'
+          '        second failed assertion'
+          ''
+        ]

-  describe 'when suite', ->
+    describe 'when suite', ->

-    it 'should display multiple specs', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @it 'spec 1', ->
-            @passed()
-          @it 'spec 2', ->
-            @passed()
-      ).outputs).contains [
-        ''
-        '  suite'
-        '    ✓ spec 1'
-        '    ✓ spec 2'
-        ''
-      ]
-
-
-    it 'should display multiple suites', ->
-      expect(new Test(->
-        @describe 'suite 1', ->
-          @it 'spec 1', ->
-            @passed()
-        @describe 'suite 2', ->
-          @it 'spec 2', ->
-            @passed()
-      ).outputs).contains [
-        ''
-        '  suite 1'
-        '    ✓ spec 1'
-        ''
-        '  suite 2'
-        '    ✓ spec 2'
-        ''
-      ]
+      it 'should display multiple specs', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @it 'spec 1', ->
+              @passed()
+            @it 'spec 2', ->
+              @passed()
+        ).outputs).contains [
+          ''
+          '  suite'
+          '    ✓ spec 1'
+          '    ✓ spec 2'
+          ''
+        ]

-    it 'should display nested suite at first position', ->
-      expect(new Test(->
-        @describe 'suite 1', ->
-          @describe 'suite 2', ->
+      it 'should display multiple suites', ->
+        expect(new Test(->
+          @describe 'suite 1', ->
             @it 'spec 1', ->
               @passed()
-          @it 'spec 2', ->
-            @passed()
-      ).outputs).contains [
-        ''
-        '  suite 1'
-        ''
-        '    suite 2'
-        '      ✓ spec 1'
-        ''
-        '    ✓ spec 2'
-        ''
-      ]
-
-
-    it 'should display nested suite at last position', ->
-      expect(new Test(->
-        @describe 'suite 1', ->
-          @it 'spec 1', ->
-            @passed()
           @describe 'suite 2', ->
             @it 'spec 2', ->
               @passed()
-      ).outputs).contains [
-        ''
-        '  suite 1'
-        '    ✓ spec 1'
-        ''
-        '    suite 2'
-        '      ✓ spec 2'
-        ''
-        /Executed/
-      ]
+        ).outputs).contains [
+          ''
+          '  suite 1'
+          '    ✓ spec 1'
+          ''
+          '  suite 2'
+          '    ✓ spec 2'
+          ''
+        ]

-  describe 'when summary', ->
-
-    it 'should report success', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @it 'spec', ->
-            @passed()
-      ).outputs)
-      .contains  'Executed 1 of 1 spec SUCCESS in {time}.'
+      it 'should display nested suite at first position', ->
+        expect(new Test(->
+          @describe 'suite 1', ->
+            @describe 'suite 2', ->
+              @it 'spec 1', ->
+                @passed()
+            @it 'spec 2', ->
+              @passed()
+        ).outputs).contains [
+          ''
+          '  suite 1'
+          ''
+          '    suite 2'
+          '      ✓ spec 1'
+          ''
+          '    ✓ spec 2'
+          ''
+        ]

-    it 'should report failure', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @it 'spec', ->
-            @failed()
-      ).outputs)
-      .contains  'Executed 1 of 1 spec (1 FAILED) in {time}.'
+      it 'should display nested suite at last position', ->
+        expect(new Test(->
+          @describe 'suite 1', ->
+            @it 'spec 1', ->
+              @passed()
+            @describe 'suite 2', ->
+              @it 'spec 2', ->
+                @passed()
+        ).outputs).contains [
+          ''
+          '  suite 1'
+          '    ✓ spec 1'
+          ''
+          '    suite 2'
+          '      ✓ spec 2'
+          ''
+          /Executed/
+        ]

-    it 'should report skipped whith success', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @xit 'spec', ->
-      ).outputs)
-      .contains  'Executed 0 of 1 spec SUCCESS (skipped 1) in {time}.'
+    describe 'when summary', ->
+
+      it 'should report success', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @it 'spec', ->
+              @passed()
+        ).outputs)
+        .contains  'Executed 1 of 1 spec SUCCESS in {time}.'

-    it 'should report skipped whith failure', ->
-      expect(new Test(->
-        @describe 'suite', ->
-          @xit 'spec', ->
-          @it 'spec', ->
-            @failed()
-      ).outputs)
-      .toContain  'Executed 1 of 2 specs (1 FAILED) (skipped 1) in {time}.'
+      it 'should report failure', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @it 'spec', ->
+              @failed()
+        ).outputs)
+        .contains  'Executed 1 of 1 spec (1 FAILED) in {time}.'
+
+
+      it 'should report skipped whith success', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @xit 'spec', ->
+        ).outputs)
+        .contains  'Executed 0 of 1 spec SUCCESS (skipped 1) in {time}.'
+
+
+      it 'should report skipped whith failure', ->
+        expect(new Test(->
+          @describe 'suite', ->
+            @xit 'spec', ->
+            @it 'spec', ->
+              @failed()
+        ).outputs)
+        .toContain  'Executed 1 of 2 specs (1 FAILED) (skipped 1) in {time}.'


window.GSS.console debugs for VFL

window.GSS.console debugs for VFL

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -56,6 +56,7 @@
       window.GSS.console.row('@' + name, o.statements.concat([ruleSet]), terms)

   return {commands:newCommands}
+

 vglHook = (name,terms,commands=[]) ->
   newCommands = []


Social: use wrapper functions

Social: use wrapper functions

--- workers/social/lib/social/models/socialapi/channel.coffee
+++ workers/social/lib/social/models/socialapi/channel.coffee
@@ -63,7 +63,7 @@
   Validators = require '../group/validators'
   {permit}   = require '../group/permissionset'

-  {fetchGroup, socialRequestWrapper, doRequest} = require "./helper"
+  {fetchGroup, secureRequest, doRequest, permittedRequest} = require "./helper"

   @generateChannelName = ({groupSlug, apiChannelType, apiChannelName})->
     return "socialapi-\
@@ -106,56 +106,54 @@
     @doRequest 'fetchChannelActivities', client, options, callback

   # searchTopics - search topics for autocompletion
-  @searchTopics          = socialRequestWrapper fnName: 'searchTopics'
+  @searchTopics          = secureRequest fnName: 'searchTopics'

   # fetchProfileFeed - lists all activities of an account
   # within a specified group
-  @fetchProfileFeed      = socialRequestWrapper fnName: 'fetchProfileFeed'
+  @fetchProfileFeed      = secureRequest fnName: 'fetchProfileFeed'

   # fetchPopularTopics - lists group specific popular topics
   # it can be daily, weekly, monthly
-  @fetchPopularTopics    = socialRequestWrapper fnName: 'fetchPopularTopics'
+  @fetchPopularTopics    = secureRequest fnName: 'fetchPopularTopics'

   # fetchPopularPosts -  lists group specific popular posts
   # it can be daily, weekly, monthly
-  @fetchPopularPosts     = socialRequestWrapper fnName: 'fetchPopularPosts'
+  @fetchPopularPosts     = secureRequest fnName: 'fetchPopularPosts'

   # fetchChannels - lists group's topic channels
-  @fetchChannels         = socialRequestWrapper fnName: 'fetchGroupChannels'
+  @fetchChannels         = secureRequest fnName: 'fetchGroupChannels'

   # fetchFollowedChannels - lists followed channels(topics) of an account
-  @fetchFollowedChannels = socialRequestWrapper fnName: 'fetchFollowedChannels'
+  @fetchFollowedChannels = secureRequest fnName: 'fetchFollowedChannels'

   # follow - endpoint for users to follow topics
-  @follow = socialRequestWrapper
+  @follow = secureRequest
     fnName  : 'followTopic'
     validate: ["channelId"]

   # unfollow - endpoint for users to give up following a channel
-  @unfollow = socialRequestWrapper
+  @unfollow = secureRequest
     fnName  : 'unfollowTopic'
     validate: ["channelId"]

   # updateLastSeenTime - updates user's channel presence data
-  @updateLastSeenTime = socialRequestWrapper
+  @updateLastSeenTime = secureRequest
     fnName  : 'updateLastSeenTime'
     validate: ["channelId"]

-  @fetchPinnedMessages = permit 'pin posts',
-    success: (client, options, callback)->
-      @doRequest 'fetchPinnedMessages', client, options, callback
+  @fetchPinnedMessages = permittedRequest
+    permissionName: 'pin posts'
+    fnName        : 'fetchPinnedMessages'

-  @pinMessage = permit 'pin posts',
-    success:  (client, options, callback)->
-      unless options.messageId
-        return callback {message: "Message id is not set for pinning "}
-      @doRequest 'pinMessage', client, options, callback
+  @pinMessage = permittedRequest
+    permissionName: 'pin posts'
+    fnName        : 'pinMessage'
+    validate      : ['messageId']

-  @unpinMessage = permit 'like posts',
-    success:  (client, options, callback)->
-      unless options.messageId
-        return callback {message: "Message id is not set for un-pinning "}
-      @doRequest 'unpinMessage', client, options, callback
+  @unpinMessage = permittedRequest
+    permissionName: 'pin posts'
+    fnName        : 'unpinMessage'
+    validate      : ['messageId']

   @followUser = secure (client, options, callback)->
     {connection:{delegate}} = client


tweak specs

tweak specs

--- specs/access/table-gateway.spec.coffee
+++ specs/access/table-gateway.spec.coffee
@@ -55,9 +55,16 @@

     it 'tryOneObject fails', (done) ->
         g = fighterGateway()
-        g.findOne(101).tryOneObject((err, fighter) ->
+        g.findOne(101).tryOneObject((err, r) ->
             return done(err) if err?
-            (fighter.id == undefined).should.be.true
+            done("Resultset should be null") if r?
+            done()
+        )
+
+    it 'tryOneObject finds more than 1 row', (done) ->
+        g = fighterGateway()
+        g.where({ lastName: 'Silva' }).tryOneObject((err, r) ->
+            err.should.match(/Too many rows returned/)
             done()
         )


add spec for tryOneObject

add spec for tryOneObject

--- specs/access/table-gateway.spec.coffee
+++ specs/access/table-gateway.spec.coffee
@@ -40,10 +40,8 @@
 describe 'TableGateway', () ->
     it 'can be instantiated', () -> fighterGateway()

-
     it 'is accessible via database property', () ->
         db.fighters.should.be.instanceof(TableGateway)
-

     it 'can count rows', (done) ->
         db.fighters.count (err, cnt) ->
@@ -51,6 +49,17 @@
             cnt.should.eql(cntFighters)
             done()

+    it 'tryOneObject success', (done) ->
+        g = fighterGateway()
+        g.findOne(1).tryOneObject(assertFighterOne(done))
+
+    it 'tryOneObject fails', (done) ->
+        g = fighterGateway()
+        g.findOne(101).tryOneObject((err, fighter) ->
+            return done(err) if err?
+            (fighter.id == undefined).should.be.true
+            done()
+        )

     it 'can postpone query execution', (done) ->
         g = fighterGateway()


make select2 optional

make select2 optional

--- vendor/assets/javascripts/dependent-fields.js.coffee
+++ vendor/assets/javascripts/dependent-fields.js.coffee
@@ -8,14 +8,14 @@
     if method == 'disable'
       # use attr instead of prop, because prop does not work with twitter bootstrap button
       $parent.find('input,textarea,select,button,.btn').removeAttr('disabled')
-      $parent.find('.select2').select2('enable')
+      $parent.find('.select2').select2('enable') if $.fn.select2
     else
       $parent.show(duration)
   else
     if method == 'disable'
       # use attr instead of prop, because prop does not work with twitter bootstrap button
       $parent.find('input,textarea,select,button,.btn').attr('disabled', 'disabled')
-      $parent.find('.select2').select2('disable')
+      $parent.find('.select2').select2('disable') if $.fn.select2
     else
       $parent.hide(duration)


Added JS code for AJAX updation of workgroup. This took time. Hooraah!

--- plugins/020-shares/app/assets/javascripts/shares.js.coffee
+++ plugins/020-shares/app/assets/javascripts/shares.js.coffee
@@ -112,6 +112,45 @@
 					form.remove()
 					link.show()

+		SmartLinks.initialize
+			open_selector: ".open-update-workgroup-area"
+			close_selector: ".close-update-workgroup-area"
+			onShow: (open_link) ->
+				console.log(open_link)
+				placeholder = open_link.text()
+				workgroup_div = _this.share(open_link) #extract original workgroup container
+				#parse dumped "id" from div, so that it can be passed to form for update
+				workgroup_id = _this.parse_id(workgroup_div.attr('id'))
+
+				open_link.after Templates.run("updateWorkgroup",
+					workgroup_id: workgroup_id,
+				)
+
+				#extract the <form> from above:
+				form = open_link.next()
+				FormHelpers.update_first form, open_link.text()
+				FormHelpers.focus_first form
+
+		$(document).on "ajax:success", ".update-workgroup-form", (data, results) ->
+			if results["status"] is "ok"
+				#on completion, acquire form,
+				form = $(this)
+				#span.open-update-workgroup-area element which holds the value,
+				#to be displayed on page after AJAX request
+				link = form.prev()
+				#find the updated value from form:
+				value = FormHelpers.find_first(form).val()
+				#update that link value with the new value entered in form.
+				link.text value
+
+		$(document).on "ajax:complete", ".update-workgroup-form", ->
+			form = $(this)
+			link = form.prev()
+			form.hide "slow", ->
+				form.remove()
+				link.show()
+
+

 		RemoteCheckbox.initialize
 			selector: ".disk_pooling_checkbox"


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -14,7 +14,7 @@

     unless I.cooldowns[name]
       I.cooldowns[name] = {
-
+        options.target
       }


Remove the automatically created empty list-item if left empty (+option to disable this)

--- lib/main.coffee
+++ lib/main.coffee
@@ -22,6 +22,12 @@
     indentListItems:
       title: 'Indent list-items'
       description: 'Automatically in- and outdent list-items by pressing `TAB` and `SHIFT+TAB`'
+      type: 'boolean'
+      default: true
+
+    removeEmptyListItems:
+      title: 'Remove empty list-items'
+      description: 'Remove the automatically created empty list-items when left empty, leaving an empty line'
       type: 'boolean'
       default: true

@@ -109,9 +115,13 @@
                           # Skip definition-lists
                           isListItem = false
                         break
+
                       else
                         isListItem = false
                         isPunctuation = false
+                        if atom.config.get('language-markdown.removeEmptyListItems')
+                          editor.setTextInBufferRange(previousRowRange, "")
+
                     else
                       isPunctuation = false


Remove useless lines

Remove useless lines

--- lib/main.coffee
+++ lib/main.coffee
@@ -22,11 +22,6 @@
   serialize: ->
     # console.log 'serialize'

-
-  # callViews: (e, methodName, args...) ->
-  #   for view, i in @refactoringViews
-  #     view[methodName].apply view, args
-
   callActiveViews: (e, methodName, args...) ->
     activePaneItem = atom.workspaceView.getActivePaneItem()
     isCalled = false
@@ -36,7 +31,6 @@

     unless isCalled
       e.abortKeyBinding()
-

   onEditorViewCreated: (editorView) =>
     refactoringView = new RefactoringView editorView


Make sure user exists, which can be undefined in tests

--- lib/main.coffee
+++ lib/main.coffee
@@ -53,7 +53,7 @@
     unless localStorage.getItem('hasSeenDeprecatedNotification')
       packageManager ?= new PackageManager()
       packageManager.getInstalled().then (packages) =>
-        @showDeprecatedNotification(packages) if packages.user.length
+        @showDeprecatedNotification(packages) if packages.user?.length

   deactivate: ->
     settingsView?.dispose()


Add disposables

Add disposables

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,5 +1,6 @@
 url = require 'url'
 fs = require 'fs-plus'
+{CompositeDisposable} = require 'atom'

 MarkdownPreviewView = null
 renderer = null
@@ -10,13 +11,14 @@

 module.exports =
   activate: ->
-    atom.commands.add 'atom-workspace',
+    @disposables = new CompositeDisposable()
+    @disposables.add atom.commands.add 'atom-workspace',
       'markdown-preview:toggle': =>
         @toggle()
       'markdown-preview:copy-html': =>
-        @copyHtml()
+        @copyHTML()
       'markdown-preview:save-as-html': =>
-        @saveAsHtml()
+        @saveAsHTML()
       'markdown-preview:toggle-break-on-single-newline': ->
         keyPath = 'markdown-preview.breakOnSingleNewline'
         atom.config.set(keyPath, not atom.config.get(keyPath))
@@ -25,15 +27,13 @@
         atom.config.set(keyPath, not atom.config.get(keyPath))

     previewFile = @previewFile.bind(this)
-    atom.commands.add '.tree-view .file .name[data-name$=\\.markdown]', 'markdown-preview:preview-file', previewFile
-    atom.commands.add '.tree-view .file .name[data-name$=\\.md]', 'markdown-preview:preview-file', previewFile
-    atom.commands.add '.tree-view .file .name[data-name$=\\.mdown]', 'markdown-preview:preview-file', previewFile
-    atom.commands.add '.tree-view .file .name[data-name$=\\.mkd]', 'markdown-preview:preview-file', previewFile
-    atom.commands.add '.tree-view .file .name[data-name$=\\.mkdown]', 'markdown-preview:preview-file', previewFile
-    atom.commands.add '.tree-view .file .name[data-name$=\\.ron]', 'markdown-preview:preview-file', previewFile
-    atom.commands.add '.tree-view .file .name[data-name$=\\.txt]', 'markdown-preview:preview-file', previewFile
+    for extension in ['markdown', 'md', 'mdown', 'mkd', 'mkdown', 'ron', 'txt']
+      # FIXME: This doesn't work. Individual elements in the Tree View do not get focus
+      # so this selector never matches.
+      @disposables.add atom.commands.add ".tree-view .file .name[data-name$=\\.#{extension}]",
+        'markdown-preview:preview-file', previewFile

-    atom.workspace.addOpener (uriToOpen) =>
+    @disposables.add atom.workspace.addOpener (uriToOpen) =>
       [protocol, path] = uriToOpen.split('://')
       return unless protocol is 'markdown-preview'

@@ -46,6 +46,9 @@
         @createMarkdownPreviewView(editorId: path.substring(7))
       else
         @createMarkdownPreviewView(filePath: path)
+
+  deactivate: ->
+    @disposables.dispose()

   createMarkdownPreviewView: (state) ->
     if state.editorId or fs.isFileSync(state.filePath)
@@ -98,7 +101,7 @@

     atom.workspace.open "markdown-preview://#{encodeURI(filePath)}", searchAllPanes: true

-  copyHtml: ->
+  copyHTML: ->
     editor = atom.workspace.getActiveTextEditor()
     return unless editor?

@@ -110,7 +113,7 @@
       else
         atom.clipboard.write(html)

-  saveAsHtml: ->
+  saveAsHTML: ->
     activePane = atom.workspace.getActivePaneItem()
     if isMarkdownPreviewView(activePane)
       activePane.saveAs()


:fire: Remove an outdated config

:fire: Remove an outdated config

--- lib/main.coffee
+++ lib/main.coffee
@@ -32,12 +32,6 @@
       title: 'Underline Issues'
       type: 'boolean'
       default: true
-    statusIconPosition:
-      title: 'Position of Status Icon on Bottom Bar'
-      description: 'Requires a reload/restart to update'
-      enum: ['Left', 'Right']
-      type: 'string'
-      default: 'Left'
     ignoredMessageTypes:
       title: "Ignored message Types"
       type: 'array'


Update RangesTracker

Update RangesTracker

--- services/web/public/coffee/ide/review-panel/RangesTracker.coffee
+++ services/web/public/coffee/ide/review-panel/RangesTracker.coffee
@@ -410,14 +410,10 @@
 				@emit "changes:moved", moved_changes

 		_addOp: (op, metadata) ->
-			# Don't take a reference to the existing op since we'll modify this in place with future changes
-			clone_op = {}
-			for k,v of op
-				clone_op[k] = v
 			change = {
 				id: @newId()
-				op: clone_op
-				metadata: metadata
+				op: @_clone(op) # Don't take a reference to the existing op since we'll modify this in place with future changes
+				metadata: @_clone(metadata)
 			}
 			@changes.push change

@@ -489,6 +485,11 @@
 				else # Only update to the current change if we haven't removed it.
 					previous_change = change
 			return { moved_changes, remove_changes }
+
+		_clone: (object) ->
+			clone = {}
+			(clone[k] = v for k,v of object)
+			return clone

 if define?
 	define ["utils/EventEmitter"], load


misc settings cleanup

misc settings cleanup

--- public/dreamcatcher/controllers/settings_controller.coffee
+++ public/dreamcatcher/controllers/settings_controller.coffee
@@ -30,6 +30,7 @@
       when 150 then newClass = 'follower'
       when 50 then newClass = 'anonymous'
       when 0 then newClass = 'private'
+
     log('newClass '+ newClass)
     # $('.sharing-icon').css "background","url(/images/icons/#{background}) no-repeat center transparent"


Fixed a misnamed test set.

Fixed a misnamed test set.

--- src/coffee/tokenize/keywords.spec.coffee
+++ src/coffee/tokenize/keywords.spec.coffee
@@ -1,4 +1,4 @@
-describe "tokenize", -> describe "symbols", ->
+describe "tokenize", -> describe "keywords", ->
     tokenizeKeywords = require "./keywords"

     describe "excludes", ->


Updated version

Updated version

--- src/core/exo.coffee
+++ src/core/exo.coffee
@@ -1,3 +1,3 @@
 Exo = @Exo = {}
-Exo.VERSION = '0.1.2'
+Exo.VERSION = '0.2.0'
 module?.exports = Exo


Only render the guide body if there are sections

Otherwise only the header and the empty message should appear

--- src/components/learning-guide/guide.cjsx
+++ src/components/learning-guide/guide.cjsx
@@ -26,35 +26,18 @@
     weakerExplanation: React.PropTypes.element
     sampleSizeThreshold: React.PropTypes.number.isRequired

+  renderBody: ->
+

+      <WeakerPanel sections={@props.allSections} {...@props} />
+      <BS.Row>
+
Individual Chapters

+      </BS.Row>
+      {for chapter, i in (@props.chapters or [])
+        <Chapter key={i} chapter={chapter} {...@props} />}
+

+
   render: ->
-    {courseId} = @props
-
-    noData = @props.allSections.length is 0
-

-
       {@props.heading}
-
-      {@props.emptyMessage if noData}
-
-

-
-
-        <WeakerPanel sections={@props.allSections} {...@props} />
-
-        <BS.Row>
-
Individual Chapters

-        </BS.Row>
-
-        {for chapter, i in (@props.chapters or [])
-          <Chapter key={i} chapter={chapter} {...@props} />}
-
-

-
-
-

-
-
-

-
+      {if _.isEmpty(@props.allSections) then @props.emptyMessage else @renderBody()}


Fix spelling

Fix spelling

--- packages/rocketchat-oembed/server/models/OEmbedCache.coffee
+++ packages/rocketchat-oembed/server/models/OEmbedCache.coffee
@@ -12,7 +12,7 @@

 	# INSERT
-	createWithIdAndData: (_id: data) ->
+	createWithIdAndData: (_id, data) ->
 		record =
 			_id: _id
 			data: data


only save task step if it's not completed

--- tutor/src/components/task-step/index.cjsx
+++ tutor/src/components/task-step/index.cjsx
@@ -39,7 +39,8 @@

   onStepCompleted: (id) ->
     {id} = @props unless id? # need to allow `id` from arguments pass through, especially for multi-part
-    if StepPanel.canWrite(id)
+    step = TaskStepStore.get(id)
+    if !step.is_completed && StepPanel.canWrite(id)
       TaskActions.completeStep(id, @props.taskId)
       return new Promise((resolve) -> TaskStore.once('step.completed', resolve))
     else


stop listening after trapped

stop listening after trapped

--- resources/assets/coffee/_classes/store-checkout.coffee
+++ resources/assets/coffee/_classes/store-checkout.coffee
@@ -22,10 +22,15 @@
     return unless document.querySelector('#js-xsolla-pay')
     button = document.querySelector('#js-xsolla-pay')

+    trap = DeferrablePromise()
+    $(button).on 'click.trap', ->
+      $(button).off 'click.trap'
+      button.classList.add('store-payment-button--waiting')
+      trap.resolve()
+
     # load scripts
     init = Promise.all([@loadXsollaToken(), @loadXsollaScript()])
     .then (values) =>
-      console.debug('resolved all')
       token = values[0]
       options = @optionsWithToken(token)
       XPayStationWidget.init(options)
@@ -33,15 +38,8 @@
     .catch (error) ->
       console.error error

-    click = DeferrablePromise()
-
-    $(button).on 'click', ->
-      button.classList.add('store-payment-button--waiting')
-      click.resolve()
-
-    $(button).on 'click', ->
-      Promise.all([init, click]).then (values) ->
-        console.log(values)
+    $(button).on 'click.xsolla', ->
+      Promise.all([init, trap]).then (values) ->
         XPayStationWidget.open()

   @loadXsollaScript: ->
@@ -51,7 +49,6 @@
       script.async = true
       script.src = "https://static.xsolla.com/embed/paystation/1.0.7/widget.min.js"
       script.addEventListener 'load', ->
-        console.debug('script loaded')
         # TODO: remove after testing
         Timeout.set 5000, ->
           resolve()
@@ -63,7 +60,6 @@
     new Promise (resolve, reject) ->
       $.get laroute.route('payments.xsolla.token')
       .done (data) ->
-        console.debug('token loaded')
         resolve(data)
       .fail (xhr, error) ->
         console.error xhr


增加支持#零作为创建标签 #50

增加支持#零作为创建标签 #50

--- biz/commit.coffee
+++ biz/commit.coffee
@@ -26,8 +26,9 @@
     issue_id = if message.match /#(\d+)/i then RegExp.$1 else 0
     #提取done标签是否存在
     isDone = /#(done|ok)/i.test(message)
+    #console.log /#(done|ok)/i.test(message), message
     #提取创建标签
-    isCreate = /#create/i.test(message)
+    isCreate = parseInt(issue_id) <= 0 or /#create/i.test(message)
     #提取doing标签
     isDoing = /#doing/i.test(message)
     _log "issue id -> #{issue_id}, done: #{isDone}, new: #{isCreate}, doing: #{isDoing}"
@@ -52,7 +53,6 @@
           project_id: project_id

-        console.log data
         issue = new _Issue member
         issue.save data, (err, id)->
           _log "创建新的issue，id -> #{id}"
@@ -73,15 +73,15 @@
     #处理done
     queue.push(
       (done)->
+        #console.log issue_id, isDone, member_id, '测试'
         return done null if not (issue_id and isDone and member_id)
+        #console.log 'done', issue_id
         #如果有done标签，则完成这个issue
         issue = new _Issue member
         issue.finishedIssue issue_id, done
     )

-    _async.waterfall queue, (err)->
-      console.log '新的哈哈哈id', issue_id
-      cb err, issue_id
+    _async.waterfall queue, (err)-> cb err, issue_id

   #根据git用户名查找对应的用户id
@@ -138,8 +138,8 @@
         commit = commits[index++]
         cond = sha: commit.id

-        self.find cond, (err, result)->
-          if result.items.length > 0
+        self.count cond, (err, count)->
+          if count > 0
             _log "Commit has already exists -> #{commit.id}"
             done null
           else


Preroller: Include request count in debugging messages

--- src/streammachine/slave/preroller.coffee
+++ src/streammachine/slave/preroller.coffee
@@ -37,11 +37,15 @@

         count = @_counter++

+        pdebug = (msg,args...) ->
+            debug "#{count}: #{msg}", args...
+
         # If the preroll request can't be made in 5 seconds or less,
         # abort the preroll.
         # TODO: Make the timeout wait configurable
         prerollTimeout = setTimeout(=>
             @stream.log.debug "preroll request timeout. Aborting.", count
+            pdebug "Hit timeout. Triggering abort."
             req.abort()
             aborted = true
             detach new Error("Preroll request timed out.")
@@ -56,7 +60,7 @@
             .replace("!UA!", encodeURIComponent(client.ua))
             .replace("!UUID!", client.session_id)

-        debug "Ad request URI is #{uri}"
+        pdebug "Ad request URI is #{uri}"

         # -- make a request to the ad server -- #

@@ -65,28 +69,30 @@
             if err
                 perr = new Error "Ad request returned error: #{err}"
                 @stream.log.error perr, error:err
-                debug perr
+                pdebug perr
                 return detach perr

             if res.statusCode == 200
                 new Preroller.AdObject body, (err,obj) =>
                     if err
-                        @stream.log.debug "Ad request was unsuccessful: #{err}"
+                        perr = "Ad request was unsuccessful: #{err}"
+                        @stream.log.debug perr
+                        pdebug perr
                         return detach err

                     if obj.creativeURL
                         # we need to take the creative URL and pass it off
                         # to the transcoder
-                        debug "Preparing transcoder request for #{obj.creativeURL} with key #{@streamKey}."
+                        pdebug "Preparing transcoder request for #{obj.creativeURL} with key #{@streamKey}."

                         treq = request.get(@transcode_uri, qs:{ uri:obj.creativeURL, key:@streamKey })
                             .once "response", (resp) =>
-                                debug "Transcoder response received: #{resp.statusCode}"
+                                pdebug "Transcoder response received: #{resp.statusCode}"

                                 if resp.statusCode == 200
                                     treq.pipe(writer,end:false)
                                     treq.once "end", =>
-                                        debug "Transcoder pipe complete."
+                                        pdebug "Transcoder pipe complete."

                                         # we return by giving a function that should be called when the
                                         # impression criteria have been met
@@ -96,16 +102,16 @@
                                                     if err
                                                         @stream.log.error "Failed to hit impression URL #{obj.impressionURL}: #{err}"
                                                     else
-                                                        debug "Impression URL hit successfully."
+                                                        pdebug "Impression URL hit successfully."
                                             else
                                                 @stream.log.debug "Session reached preroll impression criteria, but no impression URL present."
-                                                debug "No impression URL found."
+                                                pdebug "No impression URL found."
                                 else
                                     err = new Error "Non-200 response from transcoder."
-                                    debug err
+                                    pdebug err
                                     detach err
                             .once "error", (err) =>
-                                debug "Transcoder request error: #{err}"
+                                pdebug "Transcoder request error: #{err}"
                                 detach err

                     else
@@ -115,9 +121,11 @@
             else
                 perr = new Error "Ad request returned non-200 response: #{body}"
                 @stream.log.debug perr
+                pdebug perr
                 return detach perr

-        detach = (err,impcb) =>
+        detach = _.once (err,impcb) =>
+            pdebug "In detach"
             clearTimeout(prerollTimeout) if prerollTimeout
             socket.removeListener "close", conn_pre_abort
             socket.removeListener "end", conn_pre_abort
@@ -129,6 +137,7 @@
         conn_pre_abort = =>
             detach()
             if socket.destroyed
+                pdebug "Aborting"
                 @stream.log.debug "aborting preroll ", count
                 adreq?.abort()
                 treq?.abort()


conversation msg modals focus close button on open

For a11y, the compose and reply modals for conversations correctly focus
the close button when the dialog opens instead of the first control.

There are no unit test changes because I'm removing code for the old
explicit behavior, which wasn't tested. Now it has the default behavior
for modals, which is functionality that should be tested with the modal
modal implementation we use, and not with this particular use of it.

fixes CNVS-36820

test plan:
* open the conversations compose modal
* see the focus is set to the close button of the modal
* ditto with the reply modal

Change-Id: I25d3ae85a597e93ca2a2b00e240dee250adfbe40
Reviewed-on: https://gerrit.instructure.com/111573
Tested-by: Jenkins
Reviewed-by: Felix Milea-Ciobanu <9384aa226ac9d3237ed0bc52e18ffcb251ee0bf7@instructure.com>
QA-Review: David Tan <6283e310a2278fc51bdc4fb5974f70dd9c3366b9@instructure.com>
Product-Review: Jon Willesen <0321efbc0acf6204eb6035acb1d418838b450d08@instructure.com>

--- app/coffeescripts/views/conversations/MessageFormDialog.coffee
+++ app/coffeescripts/views/conversations/MessageFormDialog.coffee
@@ -205,10 +205,8 @@
         @courseView.setValue(@defaultCourse)
       if @model
         @courseView.$picker.css('display', 'none')
-        @recipientView.$input.focus()
       else
         @$messageCourseRO.css('display', 'none')
-        @courseView.focus()

       if @tokenInput = @$el.find('.recipients').data('token_input')
         # since it doesn't infer percentage widths, just whatever the current pixels are


show name in display panel

show name in display panel

--- app/assets/javascripts/osc_jobs.js.coffee
+++ app/assets/javascripts/osc_jobs.js.coffee
@@ -6,10 +6,10 @@
   request_job_data(id)
   update_copy_button(id)
   update_destroy_button(id)
-  show_job_panel(id)

 @request_job_data = (id) ->
   if id?
+    show_job_panel()
     $.ajax
       type: 'GET'
       url: '.' + Routes.osc_job_path(id)
@@ -19,16 +19,28 @@
         console.log jqXHR
       success: (data, textStatus, jqXHR) ->
         # TODO add display method
+        update_job_details_panel(data)
         update_template_button(data.script_path, data.batch_host)
+        # TODO remove the console.log, this is for devving
         console.log data
   else
+    update_job_details_panel()
     update_template_button()

-@show_job_panel = (id) ->
-  if id?
+@show_job_panel = (show) ->
+  if show?
     $("#jobDetailsPanel").fadeIn(200)
   else
     $("#jobDetailsPanel").fadeOut(200)
+
+@update_job_details_panel = (data) ->
+  if data?
+    $("#jobDetailsName").text(data.name)
+    show_job_panel(true)
+  else
+    show_job_panel()
+
+

 @update_copy_button = (id) ->
   if id?


add function for getting absolute path

add function for getting absolute path

--- app/assets/javascripts/osc_jobs.js.coffee
+++ app/assets/javascripts/osc_jobs.js.coffee
@@ -19,6 +19,7 @@
         console.log jqXHR
       success: (data, textStatus, jqXHR) ->
         update_job_details_panel(data)
+        update_stop_button(data)
         update_template_button(data.script_path, data.batch_host)
         # TODO remove the console.log, this is for devving
         console.log data
@@ -37,9 +38,11 @@
     $("#jobDetailsName").text(data.name)
     # TODO On selected change, do an ajax call to change it on the object
     $("#jobDetailsServerSelect option[value=#{data.batch_host}]").prop("selected", "selected")
+    $("#jobDetailsStagedDir").text(abs_path(data.script_path))
     show_job_panel(true)
   else
     show_job_panel()
+

 @update_copy_button = (id) ->
   if id?
@@ -84,4 +87,8 @@
     $("#destroy_button").attr("disabled", true)
     $("#destroy_button").bind('click', false)

-
+# Return the directory path of a file path
+abs_path = (filepath) ->
+  f = filepath.split('/')
+  f.pop()
+  f.join('/')


Issues tracker settings enableds/disableds without page reloading now

--- app/assets/javascripts/projects.js.coffee
+++ app/assets/javascripts/projects.js.coffee
@@ -18,3 +18,18 @@
   # Ref switcher
   $('.project-refs-select').on 'change', ->
     $(@).parents('form').submit()
+
+  $('#project_issues_enabled').change ->
+    if ($(this).is(':checked') == true)
+      $('#project_issues_tracker').removeAttr('disabled')
+    else
+      $('#project_issues_tracker').attr('disabled', 'disabled')
+
+    $('#project_issues_tracker').change()
+
+  $('#project_issues_tracker').change ->
+    if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
+      $('#project_issues_tracker_id').attr('disabled', 'disabled')
+    else
+      $('#project_issues_tracker_id').removeAttr('disabled')
+


clean up binary utils

clean up binary utils

--- js/src/util/binary-utils.coffee
+++ js/src/util/binary-utils.coffee
@@ -127,7 +127,6 @@
         buffer  = new Uint8Array(chunkSize)
         copyIndex = 0

-
     return arrays

   return {


Fix Users GET

Fix Users GET

--- server/routes/api/users.coffee
+++ server/routes/api/users.coffee
@@ -15,5 +15,5 @@
         res.send newUser, 200

   .get (req, res) ->
-    User.find (users) ->
+    User.find {}, (err, users) ->
       res.send users


Remove debug line.

Remove debug line.

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -288,7 +288,6 @@
 					return cb()
 				CollaboratorsHandler.userIsTokenMember user_id, project_id, cb
 		}, (err, results)->
-			logger.log locals: res.locals, "### locals"
 			if err?
 				logger.err err:err, "error getting details for project page"
 				return next err


Refresh nav menu when adding a media folder from modal context

--- MediaAdminBundle/Resources/public/coffee/MediaModalView.coffee
+++ MediaAdminBundle/Resources/public/coffee/MediaModalView.coffee
@@ -44,6 +44,7 @@
   reloadFolder: ->
     displayLoader $('.modal-body-menu', @$el)
     viewContext = @
+    displayMenu()
     $.ajax
       url: @options.url
       method: 'GET'


Fix typo in userLink url

Fix typo in userLink url

--- app/assets/javascripts/components/assignments/assignment.cjsx
+++ app/assets/javascripts/components/assignments/assignment.cjsx
@@ -2,7 +2,7 @@
 ArticleStore      = require '../../stores/article_store'

 userLink = (wiki_id) ->
-  {wiki_id}
+  {wiki_id}

 Assignment = React.createClass(
   displayName: 'Assignment'


encryption data in log

encryption data in log

--- app/controllers/account_controller.coffee
+++ app/controllers/account_controller.coffee
@@ -26,6 +26,7 @@
         else
             send 404
 , only: ['findAccount', 'updateAccount', 'mergeAccount', 'deleteAccount']
+

 # helpers
 encryptPassword = (callback)->
@@ -40,6 +41,9 @@
             callback false, new Error("master key and slave key don't exist")
     else
         callback false
+
+toString = ->
+    "[Account for model: #{@id}]"

 # POST /accounts/password/
@@ -119,7 +123,7 @@
 action 'createAccount', ->
     @body = body
     body.docType = "Account"
-    body.toString = toString "Account"
+    body.toString = @toString "Account"
     encryptPassword (pwdExist, err) ->
         if err
             console.log "[createAccount] err: #{err}"
@@ -128,8 +132,8 @@
             if pwdExist
                 db.save @body, (err, res) ->
                     if err
-                        railway.logger.write "[Create] err: #{err}"
-                        send 500
+                        railway.logger.write "[createAccount] err: #{err}"
+                        send 50
                     else
                         send _id: res._id, 201
             else


reactivity: test minor fix

reactivity: test minor fix

--- client/activity/lib/flux/chatinput/tests/value/valuegetter.coffee
+++ client/activity/lib/flux/chatinput/tests/value/valuegetter.coffee
@@ -25,7 +25,7 @@
     value1       = '12345'
     value2       = 'qwerty'
     emptyChannel = 'channel3'
-    { getters } = ChatInputFlux
+    { getters }  = ChatInputFlux

     it 'gets value depending on the current channel id', ->


[♻] Cleaned up.

[♻] Cleaned up.

--- vendor/assets/javascripts/jquery/plugins/slidify.js.coffee
+++ vendor/assets/javascripts/jquery/plugins/slidify.js.coffee
@@ -1,6 +1,4 @@
 # This is the manifest for `slidify.js`.
-
-#= require fss

 #= require ./slidify/slidy
 #= require ./slidify/plugin


Remove log statement.

Remove log statement.

--- src/dom/View.coffee
+++ src/dom/View.coffee
@@ -160,7 +160,6 @@
       @style[key] = val + "px"
     for key, val of @style
       @el.style[key] = val
-      console.log key, val
     @

   ###
@@ -332,4 +331,4 @@
     view = new View()
   view.attach el, id

-module.exports = View+module.exports = View


remove duplicate property

remove duplicate property

because of the dupe, the generated file fails to parse under strict mode. this fixes said error.

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -142,16 +142,14 @@

     # The base that is used to calculate the filesize. You can change this to
     # 1024 if you would rather display kibibytes, mebibytes, etc...
+    # 1024 is technically incorrect,
+    # because `1024 bytes` are `1 kibibyte` not `1 kilobyte`.
+    # You can change this to `1024` if you don't care about validity.
     filesizeBase: 1000

     # Can be used to limit the maximum number of files that will be handled
     # by this Dropzone
     maxFiles: null
-
-    # The base used to calculate filesizes. 1024 is technically incorrect,
-    # because `1024 bytes` are `1 kibibyte` not `1 kilobyte`.
-    # You can change this to `1024` if you don't care about validity.
-    filesizeBase: 1000

     # Can be an object of additional parameters to transfer to the server.
     # This is the same as adding hidden input fields in the form element.


Removed all the acceptedFiles, filesQueue and filesProcessing arrays and replaced them with getters.

Corrected the totaluploadprogress bug as well when removing files.

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -99,8 +99,8 @@
     # If false, files will not be added to the process queue automatically.
     # This can be useful if you need some additional user input before sending
     # files.
-    # If you're ready to send the file, add it to the `filesQueue` and call
-    # processQueue()
+    # If you're ready to send the file, set the file.status to Dropzone.QUEUED
+    # and call processQueue()
     enqueueForUpload: yes

@@ -356,9 +356,6 @@
     @clickableElements = [ ]
     @listeners = [ ]
     @files = [] # All files
-    @acceptedFiles = [] # All files that are actually accepted
-    @filesQueue = [] # The files that still have to be processed
-    @filesProcessing = [] # The files currently processed

     @element = document.querySelector @element if typeof @element == "string"

@@ -407,6 +404,17 @@
     @init()

+  # Returns all files that have been accepted
+  getAcceptedFiles: -> file for file in @files when file.accepted
+
+  # Returns all files that have been rejected
+  # Not sure when that's going to be useful, but added for completeness.
+  getRejectedFiles: -> file for file in @files unless file.accepted
+
+  # Returns all files that are in the queue
+  getQueuedFiles: -> file for file in @files when file.status == Dropzone.QUEUED
+
+  getUploadingFiles: -> file for file in @files when file.status == Dropzone.UPLOADING

   init: ->
@@ -453,16 +461,9 @@
     # again when the dropzone gets disabled.
     @on eventName, @options[eventName] for eventName in @events

-    @on "uploadprogress", (file) =>
-      totalBytesSent = 0;
-      totalBytes = 0;
-      for file in @acceptedFiles
-        totalBytesSent += file.upload.bytesSent
-        totalBytes += file.upload.total
-      totalUploadProgress = 100 * totalBytesSent / totalBytes
-
-      @emit "totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent
-
+    @on "uploadprogress", => @updateTotalUploadProgress()
+
+    @on "removedfile", => @updateTotalUploadProgress()

     noPropagation = (e) ->
       e.stopPropagation()
@@ -518,6 +519,19 @@
       @hiddenFileInput = null

+  updateTotalUploadProgress: ->
+    totalBytesSent = 0
+    totalBytes = 0
+
+    for file in @getAcceptedFiles()
+      totalBytesSent += file.upload.bytesSent
+      totalBytes += file.upload.total
+    totalUploadProgress = 100 * totalBytesSent / totalBytes
+
+    @emit "totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent
+
+
+
   # Returns a form that can be used as fallback if the browser does not support DragnDrop
   #
   # If the dropzone is already a form, only the input field and button are returned. Otherwise a complete form element is provided.
@@ -638,16 +652,14 @@
         file.status = Dropzone.ACCEPTED
         file.accepted = true # Backwards compatibility

-        @acceptedFiles.push file
         if @options.enqueueForUpload
-          @filesQueue.push file
+          file.status = Dropzone.QUEUED
           @processQueue()

   # Can be called by the user to remove a file
   removeFile: (file) ->
     @cancelUpload file if file.status == Dropzone.UPLOADING
     @files = without @files, file
-    @filesQueue = without @filesQueue, file

     @emit "removedfile", file
     @emit "reset" if @files.length == 0
@@ -656,7 +668,7 @@
   removeAllFiles: ->
     # Create a copy of files since removeFile() changes the @files array.
     for file in @files.slice()
-      @removeFile file unless file in @filesProcessing
+      @removeFile file unless file.status == Dropzone.UPLOADING
     return null

   createThumbnail: (file) ->
@@ -692,18 +704,18 @@
   # Goes through the queue and processes files if there aren't too many already.
   processQueue: ->
     parallelUploads = @options.parallelUploads
-    processingLength = @filesProcessing.length
+    processingLength = @getUploadingFiles().length
     i = processingLength

+    queuedFiles = @getQueuedFiles()
     while i < parallelUploads
-      return unless @filesQueue.length # Nothing left to process
-      @processFile @filesQueue.shift()
+      return unless queuedFiles.length # Nothing left to process
+      @processFile queuedFiles.shift()
       i++

   # Loads the file, then calls finishedLoading()
   processFile: (file) ->
-    @filesProcessing.push file
     file.processing = yes # Backwards compatibility
     file.status = Dropzone.UPLOADING

@@ -721,10 +733,11 @@
     if file.status == Dropzone.UPLOADING
       file.status = Dropzone.CANCELED
       file.xhr.abort()
-      @filesProcessing = without(@filesProcessing, file)
     else if file.status in [ Dropzone.ADDED, Dropzone.ACCEPTED ]
       file.status = Dropzone.CANCELED
-      @filesQueue = without(@filesQueue, file)
+
+    @emit "complete", file
+
     @processQueue()

   uploadFile: (file) ->
@@ -833,8 +846,6 @@
   # Called internally when processing is finished.
   # Individual callbacks have to be called in the appropriate sections.
   finished: (file, responseText, e) ->
-    @filesProcessing = without(@filesProcessing, file)
-    file.processing = no # Backwards compatibility
     file.status = Dropzone.SUCCESS
     @processQueue()
     @emit "success", file, responseText, e
@@ -845,8 +856,6 @@
   # Called internally when processing is finished.
   # Individual callbacks have to be called in the appropriate sections.
   errorProcessing: (file, message, xhr) ->
-    @filesProcessing = without(@filesProcessing, file)
-    file.processing = no # Backwards compatibility
     file.status = Dropzone.ERROR
     @processQueue()
     @emit "error", file, message, xhr
@@ -1038,12 +1047,20 @@

-Dropzone.ADDED = "added";
-Dropzone.ACCEPTED = "accepted";
-Dropzone.UPLOADING = "uploading";
-Dropzone.CANCELED = "canceled";
-Dropzone.ERROR = "error";
-Dropzone.SUCCESS = "success";
+# Dropzone file status codes
+Dropzone.ADDED = "added"
+
+# Accepted does not necessarely mean queued.
+Dropzone.ACCEPTED = "accepted"
+
+Dropzone.QUEUED = "queued"
+
+Dropzone.UPLOADING = "uploading"
+Dropzone.PROCESSING = Dropzone.UPLOADING # alias
+
+Dropzone.CANCELED = "canceled"
+Dropzone.ERROR = "error"
+Dropzone.SUCCESS = "success"


Adjusted template for update manager

Adjusted template for update manager

--- olca-app-html/src/update_manager/coffeescript/main.coffee
+++ olca-app-html/src/update_manager/coffeescript/main.coffee
@@ -1,4 +1,6 @@
 renderUpdate = (update) ->
+	unless update.parentRefId
+		update.parentRefId = null
 	update.hasAttachment = window.java.hasAttachment update.refId
 	template = $ jade.templates['update'] update
 	for depRefId in update.dependencies


Removed default argument

Removed default argument

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -24,7 +24,7 @@
     @frame.setContent($.parseHTML(@content.trim())[0])
     @$el = $(@frame.frameBody.firstChild)

-  _bindCallbacks: (callbacks={}) ->
+  _bindCallbacks: (callbacks) ->
     @_robustHover = new RobustHover @$el, callbacks
     @$el.on 'click', -> callbacks.click?()


limit parallel resync doc requests to web

--- services/document-updater/app/coffee/HistoryManager.coffee
+++ services/document-updater/app/coffee/HistoryManager.coffee
@@ -65,10 +65,12 @@
 		newBlock  = Math.floor(length / threshold)
 		return newBlock != prevBlock

+	MAX_PARALLEL_REQUESTS: 4
+
 	resyncProjectHistory: (project_id, projectHistoryId, docs, files, callback) ->
 		ProjectHistoryRedisManager.queueResyncProjectStructure project_id, projectHistoryId, docs, files, (error) ->
 			return callback(error) if error?
 			DocumentManager = require "./DocumentManager"
 			resyncDoc = (doc, cb) ->
 				DocumentManager.resyncDocContentsWithLock project_id, doc.doc, cb
-			async.each docs, resyncDoc, callback
+			async.eachLimit docs, HistoryManager.MAX_PARALLEL_REQUESTS, resyncDoc, callback


better error message

better error message

--- lib/less-autocompile-view.coffee
+++ lib/less-autocompile-view.coffee
@@ -138,7 +138,7 @@

           if error
             @inProgress = false
-            @addMessagePanel '', 'error', error.message + ' : ' + error.filename
+            @addMessagePanel '', 'error', "#{error.message} - index: #{error.index}, line: #{error.line}, file: #{error.filename}"
           else
             css = tree.toCSS
               compress: params.compress


disable multiline bin op

disable multiline bin op

--- test/gram.coffee
+++ test/gram.coffee
@@ -110,15 +110,16 @@
     do (sample)->
       it sample, ()->
         full sample
-  sample_list = """
-    a +
-      b
-  """.split /\n?---\n?/g
-  for sample in sample_list
-    continue if !sample
-    do (sample)->
-      it JSON.stringify(sample), ()->
-        full sample
+  # BUG in gram2
+  # sample_list = """
+  #   a +
+  #     b
+  # """.split /\n?---\n?/g
+  # for sample in sample_list
+  #   continue if !sample
+  #   do (sample)->
+  #     it JSON.stringify(sample), ()->
+  #       full sample

   sample_list = """
     a ?


add a specific help

add a specific help

--- src/incident.coffee
+++ src/incident.coffee
@@ -46,3 +46,8 @@
     incidentNumber = robot.brain.get('incidentNumber')
     msg.send "INCIDENT NOTIFY: Resolved incident #{incidentNumber}"

+  robot.respond /incident help/i, (msg) ->
+    commands = robot.helpCommands()
+    commands = (command for command in commands when command.match(/incident/))
+    msg.send commands.join("\n")
+


disable button while image is cropped

disable button while image is cropped

--- app/assets/javascripts/active_admin_jcrop/crop_modal.js.coffee
+++ app/assets/javascripts/active_admin_jcrop/crop_modal.js.coffee
@@ -4,7 +4,7 @@
       $('.crop_modal_open').click ->
         content = $(this).parent().find('.crop_modal_content').clone()
         image = content.find('img.cropping_image')
-        active_admin_jcrop.buttons_text =
+        active_admin_jcrop.buttons_text =
           save_cropped_image: image.data('translateSaveCroppedImage')
           cancel: image.data('translateCancel')
         active_admin_jcrop.cropper =
@@ -14,7 +14,7 @@
           jcropper_url: image.data('jcropperUrl')

         $(content).appendTo('body').dialog
-          width: content.width()
+          width: content.width()
           height: content.height() + 100
           modal: true
           position: {
@@ -26,6 +26,10 @@
             {
               text: active_admin_jcrop.buttons_text.save_cropped_image
               click: ->
+                submitButton = $('input[type="submit"]')[0]
+                submitButton.disabled = true
+                previousValue = submitButton.value
+                submitButton.value = 'Cropping image...'
                 text: 'aews'
                 cropper = active_admin_jcrop.cropper
                 $.ajax
@@ -33,9 +37,12 @@
                   url: cropper.jcropper_url
                   data:
                     image_data: cropper
+                  success: ->
+                    submitButton.value = previousValue
+                    submitButton.disabled = false
                   error: ->
                     alert('There was an error while cropping the image')
-                  $(@).dialog('close')
+                  $(@).dialog('close')
             }
             {
               text: active_admin_jcrop.buttons_text.cancel
@@ -84,4 +91,4 @@
       return

 $ ->
-  active_admin_jcrop.start()+  active_admin_jcrop.start()


When performing operations ensure that it's always scoped to the list-view-items-container or list-view-item.  This should prevent bleeding the operations into other parts (e.g. header/footer views in SourceListView)

--- app/assets/javascripts/cartilage/views/list_view.js.coffee
+++ app/assets/javascripts/cartilage/views/list_view.js.coffee
@@ -106,9 +106,8 @@
     @update()

   update: ->
-
     # Clean up all existing item views and their container elements.
-    (@$ "li").each (idx, element) =>
+    (@$ "ul.list-view-items-container > li.list-view-item").each (idx, element) =>
       @removeItem(element)

     _.each @renderModels(), (view) => @addSubview(view, @_listViewItemsContainer)
@@ -187,7 +186,7 @@
       @clearSelection() if @_selected.length > 0
     else
       # In single select, call deselect on each selected element so observing "deselect" works as expected
-      _.each(($ @el).find("li.selected"), (element) => @deselect(element))
+      _.each(($ @el).find("li.list-view-item.selected"), (element) => @deselect(element))

     @_selected.add model
     ($ element).addClass "selected"
@@ -216,14 +215,14 @@
   # Selects the first element in the list.
   #
   selectFirst: ->
-    element = ($ @el).find("li").first()
+    element = ($ @el).find("ul.list-view-items-container > li.list-view-item").first()
     @select element

   #
   # Selects the last element in the list.
   #
   selectLast: ->
-    element = ($ @el).find "li:last-of-type"
+    element = ($ @el).find "ul.list-view-items-container > li.list-view-item:last-of-type"
     @select element

   #
@@ -231,14 +230,14 @@
   #
   selectAll: ->
     return unless @allowsSelection and @allowsMultipleSelection
-    elements = ($ @el).find "li:not(.selected)"
+    elements = ($ @el).find "ul.list-view-items-container > li.list-view-item:not(.selected)"
     _.each elements, @selectAnother

   #
   # Restores the selection after a re-rendering.
   #
   restoreSelection: =>
-    elements = ($ @el).find "li"
+    elements = ($ @el).find "ul.list-view-items-container > li.list-view-item"
     _.each elements, (element) =>
       model = ($ element).data("model")
       if model in @_selected.models
@@ -263,7 +262,7 @@
   # performed.
   #
   clearSelection: (options = {}) ->
-    ($ @el).find("li.selected").removeClass "selected"
+    ($ @el).find("ul.list-view-items-container > li.list-view-item.selected").removeClass "selected"
     @_selected.reset(null, { silent: options["silent"] })
     @trigger("clear", @_selected) unless options["silent"]

@@ -288,7 +287,7 @@
   #
   onMouseDown: (event) =>
     # Get the list item element
-    element = if ($ event.target).is("li") then event.target else ($ event.target).parents("li")
+    element = if ($ event.target).is("li.list-view-item") then event.target else ($ event.target).parents("li.list-view-item")

     # Clear the selection if the user clicks in the list container.
     @clearSelection() if @allowsDeselection and event.target.tagName == "UL"
@@ -303,14 +302,14 @@

     else if event.shiftKey
       element = event.target
-      @expandSelectionToElement ($ element).parents("li") || element
+      @expandSelectionToElement ($ element).parents("li.list-view-item") || element
       event.preventDefault()

   #
   # Handles focus events.
   #
   onFocus: (event) =>
-    @focusedElement = $(event.target).closest("li")
+    @focusedElement = $(event.target).closest("li.list-view-item")
     @select @focusedElement unless event.metaKey
     event.preventDefault()

@@ -396,7 +395,7 @@
   # is no selection the first item will be selected.
   #
   moveSelectionUp: (e) =>
-    element = ($ @focusedElement).prev "li"
+    element = ($ @focusedElement).prev "li.list-view-item"
     @select element if element.length > 0

   #
@@ -404,14 +403,14 @@
   # is no selection the first item will be selected.
   #
   moveSelectionDown: (e) =>
-    element = ($ @focusedElement).next "li"
+    element = ($ @focusedElement).next "li.list-view-item"
     @select element if element.length > 0

   #
   # Expands the selection downward from the currently selected element.
   #
   expandSelectionDown: (e) ->
-    element = ($ @focusedElement).next "li"
+    element = ($ @focusedElement).next "li.list-view-item"
     if element.length > 0
       @selectAnother element
       @focusedElement = ($ element).focus()
@@ -420,7 +419,7 @@
   # Expands the selection upward from the currently selected element.
   #
   expandSelectionUp: (e) ->
-    element = ($ @focusedElement).prev "li"
+    element = ($ @focusedElement).prev "li.list-view-item"
     if element.length > 0
       @selectAnother element
       @focusedElement = ($ element).focus()
@@ -439,7 +438,7 @@
     indexes[1] += 1

     # Select the elements in the requested indexes.
-    elements = ($ @el).find("li").slice indexes...
+    elements = ($ @el).find("li.list-view-item").slice indexes...
     _.each elements, @selectAnother

   #


added requests list + request viewer

added requests list + request viewer

--- app/assets/js/app.coffee
+++ app/assets/js/app.coffee
@@ -4,23 +4,25 @@
   this.resource 'monitor', ->
     this.resource 'request', path: '/:request_id'

-App.RequestModel = Ember.Object.extend
-  name: ''
-  id: 0
+#App.Requests = Ember.MutableArray.create()

+App.RequestModel = DS.Model.extend
+  body: DS.attr()
+  headers: DS.attr()
+
+App.RequestId = 1
+
+# Index
 App.IndexRoute = Ember.Route.extend
   activate: (controller) ->
     $('.top-bg').animate
       height: 500
     $('.start').fadeIn()
-  model: ->
-    ['red', 'yellow', 'blue']

 App.IndexController = Ember.Controller.extend
   setupController: (controller) ->

-
-
+# Monitor
 App.MonitorRoute = Ember.Route.extend
   activate: (controller) ->
     $('.top-bg').animate
@@ -30,17 +32,21 @@

 App.MonitorController = Ember.ArrayController.extend

+  content: []
+
+  sortProperties: ['id']
+  sortAscending: false
+
   init: ->
-    me = this
-    @set 'content', []
+    @._super()
+    self = @

-    @pushObject
-      name: 'test'
+    content = @get 'content'

-    Ember.$.getJSON config.baseUrl + "monitor/request", (data) ->
+    $.getJSON config.baseUrl + "monitor/request", (data) ->
       if data.success
-        me.websocket = websocket = new WebSocket config.liveUrl + "?id=" + data.session
-        me.set 'sessionid', data.session
+        self.websocket = websocket = new WebSocket config.liveUrl + "?id=" + data.session
+        self.set 'sessionid', data.session

         websocket.onopen = (ev) ->

@@ -56,17 +62,22 @@
         ###
         websocket.onmessage = (ev) ->
           data = JSON.parse ev.data
-          console.log me, data
+          store = self.get 'store'

-          me.set 'requestContent', data.content
+          data.id = ++App.RequestId
+          data.name = "Request " + data.id
+          data.headers = for key, value of data.headers
+            name: key, value: value

-          me.pushObject
-            name: 'test'
+          self.pushObject data
+          store.push 'request', data

-          me.set 'content', []
-
+# Request
 App.RequestRoute = Ember.Route.extend
   model: (params) ->
     store = @get 'store'
+    request = store.find 'request', params.request_id
+    request

-#App.RequestController = Ember.Route.extend+
+window.App = App


fix bug I introduced. radio button with only one condition on one answer. i had made a change to deal with radio buttons with conditions on both answers. that broke the scenario with one condition. i need to bind all radio buttons and I had changed the code to only bind the radio button with the condition. removed that code and now both scenarios seem to be working.

--- app/assets/javascripts/hanuman/conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/conditional-logic.js.coffee
@@ -40,7 +40,7 @@
           self.bindConditions($triggerElement)
         # radio buttons
         else
-          if ($triggerElement.is(":checkbox") || $triggerElement.is(":radio"))
+          if $triggerElement.is(":checkbox")
             # limit binding of each checkbox if data-label-value and answer are the same-kdh
             $triggerElement = $triggerContainer.find(".form-control[data-label-value=" + condition.answer.replace("/","\\/") + "]")
             self.bindConditions($triggerElement)


Call `@config.load()` in `AtomApplication` constructor

This is needed in order to test configuration settings like
`restorePreviousWindowsOnStart`. Even if this call has side effects, its
scope is pretty constrained because it affects only the temporary
directory we create before each atom-application test.

--- src/main-process/atom-application.coffee
+++ src/main-process/atom-application.coffee
@@ -69,6 +69,7 @@

     @config = new Config({configDirPath: process.env.ATOM_HOME, @resourcePath, enablePersistence: true})
     @config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))}
+    @config.load()
     @fileRecoveryService = new FileRecoveryService(path.join(process.env.ATOM_HOME, "recovery"))
     @storageFolder = new StorageFolder(process.env.ATOM_HOME)

@@ -82,7 +83,6 @@
   initialize: (options) ->
     global.atomApplication = this

-    @config.load()
     @config.onDidChange 'core.useCustomTitleBar', @promptForRelaunch

     @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config)


Added taxons list to Catalog service

Added taxons list to Catalog service

--- app/assets/javascripts/sprangular/resources/catalog.coffee
+++ app/assets/javascripts/sprangular/resources/catalog.coffee
@@ -1,23 +1,21 @@
 Sprangular.service 'Catalog', ($http, $q, _) ->
+  products: (page=1) ->
+    @getPaged('/products', page)

-  service =
-    products: (page=1) ->
-      @getPaged('/products', page)
+  taxon: (path,page=1) ->
+    @getPaged("/products/?category=#{path}", page)

-    taxon: (path,page=1) ->
-      @getPaged("/products/?category=#{path}", page)
+  taxons: ->
+    $http.get("/taxons/#{id}")
+      .then (response) ->
+        response.data

-    find: (id) ->
-      @get("/products/#{id}")
+  find: (id) ->
+    $http.get("/products/#{id}", class: Sprangular.Product)

-    get: (path) ->
-      $http.get(path, class: Sprangular.Product)
-
-    getPaged: (path, page=1) ->
-      $http.get(path, params: {per_page: 40, page: page})
-           .then (response) ->
-             list = Sprangular.extend(response.data?.products || [], Sprangular.Product)
-             list.isLastPage = list.length < 40
-             list
-
-  service
+  getPaged: (path, page=1) ->
+    $http.get(path, params: {per_page: 40, page: page})
+         .then (response) ->
+           list = Sprangular.extend(response.data?.products || [], Sprangular.Product)
+           list.isLastPage = list.length < 40
+           list


cleanup tinymce for outcomes and groups

cleanup tinymce for outcomes and groups

fixes CNVS-5360

test plan:
- in a course or account, create several outcomes and several outcome groups
- without refreshing, go back and edit their descriptions one by one
- each time you edit, tinymce should show up, work correctly, allow you to
  save, and your changes should be preserved

Change-Id: Ib2b4c30c1dda2b2c94b7745dc725c3308f83d7d8
Reviewed-on: https://gerrit.instructure.com/20424
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Stanley Stuart <2b43fb8b7a234825d50dd49ce7892d78a59da8f3@instructure.com>
Reviewed-by: Cameron Sutter <853bcc0cb8f5834b7805e8eb9a762054be43c731@instructure.com>
QA-Review: Amber Taniuchi <ef4bfd1e6d278f6bc7d4a478f02be2cb9581a0a5@instructure.com>
Product-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>

--- app/coffeescripts/views/outcomes/OutcomeContentBase.coffee
+++ app/coffeescripts/views/outcomes/OutcomeContentBase.coffee
@@ -76,7 +76,7 @@
           @render()
       super()

-    _cleanUpTiny: => @$el.find( '#description' ).editorBox 'destroy'
+    _cleanUpTiny: => @$el.find('[name="description"]').editorBox 'destroy'

     submit: (e) =>
       e.preventDefault()


Cleaned up loadFixture test.

Cleaned up loadFixture test.

--- lib/test.coffee
+++ lib/test.coffee
@@ -1,5 +1,6 @@
 should = require('chai').should()
 fs = require 'graceful-fs'
+read = fs.readFileSync
 request = require 'supertest'
 express = require 'express'
 stasset = require './index'
@@ -10,20 +11,18 @@
 middleware = stasset(require("#{__dirname}/../test/server/config"))
 app.use middleware

-sum = (expected = '')->
+loadFixture = (fixture)->
+    expected = read("#{__dirname}/../test/fixtures/#{fixture}", 'utf-8')
     expectedSum = shasum(expected)
     (res)->
         if process.env.DEBUG
             try
-                expected.should.equal(res.text)
+                res.text.should.equal expected
             catch e
                 console.log e
-        expected.length.should.equal res.text.length, 'Lengths match.'
-        expectedSum.should.equal(shasum(res.text or ''), 'Sums match.')
+        res.text.length.should.equal expected.length, 'Lengths match.'
+        shasum(res.text or '').should.equal(expectedSum, 'Sums match.')
         false
-
-loadFixture = (fixture)->
-    sum(fs.readFileSync("#{__dirname}/../test/fixtures/#{fixture}", 'utf-8'))

 checkMap = (fixture)->
     src = fs.readFileSync("#{__dirname}/../test/fixtures/#{fixture}", 'utf-8')


Rearranged quotation marks in es-419.coffee

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -52,7 +52,7 @@
     compared: "comparados"
     conventional: "con los métodos de evaluación convencionales, la diferencia es clara: los juegos son mejores al ayudar a los estudiantes a retener el conocimiento, a concentrarse y"
     perform_at_higher_level: "a desempeñarse con un mayor nivel de éxito"
-    feedback: 'Los juegos también proveen retroalimentación en tiempo real que permite a los estudiantes ajustar su camino a la solución y entender conceptos de forma más holística, en lugar de estar limitados solamente a respuestas "correctas" e "incorrectas"'
+    feedback: "Los juegos también proveen retroalimentación en tiempo real que permite a los estudiantes ajustar su camino a la solución y entender conceptos de forma más holística, en lugar de estar limitados solamente a respuestas 'correctas' e 'incorrectas'"
     real_game: "Un juego de verdad, jugado con código auténtico."
     great_game: "Un gran juego es más que solo medallas y logros - se trata sobre una travesía del jugador, acertijos bien diseñados, y la habilidad de abordar desafios con voluntad y confianza."
     agency: "CodeCombat es un juego que ofrece a los jugadores esa voluntad y confianza mediante nuestro motor de código robusto, que ayuda tanto a principiantes como a estudiantes avanzados por igual a escribir código correcto y válido."


Increase specificity on event that prevents the editor form submit

If we're just trying to prevent the form from being submit, there's
no need to stop propagation and default on *all* clicks on these
buttons. It may cause unwanted behavior in the future. Also,
`BlogEditor.make` runs on every auto-save, which in turn calls this
event bind, which sometimes caused the buttons to do nothing when
clicked since `stopPropagation` was bound multiple times. Also,
since the event is delegated directly to the button, if for some reason
we removed the buttons and put them back, the bindings would be gone.

By delegating the event to the `form`'s click event and checking
that the event originated from a `.mediumInsert`, we only block
the event when necessary.

--- client/views/admin/editor.coffee
+++ client/views/admin/editor.coffee
@@ -49,9 +49,8 @@
     @init.apply @, arguments

     # Don't let the medium insert plugin submit the form
-    $(@elements[0]).find('.mediumInsert-action').on 'click', (e) ->
-      e.preventDefault()
-      e.stopPropagation()
+    $('form').on 'click', (event) ->
+      if $(event.target).is '.mediumInsert' then event.preventDefault();

   # Return medium editor's contents
   contents: ->


Hide DuelStatsView while blue script messages are showing, to avoid overlap

--- app/views/play/level/DuelStatsView.coffee
+++ app/views/play/level/DuelStatsView.coffee
@@ -20,6 +20,8 @@
     'god:new-world-created': 'onNewWorld'
     'god:streaming-world-updated': 'onNewWorld'
     'surface:frame-changed': 'onFrameChanged'
+    'sprite:speech-updated': 'onSpriteDialogue'
+    'level:sprite-clear-dialogue': 'onSpriteClearDialogue'

   constructor: (options) ->
     super options
@@ -112,3 +114,10 @@
     return if @teamGold[e.team] is e.gold
     @teamGold[e.team] = e.gold
     utils.replaceText @$find(e.team, '.gold-value'), '' + e.gold
+
+  onSpriteDialogue: (e) ->
+    return unless e.message
+    @$el.css 'display', 'none'  # Hide it while a blue message is showing
+
+  onSpriteClearDialogue: ->
+    @$el.css 'display', 'flex'  # Show it


Fetched song name from result

Fetched song name from result

--- source/Downloader.coffee
+++ source/Downloader.coffee
@@ -48,3 +48,4 @@
     choices: titles
   ], (result) ->
     winston.info('Received answer from user.')
+    name = result['song']


Refactored Checkout.savePromo to use checkouts/apply_coupon_code action

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -6,7 +6,7 @@
         order:
           coupon_code: code

-      @put(null, params)
+      @put("apply_coupon_code", params)

     update: ->
       order = Cart.current
@@ -86,7 +86,7 @@
         headers:
           'X-Spree-Order-Token': Cart.current.token

-      url = _.compact(["/api/checkouts/#{Cart.current.number}",path]).join("/")
+      url = _.compact(["/api/checkouts", Cart.current.number, path]).join("/")
       Cart.current.errors = null

       $http.put(url, $.param(params), config)


Allow non logged-in user to add coupon to order

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -5,9 +5,13 @@
       params =
         coupon_code: code

+      config =
+        headers:
+          'X-Spree-Order-Token': Cart.current.token
+
       deferred = $q.defer()

-      $http.put("/spree/api/orders/#{Cart.current.number}/apply_coupon_code", $.param(params))
+      $http.put("/spree/api/orders/#{Cart.current.number}/apply_coupon_code", $.param(params), config)
            .success (response) ->
              Cart.load(response.order)


Update timeout.

Update timeout.

--- test/test.coffee
+++ test/test.coffee
@@ -14,7 +14,7 @@
     browser = getBrowser()
     browser
       .init()
-      .timeoutsAsyncScript(5000)
+      .timeoutsAsyncScript(10000)
       .url testPage
       .call done


refactor tests

refactor tests

--- test/test.coffee
+++ test/test.coffee
@@ -18,7 +18,6 @@
       fs.removeSync("#{process.cwd()}/.#{pkg.name}rc")

     describe 'core ::', ->
-
       it 'initialize a configuration file', (done) ->
         @bumped.init ->
           config = fs.readFileSync('.bumpedrc', encoding: 'utf8')
@@ -27,78 +26,81 @@
           done()

     describe 'semver ::', ->
+      describe 'version ::', ->
+        it 'sync correctly the version between the files', ->
+          @bumped.semver.version (version) ->
+            version.should.be.equal('0.2.0')

-      it 'sync correctly the version between the files', ->
-        @bumped.semver.version (version) ->
-          version.should.be.equal('0.2.0')
+      describe 'release', ->
+        it 'try to release a new version that is not valid', (done) ->
+          @bumped.semver.release version:null, (err, version) ->
+            (err?).should.be.equal true
+            done()

-      it 'try to release a new version that is not valid', (done) ->
-        @bumped.semver.release version:null, (err, version) ->
-          (err?).should.be.equal true
-          done()
+        it 'try to release a new version that is not valid string', (done) ->
+          @bumped.semver.release version:'1.0', (err, version) ->
+            (err?).should.be.equal true
+            done()

-      it 'try to release a new version that is not valid string', (done) ->
-        @bumped.semver.release version:'1.0', (err, version) ->
-          (err?).should.be.equal true
-          done()
+        it 'try to release a new version that is a valid string but is not greater', (done) ->
+          @bumped.semver.release version:'0.1.0', (err, version) ->
+            (err?).should.be.equal true
+            done()

-      it 'try to release a new version that is a valid string but is not greater', (done) ->
-        @bumped.semver.release version:'0.1.0', (err, version) ->
-          (err?).should.be.equal true
-          done()
+        it 'releases a new version that is a valid based in a number', (done) ->
+          @bumped.semver.release version:'1.0.0', (err, version) ->
+            (err?).should.be.equal false
+            version.should.be.equal('1.0.0')
+            require('./sample_directory/bower.json').version.should.be.equal('1.0.0')
+            done()

-      it 'releases a new version that is a valid based in a number', (done) ->
-        @bumped.semver.release version:'1.0.0', (err, version) ->
-          (err?).should.be.equal false
-          version.should.be.equal('1.0.0')
-          require('./sample_directory/bower.json').version.should.be.equal('1.0.0')
-          done()
-
-      it 'release a new version that is valid based in a semver keyword', (done) ->
-        @bumped.semver.release version:'minor', (err, version) ->
-          (err?).should.be.equal false
-          version.should.be.equal('1.1.0')
-          require('./sample_directory/bower.json').version.should.be.equal('1.1.0')
-          done()
+        it 'release a new version that is valid based in a semver keyword', (done) ->
+          @bumped.semver.release version:'minor', (err, version) ->
+            (err?).should.be.equal false
+            version.should.be.equal('1.1.0')
+            require('./sample_directory/bower.json').version.should.be.equal('1.1.0')
+            done()

     describe 'config ::', ->
-      it 'just add a file', (done) ->
-        @bumped.config.add
-          file: 'test.json'
-          outputMessage: true
-        , (err, files) ->
-          files.length.should.be.equal 3
-          done()
+      describe 'add ::', ->
+        it 'just add a file', (done) ->
+          @bumped.config.add
+            file: 'test.json'
+            outputMessage: true
+          , (err, files) ->
+            files.length.should.be.equal 3
+            done()

-      it 'try to add a file that is already added', (done) ->
-        @bumped.config.add
-          file: 'test.json'
-          outputMessage: true
-        , (err, files) ->
-          (err?).should.be.equal true
-          files.length.should.be.equal 3
-          done()
+        it 'try to add a file that is already added', (done) ->
+          @bumped.config.add
+            file: 'test.json'
+            outputMessage: true
+          , (err, files) ->
+            (err?).should.be.equal true
+            files.length.should.be.equal 3
+            done()

-      it 'prevent add a file that doesn\'t exist in the directory', (done) ->
-        @bumped.config.add
-          file: 'testing.json'
-          detect: true
-          outputMessage: true
-        , (err, files) ->
-          (err?).should.be.equal true
-          files.length.should.be.equal 3
-          done()
+        it 'prevent add a file that doesn\'t exist in the directory', (done) ->
+          @bumped.config.add
+            file: 'testing.json'
+            detect: true
+            outputMessage: true
+          , (err, files) ->
+            (err?).should.be.equal true
+            files.length.should.be.equal 3
+            done()

-      it 'add a file that exist in the directory and then save it', (done) ->
-        @bumped.config.add
-          file: 'component.json'
-          detect: true
-          outputMessage: true
-          save: true
-        , (err, files) ->
-          (err?).should.be.equal false
-          files.length.should.be.equal 4
-          config = fs.readFileSync('.bumpedrc', encoding: 'utf8')
-          config = JSON.parse(config)
-          config.files.length.should.be.equal 4
-          done()
+        it 'add a file that exist in the directory and then save it', (done) ->
+          @bumped.config.add
+            file: 'component.json'
+            detect: true
+            outputMessage: true
+            save: true
+          , (err, files) ->
+            (err?).should.be.equal false
+            files.length.should.be.equal 4
+            config = fs.readFileSync('.bumpedrc', encoding: 'utf8')
+            config = JSON.parse(config)
+            config.files.length.should.be.equal 4
+            done()
+      describe 'remove ::', ->


extend pause between tests to avoid amazon error, fix Resolution method is overspecified error on tests

--- test/test.coffee
+++ test/test.coffee
@@ -11,7 +11,7 @@
   # pause between tests, avoid getting yelled at by amazon
   setTimeout () ->
     done()
-  , 500
+  , 1000

 describe 'formatQueryParams(query, method, credentials)', ->
   it 'should return an object', ->
@@ -370,12 +370,11 @@
         done()
       , 500

-    afterEach (done) ->
+    afterEach () ->
       client.cartClear
         CartId: cartId
         HMAC: cartHMAC
       .then (results) ->
-        done()

     describe 'when no callback is passed', ->
       it 'should return results from amazon', ->
@@ -512,7 +511,7 @@
   cartId = ''
   cartHMAC = ''

-  before (done) ->
+  before () ->
     client = amazonAffiliateApi.createClient credentials
     client.cartCreate
       items: [
@@ -524,7 +523,6 @@
     .then (results) ->
       cartId = results.Cart.CartId
       cartHMAC = results.Cart.HMAC
-      done()

   describe 'when credentials are valid', ->
     client = amazonAffiliateApi.createClient credentials


:lipstick:

:lipstick:

--- renderer/api/lib/remote.coffee
+++ renderer/api/lib/remote.coffee
@@ -87,7 +87,6 @@

 # Browser releases a callback in renderer.
 ipc.on 'ATOM_RENDERER_DEREFERENCE', (callbackId) ->
-  console.log callbackId
   callbacksRegistry.remove callbackId

 # Get remote module.


Make all edit session uri's relative

Make all edit session uri's relative

This allows them to be collaborated without having absolute paths
in the shared document.

--- lib/archive-edit-session.coffee
+++ lib/archive-edit-session.coffee
@@ -14,10 +14,11 @@
       new ArchiveEditSession(filePath) if archive.isPathSupported(filePath)

   @deserialize: ({path}={}) ->
+    path = project.resolve(path)
     if fsUtils.isFileSync(path)
       new ArchiveEditSession(path)
     else
-      console.warn "Could not build edit session for path '#{path}' because that file no longer exists"
+      console.warn "Could not build archive edit session for path '#{path}' because that file no longer exists"

   constructor: (@path) ->
     @file = new File(@path)
@@ -27,7 +28,7 @@

   serialize: ->
     deserializer: 'ArchiveEditSession'
-    path: @path
+    path: @getUri()

   getViewClass: ->
     require './archive-view'
@@ -38,7 +39,7 @@
     else
       'untitled'

-  getUri: -> @path
+  getUri: -> project?.relativize(@getPath()) ? @getPath()

   getPath: -> @path


add tabs and commit feature

add tabs and commit feature

--- src/smc-webapp/editor_git.cjsx
+++ src/smc-webapp/editor_git.cjsx
@@ -5,8 +5,8 @@

 {React, ReactDOM, rclass, rtypes, Redux, Actions, Store}  = require('./smc-react')
-{Button, Panel, Row, Col} = require('react-bootstrap')
-{Icon, Space} = require('./r_misc')
+{Button, Input, Panel, Row, Col, Tabs, Tab} = require('react-bootstrap')
+{Icon, Space, Tip} = require('./r_misc')
 {salvus_client} = require('./salvus_client')
 misc = require('smc-util/misc')
 {defaults, required} = misc
@@ -15,11 +15,11 @@
     return "editor-#{project_id}-#{path}"

 class GitActions extends Actions
-    init: (@project_id, @filename) =>
+    init : (@project_id, @filename) =>
         @path = misc.path_split(@filename).head
         @setState(git_repo_root : @path)

-    exec: (opts) =>
+    exec : (opts) =>
         opts = defaults opts,
             cmd  : required
             args : []
@@ -36,7 +36,17 @@
                     console.warn("git editor ERROR exec'ing #{opts.cmd} #{opts.args.join(' ')}")
                 opts.cb(err, output)

-    update_status: () =>
+    commit : (message) =>
+        @setState(commit_status : 'commiting...')
+        @exec
+            cmd  : "git commit -a -m \"#{message}\""
+            cb   : (err, output) =>
+                if err
+                    @setState(git_commit_return : '')
+                else
+                    @setState(git_commit_return : output.stdout)
+
+    update_status : () =>
         @setState(git_status : 'updating...')
         @exec
             cmd  : "git"
@@ -47,7 +57,7 @@
                 else
                     @setState(git_status : output.stdout)

-    update_diff: () =>
+    update_diff : () =>
         @setState(git_diff : 'updating...')
         @exec
             cmd  : "git"
@@ -57,6 +67,12 @@
                     @setState(git_diff : '')
                 else
                     @setState(git_diff : output.stdout)
+
+    set_tab : (tab) =>
+        @setState(tab:tab)
+        if tab == 'commit'
+            @update_status()
+            @update_diff()

 exports.init_redux = init_redux = (redux, project_id, filename) ->
@@ -70,14 +86,53 @@
 Git = (name) -> rclass
     reduxProps:
         "#{name}" :
-            git_repo_root : rtypes.string
-            git_status    : rtypes.string
-            git_diff      : rtypes.string
-
-    propTypes:
+            tab               : rtypes.string
+            git_repo_root     : rtypes.string
+            git_commit_return : rtypes.string
+            git_status        : rtypes.string
+            git_diff          : rtypes.string
+
+    propTypes :
         actions : rtypes.object

-    render_status: ->
+    render_configuration_header : ->
+        <Tip delayShow=1300
+             title="Configuration" tip="This tab lists all ">
+
+                <Icon name="cogs"/> Configuration
+
+        </Tip>
+
+    render_commit_header : ->
+        <Tip delayShow=1300
+             title="Commit" tip="This tab lists all ">
+
+                <Icon name="cogs"/> Commit
+
+        </Tip>
+
+    render_commit_message_input : ->
+        <Input
+            ref         = 'commit_message'
+            type        = 'text'
+            placeholder = 'Commit message'
+            onChange    = {=>@setState(commit_message:@refs.commit_message.getValue())}
+            onKeyDown   = {@props.actions.commit} />
+
+    render_commit_panel : ->
+        head =
+
+                git commit -a -m "{@render_commit_message_input()}"
+                <Button
+                    onClick  = {@props.actions.commit} >
+                    Run
+                </Button>
+
+        <Panel header={head}>
+            {
{@props.git_commit_return}
 if @props.git_commit_return}
+        </Panel>
+
+    render_status : ->
         head =

                 git status
@@ -93,7 +148,7 @@
             {
{@props.git_status}
 if @props.git_status}
         </Panel>

-    render_diff: ->
+    render_diff : ->
         head =

                 git diff
@@ -109,17 +164,42 @@
             {
{@props.git_diff}
 if @props.git_diff}
         </Panel>

+    render_configuration : ->
+        <Row>
+            <Col sm=6>
+
+            </Col>
+        </Row>
+
+    render_commit : ->
+

+            <Row>
+                <Col sm=4>
+                    {@render_commit_panel()}
+                </Col>
+                <Col sm=4>
+                    {@render_status()}
+                </Col>
+                <Col sm=4>
+                    {@render_diff()}
+                </Col>
+            </Row>
+

+
     render : ->

Git Repository at {@props.git_repo_root}

-            <Row>
-                <Col sm=6>
-                    {@render_status()}
-                </Col>
-                <Col sm=6>
-                    {@render_diff()}
-                </Col>
-            </Row>
+            <Tabs animation={false} activeKey={@props.tab} onSelect={(key)=>@props.actions.set_tab(key)}>
+                <Tab eventKey={'configuration'} title={@render_configuration_header()}>
+

+                    {@render_configuration()}
+                </Tab>
+                <Tab eventKey={'commit'} title={@render_commit_header()}>
+

+                    {@render_commit()}
+                </Tab>
+            </Tabs>
+

 render = (redux, project_id, path) ->


Remove strict matching

Remove strict matching

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -29,7 +29,6 @@
   suggestionList: null
   suppressForClasses: []
   shouldDisplaySuggestions: false
-  manualActivationStrictPrefixes: null
   prefixRegex: /(\b|['"~`!@#\$%^&*\(\)\{\}\[\]=\+,/\?>])((\w+[\w-]*)|([.:;[{(< ]+))$/
   wordPrefixRegex: /^\w+[\w-]*$/

@@ -187,7 +186,6 @@
           hasEmpty = true unless suggestion.snippet or suggestion.text
           suggestion.replacementPrefix ?= @getDefaultReplacementPrefix(options.prefix)
           suggestion.provider = provider
-          @addManualActivationStrictPrefix(provider, suggestion.replacementPrefix) if options.activatedManually

         providerSuggestions = (suggestion for suggestion in providerSuggestions when (suggestion.snippet or suggestion.text)) if hasEmpty
         providerSuggestions = @filterSuggestions(providerSuggestions, options) if provider.filterSuggestions
@@ -198,7 +196,6 @@
       .then(@mergeSuggestionsFromProviders)
       .then (suggestions) =>
         return unless @currentSuggestionsPromise is suggestionsPromise
-        suggestions = @filterForManualActivationStrictPrefix(suggestions)
         if options.activatedManually and @shouldDisplaySuggestions and suggestions.length is 1
           # When there is one suggestion in manual mode, just confirm it
           @confirm(suggestions[0])
@@ -344,7 +341,6 @@

   hideSuggestionList: =>
     return if @disposed
-    @clearManualActivationStrictPrefixes()
     @suggestionList.changeItems(null)
     @suggestionList.hide()
     @shouldDisplaySuggestions = false
@@ -498,24 +494,3 @@
     @subscriptions = null
     @suggestionList = null
     @providerManager = null
-
-  clearManualActivationStrictPrefixes: ->
-    @manualActivationStrictPrefixes = null
-
-  addManualActivationStrictPrefix: (provider, prefix) ->
-    return if @manualActivationStrictPrefixes?.has(provider) or not prefix?
-    @manualActivationStrictPrefixes ?= new WeakMap
-    @manualActivationStrictPrefixes.set(provider, prefix.toLowerCase())
-
-  filterForManualActivationStrictPrefix: (suggestions) ->
-    return suggestions unless @manualActivationStrictPrefixes?
-
-    results = []
-    for suggestion in suggestions
-      lowercaseText = (suggestion.snippet ? suggestion.text).toLowerCase()
-      if lowercaseText[0] is suggestion.replacementPrefix.toLowerCase()[0]
-        strictPrefix = @manualActivationStrictPrefixes.get(suggestion.provider)
-        results.push(suggestion) if strictPrefix? and lowercaseText.startsWith(strictPrefix)
-      else
-        results.push(suggestion)
-    results


:green_heart: Make the change backwards-compatible

--- lib/bracket-matcher-view.coffee
+++ lib/bracket-matcher-view.coffee
@@ -25,8 +25,13 @@
     @pairHighlighted = false
     @tagHighlighted = false

-    @subscriptions.add @editor.getBuffer().onDidChangeText =>
-      @updateMatch()
+    # TODO: remove conditional when `onDidChangeText` ships on stable.
+    if typeof @editor.getBuffer().onDidChangeText is "function"
+      @subscriptions.add @editor.getBuffer().onDidChangeText =>
+        @updateMatch()
+    else
+      @subscriptions.add @editor.onDidChange =>
+        @updateMatch()

     @subscriptions.add @editor.onDidChangeGrammar =>
       @updateMatch()


Rearrange github link helpers

Rearrange github link helpers

So that regex and transformation appear together.

--- assets/scripts/app/helpers/helpers.coffee
+++ assets/scripts/app/helpers/helpers.coffee
@@ -79,14 +79,17 @@
       self._githubCommitReferenceLink(reference, { owner: owner, repo: repo }, { owner: matchedOwner, repo: matchedRepo, sha: matchedSHA })
     text

+  _githubReferenceRegexp: new RegExp("([\\w-]+)?\\/?([\\w-]+)?(?:#|gh-)(\\d+)", 'g')
+
   _githubReferenceLink: (reference, current, matched) ->
     owner = matched.owner || current.owner
     repo = matched.repo || current.repo
     "#{reference}"

-  _githubReferenceRegexp: new RegExp("([\\w-]+)?\\/?([\\w-]+)?(?:#|gh-)(\\d+)", 'g')
+  _githubUserRegexp: new RegExp("\\B@([\\w-]+)", 'g')

-  _githubUserRegexp: new RegExp("\\B@([\\w-]+)", 'g')
+  _githubUserLink: (reference, username) ->
+    "#{reference}"

   _githubCommitReferenceRegexp: new RegExp("([\\w-]+)?\\/([\\w-]+)?@([0-9A-Fa-f]+)", 'g')

@@ -94,9 +97,6 @@
     owner = matched.owner || current.owner
     repo = matched.repo || current.repo
     "#{reference}"
-
-  _githubUserLink: (reference, username) ->
-    "#{reference}"

   _normalizeDateString: (string) ->
     if window.JHW


Null-guard component in blur handler

Null-guard component in blur handler

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -103,7 +103,7 @@
     event.stopImmediatePropagation() if @contains(event.relatedTarget)

   blurred: (event) ->
-    @component.blurred()
+    @component?.blurred()
     event.stopImmediatePropagation() if @contains(event.relatedTarget)

   addGrammarScopeAttribute: ->


EnvironmentMachineItem: update code

EnvironmentMachineItem: update code

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -35,11 +35,10 @@
     @addSubView @statusToggle = new KodingSwitch
       cssClass     : "tiny"
       defaultValue : status.state is Machine.State.Running
-      callback     : (state)=>
+      callback     : (state)->
         if state
-          computeController.start machine
-        else
-          computeController.stop machine
+        then computeController.start machine
+        else computeController.stop machine

     @addSubView @progress = new KDProgressBarView
       cssClass : "progress"


Client: Activity: Added onKeyDown and handleChange methods to update purpose

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -16,6 +16,7 @@
 ChannelDropContainer         = require 'activity/components/channeldropcontainer'
 Link                         = require 'app/components/common/link'
 ButtonWithMenu               = require 'app/components/buttonwithmenu'
+KeyboardKeys                 = require 'app/util/keyboardKeys'

 module.exports = class ChannelThreadPane extends React.Component

@@ -111,6 +112,48 @@
     route = "/Channels/#{channelName}/NotificationSettings"

     kd.singletons.router.handleRoute route
+
+
+  onKeyDown: (event) ->
+
+    { ENTER, ESC } = KeyboardKeys
+    thread         = @state.channelThread
+    purpose        = thread.getIn(['channel', 'purpose'])
+
+    if event.which is ESC
+
+      _originalPurpose = thread.getIn ['channel', '_originalPurpose']
+      purpose = _originalPurpose or thread.getIn ['channel', 'purpose']
+      thread  = thread.setIn ['channel', 'purpose'], purpose
+      @setState channelThread: thread
+      return @setState editingPurpose: no
+
+    if event.which is ENTER
+
+      id        = thread.get 'channelId'
+      purpose   = thread.getIn(['channel', 'purpose']).trim()
+
+      { updateChannel } = ActivityFlux.actions.channel
+
+      updateChannel({ id, purpose }).then (response) =>
+        @setState editingPurpose: no
+
+
+  getPurposeAreaClassNames: -> classnames
+    'ChannelThreadPane-purposeWrapper': yes
+    'editing': @state.editingPurpose
+
+
+  handleChange: (newValue) ->
+
+    thread = @state.channelThread
+
+    unless thread.getIn ['channel', '_originalPurpose']
+      _originalPurpose = thread.getIn ['channel', 'purpose']
+      thread = thread.setIn ['channel', '_originalPurpose'], _originalPurpose
+
+    channelThread = thread.setIn ['channel', 'purpose'], newValue
+    @setState channelThread: channelThread

   renderPurposeArea: ->


Prevent race conditions

Prevent race conditions

--- src/coffee/donations-form-model.coffee
+++ src/coffee/donations-form-model.coffee
@@ -1,4 +1,4 @@
-cacheBust = 'cb23'
+cacheBust = 'cb24'

 class DonationsFormModel
   constructor: (jQuery, opts) ->
@@ -225,34 +225,35 @@
           $(".donations-callback-flash").show(0).delay(8000).hide(0)
         else
           $(".donation-payment-errors").text(data.message or "Something went wrong.").show()
-
-      customer = {}
-      customer.first_name = self.firstName()
-      customer.last_name = self.lastName()
-      customer.email = self.email()
-      customer.country = self.countryCode()
-      customer.charges_attributes = [charge]
-
-      formPost = {}
-      formPost.customer = customer
-      formPost.card_token = cardToken
-      formPost.config = $.extend(config, { 'calculatedAmounts' : self.amounts() })
-      formPost.organization_slug = self.org()
-
-      urlForCharges = if config['pathtoserver'].slice(-1) == "/" then config['pathtoserver'] + "charges" else config['pathtoserver'] + "/charges"
-      $.ajax(
-        url: urlForCharges
-        type: "post"
-        dataType: 'json'
-        contentType: 'application/json'
-        data: JSON.stringify(formPost)
-        success: (response, textStatus, jqXHR) ->
-          gaDonations('send', 'event', 'advance-button', 'click#success', 'submit', 1)
-        error: (response, textStatus, errorThrown) ->
-          gaDonations('send', 'event', 'advance-button', 'click#with-errors', 'submit', 1)
-          $form.find(".donation-payment-errors").text(response.responseText or "Something went wrong.").show()
-          $('.donation-loading-overlay').hide()
-          $form.find("button").prop "disabled", false
+      channel.bind('pusher:subscription_succeeded', ->
+        customer = {}
+        customer.first_name = self.firstName()
+        customer.last_name = self.lastName()
+        customer.email = self.email()
+        customer.country = self.countryCode()
+        customer.charges_attributes = [charge]
+
+        formPost = {}
+        formPost.customer = customer
+        formPost.card_token = cardToken
+        formPost.config = $.extend(config, { 'calculatedAmounts' : self.amounts() })
+        formPost.organization_slug = self.org()
+
+        urlForCharges = if config['pathtoserver'].slice(-1) == "/" then config['pathtoserver'] + "charges" else config['pathtoserver'] + "/charges"
+        $.ajax(
+          url: urlForCharges
+          type: "post"
+          dataType: 'json'
+          contentType: 'application/json'
+          data: JSON.stringify(formPost)
+          success: (response, textStatus, jqXHR) ->
+            gaDonations('send', 'event', 'advance-button', 'click#success', 'submit', 1)
+          error: (response, textStatus, errorThrown) ->
+            gaDonations('send', 'event', 'advance-button', 'click#with-errors', 'submit', 1)
+            $form.find(".donation-payment-errors").text(response.responseText or "Something went wrong.").show()
+            $('.donation-loading-overlay').hide()
+            $form.find("button").prop "disabled", false
+        )
       )

     stripeResponseHandler = (status, response) ->