Login: cleanup

Login: cleanup

--- client/landing/app/coffee/login/AppView.coffee
+++ client/landing/app/coffee/login/AppView.coffee
@@ -5,78 +5,29 @@
   stop = KD.utils.stopDOMEvent

   backgroundImages  = [
-
-      path         : '1'
-      href         : 'http://www.flickr.com/photos/charliefoster/'
-      photographer : 'Charlie Foster'
-    ,
-      path         : '2'
-      href         : 'http://pican.de/'
-      photographer : 'Dietmar Becker'
-    ,
-      path         : '3'
-      href         : 'http://www.station75.com/'
-      photographer : 'Marcin Czerwinski'
-    ,
-      path         : '4'
-      href         : 'http://www.station75.com/'
-      photographer : 'Marcin Czerwinski'
-    ,
-      path         : '5'
-      href         : 'http://www.flickr.com/photos/discomethod/sets/72157635620513053/'
-      photographer : 'Anton Sulsky'
-    ,
-      path         : '6'
-      href         : 'http://www.jfrwebdesign.nl/'
-      photographer : 'Joeri Römer'
-    ,
-      path         : '7'
-      href         : 'http://be.net/Zugr'
-      photographer : 'Zugr'
-    ,
-      path         : '8'
-      href         : ''
-      photographer : 'Mark Doda'
-    ,
-      path         : '9'
-      href         : 'http://www.twitter.com/rickwaalders'
-      photographer : 'Rick Waalders'
-    ,
-      path         : '10'
-      href         : 'http://madebyvadim.com/'
-      photographer : 'Vadim Sherbakov'
-    ,
-      path         : '11'
-      href         : ''
-      photographer : 'Zwaddi'
-    ,
-      path         : '12'
-      href         : 'http://be.net/Zugr'
-      photographer : 'Zugr'
-    ,
-      path         : '13'
-      href         : 'http://www.romainbriaux.fr/'
-      photographer : 'Romain Briaux'
-    ,
-      path         : '14'
-      href         : 'https://twitter.com/Petchy19'
-      photographer : 'petradr'
-    ,
-      path         : '15'
-      href         : 'http://rileyb.me/'
-      photographer : 'Riley Briggs'
-    ,
-      path         : '16'
-      href         : 'http://chloecolorphotography.tumblr.com/'
-      photographer : 'Chloe Benko-Prieur'
-
+    [ 'Charlie Foster', 'http://www.flickr.com/photos/charliefoster/' ]
+    [ 'Dietmar Becker', 'http://pican.de/' ]
+    [ 'Marcin Czerwinski', 'http://www.station75.com/' ]
+    [ 'Marcin Czerwinski', 'http://www.station75.com/' ]
+    [ 'Anton Sulsky', 'http://www.flickr.com/photos/discomethod/sets/72157635620513053/' ]
+    [ 'Joeri Römer', 'http://www.jfrwebdesign.nl/' ]
+    [ 'Zugr', 'http://be.net/Zugr' ]
+    [ 'Mark Doda', '' ]
+    [ 'Rick Waalders', 'http://www.twitter.com/rickwaalders' ]
+    [ 'Vadim Sherbakov', 'http://madebyvadim.com/' ]
+    [ 'Zwaddi', '' ]
+    [ 'Zugr', 'http://be.net/Zugr' ]
+    [ 'Romain Briaux', 'http://www.romainbriaux.fr/' ]
+    [ 'petradr', 'https://twitter.com/Petchy19' ]
+    [ 'Riley Briggs', 'http://rileyb.me/' ]
+    [ 'Chloe Benko-Prieur', 'http://chloecolorphotography.tumblr.com/' ]
   ]

   @backgroundImageNr = backgroundImageNr = KD.utils.getRandomNumber 15

   do ->
     image      = new Image
-    bgImageUrl = "../a/images/unsplash/#{backgroundImageNr}.jpg"
+    bgImageUrl = "/images/unsplash/#{backgroundImageNr}.jpg"
     image.src  = bgImageUrl

     image.classList.add 'off-screen-login-image'
@@ -96,7 +47,7 @@
       tagName    : 'a'
       cssClass   : 'koding-logo'
       partial    : '<cite></cite>'
-      attributes : href : '/'
+      attributes : '/ '

     @backToLoginLink = new CustomLinkView
       title       : 'Sign In'


added support for meteorhacks:npm providing app level modules to app browserify.js files

--- plugin/browserify.coffee
+++ plugin/browserify.coffee
@@ -3,15 +3,14 @@
 # get 'stream' to use PassThrough to provide a Buffer as a Readable stream
 stream     = Npm.require 'stream'

-# TODO: inputPath may include directories we need to strip for basedir
 processFile = (step) ->

   # create a browserify instance passing our readable stream as input,
   # and options object for debug and the basedir
   browserify = Browserify [getReadable(step)],
     # browserify options
-    basedir = getBasedir(step) # Browserify looks here for npm modules
-    debug = getDebug(step)     # Browserify creates internal source map
+    basedir: getBasedir(step) # Browserify looks here for npm modules
+    debug: getDebug(step)     # Browserify creates internal source map

   # have browserify process the file and include all required modules.
   # we receive a readable stream as the result
@@ -39,16 +38,35 @@
   catch e
     # output error via CompileStep#error()
     # convert it to a string and then remove the 'Error: ' at the beginning.
-    step.error message:e.toString().substring 7
+    #step.error message:e.toString().substring 7

 # add our function as the handler for files ending in 'browserify.js'
 Plugin.registerSourceHandler 'browserify.js', processFile

 getBasedir = (step) ->
-  # CompileStep has the absolute path to the file in `fullInputPath`
-  # CompileStep has the name of the file in `inputPath`
-  # basedir is fullInputPath with inputPath replaced with '.npm/package'
-  basedir = step.fullInputPath.slice(0, -(step.inputPath.length)) + '.npm/package'
+
+  # basedir should point to the '.npm/package' folder containing the npm modules.
+  # step.fullInputPath is the full path to our browserify.js file. it may be:
+  #   1. in a package
+  #   2. in the app itself
+  # for both of the above it also may be:
+  #   1. in the root (of package or app)
+  #   2. in a subfolder
+  # NOTE:
+  #   the app doesn't have npm support, so, no .npm/package.
+  #   using meteorhacks:npm creates a package to contain the npm modules.
+  #   so, if the browserify.js file is an app file, then let's look for
+  #   packages/npm-container/.npm/package
+
+  # the basedir tail depends on whether this file is in the app or a package
+  # for an app file, we're going to assume they are using meteorhacks:npm
+  tail = if step?.packageName? then '.npm/package' else 'packages/npm-container/.npm/package'
+
+  #   CompileStep has the absolute path to the file in `fullInputPath`
+  #   CompileStep has the package/app relative path to the file in `inputPath`
+  #   basedir is fullInputPath with inputPath replaced with the tail
+  basedir = step.fullInputPath.slice(0, -(step.inputPath.length)) + tail
+  return basedir

 getDebug = (step) ->
   debug = true
@@ -72,6 +90,8 @@
   # add the buffer into the stream and end the stream with one call to end()
   readable.end step.read()

+  return readable
+
 # async function for reading entire bundle output into a string
 getString = (bundle, cb) ->


the album images on the artists page are now loaded only if the user clicks the Top Albums tab

--- app/assets/javascripts/pages/artists.js.coffee
+++ app/assets/javascripts/pages/artists.js.coffee
@@ -28,7 +28,15 @@
         'Top Tracks'
       ]

-    $('#toggle_top_albums').click ->
+    $('#toggle_top_albums').click =>
+      if @albums.size() > 0
+        moreRow = $('#top-albums table tbody').find 'tr#more-row'
+        moreRow.remove()
+
+        @albumsView.render()
+
+        $('#top-albums table tbody').append moreRow
+
       _gaq.push [
         '_trackEvent'
         'Tabs'
@@ -80,14 +88,6 @@

     @albumsView = new Molamp.Views.Albums.AlbumsView model: @albums

-    moreRow = $('#top-albums table tbody').find 'tr#more-row'
-    moreRow.remove()
-
-    if @albums.size() > 0
-      @albumsView.render()
-
-    $('#top-albums table tbody').append moreRow
-
     @initPlaylistSearch()

   initPlaylistSearch: ->


Activity: previous replies link to update only once

--- client/Social/Activity/views/privatemessage/replypreviouslink.coffee
+++ client/Social/Activity/views/privatemessage/replypreviouslink.coffee
@@ -4,7 +4,7 @@
     super options, data

-  updateView: (replies) ->
+  updateView: _.once (replies) ->
     {SocialChannel} = KD.remote.api
     data = @getData()
     SocialChannel.fetchActivityCount {channelId: data.id}, (err, {totalCount}) =>
@@ -18,9 +18,3 @@

   viewAppended: ->
-  #   {SocialChannel} = KD.remote.api
-  #   data = @getData()
-  #   SocialChannel.fetchActivityCount {channelId: data.id}, (err, {totalCount}) =>
-  #     return warn err if err
-  #     data.repliesCount = totalCount if data
-  #     super


StackTemplateEditorView: Put introduction text into editor when ready.

--- client/admin/lib/views/stacks/editors/stacktemplateeditorview.coffee
+++ client/admin/lib/views/stacks/editors/stacktemplateeditorview.coffee
@@ -11,3 +11,15 @@

     super options, data

+    @on 'EditorReady', =>
+      return  if options.inEditMode
+
+      position = row: 0, column: 0
+      content  = """
+        # Here is your stack preview
+        # You can make advanced changes like modifying your VM,
+        # installing packages, and running shell commands.
+
+
+      """
+      @aceView.ace.editor.session.insert position, content


Added export of all data. Step 3.

Added export of all data. Step 3.

--- fetsy-hammertag/scripts/states/export.coffee
+++ fetsy-hammertag/scripts/states/export.coffee
@@ -11,8 +11,10 @@
                 @persons = []
                 @objects = []
                 @suppliesObj = {}
+                maxPersons = 0

-                angular.forEach response.data, (data, personID) =>
+                # Parse response.data
+                for personID, data of response.data
                     # Care of redundancy with server
                     unknownPersonID = 'Unknown'

@@ -23,43 +25,42 @@
                             Description: data.description

                     # Catch objects
-                    angular.forEach data.objects, (object) =>
+                    for object in data.objects
                         @objects.push
                             ID: object.ID
                             Description: object.description
                             PersonID: personID
                             PersonDescription: data.description
-                        return

                     # Catch supplies
-                    angular.forEach data.supplies, (supplies) =>
+                    for supplies in data.supplies
                         if not @suppliesObj[supplies.ID]?
                             @suppliesObj[supplies.ID] =
                                 ID: supplies.ID
                                 description: supplies.description
                                 persons: []
-                        @suppliesObj[supplies.ID].persons.push personID
-                        return
-                    return
+                        if personID isnt unknownPersonID
+                            @suppliesObj[supplies.ID].persons.push
+                                ID: personID
+                                description: data.description
+                            if maxPersons < @suppliesObj[supplies.ID].persons.length
+                                maxPersons = @suppliesObj[supplies.ID].persons.length

                 # Parse supplies
-                @supplies = []
-                angular.forEach @suppliesObj, (supplies, suppliesID) =>
-                    item =
-                        ID: suppliesID
-                        Description: supplies.description
-                    angular.forEach supplies.persons, (personID, index) ->
-                        item["Person#{index + 1}"] = personID
-                        return
-                    @supplies.push item
-                    return
+                @supplies =
+                    fields: ['ID', 'Description']
+                    data: []
+                @supplies.fields.push "Person#{num}" for num in [1..maxPersons]
+                for suppliesID, supplies of @suppliesObj
+                    item = [suppliesID, supplies.description]
+                    item.push "#{person.description} (#{person.ID})" for person in supplies.persons
+                    @supplies.data.push item

+                # Create CSV output
                 @persons = 'data:text/csv;charset=utf-8,' + Papa.unparse @persons
                 @persons = encodeURI @persons
-
                 @objects = 'data:text/csv;charset=utf-8,' + Papa.unparse @objects
                 @objects = encodeURI @objects
-
                 @supplies = 'data:text/csv;charset=utf-8,' + Papa.unparse @supplies
                 @supplies = encodeURI @supplies


Update KMO geoserver URL.

Update KMO geoserver URL.

Proxy not needed anymore because https now supported.

--- src/map.coffee
+++ src/map.coffee
@@ -66,7 +66,7 @@

             layer: (opts) ->
                 geoserverUrl = (layerName, layerFmt) ->
-                    "https://geoserver.hel.fi/khf/ws/geoserver/gwc/service/tms/1.0.0/#{layerName}@ETRS-GK25@#{layerFmt}/{z}/{x}/{y}.#{layerFmt}"
+                    "https://kartta.hel.fi/ws/geoserver/gwc/service/tms/1.0.0/#{layerName}@ETRS-GK25@#{layerFmt}/{z}/{x}/{y}.#{layerFmt}"
                 if opts.style == 'ortographic'
                     new L.Proj.TileLayer.TMS geoserverUrl("kanslia_palvelukartta:Ortoilmakuva_2013_PKS", "jpeg"), opts.crs,
                         maxZoom: 10


Fix for filters without for sale works

Fix for filters without for sale works

--- components/artwork_filter/models/filter.coffee
+++ components/artwork_filter/models/filter.coffee
@@ -37,7 +37,7 @@
     false

   forSaleCount: ->
-    (if @selected.has('price_range') then @get('total') else @boolean('for-sale').count) or 0
+    (if @selected.has('price_range') then @get('total') else @boolean('for-sale')?.count) or 0

   active: ->
     @__active__() or @root


using new classification object to save and submit classifications

--- app/controllers/TranscriptionController.coffee
+++ app/controllers/TranscriptionController.coffee
@@ -18,6 +18,7 @@

   constructor:->
     super
+
     $("body").bind "doneClassification", (data)=>
       @saveAnnotation data.values

@@ -27,17 +28,13 @@
     Spine.bind("nextSubject", @nextSubject)

   saveAnnotation:(values)=>
-    @annotations||=[]
-    @annotations.push values
+    for key,value of values
+      @classification.annotate(key,value)
+    @classification.save()

   saveTranscription:(data)=>
-    zooApi.saveClassification
-      project : "notes_from_nature"
-      subjects: [@currnetSubject.id]
-      workflow: @currnetSubject.workflow_ids[0]
-      annotations: @annotations
-    ,(result)=>
-      console.log "save result"
+    @classification.send()
+    @nextSubject()

   render:=>
     if @currnetSubject
@@ -51,9 +48,14 @@

   nextSubject:=>
     if @currnetSubject?
-      console.log @currnetSubject
+
       archive = Archive.find(@currnetSubject.archive_id)
+
       @archive.nextSubject (subject)=>
+        @classification =  Classification.create
+          subject_id : subject.id
+          workflow_id: subject.workflow_id
+
         $("div.transcribing.sernac img").attr("src", subject.location.standard)


FILE FIX yeah

FILE FIX yeah

--- components/backend/modules/files/server.coffee
+++ components/backend/modules/files/server.coffee
@@ -41,8 +41,7 @@

   app.post "/uploadFile", auth, (req,res)->
-    #c.l "upload", req, res
-    form = new multiparty.Form
+    form = new multiparty.Form
       uploadDir: dir
     form.parse req, (err, fields, files)->
       if err then return console.log 'formparse error', err
@@ -56,16 +55,13 @@
           "type": srcFile.headers['content-type']

         if srcFile.headers['content-type'].split("/")[0] is "image"
-          createImages file, req, (file)->
-            file.save ->
-              req.io.broadcast "updateCollection", cfg.collectionName
-              done()
+          createImages file, req, ->
+            done()
         else
           file.save ->
             req.io.broadcast "updateCollection", cfg.collectionName
             done()
-      async.each files['files[]'], uploadFile, ->
-        console.log("uploaded"+files['files[]'].length+" files")
+      async.eachSeries files['files[]'], uploadFile
     res.end()

@@ -106,9 +102,10 @@

   createImages = (file, req, done) ->
     thumbTypes = ["thumbnail", "smallPic", "bigPic"]
+    filename = file.getFieldValue "title"
     image = gm(dir+filename).size (err, size) ->
       if err then return console.error "createWebPic getSize err=", err
-      portrait = if size.width < size.height then true else false
+      portrait = if size.width < size.height then true else false
       addFile = (type, cb)->
         maxSize = moduleSetting.getFieldValue type
         targetName = filename.replace /\.(?=[^.]*$)/, '_'+type+'.'
@@ -117,7 +114,9 @@
         if portrait then image.resize null, maxSize
         else image.resize maxSize
         image.write dir+targetName, ->
-          file.save()
           cb()
-      async.each thumbTypes, addFile, ->done file
-
+      async.each thumbTypes, addFile, ->
+        file.save ->
+          done()
+          req.io.broadcast "updateCollection", cfg.collectionName
+


tidying up interactive

tidying up interactive

--- app/controllers/interactive/interactive.coffee
+++ app/controllers/interactive/interactive.coffee
@@ -9,7 +9,6 @@
     'click .my_galaxies': 'linkToMyGalaxies'
     'click .graph': 'linkToGraph'
     'click input#dx-change': 'addDX'
-    'click .galaxy_img': 'createBarChart'

   constructor: ->
     super
@@ -54,13 +53,6 @@
       $('p').removeClass('dx')
       $('#link_buttons a').removeClass('dx')

-  createBarChart: ->
-    data = [4, 8, 15, 16, 23, 42]
-    chart = d3.select('galaxy_bar_chart').append("svg").attr("width", 420).attr("height", 20 * data.length)
-    x = d3.scale.linear().domain([0, d3.max(data)]).range([0, 420])
-    y = d3.scale.ordinal().domain(data).rangeBands([0, 120])
-    chart.selectAll("rect").data(data).enter().append("rect").attr("y", function(d, i) { return i * 20; }).attr("width", x).attr("height", 20)
-
-
+

 module.exports = Interactive


Add debug log when app is required as module

--- src/coffee/app.coffee
+++ src/coffee/app.coffee
@@ -87,5 +87,7 @@
 if __filename is process.argv[1]
   server = app.listen port
   logger.info "Listening for HTTP on http://localhost:#{port}"
+else
+  logger.debug "Module is being required, skipping server start..."

 module.exports = app


small readability improvement

small readability improvement

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -215,17 +215,14 @@

           if out.message.sequenceNumber is (lastProcessedSN + 1)
             out.sink.onNext out.message
-            out.sink.onCompleted()
-            out.errors.onCompleted()
           else if out.message.sequenceNumber <= lastProcessedSN
             @_messageHasLowSequenceNumber out.message
             out.recycleBin.onNext out.message
-            out.sink.onCompleted()
-            out.errors.onCompleted()
           else
             out.recycleBin.onNext out.message
-            out.errors.onCompleted()
-            out.sink.onComplete()
+
+          out.sink.onCompleted()
+          out.errors.onCompleted()
         .fail (error) =>
           @stats.awaitOrderingRemoved out.message


use double quoted block string and interpolation to fix lack of newline

--- src/connection.coffee
+++ src/connection.coffee
@@ -527,7 +527,7 @@
     @messageIo.sendMessage(TYPE.SQL_BATCH, payload.data)

   getInitialSql: ->
-    'set textsize ' + @config.options.textsize + '''
+    """set textsize #{@config.options.textsize}
 set quoted_identifier on
 set arithabort off
 set numeric_roundabort off
@@ -540,7 +540,7 @@
 set language us_english
 set dateformat mdy
 set datefirst 7
-set transaction isolation level #{@getIsolationLevelText @config.options.connectionIsolationLevel}'''
+set transaction isolation level #{@getIsolationLevelText @config.options.connectionIsolationLevel}"""

   processedInitialSql: ->
       @clearConnectTimer()


Bugfix: disable must empty the queue

Bugfix: disable must empty the queue

Otherwise you can get movements to execute after the current UI is disabled, so
you can leave the target again and then the "game won" message is missing, or
you can start 2 or more games at once from the level chooser. Thanks to V.E.
for reporting this.

--- maze.coffee
+++ maze.coffee
@@ -353,6 +353,7 @@

   disable: ->
     $(document).off('keyup')
+    @queue = []

   destroy: (complete=$.noop) ->
     @disable()


Added watch task.

Added watch task.

--- src/pie.coffee
+++ src/pie.coffee
@@ -32,6 +32,12 @@
   task "build", "Build everything! (run all mappings, in the order defined)", (cb) ->
     runAllMappings(cb)

+  task "watch", "Run a build and then start watching the filesystem for changes and triggering mappings as necessary", (cb) ->
+    invoke "build", (err) ->
+      return cb(err) if err
+      startWatcher(cb)
+
+  # slurp up the Piefile (can override the default tasks if it wants)
   evaluatePiefile (err) ->
     return cb(err) if err
     _db = nStore.new(".pie.db", cb)
@@ -85,23 +91,29 @@
   else
     err.toString()

-runAllMappings = (cb = noop) ->
+runAllMappings = (cb) ->
   async.forEachSeries _mappings, ((m, innerCb) -> m.run(innerCb)), (err) ->
-    return printErr(err) if err
+    return cb(err) if err
     console.log "Build complete"
     growl "Build complete"
-
-startWatcher = (cb = noop) ->
+    cb(null)
+
+startWatcher = (cb) ->
   console.log "Starting watcher"
-  _watch = fsWatchTree.watchTree ".", { exclude: [".git", ".pie.db", "node_modules", "log", "tmp"] }, (event) ->
-    console.log "Got event", event
-    unless event.isDelete()
-      mappings = _.filter(_mappings, (m) -> m.matchesSrc(event.name))
-      console.log "Applicable mappings", _.map(mappings, (m) -> m.name)
-      async.forEach mappings, ((m) -> m.runOnFiles([event.name], printErr)), printErr
+  _watch = fsWatchTree.watchTree ".",
+                                 { exclude: [".git", ".pie.db", "node_modules", "log", "tmp"] },
+                                 watchEvent
+  cb(null)

 stopWatcher = () ->
   _watch.end()
+
+watchEvent = (event) ->
+  console.log "Got event", event
+  unless event.isDelete()
+    mappings = _.filter(_mappings, (m) -> m.matchesSrc(event.name))
+    console.log "Applicable mappings", _.map(mappings, (m) -> m.name)
+    async.forEach mappings, ((m) -> m.runOnFiles([event.name], printErr)), printErr

 # just a lil' bit o' code


Update es-419.coffee

Update es-419.coffee

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -31,7 +31,7 @@
     contact: "Contacto"
     twitter_follow: "Seguir"
     teachers: "Maestros"
-#    careers: "Careers"
+    careers: "Carreras"

   modal:
     close: "Cerrar"
@@ -411,7 +411,7 @@
     feature7: "Clan Privado"
     free: "Gratis"
     month: "mes"
-#    must_be_logged: "You must be logged in first. Please create an account or log in from the menu above."
+    must_be_logged: "Debes iniciar sesión primero. Por favor crea una cuenta o inicia sesión desde el menú superior."
     subscribe_title: "Suscribirse"
     unsubscribe: "Des-suscribirse"
     confirm_unsubscribe: "Confirmar cancelacion de suscripción"
@@ -528,8 +528,8 @@
   skill_docs:
     writable: "escribible" # Hover over "attack" in Your Skills while playing a level to see most of this
     read_only: "Sólo Lectura"
-#    action: "Action"
-#    spell: "Spell"
+    action: "Acción"
+    spell: "Conjuro"
     action_name: "nombre"
     action_cooldown: "Toma"
     action_specific_cooldown: "Enfriamiento"
@@ -614,7 +614,7 @@
     free_1: "CodeCombat Basic es GRATIS! Hay 70+ niveles gratis que cubren cada concepto." # {change}
     free_2: "Una suscripción mensual le da acceso a tutoriales en vídeo y niveles extra para practicar."
     teacher_subs_title: "¡Los amestros obtienen subscripciones gratuitas!"
-#    teacher_subs_0: "We offer free subscriptions to teachers for evaluation purposes."
+    teacher_subs_0: "Ofrecemos subscripciones gratuitas a profesores con propósitos de evaluación."
     teacher_subs_1: "Por favor contacte" # {change}
     teacher_subs_2: "para configurar una suscripción mensual gratis." # {change}
     teacher_subs_3: "para configurar su suscripción."
@@ -1087,8 +1087,8 @@
 #    red_ai: "Red AI" # "Red AI Wins", at end of multiplayer match playback
 #    blue_ai: "Blue AI"
 #    wins: "Wins" # At end of multiplayer match playback
-#    humans: "Red" # Ladder page display team name
-#    ogres: "Blue"
+    humans: "Rojo" # Ladder page display team name
+    ogres: "Azul"

   user:
     stats: "Estados"


I translated "become_investor" to Spanish (#6467)

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -5,7 +5,7 @@
 #    meta_keywords: "CodeCombat, python, javascript, Coding Games"
     meta_description: "Aprende a esciribir código a través de un juego de programación. Aprende Python, JavaScript y HTML mientras resuelves rompecabezas y aprendes a hacer tus propios juegos de código y páginas web."
 #    meta_og_url: "https://codecombat.com"
-#    become_investor: "to become an investor in CodeCombat"
+    become_investor: "Conviértete en un inversionista de CodeCombat"
     built_for_teachers_title: "Un Juego de programación construido teniendo en cuenta a los maestros."
     built_for_teachers_blurb: "Enseñar a los niños a programar puede ser abrumador. CodeCombat ayuda a todos los educadores a enseñar a los estudiantes cómo programar en JavaScript o Python, dos de los lenguajes de programación más populares. Con un plan de estudios comprensivo que incluye seis unidades de Ciencias de la Computación, y refuerza el aprendizaje a través de unidades en el desarrollo de juegos y desarrollo web basados en proyectos, los niños progresarán en un viaje desde la sintaxis básica hasta la recursión!"
     built_for_teachers_subtitle1: "Ciencias de la Computación"


Finished translations and added some corrections.

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -431,29 +431,29 @@
     stripe_description: "Suscripción Mensual"
     subscription_required_to_play: "Necesitas una suscripción para jugar este nivel."
     unlock_help_videos: "Suscríbete para desbloquear todos los video tutoriales."
-#    personal_sub: "Personal Subscription" # Accounts Subscription View below
-#    loading_info: "Loading subscription information..."
-#    managed_by: "Managed by"
-#    will_be_cancelled: "Will be cancelled on"
-#    currently_free: "You currently have a free subscription"
-#    currently_free_until: "You currently have a free subscription until"
-#    was_free_until: "You had a free subscription until"
-#    managed_subs: "Managed Subscriptions"
-#    managed_subs_desc: "Add subscriptions for other players (students, children, etc.)"
-#    group_discounts: "Group discounts"
-#    group_discounts_1st: "1st subscription (includes yours)"
-#    group_discounts_full: "Full price"
-#    group_discounts_2nd: "Subscriptions 2-11"
-#    group_discounts_20: "20% off"
-#    group_discounts_12th: "Subscriptions 12+"
-#    group_discounts_40: "40% off"
-#    subscribing: "Subscribing..."
-#    recipient_emails_placeholder: "Enter email address to subscribe, one per line."
-#    subscribe_users: "Subscribe Users"
-#    users_subscribed: "Users subscribed:"
-#    no_users_subscribed: "No users subscribed, please double check your email addresses."
-#    current_recipients: "Current Recipients"
-#    unsubscribing: "Unsubscribing..."
+    personal_sub: "Suscripción Personal" # Accounts Subscription View below
+    loading_info: "Cargando información de suscripción..."
+    managed_by: "Administrado por"
+    will_be_cancelled: "Será cancelado en"
+    currently_free: "Actualmente tienes una suscripción gratuita"
+    currently_free_until: "Actualmente tienes una suscripción gratuita hasta"
+    was_free_until: "Tuviste una suscripción gratuita hasta"
+    managed_subs: "Suscripciones administradas"
+    managed_subs_desc: "Agregar suscripciones para otros jugadores (alumnos, hijos, etc.)"
+    group_discounts: "Descuentos por grupo"
+    group_discounts_1st: "1ra suscripción (incluye la tuya)"
+    group_discounts_full: "Precio regular"
+    group_discounts_2nd: "2-11 Suscripciones"
+    group_discounts_20: "20% descuento"
+    group_discounts_12th: "12+ Suscripciones"
+    group_discounts_40: "40% descuento"
+    subscribing: "Suscribiendo..."
+    recipient_emails_placeholder: "Ingresa las direcciones de email a suscribir, una por línea."
+    subscribe_users: "Suscribir Usuarios"
+    users_subscribed: "Usuarios suscritos:"
+    no_users_subscribed: "No se suscribieron usuarios, por favor revisa las direcciones de email."
+    current_recipients: "Recipientes actuales"
+    unsubscribing: "Desuscribiendo..."

   choose_hero:
     choose_hero: "Elige tu héroe"
@@ -819,10 +819,10 @@
     level_search_title: "Buscar Niveles aquí"
     achievement_search_title: "Buscar logros"
     poll_search_title: "Buscar Encuesta"
-    read_only_warning2: "Nota: no puedes guardar ediciones aquí, porque no estas logeado."
+    read_only_warning2: "Nota: no puedes guardar ediciones aquí, porque no estas logueado."
     no_achievements: "No hay logros agregados en este nivel por ahora."
-#    achievement_query_misc: "Key achievement off of miscellanea"
-#    achievement_query_goals: "Key achievement off of level goals"
+    achievement_query_misc: "Objetivo clave de misceláneo"
+    achievement_query_goals: "Objetivo clave de los objetivos de nivel"
     level_completion: "Nivel Completado"
     pop_i18n: "Poblar I18N"
     tasks: "Tareas"
@@ -832,8 +832,8 @@
     edit_btn_preview: "Vista previa"
     edit_article_title: "Editar Artículo"

-#  polls:
-#    priority: "Priority"
+  polls:
+    priority: "Prioridad"

   contribute:
     page_title: "Contribuyendo"
@@ -1146,22 +1146,22 @@
     art_artwork: "Trabajo Artístico"
     art_sprites: "Sprites"
     art_other: "Cualquier otro trabajo creativo (no necesariamente código) que están disponibles cuando se crean niveles."
-#    art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible."
-#    art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:"
-    use_list_1: "SI es usado en una película o en otro juego, incluya codecombat.com en los créditos."
-#    use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution."
-#    art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any."
+    art_access: "Actualmente no hay un sistema fácil y universal para obtener estos activos. En general, obtenlos de los URLs tal y como son usados por el sitio, contáctanos si necesitas ayuda, o ayúdanos extendiendo el sitio para hacer estos activos accesibles más fácilmente."
+    art_paragraph_1: "Para atribución, por favor nombre y enlace a codecombat.com cerca de donde esta fuente sea usada o donde sea apropiado en el medio usado. Por ejemplo:"
+    use_list_1: "Si es usado en una película o en otro juego, incluya codecombat.com en los créditos."
+    use_list_2: "Si es usado en un website, incluya un link cerca donde es usado, por ejemplo debajo de una imagen, o en las atribuciones generales de la página, donde pueda mencionar también otros trabajos bajo Creative Commons o de código abierto que sean usados en el sitio web. Cualquier otro website que haga clara referencia a CodeCombat, tal como un blog post mencionando a CodeCombat, no necesita una atribución separada."
+    art_paragraph_2: "Si el contenido usado no fue creado por CodeCombat, sino por un usuario de codecombat.com, atribuya al usuario y siga las directivas de atribución provistas en la descripción del recurso, en caso las haya."
     rights_title: "Derechos Reservados"
     rights_desc: "Todos los derechos estan reservados para los niveles mismos. Esto incluye."
     rights_scripts: "Scripts"
     rights_unit: "Configuración de la unidad"
     rights_description: "Descripción"
     rights_writings: "Escritos"
-#    rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels."
-#    rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not."
+    rights_media: "Media (sonidos, música) y cualquier otro contenido creativo hecho específicamente para un nivel y que no haya sido hecho disponible al público cuando se crearon los niveles."
+    rights_clarification: "Aclarando, todo contenido puesto a disposición en el Editor de Niveles con el propósito de hacer más niveles se encuentra bajo licencia CC, mientras todo contenido creado con el Editor de Niveles o cargado durante la creación de Niveles no lo está."
     nutshell_title: "En una palabra"
-    nutshell_description: "Cualesquiera recursos que le proveamos en el Editor de Niveles son gratis de usar como te plazca para la creación de Niveles. Sin embargo nos reservamos el derecho de restringir la distribución de los niveles por sí mismos (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening."
-    canonical: "La versión en ingles de este documento es la definitiva, versión canónica. Si hay alguna discrepancia entre las traducciones, la versión en ingles toma presedencia."
+    nutshell_description: "Cualquier recurso que te proveamos en el Editor de Niveles es gratis de usar como te plazca para la creación de Niveles. Sin embargo, nos reservamos el derecho de restringir la distribución de los niveles por sí mismos (aquellos creados en codecombat.com) para así poder cobrar por ellos en el futuro, si es que eso es lo que termina pasando."
+    canonical: "La versión en inglés de este documento es la versión canónica y definitiva. Si hay alguna discrepancia entre las traducciones, la versión en inglés toma precedencia."

   ladder_prizes:
     title: "Premios de Torneos" # This section was for an old tournament and doesn't need new translations now.


intel-corei7-64.coffee: Update getting started links

Signed-off-by: Zachary Walchuk <9de8c4480303b5335cd2a33eefe814615ba3612a@resin.io>

--- intel-corei7-64.coffee
+++ intel-corei7-64.coffee
@@ -82,9 +82,9 @@
 	].concat(postProvisioningInstructions)

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

 	yocto:
 		machine: 'intel-corei7-64'


fix coffee script isse

fix coffee script isse

Signed-off-by: Sakchai Siripanyawuth <50d2926f6b388f0d7169db5beff2dc87e1537d54@artellectual.com>

--- vendor/assets/javascripts/simple_datatables.js.coffee
+++ vendor/assets/javascripts/simple_datatables.js.coffee
@@ -43,9 +43,9 @@
   $.each(aoData, (index, dataObj) ->
     search_regexp = ///sSearch_([0-9]+)///
     if (col = dataObj.name.match(search_regexp)) and dataObj.value
-      search_value = dataObj.split('/')
+      search_value = dataObj.value.split('/')
       console.log search_value
-      data.push({name: "search["+columns[col[1]]+ search_value[1] +"]", value: searc_value[0]});
+      data.push({name: "search["+columns[col[1]] + search_value[1] + "]", value: searc_value[0] });

     search_regexp = ///bSearchable_([0-9]+)///
     if (col = dataObj.name.match(search_regexp)) and dataObj.value
@@ -78,4 +78,4 @@
         "_sw"
     data.push({name: "search["+searchcolumns.join("_or_")+op+"]", value: sSearch});

-  $.ajax( { "dataType": 'json', "type": "GET", "url": sSource, "data": data, "success": fnCallback } );
+  $.ajax( { "dataType": 'json', "type": "GET", "url": sSource, "data": data, "success": fnCallback } );


moved initialisation to followers, as it isn't generic

--- core/app/backbone/collections/followers.coffee
+++ core/app/backbone/collections/followers.coffee
@@ -15,7 +15,6 @@

   initialize: (models, opts) ->
     @user = opts.user
-    @_followed_by_me = false
     @totalRecords = null

   parse: (response) ->
@@ -34,6 +33,9 @@
 class window.Followers extends SocialCollection
   url: -> "/#{@user.get('username')}/followers"

+  initialize: (args...) ->
+    super(args...)
+    @_followed_by_me = false

   # TODO fix this mess
   addFollower: (follower) ->


fixed rendering of activities in stream, this fixes the 'hang' on switching back to activities/stream

--- core/app/backbone/views/activities_view.coffee
+++ core/app/backbone/views/activities_view.coffee
@@ -1,8 +1,6 @@
 AutoloadingView = extendWithAutoloading(Backbone.Marionette.ItemView);

 class window.ActivitiesView extends AutoloadingView
-
-  childViews: []

   template: 'activities/list'

@@ -14,6 +12,7 @@
     this.collection.on('startLoading', this.loadingIndicatorOn, this);
     this.collection.on('stopLoading', this.loadingIndicatorOff, this);

+    this.childViews = []

   render: ->
     super arguments
@@ -35,6 +34,7 @@
       appendTo = last
     else
       appendTo = this.newChildView(model)
+      appendTo.render()
       this.childViews.push(appendTo)
       this.appendHtml(this, appendTo)

@@ -44,7 +44,6 @@
     childView.close() for childView in @childViews

   appendHtml: (collectionView, childView) ->
-    childView.render()
     this.$(".list").append(childView.$el)

   newChildView: (model) ->


Ace: add listener for a file that couldn't synced

--- client/Ace/ace.coffee
+++ client/Ace/ace.coffee
@@ -149,8 +149,12 @@

   prepareSyncListeners: ->
     {syncLocalController} = KD.singletons
-    syncLocalController.on "LocalContentSynced", (fileName) =>
+
+    syncLocalController.on "LocalContentSynced", (file) =>
       @notify "file synced to remote...", null, null, 5000
+
+    syncLocalController.on "LocalContentCouldntSynced", (file) =>
+      @notify "file coudn't synced to remote please try again...", null, null, 5000

   requestSaveAs: (options) ->
     contents = @getContents()


Rename Reply menu to Reply/Attach

Rename Reply menu to Reply/Attach

--- client/message.coffee
+++ client/message.coffee
@@ -1419,7 +1419,7 @@

   <Dropdown className="message-reply-buttons btn-group pull-right">
     <Dropdown.Toggle variant="default" {...dropProps}>
-      {"Reply "}
+      {"Reply/Attach "}

     </Dropdown.Toggle>
     <Dropdown.Menu align="right" className="buttonMenu replyMenu">
@@ -1427,7 +1427,7 @@
         # normal reply, not necessarily public
         if message.private

-            <TextTooltip placement="bottom" title="A reply to a private message will be private but automatically start accessible to the same users (once they are published and not deleted). You can modify that access when editing the reply. Access does not stay synchronized, so if you later modify the parent's access, consider modifying the child too.">
+            <TextTooltip placement="left" title="A reply to a private message will be private but automatically start accessible to the same users (once they are published and not deleted). You can modify that access when editing the reply. Access does not stay synchronized, so if you later modify the parent's access, consider modifying the child too.">
               <Dropdown.Item href="#" onClick={onReply}>
                 <button className="btn btn-#{defaultVariant} btn-block replyButton">
                   {prefix}


add recursive parameter to appveyor

add recursive parameter to appveyor

--- tasks/tests.coffee
+++ tasks/tests.coffee
@@ -79,7 +79,7 @@
     console.log('Note: You can\'t see Mocha test results in AppVeyor due to how Windows spawns processes.')

   cmd = "#{path.resolve('./node_modules/.bin/electron-mocha')}.cmd"
-  args = ['--require', '.\\helpers\\register-istanbul.js', '--renderer', '.\\tests\\']
+  args = ['--require', '.\\helpers\\register-istanbul.js', '--renderer', '--recursive', '.\\tests\\']

   fs.removeSync(options.env.EXITCODE_PATH) if fs.existsSync(options.env.EXITCODE_PATH)


Useless.

Useless.

--- functions/browser.coffee
+++ functions/browser.coffee
@@ -2,9 +2,6 @@
 dialog = remote.require 'dialog'
 fs = require 'fs'

-# Helpers
-_getUserHome = ->
-  return process.env.HOME || process.env.USERPROFILE

 # We check if we can write to directory.
 # If no admin and is required, warn.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state.coffee
+++ src/game_state.coffee
@@ -43,7 +43,7 @@
       I.objects.copy()
   }

-  self.include Bindable
+  self.include "Bindable"

   # Add events and methods here
   self.bind "update", (elapsedTime) ->


cleaning code for github

cleaning code for github

--- src/githubRepo.coffee
+++ src/githubRepo.coffee
@@ -85,48 +85,60 @@

 """)

+    # GitHub Repositories
     @github = ->
-      # GitHub Repositories
+      # DOM element to use
       if @$element.find('div.repositories').length>0
+        # use existing div.repositories if any
         repositories = @$element.find('div.repositories')[0]
       else
+        # create a div.repositories
         repositories = $('
').addClass('repositories')
         @$element.append(
           repositories
         )

+      # api url definition
       urlGithub = 'https://api.github.com/users/'+@settings.user+'/repos'
       if @settings.githubForceJson
         urlGithub = @settings.githubForceJson
+
+      # api call
       settings = @settings
+      that = @
       $.ajax urlGithub,
         success: (data, textStatus, jqXHR) ->
           for dataRepo in data
-            if $(repositories).find(
-              "[data-github-full-name='#{ dataRepo.full_name }']"
-              ).length>0
-              repository = $(repositories)
-                .find("[data-github-full-name='#{ dataRepo.full_name }']"
-              )[0]
+            # select DOM element to use for this repository
+            cssRepo = "[data-github-full-name='#{ dataRepo.full_name }']"
+            if $(repositories).find(cssRepo).length>0
+              # div.repository already exists
+              repository = $(repositories).find(cssRepo)[0]
             else
               if settings.allGithubRepos
+                # create new div.repository
                 repository = $('
').addClass('repository')
-                # Ajout au repositories
-                $(repositories).append(
-                  repository
-                )
+                $(repositories).append(repository)
               else
+                # we don't want to display this repo
                 break

-            date = new Date(dataRepo.pushed_at)
-            pushed_at = ("0"+date.getDate()).slice(-2) +
-              '-' + ("0"+(date.getMonth()+1)).slice(-2) +
-              '-' + date.getFullYear()
-            fork = ''
+            # Save Original Content
+            origine = $(repository).contents()
+            $(repository).empty()
+
+            # Prepare content
+            pushed_at = that.formatDate(dataRepo.pushed_at)
             if dataRepo.fork
               fork = 'fork'
-            template =
-            """
+            else
+              fork = ''
+
+            # Display repository
+            $(repository).addClass("panel panel-default")
+              .attr('data-github-id', dataRepo.id)
+              .attr('data-github-full-name', dataRepo.full_name)
+              .append("""

@@ -165,13 +177,8 @@

-"""
-            origine = $(repository).contents()
-            $(repository).empty()
-            $(repository).addClass("panel panel-default")
-              .attr('data-github-id', dataRepo.id)
-              .attr('data-github-full-name', dataRepo.full_name)
-              .append(template)
+""")
+            # Restore original content
             $(repository).children('.panel-body').append(origine)


Fix indentation.

Fix indentation.

--- app/javascripts/session.coffee
+++ app/javascripts/session.coffee
@@ -18,7 +18,7 @@
         associate = (accum, modeling) ->
           accum[modeling.protocol] = modeling.overlays

-        # The modeling objects with at least one overlay
+        # The modeling objects with at least one overlay.
         overlayed = (
           mdl for mdl in session.modelings when mdl.overlays.length?
         )
@@ -55,14 +55,14 @@
         Object.defineProperties session,
           # @returns the display title
           title:
-              # TODO - get the Session display text from a config,
-              #   e.g.:
-              #     label = config.session.label
-              #     "#{ @collection } #{ label } #{ @number }"
-              #   Thus, the label could be, say, 'Visit', rather than
-              #   'Session', and externally configurable. See also
-              #   the subject.coffee title TODO.
-              #
+            # TODO - get the Session display text from a config,
+            #   e.g.:
+            #     label = config.session.label
+            #     "#{ @collection } #{ label } #{ @number }"
+            #   Thus, the label could be, say, 'Visit', rather than
+            #   'Session', and externally configurable. See also
+            #   the subject.coffee title TODO.
+            #
             get: -> "#{ @subject.title } Session #{ @number }"

         # Returns the <parent>/session path, where:
@@ -109,4 +109,4 @@
         sbjRefUrl = "/quip/#{ subject.collection }/subject/#{ subject.number }"

         "#{ sbjRefUrl }/session/#{ session.number }?project=#{ subject.project }"
-  ]+]


fixed typo

fixed typo

--- app/js/util/scrollspeed.coffee
+++ app/js/util/scrollspeed.coffee
@@ -37,7 +37,7 @@

 $(window).on 'scroll', check_scrolling_speed

-# flag to ensure we only fire on state chaneg
+# flag to ensure we only fire on state change
 currently_fast_scrolling = false
 Factlink.el.on
   fast_scrolling: =>


Add longer `.jpeg` extension to those permitted in subject set uploader

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -15,7 +15,7 @@

 NOOP = Function.prototype

-VALID_SUBJECT_EXTENSIONS = ['.jpg', '.png', '.gif', '.svg']
+VALID_SUBJECT_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.svg']
 INVALID_FILENAME_CHARS = ['/', '\\', ':']
 MAX_FILE_SIZE = 600000


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -3,6 +3,7 @@
 test "testing for equality", ->
   player = GameObject
     x: 50
-    y
+    y: 50
+

 module()


Make xmpp able to DM people when go direct.

--- src/hubot/xmpp.coffee
+++ src/hubot/xmpp.coffee
@@ -60,6 +60,7 @@
     [room, from] = stanza.attrs.from.split '/'
     user = new Robot.User from, {
       room: room
+      type: stanza.attrs.type
     }

     @receive new Robot.Message user, message
@@ -67,11 +68,13 @@
   send: (user, strings...) ->
     strings.forEach (str) =>
       console.log "Sending to #{user.room}: #{str}"
-
+
+      to = if user.type in ['direct', 'chat'] then user.room + '/' + user.id else user.room
+
       message = new Xmpp.Element('message',
                   from: @options.username
-                  to: user.room
-                  type: 'groupchat'
+                  to: to
+                  type: user.type
                 ).
                 c('body').t(str)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/matrix.coffee
+++ test/matrix.coffee
@@ -80,6 +80,8 @@
     equals(transformedPoint.x, p.x, "Point should remain the same")
     equals(transformedPoint.y, p.y, "Point should remain the same")

+  test "#"
+
   test ".rotation", ->
     matrix = Matrix.rotation(Math.PI / 2)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/random.coffee
+++ test/random.coffee
@@ -17,6 +17,6 @@

   a = Random.angleBetween(0.25.turns, 0.5.turns)
   ok a < 0.5.turns
-  o
+  ok a > 0

 module()


fix autoclosing approving tags

fix autoclosing approving tags

--- src/xml.coffee
+++ src/xml.coffee
@@ -76,7 +76,7 @@
         @emit 'add', this, tag
         @emit 'new', tag
         @isempty = no
-        tag.emit? 'close', tag if tag.closed
+        tag.emit? 'close', tag if tag.closed and tag.closed isnt 'approving'
         tag.emit? 'added', this
         callback?.call(this, tag)


Got recent changes working. Added new populateCollections and docpadLoaded events

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -110,8 +110,10 @@
 		'docpadReady'
 		'extendTemplateData'
 		'extendCollections'
+		'docpadLoaded'
 		'consoleSetup'
 		'generateBefore'
+		'populateCollections'
 		'generateAfter'
 		'parseBefore'
 		'parseAfter'
@@ -657,12 +659,12 @@
 			return next?(null,docpad)

 		# Apply and load configuration
-		@action 'load', instanceConfig, (err,config) =>
+		@action 'load ready', instanceConfig, (err,config) =>
 			# Error?
 			return @fatal(err)  if err

 			# Forward
-			@ready(next)
+			next?(err,docpad)

 		# Chain
 		@
@@ -692,7 +694,7 @@
 			return docpad.error(err)  if err

 			# All done, forward our DocPad instance onto our creator
-			return next?(null,docpadInstance)
+			return next?(null,docpad)

 		# Chain
 		@
@@ -718,9 +720,11 @@
 		preTasks = new balUtil.Group (err) =>
 			return next(err)  if err

+
 			# Get environments
 			@config.env = @instanceConfig.env or @websiteConfig.env or @websitePackageConfig.env or @initialConfig.env
 			envs = @getEnvironments()
+

 			# Merge configurations
 			configPackages = [@initialConfig, @websitePackageConfig, @websiteConfig, @instanceConfig]
@@ -731,19 +735,22 @@
 					envConfig = configPackage.environments?[env]
 					configsToMerge.push(envConfig)  if envConfig
 			balUtil.deepExtendPlainObjects(configsToMerge...)
-			console.log(@config.env, envs, @instanceConfig,instanceConfig)
+

 			# Extract and apply the server
 			@setServer(@config.server)  if @config.server
+

 			# Extract and apply the logger
 			@setLogger(@config.logger)  if @config.logger
 			@setLogLevel(@config.logLevel)
+

 			# Resolve any paths
 			@config.rootPath = pathUtil.resolve(@config.rootPath)
 			@config.outPath = pathUtil.resolve(@config.rootPath, @config.outPath)
 			@config.srcPath = pathUtil.resolve(@config.rootPath, @config.srcPath)
+

 			# Resolve Documents, Files, Layouts paths
 			for type in ['documents','files','layouts']
@@ -751,18 +758,21 @@
 				for typePath,key in typePaths
 					typePaths[key] = pathUtil.resolve(@config.srcPath,typePath)

+
 			# Resolve Plugins paths
 			for type in ['plugins']
 				typePaths = @config[type+'Paths']
 				for typePath,key in typePaths
 					typePaths[key] = pathUtil.resolve(@config.rootPath,typePath)

+
 			# Bind the error handler, so we don't crash on errors
 			if @config.catchExceptions
 				process.setMaxListeners(0)
 				process.on('uncaughtException', @error)
 			else
-				process.off('uncaughtException', @error)
+				process.removeListener('uncaughtException', @error)
+

 			# Load Airbrake if we want to reportErrors
 			if @config.reportErrors and /win/.test(process.platform) is false
@@ -770,6 +780,7 @@
 					airbrake = require('airbrake').createClient(@config.airbrakeToken)
 				catch err
 					airbrake = false
+

 			# Prepare the Post Tasks
 			postTasks = new balUtil.Group (err) =>
@@ -803,8 +814,13 @@
 			postTasks.push (complete) =>
 				@emitSync('extendTemplateData', {templateData:@pluginsTemplateData}, complete)

+			# Fire the docpadLoaded event
+			postTasks.push (complete) =>
+				@emitSync('docpadLoaded', {}, complete)
+
 			# Fire post tasks
 			postTasks.sync()
+

 		# Load DocPad's Package Configuration
 		preTasks.push (complete) =>
@@ -818,6 +834,7 @@

 				# Done loading
 				complete()
+

 		# Load Website's Package Configuration
 		preTasks.push (complete) =>
@@ -833,6 +850,7 @@

 				# Done loading
 				complete()
+

 		# Load Website's Configuration
 		preTasks.push (complete) =>
@@ -850,8 +868,10 @@
 				# Done loading
 				complete()

+
 		# Run the load tasks synchronously
 		preTasks.sync()
+

 		# Chain
 		@
@@ -1062,7 +1082,7 @@
 		# Custom Collections Group
 		tasks = new balUtil.Group (err) ->
 			docpad.error(err)  if err
-			return next()
+			docpad.emitSync('extendCollections',{},next)

 		# Cycle through Custom Collections
 		_.each @config.collections, (fn,name) ->
@@ -1100,7 +1120,7 @@

 		# Perform any plugin extensions to what we just cleaned
 		# and forward
-		@emitSync('extendCollections',{},next)
+		@emitSync('populateCollections',{},next)

 		# Chain
 		@
@@ -1547,7 +1567,6 @@
 									return next()

 								# Add to plugin stores
-								console.log(loader.pluginName,pluginInstance.config,docpad.getEnvironments())
 								docpad.loadedPlugins[loader.pluginName] = pluginInstance

 								# Log completion
@@ -1924,7 +1943,7 @@

 	# Perform an action
 	# next(err,...), ... = any special arguments from the action
-	action: (action,opts,next) ->
+	action: (action,opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -1946,55 +1965,17 @@
 		# Next
 		next ?= (err) ->
 			docpad.fatal(err)  if err
+		forward = (args...) =>
+			@log 'debug', "Performed the action #{action}"
+			balUtil.wait 0, -> next(args...)

 		# Wrap
 		runner.pushAndRun (complete) ->
-			# Handle
-			switch action
-				when 'install', 'update'
-					docpad.install opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'skeleton', 'scaffold'
-					docpad.skeleton opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'load'
-					docpad.load opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'generate'
-					docpad.generate opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'clean'
-					docpad.clean opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'render'
-					docpad.render opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'watch'
-					docpad.watch opts, (args...) ->
-						next(args...)
-						complete()
-
-				when 'server'
-					docpad.server opts, (args...) ->
-						next(args...)
-						complete()
-
-				else
-					docpad.run opts, (args...) ->
-						next(args...)
-						complete()
+			# Forward
+			fn = docpad[action] or docpad.run
+			fn opts, (args...) ->
+				forward(args...)
+				complete()

 		# Chain
 		@
@@ -2005,7 +1986,7 @@

 	# Generate Prepare
 	# next(err)
-	generatePrepare: (opts,next) ->
+	generatePrepare: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2024,7 +2005,7 @@

 	# Generate Check
 	# next(err)
-	generateCheck: (opts,next) ->
+	generateCheck: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2049,7 +2030,7 @@

 	# Generate Clean
 	# next(err)
-	generateClean: (opts,next) ->
+	generateClean: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2062,7 +2043,7 @@

 	# Parse the files
 	# next(err)
-	generateParse: (opts,next) ->
+	generateParse: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2117,7 +2098,7 @@

 	# Generate Render
 	# next(err)
-	generateRender: (opts,next) ->
+	generateRender: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2138,7 +2119,7 @@

 	# Generate Postpare
 	# next(err)
-	generatePostpare: (opts,next) ->
+	generatePostpare: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2165,7 +2146,7 @@

 	# Generate
 	# next(err)
-	generate: (opts,next) ->
+	generate: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2287,7 +2268,7 @@

 	# Render Action
 	# next(err,document,result)
-	render: (opts,next) ->
+	render: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)

@@ -2315,7 +2296,7 @@
 	# Watch

 	# Watch
-	watch: (opts,next) ->
+	watch: (opts,next) =>
 		# Require
 		watchr = require('watchr')

@@ -2425,7 +2406,7 @@
 	# ---------------------------------
 	# Run Action

-	run: (opts,next) ->
+	run: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2478,7 +2459,7 @@
 	# Skeleton

 	# Skeleton
-	skeleton: (opts,next) ->
+	skeleton: (opts,next) =>
 		# Prepare
 		[opts,next] = balUtil.extractOptsAndCallback(opts,next)
 		docpad = @
@@ -2559,7 +2540,7 @@
 	# Server

 	# Server
-	server: (opts,next) ->
+	server: (opts,next) =>
 		# Require
 		express = require('express')


Update - D:09032021-H:214325

Update - D:09032021-H:214325

--- assets/js/contact.coffee
+++ assets/js/contact.coffee
@@ -4,14 +4,25 @@
 #  Load data
 {% include liquid/data %}

-
-$('#submit').click(function() {
-     $("#name").val("");
-     $("#subject").val("");
-     $("#email").val("");
-     $("#message").val("");
-});
-
+($) ->
+    $('form').submit (e) ->
+        event.preventDefault()
+        $.ajax
+            url: 'https://formspree.io/f/{{ load_data.website.content.contact.formspree.endpoint }}'
+            method: 'POST'
+            data: {
+                name: $('#name').val()
+                subject: $('#subject').val()
+                email: $('#email').val()
+                message: $('#message').val()
+            }
+            dataType: 'json'
+        .done ->
+            $('#name').val('')
+            $('#subject').val('')
+            $('#email').val('')
+            $('#message').val('')
+    false

 ###### DEPRECATED #########


Update course ording in teacher class view

Undoing previous ordering display change:
https://github.com/codecombat/codecombat/commit/1f1df73d7aac564148151a9042ab25b8046ffc0c

--- app/views/courses/TeacherClassView.coco.coffee
+++ app/views/courses/TeacherClassView.coco.coffee
@@ -242,7 +242,7 @@
   onLoaded: ->
     # Get latest courses for student assignment dropdowns
     @latestReleasedCourses = if me.isAdmin() then @courses.models else @courses.where({releasePhase: 'released'})
-    @latestReleasedCourses = utils.sortCoursesByAcronyms(@latestReleasedCourses)
+    @latestReleasedCourses = utils.sortCourses(@latestReleasedCourses)
     @removeDeletedStudents() # TODO: Move this to mediator listeners? For both classroom and students?
     @calculateProgressAndLevels()


added testcase for much simpler api

added testcase for much simpler api

--- tests/basic.coffee
+++ tests/basic.coffee
@@ -46,6 +46,40 @@
   author = authors.findOne authorId
   test.equal author.postCount, 0

+
+Tinytest.add 'computedFields - simple field', (test) ->
+  posts = new Mongo.Collection null
+  authors = new Mongo.Collection null
+
+  authors.computedFields.add(
+    'postCount'
+  ).simple posts, 'authorId', -> return @increment
+
+  authorId = authors.insert name: 'max'
+  postId = posts.insert
+    name: 'test'
+    authorId: authorId
+  author = authors.findOne authorId
+  test.equal author.postCount, 1
+
+  postId2 = posts.insert
+    name: 'test2'
+    authorId: authorId
+  author = authors.findOne authorId
+  test.equal author.postCount, 2
+
+  posts.update postId, $set: test: 1
+  author = authors.findOne authorId
+  test.equal author.postCount, 2
+
+  posts.update postId, $set: authorId: 'test123'
+  author = authors.findOne authorId
+  test.equal author.postCount, 1
+
+  posts.remove postId2
+  author = authors.findOne authorId
+  test.equal author.postCount, 0
+
 #Test API:
 #test.isFalse(v, msg)
 #test.isTrue(v, msg)


switched to simpler api

switched to simpler api

--- tests/basic.coffee
+++ tests/basic.coffee
@@ -12,16 +12,13 @@
   posts = new Mongo.Collection null
   authors = new Mongo.Collection null

-  authors.computedFields.add 'postCount',
-    dependencies:
-      posts:
-        collection: posts
-        findId: (post) -> post.authorId
-        update: (author, post) ->
-          return if @isUpdate
-          inc = 1
-          inc = inc * -1 if @isRemove
-          @set inc
+  authors.computedFields.add('postCount').addDependency posts,
+    findId: (post) -> post.authorId
+    update: (author, post) ->
+      return if @isUpdate
+      inc = 1
+      inc = inc * -1 if @isRemove
+      @set author.postCount + inc

   authorId = authors.insert name: 'max'
   posts.insert


Avoid a needless refresh when phone for a user isnt provided, and log user refreshes to Sentry

--- src/mobile/components/layout/bootstrap.coffee
+++ src/mobile/components/layout/bootstrap.coffee
@@ -59,7 +59,8 @@
     'collector_level'
   ]
   for attr in attrs
-    if not _.isEqual data[attr], sd.CURRENT_USER[attr]
+    if (data[attr] or sd.CURRENT_USER[attr]) and not _.isEqual data[attr], sd.CURRENT_USER[attr]
+      RavenClient.captureException("Forced to refresh user", { extra: { attr: attr, session: sd.CURRENT_USER[attr], current: data[attr] } } )
       $.ajax('/user/refresh').then -> window.location.reload()

 syncAuth = module.exports.syncAuth = ->


better coffee build - handles one directory deep

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -116,10 +116,10 @@
     coffee:
       jitter:
         options:
-          bare: true
           sourceMap: true
-        files:
-          '<%%= config.temp %>/scripts/<%%= pkg.name %>.js': [ '<%%= config.app %>/scripts/*.coffee' ]<% } %>
+          sourceRoot: ''
+        files:
+          '<%%= config.temp %>/scripts/<%%= pkg.name %>.js': [ '<%%= config.app %>/scripts/{,*/}*.coffee' ]<% } %>

     concat:
       imports:


Update schemas.coffee

Update schemas.coffee

--- common/schemas.coffee
+++ common/schemas.coffee
@@ -1,6 +1,3 @@
-###
-# AddressBook
-###
 ReactionCore.Schemas.Location = new SimpleSchema
   _id:
     type: String


edit redirects back to form

edit redirects back to form

--- client/apps/settings/routes.coffee
+++ client/apps/settings/routes.coffee
@@ -19,15 +19,16 @@
     error: res.backboneError
     success: (curation) ->
       res.locals.sd.CURATION = curation.toJSON()
-      res.render 'curation_edit', curation: curation
+      res.redirect "/settings/curations/#{curation.get('id')}/edit"

 @saveCuration = (req, res) ->
   data = _.pick req.body, _.identity
   new Curation(id: req.params.id).save data,
     headers: 'X-Access-Token': req.user?.get('access_token')
     error: res.backboneError
-    success: ->
-      res.redirect '/settings/curations'
+    success: (curation) ->
+      res.locals.sd.CURATION = curation.toJSON()
+      res.redirect "/settings/curations/#{curation.get('id')}/edit"

 @channels = (req, res) ->
   new Channels().fetch


sidebar: remove 'shared by'

sidebar: remove 'shared by'

--- client/Main/navigation/navigationmachineitem.coffee
+++ client/Main/navigation/navigationmachineitem.coffee
@@ -40,7 +40,7 @@
       labelPartial = """
         #{labelPartial}
         <cite class='shared-by'>
-          (shared by @#{Encoder.htmlDecode machineOwner})
+          (@#{Encoder.htmlDecode machineOwner})
         </cite>
       """


pass the overlay to kdModalView

pass the overlay to kdModalView

--- client/app/lib/components/contentModal/index.coffee
+++ client/app/lib/components/contentModal/index.coffee
@@ -14,12 +14,11 @@
     modalOptions.cssClass = 'ContentModal'
     modalOptions.width or= 500

-    super modalOptions, data
-
-    @overlay = new kd.OverlayView
+    modalOptions.overlayOptions =
       isRemovable : no
       cssClass    : 'content-overlay'

+    super modalOptions, data

     @createHeader()
     @createBody()


Fix alignment

Fix alignment

--- grammars/csound-document.cson
+++ grammars/csound-document.cson
@@ -67,7 +67,7 @@
           2: name: 'invalid.deprecated.cabbage-gui.csound-document'
       },{
         # https://github.com/rorywalsh/cabbage/search?q=IdentArray+path%3ASource+filename%3ACabbageGUIClass.h
-        name: 'support.function.widget-identifier.cabbage-gui.csound-document'
+        name:  'support.function.widget-identifier.cabbage-gui.csound-document'
         match: '\\b(?i:a(?:ctive|ddress|l(?:ign|pha)|mprange)|bo(?:ld|unds)|c(?:aption|hannel(?:array|s|type)?|o(?:lou?r(?::[01])?|rners))|displaytype|f(?:fttablenumber|il[el]|ont(?:colou?r(?::[01])?|style))|g(?:radient|uirefresh)|highlightcolour|i(?:dentchannel|tems)|kind|l(?:atched|inethickness|ogger)|m(?:ax|i(?:ddlec|n)|ode)|numberofsteps|outline(?:colou?r|thickness)|p(?:l(?:ant|uginid)|o(?:pu(?:late|p(?:text)?)|s))|r(?:a(?:diogroup|nge[xy]?)|escale|otate)|s(?:amplerange|c(?:ale[xy]|r(?:oll(?:bars)?|ubberposition))|h(?:ape|ow)|ize|liderincr|tepbpm|vg(?:debug|file|path))|t(?:able(?:(?:background|grid)?colou?r|numbers?)|ext(?:box|colou?r)?|racker(?:colou?r|thickness))|v(?:alue|elocity|isible)|widgetarray|wrap|zoom)\\b'
       }
     ]


Disabled autosave

Disabled autosave

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -35,10 +35,10 @@
     'archive-view'
     'feedback'
     'sublime-tabs'
+    'jshint'
   ]
   'useReactEditor': true
-'autosave':
-  'enabled': true
+'autosave': {}
 'spell-check':
   'grammars': [
     'source.gfm'


Update Atom config for pylama

Update Atom config for pylama

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -24,6 +24,10 @@
     coloured: false
   linter:
     showErrorPanel: false
+  "linter-pylama":
+    ignoreErrorsAndWarnings: "E501"
+    pylamaVersion: "internal"
+    usePep257: false
   "one-dark-ui":
     layoutMode: "Compact"
   "spell-check":


Commented out deprecated atom configuration

--- atom.symlink/init.coffee
+++ atom.symlink/init.coffee
@@ -1,6 +1,6 @@
 path = require 'path'

-atom.workspaceView.eachEditorView (editorView) ->
-  editor = editorView.getEditor()
-  if path.extname(editor.getPath()) is '.md'
-    editor.setSoftWrap(true)
+# atom.workspaceView.eachEditorView (editorView) ->
+#   editor = editorView.getEditor()
+#   if path.extname(editor.getPath()) is '.md'
+#     editor.setSoftWrap(true)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/oscillator.coffee
+++ src/oscillator.coffee
@@ -5,7 +5,7 @@
 returned immediately. Even though it has a draw method it will not
 draw anything to the screen until the image has been loaded.

-@name Osci
+@name Oscillator
 @constructor
 ###
 Oscillator = (options={}) ->


fix(web): Fixing compilation error, better manifest depenency

--- apps/actor-web/gulpfile.coffee
+++ apps/actor-web/gulpfile.coffee
@@ -107,7 +107,7 @@
     .pipe gulp.dest './dist/'
     .pipe connect.reload()

-gulp.task 'manifest-prod', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push']
+gulp.task 'manifest:prod', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push'], ->
   gulp.src ['./dist/**/*']
     .pipe manifest {
       hash: true,
@@ -125,7 +125,7 @@
     root: ['./dist/', './']
     livereload: true

-gulp.task 'build', ['manifest-prod']
+gulp.task 'build', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push', 'manifest:prod']

 gulp.task 'build:dev', ['assets', 'browserify:watchify', 'sass', 'html', 'push']


Workspace: Resizing finder pane when a floating pane launched.

--- client/landing/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
+++ client/landing/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
@@ -35,6 +35,8 @@
       for own key, value of state
         pane = @getPaneByType key
         if value is no then @hidePane pane, key else @showPane pane, key
+
+      @resizePanes state

   click: ->
     @toggleClass "active"
@@ -128,5 +130,32 @@

     @preview.addSubView @previewPane

+  resizePanes: (state) ->
+    activePanel = @workspace.getActivePanel()
+
+    unless activePanel
+      return @workspace.once "WorkspaceSyncedWithRemote", =>
+        @resizePanes state
+
+    finder = activePanel.getPaneByName "finder"
+    return unless finder
+
+    finderContainer   = finder.container
+    finderNeedsResize = no
+
+    for own key, value of state
+      if key isnt "chat" and value is yes
+        finderNeedsResize = yes
+
+    return if not finderNeedsResize and not @finderResized
+
+    if finderNeedsResize
+      return if @finderResized
+      finderContainer.setHeight finderContainer.getHeight() - 400
+      @finderResized = yes
+    else
+      finderContainer.setHeight finderContainer.getHeight() + 400
+      @finderResized = no
+
   getPaneByType: (type) ->
     return @[type] or null


Inlined/moved start/stopHovering

Inlined/moved start/stopHovering

--- js-library/app/js/jail_iframe/classes/icon_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/icon_buttons.coffee
@@ -15,9 +15,6 @@
   _bindCallbacks: (callbacks) ->
     @_robustHover = new FactlinkJailRoot.RobustHover @$el, callbacks
     @$el.on 'click', -> callbacks.click?()
-
-  startHovering: => @frame.addClass 'hovered'
-  stopHovering: => @frame.removeClass 'hovered'

   # TODO: do all updates of different buttons in one pass
   _updatePositionRegularly: ->
@@ -41,6 +38,9 @@
     # remove when removing client_buttons feature toggle
     unless FactlinkJailRoot.can_haz.client_buttons
       @$el.addClass 'fl-button-hide-default'
+
+  startHovering: => @frame.addClass 'hovered'
+  stopHovering: => @frame.removeClass 'hovered'

   _textContainer: (el) ->
     for el in $(el).parents()
@@ -70,8 +70,8 @@
     super

     @_bindCallbacks
-      mouseenter: => @startHovering()
-      mouseleave: => @stopHovering()
+      mouseenter: => @frame.addClass 'hovered'
+      mouseleave: => @frame.removeClass 'hovered'
       click: => @_onClick()

     @$paragraph = $(options.el)


Style improvements

Style improvements

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -270,16 +270,23 @@
 		return false

 	@equal: (colorA, colorB) ->
+
 		if !@validColorValue(colorA)
 			if !Color.isColorString(colorA)
 				return false
+
 		if !@validColorValue(colorB)
 			if !Color.isColorString(colorB)
 				return false
+
 		colorA = new Color(colorA)
 		colorB = new Color(colorB)

-		return colorA.r == colorB.r && colorA.g == colorB.g && colorA.b == colorB.b && colorA.a == colorB.a
+		return false if colorA.r isnt colorB.r
+		return false if colorA.g isnt colorB.g
+		return false if colorA.b isnt colorB.b
+		return false if colorA.a isnt colorB.a
+		return true

 	@rgbToHsl: (a, b, c) ->
 		return rgbToHsl(a, b, c)


postpone shadowStyles until after constructor

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -231,6 +231,8 @@
 		@label = @label

 		delete @__constructor
+
+		@updateShadowStyle()

 		@onChange("size", @updateForSizeChange)

@@ -369,6 +371,7 @@
 			@updateShadowStyle()

 	updateShadowStyle: ->
+		return if @__constructor
 		@_element.style.boxShadow = LayerStyle["boxShadow"](@)
 		@_element.style.textShadow = LayerStyle["textShadow"](@)
 		@_element.style.webkitFilter = LayerStyle["webkitFilter"](@)


Fix image flow

Fix image flow

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -886,12 +886,15 @@
 			@_getPropertyValue "image"
 		set: (value) ->

+			currentValue = @_getPropertyValue "image"
+
+			if currentValue is value
+				return @emit "load"
+
 			# Unset the background color only if it’s the default color
 			defaults = Defaults.getDefaults "Layer", {}
 			if @backgroundColor?.isEqual(defaults.backgroundColor)
 				@backgroundColor = null
-
-			currentValue = @_getPropertyValue "image"

 			if LinearGradient.isLinearGradient(value)
 				@emit("change:gradient", value, currentValue)
@@ -902,9 +905,6 @@

 			if not (_.isString(value) or value is null)
 				layerValueTypeError("image", value)
-
-			if currentValue is value
-				return @emit "load"

 			# Set the property value
 			@_setPropertyValue("image", value)


Fix issue where not all arguments would be pushed to GA

--- src/scripts/helpers/handlers/analytics.coffee
+++ src/scripts/helpers/handlers/analytics.coffee
@@ -26,7 +26,9 @@

     # Wrapper functions to add analytics events
     # ga: () -> window.ga?.apply(@, arguments) # analytics.js
-    gaq: () -> window._gaq?.push(arguments[0]) # ga.js
+    gaq: () ->
+      console.log arguments[0] + ', ' + arguments[1]
+      window._gaq?.push(arguments[0], arguments[1]) # ga.js

     # Send the current page to every analytics service
     send: (account) ->
@@ -37,5 +39,4 @@
       account ?= settings.analyticsID

       # @ga('send', 'pageview')
-      @gaq(['_trackPageview', fragment])
-      @gaq.push(['_setAccount', account], ['_trackPageview'])
+      @gaq(['_setAccount', account], ['_trackPageview', fragment])


Change timeout to 30 minutes.

Change timeout to 30 minutes.

Some builds can take a very long time when doing an initial content fetch.

--- compiler/index.coffee
+++ compiler/index.coffee
@@ -87,10 +87,11 @@
             writeFile           : _writeFile
         )

-        # Set a timeout for the compiler. Must complete within 60 seconds.
+        # Set a timeout for the compiler.
+        TIMEOUT = 30 * 60 # 30 minutes
         _done_timeout = setTimeout ->
-            throw new SDKError('compiler', 'Compiler timeout. Compiler MUST call `done` within 60 seconds.')
-        , 60 * 1000
+            throw new SDKError('compiler', "Compiler timeout. Compiler MUST call `done` within #{ TIMEOUT } seconds.")
+        , TIMEOUT * 1000

         _done = ->
             SDKError.clearPrefix()


Add an extra wrapper in the link dialog

Add an extra wrapper in the link dialog

--- src/trix/elements/trix_toolbar_element.coffee
+++ src/trix/elements/trix_toolbar_element.coffee
@@ -55,9 +55,11 @@

-        <input type="url" required name="href" placeholder="Enter a URL...">
-        <input type="button" value="Link" data-method="setAttribute">
-        <input type="button" value="Unlink" data-method="removeAttribute">
+

+          <input type="url" required name="href" placeholder="Enter a URL...">
+          <input type="button" value="Link" data-method="setAttribute">
+          <input type="button" value="Unlink" data-method="removeAttribute">
+

   """


highlight data and add some default patterns

--- grammars/language-idris.cson
+++ grammars/language-idris.cson
@@ -9,7 +9,7 @@
     }
     {
       name: 'keyword.control.idris'
-      match: '\\b(if|then|else|do|let|in|data|codata|record|dsl)\\b'
+      match: '\\b(if|then|else|do|let|in|codata|record|dsl)\\b'
     }
     {
       name: 'keyword.control.idris'
@@ -131,8 +131,146 @@
           }
         ]
     }
+    {
+      name: 'meta.declaration.data.idris'
+      begin: "\\b(data)\\s+([\\w']+)\\s*(:)?"
+      beginCaptures:
+        1:
+          name: 'keyword.other.idris'
+        2:
+          name: 'entity.name.type.idris'
+        3:
+          name: 'keyword.operator.colon.idris'
+      end: '\\b(where)\\b|(=)'
+      endCaptures:
+        1:
+          name: 'keyword.other.idris'
+        2:
+          name: 'keyword.operator.idris'
+      patterns:
+        [
+          include: '#type_signature'
+        ]
+    }
   ]
 repository:
+  context_signature:
+    patterns:
+      [
+        {
+          name: 'meta.context-signature.idris'
+          match: "([\\w._']+)((\\s+[\\w_']+)+)\\s*(=>)"
+          captures:
+            1:
+              name: 'entity.other.inherited-class.idris'
+            2:
+              name: 'entity.other.attribute-name.idris'
+            4:
+              name: 'keyword.operator.double-arrow.idris'
+        }
+        {
+          name: 'meta.context-signature.idris'
+          comment:
+            """For things like '(Eq a, Show b) =>'
+            It begins with '(' either followed by ') =>' on the same line,
+            or anything but ')' until the end of line."""
+          begin: "(\\()((?=.*\\)\\s*=>)|(?=[^)]*$))"
+          beginCaptures:
+            1:
+              name: 'punctuation.context.begin.idris'
+          end: "(\\))\\s*(=>)"
+          endCaptures:
+            1:
+              name: 'punctuation.context.end.idris'
+            2:
+              name: 'keyword.operator.double-arrow.idris'
+          patterns:
+            [
+              {
+                name: 'meta.class-constraint.idris'
+                match: "([\\w']+)\\s+([\\w']+)"
+                captures:
+                  1:
+                    name: 'entity.other.inherited-class.idris'
+                  2:
+                    name: 'entity.other.attribute-name.idris'
+              }
+            ]
+        }
+      ]
+  parameter_type:
+    comment: "Parameter types in a type signature"
+    patterns:
+      [
+        {
+          include: '#prelude_type'
+        }
+        {
+          name: 'meta.parameter.named.idris'
+          comment: '(x : Nat)'
+          begin: "\\(([\\w']+)\\s*:(?!:)"
+          beginCaptures:
+            1:
+              name: 'entity.name.tag.idris'
+          end: "\\)"
+          patterns:
+            [
+              {
+                include: '#prelude_type'
+              }
+            ]
+        }
+        {
+          name: 'meta.parameter.implicit.idris'
+          comment: '{auto p : a = b}'
+          begin: "\\{((auto|default .+)\\s+)?([\\w']+)\\s*:(?!:)"
+          beginCaptures:
+            1:
+              name: 'storage.modifier.idris'
+            3:
+              name: 'entity.name.tag.idris'
+          end: '\\}'
+          patterns:
+            [
+              {
+                include: '#prelude_type'
+              }
+            ]
+        }
+      ]
+  type_signature:
+    patterns:
+      [
+        {
+          name: 'keyword.operator.arrow.idris'
+          match: '->'
+        }
+        {
+          include: '#context_signature'
+        }
+        {
+          include: '#parameter_type'
+        }
+        {
+          include: '#language_const'
+        }
+      ]
+  language_const:
+    patterns:
+      [
+        {
+          name: 'constant.language.unit.idris'
+          match: "\\(\\)"
+        }
+        {
+          name: 'constant.language.bottom.idris'
+          match: "_\\|_"
+        }
+        {
+          name: 'constant.language.underscore.idris'
+          match: "\\b_\\b"
+        }
+      ]
   escape_characters:
     patterns:
       [


only track if http is present

only track if http is present

--- components/layout/body/view.coffee
+++ components/layout/body/view.coffee
@@ -70,7 +70,7 @@
     if clientRoute and clientRoute isnt 'Channel' and clientRoute isnt 'User'
       Backbone.history.navigate "#{url}", trigger: true, replace: false
     else
-      trackOutboundLink url
+      trackOutboundLink(url) if url.indexOf('http')
       if e.metaKey || e.ctrlKey
         window.open(url, '_blank')
       else


live edit mode

live edit mode

--- obot.coffee
+++ obot.coffee
@@ -1,4 +1,5 @@
 level_cnv = document.getElementById('level')
+
 prog_cnv = document.getElementById('program')
 cont_cnv = document.getElementById('controls')

@@ -69,7 +70,7 @@
   bp = bot_points
   ctx.save()
   ctx.translate((@showxi + .5) * cell_size, (@showyi + .5) * cell_size)
-  ctx.rotate(-@dir)
+  ctx.rotate(-@showdir)

   ctx.beginPath()
   ctx.moveTo(bp[0].x, bp[0].y)
@@ -221,7 +222,7 @@
   # reusing the bot graphic
   bp = bot_points

-  ctx.fillStyle = ctx.strokeStyle
+  #ctx.fillStyle = ctx.strokeStyle

   ctx.beginPath()
   ctx.moveTo(bp[0].x, bp[0].y)
@@ -229,7 +230,8 @@
   ctx.lineTo(bp[2].x, bp[2].y)
   ctx.closePath()

-  ctx.fill()
+  #ctx.fill()
+  ctx.stroke()

   return

@@ -271,15 +273,16 @@
 # buttons

 addCommandButtonAction = (level) ->
-  if level.mode == levelMode.STOPPED or
-     level.mode == levelMode.LIVE_EDIT
-    level.commands[level.current_command] = @cmd
-    level.advanceCurrentCommand()
+  switch level.mode
+    when levelMode.STOPPED
+      level.commands[level.current_command] = @cmd
+      level.advanceCurrentCommand()
+    when levelMode.LIVE_EDIT
+      level.commands[level.commands.length] = @cmd

   return

 playButtonAction = (level) ->
-  console.log("play!")
   level.resetAndRun()
   return

@@ -318,20 +321,28 @@
   LIVE_EDIT: 2

 levelAnimMode =
-  READY: 0  # ready to run the next command
-  MOVING: 1 # animating movement
-  DONE: 2   # end of the program
+  READY: 1  # ready to run the next command
+#  TURNING: 2  # animating turn
+  MOVING: 3 # animating movement
+  DONE: 4   # end of the program
+
+resetLevel = ->
+  @bot.showxi = @bot.xi = @initial_bot_location.xi
+  @bot.showyi = @bot.yi = @initial_bot_location.yi
+  @bot.showdir = @bot.dir = @initial_bot_location.dir
+
+
+  @current_command = 0
+
+  return

 resetAndRunLevel = ->
-  @bot.xi = @initial_bot_location.xi
-  @bot.yi = @initial_bot_location.yi
-  @bot.dir = @initial_bot_location.dir
-
-  @current_command = 0
-
+  @reset()
   @mode = levelMode.PLAYING
   @last_t = Date.now()/1000
   @animation_mode = levelAnimMode.READY
+
+  return

 lerp = (t, x0, x1) ->
   if t < 0
@@ -346,35 +357,46 @@
     @show_current_command = @current_command
     @bot.showxi = @bot.xi
     @bot.showyi = @bot.yi
+    @bot.showdir = @bot.dir

     return

   dt = t - @last_t

-  switch @animation_mode
-    when levelAnimMode.READY
-      @show_current_command = @current_command
-      @move_control =
-        start: (t: t, x: @bot.xi, y: @bot.yi, dir: @bot.dir)
-        duration: 1/bot_speed
-      if not @step()
-        @animation_mode = levelAnimMode.DONE
-        @mode = levelMode.STOPPED
-      else
-        @animation_mode = levelAnimMode.MOVING
-
-    when levelAnimMode.MOVING
-      rel_t = (t-@move_control.start.t)/@move_control.duration
-      @bot.showxi = lerp rel_t, @move_control.start.x, @bot.xi
-      @bot.showyi = lerp rel_t, @move_control.start.y, @bot.yi
-
-      if rel_t >= 1
-        @animation_mode = levelAnimMode.READY
+  # PLAYING or LIVE_EDIT
+
+  if @animation_mode == levelAnimMode.READY
+    @show_current_command = @current_command
+    next_move_control =
+      start: (t: t, x: @bot.xi, y: @bot.yi, dir: @bot.dir)
+      duration: 1/bot_speed
+    if not @step()
+
+      switch @mode
+        when levelMode.PLAYING
+          @mode = levelMode.STOPPED
+          @animation_mode = levelAnimMode.DONE
+        #when levelMode.LIVE_EDIT keep waiting
+
+    else
+      @move_control = next_move_control
+      @animation_mode = levelAnimMode.MOVING
+
+  # immediately run this if we switched to MOVING above
+  if @animation_mode == levelAnimMode.MOVING
+    rel_t = (t-@move_control.start.t)/@move_control.duration
+    @bot.showxi = lerp rel_t, @move_control.start.x, @bot.xi
+    @bot.showyi = lerp rel_t, @move_control.start.y, @bot.yi
+    @bot.showdir = @bot.dir
+
+    if rel_t >= 1
+      @animation_mode = levelAnimMode.READY

   @last_t = t

+  return
+
 stepLevelSimulation = ->
-  console.log("step, @current_command=" + @current_command)

   if @current_command != null && @current_command < @commands.length
     @commands[@current_command].action(this)
@@ -382,22 +404,28 @@
     @advanceCurrentCommand()

     # TODO: limitations on movement
-    return true
+    true
   else
-    return false
-
-setupLevel = ->
-  initial_bot_location: (xi: 0, yi: 5, dir: 0)
-  bot: (showxi: 0, showyi: 0, xi: 0, yi: 0, render: renderBot, dir: 0)
-  commands: [ ]
-  current_command: 0
-  render: renderLevel
-  animate: animateLevel
-  step: stepLevelSimulation
-
-  mode: levelMode.STOPPED
-  advanceCurrentCommand: -> @current_command++
-  resetAndRun: resetAndRunLevel
+    false
+
+setupLevel = (initial_bot_location) ->
+  o =
+    initial_bot_location: initial_bot_location
+    bot: (render: renderBot)
+    commands: [ ]
+    current_command: 0
+    render: renderLevel
+    animate: animateLevel
+    step: stepLevelSimulation
+
+    mode: levelMode.LIVE_EDIT
+    animation_mode: levelAnimMode.READY
+
+    advanceCurrentCommand: -> @current_command++
+    reset: resetLevel
+    resetAndRun: resetAndRunLevel
+  o.reset()
+  o

 # control panel
 setupControlPanel = ->
@@ -408,7 +436,7 @@
   click: clickControlPanel

 control_panel = setupControlPanel()
-level_state = setupLevel()
+level_state = setupLevel((xi:0, yi:5, dir:0))

 stop = false


fix phantomjs crappiness

fix phantomjs crappiness


logic_block: Pre and post draw ticks must receive frame_duration as argument.

--- engine/logic_block.coffee
+++ engine/logic_block.coffee
@@ -4,8 +4,11 @@
     constructor: (@scene, @settings={})->
         scene = @scene
         @context = context = scene.context
-        if @init? then @init()
-        if @pre_draw_tick? then scene.pre_draw_callbacks.push(@pre_draw_tick.bind @)
-        if @post_draw_tick? then scene.post_draw_callbacks.push(@pre_draw_tick.bind @)
+        @init?()
+        if @pre_draw_tick?
+            scene.pre_draw_callbacks.push (scene)=> @pre_draw_tick.bind(@)(context.main_loop.frame_duration)
+
+        if @post_draw_tick?
+            scene.pre_draw_callbacks.push (scene)=> @post_draw_tick.bind(@)(context.main_loop.frame_duration)

 module.exports = {LogicBlock}


remove broken intersection operation in discussion reader

--- vue/src/shared/models/discussion_model.coffee
+++ vue/src/shared/models/discussion_model.coffee
@@ -152,7 +152,7 @@

   update: (attributes) ->
     if _.isArray(@readRanges) && _.isArray(attributes.readRanges) && !_.isEqual(attributes.readRanges, @readRanges)
-      attributes.readRanges = RangeSet.intersectRanges(@readRanges.concat(attributes.readRanges), @ranges)
+      attributes.readRanges = RangeSet.reduce(@readRanges.concat(attributes.readRanges))
     @baseUpdate(attributes)

   updateReadRanges: _.throttle ->


Remove redundant kill command.

Remove redundant kill command.

Removes a race condition when killing the server after the database tests would cause the connection for the next set of tests to die.

--- test/db.coffee
+++ test/db.coffee
@@ -9,7 +9,8 @@
     should.exist Impromptu

 describe 'Database', ->
-  kill = ->
+  # Try to kill the server if it's running.
+  before ->
     # Check if the server is running.
     path = Impromptu.db.REDIS_PID_FILE
     return unless fs.existsSync path
@@ -19,10 +20,7 @@
     # Make it die a painful death.
     exec "kill -9 #{pid}"

-  # Try to kill the server if it's running.
-  before kill
   after ->
-    kill()
     delete Impromptu.db._client

   it 'should exist', ->


code cleanup: remove unused class

code cleanup: remove unused class

--- workers/social/lib/social/models/limit.coffee
+++ workers/social/lib/social/models/limit.coffee
@@ -1,25 +0,0 @@
-jraphical = require 'jraphical'
-
-module.exports = class JLimit extends jraphical.Module
-
-  @share()
-
-  @set
-    sharedEvents    :
-      static        : [
-        { name : "RemovedFromCollection" }
-      ]
-      instance      : [
-        { name : "RemovedFromCollection" }
-      ]
-    schema      :
-      quota     :
-        type    : Number
-        default : 0
-      usage     :
-        type    : Number
-        default : 0
-      title     : String
-      unit      : String
-
-  getValue:-> @getAt('quota') - @getAt('usage')


fixing open date disabled spec

fixing open date disabled spec

--- test/components/task-plan/builder.spec.coffee
+++ test/components/task-plan/builder.spec.coffee
@@ -14,9 +14,10 @@
 COURSES = require '../../../api/user/courses.json'
 NEW_READING = ExtendBasePlan({id: "_CREATING_1", settings: {page_ids: []}})
 PUBLISHED_MODEL = ExtendBasePlan({
+  id: '1'
   title: 'hello',
   description: 'description',
-  published_at: yesterday}, {opens_at: yesterday})
+  published_at: yesterday}, {opens_at: yesterday, due_at: yesterday, target_id: COURSES[0].periods[0].id})

 helper = (model) -> PlanRenderHelper(model, Builder)

@@ -45,7 +46,6 @@

   it 'should not allow editable open date if plan is visible', ->
     helper(PUBLISHED_MODEL).then ({dom, element}) ->
-      expect(dom.querySelectorAll('.tasking-plan.disabled').length).to.equal(COURSES[0].periods.length)
       element.setAllPeriods()
       expect(element.refs.openDate.props.disabled).to.be.true


Support for hyperref URL commands

Support for hyperref URL commands

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -138,7 +138,7 @@
       { include: '#makeAtSomething' }
       { include: '#verbatim' }
       { include: '#item' }
-      # { include: '#url' }
+      { include: '#metaHyperref' }
       # { include: '#text' } # To break out of mathmode; need to investigate how to enforce plain styling
       # { include: '#commandDefinitions' }
       # { include: '#styling' } # eg. \bf \it (deprecated)
@@ -655,6 +655,100 @@
     name: 'keyword.control.latex'
     match: '\\\\item'
   }
+  metaHyperref: {
+    comment: 'Intended to match the url commands provided by hyperref. Less lenient than I would like, but difficult to improve.'
+    patterns: [
+      {
+        comment: '\href[options]{URL}{text}'
+        begin: '((\\\\)href)\\s*(?:(\\[)(.*?)(\\]))?\\s*(\\{)(.*?)(\\})(\\{)'
+        beginCaptures:
+          1:
+            name: 'support.function.href.latex'
+          2:
+            name: 'punctuation.definition.function.latex'
+          3:
+            name: 'punctuation.definition.arguments.optional.begin.latex'
+          4:
+            name: 'variable.parameter.optional.latex'
+          5:
+            name: 'punctuation.definition.arguments.optional.end.latex'
+          6:
+            name: 'punctuation.definition.arguments.begin.latex'
+          7:
+            name: 'markup.underline.link.latex'
+          8:
+            name: 'punctuation.definition.arguments.end.latex'
+          9:
+            name: 'punctuation.definition.arguments.begin.latex'
+        end: '(\\}|^\\s*$)'
+        endCaptures:
+          0:
+            name: 'punctuation.definition.arguments.end.latex'
+        contentName: 'variable.parameter.latex'
+        patterns: [
+          { include: '$self' }
+        ]
+      }
+      {
+        comment: '\url{URL}'
+        match: '((\\\\)(?:nolink|hyperbase)url)(\\{)(.*?)(\\})'
+        captures:
+          1:
+            name: 'support.function.url.latex'
+          2:
+            name: 'punctuation.definition.function.latex'
+          3:
+            name: 'punctuation.definition.arguments.begin.latex'
+          4:
+            name: 'markup.underline.link.latex'
+          5:
+            name: 'punctuation.definition.arguments.end.latex'
+      }
+      {
+        comment: '\hyperimage{URL}{text}'
+        begin: '((\\\\)hyperimage)(\\{)(.*?)(\\})(\\{)'
+        beginCaptures:
+          1:
+            name: 'support.function.url.image.latex'
+          2:
+            name: 'punctuation.definition.function.latex'
+          3:
+            name: 'punctuation.definition.arguments.begin.latex'
+          4:
+            name: 'markup.underline.link.latex'
+          5:
+            name: 'punctuation.definition.arguments.end.latex'
+          6:
+            name: 'punctuation.definition.arguments.begin.latex'
+        end: '(\\}|^\\s*$)'
+        endCaptures:
+          0:
+            name: 'punctuation.definition.arguments.end.latex'
+        contentName: 'variable.parameter.latex'
+        patterns: [
+          { include: '$self' }
+        ]
+      }
+      # {
+      #   comment: '\hyperdef{category}{name}{text}'
+      # }
+      {
+        comment: '\hyperref{URL}{category}{name}{text}; will only attempt to match the URL part.'
+        match: '((\\\\)hyperref)(\\{)(.*?)(\\})'
+        captures:
+          1:
+            name: 'support.function.url.latex'
+          2:
+            name: 'punctuation.definition.function.latex'
+          3:
+            name: 'punctuation.definition.arguments.begin.latex'
+          4:
+            name: 'markup.underline.link.latex'
+          5:
+            name: 'punctuation.definition.arguments.end.latex'
+      }
+    ]
+  }

   # Experimental
   unitMeasure: {


More accurate #n parameter scoping

More accurate #n parameter scoping

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -104,7 +104,7 @@
   }
   metaHashtag: {
     comment: 'I dont like calling this the pound symbol. Catcode says it is a parameter identifier.'
-    match: '(\\#{3,}[0-9]+)|(\\#{2}[0-9]+)|(\\#[0-9]+)'
+    match: '(\\#{3,}[1-9])|(\\#{2}[1-9])|(\\#[1-9])'
     captures:
       1: name: 'variable.parameter.hashtag.3.latex'
       2: name: 'variable.parameter.hashtag.2.latex'


Move macro commands to repository

Move macro commands to repository

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -48,73 +48,7 @@
     'include': '#subsetting'
   }
   {
-    'begin': '\\b(gl(o|ob|oba|obal)?)\\s+(?=[a-zA-Z_`\\$])'
-    'beginCaptures':
-      '1':
-        'name': 'storage.modifier.macro.test.stata'
-    'end': '(\\})|(?=\\"|\\s|\\n|/|,|=)'
-    'patterns': [
-      {
-        'include': '#reserved_names'
-      }
-      {
-        'match': '([A-Za-z_][A-Za-z0-9_]{0,31})'
-        'name': 'entity.name.type.class.stata'
-      }
-      {
-        'include': '#macro-local'
-      }
-      {
-        'include': '#macro-global'
-      }
-    ]
-  }
-  {
-    'begin': '\\b(loc(a|al)?)\\s+(\\+\\+|\\-\\-)?(?=[a-zA-Z_`\\$])'
-    'beginCaptures':
-      '1':
-        'name': 'storage.modifier.macro.test.stata'
-      '3':
-        'name': 'keyword.operator.arithmetic.stata'
-    'end': '(?=:|\\"|\\s|\\n|/|,|=)'
-    'patterns': [
-      {
-        'match': '[^a-zA-Z0-9_\'`\\$\\(\\)]'
-        'name': 'invalid.illegal.name.stata'
-      }
-      {
-        'match': '([A-Za-z_][A-Za-z0-9_]{0,31})'
-        'name': 'entity.name.type.class.stata'
-      }
-      {
-        'include': '#macro-local'
-      }
-      {
-        'include': '#macro-global'
-      }
-    ]
-  }
-  {
-    'begin': '\\b(tempvar|tempname|tempfile)\\s+(?=[a-zA-Z_`\\$])'
-    'beginCaptures':
-      '1':
-        'name': 'storage.modifier.macro.test.stata'
-    'end': '(?=\\"|\\s|\\n|/|,)'
-    'patterns': [
-      {
-        'include': '#reserved_names'
-      }
-      {
-        'match': '([A-Za-z_][A-Za-z0-9_]{0,31})'
-        'name': 'entity.name.type.class.stata'
-      }
-      {
-        'include': '#macro-local'
-      }
-      {
-        'include': '#macro-global'
-      }
-    ]
+    'include': '#macro-commands'
   }
   {
     'comment': 'keywords that delimit flow conditionals'
@@ -777,6 +711,103 @@
             'name': 'keyword.docblockr.stata'
       }
     ]
+  'macro-commands':
+    'patterns': [
+      {
+        'begin': '\\b(gl(o|ob|oba|obal)?)\\s+(?=[a-zA-Z_`\\$])'
+        'beginCaptures':
+          '1':
+            'name': 'keyword.macro.stata'
+        'end': '(\\})|(?=\\"|\\s|\\n|/|,|=)'
+        'patterns': [
+          {
+            'include': '#reserved_names'
+          }
+          {
+            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'name': 'entity.name.type.class.stata'
+          }
+          {
+            'include': '#macro-local'
+          }
+          {
+            'include': '#macro-global'
+          }
+        ]
+      }
+      {
+        'begin': '\\b(loc(a|al)?)\\s+(\\+\\+|\\-\\-)?(?=[a-zA-Z_`\\$])'
+        'beginCaptures':
+          '1':
+            'name': 'keyword.macro.stata'
+          '3':
+            'name': 'keyword.operator.arithmetic.stata'
+        'end': '(?=\\"|\\s|\\n|/|,|=)'
+        'patterns': [
+          {
+            'match': '[^a-zA-Z0-9_\'`\\$\\(\\)]'
+            'name': 'invalid.illegal.name.stata'
+          }
+          {
+            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'name': 'entity.name.type.class.stata'
+          }
+          {
+            'include': '#macro-local'
+          }
+          {
+            'include': '#macro-global'
+          }
+        ]
+      }
+      {
+        'begin': '\\b(tempvar|tempname|tempfile)\\s+(?=[a-zA-Z_`\\$])'
+        'beginCaptures':
+          '1':
+            'name': 'keyword.macro.stata'
+        'end': '(?=\\"|\\s|\\n|/|,)'
+        'patterns': [
+          {
+            'include': '#reserved_names'
+          }
+          {
+            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'name': 'entity.name.type.class.stata'
+          }
+          {
+            'include': '#macro-local'
+          }
+          {
+            'include': '#macro-global'
+          }
+        ]
+      }
+      {
+        'begin': '\\b(ma(c|cr|cro)?)\\s+(drop|l(i|is|ist)?)'
+        'beginCaptures':
+          '0':
+            'name': 'keyword.macro.stata'
+        'end': '(?=\\n)'
+        'patterns': [
+          {
+            'include': '#constants'
+          }
+          {
+            'include': '#macro-global'
+          }
+          {
+            'include': '#macro-local'
+          }
+          {
+            'include': '#comments'
+          }
+          {
+            'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'name': 'entity.name.type.class.stata'
+          }
+        ]
+      }
+    ]
   'macro-local-escaped':
     'patterns': [
       {


social: style changes, and a a TODO

social: style changes, and a a TODO

--- workers/social/lib/social/models/name.coffee
+++ workers/social/lib/social/models/name.coffee
@@ -41,14 +41,18 @@

   @fetchSecretName =(name, callback)->
     JSecretName = require './secretname'
-    JSecretName.one {name}, (err, secretNameObj)->
+    JSecretName.one {name}, (err, secretNameObj) =>
       if err then callback err
       else unless secretNameObj
         secretNameObj = new JSecretName {name}
-        secretNameObj.save (err)->
+        secretNameObj.save (err) =>
           if err then callback err
           else callback null, secretNameObj.secretName
-      else callback null, secretNameObj.secretName, secretNameObj.oldSecretName
+      else
+        # TODO: we could possibly try to create the conversation slice by
+        #  observing an event that we could emit from here.
+        #@emit 'channel-control'
+        callback null, secretNameObj.secretName, secretNameObj.oldSecretName

   slowEach_ =(cursor, callback=->)->
     cursor.nextModel (err, name)->
@@ -132,8 +136,8 @@
       callback new KodingError 'Access denied!'
     else
       @claimAll [
-        {konstructor: require('./user'),  usedAsPath: 'username'}
-        {konstructor: require('./group'), usedAsPath: 'slug'}
+        { konstructor: require('./user'),  usedAsPath: 'username' }
+        { konstructor: require('./group'), usedAsPath: 'slug' }
       ], callback

   @claim =(fullName, slugs, konstructor, usedAsPath, callback)->


added some trace.

added some trace.

--- node_modules/koding-logger/lib/koding-logger/index.coffee
+++ node_modules/koding-logger/lib/koding-logger/index.coffee
@@ -8,7 +8,10 @@
     @readyState = 0
     @pendingQueue = []
     @connection = amqp.createConnection(amqpOptions)
+    @connection.on 'error', =>
+      console.log 'there was an error', arguments
     @connection.on 'ready', =>
+      console.log 'it worked'
       @connection.exchange 'logs',
         durable     : no
         autoDelete  : yes