Fix tab

Fix tab

Signed-off-by: Lincoln Lee <46f1a0bd5592a2f9244ca321b129902a06b53e03@lincoln.hk>

--- Daily_Apple/da.coffee
+++ Daily_Apple/da.coffee
@@ -14,8 +14,7 @@
       @set_title page.find("#articleContent h1").text()
       raw_content = page.find("#masterContent .ArticleContent_Inner")
       @set_content _.chain(raw_content).map(
-                                            (paragraph) ->
-                                            $(paragraph).find("p").removeAttr("class").prop("outerHTML")
+                                            (paragraph) -> $(paragraph).find("p").removeAttr("class").prop("outerHTML")
                                              ).reduce(
                                                 (memo, obj) -> memo + obj
                                              ).value()


event object passed as parameter to shouldOpen callback

--- src/scripts/slick-lightbox.coffee
+++ src/scripts/slick-lightbox.coffee
@@ -23,7 +23,7 @@
       e.preventDefault()
       $clickedItem = $(this)
       if typeof slickLightbox.options.shouldOpen is 'function'
-        return unless slickLightbox.options.shouldOpen(slickLightbox, $clickedItem)
+        return unless slickLightbox.options.shouldOpen(slickLightbox, $clickedItem, e)
       $items = slickLightbox.filterOutSlickClones slickLightbox.$element.find(slickLightbox.options.itemSelector)
       slickLightbox.init $items.index($clickedItem)


replace kpis#index route in kpis svc

replace kpis#index route in kpis svc

--- src/services/kpis/kpis.svc.coffee
+++ src/services/kpis/kpis.svc.coffee
@@ -213,14 +213,14 @@

       # Templates from each registered Bolt
       for bolt in ImpacRoutes.bolts()
-        kpisTemplatesPromises.push $http.get("#{bolt.path}/bolt_kpis").then(
+        kpisTemplatesPromises.push $http.get("#{bolt.path}/kpis").then(
           (response) ->
             for template in response.data.kpis
               template.metadata ||= {}
               template.metadata.bolt_path = bolt.path
               kpisTemplates.push(template)
           (error) ->
-            $log.error("Impac! - KpisSvc: cannot retrieve kpis templates from bolt", "#{boltPath}/bolt_kpis")
+            $log.error("Impac! - KpisSvc: cannot retrieve kpis templates from bolt", "#{boltPath}/kpis")
         )

       $q.all(kpisTemplatesPromises).then(->


get_quotas not get_quota

get_quotas not get_quota

--- src/smc-hub/compute-client.coffee
+++ src/smc-hub/compute-client.coffee
@@ -878,7 +878,7 @@
                 if host
                     cb()
                 else
-                    @get_quota
+                    @get_quotas
                         cb : (err, quota) =>
                             member_host = !!quota?.member_host
                             cb(err)


do not sort the blobs when doing maintenance; no point and it was causing a lot of trouble in production...

--- src/smc-hub/postgres-blobs.coffee
+++ src/smc-hub/postgres-blobs.coffee
@@ -460,7 +460,7 @@
             query    : 'SELECT id, size FROM blobs'
             where    : "expire IS NULL AND gcloud IS NULL and (last_active <= NOW() - INTERVAL '#{opts.cutoff}' OR last_active IS NULL)"
             limit    : opts.limit
-            order_by : 'id'
+            ##  order_by : 'id'  # this is not important and was causing VERY excessive load in production (due to bad query plannnig?!)
             cb       : all_results (err, v) =>
                 if err
                     dbg("fail: #{err}")


course: (1) eliminate a default 30s timeout that was causing serious trouble; (2) do more copying in parallel

--- src/smc-webapp/editor_course.cjsx
+++ src/smc-webapp/editor_course.cjsx
@@ -62,7 +62,7 @@

 {NoUpgrades} = require('./project_settings')

-PARALLEL_LIMIT = 3  # number of async things to do in parallel
+PARALLEL_LIMIT = 5  # number of async things to do in parallel

 redux_name = (project_id, course_filename) ->
     return "editor-#{project_id}-#{course_filename}"
@@ -361,7 +361,7 @@
                             cb("store not defined"); return
                         store.wait
                             until   : (store) => store.get_student(student_id)
-                            timeout : 30
+                            timeout : 60
                             cb      : cb
                     (cb) =>
                         @create_student_project(student_id)
@@ -370,7 +370,7 @@
                             cb("store not defined"); return
                         store.wait
                             until   : (store) => store.get_student(student_id).get('project_id')
-                            timeout : 30
+                            timeout : 60
                             cb      : cb
                 ], cb)
             id = @set_activity(desc:"Creating #{students.length} student projects (do not close this until done)")
@@ -854,8 +854,9 @@
                 n = misc.mswalltime()
                 @return_assignment_to_student(assignment, student_id)
                 store.wait
-                    until : => store.last_copied('return_graded', assignment, student_id) >= n
-                    cb    : (err) =>
+                    timeout : 60*15
+                    until   : => store.last_copied('return_graded', assignment, student_id) >= n
+                    cb      : (err) =>
                         if err
                             errors += "\n #{err}"
                         cb()
@@ -1035,8 +1036,9 @@
                 n = misc.mswalltime()
                 action(assignment, student_id)
                 store.wait
-                    until : => store.last_copied(step, assignment, student_id) >= n
-                    cb    : (err) =>
+                    timeout : 60*15
+                    until   : => store.last_copied(step, assignment, student_id) >= n
+                    cb      : (err) =>
                         if err
                             errors += "\n #{err}"
                         cb()


slightly address https://github.com/sagemathinc/smc/issues/1956 by making message take less vertical space...

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -2020,10 +2020,10 @@
         public_view = @props.get_my_group(@props.project_id) == 'public'
         if public_view
             if @props.redux.getStore('account')?.is_logged_in()
-                <ErrorDisplay bsStyle="warning" title="Showing only public files" error={"You are trying to access a project that you are not a collaborator on. To view non-public files or edit files in this project you need to ask a collaborator of the project to add you."} />
+                <ErrorDisplay style={maxWidth:'100%'} bsStyle="warning" title="Showing only public files" error={"You are trying to access a project that you are not a collaborator on. To view non-public files or edit files in this project you need to ask a collaborator of the project to add you."} />
             else
                 <div>
-                    <ErrorDisplay bsStyle="warning" title="Showing only public files" error={"You are not logged in. To view non-public files or edit files in this project you'll need to sign in. If you are not a collaborator then you need to ask a collaborator of the project to add you to access non public files."} />
+                    <ErrorDisplay style={maxWidth:'100%'}  bsStyle="warning" title="Showing only public files" error={"You are not logged in. To view non-public files or edit files in this project you'll need to sign in. If you are not a collaborator then you need to ask a collaborator of the project to add you to access non public files."} />
                 </div>
         else
             if @props.redux.getStore('account')?.is_logged_in()


share: implement sagews code rendering

share: implement sagews code rendering

--- src/smc-webapp/sagews/output.cjsx
+++ src/smc-webapp/sagews/output.cjsx
@@ -13,6 +13,8 @@
 {HTML, Markdown} = require('../r_misc')

 {fromJS} = require('immutable')
+
+{CodeMirrorStatic} = require('../jupyter/codemirror-static')

 exports.CellOutput = rclass
     displayName: "SageCell-Output"
@@ -69,9 +71,13 @@
                 return {text}

     render_code: (value, key) ->
-

-            code render not yet implemented
-

+        options = fromJS({mode:{name:value.mode}})
+        <CodeMirrorStatic
+            key     = {key}
+            value   = {value.source ? ''}
+            options = {options}
+            style   = {background:'white', padding:'10px'}
+        />

     render_tex: (value, key) ->

@@ -82,7 +88,6 @@

             raw input render not yet implemented

-

     render_output_mesg: (elts, mesg) ->
         for type, value of mesg


missing file in the last commit

missing file in the last commit

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -319,35 +319,15 @@
   addItem:(activity, index, animation = null) ->
     @getListView().addItem activity, index, animation

-  unhideRecursively = (item)->
-
-    item.show ->
-      hiddenItemCount++
-      if hiddenItems[hiddenItemCount]
-        unhideRecursively hiddenItems[hiddenItemCount]
+  unhide = (item)-> item.show()
+
+  unhideNewHiddenItems = (hiddenItems)->
+    interval = setInterval ->
+      item = hiddenItems.shift()
+      if item
+        unhide item
       else
-        resetHiddenItems()
-
-  resetHiddenItems = ->
-
-    hiddenItems     = []
-    hiddenItemCount = 0
-
-  # unhide = (item)->
-
-  #   item.show()
-
-  unhideNewHiddenItems = (hiddenItems)->
-    # interval = setInterval ->
-    #   item = hiddenItems.shift()
-    #   if item
-    #     unhide item
-    #   else
-    #     clearInterval interval
-    # , 50
-
-
-    if hiddenItems[0]
-      unhideRecursively hiddenItems[0]
-
-
+        clearInterval interval
+    , 177
+
+


Fix duedate tests

Fix duedate tests

--- app/modules/components/due-date/due-date-controller.spec.coffee
+++ app/modules/components/due-date/due-date-controller.spec.coffee
@@ -51,7 +51,7 @@
                 return 'Set due date'
         }
         provide.value "$translate", mocks.$translate
-
+
     _mockTgProjectService = () ->
         mocks.tgProjectService = {
             project: {
@@ -102,8 +102,8 @@

     describe "when is set", ->
         normalDue = ['normal due', '#9dce0a']
-        dueSoon = ['due soon', '#ff8a84']
-        pastDue = ['past due', '#ff9900']
+        dueSoon = ['due soon', '#ff9900']
+        pastDue = ['past due', '#ff8a84']

         runs = [
             { objType: 'us', days: -1, expect: pastDue },


Fix Classifier in the dev classifier

Fix Classifier in the dev classifier

--- app/pages/dev-classifier/index.cjsx
+++ app/pages/dev-classifier/index.cjsx
@@ -1,6 +1,6 @@
 React = require 'react'
-Classifier = require '../../classifier'
 mockData = require './mock-data'
+{ ClassifierWrapper } = require '../../classifier'
 `import tasks from '../../classifier/tasks';`

 ClassificationViewer = React.createClass
@@ -82,7 +82,7 @@

   render: ->

-      <Classifier user={@props.user}  project={@props.project} workflow={@props.classification._workflow} preferences={@props.preferences} classification={@props.classification} onClickNext={@reload} />
+      <ClassifierWrapper user={@props.user}  project={@props.project} workflow={@props.classification._workflow} preferences={@props.preferences} classification={@props.classification} onClickNext={@reload} />

       <ClassificationViewer classification={@props.classification} />


Open metrics package in Atom

Open metrics package in Atom

Refs https://github.com/atom/welcome/pull/38
Refs https://github.com/atom/about/issues/4

--- lib/about-view.coffee
+++ lib/about-view.coffee
@@ -40,7 +40,7 @@
               Note: To help us improve Atom, we anonymously
               track usage metrics, such as launch time, screen size, and current
               version. See the
-              atom/metrics
+              atom/metrics
               package for details and instructions to disable it.
             '''
         @div class: 'about-credits', outlet: 'credits', =>
@@ -70,6 +70,9 @@
       # TODO: De-dupe this and use `application:open-terms-of-use`
       shell.openExternal 'https://help.github.com/articles/github-terms-of-service'

+    @on 'click', '.metrics-open', ->
+      atom.workspace.open('atom://config/packages/metrics')
+
   serialize: ->
     deserializer: @constructor.name
     uri: @getURI()


Certain console methods are not actually functions in IE

--- src/trix/core/basic_object.coffee
+++ src/trix/core/basic_object.coffee
@@ -9,9 +9,11 @@
         @[toProperty]

       if optional
-        object?[name]?.apply(object, arguments)
+        subject = object?[name]
+        apply.call(subject, object, arguments) if subject?
       else
-        object[name].apply(object, arguments)
+        subject = object[name]
+        apply.call(subject, object, arguments)

   parseProxyMethodExpression = (expression) ->
     unless match = expression.match(proxyMethodExpressionPattern)
@@ -29,6 +31,8 @@

     args

+  {apply} = Function.prototype
+
   proxyMethodExpressionPattern = ///
     ^
       (.+?)


Fix recover_sig_pubkey (I was doing something silly... I shouldn't be programming at 3am)

--- lib/bitcoin/tx.coffee
+++ lib/bitcoin/tx.coffee
@@ -19,11 +19,11 @@

   # Recover the pubkey used to sign a multisig input
   #
-  # Uses brote force on all possible public keys and recovery parameters until
-  # something matches.
+  # Uses brute force on all the possible recovery parameters ("nRecId") until
+  # it returns one of the multisig public keys.
   recover_sig_pubkey = (sig, hash, multisig_pubs) ->
     { r, s } = parseSig sig
-    for pubkey in multisig_pubs then for i in [0..3]
+    for i in [0..3]
       pubkey = (recoverPubKey r, s, hash, i).getPub()
       return pubkey if (bytesToHex pubkey) in multisig_pubs


Fix date format in API call.

Fix date format in API call.

Upgraded config node package (probably) started parsing
dates in config.yml, must explicitly format for api call.

--- src/views/position-details.coffee
+++ src/views/position-details.coffee
@@ -2,6 +2,7 @@
     _              = require 'underscore'
     $              = require 'jquery'
     Backbone       = require 'backbone'
+    moment         = require 'moment'

     models         = require 'cs!app/models'
     MapView        = require 'cs!app/map-view'
@@ -108,7 +109,7 @@
                     geometry: 'true'
                 reset: true
             if appSettings.school_district_active_date?
-                opts.data.date = appSettings.school_district_active_date
+                opts.data.date = moment(appSettings.school_district_active_date).format 'YYYY-MM-DD'
             @divList.fetch opts
         serializeData: ->
             data = super()


Autoload all folders without index

Autoload all folders without index

--- webpack/autoload.coffee
+++ webpack/autoload.coffee
@@ -8,7 +8,14 @@
 for f of ini.devDependencies when /^[$\w]+$/.test f
   exports[f] = f

-for f in ['sys', 'tools', 'objects']
-  fs.readdirSync folder = path.join __dirname, '../src', f
-  .forEach (src)->
-    exports[path.parse(src).name] = path.join folder, src
+for lib in fs.readdirSync src = path.join __dirname, '../src'
+  continue unless fs.statSync lib = path.join src, lib
+    .isDirectory()
+  z = {}
+  for file in fs.readdirSync lib
+    if 'index' == name = path.parse(file).name
+      z = {}
+      break
+    z[name] = path.join lib, file
+  for k, v of z
+    exports[k] = v


Start working on following lines

Start working on following lines

--- lib/log-filter.coffee
+++ lib/log-filter.coffee
@@ -79,6 +79,18 @@
           if ( lineIndex + resultsHeadLength < @results.text.length and lineNumber + resultsHeadLength >= @results.text[lineIndex + resultsHeadLength] ) or ( lineIndex + resultsHeadLength >= @results.text.length and 0 == ( @results.text.length - lineIndex ) - ( @textEditor.getLineCount() - lineNumber ) )
             resultsWithHead.push(lineNumber)
         @results.text = resultsWithHead
+      resultsTailLength = atom.config.get('language-log.followingFilteredExpansion')
+      if 0 < resultsTailLength
+        resultsWithTail = []
+        @results.text.reverse()
+        # for lineNumber, lineIndex in @results.text
+        #   if !( lineIndex + resultsTailLength >= @results.text.length or lineNumber - resultsTailLength < @results.text[lineIndex + resultsTailLength] ) and ( lineNumber - resultsTailLength >= 0 )
+        #     resultsWithTail.push(lineNumber)
+        for lineNumber, lineIndex in @results.text
+          if ( lineIndex + resultsTailLength < @results.text.length and lineNumber - resultsTailLength <= @results.text[lineIndex + resultsTailLength] ) or ( lineIndex + resultsTailLength >= @results.text.length and 0 == ( @results.text.length - lineIndex ) - ( @textEditor.getLineCount() - lineNumber ) )
+            resultsWithTail.push(lineNumber)
+        resultsWithTail.reverse()
+        @results.text = resultsWithTail

       # resultsHeadLength = 2
       # if 0 < resultsHeadLength


Remove debug console.log calls

Remove debug console.log calls

--- lib/log-filter.coffee
+++ lib/log-filter.coffee
@@ -62,16 +62,12 @@
           indexesForLines.push(lineNumber)
         linesIndexes.push(indexesForLines)
       lineToDisplayIndexes = []
-      console.log (logEntriesArray)
-      console.log (linesIndexes)
       for logLine, i in logEntriesArray
-        console.log(regex.test(logLine)+" : ["+i+"]"+logLine)
         if regex.test(logLine) then else lineToDisplayIndexes = lineToDisplayIndexes.concat(linesIndexes[i])
       @results.text = lineToDisplayIndexes
     else
       @results.text = for line, i in buffer.getLines()
         if regex.test(line) then else i
-    console.log (@results)
     @filterLines()

   performLevelFilter: (scopes) ->


Cleanup coffee.

Cleanup coffee.

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

 root = exports ? this
 root.setup_taxonomy_tree = (taxonomy_id) ->


one option for toggling the class to mark the question as specified

--- dkobo/koboform/static/js/xlform/XLF.view.rowDetail.SkipLogic.coffee
+++ dkobo/koboform/static/js/xlform/XLF.view.rowDetail.SkipLogic.coffee
@@ -195,8 +195,14 @@

     @

+  mark_question_specified: (is_specified=false) ->
+    @$el.toggleClass("skiplogic__criterion--unspecified-question", is_specified)
+
   bind_question_picker: () ->
+    @mark_question_specified false
+
     @$question_picker.on 'change', () =>
+      @mark_question_specified true
       @presenter.change_question @$question_picker.val()

   bind_operator_picker: () ->


Rendering Actions column in grid is optional closes #311

In case there is no TH with actions class,
no action cell will be rendered. Othrewise will be.

--- wzk/ui/grid/Grid.coffee
+++ wzk/ui/grid/Grid.coffee
@@ -37,8 +37,8 @@
   ###*
     @enum {string}
   ###
-  @DATA:
-    SHOW_ACTIONS: 'showActions'
+  @CLS:
+    ACTIONS: 'actions'

   ###*
     @param {wzk.dom.Dom} dom
@@ -62,9 +62,8 @@
     @param {Element} table
   ###
   decorate: (@table) ->
-    if goog.dom.dataset.has @table, wzk.ui.grid.Grid.DATA.SHOW_ACTIONS
-      if goog.dom.dataset.get(@table, wzk.ui.grid.Grid.DATA.SHOW_ACTIONS) is 'false'
-        @showActions = false
+    if @dom.cls wzk.ui.grid.Grid.CLS.ACTIONS, @table
+      @showActions = false

     @removeBody()
     paginatorEl = @dom.getParentElement(@table)?.querySelector '.paginator'


use forEach instead of jQuery

use forEach instead of jQuery

--- src/opensong-parser.coffee
+++ src/opensong-parser.coffee
@@ -64,7 +64,7 @@

         # clean Chord line from trailing white spaces
         chordArrCleaned = []
-        $.each chordArr, (index, value) ->
+        chordArr.forEach (value) ->
           m = /(\S*\s?)\s*/.exec(value)
           chordArrCleaned.push m[1]


#144: clear seems to work now

#144: clear seems to work now

--- app/notebook.coffee
+++ app/notebook.coffee
@@ -34,7 +34,6 @@
   cmd 'clear', 'Clears the notebook', ->
     clear_notebook @notebook

-
   notebook_content_type = 'application/x-lead-notebook'

   forwards = +1
@@ -116,9 +115,7 @@
     for cell in notebook.cells
       cell.active = false
     notebook.cells.length = 0
-    update_view notebook
-
-
+    focus_cell add_input_cell notebook
   cell_index = (cell) ->
     cell.notebook.cells.indexOf cell


no message

no message

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -15,3 +15,8 @@
     idname = robot.adapter.client.rtm.dataStore.getUserByName('raoka0000').id
     msg.send "テスト中だよ #{idname}"

+  robot.adapter.client.rtm.on 'raw_message', (msg) ->
+    message = JSON.parse msg
+    return unless message.type is 'hello'
+    msg.send "成功"
+


The longer the variable name, the more precise it is :clock1:

--- scripts/morning.coffee
+++ scripts/morning.coffee
@@ -11,7 +11,7 @@
 #   Hello or Good Day make hubot say hello to you back
 #   Good Morning makes hubot say good morning to you back
 ONE_DAY = 86400000
-lastTimeHaytHadToRespondToAHelloLikeAnAnnoyingAsshole = 0
+theLastTimeHaytSaidSomeStupidFuckingThingThatNoOneWantedToHear = 0
 hellos = [
     "Well hello there, %",
     "Hey %, Hello!",
@@ -29,14 +29,14 @@
 module.exports = (robot) ->
     robot.hear /(he(ll|rr)o|good( [d'])?ay(e)?)/i, (msg) ->
         now = Date.now()
-        if now - lastTimeHaytHadToRespondToAHelloLikeAnAnnoyingAsshole > ONE_DAY
-            lastTimeHaytHadToRespondToAHelloLikeAnAnnoyingAsshole = now
+        if now - theLastTimeHaytSaidSomeStupidFuckingThingThatNoOneWantedToHear > ONE_DAY
+            theLastTimeHaytSaidSomeStupidFuckingThingThatNoOneWantedToHear = now
             hello = msg.random hellos
             msg.send hello.replace "%", msg.message.user.name

     robot.hear /(^(good )?m(a|o)rnin(g)?)/i, (msg) ->
         now = Date.now()
-        if now - lastTimeHaytHadToRespondToAHelloLikeAnAnnoyingAsshole > ONE_DAY
-            lastTimeHaytHadToRespondToAHelloLikeAnAnnoyingAsshole = now
+        if now - theLastTimeHaytSaidSomeStupidFuckingThingThatNoOneWantedToHear > ONE_DAY
+            theLastTimeHaytSaidSomeStupidFuckingThingThatNoOneWantedToHear = now
             hello = msg.random mornings
             msg.send hello.replace "%", msg.message.user.name


Updated weather script

Updated weather script

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -35,7 +35,7 @@
           lat = result.results[0].geometry.location.lat
           lng = result.results[0].geometry.location.lng
           darkSkyMe msg, lat,lng , (darkSkyText) ->
-            response = "Weather for #{result.results[0].formatted_address}. #{darkSkyText}"
+            response = "Here's the weather for #{result.results[0].formatted_address}. #{darkSkyText}"
             msg.send response
         else
           msg.send "Couldn't find #{location}"
@@ -59,7 +59,7 @@
       else
         celsius = result.currently.temperature
         fahrenheit = celsius * (9 / 5) + 32
-      response = "Currently: #{result.currently.summary} (#{celsius}°C) "
-      response += "Today: #{result.hourly.summary} "
-      response += "Coming week: #{result.daily.summary}"
+      response = "It's currently: #{result.currently.summary} (#{celsius}°C) "
+      response += "Today, it's going to be #{result.hourly.summary} "
+      response += "Later in the week there's going to be #{result.daily.summary}"
       cb response


weather script

weather script

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -17,7 +17,7 @@

     rp(locationOptions)
       .then( (data) ->
-        if results[0].geometry
+        if data.results[0].geometry
           latitude = data.results[0].geometry.location.lat
           longitude = data.results[0].geometry.location.lng
           latlng = latitude + ',' + longitude


feat(link): nylas:// plugin links notify when turned on or off

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -73,10 +73,34 @@
   _onChangePluginState: (event, urlToOpen = "") =>
     {query} = url.parse(urlToOpen, true)
     disabled = NylasEnv.config.get('core.disabledPackages') ? []
+    turnedOn = []
+    turnedOff = []
     for name, state of query
-      if state is "off" and name not in disabled then disabled.push(name)
-      else if state is "on" then disabled = _.without(disabled, name)
+      continue if /-displayName/gi.test(name)
+      displayName = query["#{name}-displayName"] ? name
+      if state is "off" and name not in disabled
+        turnedOff.push(displayName)
+        if name not in disabled then disabled.push(name)
+      else if state is "on"
+        turnedOn.push(displayName)
+        disabled = _.without(disabled, name)
     NylasEnv.config.set('core.disabledPackages', disabled)
+    if NylasEnv.isMainWindow() then NylasEnv.focus()
+    if turnedOn.length > 0 then @_notifyPluginsChanged(turnedOn, "enabled")
+    if turnedOff.length > 0 then @_notifyPluginsChanged(turnedOff, "disabled")
+
+  _notifyPluginsChanged: (names, dir) =>
+    if names.length >= 2
+      last = names[names.length - 1]
+      names[names.length - 1] = "and #{last}"
+    has = if names.length is 1 then "has" else "have"
+    pluginText = if names.length is 1 then "Plugin" else "Plugins"
+    remote.dialog.showMessageBox(remote.getCurrentWindow(), {
+      type: 'info',
+      message: "#{pluginText} #{dir}",
+      detail: "#{names.join(", ")} #{has} been #{dir}"
+      buttons: ['Thanks'],
+    })

   _resolvePluginIdFor: (packageName, env) =>
     metadata = @loadedPackages[packageName]?.metadata


Comment out the disease questions

Comment out the disease questions

--- app/config.coffee
+++ app/config.coffee
@@ -56,30 +56,30 @@
           value: 'place'
         }]
       }]
-    disease:
-      label: 'Disease'
-      description: 'The central role of the Medical Officer was to identify and contain significant outbreaks of disease across the city. They gathered statistics on specific infectious diseases. These were usually presented in the form of tables, but they also described specific disease outbreaks in detail, identifying who they affected and where and how they were treated.'
-      tools: [{
-        label: 'Is a specific disease or type of disease named?'
-        value: 'type'
-        required: true
-        subtasks: [{
-          label: 'Who did it affect?'
-          value: 'who'
-        },{
-          label: 'How did it affect them?'
-          value: 'how'
-        },{
-          label: 'Who or what caused it?'
-          value: 'cause'
-        },{
-          label: 'Was anything done about it?'
-          value: 'action'
-        },{
-          label: 'Is there any place information?'
-          value: 'place'
-        }]
-      }]
+    # disease:
+ #      label: 'Disease'
+ #      description: 'The central role of the Medical Officer was to identify and contain significant outbreaks of disease across the city. They gathered statistics on specific infectious diseases. These were usually presented in the form of tables, but they also described specific disease outbreaks in detail, identifying who they affected and where and how they were treated.'
+ #      tools: [{
+ #        label: 'Is a specific disease or type of disease named?'
+ #        value: 'type'
+ #        required: true
+ #        subtasks: [{
+ #          label: 'Who did it affect?'
+ #          value: 'who'
+ #        },{
+ #          label: 'How did it affect them?'
+ #          value: 'how'
+ #        },{
+ #          label: 'Who or what caused it?'
+ #          value: 'cause'
+ #        },{
+ #          label: 'Was anything done about it?'
+ #          value: 'action'
+ #        },{
+ #          label: 'Is there any place information?'
+ #          value: 'place'
+ #        }]
+ #      }]
     food:
       label: 'Food'
       description: 'The health aspects of food production and sales came under the remit of the Medical Officer. His team of food inspectors investigated all aspects of the city’s food supply, from food importers and manufacturers to abattoirs, from butchers and bakers shops to street vendors. They were particularly concerned with food adulteration – identifying hidden substances that shouldn’t be there – and incidents of food poisoning.'


config changes for new trigger.io app

config changes for new trigger.io app

--- app/config.coffee
+++ app/config.coffee
@@ -3,21 +3,32 @@
   Config =
     env: 'development'
     host: 'http://localhost:3000/api/v1'
-    clientId: 'boorgle-iphone-localhost'
+    clientId: 'boorgle-web-mobile-localhost'
     oauthEndpoint: 'http://localhost:3000/oauth/'
+    oauthRedirectUri: 'http://localhost:9294'

-#  # Development against production
+  # Development against production
 #  Config =
 #    env: 'development'
 #    host: 'http://www.boorgle.com/api/v1'
 #    clientId: 'boorgle-iphone-localhost'
 #    oauthEndpoint: 'http://www.boorgle.com/oauth/'
+else if document.location.href.match(/file:\/\//)
+  # iPhone Native
+  Config =
+    env: 'ios'
+    host: 'http://www.boorgle.com/api/v1'
+    clientId: 'boorgle-iphone'
+    oauthEndpoint: 'http://www.boorgle.com/oauth/'
+    oauthRedirectUri: 'boorgle://index'
+
 else
   # Production
   Config =
     env: 'production'
     host: 'http://www.boorgle.com/api/v1'
-    clientId: 'boorgle-iphone'
+    clientId: 'boorgle-web-mobile'
     oauthEndpoint: 'http://www.boorgle.com/oauth/'
+    oauthRedirectUri: 'http://m.boorgle.com'

-module.exports = Config+module.exports = Config


Remove userEventId from article documents

--- server/updaters.coffee
+++ server/updaters.coffee
@@ -10,7 +10,7 @@
 Constants = require '/imports/constants.coffee'
 { regexEscape } = require '/imports/utils'

-DATA_VERSION = 3
+DATA_VERSION = 4
 AppMetadata = new Meteor.Collection('appMetadata')
 priorDataVersion = AppMetadata.findOne(property: "dataVersion")?.value

@@ -122,5 +122,9 @@
     $unset: articleCount: ''
     {multi: true}

+  Articles.find().forEach (article) ->
+    Articles.update _id: article._id,
+      $unset: userEventId: ''
+
   AppMetadata.upsert({property: "dataVersion"}, $set: {value: DATA_VERSION})
   console.log "database update complete"


Bugfix: ObjectComponent merge was quashing instead of merging.

--- src/ObjectComponent.coffee
+++ src/ObjectComponent.coffee
@@ -22,7 +22,7 @@
 			when @SET
 				if shallowEqual(state, action.payload) then state else Object.assign({}, action.payload)
 			when @MERGE
-				maybeNextState = Object.assign({}, action.payload)
+				maybeNextState = Object.assign({}, state, action.payload)
 				if shallowEqual(state, maybeNextState) then state else maybeNextState
 			else
 				state


curved edges look nicer

curved edges look nicer

--- VisualGraph.coffee
+++ VisualGraph.coffee
@@ -161,7 +161,10 @@
             radius = 10
             "M#{d.source.x},#{d.source.y}A#{radius},#{radius} 0 0,0 #{d.source.x},#{d.source.y - radius * 2}A#{radius},#{radius} 0 0,0 #{d.source.x},#{d.source.y}"
           else
-            "M#{d.source.x},#{d.source.y}L#{d.target.x},#{d.target.y}"
+            dx = d.target.x - d.source.x
+            dy = d.target.y - d.source.y
+            dr = Math.sqrt(dx * dx + dy * dy) * 2
+            "M#{ d.source.x },#{ d.source.y }A#{ dr },#{ dr } 0 0,1 #{ d.target.x },#{ d.target.y }"
         )

       @$gnodes


email: remaining post address changes

email: remaining post address changes

--- workers/emailsender/templates.coffee
+++ workers/emailsender/templates.coffee
@@ -16,7 +16,7 @@

      #{content}

-     Koding, Inc. 153 Townsend, Suite 9072 San Francisco, CA 94107
+     Koding, Inc. 358 Brannan Street, San Francisco, CA 94107
     """

   htmlTemplate : (content)->
@@ -81,7 +81,7 @@

-                #{link "https://koding.com", "Koding"}, Inc. 153 Townsend, Suite 9072 San Francisco, CA 94107
+                #{link "https://koding.com", "Koding"}, Inc. 358 Brannan Street, San Francisco, CA 94107


automated random responses

automated random responses

--- brains.coffee
+++ brains.coffee
@@ -3,6 +3,9 @@
 fs      = require 'fs'

 module.exports =
+    # returns a random one from an array of strings
+    anyOf: (answers) -> answers[ Math.floor(Math.random() * (answers.length+.99)) ]
+    # detection patterns
     patterns: {}
     addPattern: (name, p) ->
         if not @patterns[name]?
@@ -60,10 +63,16 @@
             brain.wolframSearch command, (answer) ->
                 return respond answer if answer
                 console.log "I couldn't understand #{command}"  if program.verbose
-                answers = [
-                    "I don't know what you're talking about"
-                    "I don't understand"
+                respond brain.anyOf [
+                    "I don't know what you're talking about."
+                    "I have no idea what you are talking about."
+                    "What the fuck is wrong with you?"
+                    "I wish I could understand you."
+                    "I don't understand."
                     "What do you mean?"
-                    "WTF"
-                ]
-                respond answers[ Math.floor(Math.random() * (answers.length+.99)) ]+                    "WTF!"
+                    "There's only so much I can do."
+                    "You're annoying."
+                    "Leave me alone."
+                    "I don't feel like it."
+                ]


API for confirmation of actions

API for confirmation of actions

--- brains.coffee
+++ brains.coffee
@@ -5,6 +5,10 @@
 module.exports =
     # returns a random one from an array of strings
     anyOf: (answers) -> answers[ Math.floor(Math.random() * (answers.length+.99)) ]
+    # confirm some action
+    queued: []
+    confirm: (obj) ->
+        @queued.push obj #{ confirmMessage, cancelMessage, fn, args }
     # detection patterns
     patterns: {}
     addPattern: (name, p) ->
@@ -50,6 +54,19 @@
         command = command.trim().replace /\s?\<[^>]+\>/g, ''

         matched = do =>
+            # confirmations
+            if brain.queued.length > 0
+                console.log command
+                if /^yes|yeah|sure|ok(\s\!\.;)*$/i.test command
+                    service = brain.queued.pop()
+                    florinda.say service.confirmMessage
+                    service.fn?.apply null, service.args
+                    return true
+                else if /^no|nope|not|cancel|stop(\s\!\.;)*$/i.test command
+                    service = brain.queued.pop()
+                    florinda.say service.cancelMessage or "Canceled."
+                    return true
+
             for own name, pattern of @patterns
                 for match, i in pattern.match
                     if matches = command.match match


user query changefeeds: send each feed update in a separate node.js browser tick to greatly reduce the chances of BLOCKING the webserver

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -3279,7 +3279,28 @@
                         cb(); return
                     # no errors -- setup changefeed now
                     changefeed_id = opts.changes.id
-                    changefeed_cb = opts.changes.cb
+
+                    # IMPORTANT: We call changefeed_cb only once per tick;
+                    # feed.each below could call 300 times (say) synchronously since we squash updates;
+                    # if each of the values took 10ms blocking to json-ify, that would be 3000ms in which
+                    # the node.js process is completely BLOCKED.
+                    # By using process.nextTick, we let node.js handle other work
+                    # during this time.
+                    _changefeed_cb_queue = []
+                    changefeed_cb = (err, x) ->
+                        if _changefeed_cb_queue.length > 0
+                            _changefeed_cb_queue.push([err, x])
+                            #winston.debug("USING CHANGEFEED QUEUE: #{_changefeed_cb_queue.length}")
+                            return
+                        _changefeed_cb_queue.push([err, x])
+                        f = () ->
+                            #winston.debug("PROCESSING CHANGEFEED QUEUE: #{_changefeed_cb_queue.length}")
+                            z = _changefeed_cb_queue.shift()
+                            opts.changes.cb(z[0], z[1])
+                            if _changefeed_cb_queue.length > 0
+                                process.nextTick(f)
+                        process.nextTick(f)
+
                     winston.debug("FEED -- setting up a feed with id #{changefeed_id}")
                     db_query_no_opts.changes(includeStates: false, squash:.2).run (err, feed) =>
                         if err


sync'd db: fix (really implement) a major important case involving change notification, which was VERY noticeably broken with Jupyter2 (but wasn't at all obvious with other editors yet).  It is very critical that this is live in SMC soon too, incidentally.

--- src/smc-util/db-doc.coffee
+++ src/smc-util/db-doc.coffee
@@ -145,11 +145,13 @@
                     return
                 return
         @size = @_everything.size
-        @_changes ?= immutable.Set()
+        if not @_changes?
+            @reset_changes()

     reset_changes: =>
-        @_changes = immutable.Set()
-
+        @_changes = {changes: immutable.Set(), from_db:@}
+
+    # Returns object {changes: an immutable set of primary keys, from_db: db object where change tracking started}
     changes: =>
         return @_changes

@@ -222,7 +224,7 @@
         {where, set, obj} = @_parse(obj)
         # console.log("set #{misc.to_json(set)}, #{misc.to_json(where)}")
         matches = @_select(where)
-        changes = @_changes
+        {changes} = @_changes
         n = matches?.first()
         # TODO: very natural optimization would be be to fully support and use obj being immutable
         if n?
@@ -247,7 +249,7 @@
             if not before.equals(record)
                 # there was an actual change, so update; doesn't change anything involving indexes.
                 changes = changes.add(@_primary_key_cols(record))
-                return new DBDoc(@_primary_keys, @_string_cols, @_records.set(n, record), @_everything, @_indexes, changes)
+                return new DBDoc(@_primary_keys, @_string_cols, @_records.set(n, record), @_everything, @_indexes, {changes:changes, from_db:@_changes.from_db})
             else
                 return @
         else
@@ -274,7 +276,7 @@
                     else
                         matches = immutable.Set([n])
                     indexes = indexes.set(field, index.set(k, matches))
-            return new DBDoc(@_primary_keys, @_string_cols, records, everything, indexes, changes)
+            return new DBDoc(@_primary_keys, @_string_cols, records, everything, indexes, {changes:changes, from_db:@_changes.from_db})

     delete: (where) =>
         if misc.is_array(where)
@@ -287,12 +289,12 @@
         if @_everything.size == 0
             # no-op -- no data so deleting is trivial
             return @
-        changes = @_changes
+        {changes} = @_changes
         remove = @_select(where)
         if remove.size == @_everything.size
             # actually deleting everything; easy special cases
             changes = changes.union(@_records.filter((record)=>record?).map(@_primary_key_cols))
-            return new DBDoc(@_primary_keys, @_string_cols, undefined, undefined, undefined, changes)
+            return new DBDoc(@_primary_keys, @_string_cols, undefined, undefined, undefined, {changes:changes, from_db:@_changes.from_db})

         # remove matches from every index
         indexes = @_indexes
@@ -321,7 +323,7 @@

         everything = @_everything.subtract(remove)

-        return new DBDoc(@_primary_keys, @_string_cols, records, everything, indexes, changes)
+        return new DBDoc(@_primary_keys, @_string_cols, records, everything, indexes, {changes:changes, from_db:@_changes.from_db})

     # Returns immutable list of all matches
     get: (where) =>
@@ -453,7 +455,26 @@
             i += 2
         return db

-
+    # Return immutable set of primary keys of records that change in going from @ to other.
+    changed_keys: (other) =>
+        if @_records == other?._records   # identical
+            return immutable.Set()
+        t0 = immutable.Set(@_records)
+        if not other?
+            return t0.map(@_primary_key_cols)
+
+        t1 = immutable.Set(other._records)
+
+        # Remove the common intersection -- nothing going on there.
+        # Doing this greatly reduces the complexity in the common case in which little has changed
+        common = t0.intersect(t1).add(undefined)
+        t0 = t0.subtract(common)
+        t1 = t1.subtract(common)
+
+        # compute the key parts of t0 and t1 as sets
+        k0 = t0.map(@_primary_key_cols)
+        k1 = t1.map(@_primary_key_cols)
+        return k0.union(k1)

 class Doc
     constructor: (@_db) ->
@@ -566,7 +587,7 @@
         @_check()
         return @_doc.get_read_only()

-    _on_change: () =>
+    _on_change: =>
         if not @_doc?
             # This **can** happen because @_on_change is actually throttled, so
             # definitely will sometimes get called one more time,
@@ -575,10 +596,21 @@
             # is already closed and nobody is listening.
             # See https://github.com/sagemathinc/smc/issues/1829
             return
-        changes = @_doc.get_doc().changes()
-        @_doc.get_doc().reset_changes()
+        db = @_doc.get_doc()._db
+        if not @_last_db?
+            # first time ever -- just get all keys
+            changes = db.changed_keys()
+        else
+            # may be able to use tracked changes...
+            {changes, from_db} = @_doc.get_doc().changes()
+            @_doc.get_doc().reset_changes()
+            if from_db != @_last_db
+                # NOPE: have to compute the hard (but rock solid and accurate) way.
+                changes = db.changed_keys(@_last_db)
+
         if changes.size > 0 or @_first_change_event  # something actually probably changed
             @emit('change', changes)
+        @_last_db = db
         delete @_first_change_event

     close: () =>
@@ -720,12 +752,14 @@
         @_check()
         @_doc.set_doc(@_doc.undo())
         @_doc.save()
+        @_on_change()
         return

     redo: =>
         @_check()
         @_doc.set_doc(@_doc.redo())
         @_doc.save()
+        @_on_change()
         return

     exit_undo_mode: =>


working on cursors schema...

working on cursors schema...

--- src/smc-util/schema.coffee
+++ src/smc-util/schema.coffee
@@ -828,6 +828,7 @@
 schema.syncstrings.project_query = schema.syncstrings.user_query     #TODO -- will be different!

 # TODO -- currently no security/auth
+# TODO: need an index!
 schema.patches =
     primary_key: 'id'  # this is a compound primary key as an array -- [string_id, time, user_id]
     fields:
@@ -850,6 +851,29 @@
                 patch : true

 schema.patches.project_query = schema.patches.user_query     #TODO -- will be different!
+
+schema.cursors =
+    primary_key: 'id'  # this is a compound primary key as an array -- [string_id, user_id]
+    fields:
+        id   : true    # [string_id, user_id]
+        pos  : true    # [{x:?,y:?}, ...]    <-- positions of user_id's cursor(s)
+        time : true    # when these cursor positions were sent out
+    user_query:
+        get :
+            all :  # if input id in query is [string_id, t], this gets patches with given string_id and time >= t
+                cmd  : 'getAll'
+                args : (obj, db) -> [[obj.id[0], obj.id[1]]]
+            fields :
+                id   : 'null'   # [string_id, user_id]
+                pos  : null
+                time : null
+        set :
+            fields :
+                id    : true
+                patch : true
+            required_fields :
+                id    : true
+                patch : true

 # Client side versions of some db functions, which are used, e.g., when setting fields.
 sha1 = require('sha1')


组卷JS review

组卷JS review

--- app/assets/javascripts/question_bank/test_papers.coffee
+++ app/assets/javascripts/question_bank/test_papers.coffee
@@ -1,6 +1,5 @@
 class NewTestPaper
   constructor: (@$el, params) ->
-    console.log 'NewTestPaper'
     @_init()

   _init: ->
@@ -36,7 +35,6 @@
       that.$modal_questions_selector.modal('show')

     @$el.on 'click', '.section_move_up', ->
-      console.log 'section_move_up'
       $this = jQuery(this)
       $section = $this.parent().parent().parent()

@@ -45,15 +43,12 @@
       if index > 0
         $prev = jQuery($sections.get(index - 1))
         $section.insertBefore($prev) if $prev and !$prev.hasClass('empty')
-        #that.reset_question_positions($question)
-
-        #$section.insertBefore($section.prev()) if $section.prev().length > 0
+
         # position 需要做调整
         that.reset_section_positions()
         that.reset_section_title()

     @$el.on 'click', '.section_move_down', ->
-      console.log 'section_move_down'
       $this = jQuery(this)
       $section = $this.parent().parent().parent()

@@ -70,7 +65,6 @@
         that.reset_section_title()

     @$el.on 'click', '.section_destroy', ->
-      console.log 'section_destroy'
       $this = jQuery(this)
       $section = $this.parent().parent().parent()

@@ -137,7 +131,6 @@
     @$el.on 'click', '.question_destroy', ->
       $this = jQuery(this)
       $question = $this.parent().parent()
-      console.log "is_edit: #{that.is_edit}"
       if that.is_edit
         # 修改时， 设置_destroy 而非单纯移除  ????
         $question.addClass('hidden')
@@ -162,7 +155,6 @@
     @$modal_questions_selector.on 'click', '.button-questions-selector', =>
       $question = @$template_question.clone()
       $section_questions = @$control_section.find('.section_questions')
-      #$section_questions.children().remove()
       section_index = @$el.find('.sections .section').index(@$control_section)

       @$modal_questions_selector.find(':checked').each (index)->
@@ -195,22 +187,9 @@

       section_index = @$el.find('.sections .section').length

-      #@$modal_questions_selector.find(':checked').each (index)->
-        #$this = jQuery(this)
-        #$label_question = $this.parent()
-        #question = $label_question.find('span').html()
-
-        ## haml 不知道为什么会转为-
-        ##question_id = $label_question.data('question_id')
-        #question_id = $label_question.data('question-id')
-
       str_template = $section.html().replace /{{section_index_plus}}/g , section_index + 1

       str_template = str_template.replace /{{section_index}}/g , section_index
-      #str_template = str_template.replace /{{question_index}}/g , index
-
-      #str_template = str_template.replace /{{question_id}}/g , question_id
-      #str_template = str_template.replace /{{position}}/g , index

       $template = jQuery(str_template).removeClass('hidden')
       @$el.find('.sections').append($template)
@@ -243,7 +222,6 @@
       score = Number $section.find('.section_score').val()
       if score != NaN and score > 0
         total += score * $section.find('ol li:not(.hidden)').length
-    console.log total
     total

   get_questions: (params) ->
@@ -274,7 +252,6 @@
           $question_selector.append($template)

   get_random_questions: (params) ->
-    console.log params
     {$section, random_count} = params
     # ajax get_random_questions
     kind = $section.find('.kind').val()
@@ -290,13 +267,10 @@
         type: 'random'
         per: random_count
       success: (res) =>
-        #console.log res
         section_index = @$el.find('.sections .section').index($section)
         $section_questions = $section.find('.section_questions')
         question_index_prefix = $section_questions.find('li').length
         for question, index in res
-          console.log question
-          console.log index
           str_template = @$template_question.html()

           str_template = str_template.replace /{{content}}/g , question.content
@@ -314,11 +288,8 @@

   reset_section_title: ->
-    console.log 'reset_section_title'
     @$el.find('.sections .section:not(.hidden)').each (index)->
       $this1 = jQuery(this)
-      console.log index
-      console.log $this1
       $this1.find('h3').html("第#{index+1}大题")

   reset_question_positions: ($question)->
@@ -326,14 +297,11 @@
         $this1 = jQuery(this)
         $this1.html $this1.html().replace(/(section_questions_attributes\]\[)\d+(\])/g , "$1#{index}$2")
         $this1.find('.question_position').val(index)
-        console.log $this1.find('.question_position')
-        console.log index

   reset_section_positions: ()->
     that = this
     @$el.find('.sections .section').each (index)->
       $section = jQuery(this)
-      console.log $section
       # 替换地下所有的[x]
       $section.html $section.html().replace(/(sections_attributes\]\[)\d+\]/g, "$1#{index}]")
       $section.find('.section_position').val(index)
@@ -342,11 +310,6 @@
         origin_index = $section.data('origin-index')
         $id = $section.parent().find("#test_paper_sections_attributes_#{origin_index}_id")
         $id.prop 'name', $id.prop('name').replace(/(sections_attributes\]\[)\d+\]/g, "$1#{index}]") if $id.length > 0
-      #$this1.val(index)
-      #$this1.html $this1.html().replace(/(section_questions_attributes\]\[)\d+(\])/g , "$1#{index}$2")
-      #$this1.find('.question_position').val(index)
-      #console.log $this1.find('.question_position')
-      #console.log index

 jQuery(document).on 'ready page:load', ->
   new NewTestPaper(jQuery('.form-test_paper'), {}) if jQuery('.form-test_paper').length > 0


webapp: hotfix for #2542 -- not sure what this does, but it breaks for iframe embedding.

--- src/smc-webapp/last.coffee
+++ src/smc-webapp/last.coffee
@@ -63,7 +63,8 @@
 $("#smc-startup-banner")?.remove()
 $('#smc-startup-banner-status')?.remove()
 $ ->
-    $(parent).trigger('initialize:frame')
+    try
+        $(parent).trigger('initialize:frame')

     # mathjax startup. config is set above, now we dynamically insert the mathjax script URL
     mjscript = document.createElement("script")


Update javascript for latest Spree.

Update javascript for latest Spree.

--- app/assets/javascripts/store/supplier_address.js.coffee
+++ app/assets/javascripts/store/supplier_address.js.coffee
@@ -1,48 +1,55 @@
-$ ->
-  if ($ '#supplier-address').is('*')
-    ($ '#supplier-address').validate()
+Spree.ready ($) ->
+  if ($ '#new_supplier').is('*')
+    ($ '#new_supplier').validate()

-    country_from_region = (region) ->
-      ($ 'p#' + region + 'country' + ' span#' + region + 'country-selection :only-child').val()
+    getCountryId = (region) ->
+      $('p#' + region + 'country select').val()

-    get_states = (region) ->
-      state_mapper[country_from_region(region)]
+    updateState = (region) ->
+      countryId = getCountryId(region)
+      if countryId?
+        unless Spree.Checkout[countryId]?
+          $.get Spree.routes.states_search + "/?country_id=#{countryId}", (data) ->
+            Spree.Checkout[countryId] =
+              states: data.states
+              states_required: data.states_required
+            fillStates(Spree.Checkout[countryId], region)
+        else
+          fillStates(Spree.Checkout[countryId], region)

-    get_states_required = (region) ->
-      states_required_mapper[country_from_region(region)]
+    fillStates = (data, region) ->
+      statesRequired = data.states_required
+      states = data.states

-    update_state = (region) ->
-      states = get_states(region)
-      states_required  = get_states_required(region)
+      statePara = ($ 'p#' + region + 'state')
+      stateSelect = statePara.find('select')
+      stateInput = statePara.find('input')
+      stateSpanRequired = statePara.find('state-required')
+      if states.length > 0
+        selected = parseInt stateSelect.val()
+        stateSelect.html ''
+        statesWithBlank = [{ name: '', id: ''}].concat(states)
+        $.each statesWithBlank, (idx, state) ->
+          opt = ($ document.createElement('option')).attr('value', state.id).html(state.name)
+          opt.prop 'selected', true if selected is state.id
+          stateSelect.append opt

-      state_para = ($ 'p#' + region + 'state')
-      state_select = state_para.find('select')
-      state_input = state_para.find('input')
-      state_span_required = state_para.find('state-required')
-      if states
-        selected = parseInt state_select.val()
-        state_select.html ''
-        states_with_blank = [ [ '', '' ] ].concat(states)
-        $.each states_with_blank, (pos, id_nm) ->
-          opt = ($ document.createElement('option')).attr('value', id_nm[0]).html(id_nm[1])
-          opt.prop 'selected', true if selected is id_nm[0]
-          state_select.append opt
-
-        state_select.prop('disabled', false).show()
-        state_input.hide().prop 'disabled', true
-        state_span_required.show()
+        stateSelect.prop('disabled', false).show()
+        stateInput.hide().prop 'disabled', true
+        statePara.show()
+        stateSpanRequired.show()
       else
-        state_select.hide().prop 'disabled', true
-        state_input.show()
-        if states_required
-          state_span_required.show()
+        stateSelect.hide().prop 'disabled', true
+        stateInput.show()
+        if statesRequired
+          stateSpanRequired.show()
         else
-          state_input.val ''
-          state_span_required.hide()
-        state_para.toggle(!!states_required)
-        state_input.prop('disabled', !states_required)
+          stateInput.val ''
+          stateSpanRequired.hide()
+        statePara.toggle(!!statesRequired)
+        stateInput.prop('disabled', !statesRequired)

     ($ 'p#ccountry select').change ->
-      update_state 'c'
+      updateState 'c'

-    update_state 'c'
+    updateState 'c'


fixes prompt launching with new option semantics

--- src/steroids/Prompt.coffee
+++ src/steroids/Prompt.coffee
@@ -77,7 +77,7 @@
           console.log "Starting iOS Simulator of type `#{deviceType}`"

           steroidsCli.simulator.run
-            type: deviceType
+            deviceType: deviceType

         when "qr", "qr-code", "qrcode"
           QRCode = require "./QRCode"


bump up precision on compressed image test

--- test/specs/compressedimage.coffee
+++ test/specs/compressedimage.coffee
@@ -3,7 +3,7 @@
 describe "FITS CompressedImage", ->

   it 'can read a FITS compressed image', ->
-    precision = 4
+    precision = 6

     ready = false

@@ -30,12 +30,12 @@
       image.getExtent(pixels)

       expect(image.min).toBeCloseTo(-2.935214, precision)
-      expect(image.max).toBeCloseTo(1273.8491, precision)
+      expect(image.max).toBeCloseTo(1273.849121, precision)

       expect(image.getPixel(pixels, 0, 0)).toBeCloseTo(0.249601, precision)
       expect(image.getPixel(pixels, 400, 0)).toBeCloseTo(0.428947, precision)
       expect(image.getPixel(pixels, 400, 400)).toBeCloseTo(0.358678, precision)
-      expect(image.getPixel(pixels, 0, 400)).toBeCloseTo(1.29172, precision)
+      expect(image.getPixel(pixels, 0, 400)).toBeCloseTo(1.2917231, precision)

       # ... and a few other random pixels
       expect(image.getPixel(pixels, 33, 205)).toBeCloseTo(0.939594, precision)


test(test-runner-test.coffee): Migrate to Lodash

Converted underscore.clone() to lodash.cloneDeep(). Added another header to test to exercise array
portion of code.

--- test/unit/test-runner-test.coffee
+++ test/unit/test-runner-test.coffee
@@ -1,10 +1,11 @@
 chai = require 'chai'
 sinon = require 'sinon'
 sinonChai = require 'sinon-chai'
-_ = require 'underscore'
+_ = require 'lodash'
 mocha = require 'mocha'
 mute = require 'mute'
 proxyquire = require('proxyquire').noCallThru()
+pkg = require '../../package'

 TestFactory = require '../../lib/test'

@@ -450,6 +451,7 @@

         header =
           key: 'value'
+          'X-AbaoVersion': pkg.version

         options =
           server: "#{SERVER}"
@@ -458,7 +460,7 @@
         runner = new TestRunner options, ''
         sinon.stub runner.mocha, 'run'
           .callsFake (callback) ->
-            receivedTest = _.clone(test)
+            receivedTest = _.cloneDeep(test)
             callback()

         runner.run [test], hooksStub, done


Translate about.cson into Japanese

Translate about.cson into Japanese

--- def/ja/about.cson
+++ def/ja/about.cson
@@ -1,15 +1,15 @@
 About:
-  tabTitle: "About"
-  releaseNotes: "Release Notes"
-  newUpdate: "New Update"
-  upToDate: "Atom is up to date!"
-  checkingForUpdates: "Checking for updates..."
-  checkUpdatesNow: "Check now"
-  automaticDownloadUpdates: "Automatic download updates"
-  restartInstall: "Restart and install"
-  license: "License"
-  termsOfUse: "Terms of Use"
-  with: " with "
-  by: " by "
-  andTheAwesome: "And the awesome "
-  atomCommunity: "Atom Community"
+  tabTitle: "Atom について"
+  releaseNotes: "リリースノート"
+  newUpdate: "新しいアップデート"
+  upToDate: "Atom は最新の状態です！"
+  checkingForUpdates: "アップデートを確認しています…"
+  checkUpdatesNow: "今すぐ確認"
+  automaticDownloadUpdates: "アップデートを自動的にダウンロード"
+  restartInstall: "再開してインストール"
+  license: "ライセンス"
+  termsOfUse: "利用規約"
+  with: " に "
+  by: " を込めて "
+  andTheAwesome: "そして素晴らしい "
+  atomCommunity: "Atom コミュニティより"


Support multiple customViews in same level in JContextMenu

--- client/Framework/components/contextmenu/contextmenutreeviewcontroller.coffee
+++ client/Framework/components/contextmenu/contextmenutreeviewcontroller.coffee
@@ -11,7 +11,7 @@

     for title, options of items
       id = null
-      if title is "customView"
+      if (title.indexOf "customView") is 0
         newItem = { type : 'customView', parentId : pId, view : options }
         results.push newItem
         continue


return empty cursor list if user isn't admin

--- admin/admin.coffee
+++ admin/admin.coffee
@@ -1,5 +1,4 @@
 # Server admin code
-
 isAdmin = (userId) -> userId? and Meteor.users.findOne(userId)?.admin

 # Only admin gets server facts
@@ -29,7 +28,7 @@
 # Admin users - needs to update if group updates
 # Return all experiments unless in a group
 Meteor.publish "tsAdminState", (groupId) ->
-  return null unless isAdmin(@userId)
+  return [] unless isAdmin(@userId)

   cursors = [ Meteor.users.find({}, userFindOptions) ]

@@ -49,7 +48,7 @@
 # Helper publish function to get users for experiments that have ended.
 # Necessary to watch completed experiments.
 Meteor.publish "tsGroupUsers", (groupId) ->
-  return null unless isAdmin(@userId)
+  return [] unless isAdmin(@userId)
   sub = this
   exp = Experiments.findOne(groupId)
   return unless exp
@@ -68,7 +67,7 @@
   sub.onStop -> subHandle.stop()

 Meteor.publish "tsGroupLogs", (groupId, limit) ->
-  return null unless isAdmin(@userId)
+  return [] unless isAdmin(@userId)

   return Logs.find({_groupId: groupId}, {
       sort: {_timestamp: -1},


add atomic update test for flatMap

add atomic update test for flatMap

--- spec/specs/main.coffee
+++ spec/specs/main.coffee
@@ -268,6 +268,13 @@
            b = a.map((x) -> x).filter(true)
            a.combine(b, (x, y) -> x + y)
         [2, 4])
+    describe "when flatMap is involved (spawning synchronous streams)", ->
+      expectPropertyEvents(
+        ->
+           a = series(1, [1, 2]).toProperty()
+           b = a.flatMap((x) -> Bacon.once(x))
+           a.combine(b, (x, y) -> x + y)
+        [2, 4], unstable)
     describe "when root property is based on combine*", ->
       expectPropertyEvents(
         ->


Fix remove taxon js code

Fix remove taxon js code

[Fixes #3136][Fixes #3140]

--- backend/app/assets/javascripts/admin/taxonomy.js.coffee
+++ backend/app/assets/javascripts/admin/taxonomy.js.coffee
@@ -51,8 +51,8 @@
     error: handle_ajax_error

 handle_delete = (e, data) ->
-  last_rollback = data.rlb
-  node = data.rslt.ob
+  last_rollback = data.rlbk
+  node = data.rslt.obj
   delete_url = base_url.clone()
   delete_url.setPath delete_url.path() + '/' + node.attr("id")
   jConfirm Spree.translations.are_you_sure_delete, Spree.translations.confirm_delete, (r) ->


let background layer listen to orientation changes

--- framer/BackgroundLayer.coffee
+++ framer/BackgroundLayer.coffee
@@ -15,6 +15,7 @@
 		@sendToBack()
 		@layout()
 		@_context.domEventManager.wrap(window).addEventListener("resize", @layout)
+		Framer.Device.on("change:orientation", @layout)

 	layout: =>
 		if @parent


'npm run bundle' bug with no dir fixed

'npm run bundle' bug with no dir fixed

--- bundle.coffee
+++ bundle.coffee
@@ -1,4 +1,4 @@
-fs = require 'fs'
+fs = require 'fs-extra'
 Mustache = require 'mustache'
 coffee = require 'coffee-script'

@@ -8,6 +8,8 @@
 	assert: require 'neft-assert'

 bundle = require 'neft-bundle-builder'
+
+fs.ensureDir './cli/bundle'

 createBundle = (opts, callback) ->
 	bundle {


Longer delay

Longer delay

--- src/coffee/ui/topui.coffee
+++ src/coffee/ui/topui.coffee
@@ -40,7 +40,7 @@
         ui.topUI._tooltipShowTimeout = setTimeout =>
           $tooltip.fadeIn(50)
           ui.topUI._$tooltipVisible = $tooltip
-        , 400
+        , 500

   unhover:
     "slider knob": ->


rename Creation.attributes to Creation.attributeMods

This more closely reflects the actual use and it avoids confusing with Character.attributes.

--- src/common/creation.coffee
+++ src/common/creation.coffee
@@ -43,7 +43,7 @@
       magic: null
       skills: null
       resources: null
-    @attributes  = {}
+    @attributeMods  = {}
     defaultPoints = -> available: 0, used: 0
     @points =
       attributes: defaultPoints()
@@ -107,7 +107,7 @@
     # re-apply point mods
     @points.attributes.used = 0
     @points.specialAttributes.used = 0
-    for name, value of @attributes
+    for name, value of @attributeMods
       @modAttribute name, value, true

@@ -156,7 +156,7 @@
       @effects.add attrPath, new basetypes.ModValue howMuch

     if not reset
-      @attributes[attrName] = (@attributes[attrName] || 0) + howMuch
+      @attributeMods[attrName] = (@attributeMods[attrName] || 0) + howMuch

     if attrName in core.attributes.special
       @points.specialAttributes.used += howMuch
@@ -168,7 +168,7 @@
     effect = @effects.get attrPath
     return if not effect
     @effects.remove attrPath, effect
-    delete @attributes[attrName]
+    delete @attributeMods[attrName]
     if attrName in core.attributes.special
       @points.specialAttributes.used -= effect.mod
     else
@@ -209,7 +209,7 @@
     name: @char.name
     magicType: @char.magicType?.name || null
     resonanceType: @char.resonanceType?.name || null
-    attributes: @attributes
+    attributeMods: @attributeMods

   applyState = (that, state) ->
     that.setMetatype state.metatype if state.metatype
@@ -218,7 +218,7 @@
     that.setMagicType state.magicType
     that.setResonanceType state.resonanceType
     that.char.name = state.name
-    for name, value of state.attributes
+    for name, value of state.attributeMods
       that.modAttribute name, value


Client: Activity:  Added encodeString method to fix embedbox encoding problems

--- client/activity/lib/components/embedboxlinkcontent/index.coffee
+++ client/activity/lib/components/embedboxlinkcontent/index.coffee
@@ -18,18 +18,26 @@
     </a>

+  encodeString: (str) ->
+
+    p = document.createElement "p"
+    p.textContent = str;
+    return p.innerHTML
+
+
   renderDescription: ->

     { link_url, link_embed } = @props.data

-    description = if link_embed.description?
-    then "#{Encoder.XSSEncode(link_embed.description).substring 0, 128}..."
-    else ''
+    description = ''
+
+    if link_embed.description? and typeof link_embed.description is 'string'
+      description = "#{@encodeString(link_embed.description).substring 0, 128}..."

     <a href={link_url} target='_blank' className='EmbedBoxLinkContent-description'>
       { description }
     </a>
-
+

   renderProvider: ->


Move windowController assignment to constructor

--- client/app/MainApp/maincontroller/mainviews/contentpanel.coffee
+++ client/app/MainApp/maincontroller/mainviews/contentpanel.coffee
@@ -8,6 +8,7 @@
     @bindTransitionEnd()
     @listenWindowResize()
     @state = 'full'
+    @windowController or= @getSingleton 'windowController'

     @navOpenedOnce = if KD.isLoggedIn() then yes else no

@@ -31,7 +32,6 @@
   changeLayout:(options)->

     {type, hideTabs, name}    = options
-    @windowController or= @getSingleton 'windowController'

     @unsetClass 'full develop social'
     @adjustShadow hideTabs


[client][app][main][maincontroller] fixed existence checking logic

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -199,7 +199,9 @@
               modal.modalTabs.forms.BlockUser.buttons.blockUser.showLoader()
               blockingTime = calculateBlockingTime modal.modalTabs.forms.BlockUser.inputs.duration.getValue()
               @blockUser data.originId, blockingTime, (err, res)->
-                if err then warn err
+                if err
+                  warn err
+                  modal.modalTabs.forms.BlockUser.buttons.blockUser.hideLoader()
                 else
                   modal.destroy()
                   new KDNotificationView title : "User is blocked!"


homepage as an option to maincontroller

homepage as an option to maincontroller

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -6,7 +6,8 @@

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

-    options.failWait = 5000 # duration in miliseconds to show a connection failed modal
+    options.failWait  = 5000            # duration in miliseconds to show a connection failed modal
+    options.startPage = "Activity"      # start page path

     super options, data

@@ -89,8 +90,7 @@
     mainView = @mainViewController.getView()
     @loginScreen.slideUp =>
       @mainViewController.sidebarController.accountChanged account
-      appManager.openApplication "Activity", yes
-      # appManager.openApplication "Demos", yes
+      appManager.openApplication @getOptions().startPage, yes
       @mainViewController.getView().decorateLoginState yes

   goToPage:(pageInfo)=>


KodingClient: modify  button options according to given message

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -79,23 +79,49 @@

         k.connect()

-        showErrorModal = (err, callback)->
+        showErrorModal = (message, callback)->
           modal = new KDBlockingModalView
-            title        : "Want to try again?"
-            content      : "
#{err.message}
"
+            title        : "Kite Registration"
+            content      : "
#{message}
"
+            height       : "auto"
+            overlay      : yes
+            buttons      : {}
+
+          Retry      =
+            style    : "modal-clean-gray"
+            callback : ->
+              modal.destroy()
+              callback?()
+
+          Cancel     =
+            style    : "modal-clean-red"
+            callback : ->
+              modal.destroy()
+              clear()
+
+          Ok         =
+            style    : "modal-clean-gray"
+            callback : ->
+              modal.destroy()
+              clear()
+
+          if /^Authentication\ already\ established/.test message
+            modal.setButtons {Ok}, yes
+          else
+            modal.setButtons {Retry, Cancel}, yes
+
+        showSuccessfulModal = (message, callback)->
+          modal = new KDBlockingModalView
+            title        : "Kite Registration"
+            content      : "
#{message}
"
             height       : "auto"
             overlay      : yes
             buttons      :
-              Retry      :
-                style    : "modal-clean-gray"
+              Ok         :
+                style    : "modal-clean-green"
                 callback : ->
                   modal.destroy()
                   callback?()
-              Cancel      :
-                style    : "modal-clean-red"
-                callback : ->
-                  modal.destroy()
-                  clear()

         handleInfo = (err, result)=>
           KD.remote.api.JKodingKey.registerHostnameAndKey {


Login: LoginInputView: Require loadsh module and set indentations

--- client/landing/site.landing/coffee/team/tabs/teamjointab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamjointab.coffee
@@ -1,10 +1,11 @@
-articlize                      = require 'indefinite-article'
-MainHeaderView                 = require './../../core/mainheaderview'
-TeamJoinByLoginForm            = require './../forms/teamjoinbyloginform'
-TeamJoinBySignupForm           = require './../forms/teamjoinbysignupform'
-TeamJoinWithInvitedAccountForm = require './../forms/teamjoinwithinvitedaccountform'
+_                               = require 'lodash'
+articlize                       = require 'indefinite-article'
+MainHeaderView                  = require './../../core/mainheaderview'
+TeamJoinByLoginForm             = require './../forms/teamjoinbyloginform'
+TeamJoinBySignupForm            = require './../forms/teamjoinbysignupform'
+TeamJoinWithInvitedAccountForm  = require './../forms/teamjoinwithinvitedaccountform'
+TeamLoginAndCreateTabForm       = require './../forms/teamloginandcreatetabform'

-TeamLoginAndCreateTabForm = require './../forms/teamloginandcreatetabform'

 module.exports = class TeamJoinTab extends KDTabPaneView


minor fix for sonos

minor fix for sonos

--- roc_dashboard/widgets/text/text.coffee
+++ roc_dashboard/widgets/text/text.coffee
@@ -12,7 +12,7 @@
   @accessor 'album_art?', ->
     if @get('music')
       url = @get('lastfm_art')
-      if url != "" && url != undefined && url.substring(0,18) != "https://secure-img" && url.substring(0,19) != "https://lastfm-img2"
+      if url != "" && url != undefined && url.substring(0,18) != "https://secure-img"
         $(@get('node')).css 'background-image', "url(#{url})"
       else
         $(@get('node')).css 'background-image', "url(assets/sonos-default-background.jpg)"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -17,7 +17,7 @@
   ###*
   Get the game object matching the given selector that is closest to the given position.

-  @param selector
+  @param {String} selector
   @param {Point} position
   ###
   closest: (selector, position) ->


adding module tag for docs

adding module tag for docs

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -1,8 +1,9 @@
 ###*
-(Module) The Selector module provides methods to query the engine to find game objects.
+The Selector module provides methods to query the engine to find game objects.

 @name Selector
 @fieldOf Engine
+@module
 ###
 Engine.Selector = (I, self) ->


Added seeThrough fields to generated room maps

--- src/generation/maps.coffee
+++ src/generation/maps.coffee
@@ -48,8 +48,8 @@
 	data = exports.createMapData map.w, map.h, tileCb

 	map.data = convertMapData data, [
-		{ collidable: no, symbol: 'floor' }
-		{ collidable: yes, symbol: 'wall' }
+		{ collidable: no, seeThrough: yes, symbol: 'floor' }
+		{ collidable: yes, seeThrough: no, symbol: 'wall' }
 	]

 	exports.generateExits map, path, level, connections


print all events

print all events

--- src/github-calendar.coffee
+++ src/github-calendar.coffee
@@ -34,6 +34,7 @@
 	date1.getYear() is date2.getYear() and date1.getMonth() is date2.getMonth() and date1.getDate() is date2.getDate()

 paintGrids = (row,col, model)->
+	console.log 'print ' + row + ',' + col
 	square = paper.rect(col * 11, row * 11, 10, 10)
 	square.attr("fill", "#ccc")
 	square.attr("stroke-opacity", "0")
@@ -48,7 +49,7 @@
 			e = eventMap[ grid.created_at.format() ]
 			commitsLength = if e.payload.commits then e.payload.commits.length else 0
 			grid.commitsLength = commitsLength
-	paintGrids i % 7, Math.floor(i / 7), grid
+		paintGrids i % 7, Math.floor(i / 7), grid

 eventsHandler = (events)->
 	events.forEach (e)->
@@ -70,7 +71,8 @@
 				mergedResult.push current
 				return current
 	processedUrls += 1
-	if (processedUrls > 0)
+	if (processedUrls == 10)
+		console.log 'draw....'
 		draw()

@@ -86,7 +88,8 @@
 	date.setDate( today.getDate() - i )
 	model.unshift { created_at: date, commitsLength: 0}

-$.get( GITHUB_USER_URL + '/' + user + '/events?page=' + page)
-	.done eventsHandler
+urls.forEach (url)->
+	$.get( url )
+		.done eventsHandler

 paper = Raphael(10, 10, 600, 100)


Hide preview when empty

Hide preview when empty

--- resources/assets/coffee/_classes/post-preview.coffee
+++ resources/assets/coffee/_classes/post-preview.coffee
@@ -34,6 +34,10 @@

     return if $preview.attr('data-raw') == body

+    if body == ''
+      $previewBox.addClass('hidden')
+      return
+
     $.post(url, text: body)
     .done (data) =>
       $preview.html data


code format

code format

--- BackofficeBundle/Resources/public/coffee/AdminFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/AdminFormView.coffee
@@ -37,8 +37,7 @@
         actionButtons = $('<div>')
         if viewContext.deleteButton
           actionButtons.append(viewContext.renderTemplate('OpenOrchestraBackofficeBundle:BackOffice:Underscore/deleteButton', viewContext.options))
-        actionButtons = actionButtons.prepend(button)
-        .html()
+        actionButtons = actionButtons.prepend(button).html()
         extendView = viewContext.options.extendView || []
         if extendView.indexOf('submitAdmin') == -1
           extendView.push 'submitAdmin'


Added install command code

Added install command code

The outdated command code has also been types out, but sits behind a Not
Impelemented wall.

--- bin/kdc-plus.coffee
+++ bin/kdc-plus.coffee
@@ -12,6 +12,15 @@
   StdioTransform
 }                 = require '../lib/streams/load'
 maniutils         = require '../lib/maniutils'
+{
+  installDev
+  installProd
+}                 = require '../lib/deps/install'
+
+{
+  outdatedDev
+  outdatedProd
+}                 = require '../lib/deps/outdated'

@@ -221,13 +230,40 @@
 # corrisponding dependency files *(package.json, bower.json, etc)*.
 install = (appPath, unknownArgs..., opts={}, log=console.error) ->
   if typeof appPath is 'object' then [opts, appPath] = [appPath, undefined]
-  log 'Not Implemented'
-  return process.exit 1

   appPath ?= process.cwd()
   appPath = path.resolve appPath

   _loadManifest appPath, (manifest) ->
+    # Get a the package managers that we should tell to install from the
+    # manifest. For an examplanation as to why we use this method of
+    # identifying which packages to install, see <INSERT LINK HERE..>
+    if opts.production is true
+      packageManagers = manifest.packageManagers    ? {}
+      installer       = installProd
+    else
+      packageManagers = manifest.devPackageManagers ? {}
+      installer       = installDev
+
+    # To avoid unintended behavior, we'll copy just the packages we support,
+    # to ensure accidental "install options" don't leak in from the manifest
+    installOpts = {}
+    installOpts.node  = packageManagers.node
+
+    installer appPath, installOpts, (err, packages) ->
+      if err?
+        log "Error encountered during install: #{err.message}"
+        return process.exit 1
+
+      log pack for pack in packages
+
+      log 'Install completed successfully!'
+      process.exit 0
+
+
+
+
+

@@ -237,14 +273,33 @@
 # also the corrisponding dependency files *(package.json, bower.json, etc)*.
 outdated = (appPath, unknownArgs..., opts={}, log=console.error) ->
   if typeof appPath is 'object' then [opts, appPath] = [appPath, undefined]
-  log 'Not Implemented'
-  return process.exit 1
+
+  log 'Not Impelemented'
+  process.exit 1

   appPath ?= process.cwd()
   appPath = path.resolve appPath

   _loadManifest appPath, (manifest) ->
-
+    # Get a the package managers that we should tell to install from the
+    # manifest. For an examplanation as to why we use this method of
+    # identifying which packages to install, see <INSERT LINK HERE..>
+    if opts.production is true
+      packageManagers = manifest.packageManagers    ? {}
+      outdater        = outdatedProd
+    else
+      packageManagers = manifest.devPackageManagers ? {}
+      outdater        = outdatedDev
+
+    outdaterOpts = {}
+    outdaterOpts.node  = packageManagers.node
+
+    outdater appPath, outdaterOpts, (err, packages) ->
+      if err?
+        log "Error encountered during outdated: #{err.message}"
+        return process.exit 1
+
+      log pack for pack in packages


add note about deleting the migration key entries

--- services/document-updater/app/coffee/RedisMigrationManager.coffee
+++ services/document-updater/app/coffee/RedisMigrationManager.coffee
@@ -44,6 +44,12 @@
 #   the flag
 # - when all docupdaters are setting the flag (and writing to the new redis),
 #   finish the migration by writing all data to the new redis
+#
+# Final stage
+#
+# When all the queues are migrated, remove the migration code and return to a
+# single client pointing at the new redis.  Delete the
+# ProjectHistory:MigrationKey:* entries in the new redis.
 #
 # Rollback
 #


remove console log

remove console log

--- app/assets/javascripts/lale/workgroup_dependent_select.coffee
+++ app/assets/javascripts/lale/workgroup_dependent_select.coffee
@@ -46,8 +46,6 @@
     else
       working_group = this.workgroup_field.find(':selected').text()

-    console.log(working_group, @workgroup_field.siblings())
-
     escaped_wg_name = working_group.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')


minor fixes

minor fixes

--- app/assets/javascripts/uploadcare/utils/load-plugin.js.coffee
+++ app/assets/javascripts/uploadcare/utils/load-plugin.js.coffee
@@ -6,7 +6,7 @@
 namespace 'uploadcare.utils', (utils) ->

   cache = {}
-  ATTEMPTS = 3
+  MAX_ATTEMPTS = 3

   utils.loadPlugin = (filename) ->

@@ -21,12 +21,12 @@
         url = "#{scriptBase}plugins/#{filename}#{scriptExt}"

         df.fail ->
-          utils.warn "Can't load script #{url}"
+          utils.warn "Couldn't load script #{url}"

         attempts = 0
         load = ->
           attempts++
-          if attempts > ATTEMPTS
+          if attempts > MAX_ATTEMPTS
             df.reject()
           else
             $.getScript(url)


remove restore on backspace

remove restore on backspace

--- app/assets/javascripts/views/admin/admin_bikes_edit.js.coffee
+++ app/assets/javascripts/views/admin/admin_bikes_edit.js.coffee
@@ -25,8 +25,8 @@
     per_page = 10
     frame_mnfg_url = "#{window.root_url}/api/autocomplete?per_page=#{per_page}&categories=frame_mnfg&q="
     $target.selectize
-      plugins: ['restore_on_backspace']
       options: initial_opts
+      selectOnTab: true
       preload: false
       persist: false
       create: false


Add triggers of zahlen

Add triggers of zahlen

--- app/assets/javascripts/zahlen/subscription_checkout.js.coffee
+++ app/assets/javascripts/zahlen/subscription_checkout.js.coffee
@@ -26,9 +26,10 @@
           return false

       $(this).find(':submit').prop('disabled', true);
-      $('.zahlen-spinner').show();
+      $(form_selector).trigger('zahlen:processingPayment')

       if !self.payment_method?
+        $(form_selector).trigger('zahlen:noPaymentMethodFound')
         console.log "No payment method has been found. Please check your form."
         return false

@@ -42,7 +43,7 @@
     payment_method = $("input[data-zahlen='payment-method']").val()
     if payment_method?
       @payment_method = payment_method.replace(/_/, '-')
-
+
   paymentMethodToggle: ->
     self = @
     form_selector = @constructor.form_selector
@@ -165,6 +166,7 @@
     $(form_selector).find(':submit').prop('disabled', false).trigger('error', msg)
     $(form_selector).find('.zahlen-error').text(msg)
     $(form_selector).find('.zahlen-error').show()
+    $(form_selector).trigger('zahlen:error');
     console.log $(form_selector).find('.zahlen-error')

   @poll: (num_retries_left, uuid, base_path) ->


oops, same thing

oops, same thing

--- src/ClassLoader.coffee
+++ src/ClassLoader.coffee
@@ -36,7 +36,7 @@
     @_rem_class type_str
     return if util.is_primitive_type type_str
     for k,cdata of @loaded_classes when type_str in [cdata.get_component_type?(), cdata.get_super_class_type()]
-      @remove_class cdata.get_type()
+      @remove_class k
     return

   # Remove a class. Should only be used in the event of a class loading failure.


Add fire

Add fire

--- scripts/memes.coffee
+++ scripts/memes.coffee
@@ -12,14 +12,16 @@
 #   when do we want it - for when we want brains

 module.exports = (robot) ->
+  robot.hear /\b(alarm|fire)\b/i, (msg) ->
+    msg.send 'http://media.giphy.com/media/d8695TvC8G1Co/giphy.gif'
+
   robot.hear /aribot/i, (msg) ->
-    console.log msg
     msg.send 'http://i.imgur.com/vQja8jB.jpg'

   robot.hear /\bbaber'?s\s?way\b/i, (msg) ->
     msg.send 'http://i.imgur.com/rIC8Vuc.gif'

-  robot.hear /bucks|money/i, (msg) ->
+  robot.hear /\bbucks\b/i, (msg) ->
     msg.send 'http://stuff.ebnj.net/funny/moneygun.gif'

   robot.respond /capybara/i, (msg) ->


Comment model/collections test logs

Comment model/collections test logs

--- app/src/app.coffee
+++ app/src/app.coffee
@@ -44,6 +44,7 @@

       ledger.storage.openStores('merguez')

+      ### MODELS/COLLECTIONS LEGACY TESTS DO NOT REMOVE
       account = Account.findOrCreate 1, {name: 'Toto', balance: 16, operations: [{_id: 1, name: 'opTest'}]}
       account.get (result) =>
        account.getOperations (operations) =>
@@ -70,7 +71,7 @@
                           l a
                         ledger.collections.accounts.each (object) =>
                           l object
-
+      ###

     navigate: (layoutName, viewController) ->
       @router.once 'routed', (event, data) =>


ide.coffee: renamed @isDead to @dead

ide.coffee: renamed @isDead to @dead

--- client/ide.coffee
+++ client/ide.coffee
@@ -24,7 +24,7 @@
       </div>
     """

-    @isDead = 0
+    @dead = 0
     @pending = [] # lines to execute: AtInputPrompt consumes one item from the queue, HadError empties it
     @w3500 = null # window for 3500⌶
     @host = @port = @wsid = ''; prefs.title @updateTitle.bind @
@@ -61,9 +61,9 @@
       '*spawnedError': ({message}) =>
         @die(); delay 100, -> $.alert message, 'Error'; return # give the window a chance to restore its original dimensions
         return
-      '*disconnected': => (if !@isDead then $.alert 'Interpreter disconnected', 'Error'; @die()); return
+      '*disconnected': => (if !@dead then $.alert 'Interpreter disconnected', 'Error'; @die()); return
       Disconnect: ({message}) =>
-        if !@isDead
+        if !@dead
           @die()
           if message == 'Dyalog session has ended'
             (try close()); D.process?.exit? 0
@@ -206,12 +206,12 @@

   setHostAndPort: (@host, @port) -> @updateTitle(); return

-  emit: (x, y) -> @isDead || D.socket.emit x, y; return
+  emit: (x, y) -> @dead || D.socket.emit x, y; return
   WI: -> @emit 'WeakInterrupt'; return
   SI: -> @emit 'StrongInterrupt'; return

   die: -> # don't really, just pretend
-    if !@isDead then @isDead = 1; @$ide.addClass 'disconnected'; for _, widget of @wins then widget.die()
+    if !@dead then @dead = 1; @$ide.addClass 'disconnected'; for _, widget of @wins then widget.die()
     return

   updateTitle: -> # add updateTitle() as a change listener for preference "title"
@@ -251,7 +251,7 @@
   openWindow: (ee) -> # "ee" for EditableEntity
     w = ee.token
     editorOpts = id: w, name: ee.name, tracer: ee.debugger, emit: @emit.bind(@), weakInterrupt: @WI.bind(@)
-    if prefs.floating() && !D.floating && !@isDead
+    if prefs.floating() && !D.floating && !@dead
       pos = if ee.debugger then prefs.posTracer() else prefs.posEditor()
       delta = 32 * (ee.token - 1); pos[0] += delta; pos[1] += delta
       posH = x: pos[0], y: pos[1], width: pos[2], height: pos[3]


add an index on pubkey_hash

add an index on pubkey_hash

--- server/models.coffee
+++ server/models.coffee
@@ -20,7 +20,7 @@
     pubkey:  type: Buffer, required: true, set: to_buff
     content: type: String, required: true
     sig:     type: Buffer, required: true, set: to_buff
-    pubkey_hash: type: Buffer
+    pubkey_hash: type: Buffer, index: true

   userSchema.plugin timestamp
   userSchema.virtual('address').get -> get_address [ @pubkey... ], ADDR_PUB


sidebar: skip listed channels in search modal

--- client/Social/Activity/sidebar/morechannelsmodal.coffee
+++ client/Social/Activity/sidebar/morechannelsmodal.coffee
@@ -11,9 +11,28 @@

     super options, data

+    @setSkipCount()
+
+
+  setSkipCount: ->
+
+    {mainView: {activitySidebar}} = KD.singletons
+    {sections: {channels}}        = activitySidebar
+    {listController}              = channels
+
+    @skipCount = listController.getItemCount() - 1 or 0
+

   fetch: (options = {}, callback = noop) ->

-    options.skip ?= 9
+    options.skip ?= @skipCount

     super options, callback
+
+
+  getLazyLoadOptions: ->
+
+    skip  = @listController.getItemCount()
+    skip += @skipCount  unless @searchActive
+
+    return {skip}


Activity: private message modal placeholder optionalized

--- client/Social/Activity/views/privatemessagemodal.coffee
+++ client/Social/Activity/views/privatemessagemodal.coffee
@@ -159,8 +159,8 @@
     input  = @autoComplete.getView()
     [item] = (item for item in @autoComplete.itemWrapper.getSubViews() when item.getData() is lastItemData)

-    reset = ->
-      input.setPlaceHolder 'Type a username to start your conversation...'
+    reset = =>
+      input.setPlaceHolder @autoComplete.getOptions().placeholder
       item.unsetClass 'selected'
       placeholderIsChanged_ = no


Use the correct CoffeeScript if-then-else construct for the restMode.

--- server/server.coffee
+++ server/server.coffee
@@ -136,7 +136,7 @@
 # Start MongoDB, if necessary...
 spawn 'mongod', MONGODB_PORT, ->
   # ...then the REST app...
-  restMode = env is 'test' ? 'development' : env
+  restMode = if env is 'test' then 'development' else env
   cmd = if restMode? then "qirest --#{ restMode }" else 'qirest'

   spawn cmd, EVE_PORT, ->


RequirementsParser: change requirements parser to support Koding specific variables

--- client/admin/lib/views/stacks/requirementsparser.coffee
+++ client/admin/lib/views/stacks/requirementsparser.coffee
@@ -1,13 +1,41 @@
 module.exports = requirementsParser = (content) ->

-    regex = /\{\{(user|account|group)\ (.*?)\}\}/g
+    allowedProps =
+      user       : ['username', 'email'] # JUser
+      account    : ['profile']           # JAccount
+      group      : ['title', 'slug']     # JGroup
+
+    props        = []
+    sections     = []
+
+    for i, v of allowedProps
+      sections   = sections.concat i
+      props      = props.concat v
+
+    props        = props.join '|'
+    sections     = sections.join '|'
+
+    # Check the example below
+    # http://regexr.com/3blbp ~ GG
+
+    regexs = [
+      ///\$\{var\.koding\_(#{sections})\_(#{props})(\_(.*)\}|\})///g
+      /\$\{var\.(userInput)\_(.*)\}/g
+    ]
+
     requirements = {}
-    match = regex.exec content

-    while match
-      requirements[match[1]] ?= {}
-      requirements[match[1]][match[2]] = null
+    for regex in regexs
       match = regex.exec content
+
+      while match
+        requirements[match[1]] ?= {}
+        substring = if match[4]
+          "#{match[2]}.#{match[4..].join '.'}"
+        else
+          match[2]
+        requirements[match[1]][substring] = null
+        match = regex.exec content

     for match of requirements
       requirements[match] = Object.keys requirements[match]


MachineItem: no more vms no more hostnameAliases

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -151,10 +151,7 @@

   openTerminal:->

-    vmName = @getData().hostnameAlias
     KD.getSingleton("router").handleRoute "/Terminal", replaceState: yes
-    KD.getSingleton("appManager").open "Terminal", params: {vmName}, forceNew: yes
-

   confirmDestroy:->


EnvironmentMachineItem: update the state on the data, disable contextmenu if machine is state is notInitialized

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -85,6 +85,7 @@
     then @statusToggle.setOn no
     else @statusToggle.setOff no

+    @getData().setAt "status.state", status
     @state.updatePartial status

@@ -102,11 +103,14 @@

   contextMenuItems: ->

+    machine = @getData()
+
+    return  if machine.status.state is Machine.State.NotInitialized
+
     colorSelection = new ColorSelection selectedColor : @getOption 'colorTag'
     colorSelection.on "ColorChanged", @bound 'setColorTag'

     this_   = this
-    machine = @getData()

     vmAlwaysOnSwitch = new VMAlwaysOnToggleButtonView


Client: Activity: Added CreateChannelModal props

--- client/app/lib/components/sidebarchannelssection/index.coffee
+++ client/app/lib/components/sidebarchannelssection/index.coffee
@@ -35,6 +35,13 @@
     @setState isModalOpen: no

+  getCreateChannelModalProps: ->
+
+    title              : 'Create a channel'
+    className          : 'CreateChannel-Modal'
+    buttonConfirmTitle : 'Create Channel'
+
+
   renderFollowedChannelsModal: ->

     title = 'Other Channels you are following:'
@@ -60,7 +67,11 @@
   render: ->

     <div>
-      <SidebarSection title="Channels" onHeaderClick={@bound 'showFollowedPublicChannelsModal'} className="SidebarChannelsSection">
+      <SidebarSection
+        title="Channels"
+        modalProps={@getCreateChannelModalProps()}
+        onHeaderClick={@bound 'showFollowedPublicChannelsModal'}
+        className="SidebarChannelsSection">
         <SidebarList
           previewCount={@props.previewCount}
           itemComponent={SidebarChannelsListItem}


Fix mydevices

Fix mydevices

--- lib/MeshbluWebsocketHandler.coffee
+++ lib/MeshbluWebsocketHandler.coffee
@@ -65,8 +65,7 @@
     @authDevice @uuid, @token, (error, device) =>
       debug 'devices', data
       return @sendError error.message, ['devices', data] if error?
-      @getDevices device, data, null, (error, foundDevices) =>
-        return @sendError error.message, ['devices', data] if error?
+      @getDevices device, data, null, (foundDevices) =>
         @sendFrame 'devices', foundDevices

   identity: (data) =>
@@ -85,12 +84,12 @@
       return @sendError error.message, ['message', data] if error?
       @sendMessage device, data

-  mydevices: (data) =>
+  mydevices: (data={}) =>
     @authDevice @uuid, @token, (error, device) =>
       debug 'mydevices', data
       return @sendError error.message, ['mydevices', data] if error?
       data.owner = device.uuid
-      @getDevices device, data, (error, foundDevices) =>
+      @getDevices device, data, null, (foundDevices) =>
         return @sendError error.message, ['mydevices', data] if error?
         @sendFrame 'mydevices', foundDevices

@@ -193,10 +192,13 @@
   sendFrame: (type, data) =>
     frame = [type, data]
     debug 'sendFrame', frame
-    @socket.send JSON.stringify frame
+    @socket?.send JSON.stringify frame

   sendError: (message, frame, code) =>
-    debug 'sendError', message
+    try
+      throw new Error message
+    catch e
+      debug 'sendError', e.message, e.stack
     @sendFrame 'error', message: message, frame: frame, code: code

   subscribeWithToken: (data) =>


Add Atom snippet for Ruby curly braces block

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -2,6 +2,10 @@
   "Hashrocket":
     prefix: "="
     body: " => "
+
+  "Curly braces block":
+    prefix: "{"
+    body: "{ |${1:variable}| $2 "

   "Context block":
     prefix: "context"


Add Handlebars snippets

Add Handlebars snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -1000,6 +1000,44 @@
     'prefix': 'endfor'
     'body': '{% endfor %}'

+'.text.html.handlebars':
+
+  'expression':
+    'prefix': 'exp'
+    'body': '{{$1}}$2'
+
+  'if':
+    'prefix': 'if'
+    'body': '{{#if ${1:condition}}}$2'
+
+  'elseif':
+    'prefix': 'elseif'
+    'body': '{{else if ${1:condition}}}$2'
+
+  'else':
+    'prefix': 'else'
+    'body': '{{else}}$2'
+
+  'endif':
+    'prefix': 'endif'
+    'body': '{{/endif}}'
+
+  'each':
+    'prefix': 'each'
+    'body': '{{#each ${1:object}}}$2'
+
+  'endeach':
+    'prefix': 'endeach'
+    'body': '{{/each}}'
+
+  'with':
+    'prefix': 'with'
+    'body': '{{#with ${1:object}}}$2'
+
+  'endwith':
+    'prefix': 'endwith'
+    'body': '{{/with}}'
+
 # Apache configuration

 '.source.apache-config':


stuff

stuff

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -18,6 +18,8 @@
 # in order to deeplink. Params is populated from
 # the route.
 App.PostRoute = Ember.Route.extend
+  # Can replace these models with AJAX requests. To
+  # remain async it supports .promises (ie. $.getJSON('url').then( massage data ))
   model: (params) ->
     posts.findBy 'id', params.post_id


start

start

--- app/start.coffee
+++ app/start.coffee
@@ -0,0 +1 @@
+module.exports = ->


Fix issue #245

Fix issue #245

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -380,6 +380,7 @@
   execute: (count=1) ->
     _.times count, =>
       @editor.moveCursorToEndOfLine()
+      @editor.moveCursorLeft()

   select: (count=1) ->
     _.times count, =>


Fix name of course-ladder bypass level

Fix name of course-ladder bypass level

--- server/middleware/levels.coffee
+++ server/middleware/levels.coffee
@@ -84,7 +84,7 @@
     if code
       attrs.code = code

-    if not req.user.isAnonymous() and level.get('slug') in ['treasure-game']
+    if not req.user.isAnonymous() and level.get('slug') in ['treasure-games']
       console.log "Allowing session creation for #{level.get('slug')} outside of any course"
       attrs.isForClassroom = true
     else if level.get('type') in ['course', 'course-ladder'] or req.query.course?


crawler: change activity if statement

crawler: change activity if statement

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -659,7 +659,7 @@
     if name is 'Develop'
       return res.redirect 301, '/IDE'

-    if name in ['Activity']
+    if name is 'Activity'
       isLoggedIn req, res, (err, loggedIn, account)->

         return  serveHome req, res, next  if loggedIn


server: update endpoints path

server: update endpoints path

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -123,8 +123,8 @@
 # app.get  '/-/oauth/github/callback'              , require './github_callback'
 app.get  '/-/oauth/gitlab/callback'              , require './gitlab_callback'

-app.get '/-/stack/search/:query'                 , cors(), require './handlers/stackscripttitle'
-app.get '/-/stack/getContent/:query'             , cors(), require './handlers/stackscriptmarkdown'
+app.get '/-/terraform/document-search/:query'    , cors(), require './handlers/stackscripttitle'
+app.get '/-/terraform/document-content/:query'   , cors(), require './handlers/stackscriptmarkdown'

 # app.get  '/-/oauth/facebook/callback'            , require './facebook_callback'
 # app.get  '/-/oauth/google/callback'              , require './google_callback'