:art:

:art:

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -79,7 +79,7 @@
     @tagsByCode = new Map
     @nextOpenTagCode = -1
     @indexedBufferRowCount = 0
-    @bufferChangeBeingProcessed = null
+    @processingBufferChange = false
     @reset({
       invisibles: settings.invisibles ? {}
       tabLength: settings.tabLength ? 4
@@ -229,10 +229,10 @@
     endRow = change.oldRange.end.row
     endRow++ while @buffer.lineForRow(endRow + 1)?.length is 0
     @computeSpatialScreenLinesThroughBufferRow(endRow)
-    @bufferChangeBeingProcessed = change
-
-  bufferDidChange: ->
-    {oldRange, newRange} = @expandChangeRegionToSurroundingEmptyLines(@bufferChangeBeingProcessed)
+    @processingBufferChange = true
+
+  bufferDidChange: (change) ->
+    {oldRange, newRange} = @expandChangeRegionToSurroundingEmptyLines(change)

     {startScreenRow, endScreenRow, startBufferRow, endBufferRow} = @expandBufferRangeToLineBoundaries(oldRange)
     endBufferRow = newRange.end.row + (endBufferRow - oldRange.end.row)
@@ -242,7 +242,7 @@
     newRowExtent = spatialScreenLines.length
     @spliceDisplayIndex(startScreenRow, oldRowExtent, spatialScreenLines)
     @indexedBufferRowCount += newRange.end.row - oldRange.end.row
-    @bufferChangeBeingProcessed = null
+    @processingBufferChange = false
     start = Point(startScreenRow, 0)
     oldExtent = Point(oldRowExtent, 0)
     newExtent = Point(newRowExtent, 0)
@@ -346,7 +346,7 @@
     @computeSpatialScreenLines(Infinity, screenRow + 1)

   computeSpatialScreenLines: (endBufferRow, endScreenRow) ->
-    return if @bufferChangeBeingProcessed?
+    return if @processingBufferChange
     if @indexedBufferRowCount < Math.min(endBufferRow, @buffer.getLineCount())
       lastScreenRow = @displayIndex.getScreenLineCount()
       if lastScreenRow < endScreenRow


opt: [utils.async] performance

opt: [utils.async] performance

--- src/utils.coffee
+++ src/utils.coffee
@@ -89,15 +89,9 @@
 		else
 			return throw new TypeError 'wrong argument type: ' + list

-		tryIter = ->
-			try
-				iter()
-			catch e
-				Promise.reject e
-
 		new Promise (resolve, reject) ->
 			addTask = ->
-				task = tryIter()
+				task = iter()
 				index = iterIndex++

 				if isIterDone or task == utils.end
@@ -117,7 +111,7 @@
 						resutls[index] = ret
 					progress? ret
 					addTask()
-				, (err) ->
+				.catch (err) ->
 					running--
 					reject err


Client:Account:Profile: if url has token, use it for validation

--- client/Account/views/username.coffee
+++ client/Account/views/username.coffee
@@ -60,6 +60,7 @@

     {email, password, confirmPassword, firstName, lastName, username} = formData

+    profileUpdated = yes
     queue = [
       =>
         # update firstname and lastname
@@ -92,25 +93,36 @@
         # check for password confirmation
         return  notify "Passwords did not match" if password isnt confirmPassword
         # if password is empty than discard operation
-        return queue.next() if password is ""
+        if password is ""
+          query = KD.utils.parseQuery()
+          if query.token
+            profileUpdated = no
+            notify "You should set your password"
+          return queue.next()

         KD.remote.api.JUser.changePassword password, (err,docs)=>
-          log "arguments"
-          log arguments
           if err
             return queue.next()  if err.message is "PasswordIsSame"
             return  notify err.message
           return queue.next()
       =>
         # if everything is OK or didnt change, show profile updated modal
-        notify "Profile Updated"
+        notify "Profile Updated" if profileUpdated
     ]
     daisy queue

   viewAppended:->
-
-    KD.remote.api.JUser.fetchUser (err,user)=>
+    {JPasswordRecovery, JUser} = KD.remote.api
+    query = KD.utils.parseQuery()
+    if token = query.token
+      JPasswordRecovery.validate token, (err, isValid)=>
+        if err
+          notify err.message
+        else if isValid
+          notify "Thanks for confirming your email address"
+
+    JUser.fetchUser (err,user)=>

       @user    = user


Code cleanup

Code cleanup

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -37,7 +37,8 @@
         settings:             React.PropTypes.object.isRequired

     shouldComponentUpdate: (nextProps, nextState) ->
-        return not(_.isEqual(nextState, @state)) or not (_.isEqual(nextProps, @props))
+        return not(_.isEqual(nextState, @state)) or
+            not (_.isEqual(nextProps, @props))

     render: ->

@@ -63,16 +64,25 @@
         classCc    = if @state.cc.length is 0 then '' else ' shown'
         classBcc   = if @state.bcc.length is 0 then '' else ' shown'

-        labelSend   = if @state.sending then t 'compose action sending' else t 'compose action send'
-        focusEditor = Array.isArray(@state.to) and @state.to.length > 0 and @state.subject isnt ''
+        if @state.sending
+            labelSend = t 'compose action sending'
+        else
+            labelSend = t 'compose action send'
+        focusEditor = Array.isArray(@state.to) and
+            @state.to.length > 0 and
+            @state.subject isnt ''

         div id: 'email-compose',
             if @props.layout isnt 'full'
-                a onClick: toggleFullscreen, className: 'expand pull-right clickable',
-                    i className: 'fa fa-arrows-h'
+                a
+                    onClick: toggleFullscreen,
+                    className: 'expand pull-right clickable',
+                        i className: 'fa fa-arrows-h'
             else
-                a onClick: toggleFullscreen, className: 'close-email pull-right clickable',
-                    i className:'fa fa-compress'
+                a
+                    onClick: toggleFullscreen,
+                    className: 'close-email pull-right clickable',
+                        i className:'fa fa-compress'
             h3
                 'data-message-id': @props.message?.get('id') or ''
                 @state.subject or t 'compose'
@@ -222,13 +232,15 @@
     componentWillUnmount: ->
         if @_saveInterval
             window.clearInterval @_saveInterval
-        #if @state.isDraft and @state.id?
-        #    if not window.confirm(t 'compose confirm keep draft')
-        #        MessageActionCreator.delete @state.id, (error) ->
-        #            if error?
-        #                LayoutActionCreator.alertError "#{t("message action delete ko")} #{error}"
-        #            else
-        #                LayoutActionCreator.notify t('compose draft deleted')
+        if @state.isDraft and
+           @state.id? and
+           not window.confirm(t 'compose confirm keep draft')
+            MessageActionCreator.delete @state.id, (error) ->
+                if error?
+                    LayoutActionCreator.alertError \
+                        "#{t("message action delete ko")} #{error}"
+                else
+                    LayoutActionCreator.notify t('compose draft deleted')

     getInitialState: (forceDefault) ->

@@ -293,7 +305,9 @@

         valid = true
         if not isDraft
-            if @state.to.length is 0 and @state.cc.length is 0 and @state.bcc.length is 0
+            if @state.to.length is 0 and
+               @state.cc.length is 0 and
+               @state.bcc.length is 0
                 valid = false
                 LayoutActionCreator.alertError t "compose error no dest"
                 setTimeout ->
@@ -498,7 +512,8 @@
                               document.documentElement.msMatchesSelector

                         if matchesSelector?
-                            return matchesSelector.call node, '.rt-editor blockquote, .rt-editor blockquote *'
+                            return matchesSelector.call node,
+                                '.rt-editor blockquote, .rt-editor blockquote *'
                         else
                             while node? and node.tagName isnt 'BLOCKQUOTE'
                                 node = node.parentNode
@@ -524,30 +539,40 @@
                     if gecko
                         br = "\r\n
\r\n
\r\n"
                     else
-                        br = "\r\n

\r\n"
+                        br = """
+                          \r\n

\r\n
+                            """
                     document.execCommand 'insertHTML', false, br

+                    node = document.querySelector('.cozyInsertedBr')
                     if gecko
-                        node = document.querySelector('.cozyInsertedBr').previousElementSibling
-                    else
-                        node = document.querySelector('.cozyInsertedBr')
+                        node = node.previousElementSibling
+
+                    # get path of a node inside the contentEditable
                     getPath = (node) ->
                         path = node.tagName
                         while node.parentNode? and node.contentEditable isnt 'true'
                             node = node.parentNode
                             path = "#{node.tagName} > #{path}"
                         return path
-                    s = window.getSelection()
-                    r = document.createRange()
-                    r.selectNode(node)
-                    s.removeAllRanges()
-                    s.addRange(r)
+
+                    # ensure focus is on newly inserted node
+                    selection = window.getSelection()
+                    range = document.createRange()
+                    range.selectNode(node)
+                    selection.removeAllRanges()
+                    selection.addRange(range)
+
+                    # outdent node
                     depth = getPath(node).split('>').length
                     while depth > 0
                         document.execCommand 'outdent', false, null
                         depth--
+                    # remove the surnumerous block
                     node = document.querySelector '.cozyInsertedBr'
                     node?.parentNode.removeChild node
+
+                    # try to remove format that may remains from the quote
                     document.execCommand 'removeFormat', false, null
                     return


minor change

minor change

--- library/membrane/transit.coffee
+++ library/membrane/transit.coffee
@@ -114,7 +114,7 @@
         assert value = _.first _.values(signature or 0)
         assert _.isFunction(value) or value.remote?, fx
         remotes.push value if (try value.remote.compile)
-        raw = (try value.remote?.symbol) or ("#{value}")
+        raw = (try value.remote.symbol) or value.toString()
         logger.debug message.grey, token.bold, identify
         @considerations = previous.concat [[token, raw]]


implement graceful shutdown of forevers

implement graceful shutdown of forevers

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -139,6 +139,8 @@
         assert monitor = forever.start command, opts
         restart = ("-" for i in [0..col - 1]).join ""
         monitor.on "restart", -> puts restart.red
+        process.on "SIGTERM", -> monitor.stop()
+        process.on "SIGINT", -> monitor.stop()

     # This task launches an instance of application where this task
     # is invoked at. It should be either an application build within
@@ -158,6 +160,8 @@
         assert monitor = forever.start command, opts
         restart = ("-" for i in [0..col - 1]).join ""
         monitor.on "restart", -> puts restart.red
+        process.on "SIGTERM", -> monitor.stop()
+        process.on "SIGINT", -> monitor.stop()

     # This task launches an instance of application where this task
     # is invoked at. It should be either an application build within


class-provider - Group the use statement adding and class renaming after insertion in a transaction.

--- lib/autocompletion/class-provider.coffee
+++ lib/autocompletion/class-provider.coffee
@@ -109,23 +109,24 @@
         return unless suggestion.data?.kind

         if suggestion.data.kind == 'instantiation' or suggestion.data.kind == 'static'
-            added = parser.addUseClass(editor, suggestion.text)
+            editor.transact () =>
+                added = parser.addUseClass(editor, suggestion.text)

-            # Removes namespace from classname
-            if added?
-                name = suggestion.text
-                splits = name.split('\\')
+                # Removes namespace from classname
+                if added?
+                    name = suggestion.text
+                    splits = name.split('\\')

-                nameLength = splits[splits.length-1].length
-                wordStart = triggerPosition.column - suggestion.data.prefix.length
-                lineStart = if added == "added" then triggerPosition.row + 1 else triggerPosition.row
+                    nameLength = splits[splits.length-1].length
+                    wordStart = triggerPosition.column - suggestion.data.prefix.length
+                    lineStart = if added == "added" then triggerPosition.row + 1 else triggerPosition.row

-                if suggestion.data.kind == 'instantiation'
-                    lineEnd = wordStart + name.length - nameLength - splits.length + 1
-                else
-                    lineEnd = wordStart + name.length - nameLength
+                    if suggestion.data.kind == 'instantiation'
+                        lineEnd = wordStart + name.length - nameLength - splits.length + 1
+                    else
+                        lineEnd = wordStart + name.length - nameLength

-                editor.setTextInBufferRange([
-                    [lineStart, wordStart],
-                    [lineStart, lineEnd] # Because when selected there's not \ (why?)
-                ], "")
+                    editor.setTextInBufferRange([
+                        [lineStart, wordStart],
+                        [lineStart, lineEnd] # Because when selected there's not \ (why?)
+                    ], "")


add overrides for template strings

add overrides for template strings

--- lib/closure-conversion.coffee
+++ lib/closure-conversion.coffee
@@ -816,11 +816,13 @@
                         when UnaryExpression       then exp.ejs_free_vars = @free exp.argument
                         when BinaryExpression      then exp.ejs_free_vars = @free(exp.left).union @free exp.right
                         when LogicalExpression     then exp.ejs_free_vars = @free(exp.left).union @free exp.right
-                        when MemberExpression      then exp.ejs_free_vars = @free exp.object # we don't traverse into the property
+                        when MemberExpression      then exp.ejs_free_vars = @free(exp.object) # we don't traverse into the property
                         when CallExpression        then exp.ejs_free_vars = @setUnion.apply null, [@free exp.callee].concat exp.arguments.map @call_free
                         when NewExpression         then exp.ejs_free_vars = @setUnion.apply null, [@free exp.callee].concat exp.arguments.map @call_free
                         when SequenceExpression    then exp.ejs_free_vars = @setUnion.apply null, exp.expressions.map @call_free
                         when ConditionalExpression then exp.ejs_free_vars = @setUnion.call null, @free(exp.test), @free(exp.consequent), @free(exp.alternate)
+                        when TaggedTemplateExpression then exp.ejs_free_vars = @free exp.quasi
+                        when TemplateLiteral       then exp.ejs_free_vars = @setUnion.apply null, exp.expressions.map @call_free
                         when Literal               then exp.ejs_free_vars = new Set
                         when ThisExpression        then exp.ejs_free_vars = new Set
                         when Property              then exp.ejs_free_vars = @free exp.value # we skip the key
@@ -834,7 +836,6 @@
                         when ExportDeclaration
                                 exp.ejs_free_vars = @free exp.declaration
                                 exp.ejs_decls = exp.declaration.ejs_decls
-
                         when ImportDeclaration
                                 exp.ejs_decls = new Set exp.specifiers.map ((specifier) -> specifier.id.name)
                                 # no free vars in an ImportDeclaration


added support for TypeScript

added support for TypeScript

--- lib/comment-characters.coffee
+++ lib/comment-characters.coffee
@@ -36,6 +36,7 @@
     SCSS: "//"
     "Shell Script": "#"
     SQL: "--"
+    TypeScript: "//"

     # creates the same kinf of object for all language data (independent of how it was written down)
     format: (value) ->


fix interrupting on windows once again yes, that return statement is important...

--- lib/connection/process.coffee
+++ lib/connection/process.coffee
@@ -96,18 +96,18 @@
                                                     ["-NoProfile", "$PSVersionTable.PSVersion.Major"])
                                           .output[1].toString()) > 2
       if @useWrapper
-        # get a random port between 1000 and 20_000; this may not be free, of course,
-        # but hopefully not very often
+        # get a random and hopefully free port:
         @getFreePort =>
           @proc = child_process.spawn("powershell",
                                       ["-NoProfile", "-ExecutionPolicy", "bypass",
                                        "& \"#{@script "spawnInterruptible.ps1"}\"
-                                        -cwd \"#{@workingDir()}\"
-                                        -port #{port}
-                                        -wrapPort #{@wrapPort}
-                                        -jlpath \"#{@jlpath()}\"
-                                        -boot \"#{@script 'boot.jl'}\""])
+                                       -cwd \"#{@workingDir()}\"
+                                       -port #{port}
+                                       -wrapPort #{@wrapPort}
+                                       -jlpath \"#{@jlpath()}\"
+                                       -boot \"#{@script 'boot.jl'}\""])
           fn()
+        return
       else
         cons.c.out "PowerShell version < 3 encountered. Running without wrapper (interrupts won't work)."
     @proc = child_process.spawn(@jlpath(), [@script("boot.jl"), port], cwd: @workingDir())


Fixed error with setting of session

Fixed error with setting of session

--- app/account/index.coffee
+++ app/account/index.coffee
@@ -63,14 +63,18 @@

 		if(status is 200)
 			url = "/users/" + response.username
-			console.log url
 			@get url, (sta, res) =>
 				console.log sta
 				if(sta is 200)
-					credentials =
-						toke: response
-						user: res.users[0]
-					req.auth.session.set credentials
+					data =
+						token: response
+						user:
+							username: res.users[0].username
+							gravatar: res.users[0].gravatar
+							settings: res.users[0].settings
+
+					req.auth.session.set data
+
 					reply '
Welcome
Logout'
 				else
 					reply '
 Failed duh duh duuuuuhhh
'
@@ -79,8 +83,6 @@

 #Logout the use, by clearing the session.
 logoutHandler = (req, reply) =>
-	credentials = req.auth.credentials
-	console.log credentials
 	req.auth.session.clear()
 	html = "Logout"


Fix test for updating SEO attributes.

Fix test for updating SEO attributes.

--- src/spec/integration/import.spec.coffee
+++ src/spec/integration/import.spec.coffee
@@ -489,7 +489,7 @@
         done(_.prettify err)
       .done()

-    it 'should do a partial update of SEO attribute', (done) ->
+    it 'should do a full update of SEO attribute', (done) ->
       csv =
         """
         productType,variantId,sku,name,metaTitle,metaDescription,metaKeywords
@@ -501,8 +501,8 @@
         expect(result[0]).toBe '[row 2] New product created.'
         csv =
           """
-          productType,variantId,sku,name,metaTitle,metaKeywords
-          #{@productType.id},1,a111,mySeoProdcut,,changed
+          productType,variantId,sku,name,metaTitle,metaDescription,metaKeywords
+          #{@productType.id},1,a111,mySeoProdcut,x,y,z
           """
         im = createImporter()
         im.import(csv)
@@ -514,10 +514,10 @@
         expect(_.size result.body.results).toBe 1
         p = result.body.results[0].masterData.staged
         expect(p.name.en).toBe 'mySeoProdcut'
-        expect(p.metaTitle.en).toBe 'a' # I would actually expect ''
-        expect(p.metaDescription.en).toBe 'b'
-        expect(p.metaKeywords.en).toBe 'changed'
-        done()
-      .fail (err) ->
-        done(_.prettify err)
-      .done()
+        expect(p.metaTitle.en).toBe 'x' # I would actually expect ''
+        expect(p.metaDescription.en).toBe 'y'
+        expect(p.metaKeywords.en).toBe 'z'
+        done()
+      .fail (err) ->
+        done(_.prettify err)
+      .done()


Age in seconds

Age in seconds

--- test/text.coffee
+++ test/text.coffee
@@ -19,6 +19,7 @@
   text = TextEffect()

   text.update(1)
+  text.t

   equals text.I.alpha, 1


ToolbarView: added className

ToolbarView: added className

--- coffee/src/views/toolbar_view.coffee
+++ coffee/src/views/toolbar_view.coffee
@@ -4,6 +4,8 @@

 # view for generating and containing buttons
 class athena.lib.ToolbarView extends athena.lib.View
+
+  className: 'toolbar-view'

   initialize: =>
     super


KDInputView: optimize initial height calculation procedure

Set initial height once view is focued and it is not set before

--- components/inputs/KDInputView.coffee
+++ components/inputs/KDInputView.coffee
@@ -65,6 +65,10 @@
           @setValue o.selectOptions[Object.keys(o.selectOptions)[0]][0].value unless o.defaultValue
         else
           @setValue o.selectOptions[0].value unless o.defaultValue
+
+    if o.autogrow
+      @once "focus", =>
+        @initialHeight = @$().height()  unless @initialHeight

   setDomElement:(cssClass = "")->
     name = "name='#{@options.name}'"
@@ -399,7 +403,10 @@
       border  = parseInt(@_clone.css("borderTopWidth"), 10) + parseInt(@_clone.css("borderBottomWidth"), 10)
       height  = height + border + padding

-    @setHeight Math.max @initialHeight, height
+    @setHeight \
+      if @initialHeight
+      then Math.max @initialHeight, height
+      else height

   enableTabKey:-> @inputTabKeyEnabled = yes

@@ -489,6 +496,3 @@
     else if event.which is 39 and t.value.slice(ss,ss + tabLength) is tab
       event.preventDefault()
       t.selectionStart = t.selectionEnd = ss + tabLength
-
-  viewAppended: ->
-    @initialHeight = @$().height()


DomainCreationForm: Do not try to walk on domain list if there isn’t

--- client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -149,9 +149,12 @@
     KD.whoami().fetchDomains (err, userDomains)=>
       warn err  if err
       domainList = []
-      for domain in userDomains
-        if not domain.regYears > 0
-          domainList.push {title:".#{domain.domain}", value:domain.domain}
+
+      if userDomains
+        for domain in userDomains
+          if not domain.regYears > 0
+            domainList.push {title:".#{domain.domain}", value:domain.domain}
+
       {domains, domainName} = @form.inputs
       domainName.setValue ""
       domains.removeSelectOptions()


Show prices for each domain in domain list item and provide 5 year selection for domain register. Add a simple buy button

--- client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -56,6 +56,8 @@
     domainName            = "#{domainName.getValue()}.#{domains.getValue()}"
     {getDomainInfo, getDomainSuggestions} = KD.remote.api.JDomain

+    @newDomainEntryForm.domainList?.setClass 'out'
+
     getDomainInfo domainName, (err, status)=>

       if err
@@ -72,9 +74,6 @@
           createButton.hideLoader()
           return warn err if err
           @newDomainEntryForm.setAvailableDomainsData suggestions
-
-      new KDNotificationView
-        title: if status.available then 'Yay its available' else 'Sorry dude.'

   createSubDomain: ->
     {domains, domainName} = @subDomainEntryForm.inputs
@@ -246,10 +245,23 @@

   constructor:(options={}, data)->
     options.cssClass = KD.utils.curry "domain-buy-items", options.cssClass
+
     super options, data
+
+    @buyButton = new KDButtonView
+      title    : "Buy"
+      style    : "clean-gray"
+      callback : ->
+        alert 'Buy it'
+
+    price = (parseFloat @getData().price).toFixed 2
+    selectOptions = \
+      ({title: "#{i} year for $ #{(price * i).toFixed 2}" , value: i} for i in [1..5])
+    @yearBox = new KDSelectBox {name:'year', selectOptions}

   pistachio:->
     """
-      {h1#domain{#(domain)}}
-      {{#(price)}}
+      {h1{#(domain)}}
+      {{> @yearBox}}
+      {{> @buyButton}}
     """


Deleted app.coffee

Deleted app.coffee

--- assets/js/app/app.coffee
+++ assets/js/app/app.coffee
@@ -1,72 +0,0 @@
-dndAcceptedTypes =
-	'image/png': true,
-	'image/jpeg': true,
-	'image/gif': true
-
-qsMaquisLogo = document.querySelector('.marquis-logo')
-qsMaquisChoices = document.querySelector('.marquis-choices')
-
-# Drag'n'drop upload
-qsDndUploadholder = document.getElementById 'dnd-upload-holder'
-uploading = false
-
-window.onload = ->
-
-	if qsMaquisLogo and qsMaquisChoices
-		qsMaquisLogo.classList.add 'loaded'
-		qsMaquisChoices.classList.add 'loaded'
-
-	if qsDndUploadholder
-		qsDndUploadholder.ondragenter = (e) ->
-			e.stopPropagation()
-			e.preventDefault()
-
-			@className = 'ondrag'
-
-		qsDndUploadholder.ondragleave = ->
-			if !uploading
-				@className = ''
-
-		# http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
-		qsDndUploadholder.addEventListener 'dragover', onDragEvent, false
-		qsDndUploadholder.addEventListener 'drop', onDragEvent, false
-
-onDragEvent = (e) ->
-
-	e.stopPropagation()
-	e.preventDefault()
-
-	if e.type == "drop"
-		dndFile = e.target.files || e.dataTransfer.files;
-		if isUnique(dndFile) && isAcceptedType(dndFile)
-			formData = new FormData
-			formData.append 'file', dndFile[0]
-
-			uploading = true
-
-			xhr = new XMLHttpRequest
-			xhr.open 'POST', '/', true
-			xhr.onload = () ->
-				uploading = false
-				qsDndUploadholder.className = ''
-
-				window.location.href = JSON.parse(@response).redirect
-				return
-
-			xhr.upload.onprogress = (e) ->
-				if e.lengthComputable
-					complete = e.loaded / e.total * 100 | 0
-				return
-
-			xhr.send formData
-
-isUnique = (file) ->
-	return file.length == 1
-
-
-isAcceptedType = (file) ->
-	return dndAcceptedTypes[file[0].type] == true
-
-displayErrorMessage = (message) ->
-	console.log message
-


Add test for removing a specific responder

--- tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-pubsub.coffee
+++ tapestry-core/src/test/coffeescript/org/apache/tapestry5/integration/app1/pages/test-pubsub.coffee
@@ -17,3 +17,18 @@

     ok memoValue is expectedMemo, "responder function was invoked"

+  test "off match by specific responder", ->
+    count = 0
+
+    responder = -> count++
+
+    pubsub.on "stim", responder
+    pubsub.fire "stim"
+
+    equal count, 1, "responder invoked on first fire"
+
+    pubsub.off null, responder
+    pubsub.fire "stim"
+
+    equal count, 1, "responder not invoked after removal"
+


whoa, atom's config file totally changed formats, man. Committing that.

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,35 +1,45 @@
-'editor':
-  'showIndentGuide': true
-  'fontFamily': 'Menlo'
-  'fontSize': 15
-'core':
-  'excludeVcsIgnoredPaths': true
-  'ignoredNames': [
-    '.bundle'
-    '.git'
-    'log'
-    'repositories'
-    'tmp'
-    'vendor'
-  ]
-  'themes': [
-    'unity-ui'
-    'glacier-syntax'
-  ]
-  'hideGitIgnoredFiles': true
-  'disabledPackages': [
-    'background-tips'
-    'exception-reporting'
-    'metrics'
-  ]
-'spell-check':
-  'grammars': [
-    'text.plain'
-    'source.gfm'
-    'text.git-commit'
-  ]
-'welcome':
-  'showOnStartup': false
-'release-notes':
-  'viewedVersion': '0.89.0'
-'tree-view': {}
+'global':
+  'editor':
+    'showIndentGuide': true
+    'fontFamily': 'Source Code Pro'
+    'showInvisibles': true
+    'fontSize': 13
+    'invisibles': {}
+  'core':
+    'ignoredNames': [
+      '.bundle'
+      '.git'
+      'log'
+      'repositories'
+      'tmp'
+      'vendor'
+    ]
+    'hideGitIgnoredFiles': true
+    'disabledPackages': [
+      'background-tips'
+      'exception-reporting'
+      'metrics'
+    ]
+    'themes': [
+      'quantum-ui'
+      'muukii'
+    ]
+  'spell-check':
+    'grammars': [
+      'text.plain'
+      'source.gfm'
+      'text.git-commit'
+    ]
+  'welcome':
+    'showOnStartup': false
+  'release-notes':
+    'viewedVersion': '0.89.0'
+  'wakatime':
+    'apikey': ''
+    'ignore': [
+      '^/var/'
+      '^/tmp/'
+      '^/private/'
+    ]
+'.ini.source':
+  'editor': {}


Update atom config

Update atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,4 +1,27 @@
 "*":
+  "":
+    coffee:
+      jsx:
+        source:
+          editor:
+            preferredLineLength: 100
+    diff:
+      source:
+        editor:
+          preferredLineLength: 100
+    js:
+      jsx:
+        source:
+          editor:
+            preferredLineLength: 100
+    matlab:
+      source:
+        editor:
+          preferredLineLength: 100
+    octave:
+      source:
+        editor:
+          preferredLineLength: 100
   "atom-beautify":
     _analyticsUserId: "dd5ae96b-29e0-40aa-be70-a2344a5c6ad2"
     beautifyEntireFileOnSave: false
@@ -37,6 +60,8 @@
     formatOnSave: true
   "exception-reporting":
     userId: "927ad9ee-b6a5-4937-bc3c-31ee92e96c24"
+  "file-types":
+    "\\.config$": "source.yaml"
   "git-plus": {}
   linter:
     ignoredLinterErrors: [
@@ -76,18 +101,3 @@
   whitespace:
     ignoreWhitespaceOnCurrentLine: false
     keepMarkdownLineBreakWhitespace: false
-".coffee.jsx.source":
-  editor:
-    preferredLineLength: 100
-".diff.source":
-  editor:
-    preferredLineLength: 100
-".js.jsx.source":
-  editor:
-    preferredLineLength: 100
-".matlab.source":
-  editor:
-    preferredLineLength: 100
-".octave.source":
-  editor:
-    preferredLineLength: 100


Install jshint atom package

Install jshint atom package

--- atom/.atom/packages.cson
+++ atom/.atom/packages.cson
@@ -13,6 +13,7 @@
   'language-vue'
   'linter'
   'linter-eslint'
+  'linter-jshint'
   'linter-php'
   'linter-stylelint'
   'markdown-preview-plus'


Add GridView support for colWidth and rowHeight props.

--- src/grid/GridView.coffee
+++ src/grid/GridView.coffee
@@ -58,56 +58,123 @@

     @context.items.on 'remove', (e) => @_removeItem e.data

-    @_packer = new Packer 100, 20
+    @_packer = new Packer 100, 5

   properties:

+    ###*
+    #
+    #
+    #
+    ###
     layout:
       default: 'columns'
       values: ['columns', 'rows']

-    size:
-      default: '10%'
+    ###*
+    #
+    #
+    #
+    ###
+    colWidth:
+      default: '20%'
       type: 'number|string'

+    ###*
+    #
+    #
+    #
+    ###
+    rowHeight:
+      default: 100
+      type: 'number|string'

   ###*
+  # Adds an item to the grid view.
   #
+  # @param {stout-ui/grid/GridItemView} itemView - The grid item to add.
   #
-  #
+  # @method _addItem
+  # @memberof stout-ui/grid/GridView
+  # @protected
   ###
   _addItem: (itemView) ->
-    SIZE = 50
+    colWidth  = @colWidth
+    rowHeight = @rowHeight
+
+    ###*
+    # Calculates the width of the grid in pixels.
+    #
+    # @returns {number} Current width of the grid in pixels.
+    #
+    # @function calcGridWidth
+    # @memberof stout-ui/grid/GridView#_addItem
+    # @inner
+    ###
+    calcGridWidth = =>
+      @root.getBoundingClientRect().width
+
+    ###*
+    # Converts a passed number of columns and converts it to a pixel or
+    # percentage width.
+    #
+    # @param {number} cols - Width in columns.
+    #
+    # @returns {string} Width in pixels or percentage, suffixed with `'%'` or
+    # `'px'`, as appropriate.
+    #
+    # @function colSize
+    # @memberof stout-ui/grid/GridView#_addItem
+    # @inner
+    ###
+    colSize = (cols) =>
+      if colWidth.indexOf and colWidth.indexOf('%') isnt -1
+        cols * parseFloat(colWidth) + '%'
+      else
+        cols * parseFloat(colWidth) + 'px'
+
+    ###*
+    # Converts a passed number of rows and converts it to a pixel height.
+    #
+    # @param {number} rows - Height in rows.
+    #
+    # @returns {string} Height in pixels, suffixed with `'px'`.
+    #
+    # @function colSize
+    # @memberof stout-ui/grid/GridView#_addItem
+    # @inner
+    ###
+    rowSize = (rows) =>
+      rows * parseFloat(rowHeight) + 'px'
+
+
+    ###*
+    # Sets the height of the containing grid element to match the contained
+    # grid items.
+    #
+    # @function colSize
+    # @memberof stout-ui/grid/GridView#_addItem
+    # @inner
+    ###
+    setGridSize = =>
+      @root.style.height = rowSize(@_packer.height())
+
     {height, width, id} = itemView
     [row, col] = @_packer.insert height, width, id

     itemView.row = row
     itemView.column = col

-    top = (row * SIZE)
-    left = (col * SIZE)
-    itemView.root.style.left = "#{left}px"
-    itemView.root.style.top = "#{top}px"
-    itemView.root.style.height = "#{itemView.height * SIZE}px"
-    itemView.root.style.width = "#{itemView.width * SIZE}px"
+    itemView.root.style.left = colSize(col)
+    itemView.root.style.top = rowSize(row)
+    itemView.root.style.height = rowSize(itemView.height)
+    itemView.root.style.width = colSize(itemView.width)
     itemView.parent = @
     itemView.parentEl = @root

-    itemView.contents = itemView.id
-
-    # set draggable axis -- TMP
-    # itemView.axis = 'x'
-
-    setGridSize = =>
-      ch = @_packer.height @_packer
-      cw = @_packer.width @_packer
-      @root.style.width = "#{cw * SIZE}px"
-      @root.style.height = "#{ch * SIZE}px"
-
-
-
+    itemView.contents = itemView.id # TEMPORARY

     # Create a shadow element indicating where the dragged item will be dropped.
     # (Called with GridItemView as "this")
@@ -121,104 +188,96 @@
       shadow.style.left = @root.style.left
       @parentEl.insertBefore shadow, @root

+
     positionShadow = (id, row, col, height, width) ->
+      if not shadow then return
       [row, col] = @parent._packer.constrain id, row, col
-      shadow.style.top = "#{row * SIZE}px"
-      shadow.style.left = "#{col * SIZE}px"
-      if height then shadow.style.height = "#{height * SIZE}px"
-      if width then shadow.style.width = "#{width * SIZE}px"
+      shadow.style.top = rowSize(row)
+      shadow.style.left = colSize(col)
+      if height then shadow.style.height = rowSize(height)
+      if width then shadow.style.width = colSize(width)
+

     # (Called with GridItemView as "this")
     deleteShadow = ->
       @parentEl.removeChild(shadow)
       shadow = null

-    dragShiftedIds = null
-
-    itemView.on 'dragstart', (e) ->
-      dragShiftedIds = []

     # (Called with GridView as "this")
     positionItems = (shifted, skip) ->
       @context.items.forEach (item) =>
-        if item.id in skip then return
         if shifted.hasOwnProperty(item.id)
           pos = shifted[item.id]
-          item.root.style.top = "#{pos.row * SIZE}px"
-          item.root.style.left = "#{pos.col * SIZE}px"
-
-    # (Called with GridItemView as "this")
-    fillUpShiftedItems = (shifted) ->
-      shiftedIdsThisDrag = keys(shifted)
-      dragShiftedIds = uniq(dragShiftedIds.concat(shiftedIdsThisDrag))
-      itemsToFill = without(dragShiftedIds, @id)
-      shifted = assign shifted, @parent._packer.shiftUpToFill(itemsToFill)
-      positionItems.call @parent, shifted, [@id]
-      shifted
+          item.row = pos.row
+          item.column = pos.col
+
+          if item.id not in skip
+            item.root.style.top = rowSize(pos.row)
+            item.root.style.left = colSize(pos.col)
+
+
+    # Takes pixel-based x, y, height, width and converts them to grid
+    # coordinates.
+    convertToGridCoords = (x, y, height, width) ->
+      gridWidth = calcGridWidth()
+      x = x / gridWidth * 100
+      width = width / gridWidth * 100
+      top = Math.round(y / parseFloat(rowHeight))
+      left = Math.round(x / parseFloat(colWidth))
+      height = Math.round(height / parseFloat(rowHeight))
+      width = Math.round(width / parseFloat(colWidth))
+      {top, left, height, width}
+

     # (Called with GridItemView as "this")
     onDrag = (e) ->
       {x, y} = e.data
-      leftGridPos = Math.round(x / SIZE)
-      topGridPos = Math.round(y / SIZE)
-      shifted = @parent._packer.moveTo @id, topGridPos, leftGridPos
-      positionShadow.call(@, @id, topGridPos, leftGridPos)
+      {top, left} = convertToGridCoords(x, y)
+      shifted = @parent._packer.moveTo @id, top, left
+      positionShadow.call(@, @id, top, left)
       setGridSize.call @parent
       positionItems.call @parent, shifted, [@id]
       shifted

-    itemView.on 'drag', throttle(onDrag, 100, trailing: false)
-
-    itemView.on 'dragstart', createShadow
-
-    itemView.on 'dragstop', (e) ->
+    onDragStop = (e) ->
       shifted = onDrag.call @, e
-      deleteShadow.call @
       @parent.context.items.forEach (item) ->
         if shifted.hasOwnProperty item.id
           pos = shifted[item.id]
-          item.root.style.top = "#{pos.row * SIZE}px"
-          item.root.style.left = "#{pos.col * SIZE}px"
+          item.root.style.top = rowSize(pos.row)
+          item.root.style.left = colSize(pos.col)
+

     onResize = (e) ->
-      {x, y, width, height} = e.data
-
-      width = Math.round(width / SIZE)
-      height = Math.round(height / SIZE)
-
+      {width, height} = e.data
       left = parseInt(@root.style.left)
-      top = parseInt(@root.style.top)
-      leftGridPos = Math.round(left / SIZE)
-      topGridPos = Math.round(top / SIZE)
-
-      shifted = @parent._packer.moveTo @id, topGridPos, leftGridPos, height, width
-
-      if shadow
-        positionShadow.call(@, @id, topGridPos, leftGridPos, height, width)
-
+      top  = parseInt(@root.style.top)
+      {top, left, width, height} = convertToGridCoords(left, top, height, width)
+      shifted = @parent._packer.moveTo @id, top, left, height, width
+      positionShadow.call(@, @id, top, left, height, width)
       setGridSize.call @parent
-
-      @parent.context.items.forEach (item) =>
-        if item.id is @id then return
-        if shifted.hasOwnProperty item.id
-          pos = shifted[item.id]
-          item.root.style.top = "#{pos.row * SIZE}px"
-          item.root.style.left = "#{pos.col * SIZE}px"
-
+      positionItems.call @parent, shifted, [@id]
       shifted

-    itemView.on 'resize', throttle(onResize, 100, trailing: false)
-    itemView.on 'resizeend', (e) ->
+
+    onResizeEnd = (e) ->
       shifted = onResize.call @, e
       @parent.context.items.forEach (item) ->
         if shifted.hasOwnProperty item.id
           pos = shifted[item.id]
-          item.root.style.top = "#{pos.row * SIZE}px"
-          item.root.style.left = "#{pos.col * SIZE}px"
-          item.root.style.width = "#{pos.width * SIZE}px"
-          item.root.style.height = "#{pos.height * SIZE}px"
-
+          item.root.style.top = rowSize(pos.row)
+          item.root.style.left = colSize(pos.col)
+          item.root.style.width = colSize(pos.width)
+          item.root.style.height = rowSize(pos.height)
+
+    itemView.on 'drag', throttle(onDrag, 300, trailing: true)
+    itemView.on 'resize', throttle(onResize, 100, trailing: false)
+    itemView.on 'resizestart dragstart', createShadow
+    itemView.on 'dragstop resizeend', deleteShadow
+    itemView.on 'dragstop', onDragStop
+    itemView.on 'resizeend', onResizeEnd

     itemView.render()
     setGridSize()


Fix prequalify

Fix prequalify

--- components/inquiry_questionnaire/views/basic_info.coffee
+++ components/inquiry_questionnaire/views/basic_info.coffee
@@ -28,7 +28,7 @@
     @locationSearch = new LocationSearch
       el: @$('.js-location-search')
       autofocus: false
-      required: !@user.get('prequalified')
+      required: @artwork.related().partner.get('pre_qualify')

     @locationSearch.render @user.location()?.cityStateCountry()


adding objects by class name only

adding objects by class name only

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -66,4 +66,4 @@
     loadLevel I.currentLevelName

   reloadLevel: ->
-    self.restartLevel
+    self.restartLevel()


Update integration with `deepzoomtools`

Update integration with `deepzoomtools`

--- app._coffee
+++ app._coffee
@@ -1,12 +1,17 @@
 config = require './config'
-deepzoom = require 'deepzoomtools'
+DeepZoomImage = require 'deepzoomtools'
 express = require 'express'
 fs = require 'fs'
 jade = require 'jade'
 path = require 'path'
 request = require 'request'

+# Deep Zoom settings
+DEFAULT_TILE_SIZE = 254
+DEFAULT_TILE_OVERLAP = 1
+DEFAULT_FORMAT = 'jpg'

+# Constants
 ID = 0
 STATIC_PATH = path.join __dirname, 'public'
 STATIC_URL = '/static'
@@ -75,7 +80,8 @@
         # handler isn't *actually* an async handler, so errors we "throw"
         # here won't propagate to an error response. how to fix? domains?
         try
-            deepzoom.create contentPath, _
+            DeepZoomImage.create _, contentPath, contentPath, DEFAULT_TILE_SIZE,
+              DEFAULT_TILE_OVERLAP, DEFAULT_FORMAT
             return res.json 200, {id, url}
         catch err
             return res.json 500, {error: err?.stack or err}


Fixed fat arrow

Fixed fat arrow

--- game.coffee
+++ game.coffee
@@ -68,8 +68,8 @@
     setTimeout((->cb(diff)), 0)

   detectCollisions: ()->
-    _.each(@bullets, (bullet)=>
-      _.each(@ships, (ship)->
+    _.each(@bullets, (bullet) =>
+      _.each(@ships, (ship) =>
         if (ship.isAlive() && bullet.shipId != ship.id && !bullet.isMarkedForDeletion())
           collisionHappened = Helper.squareDistanceBetweenVectors(bullet.position, ship.position) <= ship.size*ship.size
           if collisionHappened


Fixed call to handle_collisions

Fixed call to handle_collisions

--- game.coffee
+++ game.coffee
@@ -78,7 +78,7 @@
         else if lastpos.direction == Game.NORTH
           player.addToLine(new Position([lastpos.x, lastpos.y - (Game.VELOCITY * elapsed_time)], Game.NORTH, new_time))

-        @handle_collisions(player) if Game.use_collisions
+      @handle_collisions(player) for player in @players if Game.use_collisions

       @old_time = new_time


getFakeTextCallback

getFakeTextCallback

--- autoform-fixtures.coffee
+++ autoform-fixtures.coffee
@@ -1,4 +1,4 @@
-makeText = (len, count) ->
+makeFakeText = (len, count) ->
   len = len || 21
   count = +count
   text = ""
@@ -38,9 +38,15 @@
     i++
   _.uniq(result)

+getFakeText = (fieldName, maxLength) ->
+  if maxLength
+    return makeFakeText(maxLength, Math.round(maxLength / 10))
+  makeFakeText()
+
 AutoForm.Fixtures = {}

-AutoForm.Fixtures.getData = (ss) ->
+AutoForm.Fixtures.getPreData = (ss, getFakeTextCallback) ->
+  getFakeTextCallback = getFakeTextCallback || getFakeText
   result = {}
   schema = ss.schema()
   for k of schema
@@ -70,9 +76,9 @@
       if field.max
         max = field.max
         max = max.call() if typeof max is "function"
-        result[k] = makeText(max, Math.round(max / 10))
+        result[k] = getFakeTextCallback(k, max)
       else
-        result[k] = makeText()
+        result[k] = getFakeTextCallback(k)
       continue
     if field.type.name is "Number"
       min = 0
@@ -108,16 +114,23 @@
     if field.type.name is "Boolean"
       result[k] = !!Math.round(Math.random())
       continue
-  normalizedResult = {}
+  result
+
+AutoForm.Fixtures.normalizeData = (result) ->
+  normalData = {}
   for k of result
     namespace = k.split(".")
     # stupid code, sorry
     if namespace.length is 1
-      normalizedResult[namespace[0]] = result[k]
+      normalData[namespace[0]] = result[k]
     else if namespace.length is 2
-      normalizedResult[namespace[0]][namespace[1]] = result[k]
+      normalData[namespace[0]][namespace[1]] = result[k]
     else if namespace.length is 3
-      normalizedResult[namespace[0]][namespace[1]][namespace[2]] = result[k]
+      normalData[namespace[0]][namespace[1]][namespace[2]] = result[k]
     else
       throw new Error("Current version is support only 3 level of namespace")
-  normalizedResult
+  normalData
+
+AutoForm.Fixtures.getData = (ss, getFakeTextCallback) ->
+  result = AutoForm.Fixtures.getPreData(ss, getFakeTextCallback)
+  AutoForm.Fixtures.normalizeData(result)


Don't render empty katex

Don't render empty katex

--- packages/rocketchat-katex/katex.coffee
+++ packages/rocketchat-katex/katex.coffee
@@ -11,10 +11,9 @@
 		{ opener: '$'  , closer: '$'  , displayMode: false },
 	]

-	# Searches for the first opening delimiter in the string
-	find_opening_delimiter: (str) ->
-		# Search the string for each opening delimiter
-		matches = ({options: o, pos: str.indexOf(o.opener)} for o in @delimiters_map)
+	# Searches for the first opening delimiter in the string from a given position
+	find_opening_delimiter: (str, start) -> # Search the string for each opening delimiter
+		matches = ({options: o, pos: str.indexOf(o.opener, start)} for o in @delimiters_map)
 		positions = (m.pos for m in matches when m.pos >= 0)

 		# No opening delimiters were found
@@ -32,6 +31,9 @@
 	class Boundary
 		length: () ->
 			return @end - @start
+
+		extract: (str) ->
+			return str.substr @start, @length()

 	# Returns the outer and inner boundaries of the latex block starting
 	# at the given opening delimiter
@@ -60,14 +62,16 @@

 	# Searches for the first latex block in the given string
 	find_latex: (str) ->
-		unless str.length and (opening_delimiter_match = @find_opening_delimiter str)?
-			return null
-
-		match = @get_latex_boundaries str, opening_delimiter_match
+		start = 0
+		while (opening_delimiter_match = @find_opening_delimiter str, start++)?

-		match?.options = opening_delimiter_match.options
+			match = @get_latex_boundaries str, opening_delimiter_match

-		return match
+			if match?.inner.extract(str).trim().length
+				match.options = opening_delimiter_match.options
+				return match
+
+		return null

 	# Breaks a message to what comes before, after and to the content of a
 	# matched latex block
@@ -75,7 +79,7 @@
 		before = str.substr 0, match.outer.start
 		after  = str.substr match.outer.end

-		latex = str.substr match.inner.start, match.inner.length()
+		latex = match.inner.extract str
 		latex = s.unescapeHTML latex

 		return { before: before, latex : latex, after : after }
@@ -90,17 +94,18 @@
 			rendered =  "
"
 			rendered += 	"#{e.message}"
 			rendered += "
"
-
+
 		return rendered

 	# Takes a string and renders all latex blocks inside it
 	render: (str) ->
 		result = ''

-		while true
+		loop

 			# Find the first latex block in the string
 			match = @find_latex str
+
 			unless match?
 				result += str
 				break
@@ -111,7 +116,7 @@
 			# the rendered latex content
 			rendered = @render_latex parts.latex, match.options.displayMode
 			result += parts.before + rendered
-
+
 			# Set what comes after the latex block to be examined next
 			str = parts.after


profile supports server side referralProgram

--- src/forms/profile.coffee
+++ src/forms/profile.coffee
@@ -42,8 +42,8 @@
         requestAnimationFrame ()=>
           m.trigger Events.CreateReferralProgram
           @client.referrer.create({
-            program:
-              @data.get 'referralProgram'
+            program:   @data.get 'referralProgram'
+            programId: @data.get 'referralProgram.id'
             userId: res.id,
           }).then((res2)=>
             refrs = [res2]


privatemessage: remove settings button addition from viewAppended

--- client/Social/Activity/views/privatemessage/settingsview.coffee
+++ client/Social/Activity/views/privatemessage/settingsview.coffee
@@ -15,10 +15,6 @@
     @menu = {}

     @addSubView @settings
-
-  viewAppended: ->
-    if KD.checkFlag('super-admin') or KD.isMyChannel @getData()
-      @addSubView @settings

   addMenuItem: (title, callback) -> @menu[title] = {callback}


Select first node in file tree after fetch

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -143,7 +143,8 @@
     if fetchContent and vmItem
       @treeController.expandFolder vmItem, (err)=>
         if err?.name is 'VMNotFoundError'
-          @unmountVm vmName
+          return @unmountVm vmName
+        @treeController.selectNode vmItem
       , yes

   unmountVm:(vmName)->


[Email Confirmation] Create confirm email modal

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -187,3 +187,43 @@
           modal.buttons["Refresh Now"].destroy()

           @utils.wait 2500, -> modal?.destroy()
+
+
+  displayConfirmEmailModal:(name, username)->
+    name or= KD.whoami().profile.firstName
+    message =
+      """
+      Dear #{name},
+
+      Thanks for joining Koding.

+
+      For security reasons, we need to make sure you have activated your account. When you registered, we have sent you a link to confirm your email address, please use that link to be able to continue using Koding.

+
+      Didn't receive the email? Resend confirmation email.

+      """
+
+    modal = new KDModalView
+      title            : "You must confirm your email address!"
+      width            : 500
+      overlay          : yes
+      cssClass         : "new-kdmodal"
+      content          : "
#{Encoder.htmlDecode message}
"
+      buttons          :
+        "Resend Mail"  :
+          style        : "modal-clean-red"
+          callback     : => @resendHandler(modal, username)
+        Dismiss        :
+          style        : "modal-cancel"
+          callback     : => modal.destroy()
+
+    return modal
+
+  resendHandler : (modal, username)->
+
+    KD.remote.api.JEmailConfirmation.resetToken username, (err)=>
+      modal.buttons["Resend Mail"].hideLoader()
+      return KD.showError err if err
+      new KDNotificationView
+        title     : "Check your email"
+        content   : "We've sent you a confirmation mail."
+        duration  : 4500


moved coordinateAxes more to center

moved coordinateAxes more to center

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -94,7 +94,7 @@

 			# rotate the axis mini-map according to the cube's rotation and translate it
 			rotMatrix = cam.getMatrix()
-			rotMatrix[12] = -150 #axisMinimap[0]
+			rotMatrix[12] = -100 #axisMinimap[0]
 			rotMatrix[13] = 0 #axisMinimap[1]
 			rotMatrix[14] = -100


Additional small turbolink / lightbox bugfixing

- Prevent keyboard page navigation while in lightbox using global var disable_keyboard_nav
- Clear keyboard navigation variables on turbonav
- Clean up lightbox.coffee re. codeclimate issues
- Remove doge image centering on people pages, rely on SM
- Remove antifocus.coffee, does nothing anyway

--- _coffee/antifocus.coffee
+++ _coffee/antifocus.coffee
@@ -1,8 +0,0 @@
-noBlur = (e) ->
-  console.log "mup"
-  @blur()
-
-$(document).ready ->
-  $('a').click noBlur
-  $('div').click noBlur
-  $('button').click noBlur


Adding scroll to Quoted Code View

Adding scroll to Quoted Code View

--- lib/elixir-quoted-view.coffee
+++ lib/elixir-quoted-view.coffee
@@ -35,21 +35,22 @@
     matchesEditorElement.setAttribute('mini', true)
     matchesEditorElement.removeAttribute('tabindex')

-    @div class: 'elixir-quoted-view padded', =>
-      @header 'Code', class: 'header'
-      @section class: 'input-block', =>
-        @subview 'codeEditorElement', codeEditorElement
+    @div class: "elixir-quoted-view", style: "overflow: scroll;", =>
+      @div class: 'padded', =>
+        @header 'Code', class: 'header'
+        @section class: 'input-block', =>
+          @subview 'codeEditorElement', codeEditorElement

-      @header 'Quoted form', class: 'header'
-      @section class: 'input-block', =>
-        @subview 'quotedCodeEditorElement', quotedCodeEditorElement
+        @header 'Quoted form', class: 'header'
+        @section class: 'input-block', =>
+          @subview 'quotedCodeEditorElement', quotedCodeEditorElement

-      @header 'Pattern Matching', class: 'header'
-      @section class: 'input-block', =>
-        @subview 'patternEditorElement', patternEditorElement
+        @header 'Pattern Matching', class: 'header'
+        @section class: 'input-block', =>
+          @subview 'patternEditorElement', patternEditorElement

-      @section class: 'input-block matchesEditorSection', =>
-        @subview 'matchesEditorElement', matchesEditorElement
+        @section class: 'input-block matchesEditorSection', =>
+          @subview 'matchesEditorElement', matchesEditorElement

   constructor: ({@code, @quotedCode}) ->
     super


Temporary async issue avoidence Fix #2

Temporary async issue avoidence Fix #2

--- lib/ethereum-interface.coffee
+++ lib/ethereum-interface.coffee
@@ -129,10 +129,19 @@
                 that.compiled = web3.eth.compile.solidity(source)
                 # Clean View before creating
                 that.atomSolidityView.destroyCompiled()
+                console.log that.compiled
+
                 # Create inpus for every contract
                 for contractName of that.compiled
                     # Get estimated gas
-                    estimatedGas = 0
+                    estimatedGas = web3.eth.estimateGas { from: web3.eth.defaultAccount, data: that.compiled[contractName].code, gas: 1000000 }
+                    ###
+                    # TODO: Use asynchronous call
+                    web3.eth.estimateGas({from: '0xmyaccout...', data: "0xc6888fa1fffffffffff…..", gas: 500000 }, function(err, result){
+                      if(!err && result !=== 500000) { …  }
+                     });
+                    ###
+
                     # contractName is the name of contract in JSON object
                     bytecode = that.compiled[contractName].code
                     # Get contract  abi
@@ -142,17 +151,9 @@
                     for abiObj of ContractABI
                         if ContractABI[abiObj].type is "constructor" && ContractABI[abiObj].inputs.length > 0
                             inputs = ContractABI[abiObj].inputs
-
-                    # Finally check gas estimate and set view
-                    web3.eth.estimateGas { from: web3.eth.defaultAccount, data: that.compiled[contractName].code, gas: 1000000 }, (error, callback) ->
-                        if !error and callback != 1000000
-                            console.log callback
-                            estimatedGas = callback
-                        else
-                            console.log error
-                            estimatedGas = 1000000
-                        # Create View
-                        that.atomSolidityView.setContractView(contractName, bytecode, ContractABI, inputs, estimatedGas)
+                    # Create view
+                    that.atomSolidityView.setContractView(contractName, bytecode, ContractABI, inputs, estimatedGas)
+

                 # Show contract code
                 if not that.modalPanel.isVisible()


save the votes

save the votes

--- app/controllers/toilet/item.coffee
+++ app/controllers/toilet/item.coffee
@@ -14,4 +14,5 @@
       model.save()
       .then ->
         toastr.warning "Downvoted!"
+
 `export default ToiletItemController`


Track scroll position to select correct panel.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -1982,6 +1982,22 @@
         rgb = $("[data-theme=#{feedbin.data.theme}]").css("backgroundColor")
         feedbin.setNativeTitleColor(rgb)

+    scrollLeft: ->
+      entries = $('.entries-column')
+      article = $('.entry-column')
+      $('.app-wrap').on 'scroll', (event) ->
+        position = $(@).prop("scrollLeft")
+
+        entriesPosition = entries.prop("offsetLeft")
+        articlePosition = article.prop("offsetLeft")
+
+        if position == 0
+          feedbin.panel = 1
+        else if position > entriesPosition - 2 && position < entriesPosition + 2
+          feedbin.panel = 2
+        else if position > articlePosition - 2 && position < articlePosition + 2
+          feedbin.panel = 3
+
     statsBarTouched: ->
       $(document).on 'feedbin:native:statusbartouched', (event) ->
         feedbin.scrollToTop()


Change today button's behavior for deal editor.

--- app/assets/javascripts/deals.js.coffee
+++ app/assets/javascripts/deals.js.coffee
@@ -147,8 +147,19 @@
   $('.for_deal_editor #calendar').change (event, month) ->
     $('input#date_year').val(month.year)
     $('input#date_month').val(month.month)
-    $('input#date_day').val('') # TODO: 今日のある月なら、今日を入れてもよい
+    today = new Date()
+    if today.getFullYear() == month.year && today.getMonth() + 1 == month.month
+      day = today.getDate()
+    else
+      day = ''
+    $('input#date_day').val(day)
     url = $('#day_navigator').data('url')
     url = url.replace('_YEAR_', month.year)
     url = url.replace('_MONTH_', month.month)
     $('#day_navigator_frame').load(url)
+
+  # 今日ボタン （登録フォーム）
+  $('.for_deal_editor #today').click (event) ->
+    today = new Date
+    document.calendar.selectMonth(today.getFullYear(), today.getMonth()+1, true)
+    event.preventDefault()


Fix dragndrop areas to work in new dynamically added rows also in the edit members view

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -8,6 +8,60 @@
   $("input:checkbox, input:radio, input:file, input:text, textarea, submit", node).uniform()

 initOrganPage = (delegateFor) ->
+  sameContext = (droppable, draggable) ->
+    getContextId = (elem) ->
+      call = elem.parents('.call-for-application:first')
+      if call.length > 0
+        call.find('form.edit_call').attr('id')
+      else
+        elem.parents('.organ-members').attr('class')
+    droppableId = getContextId(droppable)
+    draggableId = getContextId(draggable)
+    droppableId? and draggableId? and droppableId == draggableId
+
+  alignApplicants = (droppable) ->
+    applicants = droppable.parents('.call-for-application').find('.applicants')
+    applicants.find('.member-card:even').removeClass('no-margin')
+    applicants.find('.member-card:odd').addClass('no-margin')
+
+  saveSelection = (droppable, draggable) ->
+    form = draggable.find('form')
+    member = form.find('.value_member')
+    deputy = form.find('.value_deputy')
+    member.val ''
+    deputy.val ''
+    if droppable.hasClass 'deputy'
+      member.val droppable.prevAll('.member:first').find('.member-card').data('id')
+    else if droppable.hasClass 'member'
+      deputy.val droppable.nextAll('.deputy:first').find('.member-card').data('id')
+    form.find('.value_position').val droppable.data('name')
+    superagent.post(form.attr 'action').
+      type('form-data').
+      send(form.serialize()).
+      end()
+
+  initDroppableSlot = (elements) ->
+    elements.droppable
+      activeClass: 'highlight-drop-area'
+      accept: (draggable) ->
+        return false unless draggable.hasClass 'member-card'
+        return false if $(@).find('.member-card').length > 0
+        return false if $(@).hasClass('no-deputy')
+        sameContext $(@), draggable
+      drop: (event, ui) ->
+        droppable = $(@)
+        droppable.prepend(ui.draggable)
+        ui.draggable.position
+          my: "top left"
+          at: "top left"
+          of: droppable
+        saveSelection(droppable, ui.draggable)
+        alignApplicants(droppable)
+
+  initDraggableCard = (elements) ->
+    elements.draggable
+      revert: 'invalid'
+
   initToggleArchived = () ->
     toggleArchived = (visible) ->
       $('.archived-call-for-applications').toggle visible
@@ -42,59 +96,27 @@
       superagent.post(form.attr 'action').
         type('form-data').
         send(form.serialize()).
-        end (response) ->
-          findOrCreateSlot = () ->
-            slot = hallopeds.find('.member-card-empty.member:last')
-            if slot.find('.member-card').length == 0
-              slot
+        end((response) ->
+          findOrCreateNewRow = () ->
+            lastRow = hallopeds.find('.empty-row:last')
+            if lastRow.find('.member .member-card').length == 0
+              lastRow
             else
-              newRow = hallopeds.find('.empty-row:last').clone()
+              newRow = lastRow.clone()
               newRow.find('.member-card').remove()
-              hallopeds.find('.empty-row:last').append newRow
-              newRow.find('.member-card-empty.member')
-          lastMemberSlot = findOrCreateSlot()
-          placeHolder = lastMemberSlot.html()
-          lastMemberSlot.html(response.text).append placeHolder
-          lastMemberSlot.find('.member-card').draggable
-            revert: 'invalid'
+              lastRow.append newRow
+              newRow
+          newMemberRow = findOrCreateNewRow()
+          newSlot = newMemberRow.find('.member-card-empty.member')
+          placeHolder = newSlot.html()
+          newSlot.html(response.text).append placeHolder
+          initDraggableCard newMemberRow.find('.member-card')
+          initDroppableSlot newMemberRow.find('.member-card-empty')
+        )
       return false

   initCallSelectionDragNDrops = () ->
-    sameContext = (droppable, draggable) ->
-      getContextId = (elem) ->
-        call = elem.parents('.call-for-application:first')
-        if call.length > 0
-          call.find('form.edit_call').attr('id')
-        else
-          elem.parents('.organ-members').attr('class')
-      droppableId = getContextId(droppable)
-      draggableId = getContextId(draggable)
-      droppableId? and draggableId? and droppableId == draggableId
-
-
-    saveSelection = (droppable, draggable) ->
-      form = draggable.find('form')
-      member = form.find('.value_member')
-      deputy = form.find('.value_deputy')
-      member.val ''
-      deputy.val ''
-      if droppable.hasClass 'deputy'
-        member.val droppable.prevAll('.member:first').find('.member-card').data('id')
-      else if droppable.hasClass 'member'
-        deputy.val droppable.nextAll('.deputy:first').find('.member-card').data('id')
-      form.find('.value_position').val droppable.data('name')
-      superagent.post(form.attr 'action').
-        type('form-data').
-        send(form.serialize()).
-        end()
-
-    alignApplicants = (droppable) ->
-      applicants = droppable.parents('.call-for-application').find('.applicants')
-      applicants.find('.member-card:even').removeClass('no-margin')
-      applicants.find('.member-card:odd').addClass('no-margin')
-
-    $('.call-for-application.open .member-card, .edit-members .member-card').draggable
-      revert: 'invalid'
+    initDraggableCard $('.call-for-application.open .member-card, .edit-members .member-card')

     $('.call-for-application.open .applicants').droppable
       activeClass: 'highlight-return-area'
@@ -107,22 +129,7 @@
         return false unless draggable.hasClass 'member-card'
         sameContext $(@), draggable

-    $('.call-for-application.open .member-card-empty, .edit-members .member-card-empty').droppable
-      activeClass: 'highlight-drop-area'
-      accept: (draggable) ->
-        return false unless draggable.hasClass 'member-card'
-        return false if $(@).find('.member-card').length > 0
-        return false if $(@).hasClass('no-deputy')
-        sameContext $(@), draggable
-      drop: (event, ui) ->
-        droppable = $(@)
-        droppable.prepend(ui.draggable)
-        ui.draggable.position
-          my: "top left"
-          at: "top left"
-          of: droppable
-        saveSelection(droppable, ui.draggable)
-        alignApplicants(droppable)
+    initDroppableSlot $('.call-for-application.open .member-card-empty, .edit-members .member-card-empty')

   initToggleArchived()
   initCallSelectionDragNDrops()


sad :( fix for turbolinks issue

sad :( fix for turbolinks issue

--- app/assets/javascripts/line_items.coffee
+++ app/assets/javascripts/line_items.coffee
@@ -7,6 +7,7 @@
   return

 this.updateQuantity = ->
+  return if $(".store.index").length > 0
   line_item_quantity_e = document.getElementById('line_item_quantity')
   url = line_item_quantity_e.dataset.url
   if line_item_quantity_e.value.localeCompare('+10') == 0


made new item behavior more logical

made new item behavior more logical

--- app/assets/javascripts/lists.js.coffee
+++ app/assets/javascripts/lists.js.coffee
@@ -423,15 +423,21 @@
     itemView = new App.backbone.ItemView(model: item)
     itemView.render()
     if selection
-      if placement == "indent"
-        item.insertAfter selection
-        item.indent true
-      else if placement == "previous"
+      if placement == "previous"
         item.insertBefore selection
         $(item.view.el).insertBefore selection.view.el
-      else # insert after
-        item.insertAfter selection
-        $(item.view.el).insertAfter selection.view.el
+      else
+        if selection.children.size() > 0
+          selection = selection.children.first()
+          item.insertBefore selection
+          $(item.view.el).insertBefore selection.view.el
+        else
+          if placement == "indent"
+            item.insertAfter selection
+            item.indent()
+          else # insert after
+            item.insertAfter selection
+            $(item.view.el).insertAfter selection.view.el
     else
       $(@el).append itemView.el
     item.select()


recenter series 1 labels

recenter series 1 labels

--- app/assets/javascripts/zvg/column.coffee
+++ app/assets/javascripts/zvg/column.coffee
@@ -358,6 +358,7 @@
       .attr('transform', '')
       .text((d) -> d.key)
       .attr('x', (d,i) => @series1x[i] + @series1width[i]/2)
+      .style('text-anchor', null)
     @series_1_labels.exit().remove()
     @constructSeries1LabelMap()
     @rotateSeries1Labels() if @detect_overlaps(@series1LabelMap)


jQuery's response datatype guessing works well enough.

--- app/assets/javascripts/store.coffee
+++ app/assets/javascripts/store.coffee
@@ -72,7 +72,6 @@
       type: method,
       contentType: 'application/json; charset=UTF-8'
       data: JSON.stringify data
-      dataType: 'json'
     ).done((json) =>
       if json
         @get('serializer').deserializeOne(json, type, model)


add high contrast color options for highlighter labels

--- app/classifier/tasks/generic-editor.cjsx
+++ app/classifier/tasks/generic-editor.cjsx
@@ -121,15 +121,17 @@
                     <div className="workflow-choice-setting" >
                       <AutoSave resource={@props.workflow} >
                         Color{' '}
-                        <select name="#{@props.taskPrefix}.#{choicesKey}.#{index}.color" value={choice.color} onChange={handleChange}>
-                          <option value="#ff6639">Red</option>
-                          <option value="#ffa539">Yellow</option>
-                          <option value="#38b978">Green</option>
-                          <option value="#43bcfd">Blue</option>
-                          <option value="#5364fd">Violet</option>
-                          <option value="#00ffff">Cyan</option>
-                          <option value="#000000">Black</option>
-                          <option value="#ffffff">White</option>
+                        <select style={{background: choice.color}} name="#{@props.taskPrefix}.#{choicesKey}.#{index}.color" value={choice.color} onChange={handleChange}>
+                          <option style={{background: "#ff6639"}} value="#ff6639">Red</option>
+                          <option style={{background: "#ffa539"}} value="#ffa539">Orange</option>
+                          <option style={{background: "#FC6399"}} value="#FC6399">Pink</option>
+                          <option style={{background: "#C9F227"}} value="#C9F227">Yellow Green</option>
+                          <option style={{background: "#38b978"}} value="#38b978">Green</option>
+                          <option style={{background: "#00FF7F"}} value="#00FF7F">Seafoam</option>
+                          <option style={{background: "#19B5FE"}} value="#19B5FE">Blue</option>
+                          <option style={{background: "#DCC6E0"}} value="#DCC6E0">Light Purple</option>
+                          <option style={{background: "#7E8BFE"}} value="#7E8BFE">Purple</option>
+                          <option style={{background: "#00ffff"}} value="#00ffff">Cyan</option>
                         </select>
                       </AutoSave>
                     </div>


deprecated application store in ember

deprecated application store in ember

--- app/assets/javascripts/store.js.coffee
+++ app/assets/javascripts/store.js.coffee
@@ -1,7 +1,7 @@
 # http://emberjs.com/guides/models/#toc_store
 # http://emberjs.com/guides/models/pushing-records-into-the-store/

-Theoj.Store = DS.Store.extend({
+Theoj.ApplicationStore = DS.Store.extend({

 })


Pass extra parameter

Pass extra parameter

--- src/gui/ask/index.coffee
+++ src/gui/ask/index.coffee
@@ -9,12 +9,12 @@
 current = 0

 # Install listener to .ask
-one = (span, fn)->
+one = (span, fn, data)->
   for a, i in $ 'a', span
     a.onclick = do (i)-> ->
       setTimeout ->
         current = span
-        fn i
+        fn i, data
         current = 0
       false
   return
@@ -22,11 +22,11 @@
 # Install listeners for many .ask
 exports.x = (dom, fn, array)->
   for span in $ 'span', dom when 'ask' == span.className
-    one span, do (data = array.shift())-> (i)->
-      fn i, data
+    one span, fn, array.shift()
+  return

 # Show "popup"
-exports.$ = (options, title)->
+exports.$ = (options, title, fn, data)->
   return unless current
   current.className += ' hide'
   append current.parentNode, t options, title


Forgot to commit this method name change.

--- core/app/backbone/views/tour/tour_interesting_users_view.coffee
+++ core/app/backbone/views/tour/tour_interesting_users_view.coffee
@@ -37,7 +37,7 @@

   updateShuffledCollection: ->
     @shuffledCollection.reset @collection.shuffle()
-    @resizeListView()
+    @resizeScrollingInner()
     @updateButtonStates()
     @listView().children.first()?.showAuthorityPopover()


Removed leftover code

Removed leftover code

--- core/app/backbone/views/tour/tour_interesting_users_view.coffee
+++ core/app/backbone/views/tour/tour_interesting_users_view.coffee
@@ -17,7 +17,6 @@
     finish: '.js-finish'
     scrollingInner: '.js-scrolling-inner'

-  userListItemWidth: 200 + 2*20 # width of .tour-interesting-user including margin
   numberOfUsersInX: 4
   numberOfUsersInY: 2


Handle if two updates cancel each other

Handle if two updates cancel each other

--- app/coffee/ConversionManager.coffee
+++ app/coffee/ConversionManager.coffee
@@ -40,7 +40,10 @@
 							compressedUpdates = [lastCompressedUpdate]
 							for rawUpdate in rawUpdates
 								lastCompressedUpdate = compressedUpdates.pop()
-								compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate
+								if lastCompressedUpdate?
+									compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate
+								else
+									compressedUpdates.push rawUpdate
 							ConversionManager.removeLatestCompressedUpdate doc_id, (error) ->
 								return callback(error) if error?
 								callback null, compressedUpdates


Fix confirm on disabled edit mode lighbox in wiki pages

--- app/coffee/modules/wiki/main.coffee
+++ app/coffee/modules/wiki/main.coffee
@@ -292,12 +292,13 @@
             $scope.$apply(cancelEdition)

         $el.on "keydown", "textarea", (event) ->
-            if event.keyCode == 27
-                $scope.$applyAsync () ->
-                    confirmTitle = $translate.instant("COMMON.CONFIRM_CLOSE_EDIT_MODE")
-                    $confirm.ask(confirmTitle).then (askResponse) ->
-                        cancelEdition()
-                        askResponse.finish()
+            return if event.keyCode != 27
+            $scope.$applyAsync () ->
+                title = $translate.instant("COMMON.CONFIRM_CLOSE_EDIT_MODE_TITLE")
+                message = $translate.instant("COMMON.CONFIRM_CLOSE_EDIT_MODE_MESSAGE")
+                $confirm.ask(title, null, message).then (askResponse) ->
+                    cancelEdition()
+                    askResponse.finish()

         $scope.$watch $attrs.ngModel, (wikiPage) ->
             return if not wikiPage


Improve resource loading on backlog.

Improve resource loading on backlog.

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -56,12 +56,17 @@

         promise = @.loadInitialData()

-        promise.then () =>
+        # On Success
+        promise.then =>
             @appTitle.set("Backlog - " + @scope.project.name)
             tgLoader.pageLoaded()

-        promise.then null, =>
-            console.log "FAIL"
+        # On Error
+        promise.then null, (xhr) =>
+            if xhr and xhr.status == 404
+                @location.path("/not-found")
+                @location.replace()
+            return @q.reject(xhr)

         @scope.$on("usform:bulk:success", @.loadUserstories)
         @scope.$on("sprintform:create:success", @.loadSprints)
@@ -109,8 +114,8 @@

             @scope.filtersQ = ""

-            _.forEach [selectedTags, selectedStatuses], (filterGrp) =>
-                _.forEach filterGrp, (item) =>
+            _.each [selectedTags, selectedStatuses], (filterGrp) =>
+                _.each filterGrp, (item) =>
                     filters = @scope.filters[item.type]
                     filter = _.find(filters, {id: taiga.toString(item.id)})
                     filter.selected = false
@@ -119,32 +124,25 @@

             @.loadUserstories()

-            # @.filterVisibleUserstories()
-            # @rootscope.$broadcast("filters:loaded", @scope.filters)
-
     loadUserstories: ->
-        @scope.urlFilters = @.getUrlFilters()
-
-        @scope.httpParams = {}
-        for name, values of @scope.urlFilters
-            @scope.httpParams[name] = values
-
+        @scope.httpParams = @.getUrlFilters()
         @rs.userstories.storeQueryParams(@scope.projectId, @scope.httpParams)

-        @.refreshTagsColors().then =>
-            @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams).then (userstories) =>
-                @scope.userstories = userstories
-
-                @.generateFilters()
-                @.filterVisibleUserstories()
-
-                @rootscope.$broadcast("filters:loaded", @scope.filters)
-                # The broadcast must be executed when the DOM has been fully reloaded.
-                # We can't assure when this exactly happens so we need a defer
-                scopeDefer @scope, =>
-                    @scope.$broadcast("userstories:loaded")
-
-                return userstories
+        promise = @.refreshTagsColors().then =>
+            return @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams)
+
+        return promise.then (userstories) =>
+            @scope.userstories = userstories
+            @.generateFilters()
+            @.filterVisibleUserstories()
+
+            @rootscope.$broadcast("filters:loaded", @scope.filters)
+            # The broadcast must be executed when the DOM has been fully reloaded.
+            # We can't assure when this exactly happens so we need a defer
+            scopeDefer @scope, =>
+                @scope.$broadcast("userstories:loaded")
+
+            return userstories

     loadBacklog: ->
         return @q.all([
@@ -169,13 +167,9 @@
             @scope.projectId = data.project
             return data

-        promise.then null, =>
-            @location.path("/not-found")
-            @location.replace()
-
-        return promise.then(=> @.loadProject())
-                      .then(=> @.loadUsersAndRoles())
-                      .then(=> @.loadBacklog())
+        return promise.then(=> @loadProject())
+                      .then(=> @q.all([@.loadUsersAndRoles(),
+                                       @.loadBacklog()]))

     filterVisibleUserstories: ->
         @scope.visibleUserstories = []


Simplify method storing function creation per @janpaul123's suggestion

--- app/js/loader/loader_common.coffee
+++ app/js/loader/loader_common.coffee
@@ -1,21 +1,22 @@
 unsupported_browser = document.documentMode < 9

 window.FACTLINK = {}
+
 # Create proxy object that stores all calls
 # proxies calls from external content page to the js-library "jail" iframe's "FactlinkJailRoot"..
 methods = 'on,triggerClick,startHighlighting,highlightAdditionalFactlinks,startAnnotating,stopAnnotating,showLoadedNotification,scrollTo,openFactlinkModal,initializeFactlinkButton'.split(',')

 storedMethodCalls = []

+storedMethodFactory = (name) -> ->
+  storedMethodCalls.push(name: name, arguments: arguments) && undefined
+
 for name in methods
-  do (name) ->
-    window.FACTLINK[name] =
-      if unsupported_browser
-        ->
-      else
-        ->
-          storedMethodCalls.push {name: name, arguments: arguments}
-          return
+  window.FACTLINK[name] =
+    if unsupported_browser
+      ->
+    else
+      storedMethodFactory name

 window.FACTLINK_START_LOADER = ->
   return if unsupported_browser


Boyscouting: simplify core_loaded usage

Boyscouting: simplify core_loaded usage

--- app/js/loader/loader_common.coffee
+++ app/js/loader/loader_common.coffee
@@ -70,11 +70,8 @@

     root.jail_ready_promise.resolve()

-    root.core_loaded_promise
-    .then( ->
-        root.perf.add_timing_event 'core_loaded'
-    )
-    .then ->
+    root.core_loaded_promise.then ->
+      root.perf.add_timing_event 'core_loaded'
       #called from jail-iframe when core iframe is ready.
       for name in methods
         do (name) ->
@@ -93,6 +90,7 @@
     if document.body
       whenHasBody()
     else
+      console.log('skipped init attempt ' + i)
       setTimeout tryToInit(i+1), i

   tryToInit(1)()


Update mini-course-content.coffee

Update mini-course-content.coffee

--- app/lib/mini-course-content.coffee
+++ app/lib/mini-course-content.coffee
@@ -14,7 +14,7 @@
     course_number: 2
     material:
       title: "How a lightcurve is formed"
-      text: "
A lightcurve is formed by taking regular observations of a star’s brightness and plotting them over time. A fraction of planetary systems have an alignment that means, when they are viewed from Earth, the planet will pass in front of its host star as it orbits. When it does so, it blocks some of the light from the star. This is called a transit. Below you can see an example of a transit lightcurve.
"
+      text: "
A lightcurve is formed by taking regular observations of a star’s brightness and plotting them over time. A fraction of planetary systems have an alignment that means, when they are viewed from Earth, the planet will pass in front of its host star as it orbits. When it does so, it blocks some of the light from the star. This is called a transit. Above you can see an example of a transit lightcurve.
"
       figure: "./images/mini-course/ph-transit-070214.gif"
       figure_credits: ""
   }
@@ -34,7 +34,7 @@

       """
       figure: "./images/mini-course/light-curve-period.png"
-      figure_credits: "Image courtesy of NASA"
+      figure_credits: "Image credit: NASA"
   }
   {
     course_number: 4
@@ -59,7 +59,7 @@
       title: "False-positives - Variable stars"
       text: """

Not all changes in a star’s brightness are caused by transiting planets. There are a few other things we have to rule out first. These are called false-positives.

-
Some stars intrinsically vary in brightness on a level that is comparable to that of a planetary transit. However, if the star is observed for a long period of time, the pattern of the variation can be easily differentiated from that of a planet transit. These variations normally happen over a much longer time than a planet transit (see example lightcurve below).

+
Some stars intrinsically vary in brightness on a level that is comparable to that of a planetary transit. However, if the star is observed for a long period of time, the pattern of the variation can be easily differentiated from that of a planet transit. These variations normally happen over a much longer time than a planet transit (see example lightcurve above).

         """
       figure: "./images/mini-course/5-FalsePositives.png"
       figure_credits: ""
@@ -72,7 +72,7 @@
     material:
       title: "False-positives - Eclipsing binary stars"
       text: """
-        One of the most common false-positives we have to deal with are eclipsing binary stars. This is when another star is transiting, rather than a planet. These tend to produce much deeper transits due to stars being much larger than planets, and they also show secondary eclipses, when the smaller star passes behind the larger star (see the diagram below). However, there a few ways in which an eclipsing binary system can mimic a transiting planet. Firstly, if the second star is only partially transiting the disc of the first star then the amount of light blocked is smaller and the depth of the transit may be similar to that caused by a planet. This is called a ‘grazing eclipsing binary’.
+        One of the most common false-positives we have to deal with are eclipsing binary stars. This is when another star is transiting, rather than a planet. These tend to produce much deeper transits due to stars being much larger than planets, and they also show secondary eclipses, when the smaller star passes behind the larger star (see the diagram above). However, there a few ways in which an eclipsing binary system can mimic a transiting planet. Firstly, if the second star is only partially transiting the disc of the first star then the amount of light blocked is smaller and the depth of the transit may be similar to that caused by a planet. This is called a ‘grazing eclipsing binary’.
         """
       figure: "./images/mini-course/slide-6.jpg"
       figure_credits: ""
@@ -86,7 +86,7 @@
       title: "False-positives - Eclipsing binary stars"
       text: """

The way that the lightcurves are produced means that sometimes light from other stars (not just the target star) is included. This is called ‘blending’, and it leads to the light of the target being diluted. This means if the target is an eclipsing binary, the dips in light may appear small enough to mimic a planet.

-
Also, if one of the background stars is actually an eclipsing binary it may appear like there are smaller transits occurring on the target star, when in fact there is nothing there at all. The right-hand panel of the diagram below shows an example of this scenario.

+
Also, if one of the background stars is actually an eclipsing binary it may appear like there are smaller transits occurring on the target star, when in fact there is nothing there at all. The right-hand panel of the diagram above shows an example of this scenario.

         """
       figure: "./images/mini-course/slide-7.jpg"
       figure_credits: "Image credit: NASA/Wendy Stenzel"
@@ -98,7 +98,7 @@
     material:
       title: "Other features in the lightcurves - flares"
       text: """
-        Stellar flares may be spotted in the lightcurves on Planet Hunters. They are characterised by rapid spikes in brightness. These are large explosions from the surface of the star, just like the solar flares that we observe on our star the Sun. Below is an example of a lightcurve with flare features.
+        Stellar flares may be spotted in the lightcurves on Planet Hunters. They are characterised by rapid spikes in brightness. These are large explosions from the surface of the star, just like the solar flares that we observe on our star the Sun. Above is an example of a lightcurve with flare features.
         """
       figure: "./images/mini-course/7-Flares.png"
       figure_credits: ""
@@ -110,10 +110,10 @@
     material:
       title: "A brief history of Exoplanets"
       text: """
-        The idea of planets orbiting other stars in our galaxy has been around for hundreds of years (in fact 16th century Italian astronomer Giordano Bruno was burnt at the stake at least in part for his beliefs on the matter!). However, it was not until 1992 that the first exoplanet was confirmed. It was found orbiting a pulsar, the rapidly-spinning, dense core of a dead star. The first confirmed planet orbiting a Sun-like star, 51 Pegesi b, was announced in 1995. Since then over one thousand exoplanets have been confirmed and many more candidates identified. The graph below shows the distribution of planet candidates discovered by the Kepler mission as of November 2013.
+        The idea of planets orbiting other stars in our galaxy has been around for hundreds of years (in fact 16th century Italian astronomer Giordano Bruno was burnt at the stake at least in part for his beliefs on the matter!). However, it was not until 1992 that the first exoplanet was confirmed. It was found orbiting a pulsar, the rapidly-spinning, dense core of a dead star. The first confirmed planet orbiting a Sun-like star, 51 Pegesi b, was announced in 1995. Since then over one thousand exoplanets have been confirmed and many more candidates identified. The graph above shows the distribution of planet candidates discovered by the Kepler mission as of November 2013.
         """
       figure: "./images/mini-course/8-size-candidates.jpg"
-      figure_credits: ""
+      figure_credits: "Image credit: NASA"

   }

@@ -122,7 +122,7 @@
     material:
       title: "The first Planet Hunters planet"
       text: """
-        The first confirmed planet discovered by volunteers, like you, on Planet Hunters was PH1b (also known as Kepler-64b). It is a Neptune-sized giant planet with a radius 6 times greater than that of the Earth. The really interesting thing about PH1b is that it’s a circumbinary planet, meaning it orbits around two stars! Those two stars are also in orbit around another binary star system, which makes PH1b the first planet ever discovered in a quadruple star system. Below is an artist’s impression of the system.
+        The first confirmed planet discovered by volunteers, like you, on Planet Hunters was PH1b (also known as Kepler-64b). It is a Neptune-sized giant planet with a radius 6 times greater than that of the Earth. The really interesting thing about PH1b is that it’s a circumbinary planet, meaning it orbits around two stars! Those two stars are also in orbit around another binary star system, which makes PH1b the first planet ever discovered in a quadruple star system. Above is an artist’s impression of the system.
         """
       figure: "./images/mini-course/9-FirstPlanet.png"
       figure_credits: "Image Credit: H. Giguere, M. Giguere"
@@ -134,7 +134,7 @@
     material:
       title: "Planet Hunters 2b"
       text: """
-        PH2b is the second planet discovered by volunteers on Planet Hunters. It is a Jupiter-sized, gas giant planet in orbit around a star very similar to our Sun. It’s orbit is only 17% smaller than that of the Earth, putting it in a region known as the habitable zone, the distance from a star where the conditions may be right for life. You will learn more about the habitable zone later. Below is an artist’s impression of the view from a hypothetical habitable moon in orbit around PH2b.
+        PH2b is the second planet discovered by volunteers on Planet Hunters. It is a Jupiter-sized, gas giant planet in orbit around a star very similar to our Sun. It’s orbit is only 17% smaller than that of the Earth, putting it in a region known as the habitable zone, the distance from a star where the conditions may be right for life. You will learn more about the habitable zone later. Above is an artist’s impression of the view from a hypothetical habitable moon in orbit around PH2b.
         """
       figure: "./images/mini-course/10-2b.png"
       figure_credits: "Image Credit: H. Giguere, M. Giguere"
@@ -146,10 +146,10 @@
     material:
       title: "Kepler-90"
       text: """
-        The third confirmed exoplanet discovered by volunteers on Planet Hunters is in a system known as Kepler-90 and now has the designation Kepler-90h. This system already had six confirmed exoplanets, but the discovery of the seventh buy Planet Hunters volunteers made it the most populous planetary system known, apart from our own. The red circle in the diagram below shows the orbit of Kepler-90h in the system.
+        The third confirmed exoplanet discovered by volunteers on Planet Hunters is in a system known as Kepler-90 and now has the designation Kepler-90h. This system already had six confirmed exoplanets, but the discovery of the seventh buy Planet Hunters volunteers made it the most populous planetary system known, apart from our own. The red circle in the diagram above shows the orbit of Kepler-90h in the system.
         """
       figure: "./images/mini-course/11-kepler90.png"
-      figure_credits: "NASA"
+      figure_credits: ""

   }

@@ -161,7 +161,7 @@
         Stars are mostly made of hydrogen, which is also true for some gas giant planets such as Jupiter. The difference is that stars are massive enough to ignite nuclear fusion of hydrogen into helium in their cores. The smallest stars are roughly 75 times more massive than Jupiter, and the largest planets are about 13 times more massive than Jupiter.
         """
       figure: "./images/mini-course/12-star-v-planet.jpg"
-      figure_credits: ""
+      figure_credits: "Image credit: NASA"

   }

@@ -173,7 +173,7 @@
         A star spends the majority of its active life burning the hydrogen in its core. Stars that are in this phase are known as main sequence stars. Main sequence stars are split into 7 main groups named O, B, A, F, G, K and M in order from hottest to coolest. Our Sun is a G-type star but the vast majority of stars in our galaxy are smaller, cooler M-type stars.
         """
       figure: "./images/mini-course/13-spectral-chart.jpg"
-      figure_credits: ""
+      figure_credits: "Image credit: NASA"

   }

@@ -258,7 +258,7 @@
     material:
       title: "How else can we discover planets? - Astrometry"
       text: """
-        As described before for the radial velocity method, stars are pulled around by the gravitation force from their orbiting planets (see animation below). Therefore, if the position of a star can be measured accurately, this movement can be observed and the existence of the planet can be confirmed. These movements are extremely small and to-date very few exoplanet candidates have been discovered in this way, however the recently launch Gaia spacecraft plans to utilise this method as part of its mission.
+        As described before for the radial velocity method, stars are pulled around by the gravitation force from their orbiting planets (see animation above). Therefore, if the position of a star can be measured accurately, this movement can be observed and the existence of the planet can be confirmed. These movements are extremely small and to-date very few exoplanet candidates have been discovered in this way, however the recently launch Gaia spacecraft plans to utilise this method as part of its mission.
         """
       figure: "./images/mini-course/slide-21.gif"
       figure_credits: ""
@@ -271,7 +271,7 @@
       title: "How else can we discover planets? - Direct Imaging"
       text: """

The separation and extreme difference in brightness between exoplanets and their host stars makes it very difficult to image them directly. However, there are a handful of exoplanet candidates  that have been discovered this way. All of them have very large radii and lie in distant orbits.

-
Below is an image showing the directly imaged exoplanet candidate orbiting in the debris disk around the star Formalhaut.

+
Above is an picture showing the directly imaged exoplanet candidate orbiting in the debris disk around the star Formalhaut.

         """
       figure: "./images/mini-course/slide-22.jpg"
       figure_credits: "Image credit: NASA"


archive user from group was broken

archive user from group was broken

--- app/models/membership-model.coffee
+++ app/models/membership-model.coffee
@@ -16,4 +16,4 @@
       !@member().isConfirmed()

     cancel: ->
-      @remote.postMember(@id, 'cancel')
+      @remote.postMember(@id, 'archive')


Should add relationships to blueprint data relationships.

--- app/controllers/items/update.coffee
+++ app/controllers/items/update.coffee
@@ -8,6 +8,20 @@
       blueprint = model.blueprint

       item.set 'blueprint', blueprint
-      item.save()
+
+      promises = []
+      item.eachRelationship (field, relationship) =>
+        field_key = "#{field}_id"
+        if item.get field_key
+          id = item.get field_key
+          type_key = relationship.type.typeKey
+
+          promise = @store.findById(type_key, id)
+          .then (result) ->
+            item.set field, result
+          promises.push promise
+
+      Ember.RSVP.allSettled(promises).then ->
+        item.save()

 `export default ItemsUpdateController`


fix indenting

fix indenting

--- assets/js/teacher.coffee
+++ assets/js/teacher.coffee
@@ -46,14 +46,16 @@

#{id}

                 #{prog}
-
").appendTo(current_row).each(() ->
-               console.log "calling click toggle listener on id #{id}"
-               ((closed_id) ->
-                 $(closed_student).toggle () ->
-                   console.log "the closure id is #{closed_id}"
-                   $("#text-container-#{closed_id}").show("explode", 1000);
-                 , () ->
-                   $("#text-container-#{closed_id}").hide("explode", 1000);)(id);
+
")
+            .appendTo(current_row)
+            .each(() ->
+                    console.log "calling click toggle listener on id #{id}"
+                    ((closed_id) ->
+                      $(closed_student).toggle () ->
+                      console.log "the closure id is #{closed_id}"
+                      $("#text-container-#{closed_id}").show("explode", 1000);
+                    , () ->
+                      $("#text-container-#{closed_id}").hide("explode", 1000);)(id);
     #also, delete any ids that are still client side but have disconnected from the server
      _.map $('.container').find('.student-box'),
           (el) ->


remove whitespace

remove whitespace

--- app/pages/project/stats/stats.cjsx
+++ app/pages/project/stats/stats.cjsx
@@ -54,7 +54,7 @@

   handleWorkflowSelect: (event) ->
     @props.handleWorkflowChange(@props.type, event)
-
+
   getRange: ->
     if @props.range?
       range = []


Atom shouldn't strip whitespace by default

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -10,7 +10,6 @@
   editor:
     fontSize: 13
     invisibles: {}
-    tabLength: 4
   "exception-reporting":
     userId: "da976a4b-6a5d-3f34-7d9b-6bfdb74474ed"
   "git-diff":
@@ -26,3 +25,5 @@
   "vim-mode": {}
   welcome:
     showOnStartup: false
+  whitespace:
+    removeTrailingWhitespace: false


imdone-atom config

imdone-atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -39,3 +39,4 @@
   "atom-material-ui":
     useRoboto: true
     compactTreeView: true
+  "imdone-atom": {}


string templates

string templates

--- autoform-fixtures.coffee
+++ autoform-fixtures.coffee
@@ -64,9 +64,9 @@
           count = field.maxCount || values.length
           result[k] = fillValues(values, count)
         else
-          throw new Error(k + " - ss without options")
+          throw new Error("#{k} - ss without options")
       else
-        throw new Error(k + " - not supported type [" + element.type.name + "]")
+        throw new Error("#{k} - not supported type [#{element.type.name}]")
       continue
     if field.autoform?.options
       values = getValues(field, field.type)


shorten timeout

shorten timeout

--- app/js/views/notification_details.coffee
+++ app/js/views/notification_details.coffee
@@ -55,11 +55,13 @@
   hide: ->
     @$el.removeClass('focused')
     @unbindKeyboardEvents()
-    # give animation time to finish
+    # FIXME: find a better way to give animation time to finish. This can leave
+    # the view in an inconsistent state if this model is selected again before
+    # the timeout fires.
     setTimeout =>
       @$el.detach()
       @subject.hide()
-    , 1000
+    , 300

   show: ->
     @bindKeyboardEvents()


transform to use as lib

transform to use as lib

--- lib/modal-file-manager.coffee
+++ lib/modal-file-manager.coffee
@@ -3,6 +3,7 @@
 {BufferedProcess} = require 'atom'

 module.exports = ModalFileManager =
+  ModalFileManagerView: ModalFileManagerView
   modalFileManagerView: null
   modalPanel: null
   subscriptions: null


Resolve setDisplaySize to width and height.

--- src/pane/PaneView.coffee
+++ src/pane/PaneView.coffee
@@ -169,6 +169,8 @@
       if h < 1 then h *= H

       setWH w, h
+
+      {width: w, height: h}

   ###*


Updated atom.io configuration

Updated atom.io configuration

--- config.cson
+++ config.cson
@@ -35,6 +35,7 @@
     userId: "110bb6f9-b221-46bb-894e-cc3b978a51b6"
   "java-generator":
     toggles:
+      appendThisToGetters: true
       generateGettersThenSetters: true
   "linter-eslint":
     fixOnSave: true


Updating

Updating

--- config.cson
+++ config.cson
@@ -44,6 +44,6 @@
       workingDirectory: "Active File"
     style: {}
   "tree-view":
-    hideVcsIgnoredFiles: true
+    autoReveal: true
   welcome:
     showOnStartup: false


:lipstick: Remove unneeded parens

:lipstick: Remove unneeded parens

--- src/path-searcher.coffee
+++ src/path-searcher.coffee
@@ -53,7 +53,7 @@
     matches = null
     lineTextOffset = 0

-    while(regex.test(line))
+    while regex.test(line)
       lineTextOffset = 0
       lineTextLength = line.length
       matchText = RegExp.lastMatch


removed mandator update message until implemented

--- app/js/controllers/unlockwallet.coffee
+++ app/js/controllers/unlockwallet.coffee
@@ -23,8 +23,8 @@
       if (Info.info.client_version)
         cancel()
         $scope.client_version = Info.info.client_version
-        if $scope.client_version != "testnet"
-            $scope.update_available = true
+        #if $scope.client_version != "testnet"
+        #    $scope.update_available = true

     $scope.submitForm = ->