Fix saveAs dialog file tree issue

Fix saveAs dialog file tree issue

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -38,6 +38,8 @@
     @noVMFoundWidget = new VMMountStateWidget
     @cleanup()

+    @appStorage = KD.getSingleton('appStorageController').storage 'Finder', '1.0'
+
   watchers: {}

   registerWatcher:(path, stopWatching)->
@@ -66,8 +68,6 @@

   reset:->
     if @getOptions().useStorage
-      @appStorage = KD.getSingleton('mainController').\
-                      getAppStorageSingleton 'Finder', '1.0'
       @appStorage.once "storageFetched", @bound 'loadVms'
     else
       @loadVms()


GroupProductsController: remove dash from Bongo

--- client/dashboard/lib/controllers/groupproductscontroller.coffee
+++ client/dashboard/lib/controllers/groupproductscontroller.coffee
@@ -12,8 +12,6 @@

 module.exports = class GroupProductsController extends KDController
-
-  { dash } = Bongo

   constructor: (options = {}, data) ->
     super options, data


Allow single line breaks

Allow single line breaks

--- resources/assets/scripts/app/app.coffee
+++ resources/assets/scripts/app/app.coffee
@@ -60,6 +60,7 @@
             sanitize: true
             smartLists: true
             smartypants: true
+            breaks: true

         markedProvider.setRenderer


add excellent, so good, so fucking good to +1 phrases

--- scripts/seal-of-approval.coffee
+++ scripts/seal-of-approval.coffee
@@ -38,7 +38,7 @@
   robot.hear /seal of approval/i, (msg) ->
     msg.send "http://www.sharingmachine.com/prodimages/seal-litebox.jpg"

-  robot.hear /((good|great|nice) (work|job)|well done|fine business)/i, (msg) ->
+  robot.hear /((good|great|nice) (work|job)|well done|fine business|excellent|so (fucking )?good)/i, (msg) ->
     msg.send msg.random images


changed fetchTeaser after chris' suggestions: ALMOST working

--- server/app/bongo/models/messages/discussion.coffee
+++ server/app/bongo/models/messages/discussion.coffee
@@ -305,7 +305,7 @@
         limit         : 5
         sort          :
           timestamp   : 1
-      .nodes()
+      # .nodes()
       .edges
         query         :
           sourceName  : 'JOpinion'
@@ -318,7 +318,7 @@
         limit         : 3
         sort          :
           timestamp   : 1
-      .nodes()
+      # .nodes()
       .and()
       .edges
         query         :


software: adding stable-2019-01-12 and tweking descriptions a littel bit

--- src/smc-util/compute-images.coffee
+++ src/smc-util/compute-images.coffee
@@ -6,8 +6,9 @@

 exports.COMPUTE_IMAGES = COMPUTE_IMAGES =
     default: {title: "Default", descr: "Regularly updated, well tested."}
-    previous: {title: "Previous", descr: "Slightly behind 'default', updated less frequently"}
-    "stable-2018-08-27" : {title: "2018-08-27", descr: "Fixed at 2018-08-27 and no longer updated"}
+    previous: {title: "Previous", descr: "One or two weeks behind 'default'"}
+    "stable-2018-08-27" : {title: "2018-08-27", descr: "Frozen at 2018-08-27 and no longer updated"}
+    "stable-2019-01-12" : {title: "2019-01-12", descr: "Frozen at 2019-01-12 and no longer updated"}
     exp: {title: "Experimental", descr: "Cutting-edge software updates (could be broken)"}
     old: {title: "Old image", descr: "In use until Summer 2018. No longer maintained!"}


redux: rewrite archive editor

redux: rewrite archive editor

--- src/smc-webapp/editor_archive.cjsx
+++ src/smc-webapp/editor_archive.cjsx
@@ -1,4 +1,4 @@
-{React, ReactDOM, rclass, rtypes, Flux, Actions, Store}  = require('./r')
+{React, ReactDOM, rclass, rtypes, Redux, Actions, Store}  = require('./smc-react')
 {Button, Panel, Row, Col} = require('react-bootstrap')
 {Icon} = require('./r_misc')
 {salvus_client} = require('./salvus_client')
@@ -49,13 +49,10 @@
             command : 'xz'
             args    : ['-vfd']

-flux_name = (project_id, path) ->
+redux_name = (project_id, path) ->
     return "editor-#{project_id}-#{path}"

 class ArchiveActions extends Actions
-    _set_to: (payload) =>
-        payload
-
     parse_file_type : (file_info) ->
         if file_info.indexOf('Zip archive data') != -1
             return 'zip'
@@ -107,7 +104,11 @@

         ], (err, info, type, contents) =>
             if not err
-                @_set_to(error : err, info : info.stdout, contents : contents.stdout, type : type)
+                @setState
+                    error    : err
+                    info     : info.stdout
+                    contents : contents.stdout
+                    type     : type
         )

     extract_archive_files : (project_id, path, type, contents) ->
@@ -144,7 +145,7 @@
                 args = args.concat(extra_args).concat([path_parts.tail]).concat(post_args)
                 args_str = ((if x.indexOf(' ')!=-1 then "'#{x}'" else x) for x in args).join(' ')
                 cmd = "cd #{path_parts.head} ; #{command} #{args_str}"
-                @_set_to(loading : true, command : cmd)
+                @setState(loading: true, command: cmd)
                 salvus_client.exec
                     project_id : project_id
                     path       : path_parts.head
@@ -153,25 +154,18 @@
                     err_on_exit: false
                     timeout    : 120
                     cb         : (err, out) =>
-                        @_set_to(loading : false)
+                        @setState(loading: false)
                         cb(err, out)
         ], (err, output) =>
-            @_set_to(error : err, extract_output : output.stdout)
+            @setState(error: err, extract_output: output.stdout)
         )

-class ArchiveStore extends Store
-    _init: (flux) =>
-        ActionIds = flux.getActionIds(@name)
-        @register(ActionIds._set_to, @setState)
-        @state = {}
-
-exports.init_flux = init_flux = (flux, project_id, filename) ->
-    name = flux_name(project_id, filename)
-    if flux.getActions(name)?
+exports.init_redux = init_redux = (redux, project_id, filename) ->
+    name = redux_name(project_id, filename)
+    if redux.getActions(name)?
         return  # already initialized
-    actions = flux.createActions(name, ArchiveActions)
-    store   = flux.createStore(name, ArchiveStore)
-    store._init(flux)
+    actions = redux.createActions(name, ArchiveActions)
+    store   = redux.createStore(name)

 ArchiveContents = rclass
     render : ->
@@ -180,10 +174,21 @@
         <pre>{@props.contents}</pre>

-Archive = rclass
+Archive = (name) -> rclass
+    reduxProps:
+        "#{name}" :
+            contents       : rtypes.string
+            info           : rtypes.string
+            type           : rtypes.string
+            loading        : rtypes.bool
+            command        : rtypes.string
+            error          : rtypes.any
+            extract_output : rtypes.string
+
     propTypes:
-        path : rtypes.string
-        actions: rtypes.object
+        path       : rtypes.string
+        actions    : rtypes.object
+        project_id : rtypes.string

     title : ->
         <tt><Icon name="file-zip-o" /> {@props.path}</tt>
@@ -204,31 +209,23 @@
             <ArchiveContents path={@props.path} contents={@props.contents} actions={@props.actions} project_id={@props.project_id} />
         </Panel>

-render = (flux, project_id, path) ->
-    name = flux_name(project_id, path)
-    actions = flux.getActions(name)
-    connect_to =
-        contents   : name
-        info       : name
-        type       : name
-        loading    : name
-        command    : name
-        error      : name
-        extract_output : name
-
-    <Flux flux={flux} connect_to=connect_to>
-        <Archive path={path} actions={actions} project_id={project_id} />
-    </Flux>
-
-exports.free = (project_id, path, dom_node, flux) ->
+render = (redux, project_id, path) ->
+    name = redux_name(project_id, path)
+    actions = redux.getActions(name)
+    Archive_connected = Archive(name)
+    <Redux redux={redux}>
+        <Archive_connected path={path} actions={actions} project_id={project_id} />
+    </Redux>
+
+exports.free = (project_id, path, dom_node, redux) ->
     ReactDOM.unmountComponentAtNode(dom_node)

-exports.render = (project_id, path, dom_node, flux) ->
-    init_flux(flux, project_id, path)
-    ReactDOM.render(render(flux, project_id, path), dom_node)
-
-exports.hide = (project_id, path, dom_node, flux) ->
+exports.render = (project_id, path, dom_node, redux) ->
+    init_redux(redux, project_id, path)
+    ReactDOM.render(render(redux, project_id, path), dom_node)
+
+exports.hide = (project_id, path, dom_node, redux) ->
     ReactDOM.unmountComponentAtNode(dom_node)

-exports.show = (project_id, path, dom_node, flux) ->
-    ReactDOM.render(render(flux, project_id, path), dom_node)+exports.show = (project_id, path, dom_node, redux) ->
+    ReactDOM.render(render(redux, project_id, path), dom_node)


webapp/latex: on firefox, the embedded preview is disabled

--- src/smc-webapp/editor_latex.coffee
+++ src/smc-webapp/editor_latex.coffee
@@ -89,7 +89,7 @@
         @latex_editor.on 'saved', () =>
             @update_preview () =>
                 if @_current_page == 'pdf-preview'
-                    @preview_embed.update()
+                    @preview_embed?.update()
             @spell_check()

         @latex_editor.syncdoc.on 'connect', () =>
@@ -315,7 +315,7 @@
         @latex_editor.remove()
         @element.remove()
         @preview.remove()
-        @preview_embed.remove()
+        @preview_embed?.remove()

     _init_buttons: () =>
         @element.find("a").tooltip(TOOLTIP_CONFIG)
@@ -498,7 +498,7 @@
             if not err
                 @update_preview (force=force) =>
                     if @_current_page == 'pdf-preview'
-                        @preview_embed.update()
+                        @preview_embed?.update()
                 @spell_check()

     update_preview: (cb, force=false, only_compile=false) =>


FinderTreeController: use machine instance's fs.create to create a new file

--- client/Finder/filetree/controllers/findertreecontroller.coffee
+++ client/Finder/filetree/controllers/findertreecontroller.coffee
@@ -255,20 +255,21 @@
       @beingEdited = null

   createFile:(nodeView, type = "file")->
+
     @notify "creating a new #{type}!"
     nodeData = nodeView.getData()

-    { vmName } = nodeData
+    { machine } = nodeData

     if nodeData.type is "file"
-      {parentPath} = nodeData
+      { parentPath } = nodeData
     else
       parentPath = nodeData.path

     path = FSHelper.plainPath \
       "#{parentPath}/New#{type.capitalize()}#{if type is 'file' then '.txt' else ''}"

-    FSItem.create { path, type, vmName, treeController: this }, (err, file)=>
+    machine.fs.create { path, type, treeController: this }, (err, file)=>
       if err
         @notify null, null, err
       else


Admin: Stacks: Fix reference error.

Admin: Stacks: Fix reference error.

--- client/admin/lib/views/stacks/onboarding/onboardingview.coffee
+++ client/admin/lib/views/stacks/onboarding/onboardingview.coffee
@@ -146,7 +146,7 @@
     if selectedCodeService
       cloneText = CLONE_REPO_TEMPLATES[selectedCodeService]

-      for serverName, serverConfig of stackTemplate.resources.aws_instance
+      for serverName, serverConfig of stackTemplate.resource.aws_instance
         { user_data } = serverConfig

         if user_data


Spring cleanup ಠ_ಠ

Spring cleanup ಠ_ಠ

--- client/finder/filetree/controllers/findertreecontroller.coffee
+++ client/finder/filetree/controllers/findertreecontroller.coffee
@@ -475,9 +475,7 @@
   cmOpenFile:      (nodeView, contextMenuItem)-> @openFile nodeView
   cmPreviewFile:   (nodeView, contextMenuItem)-> @previewFile nodeView
   # cmCompile:       (nodeView, contextMenuItem)-> @compileApp nodeView
-  # cmRunApp:        (nodeView, contextMenuItem)-> @runApp nodeView
   # cmMakeNewApp:    (nodeView, contextMenuItem)-> @makeNewApp nodeView
-  # cmDownloadApp:   (nodeView, contextMenuItem)-> @downloadAppSource nodeView
   # cmPublish:       (nodeView, contextMenuItem)-> @publishApp nodeView
   cmOpenFileWithApp: (nodeView, contextMenuItem)-> @openFileWithApp  nodeView, contextMenuItem
   cmCloneRepo:     (nodeView, contextMenuItem)-> @cloneRepo nodeView


Make position of marker relative to the correct element

--- app/assets/javascripts/avalon_player.js.coffee
+++ app/assets/javascripts/avalon_player.js.coffee
@@ -101,10 +101,10 @@
           $('.row.marker').each (i,value) ->
             offset = $(this)[0].dataset['offset']
             marker_id = $(this)[0].dataset['marker']
-            title = String($(this).find('.marker_title')[0].text).replace(/"/g, '"') + " ["+mejs.Utility.secondsToTimeCode(offset)+"]";
-            offset_percent = Math.round(if isNaN(parseFloat(offset)) then 0 else (100*offset / duration))
-            offset_percent = Math.max(0,Math.min(100,offset_percent));
-            scrubber.append('')
+            title = String($(this).find('.marker_title')[0].text).replace(/"/g, '"') + " ["+mejs.Utility.secondsToTimeCode(offset)+"]"
+            offset_percent = if isNaN(parseFloat(offset)) then 0 else Math.min(1, offset / duration)
+            offset = $('.mejs-time-total').width() * offset_percent
+            scrubber.append('')
         @player.setCurrentTime initialTime

       @player.options.playlistItemDefaultTitle = @stream_info.embed_title


Fix delete confirmation. Fixes #5891

Fix delete confirmation. Fixes #5891

--- backend/app/assets/javascripts/spree/backend/taxonomy.js.coffee
+++ backend/app/assets/javascripts/spree/backend/taxonomy.js.coffee
@@ -55,15 +55,15 @@
   node = data.rslt.obj
   delete_url = base_url.clone()
   delete_url.setPath delete_url.path() + '/' + node.prop("id")
-  jConfirm Spree.translations.are_you_sure_delete, Spree.translations.confirm_delete, (r) ->
-    if r
-      $.ajax
-        type: "POST",
-        dataType: "json",
-        url: delete_url.toString(),
-        data: {_method: "delete"},
-        error: handle_ajax_error
-    else
+  response = confirm(Spree.translations.are_you_sure_delete);
+  if (response == true)
+    $.ajax
+      type: "POST",
+      dataType: "json",
+      url: delete_url.toString(),
+      data: {_method: "delete"},
+      error: handle_ajax_error
+  else
       $.jstree.rollback(last_rollback)
       last_rollback = null


fix: Correct behavior after the update of coordinates

--- app/assets/javascripts/profile/location.coffee
+++ app/assets/javascripts/profile/location.coffee
@@ -185,7 +185,7 @@
       $('#map_canvas').set_marker($('#relic_latitude').val().toNumber(), $('#relic_longitude').val().toNumber())

     $('form.relic').on 'change',  '.foreign-location select, .foreign-location input, .street_input input, #relic_polish_relic', ->
-      $(this).addClass('wait')
+      $('form.relic').addClass('wait')
       geocode_location (lat, lng) ->
         $('#map_canvas').set_marker(lat, lng)
         $form = $('form.relic')


Displays map on cultivable_zones and land_parcels. Fixes #1611.

--- app/assets/javascripts/visualization.js.coffee
+++ app/assets/javascripts/visualization.js.coffee
@@ -170,6 +170,11 @@
     _create: ->
       $.extend(true, @options, @element.data("visualization"))
       @mapElement = $("<div>", class: "map").appendTo(@element)
+
+      if @options.box?
+        @element.css('height', @options.box.height)
+        @element.css('width', @options.box.width)
+
       @map = L.map(@mapElement[0], @options.map)
       @layers = []


refactor: change language from AAF to SSO

--- src/app/sessions/states/sign-in/sign-in.coffee
+++ src/app/sessions/states/sign-in/sign-in.coffee
@@ -32,7 +32,7 @@
   # Get the confugurable, external name of Doubtfire
   $scope.externalName = DoubtfireConstants.ExternalName

-  # Check for AAF login
+  # Check for SSO login
   $scope.api = DoubtfireConstants.API_URL
   timeoutPromise = $timeout (-> $scope.waitingAWhile = true), 1500
   $http.get("#{DoubtfireConstants.API_URL}/auth/method").then ((response) ->
@@ -41,11 +41,10 @@

     if $scope.SSOLoginUrl
       if $stateParams.authToken
-        console.log $stateParams
-        # This is AAF and we just got an auth_token? Must request to sign in
+        # This is SSO and we just got an auth_token? Must request to sign in
         $scope.signIn({ auth_token: $stateParams.authToken, username: $stateParams.username })
       else
-        # We are AAF and no auth token so we can must redirect to AAF login provider
+        # We are SSO and no auth token so we can must redirect to SSO login provider
         window.location.assign($scope.SSOLoginUrl)
     else
       $scope.authMethodLoaded = true


Only remove if not already detached

Only remove if not already detached

--- src/concept-coach/index.cjsx
+++ src/concept-coach/index.cjsx
@@ -91,7 +91,7 @@
     props.close = ->
       componentModel.channel.emit('close.clicked')
       modalCoachWrapped.unmountFrom(modalNode)
-      mountNode.removeChild(modalNode)
+      mountNode.removeChild(modalNode) if modalNode.parentNode is mountNode

     # wait until our logout request has been received and the close
     User.channel.once 'logout.received', ->


changed logging to easier to see when the http request starts

--- services/docstore/app/coffee/HttpController.coffee
+++ services/docstore/app/coffee/HttpController.coffee
@@ -50,7 +50,7 @@
 			res.send 400 # Bad Request
 			return

-		logger.log project_id: project_id, doc_id: doc_id, "updating doc"
+		logger.log project_id: project_id, doc_id: doc_id, "got http request to update doc"
 		DocManager.updateDoc project_id, doc_id, lines, (error, modified, rev) ->
 			return next(error) if error?
 			res.json {


Update the Twilio adapter

Update the Twilio adapter

--- src/adapters/twilio.coffee
+++ src/adapters/twilio.coffee
@@ -2,7 +2,7 @@
 HTTP  = require 'http'
 QS    = require 'querystring'

-class Twilio extends Robot
+class Twilio extends Robot.Adapter
   constructor: ->
     @sid   = process.env.HUBOT_SMS_SID
     @token = process.env.HUBOT_SMS_TOKEN


Use 'phonegap local plugin' because 'phonegap plugin' alias is not present in all phonegap-cli versions

--- src/tasks/build/base/plugin.coffee
+++ src/tasks/build/base/plugin.coffee
@@ -4,7 +4,7 @@
   helpers = require('../../helpers')(grunt)

   addPlugin = (plugin, fn) ->
-    cmd = "phonegap plugin add #{plugin} #{helpers.setVerbosity()}"
+    cmd = "phonegap local plugin add #{plugin} #{helpers.setVerbosity()}"
     helpers.exec cmd, fn

   add: (plugins, fn) ->


DeleteAccountView: destroy existing machines on account delete [fixes #79646108]

--- client/Account/views/deleteaccountview.coffee
+++ client/Account/views/deleteaccountview.coffee
@@ -61,27 +61,29 @@
             {username}      = dangerForm.inputs
             {confirmButton} = dangerForm.buttons

-            JUser.unregister username.getValue(), (err)=>
-              if err then new KDNotificationView title : 'There was a problem, please try again!'
-              else
-                surveyLink = "https://docs.google.com/forms/d/1fiC6wSThfXxtLpdRlQ7qnNvJrClqdUrmOT_L-_cu1tw/viewform"
-                @setTitle 'Account successfully deleted'
-                @setContent """
-

-

-                      Thanks for trying out Koding. Sorry to see you go. We'd appreciate if you take a moment to tell us why.
-

-

-
Click to take the 1 minute survey.

-

-                  """
-                  # Loading...
-                @_windowDidResize()
-                KD.mixpanel "Delete account, success"
-                KD.utils.wait 30000, ->
-                  Cookies.expire 'clientId'
-                  location.replace '/'
-              confirmButton.hideLoader()
+            @destroyExistingMachines =>
+
+              JUser.unregister username.getValue(), (err)=>
+                if err then new KDNotificationView title : 'There was a problem, please try again!'
+                else
+                  surveyLink = "https://docs.google.com/forms/d/1fiC6wSThfXxtLpdRlQ7qnNvJrClqdUrmOT_L-_cu1tw/viewform"
+                  @setTitle 'Account successfully deleted'
+                  @setContent """
+

+

+                        Thanks for trying out Koding. Sorry to see you go. We'd appreciate if you take a moment to tell us why.
+

+

+
Click to take the 1 minute survey.

+

+                    """
+                    # Loading...
+                  @_windowDidResize()
+                  KD.mixpanel "Delete account, success"
+                  KD.utils.wait 20000, ->
+                    Cookies.expire 'clientId'
+                    location.replace '/'
+                confirmButton.hideLoader()

           buttons            :
             confirmButton    :
@@ -120,3 +122,27 @@
     else
       @modalTabs.forms.dangerForm.buttons.confirmButton.disable()
       input.setValidationResult 'keyupCheck', 'Sorry, entered value does not match your username!', showError
+
+  destroyExistingMachines: (callback)->
+
+    { computeController } = KD.singletons
+
+    KD.remote.api.JMachine.some provider: "koding", (err, machines)=>
+
+      if err? or not machines? then callback()
+      else
+
+        machines.forEach (machine)->
+          computeController.kloud
+          .destroy { machineId: machine._id }
+          .then  (res)->
+            info res if res?
+            computeController.emit "revive-#{machine._id}"
+          .timeout ComputeController.timeout
+          .catch (err)->
+            KD.utils.wait 400, ->
+              computeController.kloud
+              .destroy { machineId: machine._id }
+            warn err if err?
+
+        KD.utils.wait 4000, -> callback()


PermissionForm: Add readable texts for JCredential, JStackTemplate

--- client/Dashboard/views/permissionsform.coffee
+++ client/Dashboard/views/permissionsform.coffee
@@ -70,18 +70,20 @@

   readableText = (text)->
     dictionary =
-      "JTag"        : "Tags"
-      "JNewApp"     : "Apps"
-      "JGroup"      : "Groups"
-      "JPost"       : "Social API"
-      "JVM"         : "Compute"
-      "CActivity"   : "Activity"
-      "JGroupBundle": "Group Bundles"
-      "JDomain"     : "Domains"
-      "JProxyFilter": "Proxy Filters"
-      "JInvitation" : "Invitations"
-      "JComment"    : "Comments"
-      "JStack"      : "Stacks"
+      "JTag"           : "Tags"
+      "JNewApp"        : "Apps"
+      "JGroup"         : "Groups"
+      "JPost"          : "Social API"
+      "JVM"            : "Compute"
+      "CActivity"      : "Activity"
+      "JGroupBundle"   : "Group Bundles"
+      "JDomain"        : "Domains"
+      "JProxyFilter"   : "Proxy Filters"
+      "JInvitation"    : "Invitations"
+      "JComment"       : "Comments"
+      "JStack"         : "Stacks"
+      "JStackTemplate" : "Stack Templates"
+      "JCredential"    : "Credentials"

     return dictionary[text] or text.charAt(0).toUpperCase()+text.slice(1)


Moved 'evidence-box'

Moved 'evidence-box'

--- core/app/backbone/views/evidence/top_fact_evidence_view.coffee
+++ core/app/backbone/views/evidence/top_fact_evidence_view.coffee
@@ -1,4 +1,5 @@
 class TopFactEvidenceLayoutView extends Backbone.Marionette.Layout
+  className: 'evidence-box'
   template: 'evidence/top_fact_evidence_layout'

   regions:
@@ -20,7 +21,7 @@
     @contentRegion.show new InteractingUsersView model: @model

 class window.TopFactEvidenceView extends Backbone.Marionette.CompositeView
-  className: 'top-fact-evidence evidence-box'
+  className: 'top-fact-evidence'
   template: 'evidence/top_fact_evidence'
   itemView: TopFactEvidenceLayoutView
   itemViewContainer: '.js-evidence-item-view-container'


payment: add UI for a user-provided description

the user can use this as a reminder of which payment method is which.

--- client/app/MainApp/payment/paymentform.coffee
+++ client/app/MainApp/payment/paymentform.coffee
@@ -15,12 +15,14 @@
         placeholder       : 'First Name'
         defaultValue      : KD.whoami().profile.firstName
         validate          : @required 'First name is required!'
+        keyup             : @bound 'updateDescription'
         nextElementFlat   :
           cardLastName    :
             placeholder   : 'Last Name'
             defaultValue  : KD.whoami().profile.lastName
             validate      : @required 'Last name is required!'
-
+      cardDescription     :
+        label             : 'Description'
       cardNumber          :
         label             : 'Credit Card'
         placeholder       : 'Credit Card Number'
@@ -79,6 +81,18 @@
     @billingForm.fields.cardNumber.addSubView @icon = new KDCustomHTMLView
       tagName  : 'span'
       cssClass : 'icon'
+
+    @updateDescription()
+
+  updateDescription:->
+    { inputs } = @billingForm
+    formData = @billingForm.getData()
+    cardFirstName = inputs.cardFirstName.getValue()
+    cardType = switch formData.cardType
+      when 'Unknown', undefined then 'credit card'
+      else formData.cardType
+    cardOwner = if cardFirstName then "#{ cardFirstName }'s " else ''
+    inputs.cardDescription.setPlaceHolder "#{ cardOwner }#{ cardType }"

   setBillingInfo: (billingInfo) ->
     for own key, value of billingInfo
@@ -139,6 +153,7 @@
     unless $icon.hasClass cardType
       $icon.removeClass 'visa mastercard discover amex unknown'
       $icon.addClass cardType  if cardType
+    @updateDescription()

   required:(msg)->
     rules    : required  : yes


Hide plan title when operation is downgrade to free plan.

--- client/app/lib/payment/paymentdowngradewithdeletionmodal.coffee
+++ client/app/lib/payment/paymentdowngradewithdeletionmodal.coffee
@@ -1,9 +1,10 @@
-kd = require 'kd'
-KDButtonView = kd.ButtonView
-KDCustomHTMLView = kd.CustomHTMLView
-KDLoaderView = kd.LoaderView
-PaymentBaseModal = require './paymentbasemodal'
-showError = require 'app/util/showError'
+kd                  = require 'kd'
+KDButtonView        = kd.ButtonView
+KDCustomHTMLView    = kd.CustomHTMLView
+KDLoaderView        = kd.LoaderView
+PaymentBaseModal    = require './paymentbasemodal'
+showError           = require 'app/util/showError'
+PaymentConstants    = require './paymentconstants'

 module.exports = class PaymentDowngradeWithDeletionModal extends PaymentBaseModal
@@ -59,6 +60,19 @@
       title  : 'YES, DOWNGRADE'
       callback : @lazyBound 'emit', 'PaymentDowngradeWithDeletionSubmitted'

+    @filterViews()
+
+
+  filterViews: ->
+
+    { state: { planTitle, currentPlan } } = @getOptions()
+
+    operation   = PaymentConstants.getOperation currentPlan, planTitle
+    isFreePlan  = planTitle is PaymentConstants.planTitle.FREE
+
+    if operation is PaymentConstants.operation.DOWNGRADE and isFreePlan
+      @subtitle.hide()
+

   initEvents: ->


Append host snapshot if participant is watching host

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -524,6 +524,9 @@

   appendHostSnapshot: (snapshot) ->

+    watchingHost = -1 < @myWatchMap.values().indexOf @collaborationHost
+    return snapshot  unless watchingHost
+
     hostSnapshot = @rtm.getFromModel("#{@collaborationHost}Snapshot")?.values()

     if hostSnapshot


Prevent blink at animation start (Fix #2)

--- src/chart-d3-pie.coffee
+++ src/chart-d3-pie.coffee
@@ -5,10 +5,9 @@
   constructor: (selectors, data, options) ->
     super(selectors, data, options)

-  animateRotate: (path, arc, options) ->
+  animateRotate: (sl, arc, options) ->
     return if !(options.animation and options.animateRotate)
-    path
-      .transition()
+    sl.transition()
       .call(@transitionEndAll, options)
       .duration @duration()
       .ease(options.animationEasing)
@@ -46,7 +45,6 @@
       .append('path')
       .attr(@attrSegmentStroke(options))
       .attr
-        d: arc
         transform: @attrTranslateToCenter()
         fill: (d, i) -> colors[i]

@@ -58,18 +56,33 @@

   # TODO: enable spec
   render: ->
+    options = @options
     width = @getRootElementWidth()
     height = @getRootElementHeight()
     margin = 5
     outerRadius = @getOuterRadius(width, height, margin)
     innerRadius = @getInnerRadius(outerRadius, @options)
     arc = d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius)
-    path = @drawChart(arc, @options)
+    sl = @drawChart(arc, @options)
     @transitionEndAllCount = @setAnimationComplete(@options)
     @options.onAnimationComplete.call(this) if isNaN(@transitionEndAllCount)
-    @animateRotate(path, arc, @options)
-    @animateScale(@options)
+
+    switch
+      when options.animation and options.animateRotate and options.animateScale
+        @animateRotate(sl, arc, options)
+        @animateScale(options)
+      when options.animation and options.animateRotate
+        @animateRotate(sl, arc, options)
+      when options.animation and options.animateScale
+        @renderPiePath(sl, arc)
+        @animateScale(options)
+      else
+        @renderPiePath(sl, arc)
+
     this
+
+  renderPiePath: (sl, arc) ->
+    sl.attr(d: arc)

   setAnimationComplete: (options) ->
     return Infinity unless typeof options.onAnimationComplete is 'function'


Display datetime in correct format.

Display datetime in correct format.

--- core/htdocs_source/src/pods/oxifield-datetime/component.coffee
+++ core/htdocs_source/src/pods/oxifield-datetime/component.coffee
@@ -9,11 +9,12 @@
     setup: (->
         value = @get "content.value"
         if value is "now"
-            @set "content.pickvalue", moment().format(@get "format")
+            @set "content.pickvalue", moment().format @get "format"
         else if value
-            @set "content.pickvalue", moment.unix(value).format(@get "format")
+            @set "content.pickvalue", moment.unix(value).format @get "format"
         Em.run.next =>
-            @$().find(".date").datetimepicker()
+            @$().find(".date").datetimepicker
+                format: @get "format"
     ).on "didInsertElement"

     propagate: Em.observer "content.pickvalue", ->


Fix dictionary page letters sorting problem

--- source/js/controllers/dictionary_controller.coffee
+++ source/js/controllers/dictionary_controller.coffee
@@ -6,7 +6,7 @@
         $location.path('/home')

     entryService.all().then (entries) =>
-      first_letters = _.map(entries, (entry) -> entry.entry_word[0])
+      first_letters = _.map(entries, (entry) -> entry.entry_word[0].toUpperCase())
       $scope.letters = _.uniq(first_letters).sort()

 angular.module('app').controller 'dictionaryController', ['$scope', '$rootScope', '$location', 'entryService', 'i18nService', Controller]


Fix namespacing

Fix namespacing

--- h2o-web/src/main/steam/scripts/global.tests.coffee
+++ h2o-web/src/main/steam/scripts/global.tests.coffee
@@ -4,7 +4,7 @@
 path = require 'path'
 spawn = (require 'child_process').spawn
 httpRequest = require 'request'
-_ = require 'lodash'
+lodash = require 'lodash'
 async = require 'async'
 test = require 'tape'

@@ -43,7 +43,7 @@

 # Pretty-print errors, etc.
 pp = (error) ->
-  if _.isObject error
+  if lodash.isObject error
     JSON.stringify error
   else
     error


changed title

changed title

--- platform/src/app.coffee
+++ platform/src/app.coffee
@@ -11,7 +11,7 @@
           items: [
             {
               xtype: 'header'
-              title: "Snap1k Game Editor"
+              title: "gamEvolve"
               border: false
               cls: "appTitle"
             }


Uh, icon name.

Uh, icon name.

--- Web/js/classes/notification-center.coffee
+++ Web/js/classes/notification-center.coffee
@@ -45,13 +45,13 @@
 		return unless @hasPermission
 		if isSane
 			noti = new Notification title,
-				icon: 'rohpod.png'
+				icon: 'rohbot.png'
 				body: body
 			denoti = ->
 				noti?.close()
 				noti = null
 		else
-			noti = webkitNotifications.createNotification 'rohpod.png', title, body
+			noti = webkitNotifications.createNotification 'rohbot.png', title, body
 			denoti = ->
 				noti?.cancel()
 				noti = null


Add null check.

Add null check.

--- src/coffee/index.coffee
+++ src/coffee/index.coffee
@@ -75,6 +75,7 @@
         func = _event[LOGIN]
         return callback(null, null) if not func?
         func (auth) ->
+          return if not auth?
           callback(auth.username, auth.password)
           _triedSavedAccount = false
           storage.set PROXY_AUTH, auth, (err) ->


Remove unnecessary (and error prone) regex-based HTML whitespace removal

--- src/trix/models/html_parser.coffee
+++ src/trix/models/html_parser.coffee
@@ -232,7 +232,6 @@
     JSON.parse(element.dataset.trixAttachment)

   sanitizeHTML = (html) ->
-    html = removeInsignificantWhitespace(html)
     doc = document.implementation.createHTMLDocument("")
     doc.documentElement.innerHTML = html
     {body, head} = doc
@@ -262,11 +261,6 @@

     body.innerHTML

-  removeInsignificantWhitespace = (html) ->
-    html
-      .replace(/>\n+</g, "><")
-      .replace(/>\ +</g, "> <")
-
   convertNewlinesToSpaces = (string) ->
     string.replace(/\s?\n\s?/g, " ")


Fast unification finished

Fast unification finished

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1304,7 +1304,6 @@
       unifiedType = explicitType# substitute ctx.substitution, explicitType
       inferredType = quantifyUnbound ctx, unifiedType
       if not typeEq inferredType, updatedDeclaredType
-        console.log currentType, (printType currentType)
         ctx.extendSubstitution substitutionFail "#{name}'s declared type is too general, inferred #{plainPrettyPrint inferredType}"
       # Context reduction
       isDeclaredConstraint = (c) ->
@@ -2587,8 +2586,6 @@
       if instanceContraints
         toNormalize.push instanceContraints...
       else
-        # TODO: propogate this as standard error
-        # throw new Error "no instance found to satisfy #{safePrintType constraint}"
         return error: instanceLookupFailed constraint
       after = subLimit ctx.substitution
       # There was a functional dependency, renormalize
@@ -3049,8 +3046,8 @@
   inferredType = freshInstance ctx, canonicalType
   # console.log "canonicalType", canonicalType, type
   # console.log (printType canonicalType)
-  # console.log (printType inferredType)
-  # console.log (printType type.type)
+  # console.log (JSON.stringify inferredType)
+  # console.log (JSON.stringify type.type)
   ctx.extendSubstitution matchType inferredType.type, type.type#(substitute ctx.substitution, type).type
   # console.log "after", (printType inferredType.type)
   reduceConstraints ctx, inferredType.constraints
@@ -4429,13 +4426,15 @@
       emptySubstitution()
     else
       unifyFail t1, t2
+  else if t2.TypeVariable and t2.ref.val
+    matchType t1, t2.ref.val
+  else if t1.TypeVariable and t2.TypeVariable and t1.name is t2.name
+    emptySubstitution()
   else if t1.TypeVariable and kindsEq (kind t1), (kind t2)
     t1.ref.val = t2 #newSubstitution t1.name, t2
     # console.log "attaching", t1.name, t2
     # console.log "attached", (printType t2)
     newSubstitution()
-  else if t2.TypeVariable and t2.ref.val
-    matchType t1, t2.ref.val
   else if t1.TypeConstr and t2.TypeConstr and t1.name is t2.name
     emptySubstitution()
   else if t1.TypeApp and t2.TypeApp
@@ -4449,15 +4448,29 @@
       s1 = matchType t1.types[0], t2.types[0]
       # We imply functional dependency of the form A a b c | a -> b c
       # s2 = mostGeneralUnifier (new Types (substituteList s1, t1.types[1...])), (new Types t2.types[1...])
-      s2 = unifyImpliedParams t1, t2
-      subUnion s1, s2
+      if isFailed s1
+        s1
+      else
+        unifyImpliedParams t1, t2
     else
       emptySubstitution()
   else
     unifyFail t1, t2

+# We need to check that the unification succeeds first before performing
+# it on the actual types
 unifyImpliedParams = (t1, t2) ->
-  mostGeneralUnifier (new Types t1.types[1...]), (new Types t2.types[1...])
+  types1 = new Types t1.types[1...]
+  types2 = new Types t2.types[1...]
+  makeSub = (types) ->
+    mapMap ((kind, name) -> new TypeVariable name, kind, {}), findFree types
+  subbed1 = substitute (makeSub types1), types1
+  subbed2 = substitute (makeSub types2), types2
+  if isFailed (sub = mostGeneralUnifier subbed1, subbed2)
+    sub
+  else
+    mostGeneralUnifier types1, types2
+

 # Used to convert between quantified and not quantified
 substitute = (substitution, type) ->
@@ -4501,6 +4514,23 @@
     findFreeInList type.types
   else
     newMap()
+
+# findRefs = (type) ->
+#   if type.TypeVariable
+#     newMapWith type.name, type
+#   else if type.TypeApp
+#     concatMaps (findRefs type.op), (findRefs type.arg)
+#   else if type.Constrained
+#     concatMaps (findRefs type.type), (findRefsInList type.constraints)
+#   else if type.ClassConstraint
+#     findRefs type.types
+#   else if type.Types
+#     findRefsInList type.types
+#   else
+#     newMap()
+
+# findRefsInList = (list) ->
+#   concatMaps (map findRefs, list)...

 # Type inference and checker ala Mark Jones

@@ -4796,7 +4826,7 @@
   typeOrConstraint

 mutateMarkingOrigin = (typeOrConstraint, expression) ->
-  typeOrConstraint.origin = expression
+  # typeOrConstraint.origin = expression

 # Clones constrained types and parts of them
 # Class constraint arguments are not cloned
@@ -5109,6 +5139,8 @@
     "(: #{(map printType, join [type.type], type.constraints).join ' '})"
   else if type.TempType
     "(. #{printType type.type})"
+  else if type.Types
+    (map printType, type.types).join ' '
   else if Array.isArray type
     "\"#{listOf type}\""
   else if type is undefined
@@ -5117,7 +5149,9 @@
     throw new Error "Unrecognized type in printType"

 collectArgs = (type) ->
-  if type.TypeApp
+  if type.TypeVariable and type.ref.val
+    collectArgs type.ref.val
+  else if type.TypeApp
     op = collectArgs type.op
     arg = collectArgs type.arg
     if (Array.isArray op) and (Array.isArray arg) and
@@ -6910,6 +6944,16 @@
   """
   '3', 3

+  'defer in subdefinition'
+  """
+  f (fn [x]
+    ""
+    g (fn [y]
+      ((fn [x] x) h))
+    h ((fn [x] x) x))
+  """
+  '3', 3
+
   # TODO: support matching with the same name
   #       to implement this we need the iife to take as arguments all variables
   #       with the same names, since JavaScript shadows it too strongly and


fix Jax.Shader.Program spec

fix Jax.Shader.Program spec

--- spec/javascripts/jax/shader/program_spec.js.coffee
+++ spec/javascripts/jax/shader/program_spec.js.coffee
@@ -51,10 +51,10 @@
     beforeEach -> program.compile SPEC_CONTEXT

     it "should set an arbitrary vertex position", ->
-      expect(program.vertex.toString()).toMatch /gl_Position = vec4\(1\.0, 1\.0, 1\.0, 1\.0\);/
+      expect(program.vertex.toString()).toMatch /gl_Position = vec4/

     it "should set an arbitrary fragment color", ->
-      expect(program.fragment.toString()).toMatch /gl_FragColor = vec4\(1\.0, 1\.0, 1\.0, 1\.0\);/
+      expect(program.fragment.toString()).toMatch /gl_FragColor = vec4/

   describe "with a valid vertex and fragment pair", ->
     beforeEach ->


Fixed conditions in pattern matching in quote

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2451,7 +2451,8 @@
             ctx.setAssignTo (jsAccess (jsCall "_terms", [matched]), i)
             precs = matchAst term
             ctx.resetAssignTo()
-            precs)
+            termsCheck = precs: [(cond_ (jsAccess matched, "length"))]
+            combinePatterns [termsCheck, precs])
       else
         commedAtom ast, ->
           ast.label = 'const'


Changing the trigger syntax for the delimited blocks snippets, to avoid things like periods, asterisks, etc. from accidentally triggering a block when attempting to line wrap.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_game_state.coffee
+++ src/engine_game_state.coffee
@@ -23,7 +23,7 @@
       I.currentState = requestedState

       I.currentState.trigger "enter", previousState
-      self.trigger '', I.currentState
+      self.trigger 'stateEnt', I.currentState

       requestedState = null


Fixed TOTP malfunction

Fixed TOTP malfunction

--- server/lib/passport_configurator.coffee
+++ server/lib/passport_configurator.coffee
@@ -56,4 +56,4 @@
     # totp strategy
     # we need a key (stored string)
     passport.use new TotpStrategy { codeField: "authcode" }, (user, done) ->
-        done null, user.encryptedOtpKey, 30
+        done null, new Buffer(user.encryptedOtpKey, 'hex'), 30


Nodejs Web Server Tests: add comments

Nodejs Web Server Tests: add comments

--- servers/testhelper/handler/index.coffee
+++ servers/testhelper/handler/index.coffee
@@ -17,13 +17,17 @@

   [options, callback] = [callback, options]  unless callback

+  # various csrf token cases those are expected to fail
   paramsObjects = [
     {
-      csrfCookie : false
+      body       :
+        _csrf    : generateRandomString()
+      csrfCookie : ''
     }
     {
       body       :
         _csrf    : ''
+      csrfCookie : generateRandomString()
     }
     {
       body       :
@@ -36,12 +40,14 @@

   paramsObjects.forEach (params) ->

+    # if generateHandlerRequestParams fires a callback
     if options?.generateParamsAsync
       queue.push ->
         generateHandlerRequestParams params, (requestParams) ->
           request[method] requestParams, expect403 ->
             queue.next()

+    # else generate params synchronously
     else
       queue.push ->
         requestParams = generateHandlerRequestParams params


Fix payment description for org domain

Fix payment description for org domain

--- client/lang/lang.coffee
+++ client/lang/lang.coffee
@@ -273,7 +273,7 @@
         en: (amount) -> "You pay for one month access to algoprog.org. The price for you is #{amount} rubles."
     payment_for_one_month:
         ru: "Payment for one month of classes on algoprog.ru"  # apparently unitpay has problems with encoding for russian desc
-        en: "Payment for one month of classes on algoprog.ru"
+        en: "Payment for one month of classes on algoprog.org"
     payment_sum:
         ru: "Сумма"
         en: "Sum"


Use app route instead of root

Use app route instead of root

The route needs a path for the router to redirect to for users with no
course (such as content analyst)

--- src/flux/current-user.coffee
+++ src/flux/current-user.coffee
@@ -32,7 +32,7 @@
     roles:
       teacher: 'taskplans'
       student: 'viewStudentDashboard'
-      default: 'root'
+      default: 'app'
   guide:
     label: 'Performance Forecast'
     roles: