Refactor ui watcher to use packageWatcher

--- lib/ui-watcher.coffee
+++ lib/ui-watcher.coffee
@@ -1,20 +1,36 @@
+BaseThemeWatcher = require './base-theme-watcher'
 ThemeWatcher = require './theme-watcher'
-BaseThemeWatcher = require './base-theme-watcher'
+PackageWatcher = require './package-watcher'

 module.exports =
 class UIWatcher
   constructor: ({@themeManager}) ->
-    @baseTheme = new BaseThemeWatcher()
+    @watchers = []
+    @baseTheme = @createWatcher(BaseThemeWatcher)
+    @watchThemes()
+    @watchPackages()
+
+  watchThemes: ->
     for theme in @themeManager.getActiveThemes()
-      @createThemeWatcher(theme)
+      @createWatcher(ThemeWatcher, theme)
+
     @themeManager.on 'theme-activated', (theme) =>
-      @createThemeWatcher(theme)
+      @createWatcher(ThemeWatcher, theme)

-  createThemeWatcher: (theme) ->
-    watcher = new ThemeWatcher(theme)
+  watchPackages: ->
+    for pack in atom.getActivePackages()
+      console.log pack
+      @createWatcher(PackageWatcher, pack) if PackageWatcher.supportsPackage(pack)
+
+  createWatcher: (type, object) ->
+    watcher = new type(object)
     watcher.on 'globals-changed', @reloadAll
+    watcher.on 'destroyed', =>
+      @watchers = _.without(@watchers, watcher)
+    @watchers.push(watcher)
     watcher

   reloadAll: =>
     @baseTheme.reloadStylesheet()
-    theme.load() for theme in atom.themes.getThemes()
+    theme.load() for theme in atom.themes.getActiveThemes()
+    pack.reloadStylesheets() for pack in atom.getActivePackages() when PackageWatcher.supportsPackage(pack)


Use subscribeToCommand

Use subscribeToCommand

Closes #38

--- lib/whitespace.coffee
+++ lib/whitespace.coffee
@@ -8,11 +8,11 @@
     @subscribe atom.workspace.eachEditor (editor) =>
       @handleEvents(editor)

-    @subscribe atom.workspaceView.command 'whitespace:remove-trailing-whitespace', =>
+    @subscribeToCommand atom.workspaceView, 'whitespace:remove-trailing-whitespace', =>
       if editor = atom.workspace.getActiveEditor()
         @removeTrailingWhitespace(editor, editor.getGrammar().scopeName)

-    @subscribe atom.workspaceView.command 'whitespace:convert-tabs-to-spaces', =>
+    @subscribeToCommand atom.workspaceView, 'whitespace:convert-tabs-to-spaces', =>
       if editor = atom.workspace.getActiveEditor()
         @convertTabsToSpaces(editor)


Always use VIE RdfaService

Always use VIE RdfaService

--- extension/blogsiple/staticSrc/js/editable.coffee
+++ extension/blogsiple/staticSrc/js/editable.coffee
@@ -3,6 +3,7 @@
   v.use new v.StanbolService
     proxyDisabled: true
     url : 'http://dev.iks-project.eu:8081'
+  v.use new v.RdfaService

   jQuery('body').midgardCreate
     vie: v


remove debug log message

remove debug log message

--- app/models/task.coffee
+++ app/models/task.coffee
@@ -119,12 +119,8 @@

     # Set given task as first task of todo task list.
     Task.setFirstTask = (task, callback) ->
-        compound.logger.write "first task"
-        compound.logger.write task.list
-        compound.logger.write task.id
         Task.retrieveTodoList task.list, (err, tasks) ->
             return callback err, null if err
-            compound.logger.write tasks.length

             # Case where the given task is already the first task
             if not tasks.length or
@@ -135,10 +131,6 @@
             # Case where the given task is not the first task
             else
                 firstTask = Task.getFirstTaskFromList tasks
-                compound.logger.write firstTask.id
-                compound.logger.write task.id
-
-
                 updateLinks = (task) ->
                     firstTask.previousTask = task.id
                     task.nextTask = firstTask.id


Determine type of fieldset

Determine type of fieldset

--- components/fieldset/index.cjsx
+++ components/fieldset/index.cjsx
@@ -2,10 +2,19 @@
 @todo
 ###

-Autocomplete  = require '../autocomplete'
-Dropdown      = require '../dropdown'
-Input         = require '../input'
-Switch        = require '../switch'
+# -- Components
+Autocomplete           = require './autocomplete'
+Dropdown               = require './dropdown'
+Input                  = require './input'
+Switch                 = require './switch'
+# -- Constants
+TYPE =
+  AUTOCOMPLETE: 'autocomplete'
+  CHECKBOX    : 'checkbox'
+  DROPDOWN    : 'dropdown'
+  LABEL       : 'label'
+  RADIO       : 'radio'
+  SWITCH      : 'switch'

 module.exports = React.createClass

@@ -14,24 +23,26 @@
     attributes        : React.PropTypes.array
     className         : React.PropTypes.string
     label             : React.PropTypes.string
+    value             : React.PropTypes.any
     onChange          : React.PropTypes.func

   getDefaultProps: ->
     attributes        : []
+    className         : ''

   getInitialState: ->
     attributes        : @props.attributes
-    value_object      : _determineTypeOfValue @props.attributes
+    type              : _determineType @props.attributes

   # -- Lifecycle
   componentWillReceiveProps: (next_props) ->
     if attributes = next_props.attributes
-      @setState attributes: attributes, value_object: _determineTypeOfValue attributes
-      @setValue attributes
+      @setState attributes: attributes, type: _determineType attributes
+      @setValue next_props.value or @props.value

   # -- Events
   onChange: (event) ->
-    unless @state.value_object
+    if @state.type is TYPE.RADIO
       for ref, el of @refs when el.refs.input.getDOMNode() isnt event.target
         el.setValue false

@@ -41,28 +52,28 @@
       is_valid = false
       @refs[attr.ref].setError? 'Required field'
       break
-    setTimeout (=> @props.onChange? event, @), 10
+    setTimeout (=> @props.onChange event, @), 10 if @props.onChange?

   # -- Render
   render: ->
-    <fieldset data-react-toolbox='fieldset'
+

       { {@props.label} if @props.label }
       {
         for attribute, index in @state.attributes
-          if attribute.type is 'label'
+          if attribute.type is TYPE.LABEL
             {attribute.caption}
-          else if attribute.type is 'autocomplete'
+          else if attribute.type is TYPE.AUTOCOMPLETE
             <Autocomplete key={index} {...attribute} onChange={@onChange}/>
-          else if attribute.type is 'dropdown'
+          else if attribute.type is TYPE.DROPDOWN
             <Dropdown key={index} {...attribute} onChange={@onChange}/>
-          else if attribute.type is 'switch'
+          else if attribute.type is TYPE.SWITCH
             <Switch key={index} {...attribute} onChange={@onChange}/>
           else
             <Input key={index} {...attribute} />
       }
       { @props.children }
-    </fieldset>
+

   # -- Extends
   clean: ->
@@ -70,19 +81,26 @@

   getValue: ->
     value = {}
-    if @state.value_object
+    if @state.type isnt TYPE.RADIO
       value[ref] = input.getValue() for ref, input of @refs when input.getValue?
     else
       value = ref for ref, input of @refs when input.getValue?() is true
     value

-  setValue: (data = []) ->
-    @refs[field.ref]?.setValue? field.value for field in data
+  setValue: (data = {}) ->
+    if data instanceof Object
+      @refs[key]?.setValue? value for key, value of data
+    else
+      @refs[key].setValue? key is data for key of @refs

 # -- Internal methods
-_determineTypeOfValue = (attributes) ->
-  value_object = false
-  for attribute in attributes when attribute.type not in ['label', 'radio']
-    value_object = true
-    break
-  value_object
+_determineType = (attributes) ->
+  type = ''
+  group_radio     = true
+  group_checkbox  = true
+  for attribute in attributes when attribute.type isnt TYPE.LABEL
+    group_radio = false if attribute.type isnt TYPE.RADIO
+    group_checkbox = false if attribute.type isnt TYPE.CHECKBOX
+  type = TYPE.RADIO if group_radio
+  type = TYPE.CHECKBOX if group_checkbox
+  type


Update Collection2.

Update Collection2.

--- models/collections.coffee
+++ models/collections.coffee
@@ -16,7 +16,7 @@
         type: String
 })

-@ChanslateMessages = new Meteor.Collection2(
+@ChanslateMessages = new Meteor.Collection(
     'chanslateMessages',
     {
         schema:
@@ -47,7 +47,7 @@
 })

-@ChanslateRooms = new Meteor.Collection2(
+@ChanslateRooms = new Meteor.Collection(
     'chanslateRooms',
     {
         schema:


Fix coffee test

Fix coffee test

--- test/openfisca/mapping/reverse.coffee
+++ test/openfisca/mapping/reverse.coffee
@@ -26,6 +26,7 @@
     paje_base: '2014-11': 1
     bourse_college: '2014-11': 1
     bourse_lycee: '2014-11': 1
+    paris_logement_familles: '2014-11': 1

   DATE_DE_VALEUR = new Date '2014-11'


Clean up test

Clean up test

--- test/services/dynamodb.spec.coffee
+++ test/services/dynamodb.spec.coffee
@@ -37,27 +37,31 @@
       dynamo = ddb(accessKeyId: 'AKID', secretAccessKey: 'SECRET', region: 'us-west-2')

     if AWS.util.isNode()
-      it 'does not verify response checksum when streaming', ->
+      it 'does not verify response checksum when streaming', (done) ->
+        err = null
         helpers.mockHttpResponse 200, {'x-amz-crc32': '0'}, """{"TableNames":["mock-table"]}"""
-        request = dynamo.makeRequest('listTables')
+        request = dynamo.listTables()
+        request.on 'complete', ->
+          expect(err).to.eql(null)
+          done()
         s = request.createReadStream()
-        s.on 'end', ->
-          expect(request.error).to.eql(null)
+        s.on 'data', ->
+        s.on 'error', (e) -> err = e

     it 'does verify response checksum when not streaming', ->
       helpers.mockHttpResponse 200, {'x-amz-crc32': '1575962599'}, """{"TableNames":["mock-table"]}"""
-      request = dynamo.makeRequest('listTables')
+      request = dynamo.listTables()
       request.send (err, data) ->
         expect(err).to.eql(null)

     it 'does not verify response checksum when no [x-amz-crc32] header', ->
       helpers.mockHttpResponse 200, {}, """{"TableNames":["mock-table"]}"""
-      request = dynamo.makeRequest('listTables')
+      request = dynamo.listTables()
       request.send (err, data) ->
         expect(err).to.eql(null)

     it 'throws an error when response checksum does not match', ->
       helpers.mockHttpResponse 200, {'x-amz-crc32': '0'}, """{"TableNames":["mock-table"]}"""
-      request = dynamo.makeRequest('listTables')
+      request = dynamo.listTables()
       request.send (err, data) ->
         expect(err.code).to.eql('CRC32CheckFailed')


test(navbar): allow icons in ios navbar

test(navbar): allow icons in ios navbar

--- test/spec/directives/navbar.coffee
+++ test/spec/directives/navbar.coffee
@@ -34,7 +34,7 @@

     describe 'controller', ->
-      it 'getTitleFromState', inject ($controller) ->
+      it 'should getTitleFromState', ->
         childScope = title.scope()
         stateTitle = childScope.getTitleFromState
           data: title: 'Foo'
@@ -46,6 +46,15 @@
           name: 'Buz'
           data: title: 'Baz'
         expect(stateTitle).toBe 'Baz'
+
+      it 'should convertActionToIcon', ->
+        childScope = title.scope()
+        $action = angular.element '<bp-action class="bp-button">Yo</bp-action>'
+        childScope.convertActionToIcon $action
+        expect($action.text()).toBe ''
+        expect($action.hasClass 'bp-button').toBe false
+        expect($action.hasClass 'bp-icon').toBe true
+        expect($action.attr 'aria-label').toBe 'Yo'

     describe 'element', ->
       it 'should have ARIA role', ->
@@ -74,6 +83,16 @@
         expect($spacer.attr 'style').toMatch /flex/
         expect(element.children().get 2).toBe $spacer.get 0
         element.detach()
+
+      it 'should allow icons in navbar', inject ($compile) ->
+        element2 = $compile("
+          <bp-navbar bp-navbar-no-up bp-navbar-title>
+            <bp-action class='bp-icon'>Action</bp-action>
+          </bp-navbar>") scope
+        $icon = element2.find('bp-action')
+        expect($icon.hasClass 'bp-button').toBe false
+        expect($icon.hasClass 'bp-icon').toBe true
+        expect($icon.attr 'aria-label').toBe 'Action'

   describe 'android', ->
     scope   = null


Use notifications for tabBar

Use notifications for tabBar

--- testApp/app/controllers/tabbar.coffee
+++ testApp/app/controllers/tabbar.coffee
@@ -9,12 +9,12 @@

   @testHide: ->
     steroids.tabBar.hide {},
-      onSuccess: -> alert "hided"
+      onSuccess: -> notification "hidden"
       onFailure: -> alert "failed to hide"

   @testShow: ->
     steroids.tabBar.show {},
-      onSuccess: -> alert "showed"
+      onSuccess: -> notification "showed"
       onFailure: -> alert "failed to show"

   @testUpdate: ->
@@ -37,7 +37,7 @@
           }
         ]
       },
-      onSuccess: -> alert "updated"
+      onSuccess: -> notification "updated"
       onFailure: -> alert "failed to update"
     )

@@ -56,7 +56,7 @@
           }
         ]
       },
-      onSuccess: -> alert "updated"
+      onSuccess: -> notification "updated"
       onFailure: -> alert "failed to update"
     )

@@ -70,7 +70,7 @@
       badge: "1"
     },
     onSuccess: ->
-      alert "Success updating curent tab"
+      notification "Success updating curent tab"
     onFailure: (failure) ->
       console.log "Failed to update current tab", failure
       alert "Failed to update current tab"
@@ -85,7 +85,7 @@

     @willChangeHandlers.push eventHandler

-    alert "event listener added"
+    notification "event listener added"

   @testdidchangeEvent: ->
     eventHandler = steroids.tabBar.on 'didchange', (event) ->
@@ -93,7 +93,7 @@

     @didChangeHandlers.push eventHandler

-    alert "event listener added"
+    notification "event listener added"

   @testRemoveAllEventHandlers: ->
     @didChangeHandlers.forEach (handlerId) -> steroids.tabBar.off 'didchange', handlerId
@@ -103,15 +103,15 @@
     @willChangeHandlers = []
     @didChangeHandlers = []

-    alert "event handlers removed"
+    notification "event handlers removed"

   @testRemoveDidChangeEvents: ->
     steroids.tabBar.off 'didchange'

-    alert "didchange events handlers removed"
+    notification "didchange events handlers removed"

   @testRemoveWillChangeEvents: ->
     steroids.tabBar.off 'willchange'

-    alert "willchange events handlers removed"
+    notification "willchange events handlers removed"


Try to detect redirect and follow it

Try to detect redirect and follow it

--- app/assets/javascripts/effective_datatables/inline_crud.js.coffee
+++ app/assets/javascripts/effective_datatables/inline_crud.js.coffee
@@ -21,10 +21,14 @@
   true

 # We have either completed the resource action, or fetched the inline form to load.
-$(document).on 'ajax:success', '.dataTables_wrapper .col-actions', (event) ->
+$(document).on 'ajax:success', '.dataTables_wrapper .col-actions', (event, data) ->
   $action = $(event.target)

   return true if ('' + $action.data('inline')) == 'false'
+
+  if data.length > 0
+    return true if data.indexOf('Turbolinks.clearCache()') == 0 && data.includes("Turbolinks.visit(")
+    return true if data.indexOf('<html') >= 0

   if ($action.data('method') || 'get') == 'get'
     if $action.closest('tr').parent().prop('tagName') == 'THEAD' then afterNew($action) else afterEdit($action)
@@ -34,6 +38,7 @@
   EffectiveForm.remote_form_payload = ''
   EffectiveForm.remote_form_flash = ''
   EffectiveForm.remote_form_refresh_datatables = ''
+
   true

 # There was an error completing something


[fix] Fixed updating the "Today" line on the Roadmap dashboard (10m)

--- app/assets/javascripts/houston/roadmap/views/_roadmap_view.coffee
+++ app/assets/javascripts/houston/roadmap/views/_roadmap_view.coffee
@@ -73,8 +73,9 @@
     @update(transition: false)

   update: (options)->
+    transition = options.transition ? true
     view = @
-    today = new Date()
+    @today = new Date()

     bands = @roadmap.selectAll('.roadmap-band')
       .data(@groupMilestonesIntoBands(), (band)-> band.key)
@@ -97,7 +98,7 @@
         .attr('class', 'roadmap-weekend')
         .attr('style', (date)=> "left: #{@x(date)}px; width: #{@x(2.days().after(date)) - @x(date)}px;")

-      update = if options.transition then weeks.transition(150) else weeks
+      update = if transition then weeks.transition(150) else weeks
       update
         .attr('style', (date)=> "left: #{@x(date)}px; width: #{@x(2.days().after(date)) - @x(date)}px;")

@@ -128,15 +129,15 @@
       .append('span')
         .text((milestone)-> milestone.name)

-    update = if options.transition then milestones.transition(150) else milestones
+    update = if transition then milestones.transition(150) else milestones
     update
-      .attr 'class', (milestone)->
+      .attr 'class', (milestone)=>
         classes = ['roadmap-milestone']
         classes.push(if milestone.locked then 'locked' else 'unlocked')
         classes.push(if milestone.completed then 'completed' else 'uncompleted')
-        if milestone.startDate > today
+        if milestone.startDate > @today
           classes.push 'upcoming'
-        else if milestone.endDate < today
+        else if milestone.endDate < @today
           classes.push 'past'
         else
           classes.push 'active'
@@ -154,15 +155,15 @@

     if @showToday
-      today = @roadmap.selectAll('.roadmap-today')
+      todayLine = @roadmap.selectAll('.roadmap-today')
         .data([@today])

-      today.enter()
+      todayLine.enter()
         .append('div')
           .attr('class', 'roadmap-today')
           .attr('style', (date)=> "left: #{@x(date)}px;")

-      update = if options.transition then today.transition(150) else today
+      update = if transition then todayLine.transition(150) else todayLine
       update
         .attr('style', (date)=> "left: #{@x(date)}px;")


Corrected if statment syntax

Corrected if statment syntax

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -352,10 +352,10 @@
       console.log 'Error getting vendor: ' + vendorHash
       return deferred.reject()

-    if !response.definitions
-    or !response.definitions.vendorDetails
-    or !response.definitions.vendorDetails[vendorHash]
-    or !response.definitions.vendorDetails[vendorHash].vendorName
+    if !response.definitions or
+      !response.definitions.vendorDetails or
+      !response.definitions.vendorDetails[vendorHash] or
+      !response.definitions.vendorDetails[vendorHash].vendorName
         console.log 'Error getting vendor details: ' + vendorHash
         return deferred.reject()


Use array.concat instead

Use array.concat instead

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -65,7 +65,7 @@
           for category in saleItemCategories
             if category.categoryIndex in Constants.BOUNTIES_CATEGORY_INDICES
               console.log category
-              bountyItems.push category.salesItems...
+              bountyItems = bountyItems.concat category.salesItems
               --remainingIteration

               if remainingIteration is 0


meadow > Reset on window resize.

meadow > Reset on window resize.

--- meadow/src/app.coffee
+++ meadow/src/app.coffee
@@ -41,6 +41,7 @@

         @container.surface
             .append 'line'
+            .attr 'class', 'branch'
             .attr 'x1', branch.x
             .attr 'y1', branch.y
             .attr 'x2', branch.x
@@ -75,6 +76,9 @@
             ease: 'cubic'
         stump

+x_poses = []
+y_poses = []
+
 browns = [
     '#49281F',
     '#564334',
@@ -108,8 +112,8 @@
         branchAngle: Math.random() * 90 * -1
         branchColor: greens[Math.floor(Math.random() * greens.length - 1)]
         branchLength: Math.random() * 40
-        branchWidth: Math.random() * 18 + 10
-        divergence: Math.random() * -0.5 * 2
+        branchWidth: Math.random() * 8 + 10
+        divergence: 1
         ease: eases[Math.floor(Math.random() * eases.length)]
         growSpeed: Math.random() * 1200 + 100
         stumpColor: browns[Math.floor(Math.random() * browns.length - 1)]
@@ -118,15 +122,28 @@
         x: x
         y: y

-# Grow trees
-container = new Container
-x_poses = [1..30].map (i) -> Math.random() * window.innerWidth
-y_poses = [1..30].map (i) -> Math.random() * window.innerHeight
-y_poses = y_poses.sort (a, b) -> a - b
-
-setInterval =>
+growTree = ->
     if treeCount < 30
         tree = new Tree(container, randomTree(x_poses[treeCount], y_poses[treeCount]))
         tree.grow tree.stump()
         treeCount += 1
-, 100
+        setTimeout growTree, 100
+
+reset = ->
+    branches = document.getElementsByClassName 'branch'
+    branches[0].parentElement.removeChild(branches[0]) while branches[0]
+    startGrowing()
+
+startGrowing = ->
+    container.resizeContainer()
+    x_poses = [1..30].map (i) -> Math.random() * window.innerWidth
+    y_poses = [1..30].map (i) -> Math.random() * window.innerHeight
+    y_poses = y_poses.sort (a, b) -> a - b
+    treeCount = 0
+    growTree()
+
+window.addEventListener 'resize', reset
+
+# Grow trees
+container = new Container
+startGrowing()


Inspect points.

Inspect points.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -117,7 +117,7 @@

             pointcount = robot.brain.get 'winners'
             savescore = ["#{username} has #{attrs.points}" for username, attrs of pointcount]
-            msg.send "#{Util.inspect(pointcount)}"
+            msg.send "#{Util.inspect(points)}"

     # robot.respond /top (\d*)$/i, (msg) ->
     #     if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])


Fix missing reference to handler.

Fix missing reference to handler.

--- server-src/dev.coffee
+++ server-src/dev.coffee
@@ -73,6 +73,8 @@

         res.render template, vars

+requestHandler = makeHandler('home.jade')
+
 handleUnit = (req, res, next) ->
     if req.query.service? or req.query.division?
         requestHandler req, res, next
@@ -136,11 +138,12 @@
     @use slashes(false)
     # Expose the original sources for better debugging
     @use config.url_prefix + 'src', express.static(__dirname + '/../src')
+
     # Emit unit data server side for robots
     @use config.url_prefix + 'unit', handleUnit
     # Handler for embed urls
     @use config.url_prefix + 'embed', makeHandler('embed.jade')
     # Handler for everything else
-    @use config.url_prefix, makeHandler('home.jade')
+    @use config.url_prefix, requestHandler

 server.listen serverPort


Minor fix.

Minor fix.

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -100,6 +100,7 @@
         $rootScope._login = { state, stateParams }

         $state.go 'login'
+        return

       # The user must have the `admin` permission to access the
       # settings page.


Bring back sections

Bring back sections


group controller: fat fingers

group controller: fat fingers

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -53,7 +53,7 @@
     mainController.on 'loginRequired',        @loginRequired.bind this

   getCurrentGroup:->
-    throw 'FIXME: array whould never be passed'  if Array.isArray @currentGroupData.data
+    throw 'FIXME: array should never be passed'  if Array.isArray @currentGroupData.data
     return @currentGroupData.data

   openGroupChannel:(group, callback=->)->


hide the vocabulary tab for now

hide the vocabulary tab for now

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -706,7 +706,7 @@
     @prepareSettingsTab()
     @preparePermissionsTab()
     @prepareMembersTab()
-    @prepareVocabularyTab()
+#    @prepareVocabularyTab()

     if 'private' is group.privacy
       @prepareMembershipPolicyTab()


tests/logs.spec: Increase the delay of logs.subscribe()

That's in an effort to make the CI tests more stable.

Change-type: patch
Signed-off-by: Thodoris Greasidis <5791bdc4541e79bd43d586e3c3eff1e39c16665f@balena.io>

--- tests/integration/logs.spec.coffee
+++ tests/integration/logs.spec.coffee
@@ -89,7 +89,7 @@
 						logs.on('line', (line) -> lines.push(line))
 						logs.on('error', reject)

-						Promise.delay(2000)
+						Promise.delay(10000)
 						.then ->
 							resolve(lines)
 					.finally(logs.unsubscribe)
@@ -112,7 +112,7 @@
 						logs.on('line', (line) -> lines.push(line))
 						logs.on('error', reject)

-						Promise.delay(2000)
+						Promise.delay(10000)
 						.then ->
 							resolve(lines)
 					.finally(logs.unsubscribe)
@@ -140,7 +140,7 @@
 						logs.on('line', (line) -> lines.push(line))
 						logs.on('error', reject)

-						Promise.delay(2000)
+						Promise.delay(10000)
 						.then ->
 							resolve(lines)
 					.finally(logs.unsubscribe)
@@ -167,7 +167,7 @@
 								message: 'New message',
 								timestamp: Date.now()
 							}]
-							.delay(2000)
+							.delay(10000)
 							.then ->
 								resolve(lines)
 							.catch(reject)


Adds a page route

Adds a page route

--- apps/page/index.coffee
+++ apps/page/index.coffee
@@ -22,6 +22,7 @@
 app.get '/past-privacy-10-29-12', routes.vanityUrl('past-privacy-10-29-12')
 app.get '/rrf-emerging-curator-competition-official-rules', routes.vanityUrl('rrf-emerging-curator-competition-official-rules')

+app.get '/page/:id', routes.index
 app.get '/job/:id', routes.index
 # Handle urls like /job/devops-engineer/about
 app.get '/job/:id/*', routes.index


Lest I go insane

Lest I go insane

--- apps/user/index.coffee
+++ apps/user/index.coffee
@@ -13,3 +13,4 @@
 app.get '/collector/edit', routes.settings
 app.get '/user/refresh', routes.refresh
 app.get '/user/delete', routes.delete
+app.get '/settings', (req, res) -> res.redirect '/user/edit'


move rename after task list to avoir filter to exclude file because of the _i name

--- gassetic.coffee
+++ gassetic.coffee
@@ -177,11 +177,6 @@
 		filtered = sourceFiles.filter (path) ->
 			path.indexOf('*') == -1 # remove all with stars
 		pipe = pipe.pipe expect {errorOnFailure: true, reportUnexpected: false}, filtered
-		if @isDev() and (@getMimetypes()[type][@env].autoRenaming == undefined or @getMimetypes()[type][@env].autoRenaming == true)
-			i = 0
-			pipe = pipe.pipe rename (path) ->
-				path.basename += '_' + i++
-				path
 		tasks.map (t) =>
 			if t.filter
 				filter = gulpFilter(t.filter)
@@ -199,6 +194,11 @@
 				pipe = pipe.pipe @getModuleMethod(@modules, t.name).call @
 			if filter
 				pipe = pipe.pipe filter.restore()
+		if @isDev() and (@getMimetypes()[type][@env].autoRenaming == undefined or @getMimetypes()[type][@env].autoRenaming == true)
+			i = 0
+			pipe = pipe.pipe rename (path) ->
+				path.basename += '_' + i++
+				path

 		pipe = pipe.pipe gulp.dest destination
 			.pipe tap (f) =>


adds params to transaction url

adds params to transaction url

--- models/account.coffee
+++ models/account.coffee
@@ -23,8 +23,10 @@
       position.contract is contract.get('id')

   makeTransaction: ({ transaction, contract, block_id }, options) ->
+    { authId, authToken } = authTokenPair()
     order = new Backbone.Model
-    order.url = "#{@url()}/#{transaction}/#{contract.id}/block_id/1"
+    params = "?auth_id=#{authId}&auth_token=#{authToken}"
+    order.url = "#{@url()}/#{transaction}/#{contract.id}/block_id/#{block_id}#{params}"
     order.save null, options

   canMakeTransaction: (transaction, contract) ->


work, work

work, work

--- grammars/d.cson
+++ grammars/d.cson
@@ -113,19 +113,18 @@

 'patterns' : [
-    {
-        'include': '#package'
-    }
-    {
-        'include': '#code'
-    }
+  {
+    'include': '#package'
+  }
+  {
+    'include': '#code'
+  }

 ]
 # Repository of base constructs
 # Use these to match code patterns
 #
 # all-types
-# annotations
 # anonymous-classes-and-new
 # assertions
 # attributes
@@ -147,393 +146,390 @@
 # primitive-types
 # storage-modifiers
 # strings
-# values
 # variables

 'repository':
-    'all-types':
+  'all-types':
+    'patterns': [
+
+    ]
+  'anonymous-classes-and-new':
+    'patterns': [
+
+    ]
+  'assertions':
+    # assert, enforce, exceptions and other contract structures
+    'patterns': [
+      {
+        # Exception keywords
+        'match': '\\b(try|catch|finally|throw|nothrow|in|body)\\b'
+        'name': 'keyword.other.exception.d'
+      }
+      {
+        # assertions
+        'match': '\\b(assert|enforce|invariant)'
+        'name': 'entity.name.function.assertion.d'
+      }
+    ]
+  'attributes':
+    'patterns': [
+      {
+        'match': '(@(safe|system|trusted|property))\\b'
+        'name': 'keyword.other.safety.d'
+      }
+    ]
+  'block':
+    # meta-pattern - any legal code that can exist inside { }
+    'begin': '{'
+    'end': '}'
+    'patterns': [
+      {
+        'include': '#code'
+      }
+    ]
+  'block-markup':
+    'patterns': [
+      {
+        # single-line asm
+        'match': '(asm)\\s+([^\\{\\}$]+)'
+        'captures':
+          '1':
+            'name': 'keyword.other.asm.d'
+          '2':
+            'name': 'gfm.markup.raw.asm.d'
+      }
+      {
+        # block-level asm
+        'begin': '(asm)(?:\\s+{)'
+        'beginCaptures':
+          '1':
+            'name': 'keyword.other.asm.d'
+        'end': '}'
+        'contentName': 'gfm.markup.raw.asm.d'
+      }
+    ]
+  'class':
+    'patterns': [
+
+    ]
+  'class-body':
+    'patterns': [
+
+    ]
+  'code':
+    # Meta class - basically all legal code that can be in a block
+    'patterns': [
+      {
+        'include': '#functions'
+      }
+      {
+        'include': '#assertions'
+      }
+      {
+        'include': '#strings'
+      }
+      {
+        'include': '#comments'
+      }
+      {
+        'include': '#primitive-types'
+      }
+      {
+        'include': '#constants-and-special-vars'
+      }
+      {
+        'include': '#attributes'
+      }
+      {
+        'include': '#keywords'
+      }
+      {
+        'include': '#storage-modifiers'
+      }
+      {
+        'include': '#block-markup'
+      }
+      {
+        'include': '#variables'
+      }
+      {
+        'include': '#methods'
+      }
+
+    ]
+  'comments':
+    'patterns': [
+      {
+        'captures':
+          '0':
+            'name': 'punctuation.definition.comment.d'
+        'match': '/\\*\\*/'
+        'name': 'comment.block.empty.d'
+      }
+      {
+        'include': '#comments-inline'
+      }
+    ]
+  'comments-inline':
+    'patterns': [
+      {
+        # C-style comments
+        'begin': '/\\*'
+        'end': '\\*/'
+        'name': 'comment.block.d'
+      }
+      {
+        # C++-style comments
+        'begin': '//'
+        'end': '$'
+        'name': 'comment.block.d'
+      }
+      {
+        # Embedded documentation style
+        'begin': '/\\+\\+'
+        'end': '\\+/'
+        'name': 'comment.block.embedded.d'
+      }
+    ]
+  'constants-and-special-vars':
+    'patterns': [
+      {
+        # Special constants
+        'match': '\\b(true|false|null)\\b'
+        'name': 'constant.language.d'
+      }
+      {
+        # Special variables
+        'match': '\\b(this|super)\\b'
+        'name': 'variable.language.d'
+      }
+      {
+        'include': '#numerics'
+      }
+
+    ]
+  'enums':
+    'patterns': [
+
+    ]
+  'functions':
+    # Base functions
+    'patterns': [
+      {
+        "begin": "\\b(function\\*?)"
+
+      }
+    ]
+  'keywords':
+    'patterns': [
+      {
+        # Control keywords
+        'match': '\\b(case|continue|default|do|else|for|foreach|goto|if|return|switch|while)\\b'
+        'name': 'keyword.control.d'
+      }
+      {
+        # ternary keywords
+        'match': '\\?|:'
+        'name': 'keyword.control.ternary.d'
+      }
+      {
+        # Type operators
+        'match': '(\\b(typeid|typeof|is|!is)\\b)'
+        'name': 'keyword.operator.d'
+      }
+      {
+        # Bitwise operators & comparison
+        'match': '(<<|>>>?|~|\\^|\\||&)'
+        'name': 'keyword.operator.bitwise.d'
+      }
+      {
+        # Bitwise assignment
+        'match': '((&|\\^|\\||<<|>>>?)=)'
+        'name': 'keyword.operator.assignment.bitwise.d'
+      }
+      {
+        # Comparison operators
+        'match': '(==|!=|<=|>=|<|>)'
+        'name': 'keyword.operator.comparison.d'
+      }
+      {
+        # Slice and array-specific operators
+        'match': '(\\bin\\b)'
+        'name': 'keyword.operator.array.d'
+      }
+      {
+        # Arithmetic & concatenation
+        'match': '(\\-|\\+|\\*|\\/|%|\\^\\^|~)'
+        'name': 'keyword.operator.arithmetic.d'
+      }
+      {
+        # Assignment
+        'match': '([+*/%-~]?=)'
+        'name': 'keyword.operator.assignment.d'
+      }
+      {
+        # logical operators
+        'match': '(!|&&|\\|\\|)'
+        'name': 'keyword.operator.logical.d'
+      }
+      {
+        # dereferencing, method refers, etc..
+        'match': ''
+        'name': 'keyword.operator.dereference.d'
+      }
+      {
+        # statement terminator (semicolon)
+        'match': ';'
+        'name': 'punctuation.terminator.d'
+      }
+      {
+        # alias
+        'match': '(alias)\\s+(.+)\\s+(.+);'
+        'captures':
+          '1':
+            'name': 'keyword.other.alias.d'
+          '2':
+            'name': 'variable.parameter.alias.d'
+          '3':
+            'name': 'variable.parameter.alias.d'
+      }
+
+    ]
+  'method-call':
+    'patterns': [
+
+    ]
+  'methods':
+    'patterns': [
+      {
+        'match': '([a-zA-Z_][a-zA-Z0-9_]*)(\\.[a-zA-Z_][a-zA-Z0-9]+)+'
+        'captures':
+          '1':
+            'name': 'entity.name.class.called.d'
+          '2':
+            'name': 'entity.name.function.method.d'
+      }
+
+    ]
+  'numerics':
+    # Match all numerics D is capable of (a lot!)
+    'patterns': [
+      {
+        # Handle the weirdness that is hexadecimal floats
+        'match': '0[xX][0-9a-fA-F_]*(\\.[0-9a-fA-F_]*)?([pP]|([pP][\+-]))[0-9][0-9_]*[LfF]?'
+        'name': 'constant.numeric.float.hexidecimal.d'
+      }
+      {
+        'match': '(((0[1-9_])|[1-9])?[0-9_]*\\.[0-9_]*([eE][\+-]?)?[0-9][0-9_]*[lfF]?)'
+        'name': 'constant.numeric.float.d'
+      }
+      {
+        # Hex, binary and octal integers
+        'match': '0(((x|X)[0-9a-fA-F_]+)|([0-7_]+)|(b|B)[01_]+)([lL][uU]|[uU][lL]|[uUlL])?'
+        'name': 'constant.numeric.integer.other.d'
+      }
+      {
+        # base-10 integers
+        'match': '[1-9][0-9_]*([lL][uU]|[uU][lL]|[uUlL])?'
+        'name': 'constant.numeric.integer.decimal.d'
+      }
+    ]
+  'object-types':
+    'patterns': [
+
+
+    ]
+  'object-types-inherited':
+    'patterns': [
+
+    ]
+  'package':
+    # Module-level imports, module decls and package decls
+    'patterns': [
+      {
+        # import packages
+        'match': '\\b(import|module)\\s+(.+)\\b'
+        'captures':
+          '1':
+            'name': 'keyword.other.import.d'
+          '2':
+            'name': 'support.class.import.d'
+      }
+    ]
+  'parameters':
+    # tag generic function and methos parameters
+    'patterns': [
+      {
+        'begin': '(?![\\s,)])'
+        'end': '(?=[,)])'
         'patterns': [
-
+          {
+            'match': '\\G[_a-zA-Z][_a-zA-Z0-9]*',
+            'name': 'variable.parameter.function.d'
+          },
+          {
+            'include': '$self'
+          }
         ]
-    'annotations':
+      }
+
+    ]
+  'parens':
+    # meta-pattern
+    'patterns': [
+      'begin': '\\('
+      'end': '\\)'
+      'patterns': [
+        {
+          'include': '#code'
+        }
+      ]
+
+    ]
+  'primitive-types':
+    'patterns': [
+      {
+      'match': '\\b((void|bool|byte|ubyte|short|ushort|int|uint|long|ulong|float|double|real|char|wchar|dchar|size_t|string|wstring|dstring|T|T1|T2|T3|V|R|E|S|U)(\\*{0,3})((\\[\\])*)(\\()?)\\s'
+      'captures':
+        '1':
+          'name': 'storage.type.primitive.array.d'
+      }
+    ]
+  'storage-modifiers':
+    'patterns': [
+      {
+        'match': '\\b(?:align|auto|const|export|extern|in|inout|out|pure|ref|static)\\b'
+        'name': 'storage.modifier.data.d'
+      }
+    ]
+  'strings':
+    'patterns': [
+      {
+        'begin': '"'
+        'beginCaptures':
+          '0':
+            'name': 'punctuation.definition.string.begin.d'
+        'end': '"'
+        'endCaptures':
+          '0':
+            'name': 'punctuation.definition.string.end.d'
+        'name': 'string.quoted.double.d'
+        # Deal with escape characters
         'patterns': [
-
+          {
+            'match': '\\\\.'
+            'name': 'constant.character.escape.d'
+          }
         ]
-    'attributes':
-        'patterns': [
-            {
-                'match': '(@(safe|system|trusted|property))\\b'
-                'name': 'keyword.other.safety.d'
-            }
-        ]
-    'anonymous-classes-and-new':
-        'patterns': [
-
-        ]
-    'assertions':
-        # assert, enforce, exceptions and other contract structures
-        'patterns': [
-            {
-                # Exception keywords
-                'match': '\\b(try|catch|finally|throw|nothrow|in|body)\\b'
-                'name': 'keyword.other.exception.d'
-            }
-            {
-                # assertions
-                'match': '\\b(assert|enforce|invariant)'
-                'name': 'entity.name.function.assertion.d'
-            }
-        ]
-    'block':
-        # meta-pattern - any legal code that can exist inside { }
-        'begin': '{'
-        'end': '}'
-        'patterns': [
-            {
-                'include': '#code'
-            }
-        ]
-    'block-markup':
-        'patterns': [
-            {
-                # single-line asm
-                'match': '(asm)\\s+([^\\{\\}$]+)'
-                'captures':
-                    '1':
-                        'name': 'keyword.other.asm.d'
-                    '2':
-                        'name': 'gfm.markup.raw.asm.d'
-            }
-            {
-                # block-level asm
-                'begin': '(asm)(?:\\s+{)'
-                'beginCaptures':
-                    '1':
-                        'name': 'keyword.other.asm.d'
-                'end': '}'
-                'contentName': 'gfm.markup.raw.asm.d'
-            }
-        ]
-    'class':
-        'patterns': [
-
-        ]
-    'class-body':
-        'patterns': [
-
-        ]
-    'code':
-        # Meta class - basically all legal code that can be in a block
-        'patterns': [
-            {
-                'include': '#functions'
-            }
-            {
-                'include': '#assertions'
-            }
-            {
-                'include': '#strings'
-            }
-            {
-                'include': '#comments'
-            }
-            {
-                'include': '#primitive-types'
-            }
-            {
-                'include': '#constants-and-special-vars'
-            }
-            {
-                'include': '#attributes'
-            }
-            {
-                'include': '#keywords'
-            }
-            {
-                'include': '#storage-modifiers'
-            }
-            {
-                'include': '#block-markup'
-            }
-            {
-                'include': '#variables'
-            }
-            {
-                'include': '#methods'
-            }
-
-        ]
-    'comments':
-        'patterns': [
-            {
-                'captures':
-                    '0':
-                        'name': 'punctuation.definition.comment.d'
-                'match': '/\\*\\*/'
-                'name': 'comment.block.empty.d'
-            }
-            {
-                'include': '#comments-inline'
-            }
-        ]
-    'comments-inline':
-        'patterns': [
-            {
-                # C-style comments
-                'begin': '/\\*'
-                'end': '\\*/'
-                'name': 'comment.block.d'
-            }
-            {
-                # C++-style comments
-                'begin': '//'
-                'end': '$'
-                'name': 'comment.block.d'
-            }
-            {
-                # Embedded documentation style
-                'begin': '/\\+\\+'
-                'end': '\\+/'
-                'name': 'comment.block.embedded.d'
-            }
-        ]
-    'constants-and-special-vars':
-        'patterns': [
-            {
-                # Special constants
-                'match': '\\b(true|false|null)\\b'
-                'name': 'constant.language.d'
-            }
-            {
-                # Special variables
-                'match': '\\b(this|super)\\b'
-                'name': 'variable.language.d'
-            }
-            {
-                'include': '#numerics'
-            }
-
-        ]
-    'enums':
-        'patterns': [
-
-        ]
-    'functions':
-        # Base functions
-        'patterns': [
-            {
-                'match': '\\s+([a-zA-Z_][a-zA-Z0-9_]+)\\s*\\('
-                'captures':
-                    '1':
-                        'name': 'entity.name.function.d'
-            }
-        ]
-    'keywords':
-        'patterns': [
-            {
-                # Control keywords
-                'match': '\\b(case|continue|default|do|else|for|foreach|goto|if|return|switch|while)\\b'
-                'name': 'keyword.control.d'
-            }
-            {
-                # ternary keywords
-                'match': '\\?|:'
-                'name': 'keyword.control.ternary.d'
-            }
-            {
-                # Type operators
-                'match': '(\\b(typeid|typeof|is|!is)\\b)'
-                'name': 'keyword.operator.d'
-            }
-            {
-                # Bitwise operators & comparison
-                'match': '(<<|>>>?|~|\\^|\\||&)'
-                'name': 'keyword.operator.bitwise.d'
-            }
-            {
-                # Bitwise assignment
-                'match': '((&|\\^|\\||<<|>>>?)=)'
-                'name': 'keyword.operator.assignment.bitwise.d'
-            }
-            {
-                # Comparison operators
-                'match': '(==|!=|<=|>=|<|>)'
-                'name': 'keyword.operator.comparison.d'
-            }
-            {
-                # Slice and array-specific operators
-                'match': '(\\bin\\b)'
-                'name': 'keyword.operator.array.d'
-            }
-            {
-                # Arithmetic & concatenation
-                'match': '(\\-|\\+|\\*|\\/|%|\\^\\^|~)'
-                'name': 'keyword.operator.arithmetic.d'
-            }
-            {
-                # Assignment
-                'match': '([+*/%-~]?=)'
-                'name': 'keyword.operator.assignment.d'
-            }
-            {
-                # logical operators
-                'match': '(!|&&|\\|\\|)'
-                'name': 'keyword.operator.logical.d'
-            }
-            {
-                # dereferencing, method refers, etc..
-                'match': ''
-                'name': 'keyword.operator.dereference.d'
-            }
-            {
-                # statement terminator (semicolon)
-                'match': ';'
-                'name': 'punctuation.terminator.d'
-            }
-            {
-                # alias
-                'match': '(alias)\\s+(.+)\\s+(.+);'
-                'captures':
-                    '1':
-                        'name': 'keyword.other.alias.d'
-                    '2':
-                        'name': 'variable.parameter.alias.d'
-                    '3':
-                        'name': 'variable.parameter.alias.d'
-            }
-
-        ]
-    'method-call':
-        'patterns': [
-
-        ]
-    'methods':
-        'patterns': [
-            {
-                'match': '([a-zA-Z_][a-zA-Z0-9_]*)(\\.[a-zA-Z_][a-zA-Z0-9]+)+'
-                'captures':
-                    '1':
-                        'name': 'entity.name.class.called.d'
-                    '2':
-                        'name': 'entity.name.function.method.d'
-            }
-
-        ]
-    'numerics':
-        # Match all numerics D is capable of (a lot!)
-        'patterns': [
-            {
-                # Handle the weirdness that is hexadecimal floats
-                'match': '0[xX][0-9a-fA-F_]*(\\.[0-9a-fA-F_]*)?([pP]|([pP][\+-]))[0-9][0-9_]*[LfF]?'
-                'name': 'constant.numeric.float.hexidecimal.d'
-            }
-            {
-                'match': '(((0[1-9_])|[1-9])?[0-9_]*\\.[0-9_]*([eE][\+-]?)?[0-9][0-9_]*[lfF]?)'
-                'name': 'constant.numeric.float.d'
-            }
-            {
-                # Hex, binary and octal integers
-                'match': '0(((x|X)[0-9a-fA-F_]+)|([0-7_]+)|(b|B)[01_]+)([lL][uU]|[uU][lL]|[uUlL])?'
-                'name': 'constant.numeric.integer.other.d'
-            }
-            {
-                # base-10 integers
-                'match': '[1-9][0-9_]*([lL][uU]|[uU][lL]|[uUlL])?'
-                'name': 'constant.numeric.integer.decimal.d'
-            }
-        ]
-    'object-types':
-        'patterns': [
-
-
-        ]
-    'object-types-inherited':
-        'patterns': [
-
-        ]
-    'package':
-        # Module-level imports, module decls and package decls
-        'patterns': [
-            {
-                # import packages
-                'match': '\\b(import|module)\\s+(.+)\\b'
-                'captures':
-                    '1':
-                        'name': 'keyword.other.import.d'
-                    '2':
-                        'name': 'support.class.import.d'
-            }
-        ]
-    'parameters':
-        # tag generic function and methos parameters
-        'patterns': [
-            # inout, in and out are parameter specific, handle them only here
-            {
-                'include': '#code'
-                'name': 'variable.parameter.d'
-            }
-
-        ]
-    'parens':
-        # meta-pattern
-        'patterns': [
-            'begin': '\\('
-            'end': '\\)'
-            'patterns': [
-                {
-                    'include': '#code'
-                }
-            ]
-
-        ]
-    'primitive-types':
-        'patterns': [
-            {
-            'match': '\\b((void|bool|byte|ubyte|short|ushort|int|uint|long|ulong|float|double|real|char|wchar|dchar|size_t|string|wstring|dstring|T|T1|T2|T3|V|R|E|S|U)(\\*{0,3})((\\[\\])*)(\\()?)\\s'
-            'captures':
-                '1':
-                    'name': 'storage.type.primitive.array.d'
-            }
-        ]
-    'storage-modifiers':
-        'patterns': [
-            {
-                'match': '\\b(?:align|auto|const|export|extern|in|inout|out|pure|ref|static)\\b'
-                'name': 'storage.modifier.data.d'
-            }
-        ]
-    'strings':
-        'patterns': [
-            {
-                'begin': '"'
-                'beginCaptures':
-                    '0':
-                        'name': 'punctuation.definition.string.begin.d'
-                'end': '"'
-                'endCaptures':
-                    '0':
-                        'name': 'punctuation.definition.string.end.d'
-                'name': 'string.quoted.double.d'
-                # Deal with escape characters
-                'patterns': [
-                    {
-                        'match': '\\\\.'
-                        'name': 'constant.character.escape.d'
-                    }
-                ]
-            }
-        ]
-    'values':
-        'patterns': [
-
-        ]
-    'variables':
-        'patterns': [
-            {
-                'match': '\\b([a-zA-Z_][A-Za-z0-9_]+)\\b'
-                'name': 'support.variable.d'
-            }
-        ]
+      }
+    ]
+  'variables':
+    'patterns': [
+      {
+        'match': '\\b([a-zA-Z_][A-Za-z0-9_]+)\\b'
+        'name': 'support.variable.d'
+      }
+    ]
 # Repository end
 #
 # End of d.cson


added deactivate old projects endpoint

added deactivate old projects endpoint

--- services/web/app/coffee/router.coffee
+++ services/web/app/coffee/router.coffee
@@ -35,6 +35,7 @@
 Modules = require "./infrastructure/Modules"
 RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
 RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
+InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")

 logger = require("logger-sharelatex")
 _ = require("underscore")
@@ -135,6 +136,8 @@
 		apiRouter.get  '/internal/project/:Project_id/zip', AuthenticationController.httpAuth, ProjectDownloadsController.downloadProject
 		apiRouter.get  '/internal/project/:project_id/compile/pdf', AuthenticationController.httpAuth, CompileController.compileAndDownloadPdf

+		apiRouter.get '/internal/deactivateOldProjects', AuthenticationController.httpAuth, InactiveProjectController.deactivateOldProjects
+
 		webRouter.get  /^\/internal\/project\/([^\/]*)\/output\/(.*)$/,
 			((req, res, next) ->
 				params =


Remove gulpfile

Remove gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,60 +0,0 @@
-gulp = require('gulp')
-gutil = require('gulp-util')
-coffee = require('gulp-coffee')
-concat = require('gulp-concat')
-header = require('gulp-header')
-uglify = require('gulp-uglify')
-rename = require('gulp-rename')
-karma = require('gulp-karma')
-
-p = require('./package.json')
-
-projectHeader = "/*! #{p.name} (v#{p.version}),\n
-                Simple library for creating complicated architectures,\n
-                by #{p.author}\n
-                #{new Date().toDateString()} */\n"
-
-sourceFiles = [
-  'src/configuration.coffee'
-  'src/vtree.coffee'
-  'src/vtree_src/hooks.coffee'
-  'src/vtree_src/vtree_nodes_cache.coffee'
-  'src/vtree_src/node.coffee'
-  'src/vtree_src/node_data.coffee'
-  'src/vtree_src/node_wrapper.coffee'
-  'src/vtree_src/tree_manager.coffee'
-  'src/vtree_src/launcher.coffee'
-  'src/vtree_src/dom.coffee'
-]
-
-gulp.task 'build', ['karma:ci'], ->
-  gulp.src(sourceFiles)
-    .pipe(coffee(bare: false).on('error', gutil.log))
-    .pipe(concat('vtree.js'))
-    .pipe(header(projectHeader))
-    .pipe(gulp.dest('build/'))
-
-gulp.task 'minify', ['build'], ->
-  gulp.src('build/vtree.js')
-    .pipe(uglify(outSourceMap: false))
-    .pipe(rename(suffix: '.min'))
-    .pipe(header(projectHeader))
-    .pipe(gulp.dest('build/'))
-
-gulp.task 'karma:ci', ->
-  gulp.src('')
-    .pipe(karma(
-      configFile: 'karma.conf.coffee'
-      sourceFiles: sourceFiles
-    ))
-
-gulp.task 'karma:dev', ->
-  gulp.src('')
-    .pipe(karma(
-      configFile: 'karma.conf.coffee'
-      reporters: ['dots']
-      action: 'watch'
-      sourceFiles: sourceFiles
-    ))
-
-gulp.task 'release', ['karma:ci', 'build', 'minify']


fix a bug from stylus task

fix a bug from stylus task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -34,7 +34,7 @@
     .pipe(gulp.dest("#{BUILD_CLIENT}/js"))

 gulp.task 'client:stylus',  () ->
-  gulp.src('./client/stylus/**/*.styl')
+  gulp.src('./client/stylus/main.styl')
     .pipe(stylus())
     .pipe(concat('style.css'))
     .pipe(gulp.dest("#{BUILD_CLIENT}/css"))


Always show fpm output to make debugging easier.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -413,7 +413,7 @@
             child = spawn 'fpm', fpmArgs
             child.stdout.on 'data', (data) ->
               # do nothing
-              console.log("fpm: #{data}") if target == 'rpm'
+              console.log("fpm: #{data}")
               done()
               return true
             # log all errors


make gulp beep on compile errors

make gulp beep on compile errors

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -35,15 +35,17 @@
     return
   )

+handleError = (err) ->
+  util.log(util.colors.red("!!"), err.toString())
+  util.beep()
+

 makeScripts = (dest) ->
   return eventStream.pipeline(
     plumber()
     gif(
       (file) -> return path.extname(file.path) == ".coffee"
-      coffee({}).on("error",
-        (err) -> util.log(util.colors.red("!!"), err.toString())
-      )
+      coffee({}).on("error", handleError)
     )
     gulp.dest(dest)
     logger()
@@ -54,9 +56,7 @@
   return gulp.src(paths.src.css)
     .pipe(plumber())
     .pipe(
-      less({}).on("error",
-        (err) -> util.log(util.colors.red("!!"), err.toString())
-      )
+      less({}).on("error", handleError)
     )
     .pipe(gulp.dest(dest))
     .pipe(logger())


watchタスク再整理

watchタスク再整理

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -38,12 +38,8 @@
     callback
   )

-gulp.task 'watch', ['develop'], (callback) ->
-  runSequence(
-    'develop',
-    gulp.watch('./src/**', ['develop']),
-    callback
-  )
+gulp.task 'watch', (callback) ->
+  gulp.watch('./src/**', ['develop'])

 gulp.task 'build', (callback) ->


Updated test-with-coverage task to use shell.task shorthand

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -29,8 +29,8 @@
   shell.task [specReporter, htmlCovReporter])

 # Run the mocha test suite and receive output as expected
-gulp.task 'test-with-coverage', ['turn-on-coverage', 'coffee-coverage'], ->
-  gulp.run(shell.task [specReporter, htmlCovReporter])
+gulp.task('test-with-coverage', ['turn-on-coverage', 'coffee-coverage'],
+  shell.task [specReporter, htmlCovReporter])

 coverageCmd = [
   'coffeeCoverage'


own test runs via mocha-phantomjs

own test runs via mocha-phantomjs

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -6,6 +6,7 @@
 coffeelint = require "gulp-coffeelint"
 exec = require("child_process").exec
 mocha = require "gulp-mocha"
+mocha_phantomjs = require "gulp-mocha-phantomjs"
 qunit = require "gulp-qunit"
 sourcemaps = require "gulp-sourcemaps"

@@ -49,10 +50,12 @@
         console.log stdout
         console.log stderr
         cb err
-
+
 gulp.task "test-own", ["compile"], ->
-
-
+    gulp.src "test/index.html"
+    .pipe mocha_phantomjs()
+
+
 # ***********
 #    BUILD
 # ***********


コメント記載ミスを修正

コメント記載ミスを修正

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -70,7 +70,7 @@
       .pipe($.if(production, $.uglify()))
       .pipe(gulp.dest(paths.dist.js))

-# compile Webpack [ Babel / Vue -> SPA(main.js) ]
+# compile Webpack [ Babel -> SPA(bundler.js) ]
 gulp.task "build:webpack", ->
   process.env.NODE_ENV = if production is true then "production" else "development"
   plugins =


Removed lint task from build

Removed lint task from build

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -185,7 +185,7 @@
 gulp.task 'copy', ->
   gulp.src("#{src_path}/public/fonts/*").pipe(gulp.dest(dist_path + '/fonts'))

-gulp.task 'build', ['clean', 'copy', 'css', 'img', 'js', 'cssVendor', 'jsVendor', 'lint', 'index']
+gulp.task 'build', ['clean', 'copy', 'css', 'img', 'js', 'cssVendor', 'jsVendor', 'index']

 server_main = "./server.coffee"
 gulp.task 'server', ->
@@ -195,7 +195,7 @@
     env:
       PORT: process.env.PORT or 3000

-gulp.task 'default', ['clean', 'copy', 'css', 'img', 'js', 'cssVendor', 'jsVendor', 'lint', 'index', 'server', 'watch']
+gulp.task 'default', ['clean', 'copy', 'css', 'img', 'js', 'cssVendor', 'jsVendor', 'index', 'server', 'watch']

 gulp.task 'watch', ->
   livereload.listen()


Update gulpfile.coffee

Update gulpfile.coffee

Avoid vendor script minification

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -65,7 +65,7 @@
 gulp.task 'scripts', ->
   gulp.src mainBowerFiles()
       .pipe filter('*.js')
-      .pipe uglify()
+      #.pipe uglify()
       .pipe concat('vendor.min.js')
       .pipe gulp.dest destinations.scripts

@@ -113,4 +113,4 @@
 ##
 ## Create default task
 ##
-gulp.task 'default', ['build']+gulp.task 'default', ['build']


add build task before watch task

add build task before watch task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -145,7 +145,7 @@
   ]
   null

-gulp.task 'watch', ->
+gulp.task 'watch', ['build'], ->
   gulp.watch [
     paths.appFiles
     paths.testFiles


Removed debug output

Removed debug output

--- source/javascripts/gh-pages.js.coffee
+++ source/javascripts/gh-pages.js.coffee
@@ -5,7 +5,6 @@

   commit = ->
     preview.srcdoc = editor.value
-    console.debug editor.value

   commit_button.onclick = -> commit()


a little more coffee anyone? here, let's make it more syntactically coffee

--- bootstrap-magic.coffee
+++ bootstrap-magic.coffee
@@ -76,14 +76,15 @@

   return obj

+searchTerms = new ReactiveVar("")
+
 getCurrentCategory = ->
   myCat = BootstrapMagic.dictionary.currentCategory.get()
   return _.where(bootstrap_magic_variables, { category: myCat })

 getCurrentVariables = ->
-  if noSearchWords
+  if noSearchInput
     subCatId = BootstrapMagic.dictionary.currentSubCategory.get()
-    console.log "original: ",  _.find bootstrap_magic_variables, (group) -> group._id is subCatId
     return _.find bootstrap_magic_variables, (group) -> group._id is subCatId
   else
     letsSearch()
@@ -94,11 +95,9 @@
   searchResults = _.filter items, (obj) => obj._id?.indexOf(words) >- 1
   return searchResults

-noSearchWords = ->
-  if !searchTerms.get() || searchTerms.get().length < 3
-    return true
-  else
-    return false
+noSearchInput = ->
+  mySearchTerms = searchTerms.get()
+  if !mySearchTerms or mySearchTerms.length < 3 then true else false

 Template._bootstrap_magic.onCreated ->
   BootstrapMagic.start() if BootstrapMagic.start
@@ -108,29 +107,19 @@
   BootstrapMagic.dictionary.currentSubCategory.set bootstrap_magic_variables[0]._id

 camelToSnake = (str) -> str.replace(/\W+/g, '_').replace(/([a-z\d])([A-Z])/g, '$1-$2')
-searchTerms = new ReactiveVar("")

 Template._bootstrap_magic.helpers
   "categories" : ->  _.map _.groupBy(bootstrap_magic_variables, 'category'), (obj) ->  obj[0]
   "subCategories" : getCurrentCategory
   "currentVars" : getCurrentVariables
-  "mappedVariables" : ->
-    if noSearchWords()
-      _.map @data, mapVariableOverrides
-    else
-      letsSearch()
-
+  "mappedVariables" : -> if noSearchInput() then _.map @data, mapVariableOverrides else letsSearch()
   "isSelectedCat" : -> @category is BootstrapMagic.dictionary.currentCategory.get()
   "isSelectedSubCat" : ->  @_id is BootstrapMagic.dictionary.currentSubCategory.get()
   "previewTmpl" : -> Template["bootstrap_magic_preview_#{camelToSnake @_id}"] || null
   "inputTmpl" : -> Template["bootstrap_magic_input_#{@type}"] || null
   "typeIs" : (type) -> @type is type
-  "searchInactive" : -> noSearchWords()
-  "searchHeader" :->
-    if letsSearch().length == 0
-      return "No Search Results"
-    else
-      return "Search Results: "
+  "searchInactive" : -> noSearchInput()
+  "searchHeader" :->  if letsSearch().length is 0 then "No Search Results" else "Search Results: "

 Template._bootstrap_magic.events
   'keyup .magic-search' : (e) ->


GameObject.Effect

GameObject.Effect

--- src/game_object_effect.coffee
+++ src/game_object_effect.coffee
@@ -23,7 +23,7 @@

   @name fadeOut
   @methodOf GameObject#
-  @param {Number} [duration=30] How long the effect lasts
+  @param {Number} [duration=30] How m
   @param {Function} [callback=null] The function to execute when the sprite has finished fading.
   ###
   fadeOut: (duration, callback) ->


GameObject.Effect

GameObject.Effect

--- src/game_object_effect.coffee
+++ src/game_object_effect.coffee
@@ -1,5 +1,5 @@
 ###*
-A collection of effects to make you game jui
+A collection of effects to make you game juicy.

 @name Effect
 @fieldOf GameObject


extract duplicate, version, status, title from node

--- BackofficeBundle/Resources/public/coffee/OrchestraView.coffee
+++ BackofficeBundle/Resources/public/coffee/OrchestraView.coffee
@@ -144,4 +144,4 @@
         Backbone.history.loadUrl(redirectRoute)
     return

-)+)


console.debug -> console.log

console.debug -> console.log

--- src/lib/models-generator.coffee
+++ src/lib/models-generator.coffee
@@ -113,8 +113,8 @@
                 name = klass.getName()
                 entityModels.push domain.getModel name
             catch e
-                console.debug e
-                console.debug e.stack
+                console.log e
+                console.log e.stack

         return entityModels


If oldSubmit source was empty, try re-download it


add new keyboard tests

add new keyboard tests

--- test/unit/modules/keyboard.coffee
+++ test/unit/modules/keyboard.coffee
@@ -29,49 +29,40 @@
     )
   )

-  # describe('listeners', ->
-  #   it('prevent bubbling', ->
+  describe('hotkeys', ->
+    beforeEach( ->
+      @container.innerHTML = '

0123

'
+      @quill = new Quill(@container.firstChild)
+      @keyboard = @quill.getModule('keyboard')
+    )

-  #   )
+    it('trigger', (done) ->
+      hotkey = { key: 'B', metaKey: true }
+      @keyboard.addHotkey(hotkey, (range) ->
+        expect(range.start).toEqual(1)
+        expect(range.end).toEqual(2)
+        done()
+      )
+      @quill.setSelection(1, 2)
+      Quill.DOM.triggerEvent(@quill.editor.root, 'keydown', hotkey)
+    )

-  #   it('allow bubbling', ->
+    it('format', ->
+      @quill.setSelection(0, 4)
+      Quill.DOM.triggerEvent(@quill.editor.root, 'keydown', Quill.Module.Keyboard.hotkeys.BOLD)
+      expect(@quill.editor.root).toEqualHTML('
0123
', true)
+    )

-  #   )
+    it('tab', ->
+      @quill.setSelection(1, 3)
+      Quill.DOM.triggerEvent(@quill.editor.root, 'keydown', Quill.Module.Keyboard.hotkeys.INDENT)
+      expect(@quill.editor.root).toEqualHTML('
0\t3
', true)
+    )

-  #   it('shift', ->
-
-  #   )
-
-  #   it('meta', ->
-
-  #   )
-  # )
-
-  # describe('hotkeys', ->
-  #   beforeEach( ->
-  #     @container.innerHTML = '

0123

'
-  #     @quill = new Quill(@container.firstChild)
-  #     @keyboard = @quill.getModule('keyboard')
-  #   )
-
-  #   it('format', ->
-
-  #   )
-
-  #   it('tab', ->
-
-  #   )
-
-  #   it('shift + tab', ->
-
-  #   )
-
-  #   it('prevent backspace', ->
-
-  #   )
-
-  #   it('prevent delete', ->
-
-  #   )
-  # )
+    it('shift + tab', ->
+      @quill.setSelection(0, 2)
+      Quill.DOM.triggerEvent(@quill.editor.root, 'keydown', Quill.Module.Keyboard.hotkeys.OUTDENT)
+      expect(@quill.editor.root).toEqualHTML('
0123
', true)
+    )
+  )
 )


Per-chart clip path id's

Per-chart clip path id's

--- app/lib/chart.coffee
+++ app/lib/chart.coffee
@@ -25,13 +25,12 @@
       .append('g')
         .attr('transform', "translate(#{ @margin.left }, #{ @margin.top })")

-    if @opts.name is 'primary'
-      @svg.append('defs').append('clipPath')
-          .attr('id', 'clip')
-        .append('rect')
-          .attr('transform', 'translate(0, 0)')
-          .attr('width', @width)
-          .attr('height', @height)
+    @svg.append('defs').append('clipPath')
+        .attr('id', "clip-#{ @opts.name }")
+      .append('rect')
+        .attr('transform', 'translate(0, 0)')
+        .attr('width', @width)
+        .attr('height', @height)

     if @opts.title
       @svg.append('text')
@@ -53,7 +52,7 @@
         .attr('class', 'chart-region')
         .attr('width', @width)
         .attr('height', @height)
-        .attr('clip-path', 'url(#clip)')
+        .attr('clip-path', "url(#clip-#{ @opts.name })")
       .append('path')
         .attr('class', 'fit-line')
         .attr('stroke-width', 3.0)


Green rings be gone

Green rings be gone

Closes #39

--- app/lib/en-us.coffee
+++ app/lib/en-us.coffee
@@ -379,7 +379,7 @@
       answer: '''These images are from places all along the plane of the Milky Way. At night in darker areas you can see the Milky Way as a bright stripe across the sky. The region covered by these images is about as wide as a finger's length held at arm's length away from you, and stretches all the way around the earth following the brightest part of the Milky Way's stripe.'''
       question: '''What part of the night sky are we seeing?'''
     q03:
-      answer: '''That's part of what we're trying to find out with this project! Right now our best understanding is that these are regions around young massive stars that are so bright that their light has caused a shock wave to affect the cloud around them and blown a bubble which we can see in infrared light. The red area is where the shock has already passed by and the bright green ring around it is where the shock is currently impacting the gas cloud.'''
+      answer: '''That's part of what we're trying to find out with this project! Right now our best understanding is that these are regions around young massive stars that are so bright that their light has caused a shock wave to affect the cloud around them and blown a bubble which we can see in infrared light. The dark interior is where the shock has already passed by and the bright red/pink ring around it is where the shock is currently impacting the gas cloud.'''
       question: '''What are these bubbles anyway?'''
     q04:
       answer: '''Most of the circular features (bubbles) in these images are produced by hot young stars, as winds and radiation from these young stars sweep up the surrounding gas and dust from which they formed (like a snowplow that compresses the snow in its path.) Sometimes, the swept-up material becomes dense enough for gravity to pull it together to form new stars.'''


Update he.coffee (#4099)

Update he.coffee (#4099)

init work. minor fixings

--- app/locale/he.coffee
+++ app/locale/he.coffee
@@ -9,19 +9,19 @@
     im_a_teacher: "אני מורה"
     im_a_student: "אני תלמיד"
     learn_more: "למד עוד"
-    classroom_in_a_box: "כיתה בפיתה ללימוד מדעי המחשב."
-    codecombat_is: "CodeCombat  היא פלטפורמה עבור תלמידים ללימוד תכנות באמצעות משחק."
+    classroom_in_a_box: "כיתה ללימוד מדעי המחשב."
+    codecombat_is: "קודקומבאט היא פלטפורמה עבור תלמידים ללימוד תכנות באמצעות משחק."
 #    our_courses: "Our courses have been specifically playtested to excel in the classroom, even by teachers with little to no prior programming experience."
 #    top_screenshots_hint: "Students write code and see their changes update in real-time"
 #    designed_with: "Designed with teachers in mind"
 #    real_code: "Real, typed code"
-#    from_the_first_level: "from the first level"
+    from_the_first_level: "החל משלב ראשון"
 #    getting_students: "Getting students to typed code as quickly as possible is critical to learning programming syntax and proper structure."
 #    educator_resources: "Educator resources"
 #    course_guides: "and course guides"
 #    teaching_computer_science: "Teaching computer science does not require a costly degree, because we provide tools to support educators of all backgrounds."
-#    accessible_to: "Accessible to"
-#    everyone: "everyone"
+    accessible_to: "זמין"
+    everyone: "לכולם"
 #    democratizing: "Democratizing the process of learning coding is at the core of our philosophy. Everyone should be able to learn to code."
 #    forgot_learning: "I think they actually forgot that they were actually learning something."
 #    wanted_to_do: " Coding is something I've always wanted to do, and I never thought I would be able to learn it in school."
@@ -109,7 +109,7 @@
     page_not_found: "העמוד לא נמצא"

   diplomat_suggestion:
-    title: "עזור לתרגם את CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
+    title: "עזור לתרגם את קודקומבאט!" # This shows up when a player switches to a non-English language using the language selector.
     sub_heading: "אנחנו צריכים את כישורי השפה שלך!"
     pitch_body: "פיתחנו את המשחק באנגלית, אבל יש הרבה שחקנים מכול העולם. חלק מהם רוצים לשחק בעברית והם לא מבינים אנגלית. אם אתה דובר את שני השפות, עברית ואנגלית, אז בבקשה עזור לנו לתרגם לעברית את האתר ואת השלבים."
     missing_translations: "עד שנתרגם הכול לעברית, מה שלא תורגם יופיע באנגלית."
@@ -140,7 +140,7 @@
     subscription_required: "יש צורך במנוי"
     anonymous: "משתמש אנונימי"
     level_difficulty: "רמת קושי: "
-    play_classroom_version: "שיק בגרסה הכיתתית" # Choose a level in campaign version that you also can play in one of your courses
+    play_classroom_version: "שחק בגרסה הכיתתית" # Choose a level in campaign version that you also can play in one of your courses
     campaign_beginner: "מסע המתחילים"
     awaiting_levels_adventurer_prefix: ".אנחנו מוסיפים חמישה שלבים בכל שבוע" # {change}
     awaiting_levels_adventurer: "הירשם כהרפתקן"


Update sk.coffee

Update sk.coffee

Translation new_home finished.

--- app/locale/sk.coffee
+++ app/locale/sk.coffee
@@ -62,25 +62,25 @@
     request_demo_subtitle: "Vyžiadaj si demo a tvoji študenti môžu do hodiny začať."
     get_started_title: "Vytvor si triedu už dnes"
     get_started_subtitle: "Vytvor si triedu, pridaj do nej študentov a monitoruj ich postup pri programovaní."
-#    create_class: "Or create a class and see it for yourself!"
-#    teacher_screenshots_hint: "Students write code and see their changes update in real-time"
-#    request_demo: "Request a Demo"
-#    create_a_class: "Create a Class"
-#    setup_a_class: "Set Up a Class"
-#    have_an_account: "Have an account?"
-#    log_in: "Log In"
-#    logged_in_as: "You are currently logged in as"
-#    view_my_classes: "View my classes"
-#    computer_science: "Computer science courses for all ages"
-#    show_me_lesson_time: "Show me lesson time estimates for:"
-#    curriculum: "Total curriculum hours:"
-#    ffa: "Free for all students"
-#    lesson_time: "Lesson time:"
-#    coming_soon: "Coming soon!"
-#    courses_available_in: "Courses are available in JavaScript, Python, and Java (coming soon!)"
-#    boast: "Boasts riddles that are complex enough to fascinate gamers and coders alike."
-#    winning: "A winning combination of RPG gameplay and programming homework that pulls off making kid-friendly education legitimately enjoyable."
-#    run_class: "Everything you need to run a computer science class in your school today, no CS background required."
+    create_class: "Môžeš si vytvoriť aj skúšobnú triedu."
+    teacher_screenshots_hint: "Študenti programujú a vidia dôsledky svojích krokov v reálnom čase."
+    request_demo: "Vyžiadaj si demo"
+    create_a_class: "Vytvor triedu"
+    setup_a_class: "Nastavenia triedy"
+    have_an_account: "Máš účet?"
+    log_in: "Prihlásenie"
+    logged_in_as: "Si prihlásená ako"
+    view_my_classes: "Prehľa mojích tried"
+    computer_science: "Kurz programovania pre každý vek"
+    show_me_lesson_time: "Ukáž mi predpokladaný čas kurzu pre:"
+    curriculum: "Celkový počet hodín:"
+    ffa: "Zdarma pre všetkých študentov"
+    lesson_time: "Čas lekcie:"
+    coming_soon: "Čoskoro!"
+    courses_available_in: "Kurzy sú dostupné v JavaScripte, Pythone a čoskoro aj v Jave."
+    boast: "Komplexita úloh je dostatočná na fascináciu hráčov aj programátorov."
+    winning: "Strhujúca kombinácia RPG a programovania pre výuku detí."
+    run_class: "Všetko, čo potrebuješ, aby si mohol viesť výuku programovania na tvojej škole bez predchádzajúcich skúseností v tejto oblasti."

   nav:
     play: "Hraj" # The top nav bar entry where players choose which levels to play


introduced ComposeAceEditor, simple Ace based editor for compose


Try harder to find the ip address, and dont split the ipaddress if we didnt find it.

--- server_setup.coffee
+++ server_setup.coffee
@@ -120,8 +120,8 @@
     reqHost ?= req.host

     unless reqHost.toLowerCase() is host
-      ip = req.headers['x-forwarded-for'] or req.connection.remoteAddress
-      ip = ip?.split(/,? /)[0]  # If there are two IP addresses, say because of CloudFlare, we just take the first.
+      ip = req.headers['x-forwarded-for'] or req.ip or req.connection.remoteAddress
+      ip = ip?.split(/,? /)[0] if ip? # If there are two IP addresses, say because of CloudFlare, we just take the first.
       geo = geoip.lookup(ip)
       #if speaksLanguage or geo?.country is countryCode
       #  log.info("Should we redirect to #{serverID} server? speaksLanguage: #{speaksLanguage}, acceptedLanguages: #{req.acceptedLanguages}, ip: #{ip}, geo: #{geo} -- so redirecting? #{geo?.country is 'CN' and speaksLanguage}")


changed file listing to take 30% of the page

--- app/assets/javascripts/commit.js.coffee
+++ app/assets/javascripts/commit.js.coffee
@@ -126,7 +126,7 @@
   height: 500
   duration: 500
   lineheight: 23
-  offset: 500
+  offset: window.innerWidth * .3
   padding: 20


update

update

--- app/assets/javascripts/common.js.coffee
+++ app/assets/javascripts/common.js.coffee
@@ -97,9 +97,9 @@
   $("#sidebar").hide()
   $("#menu").click ->
     if menubool is on
-      $("#sidebar").toggle("drop")
+      $("#sidebar").toggle("drop",200)
     else
-      $("#sidebar").toggle("drop")
+      $("#sidebar").toggle("drop",200)
     menubool = !menubool

 # responsiveWindow init


Focuses home search box on load

Focuses home search box on load

--- app/assets/javascripts/common.js.coffee
+++ app/assets/javascripts/common.js.coffee
@@ -45,4 +45,6 @@
         )
       )
   )
+
+  $('.explore .search-input').focus()
 )


sankey: better margin calculation

sankey: better margin calculation

--- app/assets/javascripts/d3/sankey.coffee
+++ app/assets/javascripts/d3/sankey.coffee
@@ -208,10 +208,8 @@
     horizontal_spacing: =>
       return @__horizontal_spacing if @__horizontal_spacing?
       cols = @view.number_of_columns()
-      @__horizontal_spacing = switch cols
-        when 3 then 170
-        when 4 then 120
-        else @view.width / cols
+      # this should leave enough room for the node labels
+      @__horizontal_spacing = (@view.width - 150) / (cols - 1)
       @__horizontal_spacing

     # vertical position of the top left corner of the node. Adds some margin


Unused javascript - logic in ERB

Unused javascript - logic in ERB

--- app/assets/javascripts/graphs.js.coffee
+++ app/assets/javascripts/graphs.js.coffee
@@ -50,11 +50,6 @@
     entity_y      = parseInt( $(element).attr("data-y"))
     $(element).dialog({ width: entity_width, height: entity_height, position: [entity_x,entity_y] })

-addRelationshipsToDOM = ->
-  for relationship, index in window.relationships.length
-    paths = paths + '<path id="relationship' + index + '" stroke="black" stroke-width="2" fill="none" />'
-  $('#relationships').html(paths)
-
 setupEntityDragHandler = ->
   $('.entity').on 'dialogdrag', (event, ui) ->
     drawRelationshipsFromEntity(event.currentTarget)
@@ -86,7 +81,6 @@

 $(document).ready ->
   makeEntitiesDraggable()
-  addRelationshipsToDOM()
   drawAllRelationships()
   setupEntityDragHandler()
   $("#settings").hide()


default route into appliance_types

default route into appliance_types

--- app/assets/javascripts/router.js.coffee
+++ app/assets/javascripts/router.js.coffee
@@ -10,4 +10,8 @@
 App.ApplianceTypesRoute = Ember.Route.extend(
   model: ->
     App.ApplianceType.find()
+)
+
+App.IndexRoute = Ember.Route.extend(redirect: ->
+  @transitionTo "appliance_types"
 )


pr comments

pr comments

--- app/assets/javascripts/search.js.coffee
+++ app/assets/javascripts/search.js.coffee
@@ -51,8 +51,7 @@
     $previous.addClass('selected')

   goToSelectedStack: ->
-    stack = @$items.filter('.selected').filter(':not(.not-matching)').find('.commits-path').attr('href')
-    if stack
+    if stack = @$items.filter('.selected').filter(':not(.not-matching)').find('.commits-path').attr('href')
       window.location = stack

 search = new StackSearch(document)


Whitespace changes.

Whitespace changes.

--- app/assets/javascripts/signup.js.coffee
+++ app/assets/javascripts/signup.js.coffee
@@ -153,7 +153,7 @@
         district_id.addClass 'hidden'
         state.addClass 'hidden'

-        if country_id.val() == API_V1.USA_ID
+        if country_id.val() == API_V1.USA_ID
           @hideInternational()
           state.getSelectOptions API_V1.STATES + "?country=#{country_id.val()}", (data) ->
             data.unshift undefined # we need an empty option for placeholder
@@ -222,14 +222,14 @@
           @showTeacherForm()

     showStudentForm: ->
-        @el('#common-fieldset').addClass 'hidden'
-        @el('#student-fieldset').removeClass 'hidden'
-        @armSubmitHandler(API_V1.STUDENTS)
+      @el('#common-fieldset').addClass 'hidden'
+      @el('#student-fieldset').removeClass 'hidden'
+      @armSubmitHandler(API_V1.STUDENTS)

     showTeacherForm: ->
-        @el('#common-fieldset').addClass 'hidden'
-        @el('#teacher-fieldset').removeClass 'hidden'
-        @armSubmitHandler(API_V1.TEACHERS)
+      @el('#common-fieldset').addClass 'hidden'
+      @el('#teacher-fieldset').removeClass 'hidden'
+      @armSubmitHandler(API_V1.TEACHERS)

     armSubmitHandler: (url) ->
       @el('.submit-form').on 'click', (e) =>


Make the step decrease occur later in animation

--- app/assets/javascripts/static.js.coffee
+++ app/assets/javascripts/static.js.coffee
@@ -19,11 +19,11 @@

         tween.addEventListener 'change', (event) ->
           # if the number is under 90 of the target, increment number by thousands
-          if $this.counter < $target_count * 0.7
+          if $this.counter < $target_count * 0.8
             $this.counter = roundUpBy($this.counter, 1000)
-          else if $this.counter < $target_count * 0.8
+          else if $this.counter < $target_count * 0.9
             $this.counter = roundUpBy($this.counter, 100)
-          else if $this.counter < $target_count * 0.99
+          else if $this.counter < $target_count * 0.999
             $this.counter = roundUpBy($this.counter, 10)
           $this.text(Math.round($this.counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
           return


removing blank line from stores.js

removing blank line from stores.js

--- app/assets/javascripts/stores.js.coffee
+++ app/assets/javascripts/stores.js.coffee
@@ -36,7 +36,6 @@
     update_h2 = (text) -> ($ '#product_form_wrapper').find('h2').first().html(text)

     reset_h2 = -> update_h2('Add Product')
-

     update_product_count = (product_count) ->
         ($ '#product_count').text("#{product_count} Products")


Other cosmetic fixes.

Other cosmetic fixes.

--- app/coffee/modules/issues/detail.coffee
+++ app/coffee/modules/issues/detail.coffee
@@ -123,10 +123,9 @@

     buildChangesText: (comment) ->
         size = @.countChanges(comment)
-        #TODO: i18n
+        # TODO: i18n
         if size == 1
             return "Made #{size} change"
-
         return "Made #{size} changes"

     block: ->
@@ -136,7 +135,7 @@
         @rootscope.$broadcast("unblock", @scope.issue)

     delete: ->
-        #TODO: i18n
+        # TODO: i18n
         title = "Delete Issue"
         subtitle = @scope.issue.subject

@@ -261,205 +260,65 @@

 module.directive("tgTagLine", ["$log", TagLineDirective])

-#############################################################################
-## Watchers directive
-#############################################################################
-
-WatchersDirective = ($rootscope, $confirm) ->
-    #TODO: i18n
+
+#############################################################################
+## Issue status directive
+#############################################################################
+
+IssueStatusDirective = () ->
+    # TODO: i18n
     template = _.template("""
-

-        watchers
-        <% if (editable) { %>
-
-
+

+
+        <% if (status.is_closed) { %>
+        Closed
+        <% } else { %>
+        Open
         <% } %>
-

-    <% _.each(watchers, function(watcher) { %>
-

-

-
-                <img src="<%= watcher.photo %>" alt="<%= watcher.full_name_display %>">
-
+
+        <span class="us-detail-status" style="color:<%= status.color %>"><%= status.name %>
+

+

+        <div class="severity-data <% if (editable) { %>clickable<% } %>">
+            <span class="level" style="background-color:<%= severity.color %>">
+            <%= severity.name %>
+            severity

-

-            <a href="" title="<%= watcher.full_name_display %>">
-                <%= watcher.full_name_display %>
-
-            <% if (editable) { %>
-                <a class="icon icon-delete" data-watcher-id="<%= watcher.id %>" href="" title="delete-watcher">
-            <% } %>
+        <div class="priority-data <% if (editable) { %>clickable<% } %>">
+            <span class="level" style="background-color:<%= priority.color %>">
+            <%= priority.name %>
+            priority
+

+        <div class="status-data <% if (editable) { %>clickable<% } %>">
+            <span class="level" style="background-color:<%= status.color %>">
+            <%= status.name %>
+            status

-    <% }); %>
-    """)
-
-    link = ($scope, $el, $attrs, $model) ->
-        editable = $attrs.editable?
-        $scope.$watch $attrs.ngModel, (watcherIds) ->
-            watchers = _.map(watcherIds, (watcherId) -> $scope.usersById[watcherId])
-            html = template({watchers: watchers, editable:editable})
-            $el.html(html)
-            if watchers.length == 0
-                if editable
-                    $el.find(".title").text("Add watchers")
-                    $el.find(".watchers-header").addClass("no-watchers")
-                else
-                    $el.find(".watchers-header").hide()
-
-        if not editable
-            $el.find(".add-watcher").remove()
-
-        $el.on "click", ".icon-delete", (event) ->
-            event.preventDefault()
-            target = angular.element(event.currentTarget)
-            watcherId = target.data("watcher-id")
-            title = "Remove watcher"
-            subtitle = $scope.usersById[watcherId].full_name_display
-            $confirm.ask(title, subtitle).then =>
-                watcherIds = _.clone($model.$modelValue, false)
-                watcherIds = _.pull(watcherIds, watcherId)
-                $model.$setViewValue(watcherIds)
-
-        $el.on "click", ".add-watcher", (event) ->
-            event.preventDefault()
-            target = angular.element(event.currentTarget)
-            $rootscope.$broadcast("watcher:add")
-
-        $scope.$on "watcher:added", (ctx, watcher) ->
-            watcherIds = _.clone($model.$modelValue, false)
-            watcherIds.push(watcher.id)
-            watcherIds = _.uniq(watcherIds)
-            $scope.$apply ->
-                $model.$setViewValue(watcherIds)
-
-    return {link:link, require:"ngModel"}
-
-module.directive("tgWatchers", ["$rootScope", "$tgConfirm", WatchersDirective])
-
-#############################################################################
-## Assigned to directive
-#############################################################################
-
-AssignedToDirective = ($rootscope, $confirm) ->
-    #TODO: i18n
-    template = _.template("""
-        <% if (assignedTo) { %>
-

-            <img src="<%= assignedTo.photo %>" alt="<%= assignedTo.full_name_display %>">
-

-        <% } %>
-

-            Assigned to
-
-            <% if (assignedTo) { %>
-                <%= assignedTo.full_name_display %>
-            <% } else { %>
-                Not assigned
-            <% } %>
-            <% if (editable) { %>
-
-            <% } %>
-
-            <% if (editable && assignedTo!==null) { %>
-
-            <% } %>
-

-    """)
-
-    link = ($scope, $el, $attrs, $model) ->
-        editable = $attrs.editable?
-
-        renderAssignedTo = (issue) ->
-            assignedToId = issue?.assigned_to
-            assignedTo = null
-            assignedTo = $scope.usersById[assignedToId] if assignedToId?
-            html = template({assignedTo: assignedTo, editable:editable})
-            $el.html(html)
-
-        $scope.$watch $attrs.ngModel, (instance) ->
-            renderAssignedTo(instance)
-
-        $el.on "click", ".user-assigned", (event) ->
-            event.preventDefault()
-            $scope.$apply(
-                $rootscope.$broadcast("assigned-to:add", $model.$modelValue)
-            )
-
-        $el.on "click", ".icon-delete", (event) ->
-            event.preventDefault()
-            title = "Remove assigned to"
-            subtitle = ""
-            $confirm.ask(title, subtitle).then =>
-                $model.$modelValue.assigned_to  = null
-                renderAssignedTo($model.$modelValue)
-
-        $scope.$on "assigned-to:added", (ctx, issue) ->
-            renderAssignedTo(issue)
-
-    return {link:link, require:"ngModel"}
-
-module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", AssignedToDirective])
-
-
-#############################################################################
-## Issue status directive
-#############################################################################
-
-IssueStatusDirective = () ->
-    #TODO: i18n
-    template = _.template("""
-

-
-            <% if (status.is_closed) { %>
-            Closed
-            <% } else { %>
-            Open
-            <% } %>
-
-            <span class="us-detail-status" style="color:<%= status.color %>"><%= status.name %>
-

-

-            <div class="severity-data <% if (editable) { %>clickable<% } %>">
-                <span class="level" style="background-color:<%= severity.color %>">
-                <%= severity.name %>
-                severity
-

-            <div class="priority-data <% if (editable) { %>clickable<% } %>">
-                <span class="level" style="background-color:<%= priority.color %>">
-                <%= priority.name %>
-                priority
-

-            <div class="status-data <% if (editable) { %>clickable<% } %>">
-                <span class="level" style="background-color:<%= status.color %>">
-                <%= status.name %>
-                status
-

-

     """)
     selectionSeverityTemplate = _.template("""
-

-          <% _.each(severities, function(severity) { %>
-
<a href="" class="severity" title="<%- severity.name %>"
-                 data-severity-id="<%- severity.id %>"><%- severity.name %>

-          <% }); %>
-

+

+        <% _.each(severities, function(severity) { %>
+
<a href="" class="severity" title="<%- severity.name %>"
+               data-severity-id="<%- severity.id %>"><%- severity.name %>

+        <% }); %>
+

     """)
     selectionPriorityTemplate = _.template("""
-

-          <% _.each(priorities, function(priority) { %>
-
<a href="" class="priority" title="<%- priority.name %>"
-                 data-priority-id="<%- priority.id %>"><%- priority.name %>

-          <% }); %>
-

+

+        <% _.each(priorities, function(priority) { %>
+
<a href="" class="priority" title="<%- priority.name %>"
+               data-priority-id="<%- priority.id %>"><%- priority.name %>

+        <% }); %>
+

     """)
     selectionStatusTemplate = _.template("""
-

-          <% _.each(statuses, function(status) { %>
-
<a href="" class="status" title="<%- status.name %>"
-                 data-status-id="<%- status.id %>"><%- status.name %>

-          <% }); %>
-

+

+        <% _.each(statuses, function(status) { %>
+
<a href="" class="status" title="<%- status.name %>"
+               data-status-id="<%- status.id %>"><%- status.name %>

+        <% }); %>
+

     """)

     link = ($scope, $el, $attrs, $model) ->
@@ -541,7 +400,7 @@
 ## Comment directive
 #############################################################################

-CommentDirective = () ->
+CommentDirective = ->
     link = ($scope, $el, $attrs, $model) ->
         $el.on "click", ".activity-title", (event) ->
             event.preventDefault()


[a11y] Hides course menu from keyboard only users

Makes it so that the hidden course menu gets removed from
the tab order while it is hidden.  It is then added back
when it is shown.

fixes CNVS-22770

Test Plan:
  - Enable new navigation
  - Load up a course with the viewport set small (< 1200px)
  - The hamburger menu should be shown
  - Tab through and you should go from the end of the help menu through
    the breadcrumbs and to your content.
  - Expand the viewport to > 1200px
  - You should now be able to tab through to the course menu
  - Reduce the viewport again
  - Tab to the hamburger menu and activate it.
  - The course menu should now be in the tab order
  - Verify that with screenreaders the hamburger menu is never "shown"
    to them and that all the course menu items are always "visible"
    to screenreaders

Change-Id: I0f7d25ad29e6896f662396528b206c8e34473457
Reviewed-on: https://gerrit.instructure.com/62141
Tested-by: Jenkins
Reviewed-by: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>
QA-Review: Nathan Rogowski <2e8aa918660411855c6d44d5bb2da677aa033255@instructure.com>
Product-Review: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>

--- app/coffeescripts/bundles/common.coffee
+++ app/coffeescripts/bundles/common.coffee
@@ -1,5 +1,6 @@
 require [
   'jquery'
+  'underscore'

   # true modules that we manage in this file
   'Backbone'
@@ -47,7 +48,7 @@
   'vendor/jquery.pageless'
   'vendor/jquery.scrollTo'
   'compiled/badge_counts'
-], ($, Backbone, helpDialog, tours) ->
+], ($, _, Backbone, helpDialog, tours) ->
   helpDialog.initTriggers()
   tours.init()

@@ -63,9 +64,39 @@
   # new styles only - show and hide the courses vertical menu when the user clicks the hamburger button
   # This was in the courses bundle, but it sometimes needs to work in places that don't
   # load that bundle.
-  $("body").on('click', '#courseMenuToggle', ->
-    $("body").toggleClass("course-menu-expanded")
+  #
+  WIDE_BREAKPOINT = 1200
+
+  setMenuItemTabIndex = ($menuElement) ->
+    # in testing this, it seems that $(document).width() returns 15px less than what it should.
+    if ($('body').hasClass('course-menu-expanded') || $(document).width() >= WIDE_BREAKPOINT - 15)
+      $menuElement.attr('tabIndex', 0)
+    else
+      $menuElement.attr('tabIndex', -1)
+
+  $(document).ready( ->
+    return unless window.ENV.use_new_styles?
+
+    $('#section-tabs li a').each((index, element) ->
+      setMenuItemTabIndex($(element))
+    )
   )
+
+  if window.ENV.use_new_styles?
+    $("body").on('click', '#courseMenuToggle', ->
+      $("body").toggleClass("course-menu-expanded")
+      $('#section-tabs li a').each((index, element) ->
+        setMenuItemTabIndex($(element))
+      )
+    )
+
+    $(window).on('resize', _.debounce( ->
+      $('#section-tabs li a').each((index, element) ->
+        setMenuItemTabIndex($(element))
+      )
+    , 50)
+    )
+

   ##
   # Backbone routes


Don't put date in time field, fix #2086

Don't put date in time field, fix #2086

--- src/resources-packaged/ops/javascript/orbeon/xforms/control/Calendar.coffee
+++ src/resources-packaged/ops/javascript/orbeon/xforms/control/Calendar.coffee
@@ -31,7 +31,7 @@

     setValue = (control, formattedDate) ->
         attributeName = if $(control).is(".xforms-input-appearance-minimal") then "alt" else "value"
-        inputHolder = $(control).children('.xforms-input-input')
+        inputHolder = $(control).children('.xforms-input-input').first()
         $(inputHolder).attr(attributeName, formattedDate)
         value = Controls.getCurrentValue(control)
         changeEvent = new ORBEON.xforms.server.AjaxServer.Event(null, control.id, value, "xxforms-value")


fetchMyMemberships should only look for member relationship, if as selector is not given, then admins and owners are counted multiple times

--- workers/social/lib/social/traits/joinable.coffee
+++ workers/social/lib/social/traits/joinable.coffee
@@ -4,9 +4,11 @@

   KodingError = require '../error'

-  @fetchMyMemberships = secure (client, ids, callback)->
+  @fetchMyMemberships = secure (client, ids, as, callback)->
+    [callback, as] = [as, callback] unless callback
+    as ?= 'member'
     {delegate} = client.connection
-    delegate.filterRelatedIds ids, 'member', callback
+    delegate.filterRelatedIds ids, as, callback

   addToGroup_ =(client, {as}, callback)->
     as ?= 'member'


ci(*): mkrpm only works on Fedora build machines, need to configure one

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -333,7 +333,8 @@
     ciTasks.push('codesign')
     ciTasks.push('mkdmg') if process.platform is 'darwin'
     ciTasks.push('mkdeb') if process.platform is 'linux'
-    ciTasks.push('mkrpm') if process.platform is 'linux'
+    # Only works on Fedora build machines
+    # ciTasks.push('mkrpm') if process.platform is 'linux'
     ciTasks.push('create-windows-installer:installer') if process.platform is 'win32'
     ciTasks.push('publish-nylas-build')


increase guaranteed minimums for RAM and CPU, and give members way more than non-members

--- src/smc-hub/kucalc/quota.coffee
+++ src/smc-hub/kucalc/quota.coffee
@@ -1,3 +1,18 @@
+
+# No matter what, every project gets SOME possibly tiny amount of guaranteed cpu.
+# This is important since otherwise projects will NOT start at all, e.g., if a paying
+# customer is using 100% of the cpu on the node (this will happen if their limits are
+# high and they have guaranteed cpu of about 1 or more).  The project will be so slow
+# it fails to start in time and times out.
+MIN_POSSIBLE_CPU =
+    member    : 0.1
+    nonmember : 0.04
+
+# Min possible **guaranteed** RAM.
+MIN_POSSIBLE_MEMORY =
+    member    : 200
+    nonmember : 100
+
 misc = require('smc-util/misc')

 exports.quota = (settings, users) ->
@@ -9,10 +24,10 @@
         network        : false
         member_host    : false
         disk_quota     : 3000
-        memory_limit   : 1000        # default upper bound on RAM in MB
-        memory_request : 100         # default guaranteed RAM in MB
-        cpu_limit      : 1           # default upper bound on cpu
-        cpu_request    : 0           # default guaranteed min cpu
+        memory_limit   : 1000        # upper bound on RAM in MB
+        memory_request : 0           # will hold guaranteed RAM in MB
+        cpu_limit      : 1           # upper bound on vCPU's
+        cpu_request    : 0           # will hold guaranteed min number of vCPU's as a float from 0 to infinity.
         privileged     : false       # for elevated docker privileges (FUSE mounting, later more)

     # network access
@@ -76,6 +91,22 @@
     for _, val of users
         quota.cpu_request += to_int(val?.upgrades?.cpu_shares) / 1024

+    # ensure minimums are met
+    if quota.member_host
+        if quota.cpu_request < MIN_POSSIBLE_CPU.member
+            quota.cpu_request = MIN_POSSIBLE_CPU.member
+    else
+        if quota.cpu_request < MIN_POSSIBLE_CPU.nonmember
+            quota.cpu_request = MIN_POSSIBLE_CPU.nonmember
+
+    # ensure minimums are met
+    if quota.member_host
+        if quota.memory_request < MIN_POSSIBLE_MEMORY.member
+            quota.memory_request = MIN_POSSIBLE_MEMORY.member
+    else
+        if quota.memory_request < MIN_POSSIBLE_MEMORY.nonmember
+            quota.memory_request = MIN_POSSIBLE_MEMORY.nonmember
+
     return quota


Forgot to call logout before trying to wait for logout to finish - who knew?

--- tests/mocha/apollos/create.coffee
+++ tests/mocha/apollos/create.coffee
@@ -22,6 +22,7 @@
       Meteor.setTimeout func, 250

     _logout = (callback) ->
+      Meteor.logout()
       _waitForEvent ->
         return not Meteor.userId()
       , callback


Update Document#removeTextAtLocationRange for block breaks

--- src/trix/models/document.coffee
+++ src/trix/models/document.coffee
@@ -64,32 +64,25 @@
   removeTextAtLocationRange: edit (locationRange) ->
     return if locationRange.isCollapsed()

-    if locationRange.isInSingleIndex()
-      index = locationRange.index
-      block = @blockList.getObjectAtIndex(index)
-      text = block.text.removeTextAtRange([locationRange.start.offset, locationRange.end.offset])
-
-      @blockList = if text.isEmpty()
-        @blockList.removeObjectAtIndex(index)
-      else
-        @blockList.replaceObjectAtIndex(block.copyWithText(text), index)
-
-    else
-      range = @rangeFromLocationRange(locationRange)
-      blockList = @blockList.removeObjectsInRange(range)
-
-      if blockList.length
-        leftIndex = locationRange.index
-        rightIndex = leftIndex + 1
-        leftBlock = blockList.getObjectAtIndex(leftIndex)
-        rightBlock = blockList.getObjectAtIndex(rightIndex)
-
-        if leftBlock and rightBlock
-          blockList = blockList.
-            removeObjectAtIndex(rightIndex).
-            replaceObjectAtIndex(leftBlock.consolidateWith(rightBlock), leftIndex)
-
-      @blockList = blockList
+    leftIndex = locationRange.start.index
+    leftBlock = @getBlockAtIndex(leftIndex)
+    leftText = leftBlock.text.getTextAtRange([0, locationRange.start.offset])
+
+    rightIndex = locationRange.end.index
+    rightBlock = @getBlockAtIndex(rightIndex)
+    rightText = rightBlock.text.getTextAtRange([locationRange.end.offset, rightBlock.getLength()])
+
+    text = leftText.appendText(rightText)
+    block = leftBlock.copyWithText(text)
+    blocks = @blockList.toArray()
+    affectedBlockCount = rightIndex + 1 - leftIndex
+
+    if block.isEmpty()
+      blocks.splice(leftIndex, affectedBlockCount)
+    else
+      blocks.splice(leftIndex, affectedBlockCount, block)
+
+    @blockList = new Trix.SplittableList blocks

   moveTextFromLocationRangeToPosition: edit (locationRange, position) ->
     range = @rangeFromLocationRange(locationRange)


Router: isLoggedIn logic doesn't need to wait for mainController to be ready

--- client/Main/kodingrouter.coffee
+++ client/Main/kodingrouter.coffee
@@ -244,14 +244,11 @@
     #     warn "FIXME Add tell to Login app ~ GG @ kodingrouter", formName
     #     # mainController.loginScreen.animateToForm formName

-    requireLogin =(fn)->
-      mainController.ready ->
-        if KD.isLoggedIn() then utils.defer fn
-        else clear()
-
-    requireLogout =(fn)->
-      mainController.ready ->
-        unless KD.isLoggedIn() then utils.defer fn else clear()
+    requireLogin = (fn)->
+      if KD.isLoggedIn() then utils.defer fn else clear()
+
+    requireLogout = (fn)->
+      unless KD.isLoggedIn() then utils.defer fn else clear()

     createSectionHandler = (sec)=>
       ({params:{name, slug}, query})=>


Fix TL wording

Fix TL wording

--- client/components/Submit.coffee
+++ client/components/Submit.coffee
@@ -99,7 +99,7 @@
 getClassName = (status) ->
     switch status
         when "OK" then "success"
-        when "Превышение времени" then "info"
+        when "Превышение времени", "Превышено максимальное время работы" then "info"
         else styles.wa

 class TestResult extends React.Component


Added status bar menu related methods to AppController.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -260,6 +260,16 @@
         else
           view.webtermView.updateSettings()

+  showShortcutsView: ->
+    @activeTabView.emit 'ShortcutsViewRequested'
+
+  showActionsMenu: (button) ->
+    view = @activeTabView.getActivePane().getSubViews().first
+    new IDE.StatusBarMenu
+      paneType : view?.getOptions() or null
+      x        : button.getX()
+      y        : button.getY() - 120
+
   updateStatusBar: (component, data) ->
     {status} = @statusBar