Added ability to use duplicated day abbreviations

--- spec/configuration-specs.coffee
+++ spec/configuration-specs.coffee
@@ -181,6 +181,33 @@
         it 'should have a clear button', ->
           expect($(element).find('.quickdate-clear').hasClass('ng-hide')).toEqual(false)

+    describe 'Given that it is configured with day abbreviations', ->
+      beforeEach(module('ngQuickDate', (ngQuickDateDefaultsProvider) ->
+        ngQuickDateDefaultsProvider.set('dayAbbreviations', ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"])
+        null
+      ))
+      describe 'and given a basic datepicker', ->
+        beforeEach(inject(($compile, $rootScope) ->
+          element = buildBasicDatepicker($compile, $rootScope, new Date())
+        ))
+
+        it 'should render abbreviations', ->
+          expect($(element).find('.quickdate-calendar thead th').size()).toEqual(7)
+
+    describe 'Given that it is configured with duplicate day abbreviations', ->
+      beforeEach(module('ngQuickDate', (ngQuickDateDefaultsProvider) ->
+        ngQuickDateDefaultsProvider.set('dayAbbreviations', ["S", "M", "T", "W", "T", "F", "S"])
+        null
+      ))
+      describe 'and given a basic datepicker', ->
+        beforeEach(inject(($compile, $rootScope) ->
+          element = buildBasicDatepicker($compile, $rootScope, new Date())
+        ))
+
+        it 'should render abbreviations', ->
+          expect($(element).find('.quickdate-calendar thead th').size()).toEqual(7)
+
+
     xdescribe 'Given that a default time of 1:52 am is configured', ->
       beforeEach(module('ngQuickDate', (ngQuickDateDefaultsProvider) ->
         ngQuickDateDefaultsProvider.set('defaultTime', '01:52')


:white_check_mark: :tada: finish spec

:white_check_mark: :tada: finish spec

--- spec/cursor-history-spec.coffee
+++ spec/cursor-history-spec.coffee
@@ -245,3 +245,39 @@
             expect(isEqualEntry(entry, {point: [10, 0], URI: pathSample1})).toBe true
             expect(getValidEntries().length).toBe 3
             expect(main.history.entries.length).toBe 3
+
+    describe "ignoreCommands setting", ->
+      [editor2, editorElement2] = []
+      beforeEach ->
+        editor.setCursorBufferPosition([1, 2])
+        expect(main.history.entries.length).toBe 0
+        expect(editorElement.hasFocus()).toBe true
+
+        jasmine.useRealClock()
+        spyOn(atom.workspace, "getActiveTextEditor").andCallThrough()
+        atom.commands.add editorElement,
+          'test:open-sample2': ->
+            atom.workspace.open(pathSample2).then (e) ->
+              editor2 = e
+              editorElement2 = atom.views.getView(e)
+
+      describe "ignoreCommands is empty", ->
+        it "save cursor position to history when editor lost focus", ->
+          atom.config.set('cursor-history.ignoreCommands', [])
+          runs -> atom.commands.dispatch(editorElement, 'test:open-sample2')
+          waitsFor -> atom.workspace.getActiveTextEditor.callCount is 1
+          waitsFor -> editorElement2.hasFocus() is true
+
+          runs ->
+            expect(main.history.entries.length).toBe 1
+            entry = _.last(main.history.entries)
+            expect(isEqualEntry(entry, {point: [1, 2], URI: pathSample1})).toBe true
+
+      describe "ignoreCommands is set and match command name", ->
+        it "won't save cursor position to history when editor lost focus", ->
+          atom.config.set('cursor-history.ignoreCommands', ['test:open-sample2'])
+          runs -> atom.commands.dispatch(editorElement, 'test:open-sample2')
+          waitsFor -> editorElement2.hasFocus() is true
+
+          runs ->
+            expect(main.history.entries.length).toBe 0


PendingInvitationsView: Added refresh method.

--- client/admin/lib/views/invitations/pendinginvitationsview.coffee
+++ client/admin/lib/views/invitations/pendinginvitationsview.coffee
@@ -56,3 +56,10 @@
     sort[sortType] = sortDirections[sortType]

     return sort
+
+
+  refresh: ->
+
+    @listController.removeAllItems()
+    @listController.lazyLoader.show()
+    @fetchMembers()


BaseStackEditorView: Set tab size properly.

--- client/admin/lib/views/stacks/editors/basestackeditorview.coffee
+++ client/admin/lib/views/stacks/editors/basestackeditorview.coffee
@@ -24,7 +24,6 @@

     options.content     = content
     options.contentType = contentType
-
     options.file        = FSHelper.createFileInstance
       path: "localfile:/stack.#{contentType}"

@@ -34,5 +33,5 @@

     ace.ready =>
       ace.setTheme 'github', no
-      @emit 'ace.changeSetting', 'tabSize', 2
+      ace.setTabSize 2, no
       @emit 'EditorReady'


minor cleanup on demo page

minor cleanup on demo page

--- client/app/Applications/Demos.kdapplication/AppController.coffee
+++ client/app/Applications/Demos.kdapplication/AppController.coffee
@@ -14,41 +14,4 @@
     super options, data

   loadView:(mainView)->
-    options =
-      name               : "Teamwork"
-      version            : "0.1"
-      joinModalTitle     : "Join a coding session"
-      joinModalContent   : "
Paste the session key that you received and start coding together.
"
-      shareSessionKeyInfo: "
This is your session key, you can share this key with your friends to work together.
"
-      firebaseInstances  : "teamwork-local"
-      # firebaseInstances  : ["kd-prod-1", "kd-prod-2", "kd-prod-3", "kd-prod-4", "kd-prod-5"]
-      enableChat         : yes
-      panels             : [
-        {
-          title          : "Teamwork"
-          hint           : "
This is a collaborative coding environment where you can team up with others and work on the same code.
"
-          buttons        : [
-            {
-              title      : "Join"
-              cssClass   : "cupid-green join-button"
-              callback   : (panel, workspace) => workspace.showJoinModal()
-            }

-          ]
-          panes: [
-            {
-              type  : "finder"
-            }
-            {
-              type  : "tabbedEditor"
-            }
-            {
-              type  : "terminal"
-            }
-          ]
-        }
-      ]
-    kolab = new CollaborativeWorkspace options
-    kolab.on "PanelCreated", =>
-      kolab.activePanel.splitView.resizePanel "20%", 0
-    mainView.addSubView kolab


teams: minor copy change

teams: minor copy change

--- client/landing/site.landing/coffee/team/tabs/teamlogintab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamlogintab.coffee
@@ -49,8 +49,8 @@

     @inviteDesc.updatePartial if domains.length > 1
       domainsPartial = ('' + d + ', ' for d in domains).join('').replace(/,\s$/, '')
-      "If you have an email address from one of these domains #{domainsPartial} join here."
-    else "If you have an email address from #{domains.first} join here."
+      "If you have an email address from one of these domains #{domainsPartial}, you can join here."
+    else "If you have an email address from #{domains.first}, you can join here."

   pistachio: ->


terminal: restore selection range on double click

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -227,6 +227,11 @@
   click: ->
     @setKeyView()

+  dblClick: ->
+    range = @utils.getSelectionRange()
+    @utils.defer =>
+      @utils.addRange range
+
   keyDown: (event) ->
     @listenFullscreen event
     @terminal.keyDown event


sitewide: main.coffee firstload flag is put back

--- client/app/MainApp/main.coffee
+++ client/app/MainApp/main.coffee
@@ -7,6 +7,7 @@
   modalTimerId     = null
   currentModal     = null
   currentModalSize = null
+  firstLoad        = yes

   mainController.tempStorage = {}


Finder: D&D: Fix Firefox issue

Finder: D&D: Fix Firefox issue

--- client/finder/lib/index.coffee
+++ client/finder/lib/index.coffee
@@ -66,11 +66,13 @@

     checkDraggingItem = (event) ->

-      { items } = event.originalEvent.dataTransfer
+      { dataTransfer } = event.originalEvent

-      # If the user isn't dragging a file and internalDragging is false,
-      # show the overlay.
-      if items?[0].kind isnt 'file' and not controller.treeController.internalDragging
+      # Workaround for FF
+      return yes  if dataTransfer.types?[0] is 'application/x-moz-file'
+
+      # If the user isn't dragging a file and internalDragging is false, show the overlay.
+      if dataTransfer.items?[0].kind isnt 'file' and not controller.treeController.internalDragging
         finderView.overlay?.show()
         return no


fixed editing multiline text bug

fixed editing multiline text bug

--- client/lib/chatMessages.coffee
+++ client/lib/chatMessages.coffee
@@ -66,8 +66,8 @@
 		editing.id = id
 		element.classList.add("editing")
 		setTimeout ->
-			input.select()
-		, 10
+			input.focus()
+		, 5

 	clearEditing = ->
 		if editing.element
@@ -156,12 +156,15 @@
 			unless k in keyCodes
 				startTyping(rid, input)
 			else if k is 38 or k is 40 # Arrow Up or down
+				if k is 38
+					return if input.value.slice(0, input.selectionStart).match(/[\n]/) isnt null
+					toPrevMessage()
+				else
+					return if input.value.slice(input.selectionEnd, input.value.length).match(/[\n]/) isnt null
+					toNextMessage()
+
 				event.preventDefault()
 				event.stopPropagation()
-				if k is 38
-					toPrevMessage()
-				else
-					toNextMessage()

 	isScrollable: isScrollable
 	toBottom: toBottom


Clean up syntax

Clean up syntax

--- client/scripts/narcissa.coffee
+++ client/scripts/narcissa.coffee
@@ -117,11 +117,11 @@
     }
     (data) ->
       self.activities(_.map data.activities.results,
-                      (result) -> new Activity(result))
+                      (result) -> new Activity result)
       self.recentTracks(_.map data.recentTracks.results,
-                        (result) -> new Track(result))
+                        (result) -> new Track result)
       self.addictiveTracks(_.map data.addictiveTracks.results,
-                           (result) -> new Track(result))
+                           (result) -> new Track result)
       self.currentPlace new Place data.currentPlace.results[0]
       _.each(_.pairs(data), (pair) ->
         name = pair[0]


fix var names

fix var names

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -229,7 +229,7 @@
         path = Blog.Router.pathFor 'blogAdminEdit', id: id
         Blog.Router.replaceState path

-      toastr.success Blog.settings.language.adminSaved
+      toastr.success Blog.settings.language.saved
   , 8000

   'blur [name=title]': (e, tpl) ->
@@ -262,7 +262,7 @@
           theFile = Blog.S3Files.find({_id: fileObj._id}).fetch()[0]
           if theFile.isUploaded() and theFile.url?()
             Session.set 'blog.featuredImage', theFile.url()
-            toastr.success Blog.settings.language.adminFeaturedImageSaved
+            toastr.success Blog.settings.language.editFeaturedImageSaved
             c.stop()
     # Local Filestore
     else
@@ -271,7 +271,7 @@
           theFile = Blog.FilesLocal.find({_id: fileObj._id}).fetch()[0]
           if theFile.isUploaded() and theFile.url?()
             Session.set 'blog.featuredImage', theFile.url()
-            toastr.success Blog.settings.language.adminFeaturedImageSaved
+            toastr.success Blog.settings.language.editFeaturedImageSaved
             c.stop()

   'submit form': (e, tpl) ->


Terminal: Fix plus button issue

Terminal: Fix plus button issue

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -25,7 +25,7 @@
       @initPane pane

       terminalView.terminal?.scrollToBottom()
-
+
       KD.utils.defer -> terminalView.setKeyView()

       @fetchStorage (storage) -> storage.setValue 'activeIndex', index
@@ -294,7 +294,7 @@

         vmc = KD.getSingleton 'vmController'
         if vmc.vms.length > 1
-          return  unless @vmselection.isDestroyed
+          return  if @vmselection and not @vmselection.isDestroyed
           @vmselection = new VMSelection
           @vmselection.once 'VMSelected', (vm)=> @createNewTab vmName: vm, mode: 'create'
         else


titles are h1 not h2

titles are h1 not h2

--- client/code/view/titles.coffee
+++ client/code/view/titles.coffee
@@ -1,5 +1,5 @@
 class Cu.View.Title extends Backbone.View
-  tagName: 'h2'
+  tagName: 'h1'

   initialize: ->
     @setDocumentTitle()


Restructured inheritance

Restructured inheritance

--- core/app/backbone/views/fact_relations/argument_vote_view.coffee
+++ core/app/backbone/views/fact_relations/argument_vote_view.coffee
@@ -16,13 +16,22 @@
     _.defer (=> @$el.addClass 'vote-up-down-highlight')
     _.delay (=> @$el.removeClass 'vote-up-down-highlight'), 1000

-class FactVoteLineView extends VoteLineView
-  modelEvents:
-    'sync': 'onChange'
+class FactVoteUpLineView extends VoteLineView
+  template: 'facts/vote_up_line'
+  modelEvents: {'sync': 'onChange'}

-class FactRelationVoteLineView extends VoteLineView
-  modelEvents:
-    'change:current_user_opinion': 'onChange'
+class FactVoteDownLineView extends VoteLineView
+  template: 'facts/vote_down_line'
+  modelEvents: {'sync': 'onChange'}
+
+class FactRelationVoteUpLineView extends VoteLineView
+  template: 'fact_relations/vote_up_line'
+  modelEvents: {'change:current_user_opinion': 'onChange'}
+
+class FactRelationVoteDownLineView extends VoteLineView
+  template: 'fact_relations/vote_down_line'
+  modelEvents: {'change:current_user_opinion': 'onChange'}
+

 class ArgumentVoteView extends Backbone.Marionette.Layout
   className: 'vote-up-down'
@@ -38,22 +47,13 @@

 class window.ArgumentVoteUpView extends ArgumentVoteView
   onRender: ->
-    @factRelationLineRegion.show new FactRelationVoteLineView
-      template: 'fact_relations/vote_up_line'
-      model: @model
-
+    @factRelationLineRegion.show new FactRelationVoteUpLineView model: @model
     if @model instanceof FactRelation
-      @factLineRegion.show new FactVoteLineView
-        template: 'facts/vote_up_line'
-        model: @model.getFact().getFactWheel()
+      @factLineRegion.show new FactVoteUpLineView @model.getFact().getFactWheel()

 class window.ArgumentVoteDownView extends ArgumentVoteView
   onRender: ->
-    @factRelationLineRegion.show new FactRelationVoteLineView
-      template: 'fact_relations/vote_down_line'
-      model: @model
+    @factRelationLineRegion.show new FactRelationVoteDownLineView model: @model

     if @model instanceof FactRelation
-      @factLineRegion.show new FactVoteLineView
-        template: 'facts/vote_down_line'
-        model: @model.getFact().getFactWheel()
+      @factLineRegion.show new FactVoteDownLineView model: @model.getFact().getFactWheel()


Make sure the overlayer class is only used once.

Using it twice causes context menus with the .overlay selector to
appear twice.
Closes #2601

--- src/react-editor-view.coffee
+++ src/react-editor-view.coffee
@@ -5,9 +5,7 @@

 module.exports =
 class ReactEditorView extends View
-  # The `overlayer` class is included for backwards compatibility with
-  # context menus. It should be removed in v1.0.0
-  @content: -> @div class: 'editor react overlayer'
+  @content: -> @div class: 'editor react'

   focusOnAttach: false


Fix another test for decorations

Fix another test for decorations

--- spec/jumpy-settings-spec.coffee
+++ spec/jumpy-settings-spec.coffee
@@ -46,12 +46,12 @@
         waitsForPromise ->
             jumpyPromise

-    describe "when the jumpy:toggle event is triggered", ->
+    fdescribe "when the jumpy:toggle event is triggered", ->
         it "draws correctly colored labels", ->
-            expect(textEditorElement.shadowRoot.querySelectorAll('.jumpy.label')[0].classList
-                .contains 'high-contrast').toBe true
+            expect(textEditor.getOverlayDecorations()[0].getProperties().item
+                .classList.contains 'high-contrast').toBe true
         it "draws labels of the right font size", ->
-            expect(textEditorElement.shadowRoot.querySelectorAll('.jumpy.label')[0]
+            expect(textEditor.getOverlayDecorations()[0].getProperties().item
                 .style.fontSize).toBe '50%'

     describe "when the jumpy:toggle event is triggered


Test for empty block todos

Test for empty block todos

--- spec/show-todo-view-spec.coffee
+++ spec/show-todo-view-spec.coffee
@@ -267,6 +267,19 @@
         expect(showTodoView.matches[0].matchText).toBe 'No details'
         expect(showTodoView.matches[1].matchText).toBe 'No details'

+    it 'handles empty block todos', ->
+      editor.setText """
+        /* TODO */
+        Line 2 /* TODO */
+      """
+
+      waitsForPromise ->
+        showTodoView.fetchOpenRegexItem(defaultLookup)
+      runs ->
+        expect(showTodoView.matches).toHaveLength 2
+        expect(showTodoView.matches[0].matchText).toBe 'No details'
+        expect(showTodoView.matches[1].matchText).toBe 'No details'
+
     it 'handles todos with @ in front', ->
       editor.setText """
         Line 1 //@TODO: text


Update scrollbar style spec for new library API

--- spec/workspace-view-spec.coffee
+++ spec/workspace-view-spec.coffee
@@ -242,10 +242,15 @@

   describe "the scrollbar visibility class", ->
     it "has a class based on the style of the scrollbar", ->
+      style = 'legacy'
       scrollbarStyle = require 'scrollbar-style'
-      scrollbarStyle.emitValue 'legacy'
+      spyOn(scrollbarStyle, 'getPreferredScrollbarStyle').andCallFake -> style
+
+      atom.workspaceView.element.observeScrollbarStyle()
       expect(atom.workspaceView).toHaveClass 'scrollbars-visible-always'
-      scrollbarStyle.emitValue 'overlay'
+
+      style = 'overlay'
+      atom.workspaceView.element.observeScrollbarStyle()
       expect(atom.workspaceView).toHaveClass 'scrollbars-visible-when-scrolling'

   describe "editor font styling", ->


last logs commenting

last logs commenting

--- src/scripts/ui-scroll.coffee
+++ src/scripts/ui-scroll.coffee
@@ -207,7 +207,7 @@
 								builder.bottomPadding(builder.bottomPadding() + bottomHeight)
 								removeFromBuffer(buffer.length - overage, buffer.length)
 								next -= overage
-								log "clipped off bottom #{overage} bottom padding #{builder.bottomPadding()}"
+								#log "clipped off bottom #{overage} bottom padding #{builder.bottomPadding()}"

 						shouldLoadTop = ->
 							!bof && (builder.topDataPos() > topVisiblePos() - bufferPadding())
@@ -232,7 +232,7 @@
 								builder.topPadding(builder.topPadding() + topHeight)
 								removeFromBuffer(0, overage)
 								first += overage
-								log "clipped off top #{overage} top padding #{builder.topPadding()}"
+								#log "clipped off top #{overage} top padding #{builder.topPadding()}"

 						enqueueFetch = (rid, direction)->
 							if (!isLoading)
@@ -290,7 +290,7 @@
 									viewport.scrollTop(viewport.scrollTop() + wrapper.element.outerHeight(true))

 						doAdjustment = (rid, finalize)->
-							log "top {actual=#{builder.topDataPos()} visible from=#{topVisiblePos()} bottom {visible through=#{bottomVisiblePos()} actual=#{builder.bottomDataPos()}}"
+							#log "top {actual=#{builder.topDataPos()} visible from=#{topVisiblePos()} bottom {visible through=#{bottomVisiblePos()} actual=#{builder.bottomDataPos()}}"
 							if shouldLoadBottom()
 								enqueueFetch(rid, true)
 							else


get project path

get project path

--- lib/linter-js-standard.coffee
+++ lib/linter-js-standard.coffee
@@ -31,16 +31,21 @@
   constructor: (editor) ->
     super(editor)

-    atom.config.observe 'linter-js-standard.style',
+    file = editor?.buffer.file
+    @filePath = file?.path
+
+    if !@filePath
+      throw new Error 'Couldn\'t get file path'
+
+    atom.config.observe 'linter-js-standard',
       @formatShellCmd

     @cwd = null

-  formatShellCmd: (style) =>
-    ## If style
-    # isn't defined return early,
-    # try next time
-    if typeof style == 'undefined'
+  formatShellCmd: (config) =>
+    ## If config isn't defined return early,
+    ## try next time
+    if typeof config == 'undefined'
       return

     standardPath = path.join __dirname,
@@ -54,13 +59,24 @@
       'semistandard',
       'bin'

-    if style == 'standard'
-      @executablePath = standardPath
+    if config.codeStyleDevDependencies
+      projectPath = atom.project.getPaths()
+      filePath = @filePath
+
+      projectPath = projectPath.filter (path) ->
+        regex = new RegExp('^' + path)
+        return regex.test(filePath)
+
+      projectPath = projectPath[0]
+
     else
-      @linterName = 'js-semistandard'
-      @executablePath = semiStandardPath
+      if config.style == 'standard'
+        @executablePath = standardPath
+      else
+        @linterName = 'js-semistandard'
+        @executablePath = semiStandardPath

-    @executablePath += '/cmd.js'
+      @executablePath += '/cmd.js'

     if !fs.existsSync @executablePath
       throw new Error 'Standard or Semistandard wasn\'t


Add Wiki controller with MathJax directive

--- services/web/public/coffee/main/learn.coffee
+++ services/web/public/coffee/main/learn.coffee
@@ -1,5 +1,6 @@
 define [
 	"base"
+	"directives/mathjax"
 	"services/algolia-search"
 ], (App) ->

@@ -55,4 +56,4 @@
 					hits = _.map response.hits, buildHitViewModel
 					updateHits hits

-
+	App.controller "LearnController", () ->


:art: lint fixup

:art: lint fixup

--- spec/package-detail-view-spec.coffee
+++ spec/package-detail-view-spec.coffee
@@ -47,7 +47,7 @@

   it "Shows an error when package metadata cannot be loaded", ->
     packageManager.client = createClientSpy()
-    packageManager.client.package.andCallFake (name, cb)->
+    packageManager.client.package.andCallFake (name, cb) ->
       error = new Error('API or cache error')
       cb(error, null)


minor changes/fixes and reorderings

minor changes/fixes and reorderings

--- library/nucleus/plumbs.coffee
+++ library/nucleus/plumbs.coffee
@@ -54,10 +54,10 @@
     writer = (d) -> logger.log level, filter d
     msg = "Configure logger middleware at U=%s"
     assert stamp = moment().unix().toString()
-    assert options = stream: write: writer
-    assert options.format = "#{format}"
-    logger.debug msg, (try stamp.bold)
-    return try connect.logger options
+    options = new Object stream: write: writer
+    assert options.format = format.toString()
+    logger.debug msg.toString(), stamp.bold
+    return try connect.logger options or {}

 # This middleware is really a wrapper around the `Connect` session
 # middleware. The reason it wraps it is to automatically configure


upgrade sender with event emissions

upgrade sender with event emissions

--- library/nucleus/plumbs.coffee
+++ library/nucleus/plumbs.coffee
@@ -165,9 +165,14 @@
     assert _.isFunction(try (send.json)), noLibrary
     assert _.isObject(response or null), terrible
     assert _.isObject(request?.headers), noHeaders
-    assert (try response.send = send), ack.toString()
-    assert response.json = try (send.json spaces: 4)
     try response.req = request unless response.req
+    assert e = (seq) -> response.emit "send", seq...
+    assert d = (seq) -> response.emit "sdat", seq...
+    assert i = (seq) -> try _.isString _.first seq
+    assert p = (seq, fn) -> fn.apply response, seq
+    e = (f) -> (s...) -> e(s); (d(s) if i(s)); p(s, f)
+    assert (try response.send = e send), ack.toString()
+    assert response.json = try (e send.json spaces: 4)
     assert unix = moment().unix().toString().bold
     message = "Running sending middleware at U=%s"
     logger.debug message.toString(), unix.toString()


fix wording

fix wording

--- library/nucleus/scaled.coffee
+++ library/nucleus/scaled.coffee
@@ -184,7 +184,7 @@
         assert proxy = queue.shift(); queue.push proxy
         a = "#{proxy.target.host}:#{proxy.target.port}"
         assert a = "#{a.toLowerCase().underline.yellow}"
-        logger.debug "Proxy %s WebSocket %s to %s", x, u, a
+        logger.debug "Proxy %s upgrade %s to %s", x, u, a
         return proxy.proxyWebSocketRequest arguments...

     # Create and launch a Seaport server in the current kernel. It


GroupController: removed unnecessary listener

--- client/Main/maincontroller/groupscontroller.coffee
+++ client/Main/maincontroller/groupscontroller.coffee
@@ -15,11 +15,11 @@
     @groups           = {}
     @currentGroupData = new GroupData

-    mainController.on 'NavigationLinkTitleClick', (pageInfo)=>
-      return unless pageInfo.path
-      if pageInfo.topLevel
-      then router.handleRoute "#{pageInfo.path}"
-      else router.handleRoute "#{pageInfo.path}", {entryPoint}
+    # mainController.on 'NavigationLinkTitleClick', (pageInfo)=>
+    #   return unless pageInfo.path
+    #   if pageInfo.topLevel
+    #   then router.handleRoute "#{pageInfo.path}"
+    #   else router.handleRoute "#{pageInfo.path}", {entryPoint}

   getCurrentGroup:->
     throw 'FIXME: array should never be passed'  if Array.isArray @currentGroupData.data


Client-side changes to adapt to new structure

--- client/dashboard/javascripts/app/app_config.coffee
+++ client/dashboard/javascripts/app/app_config.coffee
@@ -15,6 +15,7 @@
     .then ->
       if emitIfAuthenticated
         $rootScope.$broadcast "auth:#{eventToEmit}"
+        $q.reject()
     .catch ->
       if !emitIfAuthenticated
         $rootScope.$broadcast "auth:#{eventToEmit}"
@@ -39,14 +40,14 @@
     url         : '/login'
     templateUrl : templateLogin
     controller  : 'LoginController'
-    resolve     :
-      auth: resolveAuthenticationAndEmitIf 'alreadylogged', true
+    # resolve     :
+    #   auth: resolveAuthenticationAndEmitIf 'alreadylogged', true

   $stateProvider.state 'dashboard',
     abstract      : true
     templateUrl   : templateDashboard
-    resolve       :
-      auth: resolveAuthenticationAndEmitIf 'unauthorized', false
+    # resolve       :
+    #   auth: resolveAuthenticationAndEmitIf 'unauthorized', false
     ncyBreadcrumb :
       skip : true

@@ -54,8 +55,8 @@
     parent        : 'dashboard'
     url           : '/'
     templateUrl   : templateHome
-    resolve       :
-      auth: resolveAuthenticationAndEmitIf 'unauthorized', false
+    # resolve       :
+    #   auth: resolveAuthenticationAndEmitIf 'unauthorized', false
     ncyBreadcrumb :
       label: 'Dashboard'

@@ -64,8 +65,8 @@
     abstract      : true
     url           : '/users'
     templateUrl   : templateBreadcrumbs
-    resolve       :
-      auth: resolveAuthenticationAndEmitIf 'unauthorized', false
+    # resolve       :
+    #   auth: resolveAuthenticationAndEmitIf 'unauthorized', false
     ncyBreadcrumb :
       skip: true

@@ -73,8 +74,8 @@
     url           : '/list'
     templateUrl   : templateUsersList
     controller    : 'UsersListController'
-    resolve       :
-      auth: resolveAuthenticationAndEmitIf 'unauthorized', false
+    # resolve       :
+    #   auth: resolveAuthenticationAndEmitIf 'unauthorized', false
     ncyBreadcrumb :
       label: 'Users'

@@ -83,7 +84,7 @@
     templateUrl   : templateUsersProfile
     controller    : 'UsersProfileController'
     resolve       :
-      auth: resolveAuthenticationAndEmitIf 'unauthorized', false
+      # auth: resolveAuthenticationAndEmitIf 'unauthorized', false
       user: -> undefined
     ncyBreadcrumb :
       parent : 'users.list'
@@ -94,7 +95,7 @@
     templateUrl   : templateUsersProfile
     controller    : 'UsersProfileController'
     resolve       :
-      auth: resolveAuthenticationAndEmitIf 'unauthorized', false
+      # auth: resolveAuthenticationAndEmitIf 'unauthorized', false
       user: ($stateParams, API) -> API.users.get($stateParams.id)
     ncyBreadcrumb :
       parent : 'users.list'


Fixes for layout builder.

Fixes for layout builder.

--- client/ide/workspace/workspacelayoutbuilder.coffee
+++ client/ide/workspace/workspacelayoutbuilder.coffee
@@ -8,16 +8,17 @@
     splitOptions   = {
       type         : direction
       viewsConfig  : views
-      size
+      sizes
       cssClass
     }
+
     @addSubView @createSplitView splitOptions, splitName

   createSplitView: (splitOptions, splitName) ->
     {type, sizes, viewsConfig, cssClass} = splitOptions
     views = []

-    viewsConfig.forEach (config, index) =>
+    viewsConfig.forEach (config) =>
       if config.type is "split"
         {options}     = config
         {splitName}   = options


Removed not longer needed @container, added js-notificationsContainer for browser testing.

--- app/assets/javascripts/app/controllers/widget/online_notification.coffee
+++ app/assets/javascripts/app/controllers/widget/online_notification.coffee
@@ -1,7 +1,7 @@
 class App.OnlineNotificationWidget extends App.Controller
   alreadyShown: {}
   shown: false
-  className: 'popover popover--notifications right'
+  className: 'popover popover--notifications right js-notificationsContainer'
   attributes:
     role: 'tooltip'

@@ -13,7 +13,6 @@
     'keydown': 'listNavigate'

   elements:
-    '.js-notificationsContainer': 'container'
     '.js-mark': 'mark'
     '.js-item': 'item'
     '.js-content': 'content'
@@ -144,9 +143,6 @@

     if (heightPopoverHeader + heightPopoverContent + heightPopoverSpacer) > heightApp
       heightPopoverContent = heightApp - heightPopoverHeader - heightPopoverSpacer
-      @container.addClass('is-overflowing')
-    else
-      @container.removeClass('is-overflowing')

     @content.css('height', heightPopoverContent)


Remove a TODO + check 2nd verification is done in machine view - closes #654

--- admin/static/coffee/servers/index.coffee
+++ admin/static/coffee/servers/index.coffee
@@ -777,5 +777,3 @@
                 @.$('.alert').slideDown 200

             return num_not_movable_machines is 0
-
-        #TODO Handle error


Remove reference to expring localstorage cache

--- lib/atom-io-client.coffee
+++ lib/atom-io-client.coffee
@@ -12,7 +12,7 @@
     # 12 hour expiry
     @expiry = 1000 * 60 * 60 * 12
     @createAvatarCache()
-    @expireCacheItems()
+    @expireAvatarCache()

   # Public: Get an avatar image from the filesystem, fetching it first if necessary
   avatar: (login, callback) ->
@@ -124,11 +124,8 @@
     request("https://github.com/#{login}.png").pipe(stream)

   # The cache expiry doesn't need to be clever, or even compare dates, it just
-  # needs to always keep around the newest item, and that item only.
-
-  expireCacheItems: ->
-    @expireAvatarCache()
-    @expireLocalStorageCache()
+  # needs to always keep around the newest item, and that item only. The localStorage
+  # cache updates in place, so it doesn't need to be purged.

   expireAvatarCache: ->
     fs.readdir path.join(app.getDataPath(), 'Cache/settings-view'), (error, _files) ->
@@ -144,7 +141,3 @@
         children.sort()
         keep = children.pop()
         (fs.unlink(path.join(app.getDataPath(), 'Cache/settings-view', child)) for child in children) # throw away callback
-
-
-
-  expireLocalStorageCache: ->


Apps: Stopped event propagation when clicked to dev mode badge.

--- client/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
+++ client/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
@@ -144,7 +144,8 @@
         cssClass : "top-badge gray"
         tooltip  :
           title  : "Dev-Mode enabled, click for help."
-        click    : =>
+        click    : (e) ->
+          e.stopPropagation()
           new KDModalView
             overlay  : yes
             width    : 500


make links open in a new window

make links open in a new window

--- app/assets/javascripts/entries.js.coffee
+++ app/assets/javascripts/entries.js.coffee
@@ -1,3 +1,7 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
+
+$(document).on 'click', '.entries a', (evt) ->
+  evt.preventDefault()
+  window.open($(this).attr('href'))


fix(account-sidebar): Fix specs to match new unread counts

--- internal_packages/account-sidebar/spec/account-sidebar-store-spec.coffee
+++ internal_packages/account-sidebar/spec/account-sidebar-store-spec.coffee
@@ -34,217 +34,228 @@
             name: 'Drafts'
         ]

-      expected = [
-        {
-          label: 'Mailboxes',
-          items: [
-            {
-              id: '1',
+      # Note: If you replace this JSON with new JSON, you may have to replace
+      # A\E with A\\E manually.
+      expected = [{
+        label: 'Mailboxes',
+        items: [
+          {
+            id: '1',
+            name: 'Inbox',
+            mailViewFilter: {
               name: 'Inbox',
-              mailViewFilter: {
-                name: 'Inbox',
-                category: {
-                  client_id: '1',
-                  name: 'inbox',
-                  display_name: 'Inbox',
-                  id: '1'
-                },
-                iconName: 'inbox.png'
-              },
-              children: [
-
-              ]
-            },
-            {
-              id: 'starred',
+              category: {
+                client_id: '1',
+                name: 'inbox',
+                display_name: 'Inbox',
+                id: '1'
+              },
+              iconName: 'inbox.png'
+            },
+            children: [
+
+            ],
+            unreadCount: null
+          },
+          {
+            id: 'starred',
+            name: 'Starred',
+            mailViewFilter: {
               name: 'Starred',
-              mailViewFilter: {
-                name: 'Starred',
-                iconName: 'starred.png'
-              },
-              children: [
-
-              ]
-            },
-            {
-              id: '3',
+              iconName: 'starred.png'
+            },
+            children: [
+
+            ]
+          },
+          {
+            id: '3',
+            name: 'Sent',
+            mailViewFilter: {
               name: 'Sent',
-              mailViewFilter: {
-                name: 'Sent',
-                category: {
-                  client_id: '3',
-                  name: 'sent',
-                  display_name: 'Sent',
-                  id: '3'
-                },
-                iconName: 'sent.png'
-              },
-              children: [
-
-              ]
-            },
-            {
-              id: '4',
+              category: {
+                client_id: '3',
+                name: 'sent',
+                display_name: 'Sent',
+                id: '3'
+              },
+              iconName: 'sent.png'
+            },
+            children: [
+
+            ],
+            unreadCount: 0
+          },
+          {
+            id: '4',
+            name: 'Important',
+            mailViewFilter: {
               name: 'Important',
-              mailViewFilter: {
-                name: 'Important',
-                category: {
-                  client_id: '4',
-                  name: 'important',
-                  display_name: 'Important',
-                  id: '4'
-                },
-                iconName: 'important.png'
-              },
-              children: [
-
-              ]
-            },
-            {
-              id: 'Drafts',
-              component: {
-
-              },
-              name: 'Drafts',
-              sheet: 'stub',
-              children: [
-
-              ]
-            }
-          ]
-        },
-        {
-          label: 'Folders',
-          iconName: 'folder.png',
-          items: [
-            {
-              id: 'a',
+              category: {
+                client_id: '4',
+                name: 'important',
+                display_name: 'Important',
+                id: '4'
+              },
+              iconName: 'important.png'
+            },
+            children: [
+
+            ],
+            unreadCount: 0
+          },
+          {
+            id: 'Drafts',
+            component: {
+
+            },
+            name: 'Drafts',
+            sheet: 'stub',
+            children: [
+
+            ]
+          }
+        ]
+      },
+      {
+        label: 'Folders',
+        items: [
+          {
+            id: 'a',
+            name: 'A',
+            mailViewFilter: {
               name: 'A',
-              mailViewFilter: {
-                name: 'A',
-                category: {
-                  client_id: 'a',
-                  display_name: 'A',
-                  id: 'a'
-                },
-                iconName: 'folder.png'
-              },
-              children: [
-                {
-                  id: 'a+b',
-                  name: 'B',
-                  mailViewFilter: {
-                    name: 'A/B',
-                    category: {
-                      client_id: 'a+b',
-                      display_name: 'A/B',
-                      id: 'a+b'
+              category: {
+                client_id: 'a',
+                display_name: 'A',
+                id: 'a'
+              },
+              iconName: 'folder.png'
+            },
+            children: [
+              {
+                id: 'a+b',
+                name: 'B',
+                mailViewFilter: {
+                  name: 'A/B',
+                  category: {
+                    client_id: 'a+b',
+                    display_name: 'A/B',
+                    id: 'a+b'
+                  },
+                  iconName: 'folder.png'
+                },
+                children: [
+                  {
+                    id: 'a+b+c',
+                    name: 'C',
+                    mailViewFilter: {
+                      name: 'A/B/C',
+                      category: {
+                        client_id: 'a+b+c',
+                        display_name: 'A/B/C',
+                        id: 'a+b+c'
+                      },
+                      iconName: 'folder.png'
                     },
-                    iconName: 'folder.png'
-                  },
-                  children: [
-                    {
-                      id: 'a+b+c',
-                      name: 'C',
-                      mailViewFilter: {
-                        name: 'A/B/C',
-                        category: {
-                          client_id: 'a+b+c',
-                          display_name: 'A/B/C',
-                          id: 'a+b+c'
-                        },
-                        iconName: 'folder.png'
-                      },
-                      children: [
-
-                      ]
-                    }
-                  ]
-                },
-                {
-                  id: 'a+d',
-                  name: 'D',
-                  mailViewFilter: {
-                    name: 'A.D',
-                    category: {
-                      client_id: 'a+d',
-                      display_name: 'A.D',
-                      id: 'a+d'
-                    },
-                    iconName: 'folder.png'
-                  },
-                  children: [
-
-                  ]
-                },
-                {
-                  id: 'a+e',
-                  name: 'E',
-                  mailViewFilter: {
-                    name: 'A\\E',
-                    category: {
-                      client_id: 'a+e',
-                      display_name: 'A\\E',
-                      id: 'a+e'
-                    },
-                    iconName: 'folder.png'
-                  },
-                  children: [
-
-                  ]
-                },
-                {
-                  id: 'a+b-c',
-                  name: 'B-C',
-                  mailViewFilter: {
-                    name: 'A/B-C',
-                    category: {
-                      client_id: 'a+b-c',
-                      display_name: 'A/B-C',
-                      id: 'a+b-c'
-                    },
-                    iconName: 'folder.png'
-                  },
-                  children: [
-
-                  ]
-                }
-              ]
-            },
-            {
-              id: 'b',
+                    children: [
+
+                    ],
+                    unreadCount: 0
+                  }
+                ],
+                unreadCount: 0
+              },
+              {
+                id: 'a+d',
+                name: 'D',
+                mailViewFilter: {
+                  name: 'A.D',
+                  category: {
+                    client_id: 'a+d',
+                    display_name: 'A.D',
+                    id: 'a+d'
+                  },
+                  iconName: 'folder.png'
+                },
+                children: [
+
+                ],
+                unreadCount: 0
+              },
+              {
+                id: 'a+e',
+                name: 'E',
+                mailViewFilter: {
+                  name: 'A\\E',
+                  category: {
+                    client_id: 'a+e',
+                    display_name: 'A\\E',
+                    id: 'a+e'
+                  },
+                  iconName: 'folder.png'
+                },
+                children: [
+
+                ],
+                unreadCount: 0
+              },
+              {
+                id: 'a+b-c',
+                name: 'B-C',
+                mailViewFilter: {
+                  name: 'A/B-C',
+                  category: {
+                    client_id: 'a+b-c',
+                    display_name: 'A/B-C',
+                    id: 'a+b-c'
+                  },
+                  iconName: 'folder.png'
+                },
+                children: [
+
+                ],
+                unreadCount: 0
+              }
+            ],
+            unreadCount: 0
+          },
+          {
+            id: 'b',
+            name: 'B',
+            mailViewFilter: {
               name: 'B',
-              mailViewFilter: {
-                name: 'B',
-                category: {
-                  client_id: 'b',
-                  display_name: 'B',
-                  id: 'b'
-                },
-                iconName: 'folder.png'
-              },
-              children: [
-                {
-                  id: 'b+c',
-                  name: 'C',
-                  mailViewFilter: {
-                    name: 'B/C',
-                    category: {
-                      client_id: 'b+c',
-                      display_name: 'B/C',
-                      id: 'b+c'
-                    },
-                    iconName: 'folder.png'
-                  },
-                  children: [
-
-                  ]
-                }
-              ]
-            }
-          ]
-        }
-      ]
+              category: {
+                client_id: 'b',
+                display_name: 'B',
+                id: 'b'
+              },
+              iconName: 'folder.png'
+            },
+            children: [
+              {
+                id: 'b+c',
+                name: 'C',
+                mailViewFilter: {
+                  name: 'B/C',
+                  category: {
+                    client_id: 'b+c',
+                    display_name: 'B/C',
+                    id: 'b+c'
+                  },
+                  iconName: 'folder.png'
+                },
+                children: [
+
+                ],
+                unreadCount: 0
+              }
+            ],
+            unreadCount: 0
+          }
+        ],
+        iconName: 'folder.png'
+      }]

       AccountSidebarStore._refreshSections()


Show random data every five seconds

Show random data every five seconds

--- app/assets/javascripts/welcome.js.coffee
+++ app/assets/javascripts/welcome.js.coffee
@@ -4,10 +4,18 @@

 ts = 0
 series = undefined
+start = (new Date()).getTime()

 loadData = ->
   # ts = (new Date()).getTime()
   $.getJSON "/points/latest.json?id=#{ts}", (data) ->
+    if data.length == 0
+      currentTime = (new Date()).getTime()
+      if (currentTime - start) > 5000
+        add start, Math.random() * 30
+        start = currentTime
+      return
+
     $.each data, (k, v) ->
       ts = v.id
       add v.created_ts, v.temperature


Removing console.log

Removing console.log

--- app/coffee/modules/common/filters.coffee
+++ app/coffee/modules/common/filters.coffee
@@ -105,9 +105,7 @@
 darkerFilter = ->
     return (color, luminosity) ->
         # validate hex string
-        console.log color
         color = new String(color).replace(/[^0-9a-f]/gi, '')
-        console.log color
         if color.length < 6
             color = color[0]+ color[0]+ color[1]+ color[1]+ color[2]+ color[2];


fix #415

fix #415

--- app/coffee/modules/taskboard/main.coffee
+++ app/coffee/modules/taskboard/main.coffee
@@ -144,9 +144,9 @@
         task.order = order

         promise = @repo.save(task)
-        promise.then ->
-            console.log "SUCCESS TASK SAVE"
-        promise.then null, ->
+        promise.then =>
+            @.loadSprintStats()
+        promise.then null, =>
             console.log "FAIL TASK SAVE"

     ## Template actions


Add Vitalije as diplomat for sr (Serbian) (#4742)

--- app/views/contribute/DiplomatView.coffee
+++ app/views/contribute/DiplomatView.coffee
@@ -86,7 +86,7 @@
     nn: ['Ayexa']             # Norsk Nynorsk, Norwegian (Nynorsk)
     he: ['OverProgram', 'monetita']             # עברית, Hebrew
     lt: []             # lietuvių kalba, Lithuanian
-    sr: []             # српски, Serbian
+    sr: ['Vitalije']             # српски, Serbian
     uk: ['ImmortalJoker', 'OlenaGapak', 'Rarst', 'endrilian', 'fess89', 'gorodsb', 'probil', 'CatSkald']             # українська, Ukrainian
     hi: []             # मानक हिन्दी, Hindi
     ur: []             # اُردُو, Urdu


[#155049999] Set CODAP property: preventBringToFront

CODAP will soon be adjusting the default setting for preventBringToFront
from true to false. This goes along with other changes intended to make
plugins behave more like other components in the CODAP workspace. Sage
relies on being placed in the background, especially in standalone mode,
so we initialize with the property, instead of relying on the default.

--- src/code/models/codap-connect.coffee
+++ src/code/models/codap-connect.coffee
@@ -42,7 +42,10 @@
       {
         action: 'update'
         resource: 'interactiveFrame'
-        values: { title: tr "~CODAP.INTERACTIVE_FRAME.TITLE"}
+        values: {
+          title: tr "~CODAP.INTERACTIVE_FRAME.TITLE"
+          preventBringToFront: true
+        }
       },
       {
         action: 'get'


Match firstLineRegex against entire file

--- src/app/text-mate-bundle.coffee
+++ src/app/text-mate-bundle.coffee
@@ -50,11 +50,11 @@

   @grammarByShebang: (filePath) ->
     try
-      firstLine = fs.read(filePath).match(/.*/)[0]
+      fileContents = fs.read(filePath)
     catch e
       null

-    _.find @grammars, (grammar) -> grammar.firstLineRegex?.test(firstLine)
+    _.find @grammars, (grammar) -> grammar.firstLineRegex?.test(fileContents)

   @grammarForScopeName: (scopeName) ->
     @grammarsByScopeName[scopeName]


Prevent reseting account parameters when editing account, refs #424

--- client/app/components/account_config_main.coffee
+++ client/app/components/account_config_main.coffee
@@ -39,6 +39,14 @@
     componentWillReceiveProps: (props) ->
         state = {}
         state[key] = value for key, value of props
+
+        if not @_lastDiscovered?
+            # If editing account, init @_lastDiscovered with current domain
+            # so we don't try to discover parameters if domain doesn't change
+            login = state.login.value
+            if state.id?.value? and login?.indexOf('@') >= 0
+                @_lastDiscovered = login.split('@')[1]
+
         @setState state


fix: allow to load config from nested variables

fixes #59

--- src/javascripts/embed.coffee
+++ src/javascripts/embed.coffee
@@ -9,7 +9,7 @@
       if config.match(/^{/)
         JSON.parse(config)
       else
-        window[config] || {json_config: config}
+        @getInSiteConfig(config) || {json_config: config}
     else
       config

@@ -27,6 +27,19 @@
     @replaceElem()
     @injectConfiguration() if @configuration
     @setupSubscribeButton()
+
+  getInSiteConfig: (config) ->
+    inSiteConfig = if !config.startsWith('http') && config.match(/\./)
+      configSplit = config.split('.')
+      tempConfig = null
+      configSplit.forEach (cfg) ->
+        if tempConfig == null
+          tempConfig = window[cfg]
+        else
+          tempConfig = tempConfig[cfg]
+      tempConfig
+    else
+      window[config]

   randomId: (string) ->
     hash = 0


Use collaborator_solutions instead of solutions

--- src/components/exercise/preview.cjsx
+++ src/components/exercise/preview.cjsx
@@ -81,7 +81,8 @@
       details =

Detailed solution

         <ArbitraryHtmlAndMath className="solution" block
-          html={_.first(question.solutions)?.content_html} />
+          html={_.pluck(question.collaborator_solutions, 'content_html').join('')}
+        />

       <Question


Remove zombie tip-snapping logic.

Remove zombie tip-snapping logic.

--- src/js/jquery.hlf.tip.coffee
+++ src/js/jquery.hlf.tip.coffee
@@ -615,15 +615,7 @@
     # offset, so it gets used in lieu of the base `offset`.
     offsetOnTriggerMouseMove: (event, offset, $trigger) ->
       newOffset = _.clone offset
-      if @snap.toTrigger is on
-        newOffset = @_moveToTrigger $trigger, newOffset
-      else
-        if @snap.toXAxis is on
-          newOffset.top = @_offsetStart.top
-          @debugLog 'xSnap'
-        if @snap.toYAxis is on
-          newOffset.left = @_offsetStart.left
-          @debugLog 'ySnap'
+      newOffset = @_moveToTrigger $trigger, newOffset
       newOffset

   # Export


Fix typo

Fix typo

--- client/ide/lib/workspace/idelayoutmanager.coffee
+++ client/ide/lib/workspace/idelayoutmanager.coffee
@@ -269,7 +269,7 @@

   ###*
-   * Clear all split views/tabs and create new an `IDEView`
+   * Clear all split views/tabs and create an new `IDEView`
    *
    * @param {boolean=} recover  Recover opened panes if it is `yes`
    * @param {boolean=} save


Have Gunman personality use offsets

Have Gunman personality use offsets

--- src/definitions/personalities.coffee
+++ src/definitions/personalities.coffee
@@ -1,7 +1,8 @@
 game = require '../game'

 {Personality} = require '../personality'
-direction = (require '../direction')
+direction = require '../direction'
+vectorMath = require '../vector-math'

 class exports.FleeFromPlayer extends Personality
 	constructor: (@safeDist) ->
@@ -109,11 +110,12 @@
 		target = creature.findNearest 30,
 			(e) -> e.type is 'creature'

-		if target? and (creature.distanceSqTo target) > range*range
-			creature.moveTo target
+		if target?
+			if (creature.distanceSqTo target) > range*range
+				creature.moveTo target

-		else
-			gun.fire creature, creature.directionTo target
+			else
+				gun.fire creature, vectorMath.sub target, creature

 		6


start of file config

start of file config

 On branch master
	modified:   examples/rails.coffee

--- examples/rails.coffee
+++ examples/rails.coffee
@@ -1,4 +1,13 @@
 exports.config = (config) ->
   config.files "app/assets/**"
   config.files "vendor/assets/**"
+
+  config.match 'app/assets/javascripts/:file', route_path
+  config.match 'app/assets/stylesheets/:file', route_path
+  config.match 'app/assets/images/:file', route_path
+  config.match 'vendor/assets/javascripts/:file', route_path
+  config.match 'vendor/assets/stylesheets/:file', route_path
   return
+
+  route_path = (file) ->
+    return 'assets'


update usage

update usage

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -38,7 +38,7 @@
   docs                              open the dobi docs
   help                              show usage
   init                              initialize a workspace
-  lint                              lint package or -f <file> to lint file
+  lint <id@version> | -p <path>     lint package or -p <path> to lint file/dir
   login                             authenticate your user
   logout                            deauthenticate your user
   open <site-slug>                  open a site


update stats url

update stats url

--- lib/ink.coffee
+++ lib/ink.coffee
@@ -27,7 +27,7 @@

     try
       if id = localStorage.getItem 'metrics.userId'
-        http.get "http://mikeinn.es/hit?id=#{id}&app=ink"
+        http.get "http://data.junolab.org/hit?id=#{id}&app=ink"

   deactivate: ->
     results.deactivate()


Fail faster on error.

Fail faster on error.

--- server/pdf.coffee
+++ server/pdf.coffee
@@ -11,8 +11,11 @@

     processPDF = (finalCallback) -> (pdf) ->
       counter = pdf.numPages
+      terminate = false
       for pageNumber in [1..pdf.numPages]
         pdf.getPage(pageNumber).then (page) ->
+          return if terminate
+
           # pageNumber is not necessary current page number once promise is resolved, use page.pageNumber instead
           progressCallback (page.pageNumber - 1) / pdf.numPages

@@ -23,9 +26,13 @@
             appendCounter = 0
             textLayer =
               beginLayout: ->
+                return if terminate
+
                 #console.log "beginLayout"

               endLayout: ->
+                return if terminate
+
                 #console.log "endLayout"

                 if DEBUG
@@ -40,6 +47,8 @@
                   finalCallback()

               appendText: (geom) ->
+                return if terminate
+
                 width = geom.canvasWidth * geom.hScale
                 height = geom.fontSize * Math.abs geom.vScale
                 x = geom.x
@@ -81,6 +90,7 @@
             , (err) ->
               error = new Error "PDF page #{ page.pageNumber } rendering error: #{ err.message or err }"
               _.extend error, _.omit err, 'message' if _.isObject err
+              terminate = true
               throw error

         #pdf.getMetadata(pageNumber).then (metadata) ->


fixing typo

fixing typo

--- src/components/buttons/test/buttonbar.test.coffee
+++ src/components/buttons/test/buttonbar.test.coffee
@@ -9,7 +9,7 @@
   afterEach ->
     @sinon.restore()

-  describe 'contructor', ->
+  describe 'constructor', ->
     it 'should instantiate without error', ->
       buttonBar = new KDButtonBar
       assert.ok buttonBar


Stacks: Define a helper method

Stacks: Define a helper method

--- client/stacks/lib/index.coffee
+++ client/stacks/lib/index.coffee
@@ -76,6 +76,11 @@
     view.setClass className

+  getStackTemplatesViewByName: (name) ->
+
+    @mainView.tabs.getPaneByName(name)?.mainView
+
+
   reloadStackTemplatesList: ->

     { view } = @getOptions()


Improvements to clipping in terrain streamer

--- client/terrain/streamer.coffee
+++ client/terrain/streamer.coffee
@@ -55,6 +55,7 @@

 class TerrainStreamer
   constructor: (@pxWidth) ->
+    @pxHeight = @pxWidth
     @terrain = document.createElement("canvas")
     @terrain.width =  @pxWidth
     @terrain.height = @pxWidth
@@ -99,12 +100,12 @@
     if tile.terrainImage.width > 0
       @terrainCtx.drawImage(tile.terrainImage,
         sourceRect.min.x, (tile.pxHeight-sourceRect.max.y), sourceRect.getSize().x, sourceRect.getSize().y,
-        targetRect.min.x, targetRect.min.y, targetRect.getSize().x, targetRect.getSize().y)
+        targetRect.min.x, (@pxHeight-targetRect.max.y), targetRect.getSize().x, targetRect.getSize().y)
     # Only draw if the map has been loaded
     if tile.mapImage.width > 0
       @mapCtx.drawImage(tile.mapImage,
         sourceRect.min.x*MAP_SCALE, (tile.pxHeight-sourceRect.max.y)*MAP_SCALE, sourceRect.getSize().x*MAP_SCALE, sourceRect.getSize().y*MAP_SCALE,
-        targetRect.min.x*MAP_SCALE, targetRect.min.y*MAP_SCALE, targetRect.getSize().x*MAP_SCALE, targetRect.getSize().y*MAP_SCALE)
+        targetRect.min.x*MAP_SCALE, (@pxHeight-targetRect.max.y)*MAP_SCALE, targetRect.getSize().x*MAP_SCALE, targetRect.getSize().y*MAP_SCALE)
     # We will return the effective resolution even if the tile was not actually loaded yet. Even if that resolution is only incoming
     # we will consider it present in the system.
     effectiveResolution


Fix nagios list javascript

Fix nagios list javascript

--- lib/dashing-contrib/assets/widgets/nagios_list/nagios_list.coffee
+++ lib/dashing-contrib/assets/widgets/nagios_list/nagios_list.coffee
@@ -1,8 +1,8 @@
 class Dashing.NagiosList extends Dashing.Widget
   @accessor 'hasCritical', ->
-    if (@get('critical').length > 0) then true else false
+    if (@get('critical') && @get('critical').length > 0) then true else false
   @accessor 'hasWarning', ->
-    if (@get('warning').length > 0) then true else false
+    if (@get('warning') && @get('warning').length > 0) then true else false

   @accessor 'criticalMap', ->
     @_groupBy(@get('critical'))


Remove unneeded (un)subscribes nominations.coffee

--- resources/assets/coffee/react/beatmap-discussions/nominations.coffee
+++ resources/assets/coffee/react/beatmap-discussions/nominations.coffee
@@ -50,13 +50,6 @@
     .always LoadingOverlay.hide

   componentDidMount: ->
-    $.subscribe 'beatmapset:nominate.beatmapset-nominations', @nominate
-    $.subscribe 'beatmapset:disqualify.beatmapset-nominations', @disqualify
-
-  componentWillUnmount: ->
-    $.unsubscribe '.beatmapset-nominations'
-
-  componentDidMount: ->
     osu.pageChange()

   componentDidUpdate: ->
@@ -67,14 +60,14 @@
     mapCanBeNominated = (@props.beatmapset.status == 'pending')
     mapIsQualified = (@props.beatmapset.status == 'qualified')

+    return unless mapCanBeNominated or mapIsQualified
+
     if mapIsQualified
       rankingETA = @props.beatmapset.nominations.data.ranking_eta

     if mapCanBeNominated
       nominations = @props.beatmapset.nominations.data
       disqualification = nominations.disqualification
-
-    return unless mapCanBeNominated or mapIsQualified

     div className: bdn,
       if disqualification


just text

just text

--- resources/assets/coffee/react/profile-page/recent-activity.coffee
+++ resources/assets/coffee/react/profile-page/recent-activity.coffee
@@ -78,9 +78,8 @@
       when 'beatmapsetDelete'
         text = div
           className: 'profile-extra-entries__text'
-          dangerouslySetInnerHTML:
-            __html: osu.trans 'events.beatmapset_delete',
-              beatmapset: event.beatmapset.title
+          osu.trans 'events.beatmapset_delete',
+            beatmapset: event.beatmapset.title

       when 'beatmapsetRevive'
         text = div


Fix log call signature

Fix log call signature

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -229,7 +229,7 @@
 					else if event?
 						return cb(null, false)
 					else
-						logger.log user_id, event, "track changes onboarding not shown yet to this user"
+						logger.log { user_id, event }, "track changes onboarding not shown yet to this user"
 						return cb(null, true)
 			showPerUserTCNotice: (cb) ->
 				cb = underscore.once(cb)
@@ -248,7 +248,7 @@
 					else if event?
 						return cb(null, false)
 					else
-						logger.log user_id, event, "per user track changes notice not shown yet to this user"
+						logger.log { user_id, event }, "per user track changes notice not shown yet to this user"
 						return cb(null, true)
 			showAutoCompileOnboarding: (cb) ->
 				cb = underscore.once(cb)
@@ -275,7 +275,7 @@
 					else if event?
 						return cb(null, false)
 					else
-						logger.log user_id, event, "autocompile onboarding not shown yet to this user"
+						logger.log { user_id, event }, "autocompile onboarding not shown yet to this user"
 						return cb(null, true)
 		}, (err, results)->
 			if err?


Bring down proportion to 10%

Bring down proportion to 10%

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -195,7 +195,7 @@
 						viewModel.showUserDetailsArea = false

 					paidUser = user.features?.github # use a heuristic for paid account
-					freeUserProportion = 0.85
+					freeUserProportion = 0.10
 					sampleFreeUser = parseInt(user._id.toString().slice(-2), 16) < freeUserProportion * 255
 					showFrontWidget = paidUser or sampleFreeUser
 					logger.log {paidUser, sampleFreeUser, showFrontWidget}, 'deciding whether to show front widget'


Fix autocompile rollout logic

Fix autocompile rollout logic

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -258,7 +258,7 @@
 				counter = parseInt(user_id.toString().substring(18, 24), 16)

 				rolloutProportion = 0
-				if counter % 1000 < rolloutProportion
+				if counter % 1000 > rolloutProportion
 					# Don't show if user is not part of roll out
 					return cb(null, false)
 				userSignupDate = new Date(timestamp * 1000)


Labels source diff

Labels source diff

--- app/assets/javascripts/application/translation_workbench.js.coffee
+++ app/assets/javascripts/application/translation_workbench.js.coffee
@@ -134,7 +134,7 @@
             match_element.mouseenter ->
               match_element.prettyTextDiff
                 diffContainer: match_wrapper.find('.diff')
-              match_wrapper.find('.diff').prepend(Array(18).join(' '))
+              match_wrapper.find('.diff').prepend($('<span/>').addClass("match-percentage").text("Source Diff: "))
             match_element.mouseleave ->
               match_wrapper.find('.diff').empty()


teams: welcome: added a clearfix and fixed markup

--- client/landing/site.landing/coffee/team/tabs/teamwelcometab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamwelcometab.coffee
@@ -70,20 +70,18 @@

Welcome

You are about to join to #{KD.config.group.title} on Koding

-

+

         and 34 of your team members are already here.

         Your invitation was sent to:
-        <email>#{KD.utils.getTeamData().invitation.email}<email>
+        <address>#{KD.utils.getTeamData().invitation.email}</address>

-

         Is the email address above correct?

         {{> @correct}}

-

If that's not right please contact the team administrators to get a new invitation.

     """


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/number_extensions.coffee
+++ src/number_extensions.coffee
@@ -76,7 +76,8 @@
 ###*
 @name circularPoint
 ###
-Number::circularPoints = (n, block) ->
+Number::circularPoints = (block) ->
+  n =
   n.times (i) ->
     block(Point.fromAngle((i/n).turns))


rtmp: Change require grouping

rtmp: Change require grouping

--- rtmp.coffee
+++ rtmp.coffee
@@ -3,8 +3,8 @@
 net           = require 'net'
 crypto        = require 'crypto'
 Sequent       = require 'sequent'
+
 RTMPHandshake = require './rtmp_handshake'
-
 codecUtils    = require './codec_utils'
 config        = require './config'


Ignore more types of whitespace

Ignore more types of whitespace

autocomplete-plus was activating on `,`, `:`, and `}`. I couldn't come up with
a reason why anyone would want to complete on those characters so I
added them to the list.

Fixes #177

--- lib/gocodeprovider.coffee
+++ lib/gocodeprovider.coffee
@@ -31,7 +31,9 @@
       index = buffer.characterIndexForPosition(options.bufferPosition)
       offset = 'c' + index.toString()
       text = options.editor.getText()
-      return resolve() if text[index - 1] is ')' or text[index - 1] is ';'
+      switch text[index - 1]
+        when ')', ';', ':', ',', '}', ' '
+          return resolve()
       quotedRange = options.editor.displayBuffer.bufferRangeForScopeAtPosition('.string.quoted', options.bufferPosition)
       return resolve() if quotedRange


Add additional paths to config

Add additional paths to config

--- config/config.coffee
+++ config/config.coffee
@@ -11,6 +11,8 @@
   root: rootPath
   controllers: rootPath + '/app/controllers'
   models: rootPath + '/app/models'
+  views: rootPath + '/app/views'
+  static: rootPath + '/public'

   server:
     port: '3000'


Use is-invalid for invalid dates

Use is-invalid for invalid dates

And removed the "valid" class from the valid dates since we don't currently
perform any additional styling to them.

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -44,10 +44,7 @@

   render: ->
-    className = if moment(@props.date).startOf('day') < TimeStore.getNow()
-      'invalid'
-    else
-      'valid'
+    className = 'is-invalid' if moment(@props.date).startOf('day') < TimeStore.getNow()
     {@props.label}

 TutorDateInput = React.createClass


Small mistake: Data Structures: Heap

Small mistake: Data Structures: Heap

--- src/data-structures/heap.coffee
+++ src/data-structures/heap.coffee
@@ -39,7 +39,7 @@
             largestPos = rightPos

         if largestPos != pos
-            @array[pos], @array[largestPos] = @array[largestPos], @array[pos]
+            [@array[pos], @array[largestPos]] = [@array[largestPos], @array[pos]]
             @maxHeapfy(largestPos, arrayLength)

 @algCoffee = if @algCoffee then @algCoffee else {}


Replace `getPaths()[0]` instead of deprecated function `path`


Fixed bad uri output

Fixed bad uri output

--- lib/services/mongo.coffee
+++ lib/services/mongo.coffee
@@ -21,18 +21,20 @@
     if connection.user and connection.password
       url = connection.user + ":" + connection.password + "@" + url

+
     @connections[connection.name] = mongoose.createConnection "mongodb://#{url}"
     @connections[connection.name].on "error", (error) ->
-      console.log " ⚑".red, "Error connection:".grey, error.red
+      console.log "⚑".red, "Error connection:".grey, error.red

       promise.done true, null
       process.exit()
     @connections[connection.name].on "connected", (error) ->
-      console.log " ✓".green, "MongoDB:#{connection.name}", "listening at".grey, "#{connection.host}:#{connection.port}/#{connection.db}".underline.blue
+      console.log "✓".green, "MongoDB:#{connection.name}", "connected at".grey, "#{connection.host}/#{connection.db}".underline.green
       promise.done null, true
+
     promise

   close: ->
     for name of @connections
       @connections[name].close ->
-        console.log " ✓".green, "MongoDB:/#{name}", "closed connection correctly.".grey
+        console.log "✓".green, "MongoDB:/#{name}", "closed connection correctly.".grey


File extension option for Coffee/Iced compiler task

--- lib/tintan/compile.coffee
+++ lib/tintan/compile.coffee
@@ -18,18 +18,20 @@
 class Coffee

   DEFAULT_OPTIONS =
-    src: 'src/coffee'        # directory to take .coffee files from
+    src: 'src/coffee'        # directory to take .coffee or .iced files from
     target: 'Resources'      # directory to put .js files into
+    ext: '\.(coffee|iced)$'  # extensions to compile
     name: 'compile:coffee'   # name of the compiler task to generate

   init: (tintan, @options = {})->
     @options[k] = v for k,v of DEFAULT_OPTIONS when !@options.hasOwnProperty(k)
+    @options.ext = (new RegExp @options.ext) if typeof @options.ext is 'string'
     options = @options

     from = Tintan.$._(options.src)
     target = Tintan.$._(options.target)
-    map = @map = compilerMap from, /\.coffee$/, (f)-> path.join(target, f).replace(/\.coffee$/, '.js')
+    map = @map = compilerMap from, options.ext, (f)-> path.join(target, f).replace(options.ext, '.js')

     compile = @compile
     sources = (s for s of map)


Don't perform discovery when no credentials are set

--- lib/assets/javascripts/models/meta_profile.js.coffee
+++ lib/assets/javascripts/models/meta_profile.js.coffee
@@ -12,12 +12,19 @@
     else
       params = {entity:entity}

+    failureFn = (res, xhr) =>
+      @trigger("fetch:failure", params, res, xhr)
+      @trigger("#{entity}:fetch:failure", params, res, xhr)
+      options.failure?(res, xhr)
+      options.complete?(res, xhr)
+
+    if !TentStatus.tent_client.credentials
+      failureFn()
+      return
+
     completeFn = (res, xhr) =>
       if xhr.status != 200
-        @trigger("fetch:failure", params, res, xhr)
-        @trigger("#{entity}:fetch:failure", params, res, xhr)
-        options.failure?(res, xhr)
-        options.complete?(res, xhr)
+        failureFn(res, xhr)
         return

       constructorFn = @


JMachine: increase the limit to 50 per machine

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -355,9 +355,9 @@
     for user in targets
       users = addUser users, { user, asOwner, permanent }

-    if users.length > 10
+    if users.length > 50
       callback new KodingError \
-        'Machine sharing is limited up to 10 users.'
+        'Machine sharing is limited up to 50 users.'
     else
       @update $set: { users }, (err) =>
         informAccounts targets, @getAt('uid'), 'added'


Fix integration form duplicate field syncing

--- app/assets/javascripts/admin/views/home/add_integration.js.coffee
+++ app/assets/javascripts/admin/views/home/add_integration.js.coffee
@@ -23,7 +23,7 @@
     @$el.find('input').attr('disabled', true)

     # Copy text across duplicate inputs across consumers
-    @$el.find('input.param').bind "keyup paste", ->
+    @$el.find('input').bind "keyup paste", ->
       current = $(@)
       duplicates = $("[name='#{current.attr('name')}']")
       if duplicates.length > 1


Update docs on DisplayMarkerLayer

Update docs on DisplayMarkerLayer

--- src/display-marker-layer.coffee
+++ src/display-marker-layer.coffee
@@ -71,19 +71,43 @@
   Section: Marker creation
   ###

-  # Essential: Create a marker on this layer with the given range in screen
-  # coordinates.
-  #
-  # See the documentation for {TextBuffer::markRange}.
-  #
-  # Returns a {DisplayMarker}.
-  markScreenRange: (screenRange, properties) ->
+  # Public: Create a marker with the given screen range.
+  #
+  # * `range` A {Range} or range-compatible {Array}
+  # * `options` A hash of key-value pairs to associate with the marker. There
+  #   are also reserved property names that have marker-specific meaning.
+  #   * `reversed` (optional) {Boolean} Creates the marker in a reversed
+  #     orientation. (default: false)
+  #   * `persistent` (optional) {Boolean} Whether to include this marker when
+  #     serializing the buffer. (default: true)
+  #   * `invalidate` (optional) {String} Determines the rules by which changes
+  #     to the buffer *invalidate* the marker. (default: 'overlap') It can be
+  #     any of the following strategies, in order of fragility:
+  #     * __never__: The marker is never marked as invalid. This is a good choice for
+  #       markers representing selections in an editor.
+  #     * __surround__: The marker is invalidated by changes that completely surround it.
+  #     * __overlap__: The marker is invalidated by changes that surround the
+  #       start or end of the marker. This is the default.
+  #     * __inside__: The marker is invalidated by changes that extend into the
+  #       inside of the marker. Changes that end at the marker's start or
+  #       start at the marker's end do not invalidate the marker.
+  #     * __touch__: The marker is invalidated by a change that touches the marked
+  #       region in any way, including changes that end at the marker's
+  #       start or start at the marker's end. This is the most fragile strategy.
+  #   * `clipDirection` {String} If `'backward'`, returns the first valid
+  #     position preceding an invalid position. If `'forward'`, returns the
+  #     first valid position following an invalid position. If `'closest'`,
+  #     returns the first valid position closest to an invalid position.
+  #     Defaults to `'closest'`. Applies to the start and end of the given range.
+  #
+  # Returns a {DisplayMarker}.
+  markScreenRange: (screenRange, options) ->
     screenRange = Range.fromObject(screenRange)
-    bufferRange = @displayLayer.translateScreenRange(screenRange, properties)
-    @createDisplayMarker(@bufferMarkerLayer.markRange(bufferRange, properties))
-
-  # Public: Create a marker on this layer with the given screen position and no
-  # tail.
+    bufferRange = @displayLayer.translateScreenRange(screenRange, options)
+    @createDisplayMarker(@bufferMarkerLayer.markRange(bufferRange, options))
+
+  # Public: Create a marker on this layer with its head at the given screen
+  # position and no tail.
   #
   # * `screenPosition` A {Point} or point-compatible {Array}
   # * `options` (optional) An {Object} with the following keys:
@@ -113,16 +137,37 @@
     bufferPosition = @displayLayer.translateScreenPosition(screenPosition, options)
     @createDisplayMarker(@bufferMarkerLayer.markPosition(bufferPosition, options))

-  # Essential: Create a marker on this layer with the given range in buffer
-  # coordinates.
-  #
-  # See the documentation for {TextBuffer::markRange}.
-  markBufferRange: (bufferRange, properties) ->
+  # Public: Create a marker with the given buffer range.
+  #
+  # * `range` A {Range} or range-compatible {Array}
+  # * `options` A hash of key-value pairs to associate with the marker. There
+  #   are also reserved property names that have marker-specific meaning.
+  #   * `reversed` (optional) {Boolean} Creates the marker in a reversed
+  #     orientation. (default: false)
+  #   * `persistent` (optional) {Boolean} Whether to include this marker when
+  #     serializing the buffer. (default: true)
+  #   * `invalidate` (optional) {String} Determines the rules by which changes
+  #     to the buffer *invalidate* the marker. (default: 'overlap') It can be
+  #     any of the following strategies, in order of fragility:
+  #     * __never__: The marker is never marked as invalid. This is a good choice for
+  #       markers representing selections in an editor.
+  #     * __surround__: The marker is invalidated by changes that completely surround it.
+  #     * __overlap__: The marker is invalidated by changes that surround the
+  #       start or end of the marker. This is the default.
+  #     * __inside__: The marker is invalidated by changes that extend into the
+  #       inside of the marker. Changes that end at the marker's start or
+  #       start at the marker's end do not invalidate the marker.
+  #     * __touch__: The marker is invalidated by a change that touches the marked
+  #       region in any way, including changes that end at the marker's
+  #       start or start at the marker's end. This is the most fragile strategy.
+  #
+  # Returns a {DisplayMarker}.
+  markBufferRange: (bufferRange, options) ->
     bufferRange = Range.fromObject(bufferRange)
-    @createDisplayMarker(@bufferMarkerLayer.markRange(bufferRange, properties))
-
-  # Public: Create a marker on this layer with the given buffer position and no
-  # tail.
+    @createDisplayMarker(@bufferMarkerLayer.markRange(bufferRange, options))
+
+  # Public: Create a marker on this layer with its head at the given buffer
+  # position and no tail.
   #
   # * `bufferPosition` A {Point} or point-compatible {Array}
   # * `options` (optional) An {Object} with the following keys:


Display row numbers for data rows in frame-output

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -498,6 +498,7 @@
       if validMetrics = model.output.validMetrics
         inspections[ 'Validation Metrics' ] = inspectMultinomialPrediction2 'Validation Metrics', validMetrics

+
     else if modelCategory is 'Regression'
       if trainMetrics = model.output.trainMetrics
         inspections[ 'Training Metrics' ] = inspectRegressionPrediction2 'Training Metrics', trainMetrics
@@ -787,6 +788,8 @@
           createList column.label, parseNulls column.str_data
         else # uuid / etc.
           createList column.label, parseNulls column.data
+
+    vectors.unshift createVector 'Row', TNumber, (rowIndex + 1 for rowIndex in [frame.off ... frame.len])

     createDataframe 'data', vectors, (sequence frame.len - frame.off), null,
       description: 'A partial list of rows in the H2O Frame.'


KiteSubscription:added code to kite model to use as token

--- workers/social/lib/social/models/kite.coffee
+++ workers/social/lib/social/models/kite.coffee
@@ -5,6 +5,7 @@
   {permit}            = require './group/permissionset'
   {signature}         = require 'bongo'
   {Relationship}      = jraphical
+  { v4: createId }    = require 'node-uuid'

   @trait __dirname, '../traits/protected'

@@ -16,6 +17,7 @@
     schema                :
       name                : String
       description         : String
+      kiteCode            : String
       createdAt           :
         type              : Date
         default           : -> new Date
@@ -50,6 +52,7 @@
   @create: permit 'create kite',
     success:(client, formData, callback)->
       kite = new JKite formData
+      kite.kiteCode = createId()
       kite.save (err)->
         return  callback new KodingError "kite couldn't saved" if err
         account = client.connection.delegate


Fix error when app is not defined

Fix error when app is not defined


Follow up - f54df50 - Fixes #3040: Use focusout instead of click event to stabilize tests.

--- app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee
@@ -60,7 +60,6 @@

       @setArticleTypePre(data.type.name, data.signaturePosition)

-      @openTextarea(null, true)
       for key, value of data.article
         if key is 'body'
           @$("[data-name=\"#{key}\"]").html(value)
@@ -150,14 +149,9 @@
     if @subscribeIdTextModule
       App.Ticket.unsubscribe(@subscribeIdTextModule)

-    @releaseGlobalClickEvents()
-
-  releaseGlobalClickEvents: ->
-    $(window).off 'click.ticket-zoom-select-type'
-    $(window).off 'click.ticket-zoom-textarea'
+    $(window).off('click.ticket-zoom-select-type')

   render: ->
-    @releaseGlobalClickEvents()
     ticket = App.Ticket.fullLocal(@ticket_id)

     @html App.view('ticket_zoom/article_new')(
@@ -517,7 +511,7 @@
       options:
         duration: duration
         easing: 'easeOutQuad'
-        complete: => $(window).off('click.ticket-zoom-textarea').on('click.ticket-zoom-textarea', @closeTextarea)
+        complete: => @textarea.off('focusout.ticket-zoom-textarea').on('focusout.ticket-zoom-textarea', @closeTextarea)

     @textBubble.velocity
       properties:
@@ -562,7 +556,7 @@

   closeTextarea: =>
     if !@textarea.text().trim() && !@attachments.length && not @isIE10()
-      $(window).off 'click.ticket-zoom-textarea'
+      @textarea.off('focusout.ticket-zoom-textarea')

       @textarea.velocity
         properties:


[skip] Snapped milestones to weeks when resizing or dragging (15m)

--- app/assets/javascripts/houston/roadmap/views/edit_roadmap_view.coffee
+++ app/assets/javascripts/houston/roadmap/views/edit_roadmap_view.coffee
@@ -150,6 +150,9 @@
           $(@).css(left: view.drag.milestonesAfterPositions[i] + delta)

       stop: (event, ui)->
+        ui.element.resizable 'option', 'grid', false
+        ui.element.draggable 'option', 'grid', false
+
         id = ui.element.attr('data-cid')
         milestone = view.milestones.get(id)
         return unless milestone
@@ -197,7 +200,11 @@
         view.drag.$milestonesAfter.each (i)->
           $(@).css(left: view.drag.milestonesAfterPositions[i] + delta)

-      stop: -> view.drag = null
+      stop: (e, ui)->
+        view.drag = null
+        $(e.target).resizable 'option', 'grid', false
+        $(e.target).draggable 'option', 'grid', false
+
       revert: ($target)-> !$target or !$target.is('.roadmap-band')

@@ -214,6 +221,10 @@
     maxStartDate = null
     milestonesAfter = []

+    grid = [@weekWidth(), @bandHeight()]
+    $milestone.resizable 'option', 'grid', grid
+    $milestone.draggable 'option', 'grid', grid
+
     for milestone in milestonesInBand
       if milestone.get('endDate') < startDate
         minStartDate = 2.days().after(milestone.get('endDate'))
@@ -235,3 +246,12 @@
       milestonesAfterPositions: milestonesAfterPositions
       offsetLeft: ui.position.left
       originalLeft: $milestone.position().left
+
+
+
+  bandHeight: ->
+    38
+
+  weekWidth: ->
+    date = d3.time.saturdays(@x.domain()...)[0]
+    @x(7.days().after(date)) - @x(date)


javascript work-around in order to not display destroyed assets in the activity log widget

--- app/assets/javascripts/locomotive/views/dashboard/show_view.js.coffee
+++ app/assets/javascripts/locomotive/views/dashboard/show_view.js.coffee
@@ -9,6 +9,13 @@
   events:
     'ajax:beforeSend  a[data-behavior~=load-more]': 'loading_activity_feed'
     'ajax:success     a[data-behavior~=load-more]': 'refresh_activity_feed'
+
+  render: ->
+    super
+
+    # replace the img by the name of the file
+    @$('.assets img').error ->
+      $(this).parent().html($(this).attr('alt'))

   loading_activity_feed: (event) -> $(event.target).button('loading')


Remove hideWaiting call from error and success[#86637636]

- complete handles both success and error.

--- app/assets/javascripts/c_rater.coffee
+++ app/assets/javascripts/c_rater.coffee
@@ -73,12 +73,10 @@
           q.data = $(q).serialize()
         @fbOnFeedback.activate(data.submission_id)
         @submissionCount += 1
-        @hideWaiting()
         @updateSubmitBtnText()
         @updateView(data.feedback_items)
         @scrollToHeader()
       error: =>
-        @hideWaiting()
         alert(t('ARG_BLOCK.SUBMIT_ERROR'))
         # Make sure that user can proceed anyway!
         @enableForwardNavigation()


add questions list for player

add questions list for player

--- app/assets/javascripts/game.js.coffee
+++ app/assets/javascripts/game.js.coffee
@@ -7,10 +7,10 @@
   }

   state = undefined
-  _properties =
-    nose: ['small, big']
-    hair: ['brown', 'nothing']
-    face: ['smart', 'triangle']
+#  _properties =
+#    nose: ['small, big']
+#    hair: ['brown', 'nothing']
+#    face: ['smart', 'triangle']

   face_values = {
     male: 'male'
@@ -602,16 +602,45 @@

   proc_question_panel = ->
-    if state == states['player1'] # comp turn
-      question_panel.html('Comp turn')
+    data = if state == states['player1'] # comp turn
+      """
+
Comp turn

+
+
+      """
     else
-      question_panel.html('Your turn')
-
-
-  properties = -> Object.keys(_properties)
-
-
-  property_variants = (property) -> _properties[property]
+      """
+
Your turn

+
Please ask a question

+

+          A person have a <select class='property'>#{properties()}</select>
+

+      """
+
+
+    question_panel.html(data)
+
+  hair_options = ->
+    res = ''
+    for k,v of face_values['hair_color']
+      res += "<option>#{v} colored hair</option>"
+    res
+
+  properties = ->
+    #    Object.keys(_properties)
+
+    """
+      <option>male gender</option>
+      <option>female gender</option>
+      <option>glasses</option>
+      <option>visible teeth</option>
+      <option>visible beard or mustaches</option>
+      <option>no hair</option>
+      #{hair_options()}
+    """
+
+#
+#  property_variants = (property) -> _properties[property]

   choose_property_variant = (variant) ->
@@ -633,8 +662,8 @@

   return {
     initiate: initiate
-    properties: properties
-    property_variants: property_variants
-    choose_property_variant: choose_property_variant
+#    properties: properties
+#    property_variants: property_variants
+#    choose_property_variant: choose_property_variant
     get_state: -> return state;
   }


Minor main.js refactor and docs

Minor main.js refactor and docs

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -21,8 +21,8 @@
       closest_submit.enable()

 $ ->
-  $(".one_click_select").live 'click', ->
-    $(this).select()
+  # Click a .one_click_select field, select the contents
+  $(".one_click_select").live 'click', -> $(this).select()

   # Disable form buttons while a form is submitting
   $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
@@ -66,10 +66,12 @@
     $(this).text(previewLinkText)

     note = $('#note_note').val()
-    note = 'Nothing to preview' if note.trim().length is 0
-    $.post($(this).attr('href'), {note: note}, (data) ->
-      $('#preview-note').html(data)
-    )
+
+    if note.trim().length == 0
+      $('#preview-note').text("Nothing to preview.")
+    else
+      $.post $(this).attr('href'), {note: note}, (data) ->
+        $('#preview-note').html(data)

     $('#preview-note, #note_note').toggle()
     e.preventDefault()


Mark All Read no longer needs a dashboard reload

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -321,9 +321,8 @@
     if window.loaded_location == 'home'
       document.title = 'CSH WebNews'
       $('#unread_line').text('You have no unread posts.')
-      $('#activity_feed').text('Reloading activity feed...')
-      $('#group_view').append(chunks.spinner.clone())
-      after_func = -> window.onhashchange()
+      $('.activity a').removeClass('unread')
+      $('.activity .counter.unread').remove()
     else
       abort_active_scroll()
       $('#posts_list tbody tr').removeClass('unread')


Non-AJAX browser like link behavior for client-side pods navigator.

Loading the initial pods at first. Change the index on each click on "next" / "prev". Show corresponding pods as soon as possible.

--- app/assets/javascripts/pods.js.coffee
+++ app/assets/javascripts/pods.js.coffee
@@ -45,6 +45,7 @@
         if (gon.pods_index[gon.pods_index.length - 1][0] == chunk_id)
           @delegate.didLoadAll()
 class @PodsController
+  delegate: null
   constructor: ->
     @progressBar = new PodsProgressBar()
     @loader = new PodsLoader()
@@ -56,6 +57,8 @@
   didLoad: (chunk_id, pods) ->
     @progressBar.update(chunk_id)
     @pods = @pods.concat(pods)
+    if @delegate && @delegate.podsDidChange
+      @delegate.podsDidChange()
   didLoadAll: ->
     @progressBar.finish()
 class @PodsNavigator
@@ -63,6 +66,7 @@
   max_size: 50
   constructor: (podsController) ->
     @podsController = podsController
+    @podsController.delegate = @
     @render()
   render: ->
     controller = @
@@ -91,6 +95,9 @@
     @index -= @max_size
     @renderPods()
     @render()
+  podsDidChange: ->
+    @renderPods()
+    @render()
 ready = ->
   podsController = new PodsController
   podsController.loadPods()


Users name sort-by case insensitive

Users name sort-by case insensitive

--- app/coffee/modules/issues/list.coffee
+++ app/coffee/modules/issues/list.coffee
@@ -181,7 +181,7 @@

                 obj.selected = true if isSelected("assignedTo", obj.id)
                 return obj
-            @scope.filters.assignedTo = _.sortBy(@scope.filters.assignedTo, "name")
+            @scope.filters.assignedTo = _.sortBy(@scope.filters.assignedTo, (u) -> u.name.toUpperCase())

             @scope.filters.createdBy = _.map data.created_by, (t) =>
                 obj = {
@@ -196,7 +196,7 @@

                 obj.selected = true if isSelected("createdBy", obj.id)
                 return obj
-            @scope.filters.createdBy = _.sortBy(@scope.filters.createdBy, "name")
+            @scope.filters.createdBy = _.sortBy(@scope.filters.createdBy, (u) -> u.name.toUpperCase())

             @scope.filters.tags = _.map data.tags, (t) =>
                 obj = {


Refactor API calls Set initial favourited state from a prop. Only call the API when we actually need to get the favourites collection.

--- app/collections/favorites-button.cjsx
+++ app/collections/favorites-button.cjsx
@@ -11,11 +11,13 @@
     subject: React.PropTypes.object # a subject response from panoptes
     project: React.PropTypes.object # a project response from panoptes
     user: React.PropTypes.object
+    isFavorite: React.PropTypes.bool

   getDefaultProps: ->
     subject: null
     project: null
     user: null
+    isFavorite: false

   getInitialState: ->
     favorites: null
@@ -46,19 +48,14 @@
       Promise.resolve false

   componentWillMount: ->
-    @findFavoriteCollection()
-      .then (favorites) =>
-        @setState {favorites}
-        @findSubjectInCollection()
-        .then (favorited) =>
-          @setState {favorited}
+    favorited = @props.isFavorite
+    @setState {favorited}

   componentDidUpdate: (prevProps) ->
     if prevProps.subject isnt @props.subject
-      @findSubjectInCollection()
-      .then (favorited) =>
-        @setState {favorited}
+      favorited = @props.isFavorite
+      @setState {favorited}

   addSubjectTo: (collection) ->
     @setState favorited: true
@@ -69,22 +66,39 @@
     collection.removeLink('subjects', [@props.subject.id.toString()])

   createFavorites: ->
-    display_name = getFavoritesName(@props.project)
-    project = @props.project?.id
-    subjects = [@props.subject.id]
-    favorite = true
+    new Promise (resolve, reject) =>
+      @findFavoriteCollection()
+        .catch (err) ->
+          reject err
+        .then (favorites) =>
+          if favorites?
+            @setState {favorites}
+            resolve favorites
+          else
+            display_name = getFavoritesName(@props.project)
+            project = @props.project?.id
+            subjects = []
+            favorite = true

-    links = {subjects}
-    links.projects = [ project ] if project?
-    collection = {favorite, display_name, links}
-
-    @setState favorited: true
-    apiClient.type('collections').create(collection).save()
+            links = {subjects}
+            links.projects = [ project ] if project?
+            collection = {favorite, display_name, links}
+            apiClient.type('collections')
+              .create(collection)
+              .save()
+              .catch (err) ->
+                reject err
+              .then (favorites) =>
+                @setState {favorites}
+                resolve favorites

   toggleFavorite: ->
     if @props.user?
       if not @state.favorites?
+        @setState favorited: true
         @createFavorites()
+        .then (favorites) =>
+          @addSubjectTo(favorites)
         @logSubjLike 'favorite'
       else if @state.favorited
         @removeSubjectFrom(@state.favorites)


Remove unused settings/index controller

Remove unused settings/index controller

--- app/controllers/settings/index.coffee
+++ app/controllers/settings/index.coffee
@@ -1,22 +0,0 @@
-`import Ember from 'ember'`
-
-Controller = Ember.Controller.extend
-  settings: Ember.computed.alias('model.settings')
-
-  settingsChanged: (->
-    value = @get('settings.maximum_number_of_builds')
-    console.log value
-    if parseInt(value) > 0 || value == '0' || value == 0
-      @set('settings.maximum_number_of_builds_valid', '')
-      @get('model').saveSettings(@get('settings')).then null, ->
-        Travis.flash(error: 'There was an error while saving settings. Please try again.')
-    else
-      @set('settings.maximum_number_of_builds_valid', 'invalid')
-  ).observes('settings.maximum_number_of_builds')
-
-  actions:
-    save: ->
-      @get('model').saveSettings(@get('settings')).then null, ->
-        Travis.flash(error: 'There was an error while saving settings. Please try again.')
-
-`export default Controller`


Add wide to featured links

Add wide to featured links

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -163,7 +163,7 @@
       title: 'This is a page all about Bitty'
       subtitle: "If you are interested in cats, and specifically the best cat in the world, you've come to the right place"
       image_url: '/bitty/:version'
-      image_versions: ["large_square", "medium_square", "small_square", "medium_rectangle", "large_rectangle","small_rectangle"]
+      image_versions: ["large_square", "medium_square", "small_square", "medium_rectangle", "large_rectangle", "small_rectangle", "wide"]
       item_type: 'FeaturedLink'

     when 'user'


Number#even and Number#odd

Number#even and Number#odd

--- src/number_extensions.coffee
+++ src/number_extensions.coffee
@@ -119,6 +119,30 @@
     0

 ###*
+Returns true if this number is even (evenly divisible by 2).
+
+@name even
+@methodOf Number#
+
+@type Boolean
+@returns true if this number is an even integer, false otherwise.
+###
+Number::even = ->
+  this % 2 == 0
+
+###*
+Returns true if this number is odd (has remainder of 1 when divided by 2).
+
+@name odd
+@methodOf Number#
+
+@type Boolean
+@returns true if this number is an odd integer, false otherwise.
+###
+Number::odd = ->
+  this % 2 == 1
+
+###*
 Calls iterator the specified number of times, passing in the number of the
 current iteration as a parameter: 0 on first call, 1 on the second call, etc.


Remove logs

Remove logs

--- mobile/apps/auth/index.coffee
+++ mobile/apps/auth/index.coffee
@@ -20,8 +20,6 @@
 app.post '/forgot_password', routes.submitForgotPassword
 app.get '/reset_password', routes.resetPassword

-console.log 'loginPagePath', loginPagePath
-
 # Login and signup pages
 app.get '/log_in', routes.login
 app.get loginPagePath, routes.login


try something

try something

--- config.coffee
+++ config.coffee
@@ -7,7 +7,7 @@
   ROOT: __dirname
   DB:
     REDIS:
-      HOST: process.env.DB_REDIS_HOST or 'redis://rediscloud:password@localhost:6379'
+      HOST: process.env.DB_REDIS_HOST or 'redis://rediscloud:password@localhost:'
       PORT: process.env.DB_REDIS_PORT or 6379
       PREFIX: 'lendme:'
   EXPRESS:


fix for hiding proposal

fix for hiding proposal

--- @client/edit_proposal.coffee
+++ @client/edit_proposal.coffee
@@ -484,7 +484,7 @@
     category = null if category == ''

     active = document.getElementById('open_for_discussion').checked
-    hide_on_homepage = document.getElementById('listed_on_homepage').checked
+    hide_on_homepage = !document.getElementById('listed_on_homepage').checked

     if @props.fresh
       proposal =


Tweak textarea input control; trimming unnecessary events, better layout when animating.

--- core/app/backbone/factlink/text_area_view.coffee
+++ core/app/backbone/factlink/text_area_view.coffee
@@ -7,7 +7,7 @@
 class Backbone.Factlink.TextAreaView extends Backbone.Marionette.ItemView
   template: 'generic/text_area'
   events:
-    'click': 'focusInput'
+    # 'click': 'focusInput'
     'keydown .typeahead': 'parseKeyDown'
     'keyup .typeahead': 'updateModel'

@@ -21,7 +21,7 @@
   initialize: ->
     @bindTo @model, 'change', @updateHtml, this

-  focusInput: -> @$inputField().focus()
+  # focusInput: -> @$inputField().focus()

   parseKeyDown: (e) ->
     eventHandled = false
@@ -47,4 +47,4 @@

   $inputField: -> @$('.typeahead')
   onRender: ->
-    @$el.find('textarea').autosize()
+    @$el.find('textarea').autosize append: '\n'


More readable

More readable

--- core/app/backbone/views/topic_search_view.coffee
+++ core/app/backbone/views/topic_search_view.coffee
@@ -6,4 +6,4 @@
     topicStatisticsRegion: '.js-topic-statistics-region'

   onRender: ->
-    @topicStatisticsRegion.show new TopicStatisticsView model: @model
+    @topicStatisticsRegion.show new TopicStatisticsView(model: @model)


move all of the on error handlers to the promise chain

--- gulp-espruino/src/gulp-espruino.coffee
+++ gulp-espruino/src/gulp-espruino.coffee
@@ -48,6 +48,8 @@
         output.append(data.toString())
         finishCommand()

+      serialPort.on 'error', (error) -> commandExecuted.reject(error)
+
       serialPort.open -> connected.resolve(serialPort)

     if !config.port && !config.serialNumber
@@ -78,11 +80,10 @@
     connected.promise.then((serialPort) -> serialPort.close(-> closed.resolve()))
     closed.promise
   log: -> output.all()
-  send: (command, onError) ->
+  send: (command) ->
     connected.promise.then (serialPort) ->
-      serialPort.on('error', onError)
       commandExecuted = Q.defer()
-      serialPort.write command, (error) -> onError(error) if error
+      serialPort.write command, (error) -> commandExecuted.reject(error) if error
       commandExecuted.promise

 module.exports =
@@ -98,14 +99,13 @@

     through.obj (chunk, encoding, callback) ->
       publish = createPublisher(@, callback)
-      onError = (error) -> publish.error(error)

       espruino.connect()
-         .then(-> espruino.send("reset();\n", onError) if config.reset)
-         .then(-> espruino.send("echo(0);\n", onError) if config.echoOff)
-         .then(-> espruino.send("{ #{chunk.contents.toString()} }\n", onError))
-         .then(-> espruino.send("echo(1);\n", onError) if config.echoOff)
-         .then(-> espruino.send("save();\n", onError) if config.save)
+         .then(-> espruino.send("reset();\n") if config.reset)
+         .then(-> espruino.send("echo(0);\n") if config.echoOff)
+         .then(-> espruino.send("{ #{chunk.contents.toString()} }\n"))
+         .then(-> espruino.send("echo(1);\n") if config.echoOff)
+         .then(-> espruino.send("save();\n") if config.save)
          .then(-> publish.content(espruino.log()))
          .fail((error) -> publish.error(error))
          .finally(-> espruino.close())
@@ -114,5 +114,4 @@
 # Todo.
 # blow up if not stream
 # add in hook to finish stream slurp whenever you want
-# reject promise on error
 # add timeout to promise chain


ComputeProvider::Koding fetchExisting implementation is done

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -8,6 +8,7 @@

 module.exports = class Koding extends ProviderInterface

+  VMDefaultDiskSize = @VMDefaultDiskSize = 3072

   @processNonce = (nonce, callback)->

@@ -70,7 +71,23 @@

   @fetchExisting = (client, options, callback)->

-    JVM.fetchVmsByContext client, options, callback
+    { machines } = options
+
+    callback null, machines.map (machine) ->
+
+      { stack, meta, groups } = machine
+
+      meta.hostKite = null  if meta.hostKite in ['(banned)', '(maintenance)']
+
+      {
+        hostnameAlias : meta.hostnameAlias
+        region        : meta.region
+        hostKite      : meta.hostKite
+        alwaysOn      : meta.alwaysOn
+        meta          : meta.meta
+        groupId       : groups[0].id
+        stack
+      }

   @create = (client, options, callback)->


Social: Models: ComputeProvider: Change permit of fetchTeamPlans

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -188,9 +188,8 @@
       callback null, PLANS

-  @fetchTeamPlans = permit
-    advanced : [{ permission: 'sudoer', superadmin: yes }]
-    success  : (client, callback) ->
+  @fetchTeamPlans = permit 'create machines',
+    success: (client, callback) ->
       callback null, teamutils.TEAMPLANS


ComputeProvider: Basic details for base methods

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -70,9 +70,37 @@

         fn.apply @, _arguments

+
+  @providers = PROVIDERS
+
+
+  @fetchAvailableProviders = secure (client, callback)->
+
+    callback null, Object.keys PROVIDERS
+
+
   @ping = secure revive (client, options, callback)->

+    {provider} = options
     provider.ping client, callback
+
+
+  @create = secure revive (client, options, callback)->
+
+    {provider} = options
+    provider.create client, options, callback
+
+
+  @update = secure revive (client, options, callback)->
+
+    {provider} = options
+    provider.update client, options, callback
+
+
+  @remove = secure revive (client, options, callback)->
+
+    {provider} = options
+    provider.remove client, options, callback

   @fetchExisting = secure revive (client, options, callback)->