Ace: Code styling in find and replace view.

--- client/ace/lib/acefindandreplaceview.coffee
+++ client/ace/lib/acefindandreplaceview.coffee
@@ -17,6 +17,121 @@

     @mode             = null
     @lastViewHeight   = 0
+
+    @createElements()
+
+
+  handleKeyDown: (isFind, e) ->
+
+    code = e.which or e.keyCode
+    key  = keycode code
+
+    switch key
+      when 'esc' then @close()
+      when 'enter'
+        @findPrev()  if isFind and e.shiftKey is yes
+      when 'f'
+        if e.metaKey
+          e.preventDefault()
+          @setViewHeight e.shiftKey is yes
+    return
+
+
+  close: (fireEvent = yes) ->
+
+    @hide()
+    @resizeEditor 0
+    @findInput.setValue    ''
+    @replaceInput.setValue ''
+    @emit 'FindAndReplaceViewClosed'  if fireEvent
+
+  setViewHeight: (isReplaceMode) ->
+    height   = 31
+    @mode    = 'find'
+    if isReplaceMode
+      height = 60
+      @mode  = 'replace'
+
+    @$().css { height }
+    @resizeEditor height
+
+    for widget in [@replaceInput, @replaceButton, @replaceAllButton]
+      if isReplaceMode then widget.show() else widget.hide()
+
+    @show()
+
+
+  resizeEditor: (height) ->
+
+    if @getDelegate()?.ace?
+      {ace} = @getDelegate()
+      return unless ace.editor
+
+      ace.setHeight ace.getHeight() + @lastHeightTakenFromAce - height
+      ace.editor.resize yes
+      @lastHeightTakenFromAce = height
+
+
+  lastHeightTakenFromAce: 0
+
+
+  setTextIntoFindInput: (text) ->
+
+    return @findInput.setFocus() if text.indexOf("\n") > 0 or text.length is 0
+
+    @findInput.setValue text
+    @findInput.setFocus()
+
+
+  getSearchOptions: ->
+
+    @selections   = @choices.getValue()
+
+    caseSensitive : @selections.indexOf('case-sensitive') > -1
+    wholeWord     : @selections.indexOf('whole-word') > -1
+    regExp        : @selections.indexOf('regex') > -1
+    backwards     : no
+
+
+  findNext: -> @findHelper 'next'
+
+
+  findPrev: -> @findHelper 'prev'
+
+
+  findHelper: (direction) ->
+
+    keyword = @findInput.getValue()
+
+    return unless keyword
+
+    methodName = if direction is 'prev' then 'findPrevious' else 'find'
+    @getDelegate().ace.editor[methodName] @findInput.getValue(), @getSearchOptions()
+    @findInput.focus()
+
+
+  replace:    -> @replaceHelper no
+
+
+  replaceAll: -> @replaceHelper yes
+
+
+  replaceHelper: (doReplaceAll) ->
+
+    findKeyword    = @findInput.getValue()
+    replaceKeyword = @replaceInput.getValue()
+
+    return unless findKeyword or replaceKeyword
+
+    {editor}   = @getDelegate().ace
+    methodName = if doReplaceAll then 'replaceAll' else 'replace'
+
+    editor[methodName] replaceKeyword
+
+    @findNext()
+
+
+  createElements: ->

     @findInput = new KDHitEnterInputView
       type         : 'text'
@@ -69,97 +184,8 @@
       defaultValue : 'fakeValueToDeselectFirstOne'

-  handleKeyDown: (isFind, e) ->
-
-    code = e.which or e.keyCode
-    key  = keycode code
-
-    switch key
-      when 'esc' then @close()
-      when 'enter'
-        @findPrev()  if isFind and e.shiftKey is yes
-      when 'f'
-        if e.metaKey
-          e.preventDefault()
-          @setViewHeight e.shiftKey is yes
-    return
-
-
-  close: (fireEvent = yes) ->
-    @hide()
-    @resizeEditor 0
-    @findInput.setValue    ""
-    @replaceInput.setValue ""
-    @emit 'FindAndReplaceViewClosed'  if fireEvent
-
-  setViewHeight: (isReplaceMode) ->
-    height   = 31
-    @mode    = 'find'
-    if isReplaceMode
-      height = 60
-      @mode  = 'replace'
-
-    @$().css { height }
-    @resizeEditor height
-
-    for widget in [@replaceInput, @replaceButton, @replaceAllButton]
-      if isReplaceMode then widget.show() else widget.hide()
-
-    @show()
-
-
-  resizeEditor: (height) ->
-    if @getDelegate()?.ace?
-      {ace} = @getDelegate()
-      return unless ace.editor
-
-      ace.setHeight ace.getHeight() + @lastHeightTakenFromAce - height
-      ace.editor.resize yes
-      @lastHeightTakenFromAce = height
-
-  lastHeightTakenFromAce: 0
-
-  setTextIntoFindInput: (text) ->
-    return @findInput.setFocus() if text.indexOf("\n") > 0 or text.length is 0
-    @findInput.setValue text
-    @findInput.setFocus()
-
-  getSearchOptions: ->
-    @selections   = @choices.getValue()
-
-    caseSensitive : @selections.indexOf('case-sensitive') > -1
-    wholeWord     : @selections.indexOf('whole-word') > -1
-    regExp        : @selections.indexOf('regex') > -1
-    backwards     : no
-
-  findNext: -> @findHelper 'next'
-
-  findPrev: -> @findHelper 'prev'
-
-  findHelper: (direction) ->
-    keyword = @findInput.getValue()
-    return unless keyword
-    methodName = if direction is 'prev' then 'findPrevious' else 'find'
-    @getDelegate().ace.editor[methodName] @findInput.getValue(), @getSearchOptions()
-    @findInput.focus()
-
-  replace:    -> @replaceHelper no
-
-  replaceAll: -> @replaceHelper yes
-
-  replaceHelper: (doReplaceAll) ->
-    findKeyword    = @findInput.getValue()
-    replaceKeyword = @replaceInput.getValue()
-    return unless findKeyword or replaceKeyword
-
-    {editor}   = @getDelegate().ace
-    methodName = if doReplaceAll then 'replaceAll' else 'replace'
-
-    editor[methodName] replaceKeyword
-    @findNext()
-
   pistachio: ->
-    """
+    return """
       <div class="ace-find-replace-settings">
         {{> @choices}}
       </div>


stacks: aws error handling improvements

stacks: aws error handling improvements

--- client/admin/lib/views/stacks/outputparser.coffee
+++ client/admin/lib/views/stacks/outputparser.coffee
@@ -4,10 +4,22 @@

 # You can find the list of AWS error messages here
 # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html
+VPC_WIKI_URL = 'http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Appendix_Limits.html'
+VPC_FORM_URL = 'https://console.aws.amazon.com/support/home#/case/create?issueType=service-limit-increase&limitType=service-code-vpc'

 HANDLED_ERRORS =
-  VpcLimitExceeded: "You've reached the limit on the number of VPCs that you can create in the region. For more information about VPC limits, see Amazon VPC Limits To request an increase on your VPC limit, complete the Amazon VPC Limits form."
-  InternetGatewayLimitExceeded: "You've reached the limit on the number of Internet gateways that you can create. For more information, see Amazon VPC Limits. To request an increase on the Internet gateway limit, complete the Amazon VPC Limits form."
+  VpcLimitExceeded: "
+    You've reached the limit on the number of VPCs that you can create in this region.
+    For more information, see Amazon VPC Limits.
+    Unless indicated otherwise, you can request an increase by using the
+    Amazon VPC Limits form.
+    "
+  InternetGatewayLimitExceeded: "
+    You've reached the limit on the number of Internet gateways that you can create.
+    For more information, see Amazon VPC Limits.
+    Unless indicated otherwise, you can request an increase by using the
+    Amazon VPC Limits form.
+    "

 module.exports = outputParser =


show welcome page in first login

show welcome page in first login

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -174,7 +174,7 @@
       if pages.length is 0
         pages.push "table-of-contents"
         appStorage.setValue "readPages", pages
-        KD.getSingleton('mainController').emit "ShowInstructionsBook"
+        KD.getSingleton('mainController').emit "ShowInstructionsBook", 1


avoid exception in LoggerSerializers

avoid exception in LoggerSerializers

--- services/web/app/coffee/infrastructure/LoggerSerializers.coffee
+++ services/web/app/coffee/infrastructure/LoggerSerializers.coffee
@@ -1,5 +1,7 @@
 module.exports =
 	user: (user) ->
+		if !user?
+			return null
 		if !user._id?
 			user = {_id : user}
 		return {
@@ -10,6 +12,8 @@
 		}

 	project: (project) ->
+		if !project?
+			return null
 		if !project._id?
 			project = {_id: project}
 		return {


use custom classname

use custom classname

--- client/lanes/components/shared/ScreenWrapper.cjsx
+++ client/lanes/components/shared/ScreenWrapper.cjsx
@@ -5,7 +5,7 @@

     render: ->
         classes = _.classnames(
-            'screen-wrapper',  @props.identifier,
+            'screen-wrapper',  @props.identifier, @props.className
             'flex-vertically': @props.flexVertical
         )
         <div className={classes} style={@props.style}>


show commit number

show commit number

--- public/js/wireroom.coffee
+++ public/js/wireroom.coffee
@@ -120,6 +120,7 @@
   div ->
     div class: "detail-content git", ->
       div class: "commits" ,->
+        div, -> @commits.length + " commits"
         for commit in @commits
           div class: "commit", ->
             div class: "meta clearfix", ->


Inline logger.error calls

Inline logger.error calls

--- src/reporters/apiary-reporter.coffee
+++ src/reporters/apiary-reporter.coffee
@@ -111,15 +111,13 @@
       data = @_transformTestToReporter test
       path = '/apis/' + @configuration['apiSuite'] + '/tests/steps?testRunId=' + @remoteId
       @_performRequest path, 'POST', data, (error, response, parsedBody) ->
-        if error
-          logger.error error
+        logger.error error if error

     emitter.on 'test fail', (test) =>
       data = @_transformTestToReporter test
       path = '/apis/' + @configuration['apiSuite'] + '/tests/steps?testRunId=' + @remoteId
       @_performRequest path, 'POST', data, (error, response, parsedBody) ->
-        if error
-          logger.error error
+        logger.error error if error

     emitter.on 'end', (callback) =>
       data =
@@ -131,8 +129,7 @@
       path = '/apis/' + @configuration['apiSuite'] + '/tests/run/' + @remoteId

       @_performRequest path, 'PATCH', data, (error, response, parsedBody) =>
-        if error
-          logger.error error
+        logger.error error if error
         reportUrl = @reportUrl || "https://app.apiary.io/#{@configuration.apiSuite}/tests/run/#{@remoteId}"
         logger.complete "See results in Apiary at: #{reportUrl}"
         callback()


WORKING towards SemanticTree/FunctionDefinitionStn.caf ALL 747 PASS

--- source/CaffeineScript/SemanticTree/FunctionDefinitionStn.coffee
+++ source/CaffeineScript/SemanticTree/FunctionDefinitionStn.coffee
@@ -62,7 +62,7 @@
     statements = []
     argsDef = if argsDef

-      statements = (statements for arg in argsDef.children when statements = arg.getFunctionPreBodyStatementsJs())
+      statements = (preBodyStatements for arg in argsDef.children when preBodyStatements = arg.getFunctionPreBodyStatementsJs())
       argsDef.toJs()
     else "()"


fixed JS antispam protection

fixed JS antispam protection

--- app/assets/javascripts/contact_messages.js.coffee
+++ app/assets/javascripts/contact_messages.js.coffee
@@ -1,4 +1,4 @@
-$(document).on 'page:change', ->
+$(document).ready ->
   $('.contact .spam_protection').hide()
   $('.contact input[type="submit"]').click ->
     $('.contact input.spam_protection').attr('value', '2')


Remove site-feed redirects

Remove site-feed redirects

--- lib/assets/javascripts/routers/profile.js.coffee
+++ lib/assets/javascripts/routers/profile.js.coffee
@@ -14,9 +14,6 @@
       el: document.getElementById('author-info')

   currentProfile: (params) =>
-    if TentStatus.Helpers.isAppSubdomain()
-      return @navigate('/global', {trigger:true, replace: true})
-
     new Marbles.Views.Profile entity: TentStatus.config.domain_entity.toString()
     @_initMiniProfileView()
     TentStatus.setPageTitle page: @actions_titles.currentProfile
@@ -25,9 +22,6 @@
     TentStatus.initBackgroundMentionsUnreadCount()

   profile: (params) =>
-    if TentStatus.Helpers.isAppSubdomain()
-      return @navigate('/global', {trigger:true, replace: true})
-
     new Marbles.Views.Profile entity: params.entity
     @_initMiniProfileView()


Update content initializers

Update content initializers

--- app/assets/javascripts/nembrot.document.js.coffee
+++ app/assets/javascripts/nembrot.document.js.coffee
@@ -190,12 +190,10 @@

 $ ->
   document_initializers()
-  load_code()

 $(document).on 'pjax:end', '#main', ->
   content_initializers()
   content_initializers_reload_only()
-  load_code()

 $(window).on 'resize', ->
   resize_initializers()
@@ -256,6 +254,7 @@
   track_page_view()
   resize_initializers()
   insert_qr_code()
+  load_code()

   page_class = $('#main > div').attr('class')
   load_share_links(page_class)


Getting ready for change...

Getting ready for change...

--- app/assets/javascripts/roofpig/Settings.js.coffee
+++ app/assets/javascripts/roofpig/Settings.js.coffee
@@ -11,13 +11,12 @@
     @setup  = this._get("setup")
     @pov    = this._get("pov", "Ufr")

-  _get: (name, default_value = "") ->
-    @settings_dom.data(name) || @prefs[name] || default_value
-
   @from_page: (settings_dom) ->
-    if settings_dom.data("pref")
-      prefs = window["ROOFPIG_PREF_" + settings_dom.data("pref")]
+    prefs = window["ROOFPIG_PREF_" + settings_dom.data("pref")]
     new Settings(settings_dom, prefs)

   flag: (name) ->
-    @flags.indexOf(name) > -1+    @flags.indexOf(name) > -1
+
+  _get: (name, default_value = "") ->
+    @settings_dom.data(name) || @prefs[name] || default_value


Added "Word too short!" message

Added "Word too short!" message

--- client/app.coffee
+++ client/app.coffee
@@ -130,6 +130,9 @@

     playedWord = ->
       playMessage 'green', 'Played word!'
+
+    tooShort = ->
+      playMessage 'red', 'Word too short!'

     playMessage = (color, text) ->
       $('#play-message')
@@ -195,16 +198,17 @@
             lastChar = ''
           else if e.keyCode is 13
             word = battle.get('rack').word()
-            if word.length > 2
-              unless Rack.wordValue
-                notAWord()
-              else if battle.wordPlayed word
-                alreadyPlayed()
-              else
-                @emit play:
-                  word: word
-              battle.get('rack').removeAll()
-              $("#played-tiles .tile").appendTo $ '#rack'
+            if word.length < 3
+              tooShort()
+            unless Rack.wordValue
+              notAWord()
+            else if battle.wordPlayed word
+              alreadyPlayed()
+            else
+              @emit play:
+                word: word
+            battle.get('rack').removeAll()
+            $("#played-tiles .tile").appendTo $ '#rack'
             lastChar = ''
           else
             char = String.fromCharCode e.keyCode


payment: fix a type error that crept in during refactoring

We got rid of a KDModalViewWithForms, but there was still a remaining reference to @modalTabs

--- client/app/MainApp/payment/paymentformmodal.coffee
+++ client/app/MainApp/payment/paymentformmodal.coffee
@@ -26,7 +26,7 @@
         @emit 'PaymentMethodSelected', paymentMethodId
       else
         @useExistingView.hide()
-        @modalTabs.show()
+        @paymentForm.show()

     @addSubView @useExistingView


engine: improve plugin.conditions_os

engine: improve plugin.conditions_os

--- packages/engine/src/plugins/conditions_os.coffee
+++ packages/engine/src/plugins/conditions_os.coffee
@@ -9,6 +9,24 @@
     '@nikitajs/engine/src/plugins/conditions'
   ]
   hooks:
+    'nikita:session:normalize':
+      after: '@nikitajs/engine/src/plugins/conditions'
+      handler: (action, handler) ->
+        ->
+          action = handler.call null, action
+          return unless action.conditions
+          # Normalize conditions
+          for config in [action.conditions.if_os, action.conditions.unless_os]
+            continue unless config
+            for condition in config
+              condition.name ?= []
+              condition.name = [condition.name] unless Array.isArray condition.name
+              condition.version ?= []
+              condition.version = [condition.version] unless Array.isArray condition.version
+              condition.version = utils.semver.sanitize condition.version, 'x'
+              condition.arch ?= []
+              condition.arch = [condition.arch] unless Array.isArray condition.arch
+          action
     'nikita:session:action':
       after: '@nikitajs/engine/src/plugins/conditions'
       before: '@nikitajs/engine/src/metadata/disabled'
@@ -25,17 +43,6 @@
 handlers =
   if_os: (action) ->
     final_run = true
-    {conditions} = action
-    conditions.if_os = [conditions.if_os] unless Array.isArray conditions.if_os
-    for condition in conditions.if_os
-      # Normalize conditions
-      condition.name ?= []
-      condition.name = [condition.name] unless Array.isArray condition.name
-      condition.version ?= []
-      condition.version = [condition.version] unless Array.isArray condition.version
-      condition.version = utils.semver.sanitize condition.version, 'x'
-      condition.arch ?= []
-      condition.arch = [condition.arch] unless Array.isArray condition.arch
     await session null, ({run}) ->
       run
         hooks:
@@ -52,7 +59,7 @@
         name = 'redhat' if name.toLowerCase() is 'red hat'
         # Remove patch version (eg centos 7.8)
         version = "#{match[0]}" if match = /^(\d+)\.(\d+)/.exec version
-        match = conditions.if_os.some (condition) ->
+        match = action.conditions.if_os.some (condition) ->
           a = !condition.arch.length || condition.arch.some (value) ->
             return true if typeof value is 'string' and value is arch
             return true if value instanceof RegExp and value.test arch
@@ -68,17 +75,6 @@
     final_run
   unless_os: (action) ->
     final_run = true
-    {conditions} = action
-    conditions.unless_os = [conditions.unless_os] unless Array.isArray conditions.unless_os
-    for condition in conditions.unless_os
-      # Normalize conditions
-      condition.name ?= []
-      condition.name = [condition.name] unless Array.isArray condition.name
-      condition.version ?= []
-      condition.version = [condition.version] unless Array.isArray condition.version
-      condition.version = utils.semver.sanitize condition.version, 'x'
-      condition.arch ?= []
-      condition.arch = [condition.arch] unless Array.isArray condition.arch
     await session null, ({run}) ->
       run
         hooks:
@@ -95,7 +91,7 @@
         name = 'redhat' if name.toLowerCase() is 'red hat'
         # Remove patch version (eg centos 7.8)
         version = "#{match[0]}" if match = /^(\d+)\.(\d+)/.exec version
-        match = conditions.unless_os.some (condition) ->
+        match = action.conditions.unless_os.some (condition) ->
           a = !condition.arch.length || condition.arch.some (value) ->
             return true if typeof value is 'string' and value is arch
             return true if value instanceof RegExp and value.test arch


Fixed merge problem

Fixed merge problem

--- SingularityUI/app/controllers/RequestDetail.coffee
+++ SingularityUI/app/controllers/RequestDetail.coffee
@@ -39,7 +39,11 @@

         @collections.activeTasks = new RequestTasks [],
             requestId: @requestId
-            state: 'active'
+            state:    'active'
+
+        @collections.scheduledTasks = new Tasks [],
+            requestId: @requestId
+            state:     'scheduled'

         @collections.requestHistory  = new RequestHistory         [], {@requestId}
         @collections.taskHistory     = new RequestHistoricalTasks [], {@requestId}


Remove pie draw logging

Remove pie draw logging

--- angular/core/components/pie_chart/pie_chart.coffee
+++ angular/core/components/pie_chart/pie_chart.coffee
@@ -32,7 +32,6 @@
       _.sortBy(_.pairs($scope.votes), ([_, count]) -> - count)

     $scope.$watchCollection 'votes', ->
-      console.time('pieDraw')
       _.each shapes, (shape) -> shape.remove()
       start = 90

@@ -54,4 +53,3 @@
               'stroke-width': 0
               fill: positionColors[position]
             start += angle
-      console.timeEnd('pieDraw')


loading-popup: debounce display

loading-popup: debounce display

--- app/assets/javascripts/admin/spree/progress.coffee
+++ app/assets/javascripts/admin/spree/progress.coffee
@@ -1,7 +1,11 @@
 $(document).ready ->
+  progressTimer = null
   $(document).ajaxStart ->
-    $("#progress").fadeIn()
+    progressTimer = setTimeout ->
+      $("#progress").fadeIn()
+    , 500

   $(document).ajaxStop ->
-    $("#progress").fadeOut()
+    clearTimeout(progressTimer) if progressTimer?
+    $("#progress").stop().hide()


ide: file permissions method changed to fetchPermissions

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -115,7 +115,7 @@
     if file.isDummyFile() or not notifyIfNoPermissions
       return @createEditorAfterFileCheck file, content, callback, emitChange, no

-    file.permissions (err, result) =>
+    file.fetchPermissions (err, result) =>

       return showErrorNotification err  if err


Listen close button clicks to close splits

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -34,6 +34,7 @@
   bindListeners: ->

     @on 'PlusHandleClicked', @bound 'createPlusContextMenu'
+    @on 'CloseHandleClicked', @bound 'closeSplitView'

     @tabView.on 'MachineTerminalRequested', @bound 'openMachineTerminal'
     @tabView.on 'MachineWebPageRequested',  @bound 'openMachineWebPage'
@@ -509,6 +510,12 @@
     contextMenu.once 'ContextMenuItemReceivedClick', -> contextMenu.destroy()

+  closeSplitView: ->
+
+    { frontApp } = kd.singletons.appManager
+    frontApp.mergeSplitView()
+
+
   terminateSessions: (machine)->

     machine.getBaseKite().webtermKillSessions()


d'oh we need to support ghosts in the broker.

--- client/landing/app/MainApp/mq.config.coffee
+++ client/landing/app/MainApp/mq.config.coffee
@@ -8,11 +8,6 @@
       encrypted     : yes
       sockURL       : broker.sockJS
       authEndPoint  : broker.auth
+      vhost         : broker.vhost
     }
-    new Broker broker.apiKey, brokerOptions
-    # switch KD.env
-    #   when 'beta'
-    #     new Broker 'a19c8bf6d2cad6c7a006', brokerOptions
-    #   else
-    #     new Broker 'a6f121a130a44c7f5325', brokerOptions
-  # _addFlashFallback BONGO_MQ, connectionTimeout: 10000
+    new Broker broker.apiKey, brokerOptions


test/teamsHelper: minor updates

test/teamsHelper: minor updates

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -1113,20 +1113,20 @@
   inviteAndJoinWithUsers: (browser, users, callback) ->

     host = @loginTeam browser
-    browser.pause 5000, ->
+    browser.pause 5000, =>

       queue = [
-        (next) ->
-          myteamhelpers.inviteUsers browser, users, ->
+        (next) =>
+          @inviteUsers browser, users, ->
             next null, host
-        (next) ->
-          users.map (user) ->
-            myteamhelpers.acceptAndJoinInvitation host, browser, user, (res) ->
+        (next) =>
+          users.map (user) =>
+            @acceptAndJoinInvitation host, browser, user, (res) ->
               next null, user
       ]

       async.series queue, (err, result) ->
-        browser.end()
+
         callback result

@@ -1339,7 +1339,7 @@
               browser.url invitationUrl, =>
                 @fillJoinForm browser, user, yes, =>
                   browser.waitForElementVisible '.HomeAppView', 20000, yes, =>
-                    @logoutTeam browser, (res) ->
+                    @logoutTeam browser, (res) =>
                       @loginToTeam browser, host, no, ->
                         callback res
         else


Change to 3% rollout

Change to 3% rollout

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -276,7 +276,7 @@
 				# Extract data from user's ObjectId
 				timestamp = parseInt(user_id.toString().substring(0, 8), 16)

-				rolloutPercentage = 5 # Percentage of users to roll out to
+				rolloutPercentage = 3 # Percentage of users to roll out to
 				if !ProjectController._isInPercentageRollout('autocompile', user_id, rolloutPercentage)
 					# Don't show if user is not part of roll out
 					return cb(null, { enabled: false, showOnboarding: false })


Don't use brand prefix setting to mark v1

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -144,7 +144,6 @@
 		timer = new metrics.Timer("project-list")
 		user_id = AuthenticationController.getLoggedInUserId(req)
 		currentUser = AuthenticationController.getSessionUser(req)
-		isV1 = Settings.brandPrefix == "ol-"
 		async.parallel {
 			tags: (cb)->
 				TagsHandler.getAllTags user_id, cb
@@ -180,7 +179,7 @@
 						notifications: notifications or []
 						user: user
 						hasSubscription: results.hasSubscription[0]
-						isV1: isV1
+						isV1: results.v1Projects?
 					}

 					if Settings?.algolia?.app_id? and Settings?.algolia?.read_only_api_key?


webapp/custom software: make some info abould available features human readable

--- src/smc-webapp/project_settings.cjsx
+++ src/smc-webapp/project_settings.cjsx
@@ -616,18 +616,43 @@
     reduxProps :
         "#{name}" :
             configuration         : rtypes.immutable
+            available_features    : rtypes.object

     shouldComponentUpdate: (props) ->
-        return misc.is_different(@props, props, ['project', 'configuration'])
+        return misc.is_different(@props, props, ['project', 'configuration', 'available_features'])
+
+    render_available: ->
+        avail = @props.available_features
+        return null if not avail?
+        feature_map = [['spellcheck', 'Spellchecking'],
+                       ['rmd', 'RMarkdown'],
+                       ['sage', 'SageMath Worksheets'],
+                       ['jupyter_notebook', 'Classical Jupyter Notebook'],
+                       ['jupyter_lab', 'Jupyter Lab'],
+                       ['library', 'Library of documents'],
+                       ['x11', 'Graphical applications'],
+                       ['latex', 'LaTeX editor']]
+        features = []
+        for [key, display] in feature_map
+            if avail[key] then features.push(display)
+        features.sort()
+
+        <React.Fragment>
+
Available features

+

+                {misc.to_human_list(features)}
+

+        </React.Fragment>

     render : ->
         conf = @props.configuration

         <ProjectSettingsPanel
-            title={'Configuration and capabilities'}
+            title={'Features and configuration'}
             icon={'clipboard-check'}
         >
-            {
{JSON.stringify(conf, '', 2)}
 if conf?}
+            {
{JSON.stringify(conf, '', 2)}
 if conf? and DEBUG}
+            {@render_available()}
         </ProjectSettingsPanel>

@@ -1013,9 +1038,10 @@
             compute_images : rtypes.immutable.Map
         "#{name}" :
             configuration         : rtypes.immutable
+            available_features    : rtypes.object

     shouldComponentUpdate: (props) ->
-        return misc.is_different(@props, props, ['project', 'user_map', 'project_map', 'compute_images', 'configuration']) or \
+        return misc.is_different(@props, props, ['project', 'user_map', 'project_map', 'compute_images', 'configuration', 'available_features']) or \
                 (props.customer? and not props.customer.equals(@props.customer))

     render: ->


Fix premature udc call.

Fix premature udc call.

- Move the user call to 'connect' event.

--- community/browser/app/scripts/services/UDC.coffee
+++ community/browser/app/scripts/services/UDC.coffee
@@ -35,7 +35,6 @@
           @data = @reset() unless angular.isObject(@data)
           @data.client_starts = (@data.client_starts || 0) + 1
           @save()
-          Intercom.user(@data.uuid, @data)

         reset: ->
           @data = {
@@ -63,6 +62,7 @@
           if (@shouldPing(event))
             switch event
               when 'connect'
+                Intercom.user(@data.uuid, @data)
                 Intercom.update({
                   "companies": [
                       {


dont explicitly set null

dont explicitly set null

--- components/commercial_filter/models/params.coffee
+++ components/commercial_filter/models/params.coffee
@@ -19,11 +19,8 @@
     size: 50
     page: 1
     for_sale: true
-    color: null
-    medium: null
     major_periods: []
     partner_cities: []
-    include_artworks_by_followed_artists: null
     aggregations: ['TOTAL', 'COLOR', 'MEDIUM', 'MAJOR_PERIOD', 'PARTNER_CITY', 'FOLLOWED_ARTISTS', 'MERCHANDISABLE_ARTISTS']
     ranges:
       price_range:


Implement the vector based version of the compute method.

--- bokehjs/src/coffee/models/transforms/jitter.coffee
+++ bokehjs/src/coffee/models/transforms/jitter.coffee
@@ -14,9 +14,12 @@
     # Apply the transform to a single value
     return(x + ((Math.random() - 0.5) * @get('interval')))

-  v_compute: () ->
+  v_compute: (xs) ->
     # Apply the tranform to a vector of values
-    pass
+    result = new Float64Array(xs.length)
+    for x, idx in xs
+      result[idx] = this.compute(x)
+    return result

 module.exports =
   Model: Jitter


correct spelling on registration flow

correct spelling on registration flow

--- apps/auction/client/email_registration_flow.coffee
+++ apps/auction/client/email_registration_flow.coffee
@@ -21,7 +21,7 @@

     # Open "Thank You" modal
     thankYouView = new ThankYouView
-    modal = modalize ThankYouView, dimensions: width: '400px'
+    modal = modalize thankYouView, dimensions: width: '400px'
     thankYouView.on 'done', -> modal.close()
     modal.open()

@@ -40,5 +40,5 @@
         email: emailAddress
   .catch (err)->
     console.log 'getting caught', err
-    window.location.reload()
+    # window.location.reload()
   .done()


Use madison.yardbase.org, not yardbase.herokuapp.com

--- app/assets/javascripts/api/yardbase.coffee
+++ app/assets/javascripts/api/yardbase.coffee
@@ -1,11 +1,11 @@
-window.Yardbase ||=
-  host: "http://yardbase.herokuapp.com"
+window.Yardbase ||=
+  host: "http://madison.yardbase.org"
   includeScriptTag: (url) ->
     script = document.createElement 'script'
     script.setAttribute 'type', 'text/javascript'
     script.setAttribute 'src', url
     document.body.appendChild script

-window.YardbaseCallback = (result) ->
+window.YardbaseCallback = (result) ->
     console?.log 'Please include a callback in to Yardbase.Query.execute()'
-    console?.log result+    console?.log result


Refactored

Refactored

--- coffee/sample/kinesis.producer.words.coffee
+++ coffee/sample/kinesis.producer.words.coffee
@@ -68,7 +68,6 @@
 		async.whilst truthTest, delay, callback
 		return

-
 wordProducer = new KinesisWordProducer

 argv = optimist.argv
@@ -79,7 +78,7 @@
 	if words.indexOf(',')
 		words = words.split(',')
 	wordProducer.getStreamStatus stream, (err, status) ->
-		initiatePuts = ->
+		initiatePut = ->
 			if period?
 				wordProducer.putWordsInStreamPeriodically stream, words, period, ->
 			else
@@ -93,7 +92,7 @@
 				if err?
 					console.log "KinesisProducer : Error creating stream [#{stream}]", err
 				else
-					do initiatePuts
+					do initiatePut
 				return
 		if status?
 			streamStatus = status.StreamDescription.StreamStatus
@@ -105,10 +104,10 @@
 				process.exit(1)
 			else if streamStatus is "ACTIVE"
 				console.log "KinesisProducer : stream [#{stream}] is active, initiating put"
-				do initiatePuts
+				do initiatePut
 			else
 				console.log "KinesisProducer : Waiting for stream [#{stream}]"
-				wordProducer.waitForStream stream, initiatePuts
+				wordProducer.waitForStream stream, initiatePut
 		return
 else
 	console.error """


using REST to send pushes through gateway

--- server/lib/cordova.coffee
+++ server/lib/cordova.coffee
@@ -82,8 +82,6 @@
 			sendBatchSize: 10

 		if RocketChat.settings.get('Push_enable_gateway') is true
-			pushGetway = undefined
-
 			Push.serverSend = (options) ->
 				options = options or { badge: 0 }
 				query = undefined
@@ -116,12 +114,16 @@
 						console.log('send to token', app.token)

 					if app.token.apn?
-						pushGetway.call 'sendPushNotification', 'apn', app.token.apn, options
+						service = 'apn'
+						token = app.token.apn
+					else if app.token.gcm?
+						service = 'gcm'
+						token = app.token.gcm

-					else if app.token.gcm?
-						pushGetway.call 'sendPushNotification', 'gcm', app.token.gcm, options
-
-			pushGetway = DDP.connect(RocketChat.settings.get('Push_gateway'), {_dontPrintErrors: false})
+					HTTP.post RocketChat.settings.get('Push_gateway') + "/push/#{service}/send",
+						data:
+							token: token
+							options: options

 		Push.enabled = true


Get indexer host in process env

Get indexer host in process env

--- server/lib/indexer.coffee
+++ server/lib/indexer.coffee
@@ -7,4 +7,6 @@
 else
     port = 9102

-module.exports = new Client "http://localhost:#{port}/"+indexerHost = process.env.INDEXER_HOST or 'localhost'
+
+module.exports = new Client "http://#{indexerHost}:#{port}/"


Check proxy before virtual_box

Check proxy before virtual_box

--- actions/virtual_box.coffee
+++ actions/virtual_box.coffee
@@ -6,6 +6,7 @@
 ###
 module.exports = []
 module.exports.push 'histi/actions/curl'
+module.exports.push 'histi/actions/proxy_check'

 module.exports.push (ctx, next) ->
   @name 'VirtualBox # Guest Additions'
@@ -23,20 +24,23 @@
       code_skipped: 1
     , (err, executed, stdout) ->
       return next err if err
-      console.log stdout
       return next null, ctx.PASS if executed and /\d+\.\d+\.\d+/.exec(stdout)[0] is version
       ctx.log "Install latest Guest Additions #{version}"
       source = "http://download.virtualbox.org/virtualbox/#{version}/VBoxGuestAdditions_#{version}.iso"
       destination = "/tmp/VBoxGuestAdditions_#{version}.iso"
       cmd = """
         curl -L #{source} -o #{destination}
+        echo curl
         mount #{destination} -o loop /mnt
+        echo mount
         cd /mnt
         sh VBoxLinuxAdditions.run --nox11
+        echo additions
         rm #{destination}
         /etc/init.d/vboxadd setup
         chkconfig --add vboxadd
         chkconfig vboxadd on
+        echo chkconfig
         """
       ctx.log.out.write cmd
       ctx.execute cmd, (err, executed) ->


clear cache after changes done with ajax calls to make sure correct information is shown always

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -38,7 +38,9 @@
     superagent.post(form.attr 'action').
       type('form-data').
       send(form.serialize()).
-      end()
+      end( ->
+        cached.clear()
+      )

   initDroppableSlot = (elements) ->
     elements.droppable
@@ -83,7 +85,9 @@
       superagent.post(form.attr 'action').
         type('form-data').
         send(form.serialize()).
-        end()
+        end( ->
+          cached.clear()
+        )
       $(@).parents('.member-card').fadeOut 'fast', ->
         $(@).remove()
       return false
@@ -112,6 +116,7 @@
           newSlot.html(response.text).append placeHolder
           initDraggableCard newMemberRow.find('.member-card')
           initDroppableSlot newMemberRow.find('.member-card-empty')
+          cached.clear()
         )
       return false


Button fires the hidden file input

Button fires the hidden file input

--- app/assets/javascripts/mical.js.coffee
+++ app/assets/javascripts/mical.js.coffee
@@ -4,6 +4,7 @@
     'click .js-cancel': 'cancelForm',
     'click .js-delete': 'deleteForm',
     'click .js-year-selection': 'yearDropdown',
+    'click .js-upload-dropzone button': 'openFile',
     'click .js-close': 'closeX'

   constructor: ->
@@ -49,6 +50,9 @@
     @_collapseIfVisible('.js-form')
     $('.header-years-dropdown').slideToggle()

+  openFile: ->
+    $('.js-upload-asset').click()
+
   _collapseIfVisible: (selector) ->
     if $(selector).is(':visible')
       $(selector).slideUp()


Split if

Split if

--- app/backbone/views/react_topbar.coffee
+++ app/backbone/views/react_topbar.coffee
@@ -37,7 +37,10 @@
                _i ['icon-bookmark']
                " On your site"
           ]
-        else if currentSession.user().get('admin') || currentSession.user().get('groups').length
+        else
+          []
+        )...
+        (if currentSession.user().get('admin') || currentSession.user().get('groups').length
           [
             _li ['dropdown-menu-item'],
               _a [href: "/user/#{username}/groups"],


fix var names

fix var names

--- app/coffee/modules/common/userpilot.coffee
+++ app/coffee/modules/common/userpilot.coffee
@@ -40,9 +40,9 @@
         if (@win.userpilot and ((userdata and not @.identified) or force))
             data = JSON.parse(userdata)
             if (data["id"])
-                userPilotData = @.prepareData(data)
+                userpilotData = @.prepareData(data)
                 @win.userpilot.identify(
-                    userPilotData["id"],
+                    userpilotData["id"],
                     userpilotData["extraData"]
                 )


Add grunt coffee options.

Add grunt coffee options.


avoid browserify crash when receiving an empty readable stream

--- plugin/browserify.coffee
+++ plugin/browserify.coffee
@@ -251,7 +251,10 @@

     # Meteor's InputFile provides content as a Buffer or String
     # add the buffer into the stream and end the stream with one call to end()
-    readable.end file.getContentsAsBuffer()
+    buffer = file.getContentsAsBuffer()
+    # Browserify throws an error when we provide an empty readable stream
+    # so, ensure there is some content in there
+    readable.end if buffer?.length > 0 then buffer else '\n'

     return readable


Remove debug line

Remove debug line

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -341,7 +341,6 @@
         buildModal content, callback
       else
         getBalance type, (err, balance)->
-          console.log arguments
           if not err and balance > 0
             charge = (plan.feeMonthly - balance) / 100
             balance = balance / 100


remove disable no_implicit_braces statement in workers/log/models/pagehit

--- workers/log/lib/log/models/pagehit.coffee
+++ workers/log/lib/log/models/pagehit.coffee
@@ -1,5 +1,3 @@
-# due to a bug in coffeelint 1.10.1
-# coffeelint: disable=no_implicit_braces
 ElasticSearch = require './elasticsearch'
 _             = require 'underscore'


StackTemplateListItem: Update labels when StackTemplateChanged event is emitted

--- client/app/lib/stacks/stacktemplatelistitem.coffee
+++ client/app/lib/stacks/stacktemplatelistitem.coffee
@@ -17,26 +17,57 @@
     stackTemplate         = @getData()
     { accessLevel, _id }  = stackTemplate

-    @setAttribute 'testpath', "#{accessLevel}StackListItem"
-
-    @buildLabels()
+    @setTestPath()
+    @buildViews()

     kd.singletons.groupsController.on 'StackTemplateChanged', (params) =>
       if params.contents is _id
-        stackTemplate.isDefault = yes
-        @isDefaultView?.show()
+        stackTemplate.isDefault   = yes
+        stackTemplate.accessLevel = 'group'
       else
-        stackTemplate.isDefault = no
-        @isDefaultView?.hide()
+        stackTemplate.isDefault   = no

       @setData stackTemplate
+      @setTestPath()
+      @updateLabels()
+
+
+  setTestPath: ->
+
+    { accessLevel } = @getData()
+    @setAttribute 'testpath', "#{accessLevel}StackListItem"
+
+
+  buildViews: ->
+
+    { meta, isDefault, inUse, accessLevel, config, title } = @getData()
+
+    @addSubView @info = new kd.CustomHTMLView
+      cssClass  : 'stacktemplate-info clearfix'
+
+    @info.addSubView @title = new kd.CustomHTMLView
+      cssClass  : 'title'
+      partial   : title
+
+    @info.addSubView @labels  = new kd.CustomHTMLView
+      cssClass  : 'labels'
+
+    @info.addSubView @lastUpdatedView = new kd.CustomHTMLView
+      tagName    : 'cite'
+      partial    : "Last updated #{timeago meta.modifiedAt}"
+
+    @addSubView @buttons  = new kd.CustomHTMLView
+      cssClass   : 'buttons'
+
+    @buttons.addSubView @settings
+    @buildLabels()

   buildLabels: ->

     { isDefault, inUse, accessLevel, config } = @getData()

-    @isDefaultView = new kd.CustomHTMLView
+    @labels.addSubView @isDefaultView = new kd.CustomHTMLView
       cssClass   : 'custom-tag'
       partial    : 'DEFAULT'
       attributes :
@@ -44,7 +75,7 @@
       tooltip    :
         title    : 'This group currently using this template'

-    @inUseView   = new kd.CustomHTMLView
+    @labels.addSubView @inUseView     = new kd.CustomHTMLView
       cssClass   : 'custom-tag'
       partial    : 'IN USE'
       attributes :
@@ -52,7 +83,7 @@
       tooltip    :
         title    : 'This template is in use'

-    @notReadyView = new kd.CustomHTMLView
+    @labels.addSubView @notReadyView  = new kd.CustomHTMLView
       cssClass   : 'custom-tag not-ready'
       partial    : 'NOT READY'
       attributes :
@@ -60,7 +91,7 @@
       tooltip    :
         title    : 'Template is not verified or credential data is missing'

-    @accessLevelView = new kd.CustomHTMLView
+    @labels.addSubView @accessLevelView = new kd.CustomHTMLView
       cssClass   : "custom-tag #{accessLevel}"
       partial    : accessLevel.toUpperCase()
       attributes :


:memo: Update deprecation message

:memo: Update deprecation message

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -40,7 +40,7 @@
         The `TextEditor` constructor is no longer public.

         To construct a text editor, use `atom.workspace.buildTextEditor()`.
-        To check if an object is a text editor, look for for the existence of
-        a public method that you're using (e.g. `::getText`).
+        To check if an object is a text editor, use the `isTextEditor`
+        function in the `atom` exports. (e.g. `{isTextEditor} = require('atom'))`
       """
       TextEditor


Added option flag so isDirty is not triggered

--- scripts/gh-book/toc-node.coffee
+++ scripts/gh-book/toc-node.coffee
@@ -14,7 +14,7 @@

     initialize: (options) ->
       super(options)
-      @set 'title', options.title
+      @set 'title', options.title, {parse:true}
       @htmlAttributes = options.htmlAttributes or {}

       @on 'change:title', (model, value, options) =>


Chanage projectStoryPrefixMap to projectPrefixMap

--- scripts/pivotal-listener.coffee
+++ scripts/pivotal-listener.coffee
@@ -26,7 +26,7 @@
       res.send 'OK'
       activityType = reqBody["kind"]
       projectName = reqBody["project"]["name"]
-      projectStoryPrefixMap =
+      projectPrefixMap =
         "District Portal": "DTP-",
         "CCK12": "CCK12-",
         "IRT": "IRT-",
@@ -41,8 +41,7 @@

       project =
         projectId: reqBody["project"]["id"]
-        storyPrefix: projectStoryPrefixMap[projectName]
-
+        projectPrefix: projectPrefixMap[projectName]
       if activityType == 'story_create_activity' && project.projectId
         robot.logger.info "Emitting create story for project: #{projectName}"
         robot.emit 'story_create_activity', project


added another +1

added another +1

--- scripts/seal-of-approval.coffee
+++ scripts/seal-of-approval.coffee
@@ -27,7 +27,8 @@
  "http://i.imgur.com/FSHXA.gif",
  "http://i.imgur.com/bpHdq.gif",
  "http://mlkshk.com/r/CYHO.gif",
- "http://i.imgur.com/uM5NJ.gif"
+ "http://i.imgur.com/uM5NJ.gif",
+ "http://iforce.co.nz/i/0gi03qyg.ydq.gif"
 ]

 module.exports = (robot) ->


#957 - Form settings box doesn't close when user clicks elsewhere

--- jsapp/xlform_model_view/view.utils.coffee
+++ jsapp/xlform_model_view/view.utils.coffee
@@ -30,8 +30,8 @@
       parent_element.find('.error-message').remove()
       current_value = selector.text()

+      edit_box = $('<input />', type:'text', value:current_value, class:'js-cancel-sort js-blur-on-select-row')
       selector.parent().append edit_box
-      edit_box = $('<input />', type:'text', value:current_value, class:'js-cancel-sort js-blur-on-select-row')
       selector.hide()
       edit_box.focus()


ItemList

ItemList

- Don't destroy button tooltip on click. This fixes
  the tooltip when it need to appear the second time.


ComputeProviderKoding: much better naming

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -71,11 +71,11 @@
     if err then return new KodingError err

-  calculateNextLabel = (user, group, label, callback)->
+  guessNextLabel = (user, group, label, callback)->

     return callback null, label  if label?

-    JMachine  = require './machine'
+    JMachine   = require './machine'

     selector   =
       provider : "koding"
@@ -114,7 +114,7 @@
       if err = checkUsage usage, userPlan, storage
         return callback err

-      calculateNextLabel user, group, label, (err, label)->
+      guessNextLabel user, group, label, (err, label)->

         meta =
           type          : "amazon"


Social: fix member participation operation options

--- workers/social/lib/social/models/socialapi/socialaccount.coffee
+++ workers/social/lib/social/models/socialapi/socialaccount.coffee
@@ -86,27 +86,22 @@
             member.createSocialApiId (err, socialApiId)->
               return console.error "couldnt create socialapi id", err  if err

-              # add account into public channel
-              options = [{
-                channelId: socialApiChannelId
-                accountId: socialApiId
-              }]
-              options.channelId = socialApiChannelId
+              options =
+                channelId  : socialApiChannelId
+                accountIds : [ socialApiId ]

               SocialChannel[funcName] client, options, (err, participants) ->
-                return console.error "couldnt add user into group socialapi chan", err, options  if err
+
+                return console.error "couldnt #{funcName} user into group socialapi chan", err, options  if err

                 # only add koding's members to announcement channel
                 return if group.slug isnt "koding"

-                # add account into announcement channel
-                options = [{
-                  channelId: socialApiAnnouncementChannelId
-                  accountId: socialApiId
-                }]
-                options.channelId = socialApiAnnouncementChannelId
+                options =
+                  channelId  : socialApiAnnouncementChannelId
+                  accountIds : [ socialApiId ]

                 SocialChannel[funcName] client, options, (err) ->
-                  return console.error "couldnt add user into group socialapi chan", err, options  if err
+                  return console.error "couldnt #{funcName} user into group socialapi chan", err, options  if err


Removed orphaned functions and improved readability

--- app/client/game/play.coffee
+++ app/client/game/play.coffee
@@ -3,8 +3,7 @@
 # methods

 # From: http://stackoverflow.com/a/9255507/118608
-Template.question.startCountdown = ->
-  console.log 'startCountdown called'
+startCountdown = ->
   # Reset progress bar
   Session.set 'gameProgress', 100
   $('#asset-bar')
@@ -35,14 +34,14 @@
       # Remove button
       @remove()

-      __startQuestion($insertion_point, $countdown)
+      startAnimation($insertion_point, $countdown)

     # Insert button
     $insertion_point.after($button)
   else
     Template.assets.loadSound()

-    __startQuestion($insertion_point, $countdown)
+    startAnimation($insertion_point, $countdown)

   # Skip animation if spacebar is pressed
   $('body').keyup (e) ->
@@ -51,7 +50,7 @@
       Template.question.showQuestion()

-__startQuestion = ($insertion_point, $countdown) ->
+startAnimation = ($insertion_point, $countdown) ->
   # Insert and show countdown
   $insertion_point.after($countdown)
   $countdown.show()
@@ -205,8 +204,7 @@
 # rendered

 Template.question.rendered = ->
-  console.log 'rendered'
-  Template.question.startCountdown()
+  startCountdown()

 # events


fixed typo

fixed typo

--- hubot-scripts/jenkins-script.coffee
+++ hubot-scripts/jenkins-script.coffee
@@ -42,7 +42,7 @@
     if err
       msg.reply "Jenkins error: #{err}"
     else
-      msg.reply "reqest for jenkins-script:#{script} sent to jenkins (status #{res.statusCode} #{_hs[res.statusCode]})"
+      msg.reply "request jenkins-script: #{script} sent to jenkins (status #{res.statusCode} #{_hs[res.statusCode]})"

 module.exports = (robot) ->


https is invalid on created hostname.

https is invalid on created hostname.

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -220,7 +220,7 @@

   showVMDetails: (vm)->
     vmName = vm.hostnameAlias
-    url    = "https://#{vm.hostnameAlias}"
+    url    = "http://#{vm.hostnameAlias}"

     content = """
                 <div class="item">


Fetch region just before calling the run

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -21,9 +21,11 @@
     @fetchVmName options, (err, vmName) =>
       return callback err  if err?
       options.correlationName = vmName
-      @kc.run options, (rest...) ->
-        # log rest...
-        callback rest...
+      @fetchRegion vmName, (region)=>
+        options.kiteName = "os-#{region}"
+        @kc.run options, (rest...) ->
+          # log rest...
+          callback rest...

   _runWrapper:(command, vm, callback)->
     [callback, vm] = [vm, callback]  unless 'string' is typeof vm
@@ -108,6 +110,18 @@
         callback? err, vm, info
       , no

+  fetchRegion: (vmName, callback)->
+    if region = @vmRegions[vmName]
+      return @utils.defer -> callback region
+
+    KD.remote.api.JVM.fetchVmInfo vmName, (err, info)=>
+      if err
+        warn err
+        callback 'aws'
+      else
+        @vmRegions[vmName] = info.region
+        callback @vmRegions[vmName]
+
   fetchVmName: (options, callback) ->
     if options.vmName?
       @utils.defer -> callback null, options.vmName
@@ -220,6 +234,7 @@
     @groupVms = []
     @defaultVmName = null
     @vmDomains = {}
+    @vmRegions = {}

   fetchTotalVMCount:(callback)->
     KD.remote.api.JVM.count (err, count)->


mount feed and entries

mount feed and entries

--- api/index.coffee
+++ api/index.coffee
@@ -12,11 +12,6 @@
 # db
 mongoose.connect MONGOLAB_URI

-#instagram
-ig.use
-  client_id: INSTAGRAM_CLIENT_ID
-  client_secret: INSTAGRAM_CLIENT_SECRET
-
 # Middleware
 app.use cors()
 app.use bodyParser.urlencoded()
@@ -24,6 +19,8 @@

 # Apps
 app.use require './tags'
+app.use require './entries'
+app.use require './feed'

 app.get '/system/up', (req, res) ->
   res.status(200).send { up: true }


Add callback function to update current screnshot URL

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -92,6 +92,7 @@
       this.genUUID()

     showConnectionBoard: (uuid) ->
+      that = this
       code =
         token: uuid
         url: window.location.href
@@ -105,10 +106,11 @@
       connection.set('bc', bc)
       remote = @remote = new Remote code.url
       baseFirebaseUrl = "https://qcommander.firebaseio-demo.com/#{@connection.get('token')}/"
-      #remote.bind 'transitionSlide', (e) ->
-        #info = new Firebase("https://qcommander.firebaseio-demo.com/#{connection.get('token')}/info")
-        #info.child('currentSlideUrl').set e.url
-
+
+      saveCurrentSlide = (data) ->
+        info = new Firebase("https://qcommander.firebaseio-demo.com/#{connection.get('token')}/info")
+        info.child('currentSlideUrl').set data.url
+
       # Push slide info
       slideInfo = new Firebase "#{baseFirebaseUrl}info/"
       slideInfo.set code
@@ -128,15 +130,15 @@

             localStorage['allow'] = message.from
             localStorage['device_priority'] = 1
+            that.closeWelcome()
             #if confirm("Allow connection from #{message.name}?")
               #connection.set('connected_from', message.from)
               #localStorage['allow'] = message.from
+
           when 'next'
-            remote.next()
-            remote.updateCurrentSlide()
+            remote.next(saveCurrentSlide)
           when 'prev', 'previous'
-            remote.previous()
-            remote.updateCurrentSlide()
+            remote.previous(saveCurrentSlide)
           else
             console.log "Not implement"
         #Okay, remove that command
@@ -144,7 +146,6 @@
         r.remove()

       return this.render()
-

     render: ()->
       this.$el
@@ -197,13 +198,15 @@
       url = @driver.getCurrentSlideScreenshot()
       console.log url

-    previous: () ->
+    previous: (cb) ->
       #@driver.jump(@driver.currentSlide() - 1a
       @driver.previous()
-      this.trigger('transitionSLide', {url: this.getCurrentSlide()})
-    next: () ->
+      cb({url: @driver.getCurrentSlideScreenshot()})
+      #this.trigger('transitionSLide', {url: this.getCurrentSlide()})
+    next: (cb) ->
       #@driver.jump(@driver.currentSlide() + 1)
       @driver.next()
+      cb({url: @driver.getCurrentSlideScreenshot()})
     jump: (num) ->
       @driver.jump(num)


remove lodash from sails server code

remove lodash from sails server code

--- api/blueprints/find.coffee
+++ api/blueprints/find.coffee
@@ -1,4 +1,3 @@
-_ = require 'lodash'
 Promise = require 'promise'
 actionUtil = require 'sails/lib/hooks/blueprints/actionUtil'


Tell tinyMCE to not change image paths. #143

--- common/lib/xmodule/xmodule/js/src/html/edit.coffee
+++ common/lib/xmodule/xmodule/js/src/html/edit.coffee
@@ -20,6 +20,8 @@
       theme : "advanced",
       skin: 'studio',
       schema: "html5",
+      # Necessary to preserve relative URLs to our images.
+      convert_urls : false,
       # TODO: we should share this CSS with studio (and LMS)
       content_css : "/static/css/tiny-mce.css",
       formats : {


Basicly fixed payment error modals look

Basicly fixed payment error modals look

--- client/Main/payment/paymentmethodentryform.coffee
+++ client/Main/payment/paymentmethodentryform.coffee
@@ -114,12 +114,20 @@
       @setClass 'animate shake'
       @buttons.Save.hideLoader()

-      modal      = new KDModalView
-        title    : "Form Submission Failed"
-        overlay  : yes
-        partial  : err.first.message # we should overwrite all
+      errMessage     = err.first.message
+      errMessage     = errMessage.replace errMessage[0], errMessage[0].toUpperCase()
+
+      modal          = new KDModalView
+        title        : "Form Submission Failed"
+        overlay      : yes
+        content      : errMessage # we should overwrite all
         # the possible error messages that can be returned by recurly.
-
+        buttons      :
+          ok         :
+            title    : "OK"
+            style    : "modal-clean-green"
+            callback : =>
+              modal.destroy()

     cardNumberInput.on "ValidationError", ->
       @parent.unsetClass "visa mastercard amex diners discover jcb"


pricing: pass `JPaymentPlan` instance to PlanUpgradeConfirmForm#fetchCoupons method

--- client/Main/payment/planupgradeconfirmform.coffee
+++ client/Main/payment/planupgradeconfirmform.coffee
@@ -45,7 +45,7 @@
       @plan.addSubView new VmPlanView {planOptions}, plan

       if plan.discountCode and plan.vmCode
-        @fetchCoupons ["discount", "vm"], @bound "addCouponOptions"
+        @fetchCoupons plan, ["discount", "vm"], @bound "addCouponOptions"

       if oldSubscription
         @plan.addSubView new KDView partial: "
Your old plan was:
"
@@ -106,11 +106,11 @@
         @discount.updatePartial @utils.formatMoney discountInCents / 100
         @subtotal.updatePartial @utils.formatMoney (@getData().plan.feeAmount - discountInCents) / 100

-  fetchCoupons: (codes, callback) ->
+  fetchCoupons: (plan, codes, callback) ->
     {dash} = Bongo
     queue = codes.map (code) =>
       =>
-        @plan.fetchCoupon code, (err, coupon) =>
+        plan.fetchCoupon code, (err, coupon) =>
           return  if KD.showError err
           @coupons[code] = coupon
           queue.fin()


Troubleshoot: Feedback input field is changed with textarea

--- client/Main/troubleshoot/troubleshootmodal.coffee
+++ client/Main/troubleshoot/troubleshootmodal.coffee
@@ -48,9 +48,10 @@
                   # cssClass    : "hidden"
                 feedback      :
                   label       : "Feedback"
-                  itemClass   : KDInputView
                   name        : "feedback"
-                  placeholder : "Define the situation"
+                  placeholder : "Please tell us about the problems you were having and describe the situation"
+                  type        : "textarea"
+                  autogrow    : yes

     super options, data
     @overlay.off "click"


Fixed remove overlay method for introduction tooltip.

--- client/app/CommonViews/introductiontooltip.coffee
+++ client/app/CommonViews/introductiontooltip.coffee
@@ -125,8 +125,7 @@
     @overlay.appendTo "body"
     @overlay.fadeIn 200
     @overlay.bind "click", =>
-      for tooltipInstance in @visibleTooltips
-        tooltipInstance.getOptions().parentView.tooltip?.destroy()
+      tooltipInstance.destroy() for tooltipInstance in @visibleTooltips
       @overlay.remove()
       @visibleTooltips.length = 0


ComputeController: getGroupStack: Returns the stack which generated from Group's default stack template

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -1008,3 +1008,24 @@
         "Your Koding Stack has successfully been initialized. The log here
          describes each executed step of the Stack creation process."
     }
+
+
+  ###*
+   * Returns the stack which generated from Group's default stack template
+  ###
+  getGroupStack: ->
+
+    return null  if isKoding() # we may need this for Koding group as well ~ GG
+    return null  if not @stacks?.length
+
+    { groupsController } = kd.singletons
+    currentGroup         = groupsController.getCurrentGroup()
+    { stackTemplates }   = currentGroup
+
+    return null  if not stackTemplates?.length
+
+    for stackTemplate in stackTemplates
+      for stack in @stacks when stack.baseStackId is stackTemplate
+        return stack
+
+    return null


Log debug info

Log debug info

--- client/app/libs/flux/dispatcher/dispatcher.coffee
+++ client/app/libs/flux/dispatcher/dispatcher.coffee
@@ -90,6 +90,7 @@
         @param {object} payload
     ###
     dispatch: (payload) ->
+        console.log "DIS", payload.action.type, payload.action.value
         message = 'Dispatch.dispatch(...): Cannot dispatch in the middle ' + \
                   'of a dispatch.'
         invariant(


video: viewmodel use nick to fetch and show participant afterwards

--- client/app/lib/videocollaboration/viewmodel.coffee
+++ client/app/lib/videocollaboration/viewmodel.coffee
@@ -19,8 +19,11 @@
   ###
   switchTo: (nick) ->

+    participants = @model.getParticipants()
+    participant = @model.getParticipant nick
+
     hideAll @model.getParticipants()
-    showParticipant nick
+    showParticipant participant

   ###*


fix iron router error: route never dispatch in case Meteor.loggingIn() is true

--- client/entry.coffee
+++ client/entry.coffee
@@ -30,13 +30,12 @@
   signInRequired: (router, extraCondition) ->
     extraCondition ?= true
     unless Meteor.loggingIn()
-      if Meteor.user() and extraCondition
-        router.next()
-      else
+      if not (Meteor.user() and extraCondition)
         Session.set('fromWhere', router.url)
         Router.go('/sign-in')
         Session.set('entryError', t9n('error.signInRequired'))
-        router.next()
+    router.next()
+

 @AccountsEntry = AccountsEntry


better python tokenizer

better python tokenizer

--- client/front.coffee
+++ client/front.coffee
@@ -202,7 +202,8 @@
                         add_str(mode, next_char())
                     else if c = match(spec.QUOTATION_MARK1) or
                       c = match(spec.QUOTATION_MARK2) or
-                      c = match(spec.QUOTATION_MARK3)
+                      c = match(spec.QUOTATION_MARK3) or
+                      c = match(spec.QUOTATION_MARK4)
                         mode = c
                         add_str("string", c)

@@ -220,8 +221,7 @@
                     else
                         add_str("text", next_char())

-                when spec.QUOTATION_MARK1, spec.QUOTATION_MARK2,
-                  spec.QUOTATION_MARK3, spec.QUOTATION_MARK4
+                when spec.QUOTATION_MARK1, spec.QUOTATION_MARK2, spec.QUOTATION_MARK3, spec.QUOTATION_MARK4
                     if c = match(spec.ESCAPECHAR)
                         add_str("string", c)
                         add_str("string", next_char())


index.coffee encoding changed.

index.coffee encoding changed.

--- client/index.coffee
+++ client/index.coffee
@@ -3,7 +3,7 @@

 # Helpers

-Handlebars.registerHelper 'app_name', -> "Mlsuppe"
+Handlebars.registerHelper 'app_name', -> "Målsuppe"

 current_player = -> @Players.findOne Session.get('player_id')
 Handlebars.registerHelper 'current_player', current_player


Reset commit message whether function is called directly or from an event.

--- lib/changes/commit-message-element.coffee
+++ lib/changes/commit-message-element.coffee
@@ -82,6 +82,7 @@
     @messageNode.focus()

   setMessage: (e, text) ->
+    text = e if text is undefined
     @messageModel.setText(text)

   updateCommitButton: ->


gf support multiple file selections

gf support multiple file selections

--- lib/motions/open-file-under-cursor.coffee
+++ lib/motions/open-file-under-cursor.coffee
@@ -16,7 +16,7 @@
       //github.com
     ###
     wordRegex = /[a-z0-9\.\-_\/\\%:]+/i
-    for cursor in @editor.getCursors()
+    @editor.getCursors().forEach (cursor) =>
       range = cursor.getCurrentWordBufferRange wordRegex: wordRegex
       selectedPath = @editor.getTextInRange range


Fix join, leave and topic change messages not being dispatched to the robot

--- src/telegram.coffee
+++ src/telegram.coffee
@@ -157,19 +157,19 @@
         else if message.new_chat_participant
             user = @createUser message.new_chat_participant, message.chat.id
             @robot.logger.info "User " + user.id + " joined chat " + message.chat.id
-            message = new EnterMessage user, null, message.message_id
+            @receive new EnterMessage user, null, message.message_id

         # Exit event
         else if message.left_chat_participant
             user = @createUser message.left_chat_participant, message.chat.id
             @robot.logger.info "User " + user.id + " left chat " + message.chat.id
-            message = new LeaveMessage user, null, message.message_id
+            @receive new LeaveMessage user, null, message.message_id

         # Chat topic event
         else if message.new_chat_title
             user = @createUser message.from, message.chat.id
             @robot.logger.info "User " + user.id + " changed chat " + message.chat.id + " title: " + message.new_chat_title
-            message = new TopicMessage user, message.new_chat_title, message.message_id
+            @receive new TopicMessage user, message.new_chat_title, message.message_id

         # Increment the current offset
         @offset = update.update_id


Fix router forward after relabel.

Fix router forward after relabel.

--- client/views/app/sideNav/directMessagesFlex.coffee
+++ client/views/app/sideNav/directMessagesFlex.coffee
@@ -83,7 +83,7 @@
 					SideNav.closeFlex()
 					SideNav.setFlex null
 					instance.clearForm()
-					FlowRouter.go 'room', { _id: result.rid }
+					FlowRouter.go 'direct', { username: username}
 			else
 				Meteor.call 'createDirectMessage', username, accessPermissions, (err, result) ->
 					if err
@@ -222,7 +222,6 @@
 	# input fields for the room.
 	#
 	instance.autorun (c) ->
-
 		roomId = instance.data.relabelRoom
 		# check if we are relabeling the room
 		if roomId


Model.all is not bound

Model.all is not bound

--- server/controllers/index.coffee
+++ server/controllers/index.coffee
@@ -14,10 +14,10 @@
                 contacts[index] = contact.asNameAndEmails()
             done null, contacts

-        Tag.all
-        Event.all
-        CozyInstance.first
-        WebDavAccount.first
+        (cb) -> Tag.all cb
+        (cb) -> Event.all cb
+        (cb) -> CozyInstance.first cb
+        (cb) -> WebDavAccount.first cb

     ], (err, results) ->


fix wrong data in messages

fix wrong data in messages

--- app/static/scripts/modules/textChat.coffee
+++ app/static/scripts/modules/textChat.coffee
@@ -152,6 +152,8 @@
             @emitter.trigger 'chat.message', message

     newMessage : (event, message) =>
+        # Remove unwanted server data
+        message = message.message if message.message?
         #Append a timestamp to the object
         message.time = do (new Date).getTime
         #Push the object into the watched array


upped https connections to 300 and added debug logging

--- app/coffee/s3Wrapper.coffee
+++ app/coffee/s3Wrapper.coffee
@@ -1,5 +1,7 @@
 http = require('http')
 http.globalAgent.maxSockets = 300
+https = require('https')
+https.globalAgent.maxSockets = 300
 settings = require("settings-sharelatex")
 request = require("request")
 logger = require("logger-sharelatex")
@@ -10,6 +12,14 @@
 _ = require("underscore")

 thirtySeconds = 30 * 1000
+
+
+printSockets = ->
+	console.log require('https').globalAgent.sockets
+	console.log require('http').globalAgent.sockets
+	setTimeout printSockets, thirtySeconds
+
+printSockets()

 buildDefaultOptions = (bucketName, method, key)->
 	return {


Disable query cache tests

Disable query cache tests

--- test/generators/all.coffee
+++ test/generators/all.coffee
@@ -4,7 +4,7 @@
 # beforeEach should return the models_json for the current run
 module.exports = (options, callback) ->
   queue = new Queue(1)
-  queue.defer (callback) -> require('./all_query_cache')(options, callback)
+  # queue.defer (callback) -> require('./all_query_cache')(options, callback)
   queue.defer (callback) -> require('./all_cache')(options, callback)
   queue.defer (callback) -> require('./all_collection')(options, callback)
   queue.defer (callback) -> require('./all_conventions')(options, callback)


Fix updating user attributes

Fix updating user attributes

--- app/assets/javascripts/components/admin_cell_component.js.coffee
+++ app/assets/javascripts/components/admin_cell_component.js.coffee
@@ -6,8 +6,8 @@
   actions:
     setAdmin: (user)->
       user.set('admin', true)
-      user.get('store').commit()
+      user.save()

     removeAdmin: (user)->
       user.set('admin', false)
-      user.get('store').commit()
+      user.save()


yes! new question working

yes! new question working

--- app/assets/javascripts/hanuman/controllers/survey_step.js.coffee
+++ app/assets/javascripts/hanuman/controllers/survey_step.js.coffee
@@ -9,14 +9,18 @@
       @set "isNewQuestion", true
       return
     createQuestion: ->
+      survey_step = @get('model')
       question = @store.createRecord('question',
         question_text: @get('question_text')
         survey_step: @get('model')
         sort_order: 100
       )
       question.set('answer_type', @get('selectedAnswerType'))
-      question.save()
+      question.save().then (question) ->
+        survey_step.get('questions').addObject(question)
+
       # need to add new question to bottom of listing with the right sort order
+
       @set "isNewQuestion", false
       return
     exitCreateQuestion: ->


Fix failing spec

Fix failing spec

--- components/artwork_item/test/save_controls.coffee
+++ components/artwork_item/test/save_controls.coffee
@@ -7,6 +7,7 @@

 model           = new Backbone.Model(id: 'artwork')
 model.isSaved   = sinon.stub()
+model.href      = -> '/foo/bar'

 artworkCollection                 = new Backbone.Collection
 artworkCollection.unsaveArtwork   = sinon.stub()


Refactoring edges

Refactoring edges

--- app/index.coffee
+++ app/index.coffee
@@ -62,26 +62,29 @@
     phraseFrame = phrase.frame()
     $("#bounds").css(phraseFrame)

+    edges = @_allEdges(phrase, hilights)
+    balancedEdges = @_balanceEdges(edges)
+    console.log balancedEdges
+
+  _allEdges: (phrase, hilights) ->
     edges = {}
-
+    phraseFrame = phrase.frame()
     for hilightName, hilight of hilights
       # Find the closest edge of it
       hilight = $(hilight)
       frame = hilight.frame()
       edges[hilightName] = @_edges(phraseFrame, frame)
-
-    # Balance hilights to the edges
+    edges
+
+  _balanceEdges: (edges) ->
     balancedEdges = {left: [], right: [], top: [], bottom: []}
     while !_.isEmpty(edges)
       for edge, items of balancedEdges
-        # Grab the first hilight
         hilightName = @_hilightForEdge(edges, edge)
-        # console.log "hilight for #{edge}: #{hilightName}", edges
         continue unless hilightName
         balancedEdges[edge].push(hilightName)
         delete edges[hilightName]
-
-    console.log balancedEdges
+    balancedEdges

   _hilightForEdge: (hilightsByEdges, edge) ->
     for hilightName, edges of hilightsByEdges


camelize -> classify

camelize -> classify

--- app/index.coffee
+++ app/index.coffee
@@ -61,7 +61,7 @@
     @mkdir 'src'
     @mkdir 'src/assets'

-    @template 'src/_Game'+@ext, 'src/' + @_.camelize(@gameName)+@ext
+    @template 'src/_Game'+@ext, 'src/' + @_.classify(@gameName)+@ext
     @template 'src/_main'+@ext, 'src/main'+@ext
     @template '../common/src/_index.html', 'src/index.html'
     @template '../common/src/_mainWrapper.js', 'src/mainWrapper.js'


Webserver: Remove global KONFIG definition

--- server/lib/server/client.coffee
+++ server/lib/server/client.coffee
@@ -1,6 +1,6 @@
 {argv} = require 'optimist'
-Object.defineProperty global, 'KONFIG',
-  value: require('koding-config-manager').load("main.#{argv.c}")
+
+KONFIG = require('koding-config-manager').load("main.#{argv.c}")
 bongo = require './bongo'

 handleError = (err, callback) ->


Replacing old @_eventListeners prop for @_domEventManager. Removing hardcoded string event names to use 'Events'.

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -650,7 +650,8 @@

 			# As an optimization, we will only use a loader
 			# if something is explicitly listening to the load event
-			if @_eventListeners?.hasOwnProperty "load" or @_eventListeners?.hasOwnProperty "error"
+
+			if @_domEventManager.listeners(Events.ImageLoaded) or @_domEventManager.listeners(Events.ImageLoadError)

 				loader = new Image()
 				loader.name = imageUrl
@@ -658,10 +659,10 @@

 				loader.onload = =>
 					@style["background-image"] = "url('#{imageUrl}')"
-					@emit "load", loader
+					@emit Events.ImageLoaded, loader

 				loader.onerror = =>
-					@emit "error", loader
+					@emit Events.ImageLoadError, loader

 			else
 				@style["background-image"] = "url('#{imageUrl}')"


Show subscribers

Show subscribers

--- client/app/Applications/Groups.kdapplication/Views/productsettingsview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/productsettingsview.coffee
@@ -109,6 +109,13 @@
                      console.log "User is subscribed to the plan."
                  """

+    codeGet    = """
+                 KD.remote.api.JRecurlyPlan.getPlanWithCode '#{code}', (err, plan)->
+                   if not err and plan
+                    plan.getSubscriptions (err, subs)->
+                      console.log "Subscribers:", subs
+                 """
+
     codeWidget = """@content = new KDButtonView
                     cssClass   : "clean-gray test-input"
                     title      : "Subscribed! View Video"
@@ -137,6 +144,7 @@
       hideHandleCloseIcons : yes
       paneData             : [
         { name : "Check Subscription", partial: "
#{codeCheck}
" }
+        { name : "Get Subscribers",    partial: "
#{codeGet}
" }
         { name : "Subscribe Widget",   partial: "
#{codeWidget}
" }
       ]


render as column diagram

render as column diagram

--- frontend/app.coffee
+++ frontend/app.coffee
@@ -34,6 +34,7 @@
                 byCities[entry.city_id][entry.set_id] ?= []
                 byCities[entry.city_id][entry.set_id].push entry.value

+            columns = Object.keys(sets).length
             for setId, set of sets
                 for cityId, city of cities
                     value = byCities[cityId]?[setId]?[0] or 0
@@ -41,8 +42,18 @@

                     city.markers ?= {}
                     if value > 0
+                        y = parseFloat(city.lat)
+                        x = parseFloat(city.lon) + (parseInt(setId) - columns/2 - 1) * .3
+
+                        polygon = [
+                            [y, x]
+                            [y+value/200, x]
+                            [y+value/200, x+.3]
+                            [y, x+.3]
+                        ]
+
                         city.markers[set.id] = L
-                            .circle [city.lat, city.lon], value * 100,
+                            .polygon polygon,
                                 color: color
                                 fillColor: color
                                 fillOpacity: 0.5


Properly introduced `@options` helper

Properly introduced `@options` helper

--- src/js/views/notification.coffee
+++ src/js/views/notification.coffee
@@ -9,14 +9,14 @@
     in_screen_time: 2000
     fade_time: 'slow'

-  constructor: (message) ->
-    @message = message
+  constructor: (options) ->
+    @options = $.extend {}, @default_options, options

   render: ->
     @$el = $("<#{@tagName}/>")
               .addClass(@className)
               .hide()
-              .text(@message)
+              .text(@options.message)

     $(@appendTo).append(@$el)


Commented out some Tutorial.coffee code

Commented out some Tutorial.coffee code

--- client/src/Tutorial.coffee
+++ client/src/Tutorial.coffee
@@ -1,12 +1,24 @@
 class Tutorial
   @window: undefined
-  @windowOptions = undefined
-  @steps = undefined
-  @stepIds = undefined
-  @nextStepIndex = 0
-  @tip = undefined
+  @windowOptions: undefined
+  @steps: undefined
+  @stepIds: undefined
+  @nextStepIndex: 0
+  @tip: undefined
+
+  ###*
+   * Initialises the tutorial system's variables and configuration
+  ###
   @init : ->
+    @window = undefined
+    @windowOptions = undefined
+    @steps = undefined
+    @stepIds = undefined
+    @nextStepIndex = 0
+    @tip = undefined
+
     if $('#tutorial-window').length > 0 then $('#tutorial-window').remove()
+
     @steps = []
     @stepIds = []
     @tip = []
@@ -14,8 +26,11 @@
       backdrop: 'static'
       keyboard: false
       show: false
+
     @initSteps()
+
     @nextStepIndex = 0
+
     html = '

@@ -33,131 +48,26 @@
     $('.tutorial-next-button').click (=> @doStep())
     $(@window).modal(@windowOptions)

-  @isValid: -> @window? and @window.length > 0
-
-  @changeContent: (content) -> if @isValid then $('#tutorial-window-content').html(content)
-
-  @changeHeader: (header) -> if @isValid then $('#tutorial-window-header').html(header)
-
-  @changeWindow: (header, content) -> @changeHeader(header); @changeContent(content)
-
-  @initSteps: ->
-    @addStep
-      header: 'Welcome to the Equations Tutorial!'
-      content: 'Why hello there young chap. Welcome to Equations! In no time, you\'ll be up and running and ready to play'
-      modal: true
-
-    @addStep
-      header: "The Lobby"
-      content: "We're now in the Lobby. Here, we get to see what games are available to join, or we could create our own game."
-
-    @addStep
-      header: "Join a game"
-      content: "Let's get started! So there's a game that is available to join right now."
-      modal: true
-
-    @addStep
-      modal: false
-      tipselector: 'tr[data-gamenumber="0"] td:first-child'
-      tipheading: 'Join this game'
-      tipmessage: 'Click here to join the game!'
-
-    @addStep
-      id: "goal"
-      modal: true
-      header: "Goal setting"
-      content: "Great, we're in a game! You've been chosen to be the goal setter. Let's make the goal '1+2'."
-
-    @doStepWhenScreen GoalScreen, "goal"
-
-    @addStep
-      modal: false
-      tipselector: 'li.dice[data-index="0"]'
-      tipmessage: 'Let\'s make the goal "1+2". Click this dice to add it to the goal.'
-
-    @addStep
-      modal: false
-      tipselector: 'li.dice[data-index="4"]'
-      tipmessage: 'Cool, we can see that "1" has been added below. Now let\'s add "+".'
-
-    @addStep
-      modal: false
-      tipselector: 'li.dice[data-index="19"]'
-      tipmessage: 'Finally, add the number "2" to the goal.'
-
-    @addStep
-      modal: false
-      tipselector: '#sendgoal'
-      tipmessage: 'Great, now click here to submit the goal and start the game.'
-
-    @addStep
-      id: "game"
-      modal: true
-      header: "We're in!"
-      content: "We've finally started playing the game. Let me now show you around."
-    @doStepWhenScreen GameScreen, "game"
-
-    @addStep
-      modal: false
-      tipselector: '#goal-ctnr'
-      tipmessage: 'This is the goal.'
-      tipnext: true
-
-    @addStep
-      tipselector: '#dice_container'
-      tipmessage: 'These are the dice. We must use these to either prove that the goal is possible to achieve using these dice or impossible.'
-      tipnext: true
-
-    @addStep
-      tipselector: '#player-list'
-      tipmessage: 'This shows you the players who are in the game. The player highlighted in orange is the player whose turn it currently is.'
-      tipnext: true
-
-    @addStep
-      tipselector: '#timer-knob-ctnr'
-      tipmessage: 'This is the turn timer. A player must make a move on the dice within this time if it is their turn.'
-      tipnext: true
-
-    @addStep
-      tipselector: '#rough-area-board'
-      tipmessage: 'This is your personal rough area. Feel free to take notes and do rough working here.'
-      tipnext: true
-
-    @addStep
-      modal: true
-      header: "It's your turn"
-      content: "We must now make a move. Try clicking on one of the dice to move it to another area of the mat."
-
-    @addStep
-      modal: false
-
-    @addStep
-      modal: true
-      header: "Forbidden, required and optional"
-      content: "As you can see, in a move, you can move a die to one of three areas of the board:
-

-
Forbidden: The forbidden section includes all dice that can not be used when forming a solution to the goal.

-
Optional: You can use any number of optional dice to form the goal.

-
Required: You must use ALL of these dice when forming the goal.

-      The idea is that when someone makes a 'challenge', you must use a combination of NONE of the forbidden dice, ANY of the optional dice and ALL of the required dice to form your solution to the goal.
-

-      "
-
-    @addStep
-      modal: false
-
-
-
-
-
-  @show: -> $(@window).modal('show')
-  @hide: -> $(@window).modal('hide')
-  @toggle: -> $(@window).modal('toggle')
-
+
+  ###*
+   * Adds a step to the tutorial system (in)
+   * @param {String} {id        An identification to give to that particular step
+   * @param {String} header      The header of the modal window if one exists
+   * @param {String} content     The content of the modal window if one exists
+   * @param {CSS Selector} tipselector The css selector to add a tip to
+   * @param {String} tipmessage  The html to add to the tip if one exists
+   * @param {String} tipheading  The html to add to the heading of the tip if one exists
+   * @param {Boolean} modal       Do we want the window to be modal?
+   * @param {Boolean} tipnext}   Do we want a next button inside the tip?
+  ###
   @addStep: ({id, header, content, tipselector, tipmessage, tipheading, modal, tipnext}) ->
     index = @steps.push {id: id, header: header, content: content, tipselector: tipselector, tipmessage: tipmessage, tipheading: tipheading, modal: modal, tipnext: tipnext}
     if id? then @stepIds[id] = index - 1

+  ###*
+   * Executes the next step in sequence OR executes the step with the given ID
+   * @param  {String} stepId If given, the step with this ID will be executed. Otherwise, the next step will be executed.
+  ###
   @doStep: (stepId) ->
     nextIndex = if stepId? then @stepIds[stepId] else @nextStepIndex
     theStep = @steps[nextIndex]
@@ -165,23 +75,7 @@
       {id, header, content, tipselector, tipmessage, tipheading, tipnext, modal} = theStep
       if header? then @changeHeader(header)
       if content? then @changeContent(content)
-      if tipselector?
-        theHtml = tipmessage+(if tipnext then 'Next →' else '')
-        $(tipselector).qtip
-          content: theHtml
-          position:
-            my: 'top center'
-            at: 'bottom center'
-            viewport: $(window)
-          show:
-            event: false
-            ready: true
-          hide: false
-          events:
-            render: (event, api) ->
-              if tipnext then $('.tutorial-next-button').bind 'click', ->
-                api.hide()
-                Tutorial.doStep()
+      if tipselector? then @displayTip(tipselector, tipmessage, tipheading, tipnext)
       if modal?
         if modal then @show() else @hide()
       @steps[nextIndex] = undefined
@@ -189,7 +83,14 @@
         $('.tutorial-next-button').unbind "click"
         $('.tutorial-next-button').click (=> @doStep())
       @nextStepIndex++ until @steps[@nextStepIndex]? or @nextStepIndex is @steps.length
-
+  ###*
+   * Executes a step with given step id only when the current screen
+   * is an instance of the given class name. This is done using a timer
+   * to periodically check if the screen has changed.
+   *
+   * @param  {function} className The actual class name (note: not a string)
+   * @param  {String} stepId    The step id to execute
+  ###
   @doStepWhenScreen: (className, stepId) ->
     if typeof className is "function"
       thisReference = this
@@ -204,3 +105,185 @@
       checker()

+  ###*
+   * Shows the tutorial modal box (bootstrap) window
+  ###
+  @show: -> $(@window).modal('show')
+
+  ###*
+   * Hides the tutorial modal box (bootstrap) window
+  ###
+  @hide: -> $(@window).modal('hide')
+
+  ###*
+   * Toggles the tutorial modal box (bootstrap) window
+  ###
+  @toggle: -> $(@window).modal('toggle')
+
+
+  ###*
+   * Displays a qTip2 tip on the given selector.
+   * @param  {String} tipselector The css selector to add a bubble tip to.
+   * @param  {String} tipmessage  The message the tip will have
+   * @param  {String} tipheading  The html heading the tip will have
+   * @param  {Boolean} tipnext    Whether or not we want a next button in the tip.
+  ###
+  @displayTip: (tipselector, tipmessage, tipheading, tipnext) ->
+    nextHtml = if tipnext then 'Next →' else ''
+    theHtml = tipmessage+nextHtml
+    $(tipselector).qtip
+      content: theHtml
+      position:
+        my: 'top center'
+        at: 'bottom center'
+        viewport: $(window)
+      show:
+        event: false
+        ready: true
+      hide: false
+      events:
+        render: (event, api) ->
+          if tipnext then $('.tutorial-next-button').bind 'click', ->
+            api.hide()
+            Tutorial.doStep()
+
+  ###*
+   * Returns whether or not the window exists
+   * @return {Boolean} True if the window exists.
+  ###
+  @isValid: -> @window? and @window.length > 0
+
+  ###*
+   * Changes the content part of the window
+   * @param  {String} content The html that would go into the content
+  ###
+  @changeContent: (content) -> if @isValid then $('#tutorial-window-content').html(content)
+
+  ###*
+   * Changes the header part of the window
+   * @param  {String} header The html that would go into the header
+  ###
+  @changeHeader: (header) -> if @isValid then $('#tutorial-window-header').html(header)
+
+  ###*
+   * Changes the content and header parts of the window
+   * @param  {String} content The html that would go into the content
+   * @param {String} header The html that would go into the header
+  ###
+  @changeWindow: (header, content) -> @changeHeader(header); @changeContent(content)
+
+
+
+
+#####################################################################################
+#                         The actual steps of the tutorial.                         #
+#####################################################################################
+
+  ###*
+   * Adds all the steps of the tutorial system. These steps are added *in order*
+  ###
+  @initSteps: ->
+    @addStep
+      header: 'Welcome to the Equations Tutorial!'
+      content: 'Why hello there young chap. Welcome to Equations! In no time, you\'ll be up and running and ready to play'
+      modal: true
+
+    @addStep
+      header: "The Lobby"
+      content: "We're now in the Lobby. Here, we get to see what games are available to join, or we could create our own game."
+
+    @addStep
+      header: "Join a game"
+      content: "Let's get started! So there's a game that is available to join right now."
+      modal: true
+
+    @addStep
+      modal: false
+      tipselector: 'tr[data-gamenumber="0"] td:first-child'
+      tipheading: 'Join this game'
+      tipmessage: 'Click here to join the game!'
+
+    @addStep
+      id: "goal"
+      modal: true
+      header: "Goal setting"
+      content: "Great, we're in a game! You've been chosen to be the goal setter. Let's make the goal '1+2'."
+
+    @doStepWhenScreen GoalScreen, "goal"
+
+    @addStep
+      modal: false
+      tipselector: 'li.dice[data-index="0"]'
+      tipmessage: 'Let\'s make the goal "1+2". Click this dice to add it to the goal.'
+
+    @addStep
+      modal: false
+      tipselector: 'li.dice[data-index="4"]'
+      tipmessage: 'Cool, we can see that "1" has been added below. Now let\'s add "+".'
+
+    @addStep
+      modal: false
+      tipselector: 'li.dice[data-index="19"]'
+      tipmessage: 'Finally, add the number "2" to the goal.'
+
+    @addStep
+      modal: false
+      tipselector: '#sendgoal'
+      tipmessage: 'Great, now click here to submit the goal and start the game.'
+
+    @addStep
+      id: "game"
+      modal: true
+      header: "We're in!"
+      content: "We've finally started playing the game. Let me now show you around."
+    @doStepWhenScreen GameScreen, "game"
+
+    @addStep
+      modal: false
+      tipselector: '#goal-ctnr'
+      tipmessage: 'This is the goal.'
+      tipnext: true
+
+    @addStep
+      tipselector: '#dice_container'
+      tipmessage: 'These are the dice. We must use these to either prove that the goal is possible to achieve using these dice or impossible.'
+      tipnext: true
+
+    @addStep
+      tipselector: '#player-list'
+      tipmessage: 'This shows you the players who are in the game. The player highlighted in orange is the player whose turn it currently is.'
+      tipnext: true
+
+    @addStep
+      tipselector: '#timer-knob-ctnr'
+      tipmessage: 'This is the turn timer. A player must make a move on the dice within this time if it is their turn.'
+      tipnext: true
+
+    @addStep
+      tipselector: '#rough-area-board'
+      tipmessage: 'This is your personal rough area. Feel free to take notes and do rough working here.'
+      tipnext: true
+
+    @addStep
+      modal: true
+      header: "It's your turn"
+      content: "We must now make a move. Try clicking on one of the dice to move it to another area of the mat."
+
+    @addStep
+      modal: false
+
+    @addStep
+      modal: true
+      header: "Forbidden, required and optional"
+      content: "As you can see, in a move, you can move a die to one of three areas of the board:
+

+
Forbidden: The forbidden section includes all dice that can not be used when forming a solution to the goal.

+
Optional: You can use any number of optional dice to form the goal.

+
Required: You must use ALL of these dice when forming the goal.

+      The idea is that when someone makes a 'challenge', you must use a combination of NONE of the forbidden dice, ANY of the optional dice and ALL of the required dice to form your solution to the goal.
+

+      "
+
+    @addStep
+      modal: false
+


Converted the remaining rspec tests to jasmine.

--- spec/hash_set_spec.coffee
+++ spec/hash_set_spec.coffee
@@ -13,6 +13,8 @@

   toString: -> "FunnyKey(#{@value})"

+FunnyKey.sorter = (a, b) -> a.value - b.value
+

 describe "A Hash", ->

@@ -21,6 +23,9 @@

     it "should not be empty", ->
       expect(hash.isEmpty).toBe false
+
+    it "should not be the same object as another one constructed like it", ->
+      expect(hash).not.toBe(new HashSet().plus('A').plus('B').minus('A'))

   describe "when empty", ->
@@ -201,3 +206,69 @@

     it "should return false when fed another key", ->
       expect(hash.get("third")).toBe false
+
+    it "should contain all the keys when converted to an array", ->
+      expect(hash.toArray().sort(FunnyKey.sorter)).toEqual(keys)
+
+    describe "some of which are then removed", ->
+      ex_keys = keys[0..100]
+      h = hash.without ex_keys...
+
+      it "should have the correct size", ->
+        expect(h.size).toEqual keys.length - ex_keys.length
+
+      it "should not be the same as the original hash", ->
+        expect(h).not.toEqual hash
+
+      it "should not be empty", ->
+        expect(h.isEmpty).toBe false
+
+      it "should return true for the remaining keys", ->
+        expect(h.get(key)).toBe true for key in keys when not key in ex_keys
+
+      it "should return false for the removed keys", ->
+        expect(h.get(key)).toBe false for key in ex_keys
+
+      it "should have exactly the remaining elements when made an array", ->
+        expect(h.toArray().sort(FunnyKey.sorter)).toEqual(keys[101..])
+
+    describe "from which some keys not included are removed", ->
+      ex_keys = new FunnyKey(x) for x in [1000..1100]
+      h = hash.without ex_keys...
+
+      it "should be the same object as before", ->
+        expect(h).toBe hash
+
+      it "should have the correct size", ->
+        expect(h.size).toEqual hash.size
+
+      it "should not be empty", ->
+        expect(h.isEmpty).toBe false
+
+    describe "all of which are then removed", ->
+      h = hash.without keys...
+
+      it "should have size 0", ->
+        expect(h.size).toEqual 0
+
+      it "should be empty", ->
+        expect(h.isEmpty).toBe true
+
+      it "should return false for the removed keys", ->
+        expect(h.get(key)).toBe false for key in keys
+
+      it "should convert to an empty array", ->
+        expect(h.toArray().length).toBe 0
+
+    describe "some of which are then inserted again", ->
+      ex_keys = keys[0..100]
+      h = hash.with ex_keys...
+
+      it "should be the same object as before", ->
+         expect(h).toBe hash
+
+      it "should have the same size as before", ->
+        expect(h.size).toEqual hash.size
+
+      it "should not be empty", ->
+        expect(h.isEmpty).toBe false


mo' stubs

mo' stubs

--- apps/auction_support/test/templates.coffee
+++ apps/auction_support/test/templates.coffee
@@ -35,7 +35,8 @@
         JS_EXT: '.js.gz'
         NODE_ENV: 'test'
       @order = new Order()
-      @saleArtwork = new SaleArtwork(fabricate 'sale_artwork')
+      @saleArtwork = new SaleArtwork fabricate 'sale_artwork',
+        highest_bid: amount_cents: 100
       @sale = new Sale(fabricate 'sale')
       done()

@@ -65,6 +66,7 @@
         maxBid: 1234
         monthRange: @order.getMonthRange()
         yearRange: @order.getYearRange()
+        accounting: formatMoney: ->
         asset: ->
       @$template = $(template)
       @$template.html().should.not.containEql 'undefined'
@@ -82,6 +84,7 @@
         maxBid: 1234
         monthRange: @order.getMonthRange()
         yearRange: @order.getYearRange()
+        accounting: formatMoney: ->
         asset: ->
       @$template = $(template)
       @$template.html().should.not.containEql 'undefined'


using redis for session storing

using redis for session storing

--- src/server/app.coffee
+++ src/server/app.coffee
@@ -1,5 +1,6 @@
 express = require('express')
 passport = require('passport')
+RedisStore = require('connect-redis')(express)
 conf = require('./conf')('app')

 app = express()
@@ -9,7 +10,7 @@
     app.use(express.cookieParser())
     app.use(express.bodyParser())
     app.use(express.logger())
-    app.use(express.session({ secret: conf.sessionSecret}));
+    app.use(express.session({ store: new RedisStore({db: 2}), secret: conf.sessionSecret}));
     app.use(passport.initialize());
     app.use(passport.session());
     app.use(app.router)


Add full/partial membership to TSV output

--- client/stats.coffee
+++ client/stats.coffee
@@ -235,18 +235,20 @@
       datasets: [
         fullname: 'ALL MESSAGES'
         msgFilter: (msg) -> true
-      ].concat Meteor.users.find({}, sort: username: 1).map (user) ->
+      ].concat Meteor.users.find({}, sort: username: 1).map (user) =>
         username: user.username
         fullname: user.profile?.fullname
+        member: if fullMemberOfGroup @group, user then 'full' else 'partial'
         email: user.emails?[0]?.address
         msgFilter: msgFilter user.username
     buildStats stats, t.data
-    rows = [['Username', 'Fullname', 'Email', 'Total'].concat stats.labels]
+    rows = [['Username', 'Fullname', 'Email', 'Membership', 'Total'].concat stats.labels]
     for dataset in stats.datasets
       rows.push [
         dataset.username ? ''
         dataset.fullname ? ''
         dataset.email ? ''
+        dataset.member ? ''
         dataset.data.reduce ((x, y) -> x+y), 0
       ].concat dataset.data
     tsv =


change the mock a bit

change the mock a bit

--- client/utils.coffee
+++ client/utils.coffee
@@ -17,11 +17,14 @@

Create Slide Show

-
Add slides

-
Slide content is simple HTML.

-
<div class="slide">
-    <h1>Hello World!</h1>
-  </div>

+

+
Add slides

+
Slide content is simple HTML.

+

+

+
Click save

+
The Slide Show is saved and public URL becomes available.

+

@@ -30,12 +33,17 @@

-
Powered by

-
-
-
-
-
also Socket.IO, Express, Backbone.js and many more

+
Learn more

+

+

+
Flips.io presentation

+
Deck.js presentation

+

+

+
+

+
Thanks and don't forget to vote! :)

+

"""

 utils.getSocket = -> io.connect window.location.origin


Uncomment play/game-dev-level url to share game dev projects (#56)


Input coffee class return function

Input coffee class return function

--- app/js/canvas/Input.coffee
+++ app/js/canvas/Input.coffee
@@ -42,58 +42,60 @@

   getInput = (doc, canvas) ->
-    keyboard: (callback) ->
-      # Callback returns 2 paramaters:
-      # -- Pressed keycode
-      # -- True if button is down / False if button is up
+    return {
+      keyboard: (callback) ->
+        # Callback returns 2 paramaters:
+        # -- Pressed keycode
+        # -- True if button is down / False if button is up

-      doc.onkeydown = (event) ->
-        _keyboardInput event, callback, true
+        doc.onkeydown = (event) ->
+          _keyboardInput event, callback, true
+          return
+
+        doc.onkeyup = (event) ->
+          _keyboardInput event, callback, false
+          return
+
         return

-      doc.onkeyup = (event) ->
-        _keyboardInput event, callback, false
+      orientationChange: (callback) ->
+        # Callback returns if orientation of screen is changed
+        _orientationChange callback
         return

-      return
+      mobile: (callback) ->
+        touchendCoords = {}
+        # Callback returns when screen is touched and when screen touch ends
+        canvas.addEventListener 'touchstart', ((event) ->
+          event.preventDefault()
+          _mobileInput event, ((coords, pressed) ->
+            touchendCoords = coords
+            callback coords, pressed
+            return
+          ), true
+          return
+        ), false
+        canvas.addEventListener 'touchend', (event) ->
+          event.preventDefault()
+          callback touchendCoords, false
+          return
+        return

-    orientationChange: (callback) ->
-      # Callback returns if orientation of screen is changed
-      _orientationChange callback
-      return
+      mouse_action: (callback) ->
+        # Callback returns on mouse down
+        canvas.addEventListener 'mousedown', ((event) ->
+          event.preventDefault()
+          _mouseInput event, callback
+          return
+        ), false
+        return

-    mobile: (callback) ->
-      touchendCoords = {}
-      # Callback returns when screen is touched and when screen touch ends
-      canvas.addEventListener 'touchstart', ((event) ->
-        event.preventDefault()
-        _mobileInput event, ((coords, pressed) ->
-          touchendCoords = coords
-          callback coords, pressed
+      mouse_move: (callback) ->
+        # Callback returns when mouse is moved
+        canvas.addEventListener 'mousemove', ((event) ->
+          event.preventDefault()
+          _mouseInput event, callback
           return
-        ), true
+        ), false
         return
-      ), false
-      canvas.addEventListener 'touchend', (event) ->
-        event.preventDefault()
-        callback touchendCoords, false
-        return
-      return
-
-    mouse_action: (callback) ->
-      # Callback returns on mouse down
-      canvas.addEventListener 'mousedown', ((event) ->
-        event.preventDefault()
-        _mouseInput event, callback
-        return
-      ), false
-      return
-
-    mouse_move: (callback) ->
-      # Callback returns when mouse is moved
-      canvas.addEventListener 'mousemove', ((event) ->
-        event.preventDefault()
-        _mouseInput event, callback
-        return
-      ), false
-      return
+    }


Don't reposition show buttons after they have once been shown

--- app/js/views/buttons.coffee
+++ app/js/views/buttons.coffee
@@ -31,6 +31,11 @@
     </div>
   """

+  setCoordinates: (top, left) =>
+    return if @position_already_determined
+    @position_already_determined = true
+    super
+
 class Factlink.CreateButton extends Button
   template: """
     <div class="fl-button fl-create-button">


Ensure the comment is in view after toggling expansion state

--- app/js/views/comment.coffee
+++ app/js/views/comment.coffee
@@ -35,7 +35,7 @@
   # Toggle the expanded or collapsed state of the comment
   toggle: (e) ->
     e.preventDefault()
-    @$el.toggleClass('collapsed expanded')
+    @$el.toggleClass('collapsed expanded').scrollIntoView(20)

   # Select this comment
   select: ->