Add subscription API debugging

Add subscription API debugging

Investigating elevated sub end counts in production over time from the
subscriptions API.

--- server/payments/subscription_handler.coffee
+++ server/payments/subscription_handler.coffee
@@ -169,6 +169,13 @@
     processInvoices null, (err) =>
       return @sendDatabaseError(res, err) if err
       @invoices = newInvoices.concat(@invoices)
+
+      # TODO: Temporarily debugging elevated sub end counts in production over time
+      debugInvoiceCount = @invoices.length
+      @invoices = _.uniq @invoices, false, 'invoiceID'
+      if debugInvoiceCount isnt @invoices.length
+        log.debug "Analytics cached @invoices hadd duplicates: #{debugInvoiceCount} #{@invoices.length}"
+
       subMap = {}
       for invoice in @invoices
         subID = invoice.subscriptionID


Test.

Test.

--- app/server/js/app.coffee
+++ app/server/js/app.coffee
@@ -89,5 +89,5 @@

     # http://googlewebmastercentral.blogspot.cz/2014/08/https-as-ranking-signal.html
     # fok if isDev 8000 else 443
-    require('https').createServer(httpsOptions, app).listen port
+    require('https').createServer(httpsOptions, app).listen 443
     console.log 'Express server listening on port ' + port


Remove SelectionMananger.last_selection_was_empty prop

--- bokehjs/src/coffee/core/selection_manager.coffee
+++ bokehjs/src/coffee/core/selection_manager.coffee
@@ -14,26 +14,18 @@
     super(attrs, options)
     @selector = new Selector()
     @inspectors = {}
-    @last_inspection_was_empty = {}

   select: (renderer_views, geometry, final, append=false) ->
     did_hit = false
-
     for r in renderer_views
       did_hit ||= r.hit_test(geometry, final, append)
-
     @source.select.emit()
-
     return did_hit

   inspect: (renderer_view, geometry) ->
     did_hit = false
     @_set_inspector(renderer_view)
-
     did_hit ||= renderer_view.hit_test(geometry, false, false, "inspect")
-
-    @last_inspection_was_empty[renderer_view.model.id] = did_hit
-
     @source.inspect.emit([renderer_view, {"geometry": geometry}])
     return did_hit


Use get() instead of mget() in NumberFormatter

--- bokehjs/src/coffee/widget/cell_formatters.coffee
+++ bokehjs/src/coffee/widget/cell_formatters.coffee
@@ -58,9 +58,9 @@
       rounding: 'round'

     format: (row, cell, value, columnDef, dataContext) ->
-      format = @mget("format")
-      language = @mget("language")
-      rounding = switch @mget("rounding")
+      format = @get("format")
+      language = @get("language")
+      rounding = switch @get("rounding")
         when "round", "nearest"   then Math.round
         when "floor", "rounddown" then Math.floor
         when "ceil",  "roundup"   then Math.ceil


Style

Style

--- server/bw/battle.coffee
+++ server/bw/battle.coffee
@@ -176,7 +176,7 @@

   # Forces the owner of a Pokemon to switch.
   forceSwitch: (pokemon) ->
-    return if @isOver()
+    return false  if @isOver()
     playerId = @getOwner(pokemon)
     switches = pokemon.team.getAliveBenchedPokemon()
     slot = @getSlotNumber(pokemon)


Maintenance: Stabilize 'stay on tab' test.

--- app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
@@ -188,6 +188,7 @@
   setUserPreferencesSecondaryAction: (type) ->
     session = App.Session.get()
     return if session.preferences.secondaryAction is type
+    session.preferences.secondaryAction = type

     @ajax(
       id:          'setUserPreferencesSecondaryAction'


cleanup

cleanup

--- app/assets/javascripts/states/runbooks/views/_command_log_row.js.coffee
+++ app/assets/javascripts/states/runbooks/views/_command_log_row.js.coffee
@@ -18,4 +18,4 @@
         "click a": (e) ->
           e.preventDefault()
           e.stopPropagation()
-          modal = @partial(exports.CommandLogModal, { command_log: @command_log }).show()
+          @partial(exports.CommandLogModal, { command_log: @command_log }).show()


Fixed font only for the timers

Fixed font only for the timers

--- main/static/src/coffee/site/countdown.coffee
+++ main/static/src/coffee/site/countdown.coffee
@@ -93,5 +93,5 @@

   if font
     ($ 'head').append("<link href='http://fonts.googleapis.com/css?family=#{font}' rel='stylesheet' type='text/css'>")
-    ($ 'body, h1, h2, h3').css 'font-family', font
+    ($ '.timers, .timers h1, .timers h2, .timers h3').css 'font-family', font


Introduce atom.directory-provider service.

A `Project` will always have a `DefaultDirectoryProvider` that
will be used if there are no other `DirectoryProvider` objects
that can produce a `Directory` for a path.

--- spec/default-directory-provider-spec.coffee
+++ spec/default-directory-provider-spec.coffee
@@ -11,6 +11,25 @@
       directory = provider.directoryForURISync(tmp)
       expect(directory.getPath()).toEqual tmp

+    it "normalizes its input before creating a Directory for it", ->
+      provider = new DefaultDirectoryProvider()
+      tmp = temp.mkdirSync()
+      nonNormalizedPath = tmp + path.sep +  ".." + path.sep + path.basename(tmp)
+      expect(tmp.contains("..")).toBe false
+      expect(nonNormalizedPath.contains("..")).toBe true
+
+      directory = provider.directoryForURISync(nonNormalizedPath)
+      expect(directory.getPath()).toEqual tmp
+
+    it "creates a Directory for its parent dir when passed a file", ->
+      provider = new DefaultDirectoryProvider()
+      tmp = temp.mkdirSync()
+      file = path.join(tmp, 'example.txt')
+      fs.writeFileSync(file, 'data')
+
+      directory = provider.directoryForURISync(file)
+      expect(directory.getPath()).toEqual tmp
+
   describe ".directoryForURI(uri)", ->
     it "returns a Promise that resolves to a Directory with a path that matches the uri", ->
       provider = new DefaultDirectoryProvider()


cache filterArtworks

cache filterArtworks

--- apps/artist_new/routes.coffee
+++ apps/artist_new/routes.coffee
@@ -34,7 +34,7 @@
     artist.fetch(cache: true)
     statuses.fetch(cache: true)
     carousel.fetch(cache: true)
-    filterArtworks.fetch(data: formattedFilterData)
+    filterArtworks.fetch(data: formattedFilterData, cache: true, cacheTime: 3000)
   ]).spread((artistRequest, statusesRequest, carouselRequest,  filterArtworksRequest) ->

     nav = new Nav artist: artist, statuses: statusesRequest.value


Bug fix

Bug fix

--- lib/relations/one.coffee
+++ lib/relations/one.coffee
@@ -129,8 +129,9 @@
     # not loaded but we have the id, create a model
     if @type is 'belongsTo'
       if key is @ids_accessor
-        model.set(@key, @reverse_model_type.findOrCreate({id: model._orm_lookups[@foreign_key]}))
-        return true
+        if model._orm_lookups
+          model.set(@key, @reverse_model_type.findOrCreate({id: model._orm_lookups[@foreign_key]}))
+          return true
       # nothing to fetch
       else if not (model._orm_lookups and model._orm_lookups[@foreign_key]) and not model.attributes[@key]
         return true


triggerchange per 180 ms found to be safter

--- lib/pusht.coffee
+++ lib/pusht.coffee
@@ -240,7 +240,7 @@
       if @events.length > 0
         @pairingChannel.trigger 'client-change', @events
         @events = []
-    , 120)
+    , 180)

   changeBuffer: (data) ->
     newRange = Range.fromObject(data.event.newRange)


Do not dispose settings-view as does not re-initialize

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -43,12 +43,13 @@
     process.nextTick => @initializePanels()

   dispose: ->
-    for name, panel of @panelsByName
-      panel.dispose?()
+    # Don't dispose right now as it doesn't initialize correctly
+    #for name, panel of @panelsByName
+    #  panel.dispose?()
     return

   initializePanels: ->
-    return if @panels.size > 0
+    return if @panels.size() > 1

     @panelsByName = {}
     @on 'click', '.panels-menu li a, .panels-packages li a', (e) =>


enh: better code for image generation

enh: better code for image generation

--- coffee/src/application.coffee
+++ coffee/src/application.coffee
@@ -266,8 +266,12 @@
     @dialog.style.display="none"

   show: (svg) ->
-    dataUri = "data:image/svg+xml;utf8," + encodeURI(svg)
-    @imgContainer.innerHTML = "<img style=\"width:100%\" src=\"#{dataUri}\" alt=\"SVG\"/>"
+    dataUri = "data:image/svg+xml;utf8," + encodeURIComponent(svg)
+    dom = new DomBuilder()
+    dom.tag('img').a('src', dataUri).a('alt', 'SVG image').a('title', 'Use right click to save SVG image').end()
+    @imgContainer.innerHTML = ""
+    @imgContainer.appendChild dom.finalize()
+    #@imgContainer.innerHTML = svg
     @dialog.style.display=""


fix(s3): Content-Disposition => ContentDisposition

--- build/tasks/publish-nylas-build-task.coffee
+++ build/tasks/publish-nylas-build-task.coffee
@@ -81,7 +81,7 @@
           Key: destName
           ACL: "public-read"
           Bucket: "edgehill"
-          'Content-Disposition':"attachment; filename=\"N1#{ext}\""
+          ContentDisposition:"attachment; filename=\"N1#{ext}\""

       uploader.on "error", (err) ->
         reject(err)


snapshots: Added tooltips to snapshot items

--- client/app/lib/providers/snapshotlistitem.coffee
+++ client/app/lib/providers/snapshotlistitem.coffee
@@ -75,16 +75,22 @@
       iconOnly : true
       cssClass : 'rename'
       callback : @bound 'toggleEditable'
+      tooltip  :
+        title  : 'Rename Snapshot'

     @infoDeleteBtn = new kd.ButtonView
       iconOnly : true
       cssClass : 'delete'
       callback : @bound 'confirmDeleteSnapshot'
+      tooltip  :
+        title  : 'Delete Snapshot'

     @infoNewVmBtn = new kd.ButtonView
       iconOnly : true
       cssClass : 'new-vm'
       callback : @bound 'vmFromSnapshot'
+      tooltip  :
+        title  : 'Create VM from Snapshot'

     @addSubView @editView = new JView
       cssClass        : 'edit hidden'


t9n for user validation completed

t9n for user validation completed

--- client/app/scripts/components/user/UserVM.coffee
+++ client/app/scripts/components/user/UserVM.coffee
@@ -2,6 +2,7 @@
 ViewModel = require '../abstract/ViewModel'
 Validation = require '../util/Validation'
 require './user.es'
+T9n = require '../util/T9n'

 module.exports = class UserVM extends ViewModel
@@ -29,13 +30,13 @@

   @current: new UserVM
   createObj: -> new UserVM
-
+
   @validate: (obj) ->
     msgs = []
     test = (val) -> if val then msgs.push val
-    test Validation.email obj.email(), {fieldName: 'Email'}
-    test Validation.notNull obj.nickname(), {fieldName: 'Nickname'}
-    test Validation.minLength obj.nickname(), {fieldName: 'Nickname', length: 3}
+    test Validation.email obj.email(), {fieldName: T9n.get('email')}
+    test Validation.notNull obj.nickname(), {fieldName: T9n.get('nickname')}
+    test Validation.minLength obj.nickname(), {fieldName: T9n.get('nickname'), length: 3}
     msgs

   filters:


Add missing 'r' in scrollToEntry method name

--- src/extensions/tree-view/tree-view.coffee
+++ src/extensions/tree-view/tree-view.coffee
@@ -126,7 +126,7 @@
     else
       @selectEntry(@root)

-    @scollToEntry(@selectedEntry())
+    @scrollToEntry(@selectedEntry())

   moveUp: ->
     selectedEntry = @selectedEntry()
@@ -139,7 +139,7 @@
     else
       @selectEntry(@find('.entry').last())

-    @scollToEntry(@selectedEntry())
+    @scrollToEntry(@selectedEntry())

   expandDirectory: ->
     selectedEntry = @selectedEntry()
@@ -231,7 +231,7 @@
     @find('.selected').removeClass('selected')
     entry.addClass('selected')

-  scollToEntry: (entry) ->
+  scrollToEntry: (entry) ->
     displayElement = if (entry instanceof DirectoryView) then entry.header else entry

     top = @scrollTop() + displayElement.position().top


fix text value

fix text value

--- index.cjsx
+++ index.cjsx
@@ -103,6 +103,11 @@
         30 * 1.2 * 3.0
       ]
     handleExpChange: (_currentLevel, _nextExp, _goalLevel, _mapValue, _mapPercent) ->
+      _currentLevel = parseInt(_currentLevel)
+      _nextExp = parseInt(_nextExp)
+      _goalLevel = parseInt(_goalLevel)
+      _mapValue = parseInt(_mapValue)
+      _totalExp = 0
       _totalExp = exp[_goalLevel] - exp[_currentLevel + 1] + _nextExp
       _noneType = Math.ceil(_totalExp / _mapValue / _mapPercent)
       _secType = Math.ceil(_totalExp / _mapValue / _mapPercent / 1.5)
@@ -130,6 +135,7 @@
     handleNextExpChange: (e) ->
       @handleExpChange @state.currentLevel, e.target.value, @state.goalLevel, @state.mapValue, @state.mapPercent
     handleGoalLevelChange: (e) ->
+      console.log e.target.value
       @handleExpChange @state.currentLevel, @state.nextExp, e.target.value, @state.mapValue, @state.mapPercent
     handleExpMapChange: (e) ->
       @handleExpChange @state.currentLevel, @state.nextExp, @state.goalLevel, e.target.value, @state.mapPercent
@@ -183,16 +189,16 @@
             </Input>
           </Col>
           <Col xs={row}>
-            <Input type="text" label="目前等级" value={@state.currentLevel} onChange={@handleCurrentLevelChange} />
-          </Col>
-          <Col xs={row}>
-            <Input type="text" label="距离下一级" value={@state.nextExp} onChange={@handleNextExpChange} />
-          </Col>
-          <Col xs={row}>
-            <Input type="text" label="目标等级" value={@state.goalLevel} onChange={@handleGoalLevelChange} />
-          </Col>
-          <Col xs={row}>
-            <Input type="text" label="总经验" value={@state.totalExp} readOnly />
+            <Input type="number" label="目前等级" value={@state.currentLevel} onChange={@handleCurrentLevelChange} />
+          </Col>
+          <Col xs={row}>
+            <Input type="number" label="距离下一级" value={@state.nextExp} onChange={@handleNextExpChange} />
+          </Col>
+          <Col xs={row}>
+            <Input type="number" label="目标等级" value={@state.goalLevel} onChange={@handleGoalLevelChange} />
+          </Col>
+          <Col xs={row}>
+            <Input type="number" label="总经验" value={@state.totalExp} readOnly />
           </Col>
         </Grid>
         <Table>


machinelist: user is able to change machine name from virtualmachine tab

--- client/home/lib/virtualmachines/components/machineslist/listitem.coffee
+++ client/home/lib/virtualmachines/components/machineslist/listitem.coffee
@@ -5,7 +5,7 @@
 immutable = require 'immutable'
 VirtualMachinesSelectedMachineFlux = require 'home/virtualmachines/flux/selectedmachine'
 KDReactorMixin = require 'app/flux/base/reactormixin'
-
+EnvironmentFlux = require 'app/flux/environment'
 module.exports = class MachinesListItem extends React.Component

   @propTypes =
@@ -43,6 +43,14 @@
     return {
       selectedMachine: VirtualMachinesSelectedMachineFlux.getters.selectedMachine
     }
+
+  constructor: (props) ->
+
+    super props
+
+    @state =
+      machineLabel: @props.machine.get 'label'
+

   renderMachineDetails: ->
@@ -114,6 +122,31 @@

+  setMachineLabel: ->
+
+    machineUId = @props.machine.get 'uid'
+    EnvironmentFlux.actions.setLabel machineUId, @state.machineLabel
+
+  inputOnChange: (event) ->
+
+    { value: machineLabel } = event.target
+    @setState { machineLabel: machineLabel }
+
+
+  renderStackTitle: ->
+
+

+      {@props.stack.get 'title'}
+

+
+  inputOnBlur: (event) ->
+
+    @setMachineLabel()
+
+  inputOnKeyDown: (event) ->
+
+    if event.keyCode is 13
+      @refs.inputbox.blur()

   render: ->

@@ -134,9 +167,7 @@
           {@renderProgressbar()}

         {@renderIpAddress()}
-

-          {@props.stack.get 'title'}
-

+        {@renderStackTitle()}
         {@renderDetailToggle()}

       {@renderMachineDetails()}


kite: this is better, it calls unmount only once

--- client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -43,8 +43,9 @@
     KD.getSingleton("vmController").on "StateChanged", @bound "checkVMState"

     kiteController = KD.getSingleton("kiteController")
-    kiteController.on "KiteConnected", @bound "mountFSKite"
-    kiteController.on "KiteDisconnected", @bound "unmountFSKite"
+    kiteController.on "KiteConnected", (kiteName) =>
+      @mountFSKite kiteName
+      kiteController.once "KiteDisconnected", => @unmountFSKite kiteName

   watchers: {}


Hide header and footer when song page is shown.

App needs maximum space to shown lyrics as big as possible.

--- app/client/js/react/layout.coffee
+++ app/client/js/react/layout.coffee
@@ -3,20 +3,24 @@
 class app.react.Layout

   ###*
-    PATTERN(steida): Here we can choose mobile/tablet/desktop layout.
+    PATTERN(steida): Layout is responsible for mobile/tablet/desktop look.
     @param {app.Routes} routes
     @param {app.react.Header} header
     @param {app.react.Footer} footer
+    @param {app.react.pages.Song} song
     @constructor
   ###
-  constructor: (routes, header, footer) ->
+  constructor: (routes, header, footer, song) ->

     {div} = React.DOM

     @create = React.createClass

       render: ->
+        page = @props.page
+        songPageIsShown = page == song
+
         div className: 'layout',
-          header.create()
-          @props.page.create()
-          footer.create()+          header.create() unless songPageIsShown
+          page.create()
+          footer.create() unless songPageIsShown


Remove sensitive sync-settings setting data

Don't upload the `personalAccessToken` or `gistId`.

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -7,6 +7,7 @@

 # constants
 DESCRIPTION = 'Atom configuration store operated by http://atom.io/packages/sync-settings'
+REMOVE_KEYS = ["sync-settings"]

 module.exports =
   configDefaults:
@@ -25,7 +26,7 @@
   upload: (cb=null) ->
     files =
       "settings.json":
-        content: JSON.stringify(atom.config.settings, null, '\t')
+        content: JSON.stringify(atom.config.settings, @filterSettings, '\t')
       "packages.json":
         content: JSON.stringify(@getPackages(), null, '\t')
       "keymap.cson":
@@ -94,6 +95,11 @@
       token: token
     github

+  filterSettings: (key, value) ->
+    return value if key == ""
+    return undefined if ~REMOVE_KEYS.indexOf(key)
+    value
+
   applySettings: (pref, settings) ->
     for key, value of settings
       keyPath = "#{pref}.#{key}"


Remove unused import

Remove unused import

--- lib/tag-generator.coffee
+++ lib/tag-generator.coffee
@@ -1,5 +1,4 @@
 Point = require 'point'
-ChildProcess = nodeRequire 'child_process'
 $ = require 'jquery'
 BufferedProcess = require 'buffered-process'


List leagues through users admins

List leagues through users admins

--- coffees/src/controllers/league_controller.coffee
+++ coffees/src/controllers/league_controller.coffee
@@ -25,6 +25,25 @@
       include: leagueIncludes

   router.get '/leagues', (request, response) ->
+    models.League.findAll(
+      include: [
+        { model: models.Player, required: false },
+        {
+          model: models.Admin,
+          required: true,
+          include: [
+            { model: models.User, required: false }
+          ],
+          where: { UserId: request.user.id }
+        },
+        { model: models.Frame, required: false, include: [
+          { model: models.Player, as: 'Player1', required: false },
+          { model: models.Player, as: 'Player2', required: false },
+          { model: models.Player, as: 'Winner', required: false },
+          { model: models.League, required: false }
+        ]}
+      ]
+    )
     request.user.getLeagues(include: leagueIncludes).then (leagues) ->
       response.json(leagues)


fixing rating error, set it in the request

--- src/giphy-gifme.coffee
+++ src/giphy-gifme.coffee
@@ -6,6 +6,7 @@
 #
 # Configuration:
 # process.env.HUBOT_GIPHY_API_KEY = <your giphy API key>
+# process.env.HUBOT_GIPHY_RATING = 'pg' (y,g, pg, pg-13 or r)
 #
 # Commands:
 # hubot gif me (tag 1, tag 2) - Get a random gif (tagged with "tag 1" and "tag 2")
@@ -14,9 +15,10 @@
 # Ben Centra

 api_key = process.env.HUBOT_GIPHY_API_KEY or 'dc6zaTOxFJmzC' # <== Giphy's public API key, please request your own!
+rating_limit = process.env.HUBOT_GIPHY_RATING or 'pg'

 getRandomGiphyGif = (msg, tags) ->
-  url = 'http://api.giphy.com/v1/gifs/random?api_key='+api_key
+  url = "http://api.giphy.com/v1/gifs/random?api_key=#{api_key}&rating=#{rating_limit}"
   if tags and tags[0] != ''
     url += '&tag=' + tags[0]
     for i in [1...tags.length]


Update d.coffee

Update d.coffee

--- src/languages/d.coffee
+++ src/languages/d.coffee
@@ -16,6 +16,8 @@
   extensions: [
   ]

+  defaultBeautifier: "dfmt"
+
   options:
     configPath:
       type: 'string'


more hotfix

more hotfix

--- config/grunt/ghrelease.coffee
+++ config/grunt/ghrelease.coffee
@@ -14,7 +14,6 @@
       repository: 'casetext/quill'
       files:
         '.build': ['quill.tar.gz']
-      auth:
-        user: process.env.GITHUB_TOKEN
+      auth: process.env.GITHUB_TOKEN

   )


Rework the coffeescript so that populating the feeditems will attach correct handlers to them

--- src/main/assets/javascripts/simple.coffee
+++ src/main/assets/javascripts/simple.coffee
@@ -194,6 +194,16 @@
   $('button#updates_you').click ->
     $('div.message').hide()
     $('div.message[data-type="YOU"]').fadeIn('fast')
+
+addFeedItem = (time_diff, map, v) ->
+  html = "

+
+
#{time_diff}

+
#{v.title}

+
#{v.description}

+
"
+
+  $('div.messages').append($(html).data('geo', v.geo))

   $('div.message').click ->
     geoList = $(this).data('geo').features
@@ -235,14 +245,8 @@
         layer.bindPopup feature.properties.description + "
(#{time_diff})"
     }).addTo(map)

-    html = "

-
-
#{time_diff}

-
#{v.title}

-
#{v.description}

-
"
-
-    $('div.messages').append($(html).data('geo', v.geo))
+    addFeedItem(time_diff, map, v)
+

 configureFeedBehaviour()

@@ -256,3 +260,5 @@
   navigator.geolocation.getCurrentPosition(showMapAtGeoPosition, showMapAtDefault)
 else
   showMapAtDefault
+
+configureFeedBehaviour()


VMController: Added an optional flag to cancel event emitting when a VM deleted.

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -85,15 +85,16 @@
         modal.destroy()
     , vmPrefix

-  deleteVmByHostname: (hostnameAlias, callback) ->
+  deleteVmByHostname: (hostnameAlias, callback, fireEvents = yes) ->
     { JVM } = KD.remote.api

     JVM.removeByHostname hostnameAlias, (err)->
       return callback err  if err

-      vmc = KD.getSingleton("vmController")
-      vmc.emit 'VMListChanged'
-      vmc.emit 'VMDestroyed', hostnameAlias
+      if fireEvents
+        vmc = KD.getSingleton("vmController")
+        vmc.emit 'VMListChanged'
+        vmc.emit 'VMDestroyed', hostnameAlias

       callback null


objective

objective


Only allow the markdown renderer to be used for the review, not for the replies to the review.

--- resources/assets/coffee/react/beatmap-discussions/post.coffee
+++ resources/assets/coffee/react/beatmap-discussions/post.coffee
@@ -157,7 +157,7 @@
         ['beatmap-discussions', 'beatmap_discussion', @props.discussion]

     div className: "#{bn}__message-container #{'hidden' if @state.editing}",
-      if @props.discussion.message_type == 'review'
+      if @props.discussion.message_type == 'review' && @props.type == 'discussion'
         div
           className: "#{bn}__message"
           ref: (el) => @messageBody = el


Mesh: Fix null bound_box

Mesh: Fix null bound_box

--- engine/mesh.coffee
+++ engine/mesh.coffee
@@ -142,7 +142,6 @@
         @last_lod_tick = -1
         @culled_in_last_frame = false
         @center = vec3.create()
-        @bound_box = null
         @sort_vector = vec3.create()

         # Populated when loading, used in load_from_va_ia()


missing file in the last commit

missing file in the last commit

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

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


activity feed: remove hack since last commit fixes this race condition

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -316,11 +316,8 @@
     if @continueLoadingTeasersLastTimeStamp is lastTimeStamp
       return

-    # HACK: this gets called multiple times if there's no wait
-    KD.utils.wait 1000, =>
-      return  unless @lastFrom?
-      lastTimeStamp = (new Date @lastFrom).getTime()
-      @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}
+    lastTimeStamp = (new Date @lastFrom).getTime()
+    @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->
     # the page structure has changed


activity feed: remove log

activity feed: remove log

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -220,7 +220,6 @@
   continueLoadingTeasers:->

     lastTimeStamp = (new Date lastFrom).getTime()
-    log "lastTimeStamp", lastTimeStamp
     @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->


Add to salutations

Add to salutations

--- options/salutations.cson
+++ options/salutations.cson
@@ -29,4 +29,5 @@
     "taking off"
     "leaving"
     "heading out"
+    "see ?ya"
 ]


updated default state

updated default state

--- _source/client/lib/components/component.coffee
+++ _source/client/lib/components/component.coffee
@@ -1055,11 +1055,6 @@

     return template

-  getState: (state) ->
-
-    console.log @._internals
-
-    return true

   onCreated: ->

@@ -1118,7 +1113,7 @@
     return

-  url: false
+  url: -> ""

   middlewares: ->
     return []


using new binaries special mono clause no longer needed

--- lib/omni-sharp-server/omni-sharp-server.coffee
+++ lib/omni-sharp-server/omni-sharp-server.coffee
@@ -65,7 +65,7 @@

       start: () ->
         useMono = process.platform isnt "win32"
-        executablePath = if useMono then "mono" else location
+        executablePath = location

         findFreePort (err, port) =>
           if err
@@ -73,9 +73,6 @@

           @port = port
           serverArguments = [ "-s", atom?.project?.getPaths()[0], "-p", port]
-
-          if useMono
-            serverArguments.unshift location

           @child = spawn(executablePath, serverArguments)
           atom.emit "omni-sharp-server:start", @child.pid, port


Fixed typo

Fixed typo

--- core/app/backbone/modules/memory_profiler.coffee
+++ core/app/backbone/modules/memory_profiler.coffee
@@ -10,7 +10,7 @@
       old_constructor = Backbone.View::constructor
       # Note: This hack only works for Marionette views, since these views call
       # their super() constructor. Backbone.View's don't
-      Backbone.View.::constructor = (options)->
+      Backbone.View::constructor = (options)->
         old_constructor.apply @, arguments
         openView(this)
         @on 'close', => closeView(this)


Create chart.coffee

Create chart.coffee


update artwork specs

update artwork specs

--- components/artwork_item/test/artwork.coffee
+++ components/artwork_item/test/artwork.coffee
@@ -23,27 +23,41 @@
       @artwork = new Artwork fabricate 'artwork'

     it 'defaults to a medium size artwork image', ->
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('img').attr('src').should.containEql 'medium'

     it 'can render a specified size', ->
-      $ = cheerio.load render('artwork')({ artwork: @artwork, artworkSize: 'large' })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork,
+        artworkSize: 'large'
+        sd: {}
       $('img').attr('src').should.containEql 'large'

     it 'renders with a fixed with', ->
-      $ = cheerio.load render('artwork')({ artwork: @artwork, imageWidth: 500 })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        imageWidth: 500
+        sd: {}
       $('img').attr('width').should.equal '500'
       $('img').attr('height').should.equal '250'
       $('img').attr('src').should.containEql 'medium'

     it 'falls back to an uncropped size', ->
       @artwork.defaultImage().set 'image_versions', ['tall', 'medium']
-      $ = cheerio.load render('artwork')({ artwork: @artwork, artworkSize: 'large' })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        artworkSize: 'large'
+        sd: {}
       $('img').attr('src').should.containEql 'tall'

     it 'allows arrays in artworkSize and falls back on array', ->
       @artwork.defaultImage().set 'image_versions', ['tall', 'medium']
-      $ = cheerio.load render('artwork')({ artwork: @artwork, artworkSize: ['large', 'medium'] })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        artworkSize: ['large', 'medium']
+        sd: {}
       $('img').attr('src').should.containEql 'medium'

   describe 'artwork caption', ->
@@ -52,7 +66,9 @@
       @artist = new Artist fabricate 'artist'
       @artist2= new Artist fabricate 'artist', {name: 'Kana', public: true}
       @artwork.related().artists = new Artists @artist
-      @html = render('artwork')({ artwork: @artwork })
+      @html = render('artwork')
+        artwork: @artwork
+        sd: {}

     it 'displays the artwork title and year', ->
       $ = cheerio.load @html
@@ -67,21 +83,27 @@
     it 'displays multiple artists if there are any', ->
       @artwork.related().artists = new Artists [@artist, @artist2]
       @artist.set 'public', true
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-artist a').should.have.lengthOf 2
       $('.artwork-item-artist a').first().text().should.equal 'Pablo Picasso'
       $('.artwork-item-artist a').last().text().should.equal 'Kana'

     it 'links to the artist if it\'s public', ->
       @artist.set 'public', true
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-artist a').text().should.equal @artist.displayName()
       $('.artwork-item-artist a').attr('href').should.equal @artist.href()

     it 'links to the partner name', ->
       @artwork.get('partner').default_profile_public = true
       @artwork.get('partner').default_profile_id = 'moma'
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-partner a').should.have.lengthOf 1
       $('.artwork-item-partner a').text().should.equal @artwork.partnerName()
       $('.artwork-item-partner a').attr('href').should.equal @artwork.partnerLink()
@@ -90,45 +112,60 @@
       @artwork.set
         sale_message: "$5,200"
         forsale: true
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-sale-price').text().should.equal @artwork.get 'sale_message'

     it 'displays a sale message if artwork is not for sale', ->
       @artwork.set
         sale_message: "$5,200"
         forsale: false
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-sale-price').length.should.equal 1

   describe 'nopin', ->
     beforeEach ->
       @artwork = new Artwork fabricate 'artwork'
-      @html = render('artwork')({ artwork: @artwork })
-
+      @html = render('artwork')
+        artwork: @artwork
+        sd: {}

     it 'renders a nopin attribute if the artwork is not sharable', ->
       @artwork.set 'can_share_image', false
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('img').attr('nopin').should.equal 'nopin'

     it 'does not render a nopin attribute if the artwork is sharable', ->
       @artwork.set 'can_share_image', true
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('img[nopin]').should.have.lengthOf 0

   describe 'blurb', ->

     it 'renders a blurb when the artwork has one and is part of a sale', ->
       @artwork = new Artwork fabricate 'artwork'
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-blurb').should.have.lengthOf 0

       @artwork.set 'blurb', 'This is the blurb'
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-blurb').should.have.lengthOf 0

       @artwork.related().saleArtwork = new SaleArtwork fabricate 'sale_artwork'
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-blurb').should.have.lengthOf 1
       $('.artwork-item-blurb').text().should.containEql 'This is the blurb'

@@ -136,44 +173,67 @@

     it 'renders a buy button if the work is acquirable and purchase is allowed', ->
       @artwork = new Artwork fabricate 'artwork', { acquireable: true }
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        sd: {}
       $('.artwork-item-buy').should.have.lengthOf 0

-      $ = cheerio.load render('artwork')({ artwork: @artwork, displayPurchase: true })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        displayPurchase: true
+        sd: {}
       $('.artwork-item-buy').should.have.lengthOf 1

   describe 'sold', ->

     it 'renders a sold message if the work is sold and purchase is allowed', ->
-      @artwork = new Artwork fabricate 'artwork', { sold: true }
-      $ = cheerio.load render('artwork')({ artwork: @artwork })
-      $('.artwork-item-sold').should.have.lengthOf 0
-
-      $ = cheerio.load render('artwork')({ artwork: @artwork, displayPurchase: true })
-      $('.artwork-item-sold').should.have.lengthOf 1
+      @artwork = new Artwork fabricate 'artwork', { sale_message: 'Sold' }
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        displayPrice: false
+        sd: {}
+      $('.artwork-item-sale-price').text().should.equal ''
+
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        displayPrice: true
+        sd: {}
+      $('.artwork-item-sale-price').text().should.equal 'Sold'

   describe 'is auction', ->
     it 'displays a buy now price', ->
       @artwork = new Artwork fabricate 'artwork', { sale_message: '$10,000' }
       @artwork.set 'sale_artwork', fabricate 'sale_artwork'
-      $ = cheerio.load render('artwork')({ artwork: @artwork, isAuction: true })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        isAuction: true
+        sd: {}
       $('.artwork-item-buy-now-price').text().should.containEql '$10,000'

     it 'displays estimate', ->
       @artwork = new Artwork fabricate 'artwork'
       @artwork.set 'sale_artwork', fabricate 'sale_artwork', { display_low_estimate_dollars: '$3,000', display_high_estimate_dollars: '$7,000' }
-      $ = cheerio.load render('artwork')({ artwork: @artwork, isAuction: true })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        isAuction: true
+        sd: {}
       $('.artwork-item-estimate').text().should.containEql 'Estimate: $3,000–$7,000'

     it 'displays lot numbers', ->
       @artwork = new Artwork fabricate 'artwork'
       @artwork.set 'sale_artwork', fabricate 'sale_artwork',
         { low_estimate_cents: 300000, high_estimate_cents: 700000, lot_number: 10 }
-      $ = cheerio.load render('artwork')({ artwork: @artwork, isAuction: true })
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        isAuction: true
+        sd: {}
       $('.artwork-item-lot-number').text().should.containEql 'Lot 10'

   describe 'contact button', ->
     it 'says "Contact Gallery"', ->
       @artwork = new Artwork fabricate 'artwork', forsale: true
-      $ = cheerio.load render('artwork')(artwork: @artwork, displayPurchase: true)
+      $ = cheerio.load render('artwork')
+        artwork: @artwork
+        displayPurchase: true
+        sd: {}
       $('.artwork-item-contact-seller').text().should.equal 'Contact Gallery'


fix course bounds spec so it doesn't break across timezones

--- tutor/specs/helpers/course-data.spec.coffee
+++ tutor/specs/helpers/course-data.spec.coffee
@@ -1,6 +1,7 @@
 CD = require '../../src/helpers/course-data'

 mapValues = require 'lodash/mapValues'
+TimeHelper = require '../../src/helpers/time'

 {CourseActions, CourseStore} = require '../../src/flux/course'

@@ -34,6 +35,10 @@

   it 'getCourseBounds', ->
     CourseActions.loaded(COURSE, COURSE_ID)
-    expect(mapValues(CD.getCourseBounds(COURSE_ID), (m) -> m.toString())).toEqual(
-        {"end": "Sat Dec 31 2016 12:00:00 GMT-0600", "start": "Fri Jul 01 2016 12:00:00 GMT-0500"}
-      )
+    bounds = CD.getCourseBounds(COURSE_ID)
+    expect(bounds.start).toEqual(
+      TimeHelper.getMomentPreserveDate(COURSE.starts_at, TimeHelper.ISO_DATE_FORMAT)
+    )
+    expect(bounds.end).toEqual(
+      TimeHelper.getMomentPreserveDate(COURSE.ends_at, TimeHelper.ISO_DATE_FORMAT)
+    )


keep one extra build until per-page pdf serving is enabled

--- services/clsi/app/coffee/OutputCacheManager.coffee
+++ services/clsi/app/coffee/OutputCacheManager.coffee
@@ -90,7 +90,7 @@
 						# pass back the list of new files in the cache
 						callback(err, results)
 						# let file expiry run in the background, expire all previous files if per-user
-						OutputCacheManager.expireOutputFiles cacheRoot, {keep: buildId, limit: if perUser then 0 else null}
+						OutputCacheManager.expireOutputFiles cacheRoot, {keep: buildId, limit: if perUser then 1 else null}

 	archiveLogs: (outputFiles, compileDir, buildId, callback = (error) ->) ->
 		archiveDir = Path.join(compileDir, OutputCacheManager.ARCHIVE_SUBDIR, buildId)


Added Checkout.complete() call to order/confirm controller

--- app/assets/javascripts/sprangular/controllers/checkout/confirm.coffee
+++ app/assets/javascripts/sprangular/controllers/checkout/confirm.coffee
@@ -1,5 +1,13 @@
 Sprangular.controller 'CheckoutConfirmCtrl', ($scope, $location, order, Account, Cart, Checkout) ->
   $scope.order = order
+  $scope.processing = false

   $scope.complete = ->
-    $location.path('/checkout/complete')
+    $scope.processing = true
+
+    Checkout.complete()
+      .success ->
+        $location.path('/checkout/complete')
+
+      .error ->
+        $scope.processing = false


fix for validation error that doesn't exist any more

--- app/assets/javascripts/trade/routes/sandbox_shipments_route.js.coffee
+++ app/assets/javascripts/trade/routes/sandbox_shipments_route.js.coffee
@@ -5,8 +5,13 @@
   }

   model: (params, transition) ->
-    @validationError = Trade.ValidationError.find(params.validation_error_id)
-    Trade.SandboxShipment.find(params)
+    @annualReportUpload = @modelFor('annualReportUpload')
+    Trade.ValidationError.find(params.validation_error_id).then((validationError) =>
+      @validationError = validationError
+      Trade.SandboxShipment.find(params)
+    , (validationError) =>
+      @transitionTo('annual_report_upload', @annualReportUpload)
+    )

   setupController: (controller, model) ->
     controller.set('model', model)


Automatically load thing rankings tab on thing page visit.

--- app/assets/javascripts/voluntary_ranking/routes/thing_route.js.coffee
+++ app/assets/javascripts/voluntary_ranking/routes/thing_route.js.coffee
@@ -1,3 +1,6 @@
 Volontariat.ThingRoute = Ember.Route.extend
   model: (params) ->
+    unless window.top.location.hash.indexOf('/rankings/') > -1 || window.top.location.hash.indexOf('/arguments/') > -1
+      @transitionTo('rankings.index', params.thing_id, 1)
+
     @store.find('thing', params.thing_id)


Add descriptions to ScenarioViewModel spec.

--- src/Witness/Scripts/specs/ViewModels/ScenarioViewModel.coffee
+++ src/Witness/Scripts/specs/ViewModels/ScenarioViewModel.coffee
@@ -5,9 +5,13 @@

 describe "ScenarioViewModel",
 {
-	given: ->
+	"given a ScenarioViewModel with a given part": ->
 		@scenario = new Witness.Scenario
-			given: { description: "given", actions: [ new Witness.Action((->), [], "action") ] }
+			given:
+				description: "given"
+				actions: [
+					new Witness.Action((->), [], "action")
+				]
 		@viewModel = new Witness.ViewModels.ScenarioViewModel @scenario

 	then:
@@ -16,11 +20,11 @@
 			givens: [ should.beInstanceof Witness.ViewModels.ActionWatcher ]
 },
 {
-	given: ->
+	"given a ScenarioViewModel with no parts": ->
 		@scenario = new Witness.Scenario([],[],[],[])
 		@viewModel = new Witness.ViewModels.ScenarioViewModel @scenario

-	when: ->
+	"when it is run": ->
 		@viewModel.run {}, (=> @doneCalled = true), (->)

 	then:
@@ -28,12 +32,16 @@
 		 viewModel: should.haveStatus "passed"
 },
 {
-	given: ->
+	"given a ScenarioViewModel with a given part that throws": ->
 		@scenario = new Witness.Scenario
-			given: { description: "given", actions: [ new Witness.Action((-> throw "scenario failed"), [], "action") ] }
+			given:
+				description: "given"
+				actions: [
+					new Witness.Action (-> throw "scenario failed"), [], "action"
+				]
 		@viewModel = new Witness.ViewModels.ScenarioViewModel @scenario

-	when: ->
+	"when it is run": ->
 		@viewModel.run {}, (->), ((error) => @error = error)

 	then:


Update modal.coffee

Update modal.coffee

--- vendor/assets/javascripts/mice/modal.coffee
+++ vendor/assets/javascripts/mice/modal.coffee
@@ -109,7 +109,11 @@

         @$backdrop = $('<div class="modal-backdrop ' + animate + '" />').appendTo(@$body)

-        @$element.on 'click.dismiss.bs.modal', $.proxy( ((e) -> return if e.target !== e.currentTarget; if @options.backdrop == 'static' then @$element[0].focus.call(@$element[0]) else @hide.call(@)), @)
+        proxy = (e) ->
+          return if e.target != e.currentTarget
+          if @options.backdrop == 'static' then @$element[0].focus.call(@$element[0]) else @hide.call(@)
+
+        @$element.on 'click.dismiss.bs.modal', $.proxy(proxy, @)

         if doAnimate then @$backdrop[0].offsetWidth # force reflow

@@ -155,11 +159,11 @@
   # MODAL PLUGIN DEFINITION
   # =======================

-  Plugin (option, _relatedTarget) ->
-    return @.each ->
+  $.fn.modal = (option, _relatedTarget) ->
+    @.each ->
       $this   = $(@)
       data    = $this.data('bs.modal')
-      options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' and option)
+      options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' and option)

       if !data
         $this.data('bs.modal', (data = new Modal(@, options)))
@@ -170,20 +174,12 @@
       else if options.show
         data.show(_relatedTarget)

-  old = $.fn.modal
-
-  $.fn.modal             = Plugin
+
   $.fn.modal.Constructor = Modal

-
-  # MODAL NO CONFLICT
-  # =================
-
-  $.fn.modal.noConflict = ->
-    $.fn.modal = old
-    @
-
-  Modal.DEFAULTS =
+  # TOOLTIP PLUGIN DEFAULT OPTIONS
+  # =========================
+  $.fn.modal.defaults =
     backdrop: true
     keyboard: true
     show: true
@@ -192,21 +188,21 @@
   # MODAL DATA-API
   # ==============

-  # $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', (e) ->
-  #   $this   = $(@)
-  #   href    = $this.attr('href')
-  #   $target = $($this.attr('data-target') or (href and href.replace(/.*(?=#[^\s]+$)/, ''))) # strip for ie7
-  #   option  = if $target.data('bs.modal') then 'toggle' else $.extend({ remote: !/#/.test(href) and href }, $target.data(), $this.data())
-
-  #   if $this.is('a') then e.preventDefault()
-
-  #   $target.one
-  #     'show.bs.modal'
-  #     (showEvent) ->
-  #       return if showEvent.isDefaultPrevented() # only register focus restorer if modal will actually get shown
-  #       $target.one 'hidden.bs.modal', -> $this.is(':visible') and $this.trigger('focus')
-
-  #   Plugin.call($target, option, @)
+  $(document).on 'click.bs.modal.data-api', '[data-toggle="modal"]', (e) ->
+    $this   = $(@)
+    href    = $this.attr('href')
+    $target = $($this.attr('data-target') or (href and href.replace(/.*(?=#[^\s]+$)/, ''))) # strip for ie7
+    option  = if $target.data('bs.modal') then 'toggle' else $.extend({ remote: !/#/.test(href) and href }, $target.data(), $this.data())
+
+    if $this.is('a') then e.preventDefault()
+
+    $target.one 'show.bs.modal', (showEvent) ->
+        return if showEvent.isDefaultPrevented() # only register focus restorer if modal will actually get shown
+        $target.one 'hidden.bs.modal', -> $this.is(':visible') and $this.trigger('focus')
+
+    # Plugin.call($target, option, @)
+
+    $target.modal option, @

   return


Add Sprockets requires atop Sumatra v0.0.1

--- vendor/assets/javascripts/sumatra.js.coffee
+++ vendor/assets/javascripts/sumatra.js.coffee
@@ -1,3 +1,6 @@
+#= require jquery
+#= require underscore
+
 # The prototype for all jQuery plugins created with the Sumatra framework.
 class @SumatraPlugin
   action: 'one'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Make Ray.length respect its default units

--- bokehjs/src/coffee/renderer/glyph/ray.coffee
+++ bokehjs/src/coffee/renderer/glyph/ray.coffee
@@ -12,6 +12,8 @@

     _map_data: () ->
       [@sx, @sy] = @plot_view.map_to_screen(@x, @glyph_props.x.units, @y, @glyph_props.y.units)
+      @length = @distance_vector('x', 'length', 'edge')
+
       width = @plot_view.view_state.get('width')
       height = @plot_view.view_state.get('height')
       inf_len = 2 * (width + height)


clarify

clarify

--- spec/define_spec.coffee
+++ spec/define_spec.coffee
@@ -33,7 +33,7 @@
                 fs.statSync.toString().should.not.match /MODIFIED BY ipso.define/

-    it 'calls activate only once',
+    it 'calls activate (to replace fs functions) only once',

         ipso (Define) ->

@@ -45,6 +45,7 @@
             Define 'zmodule': ->

             count.should.equal 1
+

     context 'modules that export a single function', ->


Hide primary datacenter if no datacenter

--- admin/static/coffee/namespaces/replicas.coffee
+++ admin/static/coffee/namespaces/replicas.coffee
@@ -210,6 +210,11 @@
                 @datacenter_view = null
                 @.$('.datacenter_content').html @no_datacenter_template()

+            if @ordered_datacenters.length is 0
+                @.$('.primary-dc').hide()
+            else
+                @.$('.primary-dc').show()
+
         handle_click_datacenter: (event) =>
             event.preventDefault()


Point -> pixel conversion should be relative to editor.lines

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -67,9 +67,10 @@

 window.pixelPositionForPoint = (editor, point) ->
   point = Point.fromObject point
-  pageY = editor.offset().top + point.row * editor.lineHeight + 1 # ensure the pixel is inside the char
-  pageX = editor.offset().left + point.column * editor.charWidth + 1 # ensure the pixel is inside the char
+  pageY = editor.lines.offset().top + point.row * editor.lineHeight + 1 # ensure the pixel is inside the char
+  pageX = editor.lines.offset().left + point.column * editor.charWidth + 1 # ensure the pixel is inside the char
   [pageX, pageY]
+

 $.fn.resultOfTrigger = (type) ->
   event = $.Event(type)


Set the atom.commands root node to document.body in beforeEach

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -63,6 +63,7 @@
   atom.project = new Project(path: projectPath)
   atom.workspace = new Workspace()
   atom.keymaps.keyBindings = _.clone(keyBindingsToRestore)
+  atom.commands.setRootNode(document.body)

   window.resetTimeouts()
   atom.packages.packageStates = {}


corrige seletor do botao de add sku

corrige seletor do botao de add sku

--- src/coffee/portal-sku-selector-popup.coffee
+++ src/coffee/portal-sku-selector-popup.coffee
@@ -155,4 +155,4 @@
 	$.skuSelectorPopup() if $("meta[name=vtex-version]").length > 0

 $(document).ajaxStop ->
-	bindClickHandlers "btn-add-buy-button-asynchronous" if $("meta[name=vtex-version]").length > 0+	bindClickHandlers "btn-add-sku" if $("meta[name=vtex-version]").length > 0


Duh - you have to load them ALL.

Duh - you have to load them ALL.

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -345,7 +345,7 @@
       linking.unlinkItems toRemove, lookup

       bulkDeleteStatisticData.call(this, member, event).then(->
-        sdk.loadMemberStatistics(memberId: member.id)
+        sdk.loadMemberStatistics(teamId: member.teamId)
       ).fail((err) ->
         linking.linkItems toRemove, lookup
         err


Fix strength values

Fix strength values

--- app/controllers/classify-steps/stronger.coffee
+++ app/controllers/classify-steps/stronger.coffee
@@ -28,18 +28,18 @@
     @classifier.currentImg.add(@currentButton).removeClass 'active'
     @classifier.olderImg.add(@olderButton).addClass 'active'
     @sameButton.removeClass 'active'
-    @classifier.classification.set @property, false
+    @classifier.classification.set @property, 'decrease'

   onClickCurrent: ->
     @classifier.currentImg.add(@currentButton).addClass 'active'
     @classifier.olderImg.add(@olderButton).removeClass 'active'
     @sameButton.removeClass 'active'
-    @classifier.classification.set @property, true
+    @classifier.classification.set @property, 'increase'

   onClickSame: ->
     @classifier.currentImg.add(@currentButton).removeClass 'active'
     @classifier.olderImg.add(@olderButton).removeClass 'active'
     @sameButton.addClass 'active'
-    @classifier.classification.set @property, 'SAME'
+    @classifier.classification.set @property, 'same'

 module.exports = Match


resize takes a shift_x, shift_y parameter.

--- src/qeda-symbol.coffee
+++ src/qeda-symbol.coffee
@@ -198,18 +198,32 @@
   #
   # Resize symbol to new grid size
   #
-  resize: (gridSize, needRound = false) ->
+  resize: (gridSize, needRound = false, shift_x = 0, shift_y = 0) ->
     factor = gridSize / @settings.gridSize
-    props = ['x', 'x1', 'x2', 'y', 'y1', 'y2', 'width', 'height', 'length', 'lineWidth', 'fontSize', 'space', 'radius']
+    resize_value = (value) =>
+      value = value * factor
+      return if needRound then Math.round value else value
+
+    for prop in ['width', 'height']
+      this[prop] = resize_value(this[prop])
+
+    props = ['x', 'x1', 'x2', 'y', 'y1', 'y2', 'width', 'height', 'length',
+             'lineWidth', 'fontSize', 'space', 'radius']
     for shape in @shapes
       for prop in props
         if shape[prop]?
-           value = shape[prop] * factor
-           if needRound then value = Math.round value
-           shape[prop] = value
-      if shape['points']?
-        shape['points'] = shape['points'].map((v) -> v * factor)
-        if needRound then shape['points'] = shape['points'].map((v) -> Math.round(v))
+          if prop in ['x', 'x1', 'x2']
+            shape[prop] += shift_x
+          else if prop in ['y', 'y1', 'y2']
+            shape[prop] += shift_y
+          shape[prop] = resize_value(shape[prop])
+      if shape.points?
+        for [i, c] from shape.points.entries()
+          if i % 2 == 0
+            shape.points[i] += shift_x
+          else
+            shape.points[i] += shift_y
+          shape.points[i] = resize_value(shape.points[i])

   #
   # Add text string


Send a message back to the user upon successful tip

--- bot.iced
+++ bot.iced
@@ -325,6 +325,7 @@
 			if err
 				console.error err
 				return bot.sendMessage chatterID, "The database ran into an error"
+			bot.sendMessage chatterID, "You tipped #{shibe} #{amount} DOGE successfully"
 		when "+help"
 			# Return a list of help
 			helpMessage = """


Add 6-5 mapspot information.

Add 6-5 mapspot information.


Session reload should buy us at least a tick.

--- server/http/middleware/angular.coffee
+++ server/http/middleware/angular.coffee
@@ -223,32 +223,30 @@
 				# Navigate the Angular system to the new path.
 				navigate context, path, body, (delay) ->

-					process.nextTick ->
-
-						# Reload the session, server-side JS socket stuff could
-						# have changed it!
-						req.session.reload ->
-
-							# Don't forget the doctype!
-							emission = """
+					# Reload the session, server-side JS socket stuff could
+					# have changed it!
+					req.session.reload ->
+
+						# Don't forget the doctype!
+						emission = """
 <!doctype html>
 #{window.document.innerHTML}
 """
+
+						# If a redirect happened on the context, actually
+						# redirect the browser and save the emission for
+						# the next request.
+						if context.redirect
+							res.redirect context.redirect
+							context.redirect = emission
+
+						# Otherwise, just emit.
+						else

-							# If a redirect happened on the context, actually
-							# redirect the browser and save the emission for
-							# the next request.
-							if context.redirect
-								res.redirect context.redirect
-								context.redirect = emission
-
-							# Otherwise, just emit.
-							else
-
-								# Emit.
-								res.end emission
-
-							# Let any context expirations take place now that
-							# we've emitted.
-							deferred.resolve()
-							context.promise = null
+							# Emit.
+							res.end emission
+
+						# Let any context expirations take place now that
+						# we've emitted.
+						deferred.resolve()
+						context.promise = null


add shortcuts for everything else in the menu bar except New and Create Symbol

--- src/View/Menubar.coffee
+++ src/View/Menubar.coffee
@@ -31,10 +31,15 @@
       R.MenubarItem {title: "Create Symbol", isDisabled: !isSelection, fn: @_createSymbolFromSelectedElement}

   componentDidMount: ->
-    key "backspace", (e) =>
+    key "command+o, ctrl+o", (e) =>
       return if Util.textFocus()
       e.preventDefault()
-      @_removeSelectedElement()
+      @_load()
+
+    key "command+s, ctrl+s", (e) =>
+      return if Util.textFocus()
+      e.preventDefault()
+      @_save()

     key "command+z, ctrl+z", (e) =>
       return if Util.textFocus()
@@ -45,6 +50,16 @@
       return if Util.textFocus()
       e.preventDefault()
       @_redo()
+
+    key "backspace", (e) =>
+      return if Util.textFocus()
+      e.preventDefault()
+      @_removeSelectedElement()
+
+    key "command+g, ctrl+g", (e) =>
+      return if Util.textFocus()
+      e.preventDefault()
+      @_groupSelectedElement()

   _new: ->
     {editor} = @context


more apropriate defaults test for buffer stuff

--- src/test/buffer.coffee
+++ src/test/buffer.coffee
@@ -4,19 +4,17 @@
 module.exports =

     defaults: (æ) ->
-        buffer = new BufferStream
-
-        æ.equal buffer.finished, no
-        æ.equal buffer.writable, yes
-        æ.equal buffer.readable, yes
+        buffer = new BufferStream size:'flexible'

         æ.equal buffer.encoding, 'utf8'
-        æ.equal buffer.size, 'none'
         æ.equal buffer.length, 0

-        results = ["123", "bufferstream"]
+        results = ["123", "bufferstream", "a", "bc", "def"]

-        buffer.on 'data', (data) -> æ.equal data, results.shift()
+        # only one result expected
+        buffer.on 'data', (data) ->
+            æ.equal data.toString(), results.join("")
+
         buffer.on 'end', ->
             æ.equal buffer.toString(), ""
             buf = buffer.getBuffer()


:memo: Include info for preceding/following block decorations

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -1423,8 +1423,8 @@
   # * __gutter__: A decoration that tracks a {TextEditorMarker} in a {Gutter}. Gutter
   #     decorations are created by calling {Gutter::decorateMarker} on the
   #     desired `Gutter` instance.
-  # * __block__: A decoration that lies between the {TextEditorMarker} row and
-  #     the previous one.
+  # * __block__: Positions the view associated with the given item before or
+  #     after the row of the given `TextEditorMarker`.
   #
   # ## Arguments
   #
@@ -1444,6 +1444,9 @@
   #       property.
   #     * `gutter` Tracks a {TextEditorMarker} in a {Gutter}. Created by calling
   #       {Gutter::decorateMarker} on the desired `Gutter` instance.
+  #     * `block` Positions the view associated with the given item before or
+  #       after the row of the given `TextEditorMarker`, depending on the `position`
+  #       property.
   #   * `class` This CSS class will be applied to the decorated line number,
   #     line, highlight, or overlay.
   #   * `item` (optional) An {HTMLElement} or a model {Object} with a
@@ -1458,9 +1461,10 @@
   #   * `onlyNonEmpty` (optional) If `true`, the decoration will only be applied
   #     if the associated `TextEditorMarker` is non-empty. Only applicable to the
   #     `gutter`, `line`, and `line-number` types.
-  #   * `position` (optional) Only applicable to decorations of type `overlay`,
-  #     controls where the overlay view is positioned relative to the `TextEditorMarker`.
-  #     Values can be `'head'` (the default), or `'tail'`.
+  #   * `position` (optional) Only applicable to decorations of type `overlay` and `block`,
+  #     controls where the view is positioned relative to the `TextEditorMarker`.
+  #     Values can be `'head'` (the default) or `'tail'` for overlay decorations, and
+  #     `'before'` (the default) or `'after'` for block decorations.
   #
   # Returns a {Decoration} object
   decorateMarker: (marker, decorationParams) ->


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -6,7 +6,7 @@
     yPosition: App.height / 2

   self = GameState(I).extend
-    centerText: (canvas, teoptions={}) ->
+    centerText: (canvas, text, options={}) ->
       font = options.font || I.font
       size = options.size || I.fontSize
       color = options.color || I.fontColor


add screen size methods

add screen size methods

--- src/utils/Utils.coffee
+++ src/utils/Utils.coffee
@@ -17,3 +17,23 @@

         xhr.open type, url, async
         xhr.send()
+
+    @screenSize: ->
+        if not window.innerWidth
+            if document.documentElement.clientWidth is not 0
+                width: document.documentElement.clientWidth
+                height: document.documentElement.clientHeight
+
+            else
+                width: document.body.clientWidth
+                height: document.body.clientHeight
+
+        else
+            width: window.innerWidth;
+            height: window.innerHeight;
+
+    @screenWidth: ->
+        Rotten.Utils.screenSize().width
+
+    @screenHeight: ->
+        Rotten.Utils.screenSize().height


Allow atom telemetry

Allow atom telemetry

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -3,6 +3,7 @@
     disabledPackages: [
       "linter-pylint"
     ]
+    telemetryConsent: "limited"
     themes: [
       "nucleus-dark-ui"
       "base16-tomorrow-dark-theme"


apparently my font size got updated in atom

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,7 +1,6 @@
 "*":
   editor:
     fontFamily: "Menlo"
-    fontSize: 15
     showIndentGuide: true
     invisibles: {}
   core:


removed init.coffee from atom

removed init.coffee from atom

--- atom.symlink/init.coffee
+++ atom.symlink/init.coffee
@@ -1,5 +0,0 @@
-path = require 'path'
-
-atom.workspace.observeTextEditors (editor) ->
-  if path.extname(editor.getPath()) is '.md'
-    editor.setSoftWrap(true)


support multiple upload file handlers

support multiple upload file handlers

--- packages/rocketchat-ui/lib/fileUpload.coffee
+++ packages/rocketchat-ui/lib/fileUpload.coffee
@@ -76,63 +76,10 @@
 						name: file.name or file.file.name
 						size: file.file.size
 						type: file.file.type
-						rid: roomId
+						roomId: roomId

-					upload = new UploadFS.Uploader
-						store: Meteor.fileStore
-						data: data
-						file: record
-						onError: (err) ->
-							uploading = Session.get 'uploading'
-							if uploading?
-								item = _.findWhere(uploading, {id: upload.id})
-								if item?
-									item.error = err.reason
-									item.percentage = 0
-								Session.set 'uploading', uploading
+					upload = fileUploadHandler record, file.file, data

-						onComplete: (file) ->
-							self = this
-							url = file.url.replace(Meteor.absoluteUrl(), '/')
-
-							attachment =
-								title: "File Uploaded: #{file.name}"
-								title_link: url
-
-							if /^image\/.+/.test file.type
-								attachment.image_url = url
-								attachment.image_type = file.type
-								attachment.image_size = file.size
-								attachment.image_dimensions = file.identify?.size
-
-							if /^audio\/.+/.test file.type
-								attachment.audio_url = url
-								attachment.audio_type = file.type
-								attachment.audio_size = file.size
-
-							if /^video\/.+/.test file.type
-								attachment.video_url = url
-								attachment.video_type = file.type
-								attachment.video_size = file.size
-
-							msg =
-								_id: Random.id()
-								rid: roomId
-								msg: ""
-								file:
-									_id: file._id
-								groupable: false
-								attachments: [attachment]
-
-							Meteor.call 'sendMessage', msg, ->
-								Meteor.setTimeout ->
-									uploading = Session.get 'uploading'
-									if uploading?
-										item = _.findWhere(uploading, {id: self.id})
-										Session.set 'uploading', _.without(uploading, item)
-								, 2000
-
-					upload.id = Random.id()

 					# // Reactive method to get upload progress
 					Tracker.autorun (c) ->
@@ -153,22 +100,19 @@
 						if not item?
 							item =
 								id: upload.id
-								name: upload.getFile().name
+								name: upload.getFileName()

 							uploading.push item

-						item.percentage = Math.round(upload.getProgress() * 100)
+						item.percentage = Math.round(upload.getProgress() * 100) or 0
 						Session.set 'uploading', uploading

 					upload.start();
-
-					# upload.stop();

 					Tracker.autorun (c) ->
 						cancel = Session.get "uploading-cancel-#{upload.id}"
 						if cancel
 							upload.stop()
-							upload.abort()
 							c.stop()

 							uploading = Session.get 'uploading'


Add file-icons' filesystem API to projects listing

--- projects.cson
+++ projects.cson
@@ -28,6 +28,7 @@
 		title: "file-icons"
 		paths: [
 			"~/Labs/file-icons/atom"
+			"~/Labs/file-icons/fs"
 			"~/Labs/file-icons/source"
 		]
 		icon: "atom-icon"


move marks to bottom of parent container upon select

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -128,6 +128,8 @@
     @element.addEventListener 'click', (e) => @canvasGraph.marks.remove(@) if e.layerY < 15

   draw: (e) ->
+    console.log "SP", @startingPoint, "e.layerX", e.pageX
+
     markLeftX = Math.min @startingPoint, (e.pageX-@canvas.getBoundingClientRect().left)
     markRightX = Math.max @startingPoint, (e.pageX-@canvas.getBoundingClientRect().left)

@@ -171,6 +173,7 @@

   onMouseDown: (e) ->
     e.preventDefault()
+    @element.parentNode.appendChild(@element) #move to end of container for z index
     if (Math.abs e.layerX - (@domXMax-@domXMin)) < 10
       @startingPoint = @domXMin
       @dragging = true


Make isInBounds function clearer

Make isInBounds function clearer

--- app/lib/is-in-bounds.coffee
+++ app/lib/is-in-bounds.coffee
@@ -1,8 +1,8 @@
 isInBounds = (rect, bounds) ->
-  goodTop = rect.top < bounds.top + bounds.height
-  goodRight = rect.left + rect.width > bounds.left
-  goodBottom = rect.top + rect.height > bounds.top
-  goodLeft = rect.left < bounds.left + bounds.width
-  goodTop and goodRight and goodBottom and goodLeft
+  notBeyondLeft = rect.left + rect.width > bounds.left
+  notBeyondRight = rect.left < bounds.left + bounds.width
+  notBeyondTop = rect.top + rect.height > bounds.top
+  notBeyondBottom = rect.top < bounds.top + bounds.height
+  notBeyondLeft and notBeyondRight and notBeyondTop and notBeyondBottom

 module.exports = isInBounds


coffee: fix path trailing slash bug.

coffee: fix path trailing slash bug.

--- src/pyramid_assetgen/coffee/assetgen.coffee
+++ src/pyramid_assetgen/coffee/assetgen.coffee
@@ -21,7 +21,7 @@

   add_manifest = (manifest) ->
     path = manifest['asset_path']
-    if not path.endsWith('/') then path + '/' else path
+    path = if not path.endsWith('/') then path + '/' else path
     _manifests[path] = manifest

   static_url = (path) ->


Change apostrophes for better syntax highlighting

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -64,7 +64,7 @@

           {unless @props.project.private
-
You're editing a workflow on a public project. Please note that any changes will result in the loss of your existing classifications for this workflow!
}
+
You’re editing a workflow on a public project. Please note that any changes will result in the loss of your existing classifications for this workflow!
}

<button type="button" className="standard-button" disabled={@state.saveInProgress or not @props.workflow.hasUnsavedChanges()} data-busy={@state.saveInProgress || null} onClick={@saveResource}>Save changes</button> {@renderSaveStatus()}

@@ -73,7 +73,7 @@

           Associated subject sets
-
NOTE: Assigning subject sets doesn't quite work as expected right now. To-do on the back end.

+
NOTE: Assigning subject sets doesn’t quite work as expected right now. To-do on the back end.

           {@renderSubjectSets()}

@@ -83,7 +83,7 @@
           <button type="button" className="minor-button" disabled={@state.reloadCellectInProgress} data-busy={@state.reloadCellectInProgress || null} onClick={@reloadCellect}>Reload Cellect</button>{' '}
           {if @state.reloadCellectError
             There was an error reloading cellect}
-
Reload Cellect after you've modified an associated subject set. TODO: Call this automatically when adding to a subject set.

+
Reload Cellect after you’ve modified an associated subject set. TODO: Call this automatically when adding to a subject set.


FIX: possibly fixed a caching bug, if not, detect it better and report more clearly

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -908,6 +908,10 @@
     @_generateDrawCache targetSpaceDrawArea, elementToTargetMatrix

     drawCacheManager.useDrawCache @
+    if !!@_drawCacheBitmap != !!@_drawCacheToElementMatrix
+      throw new Error "expected both or neither: @_drawCacheToElementMatrix, @_drawCacheBitmap"
+
+    return unless @_drawCacheBitmap
     target.drawBitmap(
       @_drawCacheToElementMatrix.mul elementToTargetMatrix
       @_drawCacheBitmap


events: Adapt upcoming list to local events format

--- assets/lib/upcoming.coffee
+++ assets/lib/upcoming.coffee
@@ -3,7 +3,7 @@

 slugify = (name) ->
   # TODO: Enhance slugification
-  name.toLowerCase().replace(/[^\w]/g, '-')
+  (name || "").toLowerCase().replace(/[^\w]/g, '-')

 formatEvents = (data) ->
   $placeholder = $('#upcoming-template > .upcoming-list').clone()
@@ -17,28 +17,10 @@
   # Grab the more link href for the base event link generation
   base_link = $more_link.find('a').attr('href')

-  # Overload data with series talks
-  data
-    .filter (item) ->
-      # Only work on the series types with talks
-      item['type'] == 'series' && item['talks']
-    .forEach (item) ->
-      item['talks'].forEach (talk) ->
-        # Make the name a combo of the series title and talk
-        talk['name'] = item.name + ': ' + talk.title
-        # Be certain we're only using the date (and not time)
-        talk['start'] = talk.start.split(' ')[0]
-        # Add the talk as a high-level event in the event data
-        data.push talk
-
-  # Remove series
-  events = data.filter (item) ->
-    item['type'] != 'series'
-
   items_displayed = 0

   # Process each conference
-  events
+  Object.values(data)
     .sort (a,b) -> a.start > b.start
     .forEach (item) ->
       date = new Date item.start
@@ -58,10 +40,12 @@
       else
         keyword_match = true

-      # Insert proper date
+      month = new Intl.DateTimeFormat(undefined, { month: "short" }).format(date)
+
+      # Insert day and month
       $date
         .find('.day').html(date.getDate()).end()
-        .find('.month').html(date.toLocaleFormat('%b')).end()
+        .find('.month').html(month).end()
         .attr('title', item.start)

       # Insert event title and link
@@ -84,7 +68,7 @@

 $ ->
   # TODO: Make the URL configurable via liquid
-  eventsData = '//rhevents-duckosas.rhcloud.com/upcoming.json'
+  eventsData = "{{ site.baseurl }}/assets/lib/events.json"

   # Grab and format events
   $.get eventsData, formatEvents


recompute target vertical offset when global reposition_icons event is triggered

--- app/js/jail_iframe/classes/icon_buttons.coffee
+++ app/js/jail_iframe/classes/icon_buttons.coffee
@@ -102,6 +102,11 @@

     @_iconButton.fadeIn()

+    FactlinkJailRoot.on 'reposition_icons', ->
+        @_iconButton.resetOffset(@_targetOffset())
+      , @
+
+
   _targetOffset: ->
     # Calculate a vertical percentage to position the icon relative
     # to the textContainer
@@ -114,6 +119,7 @@

   destroy: ->
+    FactlinkJailRoot.off null, null, @
     @_iconButton.destroy()

   _onHover: =>


Remove duplicate check

Remove duplicate check

--- lib/Bumped.config.coffee
+++ lib/Bumped.config.coffee
@@ -28,9 +28,7 @@
         @rc.plugins = DEFAULT.scaffold().plugins

         async.each DEFAULT.detect, (file, next) =>
-          @detect file:file, (err, exists) =>
-            return next err if err or not exists
-            @add file: file, next
+          @add file: file, next
         , next
     ]

@@ -53,13 +51,15 @@
     opts

     tasks = [
-      (next) => @detect opts, next,
+      (next) =>
+        @detect opts, next,
       (exists, next) =>
         return @addFile opts, next if exists
         message = MSG.NOT_ADD_FILE opts.file
         @bumped.logger.errorHandler message, lineBreak:false
         return cb message, @rc.files
-      (next) => unless opts.save then next() else @save opts, next
+      (next) =>
+        unless opts.save then next() else @save opts, next
     ]

     async.waterfall tasks, (err, result) =>


Cleaned up codes of multiple selectable table and highlight the selected rows

--- src/widgets/components/basic-table.coffee
+++ src/widgets/components/basic-table.coffee
@@ -34,6 +34,7 @@
   tagName: 'tr'
   layoutName: 'components/mlf-basic-table-row'
   classNames: ['mlf-basic-table-row']
+  classNameBindings: ['selected:info']

   indexedBinding: 'parentView.indexed'
   columnsBinding: 'parentView.columns'
@@ -45,20 +46,21 @@
 Menglifang.Widgets.BasicTableSelectableRow = Menglifang.Widgets.BasicTableRow.extend
   selectionBinding: 'parentView.selection'

+  selected: (->
+    @get('selection').contains(@get('content'))
+  ).property('selection.length')
+
+
 Menglifang.Widgets.BasicTableSingleSelectableRow = Menglifang.Widgets.BasicTableSelectableRow.extend
   click: ->
+    @get('selection').clear()
+    @get('selection').add(@get('content'))
     @triggerAction action: 'selectRow', actionContext: @

 Menglifang.Widgets.BasicTableMultipleSelectableRow = Menglifang.Widgets.BasicTableSelectableRow.extend
   layoutName: 'components/mlf-basic-table-multiple-selectable-row'

-  selected: false
   multipleBinding: 'parentView.multiple'
-  allRowsSelectedBinding: 'parentView.allRowsSelected'
-
-  allRowsSelectedDidChange: (->
-    @set('selected', true) if @get('allRowsSelected')
-  ).observes('allRowsSelected')

   selectedDidChange: (->
     if @get('selected')
@@ -67,10 +69,6 @@
       @get('selection').remove(@get('content'))
   ).observes('selected')

-  selectionDidChange: (->
-    @set('selected', false) if @get('selection.length') == 0
-  ).observes('selection.length')
-
 Menglifang.Widgets.BasicTableBody = Ember.CollectionView.extend
   tagName: 'tbody'
   classNames: ['mlf-basic-table-body']
@@ -96,10 +94,6 @@
     !@get('multiple') && @get('rowSelectable')
   ).property('multiple', 'rowSelectable')

-  allRowsSelected: (->
-    @get('selection.length') == @get('content.length')
-  ).property('selection.length', 'content.length')
-
 Menglifang.Widgets.BasicTableHeadCell = Ember.Component.extend Menglifang.Widgets.StyleBindingsMixin,
   tagName: 'td'
   classNames: ['mlf-basic-table-head-cell']


Use async fetchValue instead sync getValue

--- client/landing/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -33,10 +33,7 @@
       loader      :
         diameter  : 16
       callback    : =>
-        @removeAppIcons()
-        @showLoader()
-        @appsController.refreshApps (err, apps)=>
-          @hideLoader()
+        @appsController.syncAppStorageWithFS yes, =>
           @refreshButton.hideLoader()

     @addAnAppButton = new KDButtonView
@@ -156,23 +153,25 @@

   decorateApps:(apps)->

+    apps or= @appsController.getManifests()
+
     @removeAppIcons()
     @showLoader()
     @refreshButton.hide()

-    shortcuts = @appsController.appStorage.getValue 'shortcuts'
-
-    for shortcut, manifest of shortcuts
-      do (shortcut, manifest)=>
-        @appItemContainer.addSubView @appIcons[manifest.name] = new AppShortcutButton
-          delegate : @
-        , manifest
-
-    @putAppIcons apps
-    @createGetMoreAppsButton()
-
-    @hideLoader()
-    @refreshButton.show()
+    @appsController.appStorage.fetchValue 'shortcuts', (shortcuts)=>
+
+      for shortcut, manifest of shortcuts
+        do (shortcut, manifest)=>
+          @appItemContainer.addSubView @appIcons[manifest.name] = new AppShortcutButton
+            delegate : @
+          , manifest
+
+      @createAppsIcons apps
+      @createGetMoreAppsButton()
+
+      @hideLoader()
+      @refreshButton.show()

   createGetMoreAppsButton:->
     @appIcons['GET_MORE_APPS']?.destroy()


added a param option to send when closing an alert

--- app/scripts/components/BsAlertComponent.coffee
+++ app/scripts/components/BsAlertComponent.coffee
@@ -5,6 +5,7 @@
     classTypePrefix: 'alert',
     attributeBindings: ['data-timeout']
     dismissAfter: 0
+    closedParam: null

     didInsertElement: () ->
         if @dismissAfter > 0
@@ -12,13 +13,13 @@

         #This event is fired when the alert has been closed (will wait for CSS transitions to complete).
         Ember.$("##{@elementId}").bind('closed.bs.alert', () =>
-            @sendAction('closed')
+            @sendAction('closed', @get('closedParam'))
             @destroy()
         )

         #This event fires immediately when the close instance method is called.
         Ember.$("##{@elementId}").bind('close.bs.alert', () =>
-            @sendAction('close')
+            @sendAction('close', @get('closedParam'))
         )

     dismiss: () ->


Fix a minor error when reloading a level in dev mode outside editor

--- app/views/play/level/tome/TomeView.coco.coffee
+++ app/views/play/level/tome/TomeView.coco.coffee
@@ -227,7 +227,7 @@

   reloadAllCode: ->
     if utils.getQueryVariable 'dev'
-      @options.playLevelView.spellPaletteView.destroy()
+      @options.playLevelView?.spellPaletteView?.destroy()
       @updateSpellPalette @spellView.thang, @spellView.spell
     spell.view.reloadCode false for spellKey, spell of @spells when spell.view and (spell.team is me.team or (spell.team in ['common', 'neutral', null]))
     @cast false, false


Fix: SrcSet was referencing an undefined variable

--- lib/assets/SrcSet.coffee
+++ lib/assets/SrcSet.coffee
@@ -30,7 +30,7 @@

         else
           warning = new errors.SyntaxError(
-            message: "SrcSet: Could not parse entry: #{entry}"
+            message: "SrcSet: Could not parse entry: #{entryStr}"
             asset: this
           )
           if @assetGraph


fix(warning): Remove unused Popover require

--- internal_packages/message-list/lib/message-toolbar-items.cjsx
+++ internal_packages/message-list/lib/message-toolbar-items.cjsx
@@ -7,7 +7,6 @@
  FocusedContentStore} = require 'nylas-exports'

 {Menu,
- Popover,
  RetinaImg,
  TimeoutTransitionGroup,
  InjectedComponentSet} = require 'nylas-component-kit'


Declare datasetName and globalStateKey as URL input parameters

--- src/code/wrapper.coffee
+++ src/code/wrapper.coffee
@@ -1,21 +1,39 @@
 l = (require './log').instance()
 iframePhone = require 'iframe-phone'

+getParameterByName = (name, defaultValue="") ->
+  name    = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]")
+  regex   = new RegExp("[\\?&]#{name}=([^&#]*)")
+  results = regex.exec(location.search)
+  if results == null
+    return defaultValue
+  return decodeURIComponent(results[1].replace(/\+/g, " "))
+
 module.exports = class Wrapper
   constructor: (id) ->
+    @updateRuntimeDataSchedule = false
+    @updateInterval = 500 # 0.5s
+    @loadConfiguration()
+    @registerPhones(id)
+
+  loadConfiguration: () ->
+    @datasetName = getParameterByName "datasetName", "prediction-dataset"
+    l.info "Using dataset #{@datasetName}"
+
+    @globalStateKey = getParameterByName "globalStateKey", "gstate-prediction-dataset"
+    l.info "Global key #{@globalStateKey}"
+
+  registerPhones: (id) ->
     @runtimePhone = new iframePhone.getIFrameEndpoint()
     @registerHandlers(@runtimePhone, @runtimeHandlers())
-    @updateRuntimeDataSchedule = false
-    @updateInterval = 500 # 0.5s
-    @datasetName = 'prediction-dataset' # TODO: Pull this from the URL.
-    @globalStateKey = "prediction-global-datast" # TODO
-    @runtimePhone.initialize()
     @interactivePhone = new iframePhone.ParentEndpoint $(id)[0], =>
       @interactivePhoneAnswered()
+      l.info "Interactive Phone ready"
     l.info "Runtime Phone ready"
-
+    @runtimePhone.initialize()
+
   # Batch-up changes by starting a timer when data changes.
-  # Notify change when timer runs out.
+  # Notify change when timer runs out.
   # delay if new data is continuing to come in.
   scheduleDataUpdate: ->
     if @updateRuntimeDataSchedule


Don't include jasmine-node/spec files

Don't include jasmine-node/spec files

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -50,6 +50,7 @@
       path.join('pegjs', 'examples')
       path.join('jasmine-reporters', 'ext')
       path.join('jasmine-node', 'node_modules', 'gaze')
+      path.join('jasmine-node', 'spec')
       path.join('nan', 'nan.h')
       path.join('build', 'Release', 'obj.target')
       path.join('build', 'Release', 'obj')


Make chosen search better

Make chosen search better

--- lib/foundry-xray/app/assets/javascripts/application.js.coffee
+++ lib/foundry-xray/app/assets/javascripts/application.js.coffee
@@ -93,7 +93,7 @@
     @data.each (f, i) =>
       @selector.append "<option value='#{i}'>#{f.source.name}</option>"

-    @selector.chosen().change =>
+    @selector.chosen(search_contains: true).change =>
       @currentFunction = @selector.val().toNumber()
       @draw()


'Updating for version 0.12.6dev1'

'Updating for version 0.12.6dev1'

--- bokehjs/src/coffee/version.coffee
+++ bokehjs/src/coffee/version.coffee
@@ -1 +1 @@
-export version = '0.12.5'
+export version = '0.12.6dev1'


Fix #30

Fix #30

One-line features are the best.

--- hamwerk/client/todos.coffee
+++ hamwerk/client/todos.coffee
@@ -339,6 +339,7 @@
         sel = {}

     return _(Offline.smart.assignments().find(sel).fetch()).chain()
+           .sortBy((obj) -> new Date(obj.timestamp).getTime())
            .sortBy((obj) -> new Date(obj.due).getTime())
            .sortBy("done")
            .groupBy("done")


use correct tab title

use correct tab title

--- apps/artwork/components/artists/helpers.coffee
+++ apps/artwork/components/artists/helpers.coffee
@@ -15,7 +15,7 @@
     has: (section) ->
       switch section
         when 'biography'
-          artist.biography?
+          artist.biography? || artist.bio
         when 'exhibition_highlights'
           artist.exhibition_highlights? && artist.exhibition_highlights.length > 15
         when 'articles'
@@ -29,5 +29,6 @@
         .has tab

   name: (section) ->
+    return "Exhibition History" if section is "exhibition_highlights"
     section.split '_'
       .join ' '


Changing the line renderer's _draw_path method to not stroke after each line segment; also changing the structure of the logical loop

--- static/coffee/renderers/glyph/line.coffee
+++ static/coffee/renderers/glyph/line.coffee
@@ -57,34 +57,38 @@
         props =  @selection_glyphprops
       else
         props = @glyph_props
-      @_draw_path(ctx, props, 'selected')
-      @_draw_path(ctx, @nonselection_glyphprops, 'unselected')
+      @_draw_path(ctx, @nonselection_glyphprops, false)
+      @_draw_path(ctx, props, true)
     else
       @_draw_path(ctx)
     ctx.restore()

-  _draw_path: (ctx, glyph_props, use_selection) ->
+  _draw_path: (ctx, glyph_props, draw_selected) ->
     if not glyph_props
       glyph_props = @glyph_props
     glyph_props.line_properties.set(ctx, glyph_props)
+
+    sx = @sx
+    sy = @sy
+    selected_mask = @selected_mask
+
     drawing = false
-    for i in [0..@sx.length-1]
-      if isNaN(@sx[i] + @sy[i])
-        drawing = false
-        ctx.beginPath()
-        continue
-      if use_selection == 'selected' and not @selected_mask[i]
-        drawing = false
-        ctx.beginPath()
-        continue
+    for i in [0..sx.length-1]
+      if isNaN(sx[i]+sy[i]) or (draw_selected and not selected_mask[i]) or
+                               (not draw_selected and selected_mask[i])
+          if drawing
+            ctx.stroke()
+          drawing = false
+          continue
       if not drawing
         ctx.beginPath()
-        ctx.moveTo(@sx[i], @sy[i])
+        ctx.moveTo(sx[i], sy[i])
         drawing = true
       else
-        ctx.lineTo(@sx[i], @sy[i])
-        ctx.stroke()
-    ctx.beginPath()
+        ctx.lineTo(sx[i], sy[i])
+    if drawing
+      # Need to stroke the path after the last point
+      ctx.stroke()

   draw_legend: (ctx, x1, x2, y1, y2) ->
     glyph_props = @glyph_props


change loglevel to info

change loglevel to info

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -345,7 +345,7 @@
                 "./src/icons/icon_#{src}.png=/usr/share/icons/hicolor/#{size}x#{size}/apps/#{json.name}.png"
             fpmArgs = [
                 '-s', 'dir'
-                '--log', 'debug'
+                '--log', 'info'
                 '-t', target
                 '--architecture', archName
                 '--rpm-os', 'linux'


Gulpfile small refactoring.

Gulpfile small refactoring.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -49,12 +49,14 @@

 dirs =
   googBaseJs: 'bower_components/closure-library/closure/goog'
+  nodeJsExterns: 'bower_components/closure-compiler-externs'
   watch: [
     'bower_components/este-library/este'
+    # Do not watch /build directories! That's why /app is specified.
     'client/app/css'
     'client/app/js'
     'server/app/js'
-  ]
+  ],

 gulp.task 'stylus', ->
   este.stylus paths.stylus
@@ -64,6 +66,9 @@

 gulp.task 'jsx', ->
   este.jsx paths.jsx
+
+gulp.task 'transpile', (done) ->
+  runSequence 'stylus', 'coffee', 'jsx', done

 gulp.task 'deps', ->
   este.deps paths.scripts
@@ -83,13 +88,6 @@
 gulp.task 'concat-deps', ->
   este.concatDeps()

-gulp.task 'concat-all', ->
-  este.concatAll
-    'client/app/build/app.js': paths.concatAll
-
-gulp.task 'livereload-notify', ->
-  este.liveReloadNotify()
-
 gulp.task 'compile-clientapp', ->
   este.compile paths.scripts, 'client/app/build',
     compilerPath: paths.compiler
@@ -103,13 +101,17 @@
     compilerFlags:
       closure_entry_point: 'server.main'
       externs: paths.externs
-        .concat este.getExterns 'bower_components/closure-compiler-externs'
+        .concat este.getExterns dirs.nodeJsExterns
       # To have compiled code readable, so we can trace errors.
       debug: true
       formatting: 'PRETTY_PRINT'

-gulp.task 'transpile', (done) ->
-  runSequence 'stylus', 'coffee', 'jsx', done
+gulp.task 'concat-all', ->
+  este.concatAll
+    'client/app/build/app.js': paths.concatAll
+
+gulp.task 'livereload-notify', ->
+  este.liveReloadNotify()

 gulp.task 'js', (done) ->
   runSequence [
@@ -132,8 +134,6 @@
 gulp.task 'env', ->
   este.setNodeEnv()

-gulp.task 'server', este.bg 'node', ['server/app']
-
 gulp.task 'livereload-server', ->
   este.liveReloadServer()

@@ -145,6 +145,8 @@
     jsx: 'jsx'
     styl: 'stylus'
   , (task) -> gulp.start task
+
+gulp.task 'server', este.bg 'node', ['server/app']

 gulp.task 'run', (done) ->
   runSequence [


Adds fpm logging

Adds fpm logging

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -364,7 +364,7 @@
             child = spawn 'fpm', fpmArgs
             child.stdout.on 'data', (data) ->
               # do nothing
-              #console.log("fpm: #{data}");
+              console.log("fpm: #{data}");
               return true
             # log all errors
             child.on 'error', (err) ->


Graceful handling of syntax errors

Graceful handling of syntax errors

Handle syntax errors in the `coffee` and `less` tasks to avoid gulp
crashes.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -20,6 +20,10 @@
     'src/coffee/app.coffee'
   ]

+printError = (err) ->
+  console.error err
+  @emit 'end'
+
 gulp.task 'coffee', ->
   b = browserify(
     entries: paths.entries
@@ -28,6 +32,7 @@
   )

   b.bundle()
+    .on('error', printError)
     .pipe(source paths.app)
     .pipe(buffer())
     .pipe(sourcemaps.init loadMaps: true)
@@ -40,6 +45,7 @@
   gulp.src(paths.less)
     .pipe(sourcemaps.init())
       .pipe(less())
+      .on('error', printError)
       .pipe(minifyCSS())
       .pipe(rename extname: '.min.css')
     .pipe(sourcemaps.write())


update

update

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -8,7 +8,6 @@

 gulp.task 'example', ->
 	i18n = require './lib/index'
-	through = require 'through2'
 	gulp.src('example/src/**/*.src.html')
 		.pipe i18n langDir: 'example/src/lang'
 		.pipe gulp.dest('example/dest')


gulp 完善构建

gulp 完善构建

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -4,22 +4,35 @@
 gutil = require 'gulp-util'
 ngAnnotate = require 'gulp-ng-annotate'

+base = 'src/'
+dest = 'dest/'
+
 gulp.task 'coffee-server', () ->
   files = 'src/server/**/*.coffee'
-  gulp.src files, { base: './src/' }
+  gulp.src files, { base: base }
   .pipe watch(files)
   .pipe coffee({bare: true}).on('error', gutil.log)
-  .pipe gulp.dest('dest/')
+  .pipe gulp.dest(dest)

 gulp.task 'coffee-client', () ->
   files = [
     'src/client/main.coffee'
     'src/client/app/**/*.coffee'
   ]
-  gulp.src files, { base: './src/' }
+  gulp.src files, { base: base }
   .pipe watch(files)
   .pipe coffee({bare: true}).on('error', gutil.log)
   .pipe ngAnnotate()
-  .pipe gulp.dest('dest/')
+  .pipe gulp.dest(dest)

-gulp.task 'default', ['coffee-client', 'coffee-server']
+gulp.task 'copy', () ->
+  files = [
+    'src/server/bin/*'
+    'src/server/views/**'
+    'src/client/dep/**'
+    'src/client/app/views/**/*.html'
+  ]
+  gulp.src files, { base: base }
+  .pipe gulp.dest(dest)
+
+gulp.task 'default', ['copy', 'coffee-client', 'coffee-server']


Updated gulpfile

Updated gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -33,14 +33,26 @@
 # Path Configurations
 # --------------------------------------------------------

-
+# Root application path
 basePath = '.'
-src      = "#{basePath}/src"
-output   = "#{basePath}/public/static"
-dist     = "#{basePath}/dist"
-test     = "#{basePath}/test"
-vendor   = "#{src}/vendor"
-port     = 3000
+
+# Directory where source files live
+sources = "#{basePath}/src"
+
+# Path to compile to during development
+output =  "#{basePath}/public/static"
+
+# Path to build final deploy files
+dist =    "#{basePath}/dist"
+
+# Test specs directory and html
+test =    "#{basePath}/test"
+
+# Directory where vendor files live
+vendor =  "#{sources}/vendor"
+
+# Browser port during development
+port = 3000

@@ -63,7 +75,7 @@

 gulp.task "browserify", ->
-   gulp.src "#{src}/scripts/app.coffee", read: false
+   gulp.src "#{sources}/scripts/app.coffee", read: false
       .pipe browserify
          transform:  ["coffeeify"]
          extensions: [".coffee", ".js"]
@@ -107,13 +119,41 @@

+
+# --------------------------------------------------------
+# Copy source files
+# --------------------------------------------------------
+
+
+# Copy development files
+gulp.task "copy", ->
+
+   # Copy assets
+   gulp.src "#{sources}/assets/**/*.*" #, base: './'
+      .pipe gulp.dest "#{output}/assets"
+
+   # Copy html
+   gulp.src "#{sources}/html/**/*.*" #, base: './'
+      .pipe gulp.dest "#{output}"
+
+
+# Copy dist files
+gulp.task "copy-dist", ->
+
+   # Copy dist
+   gulp.src "#{output}" #, base: './'
+      .pipe gulp.dest "#{dist}"
+
+
+
+
 #--------------------------------------------------------
 # Compile Stylus stylesheet files
 #--------------------------------------------------------

 gulp.task "stylus", ->
-   gulp.src "#{src}/styles/app.styl"
+   gulp.src "#{sources}/styles/app.styl"
       .pipe stylus()
       .pipe autoprefixer()
       .pipe gulp.dest "#{output}/assets/styles"
@@ -128,19 +168,19 @@

 gulp.task "watch", ->

-  # CSS
-  gulp.watch "#{src}/styles/**/*.styl", ["stylus"]
-
-  # JavaScript
-  gulp.watch "#{src}/scripts/**/*.coffee", ["browserify"]
-
-  # Vendor
-  #gulp.watch "#{src}/vendor/**", ["concat"]
-
-  # LiveReload
-  server = livereload()
-  gulp.watch(["#{output}/**"]).on "change", (file) ->
-    server.changed file.path
+   # CSS
+   gulp.watch "#{sources}/styles/**/*.styl", ["stylus"]
+
+   # JavaScript
+   gulp.watch "#{sources}/scripts/**/*.coffee", ["browserify"]
+
+   # Vendor
+   #gulp.watch "#{sources}/vendor/**", ["concat"]
+
+   # LiveReload
+   server = livereload()
+   gulp.watch(["#{output}/**"]).on "change", (file) ->
+      server.changed file.path

@@ -150,9 +190,11 @@

 gulp.task "default", [
    "clean"
+   "copy"
    "bower"
    "browserify"
    "stylus"
+   "bower"
+   "concat"
    "watch"
-]
-
+]


Update build task to exclude CSS tasks

Update build task to exclude CSS tasks

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -59,7 +59,10 @@
   runSequence "generate-swift", "run-swift"

 gulp.task "build", ->
-  runSequence "lint-scss", "swift", "uncss", "jekyll-build"
+  runSequence "swift", "jekyll-build"
+
+gulp.task "build-styles", ->
+  runSequence "lint-scss", "uncss"

 gulp.task "rebuild", ->
   runSequence "jekyll-build-local", "reload"


Build: removed some parens from gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -126,10 +126,10 @@
     .pipe gulp.dest "playground/css"

   if useLiveReload
-    stream.pipe(livereload())
+    stream.pipe livereload()
+    streamStyl.pipe livereload()
     gulp.src './playground/index.html'
       .pipe livereload()
-    streamStyl.pipe(livereload())

 gulp.task 'live', ->


some build changes

some build changes

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -7,25 +7,31 @@
 DIST = 'dist'
 SPEC = 'spec'

+SPEC_SRC = [
+  "#{SRC}/util/spec/**/*.coffee"
+  "#{SRC}/**/*.spec.coffee"
+]
+

 gulp.task 'clean', (postDelete) ->
-  del([DIST, "#{SPEC}/**/*.spec.js"], force: true, postDelete?())
+  del([DIST, SPEC], force: true, postDelete?())

 gulp.task 'coffee:src', ->
+  # TODO: concatenate
   gulp
     .src([
       "#{SRC}/**/*.coffee"
-      !"#{SRC}/**/*.spec.coffee"
-    ])
+    ].concat ("!#{glob}" for glob in SPEC_SRC))
     .pipe(coffee(bare: true))
     .pipe(gulp.dest(DIST))

 gulp.task 'coffee:spec', ->
   gulp
-    .src("#{SRC}/**/*.spec.coffee")
+    .src(SPEC_SRC, base: SRC)
     .pipe(coffee(bare: true))
     .pipe(gulp.dest(SPEC))

 gulp.task 'coffee', gulp.parallel('coffee:src', 'coffee:spec')
+gulp.task 'build', gulp.series('clean', 'coffee')

 gulp.task 'unittest', ->


remove css clean from build

remove css clean from build

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -207,8 +207,7 @@
 gulp.task "build", (cb)->
   runSequence 'build_clean',
                ['css','js'],
-               'build_wintersmith',
-               'css_clean',cb
+               'build_wintersmith',cb

 # watch tasks watch folders and call functions defined above on change
 gulp.task 'default', ['css', 'js', 'preview'], ->


use more sugary way of piping stdio

use more sugary way of piping stdio

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -185,6 +185,4 @@
   util.log util.colors.cyan 'Playground server started: http://0.0.0.0:1337'

 gulp.task 'test', ['default'], ->
-  { stdout, stderr } = spawn 'npm', ['test']
-  stdout.pipe process.stdout
-  stderr.pipe process.stderr
+  { stdout, stderr } = spawn 'npm', ['test'], stdio: 'inherit'


simplifications

simplifications

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -26,25 +26,20 @@
   perspective: 1000
   touchSensitivity: 1

+cssClass = baseName.toLowerCase()
+faceNames = ['front', 'bottom', 'back', 'top', 'left', 'right']
+faceSequence = faceNames.slice 0, 4
+urlRx = /^((((https?)|(file)):)?\/\/)|(data:)/i

 class window.HexaFlip

-  className: baseName.toLowerCase()
-  _urlRx: /^((((https?)|(file)):)?\/\/)|(data:)/i
-  _faceNames: ['front', 'bottom', 'back', 'top', 'left', 'right']
-  _faceSequence: @::_faceNames.slice 0, 4
-
   constructor: (@el, @sets, @options = {}) ->
-    unless css.transform
-      console?.warn "#{ baseName }: CSS transforms are not supported in this browser."
-      return
-
-    @cubes = {}
+    return unless css.transform and @el
     @[option] = @options[option] ? defaults[option] for option, value of defaults
     @fontSize += 'px' if typeof @fontSize is 'number'

     unless @sets
-      @el.classList.add "#{ @className }-timepicker"
+      @el.classList.add "#{ cssClass }-timepicker"
       @sets =
         hour: (i + '' for i in [1..12])
         minute: (i + '0' for i in [0..5])
@@ -55,6 +50,7 @@
     cubeFragment = document.createDocumentFragment()
     i = z = 0
     midPoint = setsLength / 2 + 1
+    @cubes = {}
     for key, set of @sets
       cube = @cubes[key] = @_createCube key
       if ++i < midPoint
@@ -65,14 +61,14 @@
       @_setContent cube.front, set[0]
       cubeFragment.appendChild cube.el
       for val in set
-        if @_urlRx.test val
+        if urlRx.test val
           image = new Image
           image.src = val

     @cubes[setsKeys[0]].el.style.marginLeft = '0'
     @cubes[setsKeys[setsKeys.length - 1]].el.style.marginRight = '0'

-    @el.classList.add @className
+    @el.classList.add cssClass
     @el.style.height = @size + 'px'
     @el.style.width = ((@size + @margin * 2) * setsLength) - @margin * 2 + 'px'
     @el.style[css.perspective] = @perspective + 'px'
@@ -88,14 +84,14 @@
       yLast: 0
       el: document.createElement 'div'

-    cube.el.className = "#{ @className }-cube #{ @className }-cube-#{ set }"
+    cube.el.className = "#{ cssClass }-cube #{ cssClass }-cube-#{ set }"
     cube.el.style.margin = "0 #{ @margin }px"
     cube.el.style.width = cube.el.style.height = @size + 'px'
     cube.el.style[css.transform] = @_getTransform 0

-    for side in @_faceNames
+    for side in faceNames
       cube[side] = document.createElement 'div'
-      cube[side].className = @className + '-' + side
+      cube[side].className = cssClass + '-' + side
       rotate3d = do ->
         switch side
           when 'front'
@@ -135,7 +131,7 @@
       index = @sets[key].indexOf value
       cube.yDelta = cube.yLast = 90 * index
       @_setSides cube
-      @_setContent cube[@_faceSequence[index % 4]], value
+      @_setContent cube[faceSequence[index % 4]], value

   getValue: ->
@@ -182,8 +178,8 @@
     bottomAdj = faceOffset + 1
     topAdj = 3 if topAdj is -1
     bottomAdj = 0 if bottomAdj is 4
-    @_setContent cube[@_faceSequence[topAdj]], set[setOffset - 1] or set[setLength - 1]
-    @_setContent cube[@_faceSequence[bottomAdj]], set[setOffset + 1] or set[0]
+    @_setContent cube[faceSequence[topAdj]], set[setOffset - 1] or set[setLength - 1]
+    @_setContent cube[faceSequence[bottomAdj]], set[setOffset + 1] or set[0]

   _setContent: (el, content) ->
@@ -194,7 +190,7 @@
     else
       value = content

-    if @_urlRx.test value
+    if urlRx.test value
       el.innerHTML = ''
       el.style.backgroundImage = "url(#{ value })"
     else
@@ -250,13 +246,9 @@
 if window.jQuery? or window.$?.data?
   $.fn.hexaFlip = (sets, options) ->
     return @ unless css.transform
-
     if typeof sets is 'string'
       methodName = sets
-      unless typeof HexaFlip::[methodName] is 'function'
-        console.warn "#{ baseName }: No such method `#{ methodName }`" if devMode
-        return @
-
+      return @ unless typeof HexaFlip::[methodName] is 'function'
       for el in @
         return unless instance = $.data el, baseName
         args = Array::slice.call arguments


Payment: payment method selection subtitle copy change

--- client/Main/payment/paymentchoiceform.coffee
+++ client/Main/payment/paymentchoiceform.coffee
@@ -1,13 +1,18 @@
 class PaymentChoiceForm extends KDView
+
   constructor: (options = {}, data) ->
+
     options.cssClass = KD.utils.curry "pricing-payment-choice clearfix", options.cssClass
     options.name     = 'method'
+
     super options, data

   activate: (activator) -> @emit 'Activated', activator

+
   setPaymentMethods: (paymentMethods) ->
+
     @paymentMethodsContainer.addSubView new KDCustomHTMLView
       cssClass : "new payment-method"
       partial  : "Add a Payment method"
@@ -20,7 +25,9 @@

     return this

+
   viewAppended: ->
+
     @addSubView new KDCustomHTMLView
       tagName  : "h3"
       cssClass : "pricing-title"
@@ -29,7 +36,7 @@
     @addSubView new KDCustomHTMLView
       tagName  : "h6"
       cssClass : "pricing-subtitle"
-      partial  : "Or add a new one, whatever works"
+      partial  : "Click on one of your credit cards to use it or add a new one"

     @addSubView @paymentMethodsContainer = new KDCustomHTMLView
       cssClass  : "payment-methods"


payment: close the opening span tag

payment: close the opening span tag

--- client/Main/payment/paymentmethodview.coffee
+++ client/Main/payment/paymentmethodview.coffee
@@ -34,7 +34,7 @@

   getCardInfoPartial: (paymentMethod) ->
-    return "You have no payment methods"  unless paymentMethod
+    return "You have no payment methods"  unless paymentMethod

     { last4 } = paymentMethod