@__resizedWidth / @__resizedHeight → @__resizedSize Scale in @__resizedScale.

--- app/assets/javascripts/uploadcare/ui/crop/crop-widget.js.coffee
+++ app/assets/javascripts/uploadcare/ui/crop/crop-widget.js.coffee
@@ -137,8 +137,7 @@

     # Returns last selected area coords
     getCurrentCoords: ->
-      scaleX = @__resizedWidth / @__originalSize[0]
-      scaleY = @__resizedHeight / @__originalSize[1]
+      [scaleX, scaleY] = @__resizedScale
       left: Math.round @__currentCoords.left / scaleX
       top: Math.round @__currentCoords.top / scaleY
       width: Math.round @__currentCoords.width / scaleX
@@ -157,14 +156,9 @@

     __clearImage: ->
       @__jCropApi?.destroy()
-      if @__deferred and @__deferred.state() is 'pending'
-        @__deferred.reject(IMAGE_CLEARED)
+      @__img?.off().remove()
+      @__deferred?.reject(IMAGE_CLEARED)
       @__deferred = $.Deferred()
-      if @__img
-        @__img.remove()
-        @__img.off()
-        @__img = null
-      @__resizedHeight = @__resizedWidth = @__originalSize = null
       @__setState 'waiting'

     __setImage: (@__url) ->
@@ -177,14 +171,19 @@
           @__img.remove()
         .attr
           src: @__url
-          width: @__resizedWidth
-          height: @__resizedHeight
+          width: @__resizedSize[0]
+          height: @__resizedSize[1]
         .appendTo @__widgetElement

     __calcSizes: (originalSize) ->
+      widgetSize = @__options.widgetSize or [@container.width(), @container.height()]
+      resizedSize = utils.fitSize originalSize, widgetSize
       @__originalSize = originalSize
-      widgetSize = @__options.widgetSize or [@container.width(), @container.height()]
-      [@__resizedWidth, @__resizedHeight] = utils.fitSize @__originalSize, widgetSize
+      @__resizedSize = resizedSize
+      @__resizedScale = [
+        resizedSize[0] / originalSize[0],
+        resizedSize[1] / originalSize[1],
+      ]

     #             |
     #             v
@@ -230,8 +229,7 @@
         left = previousCoords.left or 0
         top = previousCoords.top or 0

-      scaleX = @__resizedWidth / @__originalSize[0]
-      scaleY = @__resizedHeight / @__originalSize[1]
+      [scaleX, scaleY] = @__resizedScale

       if @__options.notLess
         [width, height] = utils.fitSize(@__options.preferedSize


proper getCurrentCoords() in crop widget

--- app/assets/javascripts/uploadcare/ui/crop/crop-widget.js.coffee
+++ app/assets/javascripts/uploadcare/ui/crop/crop-widget.js.coffee
@@ -96,13 +96,17 @@
       # This method could be usefull if you want to make your own done button.
       forceDone: ->
         if @__currentCoords
-          @__deferred.resolve @__buildUrl(@__currentCoords)
+          @__deferred.resolve @__buildUrl @getCurrentCoords()
         else
           throw "not ready"

       # Returns last selected area coords
       getCurrentCoords: ->
-        @__currentCoords
+        scaleRatio = @__resizedWidth / @__originalWidth
+        fixedCoords = {}
+        for key, value of @__currentCoords
+          fixedCoords[key] = Math.round value / scaleRatio
+        fixedCoords

       # Destroys widget completly
       destroy: ->
@@ -112,12 +116,8 @@
         @__currentCoords = null

       __buildUrl: (coords) ->
-        scaleRatio = @__resizedWidth / @__originalWidth
-        fixedCoords = {}
-        for key, value of coords
-          fixedCoords[key] = Math.round value / scaleRatio
-        topLeft = "#{fixedCoords.x}x#{fixedCoords.y}"
-        bottomRight = "#{fixedCoords.x2}x#{fixedCoords.y2}"
+        topLeft = "#{coords.x}x#{coords.y}"
+        bottomRight = "#{coords.x2}x#{coords.y2}"
         url = "#{@__options.url}-/custom_crop/#{topLeft}/#{bottomRight}/"
         if @__options.scale
           url += "-/resize/#{@__options.preferedSize}/"


Adding note about moment.js months' 0-indexing

moment().month() returns a number in [0..11]

--- app/assets/javascripts/views/_shared/date_driven_view.js.coffee
+++ app/assets/javascripts/views/_shared/date_driven_view.js.coffee
@@ -26,7 +26,7 @@
       yearsAndWeeks.push
         year:  from.year()
         cweek: +from.format('w') # moment is nice but unfortunately doesn't yet provide an .isoWeek function
-        month: from.month() + 1
+        month: from.month() + 1 # NOTE: Months in moment.js are 0-indexed
         mweek: +from.format('w') # moment is nice but unfortunately doesn't yet provide an .isoWeek function
         mday:  from.date()
         weekHasPassed: from < moment()


operations: PushInbox TODO note

operations: PushInbox TODO note

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -486,6 +486,7 @@
             # Memorize updates for notifications, same format:
             @notification = ->
                 updates
+            # TODO: no subscriptions left? DELETE NODE!
         ], cb


operations: typo

operations: typo

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -132,7 +132,7 @@

                 @actorAffiliation = 'none'
                 for affiliation in affiliations
-                    if isAffiliationAtLeast affilliation, @actorAffiliation
+                    if isAffiliationAtLeast affiliation, @actorAffiliation
                         @actorAffiliation = affiliation
                 cb()


make 2 letter account names valid

make 2 letter account names valid

--- dl/src/common/validation.coffee
+++ dl/src/common/validation.coffee
@@ -11,7 +11,7 @@
     is_account_name: (value)->
         return no if is_empty value
         length = value.length
-        return no if length < 3 or length > 63
+        return no if length < 2 or length > 63
         for label in value.split('.')
             if !(
                 # Starts with a letter, has only letters, digits, and dashes


set course name to selected course

set course name to selected course

--- tutor/src/components/new-course/course-details.cjsx
+++ tutor/src/components/new-course/course-details.cjsx
@@ -83,7 +83,19 @@

   onSelect: (source_course_id) ->
     source_course_id = null if source_course_id is @state.source_course_id
-    @setState({source_course_id})
+    course_name = @state.course_name
+    newCourse = find(@state.teachingCourses, id: source_course_id)
+
+    if newCourse
+      # is the current name blank or the same as the previous course?
+      if isEmpty(course_name) or (
+        @state.source_course_id and find(@state.teachingCourses, id: @state.source_course_id).name is course_name
+      )
+        course_name = newCourse.name
+    else
+      course_name = ''
+
+    @setState({course_name, source_course_id})

   updateName: (ev) ->


Change default theme

Change default theme

--- views/components/settings/parts/display-config.cjsx
+++ views/components/settings/parts/display-config.cjsx
@@ -51,7 +51,7 @@

 ChangeThemeConfig = React.createClass
   getInitialState: ->
-    theme: config.get 'poi.theme', '__default__'
+    theme: config.get 'poi.theme', 'paperdark'
     enableSVGIcon: config.get 'poi.useSVGIcon', false
   handleSetTheme: (theme) ->
     theme = @refs.theme.getValue()
@@ -192,9 +192,9 @@
   render: ->
     <Grid>
       <Col xs=8>
-        <Input type='checkbox'
-         ref="useFixedResolution"
-         label={__ 'Adaptive resolution based on the window'}
+        <Input type='checkbox'
+         ref="useFixedResolution"
+         label={__ 'Adaptive resolution based on the window'}
          checked={!@state.useFixedResolution} onChange={@handleSetFixedResolution} />
       </Col>
       <Col xs=4>
@@ -251,7 +251,7 @@
       </div>
       <div className="form-group">
         <Divider text={__ 'Game resoultion'} />
-        <ChangeResolutionConfig />
+        <ChangeResolutionConfig />
       </div>
     </form>


gaq: use same access token as koding

gaq: use same access token as koding

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -55,7 +55,7 @@

     <script type="text/javascript">
       var _gaq = _gaq || [];
-      _gaq.push(['_setAccount', 'UA-6520910-21']);
+      _gaq.push(['_setAccount', 'UA-6520910-8']);
       _gaq.push(['_setDomainName', 'none']);

       _gaq.push(['_trackPageview']);


servers: add hasCreditCard info to globals

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -5,6 +5,7 @@
   encoder   = require 'htmlencode'
   { argv }  = require 'optimist'
   _         = require 'lodash'
+  { hasCreditCard } = require '../../../../../servers/models/socialapi/requests'

   options.client               or= {}
   options.client.context       or= {}
@@ -22,6 +23,7 @@
   roles               = null
   permissions         = null
   combinedStorage     = null
+  hasCard             = no

   { bongoModels, client, session } = options

@@ -67,6 +69,7 @@
         userRoles: #{userRoles},
         userPermissions: #{userPermissions},
         currentGroup: #{currentGroup},
+        hasCreditCard: #{hasCard},
         isLoggedInOnLoad: true,
         socialApiData: #{encodedSocialApiData},
         userEnvironmentData: #{userEnvironmentData}
@@ -164,6 +167,12 @@
         if user then userId = user.getId()
         else console.error '[scriptblock] user not found', err
         fin()
+
+    (fin) ->
+      hasCreditCard client, (err) ->
+        hasCard = not err?
+        return fin()
+
   ]

   async.parallel queue, -> callback null, createHTML(), socialapidata


remove unused

remove unused

--- app/assets/javascripts/chaltron/datatables.js.coffee
+++ app/assets/javascripts/chaltron/datatables.js.coffee
@@ -20,7 +20,6 @@
       columnDefs: [
         { orderable: false, className: 'select-checkbox', targets: 0 }
       ]
-      dom: 'T<"clear">lfrtip'
       select: { style: 'multi', info: false }
     })


Make sure all palette instances use their own set of used colors

--- app/assets/javascripts/pgdatagraph/palette.js.coffee
+++ app/assets/javascripts/pgdatagraph/palette.js.coffee
@@ -1,6 +1,6 @@
 class PG.Palette

-  colors:
+  defaults:
     red:
       stroke: "#b64639"
       fill: "#dc6355"
@@ -31,9 +31,13 @@
     violetRed:
       stroke: "#b42b58"
       fill: "#ed789e"
+
   fallbackColor:
     stroke: "#ccc"
     fill: "#ccc"
+
+  constructor: (colors = {}) ->
+    @colors = $.extend yes, colors, @defaults

   color: ->
     unused = _.compact _(@colors).map (colors, name) =>


fix wrong styles when using multiple instances of CodeMirror

--- app/assets/javascripts/rails_admin/ra.widgets.coffee
+++ app/assets/javascripts/rails_admin/ra.widgets.coffee
@@ -240,6 +240,7 @@
         options = $(this).data('options')
         textarea = this
         $.getScript options['locations']['mode'], (script, textStatus, jqXHR) ->
+          options = $(domEle).data('options')
           $('head').append('<link href="' + options['locations']['theme'] + '" rel="stylesheet" media="all" type="text\/css">')
           CodeMirror.fromTextArea(textarea,options['options'])
           $(textarea).addClass('codemirrored')


Fix setting custom appIds for OFX requests.

--- application/chrome/content/wesabe/ofx/Request.coffee
+++ application/chrome/content/wesabe/ofx/Request.coffee
@@ -144,7 +144,7 @@
   this::__defineGetter__ 'appId', ->
     @_appId || 'Money'

-  this::__defineSetter__ 'appId', ->
+  this::__defineSetter__ 'appId', (appId) ->
     @_appId = appId

   this::__defineGetter__ 'appVersion', ->


Update spec

Update spec

--- apps/auction_lots/test/templates/artwork-meta.coffee
+++ apps/auction_lots/test/templates/artwork-meta.coffee
@@ -24,7 +24,7 @@
       @html.should.include "<meta property=\"twitter:card\" content=\"summary"
       @html.should.include "<link rel=\"canonical\" href=\"http://localhost:5000/artwork/#{@artwork.get('id')}"
       @html.should.include "<meta property=\"og:url\" content=\"http://localhost:5000/artwork/#{@artwork.get('id')}"
-      @html.should.include "<meta property=\"og:description\" content=\"Related auction results for From Gagosian Gallery, Andy Warhol, Skull (1999), Watercolor on Paper, 10 x 20 x 30in"
+      @html.should.include "<meta property=\"og:description\" content=\"Related auction results for From Gagosian Gallery, Andy Warhol, Skull (1999), Watercolor on Paper, 10 × 20 × 30in"
       @html.should.include "<meta property=\"og:title\" content=\"Andy Warhol, Skull (1999) | Related Auction Results | Artsy"

   describe 'with an image', ->


adds errors for invalid options

adds errors for invalid options

--- bebopt.coffee
+++ bebopt.coffee
@@ -14,18 +14,19 @@
     @_half ?= {}
     @_parent = null
     @__rargs = [] # raw args
-    @__pargs = {} # processed args
+    @__pargs = [] # processed args
+    @__uargs = {} # user-ready args

   _refError: (ref) =>
     if typeof(ref) is 'string'
       if ref[0] isnt '#'
-        err = "Bebopt: ref must begin with pound: `#{ref}'"
+        err = "Bebopt: ref must begin with pound: '#{ref}'"
         throw new Error(err)
       _refns = ref.replace(/^#(.*)Beat\..*/, '$1')
       _refchild = ref.replace(/^#.*Beat\.(.*)/, '$1')
       _ref = @["_#{_refns}"][_refchild]
       if _ref is undefined
-        err = "Bebopt: bad reference: #{_ref}: `#{ref}'"
+        err = "Bebopt: bad reference: #{_ref}: '#{ref}'"
         throw new Error(err)
       else
         _ref._isref = true
@@ -37,12 +38,12 @@
     switch parent
       when 'long'
         if name.length < 2
-          err = "Bebopt: option name too short -- `#{name}'"
+          err = "Bebopt: option name too short -- '#{name}'"
           throw new Error(err)
         return false
       when 'short'
         if name.length > 1
-          err = "Bebopt: option name too long: `#{name}'"
+          err = "Bebopt: option name too long: '#{name}'"
           throw new Error(err)
         return false

@@ -52,7 +53,6 @@

     if _ref is false
       @_long[name] =
-        name: name,
         fn: fn,
         desc: desc,
         type: 'flag'
@@ -67,7 +67,6 @@

     if _ref is false
       @_short[name] =
-        name: name,
         fn: fn,
         desc: desc,
         type: 'flag'
@@ -82,7 +81,6 @@

     if _ref is false
       @_half[name] =
-        name: name,
         fn: fn,
         desc: desc,
         type: 'flag'
@@ -94,22 +92,32 @@
   _decodeRef: (ref) ->
     if typeof(ref) is 'string'
       if ref[0] isnt '#'
-        err = "Bebopt: ref must begin with pound: `#{ref}'"
+        err = "Bebopt: ref must begin with pound: '#{ref}'"
         throw new Error(err)
       _refns = ref.replace(/^#(.*)Beat\..*/, '$1')
       _refchild = ref.replace(/^#.*Beat\.(.*)/, '$1')
       _ref =
         parent: "_#{_refns}"
         child: _refchild
-      if _ref is undefined
-        err = "Bebopt: bad reference: #{_ref}: `#{ref}'"
-        throw new Error(err)
-      else
-        return _ref
+      return _ref
+
+  _checkOption: (decRef) =># decRef -> decoded ref
+    ref = @[decRef.parent][decRef.child]
+    if ref is undefined
+      if decRef.child.length < 2
+        console.error("#{@app}: invalid option -- '#{decRef.child}'")
+        process.exit(1)
+      else if decRef.child.length > 1
+        dash = if decRef.parent is '_half' then '-' else '--'
+        console.error("#{@app}: unrecognized option '#{dash}#{decRef.child}'")
+        process.exit(1)
+    else
+      return ref
+

   op: (code, help) =>
     if @_parent is null
-      err = "Bebopt: null parent ref: cannot apply opcode: `#{code}'"
+      err = "Bebopt: null parent ref: cannot apply opcode: '#{code}'"
       throw new Error(err)
     ref = @_decodeRef(@_parent)
     switch code
@@ -124,22 +132,23 @@

   parse: () =>
     @__rargs = process.argv
-    @__rargs.slice(2).forEach((opt, ind, arr) =>
+    @__pargs = @__rargs.slice(2).map((opt, ind, arr) =>
       len = opt.replace(/^(--?).*/, '$1').length
+      dRef = {}
       switch len
         when 1
           opt = opt.replace(/^-/, '')
-          if opt.length < 2
-            ref = @_refError("#shortBeat.#{opt}")
-            @__pargs +=
-
-
-
-            console.log(ref)
+          if opt.length < 2 # short
+            dRef = @_decodeRef("#shortBeat.#{opt}")
+          else
+            dRef = @_decodeRef("#halfBeat.#{opt}")
         when 2
           opt = opt.replace(/^--/, '')
+          dRef = @_decodeRef("#longBeat.#{opt}")
+      _ref = @_checkOption(dRef)
+      _ref.name = dRef.child
+      return _ref
     )
-
     console.log(@)

 module.exports = Bebopt


Sort matchingRoutes by sort field

Sort matchingRoutes by sort field

--- server/routes/frontend.coffee
+++ server/routes/frontend.coffee
@@ -79,6 +79,9 @@

         matchingRoutes.push localTemplateData

+    matchingRoutes.sort (a, b) ->
+      a.sort > b.sort
+
     async.detectSeries matchingRoutes, (templateData, callback) ->
       globalNext = callback
       res.render templateData.template, templateData, (err, html) ->


chore(generators/app/templates): add quotes around overriden target-environment

--- generators/app/templates/@Gruntfile.coffee
+++ generators/app/templates/@Gruntfile.coffee
@@ -1123,7 +1123,7 @@
         ( environment ) ->

             if ( ( override = grunt.option( 'target' ) ? process.env.GRUNT_TARGET )? and override isnt environment )
-                grunt.log.ok( "Overriding target environment to #{override}" )
+                grunt.log.ok( "Overriding target environment to \"#{override}\"" )
                 environment = override

             grunt.config.set( 'build.environment', environment ) if environment?


F ember init

F ember init

--- templates/skeleton/app/assets/javascripts/admin/application.js.coffee
+++ templates/skeleton/app/assets/javascripts/admin/application.js.coffee
@@ -42,4 +42,4 @@

 # turn on promise failure debugging
 Ember.RSVP.on 'error', (error) ->
-Ember.Logger.assert(false, error);
+  Ember.Logger.assert(false, error);


verify if released and local versions exist before comparing

--- src/ui/views/aboutlayout.coffee
+++ src/ui/views/aboutlayout.coffee
@@ -27,7 +27,11 @@
             img src: path.join __dirname, '..', '..', 'icons', 'yakyak-logo.svg'
         div class: 'name', ->
             h2 'YakYak v' + localVersion
-        if (versionToInt(releasedVersion) > versionToInt(localVersion))
+        # TODO: if objects are undefined then it should check again on next
+        #        time about window is opened
+        if (releasedVersion? &&
+            localVersion? &&
+            versionToInt(releasedVersion) > versionToInt(localVersion))
             div class: 'update', ->
                 span 'A newer version is available, please upgrade from ' +
                      localVersion + ' to ' + releasedVersion


Add specs to set host headers on redirect

--- test/event_listeners.spec.coffee
+++ test/event_listeners.spec.coffee
@@ -387,6 +387,7 @@
         service.config.maxRedirects = 5
         response = makeRequest(->)
         expect(response.request.httpRequest.endpoint.host).to.equal('redirected')
+        expect(response.request.httpRequest.headers.Host).to.equal('redirected')
         expect(response.error.retryable).to.equal(true)
         expect(response.redirectCount).to.equal(service.config.maxRedirects)
         expect(delays).to.eql([0, 0, 0, 0, 0])
@@ -396,6 +397,7 @@
       service.config.maxRetries = 0
       response = makeRequest(->)
       expect(response.request.httpRequest.endpoint.host).not.to.equal('redirected')
+      expect(response.request.httpRequest.headers.Host).not.to.equal('redirected')
       expect(response.error.retryable).to.equal(false)

   describe 'success', ->


Don't include broken symlinks

Don't include broken symlinks

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -52,6 +52,9 @@
       path.join('npm', 'doc')
       path.join('npm', 'html')
       path.join('npm', 'man')
+      path.join('npm', 'node_modules', '.bin', 'beep')
+      path.join('npm', 'node_modules', '.bin', 'clear')
+      path.join('npm', 'node_modules', '.bin', 'starwars')
       path.join('pegjs', 'examples')
       path.join('jasmine-reporters', 'ext')
       path.join('jasmine-node', 'node_modules', 'gaze')
@@ -71,8 +74,10 @@
       # These are only require in dev mode when the grammar isn't precompiled
       path.join('atom-keymap', 'node_modules', 'loophole')
       path.join('atom-keymap', 'node_modules', 'pegjs')
+      path.join('atom-keymap', 'node_modules', '.bin', 'pegjs')
       path.join('snippets', 'node_modules', 'loophole')
       path.join('snippets', 'node_modules', 'pegjs')
+      path.join('snippets', 'node_modules', '.bin', 'pegjs')

       '.travis.yml'
       '.npmignore'


Don't include generated Makefiles

Don't include generated Makefiles

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -57,6 +57,7 @@
       path.join('build', 'binding.Makefile')
       path.join('build', 'config.gypi')
       path.join('build', 'gyp-mac-tool')
+      path.join('build', 'Makefile')
       path.join('build', 'Release', 'obj.target')
       path.join('build', 'Release', 'obj')
       path.join('build', 'Release', '.deps')


webapp/sagews: hotfix for SynchronizedWorksheetCell::output

--- src/smc-webapp/sagews.coffee
+++ src/smc-webapp/sagews.coffee
@@ -2414,6 +2414,7 @@
         return @cm.getLine(x.loc.from.line)

     output: =>
+        v = []
         for x in @raw_output().slice(38).split(MARKERS.output)
             try
                 v.push(misc.from_json(x))


need more ruby

need more ruby

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -493,10 +493,10 @@

       note = null
       switch extra.translator
-        case 'ca65189f-8815-4afe-8c8b-8c7c15f0edca' # Better BibTeX
+        when 'ca65189f-8815-4afe-8c8b-8c7c15f0edca' # Better BibTeX
           note = extra.note

-        case '0af8f14d-9af7-43d9-a016-3c5df3426c98' # BibTeX AUX Scanner
+        when '0af8f14d-9af7-43d9-a016-3c5df3426c98' # BibTeX AUX Scanner
           Zotero.BetterBibTeX.log('::: AUX', collection.id, extra.citations)
           Zotero.Items.trash([itemID])


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/string_extensions.coffee
+++ src/string_extensions.coffee
@@ -56,6 +56,7 @@
 ###
 String::capitalize = ->
   this.charAt(0).toUpperCase() + this.substring(1).toLowerCase()
+

 ###*
 Return the class or constant named in this string.


client: add hasCreditCard check to subscription container

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/home/lib/billing/components/subscriptioncontainer.coffee
+++ client/home/lib/billing/components/subscriptioncontainer.coffee
@@ -67,6 +67,7 @@
     # TODO(umut): activate this when we have coupon support.
     isSurveyTaken: yes # !!customer.coupon(state)
     isEmailVerified: isEmailVerified(state)
+    hasCreditCard: globals.hasCreditCard
   }


sellitem was renamed

sellitem was renamed

--- shared/idle/IdleExtras.coffee
+++ shared/idle/IdleExtras.coffee
@@ -59,7 +59,7 @@
         {name: 'Times Ascended',          key: 'statistics.explore transfer ascend'}
         {name: 'Times Descended',         key: 'statistics.explore transfer descend'}
         {name: 'Walls Walked Into',       key: 'statistics.explore hit wall'}
-        {name: 'Items Sold',              key: 'statistics.event sellItem'}
+        {name: 'Items Sold',              key: 'statistics.player sellItem'}
         {name: 'Monster Battles',         key: 'statistics.event monsterbattle'}
         {name: 'Boss Kills',              key: 'statistics.event bossbattle win'}
         {name: 'Items Equipped',          key: 'statistics.event findItem'}


moving addImplicitBraces and addImplicitParentheses next to each other ... perhaps they should interleave.

--- rewriter.coffee
+++ rewriter.coffee
@@ -35,8 +35,8 @@
     @closeOpenCalls()
     @closeOpenIndexes()
     @addImplicitIndentation()
+    @tagPostfixConditionals()
     @addImplicitBraces()
-    @tagPostfixConditionals()
     @addImplicitParentheses()
     @ensureBalance BALANCED_PAIRS
     @rewriteClosingParens()


CoffeeScriptified some javascript in ChannelsController.coffee

--- core/app/backbone/controllers/channels_controller.coffee
+++ core/app/backbone/controllers/channels_controller.coffee
@@ -43,12 +43,12 @@
       FactlinkApp.leftTopRegion.close()

   getChannelFacts: (username, channel_id) ->
-    this.loadChannel username, channel_id, (channel) =>
-      this.commonChannelViews(channel)
+    @loadChannel username, channel_id, (channel) =>
+      @commonChannelViews(channel)
       FactlinkApp.mainRegion.show(new ChannelView(model: channel))

   getChannelActivities: (username, channel_id) ->
-    this.loadChannel username, channel_id, (channel) =>
-      this.commonChannelViews(channel);
+    @loadChannel username, channel_id, (channel) =>
+      @commonChannelViews(channel)
       activities = new ChannelActivities([],{ channel: channel })
       FactlinkApp.mainRegion.show(new ChannelActivitiesView(model: channel, collection: activities))


websocket stuff

websocket stuff

--- app/assets/javascripts/model.coffee
+++ app/assets/javascripts/model.coffee
@@ -217,8 +217,7 @@

 	PRELOAD_TOLERANCE : 0.9
 	PRELOAD_RADIUS : 20
-	preloadVertices : []
-
+
 	# Use this method to let us know when you've changed your spot. Then we'll try to
 	# preload some data.
 	#
@@ -238,36 +237,38 @@

 		_.defer =>

-			_preloadVertices = @preloadVertices
-			_preloadRadius   = @PRELOAD_RADIUS
-			# Looks like `preload_vertices` hasn't been populated yet.
-			# This is what ppl call lazy initialization.
-			if _preloadVertices.length == 0
-				for z in [-1..10] by 3
-					for x in [-_preloadRadius.._preloadRadius] by 5
-						for y in [-_preloadRadius.._preloadRadius] by 5
-							_preloadVertices.push x, y, z
-
-			preloadCheckHits     = 0
-			preloadCheckCount    = 0
-			preloadCheckVertices = M4x4.transformPointsAffine matrix, _preloadVertices
-
-			for i in [0...preloadCheckVertices.length] by 3
-				x = preloadCheckVertices[i]
-				y = preloadCheckVertices[i + 1]
-				z = preloadCheckVertices[i + 2]
-
-				if x >= 0 and y >= 0 and z >= 0
-					preloadCheckCount++
-					preloadCheckHits++ if @getColor(x, y, z) > 0
-
-			if preloadCheckHits / preloadCheckCount < @PRELOAD_TOLERANCE
+			_preloadRadius = @PRELOAD_RADIUS
+
+			if @preloadTest(matrix, -_preloadRadius, -_preloadRadius, 2 * _preloadRadius) < @PRELOAD_TOLERANCE
 				@pull(matrix, (err) ->
 					done()
 					callback() if !err and callback
 				)
 			else
 				done()
+
+	preloadTest : (matrix, x0, y0, z, width, sparse = 5) ->
+
+		vertices = []
+		for x in [x0...(x0 + width)] by sparse
+			for y in [y0...(y0 + width)] by sparse
+				vertices.push x, y, z
+
+		vertices = M4x4.transformPointsAffine matrix, vertices
+
+		count = hits = 0
+		for i in [0...vertices.length] by 3
+			x = vertices[i]
+			y = vertices[i + 1]
+			z = vertices[i + 2]
+
+			if x >= 0 and y >= 0 and z >= 0
+				count++
+				hits++ if @getColor(x, y, z) > 0
+
+		hits / count
+
+

 	pull : (matrix, callback) ->
@@ -304,7 +305,7 @@
 		_WebSocket = if window['MozWebSocket'] then MozWebSocket else WebSocket

 		unless @loadSocket
-			socket = @loadSocket = new _WebSocket("ws://#{document.location.host}/binary/socket")
+			socket = @loadSocket = new _WebSocket("ws://#{document.location.host}/binary/ws")
 			socket.binaryType = 'arraybuffer'
 			socket.onclose = (code, reason) ->
 				alert("#{code}: #{reason}")
@@ -332,7 +333,7 @@
 		transmitPackage = new Float32Array(17)
 		transmitPackage[0] = socketHandle
 		transmitPackage.set(matrix, 1)
-
+
 		if socket.readyState == _WebSocket.OPEN
 			socket.send(transmitPackage)
 		else
@@ -340,7 +341,7 @@
 				socket.send(transmitPackage)
 				socket.removeEventListener("open", socketOpenCallback, false)
 			socket.addEventListener("open", socketOpenCallback, false)
-
+

 	# Now comes the implementation of our internal data structure.
 	# `cube` is the main array. It actually represents a cuboid


Removed double binding on view.

Removed double binding on view.

--- app/assets/javascripts/rooms.coffee
+++ app/assets/javascripts/rooms.coffee
@@ -20,7 +20,8 @@
   $(".speech.file").on "change", ->
     $(".upload.form").submit()

-  $(".view.button").click (event) ->
+  $(".view.button").unbind('click').click (event) ->
+    console.log('called')
     window.open("http://docs.google.com/gview?url=" + $(this).closest(".ui.card").attr("href"))
     event.preventDefault()


improve THymE volume level updating

improve THymE volume level updating

--- app/assets/javascripts/thyme.coffee
+++ app/assets/javascripts/thyme.coffee
@@ -691,7 +691,7 @@
     return

   # Event listener for the volume bar
-  volumeBar.addEventListener 'change', ->
+  volumeBar.addEventListener 'input', ->
     value = volumeBar.value
     video.volume = value
     return


Safety: always set params

Safety: always set params

--- src/lib/Helpers.coffee
+++ src/lib/Helpers.coffee
@@ -275,7 +275,7 @@
       component.inPorts[port].detach tempSocket

 populateParams = (config, input) ->
-  return unless config.params.length
+  return {} unless config.params.length
   params = {}
   for paramPort in config.params
     if input.ports[paramPort].isAddressable()


Add test for requireWhitespaceAfterToken behavior in separator

--- test/utils/seperator_spec.coffee
+++ test/utils/seperator_spec.coffee
@@ -54,3 +54,14 @@
     expect(segments.length).to.eql 1
     expect(segments[0].comments.length).to.eql 0
     expect(segments[0].code.length).to.be.above 0
+
+  it "should respect requireWhitespaceAfterToken option", ->
+    segments = seperator(
+      "var foo = 'bar';\n/*jshint unused:false*/\nvar baz = 'thud';",
+      LANGUAGES.JavaScript,
+      requireWhitespaceAfterToken: true
+    )
+    expect(segments).to.be.an('array')
+    expect(segments.length).to.eql 1
+    expect(segments[0].comments.length).to.eql 0
+    expect(segments[0].code.length).to.be.above 0


Axis labels, fix comfort colours to be logical, remove global var :cop:

--- web/site/src/javascript/scatter/ScatterDrawer.coffee
+++ web/site/src/javascript/scatter/ScatterDrawer.coffee
@@ -22,7 +22,10 @@

   _draw_node_status: ->
     @chart ?= new ScatterChart @parent
-    @plot_data =
+    @chart.chart.xAxis.axisLabel 'Temperature (℃)'
+    @chart.chart.yAxis.axisLabel 'Humidity (%)'
+
+    plot_data =
       [{
         key: 'Uncomfortable'
         values: []
@@ -41,12 +44,13 @@
         for comf, i in properties.comfortabilities
           time = properties.comf_times[i]
           th_index = properties.th_times.findIndex (d) -> d > time
-          #console.log th_index
           if th_index >= 0
-            @plot_data[comf-1].values.push {
+            plot_data[comf-1].values.push {
               x: properties.temperatures[th_index]
               y: properties.humidities[th_index]
               }
-    @chart.updateChart @plot_data
+    do plot_data.reverse
+    #console.log plot_data.map((x) -> x.values.length)
+    @chart.updateChart plot_data

 module.exports = ScatterDrawer


DefineStackView: keep current sum in memory then compare it with the latest one to understand if it's updated or not

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -479,8 +479,9 @@

       templateContent = convertedDoc.content

-
-    template = templateContent
+    template   = templateContent
+    currentSum = stackTemplate?.template?.sum
+

     updateStackTemplate {
       template, description, templateDetails
@@ -490,6 +491,8 @@
       if not err and stackTemplate
         @setData { stackTemplate }
         @emit 'Reload'
+
+      stackTemplate._updated = currentSum isnt stackTemplate.template.sum

       callback err, stackTemplate


DefineStackView: fix button states [completes #103399526]

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -120,6 +120,12 @@
       else
         @setFooterVisibility 'show'
         pane.getMainView().emit 'FocusToEditor'
+
+    { ace } = @stackTemplateView.editorView.aceView
+
+    ace.on 'FileContentChanged', =>
+      @setAsDefaultButton.hide()
+      @saveButton.show()

   setFooterVisibility: (state) ->
@@ -269,8 +275,10 @@
         @outputView.add "You can now close this window, or set this
                          template as default for your team members."

-        @cancelButton.setTitle 'Close'
         @setAsDefaultButton.show()
+        @saveButton.hide()
+
+      @cancelButton.setTitle 'Close'

   checkAndBootstrapCredentials: (callback) ->


DefineStackView: don't interrupt the flow if credential fetch is failed because AccessDenied

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -343,8 +343,11 @@

     showCredentialContent = (credential) =>
       credential.fetchData (err, data) =>
-        return failed err  if err
-        @outputView.add JSON.stringify data.meta, null, 2
+        if err?.name is 'AccessDenied'
+          @outputView.add "Couldn't fetch credential data, not shared."
+        else
+          return failed err  if err
+          @outputView.add JSON.stringify data.meta, null, 2
         callback null, [credential]

     @outputView


Avatars ONline Indicator: Minor codefix

Avatars ONline Indicator: Minor codefix

--- client/app/CommonViews/avatarviews/avatarview.coffee
+++ client/app/CommonViews/avatarviews/avatarview.coffee
@@ -85,10 +85,12 @@
     @render() if @getData()

     if @getOptions().showStatus
+      {statusDiameter} = @getOptions()
+
       @addSubView @statusIndicator = new KDCustomHTMLView
         cssClass : 'statusIndicator'
-      @statusIndicator.setWidth @getOptions().statusDiameter
-      @statusIndicator.setHeight @getOptions().statusDiameter
+      @statusIndicator.setWidth statusDiameter
+      @statusIndicator.setHeight statusDiameter

   pistachio:-> '<cite></cite>'


MachineSettingsPopup: Emit MachineLabelChanged event.

--- client/app/lib/providers/machinesettingspopup.coffee
+++ client/app/lib/providers/machinesettingspopup.coffee
@@ -148,7 +148,7 @@
       if (kd.utils.slugify label) is ""
         return showError "Nickname cannot be empty."

-      @machine.setLabel label, (err, newSlug)=>
+      @machine.setLabel label, (err, newSlug) =>

         return if showError err

@@ -157,18 +157,8 @@
         nickEdit.hide()
         nickname.show()

-        frontApp = appManager.getFrontApp()
-
-        if frontApp.options.name is "IDE" and frontApp.workspaceData?
-
-          return  unless @machine.slug is frontApp.workspaceData.machineLabel
-
-          newRoute = "/IDE/#{newSlug}/#{frontApp.workspaceData.slug}"
-          frontApp.workspaceData.machineLabel = newSlug
-
-          kd.utils.defer ->
-            computeController.once 'MachineDataUpdated', ->
-              router.clear newRoute
+        @machine.emit 'MachineLabelUpdated', label, newSlug
+        @destroy()

   viewAppended:->


Add collaboration settings container element

--- client/ide/lib/views/chat/idechatsettingspane.coffee
+++ client/ide/lib/views/chat/idechatsettingspane.coffee
@@ -106,6 +106,15 @@
       cssClass : 'label'
       partial  : 'Fetching participants'

+    @createSettingsElements()
+
+
+  createSettingsElements: ->
+
+    @settings  = new KDCustomHTMLView
+      tagName  : 'div'
+      cssClass : 'session-settings'
+

   setUnwatch: (state) ->

@@ -247,6 +256,7 @@

         Have sessions with people you trust, they can view and edit all your files.

+      {{> @settings}}

         {{> @startSession}} {{> @endSession}}


Finalize the database modal.

Finalize the database modal.

--- client/landing/app/CommonViews/databasesmodal.coffee
+++ client/landing/app/CommonViews/databasesmodal.coffee
@@ -4,7 +4,7 @@

     options =
       title                   : "Manage Databases"
-      content                 : ''#"You can create and manage your databases here."
+      content                 : ''
       helpContent             :
         """
           You can create and modify your databases from this modal.
@@ -99,17 +99,28 @@
         @newDBCreatedWidget = new NewDBCreatedWidget {}, data

       dbCreateForm.buttons.Create.setTitle "Ok, got it"
+
+      dbCreateForm.buttons.Create.setClass "modal-clean-gray"
+      dbCreateForm.buttons.Create.unsetClass "modal-clean-green"
+
       dbCreateForm.buttons.Create.setCallback =>
         @newDBCreatedWidget.unsetClass 'ready'
+
         dbCreateForm.inputs.dbType.makeEnabled()
         dbCreateForm.inputs.dbKind.makeEnabled()
+
         dbCreateForm.buttons.Create.setTitle "Create"
+        dbCreateForm.buttons.Create.setClass "modal-clean-green"
+        dbCreateForm.buttons.Create.unsetClass "modal-clean-gray"
         dbCreateForm.buttons.Create.hideLoader()
+
         dbCreateForm.buttons.Create.setCallback =>
           form = @modalTabs.forms["Create New Database"]
           @dbController.addDatabase form.getFormData(), =>
             form.buttons.Create.hideLoader()
+
         KD.utils.wait 300, => @setPositions()
+
       @dbController.loadItems()

 class AccountDatabaseListController extends KDListViewController
@@ -185,16 +196,17 @@
           responses = responses.concat dbs
         if responseToWait == 0
           @hideLazyLoader()
-
           if responses.length > 0
             @instantiateListItems responses
           else
-            @addCustomItem "You don't have any database to show."
-
+            @addCustomItem """You don't have any databases.
+                              Why don't you create a new one?"""
           callback?()
       , =>
+        @hideLazyLoader()
         @addCustomItem """It seems there is something wrong with
                           database provider. Please try again later."""
+        callback?()
       , 10000

   deleteDatabase:(listItem)->
@@ -214,7 +226,6 @@
   updateDatabase:(listItem, password)->

     data = listItem.getData()
-    log "Requested DB Type", data

     @talkToKite
       method        : @commands[data.dbType].update


have track changes make calls to web_api not web


utils: added EOF :cop:

utils: added EOF :cop:

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -354,4 +354,4 @@
   createTeamTitlePhrase: (title) ->
     # doesn't duplicate the word `the` if the title already has it in the beginning
     # doesn't duplicate the word `team` if the title already has it at the end
-    "#{if title.search(/^the/i) < 0 then 'the' else ''} #{title} #{if title.search(/team$/i) < 0 then 'team' else ''}"+    "#{if title.search(/^the/i) < 0 then 'the' else ''} #{title} #{if title.search(/team$/i) < 0 then 'team' else ''}"


simpler init

simpler init

--- js/src/auth/AuthenticationService.coffee
+++ js/src/auth/AuthenticationService.coffee
@@ -57,11 +57,7 @@
         keypair = _keypair
         credentialProvider.store { principal, credential, keypair }
       .then ->
-        searchCredentialService.getFhePrivateKey(notifier)
-      .then ->
-        searchCredentialService.getSearchPrivateKey(notifier)
-      .then ->
-        searchCredentialService.getClientHashFunction(notifier)
+        searchCredentialService.ensureCredentialsInitialized(notifier)
       .then ->
         Promise.resolve(notifier(AuthenticationStage.COMPLETED))
       .then ->


Reuse splice helper

Reuse splice helper

--- src/trix/models/block.coffee
+++ src/trix/models/block.coffee
@@ -230,7 +230,7 @@
     if index is -1
       array
     else
-      array.slice(0, index).concat(array.slice(index + 1))
+      splice(array, index, 1)

   splice = (array, args...) ->
     result = array.slice(0)


A tiny bit of refactoring of PhotoReader

--- lib/assets/javascripts/googleplus.photoreader.js.coffee
+++ lib/assets/javascripts/googleplus.photoreader.js.coffee
@@ -4,7 +4,7 @@
 class PhotoReader extends GooglePlus.Reader
   append: (items) ->
     for item in items
-      continue unless item.hasOwnProperty('verb')
+      continue unless item.verb?
       continue unless item.verb is 'post'

       date = null


Fix function signatures in Layer.Base

Fix function signatures in Layer.Base

--- lib/assets/javascripts/unpoly/classes/layer/base.coffee
+++ lib/assets/javascripts/unpoly/classes/layer/base.coffee
@@ -59,10 +59,10 @@
   defaultTargets: ->
     @constructor.defaults().targets

-  open: (parentElement, innerContentElement) ->
+  create: (parentElement, innerContentElement, options) ->
     throw "implement me"

-  close: ->
+  destroy: (options) ->
     throw "implement me"

   sync: ->


Add merge() for dataflows.

Add merge() for dataflows.

--- h2o-web/src/main/steam/scripts/flow.coffee
+++ h2o-web/src/main/steam/scripts/flow.coffee
@@ -1671,6 +1671,10 @@
     forEach nodes, (node) -> link$ node, -> target evaluate()
     target

+  merge = (sources..., target, f) ->
+    propagate = -> target apply f, null, _resolve sources
+    propagate()
+    map sources, (source) -> link$ source, propagate

   fork: fork
   join: (args..., go) -> _join args, _applicate go
@@ -1682,6 +1686,7 @@
   isSignal: isNode$
   react: react
   invoke: _invoke
+  merge: merge
   lift: lift
   menu: menu
   getJobs: getJobs


login界面

login界面

--- lib/gitCreatePackage/gitCreatePackage-login-view.coffee
+++ lib/gitCreatePackage/gitCreatePackage-login-view.coffee
@@ -7,9 +7,9 @@
 module.exports =
 class V extends View
   @content:->
-    @div class: 'loginView overlay from-top', =>
+    @div class: 'loginView overlay butterfly from-top', =>
       @div class: 'content', =>
-        @h1 'Input username and password:'
+        @h1 'Verify Account:'

         @div class: "form-group", =>
           @label 'Account:'


Add simple tag auto completion endpoint

Add simple tag auto completion endpoint

--- api/routes/tags.coffee
+++ api/routes/tags.coffee
@@ -8,6 +8,7 @@
 module.exports = (app, db) ->
     app.options '/tags', options ['GET', 'POST']
     app.options '/tags/:id', options ['GET', 'PATCH', 'DELETE']
+    app.options '/tags/suggest', options ['POST']

     app.get '/tags', (req, res, next) ->
         db.all '''
@@ -108,3 +109,23 @@
                 else
                     res.status 404
                     .json { status: 404, error: 'no such id' }
+
+    app.post '/tags/suggest', (req, res, next) ->
+        if !req.body.q?
+            res.status 400
+            .json { status: 400, error: 'query is required' }
+        else
+
+            db.all '''
+                SELECT id, name
+                FROM tTag
+                WHERE name LIKE ?
+                ORDER BY name ASC
+                LIMIT 10
+            ''', req.body.q + '%', (err, rows) ->
+                if err?
+                    res.status 500
+                    .json { status: 500, error: 'database error' }
+                else
+                    res.status 200
+                    .json rows


Add Keyboard.clear method

Add Keyboard.clear method

--- src/plugins/keyboard.coffee
+++ src/plugins/keyboard.coffee
@@ -40,9 +40,15 @@
 ]

 #
+# clears out the list of pressed keys
+Keyboard::clear = () ->
+  @pressed = []
+  return @
+
+#
 #
 Keyboard::handler = (e) ->
-
+  @clear()
   return if @disabled

   pressed = (if e.type is "keyup" then false else true)
@@ -61,12 +67,11 @@

   @keys[key] = pressed;

-  @pressed = []
+
   for k, v of @keys
     @pressed.push k if v

   return
-

 #
 #


fixes #6724 clones for > 1000

fixes #6724 clones for > 1000

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -638,12 +638,23 @@
       # get the source site's objects
       ({db, user, src_site}, next) ->
         log 'find the objects'
-        db.objects.find {
-          site_id: src_site.get('_id').val()
-        }, {
-          limit: 100000
-        }, (err, src_objects) ->
-          return next err if err
+        grab_data = (page, total_src_objects, finish_grab) ->
+          db.objects.find {
+            site_id: src_site.get('_id').val()
+          }, {
+            limit: 1000
+            skip: (page - 1) * 1000
+          }, (err, src_objects) ->
+            return next err if err
+            total_src_objects = total_src_objects.concat src_objects
+            if src_objects?.length >= 1000
+              page++
+              grab_data page, total_src_objects, finish_grab
+              log "#{total_src_objects.length} objects found.. grabbing next page"
+            else
+              finish_grab total_src_objects
+
+        grab_data 1, [], (src_objects) =>
           log "#{src_objects.length} objects found"
           next null, {db, user, src_site, src_objects}

@@ -668,13 +679,16 @@
         site.users[user.admin_uid] = 'admin'
         site.settings.seo = {}
         site.settings.icons = {}
+        log 'inserting site'
         db.sites.insert site, (err, dst_site) ->
           next err, {db, src_objects, dst_site}

       # insert objects
       ({db, src_objects, dst_site}, next) ->
         ids = {}
-        async.map src_objects, ((src_object, callback) ->
+        log 'inserting objects'
+        objects_inserted = 0
+        async.mapLimit src_objects, 10, ((src_object, callback) ->
           data = src_object.val()
           src_id = data._id
           delete data._id
@@ -682,14 +696,17 @@
           data.site_id = dst_site.get('_id').val()
           db.objects.insert data, (err, dst_object) ->
             return callback err if err
+            objects_inserted++
             dst_id = dst_object.get('_id').val()
             ids[src_id] = dst_id
+            log "#{objects_inserted} objects inserted"
             callback null, dst_object
         ), (err, dst_objects) ->
           next err, {db, dst_objects, ids}

       # update object references
       ({db, dst_objects, ids}, next) ->
+        log 'fixing refs'
         async.forEach dst_objects, ((dst_object, callback) ->
           data_ref = dst_object.get 'data'
           data = JSON.stringify data_ref.val()


Update ng-quick-date.coffee

Update ng-quick-date.coffee

--- src/ng-quick-date.coffee
+++ src/ng-quick-date.coffee
@@ -22,6 +22,9 @@
       closeButtonHtml: '×'
       nextLinkHtml: 'Next →'
       prevLinkHtml: '← Prev'
+      clearButtonHtml: 'Clear'
+      dateLabelHtml: 'Date'
+      timeLabelHtml: 'Time'
       disableTimepicker: false
       disableClearButton: false
       defaultTime: null
@@ -326,11 +329,11 @@

-                    Date
+
                     <input class='quickdate-date-input' ng-class="{'ng-invalid': inputDateErr}" name='inputDate' type='text' ng-model='inputDate' placeholder='1/1/2013' ng-enter="selectDateFromInput(true)" ng-blur="selectDateFromInput(false)" on-tab='onDateInputTab()' />

-                    Time
+
                     <input class='quickdate-time-input' ng-class="{'ng-invalid': inputTimeErr}" name='inputTime' type='text' ng-model='inputTime' placeholder='12:00 PM' ng-enter="selectDateFromInput(true)" ng-blur="selectDateFromInput(false)" on-tab='onTimeInputTab()'>

@@ -352,7 +355,7 @@

-                  Clear
+


Fixed Side comments not working due to not having an `allow` set.

--- collections/comment.coffee
+++ collections/comment.coffee
@@ -6,3 +6,11 @@
     name: 'post'
     identifier: 'postId'
   ]
+
+Comment._collection.allow
+  insert: (userId, doc) ->
+    !!userId
+  update: (userId, doc, fields, modifier) ->
+    doc.comment.authorId == userId
+  remove: (userId, doc) ->
+    doc.comment.authorId == userId


Set the evidence collection as addToCollection for comments

--- core/app/backbone/views/fact_relations/add_evidence_view.coffee
+++ core/app/backbone/views/fact_relations/add_evidence_view.coffee
@@ -41,7 +41,7 @@
     previewView

   addCommentView: ->
-    addCommentView = new AddCommentView( addToCollection: @model.comments(), type: @model.type() )
+    addCommentView = new AddCommentView( addToCollection: @model.evidence(), type: @model.type() )
     @bindTo addCommentView, 'switch_to_fact_relation_view', @switchToFactRelationView, @

     addCommentView


whitespace

whitespace

--- workers/social/lib/social/models/payment/base.coffee
+++ workers/social/lib/social/models/payment/base.coffee
@@ -12,7 +12,6 @@
   @create$ = permit 'manage products',
     success: (client, formData, callback) ->
       @create client.context.group, formData, callback
-

   @removeByCode = (planCode, callback) ->
     @one { planCode }, (err, product) ->


fixed coffeelint errors on workers/social/lib/social/models/systemstatus.coffee

--- workers/social/lib/social/models/systemstatus.coffee
+++ workers/social/lib/social/models/systemstatus.coffee
@@ -1,4 +1,5 @@
-{Model, Base, secure, daisy, signature} = require 'bongo'
+# coffeelint: disable=no_implicit_braces
+{ Model, Base, secure, daisy, signature } = require 'bongo'
 KodingError = require '../error'

 module.exports = class JSystemStatus extends Model
@@ -42,96 +43,96 @@
         type         : String
         default      : 'active'
         enum         : ['Invalid status',
-          ['active','stopped','paused']
+          ['active', 'stopped', 'paused']
         ]
       type           :
         type         : String
         default      : 'restart'
-        enum         : ['Invalid type', ['restart','info','reload','red','green','yellow']]
+        enum         : ['Invalid type', ['restart', 'info', 'reload', 'red', 'green', 'yellow']]

-  createKodingError =(err)->
+  createKodingError = (err) ->
     if 'string' is typeof err
-      kodingErr = message: err
+      kodingErr = { message: err }
     else
-      kodingErr = message: err.message
+      kodingErr = { message: err.message }
       for own prop of err
         kodingErr[prop] = err[prop]
     kodingErr

-  {log} = console
+  { log } = console

-  @forceReload = secure (client)->
-    {connection:{delegate}} = client
+  @forceReload = secure (client) ->
+    { connection:{ delegate } } = client
     unless delegate.checkFlag('super-admin')
       log 'status: not authorized to stop a system status'
       return
     @emit 'forceReload'

-  @stopCurrentSystemStatus = secure (client, callback=->)->
-    {connection:{delegate}} = client
+  @stopCurrentSystemStatus = secure (client, callback = -> ) ->
+    { connection:{ delegate } } = client
     unless delegate.checkFlag('super-admin')
       log 'status: not authorized to stop a system status'
-      return callback new KodingError "Not authorized to update a system status"
+      return callback new KodingError 'Not authorized to update a system status'

-    JSystemStatus.getCurrentSystemStatuses (err,status)=>
+    JSystemStatus.getCurrentSystemStatuses (err, status) ->
       # log err,status
       if err
         # log 'no status to stop'
-        callback err,status
+        callback err, status
       else
         status.update {
-          $set: status: 'stopped'
-        },(err)=>
+          $set: { status: 'stopped' }
+        }, (err) ->
           status.emit 'restartCanceled', {}
           callback err

-  @getCurrentSystemStatuses = (callback=->)->
+  @getCurrentSystemStatuses = (callback = -> ) ->
     JSystemStatus.some {
       status: 'active'
-      scheduledAt : $gt : new Date()
+      scheduledAt : { $gt : new Date() }
     }, {
-      sort: 'meta.createdAt': -1
-    }, (err,statuses)->
+      sort: { 'meta.createdAt': -1 }
+    }, (err, statuses) ->
       return callback new KodingError 'no queued messages available'  if err

       callback null, statuses

-  @create = secure (client, data, callback)->
-    {connection:{delegate}} = client
+  @create = secure (client, data, callback) ->
+    { connection:{ delegate } } = client
     unless delegate.checkFlag('super-admin')
-      return callback new KodingError "Not authorized to create a system status"
+      return callback new KodingError 'Not authorized to create a system status'

     status = new JSystemStatus data
-    status.save (err)->
-      JSystemStatus.emit 'restartScheduled',status  unless err
+    status.save (err) ->
+      JSystemStatus.emit 'restartScheduled', status  unless err
       callback err, status

   cancel: secure (client, callback) ->
-    {connection:{delegate}} = client
+    { connection:{ delegate } } = client
     unless delegate.checkFlag('super-admin')
-      return callback new KodingError "Not authorized to cancel a system status"
+      return callback new KodingError 'Not authorized to cancel a system status'

-    @update $set : status : 'stopped', (err)=>
+    @update { $set : { status : 'stopped' } }, (err) =>
       unless err
         @emit 'restartCanceled'
         callback()
       else
-        callback callback new KodingError "Could not cancel the system status"
+        callback callback new KodingError 'Could not cancel the system status'

   @healthCheck = secure (client, callback) ->
-    callback result:1
+    callback { result:1 }

   @checkRealtimeUpdates = secure (client, callback) ->
-    {connection: {delegate}} = client
-    delegate.sendNotification "healthCheck"
-    callback result:1
+    { connection: { delegate } } = client
+    delegate.sendNotification 'healthCheck'
+    callback { result:1 }

   @sendFeedback = secure (client, options, callback) ->
-    {connection: {delegate}} = client
-    {status, feedback, userAgent} = options
+    { connection: { delegate } } = client
+    { status, feedback, userAgent } = options

-    {recipientEmail} = KONFIG.troubleshoot
+    { recipientEmail } = KONFIG.troubleshoot

     delegate.fetchEmail client, (err, email) ->
       return callback err  if err
@@ -139,6 +140,8 @@
       Tracker.track delegate.profile.nickname, {
         to      : recipientEmail
         subject : Tracker.types.SENT_FEEDBACK
-      }, {status, userAgent, feedback, userEmail:email}
+      }, { status, userAgent, feedback, userEmail:email }

       callback null
+
+


Added CommentPopoverView to remove comment

--- core/app/backbone/views/comments/comment_view.coffee
+++ core/app/backbone/views/comments/comment_view.coffee
@@ -1,4 +1,7 @@
 #= require ./../evidence/evidence_view
+
+class CommentPopoverView extends EvidencePopoverView
+  delete_message: 'Remove this comment'

 class window.CommentEvidenceView extends EvidenceBaseView

@@ -6,11 +9,11 @@
     @userAvatarRegion.show new EvidenceUserAvatarView model: @model
     @activityRegion.show   new EvidenceActivityView model: @model

-    # @voteRegion.show new VoteUpDownView model: @model
     @mainRegion.show new CommentView model: @model

     if @model.get('can_destroy?')
-      @popoverRegion.show new FactRelationPopoverView model: @model
+      @popoverRegion.show new CommentPopoverView model: @model
+

 class window.CommentView extends Backbone.Marionette.ItemView


rename command

rename command

--- menus/atomizr.cson
+++ menus/atomizr.cson
@@ -26,7 +26,7 @@
         }
         {
           'label': 'Convert Sublime Text to Atom (snippet)'
-          'command': 'atomizr:convert-sublime-text-to-atom'
+          'command': 'atomizr:convert-sublime-text-snippet-to-atom'
         }
         { 'type': 'separator' },
         {


remove "Enable minimap" from context menu

We have a shotcut and the `Packages` menu, I think adding this to the context menu too is too much.

--- menus/minimap.cson
+++ menus/minimap.cson
@@ -1,8 +1,4 @@
 # See https://atom.io/docs/latest/creating-a-package#menus for more details
-'context-menu':
-  '.overlayer':
-      'Enable minimap': 'minimap:toggle'
-
 'menu': [
   {
     'label': 'Packages'


call deliver when needed

call deliver when needed

--- monocat.coffee
+++ monocat.coffee
@@ -41,6 +41,7 @@
         typeString = if type then " type=\"#{ type }\"" else ''
         $el.after("<script#{ typeString }>#{ uglify.minify($el.attr 'src').code }</script>").remove()
         deliver ++completed
+        deliver() if ++completed is total

       else if tagType is 'link' and $el.attr('rel') is 'stylesheet'
         path = $el.attr 'href'
@@ -52,12 +53,13 @@
           $el.after("<style>#{ cleanCss.process data.toString() }</style>").remove()
           deliver ++completed

+          deliver() if ++completed is total

-deliver = ->
-  return unless completed is len
       else
         deliver() if ++completed is total

+
+deliver = ->
   if args[3]
     outputFile = args[3]
   else


 Merge branch 'master.groups.minorFixes' into 'master.groups'

Master.Groups.Minor Fixes

--- client/Pricing/AppView.coffee
+++ client/Pricing/AppView.coffee
@@ -70,6 +70,7 @@

       if @formData.loggedIn
         thankYou.addSubView new KDButtonView
+          style    : "solid green"
           title    : "Go to Group"
           callback : ->
             window.open "#{window.location.origin}/#{group.slug}", "_blank"


add a few missing displayNames

add a few missing displayNames

--- tutor/src/components/exercises/cards.cjsx
+++ tutor/src/components/exercises/cards.cjsx
@@ -15,7 +15,7 @@
 ExercisePreview = require './preview'

 SectionsExercises = React.createClass
-
+  displayName: 'SectionsExercises'
   propTypes:
     ecosystemId:            React.PropTypes.string.isRequired
     exercises:              React.PropTypes.array.isRequired
@@ -44,7 +44,7 @@

 ExerciseCards = React.createClass
-
+  displayName: 'ExerciseCards'
   propTypes:
     ecosystemId:            React.PropTypes.string.isRequired
     exercises:              React.PropTypes.object.isRequired


Added tests for buckets watching

Added tests for buckets watching

--- tests/coffee/storage.test.coffee
+++ tests/coffee/storage.test.coffee
@@ -10,6 +10,8 @@
   usePolling: true
   ignored: "/rootDir/myDomain/*/**"

+chokidarWatchOn = td.function 'chokidar.watch.on'
+
 describe 'Storage', ->

   beforeEach ->
@@ -18,7 +20,7 @@
     storageLib  = td.replace '../../src/coffee/storage/lib.coffee'
     storage     = require '../../src/coffee/storage.coffee'
     td.when(chokidar.watch(td.matchers.contains("/rootDir/myDomain"), chokidarWatchOpts)).thenReturn
-      on: td.function('chokidar.watch.on')
+      on: chokidarWatchOn

   afterEach -> td.reset()

@@ -134,6 +136,9 @@
       domain: 'myDomain'
       docker: graph: path: '/docker/graph'
       datastore: scanEnabled: true
+    td.verify chokidarWatchOn 'addDir', td.matchers.isA Function
+    td.verify chokidarWatchOn 'unlinkDir', td.matchers.isA Function
+    td.verify chokidarWatchOn 'unlink', td.matchers.isA Function
     td.verify storageLib.listStorageBuckets argIsFs, '/rootDir/myDomain', captor.capture()
     captor.value null, 'some-buckets'
     td.verify mqtt.publish '/agent/storage/buckets', 'some-buckets'


I think this reads slightly more well.

I think this reads slightly more well.

--- miniLockLib.coffee
+++ miniLockLib.coffee
@@ -80,18 +80,18 @@

 # Calculate a curve25519 key pair for the given `secret` and `salt`.
 calculateCurve25519KeysFor = (secret, salt, callback) ->
+  # Decode and unpack the keys when the task is complete.
+  whenKeysAreReady = (encodedBytes) ->
+    decodedBytes = nacl.util.decodeBase64(encodedBytes)
+    keys = nacl.box.keyPair.fromSecretKey(decodedBytes)
+    callback(keys)
+
   # Define miniLock `scrypt` parameters for the calculation task:
   logN          = 17       # CPU/memory cost parameter (1 to 31).
   r             = 8        # Block size parameter. (I don’t know about this).
   dkLen         = 32       # Length of derived keys. (A miniLock key is 32 numbers).
   interruptStep = 1000     # Steps to split calculation with timeouts (default 1000).
   encoding      = 'base64' # Output encoding ('base64', 'hex', or null).
-
-  # Decode and unpack the keys when the task is complete.
-  whenKeysAreReady = (encodedBytes) ->
-    decodedBytes = nacl.util.decodeBase64(encodedBytes)
-    keys = nacl.box.keyPair.fromSecretKey(decodedBytes)
-    callback(keys)

   # Send the task to `scrypt` for processing...
   scrypt(secret, salt, logN, r, dkLen, interruptStep, whenKeysAreReady, encoding)


artik10.coffee: Reflect change to Yocto Morty

Also, change getting started links

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@resin.io>

--- artik10.coffee
+++ artik10.coffee
@@ -34,9 +34,9 @@
 	].concat(postProvisioningInstructions)

 	gettingStartedLink:
-		windows: 'http://docs.resin.io/#/pages/installing/gettingStarted-Samsung-Artik10.md#windows'
-		osx: 'http://docs.resin.io/#/pages/installing/gettingStarted-Samsung-Artik10.md#on-mac-and-linux'
-		linux: 'http://docs.resin.io/#/pages/installing/gettingStarted-Samsung-Artik10.md#on-mac-and-linux'
+		windows: 'https://docs.resin.io/artik10/nodejs/getting-started/#adding-your-first-device'
+		osx:' https://docs.resin.io/artik10/nodejs/getting-started/#adding-your-first-device'
+		linux: 'https://docs.resin.io/artik10/nodejs/getting-started/#adding-your-first-device'

 	supportsBlink: false

@@ -44,7 +44,7 @@
 		machine: 'artik10'
 		image: 'resin-image-flasher'
 		fstype: 'resinos-img'
-		version: 'yocto-jethro'
+		version: 'yocto-morty'
 		deployArtifact: 'resin-image-flasher-artik10.resinos-img'
 		compressed: true


Prevent errors on rooms without usernames

--- packages/rocketchat-lib/server/functions/sendMessage.coffee
+++ packages/rocketchat-lib/server/functions/sendMessage.coffee
@@ -13,7 +13,11 @@
 	message.rid = room._id

 	if not room.usernames? || room.usernames.length is 0
-		room = RocketChat.models.Rooms.findOneById(room._id)
+		updated_room = RocketChat.models.Rooms.findOneById(room._id)
+		if updated_room?
+			room = updated_room
+		else
+			room.usernames = []

 	if message.parseUrls isnt false
 		if urls = message.msg.match /([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g


Fix per-project configuration behaviour (#201)

* Fix per-project configuration behaviour

Override user settings only if per-project config files exist (using the first encountered one).

* Fix the way configuration file path is fetched

Now the associated project path is calculated from the linted file path.

* Simplify solution with @Arcanemagus suggestions

--- lib/main.coffee
+++ lib/main.coffee
@@ -201,18 +201,24 @@
         fileText = textEditor.getText()
         parameters = ['--format=default']

-        if maxLineLength = atom.config.get('linter-flake8.maxLineLength')
-          parameters.push('--max-line-length', maxLineLength)
-        if (ignoreErrorCodes = atom.config.get('linter-flake8.ignoreErrorCodes')).length
-          parameters.push('--ignore', ignoreErrorCodes.join(','))
-        if maxComplexity = atom.config.get('linter-flake8.maxComplexity')
-          parameters.push('--max-complexity', maxComplexity)
-        if atom.config.get('linter-flake8.hangClosing')
-          parameters.push('--hang-closing')
-        if (selectErrors = atom.config.get('linter-flake8.selectErrors')).length
-          parameters.push('--select', selectErrors.join(','))
-        if (projectConfigFile = atom.config.get('linter-flake8.projectConfigFile'))
-          parameters.push('--config', path.join(atom.project.getPaths()[0], projectConfigFile))
+        if (
+          (projectConfigFile = atom.config.get('linter-flake8.projectConfigFile')) and
+          (projectPath = atom.project.relativizePath(filePath)[0]) and
+          (configFilePath = helpers.findCached(projectPath, projectConfigFile.split(/[ ,]+/)))
+        )
+          parameters.push('--config', path.join(projectPath, configFilePath))
+        else
+          if maxLineLength = atom.config.get('linter-flake8.maxLineLength')
+            parameters.push('--max-line-length', maxLineLength)
+          if (ignoreErrorCodes = atom.config.get('linter-flake8.ignoreErrorCodes')).length
+            parameters.push('--ignore', ignoreErrorCodes.join(','))
+          if maxComplexity = atom.config.get('linter-flake8.maxComplexity')
+            parameters.push('--max-complexity', maxComplexity)
+          if atom.config.get('linter-flake8.hangClosing')
+            parameters.push('--hang-closing')
+          if (selectErrors = atom.config.get('linter-flake8.selectErrors')).length
+            parameters.push('--select', selectErrors.join(','))
+
         parameters.push('-')

         fs = require('fs-plus')


Cache text

Cache text

--- lib/Refactoring.coffee
+++ lib/Refactoring.coffee
@@ -79,5 +79,8 @@
   ###

   parse: =>
-    @ripper.parse @editor.buffer.getText()
+    text = @editor.buffer.getText()
+    if text isnt @cachedText
+      @cachedText = text
+      @ripper.parse text
     @emit 'parsed'


Allow use of core.commentchar when creating git commit message

Fixes #81

--- lib/models/git-commit.coffee
+++ lib/models/git-commit.coffee
@@ -8,6 +8,13 @@

 module.exports =
 class GitCommit extends Model
+
+  # Public: Helper method to set the what commentchar to be used in the commit message
+  setCommentChar: (char) ->
+    if char == ''
+      char = '#'
+    @commentchar = char
+    return

   # Public: Helper method to return the name of the file we should write our
   #         commit message to.
@@ -47,6 +54,17 @@
     # This sets @isAmending to check if we are amending right now.
     @isAmending = @amend.length > 0

+    # This loads the commentchar from the config, if one is not set or not found it will default
+    # back to #
+    git.cmd
+      args: ['config', '--get', 'core.commentchar'],
+      stdout: (data) =>
+        @setCommentChar data.trim()
+        return
+      stderr: =>
+        @setCommentChar '#'
+        return
+
     git.stagedFiles (files) =>
       if @amend isnt '' or files.length >= 1
         git.cmd
@@ -63,13 +81,13 @@
   prepFile: (status) ->
     # format the status to be ignored in the commit message
     status = status.replace(/\s*\(.*\)\n/g, '')
-    status = status.trim().replace(/\n/g, "\n# ")
+    status = status.trim().replace(/\n/g, "\n#{@commentchar} ")
     fs.writeFileSync @filePath(),
        """#{@amend}
-        # Please enter the commit message for your changes. Lines starting
-        # with '#' will be ignored, and an empty message aborts the commit.
-        #
-        # #{status}"""
+        #{@commentchar} Please enter the commit message for your changes. Lines starting
+        #{@commentchar} with '#{@commentchar}' will be ignored, and an empty message aborts the commit.
+        #{@commentchar}
+        #{@commentchar} #{status}"""
     @showFile()

   # Public: Helper method to open the commit message file and to subscribe the


autofocus inserted elements

autofocus inserted elements

--- lib/assets/javascripts/up/flow.js.coffee
+++ lib/assets/javascripts/up/flow.js.coffee
@@ -80,6 +80,7 @@
             # Remember where the element came from so we can make
             # smaller page loads in the future (does this even make sense?).
           setSource($new, options.source || history.url)
+          autofocus($new)

       else
         up.util.error("Could not find selector (#{step.selector}) in response (#{html})")
@@ -111,7 +112,11 @@
       selector: selectorAtom
       transition: transition

-
+  autofocus = ($element) ->
+    $control = $element.find('[autofocus]:last')
+    if $control.length && $control.get(0) != document.activeElement
+      $control.focus()
+
   ###*
   Destroys the given element or selector.
   Takes care that all destructors, if any, are called.


Check structure, not content, of source maps.

--- lib/test.coffee
+++ lib/test.coffee
@@ -30,12 +30,28 @@
 sum = (expected = '')->
     expectedSum = shasum(expected)
     (res)->
-        expected.should.equal(res.text) if DEBUG=1
-        expectedSum.should.equal(shasum(res.text or ''))
+        if DEBUG=1
+            try
+                expected.should.equal(res.text) if DEBUG=1
+            catch e
+                console.log e
+        expectedSum.should.equal(shasum(res.text or ''), 'Sums match.')
         false

 loadFixture = (fixture)->
     sum(fs.readFileSync("#{__dirname}/../test/fixtures/#{fixture}", 'utf-8'))
+
+checkMap = (fixture)->
+    src = fs.readFileSync("#{__dirname}/../test/fixtures/#{fixture}", 'utf-8')
+    expected = JSON.parse(src.substr(4))
+
+    (res)->
+        mapSrc = res.text.substr(4)
+        map = JSON.parse(mapSrc)
+        map.sections.length.should.equal
+            expected.sections.length,
+            'Sourcemap sections match.'
+        false

 describe "DS Asset Middleware", ->
     before (done)->
@@ -99,7 +115,7 @@
             request(app)
             .get('/all.css.map')
             .expect(200)
-            .expect(loadFixture('all.css.map'))
+            .expect(checkMap('all.css.map'))
             .end(done)

     describe "Application", ->
@@ -126,7 +142,7 @@
             request(app)
             .get('/app.js.map')
             .expect(200)
-            .expect(loadFixture('application.js.map'))
+            .expect(checkMap('application.js.map'))
             .end(done)

     describe "Vendors", ->
@@ -161,5 +177,5 @@
                 request(app)
                 .get('/vendors.css.map')
                 .expect(200)
-                .expect(loadFixture('vendors.css.map'))
+                .expect(checkMap('vendors.css.map'))
                 .end(done)


Null guard auto-prefixing of '.' to scopes for backward compatibility

In some cases, packages such as atom-beautify manually construct scope
descriptors with an empty scopes array.

--- src/scope-descriptor.coffee
+++ src/scope-descriptor.coffee
@@ -41,7 +41,7 @@
   getScopeChain: ->
     # For backward compatibility, prefix TextMate-style scope names with
     # leading dots (e.g. 'source.js' -> '.source.js').
-    if @scopes[0].includes('.')
+    if @scopes[0]?.includes('.')
       result = ''
       for scope, i in @scopes
         result += ' ' if i > 0


:lipstick:

:lipstick:

--- lib/atom-ternjs.coffee
+++ lib/atom-ternjs.coffee
@@ -110,14 +110,14 @@
       if !@client
         @client = new TernClient()
       @client.port = port
-      if (!@provider)
+      if !@provider
         @init()

   registerEvents: ->
     @disposables.push atom.workspace.observeTextEditors (editor) =>
       @disposables.push editor.onDidChangeCursorPosition (event) =>
-        if !event.textChanged
-          @documentationView.hide()
+        return unless event.textChanged
+        @documentationView.hide()
     @disposables.push atom.workspace.onDidChangeActivePaneItem =>
       @documentationView.hide()
     @disposables.push atom.config.observe 'atom-ternjs.coffeeScript', =>


Variable typo

Variable typo

--- client/client_timeout.coffee
+++ client/client_timeout.coffee
@@ -1,6 +1,6 @@
 if Meteor.isClient
     session_heartbeat = null
-    session_checkInterval = Meteor.settings.purgeInterval || 3000
+    session_purgeInterval = Meteor.settings.purgeInterval || 3000
     Deps.autorun () ->
       ###
       # The way this works is by setting up an interval that will


Teambuilder: Hardy is now neutral nature

--- client/app/js/models/pokemon.coffee
+++ client/app/js/models/pokemon.coffee
@@ -57,6 +57,7 @@
     @set('ability', @getAbilities()[0])  unless attributes.ability
     @set('level', 100)  unless attributes.level
     @set('happiness', 100)  unless attributes.happiness
+    @set('nature', 'Hardy')  unless attributes.nature
     hiddenPowerType = HiddenPower.BW.type(@get('ivs')).toLowerCase()
     @set('hiddenPowerType', hiddenPowerType, silent: true)


camelCase

camelCase

--- lib/modx-component-generator-view.coffee
+++ lib/modx-component-generator-view.coffee
@@ -95,15 +95,15 @@
       else if fs.isFileSync(templateChildPath)
         fs.makeTreeSync(path.dirname(sourcePath))
         contents = fs.readFileSync(templateChildPath).toString()
-        contents = @replacecomponentNamePlaceholders(contents, componentName)
-        contents = @replacecomponentAuthorPlaceholders(contents, componentAuthor)
+        contents = @replaceComponentNamePlaceholders(contents, componentName)
+        contents = @replaceComponentAuthorPlaceholders(contents, componentAuthor)
         fs.writeFileSync(sourcePath, contents)
         callback()

-  replacecomponentAuthorPlaceholders: (string, componentAuthor) ->
+  replaceComponentAuthorPlaceholders: (string, componentAuthor) ->
     string.replace(/__component-author__/g, componentAuthor)

-  replacecomponentNamePlaceholders: (string, componentName) ->
+  replaceComponentNamePlaceholders: (string, componentName) ->
     placeholderRegex = /__(?:(component-name)|([cC]omponentName)|(component))__/g
     string = string.replace placeholderRegex, (match, dash, camel, underscore) =>
       if dash


move default value of lastVersion into function body


added new handlebars helper: "bind-style"


 Merge branch 'master.oauthFlag' into 'master'

Master.Oauth Flag

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -129,13 +129,19 @@
       tagName     : "h4"
       cssClass    : "form-header"

-    @github          = new KDCustomHTMLView
-      tagName     : "a"
-      cssClass    : "github-login"
-      partial     : "Sign in using GitHub"
-      click       : ->
-        KD.mixpanel "Github auth button in /Login, click"
-        KD.singletons.oauthController.openPopup "github"
+    if KD.utils.oauthEnabled() is yes
+      @github          = new KDCustomHTMLView
+        tagName     : "a"
+        cssClass    : "github-login"
+        partial     : "Sign in using GitHub"
+        click       : ->
+          KD.mixpanel "Github auth button in /Login, click"
+          KD.singletons.oauthController.openPopup "github"
+    else
+      @github = new KDCustomHTMLView
+        tagName     : "a"
+        cssClass    : "github-login"
+        partial     : "Learn more"

     @github.setPartial ""


Fix user validation in campaign

Fix user validation in campaign

--- lib/collections.coffee
+++ lib/collections.coffee
@@ -37,6 +37,7 @@
 		type: Object
 		label: 'Criado por'
 		autoValue: ->
+			if @value then return @value
 			return {
 				_id: @userId
 				name: Users.findOne(@userId).profile.name or Users.findOne(@userId).emails[0].address


Fix SEO configuration

Fix SEO configuration

--- client/project/project.coffee
+++ client/project/project.coffee
@@ -77,11 +77,10 @@
       title = "#{data.owner}/#{data.projectId}"
       logger.debug("Setting SEO properties")
       SEO.set({
-        author: data.owner,
-        title: title,
-        description: data.description,
-        instructions: data.instructions,
-        tags: S.join(",", data.tags),
+        title: title
+        meta: {
+          description: data.description
+        }
       })
     else
       logger.debug("@data is not defined, cannot set SEO properties")


Tests: Return user in beginTest helper.

Tests: Return user in beginTest helper.

--- tests/src/helpers/helpers.coffee
+++ tests/src/helpers/helpers.coffee
@@ -16,6 +16,8 @@
     @doLogin(browser, user)

     browser.execute 'KD.isTesting = true;'
+
+    return user

   doLogin: (browser, user) ->


using 'is' instead of '=='

using 'is' instead of '=='

--- src/scripts/modules/media/editbar/modals/publish.coffee
+++ src/scripts/modules/media/editbar/modals/publish.coffee
@@ -81,7 +81,7 @@
       requiredCheckboxes = @$el.find('input[type="checkbox"].required')
       requiredCheckboxesThatAreChecked = @$el.find('.required:checked')

-      if requiredCheckboxes.length == requiredCheckboxesThatAreChecked.length and requiredTextBox.length > 0
+      if requiredCheckboxes.length is requiredCheckboxesThatAreChecked.length and requiredTextBox.length > 0
         submitBtn.removeAttr('disabled')
       else
         submitBtn.prop('disabled',true)


add chart formatter for combined bar charts

--- src/services/chart-formatter/chart-formatter.svc.coffee
+++ src/services/chart-formatter/chart-formatter.svc.coffee
@@ -118,6 +118,44 @@
       }

+    # Bar Chart of several datasets with several values each
+    # ----------
+    # inputData:
+    #   labels: ['Company A', 'Company B']
+    #   datasets:
+    #     [
+    #       {title: 'ASSET', values: [1566,56156]}
+    #       {title: 'LIABILITY', values: [67868,686]}
+    #     ]
+    # positivesOnly: if true (default), negative bars will be turned into their opposite
+    @combinedBarChart = (inputData, opts={}, positivesOnly=true) ->
+      index = 0
+      result = {
+        chartType: 'Bar',
+        options: opts,
+        data: {
+          labels: inputData.labels
+          datasets: _.map inputData.datasets, (dataset) ->
+            color = _self.getColor(index)
+            index++
+
+            if positivesOnly
+              for value in dataset.values
+                value = Math.abs(value)
+
+            return {
+              label: dataset.title,
+              data: dataset.values,
+              fillColor: color,
+              strokeColor: color,
+              highlightFill: color,
+              highlightStroke: color,
+            }
+        }
+      }
+      return result
+
+
     # inputData:
     # [ {
     #     label: "Customer 1",


Update ageOfConsent test now that Bulgaria changed its age of consent

--- test/app/core/utils.spec.coffee
+++ test/app/core/utils.spec.coffee
@@ -68,7 +68,7 @@
   describe 'inEU', ->
     it 'EU countries return true', ->
       euCountries = ['Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'United Kingdom']
-      try
+      try
         euCountries.forEach((c) -> expect(utils.inEU(c)).toEqual(true))
       catch err
         # NOTE: without try/catch, exceptions do not yield failed tests.
@@ -76,7 +76,7 @@
         expect(err).not.toBeDefined()
     it 'non-EU countries return false', ->
       nonEuCountries = ['united-states', 'peru', 'vietnam']
-      try
+      try
         nonEuCountries.forEach((c) -> expect(utils.inEU(c)).toEqual(false))
       catch err
         expect(err).not.toBeDefined()
@@ -93,7 +93,7 @@
     it 'Slovakia is 16', ->
       expect(utils.ageOfConsent('slovakia')).toEqual(16)
     it 'default for EU countries 16', ->
-      expect(utils.ageOfConsent('bulgaria')).toEqual(16)
+      expect(utils.ageOfConsent('israel')).toEqual(16)
     it 'default for other countries is 0', ->
       expect(utils.ageOfConsent('hong-kong')).toEqual(0)
     it 'default for unknown countries is 0', ->


Implement NestedAttributesStrategy for autocompleteTokenField widget

--- app/assets/javascripts/exo/widgets/auto_complete_token_field.js.coffee
+++ app/assets/javascripts/exo/widgets/auto_complete_token_field.js.coffee
@@ -1,5 +1,7 @@
 namespace 'Exo.Widgets.AutocompleteTokenField', (exports) ->

+  # TODO add docs for all of these!!!
+
   exports.ReplaceIdStrategy = (tokens) ->
     if id = tokens[0]?.id
       @originalInput.value = id
@@ -15,12 +17,31 @@
       @el.appendChild(input)
     return

-  exports.NestedAttributesStrategy = (instance, tokens) ->
+  exports.NestedAttributesStrategy = (tokens) ->
+    token = tokens[0]
+
+
+    if !token.isNew()
+      # If token is an existing object than convert nested attributes to ID attribute instead
+      #
+      # Example:
+      #
+      # employee[employer_attributes][name]
+      #
+      # becomes
+      #
+      # employee[employer_id]
+
+      replaceNestedAttributesRegex = /_attributes](\[\w+\])+$/
+
+      @originalInput.name = @originalInput.name.replace(replaceNestedAttributesRegex, "_id]")
+      @originalInput.value = token.id
+    else
+      @originalInput.value = token.get("query")

   klass = class extends Exo.Views.AutocompleteTokenField
-
     constructor: (el, options = {}) ->
-      @serializeStrategy = options.serializeStrategy || exports.NestedAttributesStrategy
+      @serializeStrategy = options.serializeStrategy || exports.ArrayStrategy

       options.input = el[0]
       super(options)
@@ -41,6 +62,6 @@

       @on "add remove", (tokens) =>
         tokens = if tokens instanceof Array then tokens else [tokens]
-        @serializeStrategy.call(this, tokens)
+        @serializeStrategy.call(this, tokens, options)

   Exo.Widget.register("autoCompleteTokenField", klass)


[content] fix for profile pages

[content] fix for profile pages

--- coffee/content/profile.coffee
+++ coffee/content/profile.coffee
@@ -74,7 +74,7 @@
                                 .append(
                                     $ items.map ->
                                             $ '
'
-                                                .append(this)[0]
+                                                .append(@)[0]
                                 )
                         )
                 )
@@ -176,8 +176,7 @@

             $profile.append helper.createSubtitle 'Errors', $ player.err.map (e) ->
                 $ '

'
-                    .text e
-                    [0]
+                    .text(e)[0]

         customExtra.keys().filter (e) ->
                 v = customExtra[e]
@@ -186,8 +185,7 @@
             .each (name) ->
                 $profile.append helper.createSubtitle name.humanize().pluralize(), $ customExtra[name].map (value) ->
                     $ '

'
-                        .text value
-                        [0]
+                        .text(value)[0]

     module.checkProfile = ->
         $tabs = $ '#contentPane div[role="tabpanel"]'
@@ -253,5 +251,5 @@
             $elem.attr 'data-iidentity', dot
             $root.attr 'data-iidentity', null

-            createProfile player, $elem
+            create player, $elem
 )(iidentity or (iidentity = window.iidentity = {}), window.jQuery)


Update cards-es.coffee

Update cards-es.coffee

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -585,11 +585,11 @@
         "Warden Squadron Pilot":
             ship: 'Ala-K'
         '"Redline"':
-            name: ""Velocidad Terminal""
+            name: '"Velocidad Terminal"'
             ship: 'Castigador TIE'
             text: """Puedes mantener 2 blancos fijados sobre una misma nave. Cuando fijes un blanco, puedes fijar la misma nave como blanco por segunda vez."""
         '"Deathrain"':
-            name: ""Lluvia de Muerte""
+            name: '"Lluvia de Muerte"'
             ship: 'Castigador TIE'
             text: """Cuando sueltes una bomba, puedes usar los salientes frontales de la peana de tu nave. Puedes realizar una acción gratuita de tonel volado después de soltar una bomba."""
         'Black Eight Squadron Pilot':


allow number strings as shorthand

allow number strings as shorthand

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -446,13 +446,13 @@

   _getLonghandAnchor: (shorthand) ->
     switch shorthand
-      when 'left', 'l', 4
+      when 'left', 'l', '4', 4
         'left'
-      when 'right', 'r', 2
+      when 'right', 'r', '2', 2
         'right'
-      when 'top', 't', 1
+      when 'top', 't', '1', 1
         'top'
-      when 'bottom', 'b', 3
+      when 'bottom', 'b', '3', 3
         'bottom'
       else
         'left'


add 3d perspective on stages

add 3d perspective on stages

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -131,6 +131,7 @@
     stage.style.position = 'absolute'
     stage.style.padding = '0'
     stage.style.margin = '0'
+    stage.style[css.perspective] = @settings.perspective + 'px'

     for axis in @axes
@@ -270,7 +271,6 @@
     @el.style.width = @width + 'px'
     @el.style.height = @height + 'px'
     @el.style.backgroundColor = 'transparent'
-    @el.style[css.perspective] = @settings.perspective
     @stages.left.style.display = 'block'
     @el.innerHTML = ''


default intensity == 1

default intensity == 1

--- lib/assets/javascripts/shaders/light_ambient/material.js.coffee
+++ lib/assets/javascripts/shaders/light_ambient/material.js.coffee
@@ -3,6 +3,7 @@
     # refers to the material's ambient component, so
     # default full white just means use world ambient
     @color = Jax.Color.parse '#ffff'
+    @intensity = 1
     super options

   illuminate: (context, mesh, model, vars, light) ->


:bug: Lint on Project Paths change

:bug: Lint on Project Paths change

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -27,8 +27,9 @@
       @views.setUnderlineIssues(underlineIssues)
     @_subscriptions.add atom.config.observe 'linter.lintOnFly', (value) =>
       @lintOnFly = value
+    @_subscriptions.add atom.project.onDidChangePaths =>
+      @commands.lint()
     @_subscriptions.add atom.workspace.onDidChangeActivePaneItem =>
-      # Exceptions thrown here prevent switching tabs
       @commands.lint()

     @_subscriptions.add atom.workspace.observeTextEditors (editor) =>


Add version 1.0

Add version 1.0

--- Dom.coffee
+++ Dom.coffee
@@ -1,3 +1,9 @@
+###
+Dom.coffee
+v1.0
+Written at Phyramid (http://phyramid.com)
+###
+
 class Dom
   ###
   Stores display data for elements (used for `show()` and `hide()`).


display error ids if present

display error ids if present

--- tutor/src/components/error-monitoring/server-error-message.cjsx
+++ tutor/src/components/error-monitoring/server-error-message.cjsx
@@ -1,4 +1,5 @@
 React = require 'react'
+map = require 'lodash/map'
 UserMenu = require('../../models/user/menu').default

 SUPPORT_LINK_PARAMS = '&cu=1&fs=ContactUs&q='
@@ -38,18 +39,23 @@
     debug: true

   render: ->
-    {status, statusMessage, config, supportLinkBase, debug} = @props
+    {status, statusMessage, config, supportLinkBase, debug, data} = @props
     statusMessage ?= 'No response was received'
     q = makeContactMessage(status, statusMessage, config)

-    dataMessage =
+    errorsMessage =
+
+        {if data?.errors then map(data.errors, 'code').join(', ') else statusMessage}
+
+
+    dataMessage =
       with
{config.data}

      if config.data?

     debugInfo = [

Additional error messages returned from the server is:

-
{statusMessage}

-

+
{errorsMessage}

+

         {config.method} on {config.url} {dataMessage}

     ] if debug


Use atom.requireWithGlobals

Use atom.requireWithGlobals

--- lib/theme-panel.coffee
+++ lib/theme-panel.coffee
@@ -1,16 +1,10 @@
 {_, $, $$, View} = require 'atom'

-###
-# Internal #
-###
-
-window.jQuery = $
-require 'jqueryui-browser/ui/jquery.ui.core'
-require 'jqueryui-browser/ui/jquery.ui.widget'
-require 'jqueryui-browser/ui/jquery.ui.mouse'
-require 'jqueryui-browser/ui/jquery.ui.sortable'
-require 'jqueryui-browser/ui/jquery.ui.draggable'
-delete window.jQuery
+atom.requireWithGlobals require.resolve('jqueryui-browser/ui/jquery.ui.core'), {jQuery: $}
+atom.requireWithGlobals require.resolve('jqueryui-browser/ui/jquery.ui.widget'), {jQuery: $}
+atom.requireWithGlobals require.resolve('jqueryui-browser/ui/jquery.ui.mouse'), {jQuery: $}
+atom.requireWithGlobals require.resolve('jqueryui-browser/ui/jquery.ui.sortable'), {jQuery: $}
+atom.requireWithGlobals require.resolve('jqueryui-browser/ui/jquery.ui.draggable'), {jQuery: $}

 module.exports =
 class ThemeConfigPanel extends View


fix defaults

fix defaults

--- lib/understream.coffee
+++ lib/understream.coffee
@@ -40,7 +40,7 @@

 class Understream
   constructor: (head) ->
-    @defaults = highWaterMark: 1000, objectMode: true
+    @_defaults = highWaterMark: 1000, objectMode: true
     head = new ArrayStream {}, head if _(head).isArray()
     if is_readable head
       @_streams = [head]
@@ -49,7 +49,7 @@
     else
       throw new Error 'Understream expects a readable stream, an array, or nothing'

-  defaults: (@defaults) =>
+  defaults: (@_defaults) => @
   run: (cb) =>
     throw new Error 'Understream::run requires an error handler' unless _(cb).isFunction()
     report = =>
@@ -98,9 +98,9 @@
       else
         # If this is a class and argument length is < constructor length, prepend defaults to arguments list
         if args.length < FunctionOrStreamKlass.length
-          args.unshift _(@defaults).clone()
+          args.unshift _(@_defaults).clone()
         else if args.length is FunctionOrStreamKlass.length
-          _(args[0]).defaults @defaults
+          _(args[0]).defaults @_defaults
         else
           throw new Error "Expected #{FunctionOrStreamKlass.length} or #{FunctionOrStreamKlass.length-1} arguments to #{name}, got #{args.length}"
         instance = new FunctionOrStreamKlass args...


Atom config update

Atom config update

--- link/.atom/config.cson
+++ link/.atom/config.cson
@@ -5,11 +5,7 @@
       beautifyEntireFileOnSave: false
   "atom-material-ui":
     colors:
-      abaseColor:
-        red: 92
-        green: 107
-        blue: 192
-        alpha: 1
+      abaseColor: "#5c6bc0"
       predefinedColor: "Indigo"
     fonts: {}
     tabs:


Some fixes to last commit

Some fixes to last commit

--- app/locale/zh-HANT.coffee
+++ app/locale/zh-HANT.coffee
@@ -403,6 +403,14 @@
     heroes: "更多強大的英雄!"
     gems: "每月3500寶石獎勵!"
     items: "超過250物品獎勵!"
+#    parent_button: "Ask your parent"
+#    parent_email_description: "We'll email them so they can buy you a CodeCombat subscription."
+#    parent_email_input_invalid: "Email address invalid."
+#    parent_email_input_label: "Parent email address"
+#    parent_email_input_placeholder: "Enter parent email"
+#    parent_email_send: "Send Email"
+#    parent_email_sent: "Email sent!"
+#    parent_email_title: "What's your parent's email?"
     parents: "致家長"
     parents_title: "您的孩子將學習編寫程式."
     parents_blurb1: "使用CodeCombat, 您的孩子學習真正的編寫程式. 他們學習從簡單的指令，漸進到更加進階的課題."


debug...

debug...

--- app/models/mailbox.coffee
+++ app/models/mailbox.coffee
@@ -15,7 +15,7 @@

 # Delete mailbox and everthing related mails, mailToBes, attachments,
 # accounts...
-Mailbox::remove =>
+Mailbox::remove = (callback) ->
     @log "destroying box..."
     box.destroyMails (err) =>
         @log err if err


kill old js

kill old js

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -1,176 +1,2 @@
 #= require tabs
 #= require master-map
-render = ->
-#   width       = $('#master-map').width()
-#   height      = 1200
-#   projection  = d3.geo.albersUsa().scale(1).translate [ 0, 0 ]
-#   path        = d3.geo.path().projection(projection)
-#   fill        = d3.scale.log().clamp(true).range ['#111', '#00ff00']
-#   starCount   = 'total'
-#   centered    = null
-#   geometries  = null
-#   colors      = ['#a634f4', '#f1f42f', '#bcf020', '#eeb016', '#ec180c']
-
-#   map = d3.select('#master-map').append('svg')
-#     .attr('width', width)
-#     .attr('height', height)
-
-#   tip = d3.tip().attr('class', 'd3-tip').offset([-10, 0]).html (d) ->
-#     if d.properties?
-#       p = d.properties
-#       name = if /county/i.test(p.name) then p.name else "#{p.name} County"
-#       "
#{name}

#{p[starCount] || 0} athletes.
"
-#     else if d.weight?
-#       "
#{d.stars} ☆ #{d.name} - #{d.year}

#{d.weight}lb #{d.position} from #{d.school} in #{d.location}"
-#     else
-#       "
#{d.team}

#{d.stadium} in #{d.city}, #{d.state}
"
-
-#   map.call(tip)
-
-#   onCountyClick = (d) ->
-#     if d && centered != d
-#       [x,y] = path.centroid(d)
-#       centered = d
-#       k = 4
-#     else
-#       x = width / 2
-#       y = height / 2
-#       centered = null
-#       k = 1
-
-#     geometries.selectAll('path')
-#       .classed('active', centered && ((d) -> d == centered))
-
-#     translate = " translate(#{width / 2},#{height / 2})
-#         scale(#{k})
-#         translate({#{-x},#{-y})"
-
-#     geometries.transition()
-#       .duration(750)
-#       .attr('transform', "
-#         translate(#{width / 2},#{height / 2})
-#         scale(#{k})
-#         translate(#{-x},#{-y})")
-#       .style('stroke-width', "#{1.5 / k}px")
-
-#   $.when($.ajax('/data/counties.json'),
-#          $.ajax('/data/schools.csv'),
-#          $.ajax('/data/recruits.csv')).then (r1, r2, r3) ->
-
-#     usa      = r1[0]
-#     schools = d3.csv.parse(r2[0])
-#     recruits = d3.csv.parse(r3[0])
-
-#     # Convert to GeoJSON
-#     states   = topojson.mesh usa, usa.objects.states, (a, b) -> a.id != b.id
-#     counties = topojson.feature usa, usa.objects.counties
-#     nation   = topojson.mesh usa, usa.objects.nation
-
-
-#     fill.domain [0.1, d3.max(counties.features, (d) -> d.properties[starCount])]
-
-#     # Auto scale map based on bounds
-#     projection.scale(1).translate([0, 0])
-#     b = path.bounds(nation)
-#     s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height)
-#     t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]
-#     projection.scale(s).translate(t)
-
-#     geometries = map.append('g')
-
-#     # Add counties
-#     geometries.append('g')
-#       .attr('class', 'counties')
-#     .selectAll('path.county')
-#       .data(counties.features)
-#     .enter().append('path')
-#       .attr('class', 'county')
-#       .style('fill', (d) -> fill(d.properties[starCount] || 0))
-#       .attr('d', path)
-#       .on('mouseover', tip.show)
-#       .on('mouseout', tip.hide)
-#       .on('click', onCountyClick)
-#       .style('stroke', (d) ->
-#         stars = d.properties[starCount] || 0
-#         if stars > 0 then fill(stars) else '#333')
-
-#     # Add states mesh
-#     geometries.append('path')
-#       .datum(states)
-#       .attr('class', 'states')
-#       .attr('d', path)
-
-#     # Add nation mesh
-#     geometries.append('path')
-#       .datum(nation)
-#       .attr('class', 'nation')
-#       .attr('d', path)
-
-#     for recruit in recruits
-#       recruit.coordinates = projection [recruit.lat, recruit.lon]
-
-#     # recgroup = geometries.append('g')
-#     #   .attr('class', 'recruits')
-
-#     # recs = recgroup.selectAll('circle.recruit')
-#     #   .data(recruits)
-#     # .enter().append('circle')
-#     #   .attr('cx', (d) -> d.position[0])
-#     #   .attr('cy', (d) -> d.position[1])
-#     #   .attr('r', 1)
-#     #   .style('fill', '#ff00ff')
-#     #   .style('fill-opacity', 0)
-#     #   .attr('class', (d) -> d.school.toLowerCase().replace(' ', '-'))
-
-# # svg.append("path")
-# #     .datum({type: "LineString", coordinates: [[-77.05, 38.91], [116.35, 39.91]]})
-# #     .attr("class", "arc")
-# #     .attr("d", path)
-
-#     for school in schools
-#       school.position = projection [school.lat, school.lon]
-
-#     geometries.selectAll('.schools')
-#       .data(schools)
-#     .enter().append('circle')
-#       .attr('cx', (d) -> d.position[0])
-#       .attr('cy', (d) -> d.position[1])
-#       .attr('r', 4)
-#       .attr('class', 'school')
-#       .classed('gt', (d) -> d.name == 'Georgia Tech')
-#       .on('mouseover', tip.show)
-#       .on('mouseout', tip.hide)
-#       .on('click', (d) ->
-#         players = recruits.filter (r) -> r.institution in [d.team, d.alt]
-#         features = players.map (p) ->
-#           type: "LineString"
-#           coordinates: [[d.lat, d.lon], [p.lat, p.lon]]
-#           properties: p
-
-#         connections = geometries.selectAll('.connection')
-#           .data(features, (d) -> "#{d.properties.name}:#{d.properties.school}")
-
-#         connections.enter().append('path')
-#           .attr('class', 'connection')
-#           .attr('d', path)
-#           .style('stroke', (d) -> colors[d.properties.stars - 1])
-
-#         connections.exit().remove()
-
-#         recs = geometries.selectAll('.recruit')
-#           .data(players, (d) -> "#{d.name}:#{d.school}")
-
-#         recs.enter().append('circle')
-#           .attr('cx', (d) -> d.coordinates[0])
-#           .attr('cy', (d) -> d.coordinates[1])
-#           .attr('r', 2)
-#           .style('fill', (d) -> console.log d.stars; colors[d.stars - 1])
-#           .attr('class', 'recruit')
-#           .on('mouseover', tip.show)
-#           .on('mouseout', tip.hide)
-
-#         recs.exit().remove()
-
-#       )
-
-$(render)


KodingKite: add connect/disconnect helpers

--- client/Main/kite/kodingkite.coffee
+++ client/Main/kite/kodingkite.coffee
@@ -38,3 +38,6 @@
       @::[method] = @createMethod @prototype, { method, rpcMethod }

   @constructors = {}
+
+  connect:    -> @transport?.connect()
+  disconnect: -> @transport?.disconnect()


terminal: fix property name typo

terminal: fix property name typo

--- client/Terminal/webtermview.coffee
+++ client/Terminal/webtermview.coffee
@@ -83,8 +83,8 @@
     return vmName

   webtermConnect:(mode)->
-    return console.info "reconnection is in progrees" if @reconnectionInPrgress
-    @reconnectionInPrgress = yes
+    return console.info "reconnection is in progress" if @reconnectionInProgress
+    @reconnectionInProgress = yes
     options = @generateOptions()
     options.mode = mode   if mode
     KD.getSingleton("vmController").run
@@ -95,14 +95,14 @@
       if err
         warn err
         if err.code is "ErrInvalidSession"
-          @reconnectionInPrgress = false
+          @reconnectionInProgress = false
           @emit 'TerminalCanceled',
             vmName: @getVMName()
             sessionId: @getOptions().session
             error: err
           return
         else
-          @reconnectionInPrgress = false
+          @reconnectionInProgress = false
           throw err

       unless remote?
@@ -116,7 +116,7 @@

       @emit "WebTermConnected", remote
       console.error "just connected"  if mode is "resume"
-      @reconnectionInPrgress = false
+      @reconnectionInProgress = false

   connectToTerminal: ->
     @appStorage = KD.getSingleton('appStorageController').storage 'Terminal', '1.0.1'


new history requires setting current on start

--- client/lanes/workspace/Layout.cjsx
+++ client/lanes/workspace/Layout.cjsx
@@ -21,8 +21,12 @@
         history = useBasename(Lanes.Vendor.BrowserHistory.createHistory)({
             basename: Lanes.config.root_path
         })
+
         @historyStopListening = history.listen (location) ->
             Lanes.Screens.Definitions.setBrowserLocation(location)
+        Lanes.Screens.Definitions.setBrowserLocation(
+            history.getCurrentLocation()
+        )
         @setState({history})

     componentWillUnmount: -> @historyStopListening()


added stubs for nodemailer into unit tests

--- services/web/test/unit/coffee/Email/EmailSenderTests.coffee
+++ services/web/test/unit/coffee/Email/EmailSenderTests.coffee
@@ -31,6 +31,8 @@

 		@sender = SandboxedModule.require modulePath, requires:
 			'nodemailer': @ses
+			"nodemailer-mandrill-transport":{}
+			"nodemailer-sendgrid-transport":{}
 			"settings-sharelatex":@settings
 			'../../infrastructure/RateLimiter':@RateLimiter
 			"logger-sharelatex":


fix method analizeChain

fix method analizeChain

--- lib/client/javascripts/nali/view.js.coffee
+++ lib/client/javascripts/nali/view.js.coffee
@@ -96,7 +96,6 @@
         args = @parseUrlSegments segments
         args.unshift params if params
         source[ method ] args...
-      else console.warn "Method %s not exists", chain
     else @redirect url, params
     @

@@ -303,5 +302,8 @@
   analizeChain: ( chain, source = @model ) ->
     segments = chain.split '.'
     property = segments.pop()
-    return null unless source = @getSource segments, source
-    [ source, property ]
+    source = @getSource segments, source
+    if source and property of source then [ source, property ]
+    else
+      console.warn "Property \"%s\" not exists in %O", property, source
+      null