fix error in log for expiry timeout

fix error in log for expiry timeout

--- services/clsi/app/coffee/ProjectPersistenceManager.coffee
+++ services/clsi/app/coffee/ProjectPersistenceManager.coffee
@@ -55,4 +55,4 @@
 			).error callback

-logger.log EXPIRY_TIMEOUT:EXPIRY_TIMEOUT, "project assets kept timeout"+logger.log {EXPIRY_TIMEOUT: ProjectPersistenceManager.EXPIRY_TIMEOUT}, "project assets kept timeout"


jsdom will be the end of me

jsdom will be the end of me


Edited isNaN check in fullpie.coffee

Edited isNaN check in fullpie.coffee

--- widgets/fullpie/fullpie.coffee
+++ widgets/fullpie/fullpie.coffee
@@ -60,7 +60,7 @@
       .attr('dy', 25)
       .attr('font-size', '90%')
 #      .text((d,i) -> data[i].value + ' (' + Math.round(data[i].value/sum * 100) + '%)')
-      .text((d,i) -> data[i].value + ' (' + Math.round(isNan(data[i].value/sum * 100) ? 0 : data[i].value/sum * 100) + '%)')
+      .text((d,i) -> data[i].value + ' (' + Math.round(isNan(data[i].value/sum * 100) ? '0' : data[i].value/sum * 100) + '%)')


pass country_id as data to jQuery.get

pass country_id as data to jQuery.get

if your default url options happen to include ?locale=en then
the previous implementation would end up generating:
api/states?locale=en?country_id=49 instead of
api/states?locale=en&country_id=49

Fixes #3108

--- frontend/app/assets/javascripts/store/checkout.js.coffee
+++ frontend/app/assets/javascripts/store/checkout.js.coffee
@@ -15,7 +15,7 @@
       countryId = getCountryId(region)
       if countryId?
         unless Spree.Checkout[countryId]?
-          $.get Spree.routes.states_search + "/?country_id=#{countryId}", (data) ->
+          $.get Spree.routes.states_search, {country_id: countryId}, (data) ->
             Spree.Checkout[countryId] =
               states: data.states
               states_required: data.states_required


Fix tests

Fix tests

--- bokehjs/src/coffee/models/annotations/color_bar.coffee
+++ bokehjs/src/coffee/models/annotations/color_bar.coffee
@@ -497,8 +497,8 @@

     # Because we want the scale to be reversed
     if @orientation == 'vertical'
-      major_coords[i] = _.map(major_coords[i], (coord) -> return scale_length - coord)
-      minor_coords[i] = _.map(minor_coords[i], (coord) -> return scale_length - coord)
+      major_coords[i] = new Float64Array((scale_length - coord for coord in major_coords[i]))
+      minor_coords[i] = new Float64Array((scale_length - coord for coord in minor_coords[i]))

     return {
       "major": major_coords


AutoComplete: fetchInterval made optional, defaulted to 300ms

--- src/components/autocomplete/autocompletecontroller.coffee
+++ src/components/autocomplete/autocompletecontroller.coffee
@@ -20,6 +20,7 @@
       selectedItemClass     : KDAutoCompletedItem
       nothingFoundItemClass : KDAutoCompleteNothingFoundItem
       fetchingItemClass     : KDAutoCompleteFetchingItem
+      fetchInterval         : options.fetchInterval ? 300
       listWrapperCssClass   : ''
       minSuggestionLength   : 2
       selectedItemsLimit    : null
@@ -347,7 +348,9 @@
     @dropdownPrefix = value
     @showFetching()

-    @fetch KD.utils.debounce 177, (data) =>
+    {fetchInterval} = @getOptions()
+
+    @fetch KD.utils.debounce fetchInterval, (data) =>

       if data.length > 0
         @refreshDropDown data


Added workaround for requiring fabric from module

--- position.coffee
+++ position.coffee
@@ -14,7 +14,8 @@
 #    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see http://www.gnu.org/licenses/.

-define(['lib/fabric'], ->
+define(['fabric'], (fabric) ->
+  fabric = fabric.fabric if fabric.fabric?

   class Position


Troubleshoot: Remove BrokerRecovery

Troubleshoot: Remove BrokerRecovery

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -12,8 +12,8 @@
     @status = PENDING

     @prepareCheckSequence()
-    @brokerRecovery = new BrokerRecovery
-    @brokerKiteRecovery = new BrokerRecovery type: 'kite'
+    # @brokerRecovery = new BrokerRecovery
+    # @brokerKiteRecovery = new BrokerRecovery type: 'kite'
     @registerItems()
     # when a user stays idle for an hour we forward userIdle event
     @idleUserDetector = new IdleUserDetector threshold: KD.config.troubleshoot.idleTime
@@ -112,7 +112,7 @@
     broker = KD.remote.mq
     @registerItem "broker",
       troubleshoot : broker.bound 'ping'
-      recover      : @brokerRecovery.bound 'recover'
+      # recover      : @brokerRecovery.bound 'recover'

   registerItem : (name, options) ->
     options.name = name


Opein in background runtime option for application manager

--- client/app/MainApp/ApplicationManager.coffee
+++ client/app/MainApp/ApplicationManager.coffee
@@ -47,17 +47,17 @@

   open: do ->

-    createOrShow = (appOptions, appParams, callback = noop)->
-
-      appManager  = KD.getSingleton "appManager"
-      name        = appOptions?.name
-
-      return appManager.handleAppNotFound()  unless name
-
-      appInstance = appManager.get name
-      cb          = -> appManager.show appOptions, callback
-      if appInstance then do cb
-      else appManager.create name, appParams, cb
+    createOrShow = (appOptions = {}, appParams, callback = noop)->
+
+      name = appOptions?.name
+      return @handleAppNotFound()  unless name
+
+      appInstance = @get name
+      cb = if appParams.background or appOptions.background
+      then -> KD.utils.defer callback
+      else @show.bind this, name, appParams, callback
+
+      if appInstance then cb() else @create name, appParams, cb

     (name, options, callback)->

@@ -67,7 +67,7 @@
       options            or= {}
       appOptions           = KD.getAppOptions name
       appParams            = options.params or {}
-      defaultCallback      = -> createOrShow appOptions, appParams, callback
+      defaultCallback      = createOrShow.bind this, appOptions, appParams, callback
       kodingAppsController = KD.getSingleton("kodingAppsController")

       # If app has a preCondition then first check condition in it
@@ -212,12 +212,10 @@
         KD.getSingleton("kodingAppsController").putAppResources appInstance, callback
       callback? appInstance

-  show:(appOptions, callback)->
-
-    appInstance = @get appOptions.name
-
-    if appOptions.background
-      return @utils.defer -> callback? appInstance
+  show:(name, appParams, callback)->
+
+    appOptions  = KD.getAppOptions name
+    appInstance = @get name

     appView     = appInstance.getView?()
     return unless appView


Markdown: Require highlight.js module

Markdown: Require highlight.js module

--- client/app/lib/util/formatBlockquotes.coffee
+++ client/app/lib/util/formatBlockquotes.coffee
@@ -1,3 +1,5 @@
+hljs = require 'highlight.js'
+
 module.exports = (text = '') ->

   parts = text.split '```'


Removed: dead code

Removed: dead code

--- client/app/views/popover_screens/main.coffee
+++ client/app/views/popover_screens/main.coffee
@@ -243,19 +243,6 @@
         ev.preventDefault()

-    onRemoveClicked: ->
-        if confirm t('are you sure')
-            @spinner.show()
-            @removeButton.hide()
-            @model.destroy
-                wait: true
-                error: ->
-                    alert t('server error occured')
-                complete: =>
-                    @spinner.hide()
-                    @popover.selfclose()
-
-
     # When duplicate button is clicked, an new event with exact same date
     # is created.
     onDuplicateClicked: ->


Guard stopPropagation() call.

Guard stopPropagation() call.

--- keypress.coffee
+++ keypress.coffee
@@ -70,7 +70,8 @@
     # one, we should prevent the default keydown event.
     if (should_prevent or keypress.suppress_event_defaults) and not keypress.force_event_defaults
         e.preventDefault()
-        e.stopPropagation()
+        if e.stopPropagation
+            e.stopPropagation()

 _allow_key_repeat = (combo) ->
     return false if combo.prevent_repeat


Check if CC is valid when setting payment

--- app/assets/javascripts/sprangular/controllers/checkout/deliveryAndPayment.coffee
+++ app/assets/javascripts/sprangular/controllers/checkout/deliveryAndPayment.coffee
@@ -12,7 +12,7 @@

   $scope.advance = ->
     order = $scope.order
-    return if order.shippingAddress.isInvalid() || (!order.billToShipAddress && order.billingAddress.isInvalid())
+    return if order.creditCard.isInvalid()

     $scope.processing = true


added addFrame(), simplifying parseNotes() and parseChord()

--- marktab.coffee
+++ marktab.coffee
@@ -28,10 +28,11 @@
 				jsonPart = {}
 				if part.match(notePattern)
 					# note
-					jsonPart = this.parseNotes(part, i)
+					jsonPart = this.parseNotes(part)
 					lastString = parseInt(_.keys(jsonPart), 10)
 				else if part.match(restPattern)
 					# rest
+					jsonPart = {1: [undefined]}
 				else if part.match(hammerPattern)
 					# hammer-on
 					if !lastString
@@ -58,14 +59,29 @@
 					jsonPart[lastString][i] = "\\"
 				else if part.match(chordPattern)
 					# chord
-					jsonPart = this.parseChord(part, i)
+					jsonPart = this.parseChord(part)
 				else
 					throw "unknown pattern: " + part
-				this.mergeMaps(json, jsonPart)
-				# console.log(json)
+				this.addFrame(json, jsonPart)
 			this.parseJson(json)
 		json

+	# adds another frame to a tabMap
+	addFrame: (json, frame) ->
+		idx = this.longestString(json)
+		for stringNum, stringNotes of frame
+			for fret, i in stringNotes
+				json[stringNum] ?= []
+				json[stringNum][idx] = fret
+		json
+
+	longestString: (tabMap = {}) ->
+		max = 0
+		for stringNum, stringNotes of tabMap
+			size = _.size(stringNotes)
+			max = size if size > max
+		max
+
 	# merges two tabMaps together, with the source overwriting the destination
 	mergeMaps: (dest, source) ->
 		for stringNum, stringNotes of source
@@ -87,17 +103,17 @@

 	# parses marktab note into json
 	# example: parseNotes("5:6", 2) => { 5: [undefined, 6] }
-	parseNotes: (note, i = 0) ->
+	parseNotes: (note) ->
 		result = {}
 		stringAndFret = note.split(":")
 		string = stringAndFret[0]
 		fret = stringAndFret[1]
 		result[string] ?= []
-		result[string][i] = parseInt(fret, 10)
+		result[string][0] = parseInt(fret, 10)
 		result

 	# parses marktab chords into json
-	parseChord: (chord, i = 0) ->
+	parseChord: (chord) ->
 		""

 	# parses marktab riffs into json
@@ -111,10 +127,7 @@
 	# makes each string's notes array the same length
 	normalizeJson: (json = {}) ->
 		result = this.cloneTabMap(json)
-		max = 0
-		for stringNum, stringNotes of json
-			size = _.size(stringNotes)
-			max = size if size > max
+		max = this.longestString(json)
 		for stringNum in [1..6]
 			result[stringNum] ?= []
 			result[stringNum][max-1] ?= undefined if max > 0
@@ -128,7 +141,9 @@
 			notes = tabMap[stringNum]
 			line = stringDefaults[stringNum] + "|-"
 			for note in notes
-				line += (note || '-') + "-"
+				line += (note || '-')
+				if note < 10 || !note
+					line += "-"
 			line += "|"
 			@lines.push(line)
 		@lines


removed a commented-out block

removed a commented-out block

--- demo.coffee
+++ demo.coffee
@@ -184,11 +184,6 @@
         title: htitle
       vAxis:
         title: vtitle
-      # chartArea:
-      #   left: 30
-      #   top: 30
-      #   width: '80%'
-      #   height: '80%'
     if chart
       chart.clearChart()
     else


include filter label map in dropdown test

--- components/filter2/dropdown/test/view.coffee
+++ components/filter2/dropdown/test/view.coffee
@@ -13,6 +13,7 @@
       benv.expose
         $: benv.require 'jquery'
         _s: require('underscore.string')
+        filterLabelMap: require '../label_map.coffee'
         sd:
           FILTER_ROOT: '/browse'
       Backbone.$ = $
@@ -32,9 +33,9 @@
   it 'renders properly', ->
     @view.renderCounts @view.collection

-    @view.$('.filter-nav-main-text').text().should.equal 'Price'
+    @view.$('.filter-nav-main-text').text().should.equal 'price'
     @view.$('.filter-nav-active-text').text().should.equal ''
-    @view.$('.filter-dropdown-nav > a').first().text().should.equal 'All Prices'
+    @view.$('.filter-dropdown-nav > a').first().text().should.equal 'All prices'
     @view.$('.filter-dropdown-nav > a:nth-child(2) .filter-dropdown-text').text().should.equal 'Under $1,000'
     @view.$('.filter-dropdown-nav > a:nth-child(2) .filter-dropdown-count').text().should.equal '(535)'


Remove a collection 'unique' constraint This allows us to have multiple direct messages with the same name

--- server/startup/indexes.coffee
+++ server/startup/indexes.coffee
@@ -4,7 +4,7 @@
 		try ChatRoom._ensureIndex { 'u._id': 1 } catch e then console.log e

 		try ChatSubscription._ensureIndex { 'rid': 1, 'u._id': 1 }, { unique: 1 } catch e then console.log e
-		try ChatSubscription._ensureIndex { 'u._id': 1, 'name': 1, 't': 1 }, { unique: 1 } catch e then console.log e
+		try ChatSubscription._ensureIndex { 'u._id': 1, 'name': 1, 't': 1 } catch e then console.log e
 		try ChatSubscription._ensureIndex { 'open': 1 } catch e then console.log e
 		try ChatSubscription._ensureIndex { 'alert': 1 } catch e then console.log e
 		try ChatSubscription._ensureIndex { 'unread': 1 } catch e then console.log e


Log errors for mocha

Log errors for mocha

--- server/test/units/user.coffee
+++ server/test/units/user.coffee
@@ -45,6 +45,7 @@

   user.save( (err, savedUser) ->
     if err?
+      console.error err
       throw new Error(err)

     savedUser


StacksCustomViews: use repoList in a repoListView wrapper to show loading state

--- client/admin/lib/views/stacks/stackscustomviews.coffee
+++ client/admin/lib/views/stacks/stackscustomviews.coffee
@@ -414,7 +414,7 @@
           steps           : STEPS.REPO_FLOW
           selected        : 2
         text              : "Github: Select a repository from your account"
-        repoList          :
+        repoListView      :
           username        : 'gokmen'
         navCancelButton   :
           title           : '< Select another provider'
@@ -448,8 +448,6 @@

     repoList: (options) =>

-      { username } = options
-
       controller          = new kd.ListViewController
         viewOptions       :
           type            : 'github'
@@ -461,12 +459,28 @@
           cssClass        : 'noitem-warning'
           partial         : 'There is no repository to show.'

+      __view = controller.getView()
+      return { __view, controller }
+
+
+    repoListView: (options) =>
+
+      { username } = options
+      container    = @views.container 'repo-list'
+      views        = @addTo container,
+        mainLoader : 'Fetching repository list...'
+        repoList   : options
+
+      {controller, __view: repoList} = views.repoList
+
+      repoList.hide()
       GitHub.fetchUserRepos username, (err, repos) =>
+        views.mainLoader.hide()
         return  if showError err
         controller.replaceAllItems repos
-
-      __view = controller.getView()
-      return { __view, controller }
+        repoList.show()
+
+      return container

     credentialList: (options) =>


profile: refactored the giant constructor of many things into many methods of lesser things

--- client/app/MainApp/lazy/staticprofilecontroller.coffee
+++ client/app/MainApp/lazy/staticprofilecontroller.coffee
@@ -26,10 +26,105 @@
         cssClass        : 'static-content activity-related'
       showHeader        : no

+    # this is the JAccount object of the static profile
+    @profileUser  = null
+
     @navLinks     = {}
+    @lockSidebar  = no

     @registerSingleton 'staticProfileController', @, no

+    @reviveViewsOnPageLoad()
+    @addEventListeners()
+
+    # KD.remote.cacheable KD.config.profileEntryPoint, (err, user, name)=>
+    # FIXME - we want to use cacheable, not a JAccount call
+    KD.remote.api.JAccount.one
+      "profile.nickname" : KD.config.profileEntryPoint
+    , (err, user, name)=>
+      if err then log err,user,name
+      unless err
+        @reviveViewsOnUserLoad user
+
+  addEventListeners:->
+
+    @on 'HomeLinkClicked', =>
+      @lockSidebar = no
+      @utils.wait 250, =>
+        @profileContentLinks.setClass 'links-hidden'
+        @profileContentList.unsetClass 'has-links'
+
+      # HERE is the place to implement custom profile splash screens, reveal.js and such
+
+      @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity', yes
+
+    @on 'ActivityLinkClicked', (path)=>
+      @lockSidebar = yes
+      @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity'
+      @utils.wait 250, =>
+        @profileContentList.setClass 'has-links'
+        @profileContentLinks.unsetClass 'links-hidden'
+
+    @on 'CustomizeLinkClicked',=>
+      return if @customizeViewsAttached or KD.whoami().getId() isnt @profileUser.getId()
+      @customizeViewsAttached = yes
+
+      @avatarAreaIconMenu.emit 'CustomizeLinkClicked'
+
+      # reviving customization
+
+      types = @getAllowedTypes @profileUser
+
+      for type in CONTENT_TYPES
+        @navLinks[type].addSubView new StaticNavCheckBox
+          activityType : type
+          defaultValue : type in types
+          delegate     : @
+        , @profileUser
+
+    @on 'ShowMoreButtonClicked', =>
+      @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity'
+
+    @on 'StaticProfileNavLinkClicked', (facets,isStatic=no)=>
+      @profileLoadingBar.setClass 'active'
+      @profileLoaderView.show()
+      facets = [facets] if 'string' is typeof facets
+
+      if @profileUser
+        allowedTypes = @getAllowedTypes @profileUser
+
+        blockedTypes = facets.reduce (acc, facet)->
+          acc.push facet unless facet in allowedTypes
+          return acc
+        , []
+
+        @emit 'DecorateStaticNavLinks', allowedTypes
+
+        if blockedTypes.length is 0
+          @currentFacets = facets
+          appManager.tell 'Activity', 'fetchActivity',
+            originId : @profileUser.getId()
+            facets : facets
+            bypass : yes
+          , (err, activities)=>
+            @refreshActivities err, activities, isStatic
+        else @emit 'BlockedTypesRequested', blockedTypes
+
+    @controller.on 'LazyLoadThresholdReached', =>
+      appManager.tell 'Activity', 'fetchActivity',
+        originId  : @profileUser.getId()
+        facets    : @currentFacets
+        to        : @controller.itemsOrdered.last.getData().meta.createdAt
+        bypass    : yes
+      , @bound "appendActivities"
+
+  reviveViewsOnPageLoad:->
+
+    console.time 'reviving page elements on pageload.'
+
+    allowedTypes  = ['CBlogPostActivity']
+    blockedTypes  = []
+    @currentFacets = []
     # reviving the content view. this encapsulates the listitem feed after
     # user input (type selection, more-button)
     @profileContentView = new KDListView
@@ -44,12 +139,6 @@
     @profileContentView.addSubView @listWrapper
     @profileContentView.addSubView @staticListWrapper

-    # this is the JAccount object of the static profile
-    profileUser   = null
-    allowedTypes  = ['CBlogPostActivity']
-    blockedTypes  = []
-    currentFacets = []
-
     # reviving the landing page. this is needed to handle window
     # resize events for the view and subviews
     @profileLandingView = new KDView
@@ -58,10 +147,10 @@
     @profileLandingView.listenWindowResize()
     @profileLandingView._windowDidResize = =>
       @profileLandingView.setHeight window.innerHeight
-      @profileContentView.setHeight window.innerHeight-profileTitleView.getHeight()
+      @profileContentView.setHeight window.innerHeight-@profileTitleView.getHeight()
       @repositionLogoView()

-    profileTitleView = new KDView
+    @profileTitleView = new KDView
       lazyDomId : 'profile-title'

     @profileShowMoreView = new KDView
@@ -82,60 +171,35 @@

     # reviving wrapper views for resize/slide animations as well as
     # adding administrative views
-    profileContentWrapperView = new KDView
+    @profileContentWrapperView = new KDView
       lazyDomId : 'profile-content-wrapper'
       cssClass  : 'slideable'

-    profilePersonalWrapperView = new KDView
+    @profilePersonalWrapperView = new KDView
       lazyDomId : 'profile-personal-wrapper'
       cssClass  : 'slideable'
       bind : 'mouseenter mouseleave'
-
-    @lockSidebar = no
-
-    @on 'SidebarLockRequested', (state = yes)=>
-      @lockSidebar = state
-
-    @on 'HomeLinkClicked', =>
-      # @listWrapper.hide()
-      # @staticListWrapper.show()
-      @lockSidebar = no
-      profileContentLinks.setClass 'links-hidden'
-      profileContentList.unsetClass 'has-links'
-
-      # HERE is the place to implement custom profile splash screens, reveal.js and such
-      @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity', yes
-
-    @on 'ActivityLinkClicked', (path)=>
-      # @staticListWrapper.hide()
-      # @listWrapper.show()
-      @lockSidebar = yes
-      profileContentList.setClass 'has-links'
-      profileContentLinks.unsetClass 'links-hidden'
-      @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity'
-

     #
     # allow for sidebar lock here!
     # resize avatar to center or something
     #

-    profilePersonalWrapperView.on 'mouseenter',(event)=>
-        profilePersonalWrapperView.setWidth 160
-        profileContentWrapperView.$().css marginLeft : "160px"
-
-    profilePersonalWrapperView.on 'mouseleave',(event)=>
-      log @lockSidebar
+    @profilePersonalWrapperView.on 'mouseenter',(event)=>
+        @profilePersonalWrapperView.setWidth 160
+        @profileContentWrapperView.$().css marginLeft : "160px"
+
+    @profilePersonalWrapperView.on 'mouseleave',(event)=>
       unless @lockSidebar
-        profilePersonalWrapperView.setWidth 50
-        profileContentWrapperView.$().css marginLeft : "50px"
+        @profilePersonalWrapperView.setWidth 50
+        @profileContentWrapperView.$().css marginLeft : "50px"

     # reviving feed type selectors that will activate feed facets

-    profileContentLinks = new KDView
+    @profileContentLinks = new KDView
       lazyDomId : 'profile-content-links'

-    profileContentList = new KDView
+    @profileContentList = new KDView
       lazyDomId : 'profile-content-list'

     for type in CONTENT_TYPES
@@ -160,8 +224,8 @@
     @profileLogoView = new KDView
       lazyDomId: 'profile-koding-logo'
       click :=>
-        profilePersonalWrapperView.setClass 'slide-down'
-        profileContentWrapperView.setClass 'slide-down'
+        @profilePersonalWrapperView.setClass 'slide-down'
+        @profileContentWrapperView.setClass 'slide-down'
         @profileLogoView.setClass 'top'

         @profileLandingView.setClass 'profile-fading'
@@ -171,137 +235,81 @@

     @utils.wait => @profileLogoView.setClass 'animate'

-
-    # KD.remote.cacheable KD.config.profileEntryPoint, (err, user, name)=>
-
-
-    KD.remote.api.JAccount.one
-      "profile.nickname" : KD.config.profileEntryPoint
-    , (err, user, name)=>
-      if err then log err,user,name
-      unless err
-
-        profileUser = user
-        @emit 'DecorateStaticNavLinks', @getAllowedTypes profileUser
-
-        @avatarAreaIconMenu = new StaticAvatarAreaIconMenu
-          lazyDomId    : 'profile-buttons'
-          delegate     : @
-        , profileUser
-
-        @avatarAreaIconMenu.$('.static-profile-button').remove()
-
-        if user.getId() is KD.whoami().getId()
-
-          # reviving admin stuff
-
-          profileAdminCustomizeView = new KDView
-            lazyDomId : 'profile-admin-customize'
-
-          profileAdminCustomizeView.addSubView staticPageSettingsButton = new CustomLinkView
-            title : 'Customize'
-            cssClass : 'static-page-settings-button clean-gray'
-            click :(event)=>
-              @emit 'CustomizeLinkClicked'
-              event.stopPropagation()
-              event.preventDefault()
-
-          profileAdminCustomizeView.show()
-
-          profileAdminMessageView = new KDView
-            lazyDomId : 'profile-admin-message'
-
-          showPage = user.profile.staticPage?.show
-
-          profileAdminMessageView.addSubView disableLink = new CustomLinkView
-            title     : "#{if showPage is yes then 'Disable' else 'Enable'}"
-            cssClass  : 'message-disable'
-            click     : (event)=>
-              event?.stopPropagation()
-              event?.preventDefault()
-
-              if user.profile.staticPage?.show is yes
-                modal           = new KDModalView
-                  cssClass      : 'disable-static-page-modal'
-                  title         : 'Do you really want to disable your Public Page?'
-                  content       : """
-

-
Disabling this feature will disable other people
-                      from publicly viewing your profile. You will still be
-                      able to access the page yourself.

-
Do you want to continue?

-

-                    """
-                  buttons       :
-                    "Disable the Public Page" :
-                      cssClass  : 'modal-clean-red'
-                      callback  : =>
-                        modal.destroy()
-                        user.setStaticPageVisibility no, (err,res)=>
-                          if err then log err
-                          disableLink.updatePartial 'Enable this Public Page'
-                    Cancel      :
-                      cssClass  : 'modal-cancel'
-                      callback  : =>
-                        modal.destroy()
-              else
-                user.setStaticPageVisibility yes, (err,res)=>
-                  if err then log err
-                  disableLink.updatePartial 'Disable this Public Page'
-
-
-    @on 'CustomizeLinkClicked',=>
-      return if @customizeViewsAttached or KD.whoami().getId() isnt profileUser.getId()
-      @customizeViewsAttached = yes
-
-      @avatarAreaIconMenu.emit 'CustomizeLinkClicked'
-
-      # reviving customization
-
-      types = @getAllowedTypes profileUser
-
-      for type in CONTENT_TYPES
-        @navLinks[type].addSubView new StaticNavCheckBox
-          activityType : type
-          defaultValue : type in types
-          delegate     : @
-        , profileUser
-
-    @on 'ShowMoreButtonClicked', =>
-      @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity'
-
-    @on 'StaticProfileNavLinkClicked', (facets,isStatic=no)=>
-      @profileLoadingBar.setClass 'active'
-      @profileLoaderView.show()
-      facets = [facets] if 'string' is typeof facets
-
-      if profileUser
-        allowedTypes = @getAllowedTypes profileUser
-
-        blockedTypes = facets.reduce (acc, facet)->
-          acc.push facet unless facet in allowedTypes
-          return acc
-        , []
-
-        @emit 'DecorateStaticNavLinks', allowedTypes
-
-        if blockedTypes.length is 0
-          currentFacets = facets
-          appManager.tell 'Activity', 'fetchActivity',
-            originId : profileUser.getId()
-            facets : facets
-            bypass : yes
-          , (err, activities)=>
-            @refreshActivities err, activities, isStatic
-        else @emit 'BlockedTypesRequested', blockedTypes
-
-    @controller.on 'LazyLoadThresholdReached', =>
-      appManager.tell 'Activity', 'fetchActivity',
-        originId  : profileUser.getId()
-        facets    : currentFacets
-        to        : @controller.itemsOrdered.last.getData().meta.createdAt
-        bypass    : yes
-      , @bound "appendActivities"
+    console.timeEnd 'reviving page elements on pageload.'
+
+  reviveViewsOnUserLoad:(user)->
+
+    console.time 'reviving page elements on userload.'
+
+    @profileUser = user
+    @emit 'DecorateStaticNavLinks', @getAllowedTypes @profileUser
+
+    @avatarAreaIconMenu = new StaticAvatarAreaIconMenu
+      lazyDomId    : 'profile-buttons'
+      delegate     : @
+    , @profileUser
+
+    @avatarAreaIconMenu.$('.static-profile-button').remove()
+
+    if user.getId() is KD.whoami().getId()
+
+      # reviving admin stuff
+
+      profileAdminCustomizeView = new KDView
+        lazyDomId : 'profile-admin-customize'
+
+      profileAdminCustomizeView.addSubView staticPageSettingsButton = new CustomLinkView
+        title : 'Customize'
+        cssClass : 'static-page-settings-button clean-gray'
+        click :(event)=>
+          @emit 'CustomizeLinkClicked'
+          event.stopPropagation()
+          event.preventDefault()
+
+      profileAdminCustomizeView.show()
+
+      profileAdminMessageView = new KDView
+        lazyDomId : 'profile-admin-message'
+
+      showPage = user.profile.staticPage?.show
+
+      profileAdminMessageView.addSubView disableLink = new CustomLinkView
+        title     : "#{if showPage is yes then 'Disable' else 'Enable'}"
+        cssClass  : 'message-disable'
+        click     : (event)=>
+          event?.stopPropagation()
+          event?.preventDefault()
+
+          if user.profile.staticPage?.show is yes
+            modal           = new KDModalView
+              cssClass      : 'disable-static-page-modal'
+              title         : 'Do you really want to disable your Public Page?'
+              content       : """
+

+
Disabling this feature will disable other people
+                  from publicly viewing your profile. You will still be
+                  able to access the page yourself.

+
Do you want to continue?

+

+                """
+              buttons       :
+                "Disable the Public Page" :
+                  cssClass  : 'modal-clean-red'
+                  callback  : =>
+                    modal.destroy()
+                    user.setStaticPageVisibility no, (err,res)=>
+                      if err then log err
+                      disableLink.updatePartial 'Enable this Public Page'
+                Cancel      :
+                  cssClass  : 'modal-cancel'
+                  callback  : =>
+                    modal.destroy()
+          else
+            user.setStaticPageVisibility yes, (err,res)=>
+              if err then log err
+              disableLink.updatePartial 'Disable this Public Page'
+
+    console.timeEnd 'reviving page elements on userload.'

   repositionLogoView:->
     @profileLogoView.$().css
@@ -310,7 +318,6 @@
   appendActivities:(err,activities)->
     @controller.listActivities activities
     @controller.hideLazyLoader()
-

   refreshActivities:(err,activities,isStatic)->
     @profileContentView.$('.content-item').remove()
@@ -332,8 +339,8 @@
     @profileLoadingBar.unsetClass 'active'
     @profileLoaderView.hide()

-  getAllowedTypes:(profileUser)->
-      allowedTypes = profileUser.profile.staticPage?.showTypes or CONTENT_TYPES
+  getAllowedTypes:(@profileUser)->
+    allowedTypes = @profileUser.profile.staticPage?.showTypes or CONTENT_TYPES

 class StaticNavLink extends KDView


profile: fix for settings button on non-admin profile views

--- client/app/MainApp/lazy/staticprofilecontroller.coffee
+++ client/app/MainApp/lazy/staticprofilecontroller.coffee
@@ -113,7 +113,7 @@
       @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity', 'static', =>
         @showWrapper @wrappers['static']
         @staticDefaultItem.show()
-        @staticPageSettingsButton.hide()
+        @staticPageSettingsButton?.hide()
         callback()

@@ -124,7 +124,7 @@
       @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity', 'activity', =>
         @showWrapper @wrappers['activity']
         @displaySidebar yes
-        @staticPageSettingsButton.show()
+        @staticPageSettingsButton?.show()
         callback()

@@ -132,7 +132,7 @@
       @addLogic 'about', =>
         callback()
       @displaySidebar no
-      @staticPageSettingsButton.hide()
+      @staticPageSettingsButton?.hide()

     @on 'CustomizeLinkClicked',=>


ActivitySidebar: Always create shared machines list and cache them.

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -709,6 +709,7 @@
   #   @watchedMachines[machine._id] = yes

+  # TODO: Rename this method to fetchEnvironment - acet!
   fetchMachines: (callback) ->

     remote.api.Sidebar.fetchEnvironment (err, data) =>
@@ -778,11 +779,10 @@
       @machineListsByName = {}

       { shared, collaboration } = machines
-
-      if shared.length or collaboration.length
-        @createMachineList 'shared', {}, { shared, collaboration }
-
-      @createMachineList 'own', {}, machines.own
+      sharedData = { shared, collaboration }
+
+      @sharedMachinesList = @createMachineList 'shared', {}, sharedData
+      @ownMachinesList    = @createMachineList 'own', {}, machines.own

   createMachineList: (type, options, data) ->
@@ -796,6 +796,8 @@
     @machineListsByName[type] = list

     @addSubView list, null, yes
+
+    return list


Sidebar: Set unread message count of workspace through machine box

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -166,7 +166,7 @@
     item.setUnreadCount? unreadCount

-  setWorkspaceUnreadCount: (data, unreadCount) ->
+  setWorkspaceUnreadCount: (data, count) ->

     channelId = data._id
     provider  = environmentDataProvider
@@ -175,14 +175,7 @@

       return  unless machine and workspace
       return  unless box = @getMachineBoxByMachineUId machine.uid
-      return  unless wsItem = box.getWorkspaceItemByChannelId channelId
-
-      wsItem.setUnreadCount unreadCount
-
-      return  unless unreadCount is 0
-
-      { socialapi } = kd.singletons
-      socialapi.channel.updateLastSeenTime channelId : data._id, kd.noop
+      box.setUnreadCount channelId, count

   handleFollowedFeedUpdate: (update) ->


added default category name

added default category name

--- static/coffee/flows/controllers.coffee
+++ static/coffee/flows/controllers.coffee
@@ -937,7 +937,6 @@
   # rules = []
   toRemove = []
   for rule in $scope.ruleset.rules
-
     if not rule.category
       toRemove.push(rule)
       continue
@@ -1063,6 +1062,8 @@

     else if op == "number"
       categoryName = "numeric"
+    else if op == "ward"
+      categoryName = "ward"
     else if op == "district"
       categoryName = "district"
     else if op == "state"


use remote peer

use remote peer

--- src/Main.coffee
+++ src/Main.coffee
@@ -52,8 +52,8 @@

     @allPeers = [ ]
     @myPeerId = null
-    #@peer = new Peer({ host: 'router.kranzky.com', port: 80, config: { 'iceServers': [] }, debug: 0 })
-    @peer = new Peer({ host: 'localhost', port: 9000, config: { 'iceServers': [] }, debug: 0 })
+    @peer = new Peer({ host: 'router.kranzky.com', port: 80, config: { 'iceServers': [] }, debug: 0 })
+    #@peer = new Peer({ host: 'localhost', port: 9000, config: { 'iceServers': [] }, debug: 0 })
     @peer.on 'open', (id)=>
       console.info('Starting as peer ' + id)
       @myPeerId = id


Test case: Leave collaboration session and invite same participant

--- client/test/lib/collaboration/collaborationchat.coffee
+++ client/test/lib/collaboration/collaborationchat.coffee
@@ -76,3 +76,31 @@
       collaborationHelpers.sendMessage(browser)
       collaborationHelpers.leaveSessionFromSidebar(browser)
       browser.end()
+
+
+  leaveSessionInviteSameUserAgain: (browser) ->
+
+    host        = utils.getUser no, 0
+    hostBrowser = process.env.__NIGHTWATCH_ENV_KEY is 'host_1'
+    participant = utils.getUser no, 1
+    participantAvatar = ".avatars .avatarview.online[href='/#{participant.username}']"
+
+
+    if hostBrowser
+      collaborationHelpers.startSessionAndInviteUser(browser, host, participant, yes, no)
+
+      browser.waitForElementNotPresent participantAvatar, 60000
+
+      collaborationHelpers.inviteUser(browser,participant.username)
+
+      browser.pause 3000 # wait for invitation
+
+      collaborationHelpers.waitParticipantLeaveAndEndSession(browser)
+      browser.end()
+    else
+      collaborationHelpers.joinSession(browser, host, participant)
+      collaborationHelpers.leaveSessionFromSidebar(browser)
+
+      browser.waitForElementVisible '.share-modal', 20000
+
+      browser.end()


Use the _ide version of browserIsSafari

Use the _ide version of browserIsSafari

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee
@@ -1,13 +1,4 @@
 define [], () ->
-
-	browserIsSafari = () ->
-		userAgent = navigator.userAgent
-		(
-			userAgent.match(/.*Safari\/.*/) &&
-			!userAgent.match(/.*Chrome\/.*/) &&
-			!userAgent.match(/.*Chromium\/.*/)
-		)
-

 	class Parser
 		constructor: (@doc) ->
@@ -16,7 +7,7 @@
 			# Safari regex is super slow, freezes browser for minutes on end,
 			# hacky solution: limit iterations
 			limit = null
-			if browserIsSafari()
+			if window?._ide?.browserIsSafari
 				limit = 100

 			commands = []


Revert back to block

Revert back to block

See http://www.quirksmode.org/css/display.html

--- src/scripts/coffee/app.coffee
+++ src/scripts/coffee/app.coffee
@@ -68,7 +68,7 @@

       canvas = @canvas
       canvas.css('overflow', 'hidden')
-      canvas.css('display', 'inline-block')
+      canvas.css('display', 'block')
       canvas.css('border', '1px solid black')

       canvas = @canvas


Add inline-block support for IE6/7

Add inline-block support for IE6/7

--- src/scripts/coffee/app.coffee
+++ src/scripts/coffee/app.coffee
@@ -68,7 +68,16 @@

       canvas = @canvas
       canvas.css('overflow', 'hidden')
-      canvas.css('display', 'block')
+
+      if !$.support.inlineBlockNeedsLayout)
+        canvas.css("display", "inline-block")
+
+      # IE6/7
+      # see: http://stackoverflow.com/questions/6478876/how-do-you-mix-jquery-and-ie-css-hacks
+      else
+        canvas.css("display", "inline");
+        canvas.css("zoom", "1");
+      #canvas.css('display', 'block')
       canvas.css('border', '1px solid black')

       canvas = @canvas


Close other regions when searching.

Close other regions when searching.

--- core/app/backbone/controllers/search_controller.coffee
+++ core/app/backbone/controllers/search_controller.coffee
@@ -2,11 +2,10 @@

   search: (params={}) =>
     query = params['s']
-
     $('#factlink_search').val(query)
-
     results = new SearchResults [], search: query

+    FactlinkApp.closeAllContentRegions()
     FactlinkApp.mainRegion.show new SearchResultView
       collection: results


removing debug msgs

removing debug msgs

--- src/server/http_server.coffee
+++ src/server/http_server.coffee
@@ -289,13 +289,9 @@

     terminateUserAppSession : (req, app) ->
         if not (req.session and req.session.user) then return
-        console.log "before"
-        console.log req.session.user
         list = []
         list.push(user) for user in req.session.user when user.app isnt app
         req.session.user = list
-        console.log "after"
-        console.log list
         req.session.save()
         if req.session.user.length is 0
             req.session.destroy()


pass browser and session ids to authUrl (Safari fix)

--- src/client/auth.coffee
+++ src/client/auth.coffee
@@ -96,6 +96,8 @@
     params.callback_url = opts.redirect_url || params.callback_url || @currentConfig.get('callback_url') || @currentConfig.get('callbackUrl') || document.location.toString()
     params.auth_referer = document.location.toString()
     params.version      = @currentConfig.get('version')
+    params._bid         = @currentConfig.identifyBrowser().id
+    params._sid         = @currentConfig.identifySession().id
     querystring         = _.map params,(v,k) ->
       encodeURIComponent(k)+'='+encodeURIComponent(v)
     .join('&')


returning collection

returning collection

--- client/router.coffee
+++ client/router.coffee
@@ -8,6 +8,7 @@
     window[inspector_name] = new Meteor.Collection(collection_name)
     Meteor.subscribe subscription_name
   Session.set("collection_name", collection_name)
+  return window[inspector_name]

 Meteor.Router.add
   '/': 'homePage'
@@ -18,11 +19,11 @@
   '/admin/login': 'adminLogin'

   '/admin/:collection': (collection_name) ->
-    setup_collection collection_name
+    collection = setup_collection collection_name
     return 'list_view'

   '/admin/:collection/:document': (collection_name, document_id) ->
-    setup_collection collection_name
+    collection = setup_collection collection_name
     Session.set('document_id', document_id)
     return 'document_view'


add swipe support

add swipe support

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -9,9 +9,39 @@
 $popout = null
 $black = null
 ratio = 1.413684211
+xDown = null
+yDown = null

 delay = (time, func) ->
   setTimeout func, time
+
+
+handleTouchStart = (evt) ->
+  xDown = evt.touches[0].clientX
+  yDown = evt.touches[0].clientY
+
+handleTouchMove = (evt) ->
+  return if !xDown || !yDown
+
+  xUp = evt.touches[0].clientX
+  yUp = evt.touches[0].clientY
+
+  xDiff = xDown - xUp
+  yDiff = yDown - yUp
+
+  if (Math.abs(xDiff) > Math.abs(yDiff))
+    if xDiff > 0
+      prevP()
+    else
+      nextP()
+  else
+    if yDiff > 0
+        # up swipe
+    else
+        # down swipe
+  # reset
+  xDown = null
+  yDown = null

 arrPhilosophy = [
   'absolutism'
@@ -30,7 +60,7 @@
   'The assertion that no belief can be said to have absolute truth, having value only within a certain context or frame of reference.'
 ]

-currIdx = 3
+currIdx = 0

 domready ->
   $main = $tag('main')[0]
@@ -38,6 +68,8 @@
   $desc = $id('desc')
   $black = $id('black')
   $popout = $id('popout')
+  window.addEventListener 'touchstart', handleTouchStart, false
+  window.addEventListener 'touchmove', handleTouchMove, false
   window.addEventListener 'resize', onResize
   window.addEventListener 'keydown', (e) ->
     switch e.keyCode


synctable: fruther robustify reconnects; deal with a potential race condition and also make sure that we don't fail to push changes in case of error

--- src/smc-util/synctable.coffee
+++ src/smc-util/synctable.coffee
@@ -167,21 +167,35 @@
                 dbg("success!")
                 cb?()

-    # Try to connect exactly once.  cb gets error if and only if fails to connect
+    # Try to connect exactly once.  cb gets error if and only if fails to connect.
     __try_to_connect_once: (cb) =>
-        {no_sign_in, client, connect} = @_opts
-        if no_sign_in
-            # just need to be connected
-            if client.is_connected()
-                connect(cb)
-            else
-                client.once 'connected', (=> connect(cb))
-        else
-            # need to be signed in
-            if client.is_signed_in()
-                connect(cb)
-            else
-                client.once 'signed_in', (=> connect(cb))
+        # timer for giving up on waiting to try to connect
+        give_up_timer = undefined
+
+        # actually try to connect
+        do_connect = =>
+            if give_up_timer
+                clearInterval(give_up_timer)
+            @_opts.connect(cb)
+
+        # Which event/condition has too be true before we even try to connect.
+        if @_opts.no_sign_in
+            event = 'connected'
+        else
+            event = 'signed_in'
+
+        if @_opts.client["is_#{event}"]()
+            # The condition is satisfied, so try once to connect.
+            do_connect()
+        else
+            # Wait until condition is satisfied...
+            @_opts.client.once(event, do_connect)
+            # ... but don't wait forever, in case for some reason we miss
+            # the event (this can maybe rarely happen).
+            give_up = =>
+                @_opts.client.removeListener(event, do_connect)
+                cb("timeout")
+            timer = setTimeout(give_up, 5000+Math.random()*10000)

 class SyncTable extends EventEmitter
     constructor: (@_query, @_options, @_client, @_debounce_interval, @_cache_key) ->
@@ -224,10 +238,18 @@
         if @_id?
             @_client.query_cancel(id:@_id)
             @_id = undefined
-        # 1. save, in case we have any local unsaved changes, then sync with upstream.
-        @_save () =>
-            # 2. Now actually do the changefeed query.
-            @_reconnect(cb)
+
+        async.series([
+            (cb) =>
+                # 1. save, in case we have any local unsaved changes, then sync with upstream.
+                if @_value_local? and @_value_server?
+                    @_save(cb)
+                else
+                    cb()
+            (cb) =>
+                # 2. Now actually do the changefeed query.
+                @_reconnect(cb)
+        ], cb)

     _reconnect: (cb) =>
         dbg = @dbg("_run")
@@ -424,11 +446,17 @@
         return changed

     _save: (cb) =>
+        if @__is_saving
+            cb("already saving")
+        else
+            @__is_saving = true
+            @__save (err) =>
+                @__is_saving = false
+                cb?(err)
+
+    __save: (cb) =>
         if @_state == 'closed'
             cb?("closed")
-            return
-        if @_state != 'connected'
-            cb?("not connected")    # do not change this error message; it is assumed elsewhere.
             return
         # console.log("_save('#{@_table}')")
         # Determine which records have changed and what their new values are.
@@ -492,6 +520,7 @@
         @_client.query
             query   : query
             options : [{set:true}]  # force it to be a set query
+            timeout : 30
             cb      : (err) =>
                 if err
                     console.warn("_save('#{@_table}') error:", err)


disable jupyter classic option for minerva users

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -856,6 +856,7 @@

     propTypes :
         editor_settings : rtypes.immutable.Map.isRequired
+        email_address : rtypes.string
         on_change       : rtypes.func.isRequired

     shouldComponentUpdate: (props) ->
@@ -868,6 +869,8 @@
         </span>

     render_checkbox: (name, desc) ->
+        if @props.email_address.indexOf('minerva') != -1 and name == 'jupyter_classic'
+            return
         <Checkbox checked  = {@props.editor_settings.get(name)}
                key      = {name}
                ref      = {name}
@@ -1078,6 +1081,7 @@
         autosave        : rtypes.number
         tab_size        : rtypes.number
         font_size       : rtypes.number
+        email_address   : rtypes.string
         editor_settings : rtypes.immutable.Map

     shouldComponentUpdate: (props) ->
@@ -1124,7 +1128,7 @@
             <EditorSettingsKeyboardVariant
                 on_change={(value)=>@on_change('keyboard_variant',value)} keyboard_variant={@props.editor_settings.get('keyboard_variant')} keyboard_variant_options = {@get_keyboard_variant_options()} />
             <EditorSettingsCheckboxes
-                on_change={@on_change} editor_settings={@props.editor_settings} />
+                on_change={@on_change} editor_settings={@props.editor_settings} email_address={@props.email_address}/>
         </Panel>

 KEYBOARD_SHORTCUTS =
@@ -1385,6 +1389,7 @@
                         tab_size        = {@props.tab_size}
                         font_size       = {@props.font_size}
                         editor_settings = {@props.editor_settings}
+                        email_address   = {@props.email_address}
                         redux           = {@props.redux} />
                     <TerminalSettings
                         terminal = {@props.terminal}


cleanup logging text

cleanup logging text

--- src/3d/extra/LoadingScene.coffee
+++ src/3d/extra/LoadingScene.coffee
@@ -69,7 +69,7 @@
         console.log "WARNING: #{url} is not a valid format"

     interval = setInterval =>
-      console.log(@getLoadedPercent())
+      console.ce("Loaded #{@getLoadedPercent()}%")
       if @isLoadingDone()
         clearInterval(interval)
         console.ce 'Finished loading'


ensure rootview has location property too

--- src/steroids/Converter.coffee
+++ src/steroids/Converter.coffee
@@ -93,7 +93,7 @@
         ->
           statusBar: "default" # will be overridden by native CSS
           fullscreen: !(config.structure.tabs?)
-          location: if config.structure.rootView?
+          location: if config.structure.rootView?.location?
             routingHelpers.parseLocation(config.structure.rootView.location)
           else
             ""


amqp: Use noAck option

amqp: Use noAck option

--- src/amqp.coffee
+++ src/amqp.coffee
@@ -191,10 +191,9 @@
             catch e
               null
             sub.handler sub.binding, data
-          @channel.ack message

         subscribeOptions =
-          noAck: false
+          noAck: true
         @channel.consume name, onSubscribedQueueData, subscribeOptions, (err) ->
           debug 'broker created subscription queue', err
           return callback err


Skipping and limiting results

Skipping and limiting results

--- src/base.coffee
+++ src/base.coffee
@@ -332,6 +332,49 @@
         x[0].age.should.eql 27
         x[1].age.should.eql 28
         x[2].age.should.eql 42
+      ).then ->
+        api.close(done)
+
+
+
+    it "can limit number of results returned", (done) ->
+      api = manikin.create()
+
+      model =
+        stuffz:
+          fields:
+            name: 'string'
+            age: 'number'
+
+      saved = {}
+
+      promise(api).connect(connectionData, model, noErr())
+      .post('stuffz', { name: 'jakob', age: 28 }, noErr())
+      .post('stuffz', { name: 'jakob', age: 42 }, noErr())
+      .post('stuffz', { name: 'julia', age: 27 }, noErr())
+      .list('stuffz', { limit: 2 }, noErr (x) ->
+        x.should.have.length 2
+      ).then ->
+        api.close(done)
+
+
+    it "can skip items", (done) ->
+      api = manikin.create()
+
+      model =
+        stuffz:
+          fields:
+            name: 'string'
+            age: 'number'
+
+      saved = {}
+
+      promise(api).connect(connectionData, model, noErr())
+      .post('stuffz', { name: 'jakob', age: 28 }, noErr())
+      .post('stuffz', { name: 'jakob', age: 42 }, noErr())
+      .post('stuffz', { name: 'julia', age: 27 }, noErr())
+      .list('stuffz', { skip: 2 }, noErr (x) ->
+        x.should.have.length 1
       ).then ->
         api.close(done)


Remove unused code

Remove unused code

--- src/base.coffee
+++ src/base.coffee
@@ -50,8 +50,6 @@
             return parseInt ua.substring(edge + 5, ua.indexOf('.', edge)), 10
         # other browser
         false
-        matches = new RegExp(" MSIE ([0-9]+)\\.([0-9])").exec window.navigator.userAgent
-        return parseInt matches[1]

     getLangURL: (code) ->
         languageSubdomain:


移除EntityList.flush()方法

移除EntityList.flush()方法

--- src/core/entity.coffee
+++ src/core/entity.coffee
@@ -1423,32 +1423,6 @@
 				@_triggerWatcher(["*"], type, arg)
 		return

-	flush: (loadMode) ->
-		if !@_providerInvoker?
-			throw new cola.Exception("Provider undefined.")
-
-		if loadMode and (typeof loadMode == "function" or typeof loadMode == "object")
-			callback = loadMode
-			loadMode = "async"
-
-		@_reset()
-		page = @_findPage(@pageNo)
-		page ?= @_createPage(@pageNo)
-
-		if loadMode is "async"
-			notifyArg = {
-				data: @
-			}
-			@_notify(cola.constants.MESSAGE_LOADING_START, notifyArg)
-			page.loadData(
-				complete: (success, result) =>
-					cola.callback(callback, success, result)
-					@_notify(cola.constants.MESSAGE_LOADING_END, notifyArg)
-			)
-		else
-			page.loadData()
-		return @
-
 	each: (fn, options) ->
 		page = @_first
 		return @ unless page


Remove artifact

Remove artifact

--- client/boot.coffee
+++ client/boot.coffee
@@ -75,6 +75,3 @@

 Handlebars.registerHelper "blogShow", ->
   new Handlebars.SafeString Template.blogShowBody(this)
-
-Handlebars.registerHelper "blogName", ->
-  'Foo'


Fixed issue relating to trailing / on routes being unrecognized

--- client/router.cjsx
+++ client/router.cjsx
@@ -13,10 +13,10 @@
   <Route handler={App} path="/">
     <DefaultRoute handler={FlowDashboard} />
     <NotFoundRoute handler={FlowDashboard} />
-    <Route name="gateblu-dashboard" path="/gateblu" handler={GatebluDashboard} />
-    <Route name="gateblu-device-installs" path="/gateblu/device-installs" handler={GatebluDeviceInstalls} />
-    <Route name="flow-deployment" path="/flow-deployments/:uuid" handler={FlowDeployment} />
-    <Route name="flow-deployment-aggs" path="/flow-deployments" handler={FlowDeploymentAggs} />
+    <Route name="gateblu-dashboard" path="/gateblu/?" handler={GatebluDashboard} />
+    <Route name="gateblu-device-installs" path="/gateblu/device-installs/?" handler={GatebluDeviceInstalls} />
+    <Route name="flow-deployment" path="/flow-deployments/:uuid/?" handler={FlowDeployment} />
+    <Route name="flow-deployment-aggs" path="/flow-deployments/?" handler={FlowDeploymentAggs} />
   </Route>

 Router.run routes, (Handler) ->


configure command bus and commits collection correctly

--- tests/server_module.integration.coffee
+++ tests/server_module.integration.coffee
@@ -1,14 +1,12 @@

 # ============== INTEGRATION SETUP =============== #

-class @CustomerApp extends Space.Application
+class CustomerApp extends Space.Application

   RequiredModules: ['Space.cqrs']

   Dependencies:
-    commandBus: 'Space.cqrs.CommandBus'
     eventBus: 'Space.cqrs.EventBus'
-    commits: 'Space.cqrs.CommitCollection'
     configuration: 'Space.cqrs.Configuration'
     Mongo: 'Mongo'

@@ -16,6 +14,9 @@

     @configuration.createMeteorMethods = false
     @configuration.useInMemoryCollections = true
+
+    @commandBus = @injector.get 'Space.cqrs.CommandBus'
+    @commits = @injector.get 'Space.cqrs.CommitCollection'

     @injector.map('CustomerRegistrations').toStaticValue new @Mongo.Collection(null)
     @injector.map(CustomerRegistrationRouter).asSingleton()
@@ -67,7 +68,7 @@
 class CustomerApp.RegistrationCompleted extends Space.cqrs.Event
   @type 'CustomerApp.RegistrationCompleted'

-# -------------- ENTITIES ---------------
+# -------------- AGGREGATES ---------------

 class Customer extends Space.cqrs.AggregateRoot

@@ -286,4 +287,3 @@
       sourceId: registration.id
       version: 3
     }
-


Use a config schema

Use a config schema

Closes #3
Closes #4
Closes #6

--- lib/init.coffee
+++ lib/init.coffee
@@ -1,8 +1,14 @@
 module.exports =
-  configDefaults:
-    elixircExecutablePath: ""
-    includeDirs: ""
-    pa: ""
+  config:
+    elixircExecutablePath:
+      type: 'string'
+      default: ''
+    includeDirs:
+      type: 'string'
+      default: ''
+    pa:
+      type: 'string'
+      default: ''

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


Clarify error message

Clarify error message

--- lib/main.coffee
+++ lib/main.coffee
@@ -5,7 +5,7 @@
 module.exports =
   config:
     noConfigDisable:
-      title: 'Disable when no sass-lint config file is found in your project'
+      title: 'Disable when no sass-lint config file is found in your project and a .sass-lint.yml file is not specified in the .sass-lint.yml Path option'
       type: 'boolean'
       default: false
     configPath:


:art: Separate pointStart and pointEnd We don't see any decorations in the text editor if start and end is same

--- lib/main.coffee
+++ lib/main.coffee
@@ -15,10 +15,11 @@
         return helpers.exec(jshintPath, parameters, {stdin: text}).then (output) ->
           return JSON.parse(output).result.filter((entry) -> entry.error.id).map (entry) ->
             error = entry.error
-            point = [error.line - 1, error.character - 1]
+            pointStart = [error.line - 1, error.character - 1]
+            pointEnd = [error.line - 1, error.character]
             return {
               type: error.id.substr(1, error.id.length - 2)
               text: "#{error.code} - #{error.reason}"
               filePath
-              range: [point, point]
+              range: [pointStart, pointEnd]
             }


:memo: #196

:memo: #196

--- lib/main.coffee
+++ lib/main.coffee
@@ -50,6 +50,8 @@
       workspaceElement.classList.remove(selector)

       if atom.workspace.isTextEditor?(item)
+        # Still there is possibility editor is destroyed and don't have corresponding
+        # vimState #196.
         @getEditorState(item)?.refreshHighlightSearch()

     @onDidSetHighlightSearchPattern =>


Only show status when at least one incompatible package

--- lib/main.coffee
+++ lib/main.coffee
@@ -54,6 +54,6 @@
   for pack in atom.packages.getLoadedPackages()
     incompatibleCount++ unless pack.isCompatible()

-  if incompatibleCount > -1
+  if incompatibleCount > 0
     IncompatiblePackagesStatusView = require './incompatible-packages-status-view'
     incompatiblePackagesStatusView ?= new IncompatiblePackagesStatusView(statusBar, incompatibleCount)


:new: Add ignoreVCSIgnoredFiles config

:new: Add ignoreVCSIgnoredFiles config

--- lib/main.coffee
+++ lib/main.coffee
@@ -14,12 +14,16 @@
       order: 1

     ignoredMessageTypes:
-      title: 'Ignored message Types'
       description: 'Comma separated list of message types to completely ignore'
       type: 'array'
       default: []
       items:
         type: 'string'
+      order: 2
+    ignoreVCSIgnoredFiles:
+      title: 'Ignore VCS Ignored Files'
+      type: 'boolean'
+      default: true
       order: 2

     showErrorInline:


Add links: thanks, Monica!

Add links: thanks, Monica!

--- src/data.coffee
+++ src/data.coffee
@@ -14,18 +14,18 @@

 data.REGION = {
-  bucegi:     {bbox: [25.36, 45.33, 25.58, 45.52], title: "Bucegi"}
-  capatanii:  {bbox: [24.00, 45.17, 24.19, 45.34], title: "Căpățânii"}
-  ceahlau:    {bbox: [25.85, 46.84, 26.08, 47.04], title: "Ceahlău"}
-  ciucas:     {bbox: [25.84, 45.43, 26.05, 45.56], title: "Ciucaș"}
-  crai:       {bbox: [25.15, 45.49, 25.30, 45.58], title: "Piatra Craiului"}
-  fagaras:    {bbox: [24.30, 45.47, 24.89, 45.73], title: "Făgăraș"}
-  iezer:      {bbox: [24.85, 45.38, 25.10, 45.55], title: "Iezer"}
-  macin:      {bbox: [28.13, 45.07, 28.42, 45.29], title: "Măcin"}
-  nerei:      {bbox: [21.70, 44.80, 22.00, 45.03], title: "Cheile Nerei"}
-  parang:     {bbox: [23.43, 45.31, 23.82, 45.41], title: "Parâng"}
-  piatramare: {bbox: [25.59, 45.48, 25.75, 45.60], title: "Piatra Mare"}
-  retezat:    {bbox: [22.72, 45.29, 23.00, 45.42], title: "Retezat"}
+  bucegi:     {bbox: [25.36, 45.33, 25.58, 45.52], title: "Bucegi", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/mun%C5%A3ii-bucegi_romania_683598"}
+  capatanii:  {bbox: [24.00, 45.17, 24.19, 45.34], title: "Căpățânii", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/mun%C5%A3ii-c%C4%83p%C4%83%C5%A3%C3%A2nii_romania_682818"}
+  ceahlau:    {bbox: [25.85, 46.84, 26.08, 47.04], title: "Ceahlău", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/masivul-ceahl%C4%83u_romania_682481 "}
+  ciucas:     {bbox: [25.84, 45.43, 26.05, 45.56], title: "Ciucaș", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/ciucas_romania_7874266"}
+  crai:       {bbox: [25.15, 45.49, 25.30, 45.58], title: "Piatra Craiului", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/piatra-craiului_romania_670901"}
+  fagaras:    {bbox: [24.30, 45.47, 24.89, 45.73], title: "Făgăraș", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/mun%C5%A3ii-f%C4%83g%C4%83ra%C5%9F_romania_678498"}
+  iezer:      {bbox: [24.85, 45.38, 25.10, 45.55], title: "Iezer", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/mun%C5%A3ii-iezer_romania_675717"}
+  macin:      {bbox: [28.13, 45.07, 28.42, 45.29], title: "Măcin", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/muntii-macin_romania_7289808"}
+  nerei:      {bbox: [21.70, 44.80, 22.00, 45.03], title: "Cheile Nerei", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/44.946N21.863E"}
+  parang:     {bbox: [23.43, 45.31, 23.82, 45.41], title: "Parâng", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/mun%C5%A3ii-par%C3%A2ng_romania_671333"}
+  piatramare: {bbox: [25.59, 45.48, 25.75, 45.60], title: "Piatra Mare", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/piatra-mare_romania_7874272"}
+  retezat:    {bbox: [22.72, 45.29, 23.00, 45.42], title: "Retezat", meteoLink: "https://www.meteoblue.com/en/weather/forecast/week/mun%C5%A3ii-retezatului_romania_668938"}
 }

 SLEEPING_PLACE = {


reticulating multilinking

reticulating multilinking

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -2310,6 +2310,34 @@

     true

+doMultiplink = (sel) =>
+
+
+class MultiLinker
+  constructor: (@ve, @sel) ->
+    console.log("multiplink")
+    console.log(sel)
+
+    @ve.sview.container.style.cursor = "crosshair"
+    @ve.sview.container.onmousedown = (eve) => @handleDown(eve)
+    @tgtP = new THREE.Vector3(0,0,0)
+
+    @links = []
+    for s in @sel
+      l = new BaseElements.Link(@ve.surface.baseRect,
+        s.shp.obj3d.linep,
+        tgtP, 0, 0, 5
+      )
+      l.props.name = @ve.namemanager.getName("link")
+      l.id.name = l.props.name
+      #TODO create the interface (2156)
+      @links.push(l)
+
+  handleDown: (eve) =>
+    @ve.sview.container.style.cursor = "default"
+    @ve.sview.container.onmousedown = (eve) => @ve.sview.mouseh.baseDown(eve)
+
+
 class KeyHandler

   constructor: (@ve) ->
@@ -2326,11 +2354,16 @@
       @multiselect = true
       true
     else if(keycode == 67)
-      @ve.sview.container.style.cursor = "crosshair"
-      @ve.sview.container.onmousemove = (eve) =>
-        @ve.sview.mouseh.linkingH.handleMove0(eve)
-      @ve.sview.container.onmousedown = (eve) =>
-        @ve.sview.mouseh.linkingH.handleDown0(eve)
+      xs = @ve.surface.getSelected()
+      if xs.length == 0
+        @ve.sview.container.style.cursor = "crosshair"
+        @ve.sview.container.onmousemove = (eve) =>
+          @ve.sview.mouseh.linkingH.handleMove0(eve)
+        @ve.sview.container.onmousedown = (eve) =>
+          @ve.sview.mouseh.linkingH.handleDown0(eve)
+      else
+        ml = new MultiLinker(@ve, xs)
+        true

   onup: (event) =>


Fixed eye dropper not updating on drag

Fixed eye dropper not updating on drag

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -136,6 +136,7 @@

   continue: (x, y, lc) ->
     lc.primaryColor = lc.getPixel(x, y)
+    lc.trigger 'colorChange', lc.primaryColor

 class LC.LiterallyCanvas


update behavior seems to be behaving

update behavior seems to be behaving

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -174,6 +174,13 @@
 #The BaseElements object holds classes which are Visual representations of the
 #objects that comprise a Cypress networked CPS system
 BaseElements = {
+
+  updateId: (e) ->
+    e.id.name = e.props.name
+    e.id.sys = e.props.sys
+
+  currentId: (d) ->
+    {name: d.props.name, sys: d.props.sys, design: dsg }

   #The Computer class is a representation of a computer
   Computer: class Computer
@@ -697,37 +704,101 @@
     console.log("updating objects")
     console.log(xs)

-    msg = { Elements: [] }
-
-    lnk_updates = {}
+    #build the update sets
+    link_updates = {}
+    node_updates = {}

     for x in xs
-      if x.shp?
-        x.props.position = x.shp.obj3d.position
+
+      if x.links? #x is a node if it has links
+        node_updates[JSON.stringify(x.id)] = x
+        for l in x.links
+          link_updates[JSON.stringify(l.id)] = l
+
+      if x instanceof BaseElements.Link
+        link_updates[JSON.stringify(x.id)] = x
+        node_updates[JSON.stringify(x.endpoint[0].id)] = x.endpoint[0]
+        node_updates[JSON.stringify(x.endpoint[1].id)] = x.endpoint[1]
+
+      true
+
+    #build the update messages
+    node_msg = { Elements: [] }
+    link_msg = { Elements: [] }
+
+    for _, n of node_updates
+      node_msg.Elements.push(
+        { OID: n.id, Type: n.constructor.name, Element: n.props }
+      )
+      true
+
+    for _, l of link_updates
+      link_msg.Elements.push(
+        { OID: l.id, Type: l.constructor.name, Element: l.props }
+      )
+      true
+
+    doLinkUpdate = () =>
+      console.log("link update")
+      console.log(link_msg)
+
+      if link_msg.Elements.length > 0
+        $.post "/addie/"+dsg+"/design/update", JSON.stringify(link_msg), (data) =>
+          for _, l of link_updates
+            BaseElements.updateId(l)
+
+    doNodeUpdate = () =>
+      console.log("node update")
+      console.log(node_msg)
+
+      if node_msg.Elements.length > 0
+        $.post "/addie/"+dsg+"/design/update", JSON.stringify(node_msg), (data) =>
+          for _, n of node_updates
+            BaseElements.updateId(n)
+          for _, l of link_updates
+            l.setEndpointData()
+
+          doLinkUpdate()
+
+
+    #do the updates
+    if node_msg.Elements.length > 0
+      doNodeUpdate()
+    else if link_msg.Elements.length > 0
+      doLinkUpdate()
+
+
+    ###
+
+    for x in xs

       ido = { OID: x.id, Type: x.constructor.name, Element: x.props }
-      msg.Elements.push(ido)
-
+
+      #if node need to update links too
       if x.links?
         for l in x.links
-          l.setEndpointData()
-          ido = { OID: l.id, Type: l.constructor.name, Element: l.props }
-          #msg.Elements.push(ido)
-          lnk_updates[JSON.stringify(l.id)] = ido
-
-      if x.setEndpointData?
-        x.setEndpointData()
+          lido = { OID: l.id, Type: l.constructor.name, Element: l.props }
+          lnk_updates[JSON.stringify(l.id)] = lido
+
+        node_updates[JSON.stringify(x.id)] = ido
+        #msg.Elements.push(ido)
+
+      #if link need to update node too
+      if x instanceof BaseElements.Link
+        #x.setEndpointData()
         ep = x.endpoint[0]
-        msg.Elements.push(
-          { OID: ep.id, Type: ep.constructor.name, Element: ep.props })
+        nido = { OID: ep.id, Type: ep.constructor.name, Element: ep.props }
+        node_updates[JSON.stringify(ep.id)] = nido

         ep = x.endpoint[1]
-        msg.Elements.push(
-          { OID: ep.id, Type: ep.constructor.name, Element: ep.props })
+        nido = { OID: ep.id, Type: ep.constructor.name, Element: ep.props }
+        node_updates[JSON.stringify(ep.id)] = nido
+
+        lnk_updates[JSON.stringify(x.id)] = ido
+        ls.push(x)

       true
-

     console.log(msg)

@@ -735,9 +806,17 @@
       for x in xs
         x.id.name = x.props.name
         x.id.sys = x.props.sys
+        for y in ls
+          y.setEndpointData()
         msg = { Elements: [] }
-        msg.Elements.push(lu) for _, lu of lnk_updates
+        for _, lu of lnk_updates
+          msg.Elements.push(lu)
         $.post "/addie/"+dsg+"/design/update", JSON.stringify(msg), (data) =>
+          for x in ls
+            x.id.name = x.props.name
+            x.id.sys = x.props.sys
+
+  ###

   load: () =>
     ($.get "/addie/"+dsg+"/design/read", (data, status, jqXHR) =>
@@ -860,6 +939,7 @@
       @mh.ve.container.onmouseup = (eve) => @handleUp(eve)

   handleUp: (event) ->
+    @mh.placingObject.props.position = @mh.placingObject.shp.obj3d.position
     @mh.ve.addie.update([@mh.placingObject])

     @mh.ve.container.onmousemove = null
@@ -879,6 +959,8 @@

 class SurfaceElementSelectHandler
   constructor: (@mh) ->
+    @start = new THREE.Vector3(0,0,0)
+    @end= new THREE.Vector3(0,0,0)

   test: (ixs) ->
     ixs.length > 1 and
@@ -886,6 +968,8 @@
     ixs[0].object.userData.cyjs?

   handleDown: (ixs) ->
+    @mh.updateMouse(event)
+    @start.copy(@mh.pos)
     e = ixs[0].object.userData
     console.log "! surface select -- " + e.constructor.name
     @mh.ve.surface.clearSelection()
@@ -897,7 +981,12 @@
     @mh.ve.container.onmousemove = (eve) => @handleMove(eve)

   handleUp: (ixs) ->
-    @mh.ve.addie.update([@mh.placingObject])
+    @mh.updateMouse(event)
+    @end.copy(@mh.pos)
+    if @mh.placingObject.shp?
+      @mh.placingObject.props.position = @mh.placingObject.shp.obj3d.position
+      if @start.distanceTo(@end) > 0
+        @mh.ve.addie.update([@mh.placingObject])

     @mh.ve.container.onmousemove = null
     @mh.ve.container.onmousedown = (eve) => @mh.baseDown(eve)
@@ -930,17 +1019,26 @@
     for k, v of @cprops
       @datgui.add(@cprops, k)

+    ###
     $(@datgui.domElement).focusout () =>
       for k, v of @cprops
         for e in @elements
           e.props[k] = v
       @ve.addie.update(@elements)
       true
+    ###

     true
+
+  save: () ->
+      for k, v of @cprops
+        for e in @elements
+          e.props[k] = v
+      @ve.addie.update(@elements)

   hide: () ->
     if @datgui?
+      @save()
       @datgui.destroy()
       @elements = []
       @cprops = {}


Fix misclassified output.

Fix misclassified output.

Finding RAML schema files is not an error, so changed from
console.error() to console.log().

--- lib/test.coffee
+++ lib/test.coffee
@@ -18,8 +18,7 @@
     if schemaLocation

       files = glob.sync schemaLocation
-      console.error 'Found JSON ref schemas: ' + files
-      console.error ''
+      console.log '\tJSON ref schemas: ' + files.join(', ')

       tv4.banUnknown = true


It looks like it's needed after all.

It looks like it's needed after all.

--- src/file.coffee
+++ src/file.coffee
@@ -7,6 +7,7 @@
 Q = require 'q'
 runas = require 'runas'

+Directory = null
 PathWatcher = require './main'

 # Public: Represents an individual file that can be watched, read from, and


Initialize UPnP in a different way

Initialize UPnP in a different way

--- lib/upnp.coffee
+++ lib/upnp.coffee
@@ -1,23 +1,42 @@
 uuid = require 'node-uuid'
 ssdp = require './ssdp'
-extend = require('./helpers').extend
-
-configDefaults =
-    device:
-        schema:
-            prefix: 'urn:schemas-upnp-org:device'
-        uuid: 'uuid:' + uuid()
-    network:
-        ssdp:
-            timeout: 1800
-            address: '239.255.255.250'
-            port: 1900
+xmlServer = require './xml-server'

 upnp = {}
-upnp.start = (config, callback) ->
-    # add config values recieved from caller
-    extend config, configDefaults
-    ssdp.start config, (err, msg) ->
+upnp.createDevice = (type, version, callback) =>
+    for arg in arguments
+        if typeof arg is 'Function'
+            callback = arg
+
+    @config =
+        device:
+            schema:
+                prefix: 'urn:schemas-upnp-org:device'
+            uuid: 'uuid:' + uuid()
+            type: type ? 'Basic'
+            version: version ? '1.0'
+        network:
+            ssdp:
+                timeout: 1800
+                address: '239.255.255.250'
+                port: 1900
+            http:
+                address: '192.168.9.3'
+                port: 3000
+
+    xmlServer.start @config, (err, msg) ->
+        callback err, msg
+
+upnp.announce = (name, version, callback) =>
+    for arg in arguments
+        if typeof arg is 'Function'
+            callback = arg
+
+    @config['app'] =
+        name: name ? 'Generic UPnP Device'
+        version: version ? '1.0'
+
+    ssdp.start @config, (err, msg) ->
         callback err, msg

 module.exports = upnp


created d3plus.geom.path2poly

created d3plus.geom.path2poly

--- src/init.coffee
+++ src/init.coffee
@@ -92,6 +92,7 @@
 ###
 d3plus.geom =
   largestRect: require "./geom/largestRect.coffee"
+  path2poly:   require "./geom/path2poly.coffee"

 ###*
 # Utilities related to network graphs.


Cleanup

Cleanup

--- observe.coffee
+++ observe.coffee
@@ -1,143 +0,0 @@
-someObj = {}
-
-Object.observe someObj, (added, changed, removed) ->
-  console.log 'added', added[0]
-  console.log 'changed', changed
-  console.log 'removed', removed
-
-someObj.nestedObject = {foo: 'bar'}
-
-Object.observe someObj.nestedObject, ->
-  # changetracking
-
-someObj.someArray = ['moo']
-
-Array.observe someObj.someArray, ->
-  console.log 'array changed', arguments[0][0]
-
-setTimeout ->
-  console.log 'changing object'
-  #someObj.nestedObject.foo = 'somethingelse'
-  someObj.someArray.push 'somethingelse'
-  someObj.someArray[13] = 'wat'
-, 1000
-
-
-setTimeout ->
-  someObj.someArray[23] = 'illuminati'
-  delete someObj.someArray[23]
-  someObj.someArray['3s'] = '3'
-, 1002
-
-setTimeout ->
-  console.log 'array data'
-  console.log Object.keys(someObj.someArray)
-  console.log Object.keys(someObj.someArray).map((x) -> someObj.someArray[x])
-  console.log someObj.someArray.length
-, 1003
-
-# undefined in mongo db?
-# type (value, reference/objects, arrays), operation (assignment, delete), value, optional property
-
-
-
-
-{
-
-  changed:
-
-  deleted:
-
-  arrays:
-
-  changed:
-    property1: 'value'
-
-    property2: undefined
-
-    nestedObject:
-      nestedProperty: 'value'
-
-    roomOwners[3].name = 'Hans'
-
-    roomOwners:
-      3:
-        name: 'Hans'
-
-  deleted:
-
-
-  types:
-    property1
-
-
-## Super Alex Sample!
-
-a =
-  foo: 'bar'
-
-b =
-  foo: 'bar'
-  foo2: 'bar2'
-
-differences = getDifference a, b
-c = {}
-applyDifferences c, differences
-
-expect(c.hullebulle).to.be 'waat'
-expect(c.foo).to.not.exist
-
-applyDifferences a, differences
-expect(a).to.be.deep.equal b
-
-
-
-
-##
-
-{
-  changed:
-    owners:
-      3:
-        name: 'Hans'
-
-  deleted:
-    owners:
-      3: null
-}
-
-class Room
-  changeOwnerName: (ownerId, name) ->
-    @owners[ownerId].changeName name
-
-
-class RoomOwner
-  changeName: (name) ->
-    @name = name
-
-# '1234', 'Hans'
-
-# RoomOwner:changeName
-# changed
-# name:
-
-collaboration.addDomaRinEvRntHandler 'Room:changeOwnerName', (domainEvent) ->
-  changes = domainEvent.getAggregateChanges()
-  [null, null, {name: 'Hans'}]
-  changes.roomOwners.filter(!null)[0].name
-
-
-  changed:
-    owners: [null, null, name: 'Hans']
-
-
-  for ownerId, owner in changes.owners.map((owner) -> owner not null)
-
-
-  getAggregateChanges '$.owners.*', (index, owner, type) -> ui.owners[index].name = name
-}
-
-accessIdentity.addDomainEventHandler 'Account:create', (domainEvent) ->
-  accountId = domainEvent.aggregate.id
-  changes = domainEvent.getAggregateChanges()
-  email = changes.email


Refactor scrubber queue process

Refactor scrubber queue process

--- node_modules_koding/scrubber/lib/scrubber/index.coffee
+++ node_modules_koding/scrubber/lib/scrubber/index.coffee
@@ -1,16 +1,6 @@
+async = require 'async'
 Traverse = require 'traverse'
 global = if window? then window else this
-###*
-* @helper daisy
-* @description - serial async helper
-###
-daisy =(args)->
-  process.nextTick args.next = ->
-    if fn = args.shift() then !!fn args
-
-slowDaisy =(args)->
-  process.nextTick args.next = ->
-    if fn = args.shift() then process.nextTick fn.bind null, args...

 module.exports = class Scrubber

@@ -53,23 +43,9 @@
     @out = new Traverse(obj).map ->
       cursor = this
       steps.forEach (step)->
-        queue.push -> step.call scrubber, cursor, -> queue.next()
-      return
-    queue.push ->
-      callback.call scrubber
-    if seemsTooComplex queue.length, 4
-      slowDaisy queue
-    else
-      daisy queue
+        queue.push (next) -> step.call scrubber, cursor, next

-  seemsTooComplex =do->
-    maxStackSize = try
-      i = 0
-      do f =-> i++; f()
-    catch e then i
-    (length, weight)->
-      guess = length * weight
-      guess > maxStackSize
+    async.series queue, -> callback scrubber

   ###*
   * @method Scrubber#forEach


trailing space cleanup

trailing space cleanup

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -40,7 +40,7 @@
 prefixList = ['Webkit', 'Moz', 'O', 'ms', 'Khtml']

 # A map of the CSS3 properties needed to support oriDomi, with shorthand names as keys.
-css =
+css =
   transform: 'transform'
   origin: 'transformOrigin'
   transformStyle: 'transformStyle'
@@ -126,7 +126,7 @@
   for prop of source
     if not target[prop]?
       target[prop] = source[prop]
-
+
   # Return the extended target object.
   target

@@ -177,7 +177,7 @@
     devMode and console.time 'oridomiConstruction'
     # If the browser doesn't support oriDomi, return the element unmodified.
     return @el unless oriDomiSupport
-
+
     # If the constructor wasn't called with the `new` keyword, invoke it again.
     unless @ instanceof OriDomi
       return new oriDomi @el, @settings
@@ -900,7 +900,7 @@
         if @shading
           @_setShader i, @lastAnchor, angle
       , 0
-
+
     onTransitionEnd = (e) =>
       @panels[@lastAnchor][i].removeEventListener css.transitionEnd, onTransitionEnd, false
       # Increment the iterator and check if we're past the last panel.


Classroom: Used router to handle display a class view.

--- client/landing/app/Applications/Classroom.kdapplication/Views/classroomclassthumbview.coffee
+++ client/landing/app/Applications/Classroom.kdapplication/Views/classroomclassthumbview.coffee
@@ -40,7 +40,7 @@
     appView = @getDelegate()
     chapter = data.chapter

-    appView.goToClass data.name, =>
+    KD.getSingleton("router").handleQuery "?class=#{data.name}"
     @emit "EnrollmentRequested", data  if @getOptions().type isnt "enrolled"

   pistachio: ->


Use the logo from uploaded assets for the menu footer

--- packages/rocketchat-lib/server/startup/settings.coffee
+++ packages/rocketchat-lib/server/startup/settings.coffee
@@ -174,14 +174,14 @@
 		@add 'Layout_Home_Body', 'Welcome to Rocket.Chat
 Go to APP SETTINGS -> Layout to customize this intro.', { type: 'code', code: 'text/html', multiline: true, public: true }
 		@add 'Layout_Terms_of_Service', 'Terms of Service
 Go to APP SETTINGS -> Layout to customize this page.', { type: 'code', code: 'text/html', multiline: true, public: true }
 		@add 'Layout_Privacy_Policy', 'Privacy Policy
 Go to APP SETTINGS -> Layout to customize this page.', { type: 'code', code: 'text/html', multiline: true, public: true }
-		@add 'Layout_Sidenav_Footer', '

 with  on

', { type: 'code', code: 'text/html', public: true, i18nDescription: 'Layout_Sidenav_Footer_description' }
+		@add 'Layout_Sidenav_Footer', '', { type: 'code', code: 'text/html', public: true, i18nDescription: 'Layout_Sidenav_Footer_description' }

 	@section 'Custom Scripts', ->
 		@add 'Custom_Script_Logged_Out', '//Add your script', { type: 'code', multiline: true, public: true }
 		@add 'Custom_Script_Logged_In', '//Add your script', { type: 'code', multiline: true, public: true }

 	@section 'Login', ->
-		@add 'Layout_Login_Terms', 'By proceeding to create your account and use Rocket.Chat, you are agreeing to our Terms of Service and Privacy Policy. If you do not agree, you cannot use Rocket.Chat.', { type: 'string', multiline: true, public: true }
+		@add 'Layout_Login_Terms', 'By proceeding you are agreeing to our Terms of Service and Privacy Policy.', { type: 'string', multiline: true, public: true }

 RocketChat.settings.addGroup 'Logs', ->


removing mimosa

removing mimosa

--- mimosa-config.coffee
+++ mimosa-config.coffee
@@ -1,10 +0,0 @@
-exports.config =
-  modules: ['copy', 'jshint']
-  watch:
-    sourceDir: "lib"
-    compiledDir: "ignore"
-    javascriptDir: null
-  jshint:
-    rules:
-      node: true
-      laxcomma: true


engine data module

engine data module

--- src/engine_data.coffee
+++ src/engine_data.coffee
@@ -7,7 +7,7 @@
 @param {Object} I Instance variables
 @param {Object} self Reference to the engine
 ###
-Engine.Delay = (I, self) ->
+Engine.Data = (I, self) ->
   delayedEvents = []

   self.bind 'afterUpdate', ->


refactor: singleton object created from anonymouse class

--- src/kick.coffee
+++ src/kick.coffee
@@ -5,7 +5,7 @@
 stage_width = 1000

-fps = new class FramesPerSecond
+fps = new class
     constructor: ->
         @frames = 20
         @time = 0
@@ -23,7 +23,7 @@
         @rate().toFixed(1) + ' fps'

-player = new class Player
+player = new class
     constructor: ->
         @lvl = 1
         @point = 0
@@ -34,7 +34,7 @@
         @longest = []

-pool_words = new class PoolWord
+pool_words = new class
     constructor: ->
         @words = []
         @actions = ['box', 'kick', 'punch', 'strike']
@@ -66,7 +66,7 @@
             $('#playground').append(word.repr)

-inventory = new class Inventory
+inventory = new class
     constructor: ->
         @words = []
         @name = null


tie logging in with github/twitter to contestant/judge. #142

--- models/person.coffee
+++ models/person.coffee
@@ -34,21 +34,22 @@
       appSecret: env.secrets.github
       findOrCreateUser: (sess, accessTok, accessTokExtra, ghUser) ->
         promise = @Promise()
-        Person.findOne 'github.id': ghUser.id, (err, foundUser) ->
-          if foundUser
-            foundUser.updateWithGithub ghUser, (err, updatedUser) ->
-              return promise.fail err if err
-              promise.fulfill updatedUser
-          else if sess.invite
-            Team = mongoose.model 'Team'
-            Team.findOne 'invites.code': sess.invite, (err, team) ->
-              return promise.fail err if err
-              return promise.fulfill(id: null) unless team
-              Person.createWithGithub ghUser, accessTok, (err, createdUser) ->
+        Person.findOne 'github.id': ghUser.id, role: 'contestant',
+          (err, foundUser) ->
+            if foundUser
+              foundUser.updateWithGithub ghUser, (err, updatedUser) ->
                 return promise.fail err if err
-                promise.fulfill createdUser
-          else
-            promise.fulfill id: null
+                promise.fulfill updatedUser
+            else if sess.invite
+              Team = mongoose.model 'Team'
+              Team.findOne 'invites.code': sess.invite, (err, team) ->
+                return promise.fail err if err
+                return promise.fulfill(id: null) unless team
+                Person.createWithGithub ghUser, accessTok, (err, createdUser) ->
+                  return promise.fail err if err
+                  promise.fulfill createdUser
+            else
+              promise.fulfill id: null
         promise
   twitter:
     everyauth:
@@ -62,6 +63,7 @@
         screenName = new RegExp("^#{RegExp.escape twit.screen_name}$", 'i')
         Person.findOne
           $or: [ { 'twit.id': twit.id }, { twitterScreenName: screenName } ]
+          role: { $in: [ 'judge', 'nomination' ] }
           (err, person) ->
             return promise.fail err if err
             return promise.fulfill(id: null) unless person


Add support for Enter, Leave and Topic messages

--- src/fleepClient.coffee
+++ src/fleepClient.coffee
@@ -1,5 +1,5 @@
 {EventEmitter} = require 'events'
-{TextMessage} = require 'hubot'
+{TextMessage, EnterMessage, LeaveMessage, TopicMessage} = require 'hubot'

 WebRequest = require './webRequest'
 Util = require './util'
@@ -147,14 +147,8 @@

     # Ignore edited messages (messages that were posted, then edited)
     # See https://github.com/anroots/hubot-fleep/issues/4
-    if event.revision_message_nr?
+    if event.revision_message_nr? and event.mk_message_type isnt 'topic'
       @robot.logger.debug 'This is an edited message, skipping...'
-      return
-
-    # Ignore messages without the 'message' key - some invalid state
-    if not event.message?
-      @robot.logger.error 'Invalid API response from the server!' +
-      ' Expected a "message" key.'
       return

     # Patch the received text message to Hubot
@@ -170,23 +164,48 @@
   handleMessage: (message) =>

     # Do nothing if the message is already seen
-    return if @isMessageSeen message.conversation_id, message.message_nr
-
-    # Strip HTML tags
-    text = message.message.replace(/(<([^>]+)>)/ig,"")
+    if @isMessageSeen message.conversation_id, message.message_nr
+      @robot.logger.debug 'Already seen message ' + message.message_nr
+      return
+
+    @robot.logger.info 'Got message: ' + JSON.stringify message
+
+    text = null
+
+    if message.message?
+      # Strip HTML tags
+      text = message.message.replace(/(<([^>]+)>)/ig,"")

     # Mark message as read
     @markRead message.conversation_id, message.message_nr

-    @robot.logger.info 'Got message: ' + JSON.stringify message
-
     # Create a Hubot user object
-    author = @robot.brain.userForId message.account_id
+    if message.mk_message_type? and message.mk_message_type in ['kick','add']
+      senderId = JSON.parse(message.message).members[0]
+    else
+      senderId = message.account_id
+
+    author = @robot.brain.userForId senderId
     author.room = message.conversation_id
-    author.reply_to = message.account_id
-
-    textMessage = new TextMessage author, text
-    @emit 'gotMessage', textMessage
+    author.reply_to = senderId
+
+    msg = new TextMessage author, text, message.message_nr
+    if message.mk_message_type?
+      switch message.mk_message_type
+        when 'kick'
+          @robot.logger.debug 'User kicked from conversation'
+          msg = new LeaveMessage author
+        when 'add'
+          @robot.logger.debug 'User added to conversation'
+          msg = new EnterMessage author
+        when 'topic'
+          @robot.logger.debug 'Topic changed'
+          msg = new TopicMessage(author,
+            JSON.parse(message.message).topic,
+            message.message_nr
+          )
+
+    @emit 'gotMessage', msg

   # Send a new long poll request to the Fleep API
   # The request will wait ~90 seconds


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -48,7 +48,7 @@
   @param {Number} [frequency=3] The number of frames in between opacity changes
   @param {Number} [alpha=0.5] The alpha value to flicker to
   ###
-  flicker: ({duration = 30, frequency = 3, alpha = 0.5}) ->
+  flicker: ({duration: 30, frequency: 3, alpha: 0.5}) ->
     I.flickerDuration = duration
     I.flickerFrequency = frequency
     I.flickerAlpha = alpha


fix toDelta pointer bug

fix toDelta pointer bug

--- src/line.coffee
+++ src/line.coffee
@@ -127,7 +127,7 @@

   toDelta: ->
     deltas = _.map(@leaves.toArray(), (leaf) ->
-      return new JetInsert(leaf.text, leaf.attributes)
+      return new JetInsert(leaf.text, _.clone(leaf.attributes))
     )
     delta = new JetDelta(0, @length, deltas)
     delta.compact()


Offer Disabled Choice

Offer Disabled Choice

--- src/coffee/game.coffee
+++ src/coffee/game.coffee
@@ -6,6 +6,11 @@
       'Punchiness'
       'One\'s capability for punching.'
       value: 2
+
+    quality 'deathWish',
+      'Death Wish'
+      'Has existence become a burden?'
+      value: false

     quality 'luck',
       'Luck'
@@ -37,6 +42,11 @@
         'Lightly'
         'I don\'t want to muss my moustache.'
         'body-soft'
+      choice 'to-the-death',
+        'To the death!',
+        'Wait, actually, that sounds like a terrible idea.'
+        'to-the-death'
+        active: (deathWish) -> deathWish

     storylet 'body-hard',
       'You work out hard'


Added mapping plugin to require.js config

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -3,7 +3,7 @@
   paths:
     knockout: 'http://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.0/knockout-min'
     jquery:   'https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min'
-
+    mapping:  '../vendor/knockout.mapping'

 define ['jquery'], ($) ->


Fix minor indentation issue

Fix minor indentation issue

--- src/commandline.coffee
+++ src/commandline.coffee
@@ -187,9 +187,9 @@
     options.argv.color ?= if options.argv.nocolor then "never" else "auto"

     colorize = switch options.argv.color
-      when "always" then true
-      when "never" then false
-      else process.stdout.isTTY
+        when "always" then true
+        when "never" then false
+        else process.stdout.isTTY

     reporter = new SelectedReporter errorReport, {
         colorize: colorize


implement `_quickscale()` to perform faster SVG path scaling

--- src/main.coffee
+++ src/main.coffee
@@ -37,7 +37,9 @@
 #...........................................................................................................
 OT                        = @_OT      = require 'opentype.js'
 SvgPath                   = @_SvgPath = require 'svgpath'
-path_precision            = 5
+# DUMBSVGPATH               = require './experiments/dumb-svg-parser'
+path_precision            = 3
+

 #===========================================================================================================
 # METRICS
@@ -193,18 +195,44 @@
   return if R.unicode? then R else null

 #-----------------------------------------------------------------------------------------------------------
-@glyph_and_pathdata_from_cid = ( me, otjsfont, cid ) ->
+@_quickscale = ( path_obj, scale_x, scale_y = null ) ->
+  scale_y ?= scale_x
+  for command in path_obj.commands
+    for key, value of command
+      switch key
+        when 'x', 'x1', 'x2' then command[ key ] *= scale_x
+        when 'y', 'y1', 'y2' then command[ key ] *= scale_y
+  return null
+
+#-----------------------------------------------------------------------------------------------------------
+@glyph_and_pathdata_from_cid = ( me, otjsfont, cid, tag = 'use-quickscale' ) ->
   validate.positive_integer cid
   fglyph              = @glyph_from_cid otjsfont, cid
   return null unless fglyph?
   path_obj            = fglyph.getPath 0, 0, me.font_size
-  pathdata            = path_obj.toPathData path_precision
-  return null if pathdata.length is 0
-  svg_path            = new SvgPath pathdata
+  return null if path_obj.commands.length is 0
   global_glyph_scale  = me.global_glyph_scale ? 1
   scale_factor        = me.scale_factor * global_glyph_scale
-  svg_path            = svg_path.scale scale_factor, -scale_factor
-  return { glyph: fglyph, pathdata: svg_path.toString(), }
+  switch tag
+    when 'use-quickscale'
+      @_quickscale path_obj, scale_factor, -scale_factor
+      pathdata = path_obj.toPathData path_precision
+      return { glyph: fglyph, pathdata, }
+    when 'use-dumb-svg-parser'
+      throw new Error "^svgttf@3223 dumb-svg-parser not yet supported"
+      pathdata            = path_obj.toPathData path_precision
+      svg_path            = DUMBSVGPATH.parse pathdata
+      # debug '^3362^', svg_path
+      DUMBSVGPATH.scale scale_factor, -scale_factor
+      # debug '^3362^', svg_path
+      return { glyph: fglyph, pathdata: ( DUMBSVGPATH.as_compressed_text svg_path ), }
+    when 'use-svgpath'
+      pathdata            = path_obj.toPathData path_precision
+      svg_path            = new SvgPath pathdata
+      svg_path            = svg_path.scale scale_factor, -scale_factor
+      svg_path            = svg_path.round path_precision
+      return { glyph: fglyph, pathdata: svg_path.toString(), }
+  throw new Error "^svgttf@4582^ unknown tag #{rpr tag}"

 #-----------------------------------------------------------------------------------------------------------
 @otjspath_from_pathdata = ( pathdata ) ->


Delete main.coffee

Delete main.coffee


:memo:

:memo:

--- spec/module-cache-spec.coffee
+++ spec/module-cache-spec.coffee
@@ -8,7 +8,7 @@
   beforeEach ->
     spyOn(Module, '_findPath').andCallThrough()

-  it 'resolves atom shell module paths without hitting the filesystem', ->
+  it 'resolves Electron module paths without hitting the filesystem', ->
     builtins = ModuleCache.cache.builtins
     expect(Object.keys(builtins).length).toBeGreaterThan 0


Add another spec for including globally excluded file patterns

--- spec/path-scanner-spec.coffee
+++ spec/path-scanner-spec.coffee
@@ -107,17 +107,7 @@
         runs ->
           expect(paths).not.toContain path.join(rootPath, 'dir', 'file7_ignorable.rb')

-      it "local inclusions override global exclusions", ->
-        scanner = new PathScanner(rootPath, inclusions: ['dir'], globalExclusions: ['dir'])
-        scanner.on('path-found', pathHandler = createPathCollector())
-        scanner.on('finished-scanning', finishedHandler = jasmine.createSpy())
-
-        runs -> scanner.scan()
-        waitsFor -> finishedHandler.callCount > 0
-        runs ->
-          expect(paths).toContain path.join(rootPath, 'dir', 'file7_ignorable.rb')
-
-      it "lists only paths specified by file pattern", ->
+      it "excludes paths matching negated patterns in `inclusions`", ->
         scanner = new PathScanner(rootPath, inclusions: ['!*.js'])
         scanner.on('path-found', pathHandler = createPathCollector())
         scanner.on('finished-scanning', finishedHandler = jasmine.createSpy())
@@ -128,6 +118,29 @@
           expect(paths).not.toContain path.join(rootPath, 'newdir', 'deep_dir.js')
           expect(paths).not.toContain path.join(rootPath, 'sample.js')
           expect(paths).toContain path.join(rootPath, 'sample.txt')
+
+      it "local directory inclusions override global directory exclusions", ->
+        scanner = new PathScanner(rootPath, inclusions: ['dir'], globalExclusions: ['dir'])
+        scanner.on('path-found', pathHandler = createPathCollector())
+        scanner.on('finished-scanning', finishedHandler = jasmine.createSpy())
+
+        runs -> scanner.scan()
+        waitsFor -> finishedHandler.callCount > 0
+        runs ->
+          expect(paths).toContain path.join(rootPath, 'dir', 'file7_ignorable.rb')
+
+      it "local file inclusions override global file exclusions", ->
+        scanner = new PathScanner(rootPath, inclusions: ['*.txt'], globalExclusions: ['*.txt', '.root' + path.sep])
+        scanner.on('path-found', pathHandler = createPathCollector())
+        scanner.on('finished-scanning', finishedHandler = jasmine.createSpy())
+
+        runs -> scanner.scan()
+        waitsFor -> finishedHandler.callCount > 0
+        runs ->
+          expect(paths).toContain path.join(rootPath, 'file1.txt')
+          expect(paths).not.toContain path.join(rootPath, 'file4_noext')
+          expect(paths).not.toContain path.join(rootPath, '.root', 'file3.txt')
+          expect(paths).not.toContain path.join(rootPath, '.root', 'subdir', 'file1.txt')

       it "correctly matches local inclusions and exclusions", ->
         scanner = new PathScanner(rootPath, inclusions: ['file*', '!*.txt'])


Hardcode moment dependencies.

Hardcode moment dependencies.

Required by r.js optimization.

--- src/p13n.coffee
+++ src/p13n.coffee
@@ -1,22 +1,14 @@
 # Personalization support code
-
-#momentDeps = ("moment/#{makeMomentLang(lang)}" for lang in SUPPORTED_LANGUAGES)
-#
-#p13nDeps = [
-#    'module',
-#    'cs!app/models',
-#    'underscore',
-#    'backbone',
-#    'i18next',
-#    'moment'].concat momentDeps
-
 define [
     'module',
     'cs!app/models',
     'underscore',
     'backbone',
     'i18next',
-    'moment'
+    'moment',
+    'moment/fi',
+    'moment/sv',
+    'moment/en-gb'
 ], (
     module,
     models,


:lipstick:

:lipstick:

--- src/pane.coffee
+++ src/pane.coffee
@@ -204,7 +204,7 @@
       when 'before-item-destroyed'
         Grim.deprecate("Use Pane::onWillDestroyItem instead")
       else
-        Grim.deprecate("Subscribing via ::on is deprecated. Use event subscription methods instead.")
+        Grim.deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.")
     super

   behavior: (behaviorName) ->


Remove unused require

Remove unused require

--- src/key-binding.coffee
+++ src/key-binding.coffee
@@ -1,4 +1,3 @@
-{$} = require './space-pen-extensions'
 _ = require 'underscore-plus'
 fs = require 'fs-plus'
 {specificity} = require 'clear-cut'


coordinator: Don't crash on wrong discovery messages

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -63,7 +63,10 @@
         debug 'connected', err
         return callback err if err
         @broker.subscribeParticipantChange (msg) =>
-          @handleFbpMessage msg.data
+          try
+            @handleFbpMessage msg.data
+          catch e
+            console.log 'Participant discovery failed:', e.message
           @broker.ackMessage msg
         @started = true
         debug 'started', err, @started
@@ -79,7 +82,7 @@
     if data.protocol == 'discovery' and data.command == 'participant'
       @addParticipant data.payload
     else
-      throw new Error 'Unknown FBP message'
+      throw new Error "Unknown FBP message: #{typeof(data)} #{data?.protocol}:#{data?.command}"

   addParticipant: (definition) ->
     debug 'addParticipant', definition.id


switch to class-based formatting for autocomplete underlay

--- src/core/format.coffee
+++ src/core/format.coffee
@@ -98,9 +98,11 @@
       exclude: ['h1', 'h2', 'bullet', 'list', 'blockquote']
       tag: 'H3'

-    opacity:
-      style: 'opacity'
-      default: '1'
+    invisible:
+      class: 'invisible'
+
+    dashedUnderline:
+      class: 'dashed-underline'

     pdf:
       type: Format.types.EMBED


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -22,7 +22,7 @@
   equals (-5).clamp(0, 1), 0
   equals 1.clamp(0, null), 1
   equals (-1).clamp(0, null), 0
-  equals (-10).clamp(
+  equals (-10).clamp()

 test "#floor", ->
   equals 4.9.floor(), 4, "(4.9).floor() equals 4"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -148,7 +148,7 @@
           Module = Module.constantize()
         else
           for key, value of root
-            if
+            if value is

         unless I.included_modules.include moduleName
           I.included_modules.push moduleName


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -150,5 +150,5 @@
     @methodOf Core#
     @param {Module} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
     ###
-    include: (Module) ->
+    include: (modules...) ->
       self.extend Module(I, self)


Using crazy new include lookup for non-string modules

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -170,7 +170,7 @@

       send: (name, args...) ->
         self[name](args...)
-
+
     # Initial module inclue, for reconstructing objects from JSON
     for moduleName in I.includedModules
       Module = moduleName.constantize()


get username?

get username?

--- src/poll.coffee
+++ src/poll.coffee
@@ -55,7 +55,7 @@

 	robot.respond /vote\s+(\d)/i, (msg) ->
 		vote = msg.match[1]
-		user = msg.envelope.user.id
+		user = msg.envelope.user['name']
 		@robot.logger.info "Adding a vote for #{vote} for #{user}"
 		#check if the user already voted
 		for option in @robot.brain.data.poll.options


debugging

debugging

--- src/poll.coffee
+++ src/poll.coffee
@@ -57,7 +57,8 @@
 		try
 			vote = msg.match[1]
 			user = msg.envelope.user['name']
-			options = @robot.brain.data.poll.options
+			options = @robot.brain.data.poll.options
+			@robot.logger.info "Adding new vote for #{@robot.brain.data.poll.topic} with #{@robot.brain.data.poll.options.length} options"
 			if vote >= options.length
 				@robot.logger.info "Invalid vote. #{vote} >= #{options.length}"
 				msg.reply "Please vote for a valid option"