Use Collapse in checkbox-area

Use Collapse in checkbox-area

--- views/akashic-records-checkbox-area.cjsx
+++ views/akashic-records-checkbox-area.cjsx
@@ -1,5 +1,5 @@
 {React, ReactBootstrap, jQuery, config, __} = window
-{Panel, Button, Col, Input, Grid, Row, ButtonGroup, DropdownButton, MenuItem, Table, OverlayTrigger, Popover} = ReactBootstrap
+{Panel, Button, Col, Input, Grid, Row, ButtonGroup, DropdownButton, MenuItem, Table, OverlayTrigger, Popover, Collapse, Well} = ReactBootstrap
 Divider = require './divider'
 {openExternal} = require 'shell'

@@ -220,60 +220,60 @@

           </Col>
         </Row>
-      </Grid>
-      <Grid className='akashic-records-filter' style={if @state.filterPaneShow then {display: 'block'} else {display: 'none'} }>
-        <Row>
-        {
-          for checkedVal, index in @props.tableTab
-            continue if !index
-            <Col key={index} xs={2}>
-              <Input type='checkbox' value={index} onChange={@handleClickCheckbox.bind(@, index)} checked={@props.rowChooseChecked[index]} style={verticalAlign: 'middle'} label={checkedVal} />
-            </Col>
-        }
-        </Row>
-

-        <Row>
-          <Col xs={2}>
-            <ButtonGroup justified>
-              <DropdownButton bsSize='xsmall' id="dropdown-showOption-selector" eventKey={4} title={__ "Newer %s", @props.showAmount}>
-                <MenuItem eventKey=10 onSelect={@handleShowAmountSelect}>{__ "Newer %s", "10"}</MenuItem>
-                <MenuItem eventKey=20 onSelect={@handleShowAmountSelect}>{__ "Newer %s", "20"}</MenuItem>
-                <MenuItem eventKey=50 onSelect={@handleShowAmountSelect}>{__ "Newer %s", "50"}</MenuItem>
-                <MenuItem divider />
-                <MenuItem eventKey=999999 onSelect={@handleShowAmountSelect}>{__ "View All"}</MenuItem>
-              </DropdownButton>
-            </ButtonGroup>
-          </Col>
-          <Col xs={2}>
-            <ButtonGroup justified>
-              <DropdownButton bsSize='xsmall' id="dropdown-page-selector" eventKey={4} title={__ "Page %s", @props.activePage}>
+        <Collapse className='akashic-records-filter' in={@state.filterPaneShow}>
+

+            <Row>
+            {
+              for checkedVal, index in @props.tableTab
+                continue if !index
+                <Col key={index} xs={2}>
+                  <Input type='checkbox' value={index} onChange={@handleClickCheckbox.bind(@, index)} checked={@props.rowChooseChecked[index]} style={verticalAlign: 'middle'} label={checkedVal} />
+                </Col>
+            }
+            </Row>
+

+            <Row>
+              <Col xs={2}>
+                <ButtonGroup justified>
+                  <DropdownButton bsSize='xsmall' id="dropdown-showOption-selector" eventKey={4} title={__ "Newer %s", @props.showAmount}>
+                    <MenuItem eventKey=10 onSelect={@handleShowAmountSelect}>{__ "Newer %s", "10"}</MenuItem>
+                    <MenuItem eventKey=20 onSelect={@handleShowAmountSelect}>{__ "Newer %s", "20"}</MenuItem>
+                    <MenuItem eventKey=50 onSelect={@handleShowAmountSelect}>{__ "Newer %s", "50"}</MenuItem>
+                    <MenuItem divider />
+                    <MenuItem eventKey=999999 onSelect={@handleShowAmountSelect}>{__ "View All"}</MenuItem>
+                  </DropdownButton>
+                </ButtonGroup>
+              </Col>
+              <Col xs={2}>
+                <ButtonGroup justified>
+                  <DropdownButton bsSize='xsmall' id="dropdown-page-selector" eventKey={4} title={__ "Page %s", @props.activePage}>
+                  {
+                    if @props.dataShowLength isnt 0
+                      for index in [1..Math.ceil(@props.dataShowLength/@props.showAmount)]
+                        <MenuItem key={index} eventKey={index} onSelect={@handleShowPageSelect}>{__ "Page %s", index}</MenuItem>
+                  }
+                  </DropdownButton>
+                </ButtonGroup>
+              </Col>
+              <Col xs={5}>
               {
-                if @props.dataShowLength isnt 0
-                  for index in [1..Math.ceil(@props.dataShowLength/@props.showAmount)]
-                    <MenuItem key={index} eventKey={index} onSelect={@handleShowPageSelect}>{__ "Page %s", index}</MenuItem>
+                for checkedVal, index in @props.configList
+                  continue if index is 3
+                  <Col key={index} xs={4}>
+                    <Input type='checkbox' value={index} onChange={@handleClickConfigCheckbox.bind(@, index)} checked={@props.configChecked[index]} style={verticalAlign: 'middle'} label={checkedVal} />
+                  </Col>
               }
-              </DropdownButton>
-            </ButtonGroup>
-          </Col>
-          <Col xs={5}>
-          {
-            for checkedVal, index in @props.configList
-              continue if index is 3
-              <Col key={index} xs={4}>
+              </Col>
+              <Col xs={3}>
+              {
+                index = 3
+                checkedVal = @props.configList[index]
                 <Input type='checkbox' value={index} onChange={@handleClickConfigCheckbox.bind(@, index)} checked={@props.configChecked[index]} style={verticalAlign: 'middle'} label={checkedVal} />
+              }
               </Col>
-          }
-          </Col>
-          <Col xs={3}>
-          {
-            index = 3
-            checkedVal = @props.configList[index]
-            <Input type='checkbox' value={index} onChange={@handleClickConfigCheckbox.bind(@, index)} checked={@props.configChecked[index]} style={verticalAlign: 'middle'} label={checkedVal} />
-          }
-          </Col>
-        </Row>
-      </Grid>
-      <Grid>
+            </Row>
+

+        </Collapse>
         <Row>
           <Col xs={12}>

@@ -281,146 +281,148 @@

           </Col>
         </Row>
-      </Grid>
-      <Grid className='akashic-records-statistics' style={if @state.statisticsPaneShow then {display: 'block'} else {display: 'none'} }>
-        <Row>
-          <Col xs={12}>
-

-

-

-
-                    <OverlayTrigger trigger='click' rootClose={true} placement='right' overlay={
-                      <Popover title={__ "Tips"} id={"regExp-Hint"}>
-
{__ "Support the Javascript's "}{"RegExp"}

-                      </Popover>
-                      }>
-                      <FontAwesome name='question-circle'/>
-                    </OverlayTrigger>
-
-                  No.
-                  {__ "Base on"}
-                  {__ "Keywords"}
-                  {__ "Result"}
-                  {__ "Sample Size"}
-                  {__ "Percentage"}
-

-

-

-              {
-                for index in [0..@state.searchArgv.length-1]
-

-                    {
-                      if index is 0
-                        <FontAwesome name='plus-circle' onClick={@addSearchLine}/>
-                      else
-                        <FontAwesome name='minus-circle' onClick={@deleteSearchLine.bind(@, index)}/>
-                    }
-                    {index+1}
-
-                      <Input type="select" ref="baseOn#{index}" groupClassName='search-area' value={"#{@state.searchArgv[index].searchBaseOn}"} onChange={@handleSearchChange}>
-                        <option key={-2} value={-2}>{__ "All Data"}</option>
-                        <option key={-1} value={-1}>{__ "Filtered"}</option>
+        <Collapse className='akashic-records-statistics' in={@state.statisticsPaneShow}>
+

+            <Row>
+              <Col xs={12}>
+

+

+

+
+                        <OverlayTrigger trigger='click' rootClose={true} placement='right' overlay={
+                          <Popover title={__ "Tips"} id={"regExp-Hint"}>
+
{__ "Support the Javascript's "}{"RegExp"}

+                          </Popover>
+                          }>
+                          <FontAwesome name='question-circle'/>
+                        </OverlayTrigger>
+
+                      No.
+                      {__ "Base on"}
+                      {__ "Keywords"}
+                      {__ "Result"}
+                      {__ "Sample Size"}
+                      {__ "Percentage"}
+

+

+

+                  {
+                    for index in [0..@state.searchArgv.length-1]
+

                         {
-                          for i in [0..@state.searchArgv.length-1]
-                            break if i >= index
-                            <option key={i} value={i}>{__ "Search Result No. %s", i+1}</option>
+                          if index is 0
+                            <FontAwesome name='plus-circle' onClick={@addSearchLine}/>
+                          else
+                            <FontAwesome name='minus-circle' onClick={@deleteSearchLine.bind(@, index)}/>
                         }
-                      </Input>
-
-
-                       <Input
-                          type='text'
-                          value={@state.searchArgv[index].filterKey}
-                          placeholder={__ "Keywords"}
-                          ref="search#{index}"
-                          groupClassName='search-area'
-                          onChange={@handleSearchChange} />
-
-                    {@state.searchArgv[index]['result'].length}
-                    {@state.searchArgv[index]['num']}
-                    {"#{@state.searchArgv[index]['percent']}%"}
-

-              }
-

-

-

-
-                  No.
-                  {__ "Numerator"}
-                  {__ "Denominator"}
-                  {__ "Numerator Number"}
-                  {__ "Denominator Number"}
-                  {__ "Percentage"}
-

-

-

-              {
-                for index in [0..@state.compareArgv.length-1]
-

-                    {
-                      if index is 0
-                        <FontAwesome name='plus-circle' onClick={@addCompareLine}/>
-                      else
-                        <FontAwesome name='minus-circle' onClick={@deleteCompareLine.bind(@, index)}/>
-                    }
-                    {index+1}
-
-                      <Input type="select" ref="numeratorBaseon#{index}" groupClassName='search-area' value={"#{@state.compareArgv[index]['numeratorBaseon']}"} onChange={@handleCompareChange}>
-                        <option key={-2} value={-2}>{__ "All Data"}</option>
-                        <option key={-1} value={-1}>{__ "Filtered"}</option>
+                        {index+1}
+
+                          <Input type="select" ref="baseOn#{index}" groupClassName='search-area' value={"#{@state.searchArgv[index].searchBaseOn}"} onChange={@handleSearchChange}>
+                            <option key={-2} value={-2}>{__ "All Data"}</option>
+                            <option key={-1} value={-1}>{__ "Filtered"}</option>
+                            {
+                              for i in [0..@state.searchArgv.length-1]
+                                break if i >= index
+                                <option key={i} value={i}>{__ "Search Result No. %s", i+1}</option>
+                            }
+                          </Input>
+
+
+                           <Input
+                              type='text'
+                              value={@state.searchArgv[index].filterKey}
+                              placeholder={__ "Keywords"}
+                              ref="search#{index}"
+                              groupClassName='search-area'
+                              onChange={@handleSearchChange} />
+
+                        {@state.searchArgv[index]['result'].length}
+                        {@state.searchArgv[index]['num']}
+                        {"#{@state.searchArgv[index]['percent']}%"}
+

+                  }
+

+

+

+
+                      No.
+                      {__ "Numerator"}
+                      {__ "Denominator"}
+                      {__ "Numerator Number"}
+                      {__ "Denominator Number"}
+                      {__ "Percentage"}
+

+

+

+                  {
+                    for index in [0..@state.compareArgv.length-1]
+

                         {
-                          for i in [0..@state.searchArgv.length-1]
-                            <option key={i} value={i}>{__ "Search Result No. %s", i+1}</option>
+                          if index is 0
+                            <FontAwesome name='plus-circle' onClick={@addCompareLine}/>
+                          else
+                            <FontAwesome name='minus-circle' onClick={@deleteCompareLine.bind(@, index)}/>
                         }
-                        <option key={-3} value={-3}>{__ "Custom"}</option>
-                      </Input>
-
-
-                      <Input type="select" ref="denominatorBaseon#{index}" groupClassName='search-area' value={"#{@state.compareArgv[index]['denominatorBaseon']}"} onChange={@handleCompareChange}>
-                        <option key={-2} value={-2}>{__ "All Data"}</option>
-                        <option key={-1} value={-1}>{__ "Filtered"}</option>
+                        {index+1}
+
+                          <Input type="select" ref="numeratorBaseon#{index}" groupClassName='search-area' value={"#{@state.compareArgv[index]['numeratorBaseon']}"} onChange={@handleCompareChange}>
+                            <option key={-2} value={-2}>{__ "All Data"}</option>
+                            <option key={-1} value={-1}>{__ "Filtered"}</option>
+                            {
+                              for i in [0..@state.searchArgv.length-1]
+                                <option key={i} value={i}>{__ "Search Result No. %s", i+1}</option>
+                            }
+                            <option key={-3} value={-3}>{__ "Custom"}</option>
+                          </Input>
+
+
+                          <Input type="select" ref="denominatorBaseon#{index}" groupClassName='search-area' value={"#{@state.compareArgv[index]['denominatorBaseon']}"} onChange={@handleCompareChange}>
+                            <option key={-2} value={-2}>{__ "All Data"}</option>
+                            <option key={-1} value={-1}>{__ "Filtered"}</option>
+                            {
+                              for i in [0..@state.searchArgv.length-1]
+                                <option key={i} value={i}>{__ "Search Result No. %s", i+1}</option>
+                            }
+                            <option key={-3} value={-3}>{__ "Custom"}</option>
+                          </Input>
+
                         {
-                          for i in [0..@state.searchArgv.length-1]
-                            <option key={i} value={i}>{__ "Search Result No. %s", i+1}</option>
+                          if @state.compareArgv[index].numeratorBaseon is -3
+
+                              <Input
+                                type='number'
+                                placeholder={"0"}
+                                value={"#{@compareResult[index].numerator}"}
+                                ref="numerator#{index}"
+                                groupClassName='search-area'
+                                onChange={@handleCompareChange} />
+
+                          else
+                            {@compareResult[index].numerator}
                         }
-                        <option key={-3} value={-3}>{__ "Custom"}</option>
-                      </Input>
-
-                    {
-                      if @state.compareArgv[index].numeratorBaseon is -3
-
-                          <Input
-                            type='number'
-                            placeholder={"0"}
-                            value={"#{@compareResult[index].numerator}"}
-                            ref="numerator#{index}"
-                            groupClassName='search-area'
-                            onChange={@handleCompareChange} />
-
-                      else
-                        {@compareResult[index].numerator}
-                    }
-                    {
-                      if @state.compareArgv[index].denominatorBaseon is -3
-
-                          <Input
-                            type='number'
-                            placeholder={"0"}
-                            value={"#{@compareResult[index].denominator}"}
-                            ref="denominator#{index}"
-                            groupClassName='search-area'
-                            onChange={@handleCompareChange} />
-
-                      else
-                        {@compareResult[index].denominator}
-                    }
-                    {"#{@compareResult[index].percent}%"}
-

-              }
-

-

-          </Col>
-        </Row>
+                        {
+                          if @state.compareArgv[index].denominatorBaseon is -3
+
+                              <Input
+                                type='number'
+                                placeholder={"0"}
+                                value={"#{@compareResult[index].denominator}"}
+                                ref="denominator#{index}"
+                                groupClassName='search-area'
+                                onChange={@handleCompareChange} />
+
+                          else
+                            {@compareResult[index].denominator}
+                        }
+                        {"#{@compareResult[index].percent}%"}
+

+                  }
+

+

+              </Col>
+            </Row>
+

+        </Collapse>
       </Grid>


Only use toggle_type to activate opinon

Only use toggle_type to activate opinon

--- core/app/backbone/views/wheel/interactive_wheel_view.coffee
+++ core/app/backbone/views/wheel/interactive_wheel_view.coffee
@@ -2,7 +2,7 @@
   clickOpinionType: (opinionType) ->
     fact_id = @options.fact.id

-    @toggleActiveOpinionType opinionType
+    @toggleActiveOpinionType opinionType.type
     if opinionType.is_user_opinion
       $.ajax
         url: "/facts/" + fact_id + "/opinion/" + opinionType.type + "s.json"
@@ -14,7 +14,7 @@
             opinion: opinionType.type

         error: =>
-          @toggleActiveOpinionType opinionType
+          @toggleActiveOpinionType opinionType.type
           alert "Something went wrong while setting your opinion on the Factlink, please try again"

     else
@@ -27,17 +27,27 @@
             factlink: @options.fact.id

         error: =>
-          @toggleActiveOpinionType opinionType
+          @toggleActiveOpinionType opinionType.type
           alert "Something went wrong while removing your opinion on the Factlink, please try again"

-  toggleActiveOpinionType: (opinionType) ->
-    oldAuthority = @model.get("authority")
-    updateObj = {}
-    _.each @model.get('opinion_types'), (oldOpinionType) ->
-      updateObj[oldOpinionType.type] = _.clone(oldOpinionType)
-      unless opinionType.is_user_opinion
-        updateObj[oldOpinionType.type].is_user_opinion = false
-      if oldOpinionType == opinionType
-        updateObj[oldOpinionType.type].is_user_opinion = !opinionType.is_user_opinion
+  toggleActiveOpinionType: (toggle_type) ->
+    new_opinion_types = {}

-    @updateTo oldAuthority, updateObj
+    opinion_types = @model.get('opinion_types')
+
+    activating_an_opinion = not opinion_types[toggle_type].is_user_opinion
+
+    _.each opinion_types, (old_opinion_type) ->
+
+      new_opinion_type = {}
+      new_opinion_type.percentage = old_opinion_type.percentage
+
+      new_opinion_type.is_user_opinion =
+        if old_opinion_type.type == toggle_type
+          not old_opinion_type.is_user_opinion
+        else
+          false
+
+      new_opinion_types[old_opinion_type.type] = new_opinion_type
+
+    @updateTo @model.get("authority"), new_opinion_types


fixed label regex

fixed label regex

--- app/assets/javascripts/models/metric.js.coffee
+++ app/assets/javascripts/models/metric.js.coffee
@@ -27,12 +27,9 @@
       if @get("label")
         tags = @get("tags")
         l = @get("label")
-        matches = _.getMatches(l, /(^|[\b\s])\$([\w]+)\b/g)
+        matches = _.getMatches(l, /(^|[\b\s])\$([\w]+)\b/g, 2)

         _.each matches, (m) ->
-          m = _.string.trim(m)
-          if !m
-            return
           tag = tags[m]
           if tag
             l = l.replace("$#{m}", tag)


Notifications quick fix

Notifications quick fix

--- app/assets/javascripts/notifications.js.coffee
+++ app/assets/javascripts/notifications.js.coffee
@@ -180,7 +180,7 @@
         console.log error.stack
       success: (data) ->
         return if pause_polling
-        addNotifications(data)
+        addNotifications(data.notifications)
         updateUsers()
         if first_run
           if match = window.location.hash.match(/^#([0-9]+)$/)
@@ -243,9 +243,9 @@
       pause_polling = false

   $.getJSON "/users.json", (data) ->
-    data = _.sortBy data, (u) ->
+    users = _.sortBy data.users, (u) ->
       u.display_name
-    for user in data
+    for user in users
       continue if user.login.match(/-disabled/)
       users[user.id] = user
       continue if user.id == user_id


Fixed bug where auto post loading interferes with post refresh by submitting

--- app/assets/javascripts/redcursor_app.js.coffee
+++ app/assets/javascripts/redcursor_app.js.coffee
@@ -115,13 +115,13 @@
       self.parents(".date-content").find(".post-options").toggle()
     return false

-  refreshPosts = (callback) ->
+  refreshPosts = () ->
     last_id = $('.post').last().attr("data-post-id")
     ourl = document.location.href.replace(/#.*$/, '')
     url = "#{ourl}/posts?last_id=#{last_id}"
     $.get url, (data) ->
       $(data).insertBefore('.comment-box-form')
-      callback() if callback?
+      $(document).trigger 'fu2.refreshPosts'

   refreshChannels = () ->
     last_id = $('#recent_activities .channel').first().attr("data-last-id")
@@ -133,8 +133,10 @@
   if $('.comment-box-form').length
     setInterval refreshPosts, 15 * 1000
     $('.comment-box-form form').ajaxForm ->
-      refreshPosts ->
+      $(document).one 'fu2.refreshPosts', ->
         $('.comment-box-form textarea').val ''
+        console.log 'yay'
+      refreshPosts()
     $('.comment-box-form textarea').keydown (e) ->
       if e.keyCode == 13 && (e.metaKey || e.ctrlKey)
         $(this).parents('form').submit()


Updated measure view to fix popChart stuff

--- app/assets/javascripts/views/measure.js.coffee
+++ app/assets/javascripts/views/measure.js.coffee
@@ -3,7 +3,7 @@
   events:
     rendered: ->
       @$('.dial').knob()
-      myChart = PopHealth.viz.populationChart().width(125).height(50).numerSpacing(2).maximumValue(PopHealth.patientCount)
+      myChart = PopHealth.viz.populationChart().width(125).height(50).barHeight(20).maximumValue(PopHealth.patientCount)
       select = d3.select(@el).select('#test')
         .datum(NUMER: 5, DENOM: 7, DENEX: 2, DENEXCEP: 6)
         .call(myChart)


rename Cu.View.ViewContent to Cu.View.ToolContent

--- client/code/view/view/content.coffee
+++ client/code/view/view/content.coffee
@@ -1,4 +1,4 @@
-class Cu.View.ViewContent extends Backbone.View
+class Cu.View.ToolContent extends Backbone.View
   id: 'fullscreen'

   showContent: ->
@@ -60,7 +60,7 @@
                 error: (model, xhr, options) ->
                   console.warn "Error creating dataset (xhr status: #{xhr.status} #{xhr.statusText})"

-class Cu.View.AppContent extends Cu.View.ViewContent
+class Cu.View.AppContent extends Cu.View.ToolContent
   settings: (callback) ->
     query = window.app.pushSqlQuery
     window.app.pushSqlQuery = null
@@ -73,7 +73,7 @@
         box: @model.get 'box'
         sqlQuery: query

-class Cu.View.PluginContent extends Cu.View.ViewContent
+class Cu.View.PluginContent extends Cu.View.ToolContent
   settings: (callback) ->
     query = window.app.pushSqlQuery
     window.app.pushSqlQuery = null


Fixed: a typo in a variable name caused an error

--- app/controllers/notification_controller.coffee
+++ app/controllers/notification_controller.coffee
@@ -34,4 +34,4 @@
         if err?
             send error: true, msg: "Server error while saving notification", 500
         else
-            send notification, 200+            send notif, 200


don't intercept clicks if it's a normal hash click

--- shared/base/app_view.coffee
+++ shared/base/app_view.coffee
@@ -26,6 +26,9 @@
     # We want the actual value of the attribute, rather than the
     # full URL, so we use jQuery instead of just e.currentTarget.href
     href = $(e.currentTarget).attr('href')
-    if href && href.slice(0, 1) == '/' && href.slice(0, 2) != '//'
-      e.preventDefault()
-      @app.router.navigate(href, true)
+    if href
+      hashParts = href.split('#')
+      isHashClick = hashParts.length > 1 && hashParts[0] == window.location.pathname
+      if !isHashClick && href.slice(0, 1) == '/' && href.slice(0, 2) != '//'
+        e.preventDefault()
+        @app.router.navigate(href, true)


Renamed confusingly named token to symbol

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1,5 +1,5 @@
-tokenize = (input) ->
-  currentPos = 0
+tokenize = (input, initPos = 0) ->
+  currentPos = initPos
   while input.length > 0
     match = input.match ///
       ^ # must be at the start
@@ -14,11 +14,11 @@
       )///
     if not match
       throw new Error "Could not recognize a token starting with `#{input[0..10]}`"
-    [token] = match
-    input = input[token.length...]
+    [symbol] = match
+    input = input[symbol.length...]
     pos = currentPos
-    currentPos += token.length
-    constantLabeling {token, pos}
+    currentPos += symbol.length
+    constantLabeling {symbol, pos}

 controls = '\\(\\)\\[\\]\\{\\}'

@@ -30,21 +30,21 @@
   current = []
   stack = [[]]
   for token in tokens
-    if token.token in leftDelims
+    if token.symbol in leftDelims
       stack.push [token]
-    else if token.token in rightDelims
+    else if token.symbol in rightDelims
       closed = stack.pop()
-      if token.token isnt delims[closed[0].token]
-        throw "Wrong closing delimiter #{token.token} for opening delimiter #{closed[0].token}"
+      if token.symbol isnt delims[closed[0].symbol]
+        throw "Wrong closing delimiter #{token.symbol} for opening delimiter #{closed[0].symbol}"
       closed.push token
       if not stack[stack.length - 1]
-        throw "Missing opening delimeter matching #{token.token}"
+        throw "Missing opening delimeter matching #{token.symbol}"
       stack[stack.length - 1].push closed
     else
       stack[stack.length - 1].push token
   ast = stack[0][0]
   if not ast
-    throw "Missing closing delimeter matching #{stack[stack.length - 1][0].token}"
+    throw "Missing closing delimeter matching #{stack[stack.length - 1][0].symbol}"
   else
     ast

@@ -53,18 +53,18 @@
 delims = '(': ')', '[': ']', '{': '}'

 constantLabeling = (atom) ->
-  {token} = atom
+  {symbol} = atom
   labelMapping atom,
-    ['numerical', /^-?\d+/.test token]
+    ['numerical', /^-?\d+/.test symbol]
     ['label', isLabel atom]
-    ['string', /^["']/.test token]
-    ['regex', /^\/[^ \/]/.test token]
-    ['paren', token in ['(', ')']]
-    ['bracket', token in ['[', ']']]
-    ['brace', token in ['{', '}']]
-    ['whitespace', /^\s+$/.test token]
-
-_isCollectionDelim = (atom) ->
+    ['string', /^["']/.test symbol]
+    ['regex', /^\/[^ \/]/.test symbol]
+    ['paren', symbol in ['(', ')']]
+    ['bracket', symbol in ['[', ']']]
+    ['brace', symbol in ['{', '}']]
+    ['whitespace', /^\s+$/.test symbol]
+
+isCollectionDelim = (atom) ->
   atom.label in ['bracket', 'brace']

 # TODO: remove
@@ -86,43 +86,13 @@
     typed ast, (for node in ast
       crawl node, cb, ast)
   else
-    cb ast, ast.token, parent
+    cb ast, ast.symbol, parent

 visitExpressions = (expression, cb) ->
   cb expression
   if isForm expression
     for term in _terms expression
       visitExpressions term, cb
-
-inside = (node) -> node[1...-1]
-
-matchNode = (to, node) ->
-  return false unless node.length >= 3
-  [paren, {token}] = node
-  paren.token is '(' and token is to
-
-matchAnyNode = (tos, node) ->
-  for to in tos
-    if matchNode to, node
-      return true
-  return false
-
-macro = (operator, ast, cb) ->
-  walk ast, (node) ->
-    if matchNode operator, node
-      return cb node, inside node
-    node
-
-typedMacro = (type, ast, cb) ->
-  walk ast, (node) ->
-    if node.type is type
-      return cb node, inside node
-    node
-
-mapTokens = (tokens, ast, cb) ->
-  crawl ast, (word, token) ->
-    if token in tokens
-      cb word

 teas = (fn, string) ->
   ast = astize tokenize string
@@ -199,7 +169,7 @@

   setName: (expression) ->
     @names.push
-      name: expression?.token
+      name: expression?.symbol
       expression: expression
       deferredBindings: []
       definedNames: []
@@ -394,7 +364,7 @@

 callCompile = (ctx, call) ->
   operator = _operator call
-  operatorName = _token operator
+  operatorName = _symbol operator
   if isName operator
     (if operatorName of ctx.macros()
       macroCompile
@@ -413,7 +383,7 @@
 macroCompile = (ctx, call) ->
   op = _operator call
   op.label = 'keyword'
-  expanded = ctx.macros()[op.token] ctx, call
+  expanded = ctx.macros()[op.symbol] ctx, call
   if not isWellformed expanded
     'malformed'
   else if isTranslated expanded
@@ -435,10 +405,10 @@
   if tagFreeLabels args
     return malformed 'labels without values inside call', call

-  paramNames = ctx.arity operator.token
+  paramNames = ctx.arity operator.symbol
   if not paramNames
-    log "deferring in known call #{operator.token}"
-    ctx.setDefer operator.token
+    log "deferring in known call #{operator.symbol}"
+    ctx.setDefer operator.symbol
     return 'deferred'
   positionalParams = filter ((param) -> not (lookupInMap labeledArgs, param)), paramNames
   nonLabeledArgs = map _snd, filter (([label, value]) -> not label), args
@@ -481,8 +451,8 @@
 callConstructorPattern = (ctx, call, extraParamNames) ->
   operator = _operator call
   args = _arguments call
-  isExtra = (arg) -> (isAtom arg) && (_token arg) in extraParamNames
-  paramNames = ctx.arity operator.token
+  isExtra = (arg) -> (isAtom arg) && (_symbol arg) in extraParamNames
+  paramNames = ctx.arity operator.symbol

   if args.length - extraParamNames.length > 1
     ctx.cacheAssignTo()
@@ -674,11 +644,11 @@
     assignCompile ctx, form, "[#{listOf compiledElems}]"

 isSplat = (expression) ->
-  (isAtom expression) and (_token expression)[...2] is '..'
+  (isAtom expression) and (_symbol expression)[...2] is '..'

 splatToName = (splat) ->
   replicate splat,
-    (token_ (_token splat)[2...])
+    (token_ (_symbol splat)[2...])

 # arrayToConses = (elems) ->
 #   if elems.length is 0
@@ -846,12 +816,12 @@
         [type, body, wheres...] = defs
       else
         [body, wheres...] = defs
-      paramNames = map _token, params
+      paramNames = map _symbol, params
       # pattern
       paramTypes = map (-> toForAll ctx.freshTypeVariable star), params
       ctx.newLateScope()
-      # log "adding types", (map _token, params), paramTypes
-      ctx.addTypes (map _token, params), paramTypes
+      # log "adding types", (map _symbol, params), paramTypes
+      ctx.addTypes (map _symbol, params), paramTypes

       log "compiling wheres", pairs wheres
       compiledWheres = definitionList ctx, pairs wheres
@@ -865,7 +835,7 @@
       # Syntax - used params in function body
       # !! TODO: possibly add to nameCompile instead, or defer to IDE
       isUsedParam = (expression) ->
-        (isName expression) and (_token expression) in paramNames
+        (isName expression) and (_symbol expression) in paramNames
       labelUsedParams = (expression) ->
         map (syntaxNameAs '', 'param'), filterAst isUsedParam, expression
       map labelUsedParams, join [body], wheres
@@ -911,23 +881,23 @@
     # Types
     for [constr, params] in defs
       constrType = desiplifyType if params
-        join (_labeled _terms params).map(_snd).map(_token), [ctx._name()]
+        join (_labeled _terms params).map(_snd).map(_symbol), [ctx._name()]
       else
         ctx._name()
       # TODO support polymorphic data
-      log "Adding constructor #{constr.token}"
-      ctx.addType constr.token, toForAll constrType
+      log "Adding constructor #{constr.symbol}"
+      ctx.addType constr.symbol, toForAll constrType
       constr.tea = constrType
     # TODO: support polymorphic data
     # We don't add binding to types, but maybe need to store elsewhere
     # ctx.addType ctx._name(), typeConstant ctx._name()
     # Arity
     for [constr, params] in defs when params
-      ctx.addArity constr.token,
+      ctx.addArity constr.symbol,
         (_labeled _terms params).map(_fst).map(_labelName)
     # Translate
     listOfLines (for [constr, params] in defs
-      identifier = validIdentifier constr.token
+      identifier = validIdentifier constr.symbol
       paramNames = (_labeled _terms params or []).map(_fst).map(_labelName)
         .map(validIdentifier)
       paramList = paramNames.join(', ')
@@ -1097,9 +1067,9 @@
     yes

 atomCompile = (ctx, atom) ->
-  {token} = atom
+  {symbol} = atom
   # Syntax
-  atom.label = regexMapping token,
+  atom.label = regexMapping symbol,
     ['numerical', /^~?\d+/]
     ['const', /^[A-Z][^\s]*$/]
     ['string', /^["]/]
@@ -1110,19 +1080,19 @@
   {type, translation, pattern} =
     switch label
       when 'numerical'
-        numericalCompile ctx, token
+        numericalCompile ctx, symbol
       when 'regex'
-        regexCompile ctx, token
+        regexCompile ctx, symbol
       when 'char'
         type: typeConstant 'Char'
-        translation: token
-        pattern: literalPattern ctx, token
+        translation: symbol
+        pattern: literalPattern ctx, symbol
       when 'string'
         type: typeConstant 'String'
-        translation: token
-        pattern: literalPattern ctx, token
+        translation: symbol
+        pattern: literalPattern ctx, symbol
       else
-        nameCompile ctx, atom, token
+        nameCompile ctx, atom, symbol
   atom.tea = type if type
   if ctx.isOperator()
     # TODO: maybe don't use label here, it's getting confusing what is its purpose
@@ -1132,84 +1102,84 @@
   else
     assignCompile ctx, atom, translation

-nameCompile = (ctx, atom, token) ->
-  contextType = ctx.type token
+nameCompile = (ctx, atom, symbol) ->
+  contextType = ctx.type symbol
   if exp = ctx.assignTo()
     if atom.label is 'const'
       if contextType
-        type: freshInstance ctx, ctx.type token
-        pattern: constPattern ctx, token
+        type: freshInstance ctx, ctx.type symbol
+        pattern: constPattern ctx, symbol
       else
-        log "deferring in pattern for #{token}"
-        ctx.setDefer token
+        log "deferring in pattern for #{symbol}"
+        ctx.setDefer symbol
         pattern: []
     else
       atom.label = 'name'
       type = ctx.freshTypeVariable star
-      ctx.addToDefinedNames {name: token, type: type}
+      ctx.addToDefinedNames {name: symbol, type: type}
       type: type
       pattern:
         assigns:
-          [[(validIdentifier token), exp]]
+          [[(validIdentifier symbol), exp]]
   else
     # Name typed, use a fresh instance
     if contextType and contextType not instanceof TempType
       {
         type: freshInstance ctx, contextType
-        translation: nameTranslate ctx, atom, token
+        translation: nameTranslate ctx, atom, symbol
       }
     # Inside function only defer compilation if we don't know arity
-    else if ctx.isInLateScope() and (ctx.declaration token) or contextType instanceof TempType
+    else if ctx.isInLateScope() and (ctx.declaration symbol) or contextType instanceof TempType
       # Typing deferred, use an impricise type var
       type = ctx.freshTypeVariable star
-      ctx.addToDeferredNames {name: token, type: type}
+      ctx.addToDeferredNames {name: symbol, type: type}
       {
         type: type
-        translation: nameTranslate ctx, atom, token
+        translation: nameTranslate ctx, atom, symbol
       }
     else
-      log "deferring for #{token}"
-      ctx.setDefer token
+      log "deferring for #{symbol}"
+      ctx.setDefer symbol
       translation: 'deferred'

-constPattern = (ctx, token) ->
+constPattern = (ctx, symbol) ->
   exp = ctx.assignTo()
-  precs: [(cond_ switch token
+  precs: [(cond_ switch symbol
       when 'True' then "#{exp}"
       when 'False' then "!#{exp}"
       else
-        "#{exp} instanceof #{token}")]
-
-nameTranslate = (ctx, atom, token) ->
+        "#{exp} instanceof #{symbol}")]
+
+nameTranslate = (ctx, atom, symbol) ->
   if atom.label is 'const'
-    switch token
+    switch symbol
       when 'True' then 'true'
       when 'False' then 'false'
       else
-        "#{validIdentifier token}.value"
-  else
-    validIdentifier token
-
-numericalCompile = (ctx, token) ->
-  translation = if token[0] is '~' then "(-#{token})" else token
+        "#{validIdentifier symbol}.value"
+  else
+    validIdentifier symbol
+
+numericalCompile = (ctx, symbol) ->
+  translation = if symbol[0] is '~' then "(-#{symbol})" else symbol
   type: typeConstant 'Num'
   translation: translation
   pattern: literalPattern ctx, translation

-regexCompile = (ctx, token) ->
+regexCompile = (ctx, symbol) ->
   type: typeConstant 'Regex'
-  translation: token
+  translation: symbol
   pattern:
     if ctx.assignTo()
-      precs: [cond_ "#{ctx.assignTo()}.string" + " === #{token}.string"]
+      precs: [cond_ "#{ctx.assignTo()}.string" + " === #{symbol}.string"]

 literalPattern = (ctx, translation) ->
   if ctx.assignTo()
     precs: [cond_ "#{ctx.assignTo()}" + " === #{translation}"]

-regexMapping = (token, regexes...) ->
+regexMapping = (symbol, regexes...) ->
   for [label, regex] in regexes
-    if regex.test token
+    if regex.test symbol
       return label

 # type expressions syntax
@@ -1287,7 +1257,7 @@
   args.join ', '

 isComment = (expression) ->
-  (isCall expression) and ('#' is _token _operator expression)
+  (isCall expression) and ('#' is _symbol _operator expression)

 isCall = (expression) ->
   (isForm expression) and (isEmptyForm expression) and
@@ -1311,14 +1281,14 @@
   Array.isArray expression

 isLabel = (atom) ->
-  /:$/.test atom.token
+  /:$/.test atom.symbol

 isCapital = (atom) ->
-  /[A-Z]/.test atom.token
+  /[A-Z]/.test atom.symbol

 isName = (expression) ->
   throw "Nothing passed to isName" unless expression
-  (isAtom expression) and /[^~"'\/].*/.test expression.token
+  (isAtom expression) and /[^~"'\/].*/.test expression.symbol

 isAtom = (expression) ->
   not (Array.isArray expression)
@@ -1385,9 +1355,9 @@

 _fst = ([a, b]) -> a

-_labelName = (atom) -> (_token atom)[0...-1]
-
-_token = ({token}) -> token
+_labelName = (atom) -> (_symbol atom)[0...-1]
+
+_symbol = ({symbol}) -> symbol

 filterAst = (test, expression) ->
   join (filter test, [expression]),
@@ -1455,7 +1425,7 @@
   word

 isDelim = (token) ->
-  /[\(\)\[\]\{\}]/.test token.token
+  /[\(\)\[\]\{\}]/.test token.symbol

 # TODO: support classes and instances
 # classDefinition = (node) ->
@@ -1523,11 +1493,11 @@
 # Syntax printing to HTML

 toHtml = (highlighted) ->
-  crawl highlighted, (word, token, parent) ->
-    (word.ws or '') + colorize(theme[labelOf word, parent], token)
+  crawl highlighted, (word, symbol, parent) ->
+    (word.ws or '') + colorize(theme[labelOf word, parent], symbol)

 labelOf = (word, parent) ->
-  if (_isCollectionDelim word) and parent
+  if (isCollectionDelim word) and parent
     parent.label
   else
     word.label or 'normal'
@@ -1561,8 +1531,8 @@
   wheres = whereList inside preCompileDefs source
   names = []
   for [pattern] in wheres
-    if pattern.token and pattern.token isnt '_'
-      names.push pattern.token
+    if pattern.symbol and pattern.symbol isnt '_'
+      names.push pattern.symbol
   names

 # Valid identifiers
@@ -1700,7 +1670,7 @@
 #           addToSet child.set, name for name in parent.names unless child is parent
 #           addToSet deps, parent
 #         else if isReference(node) and !lookupIdentifier token, node
-#           addToSet child.missing, node.token
+#           addToSet child.missing, node.symbol
 #   [graph, lookupByName]

 # lookupTableForGraph = (graph) ->
@@ -1715,7 +1685,7 @@
 #   names = []
 #   crawl pattern, (node) ->
 #     if node.label is 'name'
-#       names.push node.token
+#       names.push node.symbol
 #   names

@@ -1725,7 +1695,7 @@
 # (pattern) ->
 #   # expect lists from here on
 #   if not Array.isArray pattern
-#     throw new Error "pattern match expected pattern but saw token #{pattern.token}"
+#     throw new Error "pattern match expected pattern but saw token #{pattern.symbol}"
 #   [constr, elems...] = inside pattern
 #   label = "'#{constructorToJsField constr}'" if constr
 #   trigger: isMap pattern
@@ -1775,7 +1745,7 @@
 #       #{content} else {throw new Error('match failed to match');}}())"""
 #   'require': (from, list) ->
 #     args = inside(list).map(compileName).map(toJsString).join ', '
-#     "$listize(window.requireModule(#{toJsString from.token}, [#{args}]))"
+#     "$listize(window.requireModule(#{toJsString from.symbol}, [#{args}]))"
 #   'list': (items...) ->
 #     "$listize(#{compileList items})"

@@ -1803,8 +1773,8 @@
 #         set: stillMissingDeps
 #   [hoisted, notHoisted]

-toJsString = (token) ->
-  "'#{token}'"
+toJsString = (symbol) ->
+  "'#{symbol}'"

 compileAssign = ([to, from]) ->
   "var #{to} = #{from};"
@@ -2469,25 +2439,28 @@

-exports.compile = (source) ->
-  library + compileDefinitions source
-
-exports.compileModule = (source) ->
-  """
-  #{library}
-  var exports = {};
-  #{compileDefinitionsInModule source}
-  exports"""
-
-exports.compileExp = compiledBottom
-
-exports.tokenize = tokenizeAndTypeInferDefinitions
-
-exports.tokenizeExp = tokenizedExp
-
-exports.syntaxedExpHtml = syntaxedExp
-
-exports.exportList = exportList
+exports.compileTopLevel = (ast) ->
+  compiled = topLevel (ctx = new Context), ast
+    types: values mapMap (__ highlightType, _type), ctx._scope()
+    js: library + compiled
+
+exports.compileExpression = (ast) ->
+  compiled = expressionCompile (ctx = new Context), ast
+    types: values mapMap (__ highlightType, _type), ctx._scope()
+    js: compiled
+
+# exports.compileModule = (source) ->
+#   """
+#   #{library}
+#   var exports = {};
+#   #{compileDefinitionsInModule source}
+#   exports"""
+
+exports.astizeList = (source) ->
+  astize tokenize "(#{source})", -1
+
+exports.astize = (source) ->
+  astize tokenize source

 exports.library = library


Auto-bracketing and syntax highlight command line

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -272,6 +272,9 @@

 tokenizedDefinitions = (source) ->
   toTokens [inside typifyDefinitions astize tokenize "(#{source})"]
+
+tokenizedExp = (source) ->
+  toTokens typify astize tokenize source

 variableCounter = 1

@@ -685,3 +688,6 @@

 exports.tokenize = (source) ->
   tokenizedDefinitions source
+
+exports.tokenizeExp = (source) ->
+  tokenizedExp source


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -7,6 +7,7 @@
         #@testData = 123
         #@oldPieData = ''
         @instanceDataId = $(@node).attr('data-id')
+        console.log('ready -> @instanceDataId: ',@instanceDataId)
         #console.log('@instanceDataId',@instanceDataId)
         #@oldPieData[@instanceDataId] = ''
         window.oldPieData = window.oldPieData or {}


another parent() call

another parent() call

--- vue/src/shared/models/group_model.coffee
+++ vue/src/shared/models/group_model.coffee
@@ -168,7 +168,7 @@
       AppConfig.theme.icon_src

   coverUrl: (size = 'large') ->
-    if @parent() && !@hasCustomCover
+    if @parentId && !@hasCustomCover
       @parent().coverUrl(size)
     else
       @coverUrls[size]


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -101,7 +101,7 @@
         mx = 1.2
         my = 1.2
         h = Math.sqrt(x*x + y*y)
-        r = 100
+        r = labelRadius
         if x <= 0
             r = -r
         #return "translate(" + (x/h * labelRadius) + "," + (y/h * labelRadius) + ")"


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -3,7 +3,7 @@
     @accessor 'data'

     ready: ->
-        @oldPieData = ''
+        #@oldPieData = ''

     onData: (data) ->

@@ -149,34 +149,41 @@
         path = pathGroup.selectAll('path.pie').data(piedata)
         path.enter().append('path').attr('class', 'pie').attr('fill', (d, i) -> color i)

-        path.transition().duration(750).attrTween('d', (d,i) ->
-            theOldDataInPie = @oldPieData ? piedata
-            console.log('@oldPieData',@oldPieData)
-            # Interpolate the arcs in data space
-            s0 = undefined
-            e0 = undefined
-            if theOldDataInPie[i]
-                s0 = theOldDataInPie[i].startAngle
-                e0 = theOldDataInPie[i].endAngle
-            else if !theOldDataInPie[i] and theOldDataInPie[i - 1]
-                s0 = theOldDataInPie[i - 1].endAngle
-                e0 = theOldDataInPie[i - 1].endAngle
-            else if !theOldDataInPie[i - 1] and theOldDataInPie.length > 0
-                s0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
-                e0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
-            else
-                s0 = 0
-                e0 = 0
+        #path.transition().duration(750).attrTween('d', (d,i) ->
+            #theOldDataInPie = @oldPieData ? piedata
+            #console.log('@oldPieData',@oldPieData)
+            ## Interpolate the arcs in data space
+            #s0 = undefined
+            #e0 = undefined
+            #if theOldDataInPie[i]
+            #    s0 = theOldDataInPie[i].startAngle
+            #    e0 = theOldDataInPie[i].endAngle
+            #else if !theOldDataInPie[i] and theOldDataInPie[i - 1]
+            #    s0 = theOldDataInPie[i - 1].endAngle
+            #    e0 = theOldDataInPie[i - 1].endAngle
+            #else if !theOldDataInPie[i - 1] and theOldDataInPie.length > 0
+            #    s0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
+            #    e0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
+            #else
+            #    s0 = 0
+            #    e0 = 0
+            #`var i`
+            #i = d3.interpolate({
+            #    startAngle: s0
+            #    endAngle: e0
+            #},
+            #    startAngle: d.startAngle
+            #    endAngle: d.endAngle)
+            #(t) ->
+            #    b = i(t)
+            #    return arc b
+        #)
+        path.transition().duration(750).attrTween('d', (d) ->
             `var i`
-            i = d3.interpolate({
-                startAngle: s0
-                endAngle: e0
-            },
-                startAngle: d.startAngle
-                endAngle: d.endAngle)
-            (t) ->
-                b = i(t)
-                return arc b
+            i = d3.interpolate(this._current, a)
+            this._current = i(0)
+            return (t) ->
+                return arc(i(t))
         )
         path.exit()
             #.transition()
@@ -362,5 +369,5 @@
             .attr('font-size',(if typeof totalTickets isnt 'string' then radiusi + 'px' else '2em'))
             .style('opacity', 1)

-        @oldPieData = piedata
+        #@oldPieData = piedata
         return


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -105,7 +105,7 @@
         r = labelRadius
         if x <= 0
             r = -r
-        if x < 0 and y < 0 and x < -radiusi
+        if x < 0 and y < 0 and x > -radiusi
             x = x * mx
         return "translate(" + (x/h * labelRadius) + "," + (y/h * labelRadius) + ")"
         #return "translate(" + r + "," + y + ")"#(y/h * labelRadius) + ")"


TEST: added test for &package - using a reserved word - which is now fully legal with &Require refactor ALL 948 PASSING

--- test/tests/CaffeineScript/Parser/Require.coffee
+++ test/tests/CaffeineScript/Parser/Require.coffee
@@ -25,6 +25,9 @@
     basic:
       "&Lib":                   "require('./Lib');"
       "&Source":                "require('../../source');"
+
+    reservedWords:
+      "&package":               "require('../../package');"

     pathed:
       "&Perf/Perfs":            "require('../../perf/Perfs');"


JVM: comment-out all the code working based on triggers (like memberJoinedGroup..)

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -716,6 +716,8 @@

   wrapGroup = (group)-> [ { id: group.getId() } ]

+  ###
+
   do ->

     JAccount  = require './account'
@@ -927,3 +929,4 @@
                       return userRecord  unless isMatch
                       return { id, sudo: hasPermission }
                   }, handleError
+  ###


spelling: doesn't

spelling: doesn't

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -4992,7 +4992,7 @@
             editor.insertText('\n')
             expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1

-        describe "when the line preceding the newline does't add a level of indentation", ->
+        describe "when the line preceding the newline doesn't add a level of indentation", ->
           it "indents the new line to the same level as the preceding line", ->
             editor.setCursorBufferPosition([5, 14])
             editor.insertText('\n')


App: RouteHandler: on collaboration requests open IDE instead of Home

--- client/app/lib/routehandler.coffee
+++ client/app/lib/routehandler.coffee
@@ -140,19 +140,19 @@

   kd.singletons.mainController.ready ->

-    whoami().pushNotification
     if nickname is nick()
       return kd.singletons.router.back()
+
+    kd.utils.defer -> kd.singletons.router.handleRoute '/IDE'
+
+    me = whoami()
+    me.pushNotification
       receiver: nickname
       channelId: channelId
       action: 'COLLABORATION_REQUEST'
-      senderUserId: whoami()._id
-      senderAccountId: whoami().socialApiId
+      senderUserId: me._id
+      senderAccountId: me.socialApiId
     , (err) ->

-      # FIXME: better error message
-      return new kd.NotificationView { title: 'There is an error' }  if err
-
-      kd.singletons.router.back()
-
-
+      showError err
+      kd.singletons.router.back()


client: remove unnecessary docs

client: remove unnecessary docs

--- client/app/lib/setupcountly.coffee
+++ client/app/lib/setupcountly.coffee
@@ -7,9 +7,7 @@
   c.q = []
   c.app_key = appKey
   c.url = '/countly'
-  # track sessions automatically
   c.q.push ['track_sessions']
-  #track sessions automatically
   c.q.push ['track_pageview']

   window.Countly = c


debug autoatic loading after registering

--- client/app/views/login_view.coffee
+++ client/app/views/login_view.coffee
@@ -19,6 +19,7 @@
     # Send a login request to backend. If it succeeds, home view is displayed
     # else an error message is displayed.
     logUser: (password) =>
+        @errorAlert = $ "#login-error" if @errorAlert?
         @errorAlert.hide()
         $.ajax
             type: 'POST'


stack-editor: fix wrong require result of wrong rebase

--- client/stack-editor/lib/index.coffee
+++ client/stack-editor/lib/index.coffee
@@ -1,7 +1,7 @@
 _ = require 'lodash'
 kd = require 'kd'
 AppController = require 'app/appcontroller'
-DefineStackView = require 'stacks/views/stacks/definestackview'
+StackEditorView = require './editor'
 showError = require 'app/util/showError'
 OnboardingView = require 'stacks/views/stacks/onboarding/onboardingview'
 EnvironmentFlux = require 'app/flux/environment'


Refactor

Refactor

--- client/src/ActionController.coffee
+++ client/src/ActionController.coffee
@@ -9,15 +9,14 @@
 #
 class ActionController
     constructor: ->
-        @robot = robot
+        @screenSize = robot.getScreenSize()
+        console.log "Screen size: ", @screenSize
     mouseMove: (handModel) =>
-        screenSize = @robot.getScreenSize()
-        console.log "Screen size: ", screenSize
         moveTo =
-            x: handModel.position.x * screenSize.width
-            y: handModel.position.y * screenSize.height
+            x: handModel.position.x * @screenSize.width
+            y: handModel.position.y * @screenSize.height
         console.log "Move to: ", moveTo
-        @robot.moveMouse(moveTo.x, moveTo.y)
+        robot.moveMouse(moveTo.x, moveTo.y)
     parseGestures: (handModel) =>
         console.log "handModel: ", handModel
         # Mouse Move test


Tweak ie8 shim syntax

Tweak ie8 shim syntax

--- keypress.coffee
+++ keypress.coffee
@@ -633,11 +633,10 @@
         return false unless item in a2
     return true

-_index_of_in_array = Array.prototype.indexOf || ((arr, el) ->
-    for i in [0..arr.length]
-        return i if arr[i] is el
+_index_of_in_array = Array.prototype.indexOf or (a, item) ->
+    for i in [0..a.length]
+        return i if a[i] is item
     return -1
-)

 _is_array_in_array_sorted = (a1, a2) ->
     # Return true only if all of the contents of
@@ -645,7 +644,7 @@
     # same order in both.
     prev = 0
     for item in a1
-        index = _index_of_in_array.call(a2, item)
+        index = _index_of_in_array.call a2, item
         if index >= prev
             prev = index
         else


Update atom config

Update atom config

--- atom/atom.symlink/config.cson
+++ atom/atom.symlink/config.cson
@@ -1,20 +1,14 @@
 "*":
+  "autocomplete-python":
+    useKite: false
+  "command-palette": {}
   core:
     disabledPackages: [
-      "exception-reporting"
       "metrics"
-      "figlet"
-      "linter-flake8"
-      "jslint"
-      "latextools"
-      "latex-completions"
-      "latex"
       "markdown-preview"
+      "atom-css-comb"
       "emmet"
-      "uber-juno"
-      "tool-bar"
-      "julia-client"
-      "ink"
+      "archive-view"
     ]
     excludeVcsIgnoredPaths: false
     hideGitIgnoredFiles: false
@@ -23,20 +17,34 @@
       ".git"
       "tmp"
     ]
+    packagesWithKeymapsDisabled: [
+      "emmet"
+    ]
+    telemetryConsent: "limited"
     themes: [
       "one-dark-ui"
       "material-syntax"
     ]
   editor:
-    fontSize: 13
+    fontSize: 15
     invisibles: {}
     scrollPastEnd: true
     showIndentGuide: true
     showInvisibles: true
     softWrapAtPreferredLineLength: true
-  emmet: {}
+  emmet:
+    useEmmetComments: true
+  "exception-reporting":
+    userId: "5f65f7e8-d082-bbd4-6180-10399e1a9442"
+  "file-icons":
+    coloured: false
   "git-log": {}
   "git-plus": {}
+  # JULIA:
+  "julia-client":
+    enableMenu: false
+    enableToolBar: false
+    launchOnStartup: false
   "latex-plus":
     advancedEnabled: true
     ghostscriptBin: "/usr/local/bin/"
@@ -44,15 +52,6 @@
   latextools:
     darwin:
       texpath: "/Library/TeX/texbin:/usr/texbin:/usr/local/bin"
-  linter: {}
-  "linter-chktex":
-    chktexArgs: [
-      "-n22"
-      "-n30"
-      "-e16"
-    ]
-  "linter-flake8": {}
-  "markdown-pdf": {}
   "markdown-preview-plus":
     enableLatexRenderingByDefault: true
     enablePandoc: true
@@ -60,6 +59,7 @@
     pandocPath: "/usr/local/bin/pandoc"
     pandocRemoveReferences: false
     useGitHubStyle: true
+    useLazyHeaders: false
   pigments:
     extendAutocompleteToVariables: true
   "spell-check":
@@ -69,20 +69,12 @@
       "text.git-commit"
       "text.tex.latex"
     ]
-  tidal:
-    ghciPath: "/usr/local/bin/ghc"
+  "tool-bar":
+    position: "Left"
+    visible: false
+  "uber-juno":
+    disable: true
   welcome:
     showOnStartup: false
-".bibtex.text":
-  editor:
-    softWrap: false
-    softWrapAtPreferredLineLength: true
-".latex.memoir.tex.text":
-  editor:
-    softWrapAtPreferredLineLength: false
-".latex.tex.text":
-  editor:
-    softWrap: true
-    softWrapAtPreferredLineLength: false
 "tree-view":
   hideVcsIgnoredFiles: false


Well, we should set params regardless

Well, we should set params regardless

--- src/quest.coffee
+++ src/quest.coffee
@@ -246,9 +246,8 @@
       view = null
     if typeof(queries) != 'string'
       split = queries.split if queries.split?
-      if queries.text
-        params = queries.params
-      else
+      params = queries.params
+      if queries.file
         sqlPath = path.join @sqlPath, queries.file
         console.log ">>".blue.bold, "#{sqlPath}".blue.bold if queries.file
       rawQueries = queries.text or fs.readFileSync(sqlPath, encoding: 'utf-8')


code cleanup

code cleanup

--- src/range.coffee
+++ src/range.coffee
@@ -34,6 +34,26 @@

     [@node, @offset] = TandemPosition.adjustOffset(@node, @offset)
     @text = @node.textContent   # For testing convenience
+
+  getAncestorNodes: (includeSelf = false) ->
+    ancestors = []
+    node = @node
+    ancestors.push(node) if includeSelf && node.className != 'line'
+    while node.className != 'line'
+      ancestors.push(node)
+      node = node.parentNode
+    ancestors.push(node)
+    return ancestors
+
+  getAttributes: ->
+    return _.reduce(this.getAncestorNodes(), (attributes, node) ->
+      switch node.tagName
+        when 'B' then attributes['bold'] = true
+        when 'I' then attributes['italic'] = true
+        when 'S' then attributes['strike'] = true
+        when 'U' then attributes['underline'] = true
+      return attributes
+    , {})

   getIndex: ->
     return @index if @index?
@@ -47,10 +67,8 @@
     return @index

   getLine: ->
-    line = @node
-    while @node.className != 'line'
-      line = @node.parentNode
-    return line
+    ancestors = this.getAncestorNodes()
+    return ancestors[ancestors.length - 1]

 class TandemRange
@@ -78,16 +96,8 @@

   getAttributeIntersection: ->
     attributes = null
-    console.log 'leaf', this.getLeafNodes()
-    _.all(this.getLeafNodes(), (node) ->
-      leafAttributes = []
-      while node.className != 'line'
-        switch node.tagName
-          when 'B' then leafAttributes['bold'] = true
-          when 'I' then leafAttributes['italic'] = true
-          when 'S' then leafAttributes['strike'] = true
-          when 'U' then leafAttributes['underline'] = true
-        node = node.parentNode
+    _.all(this.getLeafPositions(), (position) ->
+      leafAttributes = position.getAttributes()
       if attributes
         _.each(attributes, (value, key) ->
           if leafAttributes[key] != true
@@ -95,6 +105,9 @@
         )
       else
         attributes = leafAttributes
+      for key,value of attributes when value == true
+        return true
+      return false
     )

     return attributes || {}
@@ -110,22 +123,26 @@
     if range.collapsed
       return [@start.node]
     else
-      return _.map(range.getNodes([3]), (node) -> return node.parentNode)
+      return _.map(range.getNodes([3]), (node) ->
+        return node.parentNode
+      )
+
+  getLeafPositions: ->
+    return _.map(this.getLeafNodes(), (node) =>
+      return new TandemPosition(@editor, node, 0)
+    )

   groupNodesByLine: ->
-    currentAncestor = 0
-    arr = _.reduce(this.getLeafNodes(), (memo, node) ->
-      ancestor = node.parentNode
-      while ancestor.className != "line"
-        ancestor = ancestor.parentNode
-      if currentAncestor == ancestor
-        memo[memo.length - 1].push(node)
+    currentLine = 0
+    return _.reduce(this.getLeafPositions(), (lines, position) ->
+      line = position.getLine()
+      if currentLine == line
+        lines[lines.length - 1].push(position.node)
       else
-        memo.push([node])
-        currentAncestor = ancestor
-      return memo
+        lines.push([position.node])
+        currentLine = line
+      return lines
     , [])
-    return arr

   split: (position, before = true) ->
     newNode = @editor.iframeDoc.createElement(position.node.tagName)


viewSpec: stylefix: remove trailing semicolons

--- coffee/test/views/view.spec.coffee
+++ coffee/test/views/view.spec.coffee
@@ -5,7 +5,7 @@

 describe 'athena.lib.View', ->
-  View = athena.lib.View;
+  View = athena.lib.View

   test.describeView View, Backbone.View

@@ -82,8 +82,8 @@
     expect(renderSpy2.callCount).toBe 1

     # call destroy, check state,
-    view1.destroy();
-    view2.destroy();
+    view1.destroy()
+    view2.destroy()
     expect(view1.rendering).toBe false
     expect(view2.rendering).toBe false


when patching the localStorage copy of the data with the localStorage copy of the send_queue, only patch the data sent to the client, not the data stored in the library, which is the library's representation of the server data

--- sync.coffee
+++ sync.coffee
@@ -262,7 +262,7 @@
 		for item in queue
 			@data.send_queue.push item
 		@_save_queue()
-		@_send_changes false
+		@data.send_queue_timer = setTimeout @_send_changes, 100

 	_save_entity: (id) =>
 		if not @s.supports_html5_storage()
@@ -302,14 +302,14 @@

 		if @loaded
 			for own id, entity of @data.store
+				entity_copy = @jd.deepCopy(entity)
 				for change in @data.send_queue
 					if String(id) is String(change.id)
 						try
-							entity.object = @jd.apply_object_diff entity.object, change.v
+							entity_copy.object = @jd.apply_object_diff entity_copy.object, change.v
 						catch error
-							console.error error
-							console.log id, entity, change
-				@_notify_client id, entity.object, entity.version
+							console.log id, entity_copy, change
+				@_notify_client id, entity_copy.object, entity_copy.version
 			@loaded = 0

 		@started = true
@@ -741,7 +741,6 @@
 		# Add to the pending queue.  It will be removed from the queue when the
 		# change is successfully processed by the server
 		@data.send_queue.push change
-		console.log "ABOUT TO SAVE"
 		@_save_queue()
 		# Request: Change, change data
 		@send("c:#{JSON.stringify(change)}")
@@ -758,22 +757,21 @@
 	# Runs on a timeout to send any pending changes.
 	# Queues each change to rerun in case of failure.
 	# For each failure, the timeout delay increases.
-	_send_changes: ( increase_backoff = true ) =>
+	_send_changes: =>
 		if @data.send_queue.length is 0
 			console.log "#{@name}: send_queue empty, done"
 			@data.send_queue_timer = null
 			return
 		if not @s.connected
-			console.log "#{@name}: _send_changes: not connected"
+			console.log "#{@name}: _send_changes: not connected", @_send_backoff
 		else
 			for change in @data.send_queue
 				console.log "#{@name}: sending change: #{JSON.stringify(change)}"
 				@send("c:#{JSON.stringify(change)}")

-		if increase_backoff
-			@_send_backoff = @_send_backoff * 2
-			if @_send_backoff > @_backoff_max
-				@_send_backoff = @_backoff_max
+		@_send_backoff = @_send_backoff * 2
+		if @_send_backoff > @_backoff_max
+			@_send_backoff = @_backoff_max

 		@data.send_queue_timer = setTimeout @_send_changes, @_send_backoff

@@ -786,6 +784,7 @@
 	on_changes: (response) =>
 		check_updates = []
 		reload_needed = false
+		# @todo reset @_send_backoff to @_backoff_min when we receive any data, not just changes
 		@_send_backoff = @_backoff_min
 		console.log "#{@name}: on_changes(): response="
 		console.log response
@@ -793,12 +792,14 @@
 			id = change['id']
 			console.log "#{@name}: processing id=#{id}"
 			pending_to_delete = []
+			console.log change, @data.send_queue
 			for pending in @data.send_queue
-				if change['clientid'] is @clientid and id is pending['id']
-#                    console.log "#{@name}: deleting change for id #{id}"
+				if change['clientid'] is @clientid
 					change['local'] = true
-					pending_to_delete.push pending
 					check_updates.push id
+					if id is pending['id']
+#						console.log "#{@name}: deleting change for id #{id}"
+						pending_to_delete.push pending
 			for pd in pending_to_delete
 				@data.store[pd['id']]['change'] = null
 				@_save_entity pd['id']


Use caching for the scale property.

Use caching for the scale property.

--- bokehjs/src/coffee/renderer/guide/grid.coffee
+++ bokehjs/src/coffee/renderer/guide/grid.coffee
@@ -52,7 +52,7 @@

       # FIXME Is it better to register a property?  Or just use a member
       # variable?
-      @register_property('scale', @_scale, false)
+      @register_property('scale', @_scale, true)
       @add_dependencies('scale', this, ['is_datetime'])

       @register_property('grid_coords', @_grid_coords, false)


match changed to test

match changed to test

--- client/Framework/core/utils.coffee
+++ client/Framework/core/utils.coffee
@@ -243,12 +243,13 @@
       return prefix if prefix.length >= length

       if memorable
-        pattern = if prefix.match(consonant) then vowel else consonant
-
-      n = (Math.floor(Math.random() * 100) % 94) + 33
+        pattern = if consonant.test(prefix) then vowel else consonant
+
+      n    = (Math.floor(Math.random() * 100) % 94) + 33
       char = String.fromCharCode(n)
       char = char.toLowerCase() if memorable
-      if !char.match pattern
+
+      unless pattern.test char
         return __utils.generatePassword length, memorable, pattern, prefix

       return __utils.generatePassword length, memorable, pattern, "" + prefix + char


KodingKite: instead sending all calls when they arrived, wait for connect state

--- client/Main/kite/kodingkite.coffee
+++ client/Main/kite/kodingkite.coffee
@@ -17,6 +17,10 @@
     @on 'close', =>
       console.log "DISCONNECTED from #{name}"
       @_state = DISCONNECTED
+
+    @waitingCalls = []
+    @waitingPromises = []
+
   extractInfoFromWsEvent = (event)->
     {reason, code, wasClean, timestamp, type} = event

@@ -44,15 +48,27 @@
     @emit 'ready'

   tell: (rpcMethod, params, callback) ->
+
+    { name } = @getOptions()
+
+    console.log "TELL :: #{name}.#{rpcMethod}"
+
     @connect()  if not @_connectAttempted or @isDisconnected

     unless @invalid

-      @ready().then => @transport?.tell rpcMethod, [params], callback
+      promise = new Promise (resolve, reject)=>
+
+        (@waitForConnection [rpcMethod, [params], callback]).then (args)=>
+          console.info "# CONNECTED TO #{name} SENDING #{rpcMethod}... "
+          resolve @transport?.tell args...
+
+      unless @_state is CONNECTED
+        @waitingPromises.push promise
+
+      promise

     else
-
-      { name } = @getOptions()

       Promise.reject
         name    : "KiteInvalid"
@@ -93,3 +109,20 @@

     KD.utils.wait 1000, =>
       @transport?.connect()
+
+
+  waitForConnection: (args)->
+
+    { name } = @getOptions()
+
+    new Promise (resolve, reject)=>
+      return resolve args if @_state is CONNECTED
+
+      cid = (@waitingCalls.push args) - 1
+
+      console.log "# WAITING CALLS ON #{name}:", @waitingCalls
+
+      @once 'connected', ->
+        resolve @waitingCalls[cid]
+        delete  @waitingCalls[cid]
+


Fixed: default parameters are now applied for each matched route (default route now uses default parameters)

--- client/app/libs/PanelRouter.coffee
+++ client/app/libs/PanelRouter.coffee
@@ -117,14 +117,36 @@
             args = @_extractParameters route.pattern, secondPanelString
             # remove the last argument which is alway `null`, not sure why
             args.pop()
-            secondPanelInfo = action: route.key, parameters: args
+
+            # normalizes the secondPanelInfo and adds default parameters if
+            # needed
+            secondPanelInfo = @_mergeDefaultParameter
+                action: route.key
+                parameters: args
         else
             secondPanelInfo = null

-
-        # normalizes the firstPanelInfo
-        firstPanelInfo = action: name, parameters: firstPanelParameters
+        # normalizes the firstPanelInfo and adds default parameters, if needed
+        firstPanelInfo = @_mergeDefaultParameter
+            action: name
+            parameters: firstPanelParameters
+
         return [firstPanelInfo, secondPanelInfo]
+
+
+    ###
+        Turns a parameters array into an object of named parameters
+    ###
+    _arrayToNamedParameters: (patternName, parametersArray) ->
+
+        namedParameters = {}
+        parametersName = @patterns[patternName].pattern.match(/:[\w]+/g) or []
+        for paramName, index in parametersName
+            # Removes the initial ":"
+            unPrefixedParamName = paramName.substr 1
+            namedParameters[unPrefixedParamName] = parametersArray[index]
+
+        return namedParameters

     ###
@@ -203,6 +225,7 @@

     # Builds the URL string from a route.
     _getURLFromRoute: (panel) ->
+
         # Clones the parameter because we are going to mutate it
         panel = _.clone panel

@@ -212,18 +235,7 @@
             if panel.parameters? and not (panel.parameters instanceof Array)
                 panel.parameters = [panel.parameters]

-            # gets default values (if relevant) to ease `@buildUrl` usage
-            if (defaultParameters = @_getDefaultParameters(panel.action))?
-                # sets the parameters if they don't exist at all...
-                if not panel.parameters? or panel.parameters.length is 0
-                    panel.parameters = defaultParameters
-
-                # ... or adds them in the relevant place if only some of them
-                # are missing
-                else
-                    for defaultParameter, key in defaultParameters
-                        if not panel.parameters[key]?
-                            panel.parameters.splice key, 0, defaultParameter
+            panel = @_mergeDefaultParameter panel

             # we default to empty array if there is no parameter in the route
             parametersInPattern = pattern.match(/:[\w]+/gi) or []
@@ -239,3 +251,19 @@
             return filledPattern
         else
             return ''
+
+
+    # Merges defaut parameters into a panel info if there are missing parameters
+    _mergeDefaultParameter: (panelInfo) ->
+        panelInfo = _.clone panelInfo
+        parameters = _.clone panelInfo.parameters or []
+        # gets default values, if there are
+        if (defaultParameters = @_getDefaultParameters panelInfo.action)?
+
+            # merges the parameters in the relevant place
+            for defaultParameter, key in defaultParameters
+                if not parameters[key]?
+                    parameters.splice key, 0, defaultParameter
+
+        panelInfo.parameters = parameters
+        return panelInfo


Disabled syntax highlighting

Disabled syntax highlighting

--- community/server/src/main/coffeescript/neo4j/webadmin/modules/databrowser/views/ConsoleView.coffee
+++ community/server/src/main/coffeescript/neo4j/webadmin/modules/databrowser/views/ConsoleView.coffee
@@ -22,10 +22,10 @@
   ['ribcage/View'
    'neo4j/webadmin/utils/Keys'
    'lib/amd/CodeMirror'
-   'neo4j/codemirror/cypher'
+   #'neo4j/codemirror/cypher' # Disabled awaiting a test suite for it
    './consoleTemplate'
    'lib/amd/jQuery.putCursorAtEnd'],
-  (View, Keys, CodeMirror, cypherHighlighting, template, $) ->
+  (View, Keys, CodeMirror, template, $) ->

     class ConsoleView extends View

@@ -47,7 +47,8 @@
         @_editor = CodeMirror($("#data-console").get(0),{
           value: @dataModel.getQuery()
           onKeyEvent: @onKeyEvent
-          mode: "text/x-cypher"
+          #mode: "text/x-cypher"
+          mode : "text"
         })

         # WebDriver functional tests are unable to enter


Synchronous WirePattern is deprecated

Synchronous WirePattern is deprecated

--- components/FindFreeRectangles.coffee
+++ components/FindFreeRectangles.coffee
@@ -165,6 +165,7 @@
     params: ['threshold', 'max']
     out: 'out'
     forwardGroups: true
+    async: true
   , (payload, groups, out, callback) ->
     {canvas, polygon} = payload
     {threshold, max} = c.params
@@ -172,5 +173,5 @@
     validRects = compute canvas, polygon, threshold, max

     out.send validRects
-
-  c+    do callback
+    return


KdModal: default width changed

KdModal: default width changed

--- components/modals/KDModalView.coffee
+++ components/modals/KDModalView.coffee
@@ -4,7 +4,7 @@
     options.overlay       ?= no            # a Boolean
     options.overlayClick  ?= yes           # a Boolean
     options.height       or= "auto"        # a Number for pixel value or a String e.g. "100px" or "20%" or "auto"
-    options.width         ?= 400           # a Number for pixel value or a String e.g. "100px" or "20%"
+    options.width         ?= 600           # a Number for pixel value or a String e.g. "100px" or "20%"
     options.position     or= {}            # an Object holding top and left values
     options.title        or= null          # a String of text or HTML
     options.subtitle     or= null          # a String of text or HTML


This trailing space was really annoying me

--- src/slack.coffee
+++ src/slack.coffee
@@ -11,7 +11,7 @@

   run: ->
     # Take our options from the environment, and set otherwise suitable defaults
-    options =
+    options =
       token: process.env.HUBOT_SLACK_TOKEN
       autoReconnect: true
       autoMark: true


Finder: Fix the Uploader o_O

Finder: Fix the Uploader o_O

--- client/finder/AppController.coffee
+++ client/finder/AppController.coffee
@@ -29,7 +29,7 @@
       partial  : "Ace Editor"

   getMountVMButton: ->
-    @uploaderPlaceholder = new KDButtonView
+    return new KDButtonView
       title    : "Mount other VMs"
       cssClass : "finder-mountvm clean-gray"
       callback : @bound 'showMountVMModal'


Fix glitchy Player#cameraFocus in Phaser 2.4.4.

--- src/player.coffee
+++ src/player.coffee
@@ -46,6 +46,7 @@
       @sprite.anchor = new Point 0.5, 0.5

       @cameraFocus = game.add.sprite x, y
+      @cameraFocus.visible = off

       @animations = @sprite.animations
       @_initAnimations()


test instances separately from factory method

--- src/mixinfactory.spec.coffee
+++ src/mixinfactory.spec.coffee
@@ -1,37 +1,64 @@
-fdescribe 'mixinfactory', ->
+describe 'mixinfactory', ->

   Mixin = require './mixinfactory'
-  UTIL = require './util'

-  it 'should reject bad mixin types', ->
-    invalid_mixin_args = [
-      []
-      'string'
-      1
-      null
-      undefined
-    ]

-    for invalid_mixin_arg in invalid_mixin_args
+  describe 'factory', ->
+
+    it 'should reject bad mixin types', ->
+      invalid_mixin_args = [
+        []
+        'string'
+        1
+        null
+        undefined
+      ]
+
+      for invalid_mixin_arg in invalid_mixin_args
+        expect(->
+          Mixin.make_mixin invalid_mixin_arg
+        ).toThrow new TypeError "Expected non-empty mixin object"
+
+    it 'should reject objects with no name property', ->
       expect(->
-        Mixin.make_mixin invalid_mixin_arg
-      ).toThrow new TypeError "Expected non-empty mixin object"
+        Mixin.make_mixin quack: -> console.log 'Quack!'
+      ).toThrow new Mixin.ArgumentError "Expected String name in options argument"

-  it 'should reject objects with no name property', ->
-    expect(->
-      Mixin.make_mixin quack: -> console.log 'Quack!'
-    ).toThrow new UTIL.ArgumentError "Expected String name in options argument"
+    it 'should return a Mixin', ->
+      mixin = Mixin.make_mixin
+        speak: ->
+          'Hello, World!'
+        name: 'Speaker'

-  it 'should return an immutable Mixin', ->
-    mixin = Mixin.make_mixin
-      speak: ->
-        'Hello, World!'
-      name: 'Speaker'
+      expect(mixin instanceof Mixin).toBe true

-    expect(mixin instanceof Mixin).toBe true
-    expect(mixin.mixin_keys).toEqual ['name', 'speak']
+  describe 'Mixin', ->

-    for key in mixin.mixin_keys
-      expect(->
-        mixin[key] = null
-      ).toThrow new Error "Cannot change #{key} on Mixin(Speaker); Mixins are immutable"+    beforeEach ->
+      @mixin = Mixin.make_mixin
+        speak: ->
+          "Hello, my name is #{@name}!"
+        shout: ->
+          "HELLO, MY NAME IS #{@name}!"
+        whisper: ->
+          "Shh, my name is #{@name}!"
+        name: 'Speaker'
+
+    it 'should have a sorted mixin_keys property', ->
+      expect(@mixin.mixin_keys).toEqual ['name', 'shout', 'speak', 'whisper']
+
+    it 'should be immutable with loud failures on change attempts', ->
+      for key in @mixin.mixin_keys
+        expect(=>
+          @mixin[key] = null
+        ).toThrow new Mixin.MutabilityError "Cannot change #{key} on Mixin(Speaker)"
+
+    it 'should have a frozen prototype with silent failures on change attempts', ->
+      old_keys = Object.keys Mixin::
+
+      Mixin::foo = 'bar'
+      Mixin::baz = ->
+        'qux'
+
+      expect(Object.keys(Mixin::)).toEqual old_keys
+


add second navigation by default

add second navigation by default

--- src/plugin.coffee
+++ src/plugin.coffee
@@ -64,20 +64,17 @@
     data = $el.data 'flexirails'

     $el.append data.createFlexiTable data
-    data.flexiTable = $el.find '.fr-table'
-
-    $el.data 'flexirails', data
+    plugin.flexiTable = $el.find '.fr-table'

   # append flexirails navigation elements
   createNavigation = ->
     data = $el.data 'flexirails'

-    data.flexiTable.prepend data.createFlexiNavigation plugin.adapter
+    $el.append data.createFlexiNavigation plugin.adapter
+    $(data.createFlexiNavigation plugin.adapter).insertBefore plugin.flexiTable
     plugin.flexiNavigation = $el.find '.fr-navigation'

     bindNavigation()
-
-    $el.data 'flexirails', data

   # bind events to navigational elements
   bindNavigation = ->
@@ -132,7 +129,7 @@

     adapter = plugin.adapter

-    table = data.flexiTable
+    table = plugin.flexiTable
     table.find("tr:not(.fr-header)").remove()

     for item in adapter.paginatedData()
@@ -140,6 +137,11 @@
       table.append data.createFlexiRow rowData
       true

+    updateNavigation()
+
+  updateNavigation = ->
+    adapter = plugin.adapter
+
     currentPage = $el.find '.fr-current-page'
     currentPage.html adapter.options.currentPage

@@ -148,6 +150,9 @@

     totalResults = $el.find '.fr-total-results'
     totalResults.html adapter.options.entries
+
+    perPage = $el.find '.fr-per-page'
+    perPage.val adapter.options.perPage

   # public methods

@@ -155,6 +160,7 @@
   plugin.destroy = ->
     $el.data 'flexirails', null
     $el.find(".fr-table").remove()
+    $el.find(".fr-navigation").remove()

   # set up a datasource for flexirails
   plugin.initializeAdapter = (ds) ->


update selected perPage value prior binding perPage event

--- src/plugin.coffee
+++ src/plugin.coffee
@@ -73,15 +73,20 @@
     data = $el.data 'flexirails'

     data.flexiTable.prepend data.createFlexiNavigation data
-    data.flexiNavigation = $el.find '.fr-navigation'
-
+    plugin.flexiNavigation = $el.find '.fr-navigation'
+
     bindNavigation()

     $el.data 'flexirails', data

   # bind events to navigational elements
   bindNavigation = ->
+    bindPerPageSelection()
+
+  # bind per page selection events
+  bindPerPageSelection = ->
     perPage = $el.find '.fr-per-page'
+    perPage.val plugin.adapter.options.perPage
     perPage.bind 'change', ->
       plugin.adapter.perPage $(this).val()


fix(search): Increase search timeout to 45s

--- src/search-subscription.coffee
+++ src/search-subscription.coffee
@@ -45,6 +45,7 @@
         path: "/threads/search?q=#{encodeURIComponent(@_terms)}"
         accountId: aid
         json: true
+        timeout: 45000
         returnsModel: true

       .then (threads) =>