update sample code

update sample code

--- gae/static/js/sample.coffee
+++ gae/static/js/sample.coffee
@@ -17,8 +17,9 @@
 .config([
   "$routeProvider"
   "$locationProvider"
+  "$interpolateProvider"

-($routeProvider, $locationProvider, config) ->
+($routeProvider, $locationProvider, $interpolateProvider, config) ->

   $routeProvider

@@ -31,6 +32,11 @@

   # Without server side support html5 must be disabled.
   $locationProvider.html5Mode(false)
+
+  $interpolateProvider.startSymbol "[["
+  $interpolateProvider.endSymbol "]]"
+
+  return
 ])

@@ -41,10 +47,11 @@
 .controller("AppCtrl", [
   "$scope"
   "$location"
-  "$resource"
-  "$rootScope"
+  "$log"

-($scope, $location, $resource, $rootScope) ->
+($scope, $location, $log) ->
+  unless $scope.$$phase?
+    $scope.$apply()

   # Uses the url to determine if the selected
   # menu item should have the class active.


Update wyes.test.coffee

Update wyes.test.coffee


feed the hound!

feed the hound!

--- app/assets/javascripts/routes/manuscript_manager_template_new_route.js.coffee
+++ app/assets/javascripts/routes/manuscript_manager_template_new_route.js.coffee
@@ -8,13 +8,13 @@
       paperType: "Research"

     newTemplate.get('phaseTemplates').pushObject
-      @store.createRecord 'phaseTemplate', name: "Phase 1", position: 1
+      @store.createRecord('phaseTemplate', name: "Phase 1", position: 1)

     newTemplate.get('phaseTemplates').pushObject
-      @store.createRecord 'phaseTemplate', name: "Phase 2", position: 2
+      @store.createRecord('phaseTemplate', name: "Phase 2", position: 2)

     newTemplate.get('phaseTemplates').pushObject
-      @store.createRecord 'phaseTemplate', name: "Phase 3", position: 3
+      @store.createRecord('phaseTemplate', name: "Phase 3", position: 3)

     @set('journal', journal)
     @set('newTemplate', newTemplate)


collapse coffeescript cell exceptions

collapse coffeescript cell exceptions

--- app/coffeescript_cell.coffee
+++ app/coffeescript_cell.coffee
@@ -42,10 +42,12 @@
         Context.add_component ctx, Builtins.ErrorComponent message: "Syntax Error: #{e.message} at #{e.location.first_line + 1}:#{e.location.first_column + 1}"
       else
         console.error e.stack
-        Context.add_component ctx, React.DOM.div null,
-          Builtins.ErrorComponent message: printStackTrace({e}).join('\n')
-          'Compiled JavaScript:'
-          Components.SourceComponent language: 'javascript', value: compiled
+        trace = printStackTrace({e})
+        Context.add_component ctx, React.DOM.div {className: 'error'},
+          Components.ToggleComponent {title: React.DOM.pre {}, trace[0]},
+            React.DOM.pre {}, trace[1..].join('\n')
+          Components.ToggleComponent {title: 'Compiled JavaScript'},
+            Components.SourceComponent language: 'javascript', value: compiled
      # this isn't a context fn, so it's value will be displayed
      Context.IGNORE


client: update machinedetails for dynamic view

--- client/home/lib/virtualmachines/components/machineslist/machinedetails.coffee
+++ client/home/lib/virtualmachines/components/machineslist/machinedetails.coffee
@@ -1,5 +1,6 @@
 _                   = require 'lodash'
 kd                  = require 'kd'
+globals             = require 'globals'
 React               = require 'app/react'
 classnames          = require 'classnames'
 GenericToggler      = require './generictoggler'
@@ -302,29 +303,18 @@
 generateSpecs = (machine) ->

   jMachine = machine.toJS()
-
-  specs = []
-
+  providerName = jMachine.meta?.type
+
+  { instanceTypes } = globals.config.providers[providerName]
+
+  instanceType = jMachine.meta?.instance_type ? configs[providerName]['base-vm']
   size = jMachine.meta?.storage_size

-  specs.push provider = jMachine.provider
-  specs.push type = jMachine.meta?.instance_type ? 't2.micro'
-  specs.push ram  = {
-    't2.nano'   : '512MB RAM'
-    't2.micro'  : '1GB RAM'
-    't2.medium' : '4GB RAM'
-    'm4.large'  : '8GB RAM'
-    'm4.xlarge' : '16GB RAM'
-    'm4.2xlarge': '32GB RAM'
-  }[type] ? '1GB RAM'
-  specs.push cpu = {
-    'm4.large'   : '2x CPU'
-    'm4.xlarge'  : '4x CPU'
-    'm4.2xlarge' : '8X CPU'
-  }[type] ? '1x CPU'
-  specs.push disk = if size? then "#{size}GB HDD" else 'N/A'
-
-  return specs
+  { ram, cpu } = instanceTypes[instanceType]
+
+  disk = if size? then "#{size}GB HDD" else 'N/A'
+
+  return [providerName, instanceType, ram, cpu, disk]

 EditVMNameDescription = ->


toc.html => tos.html

toc.html => tos.html

--- client/landing/app/Applications/Home.kdapplication/Views/homeintroview.coffee
+++ client/landing/app/Applications/Home.kdapplication/Views/homeintroview.coffee
@@ -40,7 +40,7 @@
     @toc = new KDCustomHTMLView
       cssClass : 'toc'
       # partial  : 'By signing up, you agree to our privacy policy.'
-      partial  : 'By signing up, you agree to our terms of service and privacy policy.'
+      partial  : 'By signing up, you agree to our terms of service and privacy policy.'

     @twoMinsThumb = new KDCustomHTMLView


Paginate boards list, increase request size

- Closes #1815

--- app/talk/init.cjsx
+++ app/talk/init.cjsx
@@ -17,6 +17,7 @@
 alert = require '../lib/alert'
 AddZooTeamForm = require './add-zoo-team-form'
 DragReorderable = require 'drag-reorderable'
+Paginator = require './lib/paginator'

 module?.exports = React.createClass
   displayName: 'TalkInit'
@@ -31,13 +32,22 @@

   getInitialState: ->
     boards: []
+    boardsMeta: {}
     loading: true
     moderationOpen: false

+  getDefaultProps: ->
+    query: page: 1
+
+  componentWillReceiveProps: (nextProps) ->
+    if nextProps.query.page isnt @props.query.page
+      @setBoards({}, nextProps)
+
   setBoards: (propValue, props = @props) ->
-    talkClient.type('boards').get(section: props.section)
+    talkClient.type('boards').get(section: props.section, page_size: 20, page: props.query.page)
       .then (boards) =>
-        @setState {boards, loading: false}
+        boardsMeta = boards[0]?.getMeta()
+        @setState {boards, boardsMeta, loading: false}

   boardPreview: (data, i) ->
     <BoardPreview {...@props} key={i} data={data} />
@@ -170,4 +180,10 @@
           </section>
         </div>
       </div>
+
+      {if +@state.boardsMeta?.page_count > 1
+        <Paginator
+          page={+@state.boardsMeta?.page}
+          pageCount={+@state.boardsMeta?.page_count} />}
+
     </div>


Fixed configuration

Fixed configuration

--- server/init.coffee
+++ server/init.coffee
@@ -10,11 +10,12 @@

 ###

-if Meteor.settings?env?type isnt 'development'
-  {port, rootUrl, mongoUrl, bindIp} = Meteor.settings.env
-  process.env.PORT = port
-  process.env.ROOT_URL = rootUrl
-  process.env.MONGO_URL = mongoUrl
-  process.env.BIND_IP = bindIp
-
-console.log Meteor.settings+if Meteor.settings and
+  Meteor.settings.env and
+  Meteor.settings.type isnt 'development'
+    console.log Meteor.settings
+    {port, rootUrl, mongoUrl, bindIp} = Meteor.settings.env
+    process.env.PORT = port
+    process.env.ROOT_URL = rootUrl
+    process.env.MONGO_URL = mongoUrl
+    process.env.BIND_IP = bindIp


Tri des consommations par ordre décroissant de date coté serveur

--- serveur/api.coffee
+++ serveur/api.coffee
@@ -26,6 +26,7 @@
   .find(user: req.session.user.id)
   .skip(req.query.skip or 0)
   .limit(req.query.limit or 50)
+  .sort("-date")
   .exec()
   .then sendRep(res), errorHandler(res)
 .post access.logged, (req, res) ->
@@ -77,7 +78,7 @@
         console.log "send Montant"
         montant: req.session.user.montant
     .then sendRep(res), errorHandler(res)
-
+
 app.route '/user'
 .get access.rf, (req, res) ->
   if req.query.search?
@@ -103,7 +104,7 @@
     .sort "nom prenom"
     .exec()
     .then sendRep(res), errorHandler(res)
-
+
 app.route '/user/:login'
 .get access.rf, (req, res) ->
   User


Added preDraw engine event

Added preDraw engine event

--- test/engine.coffee
+++ test/engine.coffee
@@ -40,6 +40,17 @@
   raises ->
     engine = Engine
       includedModules: ["HellaInvalidModule"]
+
+test "draw events", 2, ->
+  engine = Engine()
+
+  engine.bind "preDraw", ->
+    ok true
+
+  engine.bind "draw", ->
+    ok true
+
+  engine.frameAdvance()

 test "Remove event", 1, ->
   engine = Engine()


Add language-har package

Add language-har package

--- atom/packages.cson
+++ atom/packages.cson
@@ -16,6 +16,7 @@
   "language-env"
   "language-gitattributes"
   "language-gitignore"
+  "language-har"
   "language-hosts"
   "language-ini"
   "language-latex"


Lot of  small fixes in gramar processing. SQL processin is still not working.


get rid of repeatedly saving in synctable -- just do it once

--- src/smc-util/synctable.coffee
+++ src/smc-util/synctable.coffee
@@ -505,16 +505,7 @@
                     console.warn("_save('#{@_table}') error: #{err}")
                 else
                     @emit('saved', saved_objs)
-                if err
-                    cb?(err)
-                    return
-                else
-                    if @_last_set != last_set
-                        # keep saving until table doesn't change *during* the save due to user set call
-                        @_save(cb)
-                    else
-                        cb?()
-
+                cb?(err)

     ###
     Disabled --


cleaned up rendering of side chat

cleaned up rendering of side chat

--- src/smc-webapp/side_chat.cjsx
+++ src/smc-webapp/side_chat.cjsx
@@ -29,7 +29,6 @@
 misc = require('smc-util/misc')
 misc_page = require('./misc_page')
 {defaults, required} = misc
-{Markdown, TimeAgo, Tip} = require('./r_misc')
 {salvus_client} = require('./salvus_client')
 {synchronized_db} = require('./syncdb')

@@ -37,8 +36,8 @@

 # React libraries
 {React, ReactDOM, rclass, rtypes, Actions, Store, Redux}  = require('./smc-react')
-{Icon, Loading, TimeAgo} = require('./r_misc')
-{Button, Col, Grid, FormGroup, FormControl, ListGroup, ListGroupItem, Panel, Row, ButtonGroup} = require('react-bootstrap')
+{Icon, Loading, Markdown, TimeAgo, Tip} = require('./r_misc')
+{Button, Col, Grid, FormGroup, FormControl, ListGroup, ListGroupItem, Panel, Row, ButtonGroup, Well} = require('react-bootstrap')

 {User} = require('./users')

@@ -109,22 +108,22 @@
     show_history: ->
         #No history for mobile, since right now messages in mobile are too clunky
         if not IS_MOBILE
-

-                <Tip title='Message History' tip='Show history of editing of this message.'>
+
+                <Tip title='Message History' tip='Show history of editing of this message.' placement='left'>
                     <Icon name='history'/>
                 </Tip>
-

+

     hide_history: ->
         #No history for mobile, since right now messages in mobile are too clunky
         if not IS_MOBILE
-            <div className="pull-right small"
+
-                <Tip title='Message History' tip='Hide history of editing of this message.'>
+                <Tip title='Message History' tip='Hide history of editing of this message.' placement='left'>
                     <Icon name='history'/> Hide History
                 </Tip>
-

+

     disable_history_side_chat: ->
         @setState(show_history:false)
@@ -165,15 +164,15 @@
         if not is_editing(@props.message, @props.account_id) and other_editors.size == 0 and newest_content(@props.message).trim() != ''
             edit = "Last edit "
             name = " by #{@props.editor_name}"
-

+
                 {edit}
                 <TimeAgo date={new Date(@props.message.get('history').peek()?.get('date'))} />
                 {name}
-

+
         else
-

+
                 {text}
-

+

     edit_message: ->
         @props.actions.set_editing(@props.message, true)
@@ -197,7 +196,7 @@
         value = newest_content(@props.message)

         if sender_is_viewer(@props.account_id, @props.message)
-            color = '#f5f5f5'
+            color = '#eee'
         else
             color = '#fff'

@@ -219,26 +218,22 @@
         else if not @props.is_next_sender
             borderRadius = '5px 5px 10px 10px'

-        mesg_style =
-            paddingRight: "3px"
-            paddingLeft: "3px"
-            width: "100%"
-
-        <Col key={1} xs={11} style={mesg_style}>
+        message_style =
+            background    : color
+            wordWrap      : "break-word"
+            marginBottom  : "3px"
+            borderRadius  : borderRadius
+
+        <Col key={1} xs={11} style={width: "100%"}>
             {show_user_name(@props.sender_name) if not @props.is_prev_sender and not sender_is_viewer(@props.account_id, @props.message)}
-            <Panel style={background:color, wordWrap:"break-word", marginBottom: "3px", borderRadius: borderRadius}>
-                <ListGroup fill>
-                    <ListGroupItem onDoubleClick={@edit_message if not @props.message.get("payload")} style={background:color, fontSize: font_size, borderRadius: borderRadius, paddingBottom:'20px'}>
-                        {render_markdown(value, @props.project_id, @props.file_path) if not is_editing(@props.message, @props.account_id)}
-                        {@render_input() if is_editing(@props.message, @props.account_id)}
-                        {@editing_status() if @props.message.get('history').size > 1 or  @props.message.get('editing').size > 0}
-                        {get_timeago(@props.message)}
-                        {@show_history() if not @state.show_history and @props.message.get('history').size > 1}
-                        {@hide_history() if @state.show_history and @props.message.get('history').size > 1}
-                    </ListGroupItem>
-

  {#This div tag fixes a weird bug where
 tags would be rendered below the <ListGroupItem>}
-                </ListGroup>
-            </Panel>
+            <Well style={message_style} bsSize="small" onDoubleClick = {@edit_message}>
+                {get_timeago(@props.message)}
+                {render_markdown(value, @props.project_id, @props.file_path) if not is_editing(@props.message, @props.account_id)}
+                {@render_input() if is_editing(@props.message, @props.account_id)}
+                {@editing_status() if @props.message.get('history').size > 1 or  @props.message.get('editing').size > 0}
+                {@show_history() if not @state.show_history and @props.message.get('history').size > 1}
+                {@hide_history() if @state.show_history and @props.message.get('history').size > 1}
+            </Well>
             {render_history_title(color, font_size) if @state.show_history}
             {render_history(color, font_size, @props.history, @props.history_author, @props.history_date, @props.user_map) if @state.show_history}
             {render_history_footer(color, font_size) if @state.show_history}
@@ -463,7 +458,7 @@

             <Row>
                 <Col md={3} style={padding:'0px 2px 0px 2px'}>
-                    <Panel style={side_chat_log_style} ref='log_container' onScroll={@on_scroll} >
+                    <Well style={side_chat_log_style} ref='log_container' onScroll={@on_scroll} >
                         <ChatLog
                             messages     = {@props.messages}
                             account_id   = {@props.account_id}
@@ -474,7 +469,7 @@
                             actions      = {@props.actions}
                             focus_end    = {focus_endpoint}
                             show_heads   = {false} />
-                    </Panel>
+                    </Well>
                 </Col>
             </Row>
             <Row>


Use correct doc for multiple type param

Use correct doc for multiple type param

#6 Classify and objectify query handlers

--- src/queryhandlers/FindNodeQueryHandler.coffee
+++ src/queryhandlers/FindNodeQueryHandler.coffee
@@ -32,7 +32,7 @@
   @NAME = "find_node"

   ###
-  @param nodeId {Buffer|String} ID of the node handling the response
+  @param nodeId {Buffer, String} ID of the node handling the response
   @param nodes {KBucket} a DHT node's known nodes
   ###
   constructor: (@nodeId, @nodes)->


fix: check if `isAppknox` is true

fix: check if `isAppknox` is true

--- app/components/submission-box.coffee
+++ app/components/submission-box.coffee
@@ -10,7 +10,7 @@
   cookies: Ember.inject.service()

   didInsertElement: ->
-    if ENV.isAppknox
+    if ENV.isAppknox is true
       cookies = @get 'cookies'
       name = tourName(ENV.TOUR.newScan)
       try


Make Browse events all have same name

Make Browse events all have same name

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -51,7 +51,7 @@
         #console.log("mixpanel.people.set", mixpanelSettings)
         mixpanel.people.set(mixpanelSettings)
         path = Backbone.history.getFragment()
-        mixpanel.track("Browse /#{path}")
+        mixpanel.track("Browse")

   checkDaysLeft: (route) ->
     # Here we enforce the policy that expired free-trial users


Check for expired freetrial on all routes

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -15,6 +15,7 @@
     @on 'route', @trackPageView
     @on 'route', @trackIntercom
     @on 'route', @trackOptimizely
+    @on 'route', @checkDaysLeft

     # TODO: this isn't a great place for this constant
     window.latestTerms = 1
@@ -75,6 +76,18 @@
           setInterval ->
             window.Intercom 'update'
           , 30000
+
+  checkDaysLeft: (route) ->
+    # If we're already going to the pricing page, do not
+    # try and navigate to the pricing page.
+    if route == 'pricing'
+      return
+    # If a free-trial has expired, navigate to the pricing page.
+    user = window.user.effective
+    if user.accountLevel == 'free-trial' and user.daysLeft <= 0
+      # This doesn't work, but setting location.href does.
+      # app.navigate "/pricing/expired", trigger: true
+      window.location.href = "/pricing/expired"

   getIntercomSettings: (cb) ->
     real = window.user.real


Fix new coffeescript issues

Fix new coffeescript issues

--- client/finder/fs/fsitem.coffee
+++ client/finder/fs/fsitem.coffee
@@ -315,14 +315,11 @@
       kontrol = KD.getSingleton 'kontrol'
       kontrol.getKite \
         if vm?
-        then {
           name              : 'oskite'
           correlationName   : vm.hostnameAlias
           region            : vm.region
-        }
-        else {
+        else
           name              : 'oskite'
           correlationName   : vmName
-        }
     else
       KD.getSingleton('vmController').getKiteByVmName vmName


Remove unnecessary alert

Remove unnecessary alert

--- src/views/search-input.coffee
+++ src/views/search-input.coffee
@@ -49,8 +49,6 @@

         search: (e) ->
             e.stopPropagation()
-            alert();
-            #unless @isEmpty()
             @$searchEl.typeahead 'close'
             @executeQuery()
             e.preventDefault()


Fix loading between rooms change

Fix loading between rooms change

--- client/routes/roomRoute.coffee
+++ client/routes/roomRoute.coffee
@@ -60,6 +60,8 @@
 				Meteor.call 'openRoom', room._id

 roomExit = ->
+	BlazeLayout.render 'main', {center: 'none'}
+
 	mainNode = document.querySelector('.main-content')
 	if mainNode?
 		for child in mainNode.children


cleanup

cleanup

--- bookmarklet.coffee
+++ bookmarklet.coffee
@@ -7,6 +7,7 @@
 ##
 ## load_* helpers to dynamically add load in certain types of content
 ##
+#
 load_script = (url, callback) ->
     """
     Load a script from a remote URL...with a callback when it's complete


Don't rely on spaces to test korean characters' width behavior

--- spec/display-buffer-spec.coffee
+++ spec/display-buffer-spec.coffee
@@ -88,13 +88,13 @@
       describe "when there are korean characters", ->
         it "takes them into account when finding the soft wrap column", ->
           displayBuffer.setDefaultCharWidth(1, 0, 0, 10)
-          buffer.setText("1234세계를 향한 대화, 유니코 제10회유니코드국제")
-
-          expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("1234세계를 ")
-          expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("향한 대화, ")
-          expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("유니코 ")
-          expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("제10회유니")
-          expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe("코드국제")
+          buffer.setText("1234세계를향한대화,유니코제10회유니코드국제")
+
+          expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("1234세계를향")
+          expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("한대화,유")
+          expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("니코제10회")
+          expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("유니코드국")
+          expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe("제")

       describe "when editor.softWrapAtPreferredLineLength is set", ->
         it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", ->


ad loader in group tab

ad loader in group tab

--- MediaAdminBundle/Resources/public/coffee/groupTree/FormMediaFolderCollectionView.coffee
+++ MediaAdminBundle/Resources/public/coffee/groupTree/FormMediaFolderCollectionView.coffee
@@ -1,68 +1,15 @@
-###*
- * @namespace OpenOrchestra:GroupTree
-###
-window.OpenOrchestra or= {}
-window.OpenOrchestra.GroupTree or= {}
-
 ###*
  * @class FormMediaFolderCollectionView
 ###
-class OpenOrchestra.GroupTree.FormMediaFolderCollectionView extends OrchestraView
-
-  events:
-    'change .value-holder': 'changeInput'
-
-  ###*
-   * @param {Object} options
-  ###
-  initialize: (options) ->
-    ###*
-     * {Object} domContainer
-     * {Object} folderElement
-     * {Array}  folderGroupRoles
-     * {Object} group
-     * {Array}  roles
-    ###
-    @options = @reduceOption(options, [
-      'domContainer'
-      'folderElement'
-      'folderGroupRoles'
-      'group'
-      'roles'
-    ])
-    @loadTemplates [
-      'OpenOrchestraBackofficeBundle:BackOffice:Underscore/groupTree/groupTreeForm',
-    ]
-
-  ###*
-   * render
-  ###
-  render: ->
-    for role in @options.roles
-      @options.domContainer.append @renderTemplate('OpenOrchestraBackofficeBundle:BackOffice:Underscore/groupTree/groupTreeForm',
-        role: role
-        element: @options.folderElement
-      )
-    @setElement @options.domContainer
-    if @options.folderGroupRoles != undefined
-      for folderGroupRole in @options.folderGroupRoles
-        $('select[data-role-name="' + folderGroupRole.name + '"] option[value="' + folderGroupRole.access_type + '"]', @options.domContainer).attr('selected','selected')
-
-  ###*
-   * @param {Object} e
-  ###
-  changeInput: (e) ->
-    value = $(e.currentTarget).val()
-    name = $(e.currentTarget).data('role-name')
-    folderId = @options.folderElement.folder_id
-    folderGroupRoleData = []
-    folderGroupRoleData.push({'model_id': folderId, 'type':'folder', 'access_type': value, 'name': name})
-    $.ajax
-      url: @options.group.links._self_edit
-      method: 'POST'
-      data: JSON.stringify(
-        model_roles: folderGroupRoleData
-      )
+class OpenOrchestra.GroupTree.FormMediaFolderCollectionView extends OpenOrchestra.GroupTree.AbstractFormCollectionView
+  getElement: ->
+    return 'folderElement'
+  getId: ->
+    return 'folder_id'
+  getType: ->
+    return 'folder'
+  getGroupRoles: ->
+    return 'folderGroupRoles'

 jQuery ->
   appConfigurationView.setConfiguration 'group_tab_media_folder_tree_form', 'editEntityTab', OpenOrchestra.GroupTree.FormMediaFolderCollectionView


dashboard: removed membershippolicyview

dashboard: removed membershippolicyview

--- client/landing/app/Applications/Groups.kdapplication/Views/groupsmembershippolicydetailview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupsmembershippolicydetailview.coffee
@@ -15,8 +15,7 @@
             'updateMembershipPolicy',
             group,
             policy,
-            formData,
-            membershipPolicyView
+            formData
         @createSubViews policy

@@ -90,7 +89,6 @@
     @showPolicyLanguageLink = new CustomLinkView
       cssClass  : "edit-link #{if policyLanguageExists then 'hidden' else ''}"
       title     : 'Edit'
-      href      : './edit'
       click     :(event)=>
         event.preventDefault()
         @showPolicyLanguageLink.hide()


Restore test for custom match with camel case.

--- spec/jumpy-settings-spec.coffee
+++ spec/jumpy-settings-spec.coffee
@@ -53,9 +53,9 @@
             expect(textEditorElement.querySelectorAll('.jumpy.label')[0]
                 .style.fontSize).toBe '50%'

-    xdescribe "when the jumpy:toggle event is triggered
+    describe "when the jumpy:toggle event is triggered
         and a jump is performed", ->
-        it "contains no beacon", ->
+        xit "contains no beacon", ->
             textEditor.setCursorBufferPosition [1,1]
             expect(textEditorElement.find('.cursors .cursor')[0].classList
                 .contains 'beacon').toBe false
@@ -74,7 +74,7 @@
             expect(labels[82].innerHTML).toBe 'de'
             expect(labels[83].innerHTML).toBe 'df'

-    xdescribe "when a custom match is used (camel case)", ->
+    describe "when a custom match is used (camel case)", ->
         it "draws correct labels and jumps appropriately", ->
             atom.commands.dispatch textEditorElement, 'jumpy:clear'
             atom.config.set 'jumpy.matchPattern', '([A-Z]+([0-9a-z])*)|[a-z0-9]{2,}'


Add specs for PaneContainer::onDidAddPane

--- spec/pane-container-spec.coffee
+++ spec/pane-container-spec.coffee
@@ -149,6 +149,18 @@
       expect(saved).toBeFalsy()
       expect(atom.confirm).toHaveBeenCalled()

+  describe "::onDidAddPane(callback)", ->
+    it "invokes the given callback when panes are added", ->
+      container = new PaneContainer
+      events = []
+      container.onDidAddPane (event) -> events.push(event)
+
+      pane1 = container.getActivePane()
+      pane2 = pane1.splitRight()
+      pane3 = pane2.splitDown()
+
+      expect(events).toEqual [{pane: pane2}, {pane: pane3}]
+
   describe "::onWillDestroyPaneItem() and ::onDidDestroyPaneItem", ->
     it "invokes the given callbacks when an item will be destroyed on any pane", ->
       container = new PaneContainer


Notify user when they are entering the legacy site

--- src/scripts/loader.coffee
+++ src/scripts/loader.coffee
@@ -29,7 +29,13 @@

       if external.test(href)
         if /^((f|ht)tps?:)?\/\/(\w*\.?)cnx\.org/.test(href)
-          location.href = href
+          if document.cookie.indexOf('legacy') >= 0
+            location.href = href
+
+          # Going to the legacy site
+          else if confirm('You are now entering the legacy site. To return, use your browser\'s back button.')
+            document.cookie = "legacy; max-age=#{60*60*24*30*365*4}; path=/;"
+            location.href = href
         else
           window.open(href, '_blank')
       else if resources.test(href)


Remove var definition - not needed

Remove var definition - not needed

--- src/scripts/uptime.coffee
+++ src/scripts/uptime.coffee
@@ -25,7 +25,6 @@
     if value > 0
       elements.push value + ' ' + interval + numPlural(value)

-  last
   if elements.length > 1
     last = elements.pop()
     response = elements.join ', '


ui: Run tests automatically

ui: Run tests automatically

--- ui/main.coffee
+++ ui/main.coffee
@@ -1,7 +1,4 @@
-
-if not fbpspec
-  # built with component.io
-  fbpspec = require 'fbp-spec/src/index'
+fbpspec = require 'fbp-spec/src/index' if not fbpspec
 widgets = fbpspec.ui.widgets

 # DOM helpers
@@ -22,13 +19,12 @@
     console.log 'rendered'
   onTestsChanged()

-  rt = {
+  rt =
     "protocol": "websocket",
     "address": "ws://localhost:3569",
     "command": "python2 protocol-examples/python/runtime.py" # need to start manually!
-  }

-  id('runButton').onclick = () ->
+  runTests = () ->
     runner = new fbpspec.runner.Runner rt
     runner.connect (err) ->
       console.log 'connected', err
@@ -39,4 +35,8 @@
         runner.disconnect (err) ->
           console.log 'disconnected'

+  id('runButton').onclick = runTests
+  setTimeout runTests, 100
+  console.log 'main done'
+
 main()


Revert "corrected new playlist name bug"

This reverts commit e6f380c20daf8a2f53850111346745834a4c6733.

--- coffee/_Playlists.class.coffee
+++ coffee/_Playlists.class.coffee
@@ -44,8 +44,7 @@
                     i++
                 __playlists = playlists
                 success? playlists
-                return playlists
-
+
     @getTracksForPlaylist = (playlist, success) ->
         tracks = []
         db.transaction (tx) ->
@@ -59,12 +58,3 @@
     @rename: (name, new_name) ->
         db.transaction (tx) ->
             tx.executeSql 'UPDATE playlists SET name = ? WHERE name = ?', [new_name, name]
-        Playlists.getTracksForPlaylist(name, ((tracks) ->
-            i = 0
-            while i < tracks.length
-                Playlists.addTrack(tracks[i].artist, tracks[i].title, tracks[i].cover_url_medium, tracks[i].cover_url_large, new_name)
-                console.log(tracks[i].artist)
-                i++
-            success? tracks
-        ))
-        Playlists.delete(name)


corrected new playlist name bug

corrected new playlist name bug

--- coffee/_Playlists.class.coffee
+++ coffee/_Playlists.class.coffee
@@ -44,7 +44,8 @@
                     i++
                 __playlists = playlists
                 success? playlists
-
+                return playlists
+
     @getTracksForPlaylist = (playlist, success) ->
         tracks = []
         db.transaction (tx) ->
@@ -58,3 +59,12 @@
     @rename: (name, new_name) ->
         db.transaction (tx) ->
             tx.executeSql 'UPDATE playlists SET name = ? WHERE name = ?', [new_name, name]
+        Playlists.getTracksForPlaylist(name, ((tracks) ->
+            i = 0
+            while i < tracks.length
+                Playlists.addTrack(tracks[i].artist, tracks[i].title, tracks[i].cover_url_medium, tracks[i].cover_url_large, new_name)
+                console.log(tracks[i].artist)
+                i++
+            success? tracks
+        ))
+        Playlists.delete(name)


bugfixes: check for existing session for storage selection fix problems with Last-Modified header not in UTC

--- modules/http/lib/Cache.coffee
+++ modules/http/lib/Cache.coffee
@@ -17,7 +17,7 @@

                 if !@data.disabled

-                    if req.session.user
+                    if req.session?.user
                         storage = req.session.HttpCacheStorage ?= {__name: 'private'}

                     else
@@ -133,7 +133,7 @@
                             ## finish the request with cached contents
                             return @_end data.body,
                                 'Content-Type': data.ctype
-                                'Last-Modified': data.date
+                                'Last-Modified': data.date.toUTCString()
                                 'Expires': data.expires
                                 'ETag': data.ETag

@@ -172,7 +172,7 @@

                     #console.log 'check', date , @req.headers['if-modified-since'], date && date.toString() is @req.headers['if-modified-since']

-                    if date && date.toString() is @req.headers['if-modified-since']
+                    if date && date.toUTCString() is @req.headers['if-modified-since']
                         #console.log 'res 304', date , @req.headers['if-modified-since']

                         return @_end null, 304
@@ -181,7 +181,7 @@

                         #console.log 'next create', date , @req.headers['if-modified-since']

-                        headers['Last-Modified'] = date
+                        headers['Last-Modified'] = date.toUTCString()

                         next body, headers, status


the shorter the better

the shorter the better

--- client/app/Applications/Activity.kdapplication/ContentDisplays/blogpost.coffee
+++ client/app/Applications/Activity.kdapplication/ContentDisplays/blogpost.coffee
@@ -49,9 +49,8 @@

   pistachio:->
     {html, body} = @getData()
-    unless html
-      html = KD.utils.applyMarkdown body
-    @getData().html = html
+    @getData().html = html or KD.utils.applyMarkdown body
+
     """
     {{> @header}}

{{> @back}}


Don't use lonely @'s

Don't use lonely @'s

--- src/simple-machine.coffee
+++ src/simple-machine.coffee
@@ -22,7 +22,7 @@
     on: (event, callback, context) ->
       callback = (-> callback.call context) if context?
       @callbacks[event] = [callback].concat(@callbacks[event])
-      @
+      this

     trigger: (event) ->
       if @transitions[event]?[@state]? and event isnt 'all'


base.js.coffee: commented out failing function

--- app/assets/javascripts/base.js.coffee
+++ app/assets/javascripts/base.js.coffee
@@ -1,16 +1,18 @@
-$ ->
-  $(".relativetime").each((index, element) ->
-    time_utc = Date.parse($(element).text())
-    time_local = new Date()
-    time_local.setTime(time_utc)
-    month = time_local.getMonth() + 1
-    hour = time_local.getHours()
-    if hour < 12
-      hour_string = hour.toString() + "am"
-    else if hour == 12
-      hour_string = hour.toString() + "pm"
-    else
-      hour_string = (hour - 12).toString() + "pm"
-    time_local_string = time_local.getDate() + "/" + month + "/" + time_local.getFullYear() + " at " + hour_string
-    $(element).text(time_local_string)
-  )
+# Date.parse will fail on firefox as the format is incorrect
+# Function left so we can modify and use when needed - kiesia 8.5.12
+# $ ->
+  # $(".relativetime").each((index, element) ->
+    # time_utc = Date.parse($(element).text())
+    # time_local = new Date()
+    # time_local.setTime(time_utc)
+    # month = time_local.getMonth() + 1
+    # hour = time_local.getHours()
+    # if hour < 12
+      # hour_string = hour.toString() + "am"
+    # else if hour == 12
+      # hour_string = hour.toString() + "pm"
+    # else
+      # hour_string = (hour - 12).toString() + "pm"
+    # time_local_string = time_local.getDate() + "/" + month + "/" + time_local.getFullYear() + " at " + hour_string
+    # $(element).text(time_local_string)
+  # )


removed message filtering

removed message filtering

--- app/assets/javascripts/chat.js.coffee
+++ app/assets/javascripts/chat.js.coffee
@@ -53,7 +53,7 @@
   newMessage: (message) =>
     console.log("Message received: " + message['msg_body'])

-    return if message['language'] != @user.language
+    # return if message['language'] != @user.language

     message['user_type'] = "them"
     message['user_type'] = "me" if message['user_id'] == @user.user_id


make context menu always visible in window;

--- app/assets/javascripts/file.js.coffee
+++ app/assets/javascripts/file.js.coffee
@@ -272,12 +272,18 @@
         li.addClass('bottom-separator')
       li.append(''+b+'')
       $('#menu').append(li)
+    x = e.pageX - 17
+    if x + $('#context_menu').outerWidth() > $(window).width()
+      x = $(window).width() - $('#context_menu').outerWidth()
+    y = e.pageY - 10
     $('#context_menu').show().css({
       height: $('#menu').outerHeight() + 20,
       top: e.pageY - 10,
-      left: e.pageX - 17,
+      left: x,
       'z-index': 20
     })
+    if (y + $('#context_menu').height() > $(window).height())
+      $('#context_menu').css({ top: $(window).height() - $('#context_menu').height() })
     menu_items_before_clicked[type]()

   hide_context_menu = ->


fix results page tab issue

fix results page tab issue

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -44,8 +44,7 @@

   $ ->
     $("#sidebar-accordion").accordion heightStyle: "content"
-  return
-
+    return

   $ ->
     $("#tabs").tabs()


resizing of window now resizes the grid

resizing of window now resizes the grid

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -1,14 +1,15 @@
+class Grid
+  constructor: (width, height)->
+    @increment = 10
+    @resetBounds(width, height)

+  resetBounds: (width, height) ->
+    @width = width
+    @height = height

-class Grid
-  constructor: (@width, @height, @ctx)->
-    @increment = 10
-    @setLineColor()
-
-  setLineColor: ->
+  draw: (ctx) ->
+    @ctx = ctx
     @ctx.strokeStyle = '#B4D6D7'
-
-  draw: ->
     @ctx.beginPath()
     @drawHorizontalLines()
     @drawVerticalLines()
@@ -32,28 +33,37 @@
       hPosition += 40
       break if hPosition > @width

-  drawVerticalLine: (hPosition) ->
+  drawVerticalLine: (hPosition, ctx) ->
     @ctx.moveTo(hPosition+0.5, 0)
     @ctx.lineTo(hPosition+0.5, @width)

 class View
-  constructor: (@width, @height) ->
-    @canvas = $('#view').get(0)
-    @canvas.width = @width
-    @canvas.height = @height
-    @ctx = @canvas.getContext('2d')
+  constructor: ->
+    @resetBounds()
     @elements = []
+
+  canvas: ->
+    $('#view').get(0)
+
+  ctx: ->
+    @canvas().getContext('2d')
+
+  # for now just make the view the window size
+  resetBounds: ->
+    @width = $(window).width()
+    @height = $(window).height()
+    @canvas().width = @width
+    @canvas().height = @height

   draw: ->
     for i in @elements
-      i.draw()
+      i.draw(@ctx())

 class ViewController
   constructor: ->
-    @view = new View($(window).width(), $(window).height())
-    @ctx = @view.ctx
-    @grid = new Grid(@view.width, @view.height, @ctx)
+    @view = new View
+    @grid = new Grid(@view.width, @view.height)
     @addElement(@grid)

   addElement: (element) ->
@@ -62,9 +72,18 @@
   draw: ->
     @view.draw()

+  windowResized: ->
+    @view.resetBounds()
+    @grid.resetBounds(@view.width, @view.height)
+    @view.draw()
+
 $(document).ready ->
   viewController = new ViewController
   viewController.draw()

+  $(window).resize ->
+    viewController.windowResized()
+
+


Update ignore tag names list

Update ignore tag names list

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -92,7 +92,8 @@
     $('#is-pic-list-last-item').appendTo('#js-pic-list').show()

   ignoreTagNames = [
-    'support', 'skin', 'random', 'shadow', 'fashion', 'perfect', 'crazy'
+    'support', 'skin', 'random', 'shadow', 'fashion', 'perfect', 'crazy',
+    'all_shots'
   ]
   tagCountThreshold = 30
   spamFilter = (d)->


Ensure the sweep always starts from the left

--- app/assets/javascripts/logo.js.coffee
+++ app/assets/javascripts/logo.js.coffee
@@ -1,14 +1,14 @@
 NProgress.configure(showSpinner: false)

 defaultClass = 'tanuki-shape'
-highlightClass = 'highlight'
 pieces = [
+  'path#tanuki-left-cheek',
+  'path#tanuki-left-eye, path#tanuki-left-ear',
+  'path#tanuki-nose',
+  'path#tanuki-right-eye, path#tanuki-right-ear',
   'path#tanuki-right-cheek',
-  'path#tanuki-right-eye, path#tanuki-right-ear',
-  'path#tanuki-nose',
-  'path#tanuki-left-eye, path#tanuki-left-ear',
-  'path#tanuki-left-cheek',
 ]
+firstPiece = pieces[0]
 timeout = null

 clearHighlights = ->
@@ -16,18 +16,19 @@

 start = ->
   clearHighlights()
+  pieces.reverse() unless pieces[0] == firstPiece
   work(0)

 stop = ->
   window.clearTimeout(timeout)
   clearHighlights()

-work = (pieceIndex) =>
+work = (pieceIndex) ->
   # jQuery's addClass won't work on an SVG. Who knew!
   $piece = $(pieces[pieceIndex])
-  $piece.attr('class', "#{defaultClass} #{highlightClass}")
+  $piece.attr('class', "#{defaultClass} highlight")

-  timeout = setTimeout(=>
+  timeout = setTimeout(->
     $piece.attr('class', defaultClass)

     # If we hit the last piece, reset the index and then reverse the array to


Remove unwanted method

Remove unwanted method

--- app/assets/javascripts/profile.coffee
+++ app/assets/javascripts/profile.coffee
@@ -17,10 +17,7 @@
       top: o.top + @trigger.height() + margin_top
       left: o.left + @trigger.width() + margin_left
     })
-
-  re_login_handler :() ->
-    #alert('re login')
-
+

   register_handlers: () ->
     @trigger.click (e) =>


disable legacy js code when open lab page

--- app/assets/javascripts/view.js.coffee
+++ app/assets/javascripts/view.js.coffee
@@ -941,6 +941,8 @@
     .fadeOut(1000)

 $(document).ready( ->
+  return true if $('#lab_page').length isnt 0
+
   BWUtil.initialize()
   BWClient.initialize()


Issue 4590: Untranslated strings

Issue 4590: Untranslated strings

--- app/coffee/modules/admin/roles.coffee
+++ app/coffee/modules/admin/roles.coffee
@@ -392,7 +392,7 @@
                 { key: "view_tasks", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.VIEW_TASKS"}
                 { key: "add_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.ADD_TASKS"}
                 { key: "modify_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.MODIFY_TASKS"}
-                { key: "comment_task", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.COMMENT_TASKS"}
+                { key: "comment_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.COMMENT_TASKS"}
                 { key: "delete_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.DELETE_TASKS"}
             ]
             categories.push({
@@ -404,7 +404,7 @@
                 { key: "view_issues", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.VIEW_ISSUES"}
                 { key: "add_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.ADD_ISSUES"}
                 { key: "modify_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.MODIFY_ISSUES"}
-                { key: "comment_issue", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.COMMENT_ISSUES"}
+                { key: "comment_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.COMMENT_ISSUES"}
                 { key: "delete_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.DELETE_ISSUES"}
             ]
             categories.push({


hide save filters button when the user is anonymous

--- app/coffee/modules/issues/list.coffee
+++ app/coffee/modules/issues/list.coffee
@@ -440,7 +440,7 @@
 ## Issues Filters Directive
 #############################################################################

-IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $translate, $compile) ->
+IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $translate, $compile, $auth) ->
     template = $template.get("issue/issues-filters.html", true)
     templateSelected = $template.get("issue/issues-filters-selected.html", true)

@@ -477,7 +477,7 @@
             html = $compile(html)($scope)
             $el.find(".filters-applied").html(html)

-            if selectedFilters.length > 0
+            if $auth.isAuthenticated() && selectedFilters.length > 0
                 $el.find(".save-filters").show()
             else
                 $el.find(".save-filters").hide()
@@ -663,7 +663,7 @@
     return {link:link}

 module.directive("tgIssuesFilters", ["$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading",
-                                     "$tgTemplate", "$translate", "$compile", IssuesFiltersDirective])
+                                     "$tgTemplate", "$translate", "$compile", "$tgAuth", IssuesFiltersDirective])

 #############################################################################


collection search list defaults

collection search list defaults

bump default collection page size and exclude fav's by default

--- app/components/collection-search.cjsx
+++ app/components/collection-search.cjsx
@@ -15,7 +15,8 @@

   searchCollections: (value, callback) ->
     query =
-      page_size: 10
+      page_size: 20
+      favorite: false
       current_user_roles: 'owner,collaborator'
     query.search = "#{value}" unless value is ''


fixes

fixes

--- common/lib/xmodule/xmodule/js/spec/combinedopenended/display_spec.coffee
+++ common/lib/xmodule/xmodule/js/spec/combinedopenended/display_spec.coffee
@@ -121,7 +121,7 @@
       @combined.child_state = 'initial'
       @combined.rebind()
       expect(@combined.answer_area.attr("disabled")).toBeUndefined()
-      expect(@combined.submit_button.val()).toBe(gettext("Submit")
+      expect(@combined.submit_button.val()).toBe(gettext("Submit"))

     it 'when our child state is post_assessment', ->
       @combined.child_state = 'post_assessment'


make module id  changeable

make module id  changeable

--- core/module.coffee
+++ core/module.coffee
@@ -25,7 +25,7 @@

             module = @modules[from]
             throw new Error "module with id: #{from} not exists" if not module
-            delete @modules[form]
+            delete @modules[from]
             module.id = to
             @modules[to] = module

@@ -116,8 +116,10 @@
             type = el.data 'region-type'
             @regions[name] = Region.create type, @app, @module, name, el

-        render: ->
-            return @logger.error 'No region to render in' unless @region
+        render: (options = {}) ->
+            throw new Error 'No region to render in' unless @region
+            @renderOptions = options
+            @container.changeId @id, options.id if options.id

             @chain "Render module #{@name}",
                 @loadDeferred


Fix bugs, use dataconnection instead of websocket

--- app/static/scripts/connections.coffee
+++ app/static/scripts/connections.coffee
@@ -80,15 +80,18 @@
         @conn.on 'close', @onClose
         @conn.on 'error', errorHandler

+    peer        : () =>
+        return @conn.peer
+
     onOpen      : () =>

     onData      : (data) =>
-        @emitter.trigger data.type, data
+        @emitter.trigger data.type, data.params

     onClose     : () =>

     send        : (type, data) =>
-        @conn.send { type: type, data: data }
+        @conn.send { type: type, params: data }

     close       : () =>
         do @conn.close
@@ -98,6 +101,9 @@
         @call.on 'stream', @onStream
         @call.on 'close', @onClose
         @call.on 'error', errorHandler
+
+    peer        : () =>
+        return @conn.peer

     onStream    : (stream) =>
         data =
@@ -163,14 +169,14 @@
             for peer in data.peers
                 @pjs.connect peer.id
         @emitter.on 'peer.dataconnection', (event, data) =>
-            if !@peers[data.pc.peer]?
-                @peers[data.pc.peer] = {}
-            @peers[data.pc.peer].pc = data.pc
-            @sendStreamState data.pc.peer
+            if !@peers[do data.dc.peer]?
+                @peers[do data.dc.peer] = {}
+            @peers[do data.dc.peer].dc = data.dc
+            # @sendStreamState do data.dc.peer
         @emitter.on 'peer.mediaconnection', (event, data) =>
-            if !@peers[data.mc.peer]?
-                @peers[data.mc.peer] = {}
-            @peers[data.mc.peer].mc = data.mc
+            if !@peers[do data.mc.peer]?
+                @peers[do data.mc.peer] = {}
+            @peers[do data.mc.peer].mc = data.mc
         @emitter.on 'peer.remove', (event, data) =>
             if @peers[data.id]?
                 @removePeer data.id
@@ -178,29 +184,34 @@
         @emitter.on 'ping', (event, data) =>
             @ws.send { type : 'pong' , params : { token : data.token } }

+    send      : (id, message) =>
+        if @peers[id].dc?
+            @peers[id].dc.send message.type, message.params
+        else
+            @ws.forward id, message
+
     sendToAll : (event, data) =>
-        message
         if data.type?
             message = data
         else
             message = { type : 'chat.message' , params : { message : data } }
-        for id of @peers
-            @ws.forward id, message
+        for id, peer of @peers
+            @send id, message

     sendToOneName : (event, msg) =>
         message = { type : msg.type, params : msg.params }
         userId = @getIdFromUsername msg.to
         if userId is undefined
             @emitter.trigger 'chat.error', { text : 'kick: ' + msg.to + ' isn\'t in this room.' }
-        else
-            @ws.forward userId, message
+        else
+            @send userId, message

     sendToOneId : (event, msg) =>
         message = { type : msg.type, params : msg.params }
-        @ws.forward msg.to, message
+        @send msg.to, message

     sendStreamState : (id) =>
-        @ws.forward id, { type : 'stream.state', params : { streamState : @userMedia } }
+        @send id, { type : 'stream.state', params : { streamState : @userMedia } }

     enableCamera : () =>
         navigator.getUserMedia @userMedia, (stream) =>


Fixed brick collision bug

Fixed brick collision bug

On occasion, you’d bounce the ball onto a brick, and take out more than
one. Sometimes, it’d even clear an entire column of blocks! We employed
Arcade.Body’s bounce property to make this cleaner.

--- src/script/GameplayState.coffee
+++ src/script/GameplayState.coffee
@@ -21,7 +21,7 @@
     @square = new Phaser.Sprite(game, 100, 864, null)
     game.add.existing(@square)
     game.physics.enable(@square, Phaser.Physics.ARCADE)
-    @square.body.setSize(96, 32)
+    @square.body.setSize(128, 32)
     @square.body.immovable = true

     @wallTop = new Phaser.Sprite(game, 32, 0, null)
@@ -42,9 +42,11 @@
     @wallRight.body.setSize(32, 960)
     @wallRight.body.immovable = true

-    @ball = new Phaser.Sprite(game, 320, 880, null)
+    @ball = new Phaser.Sprite(game, 320, 780, null)
     game.add.existing(@ball)
     game.physics.enable(@ball, Phaser.Physics.ARCADE)
+    @ball.body.bounce.x = 1
+    @ball.body.bounce.y = 1
     @ball.body.setSize(16, 16)
     @ball.body.velocity.x = 0
     @ball.body.velocity.y = -400
@@ -56,6 +58,7 @@
           box = @boxes.add(new Phaser.Sprite(game, 64 + (i * 32), 96 + (j * 32), null))
           game.physics.enable(box, Phaser.Physics.ARCADE)
           box.body.setSize(32, 32)
+          box.body.immovable = true
           box.color = Levels[currentLevel][j][i].color
     game.add.existing(@boxes)

@@ -72,9 +75,10 @@

     if @ball.body.position.y > 1100
       lives--
-      @ball.body.position.y = 880
+      @ball.body.position.y = 780
       @ball.body.position.x = 320
-      # @ball.body.velocity.y *= -1
+      @ball.body.velocity.y = -400
+      @ball.body.velocity.x = 0

       if lives <= 0
         @ball.body.velocity.y *= 0
@@ -102,9 +106,3 @@
   collideBlock: (ball, block) ->
     block.kill()
     score += 100
-
-    if ball.body.touching.up or ball.body.touching.down
-      ball.body.velocity.y *= -2
-
-    if ball.body.touching.left or ball.body.touching.right
-      ball.body.velocity.x *= -2


subcaption support

subcaption support

--- client/import.iced
+++ client/import.iced
@@ -245,6 +245,7 @@
           url: match[2][1...-1]

   processCites = (tex) ->
+    return tex unless tex
     tex.replace /\\cite\s*(?:\[([^\[\]]*)\]\s*)?{([^{}]*)}/g, (match, p1, p2) ->
       '[' + (
         for cite in p2.split ','
@@ -263,32 +264,68 @@
     if filename[-4..] == '.tex'
       tex = content.asText()
       tex = tex.replace /%.*$\n?/mg, ''
-      .replace /\\begin\s*{(wrap)?figure}([^\0]*?)\\end\s*{(wrap)?figure}\s*/g,
-        (match, p1, p2, p3) ->
+      .replace /\\begin\s*{(wrap)?figure}([^]*?)\\end\s*{(wrap)?figure}\s*/g,
+        (match, beginFigure, body, endFigure) ->
+          ## Collect all \includegraphics, possibly with subcaptions
           graphics = []
-          gr = /\\includegraphics\s*(\[[^\[\]]*\]\s*)?{((?:[^{}]|{[^{}]*})*)}/g
-          while (match = gr.exec p2)?
-            filename = match[2]
+          findLabels = (text) ->
+            [
+              labels = []
+              text.replace /\\label\s*{((?:[^{}]|{[^{}]*})*)}/g,
+                (match3, label) ->
+                  labels.push label
+                  ''
+            ]
+          findIncludeGraphics = (text, subcaption = null, labels = []) ->
+            text.replace /\\includegraphics\s*(\[[^\[\]]*\]\s*)?{((?:[^{}]|{[^{}]*})*)}/g,
+              (match3, optional, filename) ->
+                if optional.match /page\s*=/
+                  console.warn 'Import does not support \\includegraphics[page=...]'
+                graphics.push
+                  filename: filename
+                  subcaption: subcaption
+                  labels: labels
+                ''
+            ''
+          body = body.replace /\\subcaptionbox\s*{((?:[^{}]|{[^{}]*})*)}\s*{((?:[^{}]|{[^{}]*})*)}/g,
+            (match2, subcaption, subbody) ->
+              [labels, subcaption] = findLabels subcaption
+              findIncludeGraphics subbody, subcaption, labels
+          body = body.replace /\\begin\s*{subfigure}([^]*?)\\end\s*{subfigure}/g,
+            (match2, subbody) ->
+              [labels, subbody] = findLabels subbody
+              subcaption = /\\caption\s*{((?:[^{}]|{[^{}]*})*)}/.exec(subbody)?[1]
+              findIncludeGraphics subbody, subcaption, labels
+          findIncludeGraphics body
+          ## Search for correct extensions
+          for graphic in graphics
             for extension in ['', '.svg', '.png', '.jpg', '.pdf']
-              if filename + extension of zip.files
-                filename += extension
+              if graphic.filename + extension of zip.files
+                graphic.filename += extension
                 break
-            if filename not of zip.files
-              console.warn "Missing file for \\includegraphics{#{graphics}}"
-            graphics.push filename
-          caption = /\\caption\s*{((?:[^{}]|{[^{}]*})*)}/.exec p2
-          caption = processCites caption[1]
-          labels = []
-          lr = /\\label\s*{((?:[^{}]|{[^{}]*})*)}/g
-          while (match = lr.exec p2)?
-            labels.push match[1]
-          console.log "Figure #{labels.join ' / '} = #{graphics.join ' & '} = #{caption}"
+            if graphic.filename not of zip.files
+              console.warn "Missing file for \\includegraphics{#{graphic.filename}}"
+          for graphic in graphics
+            ## Remove \\ use within subcaption, which is generally just for
+            ## layout and not relevant for web layout.
+            if graphic.subcaption
+              graphic.subcaption = graphic.subcaption.replace /\s*\\\\\s*/, ' '
+              graphic.subcaption = processCites graphic.subcaption
+          [labels, body] = findLabels body
+          caption = /\\caption\s*{((?:[^{}]|{[^{}]*})*)}/.exec(body)[1]
+          caption = processCites caption
+          console.log "Figure #{labels.join ' / '} = #{(graphic.filename + (if graphic.subcaption then "[#{graphic.subcaption}]" else '') for graphic in graphics).join ' & '} = #{caption}"
           figure =
             graphics: graphics
             caption: caption
             labels: labels
+          if labels.length > 1
+            console.warn "Multiple labels will appear: #{labels.join ' / '}"
           for label in labels
             figures[label] = figure
+          for graphic in figure.graphics
+            for label in graphic.labels
+              figures[label] = figure

   ## Extract sections.
   for filename, content of zip.files
@@ -297,7 +334,7 @@
       ## Remove comments (to ignore \sections inside comments).
       tex = tex
       .replace /%.*$\n?/mg, ''
-      .replace /\\begin\s*{(wrap)?figure}[^\0]*?\\end\s*{(wrap)?figure}\s*/g, ''
+      .replace /\\begin\s*{(wrap)?figure}[^]*?\\end\s*{(wrap)?figure}\s*/g, ''
       tex = processCites tex
       depths = []
       start = null
@@ -335,12 +372,13 @@
       for message in messages
         attach = []
         message.body = message.body
-        .replace /\\ref\s*{([^{}]*)}/g, (match, p1) ->
-          if p1 of labels
-            labels[p1]
-          else if p1 of figures
-            attach.push figures[p1]
-            "``#{figures[p1].labels[figures[p1].labels.length-1]}''"
+        .replace /\\ref\s*{([^{}]*)}/g, (match, ref) ->
+          if ref of labels
+            labels[ref]
+          else if ref of figures
+            attach.push figures[ref]
+            #"``#{figures[ref].labels[figures[p1].labels.length-1]}''"
+            "``#{ref}''"
           else
             console.warn "Unresolved #{match}"
             match
@@ -354,7 +392,8 @@
         attach = _.unique attach  ## upload \ref'd figures only once each
         for figure in attach
           figure.message =
-            title: "Figure ``#{figure.labels[figure.labels.length-1]}''"
+            title: "Figure " +
+              ("``#{label}''" for label in figure.labels).join " / "
             body: figure.caption
             published: now
             format: 'latex'
@@ -367,12 +406,9 @@

         await
           for figure in attach
-            figuremsg =
-              title: "Figure #{figure.labels[figure.labels.length-1]}"
-              body: body
-
             figure.files = []
-            for filename in figure.graphics
+            for graphic in figure.graphics
+              filename = graphic.filename
               base = basename filename
               file = new File [zip.files[filename].asArrayBuffer()],
                        base,
@@ -381,6 +417,7 @@
               file.creator = me
               file.created = now
               file.group = group
+              file.graphic = graphic
               figure.files.push file
               do (d = defer file.file2id) ->
                 file.callback = (file2) ->
@@ -391,7 +428,13 @@
             for file in figure.files
               filerev =
                 file: file.file2id
+                format: 'latex'
                 published: now
+              if file.graphic.labels.length > 0
+                filerev.title = "Figure " +
+                  ("``#{label}''" for label in file.graphic.labels).join " / "
+              if file.graphic.subcaption
+                filerev.body = file.graphic.subcaption
               filemsg = _.clone filerev
               filemsg.creator = me
               filemsg.created = now
@@ -412,8 +455,14 @@
   unless importer?
     console.warn "Unrecognized import format '#{format}'"
     return
+  sub = Meteor.subscribe 'messages.imported', group, ->
+  done = 0
   for file in files
     reader = new FileReader
     reader.onload = (e) ->
       importer group, e.target.result
+      done += 1
+      if done == files.length
+        sub.stop()
+        console.log 'Import complete'
     reader["readAs#{importer.readAs}"] file


Retina gravatar

Retina gravatar

--- client/main.coffee
+++ client/main.coffee
@@ -10,4 +10,4 @@
     # Using crypto-md5 mrt package
     email = Meteor.users.findOne(id)?.emails?[0]?.address
     hash = (if (email) then CryptoJS.MD5(email.trim().toLowerCase()).toString() else '')
-    return '//gravatar.com/avatar/' + hash + '?s=32&d=identicon'
+    return '//gravatar.com/avatar/' + hash + '?s=64&d=identicon'


Updated index.coffee (_readExpectedBytesOrRemainingBytes)

--- index.coffee
+++ index.coffee
@@ -37,6 +37,32 @@
     @writeStream.on 'end', =>
       @emit 'write-end'

+  _readExpectedBytesOrRemainingBytes: (bytesToRead, cbfn)->
+    bytesLeftToRead = bytesToRead
+    bytes = null
+
+    readIfReadable = =>
+      chunk = @readStream.read bytesLeftToRead
+      chunk = @readStream.read() if chunk is null
+      if chunk is null
+        return cbfn bytes
+
+      @writeStream.write chunk
+
+      bytesLeftToRead -= chunk.length
+      if bytes is null
+        bytes = chunk
+      else
+        bytes = Buffer.concat [ bytes, chunk ]
+
+      if bytesLeftToRead is 0
+        return cbfn bytes
+      else
+        @readStream.once 'readable', =>
+          readIfReadable()
+
+    readIfReadable()
+
   copy: ->
     @_gatherInputFileStats =>
       @_calculateUpperLimits()


removed HTTP 3xx handling

removed HTTP 3xx handling

--- index.coffee
+++ index.coffee
@@ -179,10 +179,14 @@
               @emit "crawled", txt
               @parse txt
             null
-        else if 300 <= res.statusCode < 400 # 3xx, redirect
-            req.end()
-            @uri = parseUri(res.headers.location)
-            return @crawl()
+        # removed HTTP 3xx redirects, as googlebot does not support it either
+        # as the robots.txt protocol is domain dependend
+        # and only valid if it's really the /robots.txt file
+        # yeah, the robots.txt is a pretty stupid protocol (but good enough)
+        #else if 300 <= res.statusCode < 400 # 3xx, redirect
+        #    req.end()
+        #    @uri = parseUri(res.headers.location)
+        #    return @crawl()
         else
             @emit "error", new Error 'invalid status code - is: HTTP '+res.statusCode+' - should: HTTP 200'
     #set the headers


Track temp folders and delete on exit

Track temp folders and delete on exit

--- index.coffee
+++ index.coffee
@@ -3,6 +3,8 @@
 path = require 'path'
 temp = require 'temp'
 _ = require 'underscore'
+
+temp.track()

 module.exports = (grunt) ->
   exec = (options, callback) ->


fix issue 750

fix issue 750

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -304,9 +304,18 @@
         text ?= "Last edit by #{@props.editor_name}"
         color ?= "#888"

-

-            {text}
-

+        if not @is_editing() and other_editors.size == 0
+            edit = "Last edit "
+            name = " by #{@props.editor_name}"
+

+                {edit}
+                <TimeAgo date={new Date(@props.message.get('history').peek()?.get('date'))} />
+                {name}
+

+        else
+

+                {text}
+

     show_user_name: ->

@@ -664,7 +673,7 @@
         if not IS_MOBILE
             <Grid>
                 <Row style={marginBottom:'5px'}>
-                    <Col xs={2} mdHidden >
+                    <Col xs={2} mdHidden>
                         <Button className='smc-small-only'
                                 onClick={@show_files}>
                                 <Icon name='toggle-up'/> Files


fix obvious bug in chat

fix obvious bug in chat

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -239,7 +239,7 @@
         @syncdb.save()

     set_to_last_input: =>
-        @setState(input:store.get('last_sent'))
+        @setState(input:@store.get('last_sent'))

     set_input: (input) =>
         @setState(input:input)


Make autoscroll happen on submit and not keydown

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -235,11 +235,11 @@
         input : ''

     keydown : (e) ->
-        @scroll_to_bottom()
         if e.keyCode==27 # ESC
             e.preventDefault()
             @clear_input()
         else if e.keyCode==13 and not e.shiftKey # 13: enter key
+            @scroll_to_bottom()
             e.preventDefault()
             mesg = @refs.input.getValue()
             # block sending empty messages


scoping

scoping

--- client/vein.coffee
+++ client/vein.coffee
@@ -38,4 +38,4 @@

 window.Vein = Vein
 # AMD compatibility
-window.define ["https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.2.js"], window.Vein if window.define?+window.define ["https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.2.js"], window.Vein if window.define?


changing the action method to allow a complet callback this will allow devs to use the pipeline and not have the app quit

--- lib/docpad.coffee
+++ lib/docpad.coffee
@@ -361,7 +361,7 @@

 	# Handle
-	action: (action) ->
+	action: (action, complete = -> process.exit()) ->
 		# Clear
 		if @actionTimeout
 			clearTimeout(@actionTimeout)
@@ -372,7 +372,7 @@
 			logger.log 'notice', 'Action received, but we still loading... waiting...'
 			@actionTimeout = setTimeout(
 				=>
-					@action action
+					@action action, complete
 				500
 			)
 			return
@@ -382,12 +382,12 @@
 			when 'skeleton', 'scaffold'
 				@skeletonAction (err) ->
 					return @error(err)  if err
-					process.exit()
+					complete()

 			when 'generate'
 				@generateAction (err) ->
 					return @error(err)  if err
-					process.exit()
+					complete()

 			when 'watch'
 				@watchAction (err) ->


removed a debugger

removed a debugger

--- lib/docpad.coffee
+++ lib/docpad.coffee
@@ -1310,7 +1310,6 @@
 			next?()

 		# Fetch
-		debugger
 		unless documents.length
 			tasks.exit()
 		else
@@ -1331,7 +1330,7 @@

 		# Log
 		logger.log 'debug', "Rendering #{documents.length} files"
-
+
 		# Async
 		tasks = new balUtil.Group (err) ->
 			return next?(err)  if err


fix #52

fix #52

--- lib/ensime.coffee
+++ lib/ensime.coffee
@@ -301,6 +301,6 @@
         file: editor.getPath()
         contents: editor.getText()
     @client?.post(req, (msg) ->
-      editor.setText(msg)
+      editor.setText(msg.text)
       editor.setCursorBufferPosition(cursorPos)
     )


Re-enable random movement of Controller.Random

--- src/public/scripts/models/controller.random.coffee
+++ src/public/scripts/models/controller.random.coffee
@@ -17,8 +17,8 @@
 			_.defer(@_updateAll)

 		_movement: ( time ) ->
-			leftRight = 0#Math.sin(time * Math.PI / 10)
-			forwardBackward = 0#Math.cos(time * Math.PI / 10)
+			leftRight = Math.sin(time * Math.PI / 10)
+			forwardBackward = Math.cos(time * Math.PI / 10)

 			if leftRight > 0
 				@FlyRight = leftRight
@@ -35,8 +35,8 @@
 				@FlyBackward = -forwardBackward

 		_gunRotation: ( time ) ->
-			leftRight = 0#Math.sin(time * Math.PI / 10 - Math.PI / 2)
-			upDown = 0#Math.cos(time * Math.PI / 10 - Math.PI / 2)
+			leftRight = Math.sin(time * Math.PI / 10 - Math.PI / 2)
+			upDown = Math.cos(time * Math.PI / 10 - Math.PI / 2)

 			if leftRight > 0
 				@RotateCannonRight = leftRight


Add refresh shortkey

Add refresh shortkey

--- views/app.cjsx
+++ views/app.cjsx
@@ -377,6 +377,21 @@
   ]
   window.toggleModal title, content, footer

+# F5 & Ctrl+F5
+window.addEventListener 'keydown', (e) ->
+  if process.platform == 'darwin' and e.keyCode is 82 and e.metaKey
+    if e.shiftKey # cmd + shift + r
+      $('kan-game webview').reloadIgnoringCache()
+    else # cmd + r
+      # Catched by menu
+      # $('kan-game webview').reload()
+      false
+  else if e.keyCode is 116
+    if e.ctrlKey # ctrl + f5
+      $('kan-game webview').reloadIgnoringCache()
+    else # f5
+      $('kan-game webview').reload()
+
 # Confirm before quit
 confirmExit = false
 exitPoi = ->


Right click on 'Auto adjust' button to unlock the webview

--- views/app.cjsx
+++ views/app.cjsx
@@ -359,6 +359,8 @@
   handleJustifyLayout: (e) ->
     window.dispatchEvent new Event('resize')
     e.preventDefault()
+  handleUnlockWebview: ->
+    $('kan-game webview').executeJavaScript "document.documentElement.style.overflow = 'auto'"
   componentDidMount: ->
     setTimeout =>
       try
@@ -381,7 +383,7 @@
         <Button onClick={@handleOpenScreenshotFolder} bsSize='small'><FontAwesome name='photo' /></Button>
       </OverlayTrigger>
       <OverlayTrigger placement='right' overlay={<Tooltip id='poi-adjust-button'>{__ 'Auto adjust'}</Tooltip>}>
-        <Button onClick={@handleJustifyLayout} bsSize='small'><FontAwesome name='arrows-alt' /></Button>
+        <Button onClick={@handleJustifyLayout} onContextMenu={@handleUnlockWebview} bsSize='small'><FontAwesome name='arrows-alt' /></Button>
       </OverlayTrigger>
       <OverlayTrigger placement='right' overlay={<Tooltip id='poi-always-on-top-button'>{if @state.alwaysOnTop then __ 'Dont always on top' else __ 'Always on top'}</Tooltip>}>
         <Button onClick={@handleSetAlwaysOnTop} bsSize='small' className={if @state.alwaysOnTop then 'active' else ''}><FontAwesome name={if @state.alwaysOnTop then 'arrow-down' else 'arrow-up'} /></Button>


default to no item selected

default to no item selected

--- @client/drop_menu.coffee
+++ @client/drop_menu.coffee
@@ -43,7 +43,7 @@
   displayName: 'DropMenu'

   render : ->
-
+    @local.active_option ?= -1
     open_menu_on = @props.open_menu_on or 'focus' #other option is 'activation'

     wrapper_style = _.defaults {}, @props.wrapper_style,
@@ -77,12 +77,13 @@
     render_option = @props.render_option

     set_active = (idx) =>
-      idx = 0 if !idx?
+      idx = -1 if !idx?
       @local.active_option = idx
       save @local
-      setTimeout =>
-        @refs["menuitem-#{idx}"].getDOMNode().focus()
-      , 0
+      if idx != -1
+        setTimeout =>
+          @refs["menuitem-#{idx}"].getDOMNode().focus()
+        , 0

     trigger = (e) =>
@@ -152,12 +153,12 @@

         onMouseEnter: if open_menu_on == 'focus' then (e) =>
                 @local.show_menu = true
-                set_active(0)
+                set_active(-1)
                 save @local

         onClick: if open_menu_on != 'focus' then (e) =>
                 @local.show_menu = !@local.show_menu
-                set_active(0) if @local.show_menu
+                set_active(-1) if @local.show_menu
                 save @local

         onKeyDown: (e) =>
@@ -222,11 +223,11 @@
                     set_active idx

                 onBlur: (e) =>
-                  @local.active_option = null
+                  @local.active_option = -1
                   save @local

                 onMouseExit: (e) =>
-                  @local.active_option = null
+                  @local.active_option = -1
                   save @local
                   e.stopPropagation()