Not depend on favio.js version

Not depend on favio.js version

--- static/scripts/main.coffee
+++ static/scripts/main.coffee
@@ -49,7 +49,7 @@
         angularBootstrap: '../angular-bootstrap/ui-bootstrap-tpls.min'
         ngInfiniteScroll: '../ngInfiniteScroll/ng-infinite-scroll'
         underscoreString: '../underscore.string/dist/underscore.string.min'
-        favico: '../favico.js/favico-0.3.0.min'
+        favico: '../favico.js/favico'
         angularUiJq: '../angular-ui-utils/modules/jq/jq'
         waypoints: '../jquery-waypoints/waypoints.min'
         chai: '../chai/chai'


use zero index for item_number

use zero index for item_number

--- desktop/components/search_bar/view.coffee
+++ desktop/components/search_bar/view.coffee
@@ -169,7 +169,7 @@
   emptyItemClick: ->
     analyticsHooks.trigger 'search:empty-item:click',
       query: @query
-      item_number: 1
+      item_number: 0
       item_type: 'search'
       destination_path: "#{sd.APP_URL}/search?q=#{@query}"
     @selected = true
@@ -179,7 +179,7 @@
     return @emptyItemClick() unless model
     analyticsHooks.trigger 'search:item:click',
       query: @query
-      item_number: model.get('collection').models.findIndex( (result) -> return result.id == model.id ) + 1
+      item_number: model.collection.models.findIndex( (result) -> return result.id == model.id ) + 1
       item_type: model.get('display_model')
       destination_path: "#{sd.APP_URL}/#{model.href()}"
     @selected = true


Remove unused require

Remove unused require

--- lib/rubymotion.coffee
+++ lib/rubymotion.coffee
@@ -1,5 +1,3 @@
-child_process = require 'child_process'
-
 module.exports =
   activate: (state) ->


Catch the error message, too.

Catch the error message, too.

--- lib/stacktrace.coffee
+++ lib/stacktrace.coffee
@@ -3,7 +3,7 @@
 # Internal: A heuristically parsed and interpreted stacktrace.
 class Stacktrace

-  constructor: (@frames = []) ->
+  constructor: (@frames = [], @message = '') ->

   # Internal: Compute the SHA256 checksum of the normalized stacktrace.
   getChecksum: ->
@@ -17,12 +17,14 @@

   @parse: (text) ->
     frames = (parseRubyFrame(rawLine) for rawLine in text.split(/\r?\n/))
-    new Stacktrace(frames)
+    new Stacktrace(frames, frames[0].message)
+

 # Internal: A single stack frame within a {Stacktrace}.
 class Frame

-  constructor: (@rawLine, @path, @lineNumber, @functionName) ->
+  constructor: (@rawLine, @path, @lineNumber, @functionName, @message) ->
+

 # Internal: Parse a Ruby stack frame. This is a simple placeholder until I
 # put together a class hierarchy to handle frame recognition and parsing.
@@ -32,10 +34,10 @@
     ([^:]+) :  # File path
     (\d+) :    # Line number
     in \s* ` ([^']+) ' # Function name
-    (?: : (.*))? # Error message, only on the first
+    (?: : \s (.*))? # Error message, only on the first
   ///

-  new Frame(raw, path, lineNumber, functionName)
+  new Frame(raw, path, lineNumber, functionName, message)

 module.exports =
   Stacktrace: Stacktrace


Clear undo stack of the demo after creation

--- lib/table-edit.coffee
+++ lib/table-edit.coffee
@@ -30,6 +30,8 @@
         if i % 2 is 0 then 'yes' else 'no'
       ]

+    table.clearUndoStack()
+
     tableView = new TableView(table)
     tableView.setRowHeight 30
     tableView.setRowOverdraw 4


add async tests for the last 2 commits

add async tests for the last 2 commits

--- test/bundling-async.coffee
+++ test/bundling-async.coffee
@@ -69,6 +69,25 @@
       eq 2, o
       do done

+  test 'module.parent refers to the parent module', (done) ->
+    fixtures
+      '/a.js': 'exports.a = 1; exports.b = require("./b")'
+      '/b.js': 'module.exports = module.parent.exports.a + 1;'
+    @bundleEval 'a.js', null, (err, o) ->
+      throw err if err
+      eq 1, o.a
+      eq 2, o.b
+      do done
+
+  test 'module.children contains required modules', (done) ->
+    fixtures
+      '/a.js': 'require("./b"); module.exports = module.children[0].exports'
+      '/b.js': 'module.exports = module.filename'
+    @bundleEval 'a.js', null, (err, o) ->
+      throw err if err
+      eq '/b.js', o
+      do done
+
   test 'ignoreMissing option produces null values for missing dependencies', (done) ->
     fixtures '/a.js': 'module.exports = require("./b")'
     async.parallel [


modify: selector coffee :D

modify: selector coffee :D

--- src/common/scripts/coffee/modules/Selector.coffee
+++ src/common/scripts/coffee/modules/Selector.coffee
@@ -78,9 +78,9 @@
         ua = navigator.userAgent
         if ua.indexOf('iPhone') > 0
           if (window.screen.width >= 414 and window.screen.height >= 736) or (window.screen.width >= 736 and window.screen.height >= 414) and window.devicePixelRatio == 3
-            version = 'iphone6_plus iphone6s_plus'
+            version = 'iphone6_7_plus iphone6_7s_plus'
           else if (window.screen.width >= 375 and window.screen.height >= 667) or (window.screen.width >= 667 and window.screen.height >= 375) and window.devicePixelRatio == 2
-            version = 'iphone6 iphone6s'
+            version = 'iphone6_7 iphone6_7s'
           else if (window.screen.width >= 568 and window.screen.height >= 320) or (window.screen.width >= 320 and window.screen.height >= 568) and window.devicePixelRatio == 2
             version = 'iphone5 iphone5s iphone5c'
           else if (window.screen.width >= 480 and window.screen.height >= 320) or (window.screen.width >= 320 and window.screen.height >= 480) and window.devicePixelRatio == 2


fixed members in committee

fixed members in committee

--- test/committeesSpec.coffee
+++ test/committeesSpec.coffee
@@ -27,4 +27,8 @@
         entry.description.should.exist
         entry.members.should.exist

+        for key, value of entry.members
+          key.should.be.a('string')
+          value.should.be.a('string')
+
       done()


Whitespace

Whitespace

--- src/turbolinks/visit.coffee
+++ src/turbolinks/visit.coffee
@@ -12,7 +12,7 @@
     if @state is "initialized"
       @state = "started"
       @adapter.visitStarted(this)
-
+
   cancel: ->
     if @state is "started"
       @request?.cancel()
@@ -94,16 +94,16 @@
     @adapter.visitRequestFinished?(this)

   # Scrolling
-
+
   scrollToTop: ->
     @controller.scrollToPosition(x: 0, y: 0)
-
+
   scrollToAnchor: ->
     if @location.anchor?
       @controller.scrollToAnchor(@location.anchor)
     else
       @scrollToTop()
-
+
   scrollToRestoredPosition: ->
     position = @restorationData?.scrollPosition
     if position?


test: columns labels

test: columns labels

--- test/option.columns.coffee
+++ test/option.columns.coffee
@@ -109,7 +109,7 @@
         ] unless err
         next err

-  describe 'boolean array', ->
+  describe 'array', ->

     it 'enforced by user if array', (next) ->
       parse """
@@ -227,7 +227,7 @@
         ] unless err
         next err

-    it '', (next) ->
+    it 'last column value ignore when `null`', (next) ->
       # Trigger a bug where error is try to stringify and parse an undefined
       # value, conjointly triggered by a null column and a
       # CSV_RECORD_DONT_MATCH_COLUMNS_LENGTH error


Fixing lingering brain fart. Inexcusable.

--- command.coffee
+++ command.coffee
@@ -131,7 +131,7 @@

       CoffeeScript.emit 'success', task
       if o.print          then printLine t.output.trim()
-      else if o.compile || o.maps
+      else if o.compile || o.map
         writeJs base, t.file, t.output, t.sourceMap
       else if o.lint      then lint t.file, t.output
   catch err
@@ -311,7 +311,7 @@
   optionParser  = new optparse.OptionParser SWITCHES, BANNER
   o = opts      = optionParser.parse process.argv[2..]
   o.compile     or=  !!o.output
-  o.run         = not (o.compile or o.print or o.lint or o.maps)
+  o.run         = not (o.compile or o.print or o.lint or o.map)
   o.print       = !!  (o.print or (o.eval or o.stdio and o.compile))
   sources       = o.arguments
   sourceCode[i] = null for source, i in sources


fixing relative path to jsl.conf

fixing relative path to jsl.conf

--- command.coffee
+++ command.coffee
@@ -201,7 +201,7 @@
 # any errors or warnings that arise.
 lint = (file, js) ->
   printIt = (buffer) -> printLine file + ':\t' + buffer.toString().trim()
-  conf = __dirname + '/../extras/jsl.conf'
+  conf = __dirname + '/../../extras/jsl.conf'
   jsl = spawn 'jsl', ['-nologo', '-stdin', '-conf', conf]
   jsl.stdout.on 'data', printIt
   jsl.stderr.on 'data', printIt


test(macModal): Fixed modal e2e test

test(macModal): Fixed modal e2e test

--- test/e2e/modal.spec.coffee
+++ test/e2e/modal.spec.coffee
@@ -18,7 +18,7 @@
     browser.sleep 500

     modal    = element(By.id("test-modal"))
-    closeBtn = element(By.css("#test-modal .close-modal"))
+    closeBtn = element(By.css("#test-modal .mac-close-modal"))

     closeBtn.click()


Fix problem with "no search results" result not showing

The problem was introduced in commit
182256ab50747dfec5a83b4cf29dcfda97e86aea.

Fix: we differentiate between different reasons for calling
navigationlayout.change with the value 'search', with zero
search results.

--- src/views/navigation.coffee
+++ src/views/navigation.coffee
@@ -197,7 +197,10 @@
                         collection: new models.UnitList()

                 when 'search'
-                    if @searchResults.length > 0
+                    if opts?.reason != 'userClickedSearchInputElement' or @searchResults.length > 0
+                        # We want to prevent any open UI element from
+                        # closing just because the user clicked on the
+                        # search bar.
                         view = new SearchResultsSummaryLayout
                             collection: @searchResults
                         if opts?.disableAutoFocus
@@ -307,7 +310,7 @@
             @browse.show new BrowseButtonView()

         _expandSearch: ->
-            @_open 'search', disableAutoFocus: true
+            @_open 'search', disableAutoFocus: true, reason: 'userClickedSearchInputElement'

         _open: (actionType, opts) ->
             @updateClasses actionType


Activity: unseen harmless broken code fixed

--- client/Main/activitycontroller.coffee
+++ client/Main/activitycontroller.coffee
@@ -13,10 +13,9 @@
       groupChannel.close().off()  if groupChannel?
       groupChannel = groupsController.groupChannel
       groupChannel.on 'feed-new', (activities) =>
-        @emit 'ActivitiesArrived',
-          (KD.remote.revive activity for activity in activities)
-          isOnActivityPage = KD.getSingleton("router").getCurrentPath() is "/Activity"
-          ++@newItemsCount  unless isOnActivityPage
+        revivedActivities = (KD.remote.revive activity for activity in activities)
+        isOnActivityPage = KD.getSingleton("router").getCurrentPath() is "/Activity"
+        ++@newItemsCount  unless isOnActivityPage

     @on "ActivityItemBlockUserClicked",         @bound "openBlockUserModal"
     @on "ActivityItemMarkUserAsTrollClicked",   @bound "markUserAsTroll"


Fixed: event's time and description are now in a separate span

--- client/app/views/calendar_view.coffee
+++ client/app/views/calendar_view.coffee
@@ -205,16 +205,28 @@
         @cal.fullCalendar 'unselect'
         @popover = null

-    onEventRender: (event, element) ->
+    onEventRender: (event, $element) ->
+        # TODO: use the new spinner, instead of this.
         if event.isSaving? and event.isSaving
-            spinTarget = $(element).find '.fc-event-time'
+            spinTarget = $element.find '.fc-event-time'
             spinTarget.addClass 'spinning'
             spinTarget.html " "
             spinTarget.spin "tiny"

-        $(element).attr 'title', event.title
-
-        return element
+        # Title and time are joined in the .fc-title for some reason.
+        # Let's split them to put them in the right element.
+        $displayedElement = $element.find '.fc-title'
+        titleAndTime = $displayedElement.html()
+        [time, title...] = titleAndTime.split ' '
+        title = title.join ' '
+
+        # Append the right values to the right elements.
+        $element.find('.fc-time').html time
+        $element.find('.fc-title').html title
+
+        $element.attr 'title', event.title
+
+        return $element

     onEventDragStop: (event, jsEvent, ui, view) ->
         event.isSaving = true


change from 'on' to 'once'

change from 'on' to 'once'

--- plugin/browserify.coffee
+++ plugin/browserify.coffee
@@ -4,7 +4,7 @@
 getString = (bundle, cb) -> # async function reading entire bundle
   string = '' # holds all as a string
   bundle.on 'data', (data) -> string += data
-  bundle.on 'end', -> cb undefined, string  # undefined = error
+  bundle.once 'end', -> cb undefined, string  # undefined = error

 Plugin.registerSourceHandler 'browserify.js', (step) ->
   # TODO: how to know if it's production or dev? change value of debug...


Code review: eliminate double negative

Code review: eliminate double negative

--- src/code/views/node-value-inspector-view.coffee
+++ src/code/views/node-value-inspector-view.coffee
@@ -46,7 +46,7 @@
     SimulationStore.actions.toggledCollectorTo value

   updateNegativeValuesAllowed: (evt) ->
-    value = !!evt.target.checked
+    value = evt.target.checked
     @props.graphStore.changeNode(allowNegativeValues:value)

   updateDefiningType: ->


Correctly pass env to interp during Throw call.

--- interpreter.coffee
+++ interpreter.coffee
@@ -144,7 +144,7 @@
         throw new ReturnException undefined if node.argument is null
         throw new ReturnException interp node.argument, env
       when 'ThrowStatement'
-        throw new JSException (interp node.argument), node
+        throw new JSException (interp node.argument, env), node
       when 'TryStatement'
         try
           interp node.block, env


include noops in test set

include noops in test set

--- tests/scripts/suite.coffee
+++ tests/scripts/suite.coffee
@@ -32,6 +32,9 @@
   run: ->
     _.each([0..(@docLength - 1)], (index) =>
       tests = {
+        "insert nothing":
+          value: ''
+          attributes: {}
         "insert A":
           value: 'A'
           attributes: {}
@@ -66,7 +69,7 @@

   run: ->
     _.each([0..(@docLength - 2)], (index) =>
-      _.each([1..(@docLength-index-1)], (length) =>
+      _.each([0..(@docLength-index-1)], (length) =>
         deleteDelta = Tandem.Delta.makeDeleteDelta(@docLength, index, length)
         expected = @delta.compose(deleteDelta)
         @editorTest.run("Delete #{length} characters at #{index}",
@@ -84,7 +87,7 @@

   run: ->
     _.each([0..(@docLength - 2)], (index) =>
-      _.each([1..(@docLength-index-1)], (length) =>
+      _.each([0..(@docLength-index-1)], (length) =>
         formats =
           color: ['red', null]
           bold: [true, false]


Version bump, add option to control output formats

--- src/bin/config.workshop.template.cson
+++ src/bin/config.workshop.template.cson
@@ -1,4 +1,5 @@
-version: "0.1.7"
+version: "0.1.9"
+
 liquibase:
   working_directory: ""
 scriptella:
@@ -16,20 +17,24 @@
     key: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   basecamp:
     email: ""
     password: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   open_exchange_rates:
     app_id: '='
     currencies: []
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   mailchimp:
     token: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   fogbugz:
     host: "{{subdomain}}.fogbugz.com"
     username: ""
@@ -37,37 +42,44 @@
     filter: "etl"
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   freshbooks:
     api_url: "https://{{subdomain}}.freshbooks.com/api/2.1/xml-in"
     api_token: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   github:
     token: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   google_analytics:
     user: ""
     password: ''
     profile: ''
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   harvest:
     subdomain: ""
     email: ""
     password: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   highrise:
     username: ''
     token: ''
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
   trello:
     key: ""
     token: ""
     datafile_pattern: ""
     data_path: "_workshop/data"
+    output_formats: ["csv","xlsx"]
 databases:
   drivers:
     postgresql:


Expect JSON from --download output

Expect JSON from --download output

--- src/browser/auto-updater-win32.coffee
+++ src/browser/auto-updater-win32.coffee
@@ -9,61 +9,76 @@
 class AutoUpdater
   _.extend @prototype, EventEmitter.prototype

+  constructor: ->
+    @updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe')
+
   setFeedUrl: (@updateUrl) ->
     if @updateUrl
       # Schedule an update when the feed URL is set
       process.nextTick => @checkForUpdates()

   quitAndInstall: ->
-    updateDotExe = @getUpdateExePath()
-
-    unless fs.existsSync(updateDotExe)
+    unless fs.existsSync(@updateDotExe)
       shellAutoUpdater.quitAndInstall()
       return

     args = ['--update', @updateUrl]
-    ChildProcess.execFile updateDotExe, args, (error) ->
+    ChildProcess.execFile @updateDotExe, args, (error) ->
       return if error?

       args = ['--processStart', 'atom.exe']
-      ChildProcess.execFile updateDotExe, args, ->
+      ChildProcess.execFile @updateDotExe, args, ->
         shellAutoUpdater.quitAndInstall()

-  getUpdateExePath: ->
-    path.resolve(path.dirname(process.execPath), '..', 'Update.exe')
+  downloadUpdate: (callback) ->
+    args = ['--download', @updateUrl]
+    ChildProcess.execFile @updateDotExe, args, (error, stdout) ->
+      if error?
+        error.stdout = stdout
+        return callback(error)
+
+      try
+        # Last line of output is the JSON details about the release
+        [json] = stdout.split('\n').reverse()
+        update = JSON.parse(json)?.releasesToApply?.pop?()
+      catch error
+        error.stdout = stdout
+        return callback(error)
+
+      callback(null, update)
+
+  installUpdate: (callback) ->
+    args = ['--update', @updateUrl]
+    ChildProcess.execFile @updateDotExe, args, (error, stdout) =>
+      error?.stdout = stdout
+      callback(error)

   checkForUpdates: ->
     throw new Error('Update URL is not set') unless @updateUrl

     @emit 'checking-for-update'

-    updateDotExe = @getUpdateExePath()
-
-    unless fs.existsSync(updateDotExe)
+    unless fs.existsSync(@updateDotExe)
       @emit 'update-not-available'
       return

-    args = ['--update', @updateUrl]
-    ChildProcess.execFile updateDotExe, args, (error, stdout) =>
+    @downloadUpdate (error, update) =>
       if error?
-        console.log "Failed to update: #{error.code} - #{stdout}"
+        console.log "Failed to download: #{error.message} - #{error.code} - #{error.stdout}"
         @emit 'update-not-available'
         return

-      try
-        # Last line of output is the JSON details about the release
-        [json] = stdout.split('\n').reverse()
-        latestRelease = JSON.parse(json)?.releasesToApply?.pop?()
-      catch error
-        console.log "Update output isn't valid: #{stdout}"
+      unless update?
         @emit 'update-not-available'
         return

-      if latestRelease?
+      @installUpdate (error) =>
+        if error?
+          console.log "Failed to update: #{error.message} - #{error.code} - #{error.stdout}"
+          @emit 'update-not-available'
+          return
+
         @emit 'update-available'
-        @emit 'update-downloaded', {}, latestRelease.releaseNotes, latestRelease.version, new Date(), 'https://atom.io', => @quitAndInstall()
-      else
-        console.log "You're on the latest version!"
-        @emit 'update-not-available'
+        @emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), 'https://atom.io', => @quitAndInstall()

 module.exports = new AutoUpdater()


Update welcome message

Update welcome message

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -22,7 +22,7 @@
     msg = """
 Hola {nick}, soy Robotico...un bot, un amigo, un sirviente

-Te recomiendo que antes de nada revises tu perfil y te pongas un avatar (nos gusta ver la cara a la gente para conocernos cuando coincidamos en algún sarao).
+Te recomiendo que antes de nada revises tu perfil y te pongas un avatar (nos gusta ver la cara a la gente para conocernos cuando coincidamos en algún sarao) y una pequeña bio para poder situarte.

 Luego puedes presentarte en el canal #presentaciones para que sepan que has llegado, quién eres y a qué te dedicas.


SplitView: to avoid false parent size calculations if parent isnt in dom

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -183,11 +183,11 @@
   _getParentSize:->

     if @isVertical()
-      if @parent
+      if @parent and @parent.isInDom()
       then @parent.getWidth()
       else window.innerWidth
     else
-      if @parent
+      if @parent and @parent.isInDom()
       then @parent.getHeight()
       else window.innerHeight


updating reading to use new select reading component

--- src/components/task-plan/reading.cjsx
+++ src/components/task-plan/reading.cjsx
@@ -6,114 +6,66 @@

 {TaskPlanStore, TaskPlanActions} = require '../../flux/task-plan'
 {TocStore, TocActions} = require '../../flux/toc'
+SelectTopics = require './select-topics'
 PlanFooter = require './footer'
+LoadableItem = require '../loadable-item'
 ConfirmLeaveMixin = require '../confirm-leave-mixin'

+ReviewReadings = React.createClass
+  displayName: 'ReviewReadings'
+  propTypes:
+    selected: React.PropTypes.array

-SectionTopic = React.createClass
-  render: ->
-    classes = ['section']
-    classes.push('selected') if @props.active
-    classes = classes.join(' ')
-
-

-      {@props.section.number}
-      {@props.section.title}
-

-
-  toggleSection: ->
-    section = @props.section
-    if (TaskPlanStore.hasTopic(@props.planId, section.id))
-      TaskPlanActions.removeTopic(@props.planId, section.id)
-    else
-      TaskPlanActions.addTopic(@props.planId, section.id)
-
-
-SelectTopics = React.createClass
-  mixins: [BS.OverlayMixin]
   getInitialState: ->
     {courseId} = @props
     TocActions.load(courseId) unless TocStore.isLoaded()
-    { isModalOpen: false }
+    { }

   componentWillMount:   -> TocStore.addChangeListener(@update)
   componentWillUnmount: -> TocStore.removeChangeListener(@update)

   update: -> @setState({})

-  handleToggle: ->
-    @setState({
-      isModalOpen: !@state.isModalOpen
-    })
-
-  doneWithSelection: ->
-    return
+  renderSection: (topicId) ->
+    topic = TocStore.getSectionInfo(topicId)
+

+      {topic?.number}
+      {topic?.title}
+

   render: ->
-    if TocStore.isLoaded()
-      if @props.selected.length
-        selectedReadingList =
-

-
Currently selected sections in this reading

-            {_.map(@props.selected, @renderTopics)}
-

-      else
-        selectedReadingList =
No Readings Selected Yet

-
-

-        Select Readings
-        <BS.Button id="reading-select" onClick={@handleToggle} bsStyle="primary">Edit Readings</BS.Button>
-        {selectedReadingList}
-

-
-    else
-
Loading...

-
-
-  renderTopics: (topicId) ->
-    topic = TocStore.getSectionInfo(topicId)
-

-      {topic.number}
-      {topic.title}
-

-
-  renderSections: (section) ->
-    active = TaskPlanStore.hasTopic(@props.planId, section.id)
-    <SectionTopic active={active} section={section} planId={@props.planId} />
-
-  renderChapterPanels: (chapter, i) ->
-    sections = _.map(chapter.children, @renderSections)
-    header =
-

-        {chapter.number}
-        {chapter.title}
-

-
-    <BS.Accordion>
-      <BS.Panel key={chapter.id} header={header} eventKey={chapter.id}>
-        {sections}
-      </BS.Panel>
-    </BS.Accordion>
-
-  renderOverlay: ->
-    {courseId} = @props
-    unless @state.isModalOpen
-      return
     unless TocStore.isLoaded()
-      TocActions.load(courseId)
+      TocActions.load(@props.courseId)
       return Loading...

-    chapters = _.map(TocStore.get(), @renderChapterPanels)
+    if @props.selected.length
+

+
Currently selected sections in this reading

+        {_.map(@props.selected, @renderSection)}
+

+    else
+
No Readings Selected Yet

-    <BS.Modal backdrop={true} onRequestHide={@doneWithSelection}>
-

-        {chapters}
-

-

-        <BS.Button onClick={@handleToggle}>Close</BS.Button>
-

-    </BS.Modal>
+ChooseReadings = React.createClass

+  render: ->
+    buttonStyle = if @props.selected?.length then 'primary' else 'disabled'
+    header = Select Readings
+
+    primary =
+      <BS.Button
+        className="-show-problems"
+        bsStyle={buttonStyle}
+        onClick={@props.hide}>Add Readings
+      </BS.Button>
+
+    <SelectTopics
+      primary={primary}
+      header={header}
+      courseId={@props.courseId}
+      planId={@props.planId}
+      selected={@props.selected}
+      hide={@props.hide} />

 ReadingPlan = React.createClass

@@ -133,12 +85,23 @@
     value = @refs.title.getDOMNode().value
     TaskPlanActions.updateTitle(id, value)

+  showSectionTopics: ->
+    @setState({
+      showSectionTopics: true
+    })
+
+  hideSectionTopics: ->
+    @setState({
+      showSectionTopics: false
+    })
+
   render: ->
     {id, courseId} = @props
     plan = TaskPlanStore.get(id)

     headerText = if TaskPlanStore.isNew(id) then 'Add Reading' else 'Edit Reading'
     topics = TaskPlanStore.getTopics(id)
+    formClasses = ['create-reading']

     if TaskPlanStore.isNew(id) and @context?.router?.getCurrentQuery().date
       plan.due_at = new Date(@context.router.getCurrentQuery().date)
@@ -150,45 +113,67 @@
     if plan?.due_at
       dueAt = new Date(plan.due_at)

-    footer= <PlanFooter id={id} courseId={courseId} />
+    footer = <PlanFooter id={id} courseId={courseId} />

-    <BS.Panel bsStyle="primary" className="create-reading" footer={footer} header={headerText}>
-

-        Title
-        <input
-          ref="title"
-          id="reading-title"
-          type="text"
-          value={plan.title}
-          placeholder="Enter Title"
-          onChange={@setTitle} />
-

-

-        Open Date
-        <DateTimePicker
-          id="reading-open-date"
-          format="MMM dd, yyyy"
-          time={false}
-          calendar={true}
-          readOnly={false}
-          onChange={@setOpensAt}
-          max={dueAt}
-          value={opensAt}/>
-

-

-        Due Date
-        <DateTimePicker
-          id="reading-due-date"
-          format="MMM dd, yyyy"
-          time={false}
-          calendar={true}
-          readOnly={false}
-          onChange={@setDueAt}
-          min={opensAt}
-          value={dueAt}/>
-

-      <SelectTopics courseId={courseId} planId={id} selected={topics}/>
-    </BS.Panel>
+    if (@state?.showSectionTopics)
+      formClasses.push('hide')
+      selectReadings = <ChooseReadings
+                        hide={@hideSectionTopics}
+                        courseId={courseId}
+                        planId={id}
+                        selected={topics}/>
+
+

+      <BS.Panel bsStyle="primary"
+        className={formClasses.join(' ')}
+        footer={footer}
+        header={headerText}>
+
+

+          Title
+          <input
+            ref="title"
+            id="reading-title"
+            type="text"
+            value={plan.title}
+            placeholder="Enter Title"
+            onChange={@setTitle} />
+

+

+          Open Date
+          <DateTimePicker
+            id="reading-open-date"
+            format="MMM dd, yyyy"
+            time={false}
+            calendar={true}
+            readOnly={false}
+            onChange={@setOpensAt}
+            max={dueAt}
+            value={opensAt}/>
+

+

+          Due Date
+          <DateTimePicker
+            id="reading-due-date"
+            format="MMM dd, yyyy"
+            time={false}
+            calendar={true}
+            readOnly={false}
+            onChange={@setDueAt}
+            min={opensAt}
+            value={dueAt}/>
+

+

+          Select Readings
+          <BS.Button id="reading-select"
+            onClick={@showSectionTopics}
+            bsStyle="primary">Edit Readings
+          </BS.Button>
+          <ReviewReadings courseId={courseId} selected={topics}/>
+

+      </BS.Panel>
+      {selectReadings}
+

 module.exports = {ReadingPlan}


Add test for hitory initialization

Add test for hitory initialization

--- spec/javascripts/calendarApplicationSpec.js.coffee
+++ spec/javascripts/calendarApplicationSpec.js.coffee
@@ -21,9 +21,14 @@
           expect(el).toBeHidden()

   describe 'initialization', () ->
-    spyOn(CalendarApplication, 'initializeHistory')
-    CalendarApplication.initialize({ history: true, basePath: '/calendars' })
-    expect(CalendarApplication.initializeHistory).toHaveBeenCalledWith('/calendars')
+    beforeEach () ->
+      spyOn(CalendarApplication, 'initializeHistory')
+    it 'when called with history: true, initialized history', ->
+      CalendarApplication.initialize({ history: true, basePath: '/calendars' })
+      expect(CalendarApplication.initializeHistory).toHaveBeenCalledWith('/calendars')
+    it 'when called with no arguments, doesnt initialize history', ->
+      CalendarApplication.initialize()
+      expect(CalendarApplication.initializeHistory).not.toHaveBeenCalledWith()

   describe 'integration', () ->
     it 'when activate a calendar date ,all events on the date is activated', () ->


Add more testing for source object

Add more testing for source object

--- spec/javascripts/mixingpanel_source_spec.js.coffee
+++ spec/javascripts/mixingpanel_source_spec.js.coffee
@@ -63,6 +63,8 @@
       mpp = new MixingpanelProperties("kelisto.es", "http://www.google.com")
       mps = new MixingpanelSource(mpp)

+      spyOn(mps, "writeSource")
+      spyOn(mps, "writeLastTouch")
       spyOn mixpanel, "register"

       mps.append()
@@ -76,17 +78,53 @@
       mpp = new MixingpanelProperties("kelisto.es", "http://www.google.com")
       mps = new MixingpanelSource(mpp)

+      spyOn(mps, "writeSource")
+      spyOn(mps, "writeLastTouch")
       spyOn(mixpanel, "register")
       spyOn(mixpanel, "get_property").andReturn("Direct")
+
+      mps.append()
+
+      expect(mixpanel.register).not.toHaveBeenCalled()
+
+    it "should set the last touch source property", ->
+      mpp = new MixingpanelProperties("kelisto.es", "http://www.facebook.com")
+      mps = new MixingpanelSource(mpp)
+
       spyOn(mps, "writeSource")
+      spyOn(mps, "writeFirstTouch")
+      spyOn(mixpanel, "register")

       mps.append()

       prop = {}
-      prop[mps.firstSourceProperty] = "SEO"
-      debugger
-      expect(mixpanel.register).not.toHaveBeenCalled()
+      prop[mps.lastSourceProperty] = "Social"
+      expect(mixpanel.register).toHaveBeenCalledWith(prop)

-    it "should set the last touch source property", ->
     it "should add value to source property", ->
+      mpp = new MixingpanelProperties("kelisto.es", "http://www.facebook.com")
+      mps = new MixingpanelSource(mpp)
+
+      spyOn(mps, "writeFirstTouch")
+      spyOn(mps, "writeLastTouch")
+      spyOn(mixpanel, "register")
+
+      mps.append()
+
+      prop = {}
+      prop[mps.sourceProperty] = ['Social']
+      expect(mixpanel.register).toHaveBeenCalledWith(prop)
+
     it "shouldn't add value to source property if the last value is the same", ->
+      mpp = new MixingpanelProperties("kelisto.es", "http://www.facebook.com")
+      mps = new MixingpanelSource(mpp)
+      array = ['Social']
+
+      spyOn(mps, "writeFirstTouch")
+      spyOn(mps, "writeLastTouch")
+      spyOn(mixpanel, "get_property").andReturn(array)
+      spyOn(array, "push")
+
+      mps.append()
+
+      expect(array.push).not.toHaveBeenCalledWith(['Social','Social'])


Sort by correct date field (first_released_date)

--- compiler/ContentAPI.coffee
+++ compiler/ContentAPI.coffee
@@ -320,7 +320,7 @@
         @filter
             type: ENTRY
             is_released: true
-            _sort: '-published_date'
+            _sort: '-first_released_date'
         , (result) ->
             SDKError.log("Got #{ result.length } entries from API.")
             cb?(result)
@@ -329,7 +329,7 @@
         @filter
             type: PACKAGE
             is_released: true
-            _sort: '-published_date'
+            _sort: '-first_released_date'
         , (result) ->
             SDKError.log("Got #{ result.length } packages from API.")
             cb?(result)


Update view.coffee

Update view.coffee

--- components/artwork_filter/view.coffee
+++ components/artwork_filter/view.coffee
@@ -67,7 +67,7 @@
     @filter.fetchRoot
       success: (model, response, options) =>
         mediator.trigger 'artwork_filter:filter:sync', model
-        @remove() unless (model.get('total').value > 0)
+        @remove() unless model.get('total')
       error: =>
         @remove()
     @fetchArtworks()


DateTime: Small fix to disable calendar button when it's a BC date. See #48655

--- src/elements/DateTime/DateTime.coffee
+++ src/elements/DateTime/DateTime.coffee
@@ -416,7 +416,7 @@
 			if not mom.isValid()
 				return false

-			if mom.bc
+			if mom.bc or value.startsWith("-")
 				@__calendarButton.disable()

 		else


ListView: When de-selecting prior select, pass new row to deselect call.

--- src/elements/ListView/ListView.coffee
+++ src/elements/ListView/ListView.coffee
@@ -471,7 +471,7 @@
 		do_select = =>
 			if row.isSelected()
 				if not no_deselect
-					row.deselect(ev).done(dfr.resolve).fail(dfr.reject)
+					row.deselect(ev, row).done(dfr.resolve).fail(dfr.reject)
 				else
 					dfr.resolve()
 			else
@@ -482,10 +482,10 @@
 			promises = []
 			for _row in @getSelectedRows()
 				if row == _row
+					# we handle this in do_select
 					continue
-				# don't pass an event to deselect, as this
-				# is not the node where the user clicked on
-				ret = _row.deselect()
+
+				ret = _row.deselect(ev, row)
 				if isPromise(ret)
 					promises.push(ret)
 			CUI.when(promises).done(do_select).fail(dfr.reject)


improved class handling for tabs

improved class handling for tabs

--- demo.coffee
+++ demo.coffee
@@ -325,8 +325,8 @@
   )()
 tabClicked = (c) ->
   return if c.className == 'active'
-  s.removeAttribute 'class' for s in sibs
-  c.setAttribute 'class', 'active'
+  s.classList.remove('active') for s in sibs
+  c.classList.add 'active'
   t.style.display = 'none' for t in tabs
   chartType = c.innerHTML
   if chartType == 'options' or chartType == 'about'


working simple event management

working simple event management

Former-commit-id: 03398cabebe5940c19005a91fca5dca0ef6e7ac9 [formerly d53a0b0faccbc0ecfa8bd23d5b5c7e6c71173d41]
Former-commit-id: 3f4f0a817e90c1c2cf4fec5fd154c369edee5c49

--- js/msa/stage/tileStage.coffee
+++ js/msa/stage/tileStage.coffee
@@ -57,7 +57,7 @@
       @draglock = undefined

     _onMouseDown: (e) ->
-      [mouseX,mouseY]= getMouseCoords e
+      [mouseX,mouseY]= @getMouseCoords e

       unless @checkForEvents mouseX,mouseY
         @pauseEvent e
@@ -95,7 +95,7 @@
       #  @rect = @canvas.getBoundingClientRect()
       @draglock = undefined

-      [mouseX,mouseY]= getMouseCoords e
+      [mouseX,mouseY]= @getMouseCoords e

       @moveCenter mouseX,mouseY
       @zoomCanvas @dblClickVx,@dblClickVy
@@ -150,9 +150,10 @@
     checkForEvents: (mouseX, mouseY) ->
       for name,arr of @events

-        if (arr[0] <= mouseX and mouseX <= ar[2]) and
+        if (arr[0] <= mouseX and mouseX <= arr[2]) and
         (arr[1] <= mouseY and mouseY <= arr[3])
           arr[4]()
+      false

     checkPos: ->
       [@viewportX,@viewportY] = @_checkPos @viewportX,@viewportY
@@ -288,9 +289,12 @@

       # zoom control
       @ctx.fillStyle = "red"
+      @ctx.globalAlpha = 0.5;
       @ctx.fillRect @canvas.width - 40, @canvas.height - 35,15,15
       callback = -> alert "hi"
-      @events.zoomIn = [@canvas.width - 40, @canvas.height - 35,15,15,callback]
+      @events.zoomIn = [@canvas.width - 40, @canvas.height - 35,@canvas.width -
+      25,@canvas.height - 20,callback]
+      @ctx.globalAlpha = 1

       @ctx.fillStyle = "blue"
       @ctx.fillRect @canvas.width - 40, @canvas.height - 20,15,15


Added min and max operations to the glsl api

--- src/generator/compile.glsl.api.coffee
+++ src/generator/compile.glsl.api.coffee
@@ -112,3 +112,118 @@
     # else if Array.isArray a
     # TODO: handle uniforms
     # else if typeof a == 'object'
+
+  min: (a,b) ->
+    if typeof a == typeof b == 'number'
+      Math.min a, b
+    else if typeof a == typeof b == 'string'
+      "min(#{a}, #{b})"
+    else if typeof a == 'string'
+      "min(#{a}, #{glsl.literal b})"
+    else if typeof b == 'string'
+      "min(#{glsl.literal a}, #{b})"
+    else if Array.isArray a and Array.isArray b
+      if a.length != b.length
+        throw "Cannot perform min operation with array operands of different lengths"
+      numbersOnly = true
+      for i in [0...a.length]
+        if typeof a[i] != 'number' or typeof b[i] != 'number'
+          numbersOnly = false
+          break
+      if numbersOnly
+        (Math.min a[i], b[i]) for i in [0...a.length]
+      else
+        "min(#{glsl.vec3Lit a}, #{glsl.vec3Lit b})"
+    else
+      throw "Operands passed to the min operation have incorrect types."
+
+  max: (a,b) ->
+    if typeof a == typeof b == 'number'
+      Math.max a, b
+    else if typeof a == typeof b == 'string'
+      "max(#{a}, #{b})"
+    else if typeof a == 'string'
+      "max(#{a}, #{glsl.literal b})"
+    else if typeof b == 'string'
+      "max(#{glsl.literal a}, #{b})"
+    else if Array.isArray a and Array.isArray b
+      if a.length != b.length
+        throw "Cannot perform operation with arrays of different lengths"
+      numbersOnly = true
+      for i in [0...a.length]
+        if typeof a[i] != 'number' or typeof b[i] != 'number'
+          numbersOnly = false
+          break
+      if numbersOnly
+        (Math.max a[i], b[i]) for i in [0...a.length]
+      else
+        "max(#{glsl.vec3Lit a}, #{glsl.vec3Lit b})"
+    else
+      throw "Operands passed to the max operation have incorrect types."
+
+  mini: (a,b) ->
+    if typeof a == typeof b == 'number'
+      Math.min a, b
+    else if typeof a == typeof b == 'string'
+      "min(#{a}, #{b})"
+    else if typeof a == 'string' or typeof b == 'string'
+      "max(#{a}, #{b})"
+    else if Array.isArray a and Array.isArray b
+      if a.length != b.length
+        throw "Cannot perform operation with arrays of different lengths"
+      numbersOnly = true
+      for i in [0...a.length]
+        if typeof a[i] != 'number' or typeof b[i] != 'number'
+          numbersOnly = false
+          break
+      if numbersOnly
+        (Math.max a[i], b[i]) for i in [0...a.length]
+      else
+        "max(vec3(#{a}), vec3(#{b}))"
+    else
+      throw "Operands passed to the max operation have incorrect types."
+
+  maxi: (a,b) ->
+    if typeof a == typeof b == 'number'
+      Math.max a, b
+    else if typeof a == typeof b == 'string'
+      "max(#{a}, #{b})"
+    else if typeof a == 'string' or typeof b == 'string'
+      "max(#{a}, #{b})"
+    else if Array.isArray a and Array.isArray b
+      if a.length != b.length
+        throw "Cannot perform operation with arrays of different lengths"
+      numbersOnly = true
+      for i in [0...a.length]
+        if typeof a[i] != 'number' or typeof b[i] != 'number'
+          numbersOnly = false
+          break
+      if numbersOnly
+        (Math.max a[i], b[i]) for i in [0...a.length]
+      else
+        "max(vec3(#{a}), vec3(#{b}))"
+    else
+      throw "Operands passed to the max operation have incorrect types."
+
+  literal: (a) ->
+    if typeof a == 'string'
+      a
+    else if typeof a == 'number'
+      glsl.floatLit a
+    else if Array.isArray a
+      glsl.vec3Lit a
+
+  floatLit: (a) ->
+    if typeof a == 'string'
+      a
+    else
+      if (a | 0) == a then a + '.0' else a
+
+  vec3Lit: (a) ->
+    if typeof a == 'string'
+      a
+    else if typeof a == 'number'
+      "vec3(#{glsl.floatLit a})"
+    else
+      "vec3(#{glsl.floatLit a[0]},#{glsl.floatLit a[1]},#{glsl.floatLit a[2]})"
+


updated js

updated js

--- src/javascript/components/view.coffee
+++ src/javascript/components/view.coffee
@@ -13,7 +13,7 @@

 module.exports = Backbone.View.extend

-  template: require './template.hbs'
+  template: require './template'

   initialize: ->
     @render()
@@ -23,7 +23,6 @@
     @animate()

   threeInit: ->
-    alert 'TEST'
     @camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 )
     @camera.position.z = 1000
     @scene = new THREE.Scene()


Tell other clients when comments are resolved and reopened

--- services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
+++ services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
@@ -43,6 +43,12 @@
 				$scope.$broadcast "change:accept", change_id
 			updateEntries(doc_id)
 			$scope.$apply () ->
+
+		ide.socket.on "resolve-thread", (thread_id, user) ->
+			_onCommentResolved(thread_id, user)
+
+		ide.socket.on "reopen-thread", (thread_id) ->
+			_onCommentReopened(thread_id)

 		rangesTrackers = {}

@@ -265,20 +271,26 @@
 		$scope.resolveComment = (entry, entry_id) ->
 			entry.showWhenResolved = false
 			entry.focused = false
-			thread = $scope.reviewPanel.commentThreads[entry.thread_id]
+			$http.post "/project/#{$scope.project_id}/thread/#{entry.thread_id}/resolve", {_csrf: window.csrfToken}
+			_onCommentResolved(entry.thread_id, ide.$scope.user)
+
+		$scope.unresolveComment = (entry, entry_id) ->
+			_onCommentReopened(entry.thread_id)
+			$http.post "/project/#{$scope.project_id}/thread/#{entry.thread_id}/reopen", {_csrf: window.csrfToken}
+
+		_onCommentResolved = (thread_id, user) ->
+			thread = $scope.reviewPanel.commentThreads[thread_id]
 			thread.resolved = true
-			thread.resolved_by_user = formatUser(ide.$scope.user)
+			thread.resolved_by_user = formatUser(user)
 			thread.resolved_at = new Date()
-			$http.post "/project/#{$scope.project_id}/thread/#{entry.thread_id}/resolve", {_csrf: window.csrfToken}
-			$scope.$broadcast "comment:resolve_thread", entry.thread_id
-
-		$scope.unresolveComment = (entry, entry_id) ->
-			thread = $scope.reviewPanel.commentThreads[entry.thread_id]
+			$scope.$broadcast "comment:resolve_thread", thread_id
+
+		_onCommentReopened = (thread_id) ->
+			thread = $scope.reviewPanel.commentThreads[thread_id]
 			delete thread.resolved
 			delete thread.resolved_by_user
 			delete thread.resolved_at
-			$http.post "/project/#{$scope.project_id}/thread/#{entry.thread_id}/reopen", {_csrf: window.csrfToken}
-			$scope.$broadcast "comment:unresolve_thread", entry.thread_id
+			$scope.$broadcast "comment:unresolve_thread", thread_id

 		$scope.deleteComment = (entry_id) ->
 			$scope.$broadcast "comment:remove", entry_id


Basic sanity check

Basic sanity check

--- metal/launcher.coffee
+++ metal/launcher.coffee
@@ -5,8 +5,12 @@

 sh = (cmd) ->
   console.log "sh #{cmd}"
+
   # pretty weak but oh well.
   forbidden = [";", "&&"]
+  for bidden in forbidden
+    if cmd.indexOf(bidden) >= 0
+      throw new Error "Command-line contains forbidden characters: #{cmd}"

   exe = childProcess.exec(cmd)
   exe.on 'exit', (code) ->


Add night and truck speed limit signs.

Add night and truck speed limit signs.

--- dev/us.cson
+++ dev/us.cson
@@ -733,5 +733,26 @@
     { type: 'square-rounded', color: 'white', transform: 'scale(.7,1) scale(.90)' }
     { type: 'content-4', color: 'black',  content: 'SPEED', transform: 'translate(0,-145%) scale(.8)' }
     { type: 'content-4', color: 'black',  content: 'LIMIT', transform: 'translate(0,-60%) scale(.8)' }
-    { type: 'us_speed_value', color: 'black',  transform: 'translate(0,50%)' }
-  ]
+    { type: 'us_speed_value', color: 'black',  transform: 'translate(0,45%)' }
+  ]
+
+regulatory_night_speed_limit:
+  category: 'regulatory'
+  name: 'night_speed_limit'
+  elements: [
+    { type: 'square-rounded', color: 'white' }
+    { type: 'square-rounded', color: 'black', transform: 'scale(.95)' }
+    { type: 'content-4', color: 'white',  content: 'NIGHT', transform: 'translate(0,-100%) scale(.8)' }
+    { type: 'us_speed_value', color: 'white',  transform: 'translate(0,35%) scale(1.1)' }
+  ]
+
+regulatory_truck_speed_limit:
+  category: 'regulatory'
+  name: 'truck_speed_limit'
+  elements: [
+    { type: 'square-rounded', color: 'white' }
+    { type: 'square-rounded', color: 'black', transform: 'scale(.95)' }
+    { type: 'square-rounded', color: 'white', transform: 'scale(.90)' }
+    { type: 'content-4', color: 'black',  content: 'TRUCKS', transform: 'translate(0,-100%) scale(.7,.9)' }
+    { type: 'us_speed_value', color: 'black',  transform: 'translate(0,35%) scale(1.1)' }
+  ]


docs(browser support page): fix misspelling

--- src/client/scripts/constants/browsers-const.coffee
+++ src/client/scripts/constants/browsers-const.coffee
@@ -81,7 +81,7 @@
 	notes: '
 		Firefox fully supported starting at
 		version 63.

-		For versions les than 63 do the following:
+		For versions less than 63 do the following:

 				Open its configuration editor.


disallowing --watch and --join together for the time being.

--- command.coffee
+++ command.coffee
@@ -89,7 +89,7 @@
                 compileJoin() if helpers.compact(contents).length is sources.length
               else
                 compileScript(source, code.toString(), base)
-            watch source, base if opts.watch
+            watch source, base if opts.watch and not opts.join
     compile source, true

 # Compile a single source script, containing the given code, according to the


Adds new line in between sections

Adds new line in between sections

--- src/lib/formatter.coffee
+++ src/lib/formatter.coffee
@@ -2,4 +2,4 @@
 wrap = require './wrapper'

 module.exports = (htmlContent) ->
-  split(htmlContent).map((section) -> wrap(section)).join ''
+  split(htmlContent).map((section) -> wrap(section)).join '\n'


:lipstick:

:lipstick:

--- src/browser/auto-update-manager.coffee
+++ src/browser/auto-update-manager.coffee
@@ -3,19 +3,19 @@
 _ = require 'underscore-plus'
 {EventEmitter} = require 'events'

-IDLE_STATE='idle'
-CHECKING_STATE='checking'
-DOWNLOADING_STATE='downloading'
-UPDATE_AVAILABLE_STATE='update-available'
-NO_UPDATE_AVAILABLE_STATE='no-update-available'
-ERROR_STATE='error'
+IdleState = 'idle'
+CheckingState = 'checking'
+DownladingState = 'downloading'
+UpdateAvailableState = 'update-available'
+NoUpdateAvailableState = 'no-update-available'
+ErrorState = 'error'

 module.exports =
 class AutoUpdateManager
   _.extend @prototype, EventEmitter.prototype

   constructor: (@version) ->
-    @state = IDLE_STATE
+    @state = IdleState
     @feedUrl = "https://atom.io/api/updates?version=#{@version}"

     if process.platform is 'win32'
@@ -24,20 +24,20 @@
     autoUpdater.setFeedUrl @feedUrl

     autoUpdater.on 'checking-for-update', =>
-      @setState(CHECKING_STATE)
+      @setState(CheckingState)

     autoUpdater.on 'update-not-available', =>
-      @setState(NO_UPDATE_AVAILABLE_STATE)
+      @setState(NoUpdateAvailableState)

     autoUpdater.on 'update-available', =>
-      @setState(DOWNLOADING_STATE)
+      @setState(DownladingState)

     autoUpdater.on 'error', (event, message) =>
-      @setState(ERROR_STATE)
+      @setState(ErrorState)
       console.error "Error Downloading Update: #{message}"

     autoUpdater.on 'update-downloaded', (event, @releaseNotes, @releaseVersion) =>
-      @setState(UPDATE_AVAILABLE_STATE)
+      @setState(UpdateAvailableState)
       @emitUpdateAvailableEvent(@getWindows()...)

     # Only released versions should check for updates.


Add doc for method.

Add doc for method.

--- src/coffee/producttypegenerator.coffee
+++ src/coffee/producttypegenerator.coffee
@@ -102,6 +102,13 @@
     (languages(h) for h in headers when h.match(regexp))

+  ###
+  Returns a list of languages (for i18n) used for given attribute property header.
+  @param {array} row The row array containing row values.
+  @param {string}  header The attribute property header
+  @param {array}  languages The languages used for i18n.
+  @return List with language values
+  ###
   _i18n: (row, header, languages) ->
     i18n = {}
     for language in languages


Remove debug statement

Remove debug statement

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -44,7 +44,6 @@
           vis.subscribe 'markselect', ({frame, indices}) ->
             subframe = window.plot.createFrame frame.label, frame.vectors, indices

-            debug subframe
             renderTable = (g) ->
               g(
                 if indices.length > 1 then g.select() else g.select head indices


JS: Escape user-controlled text in Handlebars helpers

--- evesrp/static/js/handlebars-helpers.coffee
+++ evesrp/static/js/handlebars-helpers.coffee
@@ -80,17 +80,19 @@
 gettext = (msgid, options) ->
     translated = ui.i18n.gettext msgid
     if _.isEmpty options.hash
-        return new Handlebars.SafeString translated
+        escaped = Handlebars.escapeExpression translated
+        return new Handlebars.SafeString escaped
     else
-        args = _.mapObject options.hash, Handlebars.Utils.escapeExpression
+        args = _.mapObject options.hash, Handlebars.escapeExpression
         return new Handlebars.SafeString (sprintf translated, args)

 transformed = (request, attr) ->
     if attr of request.transformed
+        safeText = Handlebars.escapeExpression request[attr]
         return new Handlebars.SafeString \
         "#{ request[attr] }
+            target=\"_blank\">#{ safeText }
          "
     request[attr]

@@ -109,11 +111,13 @@
             localized = ui.i18n.gettext "%(amount)s bonus"
         else
             localized = ui.i18n.gettext "%(amount)s penalty"
-    return new Handlebars.SafeString (sprintf localized, {amount: amount})
+    escaped = Handlebars.escapeExpression (sprintf localized, {amount: amount})
+    return new Handlebars.SafeString escaped

 urlize = (str, options) ->
     limit =  options.hash?.limit
-    return util.urlize str, limit
+    escaped = Handlebars.escapeExpression str
+    return new Handlebars.SafeString (util.urlize escaped, limit)

 registerHelpers = (handlebars) ->


redirect to home if you try to register and there are no spots left (there's a nice big message on the home page that says the knockout is full)

--- nodeko.coffee
+++ nodeko.coffee
@@ -35,8 +35,12 @@

 # new team
 get '/teams/new', ->
-  @team: new Team {}, =>
-    @render 'teams/new.html.haml'
+  Team.all (error, teams) =>
+    if teams.length >= 200
+      @redirect '/'
+    else
+      @team: new Team {}, =>
+        @render 'teams/new.html.haml'

 # create team
 post '/teams', ->


Possible fix for Python2 failing

Possible fix for Python2 failing

--- build/tasks/shell.coffee
+++ build/tasks/shell.coffee
@@ -6,9 +6,9 @@

   env = process.env

-  chrome = ''
-  python2 = ''
-  s3 = ''
+  chrome = 'echo Skipping Chrome'
+  python2 = 'echo Skipping Python2'
+  s3 = 'echo Skipping S3'

   # https://code.google.com/p/selenium/wiki/ChromeDriver#Requirements
   if process.platform is 'linux'
@@ -23,16 +23,11 @@

   if process.platform is 'linux'
     python2 = 'cd build/tools ; grep -Rl python . | xargs sed -ri "s/([^!]|^)python(\\s|$)/\\1python2\\2/g"'
-  else if process.platform is 'darwin'
-    python2 = ''
-  else

   # Only run the s3 task if Travis is building master and not in a pull
   # request.
   if env.TRAVIS_PULL_REQUEST is 'false' and env.TRAVIS_BRANCH is 'master'
     s3 = 'grunt s3-sync'
-  else
-    s3 = ''

   @config 'shell',
     'chrome-extension':


Cherry-picked commit 824238dcf091d89b6e7b1b4bef574b3f7d4ccdd6 into develop.

--- core/app/backbone/models/wheel.coffee
+++ core/app/backbone/models/wheel.coffee
@@ -40,7 +40,8 @@
   opinionTypesArray: -> _.values @get('opinion_types')

   reset: ->
-    @clear()
+    # DO NOT RUN CLEAR HERE, since then the objects 'believe', 'doubt', and 'disbelieve' are lost,
+    # which are required by the BaseFactWheelView!
     @setRecursive(new Wheel().attributes)

   userOpinion: ->


'-r' should work with $NODE_PATH as well (where previously it was an absolute/relative path)

--- command.coffee
+++ command.coffee
@@ -96,7 +96,7 @@
   o = opts
   options = compileOptions file
   if o.require
-    require fs.realpathSync file for file in o.require
+    require(if helpers.starts(file, '.') then fs.realpathSync(file) else file) for file in o.require
   try
     t = task = {file, input, options}
     CoffeeScript.emit 'compile', task


only disable hover events during selection when showOn is 'hover'

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -133,15 +133,14 @@
       if value.match 'hover'
         mouseEventDisposables.add 'atom-workspace', 'mouseenter', '.tree-view-resizer.autohide-hover', => @show()
         mouseEventDisposables.add 'atom-workspace', 'mouseleave', '.tree-view-resizer.autohide-hover', => @hide()
+        # disable the tree view from showing/hiding during a selection
+        mouseEventDisposables.add 'atom-text-editor', 'mousedown', => @disableHoverEvents()
+        mouseEventDisposables.add 'atom-text-editor', 'mouseup', => @enableHoverEvents()
       if value.match 'click'
         mouseEventDisposables.add 'atom-workspace', 'click', '.tree-view-resizer', (event) => @toggle value is 'click'
         mouseEventDisposables.add '.tree-view', 'blur', =>
           @clearFocusedElement()
           @hide 0
-
-    # disable the tree view from showing during a selection
-    @disposables.add 'atom-text-editor', 'mousedown', => @disableHoverEvents()
-    @disposables.add 'atom-text-editor', 'mouseup', => @enableHoverEvents()

     # add listener for core commands that should cause the tree view to hide
     @disposables.add atom.commands.add '.tree-view-resizer.autohide',


unset position instead of setting it to relative

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -177,7 +177,7 @@
   update: (pushEditor = getConfig('pushEditor'))->
     Promise.resolve().then =>
       if pushEditor
-        @treeViewEl.style.position = 'relative'
+        @treeViewEl.style.position = ''
         atom.views.getView(@treeView.panel)?.style.width = ''
       else
         @treeViewEl.style.position = 'absolute'


added coffee --lint to the self-compiler's resume

--- command_line.coffee
+++ command_line.coffee
@@ -63,11 +63,14 @@
   return unless source: @sources.shift()
   opts: @options
   posix.cat(source).addCallback (code) ->
-    if      opts.tokens then puts coffee.tokenize(code).join(' ')
-    else if opts.tree   then puts coffee.tree(code).toString()
-    else if opts.run    then eval coffee.compile code
-    else if opts.print  then puts coffee.compile code
-    else                     exports.write_js source, coffee.compile code
+    if      opts.tokens   then puts coffee.tokenize(code).join(' ')
+    else if opts.tree     then puts coffee.tree(code).toString()
+    else
+      js: coffee.compile code
+      if      opts.run    then eval js
+      else if opts.print  then puts js
+      else if opts.lint   then exports.lint js
+      else                     exports.write_js source, coffee.compile code
     exports.compile_scripts()

 # Write out a JavaScript source file with the compiled code.
@@ -77,6 +80,16 @@
   js_path:  path.join dir, filename
   posix.open(js_path, process.O_CREAT | process.O_WRONLY | process.O_TRUNC, 0755).addCallback (fd) ->
     posix.write(fd, js)
+
+# Pipe compiled JS through JSLint (requires a working 'jsl' command).
+exports.lint: (js) ->
+  jsl: process.createChildProcess('jsl', ['-nologo', '-stdin'])
+  jsl.addListener 'output', (result) ->
+    puts result.replace(/\n/g, '') if result
+  jsl.addListener 'errror', (result) ->
+    puts result if result
+  jsl.write js
+  jsl.close()

 # Use OptionParser for all the options.
 exports.parse_options: ->


bug fix in BucketModel#serialize

bug fix in BucketModel#serialize

--- src/node_modules/bucket-model/bucket-model.coffee
+++ src/node_modules/bucket-model/bucket-model.coffee
@@ -79,7 +79,7 @@
         name: @name
         description: @description
         userId: @user.id
-        targetCents: @target.times(100).fixed(0)
+        targetCents: @target.times(100).toFixed(0)
         roundId: @roundId
       }


Rename variable

Rename variable

--- client/explore.coffee
+++ client/explore.coffee
@@ -82,13 +82,13 @@
     logger.debug("Installed change observer")
   )

-  $projectElems = R.map(createProjectElement, projects)
+  projectElems = R.map(createProjectElement, projects)
   logger.debug("Configuring Isotope")
   getIsotopeContainer().isotope({
     itemSelector: ".project-item",
     layoutMode: 'fitRows',
   })
-    .isotope("insert", $projectElems)
+    .isotope("insert", projectElems)
 )
 Template.explore.onDestroyed(->
   projElems = document.querySelectorAll(".project-item")


remove cm requires

remove cm requires

--- client/message.coffee
+++ client/message.coffee
@@ -406,8 +406,8 @@
         if kb == 'normal'
           t.editor.setOption 'keyMap', ''
         else
-          require 'codemirror/keymap/vim'
-          require 'codemirror/keymap/emacs'
+          #require 'codemirror/keymap/vim'
+          #require 'codemirror/keymap/emacs'
           t.editor.setOption 'keyMap', kb
       when 'ace'
         t.editor.setKeyboardHandler if kb == 'normal' then '' else 'ace/keyboard/' + kb


Fix usage of S.startsWith

Fix usage of S.startsWith

--- client/routing.coffee
+++ client/routing.coffee
@@ -46,15 +46,15 @@
 )

 Router.onBeforeAction(->
-  if S.startsWith(@url, "/create") and !Meteor.userId()?
+  if S.startsWith("/create", @url) and !Meteor.userId()?
     logger.debug('User not logged in, rendering login page')
     @render('login')
   else
-    if S.startsWith(@url, '/account')
+    if S.startsWith('/account', @url)
       curSection = "account"
-    else if S.startsWith(@url, "/create")
+    else if S.startsWith("/create", @url)
       curSection = "create"
-    else if S.startsWith(@url, "/about")
+    else if S.startsWith("/about", @url)
       curSection = "about"
     else
       curSection = "explore"


ContentEditable: added blur support

ContentEditable: added blur support

--- src/components/inputs/contenteditableview.coffee
+++ src/components/inputs/contenteditableview.coffee
@@ -18,6 +18,11 @@
     # we shouldn't do per instance
     # quick hack unf - SY
     document.addEventListener 'focus', (event)=>
+      @focus()  if event.target is @editableElement
+    , yes
+
+    document.addEventListener 'blur', (event)=>
+      @blur()  if event.target is @editableElement
     , yes

     @validationNotifications = {}


Missed update

Missed update

--- resources/assets/coffee/react/profile-page/stats.coffee
+++ resources/assets/coffee/react/profile-page/stats.coffee
@@ -48,5 +48,5 @@
     val = @props.stats[key]

     switch key
-      when 'hit_accuracy' then "#{val.toFixed(2)}%"
+      when 'hit_accuracy' then "#{osu.formatNumber(val, 2)}%"
       else osu.formatNumber(val)


Improved scaffold further

Improved scaffold further

--- cake/lib/scaffold.coffee
+++ cake/lib/scaffold.coffee
@@ -12,21 +12,21 @@
     scaffold = new this
     scaffold.destroy arguments...

-  generate: (name = '', callback = ->) ->
+  generate: (name, callback = ->) ->
     if name
-      scaffolt @className(), name, callback
+      scaffolt @className(), name, {}, callback
       return

     @prompt (name) =>
-      scaffolt @className(), name, callback
+      scaffolt @className(), name, {}, process.exit

-  destroy: (name = '', callback = ->) ->
+  destroy: (name, callback = ->) ->
     if name
-      scaffolt @className(), name, callback
+      scaffolt @className(), name, {revert: yes}, callback
       return

     @prompt (name) =>
-      scaffolt @className(), name, {revert: yes}, callback
+      scaffolt @className(), name, {revert: yes}, process.exit

   className: ->
     _s.dasherize(@constructor.name).replace(/^-/, '')
@@ -39,7 +39,7 @@
         @prompt callback

   promptString: ->
-    name = _s.humanize(@constructor.name).toLowerCase()
+    name = _s.humanize(@className()).toLowerCase()
     "\nEnter name for #{name}: "

   validate: (name) ->


logging removed

logging removed

--- client/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
+++ client/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
@@ -276,10 +276,8 @@
       {activity} = data
       delete data.activity
       activity.modify data, (err, tutorial)=>
-        log data,activity
         if data.appendToList?
           KD.remote.api.JTutorialList.fetchForTutorialId data.appendToList._id,(existingList)=>
-
               unless existingList
                 KD.remote.api.JTutorialList.create
                   title : "New List"
@@ -296,7 +294,6 @@
                 existingList.addItemById activity._id, (err,tutlist)=>
                   if err then log err
                   else callback? err, tutorial, tutlist
-
         callback? err, tutorial
         unless err
           new KDNotificationView type : "mini", title : "Updated the tutorial successfully"


Update language select class name

Update language select class name

--- src/scripts/modules/media/footer/metadata/metadata.coffee
+++ src/scripts/modules/media/footer/metadata/metadata.coffee
@@ -13,7 +13,7 @@
       return model

     editable:
-      '.js-metadata-language-select':
+      '.metadata-language-select':
         value: () -> if @media is 'book' then return 'language' else return 'currentPage.language'
         type: 'select2'
         select2:


fix: cite_rx => cite_rx_rev

fix: cite_rx => cite_rx_rev

--- lib/completion-manager.coffee
+++ lib/completion-manager.coffee
@@ -49,7 +49,7 @@
       @refComplete(te)
       return true
     else if (keybinding or atom.config.get("latextools.citeAutoTrigger")) and
-        m = cite_rx.exec(line)
+        m = cite_rx_rev.exec(line)
       console.log("found match")
       console.log(m)
       @citeComplete(te)


Ace: add listeners for reconnect

Ace: add listeners for reconnect

--- client/Ace/ace.coffee
+++ client/Ace/ace.coffee
@@ -145,10 +145,12 @@
     else
       # add to list of files that need to be synced.
       syncLocalController.addToWillSaved file.path
-      syncLocalController.once "LocalContentSynced", (fileName) =>
-        log "synced File ", fileName
-        @notify "file synced to remote...", null, null, 5000
-
+      @prepareSyncListeners()
+
+  prepareSyncListeners: ->
+    {syncLocalController} = KD.singletons
+    syncLocalController.on "LocalContentSynced", (fileName) =>
+      @notify "file synced to remote...", null, null, 5000

   requestSaveAs: (options) ->
     contents = @getContents()


simplify the server code more

simplify the server code more

--- node.js/server.coffee
+++ node.js/server.coffee
@@ -29,22 +29,16 @@
     console.log req.method, req.url

     file = req.url[1..]
+
     if file == ''
       console.log 'getting / == welcome-visitors.html topic'
       file = 'welcome-visitors.html'
+
     #TODO: security, don't allow non-topic id chars - avoid ../ etc
-    file = file.replace(/[\/\\]/g, '')    #take unescaped unix and windows slashes in a request, and throw them away
-
-    #remove ?random= - or other urlparams
-    file = file.replace(/\?.*$/i, '')
-
+    file = file.replace(/\.\.[\/\\]/g, '')    #remove all instances of ../ and ..\ (talk about brutish and a tad simplistic
+    file = file.replace(/\?.*$/i, '')     #remove ?random= - or other urlparams

-    filePath = file
-    contentType =  'application/json'
-    encoding = 'ascii'
-
     file_extension = file.match(/\.([^\.]*)$/i)
-    console.log file_extension
     if !file_extension[0]
       file_extension = 'json'
     else
@@ -62,7 +56,7 @@
     #and here's a pointless exception to the filename.extension :(
     filePath = filePath.replace(/\.json/, '') if file_extension == 'json'

-    console.log 'reading: '+filePath
+    console.log req.method+filePath

     fs.readFile filePath, encoding, (err, data) ->
       status = 200


Add pako and encoding.

Add pako and encoding.

--- test/conf/karma-conf.coffee
+++ test/conf/karma-conf.coffee
@@ -21,6 +21,8 @@
       # The test libraries.
       {pattern: 'node_modules/angular-mocks/angular-mocks.js', included: false}
       {pattern: 'node_modules/chai-as-promised/lib/chai-as-promised.js', included: false}
+      {pattern: 'node_modules/pako/dist/pako.js', included: false}
+      {pattern: 'node_modules/text-encoding/**/*.js', included: false}
       # The test specs.
       {pattern: 'test/unit/*Spec.coffee', included: false}
       # The chai eventually assertion helper.


removed freeze tests, added partitiondtable tests

--- test/data/table-test.coffee
+++ test/data/table-test.coffee
@@ -19,43 +19,6 @@
   Table.fromArray rows, null, type

 checks = (nrows) ->
-  "frozen":
-    topic: (t) -> t.freeze()
-
-    "is frozen": (t) ->
-      assert t.isFrozen()
-      assert t.isFrozen()
-
-    "can be reshaped": (t) ->
-      assert.doesNotThrow () -> t.limit('a').all()
-      assert.doesNotThrow () -> t.partition('a').all()
-
-    "can be joined": (t) ->
-      assert.doesNotThrow () -> t.join(t, "a").all()
-
-
-    "when partitioned":
-      topic: (t) -> t.partition 'a'
-
-      "each partition is frozen": (t) ->
-        partitions = t.all('table')
-        for p in partitions
-          assert p.isFrozen()
-
-
-    "cannot be modified": (t) ->
-      assert.throws () -> t.project('a')
-      assert.throws () -> t.partition('a').aggregate(data.ops.Aggregate.count())
-
-    "when melted":
-      topic: (t) -> t.melt()
-
-      "is not frozen": (t) ->
-        assert.isFalse t.isFrozen()
-
-      "can be modified": (t) ->
-        assert.doesNotThrow () -> t.project('a')
-

   "cached":
     topic: (t) ->
@@ -361,21 +324,6 @@
         assert.equal row.get('z'), (x*100), "z is wrong #{row.get 'z'} != #{x*100}"
         assert.equal row.get('n'), -x, "n is wrong #{row.get 'n'} != #{-x}"
         assert.equal row.get('m'), (-x-1000), "m is wrong #{row.get 'm'} != #{-x-1000}"
-
-    "when frozen":
-      topic: (t) ->
-        t.freeze()
-
-      "values correct": (t) ->
-        t.each (row, idx) ->
-          x = row.get 'x'
-          assert.equal x, idx
-          assert.equal row.get('a'), null
-          assert.equal row.get('y'), (x+100), "y is wrong #{row.get 'y'} != #{x+100}"
-          assert.equal row.get('z'), (x*100), "z is wrong #{row.get 'z'} != #{x*100}"
-          assert.equal row.get('n'), -x, "n is wrong #{row.get 'n'} != #{-x}"
-          assert.equal row.get('m'), (-x-1000), "m is wrong #{row.get 'm'} != #{-x-1000}"
-

@@ -510,8 +458,13 @@
 coltests = topic: -> makeTable(nrows, 'col')
 _.extend coltests, checks(nrows)

+partitionedtests = topic: -> new data.PartitionedTable makeTable(nrows, 'col')
+_.extend partitionedtests, checks(nrows)
+
+

 suite.addBatch
+  "partitionedtable": partitionedtests
   "rowtable": rowtests
   "coltable": coltests
   "emptytable":


update group.isAdmin to work for current production app

--- src/node_modules/group-model/group-model.coffee
+++ src/node_modules/group-model/group-model.coffee
@@ -9,4 +9,7 @@
         new RoundModel(round)

     isAdmin: (user) ->
-      user.id == 1
+      if process.env.NODE_ENV == 'production'
+        return user.id == 28
+      else
+        return user.id == 1


cistrans: move point at probe

cistrans: move point at probe

--- cistrans/cistrans.coffee
+++ cistrans/cistrans.coffee
@@ -278,18 +278,6 @@
             .attr("stroke", "darkslateblue")
             .style("pointer-events", "none")

-    # point at probe
-    svg.append("circle")
-       .attr("class", "probe_data")
-       .attr("id", "probe_circle")
-       .attr("cx", chrLowXScale[data.probes[probe_data.probe].chr](data.probes[probe_data.probe].pos_cM))
-       .attr("cy", top[1] + pad.top*0.2)
-       .attr("r", bigRad)
-       .attr("fill", pink)
-       .attr("stroke", "darkslateblue")
-       .attr("stroke-width", 1)
-       .attr("opacity", 1)
-
     # title
     titletext = probe_data.probe
     probeaxes = svg.append("g").attr("id", "probe_data_axes").attr("class", "probe_data")
@@ -321,6 +309,17 @@
        .attr("width", w[1])
        .attr("class", "outerBox")

+    # point at probe
+    svg.append("circle")
+       .attr("class", "probe_data")
+       .attr("id", "probe_circle")
+       .attr("cx", chrLowXScale[data.probes[probe_data.probe].chr](data.probes[probe_data.probe].pos_cM))
+       .attr("cy", top[1])
+       .attr("r", bigRad)
+       .attr("fill", pink)
+       .attr("stroke", "darkslateblue")
+       .attr("stroke-width", 1)
+       .attr("opacity", 1)

   # circles at eQTL peaks
   peaks = svg.append("g").attr("id", "peaks")


remove only

remove only

--- src/test/tests/testDriverUtils.coffee
+++ src/test/tests/testDriverUtils.coffee
@@ -137,7 +137,7 @@
           "TSV of the table is incorrect"
         )

-  describe.only "simplify filter", ->
+  describe "simplify filter", ->
     it "it keeps regular filters unchanged", ->
       expect(driverUtil.simplifyFilter({
         type: 'is'


utils: change 'applyTextExpansions' to return  unless text

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -61,7 +61,7 @@
     $trg.parent().removeClass("show").addClass("hide") if $trg.is(less)

   applyTextExpansions: (text, shorten)->
-    return null unless text
+    return "" unless text

     text = text.replace /
/g, ' '


unneeded placeholder

unneeded placeholder

--- test/lib/test-client.coffee
+++ test/lib/test-client.coffee
@@ -1,8 +0,0 @@
-assert = require 'assert'
-buildCio = require '../../lib'
-
-describe 'test client', ->
-
-  describe 'with empty options', ->
-
-    it 'should create net.connect() default'


JDomain:createDomain: instead of putting the domain into the collection, put the domain as proposedDomain and then we will move it to domain field when its approved by the proxy

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -181,6 +181,9 @@
   createDomain = (options, callback)->

     {domainData, account, group, stack} = options
+
+    domainData.proposedDomain = domainData.domain
+    delete domainData.domain

     JStack = require './stack'
     JStack.getStack account, stack, (err, stack)=>


Router: Default username

Router: Default username

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -2,6 +2,7 @@

   loadWorkspace = ({machineLabel, username}, workspace) ->

+    username or= KD.nick()
     machine = getMachine machineLabel, username
     loadIDE { machine, workspace, username }

@@ -200,11 +201,11 @@

       {machineLabel} = params

+      params.username = KD.nick()
+
       refreshWorkspaces ->

         findWorkspace params, (workspace) ->
-
-          params.username = KD.nick()

           if workspace
             return loadWorkspace params, workspace


only call error callback after request complete and status is not 200

--- src/urlhandlers/xmlhttprequest.coffee
+++ src/urlhandlers/xmlhttprequest.coffee
@@ -19,9 +19,10 @@
             xhr.overrideMimeType && xhr.overrideMimeType('text/xml');
             xhr.onreadystatechange = ->
                 if xhr.readyState == 4
-                    cb(null, xhr.responseXML)
-                else
-                    cb(new Error("XHRURLHandler: #{xhr.statusText}"))
+                    if xhr.status == 200
+                        cb(null, xhr.responseXML)
+                    else
+                        cb(new Error("XHRURLHandler: #{xhr.statusText}"))
             xhr.send()
         catch
             cb(new Error('XHRURLHandler: Unexpected error'))


remove debug

remove debug

--- static/coffee/flows/directives.coffee
+++ static/coffee/flows/directives.coffee
@@ -253,8 +253,6 @@

     scope.updateTranslationStatus = (ruleset, baseLanguage, currentLanguage) ->

-      console.log(ruleset, baseLanguage, currentLanguage)
-
       iso_code = baseLanguage
       if currentLanguage
         iso_code = currentLanguage.iso_code
@@ -262,7 +260,6 @@
       for category in ruleset._categories

         category._missingTranslation = false
-        console.log(iso_code, category.name)
         if category.name
           if baseLanguage
             category._translation = category.name[iso_code]


Handle the error when the coverage report is not generated

--- lib/gocov/gocov-parser.coffee
+++ lib/gocov/gocov-parser.coffee
@@ -12,8 +12,10 @@

   # TODO disgusting - fix this parsing, regex
   rangesForFile: (file) ->
-    # TODO Ensure we have a data file and it exists
-    data = fs.readFileSync @dataFile, {encoding: "utf8"}
+    try
+      data = fs.readFileSync @dataFile, {encoding: "utf8"}
+    catch error
+      return []

     ranges = []


remove use of object controller

remove use of object controller

--- tests/dummy/app/controllers/documentation/calendar.coffee
+++ tests/dummy/app/controllers/documentation/calendar.coffee
@@ -1,4 +1,4 @@
-controller = Ember.ObjectController.extend
+controller = Ember.Controller.extend
   selectedDates: [
     moment(),
     moment().add(1, 'days')


Preventing compileJoins when directory events do not affect source files

--- command.coffee
+++ command.coffee
@@ -214,8 +214,10 @@
       fs.readdir source, (err, files) ->
         if err
           throw err unless err.code is 'ENOENT'
+          prevSources = sources.slice()
           toRemove = (file for file in sources when file.indexOf(source) >= 0)
           removeSource file, base, yes for file in toRemove
+          return if sources[i] is prevSources[i] for i in [0...sources.length]
           compileJoin()
         else
           files = files.map (file) -> path.join source, file


More info on rss

More info on rss

--- code/exportrss.coffee
+++ code/exportrss.coffee
@@ -44,15 +44,16 @@
       author: boxname.split('/')[0],
       feed_url: boxurl + 'http/' + filename
     exports.feed = feed
-    db.each "select * from loggit_event where type='start'", eachRow, done
+    # db.each "select * from loggit_event where type='start'", eachRow, done
+    db.each 'select start.runid as runid, start.time as start_time, exit.time as exit_time, start.pid as pid, start.command as command, exit.exit_signal as exit_signal, exit.exit_status as exit_status from (select * from loggit_event where type = "start") as start left join (select * from loggit_event where type = "exit") as exit on start.runid==exit.runid;', eachRow, done
 Gather = exports.Gather

 eachRow = (err, row) ->
   feed.item
     title: row.command,
     url: boxurl + 'sqlite?q=select*from+loggit_event+where+runid="'+row.runid+'"',
-    date: row.time,
-    description: 'do not have one'
+    date: row.start_time,
+    description: "Exit: "+row.exit_signal+row.exit_status
     guid: row.runid

 allDone = ->


changed labels

changed labels

--- widgets/graph/graph.coffee
+++ widgets/graph/graph.coffee
@@ -26,8 +26,8 @@

     @graph.series[0].data = @get('points') if @get('points')

-    labels = ['now', '-1h', '-2h', '-3h', '-4h', '-5h', '-6h', '-7h', '-8h', '-9h'];
-    x_axis = new Rickshaw.Graph.Axis.Time(graph: @graph, tickFormat: (x) -> labels[x])
+    labels = ['-9h', '-8h', '-7h', '-6h', '-5h', '-4h', '-3h', '-2h', '-1h', 'now']
+    x_axis = new Rickshaw.Graph.Axis.X(graph: @graph, tickFormat: (x) -> labels[x - 1])
     y_axis = new Rickshaw.Graph.Axis.Y(graph: @graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT)
     @graph.render()


coloring minimum and maximum

coloring minimum and maximum

--- packages/custom/transparency/coffee/public/directives/timelineDirective.coffee
+++ packages/custom/transparency/coffee/public/directives/timelineDirective.coffee
@@ -11,13 +11,20 @@
                data = () ->
                     result = [
                          {
-                              key: "Difference",
                               values:
                                    [
                                    ]
                          }
                     ]
                     if $scope.data and $scope.data.data and $scope.data.data.values
+                         max = Number.NEGATIVE_INFINITY
+                         min = Number.POSITIVE_INFINITY
+                         for transfer in $scope.data.data.values
+                              if max < transfer[1] and transfer[1] isnt 0
+                                   max = transfer[1]
+                              if min > transfer[1] and transfer[1] isnt 0
+                                   min = transfer[1]
+
                          for transfer in $scope.data.data.values
                               str = ""+transfer[0]
                               label = (""+transfer[0]).substring 0, 4
@@ -30,9 +37,16 @@
                               if (str.indexOf '.') is -1
                                    label += "/Q1"

+                              color = "steelblue"
+                              if (transfer[1] is min)
+                                   color = "lightgreen"
+                              else if (transfer[1] is max)
+                                   color = "lightcoral"
+
                               result[0].values.push {
                                    label: label,
-                                   value: transfer[1]
+                                   value: transfer[1],
+                                   color: color
                               }
                     result

@@ -43,9 +57,6 @@
                     .y((d) ->
                          d.value)
                     .staggerLabels(true)
-                    .color((d) ->
-                         "steelblue"
-                    )
                     .margin({top: 30, right: 100, bottom: 75, left: 100})

                     chart.yAxis.tickFormat (d) -> d.toLocaleString $scope.getCurrentLanguage(), {style:"currency",currency:"EUR"}


update srf field crete

update srf field crete

--- snippets/simple-react-form.cson
+++ snippets/simple-react-form.cson
@@ -9,7 +9,7 @@
   'Create a simple react form field':
     'prefix': 'field'
     'body': """
-      <Field fieldName='$1' label='$2' type={$3} />
+      <Field fieldName='$1' type={$2} />
     """
   'Import Simple React Form':
     'prefix': 'impsimplereactform'


TODO plans for ScrollElement

TODO plans for ScrollElement

--- source/Art/Engine/Elements/Widgets/ScrollElement.coffee
+++ source/Art/Engine/Elements/Widgets/ScrollElement.coffee
@@ -40,60 +40,35 @@
 animatorSpringConstant = 300
 animatorSpringFriction = 25
 flickSpeedMultiplier = 1
+
 ###
-TODO: Pages should be able to have margins!
-  But we have a big problem. Pages are split across two parents and the two parents
-  can't inherit the children's margins.
-
-I'm more and more thinking I want a fully custom ArtEngine layout for PSE.
-It would make a lot of things simpler to understand...
-###
-
-###
-PagingScrollElement
+ScrollElement

 guarantee:
   Will never scroll more than one "windowSize" per frame.
   That means you need at least as many "pages" as it will take to display one more window-full of content
   above or below the current displayed content.

-margins:
-  Margins on paging elements are currently not supported.
-  We could relatively easilly support constant margins.
-  Anything more complex gets a little tedious.
-  Recomendation: Use Padding instead of Margins.
-
 events:
-  currentPageChanged:
-    oldCurrentPage: element
-    currentPage:    element
+  scrollingUpdate:
+  scrollingActive:
   scrollUpdate:
-    childrenOffset:           number (pixels)
-    childrenSize:             number (pixels)
-    windowSize:               number (pixels)
-    focusedChild:             element
-    firstOnScreenChildIndex:  number
-    lastOnScreenChildIndex:   number
-    focusedChildIndex:        number
-
-naming:
-
-  All "positions" are scalers.
-  All "positions" are relative to the top/left of the PagingScrollElement.
-  Positive values indicate more to the bottom/right of the PagingScrollElement.
-
-  "scrollPosition" is the main geometry value for the PagingScrollElement.
-
-  I chose "scrollPosition" over just "position" or "location".
-  location vs position: http://www.eng-tips.com/viewthread.cfm?qid=180516
-    position can be used to refer to internal configuration, which scrollPosition is,
-    but location cannot. This avoids confusion with Element's currentLocations.
-
-Implementation Notes:
-
-  When to use "pending" property values:
-    - use pending values only as inputs to computation that results in setting another property
-    - use current (non-pending) poperty values for all getters
+
+TODO:
+  scrollPosition should be "absolute" instead of "relative to 'tracking'".
+    Starts out at 0 (or viewHeight-childrenHeight if track = end).
+    Absolute means it tracks the total distance scrolled over all time.
+    Why? Animation!
+
+  However, 'tracking' still needs to do everything it does.
+  Which means we need another value - trackedReferencePosition
+  The tracking-line is placed on-screen relative to parent at:
+    trackedReferencePosition + scrollPosition
+
+  If elements are only added and existing elements never change height, then
+  scrollPosition is always the start of the scroll area and trackedReferncePoisition
+  is the distance from start to the tracking-line.
+
 ###

 defineModule module, class ScrollElement extends Element


reorganize code logic

reorganize code logic

--- lib/linter-js-standard.coffee
+++ lib/linter-js-standard.coffee
@@ -52,16 +52,11 @@
     if typeof config == 'undefined'
       return

-    styleSettings =
-      standard: pkgConfig(null, { cwd: @filePath, root: 'standard' })
-      semistandard: pkgConfig(null, { cwd: @filePath, root: 'semistandard' })
-
     # see if setting checkStyleDevDependencies is true
     # if not fallback to style value to decide which bin
     # we should use
     if config.checkStyleDevDependencies
-      devDeps = pkgConfig(null, { cwd: @filePath, root: 'devDependencies' })
-      @executablePath = @formatDevDepsExecPath(devDeps, styleSettings)
+      @checkDevDeps()
     else
       if config.style == 'standard'
         @linterName = 'js-standard'
@@ -72,16 +67,7 @@

     # see if setting honorStyleSettings is true
     if config.honorStyleSettings
-      # If linter is standard and parser is present
-      # else if linter is semiStandard and parser is present
-      if @linterName == 'js-standard' and
-      styleSettings.standard and
-      styleSettings.standard.parser
-        @cmd.push '--parser', styleSettings.standard.parser
-      else if @linterName == 'js-standard' and
-      styleSettings.semistandard and
-      styleSettings.semistandard.parser
-        @cmd.push '--parser', styleSettings.semistandard.parser
+      @checkStyleSettings()

     # Check if executablePath is defined,
     # if isn't defined it means the file
@@ -97,9 +83,29 @@
       # No style path!
       @executablePath = path.resolve(__dirname, 'no-style.js')

-  formatDevDepsExecPath: (devDeps, styleSettings) ->
+  checkDevDeps: () ->
+    # This get devDependencies property
+    # from the nearest package.json
+    devDeps = pkgConfig(null, { cwd: @filePath, root: 'devDependencies' })
+
+    if devDeps and (devDeps.standard or devDeps.semistandard)
+      if devDeps.standard
+        # Set execPath to standard bin
+        # and change linter name
+        @linterName = 'js-standard'
+        @executablePath = binPath.standard
+
+      else
+        # Set execPath to semistandard bin
+        # and change linter name
+        @linterName = 'js-semistandard'
+        @executablePath = binPath.semiStandard
+    else
+      @executablePath = false
+
+  checkStyleSettings: () ->
     # Default value
-    execPath = false
+    styleSettings = false

     # This is the absolute path
     # of the project path relative
@@ -110,46 +116,26 @@
     relativeFilePath = @filePath.replace(projectPath, '')
     relativeFilePath = relativeFilePath.substring(1)

-    if devDeps and (devDeps.standard or devDeps.semistandard)
-      if devDeps.standard
-        # Set execPath to standard bin
-        # and change linter name
-        # NOTE: this variable can be changed
-        # along the logic flow
-        @linterName = 'js-standard'
-        execPath = binPath.standard
+    if @linterName == 'js-standard'
+      styleSettings = pkgConfig(null, { cwd: @filePath, root: 'standard' })
+    else if @linterName == 'js-semistandard'
+      styleSettings = pkgConfig(null, { cwd: @filePath, root: 'semistandard' })

-        # If ignore glob patterns are present
-        if styleSettings.standard.ignore
-          ignoreGlobPatterns = []
-          ignoreGlobPatterns = ignoreGlobPatterns.concat styleSettings.standard.ignore
+    if styleSettings
+      # Check parser
+      if styleSettings.parser
+        @cmd.push '--parser', styleSettings.parser

-          testGlobPatterns = ignoreGlobPatterns.some (pattern) ->
-            return minimatch(relativeFilePath, pattern)
+      # If ignore glob patterns are present
+      if styleSettings.ignore
+        ignoreGlobPatterns = []
+        ignoreGlobPatterns = ignoreGlobPatterns.concat styleSettings.ignore

-          if testGlobPatterns
-            execPath = false
+        testGlobPatterns = ignoreGlobPatterns.some (pattern) ->
+          return minimatch(relativeFilePath, pattern)

-      else
-        # Set execPath to semistandard bin
-        # and change linter name
-        # NOTE: the execPath variable can be changed
-        # along the logic flow
-        @linterName = 'js-semistandard'
-        execPath = binPath.semiStandard
-
-        # If ignore glob patterns are present
-        if styleSettings.semistandard.ignore
-          ignoreGlobPatterns = []
-          ignoreGlobPatterns = ignoreGlobPatterns.concat styleSettings.semistandard.ignore
-
-          testGlobPatterns = ignoreGlobPatterns.some (pattern) ->
-            return minimatch(relativeFilePath, pattern)
-
-          if testGlobPatterns
-            execPath = false
-
-    execPath
+        if testGlobPatterns
+          @executablePath = false

   formatMessage: (match) ->
     if !match.error && !match.warning


Remove almost duplicate, and redundant, line.

--- test/integration/datatypes-in-results-test.coffee
+++ test/integration/datatypes-in-results-test.coffee
@@ -3,7 +3,6 @@
 fs = require('fs')

 config = JSON.parse(fs.readFileSync(process.env.HOME + '/.tedious/test-connection.json', 'utf8'))
-config.options.textsize = 4 * 1024;
 config.options.textsize = 8 * 1024;
 config.options.debug =
   packet: true


fix bug that could prevent project server startup in case of .sagemathcloud.log having wrong permissions

--- salvus/local_hub.coffee
+++ salvus/local_hub.coffee
@@ -2107,7 +2107,7 @@
                 cb(); return
             # read the log file
             fs.readFile SAGEMATHCLOUD_LOG_FILE, (err, _data) ->
-                data = _data.toString()
+                data = _data?.toString()  # ? is important, since in case of err _data is not defined.
                 cb(err)
         (cb) ->
             if not exists


Consistent Function Naming

Consistent Function Naming

--- coffee/auto-updater.iced
+++ coffee/auto-updater.iced
@@ -24,11 +24,11 @@
     console.log @options

   installUpdate: ->
-    this._install_update_mac() if /^darwin/.test process.platform
-    this._install_update_win() if /^win/.test process.platform
-    this._install_update_linux() if /^linux/.test process.platform
+    this._installUpdateMac() if /^darwin/.test process.platform
+    this._installUpdateWin() if /^win/.test process.platform
+    this._installUpdateLinux() if /^linux/.test process.platform

-  _install_update_mac: ->
+  _installUpdateMac: ->
     execPath = process.execPath
     filePath = execPath.substr(0, execPath.lastIndexOf("\\"))
     appPath = path.normalize(execPath + "/../../../../../../..")
@@ -39,7 +39,7 @@

     progressCb = (state) -> @emit("download-progress", state)

-    await @_download_update downloadTarget,
+    await @_downloadUpdate downloadTarget,
       "#{@options.updateServer}/#{@options.remoteFilenameOSX}",
       progressCb, defer err

@@ -50,15 +50,15 @@

     @emit "download-finished"

-    await @_extract_update downloadTarget, extractFolder, defer err
+    await @_extractUpdate downloadTarget, extractFolder, defer err
     if err?
       @emit "download-failed", err
       return
-    await @_hide_original_mac appPath, @options.osxAppName, defer err
+    await @_hideOriginalMac appPath, @options.osxAppName, defer err
     if err?
       @emit "download-failed", err
       return
-    await @_copy_update_mac @options.osxAppName,
+    await @_copyUpdateMac @options.osxAppName,
      extractFolder, appPath, defer err
     if err?
       @emit "download-failed", err
@@ -66,35 +66,35 @@

     @emit "update-installed"

-  _download_update: (targetPath, remoteUrl, progressCb, doneCb) ->
+  _downloadUpdate: (targetPath, remoteUrl, progressCb, doneCb) ->
     progress request remoteUrl
       .on 'progress', progressCb
       .on 'error', doneCb
       .pipe fs.createWriteStream targetPath
       .on 'close', doneCb

-  _extract_update: (sourceZip, targetFolder, done) ->
+  _extractUpdate: (sourceZip, targetFolder, done) ->
     unzipper = new DecompressZip(sourceZip)
     unzipper.on 'error', done
     unzipper.on 'extract', (log) -> done(null)
     unzipper.extract({ path: targetFolder })

-  _hide_original_mac: (appPath, appName, cb) ->
+  _hideOriginalMac: (appPath, appName, cb) ->
     filename = appName + '.app'
     fs.rename appPath + '/' + filename, appPath + '/.' + filename, cb

-  _copy_update_mac: (appName, from, to, callback) ->
+  _copyUpdateMac: (appName, from, to, callback) ->
     wrench.copyDirRecursive from + '/' + appName + '.app',
      to + '/' + appName + '.app',
      {forceDelete: true},
      callback

-  _install_update_win: () ->
+  _installUpdateWin: () ->

-  _install_update_linux: () ->
+  _installUpdateLinux: () ->

-  is_update_available: (cb) ->
+  isUpdateAvailable: (cb) ->
     options = @options
     request "#{@options.updateServer}/package.json", (error, response, body) ->
       if error?


Fixed spec for custom delimiters

Fixed spec for custom delimiters

--- coffee/calculator.coffee
+++ coffee/calculator.coffee
@@ -1,8 +1,16 @@
 exports.Calculator = class Calculator
   add: (input) ->
     result = 0
-    numberStrings = input.split /\n|,/
+    [delimiter, trimmedInput] = this.getDelimiter input
+    numberStrings = trimmedInput.split delimiter
     for numberString in numberStrings
       num = parseInt numberString
       result += num if num
     result
+
+  getDelimiter: (input) ->
+    match = input.match /^\/\/(.+)\n(.+)/
+    if match
+      [match[1], match[2]]
+    else
+      [/\n|,/, input]


js blockquote expand note

js blockquote expand note

--- spirit/core/static/spirit/scripts/src/blockquote.coffee
+++ spirit/core/static/spirit/scripts/src/blockquote.coffee
@@ -2,6 +2,10 @@
     Expand blockquote instead of showing a scrollbar
 ###

+# This is not re-calculated on resize, so
+# in case the blockquote grows, the regular
+# scrollbar will show; in case it shrinks, the
+# expand will still show
 stModules.blockquoteExpand = (elms) ->
   Array.from(elms).forEach((elm) ->
     # there's a polyfill, but I don't care about IE


automatic scrolling working

automatic scrolling working

--- coffee/banners.coffee
+++ coffee/banners.coffee
@@ -1,11 +1,11 @@
 # Features remaining:
-# - Timer for automatically rotating
-# - Pause button
 # - Loading before and after image after first is loaded
 # - Option to switch to particular slide?
 #
 # Done:
 # - Fade out
+# - Timer for automatically rotating
+# - Pause button (this is possible)
   #
   # var fadeTimeOut;
   # function setFadeoutTimer() {
@@ -49,7 +49,7 @@
       @initialize_callbacks()
       @get_banner_data()
       @show_banner 0
-      @create_timer()
+      @start_automatic_scrolling()

     next: ->
       @show_banner @next_banner()
@@ -57,10 +57,13 @@
     prev: ->
       @show_banner @prev_banner()

-    create_timer: ->
+    start_automatic_scrolling: ->
       @timer = new AutomaticScroller this, 2000

-    restart_timer: ->
+    stop_automatic_scrolling: ->
+      @timer.destroy()
+
+    restart_automatic_scrolling: ->
       @timer.destroy().create()

     show_banner: (id) ->
@@ -88,11 +91,11 @@

     initialize_callbacks: ->
       @next_button.click =>
-        @timer.destroy()
+        @stop_automatic_scrolling()
         @next()

       @prev_button.click =>
-        @timer.destroy()
+        @stop_automatic_scrolling()
         @prev()

     get_banner_data: ->


Tiny refactoring

Tiny refactoring

--- coffee/options.coffee
+++ coffee/options.coffee
@@ -24,9 +24,9 @@
   Office.get (status, title, message) ->
     chrome.browserAction.setIcon {path: 'img/icon-'+status+'.png'}
     ls.currentStatus = status
-    Meetings.get (meetingPlan) ->
-      meetingPlan = $.trim meetingPlan
-      today = '### Nå\n' + title + ": " + message + "\n\n### Resten av dagen\n" + meetingPlan
+    Meetings.get (meetings) ->
+      meetings = $.trim meetings
+      today = '### Nå\n' + title + ": " + message + "\n\n### Resten av dagen\n" + meetings
       chrome.browserAction.setTitle {title: today}
       ls.currentStatusMessage = message


Encapsulates auto play settings and fluid behaviour on functions

--- coffee/suraido.coffee
+++ coffee/suraido.coffee
@@ -74,16 +74,7 @@
       )

       #autoslide
-      if @options.autoplay
-        setTimeout(=>
-          if @options.delay | 0
-            @play()
-
-            if @options.pause
-              @$el.on 'mouseover, mouseout', (event) =>
-                @stop()
-                event.type is 'mouseout' && @play()
-        , @options.init | 0)
+      @_enablesAutoPlay() if @options.autoplay

       #keypresses binding
       @_enableBindKeys() if @options.enableKeys
@@ -91,24 +82,38 @@
       @options.dots and @_createPagination('dot')
       @options.arrows and @_createPagination('arrow')

-      if @options.fluid
-        $(window).resize(=>
-         @r and clearTimeout(@r)
-
-         @r = setTimeout( =>
-           style =
-             height: @items.eq(@currentItemIndex).outerHeight()
-           width = @$el.outerWidth()
-
-           @itemsWrapper.css style
-           style['width'] = "#{Math.min(Math.round((width / @$el.parent().width()) * 100), 100)}%"
-           @$el.css style
-           , 50)
-        ).resize()
+      #fluid behavior for responsive layouts
+      @_enablesFluidBehavior() if @options.fluid

       if $.event.special['swipe'] or $.Event 'swipe'
         @$el.on 'swipeleft swiperight swipeLeft swipeRight', (e) =>
          if e.type.toLowerCase() is 'swipeleft' then @next() else @prev()
+
+    _enablesAutoPlay: =>
+      setTimeout(=>
+         if @options.delay | 0
+           @play()
+
+           if @options.pause
+             @$el.on 'mouseover, mouseout', (event) =>
+               @stop()
+               event.type is 'mouseout' && @play()
+      , @options.autoPlayDelay | 0)
+
+    _enablesFluidBehavior: =>
+      $(window).resize(=>
+        @resize and clearTimeout(@resize)
+
+        @resize = setTimeout(=>
+          style =
+            height: @items.eq(@currentItemIndex).outerHeight()
+          width = @$el.outerWidth()
+
+          @itemsWrapper.css style
+          style['width'] = "#{Math.min(Math.round((width / @$el.parent().width()) * 100), 100)}%"
+          @$el.css style
+          , 50)
+      ).resize()

     _enableBindKeys: =>
       $(document).on 'keydown', (event) =>


Fixing Mood Poll

Fixing Mood Poll

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -34,45 +34,43 @@
       robot.messageRoom room, "Are there any achievements from the week that someone @here needs to add to your board '" + board.name + "'?"
   , null, true, 'America/New_York'

-  moodPoll = new CronJob '00 20 20 * * 1-5', ->
+  moodPoll = new CronJob '00 37 20 * * 1-5', ->
     for room, board of robot.brain.data._private.board
       buttonName = "mood_" + robot.brain.data._private.team[room]._id
-      pollMessage = {
-        "attachments": [{
-          "text": "How was your day today?",
-          "fallback": "Oh no! Something went horribly wrong!"
-          "callback_id": "dab_mood"
-          "color": "#3AA3E3"
-          "attachment_type": "default"
-          "actions": [{
-            "name": buttonName
-            "text": ":bolt-ecstatic:"
-            "type": "button"
-            "value": "ecstatic"
-          },{
-            "name": buttonName
-            "text": ":bolt-happy:"
-            "type": "button"
-            "value": "happy"
-          },{
-            "name": buttonName
-            "text": ":bolt-indifferent:"
-            "type": "button"
-            "value": "indifferent"
-          },{
-            "name": buttonName
-            "text": ":bolt-disappointed:"
-            "type": "button"
-            "value": "disappointed"
-          },{
-            "name": buttonName
-            "text": ":bolt-sad:"
-            "type": "button"
-            "value": "sad"
+      robot.emit 'slack.attachment',
+        message: room
+        content:
+          text: "How was your day today?"
+          fallback: "Oh no! Something went horribly wrong!"
+          callback_id: "dab_mood"
+          color: "#3AA3E3"
+          attachment_type: "default"
+          actions: [{
+            name: buttonName
+            text: ":bolt-ecstatic:"
+            type: "button"
+            value: "ecstatic"
+          },{
+            name: buttonName
+            text: ":bolt-happy:"
+            type: "button"
+            value: "happy"
+          },{
+            name: buttonName
+            text: ":bolt-indifferent:"
+            type: "button"
+            value: "indifferent"
+          },{
+            name: buttonName
+            text: ":bolt-disappointed:"
+            type: "button"
+            value: "disappointed"
+          },{
+            name: buttonName
+            text: ":bolt-sad:"
+            type: "button"
+            value: "sad"
           }]
-        }]
-      }
-      robot.messageRoom room, pollMessage
   , null, true, 'America/New_York'

   robot.hear /what is the team's mood in the past (.*) days/i, (msg) ->