Flag computation functions refactoring.

Flag computation functions refactoring.

--- src/CPU.coffee
+++ src/CPU.coffee
@@ -1,26 +1,17 @@
-Bit0 = 1 << 0
-Bit1 = 1 << 1
-Bit2 = 1 << 2
-Bit3 = 1 << 3
-Bit4 = 1 << 4
-Bit5 = 1 << 5
-Bit6 = 1 << 6
-Bit7 = 1 << 7
-
 AddressingMode =
-    Implied:          1
-    Accumulator:      2
-    Immediate:        3
-    ZeroPage:         4
-    ZeroPageX: 5
-    IndexedYZeroPage: 6
-    Absolute:         7
-    AbsoluteX: 8
-    AbsoluteY: 9
-    Relative:         10
-    Indirect:         11
-    IndirectX: 12
-    IndirectY: 13
+    Implied:      1
+    Accumulator:  2
+    Immediate:    3
+    ZeroPage:     4
+    ZeroPageX:    5
+    ZeroPageY:    6
+    Absolute:     7
+    AbsoluteX:    8
+    AbsoluteY:    9
+    Relative:    10
+    Indirect:    11
+    IndirectX:   12
+    IndirectY:   13

 Instruction =
     ADC:  1
@@ -55,6 +46,7 @@
     LDA: 30
     LDX: 31
     LDY: 32
+    LSR: 33

 Interrupt =
     IRQ:   1
@@ -132,7 +124,7 @@
     handleInterrupt: (interruptVectorAddress)->
         @pushWord @programCounter
         @pushByte @getStatus()
-        @interruptDisable = 1
+        @interruptDisable = on
         @programCounter = @readWord interruptVectorAddress

     readOperation: ->
@@ -201,23 +193,23 @@

     getStatus: ->
         status = 0
-        status |= Bit0 if @carryFlag
-        status |= Bit1 if @zeroFlag
-        status |= Bit2 if @interruptDisable
-        status |= Bit3 if @decimalMode
-        status |= Bit4 if @breakCommand
-        status |= Bit6 if @overflowFlag
-        status |= Bit7 if @negativeFlag
+        status |= (1 << 0) if @carryFlag
+        status |= (1 << 1) if @zeroFlag
+        status |= (1 << 2) if @interruptDisable
+        status |= (1 << 3) if @decimalMode
+        status |= (1 << 4) if @breakCommand
+        status |= (1 << 6) if @overflowFlag
+        status |= (1 << 7) if @negativeFlag
         status

     setStatus: (status) ->
-        @carryFlag        = (status & Bit0) != 0
-        @zeroFlag         = (status & Bit1) != 0
-        @interruptDisable = (status & Bit2) != 0
-        @decimalMode      = (status & Bit3) != 0
-        @breakCommand     = (status & Bit4) != 0
-        @overflowFlag     = (status & Bit6) != 0
-        @negativeFlag     = (status & Bit7) != 0
+        @carryFlag        = @isBitSet status, 0
+        @zeroFlag         = @isBitSet status, 1
+        @interruptDisable = @isBitSet status, 2
+        @decimalMode      = @isBitSet status, 3
+        @breakCommand     = @isBitSet status, 4
+        @overflowFlag     = @isBitSet status, 6
+        @negativeFlag     = @isBitSet status, 7

     tick: ->
         @cycle++
@@ -300,30 +292,30 @@
         @registerInstruction Instruction.ADC, (address) ->
             operand = @readByte address
             result = @accumulator + operand
-            result++ if @carryFlag
-            @computeCarryFlag result
-            @computeZeroFlag result
-            @computeOverflowFlag @accumulator, operand, result
-            @computeNegativeFlag result
+            result++ if @carryFlag is on
+            @carryFlag = @isOverflow result
+            @zeroFlag = @isZero result
+            @overflowFlag = @isSignedOverflow @accumulator, operand, result
+            @negativeFlag = @isNegative result
             @accumulator = result & 0xFF

         @registerInstruction Instruction.AND, (address) ->
             @accumulator &= @readByte address
-            @computeZeroFlag @accumulator
-            @computeNegativeFlag @accumulator
+            @zeroFlag = @isZero @accumulator
+            @negativeFlag = @isNegative @accumulator

         @registerInstruction Instruction.ASL, (address) ->
             if address?
                 result = (@readByte address) << 1
-                @computeCarryFlag result
-                @computeZeroFlag result
-                @computeNegativeFlag result
+                @carryFlag = @isOverflow result
+                @zeroFlag = @isZero result
+                @negativeFlag = @isNegative result
                 @writeByte address, result & 0xFF
             else
                 result = @accumulator << 1
-                @computeCarryFlag result
-                @computeZeroFlag result
-                @computeNegativeFlag result
+                @carryFlag = @isOverflow result
+                @zeroFlag = @isZero result
+                @negativeFlag = @isNegative result
                 @accumulator = result & 0xFF

         @registerInstruction Instruction.BCC, (address) ->
@@ -337,21 +329,21 @@

         @registerInstruction Instruction.BIT, (address) ->
             result = @accumulator & @readByte address
-            @computeZeroFlag result
-            @overflowFlag = result & Bit7 != 0 # Exception on overflow computation
-            @computeNegativeFlag result
+            @zeroFlag = @isZero result
+            @overflowFlag = @isBitSet result, 7
+            @negativeFlag = @isNegative result

         @registerInstruction Instruction.BMI, (address) ->
-            @branchIfTrue @negativeFlag, address
+            @branchIf @negativeFlag is on, address

         @registerInstruction Instruction.BNE, (address) ->
-            @branchIfFalse @zeroFlag,  address
+            @branchIf @zeroFlag is off, address

         @registerInstruction Instruction.BPL, (address) ->
-            @branchIfFalse @negativeFlag,  address
+            @branchIf @negativeFlag is off, address

         @registerInstruction Instruction.BRK, ->
-            @breakCommand = 1
+            @breakCommand = on
             @handleInterrupt 0xFFFE

         @registerInstruction Instruction.BVC, (address) ->
@@ -383,40 +375,40 @@

         @registerInstruction Instruction.DEC, (address) ->
             result = (@readByte address) - 1
-            @computeZeroFlag result
-            @computeNegativeFlag result
-            @writeByte address, result
+            @zeroFlag = @isZero result
+            @negativeFlag = @isNegative result
+            @writeByte address, result & 0xFF

         @registerInstruction Instruction.DEX, ->
             @registerX = (@registerX - 1) & 0xFF
-            @computeZeroFlag @registerX
-            @computeNegativeFlag @registerX
+            @zeroFlag = @isZero @registerX
+            @negativeFlag = @isNegative @registerX

         @registerInstruction Instruction.DEY, ->
             @registerY = (@registerY - 1) & 0xFF
-            @computeZeroFlag @registerY
-            @computeNegativeFlag @registerY
+            @zeroFlag = @isZero @registerY
+            @negativeFlag = @isNegative @registerY

         @registerInstruction Instruction.EOR, (address) ->
             @accumulator ^= @readByte address
-            @computeZeroFlag @accumulator
-            @computeNegativeFlag @accumulator
+            @zeroFlag = @isZero @accumulator
+            @negativeFlag = @isNegative @accumulator

         @registerInstruction Instruction.INC, (address) ->
             result = (@readByte address) + 1
-            @computeZeroFlag result
-            @computeNegativeFlag result
+            @zeroFlag = @isZero result
+            @negativeFlag = @isNegative result
             @writeByte address, result & 0xFF

         @registerInstruction Instruction.INX, ->
             @registerX = (@registerX + 1) & 0xFF
-            @computeZeroFlag @registerX
-            @computeNegativeFlag @registerX
+            @zeroFlag = @isZero @registerX
+            @negativeFlag = @isNegative @registerX

         @registerInstruction Instruction.INY, ->
             @registerY = (@registerY + 1) & 0xFF
-            @computeZeroFlag @registerY
-            @computeNegativeFlag @registerY
+            @zeroFlag = @isZero @registerY
+            @negativeFlag = @isNegative @registerY

         @registerInstruction Instruction.JMP, (address) ->
             @programCounter = address
@@ -428,33 +420,50 @@

         @registerInstruction Instruction.LDA, (address) ->
             @accumulator = @readByte address
-            @computeZeroFlag @accumulator
-            @computeNegativeFlag @accumulator
+            @zeroFlag = @isZero @accumulator
+            @negativeFlag = @isNegative @accumulator

         @registerInstruction Instruction.LDX, (address) ->
             @registerX = @readByte address
-            @computeZeroFlag @registerX
-            @computeNegativeFlag @registerX
+            @zeroFlag = @isZero @registerX
+            @negativeFlag = @isNegative @registerX

         @registerInstruction Instruction.LDY, (address) ->
             @registerY = @readByte address
-            @computeZeroFlag @registerY
-            @computeNegativeFlag @registerY
+            @zeroFlag = @isZero @registerY
+            @negativeFlag = @isNegative @registerY
+
+        @registerInstruction Instruction.LSR, (address) ->
+            if address?
+                result = @readByte address
+                @carryFlag = @isSetBit result, 1
+                result >>= 1
+                @zeroFlag = @isZero result
+                @negativeFlag = @isNegative result
+                @writeByte address, result
+            else
+                @carryFlag = @isSetBit @accumulator, 1
+                @accumulator >>= 1
+                @zeroFlag = @isZero result
+                @negativeFlag = @isNegative result

     registerInstruction: (instruction, execution) ->
         @instructionsTable[instruction] = execution

-    computeCarryFlag: (result) ->
-        @carryFlag = result > 0xFF
-
-    computeZeroFlag: (result) ->
-        @zeroFlag = result & 0xFF != 0
-
-    computeOverflowFlag: (operand1, operand2, result) ->
-        @overflowFlag = (operand1 ^ result) & (operand2 ^ result) & Bit7 != 0
-
-    computeNegativeFlag: (result) ->
-        @negativeFlag = result & Bit7 != 0
+    isBitSet: (value, bit) ->
+        value & (1 << bit) != 0
+
+    isZero: (value) ->
+        value & 0xFF != 0
+
+    isNegative: (value) ->
+        @isBitSet value, 7
+
+    isOverflow: (value) ->
+        result > 0xFF
+
+    isSignedOverflow: (operand1, operand2, result) ->
+        @isBitSet (operand1 ^ result) & (operand2 ^ result), 7

     branchIf: (condition, address) ->
         if condition


Activity: rewired show prev replies private message pane

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -34,7 +34,7 @@
     list.on 'ItemWasRemoved',   @bound 'messageRemoved'
     list.on 'EditMessageReset', @input.bound 'focus'

-    KD.singleton('windowController').on 'ScrollHappened', @bound 'handleScroll'
+    @on 'TopLazyLoadThresholdReached', KD.utils.throttle 200, @bound 'listPreviousReplies'


Registration: when user log-in re-draw filetree

--- client/ide/filetree/controllers/findercontroller.coffee
+++ client/ide/filetree/controllers/findercontroller.coffee
@@ -28,6 +28,11 @@
     @appStorage = KD.getSingleton('appStorageController').storage 'Finder', '1.1.1'

     @watchers = {}
+
+    # this is here for when user login/register after opening Ace App
+    # to refresh filetree
+    mainController = KD.getSingleton("mainController")
+    mainController.on "accountChanged.to.loggedIn", @bound 'reset'

     if options.useStorage
       @appStorage.ready =>


login: add default value to redirectTo

login: add default value to redirectTo

--- client/landing/site.landing/coffee/login/AppView.coffee
+++ client/landing/site.landing/coffee/login/AppView.coffee
@@ -404,7 +404,9 @@

     {username, redirectTo} = formData

-    query = ''
+    redirectTo ?= ''
+    query       = ''
+
     if redirectTo is 'Pricing'
       { planInterval, planTitle } = formData
       query = KD.utils.stringifyQuery {planTitle, planInterval}
@@ -437,7 +439,8 @@

     {username, password, redirectTo} = formData

-    query = ''
+    redirectTo ?= ''
+    query       = ''
     if redirectTo is 'Pricing'
       { planInterval, planTitle } = formData
       query = KD.utils.stringifyQuery {planTitle, planInterval}


fix(Resource): fix m2m bug introduced with 156441f.

--- src/ORM.coffee
+++ src/ORM.coffee
@@ -698,7 +698,7 @@
     _fetchM2M: ->
       fetchM2M = (instance, m2m, promises, collections) ->
         fieldName = m2m.name
-        if (fieldName of instance) and instance[fieldName]? and isKeyLike(instance[fieldName])
+        if (fieldName of instance) and instance[fieldName]? and angular.isArray(instance[fieldName])
           refs_promises = []
           refs_collection = m2m.model._MakeCollection()
           for ref_id in instance[fieldName]


Make nav/button text configurable fixes #8

--- src/index.cjsx
+++ src/index.cjsx
@@ -8,7 +8,10 @@
   displayName: 'MarkdownTextarea'

   getDefaultProps: ->
-    buttonText: "Save"
+    saveButtonText: "Save"
+    deleteButtonText: "Delete"
+    writeTabText: "write"
+    previewTabText: "preview"
     onSave: (value) ->
     onDelete: ->
     onChange: (value) ->
@@ -80,8 +83,8 @@
     unless @props.noPreview
       tabs =

-
Write

-
Preview

+
{@props.writeTabText}

+
{@props.previewTabText}

     return (
@@ -105,7 +108,7 @@
               onClick={@_onDelete}
               disabled={if @props.saving then "disabled" else false}
               className="react-markdown-textarea__delete-button">
-                Delete
+                {@props.deleteButtonText}
             </button>
           }
           {if @props.saving and @props.spinner?


Add empty settings check and log message

--- src/ubs.coffee
+++ src/ubs.coffee
@@ -90,6 +90,11 @@

   .then (tasks) ->
     logging.debug "Tasks #{util.inspect tasks, undefined, 4}"
+
+    # That may happen, log for informational purposes only.
+    unless tasks.settings
+      logging.info 'Settings empty. Using defaults.'
+      tasks.settings = {}

     # If we have init then parse it before all other action
     # At the moment only 'plugins' is recognized but it may allow future


raw method to add html without a problem

--- src/xml.coffee
+++ src/xml.coffee
@@ -1,6 +1,6 @@
 { EventEmitter } = require 'events'
 { deep_merge, prettify, new_attrs, safe } = require './util'
-EVENTS = ['add', 'attr', 'attr:remove', 'text', 'remove', 'close']
+EVENTS = ['add','attr','attr:remove','text','raw','remove','close']

 parse_args = (name, attrs, children, opts) ->
@@ -157,6 +157,11 @@
         @emit('text', this, @content)
         this

+    raw: (html, opts = {}) =>
+        @write html, deep_merge(opts, escape:off) # this is raw html, so no escape
+        @emit('raw', this, html)
+        this
+
     write: (content, {escape} = {}) =>
         content = safe(content) if escape
         if @isempty


:bug: Derp, used wrong variable

:bug: Derp, used wrong variable

--- src/markov.coffee
+++ src/markov.coffee
@@ -93,9 +93,8 @@
     # Chance to randomly respond un-prompted
     if pct > 0 and Math.random() < pct
       words = msg.message.text.match /(?=\s*)\w+(?=\s*)/g
-      # seeds = words.split /\s+/
-      randword = Math.floor(Math.random() * seeds.length)
-      seed = seeds[randword]
+      randword = Math.floor(Math.random() * words.length)
+      seed = words[randword]
       model.generate seed[0] or '', max, (text) =>
         msg.send text


Add date to fixme comment

Add date to fixme comment

--- src/models.coffee
+++ src/models.coffee
@@ -929,7 +929,7 @@
                 q: @query
                 language: p13n.getLanguage()
                 type: 'unit,service,address'
-                # FIXME uncomment once backend works
+                # FIXME 2018-11-09 uncomment when the api supports the field filter for services
                 # only: 'unit.name,unit.location,unit.root_service_nodes,unit.contract_type,service.name'
                 include: 'unit.accessibility_properties,unit.service_nodes,unit.services'
             cities = _.map p13n.getCities(), (c) -> c.toLowerCase()


allow to jump to label :1

allow to jump to label :1

--- src/parser.coffee
+++ src/parser.coffee
@@ -18,7 +18,7 @@
 LABEL_RE = /:(\w+)$/
 REGISTER_RE = /^\s*(R10|R\d|PC|AC|MAR|MBR)\s*$/i
 ALU_RE = /^(lsh|rsh\()?\(?(~)?(R10|R\d|PC|AC|MBR|\-?1|0)\s*([+,&])?\s*\(?(R10|R\d|PC|AC|MBR|\-?1|0)?\)?\)?$/i
-GOTO_RE = /^(?:if\s+(N|Z))?\s*goto\s+(\d+|\.[a-zA-Z]\w+)$/i
+GOTO_RE = /^(?:if\s+(N|Z))?\s*goto\s+(\d+|\.\w+)$/i

 exports = class Parser
     constructor: ->
@@ -191,7 +191,7 @@
         return code

     getLocation: (target, i) ->
-        addr = parseInt(if /\.[a-zA-Z]\w+/.test(target) then @label[target.slice(1)] else target)
+        addr = parseInt(if /\.\w+/.test(target) then @label[target.slice(1)] else target)

         if isNaN(addr) or addr == undefined or addr == null
             throw { name: "SyntaxError", message: "Label \"" + target + "\" not found", line: i }


- [grunt] pngsmith image engine replaced with ImageMagick

--- tasks/grunt-spritesmith.coffee
+++ tasks/grunt-spritesmith.coffee
@@ -6,11 +6,13 @@
 module.exports = ->
   @config 'sprite',
     build:
-      src: ['<%= path.source.sprites %>/{,**/}*.png']
+      src: ['<%= path.source.sprites %>/{,**/}*']
       destImg: '<%= file.build.sprite %>'
       destCSS: '<%= path.source.styles %>/generic/_sprites.map.scss'
       padding: 2
-      engine: 'pngsmith'
+      engine: 'gm'
+      engineOpts:
+        imagemagick: true
       algorithm: 'binary-tree'
       cssTemplate: '<%= path.source.styles %>/generic/_sprites.map.mustache'
       cssVarMap: (sprite) ->


Move the drawing controls above the decision tree box Hopefully, this will help people who missed them.

--- templates/classify-page.coffee
+++ templates/classify-page.coffee
@@ -10,8 +10,6 @@

-

-

           <input type="checkbox" name="help" />
@@ -52,6 +50,8 @@
           Original page

+

+


Take out extra space in route

Take out extra space in route

--- src/routes.coffee
+++ src/routes.coffee
@@ -87,7 +87,6 @@
         if err
           res.writeHead 500,
             "Content-Type": "text/plain"
-
           res.end err.message
         else
           res.writeHead 204


runner: Allow runtime to have online=false temporarily

The iframe-runtime acts like this

--- src/runner.coffee
+++ src/runner.coffee
@@ -18,13 +18,15 @@
     @currentGraphId = null

   # TODO: check the runtime capabilities before continuing
+  # TODO: have a timeout
   connect: (callback) ->
     debug 'connect'
     onStatus = (status) =>
-      err = if status.online then null else new Error 'Runtime not online after connect()'
+      return if not status.online # ignore, might get false before getting a true
+
       @client.removeListener 'status', onStatus
-      debug 'connected', err
-      return callback err
+      debug 'connected', status
+      return callback null
     @client.on 'status', onStatus
     @client.connect()


Fixed scores when match hasn't started yet

--- src/soccer.coffee
+++ src/soccer.coffee
@@ -46,7 +46,9 @@
         respArray.push "==========="
         for game in data.games
             if game.league? and game.goalsAwayTeam? and game.goalsHomeTeam? and game.time? and game.homeTeamName? and game.awayTeamName?
-                g = "#{game.league}: #{game.homeTeamName} #{game.goalsHomeTeam} : #{game.goalsAwayTeam} #{game.awayTeamName} (#{game.time})"
+                goalsHome = if game.goalsHomeTeam < 0 then "" else game.goalsHomeTeam
+                goalsAway = if game.goalsAwayTeam < 0 then "" else game.goalsAwayTeam
+                g = "#{game.league}: #{game.homeTeamName} #{goalsHome} : #{goalsAway} #{game.awayTeamName} (#{game.time})"
                 respArray.push g

         return respArray.join("\n")


Sync (bool)/async (promise) transition rejection

Views and controllers with a uiCanExit method will have that method
called before allowing a transition to occur. If the method returns
false or a promise that is later rejected, the transition is
aborted and the router remains in the original state.

--- src/uiview.coffee
+++ src/uiview.coffee
@@ -71,6 +71,9 @@
         @listenToOnce view, "destroy", ->
           controller.destroy()

+    state = newConfig.path[newConfig.path.length-1].state.self
+    @registerEventCallbacks(state, view, controller)
+
   getResolved: (config) ->
     # Map all resolved objects (plus $stateParams and $transition$)
     # to a plain object to pass to the view and controller
@@ -89,6 +92,18 @@
     if config?.viewDecl?.controller?
       return new config.viewDecl.controller(controllerOptions)

+  registerEventCallbacks: (state, view, controller) ->
+    criteria = {exiting: state.name}
+    @registerExitCallback(view, criteria) if view?
+    @registerExitCallback(controller, criteria) if controller?
+
+  registerExitCallback: (component, criteria) ->
+    # call the view or controller's uiCanExit method to determine if we should
+    # leave the current state.
+    if typeof component.uiCanExit is 'function'
+      deregisterFn = @router.transitionService.onBefore criteria, component.uiCanExit, bind: component
+      component.on "destroy", -> console.log("dregistering uiCanExit for #{criteria.exiting}") && deregisterFn()
+
   clearPreviousConfig: ->
     @mnRegion.empty()
     @activeUIView.view? && @activeUIView.controller?.triggerMethod('view:destroyed')


Workaround for focus of webview contents

close https://github.com/poooi/poi/issues/1059

--- views/services/layout.coffee
+++ views/services/layout.coffee
@@ -42,6 +42,7 @@
     factor = Math.max(window.webviewWidth / 800.0 * 100 / 100.0, 0.00125)
   window.webviewFactor = factor

+  webviewBounds = webview?.getBoundingClientRect() || {}
   # Autoset style
   if window.layout == 'horizontal'
     tabpaneHeight = "#{window.innerHeight / window.zoomLevel - poiControlHeight}px"
@@ -70,6 +71,14 @@
     poi-nav poi-nav-tabs .nav .dropdown-menu {
       max-height: #{tabpaneHeight};
       overflow: auto;
+    }
+    .webview-focus-area {
+      top: #{webviewBounds.top}px;
+      left: #{webviewBounds.left}px;
+      width: #{webviewBounds.width}px;
+      height: #{webviewBounds.height}px;
+      position: absolute;
+      pointer-events: none;
     }
     """
   # Resize when window size smaller than webview size
@@ -214,17 +223,21 @@
 window.addEventListener 'game.start', adjustSize

 remote.getCurrentWebContents().on 'dom-ready', ->
+  # Workaround for drag area
   if process.platform == 'darwin'
-    remote.getCurrentWebContents().executeJavaScript """
-      var div = document.createElement("div");
-      div.style.position = "absolute";
-      div.style.top = 0;
-      div.style.height = "23px";
-      div.style.width = "100%";
-      div.style["-webkit-app-region"] = "drag";
-      div.style["pointer-events"] = "none";
-      document.body.appendChild(div);
-    """
+    titleBarArea = document.createElement("div");
+    titleBarArea.style.position = "absolute";
+    titleBarArea.style.top = 0;
+    titleBarArea.style.height = "23px";
+    titleBarArea.style.width = "100%";
+    titleBarArea.style["-webkit-app-region"] = "drag";
+    titleBarArea.style["pointer-events"] = "none";
+    document.body.appendChild(titleBarArea);
+  # Workaround for webview focus area
+  webviewFocusArea = document.createElement("div");
+  webviewFocusArea.className = "webview-focus-area"
+  webviewFocusArea.onclick = () => $('webview').focus();
+  document.body.appendChild(webviewFocusArea);
   $('kan-game webview').setAudioMuted(true) if config.get 'poi.content.muted', false
   $('kan-game webview').loadURL config.get 'poi.homepage', 'http://www.dmm.com/netgame/social/application/-/detail/=/app_id=854854/'
   $('kan-game webview').addEventListener 'page-title-set', handleTitleSet


add cloudage because of warmer climate

add cloudage because of warmer climate

--- atmospheric/001-balanced-temperature/avgtfield.coffee
+++ atmospheric/001-balanced-temperature/avgtfield.coffee
@@ -21,9 +21,9 @@
     acapacity = (t) -> 1005

     shtAbsorb = (t) -> (t > 263.15) ? 0.7 : 0.3
-    shtAbsorbLand = 168 / 342
-    shtAbsorbAir  =  67 / 342
-    lngAbsorbAir  =  350 / 390
+    shtAbsorbLand = 150 / 342
+    shtAbsorbAir  =  50 / 342
+    lngAbsorbAir  =  370 / 390
     landOutputRatio = (390 + 24 + 78) / 390

     da = 2 * Math.PI / 256 / 2


groups: define function `JGroup#checkUserUsage`

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -226,6 +226,8 @@
           (signature Function)
         fetchInvitationsByStatus:
           (signature Object, Function)
+        checkUserUsage:
+          (signature Function)
     schema          :
       title         :
         type        : String
@@ -1560,3 +1562,10 @@
       JVM.createVm {account, groupSlug: @slug, @planCode}, (err) =>
         console.warn "Group #{@slug} member #{account.profile.nickname} VM is not created: #{err}"  if err
         callback()
+
+  checkUserUsage: (callback) ->
+    @fetchSubscription (err, subscription) ->
+      return callback err  if err
+      JPaymentPack.one tags: "user", (err, pack) ->
+        return callback err  if err
+        subscription.checkUsage pack, callback


Add tests for foreach statements.

Add tests for foreach statements.

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -136,6 +136,20 @@
         it "should highlight 'for'", ->
           expect(tokens[0]).toEqual value: "for", scopes: ["source.powershell","keyword.control.flow.powershell"]

+      describe "'ForEach' statements", ->
+        tokens = null
+
+        beforeEach ->
+          {tokens} = grammar.tokenizeLine("foreach($i in $array) { echo $i }")
+
+        it "should tokenize 'ForEach'", ->
+          expect(tokens[0].value).toEqual "foreach"
+          expect(tokens[0]).toHaveScope "keyword.control.flow.powershell"
+
+        it "should tokenize 'in'", ->
+          expect(tokens[5].value).toEqual "in"
+          expect(tokens[5]).toHaveScope "keyword.control.flow.powershell"
+
     describe "Highlighting logical operators", ->
       logicalOperators = [ "-and", "-or", "-xor", "-not", "!"]


Removing duplicated 'destroy' method. (closes #41)

--- www/src/theoricus/mvc/view.coffee
+++ www/src/theoricus/mvc/view.coffee
@@ -21,7 +21,7 @@
   # @property [theoricus.core.Process] process
   process   : null

-   ###
+  ###
   @param [theoricus.Theoricus] @the   Shortcut for app's instance
   ###
   _boot:( @the )->
@@ -87,13 +87,6 @@
       ( @el.find sel ).bind   ev, null, @[funk]

   ###
-  Called before completely erasing the view
-  ###
-  destroy: () ->
-    @before_destroy?()
-    @el.empty()
-
-  ###
   Triggers view "animation in", "@after_in" must be called in the end
   ###
   in:()->


Added remove icon to datepicker inputs

Added remove icon to datepicker inputs

--- wzk/ui/I18NInputDatePicker.coffee
+++ wzk/ui/I18NInputDatePicker.coffee
@@ -3,8 +3,9 @@
 goog.require 'goog.i18n.DateTimeFormat'
 goog.require 'goog.i18n.DateTimeParse'
 goog.require 'goog.testing.events'
+goog.require 'wzk.ui.CloseIcon'

-class wzk.ui.I18NInputDatePicker
+class wzk.ui.I18NInputDatePicker extends wzk.ui.ClearableInput

   ###*
     @param {wzk.dom.Dom} dom
@@ -16,6 +17,7 @@
       useSimpleNavigationMenu {boolean} hides year selection
   ###
   constructor: (@dom, @pattern = "yyyy'-'MM'-'dd", @params) ->
+    super @dom
     @params = {} unless @params?

     @params.showToday ?= false
@@ -26,11 +28,12 @@
   ###*
     @param {Element} el
     @return {goog.ui.InputDatePicker}
+    @suppress {checkTypes}
   ###
-  decorate: (el) ->
-    el.type = 'text'
+  decorate: (@el) ->
+    @el.type = 'text'

-    PATTERN = @getPattern el
+    PATTERN = @getPattern @el
     formatter = new goog.i18n.DateTimeFormat PATTERN
     parser = new goog.i18n.DateTimeParse PATTERN

@@ -42,11 +45,22 @@

     picker = new goog.ui.InputDatePicker formatter, parser, datePicker

-    picker.listen goog.ui.DatePicker.Events.CHANGE, ->
-      goog.testing.events.fireBrowserEvent new goog.testing.events.Event(goog.events.EventType.CHANGE, el)
+    picker.listen goog.ui.DatePicker.Events.CHANGE, (e)  =>
+      goog.testing.events.fireBrowserEvent new goog.testing.events.Event(goog.events.EventType.CHANGE, @el)
+      @handleInputChange e

-    picker.decorate el
+    @clrBtn = new wzk.ui.CloseIcon dom: @dom
+    @clrBtn.listen goog.ui.Component.EventType.ACTION, @handleClean
+    @clrBtn.render @dom.getParentElement(@el)
+
+    picker.decorate @el
     picker
+
+  ###*
+    @override
+  ###
+  getElement: =>
+    @el

   ###*
     @protected


Fixed bug in collection_view.coffee

Fixed bug in collection_view.coffee

--- src/chaplin/views/collection_view.coffee
+++ src/chaplin/views/collection_view.coffee
@@ -52,7 +52,7 @@
   if $
     (list, viewEl, position, length, itemSelector) ->
       insertInMiddle = (0 < position < length)
-      isEnd = (length) -> length is 0 or position is length
+      isEnd = (length) -> length is 0 or position >= length

       if insertInMiddle or itemSelector
         # Get the children which originate from item views.
@@ -465,6 +465,8 @@
     # Start animation.
     if included and enableAnimation
       startAnimation elem, @useCssAnimation, @animationStartClass
+    else if !included
+      return

     # Hide or mark the view if it’s filtered.
     @filterCallback view, included if @filterer


Fixed API change.

Fixed API change.

--- app/assets/javascripts/app/controllers/user_info.js.coffee
+++ app/assets/javascripts/app/controllers/user_info.js.coffee
@@ -53,7 +53,7 @@
     e.preventDefault()
     new App.ControllerGenericEdit(
       id: @user_id,
-      genericObject: App.User,
+      genericObject: 'User',
       required: 'quick',
       pageData: {
         title: 'Users',


update subject-set-toolbar.cjsx to refrain from disabling the lightbox after the user completes the first task of a mark workflow

--- app/assets/javascripts/components/subject-set-toolbar.cjsx
+++ app/assets/javascripts/components/subject-set-toolbar.cjsx
@@ -27,7 +27,10 @@

   render: ->
     # disable LightBox if work has begun
-    disableLightBox = if @props.task.key isnt @props.workflow.first_task then true else false
+    # NB: By commenting out the following line we can prevent the lightbox
+    # from being disabled after users complete the first task
+    #disableLightBox = if @props.task.key isnt @props.workflow.first_task then true else false
+    disableLightBox = false


retain and release methods return `this`

--- src/app/buffer.coffee
+++ src/app/buffer.coffee
@@ -43,10 +43,12 @@

   retain: ->
     @refcount++
+    this

   release: ->
     @refcount--
     @destroy() if @refcount <= 0
+    this

   getPath: ->
     @file?.getPath()


add MetamapsAdaptor.create for consistency

--- src/adaptor/metamaps.coffee
+++ src/adaptor/metamaps.coffee
@@ -5,6 +5,9 @@
 Sphere = require '../core/sphere'

 class MetamapsAdaptor
+
+  @create: (args) ->
+    Promise.resolve new MetamapsAdaptor args

   fetch: ({protocol, domain, mapId}) ->
     protocol ?= 'https://'


Update session name to be safe.

Update session name to be safe.

--- src/client/xhr.coffee
+++ src/client/xhr.coffee
@@ -8,7 +8,7 @@
 module.exports = class XhrClient
   debug:       false
   endpoint:    'https://api.crowdstart.com'
-  sessionName: 'crowdstart-session'
+  sessionName: 'crwdst'

   constructor: (opts = {}) ->
     return new XhrClient opts unless @ instanceof XhrClient


fixed the time when the decompression callbacks should be called.

--- src/coffee/responsive.breadcrumbs.coffee
+++ src/coffee/responsive.breadcrumbs.coffee
@@ -152,17 +152,27 @@
                         # delete the reference since it does not correctly point it anymore the element
                         crumb = null

-                o.opts.onCompress(_this) if $.isFunction(o.opts.onCompress)
-
-                holder
-                    .children('ul')
-                    .hide()
-                    .append hiddenItems
+                if hiddenItems.length > 0
+                    # trigger first the beforeCompress callback
+                    o.opts.onBeforeCompress(_this) if $.isFunction(o.opts.onBeforeCompress)
+
+                    hiddenItems = $ hiddenItems
+                    holderUl    = holder.children 'ul'
+
+                    # hide first the child ul of the holder
+                    holderUl.hide()
+
+                    # trigger onCompress callback
+                    o.opts.onCompress(_this) if $.isFunction(o.opts.onCompress)
+
+                    # append the hiddenItems in the holder's child ul
+                    holderUl.append hiddenItems
+
+                    # trigger the afterCompress callback
+                    o.opts.onAfterCompress(_this) if $.isFunction(o.opts.onAfterCompress)

                 # delete the reference since it does not correctly point it anymore to the hidden elements
                 hiddenItems = null
-
-                o.opts.onAfterCompress(_this) if $.isFunction(o.opts.onAfterCompress)

             # bind the element to a custom event named decompress
             o.el.on "decompress.#{ pluginName }", (e) ->
@@ -173,23 +183,31 @@
                 if hiddenItems.length > 0
                     hiddenItems = $ hiddenItems.get().reverse()

+                    releaseItems = []
+
                     hiddenItems.each ->
                         crumb = $ this
                         width = crumb.data "#{ pluginName.toLowerCase() }-width"

                         if typeof width isnt "undefined" and (o.getChildrenWidth() + width) <= current.width()
-                            holder.after crumb.detach()
+                            releaseItems.unshift(crumb.detach().get(0))
                         else
                             return false

-                holder.remove() if hiddenItems.length is 0
-
-                o.opts.onDecompress(_this) if $.isFunction(o.opts.onDecompress)
-
-                # delete the reference since the holder element have been remove already
-                holder = null
-
-                o.opts.onAfterDecompress(_this) if $.isFunction(o.opts.onAfterDecompress)
+                    if releaseItems.length > 0
+                        o.opts.onBeforeDecompress(_this) if $.isFunction(o.opts.onBeforeDecompress)
+
+                        o.opts.onDecompress(_this) if $.isFunction(o.opts.onDecompress)
+
+                        holder.after ($ releaseItems)
+
+                        o.opts.onAfterDecompress(_this) if $.isFunction(o.opts.onAfterDecompress)
+
+                        if holder.find('ul > li').length is 0
+                            holder.remove()
+
+                            # delete the reference since the holder element have been remove already
+                            holder = null

             o.browserWindow.on o.resizeKey, o.resize

@@ -224,15 +242,11 @@
             return null

         o.compress = ->
-            o.opts.onBeforeCompress(_this) if $.isFunction(o.opts.onBeforeCompress)
-
             o.el
                 .addClass(o.opts.wrappedClass)
                 .trigger "compress.#{ pluginName }"

         o.decompress = ->
-            o.opts.onBeforeDecompress(_this) if $.isFunction(o.opts.onBeforeDecompress)
-
             o.el
                 .removeClass(o.opts.wrappedClass)
                 .trigger "decompress.#{ pluginName }"


Remove hardcoded limit=0.

Remove hardcoded limit=0.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -184,7 +184,7 @@
       .description 'Export your products from your SPHERE.IO project to CSV using.'
       .option '-t, --template <file>', 'CSV file containing your header that defines what you want to export'
       .option '-o, --out <file>', 'Path to the file the exporter will write the resulting CSV in'
-      .option '-q, --queryString', 'Query string to specify the sub-set of products to export. Please note that the query must be URL encoded!', 'staged=true&limit=0'
+      .option '-q, --queryString', 'Query string to specify the sub-set of products to export. Please note that the query must be URL encoded!', 'staged=true'
       .option '-l, --language [lang]', 'Language used on export for category names', 'en'
       .usage '--projectKey <project-key> --clientId <client-id> --clientSecret <client-secret> --template <file> --out <file>'
       .action (opts) ->


Set log level to warning by default. It is much faster.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -44,10 +44,11 @@
           user_agent: "#{package_json.name} - Import - #{package_json.version}"
           logConfig:
             levelStream: 'warn'
-        if program.verbose
-          options.logConfig = 'info'
-        if program.debug
-          options.logConfig = 'debug'
+            levelFile: 'warn'
+        if program.verbose
+          options.logConfig.levelStream = 'info'
+        if program.debug
+          options.logConfig.levelStream = 'debug'

         importer = new Importer options
         importer.publishProducts = opts.publish


added simpler API for color scales

added simpler API for color scales

--- src/colorscale.coffee
+++ src/colorscale.coffee
@@ -1,20 +1,32 @@
-###*
-    chroma.js - a neat JS lib for color conversions
-    Copyright (C) 2011  Gregor Aisch
+###
+    chroma.js

-    The JavaScript code in this page is free software: you can
-    redistribute it and/or modify it under the terms of the GNU
-    General Public License (GNU GPL) as published by the Free Software
-    Foundation, either version 3 of the License, or (at your option)
-    any later version.  The code is distributed WITHOUT ANY WARRANTY;
-    without even the implied warranty of MERCHANTABILITY or FITNESS
-    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+    Copyright (c) 2011-2013, Gregor Aisch
+    All rights reserved.

-    As additional permission under GNU GPL version 3 section 7, you
-    may distribute non-source (e.g., minimized or compacted) forms of
-    that code without the copy of the GNU GPL normally required by
-    section 4, provided you include this license notice and a URL
-    through which recipients can access the Corresponding Source.
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    * The name Gregor Aisch may not be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+    OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+    EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

     @source: https://github.com/gka/chroma.js
 ###
@@ -22,30 +34,38 @@
 root = (exports ? this)
 chroma = root.chroma ?= {}

-
 Color = chroma.Color
-

 class ColorScale
     ###
     base class for color scales
     ###
-    constructor: (opts) ->
+    constructor: (opts={}) ->
         me = @
-        me._colors = cols = opts.colors ? ['#ddd', '#222']
-        for c in [0..cols.length-1]
-            col = cols[c]
-            cols[c] = new Color(col) if type(col) == "string"
+        me.range opts.colors, opts.positions
+        me._mode = opts.mode ? 'rgb'
+        me._nacol = chroma.hex opts.nacol ? chroma.hex '#ccc'
+        me.domain [0, 1]
+        me

-        if opts.positions?
-            me._pos = opts.positions
+    range: (colors, positions) ->
+        me = @
+        if not colors?
+            colors = ['#ddd', '#222']
+        if colors? and type(colors) == 'string' and chroma.brewer?[colors]?
+            colors = chroma.brewer[colors].slice(0)
+        # convert to chroma classes
+        for c in [0..colors.length-1]
+            col = colors[c]
+            colors[c] = new Color(col) if type(col) == "string"
+        me._colors = colors
+        # auto-fill color position
+        if positions?
+            me._pos = positions
         else
             me._pos = []
-            for c in [0..cols.length-1]
-                me._pos.push c/(cols.length-1)
-
-        me._mode = opts.mode ? 'hsv'
-        me._nacol = chroma.hex opts.nacol ? chroma.hex '#ccc'
+            for c in [0..colors.length-1]
+                me._pos.push c/(colors.length-1)
         me

     domain: (domain = []) ->
@@ -88,7 +108,7 @@
                 break
             if f > p and f < me._pos[i+1]
                 f = (f-p)/(me._pos[i+1]-p)
-                col = chroma.interpolate cols[i], cols[i+1], f, me.mode
+                col = chroma.interpolate cols[i], cols[i+1], f, me._mode
                 break
         col

@@ -124,101 +144,40 @@
 chroma.ColorScale = ColorScale

-class Ramp extends ColorScale
-
-    constructor: (col0='#fe0000', col1='#feeeee', mode='hsl') ->
-        super
-            colors: [col0, col1]
-            positions: [0,1]
-            mode: mode
-
-chroma.Ramp = Ramp
-
-chroma.ramp = (col0, col1, mode) ->
-    s = new chroma.Ramp col0, col1, mode
-    s.getColor
-
-
-class Diverging extends ColorScale
-
-    constructor: (colors=['#d73027', '#ffffbf', '#1E6189'], center=0, mode='hsl') ->
-        me = @
-        me._center = center
-        super
-            colors: colors
-            positions: [0,.5,1]
-            mode: mode
-        me
-
-    domain: (domain = []) ->
-        me = @
-        super domain
-        me._pos[1] = (me._center - domain[0]) / (domain[domain.length-1] - domain[0])
-        me
-
-
-chroma.Diverging = Diverging
-
-chroma.diverging = (col0, col1, col2, center, mode) ->
-    s = new chroma.Diverging col0, col1, col2, center, mode
-    s.getColor
-
-
-class Categories extends ColorScale
-
-    constructor: (colors) ->
-        # colors: dictionary of id: colors
-        me = @
-        me.colors = colors
-
-    parseData: (data, data_col) ->
-        # nothing to do here..
-
-    getColor: (value) ->
-        me = @
-        if me.colors.hasOwnProperty value
-            return me.colors[value]
-        else
-            return '#cccccc'
-
-    validValue: (value) ->
-        @colors.hasOwnProperty value
-
-chroma.Categories = Categories
-
-
-class CSSColors extends ColorScale
-
-    constructor: (name) ->
-        me = @
-        me.name = name
-        me.setClasses(7)
-        me
-
-    getColor: (value) ->
-        me = @
-        c = me.getClass(value)
-        me.name + ' l'+me.numClasses+' c'+c
-
-chroma.CSSColors = CSSColors
-
+# minimal multi-purpose interface
+chroma.scale = (colors, positions) ->
+    colscale = new chroma.ColorScale()
+    colscale.range colors, positions
+    out = false
+    f = (v) ->
+        c = colscale.get v
+        if out and c[out] then c[out]() else c
+    f.domain = (domain, classes, mode='e') ->
+        if classes?
+            d = chroma.analyze domain
+            if classes == 0
+                domain = [d.min, d.max]
+            else
+                domain = chroma.limits d, mode, classes
+        colscale.domain domain
+        f
+    f.mode = (_m) ->
+        colscale._mode = _m
+        f
+    f.range = (_colors, _pos) ->
+        colscale.range _colors, _pos
+        f
+    f.out = (_o) ->
+        out = _o
+        f
+    f

 # some pre-defined color scales:
 chroma.scales ?= {}

 chroma.scales.cool = ->
-    new Ramp(chroma.hsl(180,1,.9), chroma.hsl(250,.7,.4))
+    chroma.scale [chroma.hsl(180,1,.9), chroma.hsl(250,.7,.4)]

 chroma.scales.hot = ->
-    new ColorScale
-        colors: ['#000000','#ff0000','#ffff00','#ffffff']
-        positions: [0,.25,.75,1]
-        mode: 'rgb'
+    chroma.scale(['#000','#f00','#ff0','#fff'], [0,.25,.75,1]).mode('rgb')

-chroma.scales.BlWhOr = ->
-    new Diverging(chroma.hsl(30,1,.55),'#ffffff', new Color(220,1,.55))
-
-chroma.scales.GrWhPu = ->
-    new Diverging(chroma.hsl(120,.8,.4),'#ffffff', new Color(280,.8,.4))
-
-


EmailSender: do not send emails older than maxAge (defined in config) days

--- workers/emailsender/main.coffee
+++ workers/emailsender/main.coffee
@@ -63,7 +63,20 @@

 emailSender = ->
   {JMail} = worker.models
-  JMail.fetchWithUnsubscribedInfo {status: "queued"}, {limit:300}, (err, emails)->
+
+  today = new Date()
+  today.setDate    today.getDate() - emailWorker.maxAge
+  today.setHours   0
+  today.setMinutes 0
+  daysAgo = today
+
+  query = {
+    status     : "queued"
+    dateIssued : $gte: daysAgo
+  }
+
+  JMail.fetchWithUnsubscribedInfo query, {limit:300}, (err, emails)->
+
     if err
       log "Could not load email queue!"
     else


Text input quote discourage hack

Text input quote discourage hack

--- src/controller.coffee
+++ src/controller.coffee
@@ -1452,6 +1452,12 @@

     @oldFocusValue = null

+
+    @hiddenInput.addEventListener 'keydown', (event) =>
+      if event.keyCode is 8 and @hiddenInput.value[0] is @hiddenInput.value[@hiddenInput.value.length - 1] and
+          @hiddenInput.value[0] in ['\'', '\"'] and @hiddenInput.selectionEnd is 1
+        event.preventDefault()
+
     # The hidden input should be set up
     # to mirror the text to which it is associated.
     for event in ['input', 'keyup', 'keydown', 'select']


scope indent changes to dropIntoAceAtLineStart mode

--- src/controller.coffee
+++ src/controller.coffee
@@ -1475,17 +1475,10 @@

       if @trackerPointIsInAce position
         leadingWhitespaceRegex = /^(\s*)/
-        firstNonWhitespaceRegex = /\S/
         # Get the line of text we're dropping into
         pos = @aceEditor.renderer.screenToTextCoordinates position.x, position.y
         line = @aceEditor.session.getLine pos.row
         currentIndentation = leadingWhitespaceRegex.exec(line)[0]
-        firstChar = firstNonWhitespaceRegex.exec(line)
-        if firstChar and firstChar[0] == '}'
-          # If this line starts with a closing bracket, use the previous line's indentation
-          # TODO: generalize for language indentation semantics besides C/JavaScript
-          prevLine = @aceEditor.session.getLine(pos.row - 1)
-          currentIndentation = leadingWhitespaceRegex.exec(prevLine)[0]

         skipInitialIndent = true
         prefix = ''
@@ -1493,11 +1486,22 @@
         suffix = ''

         if @dropIntoAceAtLineStart
+          # First, adjust currentIndentation if we're dropping into the start of a
+          # line that ends an indentation block
+          firstNonWhitespaceRegex = /\S/
+          firstChar = firstNonWhitespaceRegex.exec(line)
+          if firstChar and firstChar[0] == '}'
+            # If this line starts with a closing bracket, use the previous line's indentation
+            # TODO: generalize for language indentation semantics besides C/JavaScript
+            prevLine = @aceEditor.session.getLine(pos.row - 1)
+            currentIndentation = leadingWhitespaceRegex.exec(prevLine)[0]
+          # Adjust pos to start of the line (as we did during mousemove)
           pos = @adjustPosToLineStart pos
           skipInitialIndent = false
           if pos.column == 0
             suffix = '\n'
           else
+            # Handle the case where we're dropping a block at the end of the last line
             prefix = '\n'
         else if currentIndentation.length == line.length or currentIndentation.length == pos.column
           # line is whitespace only or we're inserting at the beginning of a line


Remove some old debugging comments

Remove some old debugging comments

--- lib/glsl-preview-view.coffee
+++ lib/glsl-preview-view.coffee
@@ -334,13 +334,10 @@
       @editor.getPath()

   getGrammar: ->
-    # console.log '@editor?.getGrammar()', @editor?.getGrammar()
     @editor?.getGrammar()

   showError: (error) ->
     @_getActiveTab().addClass('shader-compile-error')
-
-    # console.log 'error', error

     if atom.config.get 'glsl-preview.showErrorMessage'
       @statusView.update "[glsl-preview] #{error}"


Implement destroying bindings on deactivation

--- lib/minimap-bookmarks.coffee
+++ lib/minimap-bookmarks.coffee
@@ -1,15 +1,15 @@
 {CompositeDisposable} = require 'atom'

 MinimapBookmarksBinding = null
-

 module.exports =
   active: false

   isActive: -> @active

+  bindings: {}
+
   activate: (state) ->
-    @subscriptions = new CompositeDisposable

   consumeMinimapServiceV1: (@minimap) ->
     @minimap.registerPlugin 'bookmarks', this
@@ -21,20 +21,25 @@
   activatePlugin: ->
     return if @active

+    @subscriptions = new CompositeDisposable
     @active = true

     @minimapsSubscription = @minimap.observeMinimaps (minimap) =>
       MinimapBookmarksBinding ?= require './minimap-bookmarks-binding'
       binding = new MinimapBookmarksBinding(minimap)
+      @bindings[minimap.id] = binding

       @subscriptions.add subscription = minimap.onDidDestroy =>
         binding.destroy()
         @subscriptions.remove(subscription)
         subscription.dispose()
+        delete @bindings[minimap.id]

   deactivatePlugin: ->
     return unless @active

+    binding.destroy() for id,binding of @bindings
+    @bindings = {}
     @active = false
     @minimapsSubscription.dispose()
     @subscriptions.dispose()


No longer need extra event handler

No longer need extra event handler

Since config doesn't store default values anymore, this line isn't
needed.

--- src/app/config-panel.coffee
+++ src/app/config-panel.coffee
@@ -41,9 +41,8 @@
           value ?= ""
           editor.setText(value.toString())

-        editor.getBuffer().one 'contents-modified', =>
-          editor.getBuffer().on 'contents-modified', =>
-            config.set(name, @parseValue(type, editor.getText()))
+        editor.getBuffer().on 'contents-modified', =>
+          config.set(name, @parseValue(type, editor.getText()))

   parseValue: (type, value) ->
     switch type


fix bug

fix bug

--- apps/artist_2/routes.coffee
+++ apps/artist_2/routes.coffee
@@ -23,11 +23,11 @@
     if (req.params.tab? or artist.href.replace("/artist/", "/artist_2/") is res.locals.sd.CURRENT_PATH)
       res.locals.sd.ARTIST = artist
       res.locals.sd.TAB = tab = req.params.tab or ''
-      currentItem = currentShowAuction(artist)
-      currentItem.detail = if currentItem.type is 'auction'
-        ' '
-      else
-        helpers.formatShowDetail currentItem
+      if currentItem = currentShowAuction(artist)
+        if currentItem.type is 'auction'
+          currentItem.detail = ' '
+        else
+          currentItem.detail = helpers.formatShowDetail currentItem

       res.locals.sd.CURRENT_SHOW_AUCTION = currentItem


Update google_map.coffee

Update google_map.coffee

--- app/assets/javascripts/geocoder_google_maps/google_map.coffee
+++ app/assets/javascripts/geocoder_google_maps/google_map.coffee
@@ -1,5 +1,5 @@
 $(document).ready ->
-  $('div.geocoder-google-maps').each (index, mapElement) =>
+  $('div.geocoder-google-map').each (index, mapElement) =>
     jQMapElement = $(mapElement)
     mapOptions =
       zoom: jQMapElement.data('zoom')


fix two bugs in auto compile limit logic

1. the compileGroup is "standard" not default
2. was not excluding normal compiles from metrics

--- services/web/app/coffee/Features/Compile/CompileManager.coffee
+++ services/web/app/coffee/Features/Compile/CompileManager.coffee
@@ -77,7 +77,10 @@
 				return callback null, true

 	_checkCompileGroupAutoCompileLimit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)->
-		if compileGroup is "default"
+		if !isAutoCompile
+			return callback(null, true)
+		if compileGroup is "standard"
+			# apply extra limits to the standard compile group
 			CompileManager._checkIfAutoCompileLimitHasBeenHit isAutoCompile, compileGroup, callback
 		else
 			Metrics.inc "auto-compile-#{compileGroup}"


fixes #76

fixes #76

--- src/lib/picker.coffee
+++ src/lib/picker.coffee
@@ -4,9 +4,7 @@

 class Picker
-  @TEMPLATE: '
-
-    '
+  @TEMPLATE: ''

   constructor: (@select) ->
     @container = @select.ownerDocument.createElement('span')
@@ -19,8 +17,9 @@
       return true
     )
     DOM.addEventListener(@label, 'click', =>
-      DOM.toggleClass(@container, 'sc-expanded')
-      return false    # Avoid document click handler that closes all dropdowns
+      _.defer( =>
+        DOM.toggleClass(@container, 'sc-expanded')
+      )
     )
     DOM.addEventListener(@select, 'change', =>
       if @select.selectedIndex > -1


ComputeProvider:Koding: pass stack and groupSlug to JDomain correctly

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -131,8 +131,8 @@
     JDomain = require '../domain'

     JDomain.createDomains {
-      account, group
-      stackId       : stack
+      account, stack
+      group         : group.slug
       domains       : hostnameAliases
       hostnameAlias : hostnameAliases[0]
     }


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -386,6 +386,13 @@
 ###*
 Returns the last element of the array, undefined if the array is empty.

+

+
+  ["first", "second", "third"].last()
+=> "third"
+
+

+
 @name last
 @methodOf Array#


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -1,5 +1,5 @@
 ###*
-Calculate the a
+Calculate the average value

 [null, undefined, 3, 3, undefined, 5].compact()


fincm3.coffee: Updating links to getting started guide

Changelog-entry: Update links to getting started guide from resin.io to balena.io
Signed-off-by: Gareth Davies <2f9181a725196cd0a5e07c6a6e161d83fcc71157@balena.io>

--- fincm3.coffee
+++ fincm3.coffee
@@ -23,9 +23,9 @@
 	]

 	gettingStartedLink:
-		windows: 'https://docs.resin.io/fincm3/nodejs/getting-started/#adding-your-first-device'
-		osx: 'https://docs.resin.io/fincm3/nodejs/getting-started/#adding-your-first-device'
-		linux: 'https://docs.resin.io/fincm3/nodejs/getting-started/#adding-your-first-device'
+		windows: 'https://www.balena.io/docs/learn/getting-started/fincm3/nodejs/'
+		osx: 'https://www.balena.io/docs/learn/getting-started/fincm3/nodejs/'
+		linux: 'https://www.balena.io/docs/learn/getting-started/fincm3/nodejs/'
 	supportsBlink: true

 	options: [ networkOptions.group ]


Cleanup

Cleanup

--- src/lib/services/google-analytics.coffee
+++ src/lib/services/google-analytics.coffee
@@ -64,7 +64,5 @@

         datestamp = day.format('{yyyy}-{MM}-{dd}')

-
-
         output.toCsv "#{data_dir}/#{datestamp}.csv", data
       return


teams: activity: added missing dependency

--- client/activity/lib/util/getParticipantOrigins.coffee
+++ client/activity/lib/util/getParticipantOrigins.coffee
@@ -1,3 +1,4 @@
+whoami   = require 'app/util/whoami'
 isMyPost = require 'app/util/isMyPost'

 module.exports = getParticipantOrigins = (channel) ->


Create stack info icon only in team context

--- client/app/lib/environment/environmentlistitem.coffee
+++ client/app/lib/environment/environmentlistitem.coffee
@@ -78,8 +78,7 @@

   createExtraViews: ->

-    { group, stackRevision, _revisionStatus, machines, title } = @getData()
-
+    { title } = @getData()

     @header = new kd.CustomHTMLView
       cssClass : 'stack-info clearfix hidden'
@@ -96,6 +95,15 @@

     @updateNotification = new kd.CustomHTMLView
       cssClass : 'update-notification hidden'
+
+    if isKoding() or title is 'Managed VMs'
+    then @infoIcon = new kd.CustomHTMLView
+    else @createInfoIcon()
+
+
+  createInfoIcon: ->
+
+    { group, stackRevision, _revisionStatus, machines } = @getData()

     if _revisionStatus?.status? and _revisionStatus.status.code > 0
       @showUpdateNotification()
@@ -115,8 +123,6 @@
           from #{stackRevision[..5]} revision. <br/>
           #{revisionMessage}
         "
-
-    @infoIcon.hide()  if isKoding()

   showUpdateNotification: ->


Do not prevent default click handling in Element editor

Check boxes, their labels, and submit buttons need to handle their own click
events.

--- app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee
@@ -191,8 +191,6 @@
     element_id = $element.attr("id").replace(/\D/g, "")
     @selectElement($element)
     @selectElementInPreview(element_id)
-    # Element submit button needs to keep it's default event
-    e.preventDefault() unless $target.is(':submit')
     return

   # Double click event handler for element head.


:lipstick:

:lipstick:

--- src/atom-application.coffee
+++ src/atom-application.coffee
@@ -178,7 +178,7 @@
       pathToOpen = path.resolve(@executedFrom, pathToOpen) if @executedFrom and pathToOpen
       atomWindow = new AtomWindow
         pathToOpen: pathToOpen
-        bootstrapScript: 'window-bootstrap',
+        bootstrapScript: 'window-bootstrap'
         resourcePath: @resourcePath

   openConfig: ->


Save state on keydown rather than on keypress

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -123,7 +123,7 @@

     debouncedSaveStateSync = _.debounce((=> @saveStateSync()), @saveStateDebounceInterval)
     @document.addEventListener('mousedown', debouncedSaveStateSync, true)
-    @document.addEventListener('keypress', debouncedSaveStateSync, true)
+    @document.addEventListener('keydown', debouncedSaveState, true)

     @state = {version: @constructor.version}


removed images in exercise problem from exercises table

--- src/components/task-plan/homework/exercises.cjsx
+++ src/components/task-plan/homework/exercises.cjsx
@@ -1,6 +1,7 @@
 React = require 'react'
 _ = require 'underscore'
 BS = require 'react-bootstrap'
+$ = require 'jquery'

 ArbitraryHtmlAndMath = require '../../html'
 ChapterSection = require '../chapter-section'
@@ -26,7 +27,7 @@
     if isLO
       classes = ['lo-tag']
       loLabel = 'LO: '
-
+
     {loLabel} {content}

   renderExercise: ->
@@ -184,8 +185,10 @@

   renderExerciseRow: (exerciseId, index, hasTeks) ->
     {section, lo, tagString} = ExerciseStore.getTagStrings(exerciseId)
-    content = ExerciseStore.getContent(exerciseId)
-
+    content = $("#{ExerciseStore.getContent(exerciseId)}")
+    content.find('img').remove()
+    content = content.html()
+
     if (hasTeks)
       teksString = ExerciseStore.getTeksString(exerciseId)
       unless teksString


Event controller: allow set default num tickets and auto-select tickets if only 1 ticket type

--- src/core/javascripts/controllers/event.js.coffee
+++ src/core/javascripts/controllers/event.js.coffee
@@ -7,12 +7,13 @@
   controller : 'Event'

-angular.module('BB.Controllers').controller 'Event', ($scope,  $rootScope, EventService, $q, PageControllerService, BBModel, ValidatorService) ->
+angular.module('BB.Controllers').controller 'Event', ($scope, $attrs, $rootScope, EventService, $q, PageControllerService, BBModel, ValidatorService) ->
   $scope.controller = "public.controllers.Event"
   $scope.notLoaded $scope
   angular.extend(this, new PageControllerService($scope, $q))

   $scope.validator = ValidatorService
+  $scope.event_options = $scope.$eval($attrs.bbEvent) or {}

   $rootScope.connection_started.then ->
     if $scope.bb.company
@@ -34,8 +35,12 @@
         # colorThief = new ColorThief()
         # colorThief.getColor image.url

+
       for ticket in $scope.event.tickets
-        ticket.qty = 0
+        ticket.qty = if $scope.event_options.default_num_tickets then $scope.event_options.default_num_tickets else 0
+
+      $scope.selectTickets() if $scope.event_options.default_num_tickets and $scope.event_options.auto_select_tickets and $scope.event.tickets.length is 1
+
       $scope.setLoaded $scope

     , (err) -> $scope.setLoadedAndShowError($scope, err, 'Sorry, something went wrong')


i18n lon-capa problem explanation title

i18n lon-capa problem explanation title

--- common/lib/xmodule/xmodule/js/src/problem/edit.coffee
+++ common/lib/xmodule/xmodule/js/src/problem/edit.coffee
@@ -201,6 +201,8 @@
 #
   @markdownToXml: (markdown)->
     toXml = `function (markdown) {
+      // Translators: This is the title of the text explaining a problem
+      var explanation_title = gettext("Explanation");
       var xml = markdown,
           i, splits, scriptFlag;

@@ -348,14 +350,15 @@
       });

       // replace explanations
+
       xml = xml.replace(/\[explanation\]\n?([^\]]*)\[\/?explanation\]/gmi, function(match, p1) {
-          var selectString = '<solution>\n
\nExplanation\n\n' + p1 + '\n
\n</solution>';
+          var selectString = '<solution>\n
\n' + explanation_title + '\n\n' + p1 + '\n
\n</solution>';

           return selectString;
       });
-
+
       // replace labels
-      // looks for >>arbitrary text<< and inserts it into the label attribute of the input type directly below the text.
+      // looks for >>arbitrary text<< and inserts it into the label attribute of the input type directly below the text.
       var split = xml.split('\n');
       var new_xml = [];
       var line, i, curlabel, prevlabel = '';


Add SPPLUS predefined functions

Add SPPLUS predefined functions

--- src/predefined.coffee
+++ src/predefined.coffee
@@ -3602,4 +3602,11 @@
     'm_validateidentifier': 'function'
     'm_verifyconnection': 'function'
     'm_verifysslcert': 'function'
+
+    # SPPLUS
+    # SPPLUS constants
+    'calcul_hmac': 'function'
+    'calculhmac': 'function'
+    'nthmac': 'function'
+    'signeurlpaiement': 'function'
 }


[EVENTRIC-60] Change published event format in projection

--- src/projection.coffee
+++ src/projection.coffee
@@ -1,6 +1,5 @@
 eventric = require 'eventric'
 async    = require './helper/async'
-PubSub   = require './pub_sub'

 class Projection

@@ -47,7 +46,9 @@
         @_subscribeProjectionToDomainEvents projectionId, projectionName, projection, eventNames, aggregateId, context
       .then =>
         @_projectionInstances[projectionId] = projection
-        context.publish "#{projectionName}:initialized", projection
+        context.publish "projection:#{projectionName}:initialized",
+          id: projectionId
+          projection: projection
         resolve projectionId

       .catch (err) ->
@@ -129,7 +130,9 @@
       domainEventHandler = (domainEvent, done) =>
         @_applyDomainEventToProjection domainEvent, projection
         .then ->
-          context.publish "#{projectionName}:changed", projection
+          context.publish "projection:#{projectionName}:changed",
+            id: projectionId
+            projection: projection
           done()

       for eventName in eventNames


Don't show bottom if not saved

Don't show bottom if not saved

--- core/app/backbone/views/evidence/react_comment.coffee
+++ core/app/backbone/views/evidence/react_comment.coffee
@@ -25,7 +25,7 @@
   _bottom: ->
     sub_comment_count = @model().get('sub_comments_count')

-    [
+    _span [],
       _span ["comment-post-bottom"],
         if @model().can_destroy()
           _span ["comment-post-delete"],
@@ -39,7 +39,6 @@
         ReactSubComments
           model: @model().sub_comments()
           fact_opinionators: @props.fact_opinionators
-    ]

   _save: ->
     @model().saveWithState()
@@ -58,7 +57,8 @@
         if @model().get('save_failed') == true
           _a ['button-danger', onClick: @_save, style: {float: 'right'} ],
             'Save failed - Retry'
-        @_bottom()...
+        else
+          @_bottom()

 ReactCommentHeading = React.createBackboneClass
   displayName: 'ReactCommentHeading'


recipient file tranzit header

recipient file tranzit header


List upgrades

List upgrades

--- src/cli/cmdz/upgrade.coffee
+++ src/cli/cmdz/upgrade.coffee
@@ -1,4 +1,5 @@
 sensor = require 'self/upgrade/sensor'
+vfilter = require '../vfilter'

 exports.t = "Upgrade latest/LTS Node or #{PACKAGE.mingzi} itself"

@@ -6,8 +7,22 @@
   Download and install latest version of #{PACKAGE.mingzi}, Node or Node LTS version
   """

-exports.$ = ->
-  require 'self/upgrade/upgrade'
+exports.$ = (args)->
+  force = period args
+  up = info()
+  unless key = abbrev(keys up).$ args[0]
+    hint up
+    exit()
+
+  unless (up = up[key]).a or force
+    echo "Upgrade not needed. To force upgrade say:", period.$
+    exit()
+
+  echo "Upgrading:", up.n, '->', up.v or latestStr
+  exit()
+  return
+
+  # require 'self/upgrade/upgrade'

 # ZOG command: Fetch latest version from GitHub
 exports.z = require 'self/upgrade/sensor'
@@ -18,6 +33,7 @@
   result = {}
   for k, v of sensor.u empty
     result[if v.n then 'self' else k.toLowerCase()] = v
+    v.n ||= k
   result

 keys = (rec)->
@@ -25,4 +41,38 @@
     k

 # Command arguments
-exports._ = "[#{keys(info true).join '|'}] [.]"
+exports._ = "[#{keys(info true).join '|'}] #{vfilter.$6} [.]"
+
+hint = (info)->
+  avail = []
+  na = []
+  for k, v of info
+    v.k = k
+    (if v.a then avail else na).push v
+
+  if avail.length
+    echo "Available upgrades:"
+  for v in avail
+    hintLine v
+
+  if na.length
+    if avail.length
+      echo ''
+    echo "Possible upgrades:"
+  for v in na
+    hintLine v
+
+  return
+
+latestStr = 'latest'
+
+hintLine = (info)->
+  echo "\t",
+    PACKAGE.mingzi
+    "upgrade"
+    info.k
+    if info.a then '' else '.'
+    "\t//"
+    info.n
+    '->'
+    info.v or latestStr


Cleaning comments.

Cleaning comments.

--- app/client/js/app.coffee
+++ app/client/js/app.coffee
@@ -16,20 +16,16 @@

     routes.addToEste router, (route, params) ->
       storage.load route, params
-        # Successful load, set we can set active route.
         .then -> routes.setActive route, params
-        # Load failed for 404, 500, or whatever reason. Try set error route.
         .thenCatch (reason) -> routes.trySetErrorRoute reason
-        # Update UI.
         .then -> React.renderComponent reactApp.component(), element
-        # Handle severe error.
         .thenCatch (reason) ->
-          # TODO: Report error to server.
           # TODO: Show something more beautiful.
           alert 'App error, sorry for that. Please reload browser.'
-          # Propagate error to dev console.
+          # TODO: Report error to server.
+          # Ensure error is shown in console.
           goog.async.throwException reason if goog.DEBUG
-          # Rethrow to prevent este.Router url change.
+          # Prevent este.Router url change.
           throw reason

     router.start()


Rebind vote related javascript on ajaxComplete

--- app/assets/javascripts/courses.js.coffee
+++ app/assets/javascripts/courses.js.coffee
@@ -7,6 +7,10 @@
     'maxMessages': 5,
     'theme': 'air'
   }
+
+  $('a.btn-vote-course').unbind()
+  $('a.btn-vote-comment').unbind()
+  $('a.btn-vote-reply').unbind()

   $('a.btn-vote-course').on("ajax:success", (e, data, status, xhr) ->
     target_credit = $('#rank_on_course_' + data.votable_id)
@@ -81,4 +85,5 @@
                   action: ->
                       vote_errmsg.hide()
 $(document).ready(ready)
+$(document).ajaxComplete(ready)
 $(document).on('page:load', ready)


Fixed scatter chart

Fixed scatter chart

--- app/assets/javascripts/d3/scatter.coffee
+++ app/assets/javascripts/d3/scatter.coffee
@@ -8,30 +8,36 @@

     can_be_shown_as_table: -> false

+    margins:
+      top: 20
+      bottom: 20
+      left: 40
+      right: 10
+
     draw: =>
-      margins =
-        top: 20
-        bottom: 200
-        left: 40
-        right: 10
+      @width  = @available_width()  - (@margins.left + @margins.right)
+      @height = @available_height() - (@margins.top + @margins.bottom)

-      @width = 494 - (margins.left + margins.right)
-      @height = 360 - (margins.top + margins.bottom)
-      # height of the series section
-      @series_height = 190
+      legend_columns = 2
+      legend_rows = @series.length / legend_columns
+      legend_height = legend_rows * @legend_cell_height
+      legend_margin = 30
+
+      @series_height = @height - legend_height - legend_margin
+
       @svg = d3.select(@container_selector())
         .append("svg:svg")
-        .attr("height", @height + margins.top + margins.bottom)
-        .attr("width", @width + margins.left + margins.right)
+        .attr("height", @height + @margins.top + @margins.bottom)
+        .attr("width", @width + @margins.left + @margins.right)
         .append("svg:g")
-        .attr("transform", "translate(#{margins.left}, #{margins.top})")
+        .attr("transform", "translate(#{@margins.left}, #{@margins.top})")

       @draw_legend
         svg: @svg
         series: @series
         width: @width
-        vertical_offset: @series_height + 35
-        columns: 2
+        vertical_offset: @series_height + legend_margin
+        columns: legend_columns

       @x = d3.scale.linear().range([0, @width - 15])
         .domain([0, 100])


changed ?= to = in user.coffee

changed ?= to = in user.coffee

--- app/assets/javascripts/model/user.coffee
+++ app/assets/javascripts/model/user.coffee
@@ -3,7 +3,7 @@
 ###

 # This takes care of the userdate.
-User ?= {}
+User = {}

 # Debounce for POST User.Configuration
 DEBOUNCE_TIME = 3000


Test smarter scoreboard refresh

Test smarter scoreboard refresh

--- app/assets/javascripts/scoreboard.coffee
+++ app/assets/javascripts/scoreboard.coffee
@@ -1,35 +1,35 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
-column_data = ""
-xs_data = ""
+chart = ""

 draw_scoreboard = ->
-  if !document.getElementById('scoreboard_graph')
+  if !document.getElementById('scoreboard_graph')
     return

   $.get 'teams/get_score_data', (response,status) ->
-    column_data = JSON.parse(response.scores)
-    xs_data = JSON.parse(response.teams)
-    finish_drawing_scoreboard()
+
+    if chart == ""
+      chart = c3.generate
+        bindto: '#scoreboard_graph'
+        data:
+          xs: JSON.parse(response.teams)
+          columns: JSON.parse(resonse.scores)
+          type: 'step'
+        axis:
+          x: tick:
+            count: 10
+            format: (x) ->
+              moment(x * 1000).local().format 'LLL'
+          y: label: text: 'Score'
+        zoom: enabled: true
+    else
+      chart.load
+        xs: JSON.parse(response.teams)
+        columns: JSON.parse(resonse.scores)
+    setTimeout draw_scoreboard, 30000
+
     return
   return

-finish_drawing_scoreboard = ->
-  chart = c3.generate
-    bindto: '#scoreboard_graph'
-    data:
-      xs: xs_data
-      columns: column_data
-      type: 'step'
-    axis:
-      x: tick:
-        count: 10
-        format: (x) ->
-          moment(x * 1000).local().format 'LLL'
-      y: label: text: 'Score'
-    zoom: enabled: true
-  setTimeout draw_scoreboard, 30000
-  return
-
 $(document).on('turbolinks:load', draw_scoreboard)


Compute index lazily

Compute index lazily

No need to precompute it.

//cc @josh

--- app/assets/javascripts/task_lists.coffee
+++ app/assets/javascripts/task_lists.coffee
@@ -114,12 +114,9 @@
 # Enables task list items to trigger updates.
 enableTaskList = ($container) ->
   if $container.find('.js-task-list-field').length > 0
-    index = 0
     $container.
       find('.task-list-item').addClass('enabled').
       find('.task-list-item-checkbox').attr('disabled', null)
-    for item, i in $container.find('.task-list-item-checkbox')
-      $(item).data('taskListItemIndex', i+1)
     $container.trigger 'tasklist:enabled'

 disableTaskList = ($container) ->
@@ -133,7 +130,7 @@
 updateTaskList = ($item) ->
   $container = $item.closest '.js-task-list-container'
   $field     = $container.find '.js-task-list-field'
-  index      = $item.data('taskListItemIndex')
+  index      = 1 + $container.find('.task-list-item-checkbox').index($item)
   checked    = $item.prop 'checked'

   disableTaskList $container


Remove welcome js file

Remove welcome js file

--- app/assets/javascripts/welcome.js.coffee
+++ app/assets/javascripts/welcome.js.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/


Fix issue #112. Pull request for support in Ouroboros is also submitted

--- app/controllers/interactive/group.coffee
+++ app/controllers/interactive/group.coffee
@@ -105,6 +105,8 @@
     delete User.current.user_group_id

   leaveGroup: (e) =>
+    answer = confirm 'Are you sure?\nThe Group Leader must re-invite you if you wish to rejoin'
+    return unless answer
     e.preventDefault()
     UserGroup.leave @groupId
     @navigate '/navigator/home'


Draw rows and commands, in the wrong place

--- src/config/inspector.coffee
+++ src/config/inspector.coffee
@@ -24,9 +24,8 @@
   }

 # TODO: Add ICON authority for smaller command display
-
-PROGRAM_AUTHORITY = $.extend {}, BUTTON_AUTHORITY # shallow copy
-PROGRAM_AUTHORITY['selected'] = (world) ->
+COMMAND_AUTHORITY = $.extend {}, BUTTON_AUTHORITY # shallow copy
+COMMAND_AUTHORITY['selected'] = (world) ->
   return false unless world.up.get('selected')
   world.index == world.get('current').get('programs').get('counter') + 1

@@ -49,7 +48,7 @@
     _LABEL: name,
     selected: (world) ->
       name == world.get('current').get('programs').get('current')
-    _AUTHORITY: PROGRAM_AUTHORITY
+    _AUTHORITY: COMMAND_AUTHORITY
     _CHILDREN: children
   }

@@ -60,9 +59,13 @@
   return if programs.get_local('height')
   programs.put 'height', (world) ->
     my.row.spacing * world._child_count()
-  authority = programs.make_world PROGRAM_AUTHORITY
+
+  render_row = programs.make_world ROW_AUTHORITY
+  render_command = programs.make_world COMMAND_AUTHORITY
   programs.map_children (program) ->
-    program.put '_AUTHORITY', authority
+    program.put '_AUTHORITY', render_row
+    program.map_children (command) ->
+     command.put '_AUTHORITY', render_command

 set_current = (world, current) ->
   world.put('current', current) if current?


updated the state property to call the setState method.

--- src/statechart.coffee
+++ src/statechart.coffee
@@ -9,7 +9,7 @@
 	@define 'stateName',  get: -> @__state.name
 	@define 'state',
 		get: -> @__state
-		set: (obj) -> @__state = obj
+		set: (obj) -> @setState obj

 	@Success: false
 	@Failure: true
@@ -32,7 +32,11 @@

 		addPaths chart, @::__statechart

-		# TODO: confirm integrity of chart, makes sure all entry / exit points are accounted for
+		#
+		# TODO: confirm integrity of chart:
+		# - makes sure all entry / exit points are accounted for
+		# - make sure paths are valid
+		#

 	constructor: (config={}) ->
 		return unless @statechart?
@@ -45,7 +49,9 @@

 	dispose: -> @removeAllListeners()

-	setState: (stateObj) ->
+	setState: (nameOrObj) ->
+		stateObj = if typeof nameOrObj is 'string' then @pathResolver nameOrObj else nameOrObj
+
 		# TODO validate state change

 		if @state
@@ -54,7 +60,7 @@
 			if @isDescendantState stateObj
 				@removeMethods oldState.methods

-		@state = stateObj
+		@__state = stateObj
 		@addMethods @state.methods
 		@buildTransitions @state.transitions

@@ -88,10 +94,7 @@
 			chgMethod = @[t.action]
 			@[t.action] = =>
 				dontTransition = (chgMethod.apply @, arguments)
-
-				unless dontTransition
-					@setState destination
-
+				unless dontTransition then @setState destination
 				return dontTransition

 	pathResolver: (path) ->
@@ -101,8 +104,7 @@
 		# start from top
 		steps = path.split '/'
 		target = paths: @statechart
-		for step in steps
-			target = target.paths[step]
+		target = target.paths[step] for step in steps

 		return target


Get location data for google geolocation service Allow in or near and multiple word locations Fix bug if rating is 0

--- src/streetfood.coffee
+++ src/streetfood.coffee
@@ -26,23 +26,25 @@
   longitude: process.env.HUBOT_STREETFOOD_DEFAULT_LNG
 }

+user_agent = "Hubot Streetfood Engine"
+
 vendorCache = {}

 getVendors = (robot, city, callback) ->
   now = new Date().getTime()
+  city = city.toLowerCase().replace(/\W/g, "-",)
   if city of vendorCache and vendorCache[city].expires > now
     return callback(null, vendorCache[city].vendors)

   robot.http("#{api_url}schedule/#{city}/")
-    .headers("User-Agent": "Hubot Streetfood Engine", "Accept": "application/json")
+    .headers("User-Agent": user_agent: "Accept": "application/json")
     .get() (err, res, body) ->
-      if err or res.statusCode < 200 or res.statusCode >= 300
-        callback(err or body)
-      else
-        vendors = (JSON.parse body).vendors
-        vendors = (v for k,v of vendors)
-        vendorCache[city] = vendors : vendors, expires : now + 1000*60*5 # 5 minute expiry
-        callback(err, vendors)
+      return callback(err or body) if err or res.statusCode < 200 or res.statusCode >= 300
+
+      vendors = (JSON.parse body).vendors
+      vendors = (v for k,v of vendors)
+      vendorCache[city] = vendors : vendors, expires : now + 1000*60*5 # 5 minute expiry
+      callback(err, vendors)

 calculateOpenStateAndScore = (vendor, now) ->
   now or= new Date().getTime()
@@ -61,7 +63,7 @@
     return score:0, state:"Long Term Closure" # permanently closed?

 calculateDistance = (vendor, latitude, longitude) ->
-  if latitude? and longitude? and vendor.open.length > 0
+  if latitude? and longitude? and vendor.open?.length
     vendorLatitude = vendor.open[0].latitude
     vendorLongitude = vendor.open[0].longitude
     x_dist = (longitude - vendorLongitude) * Math.cos(vendorLatitude / 360 * 2 * Math.PI) * 110.54 # km
@@ -89,7 +91,7 @@
     openScoreAndState = calculateOpenStateAndScore(vendor, now)
     distance = calculateDistance(vendor, latitude, longitude)
     distanceScore = Math.pow(10, -1 * distance)
-    ratingScore = Math.log(vendor.rating)
+    ratingScore = Math.log(Math.max(vendor.rating, 1))

     scores.push score: openScoreAndState.score * distanceScore * ratingScore, open: openScoreAndState.state, distance: formatDistance(distance), vendor:vendor
   return scores
@@ -116,38 +118,61 @@
   if vendor.images?.header?.length
     msg.send msg.random vendor.images.header

-getLocationDetails = (location_request) ->
-  return default_location unless location_request
-  return {city: location_request.toLowerCase()}
+getLocationDetails = (robot, location_request, callback) ->
+  return callback default_location unless location_request
+  geoLookup(robot, location_request, callback)
+
+
+geoLookup = (robot, location_request, callback) ->
+    robot.http("https://maps.googleapis.com/maps/api/geocode/json")
+      .header('User-Agent': user_agent)
+      .query({
+        address: location_request
+        sensor: false
+      })
+      .get() (err, res, body) ->
+        return callback {} if err or res.statusCode < 200 or res.statusCode >= 300
+
+        response = JSON.parse(body)
+        return callback {} unless response.results?.length
+
+        city = response.results[0].address_components.filter (c) -> "locality" in c.types
+
+        return callback {} unless city?.length
+        return callback {
+          city: city[0].short_name,
+          latitude: response.results[0].geometry.location.lat,
+          longitude: response.results[0].geometry.location.lng
+        }

 module.exports = (robot) ->
-  robot.respond /(street( )?food|food( )?cart(s)?)( in (\w+))?/i, (msg) ->
-    location = getLocationDetails(msg.match[6])
-    return msg.send "I don't know where look for food carts in" unless location.city
+  robot.respond /(street( )?food|food( )?cart(s)?)( (in|near) (.+))?/i, (msg) ->
+    getLocationDetails robot, msg.match[7], (location) ->
+      return msg.send "I don't know where look for food carts" unless location.city

-    getVendors robot, location.city, (error, vendors) ->
-      return msg.send "No food carts found in #{location.city}" unless vendors?.length
+      getVendors robot, location.city, (error, vendors) ->
+        return msg.send "No food carts found in #{location.city}" unless vendors?.length

-      scoredVendors = scoreVendors(vendors, location.latitude, location.longitude)
-      choice = chooseVendor(scoredVendors)
+        scoredVendors = scoreVendors(vendors, location.latitude, location.longitude)
+        choice = chooseVendor(scoredVendors)

-      return msg.send "Sorry, I couldn't find any food carts" unless choice?
+        return msg.send "Sorry, I couldn't find any food carts" unless choice?

-      msgVendorInfo msg, choice, location.city
-      msgVendorPicture msg, choice.vendor
+        msgVendorInfo msg, choice, location.city
+        msgVendorPicture msg, choice.vendor

-  robot.respond /top (\d+) (street( )?food|food( )?cart(s)?)( in (\w+))?/i, (msg) ->
+  robot.respond /top (\d+) (street( )?food|food( )?cart(s)?)( (in|near) (.+))?/i, (msg) ->
     n = +msg.match[1]
-    location = getLocationDetails(msg.match[7])
-    return msg.send "I don't know where look for food carts in" unless location.city
+    getLocationDetails robot, msg.match[8], (location) ->
+      return msg.send "I don't know where look for food carts" unless location.city

-    getVendors robot, location.city, (error, vendors) ->
-      return msg.send "No food carts found in #{location.city}" unless vendors?.length
+      getVendors robot, location.city, (error, vendors) ->
+        return msg.send "No food carts found in #{location.city}" unless vendors?.length

-      scoredVendors = scoreVendors(vendors, location.latitude, location.longitude)
-      scoredVendors = scoredVendors.filter (a) -> return a.score > 0
-      scoredVendors.sort (a,b) -> return b.score - a.score
-      topN = scoredVendors.slice(0, n)
-      for scoredVendor in topN
-        msgVendorInfo msg, scoredVendor, location.city
+        scoredVendors = scoreVendors(vendors, location.latitude, location.longitude)
+        scoredVendors = scoredVendors.filter (a) -> return a.score > 0
+        scoredVendors.sort (a,b) -> return b.score - a.score
+        topN = scoredVendors.slice(0, n)
+        for scoredVendor in topN
+          msgVendorInfo msg, scoredVendor, location.city


delete translate.coffee

delete translate.coffee

--- app/lib/translate.coffee
+++ app/lib/translate.coffee
@@ -1,42 +0,0 @@
-strings = require '../translations/en-us'
-
-translate = (keys...) ->
-  keys = keys[0].split '.' if keys.length is 1
-  reference = strings
-
-  for key in keys
-    throw new Error "No translation for #{keys.join '.'}" unless reference[key]?
-    reference = reference[key]
-
-  reference
-
-deepMixin = (base, mixin) ->
-  # NOTE: This expects only strings and numbers nested in objects.
-  for own key, value of mixin
-    if typeof value in ['string', 'number']
-      base[key] = value
-    else:q
-      deepMixin base[key], value
-
-  base
-
-translate.init = ([language]..., callback) ->
-  gotLanguage = new $.Deferred
-  gotLanguage.done ->
-    for element in $('[data-i18n]')
-      element = $(element)
-      key = element.attr 'data-i18n'
-      element.html translate key
-
-    $(window).trigger 'translate-init'
-
-    callback? strings
-
-  if language
-    $.getJSON "translations/#{language}.json", (translations) ->
-      deepMixin strings, translations
-      gotLanguage.resolve strings
-  else
-    gotLanguage.resolve strings
-
-module.exports = translate


IMPROVED - more globalEpochCycle.startTimePerformance updates

--- source/Art/Engine/Core/EpochLayout/StateEpochLayout.coffee
+++ source/Art/Engine/Core/EpochLayout/StateEpochLayout.coffee
@@ -36,8 +36,7 @@
   isInfiniteResult
 } = LayoutTools = require './LayoutTools'

-getGlobalEpochCycle = ->
-  ArtEngineCore.GlobalEpochCycle.globalEpochCycle
+globalEpochCycle = null

 module.exports = class StateEpochLayout extends BaseObject

@@ -55,11 +54,12 @@
       markParentLayoutPropertiesChanged element

   @updateLayouts: (layoutChangedElements) =>
-
-    # TODO: add back getGlobalEpochCycle(), but don't use timePerformance; closures cost too much
-
+    globalEpochCycle ?= ArtEngineCore.GlobalEpochCycle.globalEpochCycle
+
+    start = globalEpochCycle.startTimePerformance()
     for element in layoutChangedElements when element.__layoutPropertiesChanged
       layoutElement element, element.getPendingParentSizeForChildren()
+    globalEpochCycle.endTimePerformance "aimLayout", start

     null


Cleanup

Cleanup

--- client/app/Applications/Inbox.kdapplication/AppController.coffee
+++ client/app/Applications/Inbox.kdapplication/AppController.coffee
@@ -136,9 +136,7 @@
           , callback
           disownItem item for own id, item of items
         (pubInst, modal) =>
-          # log modal
           disownAll @selection, =>
-            # log "2", modal
             for own id, {item, paneView} of @selection
               item.destroy()
               paneView.destroy()


Add enum support

Add enum support

--- library/schema/domain.coffee
+++ library/schema/domain.coffee
@@ -116,6 +116,21 @@
     @required = (required or []).concat key
     return pointer.apply this, arguments

+# Create a reference to the JSON enumeration. An enumeration defines
+# what values are valid for this data type. Everything else beyond
+# those values are considered invalid. This version takes an array
+# of values as scrubbed from all the arguments that follow after the
+# the description argument. Makes sure all values are plain strings.
+module.exports.choose = choose = (description, values) ->
+    noValues = "all values must be primitive strings"
+    noDescription = "no description has been given"
+    noArray = "values argument should be an array"
+    assert _.isArray(values), noArray
+    assert _.isString(description), noDescription
+    assert _.all(values, _.isString), noValues
+    @description = description.toString()
+    @enum = values; return this
+
 # Create a reference to a JSON schema number within the context. The
 # number can optionally be followed by an object that contains some
 # validation restrictions, as they are found in the JSON schema v4 or


update command text

update command text

--- src/cool-ascii-faces.coffee
+++ src/cool-ascii-faces.coffee
@@ -8,7 +8,7 @@
 #   None
 #
 # Commands:
-#   Hubot face me - Returns a random ascii face
+#   hubot face me - displays a random ascii face
 #
 # Notes
 #   None


EnvironmentMachineItem: keep track of stateClasses based on Machine.State

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -1,6 +1,9 @@
 class EnvironmentMachineItem extends EnvironmentItem

   JView.mixin @prototype
+  stateClasses = ""
+  for state in Object.keys Machine.State
+    stateClasses += "#{state.toLowerCase()} "

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


UI Copy: "Still waiting... This VM is booting slowly."

If you guys don't like this, let me know and I will think of something else

--- client/Finder/filetree/itemsubviews/vmtogglebuttonview.coffee
+++ client/Finder/filetree/itemsubviews/vmtogglebuttonview.coffee
@@ -23,7 +23,7 @@
         type    : "mini"
         cssClass: "error"
         duration: 5000
-        title   : "I cannot turn this machine on, please give it a few seconds."
+        title   : "Still waiting... This VM is booting slowly."

       @toggle.setDefaultValue no
       # KD.utils.notifyAndEmailVMTurnOnFailureToSysAdmin vm, err.message


Add setImmediate function in renderer.

Add setImmediate function in renderer.

--- renderer/lib/init.coffee
+++ renderer/lib/init.coffee
@@ -1,4 +1,5 @@
-path = require 'path'
+path   = require 'path'
+timers = require 'timers'
 Module = require 'module'

 # Expose information of current process.
@@ -21,6 +22,10 @@
 # Expose global variables.
 global.require = require
 global.module = module
+global.setImmediate = ->
+  process.activateUvLoop()
+  timers.setImmediate.apply this, arguments
+global.clearImmediate = timers.clearImmediate

 # Set the __filename to the path of html file if it's file:// protocol.
 if window.location.protocol is 'file:'


activity: use internal helper for resolving channel props

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -48,6 +48,9 @@
       channelParticipants   : immutable.List()

+  channel: (args...) -> @state.channelThread.getIn ['channel'].concat args
+
+
   onVideoStart: ->

     @setState isComingSoonModalOpen: yes
@@ -95,9 +98,7 @@
   leaveChannel: ->

     { unfollowChannel } = ActivityFlux.actions.channel
-    channelId   = @state.channelThread.get 'channelId'
-
-    unfollowChannel channelId
+    unfollowChannel @channel 'id'

   componentWillUpdate: (nextProps, nextState) ->
@@ -118,9 +119,7 @@

   showNotificationSettingsModal: ->

-    channelName = @state.channelThread.getIn ['channel', 'name']
-    route = "/Channels/#{channelName}/NotificationSettings"
-
+    route = "/Channels/#{@channel 'name'}/NotificationSettings"
     kd.singletons.router.handleRoute route

@@ -128,25 +127,20 @@

     { ENTER, ESC } = KeyboardKeys
     thread         = @state.channelThread
-    purpose        = thread.getIn(['channel', 'purpose'])

     if event.which is ESC
-
-      _originalPurpose = thread.getIn ['channel', '_originalPurpose']
-      purpose = _originalPurpose or thread.getIn ['channel', 'purpose']
+      _originalPurpose = @channel '_originalPurpose'
+      purpose = _originalPurpose or @channel 'purpose'
       thread  = thread.setIn ['channel', 'purpose'], purpose
       @setState channelThread: thread
       return @setState editingPurpose: no

     if event.which is ENTER
-
-      id        = thread.get 'channelId'
-      purpose   = thread.getIn(['channel', 'purpose']).trim()
-
+      id = @channel 'id'
+      purpose = @channel('purpose').trim()
       { updateChannel } = ActivityFlux.actions.channel

-      updateChannel({ id, purpose }).then (response) =>
-        @setState editingPurpose: no
+      updateChannel({ id, purpose }).then => @setState editingPurpose: no

   getPurposeAreaClassNames: -> classnames
@@ -158,20 +152,18 @@

     thread = @state.channelThread

-    unless thread.getIn ['channel', '_originalPurpose']
-      _originalPurpose = thread.getIn ['channel', 'purpose']
-      thread = thread.setIn ['channel', '_originalPurpose'], _originalPurpose
+    unless @channel '_originalPurpose'
+      thread = thread.setIn ['channel', '_originalPurpose'], @channel 'purpose'

     channelThread = thread.setIn ['channel', 'purpose'], newValue
-    @setState channelThread: channelThread
+    @setState { channelThread }

   renderPurposeArea: ->

     return  unless @state.channelThread

-    purpose = @state.channelThread.getIn ['channel', 'purpose']
-    purpose = Encoder.htmlDecode purpose
+    purpose = Encoder.htmlDecode @channel 'purpose'

     valueLink =
       value         : purpose
@@ -189,22 +181,18 @@

   renderHeader: ->

-    return  unless @state.channelThread
-    thread = @state.channelThread
-    channelName = thread.getIn ['channel', 'name']
+    return  unless thread = @state.channelThread

     <ThreadHeader thread={thread}>
       <PublicChannelLink to={thread}>
-        {"##{channelName}"}
+        {"##{@channel 'name'}"}
       </PublicChannelLink>
     </ThreadHeader>

   render: ->

-    return null  unless @state.channelThread
-    thread = @state.channelThread
-    channelName = thread.getIn ['channel', 'name']
+    return null  unless thread = @state.channelThread

       <CollaborationComingSoonModal
@@ -225,14 +213,14 @@

-            <PublicChatPane ref='pane' thread={@state.channelThread}/>
+            <PublicChatPane ref='pane' thread={thread}/>

         <ThreadSidebar
-          channelThread={@state.channelThread}
-          channelParticipants={@state.channelParticipants}/>
+          channelThread={thread}
+          channelParticipants={@state.channelParticipants} />

       {@props.children}


activity: cleanup unnecessary requires

activity: cleanup unnecessary requires

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -3,15 +3,11 @@
 KDReactorMixin       = require 'app/flux/base/reactormixin'
 ActivityFlux         = require 'activity/flux'
 immutable            = require 'immutable'
-getGroup             = require 'app/util/getGroup'
-classnames           = require 'classnames'
 ThreadSidebar        = require 'activity/components/threadsidebar'
 ThreadHeader         = require 'activity/components/threadheader'
-PublicChannelLink    = require 'activity/components/publicchannellink'
 ImmutableRenderMixin = require 'react-immutable-render-mixin'
 PublicChatPane       = require 'activity/components/publicchatpane'
 ChannelDropContainer = require 'activity/components/channeldropcontainer'
-Link                 = require 'app/components/common/link'

 module.exports = class ChannelThreadPane extends React.Component