Fix typo

Fix typo

--- src/rewriter.coffee
+++ src/rewriter.coffee
@@ -243,7 +243,7 @@
       #       b: c
       #
       # Don't accept implicit calls of this type, when on the same line
-      # as the control strucutures below as that may misinterpret constructs like:
+      # as the control structures below as that may misinterpret constructs like:
       #
       #     if f
       #        a: 1


updated addButtons

updated addButtons

--- src/drm-accordion.coffee
+++ src/drm-accordion.coffee
@@ -2,6 +2,7 @@
 # Toggles hiding and showing content with an accordion efffect
 ###############################################################################

+$ = jQuery
 class @DrmAccordion
     constructor: (@speed = 300, @container = $('.drm-accordion')) ->
         @label = '.' + @container.children().first().attr 'class'
@@ -58,11 +59,10 @@
             self.buttons.hideButton.on 'click', self.hideAll

     addButtons: =>
-        buttons =
+        return {
             showButton: @createButton 'showButton', 'Show All', 'drm-show-all drm-button-inline'
             hideButton: @createButton 'hideButton', 'Hide All', 'drm-hide-all drm-button-inline'
-
-        buttons
+        }

     createButton: (button, message, className) =>
         $('<button></button>',


Expand all defs before measuring which are used

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -1519,19 +1519,23 @@
             "##{newId}"
     findGlobalDefs svg

-    ## Include any <defs> from mapping lookups or callbacks.
-    firstSymbol = svg.firstChild
-    defsWrapper = null
+    ## Render all <defs> so far and check for additional <defs> used by them.
     ## `for def in @defs` but allowing @defs to change in length
     ## from additional global <defs> encountered along the way.
     i = 0
-    while i < @defs.length
-      def = @defs[i++]
+    defDoms =
+      while i < @defs.length
+        def = @defs[i++]
+        dom = def.useDOM()
+        ## Look for more global <defs> used by this def.
+        findGlobalDefs dom
+        {def, dom}
+    ## Add <defs> to DOM if they're used or forced.
+    firstSymbol = svg.firstChild
+    defsWrapper = null
+    for {def, dom} in defDoms
       ## Omit unused <defs> unless forced.
       continue unless def.isForced or usedIds.has def.id
-      dom = def.useDOM()
-      ## Look for more global <defs> used by this def.
-      findGlobalDefs dom
       ## Wrap in <defs> if needed.
       if skipDef.has dom.tagName
         svg.insertBefore dom, firstSymbol


Only use current path

Only use current path

--- src/tankipas.coffee
+++ src/tankipas.coffee
@@ -22,7 +22,7 @@

 tankipas
     .version pkg.version
-    .usage "[options] /path/to/repo/"
+    .usage "[options]"
     .description "Compute approximate development time passed on a project, using logs from version control system."
     .option "-s, --system <system>", "force the version system to analyse (by default, try to guess)"
     .option "-g, --gap <amount>", "number of minutes above wich the time between two commits is ignored in the total.", 120
@@ -31,7 +31,7 @@

 # --- get path

-sRepoPath = path.resolve process.cwd(), ( tankipas.args[ 0 ] ? "." )
+sRepoPath = process.cwd()

 # --- get system


pass timezone to server

pass timezone to server

--- src/worldcup.coffee
+++ src/worldcup.coffee
@@ -12,8 +12,10 @@
 #   hubot wc <red or yellow> <name> - Give someone a red/yellow card

 module.exports = (robot) ->
-  robot.respond /(worldcup|wc)( today)/i, (msg) ->
-    msg.http("http://worldcup2014bot.herokuapp.com/matches")
+  robot.respond /(worldcup|wc)( today)( [a-z]+)?/i, (msg) ->
+    timezone = msg.match[3]
+    url = "http://worldcup2014bot.herokuapp.com/matches?timezone=" + timezone
+    msg.http(url)
       .get() (err, res, body) ->
         matches = JSON.parse(body).matches
         if matches.length > 0
@@ -118,4 +120,4 @@

           msg.send formatted_standings
         else
-          msg.send "We couldn't find standings for that group. Please make sure the letter is valid and try again."+          msg.send "We couldn't find standings for that group. Please make sure the letter is valid and try again."


Fix test.

Fix test.

--- spec/javascripts/dot_ledger/views/statistics/activity_per_category/list_spec.js.coffee
+++ spec/javascripts/dot_ledger/views/statistics/activity_per_category/list_spec.js.coffee
@@ -1,4 +1,4 @@
-describe "DotLedger.Views.Accounts.List", ->
+describe "DotLedger.Views.Statistics.ActivityPerCategory.List", ->
   createView = ->
     collection =  new Backbone.Collection(
       [
@@ -37,6 +37,13 @@
         },
       ]
     )
+
+    collection.metadata = {
+      total_received: "0.0"
+      total_spent: "244.87"
+      total_net: "-244.87"
+    }
+
     view = new DotLedger.Views.Statistics.ActivityPerCategory.List
       collection: collection
     view


Implements reload action in ranking show controller.

--- app/assets/javascripts/voluntary_ranking/controllers/rankings/show_controller.js.coffee
+++ app/assets/javascripts/voluntary_ranking/controllers/rankings/show_controller.js.coffee
@@ -2,4 +2,16 @@
   listContext: 'Global', yourRanking: false, routeName: ''
   yourRankingClass: 'btn', globalRankingClass: 'btn active'
   thingType: '', adjective: '', negativeAdjective: '', topic: '', scope: ''
+
+  actions:
+
+    reload: ->
+      @set(
+        'model',
+        @store.find(
+          'ranking_item',
+          adjective: @get('adjective'), negative_adjective: @get('negativeAdjective'),
+          topic: @get('topic'), scope: @get('scope')
+        )
+      )
 )


Fix indentation for languages without indent pattern

--- src/language-mode.coffee
+++ src/language-mode.coffee
@@ -251,17 +251,15 @@
     decreaseIndentRegex = @decreaseIndentRegexForScopeDescriptor(scopeDescriptor)
     decreaseNextIndentRegex = @decreaseNextIndentRegexForScopeDescriptor(scopeDescriptor)

-    currentIndentLevel = @editor.indentationForBufferRow(bufferRow)
-    return currentIndentLevel unless increaseIndentRegex
-
     if options?.skipBlankLines ? true
       precedingRow = @buffer.previousNonBlankRow(bufferRow)
       return 0 unless precedingRow?
     else
       precedingRow = bufferRow - 1
-      return currentIndentLevel if precedingRow < 0
+      return 0 if precedingRow < 0

     desiredIndentLevel = @editor.indentationForBufferRow(precedingRow)
+    return desiredIndentLevel unless increaseIndentRegex

     unless @editor.isBufferRowCommented(precedingRow)
       precedingLine = @buffer.lineForRow(precedingRow)
@@ -270,7 +268,7 @@

     unless @buffer.isRowBlank(precedingRow)
       desiredIndentLevel -= 1 if decreaseIndentRegex?.testSync(line)
-
+
     Math.max(desiredIndentLevel, 0)

   # Calculate a minimum indent level for a range of lines excluding empty lines.


drawable transform update was one step behind

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -34,7 +34,9 @@
       I.height = sprite.height
     )

-  self.bind 'step', ->
+  self.bind 'draw', (canvas) ->
+    #TODO: Not sure if setting the transform here is the best
+    # but we want to make sure it's adjusted after *all* the updates
     center = self.center()

     if I.rotation
@@ -45,7 +47,6 @@
       # Assumes I.x and I.y are top-left
       I.transform = Matrix.translation(I.x, I.y)

-  self.bind 'draw', (canvas) ->
     if I.sprite
       I.sprite.draw(canvas, 0, 0)
     else


The text displayed can be configured now.

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -92,6 +92,20 @@
     # If null, the dropzone element will be used
     previewsContainer: null

+
+    # Dictionary
+
+    # The text used before any files are dropped
+    dictDefaultMessage: "Drop files here to upload"
+
+    # The text that replaces the default message text it the browser is not supported
+    dictFallbackMessage: "Your browser does not support drag'n'drop file uploads."
+
+    # The text that will be added before the fallback form
+    # If null, no text will be added at all.
+    dictFallbackText: "Please use the fallback form below to upload your files like in the olden days."
+
+
     # If `done()` is called without argument the file is accepted
     # If you call it with an error message, the file is rejected
     # (This allows for asynchronous validation)
@@ -117,9 +131,8 @@
         @element.appendChild messageElement

       span = messageElement.getElementsByTagName("span")[0]
-      span.textContent = "Your browser does not support drag'n'drop file uploads." if span
-
-      @element.appendChild createElement """
Please use the fallback form below to upload your files like in the olden days.
"""
+      span.textContent = @options.dictFallbackMessage if span
+
       @element.appendChild @getFallbackForm()

@@ -265,7 +278,7 @@
     @element.setAttribute("enctype", "multipart/form-data") if @element.tagName == "form"

     if @element.classList.contains("dropzone") and !@element.querySelector(".message")
-      @element.appendChild createElement """
Drop files here to upload
"""
+      @element.appendChild createElement """
#{@options.dictDefaultMessage}
"""

     if @options.clickable
       @hiddenFileInput = document.createElement "input"
@@ -334,7 +347,11 @@
   getFallbackForm: ->
     return existingFallback if existingFallback = @getExistingFallback()

-    fields = createElement """
<input type="file" name="#{@options.paramName}" multiple="multiple" /><button type="submit">Upload!</button>
"""
+    fieldsString = """
"""
+    fieldsString += """
#{@options.dictFallbackText}
""" if @options.dictFallbackText
+    fieldsString += """<input type="file" name="#{@options.paramName}" multiple="multiple" /><button type="submit">Upload!</button>
"""
+
+    fields = createElement fieldsString
     if @element.tagName isnt "FORM"
       form = createElement("""<form action="#{@options.url}" enctype="multipart/form-data" method="post"></form>""")
       form.appendChild fields


reset displaying screens on logout

reset displaying screens on logout

--- client/lanes/screens/Definitions.coffee
+++ client/lanes/screens/Definitions.coffee
@@ -244,3 +244,6 @@
     Lanes.Screens.Definitions.groups.each (group) ->
         delete group.cache
     delete Lanes.Screens.Definitions.groups.cache
+
+    unless user.isLoggedIn
+        Lanes.Screens.Definitions.displaying.reset()


Don't clear cache and update all exercises after load

--- src/flux/exercise.coffee
+++ src/flux/exercise.coffee
@@ -47,6 +47,7 @@
 ExerciseConfig =
   _exercises: []
   _asyncStatus: null
+  _exerciseCache: []
   _unsavedExclusions: {}

   FAILED: -> console.error('BUG: could not load exercises')
@@ -75,19 +76,28 @@
     @cacheExercises(obj.items)

   cacheExercises: (exercises) ->
-    _exerciseCache = []
     for exercise in exercises
-      _exerciseCache[exercise.id] = exercise
-    @_exerciseCache = _exerciseCache
+      if @_exerciseCache[exercise.id]
+        _.extend(@_exerciseCache[exercise.id], exercise)
+      else
+        @_exerciseCache[exercise.id] = exercise
     @emitChange()

   saveExclusions: (courseId) -> # Used to trigger save by API
     @_exclusionsAsyncStatus = SAVING
     @emitChange()
+
+  updateExercises: (updatedExercises) ->
+    for updatedExercise in updatedExercises
+      for pageIds, storedExercises of @_exercises
+        for storedExercise in storedExercises when storedExercise.id is updatedExercise.id
+          _.extend(storedExercise, updatedExercise)
+    @cacheExercises(updatedExercises) # will @emitChange() so we don't bother to ourselves
+
   exclusionsSaved: (exercises, courseId) ->
     @_unsavedExclusions = {}
     delete @_exclusionsAsyncStatus
-    @cacheExercises(exercises)
+    @updateExercises(exercises)

   setExerciseExclusion: (exerciseId, isExcluded) ->
     @_unsavedExclusions[exerciseId] = isExcluded


Fix error message

Fix error message

--- src/formula/error.coffee
+++ src/formula/error.coffee
@@ -2,7 +2,7 @@
 class FormulaError extends Error

   @DIV0 = new FormulaError("#DIV/0!")
-  @NA = new FormulaError("#N/A!")
+  @NA = new FormulaError("#N/A")
   @NAME = new FormulaError("#NAME?")
   @NUM = new FormulaError("#NUM!")
   @NULL = new FormulaError("#NULL!")


More verbose server errors

More verbose server errors

--- server/imap/pool.coffee
+++ server/imap/pool.coffee
@@ -128,12 +128,12 @@
         process.nextTick @_deQueue

     _onActiveError: (connection, err) ->
-        log.error "error on active imap socket on #{connection.connectionName}", err.stack
+        log.error "error on active imap socket on #{connection.connectionName}", err
         @_removeFromPool connection
         try connection.destroy()

     _onActiveClose: (connection, err) ->
-        log.error "active connection #{connection.connectionName} closed", err.stack
+        log.error "active connection #{connection.connectionName} closed", err
         task = @pending[connection.connectionID]
         if task
             delete @pending[connection.connectionID]


Allow underscores, add direction indicator

--- src/hubot-nextbus.coffee
+++ src/hubot-nextbus.coffee
@@ -30,7 +30,7 @@
         msg.send "No stops found near #{latlon}"

   # get a particular stop's next bus
-  robot.respond /(?:bus|nextbus) stop ([A-Z0-9]+)$/i, (msg) ->
+  robot.respond /(?:bus|nextbus) stop ([A-Z0-9_]+)$/i, (msg) ->
     stop_id = msg.match[1]
     robot.logger.debug stop_id
     queryStopById stop_id, msg
@@ -48,7 +48,8 @@
       robot.logger.debug stop
       if !stop.next
         return msg.send "The last bus has already run for today."
-      msg.send "Next bus arrives to #{stop.stop_name} at #{stop.next.arrival_time_str} (##{stop.next.route_id} - #{stop.next.route_long_name})"
+      direction = if stop.next.direction_id == '0' then 'outbound' else 'inbound'
+      msg.send "Next #{direction} bus arrives to #{stop.stop_name} at #{stop.next.arrival_time_str} (##{stop.next.route_id} - #{stop.next.route_long_name})"

   getAPIResponse = (method, msg, cb) ->
     url = "https://nextbus.jt2k.com/api/#{method}?key=#{api_key}"


fact/fiction story

fact/fiction story

Former-commit-id: f32b58c8b2c1091cf61fd60d017c0ee12549f22a [formerly f4e01f5b5911f440800f27af5cfebe535cd57b7b] [formerly d45de0819e4735a4ddd40bc59f1191d8e2e98929 [formerly 7e5c2d30cead888d0572aa4f483608c5f26e0d0d]] [formerly 3769b1cf732405b8e1aa5b8dda3823f7b6796568 [formerly c197401acb315de91446ed6911cfde9384ef4e0e] [formerly e2945aad87968ff206f4ea55b2e0729cc477e8da [formerly e2945aad87968ff206f4ea55b2e0729cc477e8da [formerly da107390f2764d7a721dcfa81c58838aa27e17d5]]]]
Former-commit-id: 10e47187e19b8c2866d9ed9092aff1e99ba03b11 [formerly 21cbf7d271c0ec4c4d8486943478bb52944ede00] [formerly 8ca01a62b2a847865a6ff0da18ab6b81aeb5811e [formerly 0ca7cc4a1a3350013008565db44bae440d17eaf2]]
Former-commit-id: 816443607ed3e79d5e5149a1af04eaccf0fdbdec [formerly 806a4f2140e1a8a9556d0da0a5da0a26001e137b]
Former-commit-id: 1af10de211dc2ae616ed78e5d2db069ecfc350ef
Former-commit-id: 59b001fb896cbfe49bf5934ee709684b1e65ce7c

--- @client/saas_landing_page.coffee
+++ @client/saas_landing_page.coffee
@@ -65,7 +65,7 @@
       features()
       pricing()
       Contact()
-      story()
+      Story()

 HEADER_HEIGHT = 30
@@ -515,9 +515,9 @@
     bullet
       point_style: 'bullet'
       strong: "Foster considerate interactions"
-      body: """. The interface orients people to consider the topic,
-               rather than responding directly to each other. The design limits
-               opportunities for personal attacks on others.
+      body: """. The design orients people to consider the topic,
+               rather than responding directly to each other.
+               Opportunities for personal attacks on others are limited.
             """

     bullet
@@ -669,8 +669,8 @@
         style: _.extend {}, h1,
           marginBottom: 30

-        "Get in touch with us,"
-        BR null
+        # "Get in touch with us,"
+        # BR null
         "We'd love to get to know you!"

       DIV
@@ -749,564 +749,644 @@
           name: "Michael Toomim"
           email: "toomim@consider.it"

-story = ->
-  caption_text =
-    fontSize: 14
-    fontWeight: 400
-    lineHeight: 1.4
-    paddingTop: 5
-
-  story_link = _.extend {}, a, small_text,
-    textDecoration: 'none'
-    borderBottom: "1px solid #{logo_red}"
-
-  section_style =
-    marginBottom: 20
-    paddingTop: 10
-
-  DIV
-    id: 'story'
-    style:
-      marginTop: 60
-
-    H1
-      style: _.extend {}, h1,
-        marginBottom: 30
-
-      "Our story"
-
-    DIV
-      style: section_style
+Story = ReactiveComponent
+  displayName: 'Story'
+
+  render: ->
+
+    DIV
+      id: 'story'
+      style: _.extend {}, base_text,
+        marginTop: 60
+        marginBottom: 20
+
+
+      H1
+        style: _.extend {}, h1,
+          marginBottom: 30
+
+        "Our story"
+
+        if @local.picked_poison == 'fantasy'
+
+          ', conveyed as Fantasy'
+
+        else if @local.picked_poison == 'fact'
+          ', just the Facts'
+
+        if @local.picked_poison?
+          BR null
+          DIV
+            style:
+              fontSize: 14
+
+            "switch to "
+
+            A
+              style:
+                textDecoration: 'underline'
+                color: logo_red
+
+              onClick: =>
+                @local.picked_poison = if @local.picked_poison == 'fact' then 'fantasy' else 'fact'
+                save @local
+              if @local.picked_poison == 'fact' then 'fantasy' else 'facts'
+
+
+
+
+      if @local.picked_poison == 'fact'
+        @truth()
+
+      else if @local.picked_poison == 'fantasy'
+        @fiction()
+
+      else
+        DIV
+          style: _.extend {}, h2,
+            textAlign: 'center'
+
+          "Do you prefer "
+          A
+            style:
+              fontFamily: '"Courier New",Courier,"Lucida Sans Typewriter","Lucida Typewriter",monospace'
+              textDecoration: 'underline'
+              color: logo_red
+            onClick: =>
+              @local.picked_poison = 'fact'
+              save @local
+
+            "Fact"
+
+          " or "
+
+          A
+            style:
+              fontFamily: 'Papyrus,fantasy'
+              textDecoration: 'underline'
+              color: logo_red
+            onClick: =>
+              @local.picked_poison = 'fantasy'
+              save @local
+
+            "Fantasy"
+          "?"
+
+  fiction : ->
+
+    DIV null,

       DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .6 - 40
-          marginRight: 40
-          verticalAlign: 'top'
-
-
-
-        P
+        style: _.extend {}, base_text,
+          textAlign: 'center'
+
+
+        "Fictional story starring Benjamin Franklin coming soon. Sorry to disappoint!"
+
+  truth : ->
+    caption_text =
+      fontSize: 14
+      fontWeight: 400
+      lineHeight: 1.4
+      paddingTop: 5
+
+    story_link = _.extend {}, a, small_text,
+      textDecoration: 'none'
+      borderBottom: "1px solid #{logo_red}"
+
+    section_style =
+      marginBottom: 20
+      paddingTop: 10
+
+    DIV null,
+
+      DIV
+        style: section_style
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .6 - 40
+            marginRight: 40
+            verticalAlign: 'top'
+
+          P
+            style:
+              paddingBottom: 15
+
+            """
+            The idea for Consider.it was born on a cloudy Seattle morning
+            while Travis sat on the ground outside of a """
+            A
+              href: 'https://www.google.com/maps/place/Montlake+Bicycle+Shop/@47.639382,-122.302025,3a,75y,284.92h,78.85t/data=!3m4!1e1!3m2!1sqE4cgPSn9t_eCx1xr5HsEQ!2e0!4m2!3m1!1s0x549014c486e2f9c1:0x7b96d3f907c7f742'
+              style: story_link
+              'bike shop'
+
+            """
+            . He was feeling
+            down after spending a few dark hours reading hundreds of comments
+            on news articles about the Affordable Care Act. So much talking past
+            one another. Such wasted effort.
+            """
+
+          P
+            style:
+              paddingBottom: 15
+
+            """
+            Humanity's ability to listen and learn is ever so fragile, easily broken
+            by poor habits and flawed tools. The problem is not limited to online
+            comment boards. Email threads involving close colleagues and even face
+            to face conversations with loved ones can easily degenerate. Travis
+            knew he was no exception to the problem; but that day, at least, Travis
+            was happy to turn his frustration into a blueprint for improvement.
+            """
+
+        DIV
           style:
-            paddingBottom: 15
-
-          """
-          The idea for Consider.it was born on a cloudy Seattle morning
-          while Travis sat on the ground outside of a """
-          A
-            href: 'https://www.google.com/maps/place/Montlake+Bicycle+Shop/@47.639382,-122.302025,3a,75y,284.92h,78.85t/data=!3m4!1e1!3m2!1sqE4cgPSn9t_eCx1xr5HsEQ!2e0!4m2!3m1!1s0x549014c486e2f9c1:0x7b96d3f907c7f742'
-            style: story_link
-            'bike shop'
-
-          """
-          . He was feeling
-          down after spending a few dark hours reading hundreds of comments
-          on news articles about the Affordable Care Act. So much talking past
-          one another. Such wasted effort.
-          """
-
-        P
-          style:
-            paddingBottom: 15
-
-          """
-          Humanity's ability to listen and learn is ever so fragile, easily broken
-          by poor habits and flawed tools. The problem is not limited to online
-          comment boards. Email threads involving close colleagues and even face
-          to face conversations with loved ones can easily degenerate. Travis
-          knew he was no exception to the problem; but that day, at least, Travis
-          was happy to turn his frustration into a blueprint for improvement.
-          """
-
-      DIV
-        style:
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .4
-          marginTop: 10
-
-        IMG
-          style:
-            width: 400
-            display: 'block'
-            margin: 'auto'
-          src: asset('saas_landing_page/child.png')
-
-        DIV
-          style: caption_text
-
-          """
-          The Web is only 2.0 years old (3.0 claim some pundits).
-          It's social. It's getting good at speaking. But it’s not yet very
-          good at listening.
-          """
-
-
-
-    DIV
-      style: section_style
-
-
-
-
-      DIV
-        style:
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .4
-          marginTop: 10
-
-
-        IFRAME
-          width: 400
-          height: 243
-          src: "https://www.youtube.com/embed/jl1AsVM_8hk?modestbranding=1&showinfo=0&theme=dark&fs=1"
-          frameborder: "0"
-          allowfullscreen: true
-
-        DIV
-          style: caption_text
-
-          'Travis\' Phd defense, from December 2011. Here is the full '
-          A
-            style: _.extend {}, story_link, caption_text
-            href: 'https://dl.dropboxusercontent.com/u/3403211/papers/dissertation.pdf'
-            "dissertation"
-          ' for those of you with great '
-          SPAN
-            style:
-              textDecoration: 'line-through'
-            'foolishness'
-          ' fortitude.'
-
-      DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .6 - 40
-          verticalAlign: 'top'
-          marginLeft: 40
-
-
-        """
-        Travis was in a privileged position to spend time thinking about these
-        issues. He was pursuing a PhD in Computer Science at the University of
-        Washington, doing research grounded in the belief that we can
-        improve our capacity for collective action. He was supported by a generous
-        """
-        A
-          href: "http://www.nsf.gov/awardsearch/showAward?AWD_ID=0966929"
-          style: story_link
-          "National Science Foundation grant"
-        ' he had written with his advisor '
-        A
-          style: story_link
-          href: 'http://www.cs.washington.edu/people/faculty/borning'
-          'Alan Borning'
-        ' and political communication expert '
-        A
-          style: story_link
-          href: 'http://www.com.washington.edu/bennett/'
-          'Lance Bennett'
-
-        '. Previously he had spent a couple of years researching how contributors to the
-        world\'s greatest deliberative project, Wikipedia, '
-        A
-          style: story_link
-          href: 'http://dub.washington.edu/djangosite/media/papers/tmpZ77p1r.pdf'
-          'collaborate together'
-        ' and '
-        A
-          style: story_link
-          href: 'http://www.aaai.org/Papers/ICWSM/2008/ICWSM08-011.pdf'
-          'mediate'
-        ' '
-        A
-          style: story_link
-          href: 'https://www.cs.ubc.ca/~bestchai/papers/group07.pdf'
-          'conflict'
-
-        '. Travis was ready to channel this knowledge into invention.'
-
-
-
-    DIV
-      style: section_style
-
-      DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .45 - 40
-          marginRight: 40
-          verticalAlign: 'top'
-
-
-
-        P
-          style:
-            paddingBottom: 15
-
-          """
-          Travis found a kindred spirit in fellow graduate student Michael Toomim.
-          Michael's incisive and persistent feedback helped Travis transform his
-          abstract knowledge and ideas into concrete designs. They started collaborating
-          with each other on their respective projects. Bits and pieces of ideas
-          that Michael and Travis had kicked around before had suddenly coalesced in
-          that moment when Consider.it was born outside the bike shop.
-          """
-
-
-      DIV
-        style:
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .55
-          marginTop: 10
-          position: 'relative'
-          height: 380
-
-
-        DIV
-          style:
-            width: 350
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .4
+            marginTop: 10

           IMG
             style:
-              width: 350
+              width: 400
               display: 'block'
-            src: asset('saas_landing_page/consult.png')
+              margin: 'auto'
+            src: asset('saas_landing_page/child.png')

           DIV
             style: caption_text

-            'Late night collaboration!'
+            """
+            The Web is only 2.0 years old (3.0 claim some pundits).
+            It's social. It's getting good at speaking. But it’s not yet very
+            good at listening.
+            """
+
+
+
+      DIV
+        style: section_style

         DIV
           style:
-            position: 'absolute'
-            zIndex: 1
-            width: 208
-            top: 160
-            left: 315
-
-          IMG
-            src: asset('saas_landing_page/mike_talk.png')
-            style:
-              width: 208
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .4
+            marginTop: 10
+
+
+          IFRAME
+            width: 400
+            height: 243
+            src: "https://www.youtube.com/embed/jl1AsVM_8hk?modestbranding=1&showinfo=0&theme=dark&fs=1"
+            frameborder: "0"
+            allowfullscreen: true

           DIV
             style: caption_text

-            'Mike making a profound point that has been lost to time.'
-
-
-    DIV
-      style: section_style
-
-      DIV
-        style:
-          display: 'inline-block'
-          verticalAlign: 'middle'
-          width: SAAS_PAGE_WIDTH * .4
-          marginTop: 10
-
-        IMG
-          src: asset('saas_landing_page/sifp.jpg')
-          width: 400
-
-
-        DIV
-          style: caption_text
-
-          'Travis delivers the '
+            'Travis\' Phd defense, from December 2011. Here is the full '
+            A
+              style: _.extend {}, story_link, caption_text
+              href: 'https://dl.dropboxusercontent.com/u/3403211/papers/dissertation.pdf'
+              "dissertation"
+            ' for those of you with great '
+            SPAN
+              style:
+                textDecoration: 'line-through'
+              'foolishness'
+            ' fortitude.'
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .6 - 40
+            verticalAlign: 'top'
+            marginLeft: 40
+
+
+          """
+          Travis was in a privileged position to spend time thinking about these
+          issues. He was pursuing a PhD in Computer Science at the University of
+          Washington, doing research grounded in the belief that we can
+          improve our capacity for collective action. He was supported by a generous
+          """
           A
-            style: _.extend {}, story_link, caption_text
-            href: 'https://www.youtube.com/watch?v=RIUD4Ty2ZAE'
-            'winning talk'
-          """
-           at Social Innovation Fast Pitch. This excellent experience was an
-          awkward transitionary point in our history: straddling academia and the
-          private sector, while representing our non-profit partner in a pitch
-          competition.
-          """
-
-      DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .6 - 40
-          verticalAlign: 'top'
-          marginLeft: 40
-
-        P
-          style:
-            paddingBottom: 15
-
-          'Consider.it debuted in 2010 as the engine behind the '
+            href: "http://www.nsf.gov/awardsearch/showAward?AWD_ID=0966929"
+            style: story_link
+            "National Science Foundation grant"
+          ' he had written with his advisor '
           A
             style: story_link
-            href:'https://livingvotersguide.org'
-            "Living Voters Guide"
-
-          ', in partnership with the civic non-profit '
-          A
-            style: story_link
-            href: 'http://seattlecityclub.org'
-
-            'Seattle CityClub'
-
-          """
-          . This election season dialogue creates a space for citizens to express
-          their opinions about difficult ballot initiatives and to hear and learn from
-          the opinions of their peers. The research team demonstrated
-          that the technology encouraged voters to listen to both sides,
-          recognize points by people with whom they disagree and change their opinion
-          based on something they read. The voters guide has now weathered five election cycles, with
-          """
-
+            href: 'http://www.cs.washington.edu/people/faculty/borning'
+            'Alan Borning'
+          ' and political communication expert '
           A
             style: story_link
-            href: "http://blogs.seattletimes.com/monica-guzman/2012/10/27/seattle-library-fact-check-experiment-risky-but-valuable/"
-            'on-demand fact-checking'
-
-          ' delivered by Seattle Public Librarians since 2012.'
-
-        P
-          style:
-            paddingBottom: 15
-          """
-          Travis recognized that the technology had broad applicability
-          beyond civic engagement, and he generalized the technology.
-          """
-
-
-    DIV
-      style: section_style
-
-      DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .6 - 40
-          marginRight: 40
-          verticalAlign: 'top'
-
-        P
-          style:
-            paddingBottom: 15
-
-          """
-          As Travis and Michael approached the end of their PhD programs, they
-          decided to leave academia. It had become
-          clear to Travis and Mike that academia's emphasis on prototyping, papers,
-          and peer review limited how far and in what manner ideas could be
-          brought into the world. Instead, they would create their own
-          company/laboratory that supported the form of inquiry they felt would
-          maximize their contributions. We call this organization The
-          Invisible College.
-          """
-
-      DIV
-        style:
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .4
-          verticalAlign: 'top'
-          position: 'relative'
-
-
-
-        IFRAME
-          src: "https://player.vimeo.com/video/12116723?portrait=0&byline=0&title=0"
-          width: 300
-          height: 225
-          style:
-            display: 'block'
-
-
-        DIV
-          style: caption_text
-
-          A
-            style: _.extend {}, story_link, caption_text
-            href: 'http://engage.cs.washington.edu/reflect/'
-            'Reflect'
-
-          """
-           is Consider.it’s sister project. Reflect promotes
-          active listening in comment forums. We deployed it in Slashdot
-          and Wikimedia’s strategic planning process. The project is inactive
-          currently, though it is still dear to our hearts.
-          """
-          A
-            style: _.extend {}, story_link, caption_text
-            href: 'http://dub.washington.edu/djangosite/media/papers/tmptxCAiy.pdf'
-            'Learn more'
-          '.'
-
-    DIV
-      style: section_style
-
-      DIV
-        style:
-          display: 'inline-block'
-          verticalAlign: 'middle'
-          width: SAAS_PAGE_WIDTH * .4
-          marginTop: 10
-
-        IMG
-          src: asset('saas_landing_page/truth.png')
-          width: 400
-
-
-        DIV
-          style: caption_text
-
-          'Kevin standing up for Truth and Justice.'
-
-      DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .6 - 40
-          verticalAlign: 'top'
-          marginLeft: 40
-
-        P
-          style:
-            paddingBottom: 15
-
-          """
-          While wrapping up their academic pursuits, Mike and Travis
-          met Kevin Miniter. Kevin had just arrived in Seattle, eager for his next
-          adventure after running the campaign for the """
+            href: 'http://www.com.washington.edu/bennett/'
+            'Lance Bennett'
+
+          '. Previously he had spent a couple of years researching how contributors to the
+          world\'s greatest deliberative project, Wikipedia, '
           A
             style: story_link
-            href:'http://www.washingtonpost.com/wp-dyn/content/article/2011/02/02/AR2011020203272.html'
-            "first openly gay presidential candidate"
-
-          '. The icy mornings in New Hampshire and the afternoons driving a sound
-          truck blasting reggaeton through '
-          A
-            href: 'http://news.yahoo.com/blogs/ticket/ron-paul-topped-ran-fred-karger-puerto-rican-153210979.html'
+            href: 'http://dub.washington.edu/djangosite/media/papers/tmpZ77p1r.pdf'
+            'collaborate together'
+          ' and '
+          A
             style: story_link
-            'Puerto Rico'
-          """ had affirmed his love for
-          this country, but did little to cure his skepticism about its politics.
-          Kevin saw that there was a deeper problem of listening and critical
-          thinking behind our political strife. While trudging through the
-          startup world, he learned about Consider.it. He badgered Travis for
-          a meeting and asked the question that always starts an
-          adventure: "How can I help?"
-          """
+            href: 'http://www.aaai.org/Papers/ICWSM/2008/ICWSM08-011.pdf'
+            'mediate'
+          ' '
+          A
+            style: story_link
+            href: 'https://www.cs.ubc.ca/~bestchai/papers/group07.pdf'
+            'conflict'
+
+          '. Travis was ready to channel this knowledge into invention.'
+
+
+
+      DIV
+        style: section_style
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .45 - 40
+            marginRight: 40
+            verticalAlign: 'top'
+
+
+
+          P
+            style:
+              paddingBottom: 15
+
+            """
+            Travis found a kindred spirit in fellow graduate student Michael Toomim.
+            Michael's incisive and persistent feedback helped Travis transform his
+            abstract knowledge and ideas into concrete designs. They started collaborating
+            with each other on their respective projects. Bits and pieces of ideas
+            that Michael and Travis had kicked around before had suddenly coalesced in
+            that moment when Consider.it was born outside the bike shop.
+            """
+
+
+        DIV
+          style:
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .55
+            marginTop: 10
+            position: 'relative'
+            height: 380
+

-
-    DIV
-      style: section_style
+          DIV
+            style:
+              width: 350
+
+            IMG
+              style:
+                width: 350
+                display: 'block'
+              src: asset('saas_landing_page/consult.png')
+
+            DIV
+              style: caption_text
+
+              'Late night collaboration!'
+
+          DIV
+            style:
+              position: 'absolute'
+              zIndex: 1
+              width: 208
+              top: 160
+              left: 315
+
+            IMG
+              src: asset('saas_landing_page/mike_talk.png')
+              style:
+                width: 208
+
+            DIV
+              style: caption_text
+
+              'Mike making a profound point that has been lost to time.'
+

       DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .6 - 40
-          marginRight: 40
-          verticalAlign: 'top'
-
-        P
+        style: section_style
+
+        DIV
           style:
-            paddingBottom: 15
-
-          """
-          And here we are now!
-          As a startup, we are exploring different markets beyond civic
-          engagement. We're using Consider.it to
-          align the opinions of employees during organizational change
-          efforts, where Consider.it's ability to surface knowledge and
-          identify sticking points across a large group of people can help
-          the planning process. We're applying Consider.it to decision-making
-          and deliberation in decentralized online communities. Finally,
-          Consider.it's concentration on tradeoffs is a good fit for schools
-          teaching the new Common Core critical thinking skills."""
-        P
+            display: 'inline-block'
+            verticalAlign: 'middle'
+            width: SAAS_PAGE_WIDTH * .4
+            marginTop: 10
+
+          IMG
+            src: asset('saas_landing_page/sifp.jpg')
+            width: 400
+
+
+          DIV
+            style: caption_text
+
+            'Travis delivers the '
+            A
+              style: _.extend {}, story_link, caption_text
+              href: 'https://www.youtube.com/watch?v=RIUD4Ty2ZAE'
+              'winning talk'
+            """
+             at Social Innovation Fast Pitch. This excellent experience was an
+            awkward transitionary point in our history: straddling academia and the
+            private sector, while representing our non-profit partner in a pitch
+            competition.
+            """
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .6 - 40
+            verticalAlign: 'top'
+            marginLeft: 40
+
+          P
+            style:
+              paddingBottom: 15
+
+            'Consider.it debuted in 2010 as the engine behind the '
+            A
+              style: story_link
+              href:'https://livingvotersguide.org'
+              "Living Voters Guide"
+
+            ', in partnership with the civic non-profit '
+            A
+              style: story_link
+              href: 'http://seattlecityclub.org'
+
+              'Seattle CityClub'
+
+            """
+            . This election season dialogue creates a space for citizens to express
+            their opinions about difficult ballot initiatives and to hear and learn from
+            the opinions of their peers. The research team demonstrated
+            that the technology encouraged voters to listen to both sides,
+            recognize points by people with whom they disagree and change their opinion
+            based on something they read. The voters guide has now weathered five election cycles, with
+            """
+
+            A
+              style: story_link
+              href: "http://blogs.seattletimes.com/monica-guzman/2012/10/27/seattle-library-fact-check-experiment-risky-but-valuable/"
+              'on-demand fact-checking'
+
+            ' delivered by Seattle Public Librarians since 2012.'
+
+          P
+            style:
+              paddingBottom: 15
+            """
+            Travis recognized that the technology had broad applicability
+            beyond civic engagement, and he generalized the technology.
+            """
+
+
+      DIV
+        style: section_style
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .6 - 40
+            marginRight: 40
+            verticalAlign: 'top'
+
+          P
+            style:
+              paddingBottom: 15
+
+            """
+            As Travis and Michael approached the end of their PhD programs, they
+            decided to leave academia. It had become
+            clear to Travis and Mike that academia's emphasis on prototyping, papers,
+            and peer review limited how far and in what manner ideas could be
+            brought into the world. Instead, they would create their own
+            company/laboratory that supported the form of inquiry they felt would
+            maximize their contributions. We call this organization The
+            Invisible College.
+            """
+
+        DIV
           style:
-            paddingBottom: 15
-
-          'Thanks for listening to our story. If you like our story
-          and believe in our vision, '
-          A
-            href: 'mailto:admin@consider.it'
-            style: story_link
-            'send us a message'
-          '. Maybe we can collaborate! Or send us witty hate mail if you wish, we welcome adversaries.'
-
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .4
+            verticalAlign: 'top'
+            position: 'relative'
+
+
+
+          IFRAME
+            src: "https://player.vimeo.com/video/12116723?portrait=0&byline=0&title=0"
+            width: 300
+            height: 225
+            style:
+              display: 'block'
+
+
+          DIV
+            style: caption_text
+
+            A
+              style: _.extend {}, story_link, caption_text
+              href: 'http://engage.cs.washington.edu/reflect/'
+              'Reflect'
+
+            """
+             is Consider.it’s sister project. Reflect promotes
+            active listening in comment forums. We deployed it in Slashdot
+            and Wikimedia’s strategic planning process. The project is inactive
+            currently, though it is still dear to our hearts.
+            """
+            A
+              style: _.extend {}, story_link, caption_text
+              href: 'http://dub.washington.edu/djangosite/media/papers/tmptxCAiy.pdf'
+              'Learn more'
+            '.'

       DIV
-        style:
-          display: 'inline-block'
-          width: SAAS_PAGE_WIDTH * .4
-          verticalAlign: 'top'
-          position: 'relative'
-
-        IMG
-          src: asset('saas_landing_page/kev_mike.png')
+        style: section_style
+
+        DIV
           style:
+            display: 'inline-block'
+            verticalAlign: 'middle'
+            width: SAAS_PAGE_WIDTH * .4
+            marginTop: 10
+
+          IMG
+            src: asset('saas_landing_page/truth.png')
             width: 400
+
+
+          DIV
+            style: caption_text
+
+            'Kevin standing up for Truth and Justice.'
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .6 - 40
+            verticalAlign: 'top'
+            marginLeft: 40
+
+          P
+            style:
+              paddingBottom: 15
+
+            """
+            While wrapping up their academic pursuits, Mike and Travis
+            met Kevin Miniter. Kevin had just arrived in Seattle, eager for his next
+            adventure after running the campaign for the """
+            A
+              style: story_link
+              href:'http://www.washingtonpost.com/wp-dyn/content/article/2011/02/02/AR2011020203272.html'
+              "first openly gay presidential candidate"
+
+            '. The icy mornings in New Hampshire and the afternoons driving a sound
+            truck blasting reggaeton through '
+            A
+              href: 'http://news.yahoo.com/blogs/ticket/ron-paul-topped-ran-fred-karger-puerto-rican-153210979.html'
+              style: story_link
+              'Puerto Rico'
+            """ had affirmed his love for
+            this country, but did little to cure his skepticism about its politics.
+            Kevin saw that there was a deeper problem of listening and critical
+            thinking behind our political strife. While trudging through the
+            startup world, he learned about Consider.it. He badgered Travis for
+            a meeting and asked the question that always starts an
+            adventure: "How can I help?"
+            """
+
+
+      DIV
+        style: section_style
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .6 - 40
+            marginRight: 40
+            verticalAlign: 'top'
+
+          P
+            style:
+              paddingBottom: 15
+
+            """
+            And here we are now!
+            As a startup, we are exploring different markets beyond civic
+            engagement. We're using Consider.it to
+            align the opinions of employees during organizational change
+            efforts, where Consider.it's ability to surface knowledge and
+            identify sticking points across a large group of people can help
+            the planning process. We're applying Consider.it to decision-making
+            and deliberation in decentralized online communities. Finally,
+            Consider.it's concentration on tradeoffs is a good fit for schools
+            teaching the new Common Core critical thinking skills."""
+          P
+            style:
+              paddingBottom: 15
+
+            'Thanks for listening to our story. If you like our story
+            and believe in our vision, '
+            A
+              href: 'mailto:admin@consider.it'
+              style: story_link
+              'send us a message'
+            '. Maybe we can collaborate! Or send us witty hate mail if you wish, we welcome adversaries.'
+
+
+        DIV
+          style:
+            display: 'inline-block'
+            width: SAAS_PAGE_WIDTH * .4
+            verticalAlign: 'top'
             position: 'relative'
-            zIndex: 1
-
-        DIV
-          style: caption_text
-
-          "Kevin and Mike in their natural habitat."
-
-
-    DIV
-      style: section_style
-
-
-      DIV
-        style: _.extend {}, small_text,
-          display: 'inline-block'
-          verticalAlign: 'top'
-
-        DIV null,
-          """
-          Published research about Consider.it:
-          """
-          UL
-            style:
-              listStyle: 'none'
-
-            LI
+
+          IMG
+            src: asset('saas_landing_page/kev_mike.png')
+            style:
+              width: 400
+              position: 'relative'
+              zIndex: 1
+
+          DIV
+            style: caption_text
+
+            "Kevin and Mike in their natural habitat."
+
+
+      DIV
+        style: section_style
+
+
+        DIV
+          style: _.extend {}, small_text,
+            display: 'inline-block'
+            verticalAlign: 'top'
+
+          DIV null,
+            """
+            Published research about Consider.it:
+            """
+            UL
               style:
-                paddingTop: 5
-
-              A
-                style:  _.extend {}, story_link
-                href: "http://dub.washington.edu/djangosite/media/papers/kriplean-cscw2012.pdf"
-                'Supporting Reflective Public Thought with Consider.it'
-              DIV
-                style: _.extend {}, small_text,
-                  fontSize: 16
-                '2012 ACM Conference on Computer Supported Cooperative Work'
-
-
-            LI
-              style:
-                paddingTop: 15
-              A
-                style:  _.extend {}, story_link
-                href: "https://dl.dropboxusercontent.com/u/3403211/papers/jitp.pdf"
-                'Facilitating Diverse Political Engagement'
-              DIV
-                style: _.extend {}, small_text,
-                  fontSize: 16
-                'Journal of Information Technology & Politics, Volume 9, Issue 3'
-
-            LI
-              style:
-                paddingTop: 15
-              A
-                style:  _.extend {}, story_link
-                href: "http://homes.cs.washington.edu/~borning/papers/kriplean-cscw2014.pdf"
-                'On-demand Fact-checking in Public Dialogue'
-
-              DIV
-                style: _.extend {}, small_text,
-                  fontSize: 16
-                '2014 ACM Conference on Computer Supported Cooperative Work'
+                listStyle: 'none'
+
+              LI
+                style:
+                  paddingTop: 5
+
+                A
+                  style:  _.extend {}, story_link
+                  href: "http://dub.washington.edu/djangosite/media/papers/kriplean-cscw2012.pdf"
+                  'Supporting Reflective Public Thought with Consider.it'
+                DIV
+                  style: _.extend {}, small_text,
+                    fontSize: 16
+                  '2012 ACM Conference on Computer Supported Cooperative Work'
+
+
+              LI
+                style:
+                  paddingTop: 15
+                A
+                  style:  _.extend {}, story_link
+                  href: "https://dl.dropboxusercontent.com/u/3403211/papers/jitp.pdf"
+                  'Facilitating Diverse Political Engagement'
+                DIV
+                  style: _.extend {}, small_text,
+                    fontSize: 16
+                  'Journal of Information Technology & Politics, Volume 9, Issue 3'
+
+              LI
+                style:
+                  paddingTop: 15
+                A
+                  style:  _.extend {}, story_link
+                  href: "http://homes.cs.washington.edu/~borning/papers/kriplean-cscw2014.pdf"
+                  'On-demand Fact-checking in Public Dialogue'
+
+                DIV
+                  style: _.extend {}, small_text,
+                    fontSize: 16
+                  '2014 ACM Conference on Computer Supported Cooperative Work'


Teamwork: Creating playground teamwork before waiting first one's viewAppended.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
@@ -49,8 +49,7 @@
       ]

     if options.environment
-      @teamwork.on "viewAppended", (panel) =>
-        @handleEnvironmentSelection options.environment
+      @handleEnvironmentSelection options.environment

   showToolsModal: (panel, workspace) ->
     modal       = new KDModalView


Add "chosen:update" event handler to update chosen after the target select has been changed manualy.

--- vendor/assets/javascripts/chosen/chosen.js.coffee
+++ vendor/assets/javascripts/chosen/chosen.js.coffee
@@ -18,40 +18,43 @@

     @build()
     @load()
-    @complete()
-
-  complete: ->
+
     Chosen.pool.push(@)

+    @set_state()
+
+    @$target.addClass("chosen").after(@$container).bind("chosen:update", (evt) => @set_state())
+
+  destroy: ->
+    @$target.unbind "chosen:update"
+
+    @unbind_events()
+
+    delete @$body
+
+    @$container.remove()
+    @$dropdown.remove()
+    @$target.removeData("chosen").removeClass("chosen").show()
+
+    delete @$container
+    delete @$dropdown
+    delete @$target
+    delete @target
+
+    @parser.destroy()
+
+    delete @parser
+
+    index = Chosen.pool.indexOf(@)
+    Chosen.pool.splice(index, 1) if index >= 0
+
+  set_state: ->
     if @target.disabled
       @disabled = false
       @disable()
     else
       @disabled = true
       @enable()
-
-    @$target.addClass("chosen").after(@$container)
-
-  destroy: ->
-    @unbind_events()
-
-    delete @$body
-
-    @$container.remove()
-    @$dropdown.remove()
-    @$target.removeData("chosen").removeClass("chosen").show()
-
-    delete @$container
-    delete @$dropdown
-    delete @$target
-    delete @target
-
-    @parser.destroy()
-
-    delete @parser
-
-    index = Chosen.pool.indexOf(@)
-    Chosen.pool.splice(index, 1) if index >= 0

   reset: ->
     @deselect_all()


Add height and width support to #moveTo() method.

--- src/packer/Packer.coffee
+++ src/packer/Packer.coffee
@@ -230,12 +230,16 @@
   #
   #
   ###
-  moveTo: (id, row, col) ->
+  moveTo: (id, row, col, height, width) ->
     shifted = {}

     [row, col] = @constrain id, row, col

     item = @remove id
+
+    if height then item.height = height
+
+    if width then item.width = width

     overlappingItemIds = @itemsWithin(item.height, item.width, row, col)


Update index.coffee

Update index.coffee

Forget to indent...

--- src/private/index.coffee
+++ src/private/index.coffee
@@ -1,14 +1,14 @@
 $ ()->
   $('.pause-btn').click ()->
     if game?
-    if $('.pause-btn').text() is 'Pause'
-      game.pause()
-      $('.pause-btn').text('Resume')
-      $('#game-canvas').animate {opacity:0.2}
-    else
-      game.resume()
-      $('.pause-btn').text('Pause')
-      $('#game-canvas').animate {opacity:1}
+      if $('.pause-btn').text() is 'Pause'
+        game.pause()
+        $('.pause-btn').text('Resume')
+        $('#game-canvas').animate {opacity:0.2}
+      else
+        game.resume()
+        $('.pause-btn').text('Pause')
+        $('#game-canvas').animate {opacity:1}
     return

   $('.play-btn').click ()->


`keyprefix` is now an instance var.

`keyprefix` is now an instance var.

--- src/redis-storage.coffee
+++ src/redis-storage.coffee
@@ -3,7 +3,7 @@

   # Create a storage module that uses the provided Redis connection.
   # Keypreifx used to isolate stored markov transitions from other keys in the database.
-  constructor: (@client, @keyprefix="markov:") ->
+  constructor: (@client, @keyprefix = "markov:") ->

   # Uniformly and unambiguously convert an array of Strings and nulls into a valid
   # Redis key. Uses a length-prefixed encoding.
@@ -13,7 +13,7 @@
   _encode: (key) ->
     encoded = for part in key
       if part then "#{part.length}#{part}" else "0"
-    keyprefix + encoded.join('')
+    @keyprefix + encoded.join('')

   # Record a transition within the model. "transition.from" is an array of Strings and
   # nulls marking the prior state and "transition.to" is the observed next state, which


fix settings menu on subgroup

fix settings menu on subgroup

--- vue/src/shared/models/group_model.coffee
+++ vue/src/shared/models/group_model.coffee
@@ -57,7 +57,10 @@
     @hasMany 'allDocuments', from: 'documents', with: 'groupId', of: 'id'
     @hasMany 'subgroups', from: 'groups', with: 'parentId', of: 'id', orderBy: 'name'
     @belongsTo 'parent', from: 'groups'
-
+    @belongsTo 'creator', from: 'users'
+
+
+  author: -> @creator()
   isBlank: ->
     @description == '' or @description == null or @description == '

'


Self-check before user check

Self-check before user check

--- src/scripts/hound.coffee
+++ src/scripts/hound.coffee
@@ -26,11 +26,15 @@
   #     Don’t forget to check out~

   hound = (slackuser, channel, forceHound=false) ->
+    if robot.name is slackuser.name
+      Logger.log 'Caught myself, don\'t hound the hound.'
+      return
+
     user = Organization.getUserBySlackName slackuser.name
     if not user
       Logger.log "#{slackuser.name} couldn't be found while attempting to hound"
       return
-
+
     if user.settings.shouldHound
       if not channel.private
         channel.private = !!channel.is_im or !!channel.is_group
@@ -40,9 +44,6 @@
       else if channel.private or
               channel.name in Organization.exemptChannels
         Logger.warn "##{channel.name} is not an appropriate hounding channel"
-        return
-      else if robot.name is slackuser.name
-        Logger.log 'Caught myself, don\'t hound the hound.'
         return

       now = moment.tz TIMEZONE


add console.log

add console.log

--- src/scripts/index.coffee
+++ src/scripts/index.coffee
@@ -4,3 +4,6 @@
 window.addEventListener 'DOMContentLoaded', ->
   rootElement = React.createElement AppView
   React.render rootElement, document.body
+  setTimeout ->
+    console.log '%c   ([:-P   ', 'font-size: 128px; font-family: "Fredoka One"'
+  , 1000


expose config to views

expose config to views

--- src/server/server.coffee
+++ src/server/server.coffee
@@ -147,6 +147,7 @@
   opts.user = req.user
   opts.home = false unless opts.home
   opts.h = HELPERS
+  opts.config = config
   res.render view, opts

 app = express.createServer(


now plots are scaleable

now plots are scaleable

--- src/js/bokeh.coffee
+++ src/js/bokeh.coffee
@@ -519,8 +519,13 @@
   model : GridPlotContainer

 class PlotView extends Continuum.DeferredParent
+
+  default_options : {
+    scale:1.0
+  }
+
   initialize : (options) ->
-    super(options)
+    super(_.defaults(options, @default_options))
     @renderers = {}
     @axes = {}
     @tools = {}
@@ -611,14 +616,21 @@
       .attr('fill', @mget('background_color'))
       .attr('stroke', @model.get('foreground_color'))
       .attr('width', @mget('width'))
-      .attr("height", @mget('height'))
+      .attr("height",  @mget('height'))
+
+
     @tag_d3('plotwindow')
-        .attr('height', @mget('height'))
-        .attr('width', @mget('width'))
-    node.attr('width', @mget('outerwidth')).attr("height", @mget('outerheight'))
+      .attr('width',  @mget('width'))
+      .attr('height', @mget('height'))
+
+    node.attr("width", @options.scale * @mget('outerwidth'))
+      .attr('height', @options.scale * @mget('outerheight'))
     #svg puts origin in the top left, we want it on the bottom left
-    @tag_d3('plot').attr('transform',
-      _.template('translate({{s}}, {{s}})', {'s' : @mget('border_space')}))
+    #
+    trans_string = "scale(#{@options.scale}, #{@options.scale})"
+    trans_string += "translate(#{@mget('border_space')}, #{@mget('border_space')})"
+
+    @tag_d3('plot').attr('transform', trans_string)

   render : () ->
     super()


Visual/edit modes: for "$", advance to real end of line.

--- content_scripts/mode_visual_edit.coffee
+++ content_scripts/mode_visual_edit.coffee
@@ -1,8 +1,7 @@

 # To do:
-# - handle edit-mode losing the focus
+# - edit-mode losing the focus
 # - line-visual mode
-# - definition of a line.
 # - better implementation of `o`
 # - caret mode
 # - yy
@@ -122,7 +121,7 @@
       if @alterMethod == "move" and not locked
         locked = true
         @paste (text) =>
-          result = func()
+          result = func text
           @copy text
           locked = false
           result
@@ -131,7 +130,25 @@

   # Run a movement command.  Return true if the selection changed, false otherwise.
   runMovement: (movement) ->
-    @selectionChanged => @selection.modify @alterMethod, movement.split(" ")...
+    @selectionChanged =>
+      movement = movement.split(" ")
+      if movement[0] == forward and movement[1] == "lineboundary" and @alterMethod == "extend"
+        # Special case.  When we move forward to a line boundary, we're often just moving to the point at
+        # which the text wraps, which is of no particular use.  Instead, we keep advancing until we find a
+        # newline character.  This trailing newline character is included in the selection.
+        atEndOfLine = =>
+          if @selectionChanged(=> @selection.modify @alterMethod, "forward", "character")
+            text = @selection.toString()
+            console.log text[text.length - 1] != "\n", text.length, text
+            text[text.length - 1] == "\n"
+          else
+            true
+
+        @selection.modify @alterMethod, movement...
+        @selection.modify @alterMethod, movement... while not atEndOfLine()
+      else
+        # Normal case.
+        @selection.modify @alterMethod, movement...

   # Run a sequence of movements; bail immediately on any failure to change the selection.
   runMovements: (movements) ->
@@ -188,7 +205,6 @@
               @keyQueue = ""
               @selection = window.getSelection()

-              # If there's matching a command *and* a matching movement, then choose the command.
               if @commands[keyChar]
                 @commands[keyChar].call @
                 @scrollIntoView()
@@ -244,14 +260,10 @@
     @copy @yankedText if @yankedText

   selectLine: ->
-    for direction in [ forward, backward ]
+    direction = @getDirection()
+    for direction in [ @opposite[direction], direction ]
       @runMovement "#{direction} lineboundary"
       @swapFocusAndAnchor()
-    Movement.lastLineSelection = @selection.toString()
-    console.log "lastLineSelection:", Movement.lastLineSelection
-
-  yankLine: ->
-    @selectLine()

   # Adapted from: http://roysharon.com/blog/37.
   # I have no idea how this works (smblott, 2015/1/22).
@@ -305,14 +317,12 @@
       underEditMode: false
     super extend defaults, options

-    extend @commands,
-      "y": @yank
-      "Y": @yankLine
+    extend @commands, "y": @yank

     if @options.underEditMode
       extend @commands,
-        "d": => @yank deleteFromDocument: true
-        "c": => @yank deleteFromDocument: true; enterInsertMode()
+        "d": @yank
+        "c": -> @yank(); enterInsertMode()

 class VisualModeForEdit extends VisualMode
   constructor: (options = {}) ->
@@ -360,15 +370,17 @@
     @countPrefix = ""

   pasteClipboard: (direction) ->
-    @protectClipboard =>
-      @paste (text) =>
-        if text
-          lineOriented = text == Movement.lastLineSelection
-          if lineOriented
-            @runMovement "#{direction} lineboundary"
-            text = if direction == forward then "\n" + text else text + "\n"
-          DomUtils.simulateTextEntry @element, text
-          @runMovement "backward line" if lineOriented
+    @protectClipboard (text) =>
+      if text
+        # We use the heuristic that the paste is line oriented if the last character is a newline.  This is
+        # consistent with the way runMovement selects text in visual mode for "forward lineboundary".
+        lineOriented = /\n$/.test text
+        if lineOriented
+          @runMovement "#{direction} lineboundary"
+          @runMovement "#{direction} character" if direction == forward
+        DomUtils.simulateTextEntry @element, text
+        # Slow!  Expensive!  Better way?
+        @runMovement "backward character" for [0...text.length] if lineOriented

   openLine: (direction) ->
     @runMovement "#{direction} lineboundary"


fix for import progress bar not showing; fixes 324

--- app/assets/javascripts/admin/views/dataset/dataset_list_item_view.coffee
+++ app/assets/javascripts/admin/views/dataset/dataset_list_item_view.coffee
@@ -92,7 +92,7 @@
       ).done( (responseJSON) =>
           if responseJSON.status == "inProgress"
             @ui.importLink.hide()
-            @ui.progressbarContainer.show()
+            @ui.progressbarContainer.removeClass("hide")
             @updateProgress()
       )


string split

string split

--- app/client/session/signup.coffee
+++ app/client/session/signup.coffee
@@ -42,8 +42,8 @@
       if err?
         console.log err.error
         if err.error == 403
-          FlashMessages.sendError
-            "E-mail-adresse eller brugernavn allerede registreret"
+          FlashMessages.sendError "E-mail-adresse eller"+
+          "brugernavn allerede registreret"
         else
           FlashMessages.sendError "Kunne ikke oprette bruger"
           console.log err


Tests: Case descriptions for roundrobin

Tests: Case descriptions for roundrobin

--- spec/01transport.coffee
+++ spec/01transport.coffee
@@ -256,6 +256,13 @@
               clients.sender.sendToQueue outQueue2, {data: 'ident'}, (err) ->
                 chai.expect(err).to.not.exist

+  describe 'Roundrobin binding', ->
+    describe 'data is ACKed', ->
+      it 'should be sent to only one consumer'
+      it 'should not be sent do deadletter queue'
+
+    describe 'data is NACKed', ->
+      it 'should be sent to deadletter queue'

 describe 'Transport', ->
   Object.keys(transports).forEach (type) =>


Add specs for variation sequences

Add specs for variation sequences

--- spec/editor-spec.coffee
+++ spec/editor-spec.coffee
@@ -3343,7 +3343,7 @@
         editor2.destroy()
         expect(editor.shouldPromptToSave()).toBeTruthy()

-  describe "when the edit session contains surrogate pair characters", ->
+  describe "when the editor text contains surrogate pair characters", ->
     it "correctly backspaces over them", ->
       editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97')
       editor.moveToBottom()
@@ -3384,6 +3384,47 @@
       editor.moveLeft()
       expect(editor.getCursorBufferPosition()).toEqual [0, 0]

+  describe "when the editor contains variation sequence character pairs", ->
+    it "correctly backspaces over them", ->
+      editor.setText('\u2714\uFE0E\u2714\uFE0E\u2714\uFE0E')
+      editor.moveToBottom()
+      editor.backspace()
+      expect(editor.getText()).toBe '\u2714\uFE0E\u2714\uFE0E'
+      editor.backspace()
+      expect(editor.getText()).toBe '\u2714\uFE0E'
+      editor.backspace()
+      expect(editor.getText()).toBe ''
+
+    it "correctly deletes over them", ->
+      editor.setText('\u2714\uFE0E\u2714\uFE0E\u2714\uFE0E')
+      editor.moveToTop()
+      editor.delete()
+      expect(editor.getText()).toBe '\u2714\uFE0E\u2714\uFE0E'
+      editor.delete()
+      expect(editor.getText()).toBe '\u2714\uFE0E'
+      editor.delete()
+      expect(editor.getText()).toBe ''
+
+    it "correctly moves over them", ->
+      editor.setText('\u2714\uFE0E\u2714\uFE0E\u2714\uFE0E\n')
+      editor.moveToTop()
+      editor.moveRight()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 2]
+      editor.moveRight()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 4]
+      editor.moveRight()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 6]
+      editor.moveRight()
+      expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+      editor.moveLeft()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 6]
+      editor.moveLeft()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 4]
+      editor.moveLeft()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 2]
+      editor.moveLeft()
+      expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+
   describe ".setIndentationForBufferRow", ->
     describe "when the editor uses soft tabs but the row has hard tabs", ->
       it "only replaces whitespace characters", ->


minor update

minor update

--- test-at.coffee
+++ test-at.coffee
@@ -21,6 +21,7 @@

   it 'should process flow.at without root', ->
+    window.x = undefined
     path1 = flow.at('x.y')
     expect(path1()).to.equal undefined
     path1(1)


Simplify about

Simplify about

--- views/components/settings/parts/others.cjsx
+++ views/components/settings/parts/others.cjsx
@@ -13,7 +13,6 @@

poi 基于 Electron 和 React.js 开发，是一个开源的跨平台舰队 Collection 浏览器。poi 的游戏行为与 Chrome 一致，原则上不提供任何影响收发包的功能。poi 仅提供基本浏览器功能，扩展功能请等待插件开发。

开发讨论与意见交流群: 378320628

GitHub： https://github.com/yudachi/poi

-
插件开发文档： https://github.com/yudachi/poi/blob/master/docs/plugin-cn.md

         </Col>
       </Grid>
       <Divider text="Contributors" />


Remove handler event

Remove handler event

--- spec/keymap-spec.coffee
+++ spec/keymap-spec.coffee
@@ -512,8 +512,8 @@

   describe "events", ->
     it "emits `key-binding-matched` when a key binding matches an event", ->
-      handler = jasmine.createSpy('key-binding-matched handler')
-      keymap.on 'key-binding-matched handler', handler
+      handler = jasmine.createSpy('key-binding-matched')
+      keymap.on 'key-binding-matched', handler
       keymap.addKeyBindings "test",
         "body":
           "ctrl-x": "used-command"


fix var name of TextData. rename row to rawData

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -139,8 +139,8 @@
     callback(vimState, new VimEditor(vimState))

 class TextData
-  constructor: (@row) ->
-    @lines = @row.split("\n")
+  constructor: (@rawData) ->
+    @lines = @rawData.split("\n")

   getLines: (lines, {chomp}={}) ->
     chomp ?= false
@@ -151,7 +151,7 @@
       text + "\n"

   getRaw: ->
-    @row
+    @rawData

 class VimEditor
   constructor: (@vimState) ->


Longer spec timeout

Longer spec timeout

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -85,7 +85,7 @@
   throw new Error("Not a spy") unless object[methodName].originalValue?
   object[methodName] = object[methodName].originalValue

-jasmine.getEnv().defaultTimeoutInterval = 500
+jasmine.getEnv().defaultTimeoutInterval = 1000

 window.keyIdentifierForKey = (key) ->
   if key.length > 1 # named key


Some CS tests

Some CS tests

--- test/cs_spec.coffee
+++ test/cs_spec.coffee
@@ -43,3 +43,73 @@
       fn = aether.createFunction()
       expect(aether.canTranspile(code)).toEqual true
       expect(aether.run()).toEqual 8 # fail
+
+  describe "Basics", ->
+    aether = new Aether language: "coffeescript"
+    it "Simple For", ->
+      code = """
+      count = 0
+      count++ for num in [1..10]
+      return count
+      """
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual 10
+
+    it "Simple While", ->
+      code = """
+      count = 0
+      count++ until count is 100
+      return count
+      """
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual 100
+
+    it "Should Map", ->
+      code = "return (num for num in [10..1])"
+
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
+
+    it "Should Map properties", ->
+      code = '''
+      yearsOld = max: 10, ida: 9, tim: 11
+      ages = for child, age of yearsOld
+        #{child} is #{age}'
+      return ages
+      '''
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual ["max is 10", "ida is 9", "tim is 11"]
+
+    it "Should compile empty function", ->
+      code = """
+        func = () ->
+        return typeof func
+        """
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual 'function'
+
+    it "Should compile objects", ->
+      code = """
+        singers = {Jagger: 'Rock', Elvis: 'Roll'}
+        return singers
+      """
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual ({Jagger: 'Rock', Elvis: 'Roll'})
+
+    it "Should compile classes", ->
+      code = """
+      class MyClass
+        test: ->
+          return 1000
+      myClass = new MyClass()
+      return myClass.test()
+      """
+      aether.transpile(code)
+      expect(aether.canTranspile(code)).toEqual true
+      expect(aether.run()).toEqual 1000


working on problems with async calls

working on problems with async calls

--- testify.coffee
+++ testify.coffee
@@ -1,53 +1,57 @@
+assert = require "assert"
 colors = require "colors"
 EventEmitter = require("events").EventEmitter

 module.exports = Testify =
   test: (name, fn) ->
-    if fn
-      context = new Context(name)
-      Testify.count++
-      context.emitter.once "done", ->
-        Testify.count--
+    context = new Context(name)
+    context.emitter.once "done", ->
+      console.log "suite done"
+      process.nextTick ->
         context.report()
-        if Testify.count == 0
-          Testify.emitter.emit("done")
-      fn(context)
-    else
-      process.once "exit", ->
-        console.log "Unimplemented: #{name}".cyan
+        Testify.emitter.emit("done")
+    fn(context)
+  emitter: new EventEmitter()
   count: 0
-  emitter: new EventEmitter()
   once: (args...) ->
     Testify.emitter.once(args...)

 process.on "exit", ->
   if Testify.count != 0
-    console.log "Not all tests ran".yellow
+    console.log "#{Testify.count} async tests did not complete.".yellow

 class Context
   constructor: (@name, @parent) ->
+    @emitter = new EventEmitter
     if @parent
       @level = @parent.level + 1
     else
       @level = 0
-
     @failed = false
     @children = []
+    @assert = {}
+    @finished = false
+    for name, method of assert
+      @assert[name] = @wrap_assertion(name, method)

-    @emitter = new EventEmitter
+  wrap_assertion: (name, fn) ->
+    (args...) =>
+      try
+        fn(args...)
+      catch error
+        @fail(error)

   report: ->
-    console.log()
-    top_level =
+    suite =
       name: "Passed: #{@name}"
       level: @level
       failed: @failed
-    if top_level.failed
-      top_level.name = "Failed: #{@name}"
-    result = [top_level]
+    if suite.failed
+      suite.name = "Failed: #{@name}"
+    result = [suite]

     for context in @children
-      context.collect_output(result)
+      context.collect(result)

     for test in result
       level = test.level
@@ -55,25 +59,20 @@
       indent = indent + "    " while level--

       if test.failed == false
-        console.log indent + test.name.green
+        line = indent + test.name.green
       else if test.failed == true
-        console.log indent + "#{test.name} - subtest/s failed".red
-      else if test.failed.name == "AssertionError"
-        console.log indent + "#{test.name} - #{test.failed}".red
+        line = indent + "#{test.name} - subtest failures".red
+      else if test.failed.constructor == String || test.failed.name == "AssertionError"
+        line = indent + "#{test.name} - #{test.failed}".red
       else
-        console.log indent + "#{test.name} - #{test.failed}".yellow
+        line = indent + "#{test.name} - #{test.failed}".yellow
+      console.log(line)
+    console.log()

-
-
-  collect_output: (array=[]) ->
-    array.push
-      level: @level
-      name: @name
-      failed: @failed
+  collect: (array=[]) ->
+    array.push(@)
     for context in @children
-      context.collect_output(array)
-
-
+      context.collect(array)

   test: (description, fn) ->
     context = new Context(description, @)
@@ -91,8 +90,9 @@

   async: (fn) ->
     Testify.count++
-    @emitter.once "done", =>
+    @emitter.once "done", ->
       Testify.count--
+
     try
       fn(@)
     catch error
@@ -103,33 +103,16 @@
     @failed = error
     if @parent
       @parent.fail(true)
+    @done()

   done: () ->
-    @emitter.emit("done")
+    @finished = true
+    flag = @children.every (child) -> child.finished
+    if flag
+      @emitter.emit("done")
+    else
+      console.log @children

-  success: (description) ->
-    console.log description.green
+  pass: ->
+    @done()

-  assert: require("assert")
-
-  pass: (description) ->
-    @emitter.once "done", => @success("  * " + description)
-
-  #fail: (description, error) ->
-    #if !error
-      #console.log "Failed: #{@name}\n  => #{description}".red.bold
-    #else
-      #if error.constructor == String || error.name == "AssertionError"
-        #console.log "Failed: #{@name}\n  * #{description} => #{error}".red.bold
-      #else
-        #console.log colors.yellow("Error: '#{description}' => #{error}")
-      # split,slice to remove the error message from the stack trace
-      #if error.stack
-        #console.log colors.white(error.stack.split("\n").slice(1).join("\n"))
-
-  #fail: (error) ->
-    #if error.constructor == String || error.name == "AssertionError"
-      #console.log "Failed: #{@name}\n => #{error}".red.bold
-    #else
-      #console.log colors.yellow("Error: '#{description}' => #{error}")
-


Fixed: RRule should be in jade globals

Fixed: RRule should be in jade globals

--- client/config.coffee
+++ client/config.coffee
@@ -10,7 +10,7 @@
             options:
                 indentation: value: 4, level: 'error'
         jade:
-            globals: ['t', 'moment']
+            globals: ['t', 'moment','RRule']

     conventions:
         vendor:  /(vendor)|(tests)(\/|\\)/ # do not wrap tests in modules


Implemented queue using Deps.autorun. Work in progress

--- client/import.coffee
+++ client/import.coffee
@@ -17,86 +17,124 @@
 DRAGGING_OVER_DOM = false
 fileBuffer = []

+currentUploadDocument = null
+
+uploadFetchOne = ->
+  return currentUploadDocument if currentUploadDocument
+  document = ImportingFile.documents.findOne
+    finished: false
+    errored: false
+    sha256:
+      $exists: true
+  currentUploadDocument = document
+  return currentUploadDocument
+
+uploadMarkComplete = (params) ->
+  console.log "uploadMarkComplete called "
+  ImportingFile.documents.update currentUploadDocument._id,
+    $set: params
+  delete fileBuffer[currentUploadDocument._id]
+  currentUploadDocument = null
+
 # observing files with computed checksums
-checksummed = ImportingFile.documents.find
-  sha256:
-    $exists: true
-checksummed.observe
-  added: (document) ->
-    id = document._id
-    file = fileBuffer[id].file
-    fileContent = fileBuffer[id].fileContent
-
-    console.log "Creating publication for file " + document.name
-    Meteor.call 'create-publication', document.name, document.sha256, (error, result) ->
-      if error
-        ImportingFile.documents.update id,
-          $set:
-            errored: true
-            status: error.toString()
-        return
-
-      if result.already
-        ImportingFile.documents.update id,
-          $set:
-            finished: true
-            status: "File already imported"
-            publicationId: result.publicationId
-        return
-
-      if result.verify
-        console.log "Verifying file " + document.name
-        verifyFile file, fileContent, result.publicationId, result.samples
-      else
-        console.log "Uploading file " + document.name
-        uploadFile file, result.publicationId
-
-      # remove file from buffer
-      delete fileBuffer[id]
-
-# observing files that are ready for checksum computation
-preprocessed = ImportingFile.documents.find
-  preprocessed:
-    $exists: true
-preprocessed.observe
-  added: (document) ->
-    id = document._id
-    fileContent = fileBuffer[id].fileContent
-
-    console.log "Computing checksum for " + document.name
-    ImportingFile.documents.update id,
-      $set:
-        status: "Computing checksum"
-
-    hash = new Crypto.SHA256
-      onProgress: (progress) ->
-        #TODO: update progressbar
-    hash.update fileContent, (error, result) ->
-      #TODO: handle errors
-      if error
-        console.log "Import error: " + error.message
-    hash.finalize (error, result) ->
-      #TODO: handle errors
-      if error
-        console.log "Import error: " + error.message
-
-      alreadyImporting = ImportingFile.documents.findOne
-        sha256: result
-
-      if alreadyImporting
-        ImportingFile.documents.update id,
-          $set:
-            finished: true
-            status: "File is already importing"
-            # publicationId might not yet be available, but let's try
-            publicationId: alreadyImporting.publicationId
-        return
-
-      console.log "Setting sha256 value"
-      # so that observer can do it's work
-      ImportingFile.documents.update id,
-        $set:
-          sha256: result
+Deps.autorun ->
+  return if currentUploadDocument
+  document = uploadFetchOne()
+  return unless document
+
+  console.log "Got into upload autorun " + document.name
+
+  id = document._id
+  file = fileBuffer[id].file
+  fileContent = fileBuffer[id].fileContent
+
+  console.log "Creating publication for file " + document.name
+  Meteor.call 'create-publication', document.name, document.sha256, (error, result) ->
+    if error
+      uploadMarkComplete
+        errored: true
+        status: error.toString()
+      return
+
+    if result.already
+      uploadMarkComplete
+        finished: true
+        status: "File already imported"
+        publicationId: result.publicationId
+      return
+
+    if result.verify
+      console.log "Verifying file " + document.name
+      verifyFile file, fileContent, result.publicationId, result.samples
+    else
+      console.log "Uploading file " + document.name
+      uploadFile file, result.publicationId
+
+
+currentChecksumDocument = null
+
+hashFetchOne = ->
+  document = ImportingFile.documents.findOne
+    preprocessed: true
+    sha256:
+      $exists: false
+  console.log document
+  currentChecksumDocument = document
+  return currentChecksumDocument
+
+hashMarkComplete = (params) ->
+  console.log "Called hashMarkComplete"
+  # setting hash value in collection so that autorun can move on
+  ImportingFile.documents.update currentChecksumDocument._id,
+    $set: params
+  currentChecksumDocument = null
+
+# observing documents ready for checksum computation
+Deps.autorun ->
+  return if currentChecksumDocument
+  document = hashFetchOne()
+  return unless document
+
+  console.log "Got into checksum computation autorun " + document.name
+  console.log document
+
+  id = document._id
+  currentId = id
+  fileContent = fileBuffer[id].fileContent
+
+  console.log "Computing checksum for " + document.name
+  ImportingFile.documents.update id,
+    $set:
+      status: "Computing checksum"
+
+  hash = new Crypto.SHA256
+    onProgress: (progress) ->
+      #TODO: update progressbar
+  hash.update fileContent, (error, result) ->
+    #TODO: handle errors
+    if error
+      console.log "Import error: " + error.message
+  hash.finalize (error, result) ->
+    #TODO: handle errors
+    if error
+      console.log "Import error: " + error.message
+
+    alreadyImporting = ImportingFile.documents.findOne
+      sha256: result
+
+    if alreadyImporting
+      hashMarkComplete
+        finished: true
+        status: "File is already importing"
+        # publicationId might not yet be available, but let's try
+        publicationId: alreadyImporting.publicationId
+      return
+
+    console.log "Setting sha256 value"
+    # so that observer can do it's work
+    hashMarkComplete
+      sha256: result
+      status: "Checksum computed"

 verifyFile = (file, fileContent, publicationId, samples) ->
   ImportingFile.documents.update file._id,
@@ -107,18 +145,19 @@
     new Uint8Array fileContent.slice sample.offset, sample.offset + sample.size
   Meteor.call 'verify-publication', publicationId, samplesData, (error) ->
     if error
-      ImportingFile.documents.update file._id,
-        $set:
-          errored: true
-          status: error.toString()
+      uploadMarkComplete
+        errored: true
+        status: error.toString()
       return

-    ImportingFile.documents.update file._id,
-      $set:
-        finished: true
-        publicationId: publicationId
+    uploadMarkComplete
+      finished: true
+      publicationId: publicationId

 uploadFile = (file, publicationId) ->
+  ImportingFile.documents.update file._id,
+    $set:
+      status: "Uploading file"
   meteorFile = new MeteorFile file,
     collection: ImportingFile.Meta.collection

@@ -128,16 +167,14 @@
   ,
     (error) ->
       if error
-        ImportingFile.documents.update file._id,
-          $set:
-            errored: true
-            status: error.toString()
+        uploadMarkComplete
+          errored: true
+          status: error.toString()
         return

-      ImportingFile.documents.update file._id,
-        $set:
-          finished: true
-          publicationId: publicationId
+      uploadMarkComplete
+        finished: true
+        publicationId: publicationId

 testPDF = (file, fileContent, callback) ->
   PDFJS.getDocument(data: fileContent, password: '').then callback, (message, exception) ->
@@ -169,7 +206,6 @@
     uploadProgress: 0
     finished: false
     errored: false
-    file: file
   ,
     # We are using callback to make sure ImportingFiles really has the file now
     (error, id) ->


fixed issue with `` when cursor is not at the eol

--- client/keymap.coffee
+++ client/keymap.coffee
@@ -267,12 +267,13 @@

   bqc[0].render = (e) -> e.innerHTML = "  #{Dyalog.getPrefixKey()}#{Dyalog.getPrefixKey()} <i>completion by name</i>"
   bqc[0].hint = bqbqHint = (cm) ->
-    c = cm.getCursor(); c0 = line: c.line, ch: c.ch - 1
+    c = cm.getCursor()
     cm.replaceSelection Dyalog.getPrefixKey(), 'end'
     cm.showHint completeOnSingleClick: true, hint: ->
-      u = cm.getLine(c.line)[c.ch + 1..]
+      u = cm.getLine(c.line)[c.ch + 1...cm.getCursor().ch]
       a = []; for x in bqbqc when x.name[...u.length] == u then a.push x
-      from: c0, to: cm.getCursor(), list: a
+      from: c, to: cm.getCursor(), list: a
+    return

   for line in squiggleNames.split '\n' then do ->
     [squiggle, names...] = line.split ' '


Firefox fixed #3

Firefox fixed #3

--- client/somafm.coffee
+++ client/somafm.coffee
@@ -27,8 +27,11 @@

 Template.somafm.events
   'click ul li a': (e) ->
-    console.log e
-    Session.set 'channel', e.srcElement.href.split('#')[1]
+    if e.srcElement?
+      href = e.srcElement.href
+    else
+      href = e.currentTarget.href
+    Session.set 'channel', href.split('#')[1]

 Template.somafm.somaUrl = ->
   channel = Session.get 'channel'


Refactored text selection code.

Refactored text selection code.

--- client/publication.coffee
+++ client/publication.coffee
@@ -31,6 +31,79 @@

   dims

+setupTextSelection = (publication, page, $textLayer) ->
+  $closestTextDiv = null
+  closestDistance = Number.MAX_VALUE;
+
+  $textLayer.mousemove (e) ->
+    layerOffset = $(this).offset()
+    relX = e.pageX - layerOffset.left
+    relY = e.pageY - layerOffset.top
+
+    closestDistance = Number.MAX_VALUE;
+    $textLayer.children().each ->
+      position = $(this).data 'position'
+
+      distXLeft = relX - position.left
+      distXRight = relX - (position.left + position.width)
+      distXCenter = relX - (position.left + position.width/2.0)
+
+      distYTop = relY - position.top
+      distYBottom = relY - (position.top + position.height)
+      distYCenter = relY - (position.top + position.height/2.0)
+
+
+      distX = if Math.abs(distXLeft) < Math.abs(distXRight) then distXLeft else distXRight
+      if relX > position.left and relX < position.left + position.width
+        distX = 0
+
+      distY = if Math.abs(distYTop) < Math.abs(distYBottom) then distYTop else distYBottom
+      if relY > position.top and relY < position.top + position.height
+        distY = 0
+      # distY = if Math.abs(distY) < Math.abs(distYCenter) then distY else distYCenter
+
+      dist = Math.sqrt(distX*distX + distY*distY)
+
+      if (dist < closestDistance)
+        closestDistance = dist
+        $closestTextDiv = $(this)
+
+      $(this).css("color","rgba(0,0,0,0)")
+      $(this).css("background-color","rgba(0,0,0,0)")
+      # $(this).css("color","rgba(0,0,0,#{ Math.max(1.0-dist/100,0) })")
+
+    # $closestTextDiv.css("color","rgba(0,0,0,1.0)")
+    return if $closestTextDiv is null
+    $closestTextDiv.css("background-color","rgba(255,0,0,0.3)")
+    $(this).data("closestTextDiv",$closestTextDiv)
+
+
+  $startTextDiv = null
+  $endTextDiv = null
+
+  $textLayer
+    .mousedown (e) ->
+      e.preventDefault()
+      return if not $closestTextDiv
+      $startTextDiv = $closestTextDiv
+    .mouseup (e) ->
+      return if not $closestTextDiv
+      $endTextDiv = $closestTextDiv
+
+  # outputScale = getOutputScale();
+  # if outputScale.scaled
+  #   cssScale = "scale(#{ 1 / outputScale.sx }, #{1 / outputScale.sy})";
+  #   CustomStyle.setProp 'transform', canvas, cssScale
+  #   CustomStyle.setProp 'transformOrigin', canvas, '0% 0%'
+  #   if $textLayer.get(0)
+  #     CustomStyle.setProp 'transform', $textLayer.get(0), cssScale
+  #     CustomStyle.setProp 'transformOrigin', $textLayer.get(0), '0% 0%'
+
+  # context._scaleX = outputScale.sx
+  # context._scaleY = outputScale.sy
+  # if outputScale.scaled
+  #   context.scale outputScale.sx, outputScale.sy
+
 displayPublication = (publication) ->
   PDFJS.getDocument(publication.url()).then (pdf) ->
     for pageNumber in [1..pdf.numPages]
@@ -47,86 +120,16 @@
             height: viewport.height
             width: viewport.width

-          $textLayerDiv = $('<div/>').addClass('display-text').css(
+          $textLayer = $('<div/>').addClass('display-text').css(
             height: viewport.height + 'px'
             width: viewport.width + 'px'
           ).appendTo $pageDisplay

-          $closestTextDiv = null
-          closestDistance = Number.MAX_VALUE;
-
-          $textLayerDiv.mousemove (e) ->
-            layerOffset = $(this).offset()
-            relX = e.pageX - layerOffset.left
-            relY = e.pageY - layerOffset.top
-
-            closestDistance = Number.MAX_VALUE;
-            $textLayerDiv.children().each ->
-              position = $(this).data 'position'
-
-              distXLeft = relX - position.left
-              distXRight = relX - (position.left + position.width)
-              distXCenter = relX - (position.left + position.width/2.0)
-
-              distYTop = relY - position.top
-              distYBottom = relY - (position.top + position.height)
-              distYCenter = relY - (position.top + position.height/2.0)
-
-
-              distX = if Math.abs(distXLeft) < Math.abs(distXRight) then distXLeft else distXRight
-              if relX > position.left and relX < position.left + position.width
-                distX = 0
-
-              distY = if Math.abs(distYTop) < Math.abs(distYBottom) then distYTop else distYBottom
-              if relY > position.top and relY < position.top + position.height
-                distY = 0
-              # distY = if Math.abs(distY) < Math.abs(distYCenter) then distY else distYCenter
-
-              dist = Math.sqrt(distX*distX + distY*distY)
-
-              if (dist < closestDistance)
-                closestDistance = dist
-                $closestTextDiv = $(this)
-
-              $(this).css("color","rgba(0,0,0,0)")
-              $(this).css("background-color","rgba(0,0,0,0)")
-              # $(this).css("color","rgba(0,0,0,#{ Math.max(1.0-dist/100,0) })")
-
-            # $closestTextDiv.css("color","rgba(0,0,0,1.0)")
-            return if $closestTextDiv is null
-            $closestTextDiv.css("background-color","rgba(255,0,0,0.3)")
-            $(this).data("closestTextDiv",$closestTextDiv)
-
-
-          $startTextDiv = null
-          $endTextDiv = null
-
-          $textLayerDiv
-            .mousedown (e) ->
-              e.preventDefault()
-              return if not $closestTextDiv
-              $startTextDiv = $closestTextDiv
-            .mouseup (e) ->
-              return if not $closestTextDiv
-              $endTextDiv = $closestTextDiv
-
-          # outputScale = getOutputScale();
-          # if outputScale.scaled
-          #   cssScale = "scale(#{ 1 / outputScale.sx }, #{1 / outputScale.sy})";
-          #   CustomStyle.setProp 'transform', canvas, cssScale
-          #   CustomStyle.setProp 'transformOrigin', canvas, '0% 0%'
-          #   if $textLayerDiv.get(0)
-          #     CustomStyle.setProp 'transform', $textLayerDiv.get(0), cssScale
-          #     CustomStyle.setProp 'transformOrigin', $textLayerDiv.get(0), '0% 0%'
-
-          # context._scaleX = outputScale.sx
-          # context._scaleY = outputScale.sy
-          # if outputScale.scaled
-          #   context.scale outputScale.sx, outputScale.sy
+          setupTextSelection publication, page, $textLayer

           page.getTextContent().then (textContent) ->
             textLayerOptions =
-              textLayerDiv: $textLayerDiv.get(0)
+              textLayerDiv: $textLayer.get(0)
               pageIndex: page.number - 1

             textLayer = new PDFJS.TextLayerBuilder textLayerOptions
@@ -138,7 +141,7 @@
               textLayer: textLayer

             page.render(renderContext).then ->
-              $textLayerDiv.children().each ->
+              $textLayer.children().each ->
                 $(this).data
                   position: getElementPosition this


Remove console log.

Remove console log.

--- client.coffee
+++ client.coffee
@@ -6,7 +6,6 @@

 Template.carousel.imageObjs = ->
-  console.log( this )
   imageObjs = []
   for image,index in this.images
     active = ""


avatar size fix, template selection fix

avatar size fix, template selection fix

--- client.coffee
+++ client.coffee
@@ -40,7 +40,7 @@
 	'.templateitem':
 		position: 'relative'
 		height: '60px'
-	'.templatedescription tr':
+	'.templatedescription div':
 		fontStyle: 'italic'
 		fontSize: '80%'
 		fontWeight: 'normal'
@@ -393,9 +393,10 @@
 									color: 'black'
 								Dom.text '✔'
 				Dom.div !->
+					Dom.style Flex: 1
 					Dom.cls 'templatedescription'
 					Dom.text opts.display
-					Dom.tr opts.description
+					Dom.div opts.description

 				Dom.onTap !->
 					settings.set('title', opts.title)
@@ -437,9 +438,8 @@
 						Photo.pick undefined, false, 'titlephoto'

 				Dom.div !->
+					Dom.style Flex: 1
 					Dom.div !->
-						Dom.style
-							width: '100%'
 						cte = Form.input
 							name: 'customtitle'
 							text: 'Title'
@@ -533,7 +533,7 @@
 					borderRadius: '250px'
 					border: '1px solid #aaa'
 					boxShadow: '2px 2px 8px #aaa'
-				, 250
+			, 250

 templates = [
 	{title: tr('Panda'), display: tr('The Panda'), description: tr('Too lazy to reproduce, and thus doomed to extinction.'), photo: 'panda.jpg', id: 1},


Update# add some parallax effect to speakers

--- coffeescripts/main.coffee
+++ coffeescripts/main.coffee
@@ -171,6 +171,31 @@
 page.section SECTIONS.SPEAKER, (section) ->
   transitions = []

+  speakers = $("#speaker .speaker")
+  speakersCount = speakers.length
+
+  for speaker, index in speakers
+    start = 20 + index * (100 / speakersCount)
+    transitions.push {
+      target: speaker
+      start: start
+      end: start + 50
+      key: 'opacity'
+      from: 0
+      to: 1
+    }
+    transitions.push {
+      target: speaker
+      start: start
+      end: start + 50
+      key: 'transform'
+      from: 100
+      to: 0
+      format: "translateY(%spx)"
+      afterCalculate: (val) ->
+        val = (val / 100) * screenWidth
+    }
+
   section.on "scrollIn", ()->
     setMenuActiveItem(2)


point

point

--- src/point.coffee
+++ src/point.coffee
@@ -418,10 +418,10 @@
     Calculate the magnitude of this Point (Vector).

-       point = Point(5, 7)
-
-       point.magnitude()
-    => 8.602325267042627
+    point = Point(5, 7)
+
+    point.magnitude()
+    # => 8.602325267042627

     @name magnitude


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -50,7 +50,7 @@
       @copy().clamp()

     clamp$: (n) ->
-      i
+      if

     ###*
     Creates a copy of this point.


Use the real usernames.

Use the real usernames.

--- src/quote.coffee
+++ src/quote.coffee
@@ -132,34 +132,13 @@
   robot.respond /top quotes$/i, (msg) ->
     return unless isLoaded(msg)

-    # usernames = (u.name for u in robot.brain.users)
-    usernames = [
-      "smashwilson"
-      "jlitzwilson"
-      "jrhusel"
-      "reostra"
-      "purr-purr"
-      "hdcunni"
-      "fenris"
-      "k0d3k1ttn"
-      "zmbusse"
-      "femshep"
-      "aschwa2"
-      "phbarna"
-      "blackbeardtron"
-      "iguanaditty"
-      "wrbritt"
-      "frey"
-      "clamsey"
-      "roastin41"
-      "puppetwrangler"
-      "pusscat"
-      "llynmir"
-      "gmhowar"
-      "susan"
-      "spartacus"
-      "belle.carrell"
-    ]
+    usernames = []
+    for uid in Object.keys(robot.brain.users())
+      usernames.push robot.brain.users()[uid].name
+    if usernames.length is 0
+      msg.reply "I have no users. How can that be?"
+      return
+
     results = {}

     incrementResultFor = (username, kind) ->
@@ -170,23 +149,21 @@

     for quote in quotes
       [speakers, mentioned] = [[], []]
-      mentionMatcher = new RegExp('(' + usernames.join('|') + ')', 'g')
+      mentionMatcher = new RegExp(usernames.join('|'), 'g')
       mentionsFrom = (str) ->
-        mention = null
+        mention = mentionMatcher.exec(str)
+        mentioned.push mention[0] if mention?
         while mention?
           mention = mentionMatcher.exec(str)
-          mentioned.push mention if mention?
-      console.log "Matcher is: #{mentionMatcher.toString()}"
+          mentioned.push mention[0] if mention?

       for line in quote.split(/\n/)
         m = line.match(/^\[[^\]]+\] @?([^:]+): (.*)$/)
         if m?
           [x, speaker, rest] = m
-          console.log "Speaker: #{speaker}, rest: #{rest}"
           speakers.push speaker
           mentionsFrom(rest)
         else
-          console.log "No match in #{line}"
           mentionsFrom(line)

       incrementResultFor(u, 'spoke') for u in _.uniq speakers


Use full call signature of createTreeWalker

--- src/range.coffee
+++ src/range.coffee
@@ -224,12 +224,12 @@
     document = bounds.ownerDocument

     if not contains(bounds, @start)
-      walker = document.createTreeWalker(bounds, NodeFilter.SHOW_TEXT)
-      @start = walker.firstChild()
+      w = document.createTreeWalker(bounds, NodeFilter.SHOW_TEXT, null, false)
+      @start = w.firstChild()

     if not contains(bounds, @end)
-      walker = document.createTreeWalker(bounds, NodeFilter.SHOW_TEXT)
-      @end = walker.lastChild()
+      w = document.createTreeWalker(bounds, NodeFilter.SHOW_TEXT, null, false)
+      @end = w.lastChild()

     return null unless @start and @end