maybe fixed the try task link on mobile

maybe fixed the try task link on mobile

--- app/assets/javascripts/lib/nypl_inspector.coffee
+++ app/assets/javascripts/lib/nypl_inspector.coffee
@@ -401,7 +401,7 @@

   endGame:() ->
     # no map found, die
-    msg = "No unprocessed data found for this task
Good news! This seems to be complete. Maybe try another task?"
+    msg = "No unprocessed data found for this task
Good news! This seems to be complete. Maybe try another task?"
     @showMessage(msg, false)
     $(@options.buttonsID).hide()


better permission checking for proposal

better permission checking for proposal

Former-commit-id: 71a5d033244b7e0f3fc6b87b741eca7354668b59 [formerly ddd16eab28810d3e372c2c54615f1fb88713ba53] [formerly 23ddc9c368f29e2a73637b52f4682de49e42976b [formerly 23ddc9c368f29e2a73637b52f4682de49e42976b [formerly 388d8607e47e5c8640c422edd3f680a29ad4aec3]]]
Former-commit-id: b21f474ea72cd688737d26ba361f3fcf7571fb38 [formerly 9057ec448aafcb09854dc81203a45e99bd0f5549]
Former-commit-id: 79fa9e260d44954b149cda115846f3f120638f3b
Former-commit-id: 17b1f899c5cb0579846eb94394ac47f6933ba59a

--- app/assets/javascripts/views/proposalview.coffee
+++ app/assets/javascripts/views/proposalview.coffee
@@ -48,7 +48,7 @@
     @proposal.views.results.render()
     results_el.insertAfter(@$el.find('.m-proposal-introduction'))

-    if @model.get('published')
+    if @model.get('published') || @can_edit()
       @proposal.views.take_position = new ConsiderIt.PositionView
         proposal : @proposal
         model : @proposal.position
@@ -56,6 +56,9 @@

       position_el = @proposal.views.take_position.render()
       position_el.insertAfter(results_el)
+
+    #TODO: if user logs in as admin, need to do this
+    @render_admin_strip() if @can_edit()

     @transition_unexpanded()

@@ -101,6 +104,8 @@
     @listenTo ConsiderIt.app, 'user:signout', @post_signout
     @trigger 'proposal:data_loaded'

+  can_edit : ->
+    ConsiderIt.current_user.id == @model.get('user_id') || ConsiderIt.roles.is_admin || ConsiderIt.roles.is_manager

   load_data : (callback, callback_params) ->
     @once 'proposal:data_loaded', => callback(this, callback_params)
@@ -195,8 +200,10 @@
         @proposal.views.results.show_explorer()
         @set_state(2)

-      strip = ConsiderIt.ProposalView.proposal_strip_template( @model.attributes )
-      @$el.prepend(strip)
+      if @$el.find('.m-proposal-strip.m-proposal-connect').length == 0
+        strip = ConsiderIt.ProposalView.proposal_strip_template( @model.attributes )
+        @$el.append(strip)
+
       $('.l-navigate-home').fadeIn() #TODO: handle this in userdashboard via proposal opened event

@@ -222,8 +229,6 @@

         @$el.find('.m-proposal-description-details').slideDown =>

-          #TODO: if user logs in as admin, need to do this
-          @render_admin_strip() if ConsiderIt.roles.is_admin || ConsiderIt.roles.is_manager
           callback(new_state)

@@ -242,7 +247,7 @@
         @proposal.views.take_position.close_crafting()
         @proposal.views.results.show_summary()

-        @$el.find('.m-proposal-strip').remove()
+        @$el.find('.m-proposal-connect').remove()
         $('.l-navigate-home').hide()

         if @pointdetailsview
@@ -273,6 +278,8 @@

   toggle_description : (ev) ->
+    return if @can_edit() && $(ev.target).closest('.editable-click, .editable-inline').length > 0
+
     @$el.find('.m-proposal-description-body, .m-proposal-description-details').slideToggle()

   show_details : (ev) ->
@@ -319,12 +326,9 @@

   show_point_details_handler : (point_id) ->

-    console.log 'SHOWP'
     if !@data_loaded
       @load_data(@prepare_for_point_details, {point_id : point_id})
     # if data is already loaded, then the PointListView is already properly handling this
-
-

   # TODO: move to its own view
@@ -335,7 +339,7 @@
     admin_strip_el = $('<div class="m-proposal-admin_strip m-proposal-strip">')
     template = _.template($('#tpl_proposal_admin_strip').html())
     admin_strip_el.html( template(@model.attributes))
-    admin_strip_el.insertBefore(@$el.find('.m-proposal_strip_toggle'))
+    @$el.append admin_strip_el
     for field in ConsiderIt.ProposalView.editable_fields
       [selector, name, type] = field
       @$el.find(selector).editable {


Fix #3943

Fix #3943

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -652,7 +652,7 @@
     Backbone.Mediator.publish 'tome:hide-problem-alert', {}

   saveSpade: =>
-    return if @destroyed
+    return if @destroyed or not @spade
     spadeEvents = @spade.compile()
     # Uncomment the below line for a debug panel to display inside the level
     #@spade.debugPlay(spadeEvents)


added Vatikin for 1st day of Shabuot

added Vatikin for 1st day of Shabuot

--- js/schedule.coffee
+++ js/schedule.coffee
@@ -26,6 +26,12 @@
     show_event(day, event, hebrew_date) for event in events
     if hebrew_date.isYomKippur() || hebrew_date.isRoshHashana()
       shaharit_is_fixed_at(day, 7, 0)
+    else if hebrew_date.is1stDayOfShabuot()
+      sunrise = moment(zmanim.sunrise)
+      $(".#{day} .amidah").html(time_format(sunrise))
+      $(".#{day} .yishtabach").html(time_format(sunrise.subtract('minutes', 15)))
+      $(".#{day} .hodu").html(time_format(sunrise.subtract('minutes', 25)))
+      $(".#{day} .korbanot").html(time_format(sunrise.subtract('minutes', 15)))
     else if hebrew_date.isYomTov() || hebrew_date.isShabbat()
       shaharit_is_fixed_at(day, 7, 45)
     else


teams: fixes invitations link on welcome page

--- client/welcome/lib/welcomeappview.coffee
+++ client/welcome/lib/welcomeappview.coffee
@@ -28,7 +28,7 @@

-
+
               2

                 Invite your team


incomplete rewriter code

incomplete rewriter code

--- js_rewriter.coffee
+++ js_rewriter.coffee
@@ -136,17 +136,18 @@
     get: -> @out.join ""
     reset: -> @out = []
     line: ( text)->
-        if not @debug and text.startswith("console.log"):
+        if not @debug and text.match /^console.log/
             return

-        if not @pretty and text.startswith("//"):
+        if not @pretty and text.match /^\/\//
             return

-        if @pretty or "//" in text:
-            @out.push("    "*@ident)
+        if @pretty or text.match(/\/\//)
+          for i in [0...@ident]
+            @out.push "    "

         @out.push(text)
-        @out.push("\n" if @pretty else " ")
+        @out.push(if @pretty then "\n" else " ")

     block: (callback)->
         @line("{")


minor: clean up rewriter generation code

--- js_rewriter.coffee
+++ js_rewriter.coffee
@@ -198,21 +198,20 @@
         @rewriteTable = rewriteTable
         @suffixTree = reverseSuffixTable(rewriteTable)

-    generate: ->
+    generateAppendRewriteOnce: ->
         @line("(function(chain, stack )")
         @block =>
-            @line("while( stack.length > 0)")
+            @line "if (stack.length === 0) {throw new Error('empty stack');}"
+            @line("var _e = stack.pop();")
+            @line("var element = _e[0], power = _e[1];")
+            @line("if (chain === null)")
             @block =>
-                @line("var _e = stack.pop();");
-                @line("var element = _e[0], power = _e[1];");
-                @line("if (chain === null)")
-                @block =>
-                    @line("//empty chain")
-                    @line('console.log("Append to empth chain:"+_e);');
-                    @line("var order=(element==='a')?#{@_nodeOrder('a')}:#{@_nodeOrder('b')};")
-                    @line("var lowestPow=(element==='a')?#{@_lowestPower('a')}:#{@_lowestPower('b')};")
-                    @line('chain = new nodeConstructors[element](((power - lowestPow)%order+order)%order+lowestPow, chain);')
-                @generateMain()
+                @line("//empty chain")
+                @line('console.log("Append to empth chain:"+_e);');
+                @line("var order=(element==='a')?#{@_nodeOrder('a')}:#{@_nodeOrder('b')};")
+                @line("var lowestPow=(element==='a')?#{@_lowestPower('a')}:#{@_lowestPower('b')};")
+                @line('chain = new nodeConstructors[element](((power - lowestPow)%order+order)%order+lowestPow, chain);')
+            @generateMain()
             @line("return chain;")
         @line(")")
         return @get()
@@ -377,10 +376,24 @@
         table["original"] = gSuffix
     return revTable

+
+exports.repeatRewrite = repeatRewrite = (appendRewriteOnce) -> (chain, stack) ->
+  while stack.length > 0
+    chain = appendRewriteOnce chain, stack
+  return chain
+
+exports.canAppend = (appendRewriteOnce) -> (chain, element, power) ->
+  stack = [[element, power]]
+  appendRewriteOnce(chain, stack)
+  return stack.length is 0
+
 exports.makeAppendRewrite= makeAppendRewrite = (s)->
   g = new CodeGenerator(s)
   g.debug=false
-  appendRewrite = eval g.generate()
+
+  appendRewriteOnce = eval g.generateAppendRewriteOnce()
+  appendRewrite = repeatRewrite appendRewriteOnce
+
   throw new Error("Failed to compilation gave nothing?") unless appendRewrite?
   return appendRewrite


Pressing enter should select the first result

--- apps/personalize/client/mixins/followable.coffee
+++ apps/personalize/client/mixins/followable.coffee
@@ -19,6 +19,7 @@
       restrictType : options.restrictType
       el           : @$('#personalize-search-container')
       $input       : @$searchInput ?= @$('#personalize-search')
+      autoselect   : true

     @listenTo @searchBarView, 'search:selected', @follow


Stay on the same page when loading latest version

--- src/scripts/modules/media/latest/latest.coffee
+++ src/scripts/modules/media/latest/latest.coffee
@@ -8,8 +8,13 @@
     template: template
     templateHelpers:
       url: () ->
-        id = @model.get('id').split('@', 1)[0] # Remove the version from the id
-        return "#{settings.root}contents/#{id}"
+        page = ''
+        id = @model.getUuid()
+
+        if @model.isBook()
+          page = ":#{@model.getPageNumber()}"
+
+        return "#{settings.root}contents/#{id}#{page}"

     initialize: () ->
       super()


Add test

Add test

--- components/article/test/templates.coffee
+++ components/article/test/templates.coffee
@@ -65,3 +65,15 @@
       asset: ->
     html.should.not.containEql 'article-share-fixed'
     html.should.containEql 'article-social'
+
+  it 'includes the correct links for infinite scroll articles', ->
+    html = render('index')
+      article: new Article title: 'hi', slug: 'foo', sections: [], section_ids: []
+      crop: (url) -> url
+      moment: moment
+      sd:
+        CURRENT_PATH: 'bar'
+        SCROLL_ARTICLE: 'infinite'
+      asset: ->
+    html.should.containEql 'foo'
+    html.should.not.containEql 'bar'


refactor notification to make it dismissable

--- assets/coffee/components/Notification.cjsx
+++ assets/coffee/components/Notification.cjsx
@@ -6,15 +6,22 @@
   getDefaultProps: ->
     delay: 1000
     type: 'info'
+    message: ''

   getInitialState: ->
     visible: true
+
+  show: ->
+    @setState visible: true
+
+  dismiss: ->
+    @setState visible: false

   componentWillReceiveProps: (nextProps) ->
     # reset the timer if children are changed
     if nextProps.children != @props.children
       @setTimer()
-      @setState visible: true
+      @show()

   componentDidMount: ->
     @setTimer()
@@ -23,16 +30,20 @@
     # clear any existing timer
     if @_timer != null then clearTimeout(@_timer) else null

-    # hide after delay
+    # dismiss after delay
     @_timer = setTimeout((->
-      @setState visible: false
+      @dismiss()
       @_timer = null
     ).bind(this), @props.delay)

   render: ->
     if @state.visible
       className = "notification notification--#{@props.type}"
-
{this.props.children}

+
{@props.message}

     else


Run lastBuildDidChange only once for a runloop run

When a lot of pusher events come with build updates, lastBuildDidChange
can run quite frequently. We can avoid that by using scheduleOnce, which
will ensure that only the last invocation of lastBuildDidChange will be
run for a given runloop run.

--- assets/scripts/app/controllers/repo.coffee
+++ assets/scripts/app/controllers/repo.coffee
@@ -50,6 +50,9 @@
     @connectTab('job')

   lastBuildDidChange: ->
+    Ember.run.scheduleOnce('data', this, @_lastBuildDidChange);
+
+  _lastBuildDidChange: ->
     build = @get('repo.lastBuild')
     @set('build', build)


Don't be so paranoid about empty opts

Don't be so paranoid about empty opts

--- atom/browser/lib/guest-view-manager.coffee
+++ atom/browser/lib/guest-view-manager.coffee
@@ -57,14 +57,13 @@
     min = width: params.minwidth, height: params.minheight
     max = width: params.maxwidth, height: params.maxheight
     @setAutoSize params.autosize, min, max
+
     if params.src
       opts = {}
       opts.httpreferrer = params.httpreferrer if params.httpreferrer
       opts.useragent = params.useragent if params.useragent
-      if params.httpreferrer or params.useragent
-        @loadUrl params.src, opts
-      else
-        @loadUrl params.src
+      @loadUrl params.src, opts
+
     if params.allowtransparency?
       @setAllowTransparency params.allowtransparency


minor color changes

minor color changes

--- src/environment.coffee
+++ src/environment.coffee
@@ -16,9 +16,9 @@
         titleHeight: 50
         footerHeight: 25
         bounceDecay: 0
-        accentColor: 'silver'
-        backgroundColor: 'black'
-        foregroundColor: 'darkgreen'
+        accentColor: '#FFFFFF'
+        backgroundColor: '#000000'
+        foregroundColor: '#006400'
         footer: 'BOTTOM PANEL'
         leftHeader: 'LEFT PANEL'
         rightHeader: 'RIGHT PANEL'
@@ -110,6 +110,7 @@
         margin: '2.5% auto'
         borderRadius: '15px'
         fontFamily: 'Tahoma, Geneva, sans-serif'
+        color: env.ENGINE.accentColor
         backgroundColor: env.ENGINE.foregroundColor
         border: "5px solid #{env.ENGINE.accentColor}"
         minHeight: "#{env.ENGINE.canvasHeight + env.ENGINE.titleHeight + env.ENGINE.footerHeight + 40}px"


change default cursor

change default cursor

--- src/Bu.coffee
+++ src/Bu.coffee
@@ -108,7 +108,7 @@
 	defaultOptions = {} if not defaultOptions?
 	givenOptions = args[args.length - 1]
 	if typeof givenOptions is 'object'
-		for i of givenOptions
+		for i of givenOptions when givenOptions[i]?
 			defaultOptions[i] = givenOptions[i]
 	return defaultOptions


Parse code when modified

Parse code when modified

--- lib/coffee-refactor.coffee
+++ lib/coffee-refactor.coffee
@@ -3,12 +3,9 @@
 module.exports = new class CoffeeRefactor

   activate: (state) ->
-    console.log 'activate'
-    atom.workspace.eachEditor (editor) =>
-      editor.on 'contents-modified', =>
-        @modified editor
     atom.workspaceView.command 'coffee-refactor:rename', @rename
     atom.workspaceView.command 'coffee-refactor:done', @done
+    atom.workspace.eachEditor @onEditorCreated

   deactivate: ->
     console.log 'deactivate'
@@ -16,32 +13,32 @@
   serialize: ->
     console.log 'serialize'

+  onEditorCreated: (editor) =>
+    editor.parser = new Parser
+    editor.on 'contents-modified', =>
+      @modified editor
+    @modified editor
+
   modified: (editor) =>
-    console.log 'modified'
-    refactor = new Parser editor.buffer.cachedText
+    editor.parser.parse editor.buffer.cachedText

-  rename: =>
+  rename: (e) =>
     editor = atom.workspace.getActiveEditor()
-    return unless editor?
+    return e.abortKeyBinding() unless editor?

     editor.selectWord()
     selection = editor.getSelection 0
-
     code = editor.getText()
-    if code isnt @code
-      @code = code
-      @refactor = new Parser code
-
-    nodes = @refactor.find selection.getBufferRange()
-    return if nodes.length is 0
+    nodes = editor.parser.find selection.getBufferRange()
+    return e.abortKeyBinding() if nodes.length is 0

     @target =
       editor: editor
       selection: selection

     for { locationData } in nodes
-      range = Refactor.locationDataToRange locationData
-      editor.addSelectionForBufferRange Refactor.locationDataToRange locationData
+      range = Parser.locationDataToRange locationData
+      editor.addSelectionForBufferRange Parser.locationDataToRange locationData

   done: (e) =>
     return e.abortKeyBinding() unless @target?


default grammar option

default grammar option

--- lib/console/console.coffee
+++ lib/console/console.coffee
@@ -23,6 +23,9 @@
     view: new ConsoleView

     isInput: false
+
+    setGrammar: (g) ->
+      @view.setGrammar g

     input: ->
       v = @view.inputView(this)


example cleanup

example cleanup

--- examples/coffee/5-sub-elements.coffee
+++ examples/coffee/5-sub-elements.coffee
@@ -34,7 +34,6 @@
   console.log "server status:", @status()
   @init browserName:'chrome'
   console.log "session id:", @getSessionId()
-  console.log "session capabilities:", @sessionCapabilities()

   @get "http://saucelabs.com/test/guinea-pig"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -1,7 +1,7 @@
 ###*
-Creates an oscillator function with the given parameters.

-@name Oscillator
+
+@name LevelState
 @constructor
 @param {Number} amplitude How much to scale the oscillator function value
 @param {Number} period How fast the osciallator function repeats


JournalStore should emit an event on new transactions

--- src/lib/Journal.coffee
+++ src/lib/Journal.coffee
@@ -58,6 +58,8 @@
   constructor: (@graph) ->
     @lastRevision = 0
   putTransaction: (revId, entries) ->
+    @lastRevision = revId if revId > @lastRevision
+    @emit 'transaction', revId
   fetchTransaction: (revId, entries) ->

 class MemoryJournalStore extends JournalStore
@@ -66,7 +68,7 @@
     @transactions = []

   putTransaction: (revId, entries) ->
-    @lastRevision = revId if revId > @lastRevision
+    super revId, entries
     @transactions[revId] = entries

   fetchTransaction: (revId) ->


Don't pre-set this.started

Don't pre-set this.started

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -570,7 +570,6 @@
       return callback err if err
       @sendInitials (err) =>
         return callback err if err
-        @started = true
         @sendDefaults (err) =>
           return callback err if err
           @setStarted true


Only try to dereference attrs when enough information is present

--- app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee
+++ app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee
@@ -8,5 +8,4 @@
     dereferenceAttr: (array, attr, data)->
       if array
         for object in array
-          console.log attr, object[attr].id if data[object[attr].id] == undefined
-          object[attr] = data[object[attr].id]
+          object[attr] = data[object[attr].id] unless object[attr] == null


Renamed CoffeeScript reserved keyword "package" to "pkg"

--- src/pypi/client.coffee
+++ src/pypi/client.coffee
@@ -16,29 +16,29 @@
             else if callback
                 callback(value)

-    getPackageReleases: (package, callback, onError, showHidden=false) ->
-        @callXmlrpc "package_releases", [package, showHidden], callback, onError
+    getPackageReleases: (pkg, callback, onError, showHidden=false) ->
+        @callXmlrpc "package_releases", [pkg, showHidden], callback, onError

     getPackagesList: (callback, onError) ->
         @callXmlrpc "list_packages", [], callback, onError

-    getPackageRoles: (package, callback, onError) ->
-        @callXmlrpc "package_roles", [package], callback, onError
+    getPackageRoles: (pkg, callback, onError) ->
+        @callXmlrpc "package_roles", [pkg], callback, onError

-    getUserPackages: (package, callback, onError) ->
-        @callXmlrpc "user_packages", [package], callback, onError
+    getUserPackages: (pkg, callback, onError) ->
+        @callXmlrpc "user_packages", [pkg], callback, onError

-    getReleaseData: (package, version, callback, onError) ->
-        @callXmlrpc "release_data", [package, version], callback, onError
+    getReleaseData: (pkg, version, callback, onError) ->
+        @callXmlrpc "release_data", [pkg, version], callback, onError

-    getReleaseDownloads: (package, version, callback, onError) ->
-        @callXmlrpc "release_downloads", [package, version], callback, onError
+    getReleaseDownloads: (pkg, version, callback, onError) ->
+        @callXmlrpc "release_downloads", [pkg, version], callback, onError

-    getReleaseUrls: (package, version, callback, onError) ->
-        @callXmlrpc "release_urls", [package, version], callback, onError
+    getReleaseUrls: (pkg, version, callback, onError) ->
+        @callXmlrpc "release_urls", [pkg, version], callback, onError

-    search: (package, callback, onError) ->
-        @callXmlrpc "search", [{name: package}], callback, onError
+    search: (pkg, callback, onError) ->
+        @callXmlrpc "search", [{name: pkg}], callback, onError

 module.exports = Client


add on issue update detail

add on issue update detail

--- src/scripts/fgb.coffee
+++ src/scripts/fgb.coffee
@@ -14,6 +14,7 @@

 {Promise} = require 'es6-promise'
 parseConfig = require 'hubot-config'
+formatChanges = require '../format-changes'
 newRequests = require '../review-requests'
 newSpace = require '../space'

@@ -60,11 +61,15 @@
   username = w.createdUser.name
   comment = w.content.comment.content
   room = project.getRoom()
+  changes = formatChanges w.content.changes
   resolved = w.content.changes.some (i) ->
     i.field is 'status' and i.new_value is '3'
   message = """
   #{username} が課題「#{issueKey} #{summary}」を更新したみたい。
   #{comment}
+  ```
+  #{changes}
+  ```
   #{issueUrl}
   """
   sendToChat robot, { room, message }


further elimination of dependence of hub.coffee on env vars

--- src/smc-hub/hub.coffee
+++ src/smc-hub/hub.coffee
@@ -478,7 +478,7 @@
     BASE_URL = base_url.init(program.base_url)
     winston.debug("base_url='#{BASE_URL}'")

-    if program.port
+    if program.port and SMC_ROOT
         # ONLY write the base_url file if we are serving the main hub.
         # This file is used by webpack to know what base_url to use, and
         # we don't want webpack using the base_url for the share server (say).


hub -- don't share an error in log for an anticipated error

--- src/smc-hub/hub.coffee
+++ src/smc-hub/hub.coffee
@@ -539,7 +539,8 @@
         h = @_data_handlers[channel]

         if not h?
-            winston.error("unable to handle data on an unknown channel: '#{channel}', '#{data}'")
+            if channel != 'X'  # X is a special case used on purpose -- not an error.
+                winston.error("unable to handle data on an unknown channel: '#{channel}', '#{data}'")
             # Tell the client that they had better reconnect.
             @push_to_client( message.session_reconnect(data_channel : channel) )
             return


fix(onboarding): Add missing return statement when changing endpoints

--- internal_packages/onboarding/lib/account-settings-page.cjsx
+++ internal_packages/onboarding/lib/account-settings-page.cjsx
@@ -399,6 +399,7 @@
         errorMessage: 'The IMAP/SMTP servers for this account do not match our records. Please verify that any server names you entered are correct. If your IMAP/SMTP server has changed, first remove this account from N1, then try logging in again.'
       @_resize()
       OnboardingActions.moveToPage("account-settings")
+      return

     if errorFieldNames
       {pageNumber, errorMessage} = @_stateForMissingFieldNames(errorFieldNames)


minor comment

minor comment

--- fizzylogo_populateMethods.coffee
+++ fizzylogo_populateMethods.coffee
@@ -778,6 +778,10 @@
     (flTokenize "self $plus_binary_default operandum"),
     4

+  # for power operator precedence discussions see
+  # https://esdiscuss.org/topic/exponentiation-operator-precedence
+  # Also, you can check Wolfram Alpha for what the
+  # mathematically-accurate results should be
   FLNumber.addMethod \
     (flTokenize "^ ( operandum )"),
     (flTokenize "self $power_binary_default operandum"),


Restore focus after detached

Restore focus after detached

Also only restore focus when mini editor is currently focused.

--- lib/go-to-line-view.coffee
+++ lib/go-to-line-view.coffee
@@ -31,15 +31,12 @@
     return unless @hasParent()

     @detaching = true
+    miniEditorFocused = @miniEditor.isFocused
     @miniEditor.setText('')
-
-    if @previouslyFocusedElement?.isOnDom()
-      @previouslyFocusedElement.focus()
-    else
-      atom.workspaceView.focus()

     super

+    @restoreFocus() if miniEditorFocused
     @detaching = false

   confirm: ->
@@ -54,8 +51,17 @@
     editor.setCursorBufferPosition(position)
     editor.moveCursorToFirstCharacterOfLine()

+  storeFocusedElement: ->
+    @previouslyFocusedElement = $(':focus')
+
+  restoreFocus: ->
+    if @previouslyFocusedElement?.isOnDom()
+      @previouslyFocusedElement.focus()
+    else
+      atom.workspaceView.focus()
+
   attach: ->
-    @previouslyFocusedElement = $(':focus')
+    @storeFocusedElement()
     atom.workspaceView.append(this)
     @message.text("Enter a line number 1-#{atom.workspaceView.getActiveView().getLineCount()}")
     @miniEditor.focus()


allowed multiple users to initially be in a thread

--- src/controllers/thread_controller.coffee
+++ src/controllers/thread_controller.coffee
@@ -1,19 +1,40 @@
-{Thread} = require '../models'
+{Thread, User} = require '../models'

 exports.post_create_thread = (req, res) ->
   req.assert('name', 'Thread name is not valid.').notEmpty()
   fail = (errors) ->
     res.send {ok: false, error: errors}
-
   errors = req.validationErrors()
   if errors?
     return fail errors

+  initialMembers = req.param 'members'
+
+  # Add req.user to the list of members if not in it already
+  includedSelf = false
+  if not initialMembers?
+    initialMembers = [req.user.username]
+  else
+    for member in initialMembers
+      if req.user.username == member
+        includedSelf = true
+        break
+
+    if not includedSelf
+      initialMembers.push req.user.username
+
   threadName = req.param('name')
   newThread = null
-  Thread.create(displayName: threadName).then (thread) ->
+  members = null
+  User.findAll({where: [username: initialMembers]}).then (allMembers) ->
+    if allMembers.length != initialMembers.length
+      throw "Invalid member specified."
+
+    members = allMembers
+    return Thread.create(displayName: threadName)
+  .then (thread) ->
     newThread = thread
-    return req.user.addThread thread
+    return thread.setMembers members
   .then ->
     res.send {ok: true, body: {thread: newThread.toJSON()}}
   .catch fail


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/function_extensions.coffee
+++ src/function_extensions.coffee
@@ -8,7 +8,7 @@
     return memo if ran
     ran = true

-    return memo = func.apply(arguments)
+    return memo = func.apply(this, arguments)

 Function::withBefore = (interception) ->
   method = this


Fix the link anchors

Fix the link anchors

--- js/outer/changelog.coffee
+++ js/outer/changelog.coffee
@@ -30,6 +30,12 @@
       dataType: "xml"
       success: (results) =>
         @entries(@doc2entries(results))
+        @reloadHash()
+
+
+   reloadHash: () =>
+    # The hash is created after the page loads, which I think is why we need this.
+    window.SammyApp.setLocation(window.location.hash)

   doc2entries: (document) =>
     for i in $(document).find("item")


fixing little mistakes

fixing little mistakes

--- src/smc-hub/kucalc/compute-client.coffee
+++ src/smc-hub/kucalc/compute-client.coffee
@@ -441,7 +441,7 @@
                     dbg("error -- #{err}")
                     opts.cb(err)
                     return
-                if state.state not in ['running', 'starting']
+                if x.state.state not in ['running', 'starting']
                     dbg("not running")
                     opts.cb()
                     return


Resolving system.arg conditional check. Value provided is string.

--- lib/mocha-phantomjs.coffee
+++ lib/mocha-phantomjs.coffee
@@ -135,10 +135,7 @@

 reporter = system.args[2] || 'spec'

-try
-  hooks = require(system.args[3])
-catch err
-  hooks = {}
+hooks = (if (system.args[3] isnt 'undefined') then require(system.args[3]) else {})

 config   = JSON.parse system.args[4] || '{}'


add debug msg on fail-to-compose

add debug msg on fail-to-compose

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -27,6 +27,7 @@
       # If we have started an operation that responds to canComposeWith check if it can compose
       # with the operation we're going to push onto the stack
       if (topOperation = @peekTop())? and topOperation.canComposeWith? and not topOperation.canComposeWith(op)
+        debug "can't compose #{topOperation.getKind()} with #{op.getKind()}, returning"
         @vimState.resetNormalMode()
         @vimState.emitter.emit('failed-to-compose')
         return


Should not show parsed location when encountering unidentified errors

--- lib/view/body/error.coffee
+++ lib/view/body/error.coffee
@@ -109,7 +109,6 @@
         <template v-if="unknown">

                 {{error.raw}}
-                <location :location="error.location"></location>

         </template>
     '''


Set 'name' on editor view based on field key

--- src/views/ui/form/editors/factory.coffee
+++ src/views/ui/form/editors/factory.coffee
@@ -9,4 +9,5 @@
     new fieldClass
       controller: options.controller
       presenter: field
+      name: field.get 'key'
       fieldStyle: options.fieldStyle


Load waypoints on home page

Load waypoints on home page

--- js/outer.coffee
+++ js/outer.coffee
@@ -133,8 +133,17 @@
       $.getScript "/assets/js/vendor/jquery.placeholder.js", =>
         $("input, textarea").placeholder()

+  waypoints = =>
+    if !$.waypoints?
+      $.getScript "/assets/js/vendor/waypoints-1.1.7.min.js", =>

-  home.lib = placeholder
+    else
+
+
+  home.lib = =>
+    placeholder()
+    waypoints()
+
   about.lib = placeholder
   docs.lib = highlight


Consistency

Consistency

--- sync.coffee
+++ sync.coffee
@@ -18,7 +18,7 @@
     @model_type._sync = @
     @model_type._schema = new Schema(@model_type)

-    @request = @superagent or require 'superagent'
+    @request = require 'superagent'

     ###################################
     # TEST: override request


refactor(user-agent): remove throw when sdk version is missing

should not throw when sdk version is missing

--- src/coffee/helpers/user-agent.coffee
+++ src/coffee/helpers/user-agent.coffee
@@ -4,7 +4,6 @@
 module.exports = (pkgName, pkgVersion) =>
   throw new Error 'Package name and version is required' unless pkgName and pkgVersion
   sdkPkgVersion = getPkg('sphere-node-sdk').version
-  throw new Error "'sphere-node-sdk' is required to generate the user-agent" unless sdkPkgVersion
   nodeInfo = "Node.js/#{process.version}"
   runtimeInfo = "#{platform()}; #{arch()}"
   moduleInfo = "#{pkgName}/#{pkgVersion}"


new(LandingPage): small changes on LandingExplain

--- src/components/LandingExplain.coffee
+++ src/components/LandingExplain.coffee
@@ -51,8 +51,6 @@
         img src: require "../img/tutorial/5.jpg"
         img src: require "../img/tutorial/6.jpg"

-      div style: overflow: "visible",
-        img className: "pure-img", style: {width: "100%"}, src: require "../img/postkarte.jpg"

       h2 null, "Die Vision von morgen"
       p null,
@@ -66,7 +64,9 @@
         br null
         "Das Ziel: eine menschliche Zukunft."

-      hr null
+      div style: overflow: "visible",
+        img className: "pure-img", style: {width: "100%"}, src: require "../img/postkarte.jpg"
+
       h2 null,
         "Möchtest du dabei sein?",
       div style: float: "left", paddingTop: "1px",
@@ -95,7 +95,7 @@
       h2 null, "Das Team von morgen"
       p null,
         "Das Team von morgen ist so bunt wie der Wandel. Ehemalige entwicklungspolitische Freiwillige, Studierende, Wandelgestalter*innen und Stipendiaten - Menschen, die sich ehrenamtlich für die Gesellschaft von morgen einsetzen wollen."
-
+
       div className: "team",
         div className: "teamMember",
           div className: "circle yellow",
@@ -107,37 +107,56 @@
             a href:"http://www.ideenhochdrei.org/de/verein/kennenlernen/menschen/helmut-wolmann/",
               img className: "teamImage", src: require "../img/team/Helmut.jpg"
           div className: "team-member-name", "Helmut Wolman"
-          div null, "Vorstand von Ideen³ e.V."
-          div null, "Network & Development"
+          div null, "Vorstand Ideen³ e.V."
+          div null, "Projektentwicklung"
+        div className: "teamMember",
+          div className: "circle berry",
+            img className: "teamImage", src: require "../img/team/David.jpg"
+          div className: "team-member-name", "David Ziegler"
+          div null, "Softwareentwicklung"
         div className: "teamMember",
           div className: "circle green",
             img className: "teamImage", src: require "../img/team/Lisa.jpg"
           div className: "team-member-name", "Lisa Stehr"
-          div null, "Promoviert in Psychologie"
-          div null, "Funding & Strategy"
-         div className: "teamMember",
-          div className: "circle berry",
-            img className: "teamImage", src: require "../img/team/David.jpg"
-          div className: "team-member-name", "David Ziegler"
-          div null, "Software Development"
+          div null, "Politikwissenschaftlerin"
+          div null, "Strategie und Wirkung "
+        div className: "teamMember",
+          div className: "circle green",
+           a href:"http://martenroebel.de/",
+             img className: "teamImage", src: require "../img/team/Marten.jpg"
+          div className: "team-member-name", "Marten Röbel"
+          div null, "Achtsamkeit und "
+          div null, "Change-Psychologie"
         div className: "teamMember",
           div className: "circle green",
            a href:"https://slowtec.de/team.html#",
              img className: "teamImage", src: require "../img/team/Markus.jpg"
           div className: "team-member-name", "Markus Kohlhase"
           div null, "slowtec GmbH"
-          div null, "Software Development"
+          div null, "Softwareentwicklung"
         div className: "teamMember",
           div className: "circle pink",
             img className: "teamImage", src: require "../img/team/placeholder_1.png"
           div className: "team-member-name", "Florian Jostock"
-          div null, "Software Development"
+          div null, "Beratung Softwareentwicklung"
+        div className: "teamMember",
+          div className: "circle blue",
+            img className: "teamImage", src: require "../img/team/Louisa.jpg"
+          div className: "team-member-name", "Louisa Pieper"
+          div null, "PR und Marketing"
+          div null, "Regionalpilotin Köln"
         div className: "teamMember",
           div className: "circle blue",
             img className: "teamImage", src: require "../img/team/Anja.jpg"
           div className: "team-member-name", "Anja Dannemann"
           div null, "Designerin"
           div null, "Graphic Design"
+        div className: "teamMember",
+          div className: "circle berry",
+            img className: "teamImage", src: require "../img/team/Xueqian.jpg"
+          div className: "team-member-name", "Xueqian Chen"
+          div null, "Unternehmensberaterin"
+          div null, "PR & Marketing"
         div className: "teamMember",
           div className: "circle blue",
             img className: "teamImage", src: require "../img/team/Ben.jpg"
@@ -151,16 +170,10 @@
           div null, "Netzwerk-Ass"
           div null, "Business & Finance"
         div className: "teamMember",
-          div className: "circle berry",
-            img className: "teamImage", src: require "../img/team/Xueqian.jpg"
-          div className: "team-member-name", "Xueqian Chen"
-          div null, "Unternehmensberaterin"
-          div null, "PR & Marketing"
-        div className: "teamMember",
           div className: "circle blue",
             img className: "teamImage", src: require "../img/team/placeholder_4.png"
-          div className: "team-member-name", "Wir freuen uns über neue"
-          div null, "Teammitglieder!"
+          div null, "Wir freuen uns über"
+          div className: "team-member-name", "neue Teammitglieder!"

       hr null

@@ -170,7 +183,9 @@
         a href:"http://slowtec.de",
           img className: "partnerLogo", src: require "../img/slowtec.png"

-      h2 null, "Unsere Partner",
+      hr null
+
+      h2 null, "Unsere Partner"
         a href:"https://www.engagement-global.de/rueckkehrende.html",
           img className: "partnerLogo", src: require "../img/EngagementGlobal.jpg"
         a href:"http://www.bmz.de/",


Remove innerHtml

Remove innerHtml

--- src/components/scoreboard_row.coffee
+++ src/components/scoreboard_row.coffee
@@ -2,7 +2,7 @@
 Cricket = require '../constants/cricket'
 { connect } = require 'react-redux'

-{ div, td, tr } = React.DOM
+{ div, span, td, tr } = React.DOM

 ScoreboardRow = React.createClass
   displayName: 'ScoreboardRow'
@@ -14,27 +14,25 @@
       when 2 then 'X'
       else
         points -= 3
-        output = String.fromCharCode('9421')
+        output = [span {}, String.fromCharCode('9421')]
         while points >= 5
-          output += '| | | |'
           points -= 5
+          output.push(span {className: 'tally'}, '| | | |')
         while points > 0
-          output += ' |'
           points--
-        output
+          output.push(span {}, ' |')
+        div {}, output

   render: ->
     tr {},
       td {},
-        div
-          className: 'score-ticks right'
-          dangerouslySetInnerHTML: {__html: @pointsToSymbols(@props.teamPoints[@props.teams.get(0).get('id')])}
+        div {className: 'score-ticks right'},
+          @pointsToSymbols(@props.teamPoints[@props.teams.get(0).get('id')])
       td {},
         div {className: 'middle'}, Cricket[@props.points]
       td {},
-        div
-          className: 'score-ticks left'
-          dangerouslySetInnerHTML: {__html: @pointsToSymbols(@props.teamPoints[@props.teams.get(1).get('id')])}
+        div {className: 'score-ticks left'},
+          @pointsToSymbols(@props.teamPoints[@props.teams.get(1).get('id')])

 mapStateToProps = (state) ->
   teams: state.team.get 'teams'


Protect against NaN values

Protect against NaN values

This fixes the frozen drones during crashing onto water

--- src/lib/script_modules/entity.coffee
+++ src/lib/script_modules/entity.coffee
@@ -408,7 +408,7 @@

     deltaX = if settings.x? then Math.abs(settings.x - @entity.x) else 0
     deltaY = if settings.y? then Math.abs(settings.y - @entity.y) else 0
-    delta = Math.sqrt((deltaX ** 2) + (deltaY ** 2))
+    delta = Math.sqrt(((deltaX || 0) ** 2) + ((deltaY || 0) ** 2))
     return Promise.resolve() if delta == 0

     return new Promise((resolve) =>


Update Arnie Quotes to use the new SBFVGS Arnie emoji

--- scripts/arnie-quotes.coffee
+++ scripts/arnie-quotes.coffee
@@ -50,5 +50,5 @@
 module.exports = (robot) ->
     robot.hear /(^|\s)(arnold|arnie)(\s|$|[\W])/ig, (msg) ->
         quote = msg.random arnie_quotes
-        quote += ' <:arnold:298646907658436609>'
+        quote += ' <:sbfvgsArnie:437350773744205844>'
         msg.send quote


エラーが出るのを修正

エラーが出るのを修正

--- scripts/help-message.coffee
+++ scripts/help-message.coffee
@@ -5,7 +5,7 @@

 module.exports = (robot) ->
-  robot.respond /(help|-h|-help|ヘルプ|詳細)/i, (msg) ->
+  robot.respond /(help|h|ヘルプ|詳細)/i, (msg) ->
     text = """
      バスの混み具合を確認する
      chocobot bus or バス


enable webfinger

enable webfinger

--- proxy.coffee
+++ proxy.coffee
@@ -131,6 +131,8 @@
         @app.get '/logout', @logoutAction
         @app.get '/authenticated', @authenticatedAction
         @app.get '/status', @statusAction
+        @app.get '/.well-known/host-meta.?:ext', @webfingerHostMeta
+        @app.get '/.well-known/webfinger', @webfingerAccount

         @app.all '/public/:name/*', @redirectPublicAppAction
         @app.all '/apps/:name/*', @redirectAppAction
@@ -495,3 +497,52 @@
         statusChecker.checkAllStatus (err, status) ->
             if err then res.send 500
             else res.send status
+
+    # Return the host meta file
+    # support only JSON format
+    webfingerHostMeta: (req, res) =>
+        return res.send 404 unless req.params.ext is 'json'
+
+        res.header 'Access-Control-Allow-Origin', '*'
+        res.header 'Access-Control-Allow-Credentials', true
+        res.header 'Access-Control-Allow-Methods', 'GET'
+
+        host = 'https://' + req.get 'host'
+        template = "#{host}/webfinger/json?resource={uri}"
+
+        hostmeta = links:
+            rel: 'lrdd'
+            template: template
+
+        res.send hostmeta
+
+
+    # return the account file
+    # @TODO : let the user add more information here
+    # OpenID provider, public email, public tel, ...
+    webfingerAccount: (req, res) =>
+        host = 'https://' + req.get 'host'
+        OAUTH_VERSION = 'http://tools.ietf.org/html/rfc6749#section-4.2'
+        PROTOCOL_VERSION = 'draft-dejong-remotestorage-01'
+
+        res.header 'Access-Control-Allow-Origin', '*'
+        res.header 'Access-Control-Allow-Credentials', true
+        res.header 'Access-Control-Allow-Methods', 'GET'
+
+        accountinfo = links: []
+
+        if @routes['remotestorage']
+
+            link =
+                href: "#{host}/public/remotestorage/storage"
+                rel: 'remotestorage'
+                type: PROTOCOL_VERSION
+                properties:
+                    'auth-method': OAUTH_VERSION
+                    'auth-endpoint': "#{host}/apps/remotestorage/oauth/"
+
+            link.properties[OAUTH_VERSION] = link.properties['auth-endpoint']
+
+            accountinfo.links.push link
+
+        res.send accountinfo


print "IDE" instead of "an IDE" in log

print "IDE" instead of "an IDE" in log

--- proxy.coffee
+++ proxy.coffee
@@ -36,7 +36,7 @@
 b64d = (s) -> '' + Buffer s, 'base64'
 tag = (tagName, xml) -> (///^[^]*<#{tagName}>([^<]*)</#{tagName}>[^]*$///.exec xml)?[1]
 extend = (a...) -> r = {}; (for x in a then for k, v of x then r[k] = v); r
-addr = (socket) -> socket?.request?.connection?.remoteAddress || 'an IDE' # format a socket's remote address
+addr = (socket) -> socket?.request?.connection?.remoteAddress || 'IDE' # format a socket's remote address

 ipAddresses = []
 try


fix bug on consdecutive kicks

fix bug on consdecutive kicks

--- src/frontend/connections.coffee
+++ src/frontend/connections.coffee
@@ -180,6 +180,7 @@
     removePeer   : (peerId) =>
         do @peers[peerId].close
         @peers[peerId] = null
+        delete @peers[peerId]

     toggleCamera : () =>
         @userMedia['video'] = !@userMedia['video']


remove grunt

remove grunt

--- gruntFile.coffee
+++ gruntFile.coffee
@@ -1,25 +0,0 @@
-module.exports = (grunt) ->
-  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks)
-
-  grunt.initConfig
-    coffee:
-      options:
-        bare: true
-      dist:
-        files: [
-          expand: true
-          cwd: 'lib/'
-          src: ['**/*.coffee']
-          dest: 'assets/'
-          ext: '.js'
-        ]
-
-    clean:
-      all:
-        src: "assets/**/*"
-
-
-  grunt.registerTask "default", [
-    "clean:all"
-    "coffee"
-  ]


Add js live reload to grunt file

Add js live reload to grunt file

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -82,6 +82,15 @@
 						cert: grunt.file.read('//Applications/MAMP/certificates/twd.local.crt')
 					}
 				files: ['css/**/*']
+			js:
+				options:
+					livereload: {
+						port: 9000
+						key: grunt.file.read('//Applications/MAMP/certificates/twd.local.key')
+						cert: grunt.file.read('//Applications/MAMP/certificates/twd.local.crt')
+					}
+				files: ['js/**/*', ]
+				tasks: ['concat']
 			app:
 				options:
 					livereload: {


[grunt] a bit nicer codestyle for `i18n.locales`

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -76,11 +76,13 @@

     i18n:
       locales: [
-          locale: 'en-US', url: 'en', rtl: false
+          locale: 'en-US'
+          url: 'en'
+          rtl: false
         ,
-          locale: 'ru-RU', url: 'ru', rtl: false
-        ,
-          locale: 'ar', url: 'ar', rtl: true
+          locale: 'ru-RU'
+          url: 'ru'
+          rtl: false
       ]
       baseLocale: 'en-US'
       getLocales: ->


fix(tasks): ensure NotifyPluginsOfTask gets properly serialized

Fixes:
https://sentry.nylas.com/sentry/edgehill/group/31200/

--- src/global/nylas-exports.coffee
+++ src/global/nylas-exports.coffee
@@ -104,6 +104,7 @@
   @lazyLoadAndRegisterTask "SyncbackMetadataTask", 'syncback-metadata-task'
   @lazyLoadAndRegisterTask "SyncbackDraftFilesTask", 'syncback-draft-files-task'
   @lazyLoadAndRegisterTask "ReprocessMailRulesTask", 'reprocess-mail-rules-task'
+  @lazyLoadAndRegisterTask "NotifyPluginsOfSendTask", 'notify-plugins-of-send-task'

   # Stores
   # These need to be required immediately since some Stores are


docs and local store tests

docs and local store tests

--- sync.coffee
+++ sync.coffee
@@ -121,14 +121,19 @@

 		# Tracks changes to the pending items in the send_queue.  Used by the `notify_pending` callback.
 		@_last_pending = null
+
+		# Timout delay for resending failed change events.
+		# The backoff increases with each failure, bounded by min and max.
 		@_send_backoff = 15000
 		@_backoff_max = 120000
 		@_backoff_min = 15000

+		@initFromLocalStorage = 0
+
 		console.log "#{@namespace}: bucket created: opts: #{JSON.stringify(@options)}"

 	# ### `uuid()`
-	# Generate a UUID (not RFC 4122 4.4)
+	# Generate a UUID (not RFC 4122 4.4) using a...
 	uuid: (n) ->
 		n = n || 8
 		s = @S4()
@@ -136,13 +141,17 @@
 			s += @S4()
 		return s

-	# using a random 4-hexit generator.
+	# Random 4-hexit generator.
 	S4: ->
 		(((1+Math.random())*0x10000)|0).toString(16).substring(1)

+	# ### `now()`
+	# Timestamp in milliseconds.
 	now: ->
 		new Date().getTime()

+	# ### `on()`
+	# Registers event handlers.  Only one callback per event type is allowed.
 	on: (event, callback) =>
 		if event in @cb_events
 			@cbs[event] = callback
@@ -268,6 +277,19 @@
 	start: =>
 		console.log "#{@space}: started initialized: #{@initialized} authorized: #{@authorized}"
 		@namespace = "#{@username}:#{@space}"
+
+		if @loaded
+			@initFromLocalStorage = @loaded
+
+		console.error @initFromLocalStorage
+
+		if @loaded
+			console.error "LOADED", @loaded
+			for own id, entity of @data.store
+				@_notify_client id, entity.object, entity.version
+			@loaded = 0
+
+		console.error @initFromLocalStorage
 		@started = true
 		@first = false
 		if not @authorized
@@ -288,8 +310,12 @@
 					name:   @name
 					clientid: @clientid
 					build: @s.bversion
-				if not @initialized
+				if @initFromLocalStorage
+					opts.cmd = "cv:#{@data.last_cv}"
+				else if not @initialized
 					opts.cmd = index_query
+				console.error opts,  @initFromLocalStorage
+				@initFromLocalStorage = 0
 				@send("init:#{JSON.stringify(opts)}")
 				console.log "#{@name}: sent init #{JSON.stringify(opts)} waiting for auth"
 			else
@@ -449,6 +475,7 @@
 			@data.store[id]['id'] = id
 			@data.store[id]['object'] = data
 			@data.store[id]['version'] = parseInt(version)
+			@_save_entity id

 			notify_cb id, data_copy, version
 			@notify_index[id] = true
@@ -477,7 +504,7 @@
 	# diff: the newly received incoming diff (orig_object + diff = new_object)
 	_notify_client: (key, new_object, orig_object, diff, version) =>
 		console.log "#{@name}: _notify_client(#{key}, #{new_object}, #{orig_object}, #{JSON.stringify(diff)})"
-		if not @cb_l?
+		if not @cb_l? or not @started
 			console.log "#{@name}: no get callback, notifying without transform"
 			@cb_n key, new_object, version
 			return
@@ -578,15 +605,15 @@
 		if s_data['last']? and @jd.entries(s_data['last']) > 0
 			if @jd.equals s_data['object'], s_data['last']
 				s_data['last'] = null
-				@_remove_entity id
+#				@_remove_entity id
 				return false

 		change = @_make_change id
 		if change?
 			s_data['change'] = change
 			@_queue_change change
-		else
-			@_remove_entity id
+#		else
+#			@_remove_entity id
 		return true

 	update: (id, object) =>
@@ -681,20 +708,31 @@
 #        console.log "_make_change(#{id}) returning: #{JSON.stringify(change)}"
 		return change

+	# ### `_queue_change()`
+	# Send a change to the server, and queue it to resend on failure.
 	_queue_change: (change) =>
 		if not change?
 			return

 		console.log "_queue_change(#{change['id']}:#{change['ccid']}): sending"
+		# Add to the pending queue.  It will be removed from the queue when the
+		# change is successfully processed by the server
 		@data.send_queue.push change
+		# Request: Change, change data
 		@send("c:#{JSON.stringify(change)}")
 		@_check_pending()

+		# Set a new timeout to resend the queue using...
 		if @data.send_queue_timer?
 			clearTimeout(@data.send_queue_timer)

+
 		@data.send_queue_timer = setTimeout @_send_changes, @_send_backoff

+	# ### `_send_changes()`
+	# Runs on a timeout to send any pending changes.
+	# Queues each change to rerun in case of failure.
+	# For each failure, the timeout delay increases.
 	_send_changes: =>
 		if @data.send_queue.length is 0
 			console.log "#{@name}: send_queue empty, done"
@@ -746,25 +784,28 @@
 			if 'error' of change
 				switch change['error']
 					when 412
-    					console.log "#{@name}: on_changes(): empty change, dont check"
-    					idx = check_updates.indexOf(change['id'])
-    					if idx > -1
-        					check_updates.splice(idx, 1)
+						console.log "#{@name}: on_changes(): empty change, dont check"
+						idx = check_updates.indexOf(change['id'])
+						if idx > -1
+							check_updates.splice(idx, 1)
 					when 409
-    					console.log "#{@name}: on_changes(): duplicate change, ignoring"
+						console.log "#{@name}: on_changes(): duplicate change, ignoring"
 					when 405
-    					console.log "#{@name}: on_changes(): bad version"
-    					if change['id'] of @data.store
-        					@data.store[change['id']]['version'] = null
-    					reload_needed = true
+						console.log "#{@name}: on_changes(): bad version"
+						if change['id'] of @data.store
+							@data.store[change['id']]['version'] = null
+							@_save_entity change['id']
+						reload_needed = true
 					when 440
-    					console.log "#{@name}: on_change(): bad diff, sending full object"
-    					@data.store[id]['sendfull'] = true
+						console.log "#{@name}: on_change(): bad diff, sending full object"
+						@data.store[id]['sendfull'] = true
+						@_save_entity id
 					else
-    					console.log "#{@name}: error for last change, reloading"
-    					if change['id'] of @data.store
-        					@data.store[change['id']]['version'] = null
-    					reload_needed = true
+						console.log "#{@name}: error for last change, reloading"
+						if change['id'] of @data.store
+							@data.store[change['id']]['version'] = null
+							@_save_entity change['id']
+						reload_needed = true
 			else
 				op = change['o']
 				if op is '-'
@@ -778,36 +819,38 @@
 				else if op is 'M'
 					s_data = @data.store[id]
 					if ('sv' of change and s_data? and s_data['version']? and s_data['version'] == change['sv']) or !('sv' of change) or (change['ev'] == 1)
-    					if not s_data?
-        					@data.store[id] =
-            					'id'        :   id
-            					'object'    :   {}
-            					'version'   :   null
-            					'last'      :   null
-            					'change'    :   null
-            					'check'     :   null
-        					s_data = @data.store[id]
+						if not s_data?
+							@data.store[id] =
+								'id'        :   id
+								'object'    :   {}
+								'version'   :   null
+								'last'      :   null
+								'change'    :   null
+								'check'     :   null
+							@_save_entity id
+							s_data = @data.store[id]
 #                        console.log "#{@name}: processing modify for #{JSON.stringify(s_data)}"
-    					orig_object = @jd.deepCopy s_data['object']
-    					s_data['object'] = @jd.apply_object_diff s_data['object'], change['v']
-    					s_data['version'] = change['ev']
-
-    					new_object = @jd.deepCopy s_data['object']
-
-    					if not ('local' of change)
+						orig_object = @jd.deepCopy s_data['object']
+						s_data['object'] = @jd.apply_object_diff s_data['object'], change['v']
+						s_data['version'] = change['ev']
+
+						new_object = @jd.deepCopy s_data['object']
+
+						if not ('local' of change)
 #                            setTimeout do(change, new_object, orig_object) =>
 #                                => @_notify_client change['id'], new_object, orig_object, change['v']
-        					@_notify_client change['id'], new_object, orig_object, change['v'], change['ev']
+							@_notify_client change['id'], new_object, orig_object, change['v'], change['ev']
 					else if s_data? and s_data['version']? and change['ev'] <= s_data['version']
-    					console.log "#{@name}: old or duplicate change received, ignoring, change.ev=#{change['ev']}, s_data.version:#{s_data['version']}"
+						console.log "#{@name}: old or duplicate change received, ignoring, change.ev=#{change['ev']}, s_data.version:#{s_data['version']}"
 					else
-    					if s_data?
-        					console.log "#{@name}: version mismatch couldnt apply change, change.ev:#{change['ev']}, s_data.version:#{s_data['version']}"
-    					else
-        					console.log "#{@name}: version mismatch couldnt apply change, change.ev:#{change['ev']}, s_data null"
-    					if s_data?
-        					@data.store[id]['version'] = null
-    					reload_needed = true
+						if s_data?
+							console.log "#{@name}: version mismatch couldnt apply change, change.ev:#{change['ev']}, s_data.version:#{s_data['version']}"
+						else
+							console.log "#{@name}: version mismatch couldnt apply change, change.ev:#{change['ev']}, s_data null"
+						if s_data?
+							@data.store[id]['version'] = null
+							@_save_entity id
+						reload_needed = true
 				else
 					console.log "#{@name}: no operation found for change"
 				if not reload_needed


Added some restaurants from Espoo

Added some restaurants from Espoo

--- src/hubot-lunch-at-amica.coffee
+++ src/hubot-lunch-at-amica.coffee
@@ -40,6 +40,13 @@
         'smarthouse': 3498
         'vtt': 3587
         'vtt-oulu': 3587
+        'kara3': 3205
+        'karaportti3': 3205
+        'vtt-espoo': 3588
+        'micronova': 3588
+        'micro': 3588
+        'aalto': 3579
+        'vm5': 3579

 stripMenu = (json) ->
   menu = for item in json["SetMenus"]


fix margin-top behaviour in arrowScroller

--- src/ioiojs/arrowScroller.coffee
+++ src/ioiojs/arrowScroller.coffee
@@ -72,6 +72,7 @@
         .css('height',height)
         .css('margin-left',width + margin)
         .css('cursor','pointer')
+        .css('margin-top',-height)
         .click(doScrollLeft)

       element
@@ -80,7 +81,6 @@
         .after(arrowRight)

       if (true is setttings.marginTop)
-        arrowRight.css('margin-top',-height)
         element.css('margin-top',-height)


Updated iframe

Updated iframe

--- src/javascript/generator.coffee
+++ src/javascript/generator.coffee
@@ -10,7 +10,7 @@
   url = "http://adampash.com/now_and_then/?img1=#{img1}&img2=#{img2}"
   $('.url a').attr('href', url).text(url)
   iframe = """
-    <iframe class="custom" style="border:none;" src="#{url}" height="265" width="100%">
+    <iframe class="custom" style="border:none;" src="#{url}" height="265" width="636">
     </iframe>
   """
   $('#embed').val(iframe)


implement missile attack and terrain/tank defense

--- tank.coffee
+++ tank.coffee
@@ -3,7 +3,22 @@

 class MapArea2D
   constructor: (@x1, @y1, @x2, @y2) ->
-  has_intersection: (area) ->
+  valid: () ->
+    @x2 > @x1 and @y2 > @y1
+  intersect: (area) ->
+    new MapArea2D(_.max([area.x1, @x1]), _.max([area.y1, @y1]), _.min([area.x2, @x2]), _.min([area.y2, @y2]))
+  sub: (area) ->
+    intersect_area = @intersect(area)
+    _.select([
+      new MapArea2D(@x1, @y1, @x2, intersect_area.y1),
+      new MapArea2D(@x1, intersect_area.y2, @x2, @y2),
+      new MapArea2D(@x1, intersect_area.y1, intersect_area.x1, intersect_area.y2),
+      new MapArea2D(intersect_area.x2, intersect_area.y1, @x2, intersect_area.y2)
+    ], (candidate_area) -> candidate_area.valid())
+  collide: (area) ->
+    # fast judge first
+    return false if (@x2 <= area.x1 or @y2 <= area.y1 or @x1 >= area.x2 or @y1 >= area.y2)
+    # slow
     a = (@y2 - @y1) / (@x2 - @x1)
     b = (@x2 * @y1 - @x1 * @y2) / (@x2 - @x1)
     c = (@x2 * @y2 - @x1 * @y1) / (@x2 - @x1)
@@ -15,24 +30,20 @@
     (@y1 < (a * area.x2 + b) < @y2) or
     (@y1 < (c - a * area.x1) < @y2) or
     (@y1 < (c - a * area.x2) < @y2)
-  collide: (area) ->
-    # fast method
-    return false if (@x2 <= area.x1 or @y2 <= area.y1 or @x1 >= area.x2 or @y1 >= area.y2)
-    # slow method
-    @has_intersection(area)
-  joint: (area) ->
-    new MapArea2D(_.max(area.x1, @x1), _.max(area.y1, @y1), _.min(area.x2, @x2), _.min(area.y2, @y2))
-  space_sub: (area) ->
-    joint_area = @joint(area)
-    candidates = [
-      new MapArea2D(@x1, @y1, @x2, joint_area.y1),
-      new MapArea2D(@x1, joint_area.y2, @x2, @y2),
-      new MapArea2D(@x1, joint_area.y1, joint_area.x1, joint_area.y2),
-      new MapArea2D(joint_area.x2, joint_area.y1, @x2, joint_area.y2)
-    ]
-    _.select(candidates, (candidate_area) -> candidate_area.is_valid_area())
-  is_valid_area: () ->
-    @x2 > @x1 and @y2 > @y1
+  width: () ->
+    @x2 - @x1
+  height: () ->
+    @y2 - @y1
+  multiply: (direction, factor) ->
+    switch direction
+      when 0
+        new MapArea2D(@x1, @y1 - factor * @height(), @x2, @y2)
+      when 90
+        new MapArea2D(@x1, @y1, @x2 + factor * @width(), @y2)
+      when 180
+        new MapArea2D(@x1, @y1, @x2, @y2 + factor * @height())
+      when 270
+        new MapArea2D(@x1 - factor * @width(), @y1, @x2, @y2)

 class Map2D
   max_x: 520
@@ -71,20 +82,20 @@
       _.each(battle_field.all_battle_field_objects(), (object) -> object.render())
       _.each(battle_field.all_battle_field_objects(), (object) -> object.update())
     @canvas.timeline.start()
-  find_units_at: (area) ->
+  units_at: (area) ->
     _.select(@map_units, (map_unit) ->
       map_unit.area.collide(area)
     )
-  is_out_of_bound: (area) ->
+  out_of_bound: (area) ->
     area.x1 < 0 or area.x2 > @max_x or area.y1 < 0 or area.y2 > @max_y
-  space_available: (unit, area) ->
+  area_available: (unit, area) ->
     _.all(@map_units, (map_unit) =>
       (map_unit is unit) or
         map_unit.accept(unit) or
         not map_unit.area.collide(area)
     )

-  new_map_unit: (battle_field_object, area) ->
+  add_map_unit: (battle_field_object, area) ->
     map_unit_cls = @unit_map[battle_field_object.type()]
     map_unit = new map_unit_cls(this, battle_field_object, area)
     @canvas.addChild(map_unit.display_object)
@@ -112,7 +123,7 @@
     @default_width = @map.default_width
     @default_height = @map.default_height
     @gravity_point = @update_gravity_point()
-    @init_display()
+    @new_display()

   update_area: (@area) ->
     @gravity_point = @update_gravity_point()
@@ -120,7 +131,7 @@
   update_gravity_point: () ->
     new Point(@area.x1, @area.y1)

-  init_display: () ->
+  new_display: () ->
     @display_object = @map.canvas.display.sprite({
       frames: @current_frames(),
       image: @map.image,
@@ -152,9 +163,51 @@

   accept: (other_unit) -> @model.accept(other_unit.model)

-  update: () ->
-
-  fight_missile: (missile, destroy_area) -> 0
+  max_depend_point: 9
+  defend: (missile, destroy_area) -> 0
+
+class MapUnit2DForBrick extends MapUnit2D
+  current_frames: () -> [{x: 0, y: 240}]
+  defend: (missile, destroy_area) ->
+    # cut self into pieces
+    pieces = @area.sub(destroy_area)
+    _.each(pieces, (piece) =>
+      @model.battle_field.add_terrain(BrickTerrain, piece)
+    )
+    @model.destroy()
+    # return cost of destroy
+    1
+
+class MapUnit2DForIron extends MapUnit2D
+  current_frames: () -> [{x: 80, y: 240}]
+  defend: (missile, destroy_area) ->
+    return @max_depend_point if missile.power < 2
+    double_destroy_area = destroy_area.multiply(missile.direction, 1)
+    pieces = @area.sub(double_destroy_area)
+    _.each(pieces, (piece) =>
+      @model.battle_field.add_terrain(IronTerrain, piece)
+    )
+    @model.destroy()
+    2
+
+class MapUnit2DForIce extends MapUnit2D
+  layer: 0
+  current_frames: () -> [{x: 40, y: 240}]
+
+class MapUnit2DForGrass extends MapUnit2D
+  layer: 2
+  current_frames: () -> [{x: 120, y: 240}]
+
+class MapUnit2DForWater extends MapUnit2D
+  layer: 0
+  current_frames: () -> [{x: 160, y: 240}]
+
+class MapUnit2DForHome extends MapUnit2D
+  current_frames: () ->
+    if @model.is_defeated then [{x: 240, y: 240}] else [{x: 200, y: 240}]
+  defend: (missile, destroy_area) ->
+    @model.is_defeated = true
+    @max_depend_point

 class MovableMapUnit2D extends MapUnit2D
   origin_x: 'center'
@@ -186,7 +239,7 @@
     target_x = @area.x1 + offset_x
     target_y = @area.y1 + offset_y
     target_area = new MapArea2D(target_x, target_y, target_x + @width(), target_y + @height())
-    if @map.space_available(this, target_area)
+    if @map.area_available(this, target_area)
       @update_area(target_area)
       @update_display()
       true
@@ -215,78 +268,32 @@
     @area.y2 += offset
     @update_area(@area)

-class MapUnit2DForTank extends MovableMapUnit2D
-  missile_area: () ->
-    new MapArea2D(@gravity_point.x - @default_width/4,
-      @gravity_point.y - @default_height/4,
-      @gravity_point.x + @default_width/4,
-      @gravity_point.y + @default_height/4)
-
-class MapUnit2DForUserTankP1 extends MapUnit2DForTank
-  current_frames: () ->
-    switch @model.level
-      when 1 then [{x: 0, y: 0, d: 10}, {x:40, y: 0, d: 10}]
-      when 2 then [{x: 80, y: 0, d: 100}, {x:120, y: 0, d: 100}]
-      when 3 then [{x: 160, y: 0, d: 100}, {x:200, y: 0, d: 100}]
-
-class MapUnit2DForUserTankP2 extends MapUnit2DForTank
-  current_frames: () ->
-    switch @model.level
-      when 1 then [{x: 0, y: 40, d: 100}, {x:40, y: 40, d: 100}]
-      when 2 then [{x: 80, y: 40, d: 100}, {x:120, y: 40, d: 100}]
-      when 3 then [{x: 160, y: 40, d: 100}, {x:200, y: 40, d: 100}]
-
-class MapUnit2DForBrick extends MapUnit2D
-  current_frames: () -> [{x: 0, y: 240}]
-  fight_missile: (missile, destroy_area) ->
-    # cut self into pieces
-    pieces = @area.space_sub(destroy_area)
-    _.each(pieces, (piece) =>
-      @model.battle_field.add_terrain(BrickTerrain, piece)
-    )
-    @model.destroy()
-    # return cost of destroy
-    10
-
-class MapUnit2DForIce extends MapUnit2D
-  current_frames: () -> [{x: 40, y: 240}]
-
-class MapUnit2DForIron extends MapUnit2D
-  current_frames: () -> [{x: 80, y: 240}]
-
-class MapUnit2DForGrass extends MapUnit2D
-  current_frames: () -> [{x: 120, y: 240}]
-
-class MapUnit2DForWater extends MapUnit2D
-  current_frames: () -> [{x: 160, y: 240}]
-
-class MapUnit2DForHome extends MapUnit2D
-  current_frames: () ->
-    if @model.is_defeated then [{x: 240, y: 240}] else [{x: 200, y: 240}]
-
 class MapUnit2DForMissile extends MovableMapUnit2D
   current_frames: () -> [{x: 250, y: 330}]
-  update: () ->
+  move: (offset) ->
+    can_move = super(offset)
+    @attack() unless can_move
+    can_move
+  attack: () ->
     # if collide with other object, then explode
     destroy_area = @destroy_area()
     # START HERE
-    return @model.destroy() if @map.is_out_of_bound(destroy_area)
-
-    hit_map_units = @map.find_units_at(destroy_area)
+    return @model.destroy() if @map.out_of_bound(destroy_area)
+    hit_map_units = @map.units_at(destroy_area)
     _.each(hit_map_units, (unit) =>
-      @model.energy -= unit.fight_missile(this, destroy_area)
+      @model.energy -= unit.defend(@model, destroy_area)
     )
     @model.destroy() if @model.energy <= 0
   destroy_area: ->
     switch @direction
       when 0
-        new MapArea2D(@area.x1, @area.y1 - @default_height/4, @area.x2, @area.y1)
+        new MapArea2D(@area.x1 - @default_width/4, @area.y1 - @default_height/4, @area.x2 + @default_width/4, @area.y1)
       when 90
-        new MapArea2D(@area.x2, @area.y1, @area.x2 + @default_width/4, @area.y2)
+        new MapArea2D(@area.x2, @area.y1 - @default_height/4, @area.x2 + @default_width/4, @area.y2 + @default_height/4)
       when 180
-        new MapArea2D(@area.x1, @area.y2, @area.x2, @area.y2 + @default_height/4)
+        new MapArea2D(@area.x1 - @default_width/4, @area.y2, @area.x2 + @default_width/4, @area.y2 + @default_height/4)
       when 270
-        new MapArea2D(@area.x1 - @default_width/4, @area.y1, @area.x1, @area.y2)
+        new MapArea2D(@area.x1 - @default_width/4, @area.y1 - @default_height/4, @area.x1, @area.y2 + @default_height/4)
   destroy_display: () ->
     @display_object.width = 2 * @display_object.width
     @display_object.height = 2 * @display_object.height
@@ -298,8 +305,45 @@
     ]
     @display_object.startAnimation()
     setTimeout((() => @display_object.remove()), 800)
-
-class MapUnit2DForStupidTank extends MapUnit2DForTank
+  defend: (missile, destroy_area) ->
+    @model.destroy()
+    @max_depend_point
+
+class MapUnit2DForTank extends MovableMapUnit2D
+  missile_born_area: () ->
+    new MapArea2D(@gravity_point.x - @default_width/4,
+      @gravity_point.y - @default_height/4,
+      @gravity_point.x + @default_width/4,
+      @gravity_point.y + @default_height/4)
+
+class MapUnit2DForUserTank extends MapUnit2DForTank
+  defend: (missile, destroy_area) ->
+    return 0 if missile.parent is @model
+    @max_depend_point
+
+class MapUnit2DForEnemyTank extends MapUnit2DForTank
+  defend: (missile, destroy_area) ->
+    return 0 if missile.parent is @model
+    defend_point = _.min(@model.life, missile.power)
+    @model.life -= missile.power
+    @model.destroy() if @model.dead()
+    defend_point
+
+class MapUnit2DForUserTankP1 extends MapUnit2DForUserTank
+  current_frames: () ->
+    switch @model.level
+      when 1 then [{x: 0, y: 0, d: 10}, {x:40, y: 0, d: 10}]
+      when 2 then [{x: 80, y: 0, d: 100}, {x:120, y: 0, d: 100}]
+      when 3 then [{x: 160, y: 0, d: 100}, {x:200, y: 0, d: 100}]
+
+class MapUnit2DForUserTankP2 extends MapUnit2DForUserTank
+  current_frames: () ->
+    switch @model.level
+      when 1 then [{x: 0, y: 40, d: 100}, {x:40, y: 40, d: 100}]
+      when 2 then [{x: 80, y: 40, d: 100}, {x:120, y: 40, d: 100}]
+      when 3 then [{x: 160, y: 40, d: 100}, {x:200, y: 40, d: 100}]
+
+class MapUnit2DForStupidTank extends MapUnit2DForEnemyTank
   current_frames: () ->
     origin = switch @model.level
       when 1 then [{x: 0, y: 80, d: 100}, {x:40, y: 80, d: 100}]
@@ -308,13 +352,13 @@
       when 4 then [{x: 240, y: 80, d: 100}, {x:280, y: 80, d: 100}]
       when 5 then [{x: 240, y: 40, d: 100}, {x:280, y: 40, d: 100}]

-class MapUnit2DForFoolTank extends MapUnit2DForTank
+class MapUnit2DForFoolTank extends MapUnit2DForEnemyTank
   current_frames: () ->

-class MapUnit2DForStrongTank extends MapUnit2DForTank
+class MapUnit2DForStrongTank extends MapUnit2DForEnemyTank
   current_frames: () ->

-class MapUnit2DForFishTank extends MapUnit2DForTank
+class MapUnit2DForFishTank extends MapUnit2DForEnemyTank
   current_frames: () ->

 class BattleField
@@ -324,21 +368,21 @@
   terrains: [] # has_many terrains
   add_terrain: (terrain_cls, area) ->
     terrain = new terrain_cls(this)
-    terrain.view = @map.new_map_unit(terrain, area)
+    terrain.view = @map.add_map_unit(terrain, area)
     @terrains.push(terrain)
     terrain

   tanks: [] # has_many tanks
   add_tank: (tank_cls, area) ->
     tank = new tank_cls(this)
-    tank.view = @map.new_map_unit(tank, area)
+    tank.view = @map.add_map_unit(tank, area)
     @tanks.push(tank)
     tank

   missiles: [] # has_many missiles
   add_missile: (parent) ->
     missile = new Missile(this, parent)
-    missile.view = @map.new_map_unit(missile, parent.missile_area())
+    missile.view = @map.add_map_unit(missile, parent.missile_born_area())
     @missiles.push(missile)
     missile

@@ -392,7 +436,6 @@
   update: () ->
     # next round commands
     @commands = @commander.next_commands()
-    @view.update()

   turn: (@direction) ->
     @view.turn(@direction)
@@ -431,17 +474,14 @@
     else
       battle_field_object instanceof Missile
   type: -> "water"
-  layer: 0

 class IceTerrain extends Terrain
   accept: (battle_field_object) -> true
   type: -> "ice"
-  layer: 0

 class GrassTerrain extends Terrain
   accept: (battle_field_object) -> true
   type: -> "grass"
-  layer: 2

 class HomeTerrain extends Terrain
   is_defeated: false
@@ -450,40 +490,40 @@
 class Tank extends MovableBattleFieldObject
   accept: (battle_field_object) ->
     (battle_field_object instanceof Missile) and (battle_field_object.parent is this)
+
   life: 1
   set_life: (@life) ->
-  die: ->
-    @set_life(0)
-  is_dead: ->
-    @life <= 0
-
+  dead: -> @life <= 0
+  power: 1
   level: 1
-  set_level: (@level) ->
-
-  power: 1
-  set_power: (@power) ->
+  max_missile: 1
+  missiles: []
+  update_level: (@level) ->
+    switch @level
+      when 1
+        @power = 1
+        @max_missile = 1
+      when 2
+        @power = 2
+        @max_missile = 2

   ship: false
-  set_on_ship: (@ship) ->
+  update_ship: (@ship) ->

   guard: false
-  set_on_guard: (@guard) ->
-
-  max_missile: 5
-  missiles: []
+  update_guard: (@guard) ->
+
   fire: () ->
-    missile = @battle_field.add_missile(this)
-    @missiles.push(missile)
-  handle_fire: (command) ->
-    switch(command.type)
-      when "fire"
-        @fire() if _.size(@missiles) < @max_missile
+    if _.size(@missiles) < @max_missile
+      missile = @battle_field.add_missile(this)
+      @missiles.push(missile)

   integration: (delta_time) ->
     super(delta_time)
-    @handle_fire(command) for command in @commands
-
-  missile_area: () -> @view.missile_area()
+    @fire() for command in _.select(@commands, (command) -> command.type == "fire")
+
+  missile_born_area: () -> @view.missile_born_area()
+
   delete_missile: (missile) ->
     @missiles = _.without(@missiles, missile)

@@ -528,7 +568,7 @@
 class Missile extends MovableBattleFieldObject
   constructor: (@battle_field, @parent) ->
     @power = @parent.power
-    @energy = 10 * @power
+    @energy = @power
     @direction = @parent.direction
     @commander = new MissileCommander(this)
   speed: -> super() * 4
@@ -549,97 +589,114 @@
 class Commander
   constructor: (@battle_field_object) ->
     @direction = @battle_field_object.direction
-  direction_map: {
+    @commands = []
+  direction_action_map: {
     up: 0,
     down: 180,
     left: 270,
     right: 90
   }
-  next_commands: -> []
-  direction_command: (direction) ->
+  # calculate next commands
+  next: () ->
+
+  next_commands: ->
+    @commands = []
+    @next()
+    @commands
+  direction_changed: (action) ->
+    new_direction = @direction_action_map[action]
+    @direction != new_direction
+  turn: (action) ->
+    new_direction = @direction_action_map[action]
+    if @direction != new_direction
+      @direction = new_direction
+      @commands.push(@_direction_command(new_direction))
+  move: (action) ->
+    switch action
+      when "start"
+        @commands.push(@_start_move_command())
+      when "stop"
+        @commands.push(@_stop_move_command())
+  fire: () ->
+    @commands.push(@_fire_command())
+
+  # private methods
+  _direction_command: (direction) ->
     {
       type: "direction",
       params: { direction: direction }
     }
-  start_move_command: -> { type: "start_move" }
-  stop_move_command: -> { type: "stop_move" }
-  fire_command: -> { type: "fire" }
+  _start_move_command: -> { type: "start_move" }
+  _stop_move_command: -> { type: "stop_move" }
+  _fire_command: -> { type: "fire" }
+

 class UserCommander extends Commander
   constructor: (@battle_field_object, key_setting) ->
     super(@battle_field_object)
-    for action, key of key_setting
-      @key_map[key] = action
-    @clear_inputs()
-  key_map: {}
-  inputs: null
-  inputs_key_pressed: {
+    @key_map = {}
+    for key, code of key_setting
+      @key_map[code] = key
+    @reset_input()
+  reset_input: () ->
+    @inputs = { up: [], down: [], left: [], right: [], fire: [] }
+
+  key_status: {
     up: false,
     down: false,
     left: false,
     right: false
   }
-  clear_inputs: () ->
-    @inputs = {
-      up: [],
-      down: [],
-      left: [],
-      right: [],
-      fire: []
-    }
-  is_pressed: (action) ->
-    @inputs_key_pressed[action]
+  is_pressed: (key) ->
+    @key_status[key]
+  set_pressed: (key, bool) ->
+    @key_status[key] = bool
+
+  next: ->
+    @handle_key_up_key_down()
+    @handle_key_press()
+
+  handle_key_up_key_down: () ->
+    for key, types of @inputs
+      continue if _.size(types) == 0
+      switch (key)
+        when "fire"
+          @fire()
+        when "up", "down", "left", "right"
+          if @direction_changed(key)
+            @turn(key)
+            break
+          keyup = _.contains(@inputs[key], "keyup")
+          keydown = _.contains(@inputs[key], "keydown")
+          if keydown
+            @move("start")
+          else
+            @move("stop") if keyup
+    @reset_input()
+
+  handle_key_press: () ->
+    for key in ["up", "down", "left", "right"]
+      if @is_pressed(key)
+        @turn(key)
+        @move("start")
+    @fire() if @is_pressed("fire")
+
   add_key_event: (type, key_code) ->
+    return true if _.isUndefined(@key_map[key_code])
+    key = @key_map[key_code]
     switch type
       when "keyup"
-        return true if _.isUndefined(@key_map[key_code])
-        action = @key_map[key_code]
-        @inputs_key_pressed[action] = false
-        @inputs[action].push("keyup")
+        @set_pressed(key, false)
+        @inputs[key].push("keyup")
       when "keydown"
-        return true if _.isUndefined(@key_map[key_code])
-        action = @key_map[key_code]
-        @inputs_key_pressed[action] = true
-        @inputs[action].push("keydown")
-  commands: []
-  next_commands: ->
-    @commands = []
-    for action, key_actions of @inputs
-      continue if _.size(key_actions) == 0
-      switch (action)
-        when "up", "down", "left", "right"
-          break if @change_direction(action)
-          @do_move(action)
-        when "fire"
-          @commands.push(@fire_command())
-    for action in ["up", "down", "left", "right"]
-      if @is_pressed(action)
-        @change_direction(action)
-        @commands.push(@start_move_command())
-    @clear_inputs()
-    @commands
-  change_direction: (action) ->
-    new_direction = @direction_map[action]
-    if @direction != new_direction
-      @direction = new_direction
-      @commands.push(@direction_command(new_direction))
-      true
-    else
-      false
-  do_move: (action) ->
-    keyup = _.contains(@inputs[action], "keyup")
-    keydown = _.contains(@inputs[action], "keydown")
-    if keydown
-      @commands.push(@start_move_command())
-    else
-      @commands.push(@stop_move_command()) if keyup
+        @set_pressed(key, true)
+        @inputs[key].push("keydown")

 class EnemyAICommander extends Commander
-  next_commands: -> []
+  next: ->

 class MissileCommander extends Commander
-  constructor: (@battle_field_object) ->
-  next_commands: -> [@start_move_command()]
+  next: -> @move("start")

 class TerrainBuilder
   constructor: (@battle_field, @default_width, @default_height) ->


add explode effect

add explode effect

--- tank.coffee
+++ tank.coffee
@@ -144,8 +144,12 @@
   height: () -> @area.y2 - @area.y1

   destroy: () ->
+    @destroy_display()
+    @map.delete_map_unit(this)
+
+  destroy_display: () ->
     @display_object.remove()
-    @map.delete_map_unit(this)
+
   accept: (other_unit) -> @model.accept(other_unit.model)

   update: () ->
@@ -278,11 +282,22 @@
       when 0
         new MapArea2D(@area.x1, @area.y1 - @default_height/4, @area.x2, @area.y1)
       when 90
-        new MapArea2D(@area.x2, @area.y1, @area.y2 + @default_width/4, @area.y2)
+        new MapArea2D(@area.x2, @area.y1, @area.x2 + @default_width/4, @area.y2)
       when 180
         new MapArea2D(@area.x1, @area.y2, @area.x2, @area.y2 + @default_height/4)
       when 270
         new MapArea2D(@area.x1 - @default_width/4, @area.y1, @area.x1, @area.y2)
+  destroy_display: () ->
+    @display_object.width = 2 * @display_object.width
+    @display_object.height = 2 * @display_object.height
+    @display_object.frames = [
+      {x: 360, y: 320, d: 200},
+      {x: 120, y: 320, d: 200},
+      {x: 160, y: 320, d: 200},
+      {x: 200, y: 320, d: 200}
+    ]
+    @display_object.startAnimation()
+    setTimeout((() => @display_object.remove()), 800)

 class MapUnit2DForStupidTank extends MapUnit2DForTank
   current_frames: () ->
@@ -697,6 +712,7 @@

   battle_field.add_terrain(HomeTerrain, new MapArea2D(240, 480, 280, 520))

+  # set a reference for easier debug
   document.battle_field = battle_field

   console.log "init done"


work ongoing

work ongoing

--- src/drbg.iced
+++ src/drbg.iced
@@ -1,76 +1,66 @@

-{HMAC} = require './hmac'
+hmac = require './hmac'

+#====================================================================

 # Implements an HMAC_DRBG (NIST SP 800-90A) based on HMAC_SHA512
 # Supports security strengths up to 256 bits.
 # Parameters are based on recommendations provided by Appendix D of NIST SP 800-90A.
 exports.HMAC_DRBG = class HMAC_DRBG

+	check_entropy : (entropy) ->
+		if (entropy.sigBytes * 8 * 2) < (3 * @security_strength)
+			throw new Error "entropy must be at least %f bits." % (1.5 * @security_strength)
+		else if entropy.SigBytes * 8 > 1000
+			new WordArray entropy.words[0...31]
+
 	constructor : (entropy, personalization_string) ->
 		# Only run at the most secure strength
 		@security_strength = 256
-
-		if (entropy.length * 8 * 2) < (3 * @security_strength):
-			throw new Error "entropy must be at least %f bits." % (1.5 * @security_strength)
-
-		if entropy.length * 8 > 1000:
-			throw new Error "entropy cannot exceed 1000 bits"
-
+		entropy = @check_entropy entropy
 		@_instantiate entropy, personalization_string
-

 	# Just for convenience and succinctness
-	def _hmac (self, key, data):
-		return hmac.new (key, data, hashlib.sha256).digest ()
-
+	_hmac : (key, input) -> hmac.sign { key, input }

-	def _update (self, provided_data=None):
-		self.K = self._hmac (self.K, self.V + "\x00" + ("" if provided_data is None else provided_data))
-		self.V = self._hmac (self.K, self.V)
+	_update : (provided_data) ->
+		V = new WordArray [0], 1
+		V = V.concat provided_data if provided_data?
+		@K = @_hmac @K, @V.concat V
+		@V = @_hmac @K, @V

-		if provided_data is not None:
-			self.K = self._hmac (self.K, self.V + "\x01" + provided_data)
-			self.V = self._hmac (self.K, self.V)
-
+		if provided_data?
+			@K = @_hmac @K, @V.concat(new WordArray [1], 1).concat(provided_data)
+			@V = @_hmac @K, @V

-	_instantiate : (self, entropy, personalization_string) ->
-		seed_material = entropy + personalization_string
+	_instantiate : (entropy, personalization_string) ->
+		seed_material = entropy.concat personalization_string
 		n = 32
 		@K = WordArray.from_buffer new Buffer (0 for i in [0...n])
 		@V = WordArray.from_buffer new Buffer (1 for i in [0...n])
 		@_update seed_material
 		@reseed_counter = 1

-
-	def reseed (self, entropy):
-		if (len (entropy) * 8) < self.security_strength:
-			raise RuntimeError, "entropy must be at least %f bits." % (self.security_strength)
-
-		if len (entropy) * 8 > 1000:
-			raise RuntimeError, "entropy cannot exceed 1000 bits."
-
-		self._update (entropy)
-		self.reseed_counter = 1
+	reseed : (entropy) ->
+		entropy = @check_entropy entropy
+		@_update entropy
+		@reseed_counter = 1

-	def generate (self, num_bytes, requested_security_strength=256):
-		if (num_bytes * 8) > 7500:
-			raise RuntimeError, "generate cannot generate more than 7500 bits in a single call."
+	generate : (num_bytes) ->
+		if (num_bytes * 8) > 7500
+			throw new Error "generate cannot generate more than 7500 bits in a single call."

-		if requested_security_strength > self.security_strength:
-			raise RuntimeError, "requested_security_strength exceeds this instance's security_strength (%d)" % self.security_strength
+		if @reseed_counter >= 10000
+			throw new Error "Need a reseed!"

-		if self.reseed_counter >= 10000:
-			return None
+		temp = []

-		temp = ""
+		while temp.length*4 < num_bytes:
+			@V = @_hmac @K, @V
+			temp.push @V.words

-		while len (temp) < num_bytes:
-			self.V = self._hmac (self.K, self.V)
-			temp += self.V
-
-		self._update (None)
-		self.reseed_counter += 1
+		@_update()
+		@reseed_counter += 1

 		return temp[:num_bytes]


fix comment directive detection

fix comment directive detection

--- src/binding.coffee
+++ src/binding.coffee
@@ -209,6 +209,9 @@
             attr.attrName = attrName
             attr.element = args.element
             args.list.push attr
+        else if args.attr_type is 'M'
+            args.list.push base
+        return

     (attrName, args) ->
         if args.skip_attr.indexOf(attrName) >= 0
@@ -216,15 +219,18 @@

         directive = alight.directivePreprocessor attrName, args
         if directive.noNs
-            return addAttr attrName, args
+            addAttr attrName, args
+            return
         if directive.noDirective
-            return addAttr attrName, args, { noDirective:true }
+            addAttr attrName, args, { noDirective:true }
+            return

         args.list.push
             name: attrName
             directive: directive
             priority: directive.priority
             attrName: attrName
+        return

 sortByPriority = (a, b) ->
@@ -280,7 +286,7 @@

     d = list[0]
     if d.noDirective
-        throw "Directive not found: #{d.name}"
+        throw "Comment directive not found: #{dirName}"

     directive = d.directive
     env =


Update config.cson

Update config.cson

--- dotfiles/.atom/config.cson
+++ dotfiles/.atom/config.cson
@@ -12,6 +12,7 @@
       compactList: true
     ui:
       panelContrast: true
+  "autoclose-html": {}
   core:
     autoHideMenuBar: true
     ignoredNames: [
@@ -27,11 +28,12 @@
     telemetryConsent: "limited"
     themes: [
       "atom-material-ui"
-      "one-dark-syntax"
+      "atom-material-syntax"
     ]
   editor:
     fontSize: 13
     scrollPastEnd: true
+    softWrap: true
     zoomFontWhenCtrlScrolling: false
   "git-plus":
     general:


Some real queries.

Some real queries.

--- main.coffee
+++ main.coffee
@@ -159,25 +159,38 @@
 app.get '/about/', quickRender 'about'
 app.get '/register/', mustNotBeAuthed, quickRender 'register'
 app.get '/login/', mustNotBeAuthed, quickRender 'login'
+
 # experimental
-app.get '/dicks/', (request, response) ->
+app.get '/monster/:id/', (request, response) ->
 	options = request.uonline.basicOpts
 	options.instance = 'monster'
-	#...
-	options.name = 'Молодой лесной волк'
-	options.level = 5
-	options.power = 40
-	options.agility = 34
-	options.defense = 38
-	options.intelligence = 0
-	options.accuracy = 34
-	options.initiative_min = 35
-	options.health_max = 700
-	options.mana_max = 0
-	options.energy = 0
-	options.initiative_max = 65
-	#...
-	response.render 'monster', options
+	list = [
+		'name'
+		'level'
+		'power'
+		'agility'
+		'defense'
+		'intelligence'
+		'accuracy'
+		'initiative_min'
+		'health_max'
+		'mana_max'
+		'energy'
+		'initiative_max'
+	]
+
+	result = dbConnection.query.sync(
+		dbConnection
+		"SELECT #{list.join(', ')} FROM monster_prototypes WHERE id = $1"
+		[ request.param('id') ]
+	).rows
+
+	if result.length isnt 1
+		throw new Error '404'
+	else
+		for i in list
+			options[i] = result[0][i]
+		response.render 'monster', options

 # And the rest
@@ -342,7 +355,11 @@

 # Exception handling
 app.use (error, request, response, next) ->
-	code = if error.message is '404' then 404 else 500
+	code = 500
+	if error.message is '404'
+		code = 404
+	else
+		console.error error.stack
 	options = request.uonline.basicOpts
 	options.code = code
 	options.instance = 'error'


Fixes #365

Fixes #365

--- main.coffee
+++ main.coffee
@@ -241,8 +241,11 @@

 app.get '/action/go/:to', (request, response) ->
-	lib.game.changeLocation.sync null, dbConnection, request.uonline.basicOpts.userid, request.param('to')
-	response.redirect '/game/'
+	unless request.uonline.basicOpts.loggedIn
+		response.redirect '/login/'
+	else
+		lib.game.changeLocation.sync null, dbConnection, request.uonline.basicOpts.userid, request.param('to')
+		response.redirect '/game/'

 app.get '/action/attack', (request, response) ->


Last commit before major updates.

Last commit before major updates.

--- main.coffee
+++ main.coffee
@@ -73,17 +73,17 @@
   if isPlainObject(target) and key of target
     res[key] = rescueObjStruct val, target[key]
   else if isArray target
-      res = [target...]
-      if not res[key] or Number key
-        if isPlainObject val
-          res[key] = rescueObjStruct val, res[key]
-        else
-          res[key] = val
+    res = [target...]
+    if not res[key] or Number key
+      if isPlainObject val
+        res[key] = rescueObjStruct val, res[key]
       else
-        if isPlainObject val
-          res = rescueObjStruct val, res[key]
-        else
-          res.push val
+        res[key] = val
+    else
+      if isPlainObject val
+        res = rescueObjStruct val, res[key]
+      else
+        res.push val
   else
     res[key] = val


modify xcode project

modify xcode project

--- main.coffee
+++ main.coffee
@@ -58,6 +58,54 @@
     execSync "cp #{ resources }Podfile ."
     execSync 'pod install'

+    log 'Updating Xcode project'
+    for ext in ['m', 'h']
+      path = "./iOS/AppDelegate.#{ ext }"
+      execSync "cp #{ resources }AppDelegate.#{ ext } #{ path }"
+      editSync path, [[projNameRx, projName], [projNameHyphRx, projNameHyph]]
+
+    uuid1 = crypto
+      .createHash 'md5'
+      .update projName, 'utf8'
+      .digest('hex')[...24]
+      .toUpperCase()
+
+    uuid2 = uuid1
+      .split ''
+      .splice 7, 1, ((parseInt(uuid1[7], 16) + 1) % 16).toString(16).toUpperCase()
+      .join ''
+
+    editSync \
+      "#{ projName }.xcodeproj/project.pbxproj",
+      [
+        [
+          /OTHER_LDFLAGS = "-ObjC";/g
+          'OTHER_LDFLAGS = "${inherited}";'
+        ]
+        [
+          /\/\* End PBXBuildFile section \*\//
+          "\t\t#{ uuid2 } /* out in Resources */ =
+           {isa = PBXBuildFile; fileRef = #{ uuid1 } /* out */; };
+           \n/* End PBXBuildFile section */"
+        ]
+        [
+          /\/\* End PBXFileReference section \*\//
+          "\t\t#{ uuid1 } /* out */ = {isa = PBXFileReference; lastKnownFileType
+           = folder; name = out; path = ../target/out;
+           sourceTree = \"<group>\"; };\n/* End PBXFileReference section */"
+        ]
+        [
+          /main.jsbundle \*\/\,/
+          "main.jsbundle */,\n\t\t\t\t#{ uuid1 } /* out */,"
+        ]
+        [
+          /\/\* LaunchScreen.xib in Resources \*\/\,/
+          "/* LaunchScreen.xib in Resources */,
+           \n\t\t\t\t#{ uuid2 } /* out in Resources */,"
+        ]
+      ]
+
+
   catch e
     logErr e.message


Show empty listing for empty collections

--- web/scripts/aule.coffee
+++ web/scripts/aule.coffee
@@ -72,6 +72,7 @@
             beforeSend: (r) => addAuth r; options.beforeSend r
             data: options.data
             success: options.success
+            error: options.error
             statusCode: statusHandlers this
             dataType: 'json'
             type: options.type
@@ -96,6 +97,8 @@
                     if page < pages - 1 then data.pages.next = page: page + 1, label: page + 2
                     if pages >= @app.manyPages then data.pages.many = true
                 @show title, template, data, "#{ template }_#{ options.subtemplate }"
+            error: =>
+                @show title, template, options.merge, "#{ template }_#{ options.subtemplate }"

     # Authentication state
     @user = undefined


Fixed problem POSTing UserCodeProblems for non-admins.

--- server/user_code_problems/user_code_problem_handler.coffee
+++ server/user_code_problems/user_code_problem_handler.coffee
@@ -25,7 +25,9 @@
     ucp

   hasAccess: (req) ->
-    req.user?.isAdmin() or false
+    return true if req.user?.isAdmin()
+    return true if req.method.toLowerCase() is 'post'
+    false

   getByRelationship: (req, res, args...) ->
     return @sendForbiddenError res unless @hasAccess req


Remove duplicate canned text.

Remove duplicate canned text.

--- server/xy/attachment.coffee
+++ server/xy/attachment.coffee
@@ -27,7 +27,6 @@

   shouldBlockExecution: (move, user) ->
     if move.hasFlag("protect") && !move.isNonDamaging()
-      @battle.cannedText('PROTECT_CONTINUE', @pokemon)
       if move.hasFlag("contact") then user.boost(attack: -2, @pokemon)
       return true

@@ -45,7 +44,6 @@

   shouldBlockExecution: (move, user) ->
     if move.hasFlag("protect")
-      @battle.cannedText('PROTECT_CONTINUE', @pokemon)
       if move.hasFlag("contact") then user.damage(user.stat('hp') >> 3)
       return true


Display duration as years, months.

Display duration as years, months.

--- resources/assets/coffee/_classes/store-supporter-tag.coffee
+++ resources/assets/coffee/_classes/store-supporter-tag.coffee
@@ -41,6 +41,27 @@
     discount: ->
       raw = if @duration >= 12 then 46 else ((1 - (@price / @duration) / 4) * 100)
       Math.max(0, Math.round(raw, 0))
+    durationInYears: ->
+      { years: Math.floor(@duration / 12), months: Math.floor(@duration % 12) }
+    durationText: ->
+      obj = @durationInYears()
+      yearsText = switch obj.years
+                  when 0
+                    ''
+                  when 1
+                    "#{obj.years} year"
+                  else
+                    "#{obj.years} years"
+
+      monthsText = switch obj.months
+                  when 0
+                    ''
+                  when 1
+                    "#{obj.months} month"
+                  else
+                    "#{obj.months} months"
+
+      _.compact([yearsText, monthsText]).join(', ')
   }

   constructor: ->
@@ -162,8 +183,7 @@
     @el.querySelector('input[name="item[cost]"').value = obj.price
     @el.querySelector('input[name="item[extra_data][duration]"').value = obj.duration
     @priceElement.textContent = "USD #{obj.price}"
-    monthText = if (obj.duration == 1) then 'month' else 'months'
-    @durationElement.textContent = "#{obj.duration} #{monthText}"
+    @durationElement.textContent = obj.durationText()
     @discountElement.textContent = "save #{obj.discount()}%"

   updateUserDisplay: (user) =>


Remove unused variable

Remove unused variable

--- resources/assets/coffee/react/beatmapset-page/header.coffee
+++ resources/assets/coffee/react/beatmapset-page/header.coffee
@@ -62,7 +62,6 @@
         effect: -> $(this).fadeTo(250, 0)

   render: ->
-    dateFormat = 'MMM D, YYYY'
     favouriteButton =
       if @props.hasFavourited
         action: 'unfavourite'


Add registration panel to nav settings

Add registration panel to nav settings

--- src/navigation/settings.coffee
+++ src/navigation/settings.coffee
@@ -5,8 +5,9 @@
     profile: DEFAULT_PATTERN
     dashboard: DEFAULT_PATTERN
     task: DEFAULT_PATTERN
+    registration: DEFAULT_PATTERN
     progress: DEFAULT_PATTERN
     default: '{prefix}{base}'
     close: '{prefix}'

-module.exports = settings+module.exports = settings


remove unnecessary array cloning

remove unnecessary array cloning

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -159,7 +159,6 @@

 Bacon.fromArray = (values) ->
   assertArray values
-  values = cloneArray(values)
   i = 0
   new EventStream describe(Bacon, "fromArray", values), (sink) ->
     unsubd = false


websocket client: fix some log messages when connecting

--- src/smc-webapp/client_browser.coffee
+++ src/smc-webapp/client_browser.coffee
@@ -208,8 +208,8 @@
         conn.on 'reconnect scheduled', (opts) =>
             @emit("connecting")
             conn.removeAllListeners('data')
-            console.log('websocket -- reconnecting in #{opts.scheduled} ms')
-            console.log('websocket -- this is attempt #{opts.attempt} out of #{opts.retries}')
+            console.log("websocket -- reconnecting in #{opts.scheduled} ms")
+            console.log("websocket -- this is attempt #{opts.attempt} out of #{opts.retries}")

         conn.on 'incoming::pong', (time) =>
             #console.log("pong latency=#{conn.latency}")


Display a big fat alert warning if Primus isn't defined on load -- this is what often happens when you try to refresh and things are totally broken.  Let's just make it clear immediately

--- src/smc-webapp/client_browser.coffee
+++ src/smc-webapp/client_browser.coffee
@@ -19,12 +19,13 @@
 #
 ###############################################################################

+if not Primus?
+    alert("Library not fully built (Primus not defined) -- refresh your browser")
+
 $ = window.$
 _ = require('underscore')

 client = require('smc-util/client')
-
-# Primus = require('webapp-lib/primus/primus-engine.js')

 #{SMC_ICON_URL} = require('./misc_page')
 SMC_ICON_URL = require('salvus-icon.svg')


fixing bug in url generator

fixing bug in url generator

--- src/lib/url_generator.js.coffee
+++ src/lib/url_generator.js.coffee
@@ -18,7 +18,7 @@
     params.push "access_token=#{accessToken}"
     params.push "id=#{id}" if id?
     params.push "url_token=#{URLToken}" if URLToken?
-    params.push "&return_to=#{encodeURIComponent(returnTo)}" if returnTo?
+    params.push "return_to=#{encodeURIComponent(returnTo)}" if returnTo
     if params.length > 0
       url += "?#{params.join('&')}"
     url


remove = from grammar

remove = from grammar

It only follows lower= or upper= in range, and that case is already handled

--- grammars/language-stan.cson
+++ grammars/language-stan.cson
@@ -233,10 +233,6 @@
         'name': 'keyword.operator.logical.stan'
       }
       {
-        'match': '='
-        'name': 'keyword.operator.argument.stan'
-      }
-      {
         'match': "\\+|-|\\.?\\*|\\.?/|%|\\\\|\\^|'"
         'name': 'keyword.operator.arithmetic.stan'
       }


added setters for bounded center and position methods

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -59,8 +59,12 @@
   @methodOf Bounded#
   @returns {Point} The position of this object
   ###
-  position: ->
-    Point(I.x, I.y)
+  position: (newPosition) ->
+    if newPosition?
+      I.x = newPosition.x
+      I.y = newPosition.y
+    else
+      Point(I.x, I.y)

   ###*
   Does a check to see if this object is overlapping
@@ -208,8 +212,8 @@
   @methodOf Bounded#
   @returns {Point} The middle of the calling object
   ###
-  center: () ->
-    self.position()
+  center: (newCenter) ->
+    self.position(newCenter)

   ###*
   Return the circular bounds of the object. The circle is


Pass data context to onViewReady

Pass data context to onViewReady

--- konecty:nrr.coffee
+++ konecty:nrr.coffee
@@ -43,7 +43,7 @@
 				return HTML.Raw Blaze.toHTMLWithDataNonReactive Template[templateName], data

 			view.onViewReady ->
-				Template[templateName].onViewReady?.call view
+				Template[templateName].onViewReady?.call view, data

 		return view


Use mainController's feature check to enable video

--- client/app/lib/util/isVideoFeatureEnabled.coffee
+++ client/app/lib/util/isVideoFeatureEnabled.coffee
@@ -1,10 +1,6 @@
-checkFlag = require './checkFlag'
+kd = require 'kd'

 module.exports = isVideoFeatureEnabled = ->

-  return yes
+  not kd.singletons.mainController.isFeatureDisabled 'video-collaboration'

-  # for now we are only allowing super-admins to start video chat.
-  # After we release it publicly, we only need to change the logic here. ~Umut
-  # checkFlag 'super-admin'
-


Added Where-Object

Added Where-Object

--- grammars/powershell.cson
+++ grammars/powershell.cson
@@ -139,7 +139,7 @@
       name: "support.function.powershell"
     }
     {
-      match: "(?<!\\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!=-object)|while)|%|\\?)(?!\\w)"
+      match: "(?<!\\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!-object)|while)|%|\\?)(?!\\w)"
       name: "keyword.control.powershell"
     }
     {
@@ -240,7 +240,7 @@
       patterns: [
         {
           comment: "Verb-Noun pattern:"
-          match: "(?:(\\p{L}|\\d|_|-|\\\\|\\:)*\\\\)?\\b(?i:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)\\-.+?(?:\\.(?i:exe|cmd|bat|ps1))?\\b"
+          match: "(?:(\\p{L}|\\d|_|-|\\\\|\\:)*\\\\)?\\b(?i:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Where|Write)\\-.+?(?:\\.(?i:exe|cmd|bat|ps1))?\\b"
           name: "support.function.powershell"
         }
         {
@@ -688,4 +688,4 @@
       ]

   scopeName: "source.powershell"
-}+}