Alerts buttons UI

Alerts buttons UI

--- muleview/coffee/app/view/AlertsEditor.coffee
+++ muleview/coffee/app/view/AlertsEditor.coffee
@@ -15,7 +15,6 @@
       xtype: "displayfield"
       fieldLabel: "Graph"
       name: "graphName"
-    ,
   ]

   formHashFromArray: (arr, base = {}) ->
@@ -23,12 +22,17 @@
     base

   load: (curKey, curRet, alertsArr) ->
+    # Calculate Data:
     data = {
       graphName: "#{curKey};#{curRet}"
     }
     data = @formHashFromArray(Muleview.Settings.alerts, data )
     data = @formHashFromArray(alertsArr, data) if alertsArr
     @getForm().setValues(data)
+
+    # Display correct items:
+    @updateButtons (if alertsArr then "edit" else "create")
+    @fieldsContainer.setVisible(alertsArr)

   createField: (alert) ->
     ans =
@@ -44,17 +48,75 @@
     ans

   initComponent: ->
+    # Fields:
+    fields = []
     for alert in Muleview.Settings.alerts
-      @items.push @createField(alert)
-    @items.push
-      xtype: "button"
-      text: "Update"
-      handler: =>
-        url = Muleview.Mule.getAlertCommandUrl(Muleview.currentKey, Muleview.currentRetention)
-        @submit(
-          url: url
-          method: "PUT"
-          success: ->
-            Muleview.Graphs.createGraphs()
-        )
+      fields.push @createField(alert)
+
+    # Field container
+    @fieldsContainer = Ext.create "Ext.container.Container",
+      layout:
+        type: "vbox"
+        align: "stretch"
+        pack: "start"
+
+      items: fields
+      border: false
+      hidden: true
+
+    @items.push @fieldsContainer
+
+    # Buttons:
+    @buttonsContainer = Ext.create "Ext.container.Container",
+      border: false
+      layout:
+        type: "hbox"
+        pack: "start"
+      defaults:
+        xtype: "button"
+        margin: "0px 5px 0px 0px"
+        hidden: true
+      items: [
+            text: "Save"
+            handler: => @doUpdate()
+            showInMode: ["edit", "creating"]
+        ,
+            text: "Delete"
+            handler: => @doDelete()
+            showInMode: ["edit"]
+        ,
+            text: "Set Alerts"
+            handler: => @doCreate()
+            showInMode: ["create"]
+        ,
+            text: "Cancel"
+            handler: => @doCancel()
+            showInMode: ["creating"]
+      ]
+    @items.push @buttonsContainer
     @callParent()
+
+  updateButtons: (mode) ->
+    @buttonsContainer.items.each (btn) ->
+      btn.setVisible(Ext.Array.contains(btn.showInMode, mode))
+
+  doUpdate: ->
+    url = Muleview.Mule.getAlertCommandUrl(Muleview.currentKey, Muleview.currentRetention)
+    @submit(
+      url: url
+      method: "PUT"
+      success: ->
+        Muleview.Graphs.createGraphs()
+      )
+
+  doDelete: ->
+    console.log("MuleChart.coffee\\ 148: <HERE>");
+
+  doCreate: ->
+    @getForm().clearInvalid()
+    @fieldsContainer.show()
+    @updateButtons("creating")
+
+  doCancel: ->
+    @fieldsContainer.hide()
+    @updateButtons("create")


compute image: less specific names

compute image: less specific names

--- src/smc-hub/compute-images.coffee
+++ src/smc-hub/compute-images.coffee
@@ -3,10 +3,10 @@
 {DEFAULT_COMPUTE_IMAGE} = require('smc-util/db-schema')

 COMPUTE_IMAGES =
-    default: {title: "Default", descr: "Ubuntu 18.04 based image, updated regularly"}
+    default: {title: "Default", descr: "Regularly updated, well tested."}
     stable: {title: "Stable", descr: "Slightly behind 'default', updated less frequently"}
     exp: {title: "Experimental", descr: "Cutting-edge software updates (could be broken)"}
-    old: {title: "Old image", descr: "Ubuntu 16.04 based, in use until Summer 2018. No longer maintained!"}
+    old: {title: "Old image", descr: "In use until Summer 2018. No longer maintained!"}

 exports.get_compute_images = (opts) =>
     opts = defaults opts,


Change terminal mode warning

Change terminal mode warning

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -432,9 +432,7 @@
             </Col>
             <Col sm={8}>
                 <Alert style={marginTop: '5px', fontWeight : 'bold'} bsStyle='info'>
-                Warning: You are in terminal mode.

-                This was caused by the leading / in your file search. If you want to just see your folders, enter a space in front of the /.

-                Terminal mode inside the search bar is experimental and comes with no guarantees about its usability or future existence.
+                Warning: You are in terminal mode. Click here for more information.
             </Alert>
             </Col>
             <Col sm={2}>


move aggregate type definition to top

move aggregate type definition to top

--- source/domain/aggregate.coffee
+++ source/domain/aggregate.coffee
@@ -3,12 +3,12 @@

 class Space.eventSourcing.Aggregate extends Space.Object

+  @type 'Space.eventSourcing.Aggregate'
+
   _id: null
   _version: 0
   _events: null
   _state: null
-
-  @type 'Space.eventSourcing.Aggregate'

   # Override to define which custom properties this aggregate has
   FIELDS: {}


the loop

the loop

--- src/service/device-mailer.coffee
+++ src/service/device-mailer.coffee
@@ -11,7 +11,7 @@
 class MailerService
   constructor: ({@meshbluConfig}) ->
     @channelEncryption = new ChannelEncryption {@meshbluConfig}
-    @credentialDeviceManager = new CredentialDeviceManager {@meshbluConfig, serviceUrl: ""}
+    @credentialDeviceManager = new CredentialDeviceManager {@meshbluConfig}

   onCreate: ({metadata, data}, callback) =>
     {auth} = metadata
@@ -116,6 +116,7 @@
     updateOptions =
       $set:
         owner: owner
+        secureConfig: true
       $addToSet:
         sendAsWhitelist: credentialsDeviceUuid
         receiveAsWhitelist: credentialsDeviceUuid


Add stream helpers

Add stream helpers

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -194,6 +194,26 @@
       return ips?.data ? undefined
     (ip?.data ? undefined for ip in ips)

+  hasStream: (port) ->
+    buffer = @buffer.get port
+    return false if buffer.length is 0
+    # check if we have everything until "disconnect"
+    received = 0
+    for packet in buffer
+      if packet.type is 'openBracket'
+        ++received
+      else if packet.type is 'closeBracket'
+        --received
+    received is 0
+
+  getStream: (input, port, withoutConnectAndDisconnect = false) ->
+    buf = @buffer.get port
+    @buffer.filter port, (ip) -> false
+    if withoutConnectAndDisconnect
+      buf = buf.slice 1
+      buf.pop()
+    buf
+
 class PortBuffer
   constructor: (@context) ->


final code cleanup for analytics.coffee

final code cleanup for analytics.coffee

--- src/lib/analytics.coffee
+++ src/lib/analytics.coffee
@@ -8,9 +8,9 @@
 # -----------------

 # Code for Templates analytics account.
-ANALYTICS_ACCOUNT = 'UA-28812528-1'
+ACCOUNT = 'UA-28812528-1'
 # Source URL of the analytics script.
-ANALYTICS_SOURCE  = 'https://ssl.google-analytics.com/ga.js'
+SOURCE  = 'https://ssl.google-analytics.com/ga.js'

 # Analytics setup
 # ---------------
@@ -24,41 +24,39 @@
   add: ->
     # Setup tracking details for analytics.
     _gaq = window._gaq ?= []
-    _gaq.push ['_setAccount', ANALYTICS_ACCOUNT]
+    _gaq.push ['_setAccount', ACCOUNT]
     _gaq.push ['_trackPageview']
     # Inject script to capture analytics.
     ga = document.createElement 'script'
     ga.async = 'async'
-    ga.src   = ANALYTICS_SOURCE
+    ga.src   = SOURCE
     script = document.getElementsByTagName('script')[0]
     script.parentNode.insertBefore ga, script

   # Determine whether or not analytics are enabled.
-  enabled: ->
-    not store? or store.get 'analytics'
+  enabled: -> not store? or store.get 'analytics'

   # Remove analytics from the current page.
   remove: ->
     # Delete scripts used to capture analytics.
-    scripts = document.querySelectorAll "script[src='#{ANALYTICS_SOURCE}']"
-    script.parentNode.removeChild script for script in scripts
+    for script in document.querySelectorAll "script[src='#{SOURCE}']"
+      script.parentNode.removeChild script
     # Remove tracking details for analytics.
     delete window._gaq

   # Create an event with the information provided and track it in analytics.
-  track: (category, action, label, value, nonInteraction) ->
-    if @enabled()
-      event = ['_trackEvent']
-      # Add the required information.
-      event.push category
-      event.push action
-      # Add the optional information where possible.
-      event.push label          if label?
-      event.push value          if value?
-      event.push nonInteraction if nonInteraction?
-      # Add the event to analytics.
-      _gaq = window._gaq ?= []
-      _gaq.push event
+  track: (category, action, label, value, nonInteraction) -> if @enabled()
+    event = ['_trackEvent']
+    # Add the required information.
+    event.push category
+    event.push action
+    # Add the optional information where possible.
+    event.push label          if label?
+    event.push value          if value?
+    event.push nonInteraction if nonInteraction?
+    # Add the event to analytics.
+    _gaq = window._gaq ?= []
+    _gaq.push event

 # Configuration
 # -------------


Reenable integration spec.

Reenable integration spec.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -8,7 +8,7 @@

 jasmine.getEnv().defaultTimeoutInterval = 10000

-xdescribe 'integration tests', ->
+describe 'integration tests', ->

   CHANNEL_KEY = 'OrderXmlFileExport'
   CHANNEL_ROLE = 'OrderExport'


Only show 20 items at a time in autocomplete

This improves autocomplete rendering performance considerably.

--- lib/views/autocomplete_view.coffee
+++ lib/views/autocomplete_view.coffee
@@ -7,6 +7,8 @@
     'LOW': 1
     'DEFAULT': 0
     'HIGH': -1
+
+  maxItems: 20

   initialize: (@editorView, @autocompleter) ->
     super


add anti-DOS measures.

add anti-DOS measures.

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -292,6 +292,10 @@
     ################################################################

     handle_message_from_client: (data) =>
+        # TODO: THIS IS A QUICK anti-DOS measure for early testing
+        if data.length >= 10000000  # 10 MB
+            @push_to_client(message.error(error:"Messages are limited to 10MB.", id:mesg.id))
+
         try
             mesg = from_json(data)
         catch error
@@ -465,6 +469,10 @@
         if not @account_id?
             @error_to_client(id: mesg.id, error: "You must be signed in to create a new project.")
             return
+
+        @error_to_client(id: mesg.id, error: "Project creation is temporarily disabled.")
+        return
+
         project_id = uuid.v4()
         # create project in database
         database.create_project
@@ -1535,6 +1543,9 @@
         value : required
         ttl   : undefined
         cb    : undefined
+    if opts.value.length >= 10000000
+        # PRIMITIVE anti-DOS measure
+        return
     database.uuid_value_store(name:"blobs").set(opts)

 get_blob = (opts) ->


more error/log message improvement

more error/log message improvement

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -250,7 +250,7 @@
         form = new formidable.IncomingForm()
         form.parse req, (err, fields, files) ->
             if err or not files.file? or not files.file.path? or not files.file.name?
-                e = "file upload failed -- #{err} -- #{misc.to_json(files)}"
+                e = "file upload failed -- #{misc.to_safe_str(err)} -- #{misc.to_safe_str(files)}"
                 winston.debug(e)
                 res.status(500).send(e)
                 return # nothing to do -- no actual file upload requested
@@ -327,7 +327,7 @@
             ], (err) ->
                 if err
                     winston.debug(e)
-                    e = "file upload error -- #{err}"
+                    e = "file upload error -- #{misc.to_safe_str(err)}"
                     res.status(500).send(e)
                 else
                     res.send('received upload:\n\n')


Add timeout argument in function "waitForConnection"

--- actions/nc.coffee
+++ actions/nc.coffee
@@ -1,5 +1,7 @@

 module.exports = []
+
+module.exports.push 'histi/actions/yum_epel'

 ###
 nc
@@ -8,11 +10,31 @@
 ###

 module.exports.push module.exports.configure = (ctx) ->
-  ctx.waitForConnection = (host, port, callback) ->
-    ctx.execute
-      cmd: "while ! nc -z -w5 #{host} #{port}; do sleep 2; done;"
-    , (err, executed) ->
-      callback err
+  ctx.waitForConnection = (host, port, timeout, callback) ->
+    if typeof timeout is 'function'
+      callback = timeout
+      timeout = null
+    if timeout
+      rand = Date.now()
+      timedout = false
+      clear = setTimeout ->
+        timedout = true
+        ctx.touch
+          destination: "/tmp/#{rand}"
+        , (err) ->
+          # nothing to do
+      , timeout
+      ctx.execute
+        cmd: "while [[ ! `nc -z -w5 #{host} #{port}` ]] && [[ ! -f /tmp/#{rand} ]]; do sleep 2; done;"
+      , (err, executed) ->
+        clearTimeout clear
+        err = new Error "Reached timeout #{timeout}" if not err and timedout
+        callback err
+    else
+      ctx.execute
+        cmd: "while ! nc -z -w5 #{host} #{port}; do sleep 2; done;"
+      , (err, executed) ->
+        callback err

 module.exports.push name: 'NC # Install', timeout: -1, callback: (ctx, next) ->
   ctx.service


"Travis CI (Part 51)"

"Travis CI (Part 51)"

--- spec/linter-opencl-spec.coffee
+++ spec/linter-opencl-spec.coffee
@@ -6,7 +6,7 @@

   beforeEach ->
     waitsForPromise ->
-      atom.config.set('linter-opencl.pythonPath', '/usr/bin/python')
+      atom.config.set('linter-opencl.pythonPath', 'python')
       atom.config.set('linter-opencl.openCL.platformIndex', 0)
       atom.config.set('linter-opencl.hybridGraphics.enable', false)
       atom.config.set('linter-opencl.hybridGraphics.offloadingPath', '/usr/bin/optirun')


Add parameters so we can specify flags and classes which don't get wrapped in observables

--- js/inner.coffee
+++ js/inner.coffee
@@ -1,25 +1,34 @@
+window.observableCount = 0
+
 class Base
-
-  constructor: (json, defaults = {}) ->
+  constructor: (json, defaults={}, nonObservables=[], observe=true) ->
     for k,v of defaults
-      @[k] = @observable(v)
+      if observe and nonObservables.indexOf(k) == -1
+        @[k] = @observable(v)
+      else
+        @[k] = v

     for k,v of json
-      @[k] = @observable(v)
+      if observe and nonObservables.indexOf(k) == -1
+        @[k] = @observable(v)
+      else
+        @[k] = v

   observable: (obj) ->
+    observableCount += 1
     if $.isArray obj
       ko.observableArray obj
     else
       ko.observable obj

   komp: (args...) =>
+    observableCount += 1
     ko.computed args...

 class HasUrl extends Base
-  constructor: (json) ->
-    super json
+  constructor: (json, defaults, nonObservables) ->
+    super json, defaults, nonObservables

     @project_name = @komp =>
       @vcs_url().substring(19)
@@ -43,11 +52,7 @@
 class ActionLog extends Base
   constructor: (json) ->
     json.out = (new LogOutput(j) for j in json.out) if json.out
-    super json,
-      timedout: null
-      exit_code: 0
-      out: null
-      minimize: true
+    super json, {timedout: null, exit_code: 0, out: null, minimize: true}

     @status = @komp =>
       if @end_time() == null
@@ -95,19 +100,11 @@
   constructor: (json) ->
     # make the actionlogs observable
     json.action_logs = (new ActionLog(j) for j in json.action_logs) if json.action_logs
-    super json,
-      committer_email: null,
-      committer_name: null,
-      body: null,
-      subject: null
-      user: null
-      branch: null
-      start_time: null
-      why: null
+    super json, {}, ["build_num"]

     @url = @komp =>
-      "#{@project_path()}/#{@build_num()}"
+      "#{@project_path()}/#{@build_num}"

     @style = @komp =>
       klass = switch @status()
@@ -195,15 +192,15 @@

   # TODO: CSRF protection
   retry_build: () =>
-    $.post("/api/v1/project/#{@project_path()}/#{@build_num()}/retry")
+    $.post("/api/v1/project/#{@project_path()}/#{@build_num}/retry")

   description: (include_project) =>
     return unless @build_num?

     if include_project
-      "#{@project_name()} ##{@build_num()}"
+      "#{@project_name()} ##{@build_num}"
     else
-      @build_num()
+      @build_num

@@ -211,7 +208,7 @@
 class Project extends HasUrl
   constructor: (json) ->
     json.latest_build = (new Build(json.latest_build)) if json.latest_build
-    super(json)
+    super json
     @edit_link = @komp () =>
       "#{@project_path()}/edit"

@@ -260,7 +257,7 @@
 # observables are calculated eagerly, and that breaks everything if the
 class ProjectSettings extends HasUrl
   constructor: (json) ->
-    super(json)
+    super json

     @build_url = @komp =>
       @vcs_url() + '/build'
@@ -316,19 +313,14 @@

 class User extends Base
   constructor: (json) ->
-    super json,
-      admin: false
-      login: ""
-      is_new: false
-      environment: "production"
-      basic_email_prefs: "all"
+    super json, {admin: false, login: "", is_new: false, environment: "production", basic_email_prefs: "all"}, [], false

     @showEnvironment = @komp =>
-      @admin() || (@environment() is "staging") || (@environment() is "development")
+      @admin || (@environment is "staging") || (@environment is "development")

     @environmentColor = @komp =>
       result = {}
-      result["env-" + @environment()] = true
+      result["env-" + @environment] = true
       result

   save_preferences: () =>
@@ -336,7 +328,7 @@
       type: "PUT"
       url: "/api/v1/user/save-preferences"
       contentType: "application/json"
-      data: JSON.stringify {basic_email_prefs: @basic_email_prefs()}
+      data: JSON.stringify {basic_email_prefs: @basic_email_prefs}
     )
     false # dont bubble the event up

@@ -350,6 +342,7 @@

 class CircleViewModel extends Base
   constructor: ->
+    observableCount = 0
     @current_user = ko.observable(new User {})
     $.getJSON '/api/v1/me', (data) =>
       @current_user(new User data)
@@ -362,6 +355,8 @@
     @admin = ko.observable()
     @error_message = ko.observable(null)
     @first_login = true;
+    observableCount += 8
+

   clearErrorMessage: () =>


Fix stripTrailingSlash

Fix stripTrailingSlash

--- js/inner.coffee
+++ js/inner.coffee
@@ -327,6 +327,8 @@

 VM = new CircleViewModel()
+stripTrailingSlash = (str) =>
+  str.replace(/(.+)\/$/, "$1")

 $(document).ready () ->


fixed wrong method name

fixed wrong method name

--- src/utils/simplify_claims.coffee
+++ src/utils/simplify_claims.coffee
@@ -16,7 +16,7 @@
     switch datatype
       when 'string', 'commonsMedia' then value = datavalue.value
       when 'wikibase-item' then value = 'Q' + datavalue.value['numeric-id']
-      when 'time' then value = wd_.normalizeTime(datavalue.value.time)
+      when 'time' then value = wd_.normalizeWikidataTime(datavalue.value.time)
       else value = null
     return value
   else


Fix typo

Fix typo

--- src/views/personalisation.coffee
+++ src/views/personalisation.coffee
@@ -74,7 +74,7 @@
         personalisationButtonClick: (ev) ->
             ev?.preventDefault()
             @focusAfterCloseMenu = @_getSelector $(document.activeElement)
-            @focusAfterCloseMenuBackup = @_getSelector @_findBackupParentLink($(document.activeElement)
+            @focusAfterCloseMenuBackup = @_getSelector @_findBackupParentLink($(document.activeElement))
             @toggleMenu(ev)

         toggleMenu: (ev) ->


:art: Fix tests description

:art: Fix tests description

--- spec/table-element-spec.coffee
+++ spec/table-element-spec.coffee
@@ -1332,13 +1332,13 @@
     beforeEach ->
       atom.clipboard.write('foo')

-    it 'deletes the current active column', ->
+    it 'pastes the clipboard content', ->
       tableElement.pasteClipboard()

       expect(tableEditor.getScreenRow(0)).toEqual(['foo', 0, 'yes'])

     describe 'when the read-only attribute is set', ->
-      it 'does not delete the column', ->
+      it 'does not paste the content', ->
         tableElement.setAttribute('read-only', true)
         tableElement.pasteClipboard()

@@ -1348,13 +1348,13 @@
     beforeEach ->
       atom.clipboard.write('foo')

-    it 'deletes the current active column', ->
+    it 'copies and then deletes the selected cells', ->
       tableElement.cutSelectedCells()

       expect(tableEditor.getScreenRow(0)).toEqual([undefined, 0, 'yes'])

     describe 'when the read-only attribute is set', ->
-      it 'does not delete the column', ->
+      it 'copies but does not delete the selected cells', ->
         tableElement.setAttribute('read-only', true)
         tableElement.cutSelectedCells()


Move second multiple-cursor test under the same heading

--- spec/toggle-quotes-spec.coffee
+++ spec/toggle-quotes-spec.coffee
@@ -67,8 +67,6 @@
         expect(editor.getCursors()[0].getBufferPosition()).toEqual [0, 16]
         expect(editor.getCursors()[1].getBufferPosition()).toEqual [1, 16]
         expect(buffer.lineForRow(3)).toBe "console.log('Hello \"World\"');"
-
-    describe "when the cursor is inside multiple quoted strings", ->
       it "performs the quote toggle atomically", ->
         editor.setCursorBufferPosition([0, 16])
         editor.addCursorAtBufferPosition([1, 16])


Add task support for combined battle

Add task support for combined battle

--- views/battle-env.coffee
+++ views/battle-env.coffee
@@ -15,60 +15,111 @@
 bossCell = -1
 currentCell = -1
 battled = false
-nowHp = []
 enemyShipId = []
+_sortieHp = []
+_combinedHp = []
+_enemyHp = []
 # Formation: 0 - 単縦陣, 1 - 複縦陣, 2 - 輪形陣, 3 - 梯形陣, 4 - 単横陣,
 # 5 - 第一警戒航行序列, 6 - 第二警戒航行序列, 7 - 第三警戒航行序列, 8 - 第四警戒航行序列
 enemyFormation = 0
 colorNo = -1

-koukuAttack = (nowHp, kouku) ->
+koukuAttack = (sortieHp, enemyHp, kouku) ->
   if kouku.api_edam?
     for damage, i in kouku.api_edam
       damage = Math.floor(damage)
-      continue if damage <= 0
-      nowHp[i + 5] -= damage
+      if damage > 0
+        enemyHp[i - 1] -= damage
   if kouku.api_fdam?
     for damage, i in kouku.api_fdam
       damage = Math.floor(damage)
-      continue if damage <= 0
-      nowHp[i - 1] -= damage
-openAttack = (nowHp, openingAttack) ->
-  if openingAttack.api_edam?
-    for damage, i in openingAttack.api_edam
-      damage = Math.floor(damage)
-      continue if damage <= 0
-      nowHp[i + 5] -= damage
-  if openingAttack.api_fdam?
-    for damage, i in openingAttack.api_fdam
-      damage = Math.floor(damage)
-      continue if damage <= 0
-      nowHp[i - 1] -= damage
-hougekiAttack = (nowHp, hougeki) ->
+      if damage > 0
+        sortieHp[i - 1] -= damage
+
+supportAttack = (sortieHp, enemyHp, support) ->
+  for damage, i in support
+    damage = Math.floor(damage)
+    continue if damage <= 0 || i > 6
+    enemyHp[i - 1] -= damage
+
+raigekiAttack = (sortieHp, enemyHp, raigeki) ->
+  if raigeki.api_edam?
+    for damage, i in raigeki.api_edam
+      damage = Math.floor(damage)
+      if damage > 0
+        enemyHp[i - 1] -= damage
+  if raigeki.api_fdam?
+    for damage, i in raigeki.api_fdam
+      damage = Math.floor(damage)
+      if damage > 0
+        sortieHp[i - 1] -= damage
+
+hougekiAttack = (sortieHp, enemyHp, hougeki) ->
   for damageFrom, i in hougeki.api_at_list
     continue if damageFrom == -1
     for damage, j in hougeki.api_damage[i]
       damage = Math.floor(damage)
       damageTo = hougeki.api_df_list[i][j]
       continue if damage <= 0
-      nowHp[damageTo - 1] -= damage
-raigekiAttack = (nowHp, raigeki) ->
-  if raigeki.api_edam?
-    for damage, i in raigeki.api_edam
-      damage = Math.floor(damage)
-      continue if damage <= 0
-      nowHp[i + 5] -= damage
-  if raigeki.api_fdam?
-    for damage, i in raigeki.api_fdam
-      damage = Math.floor(damage)
-      continue if damage <= 0
-      nowHp[i - 1] -= damage
-supportAttack = (nowHp, damages) ->
-  for damage, i in damages
-    damage = Math.floor(damage)
-    continue if damage <= 0
-    continue if i > 6
-    nowHp[i + 5] -= damage
+      if damageTo < 7
+        sortieHp[damageTo - 1] -= damage
+      else
+        enemyHp[damageTo - 1 - 6] -= damage
+
+analogBattle = (sortieHp, enemyHp, combinedHp, isCombined, isWater, body) ->
+  # First air battle
+  if body.api_kouku?
+    if body.api_kouku.api_stage3?
+      koukuAttack sortieHp, enemyHp, body.api_kouku.api_stage3
+    if body.api_kouku.api_stage3_combined?
+      koukuAttack combinedHp, enemyHp, body.api_kouku.api_stage3_combined
+  # Second air battle
+  if body.api_kouku2?
+    if body.api_kouku2.api_stage3?
+      koukuAttack sortieHp, enemyHp, body.api_kouku2.api_stage3
+    if body.api_kouku2.api_stage3_combined?
+      koukuAttack combinedHp, enemyHp, body.api_kouku2.api_stage3_combined
+  # Support battle
+  if body.api_support_info?
+    if body.api_support_info.api_support_airatack?
+      supportAttack soriteHp, enemyHp, body.api_support_info.api_support_airatack.api_stage3.api_edam
+    else if body.api_support_info.api_support_hourai?
+      supportAttack soriteHp, enemyHp, body.api_support_info.api_support_hourai.api_damage
+    else
+      supportAttack soriteHp, enemyHp, body.api_support_info.api_damage
+  # Opening battle
+  if body.api_opening_atack?
+    if isCombined?
+      raigekiAttack combinedHp, enemyHp, body.api_opening_atack
+    else
+      raigekiAttack sortieHp, enemyHp, body.api_opening_atack
+  # Night battle
+  if body.api_hougeki?
+    if isCombined?
+      hougekiAttack combinedHp, enemyHp, body.api_hougeki
+    else
+      hougekiAttack sortieHp, enemyHp, body.api_hougeki
+  # First hougeki battle
+  if body.api_hougeki1?
+    if isCombined && !isWater
+      hougekiAttack combinedHp, enemyHp, body.api_hougeki1
+    else
+      hougekiAttack sortieHp, enemyHp, body.api_hougeki1
+  # Second hougeki battle
+  if body.api_hougeki2?
+    hougekiAttack sortieHp, enemyHp, body.api_hougeki2
+  # Combined hougeki battle
+  if body.api_hougeki3?
+    if isCombined && isWater
+      hougekiAttack combinedHp, enemyHp, body.api_hougeki3
+    else
+      hougekiAttack sortieHp, enemyHp, body.api_hougeki3
+  # Raigeki battle
+  if body.api_raigeki?
+    if isCombined?
+      raigekiAttack combinedHp, enemyHp, body.api_raigeki
+    else
+      raigekiAttack sortieHp, enemyHp, body.api_raigeki

 window.addEventListener 'game.response', (e) ->
   {method, path, body, postBody} = e.detail
@@ -102,41 +153,38 @@
         enemyShipId = []
         colorNo = -1
         enemyFormation = 0
-      when '/kcsapi/api_req_sortie/battle'
+      # Normal battle
+      when '/kcsapi/api_req_sortie/battle', '/kcsapi/api_req_battle_midnight/battle', '/kcsapi/api_req_battle_midnight/sp_midnight', '/kcsapi/api_req_sortie/airbattle'
         battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        nowHp = body.api_nowhps.slice 1, 13
-        koukuAttack nowHp, body.api_kouku.api_stage3 if body.api_kouku.api_stage3?
-        openAttack nowHp, body.api_opening_atack if body.api_opening_atack?
-        hougekiAttack nowHp, body.api_hougeki1 if body.api_hougeki1?
-        hougekiAttack nowHp, body.api_hougeki2 if body.api_hougeki2?
-        hougekiAttack nowHp, body.api_hougeki3 if body.api_hougeki3?
-        raigekiAttack nowHp, body.api_raigeki if body.api_raigeki?
-        if body.api_support_info?
-          if body.api_support_info.api_support_airatack?
-            supportAttack nowHp, body.api_support_info.api_support_airatack.api_stage3.api_edam
-          else if body.api_support_info.api_support_hourai?
-            supportAttack nowHp, body.api_support_info.api_support_hourai.api_damage
-          else
-            supportAttack nowHp, body.api_support_info.api_damage
-      when '/kcsapi/api_req_battle_midnight/battle'
+        combined = false
+        _sortieHp = body.api_nowhps.slice(1, 7)
+        _enemyHp = body.api_nowhps.slice(7, 13)
+        enemyShipId = body.api_ship_ke.slice(1, 7)
+        enemyFormation = body.api_formation[1]
+        analogBattle _sortieHp, _enemyHp, _combinedHp, false, false, body
+        console.log [_sortieHp, _combinedHp, _enemyHp]
+      # Event Combined battle
+      when '/kcsapi/api_req_combined_battle/airbattle', '/kcsapi/api_req_combined_battle/battle', '/kcsapi/api_req_combined_battle/midnight_battle', '/kcsapi/api_req_combined_battle/sp_midnight'
         battled = true
-        hougekiAttack nowHp, body.api_hougeki if body.api_hougeki?
-      when '/kcsapi/api_req_battle_midnight/sp_midnight'
+        combined = true
+        _sortieHp = body.api_nowhps.slice(1, 7)
+        _combinedHp = body.api_nowhps_combined.slice(1, 7)
+        _enemyHp = body.api_nowhps.slice(7, 13)
+        enemyShipId = body.api_ship_ke.slice(1, 7)
+        enemyFormation = body.api_formation[1]
+        analogBattle _sortieHp, _enemyHp, _combinedHp, true, false, body
+        console.log [_sortieHp, _combinedHp, _enemyHp]
+      when '/kcsapi/api_req_combined_battle/battle_water'
         battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        nowHp = body.api_nowhps.slice 1, 13
-        hougekiAttack nowHp, body.api_hougeki if body.api_hougeki?
-      when '/kcsapi/api_req_sortie/airbattle'
-        battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        nowHp = body.api_nowhps.slice 1, 13
-        koukuAttack nowHp, body.api_kouku.api_stage3 if body.api_kouku? && body.api_kouku.api_stage3?
-        koukuAttack nowHp, body.api_kouku2.api_stage3 if body.api_kouku2? && body.api_kouku2.api_stage3?
-      when '/kcsapi/api_req_sortie/battleresult'
+        combined = true
+        _sortieHp = body.api_nowhps.slice(1, 7)
+        _combinedHp = body.api_nowhps_combined.slice(1, 7)
+        _enemyHp = body.api_nowhps.slice(7, 13)
+        enemyShipId = body.api_ship_ke.slice(1, 7)
+        enemyFormation = body.api_formation[1]
+        analogBattle _sortieHp, _enemyHp, _combinedHp, true, true, body
+        console.log [_sortieHp, _combinedHp, _enemyHp]
+      when '/kcsapi/api_req_sortie/battleresult', '/kcsapi/api_req_combined_battle/battleresult'
         if battled
           {_decks} = window
           event = new CustomEvent 'battle.result',
@@ -150,62 +198,13 @@
               quest: body.api_quest_name
               enemy: body.api_enemy_info.api_deck_name
               dropShipId: if body.api_get_ship? then body.api_get_ship.api_ship_id else -1
-              deckShipId: Object.clone _decks[deckId].api_ship
-              deckHp: nowHp.slice 0, 6
+              deckShipId: if combined then _decks[0].api_ship.concat(_decks[1].api_ship) else Object.clone _decks[deckId].api_ship
+              deckHp: if combined then _sortieHp.concat(_combinedHp)
               enemyShipId: Object.clone enemyShipId
               enemyFormation: enemyFormation
-              enemyHp: nowHp.slice 6, 12
+              enemyHp: Object.clone _enemyHp
               getEventItem: body.api_get_eventitem?
-          window.dispatchEvent event
-      # Event Combined battle
-      when '/kcsapi/api_req_combined_battle/airbattle'
-        battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        # TODO: Add hp calculate
-        nowHp = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-      when '/kcsapi/api_req_combined_battle/battle'
-        battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        # TODO: Add hp calculate
-        nowHp = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-      when '/kcsapi/api_req_combined_battle/midnight_battle'
-        battled = true
-        # TODO: Add hp calculate
-      when '/kcsapi/api_req_combined_battle/sp_midnight'
-        battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        # TODO: Add hp calculate
-        nowHp = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-      when '/kcsapi/api_req_combined_battle/battle_water'
-        battled = true
-        enemyShipId = body.api_ship_ke.slice 1, 7
-        enemyFormation = body.api_formation[1] if body.api_formation?
-        # TODO: Add hp calculate
-        nowHp = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-      when '/kcsapi/api_req_combined_battle/battleresult'
-        if battled
-          {_decks} = window
-          event = new CustomEvent 'battle.result',
-            bubbles: true
-            cancelable: true
-            detail:
-              rank: body.api_win_rank
-              boss: bossCell == currentCell or colorNo == 5
-              map: map
-              mapCell: currentCell
-              quest: body.api_quest_name
-              enemy: body.api_enemy_info.api_deck_name
-              dropShipId: if body.api_get_ship? then body.api_get_ship.api_ship_id else -1
-              # TODO
-              # deckShipId: Object.clone _decks[deckId].api_ship
-              # deckHp: nowHp.slice 0, 6
-              enemyShipId: Object.clone enemyShipId
-              enemyFormation: enemyFormation
-              enemyHp: nowHp.slice 6, 12
-              getEventItem: body.api_get_eventitem?
+          console.log event.detail
           window.dispatchEvent event
   catch err
     console.error err


PassNextKey; handle pass-next-key in insert mode.

(First "fully" functional version.)

--- content_scripts/mode_insert.coffee
+++ content_scripts/mode_insert.coffee
@@ -10,6 +10,13 @@

     handleKeyEvent = (event) =>
       return @continueBubbling unless @isActive event
+
+      # Check for the pass-next-key key.
+      keyChar = KeyboardUtils.getKeyCharString event
+      if keyChar and keyChar == Settings.get "passNextKeyKey"
+        new PassNextKeyMode
+        return false
+
       return @stopBubblingAndTrue unless event.type == 'keydown' and KeyboardUtils.isEscape event
       DomUtils.suppressKeyupAfterEscape handlerStack
       target = event.srcElement


<tests> No more need for console.log.

<tests> No more need for console.log.

--- tests/fixtures/basicDriver.coffee
+++ tests/fixtures/basicDriver.coffee
@@ -32,12 +32,12 @@
 	else

 		if isCustomIDValidExample newCustomId
-			console.log("DRIVER - UPDATE IS VALID: " + newCustomId)
+			# console.log("DRIVER - UPDATE IS VALID: " + newCustomId)
 			sensors[oldCustomId] = sensors[newCustomId]
 			sensors[newCustomId] = null
 			callback null
 		else
-			console.log("DRIVER - UPDATE IS NOT VALID: " + newCustomId)
+			# console.log("DRIVER - UPDATE IS NOT VALID: " + newCustomId)
 			callback "Invalid CustomId"

 isCustomIDValidExample = (customId) ->


metric: added shares per DAU

metric: added shares per DAU

--- src/models/metric.coffee
+++ src/models/metric.coffee
@@ -41,6 +41,19 @@
           numerator:
             select: 'count(distinct(sessionId))'
             from: 'view'
+            where: (day) ->
+              "time >= #{dayToMS day}ms AND time < #{dayToMS day + 1}ms"
+          denominator:
+            select: 'count(distinct(userId))'
+            from: 'view'
+            where: (day) ->
+              "time >= #{dayToMS day}ms AND time < #{dayToMS day + 1}ms"
+        }
+        {
+          name: 'shares / DAU'
+          numerator:
+            select: 'count(userId)'
+            from: 'share, botShare'
             where: (day) ->
               "time >= #{dayToMS day}ms AND time < #{dayToMS day + 1}ms"
           denominator:


(feature) Show postprocessor errors.

(feature) Show postprocessor errors.

--- src/more_markdown.coffee
+++ src/more_markdown.coffee
@@ -104,4 +104,10 @@
       mdInstance.domReady.fire()

       # start post-processing
-      postProcessors.runProcessors buffer, _.partial swap, active, buffer
+      postProcessors.runProcessors buffer, (err, result) ->
+        if(err)
+          if(err.stack)
+            console.error(err.stack)
+          else
+            console.log(err)
+        swap active, buffer


Added ability to use duplicated day abbreviations

--- src/ng-quick-date.coffee
+++ src/ng-quick-date.coffee
@@ -374,7 +374,7 @@

-              {{day}}
+              {{day}}


Add hover effect to embedded and floating badge

--- src/plugins/badge.coffee
+++ src/plugins/badge.coffee
@@ -391,6 +391,21 @@
       background-image: url('#{asset_url("badge/floating/small/theme/#{configuration.theme}/star_empty@2x.png")}');
     }
   }
+
+  #sa-badge-floating-plugin, #sa-badge-embedded-plugin {
+    -webkit-transform: perspective(1px) translateZ(0);
+    transform: perspective(1px) translateZ(0);
+    -webkit-transition-duration: 0.1s;
+    transition-duration: 0.1s;
+    -webkit-transition-property: transform;
+    transition-property: transform;
+  }
+
+  #sa-badge-floating-plugin:hover, #sa-badge-floating-plugin:focus, #sa-badge-floating-plugin:active,
+  #sa-badge-embedded-plugin:hover, #sa-badge-embedded-plugin:focus, #sa-badge-embedded-plugin:active {
+    -webkit-transform: scale(1.02);
+    transform: scale(1.02);
+  }
   """

   FLOATING_TEMPLATE = (assigns) -> """


Switching auth from username+pwd to API key

Switching config from hard coded variables to env variables

--- src/scripts/asana.coffee
+++ src/scripts/asana.coffee
@@ -5,7 +5,9 @@
 #   None
 #
 # Configuration:
-#   None
+#   HUBOT_ASANA_WORKSPACE_ID
+#   HUBOT_ASANA_PROJECT_ID
+#   HUBOT_ASANA_API_KEY
 #
 # Commands:
 #   todo: @name? <task directive> - public message starting with todo: will add task, optional @name to assign task
@@ -16,13 +18,13 @@

 url  = 'https://app.asana.com/api/1.0'

-workspace = "WORKSPACE_ID"
-project = "PROJECT_ID"
-user = "xxxxx.xxxxxxxxxxxxxxx"
-pass = ""
+workspace = process.env.HUBOT_ASANA_WORKSPACE_ID
+project = process.env.HUBOT_ASANA_PROJECT_ID
+api_key = process.env.HUBOT_ASANA_API_KEY
+

 getRequest = (msg, path, callback) ->
-  auth = 'Basic ' + new Buffer("#{user}:#{pass}").toString('base64')
+  auth = 'Basic ' + new Buffer(api_key + ":").toString('base64')
   msg.http("#{url}#{path}")
     .headers("Authorization": auth, "Accept": "application/json")
     .get() (err, res, body) ->


updated emailer to have the from address be the user's id

--- src/scripts/email.coffee
+++ src/scripts/email.coffee
@@ -22,12 +22,12 @@

 module.exports = (robot) ->
   emailTime = null
-  sendEmail = (addresses, subject, msg) ->
-    mailCommand = """echo '#{msg}' | mail -s '#{subject}' '#{addresses}'"""
+  sendEmail = (recipients, subject, msg, from) ->
+    mailCommand = """echo '#{msg}' | mail -s '#{subject}' -r '#{from}' '#{recipients}'"""
     exec mailCommand, (error, stdout, stderr) ->
       util.print 'stdout: ' + stdout
       util.print 'stderr: ' + stderr

   robot.respond /email (.*) -s (.*) -m (.*)/i, (msg) ->
-    sendEmail msg.match[1], msg.match[2], msg.match[3]
-    msg.send "Email Sent"
+    sendEmail msg.match[1], msg.match[2], msg.match[3], msg.message.user.name
+    msg.send "email sent"


Correctly initialise config for indicator data backup


Fixed body parser

Fixed body parser

--- src/server/server.coffee
+++ src/server/server.coffee
@@ -6,8 +6,6 @@
 routes = require "./routes/index.js"

 app = express()
-parserConf =
-	extended: true
 options =
 	root: __dirname
 port = process.env.PORT || 5000
@@ -18,7 +16,6 @@

 app.use "/assets", express.static "#{__dirname}/assets"
 app.use express.static __dirname
-app.use bodyParser.urlencoded parserConf
 app.use bodyParser.json()

 app.use "/api", router


Get contributions for user

Get contributions for user

--- src/streak-podium.coffee
+++ src/streak-podium.coffee
@@ -18,26 +18,31 @@

 module.exports = (robot) ->
-
-  robot.hear /streak ladder/i, (res) ->
+  robot.hear /org test1/i, (res) ->
+    orgLogin = "pulseenergy"  # TODO: also get this from an env var
     access_token = process.env.HUBOT_ORG_ACCESS_TOKEN
     unless access_token?
-      res.send "Missing ORG_ACCESS_TOKEN in environment: please set and try again"
+      console.log("Missing ORG_ACCESS_TOKEN in environment: please set and try again")
       return
-    res.send "Found the access token in the environment! #{access_token}"
+    console.log("Found the access token in the environment! #{access_token}")
+    ladder = buildLadder(orgLogin, access_token, robot, res)

-  robot.hear /http test/i, (res) ->
-    access_token = process.env.HUBOT_ORG_ACCESS_TOKEN
-    robot.http("https://api.github.com/user?access_token=#{access_token}")
-      .get() (err, response, body) ->
-        res.send "Got a response! #{body}"

-  robot.hear /org test/i, (res) ->
-    orgLogin = "pulseenergy"  # TODO: also get this from an env var
-    access_token = process.env.HUBOT_ORG_ACCESS_TOKEN
-    robot.http("https://api.github.com/orgs/#{orgLogin}/members")
-      .header("accept", "application/json", "Authorization", "token #{access_token}")
-      .get() (err, response, body) ->
-        usersJson = JSON.parse(body)
-        users = (((json) -> return json) json for json in usersJson)
-        res.send users[0].login
+buildLadder = (orgLogin, access_token, robot, res) ->
+  robot.http("https://api.github.com/orgs/#{orgLogin}/members")
+    .header("accept", "application/json", "Authorization", "token #{access_token}")
+    .get() (err, response, body) ->
+      if err
+        console.log("NUTS! #{err} statusCode: #{response.statusCode}")
+      else
+        result = JSON.parse(body)
+        users = (((json) -> return json) json for json in result)
+        getContributions(robot, res, users[0].login)
+
+getContributions = (robot, res, userLogin) ->
+   robot.http("https://github.com/users/#{userLogin}/contributions")
+    .get() (err, response, body) ->
+      if err
+        console.log("#{response.statusCode} getting  contributions for user #{userLogin}")
+      else
+        res.send body


Keep planet in view (fixes #79)

Keep planet in view (fixes #79)

--- app/main.coffee
+++ app/main.coffee
@@ -80,12 +80,14 @@
   # console.log 'hourFrac   = ', hourFrac
   # console.log 'dayFrac    = ', dayFrac

+  dayFrac = 10/24
   scaleFactor = dayFrac
+
   for planet in [$(".bg-planet")...]
-    planet.style.top    = Math.round( scaleFactor * 100) + '%'
-    planet.style.left   = Math.round( scaleFactor * 100) + '%'
-    planet.style.width  = Math.round( scaleFactor * 2*530) + 'px'
-    planet.style.height = Math.round( scaleFactor * 2*530) + 'px'
+    planet.style.top    = Math.round( scaleFactor * 100-30)+20 + '%'
+    planet.style.left   = Math.round( scaleFactor * 100-30)+20 + '%'
+    planet.style.width  = Math.round( scaleFactor * 2*(530-100))+200 + 'px'
+    planet.style.height = Math.round( scaleFactor * 2*(530-100))+200 + 'px'

     # # DEBUG CODE
     # console.log '-------------------------------'


keybinding now does not depends on package "language-agda"

--- keymaps/agda-mode.cson
+++ keymaps/agda-mode.cson
@@ -7,7 +7,7 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'atom-text-editor[data-grammar="source agda"]':
+'atom-text-editor':
   'ctrl-c ctrl-l': 'agda-mode:load'
   'ctrl-c ctrl-x ctrl-q': 'agda-mode:quit'
   'ctrl-c ctrl-x ctrl-r': 'agda-mode:restart'


Removed some debug output

Removed some debug output

--- app/client/lobby/lobby.coffee
+++ app/client/lobby/lobby.coffee
@@ -3,13 +3,9 @@
 # methods

 checkChallenges = (challenges) ->
-  console.log "checkChallenges"
   # check for results
   challenges.forEach (c) ->
-    console.log "check result: #{c}"
-    unless c.challengerId is currentPlayerId()
-      console.log 'continue'
-      return true
+    return true unless c.challengerId is currentPlayerId()

     challengerGame = Games.findOne c.challengerGameId
     challengeeGame = Games.findOne c.challengeeGameId
@@ -31,17 +27,13 @@

       Challenges.update currentChallenge()._id, $set: { notified: true }

-      console.log "break"
       return false

   challenges.rewind()

   # check for challenges
   challenges.forEach (c) ->
-    console.log "check challenge: #{c}"
-    unless c.challengeeId is currentPlayerId()
-      console.log 'continue'
-      return true
+    return true unless c.challengeeId is currentPlayerId()

     challengerGame = Games.findOne c.challengerGameId
     challengeeGame = Games.findOne c.challengeeGameId
@@ -58,7 +50,6 @@
         cancel:  "Nei takk"
         confirm: "Aksepter dyst"

-      console.log 'break'
       return false

 startGame = ({challengeeId, acceptChallengeId}) ->
@@ -96,7 +87,6 @@
     'disabled' unless currentPlayer()?

   challenge: ->
-    console.log 'challenge helper'
     challenges = Challenges.find $or: [
       { challengerId: currentPlayerId() }
     , { challengeeId: currentPlayerId() }


Fix Bars.setValue to deselect bars

Fix Bars.setValue to deselect bars

New bars were being selected, but existing bars that were selected
were not being deselected. This now iterates over all values and toggles
them appropriately.

--- src/coffee/cilantro/ui/infograph/bar.coffee
+++ src/coffee/cilantro/ui/infograph/bar.coffee
@@ -145,11 +145,10 @@
             c._.map @collection.where(selected: true), (model) ->
                 model.get('value')

-        setValue: (values) ->
-            if not values?.length then return
-            for value in values
-                if (model = @collection.findWhere value: value)
-                    model.set 'selected', true
+        setValue: (values=[]) ->
+            # Toggle the selection based on the presence values
+            @collection.each (model) ->
+                model.set('selected', model.get('value') in values)
             return


Correctly count updates

Correctly count updates

--- src/coffee/marketplace-stock-updater.coffee
+++ src/coffee/marketplace-stock-updater.coffee
@@ -82,8 +82,12 @@
                 existingEntry = _.find mappedInventoryEntries, (e) -> e.sku is entry.sku
                 @logger?.debug existingEntry, "Found existing retailer entry for master sku #{entry.sku}"
                 if existingEntry?
-                  @summary.toUpdate++
-                  @inventorySync.buildActions(existingEntry, entry).update()
+                  sync = @inventorySync.buildActions(existingEntry, entry)
+                  if sync.get()
+                    @summary.toUpdate++
+                    sync.update()
+                  else
+                    Q()
                 else
                   @summary.toCreate++
                   @masterClient.inventoryEntries.save(entry)


Set commit processing timeout in the more logical place outside the try block

--- source/server/infrastructure/commit_publisher.coffee
+++ source/server/infrastructure/commit_publisher.coffee
@@ -37,8 +37,8 @@
     @_publishHandle?.stop()

   publishCommit: (commit) =>
+    @_setProcessingTimeout(commit)
     try
-      @_setProcessingTimeout(commit)
       for event in commit.changes.events
         @log.debug @_logMsg("publishing #{event.typeName()}"), event
         @eventBus.publish event


old redirect link

old redirect link

zoonivese.org/signups

--- app/router.cjsx
+++ app/router.cjsx
@@ -16,7 +16,7 @@

   <Route name="unsubscribe" handler={require './pages/unsubscribe'} />

-  <Route path="account" handler={require './pages/sign-in'}>
+  <Route path="accounts?" handler={require './pages/sign-in'}>
     <Route name="sign-in" handler={require './partials/sign-in-form'} />
     <Route name="register" handler={require './partials/register-form'} />
   </Route>


fix(activities): Activities - Adding sound in the instruction input doesn't work (MEF-2722) (#18)

--- cgFileUploadWorker.coffee
+++ cgFileUploadWorker.coffee
@@ -24,7 +24,7 @@
     while start < size
         blobs.push file.slice(start, end)
         start = end
-        end = start + bytes_per_chunk
+        end = start += bytes_per_chunk

     for blob, i in blobs
         response = uploadChunk(blob, url, method, name, i, blobs.length)


feat(worker): allow the use of different method for request

--- cgFileUploadWorker.coffee
+++ cgFileUploadWorker.coffee
@@ -1,6 +1,6 @@
-uploadChunk = (blob, url, filename, chunk, chunks) ->
+uploadChunk = (blob, url, method, filename, chunk, chunks) ->
     xhr = new XMLHttpRequest
-    xhr.open 'POST', url, false
+    xhr.open method, url, false
     xhr.onerror = -> throw new Error 'error while uploading'
     formData = new FormData
     formData.append 'name', filename
@@ -13,6 +13,7 @@
 @onmessage = (e) ->
     file = e.data.file
     url = e.data.url
+    method = e.data.method or 'POST'
     name = e.data.name
     blobs = []
     bytes_per_chunk = 1024 * 1024 * 10 # 10MB
@@ -26,7 +27,7 @@
         end = start + bytes_per_chunk

     for blob, i in blobs
-        response = uploadChunk(blob, url, name, i, blobs.length)
+        response = uploadChunk(blob, url, method, name, i, blobs.length)
         data =
             message: 'progress'
             body: ((i + 1) * 100 / blobs.length).toFixed(0)


disconnect

disconnect

--- lib/header-view.coffee
+++ lib/header-view.coffee
@@ -22,7 +22,8 @@
       @disconnectBtn.disable()

    addConnection: (connectionName) ->
-      @connectionList.append('<option value="' + connectionName + '" selected>' + connectionName + '</option>')
+      @connectionList.append('<option value="' + connectionName + '">' + connectionName + '</option>')
+      @connectionList.children("option[value='" + connectionName + "']").prop('selected', true)
       @connectionList.enable()
       @disconnectBtn.enable()

@@ -30,4 +31,11 @@
       @trigger('data-atom:new-connection')

    onDisconnect: ->
+      # remove the connection from our list
+      #@connectionList.children("option[value='" + connectionName + "']").prop('selected', true)
+      @connectionList.children(":selected").remove()
+      unless @connectionList.children().length
+         @disconnectBtn.disable()
+         @connectionList.disable()
+
       @trigger('data-atom:disconnect')


Update messageHandler.

Update messageHandler.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -130,10 +130,12 @@
       ), 1

   nativeMessage: (name, data) ->
-    if typeof(webkit) != "undefined" && webkit.messageHandlers && webkit.messageHandlers.turbolinksDemo
-      webkit.messageHandlers.turbolinksDemo.postMessage
-        name: name
-        data: data
+    if typeof(webkit) != "undefined"
+      if webkit.messageHandlers
+        if handler = webkit.messageHandlers.feedbin || webkit.messageHandlers.turbolinksDemo
+          handler.postMessage
+            name: name
+            data: data

   scrollBars: ->
     width = 100


Corrige bug em que tooltips não apareciam mais

Agora as tooltips aparecem mas, em compensação, não tem mais o efeito de
transição no hover das bolinhas. É alguma coisa a ver em torno da linha 10100
do nvd3.

Quando ele está criando os vértices e gera o x em 10032, getX(point,pointIndex)
retornava uma string (o mês do ponto). String + integer em JavaScript dá
string, o que quebra todas as contas depois, e o elemento que mostra o tooltip
no hover (.nv-point-clips) acaba ficando fora do gráfico. Colocando o parseInt
desse commit resolve esse problema, mas esses elementos acabam ficando em cima
do que tem a animação.

Me parece um problema do nv. O elemento do hover deveria ser o mesmo do
tooltip.

--- app/assets/javascripts/graph.js.coffee
+++ app/assets/javascripts/graph.js.coffee
@@ -11,7 +11,7 @@
          window.testdata = testdata
          chart = nv.models.multiChart()
                .margin({top: 30, right: 60, bottom: 50, left: 70})
-               .x((d,i) -> d.x)
+               .x((d,i) -> parseInt(d.x))
                .color(d3.scale.category10().range())

          chart.xAxis.showMaxMin(true).tickFormat((d) ->


Simplify syntax highlighting of new notes

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -122,8 +122,9 @@
     # or skip if rendered
     if @isNewNote(note)
       @note_ids.push(note.id)
-      $('ul.main-notes-list').append(note.html)
-      $('.js-syntax-highlight').syntaxHighlight()
+      $('ul.main-notes-list').
+        append(note.html).
+        syntaxHighlight()
       @initTaskList()

   ###


Pass filename to upload create action.

Pass filename to upload create action.

--- app/assets/javascripts/s3_relay.coffee
+++ app/assets/javascripts/s3_relay.coffee
@@ -1,4 +1,4 @@
-saveUrl = (uuid, public_url) ->
+saveUrl = (uuid, filename, public_url) ->
   private_url = null

   $.ajax
@@ -7,6 +7,7 @@
     async: false
     data:
       uuid: uuid
+      filename: filename
       public_url: public_url
     success: (data, status, xhr) ->
       private_url = data.private_url
@@ -19,6 +20,7 @@
   form = fileField.parent()
   fileInput = document.getElementById("file")
   file = fileInput.files[0]
+  fileName = file.name

   $.ajax
     type: "GET"
@@ -46,16 +48,16 @@
       xhr.upload.addEventListener "progress", (ev) ->
         # Progress...
         percentage = ((ev.position / ev.totalSize) * 100.0).toFixed(2) + "%"
-        progressBar.text("Uploading: #{file.name} - " + percentage)
+        progressBar.text("Uploading: #{fileName} - " + percentage)

       xhr.onreadystatechange = (ev) ->
         if xhr.readyState is 4
           if xhr.status == 201
-            progressBar.text("Uploading: #{file.name} - Complete")
+            progressBar.text("Uploading: #{fileName} - Complete")

-            public_url = $("Location", xhr.responseXML).text()
-            private_url = saveUrl(uuid, public_url)
-            link = "#{file.name}"
+            publicUrl = $("Location", xhr.responseXML).text()
+            privateUrl = saveUrl(uuid, fileName, publicUrl)
+            link = "#{fileName}"

             progressBar.html(link)
           else


faster redirect after the next button is pressed

--- app/assets/javascripts/words.js.coffee
+++ app/assets/javascripts/words.js.coffee
@@ -8,7 +8,7 @@
   # redirect action to /next
   jumptoNext = ->
     nextPath = -> window.location = '/next'
-    setTimeout nextPath, 400
+    setTimeout nextPath, 200

   highlightButton = ->
     for i in [0..8]


Refresh accounts and transactions after rename

--- app/js/services/wallet.coffee
+++ app/js/services/wallet.coffee
@@ -188,6 +188,8 @@

     wallet_rename_account: (current_name, new_name) ->
         @rpc.request('wallet_rename_account', [current_name, new_name]).then (response) =>
+          @refresh_accounts().then =>
+              @refresh_transactions()
           response.result

     wallet_unlock: (password)->


:art: Append _ to private stuff in linter-plus

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -6,23 +6,23 @@

 class Linter
   constructor: ->
-    @subscriptions = new CompositeDisposable
     @lintOnFly = true # A default art value, to be immediately replaced by the observe config below
+    @_subscriptions = new CompositeDisposable

-    @emitter = new Emitter
-    @views = new LinterViews this
-    @messagesProject = new Map
+    @_emitter = new Emitter
+    @_editorLinters = new Map
+    @views = new LinterViews this # Used by editor-linter to trigger views.render
+    @messagesProject = new Map # Values set in editor-linter and consumed in views.render
     @activeEditor = atom.workspace.getActiveTextEditor()
-    @editorLinters = new Map
     @h = H
-    @linters = []
+    @linters = [] # Values are pushed here from Main::consumeLinter

     # Bubble
-    @subscriptions.add atom.config.observe 'linter.showErrorInline', (showBubble) =>
+    @_subscriptions.add atom.config.observe 'linter.showErrorInline', (showBubble) =>
       @views.showBubble = showBubble
-    @subscriptions.add atom.config.observe 'linter-plus.lintOnFly', (value) =>
+    @_subscriptions.add atom.config.observe 'linter-plus.lintOnFly', (value) =>
       @lintOnFly = value
-    @subscriptions.add atom.workspace.onDidChangeActivePaneItem (editor) =>
+    @_subscriptions.add atom.workspace.onDidChangeActivePaneItem (editor) =>
       @activeEditor = editor
       # Exceptions thrown here prevent switching tabs
       try
@@ -30,30 +30,30 @@
         @views.render()
       catch error
         atom.notifications.addError error.message, {detail: error.stack, dismissable: true}
-    @subscriptions.add atom.workspace.observeTextEditors (editor) =>
+    @_subscriptions.add atom.workspace.observeTextEditors (editor) =>
       currentEditorLinter = new EditorLinter @, editor
-      @editorLinters.set editor, currentEditorLinter
-      @emitter.emit 'linters-observe', currentEditorLinter
+      @_editorLinters.set editor, currentEditorLinter
+      @_emitter.emit 'linters-observe', currentEditorLinter
       currentEditorLinter.lint false
       editor.onDidDestroy =>
         currentEditorLinter.destroy()
-        @editorLinters.delete currentEditorLinter
+        @_editorLinters.delete currentEditorLinter

   getActiveEditorLinter: ->
     return @getLinter(@activeEditor)

   getLinter: (editor) ->
-    return @editorLinters.get editor
+    return @_editorLinters.get editor

   eachLinter: (callback) ->
-    @h.genValue(@editorLinters, callback)
+    @h.genValue(@_editorLinters, callback)

   observeLinters: (callback) ->
     @eachLinter callback
-    @emitter.on 'linters-observe', callback
+    @_emitter.on 'linters-observe', callback

   deactivate: ->
-    @subscriptions.dispose()
+    @_subscriptions.dispose()
     @eachLinter (linter) ->
       linter.subscriptions.dispose()
     @views.deactivate()


Put parantheses around template

Put parantheses around template

As per @markijbema's comments

--- app/js/views/templates.coffee
+++ app/js/views/templates.coffee
@@ -14,7 +14,7 @@
 Factlink.templates.preload = ->
   Factlink.templates.getTemplate 'indicator'
   Factlink.templates.getTemplate 'create', (template) ->
-    Factlink.prepare = new Factlink.Prepare template
+    Factlink.prepare = new Factlink.Prepare(template)

 fetchTemplate = (str, callback) ->
   requesting[str] = true


display flicker view after 'view' tutorial step

--- app/lib/tutorial-steps.coffee
+++ app/lib/tutorial-steps.coffee
@@ -32,6 +32,8 @@
     focus: '#views'
     attachment: 'left center #views right center'
     next: 'guide'
+    onExit: ->
+      window.classifier.onClickFlicker()

   guide: new Step
     header: t 'span', 'tutorial.guide.header'


Pull out fetch by time

Pull out fetch by time

--- scripts/rc.coffee
+++ scripts/rc.coffee
@@ -67,7 +67,10 @@
   fetch: (cb) =>
     @storage.zscore @key("latest"), @who, (err, stamp) =>
       return cb(err, null, this) if err
-      @storage.zscore @key("#{@who}:scores"), stamp, (err, score) =>
-        return cb(err, null, this) if err
-        @info = {score: score, when: stamp}
-        cb(false, @info, this)
+      @fetch_by_time stamp, cb
+
+  fetch_by_time: (time, cb) =>
+    @storage.zscore @key("#{@who}:scores"), time, (err, score) =>
+      return cb(err, null, this) if err
+      @info = {score: score, when: time}
+      cb(false, @info, this)


update bup_server host list

update bup_server host list

--- salvus/bup_server.coffee
+++ salvus/bup_server.coffee
@@ -2030,14 +2030,8 @@
                             else
                                 v = program.address.split('.')
                                 a = parseInt(v[1]); b = parseInt(v[3])
-                                if program.address == '10.1.1.10'
+                                if program.address == '10.1.1.10'  or a == 1 or a == 3 or a == 4
                                     hosts = ("smc#{i}dc5" for i in [1..5])
-                                else if a == 1 and b>=1 and b<=7
-                                    hosts = ("10.1.#{i}.2" for i in [1..7])
-                                else if a == 1 and b>=10 and b<=21
-                                    hosts = ("10.1.#{i}.2" for i in [10..21])
-                                else if a == 3 or a == 4
-                                    hosts = ("10.#{a}.#{i}.2" for i in [1..4])
                                 else if a == 5 or a == 6 or a == 7
                                     hosts = ("smc#{i}dc#{a}" for i in [1..5])
                             winston.debug("database hosts=#{misc.to_json(hosts)}")


correct check, help only shows in os other than macosx

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -386,7 +386,7 @@
               click: () -> action 'show-about'
             }
           ]
-        } if isDarwin
+        } if !isDarwin
         {
             label: 'Window'
             submenu: templateWindow viewstate


Fix spelling

Fix spelling

--- test/iota-observable-test.coffee
+++ test/iota-observable-test.coffee
@@ -7,9 +7,7 @@
 chai.use(sinonChai)

 # TODO
-# - Implement transactions (delayed observer invocations) - also use them in set() with map!
-# - Prevent multiple invocation of observers in one set
-# - Implement throotled observer invocations
+# - Implement throttled observer invocations
 # - add dependency old/new

 ## Reused stuff