Fehler beim Zeiteintragen im Firefox korrigieren

--- app/assets/javascripts/lib/enter_times.js.coffee
+++ app/assets/javascripts/lib/enter_times.js.coffee
@@ -24,7 +24,7 @@
     text = label.text()
     input.on('change keyup paste', () ->
       value = input.val()
-      if (!input[0].validity? || !input[0].validity.badInput) && (value.match(/^\d+[.,]\d{1,2}$/) || value.match(/^\s*$/))
+      if (!input[0].validity? || !input[0].validity.badInput) && (value.match(/^\d+([.,]\d{1,2})?$/) || value.match(/^\s*$/))
         label.text(text)
         tr.removeClass('danger')
       else


[grunt] watch for binary gettext files too

Just in case

--- tasks/grunt-contrib-watch.coffee
+++ tasks/grunt-contrib-watch.coffee
@@ -16,7 +16,7 @@
       files: ['<%= path.source.data %>/{,**/}*.{json,yml,js,coffee}']
       tasks: ['nunjucks']
     locales:
-      files: ['<%= path.source.locales %>/{,**/}*.po']
+      files: ['<%= path.source.locales %>/{,**/}*.{po.mo}']
       tasks: ['nunjucks']
     fonts:
       files: ['<%= path.source.fonts %>/{,**/}*']


revert rain

revert rain

--- app/assets/javascripts/weather/weather.js.coffee
+++ app/assets/javascripts/weather/weather.js.coffee
@@ -79,7 +79,7 @@

 init_theme = (block) ->
 #  snowfall()
-# rainfall()
+  rainfall()

 window.wheater_proc = ->


Use LikeViewClean in comments

Use LikeViewClean in comments

--- client/app/CommonViews/comments/commentlistitemview.coffee
+++ client/app/CommonViews/comments/commentlistitemview.coffee
@@ -46,7 +46,7 @@
           listenedToInstance : @deleteLink
           callback           : => @confirmDeleteComment data

-    @likeView = new LikeView {}, data
+    @likeView = new LikeViewClean { tooltipPosition : 'sw' }, data

   render:->
     if @getData().getAt 'deletedAt'
@@ -119,8 +119,8 @@
           {{@utils.applyTextExpansions #(body), yes}}

         {{> @deleteLink}}
-        {{> @likeView}}
         <time>{{$.timeago #(meta.createdAt)}}</time>
+        {{> @likeView}}

     """


Encode updated machine label for XSS vulnerabilities

--- client/app/lib/providers/machinesettingsgeneralview.coffee
+++ client/app/lib/providers/machinesettingsgeneralview.coffee
@@ -1,5 +1,6 @@
 kd                     = require 'kd'
 KDView                 = kd.View
+Encoder                = require 'htmlencode'
 Machine                = require './machine'
 showError              = require 'app/util/showError'
 KodingSwitch           = require '../commonviews/kodingswitch'
@@ -68,7 +69,9 @@

       return if showError err

-      nickname.updatePartial "update #{newLabel}"
+      label = Encoder.XSSEncode newLabel
+
+      nickname.updatePartial "update #{label}"

       nickEdit.hide()
       nickname.show()


Initialise options-page link-hint mode correctly (better).

--- pages/options.coffee
+++ pages/options.coffee
@@ -229,8 +229,6 @@
     element.className = element.className + " example info"
     element.innerHTML = "Leave empty to reset this option."

-  $("filterLinkHints").checked = bgSettings.get "filterLinkHints"
-  maintainLinkHintsView()
   window.onbeforeunload = -> "You have unsaved changes to options." unless $("saveOptions").disabled

   document.addEventListener "keyup", (event) ->
@@ -260,6 +258,8 @@
   for name, type of options
     new type(name,onUpdated)

+  maintainLinkHintsView()
+
 initPopupPage = ->
   chrome.tabs.getSelected null, (tab) ->
     exclusions = null


core logic coverd by unit test

core logic coverd by unit test

--- test/spec/componentsSpecs.coffee
+++ test/spec/componentsSpecs.coffee
@@ -28,3 +28,151 @@
         expect(range.markX(6).toString(2)).toBe('111111111100')
         expect(range.markY(2).toString(2)).toBe('111111')
         expect(range.markY(6).toString(2)).toBe('1111110000')
+
+    it 'CellModel', ->
+        list =
+            clazz:    [    State,           Point,           Range            ]
+            instance: [new State(true), new Point(1, 2), new Range(3, 4, 5, 6)]
+
+        cellModel = new CellModel()
+        cellModel.add i for i in list.instance
+
+        item = cellModel.gets State, Point, Range
+
+        for i in [0...list.clazz.length]
+            expect(cellModel.get(list.clazz[i])).toBe(list.instance[i])
+
+        for k, v in item
+            expect(v in list.instance).toBeTruthy()
+
+    it 'GridModel', ->
+        grid = new GridModel 3, 5, Array
+        expect(grid.getCell(0, 0)).toEqual(jasmine.any(Array))
+        expect(grid.width()).toBe(3)
+        expect(grid.height()).toBe(5)
+        expect(grid.getRow(1)[2]).toBe(grid.getCol(2)[1])
+        expect(grid.getRow(2)[2]).toBe(grid.getCol(2)[2])
+        expect(grid.getCol(1)[2]).toBe(grid.getCell(1, 2))
+        expect(grid.getRow(2)[1]).toBe(grid.getCell(1, 2))
+
+
+describe 'Calculate check', ->
+
+    [BLOCK, TARGET, PATH, SEPARATOR, NEWLINE] = '#@= \n'.split('')
+
+    beforeEach ->
+
+        @addMatchers
+
+            connecting: ->
+
+                matrix = for row in @actual.split NEWLINE
+                    row.split SEPARATOR
+
+                [width, height] = [matrix.length, matrix[0].length]
+
+                targets = []
+                grid = new GridModel width, height, CellModel
+                calulate = new Calculate grid
+
+                for x in [0...height]
+                    for y in [0...width]
+                        char = matrix[y][x]
+
+                        cell = grid.getCell x, y
+                        cell.add new State char is PATH
+                        cell.add new Point x, y
+                        cell.add new Range
+
+                        targets.push cell if char is TARGET
+
+                for target in targets
+                    point = target.get Point
+                    range = target.get Range
+                    row = grid.getRow point.y
+                    col = grid.getCol point.x
+
+                    for i in [point.x - 1..0]
+                        break unless row[i]?.get(State).done
+                        range.left++
+
+                    for i in [point.x + 1...row.length]
+                        break unless row[i]?.get(State).done
+                        range.right++
+
+                    for i in [point.y - 1..0]
+                        break unless col[i]?.get(State).done
+                        range.top++
+
+                    for i in [point.y + 1...col.length]
+                        break unless col[i]?.get(State).done
+                        range.bottom++
+
+                calulate.hasMatch targets...
+
+    it 'connecting', ->
+
+        expect(i).connecting() for i in [
+            '''
+            # # # # #
+            # # @ # #
+            # # @ # #
+            # # # # #
+            # # # # #
+            '''
+            '''
+            # # # # #
+            # # # # #
+            # @ @ # #
+            # # # # #
+            # # # # #
+            '''
+            '''
+            # # # # #
+            # # @ # #
+            # # = # #
+            # # @ # #
+            # # # # #
+            '''
+            '''
+            # # # # #
+            # @ = # #
+            # # = # #
+            # # = @ #
+            # # # # #
+            '''
+            '''
+            # # # # #
+            # # # @ #
+            # = = = #
+            # @ # # #
+            # # # # #
+            '''
+        ]
+
+    it 'not connecting', ->
+
+        expect(i).not.connecting() for i in [
+            '''
+            # # # # #
+            # # @ # #
+            # # # # #
+            # # @ # #
+            # # # # #
+            '''
+            '''
+            # # # # #
+            # # # # #
+            # @ # @ #
+            # # # # #
+            # # # # #
+            '''
+            '''
+            # # # # #
+            # = # = #
+            # @ # @ #
+            # # # # #
+            # # # # #
+            '''
+        ]
+


Credential: do not delete custom vars credential if vars are empty

--- client/stacks/lib/views/stacks/updatecustomvariable.coffee
+++ client/stacks/lib/views/stacks/updatecustomvariable.coffee
@@ -54,15 +54,9 @@
     JCredential.one identifier, (err, credential) ->
       if err or not credential
         createAndUpdate { provider, title, meta, stackTemplate }, callback
-      else if meta and (Object.keys meta).length
+      else
         credential.update { meta, title }, (err) ->
           shareWithGroup credential, ->
-            callback err, stackTemplate
-      else
-        credential.delete (err) ->
-          { credentials } = stackTemplate
-          delete credentials.custom
-          stackTemplate.update { credentials }, (err) ->
             callback err, stackTemplate

   else


Fourth batch of translations

Fourth batch of translations

--- def/nl/settings.cson
+++ def/nl/settings.cson
@@ -3,7 +3,7 @@
   menu: [
     {
       _label: "Core"
-      value: "Core"
+      value: "Basis"
     }
     {
       _label: "Editor"
@@ -11,19 +11,19 @@
     }
     {
       _label: "System"
-      value: "System"
+      value: "Systeem"
     }
     {
       _label: "Keybindings"
-      value: "Keybindings"
+      value: "Sneltoetsen"
     }
     {
       _label: "Packages"
-      value: "Packages"
+      value: "Pakketten"
     }
     {
       _label: "Themes"
-      value: "Themes"
+      value: "Thema's"
     }
     {
       _label: "Updates"
@@ -31,50 +31,51 @@
     }
     {
       _label: "Install"
-      value: "Install"
+      value: "Installeren"
     }
   ]
   "btn-open-config-folder": "Open Config Folder"
   sectionHeadings: [
-    {_label: "Core Settings", value: "Core Settings"}
-    {_label: "Editor Settings", value: "Editor Settings"}
-    {_label: "System Settings", value: "System Settings"}
-    {_label: "Keybindings", value: "Keybindings"}
-    {_label: "Installed Packages", value: "Installed Packages"}
-    {_label: "Choose a Theme", value: "Choose a Theme"}
-    {_label: "Installed Themes", value: "Installed Themes"}
-    {_label: "Install Packages", value: "Install Packages"}
-    {_label: "Featured Packages", value: "Featured Packages"}
-    {_label: "Install Themes", value: "Install Themes"}
-    {_label: "Featured Themes", value: "Featured Themes"}
+    {_label: "Core Settings", value: "Basis Instellingen"}
+    {_label: "Editor Settings", value: "Editor Instellingen"}
+    {_label: "System Settings", value: "Systeem Instellingen"}
+    {_label: "Keybindings", value: "Sneltoetsen"}
+    {_label: "Installed Packages", value: "Geinstalleerde Pakketten"}
+    {_label: "Choose a Theme", value: "Kies Een Thema"}
+    {_label: "Installed Themes", value: "Geinstalleerde Thema's"}
+    {_label: "Install Packages", value: "Installeer Pakket"}
+    {_label: "Featured Packages", value: "Aanbevolen Pakketten"}
+    {_label: "Install Themes", value: "Installeer Thema"}
+    {_label: "Featured Themes", value: "Aanbevolen Thema's"}
   ]
-  "heading-available-updates": {_label: "Available Updates", value: "Available Updates"}
+  "heading-available-updates": {_label: "Available Updates", value: "Beschikbare Updates"}
   subSectionHeadings: [
-    {_label: "Invisible", value: "Invisible"}
-    {_label: "Community Packages", value: "Community Packages"}
-    {_label: "Core Packages", value: "Core Packages"}
-    {_label: "Development Packages", value: "Development Packages"}
+    {_label: "Invisible", value: "Onzichtbaar"}
+    {_label: "Community Packages", value: "Community Pakketten"}
+    {_label: "Core Packages", value: "Basis Pakketten"}
+    {_label: "Development Packages", value: "Pakketten In Ontwikkeling"}
     {_label: "Git Packages", value: "Git Packages"}
-    {_label: "Community Themes", value: "Community Themes"}
-    {_label: "Core Themes", value: "Core Themes"}
-    {_label: "Development Themes", value: "Development Themes"}
-    {_label: "Git Themes", value: "Git Themes"}
+    {_label: "Community Themes", value: "Community Thema's"}
+    {_label: "Core Themes", value: "Basis Thema's"}
+    {_label: "Development Themes", value: "Thema's In Ontwikkeling"}
+    {_label: "Git Themes", value: "Git Thema's"}
   ]
   settings: {
     notes: [
       {
         _id: 'core-settings-note'
-        html: 'These are Atom\'s core settings which affect behavior
-               unrelated to text editing. Individual packages may have
-               their own additional settings found within their package
-               card in the Packages list.'
+        html: 'Dit zijn de basis instellingen van Atom welke het gedrag
+               van het pakket beïnvloeden die niet gerelateerd zijn aan
+               die van tekstbewerking. Individuele pakketten kunnen hun eigen
+               aanvullende instellingen hebben welke terug zijn te vinden
+               in de detail kaart van het pakket in de Pakkettenlijst.'
       }
       {
         _id: 'editor-settings-note'
-        html: 'These settings are related to text editing. Some of these
-               can be overriden on a per-language basis. Check language
-               settings by clicking its package card in the
-               Packages list.'
+        html: 'Deze instellingen zijn gerelateerd aan tekstbewerking.
+               Sommigen kunnen per ontwikkeltaal gezet worden. Controleer
+               ontwikkeltaal instellingen door te klikken op the detail
+               kaart van het pakket in de Pakkettenlijst.'
       }
     ]
     controls: [


Rename test folder to tests

Rename test folder to tests

--- test/hello_test.coffee
+++ test/hello_test.coffee
@@ -1,20 +0,0 @@
-Helper = require('hubot-test-helper')
-chai = require 'chai'
-
-expect = chai.expect
-
-helper = new Helper('../scripts/hello.coffee')
-
-describe 'example script', ->
-  beforeEach ->
-    @room = helper.createRoom()
-
-  afterEach ->
-    @room.destroy()
-
-  it 'say HELLO', ->
-    @room.user.say('alice', 'HELLO').then =>
-      expect(@room.messages).to.eql [
-        ['alice', 'HELLO']
-        ['hubot', 'Hi']
-      ]


Added test case for popping

Added test case for popping

--- test/leak-proof.coffee
+++ test/leak-proof.coffee
@@ -34,3 +34,6 @@
       items.push Item()
       assert.equal items.listeners.length, 1
       assert.equal activeItem.listeners.length, 5
+      items.pop()
+      assert.equal items.listeners.length, 1
+      assert.equal activeItem.listeners.length, 4


Oscillator

Oscillator


Placeholders for uploading photos and project info

--- project.coffee
+++ project.coffee
@@ -12,7 +12,16 @@
                  '''

-  pages: []
+  pages: [{
+      'Upload': 'This will be the iRecord form.'
+    },{
+    'About': [
+      {
+        'Orchid Observers': 'About the project.'
+      },{
+        'The Team': 'About the people.'
+      }]
+  }]

   externalLinks: {}


cancel logic

cancel logic

--- lib/assets/javascripts/socmap_adf/modules/file_uploader/views/main.js.coffee
+++ lib/assets/javascripts/socmap_adf/modules/file_uploader/views/main.js.coffee
@@ -15,7 +15,7 @@
     _.bindAll(this, 'render')
     @documents = []
     @onComplete = @options.onComplete if @options.onComplete
-    @onCancel = @options.onCancel if @options.onComplete
+    @onCancel = @options.onCancel if @options.onCancel
     @onProgressCallback = @options.onProgress if @options.onProgress
     @documents = @options.documents if @options.documents
     @allowedExtensions = @options.allowedExtensions if @options.allowedExtensions?
@@ -52,6 +52,8 @@

   onProgress: () =>
     @onProgressCallback() if @onProgressCallback
+
+  onCancel: () =>

   onSubmit: (id, fileName) =>
     obj = {id: id, name: fileName}
@@ -67,4 +69,5 @@
       if parseInt(document.id) == parseInt(id)
         document.remove = true
         @$("#file-uploaded-file-#{id}").remove()
+        @onCancel()
     return false


FAILS: implement transaction-per-test in migration.coffee.

--- tests/migration.coffee
+++ tests/migration.coffee
@@ -21,7 +21,9 @@
 queryUtils = require '../lib/query_utils'
 sync = require 'sync'
 anyDB = require 'any-db'
-
+transaction = require 'any-db-transaction'
+
+_conn = null
 conn = null
 query = null

@@ -49,35 +51,32 @@

 # if this won't crash, everything should be OK
-revisionBackup = null
 migrationDataBackup = []

+
 exports.setUp = (->
-	conn = anyDB.createConnection(config.DATABASE_URL_TEST)
+	unless _conn?
+		_conn = anyDB.createConnection(config.DATABASE_URL_TEST)
+	conn = transaction(_conn, autoRollback: false)
 	query = queryUtils.getFor conn
-	try
-		revisionBackup = query.val 'SELECT revision FROM revision'
-	catch e
-		revisionBackup = null
 	migrationDataBackup = mg.getMigrationsData()
-	query 'DROP TABLE IF EXISTS test_table, other_table, revision'
+	query 'DROP TABLE IF EXISTS revision'
 	mg.setMigrationsData migrationData
 ).async()

-exports.tearDown = ( ->
-	query 'DROP TABLE IF EXISTS test_table, other_table, revision'
+exports.tearDown = (->
 	mg.setMigrationsData migrationDataBackup
-	if revisionBackup != null
-		mg.setRevision.sync null, conn, revisionBackup
-	conn.end()
+	conn.rollback.sync(conn)
 ).async()

 exports.getCurrentRevision =
-	'usual': (test) ->
+	'no table yet': (test) ->
 		rev = mg.getCurrentRevision.sync null, conn
 		test.strictEqual rev, -1, 'should return -1 if revision table is not created'
-
+		test.done()
+
+	'usual': (test) ->
 		query 'CREATE TABLE revision (revision INT)'
 		query 'INSERT INTO revision VALUES (945)'
 		rev = mg.getCurrentRevision.sync null, conn


Not fixing this bug in this branch.

Not fixing this bug in this branch.

Revert "Fixed race condition in closing this layout"

This reverts commit b6fdb8ac7048e5e58c184e152b85e8ac8f64fc84.

--- core/app/backbone/layouts/tabbed_main_region_layout.coffee
+++ core/app/backbone/layouts/tabbed_main_region_layout.coffee
@@ -1,10 +1,10 @@
 class window.TabbedMainRegionLayout extends Backbone.Marionette.Layout
   template: 'layouts/tabbed_main_region'
   showTitle: (title) ->
-    @titleRegion?.show(new TextView(model: new Backbone.Model(text:title), tagName: 'h1'))
+    @titleRegion.show(new TextView(model: new Backbone.Model(text:title), tagName: 'h1'))

   setTitle: (title) ->
-    @titleRegion?.currentView.model.set('text',title)
+    @titleRegion.currentView.model.set('text',title)

   regions:
     titleRegion:   '.js-title-region'


Don't trigger autocomplete when editing existing environment

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -11,6 +11,9 @@
 			return m[1]
 		else
 			return null
+
+	getCommandNameFromFragment = (commandFragment) ->
+		commandFragment?.match(/\\(\w+)\{/)?[1]

 	class AutoCompleteManager
 		constructor: (@$scope, @editor, @element, @labelsManager) ->
@@ -122,6 +125,9 @@
 			range = new Range(end.row, 0, end.row, end.column)
 			lineUpToCursor = @editor.getSession().getTextRange(range)
 			commandFragment = getLastCommandFragment(lineUpToCursor)
+			commandName = getCommandNameFromFragment(commandFragment)
+			if commandName in ['begin', 'end']
+				return
 			# Check that this change was made by us, not a collaborator
 			# (Cursor is still one place behind)
 			# NOTE: this is also the case when a user backspaces over a highlighted region


fix pong handler

fix pong handler

--- app/coffee/modules/events.coffee
+++ app/coffee/modules/events.coffee
@@ -204,7 +204,7 @@
         @.log.debug "WebSocket message received: #{event.data}"

         data = JSON.parse(event.data)
-        if data.cmd = "pong"
+        if data.cmd == "pong"
             @.processHeartBeatPongMessage(data)
         else
             @.processMessage(data)


fix: correct columns for Ryann

fix: correct columns for Ryann

--- app/controllers/all_items.coffee
+++ app/controllers/all_items.coffee
@@ -36,19 +36,23 @@
   csvData: (->
     json = { }
     json.headers = [
+      'Number'
+      'Type'
       'Size'
-      'Link'
+      'Status'
       'Title'
       'Product'
-      'Status'
+      'Link'
     ]
     json.rows = @get('filteredContent').map (item) ->
       {
+        Link: item.get('sprintlyUrl')
+        Number: item.get('number')
+        Product: item.get('product.displayName')
         Size: item.get('score')
-        Link: item.get('sprintlyUrl')
+        Status: item.get('status')
         Title: item.get('title')
-        Product: item.get('product.displayName')
-        Status: item.get('status')
+        Type: item.get('type')
       }
     "data:text/csv;charset=utf-8,#{escape(csvjson.json2csv(json))}"
   ).property('filteredContent.@each')


Fix bug

Fix bug

--- app/services/ClickHandler.coffee
+++ app/services/ClickHandler.coffee
@@ -36,6 +36,7 @@
       e.preventDefault()
     body.mouseup (e) =>
       e.preventDefault()
+      @unselectAll() unless @isMobile
       @mousedown = false

   onSelect: (cell) ->


account: payment modal validation fixes

account: payment modal validation fixes

--- client/landing/app/Applications/Account.kdapplication/account/accSettingsPaymentMethods.coffee
+++ client/landing/app/Applications/Account.kdapplication/account/accSettingsPaymentMethods.coffee
@@ -81,8 +81,13 @@
       for key, val of ERRORS
         if e.field.indexOf(key) > -1
           val.input.giveValidationFeedback yes
-          field = val.field.subViews[0].labelTitle
-          val.input.showValidationError "#{field} #{e.message}"
+          inputName = if val.input.inputLabel
+            val.input.inputLabel.getTitle()
+          else
+            inputEl = val.input.$()[0]
+            inputEl.getAttribute("placeholder") or inputEl.getAttribute("name") or ""
+
+          val.input.showValidationError "#{inputName} #{e.message}"

 class AccountPaymentMethodsListController extends KDListViewController
   constructor:(options,data)->
@@ -134,64 +139,77 @@
 createAccountPaymentMethodModal = (data, callback) ->
   modal = new KDModalViewWithForms
     title                       : "Billing Information"
-    content                     : ''
-    helpContent                 :
-      """
-      """
+    width                       : 500
+    cssClass                    : "payments-modal"
     overlay                     : yes
-    width                       : 500
-    height                      : "auto"
-    cssClass                    : "payments-modal"
     tabs                        :
       navigable                 : yes
       goToNextFormOnSubmit      : no
       forms                     :
         "Billing Info"          :
+          callback              : (formData)=>
+            form      = modal.modalTabs.forms["Billing Info"]
+            button    = form.buttons.Save
+            onError   = (err)->
+              warn err
+              showAccountPaymentErrors form.fields, form.inputs, err
+              button.hideLoader()
+            onSuccess = modal.destroy.bind modal
+            callback formData, onError, onSuccess
           buttons               :
             Save                :
               title             : "Save"
               style             : "modal-clean-green"
               type              : "submit"
               loader            :
-                color           : "#444444"
+                color           : "#ffffff"
                 diameter        : 12
-              callback          : =>
-                tabs = modal.modalTabs
-                form = tabs.forms["Billing Info"]
-                button = form.buttons["Save"]
-                onError = ->
-                    button.hideLoader()
-                onSuccess = ->
-                    modal.destroy()
-                callback form.getFormData(), onError, onSuccess
           fields                :
             cardFirstName       :
               label             : "Name"
-              itemClass         : KDInputView
               name              : "cardFirstName"
               placeholder       : "First Name"
+              validate          :
+                # event           : "blur"
+                rules           :
+                  required      : yes
+                messages        :
+                  required      : "First name is required!"
               nextElementFlat   :
                 cardLastName    :
-                  itemClass     : KDInputView
                   name          : "cardLastName"
                   placeholder   : "Last Name"
+                  validate      :
+                    # event       : "blur"
+                    rules       :
+                      required  : yes
+                    messages    :
+                      required  : "Last name is required!"
             cardNumber          :
               label             : "Card Number"
-              itemClass         : KDInputView
               name              : "cardNumber"
               placeholder       : 'Card Number'
-              # validate          :
-              #   event           : "blur"
-              #   rules           : "creditCard"
+              validate          :
+                event           : "blur"
+                rules           :
+                  creditCard    : yes
               nextElementFlat   :
                 cardCV          :
-                  tooltip       :
-                    placement   : 'right'
-                    direction   : 'center'
-                    title       : 'The location of this verification number depends on the issuer of your credit card'
-                  itemClass     : KDInputView
+                  # tooltip       :
+                  #   placement   : 'right'
+                  #   direction   : 'center'
+                  #   title       : 'The location of this verification number depends on the issuer of your credit card'
                   name          : "cardCV"
                   placeholder   : "CV Number"
+                  validate      :
+                    rules       :
+                      required  : yes
+                      minLength : 3
+                      regExp    : /[0-9]/
+                    messages    :
+                      required  : "Card security code is required! (CVV)"
+                      minLength : "Card security code needs to be at least 3 digits!"
+                      regExp    : "Card security code should be a number!"
             cardMonth           :
               label             : "Expire Date"
               itemClass         : KDSelectBox
@@ -207,36 +225,60 @@
                   defaultValue  : (new Date().getFullYear())
             address1            :
               label             : "Address"
-              itemClass         : KDInputView
               name              : "address1"
               placeholder       : "Street Name & Number"
+              validate          :
+                rules           :
+                  required      : yes
+                messages        :
+                  required      : "First address field is required!"
             address2            :
               label             : " "
-              itemClass         : KDInputView
               name              : "address2"
               placeholder       : "Apartment/Suite Number"
             city                :
               label             : "City & State"
-              itemClass         : KDInputView
               name              : "city"
               placeholder       : "City Name"
+              validate          :
+                rules           :
+                  required      : yes
+                messages        :
+                  required      : "City is required!"
               nextElementFlat   :
                 state           :
-                  itemClass     : KDInputView
                   name          : "state"
                   placeholder   : "State"
+                  validate      :
+                    rules       :
+                      required  : yes
+                    messages    :
+                      required  : "State is required!"
             zip                 :
               label             : "ZIP & Country"
-              itemClass         : KDInputView
               name              : "zipCode"
               placeholder       : "ZIP Code"
+              validate          :
+                rules           :
+                  required      : yes
+                messages        :
+                  required      : "Zip code is required!"
               nextElementFlat   :
                 country         :
-                  itemClass     : KDInputView
                   name          : "country"
                   placeholder   : "Country"
+                  defaultValue  : "United States of America"
+                  validate      :
+                    rules       :
+                      required  : yes
+                    messages    :
+                      required  : "First address field is required!"

   form = modal.modalTabs.forms["Billing Info"]
+
+  form.on "FormValidationFailed", =>
+    form.buttons.Save.hideLoader()
+
   for k, v of data
     if form.inputs[k]
       form.inputs[k].setValue v
@@ -258,11 +300,10 @@
       modal = createAccountPaymentMethodModal data, (newData, onError, onSuccess) ->
         KD.remote.api.JRecurlyPlan.setUserAccount newData, (err, result)->
           if err
-            onError()
+            onError err
           else
-
             controller.emit 'reload'
-            onSuccess()
+            onSuccess result

       form = modal.modalTabs.forms["Billing Info"]


No need this checks then

No need this checks then

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemFollow.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemFollow.coffee
@@ -11,18 +11,15 @@
     super options,data

     @action = "followed"
-    if data.anchor?.constructorName is "JApp"
-      @anchor = new ProfileLinkView
-        origin: data.group[0]
-      @group = new AppLinkView origin: data.anchor
+    if data.group[0]?.constructorName is "JApp"
       @action = "installed"
-    else
-      @anchor = new ProfileLinkView
-        origin: data.anchor
-      @group = new LinkGroup
-        group         : data.group
-        itemsToShow   : 3
-        subItemClass  : options.subItemLinkClass
+
+    @anchor = new ProfileLinkView
+      origin: data.anchor
+    @group = new LinkGroup
+      group         : data.group
+      itemsToShow   : 3
+      subItemClass  : options.subItemLinkClass

     #@getData().on 'ItemWasAdded', -> log 'heres the event, sinan', arguments


Add ability service dep to comment item controller

--- lineman/app/components/thread_page/activity_card/new_comment_item_controller.coffee
+++ lineman/app/components/thread_page/activity_card/new_comment_item_controller.coffee
@@ -1,4 +1,4 @@
-angular.module('loomioApp').controller 'NewCommentItemController', ($scope, $rootScope, $translate, Records, CurrentUser, ModalService, EditCommentForm, DeleteCommentForm) ->
+angular.module('loomioApp').controller 'NewCommentItemController', ($scope, $rootScope, $translate, Records, CurrentUser, ModalService, EditCommentForm, DeleteCommentForm, AbilityService) ->
   renderLikedBySentence = ->
     otherIds = _.without($scope.comment.likerIds, CurrentUser.id)
     otherUsers = _.filter $scope.comment.likers(), (user) -> _.contains(otherIds, user.id)


Emptied Studio’s app.coffee

Emptied Studio’s app.coffee

--- extras/Studio.framer/app.coffee
+++ extras/Studio.framer/app.coffee
@@ -1,18 +0,0 @@
-# Create layers
-layerA = new Layer
-	size: Screen.size
-	backgroundColor: "#00AAFF"
-
-layerB = new Layer
-	size: Screen.size
-	backgroundColor: "#FFCC33"
-
-# Create FlowComponent and show layer
-flow = new FlowComponent
-flow.showNext(layerA)
-
-# Instantly show layerB on click
-layerA.onClick ->
-	flow.showNext(layerB)
-layerB.onClick ->
-	flow.showPrevious(layerA)


New data, make sure long words break to next line

--- extras/Studio.framer/app.coffee
+++ extras/Studio.framer/app.coffee
@@ -1,9 +1,9 @@
 # Info Fixture
 Framer.Metadata =
-	author: "Jorn van Dijk"
-	twitter: "jornvandijk"
-	title: "Android Tabs"
-	description: "Here's a new Framer example. It's a little grid of photos, which you can scroll, click and pinch to zoom. Made to highlight some of our latest features: scroll and click separation, pinchable layers, event shortcuts and more."
+	author: "Eelco Lempsink"
+	twitter: "eelco"
+	title: "MIDI Controller"
+	description: "This is pretty niche but actually super cool. Connect a MIDI Controller to your Framer designs and play with it on the fly. http://blog.framerjs.com/posts/midi.html"
 # 	local: true
 	date: "Jun 14 2016"

@@ -172,7 +172,7 @@
 				size: 40
 				parent: @info
 				borderRadius: 100
-				image: "http://img.tweetimag.es/i/#{@shareInfo.twitter}_n"
+				image: "http://img.tweetimag.es/i/#{@shareInfo.twitter}_o"

 			avatarBorder = new ShareLayer
 				width: @avatar.width - 2
@@ -213,6 +213,7 @@
 			html: @shareInfo.description
 			style:
 				lineHeight: "1.5"
+				wordWrap: "break-word"

 		descriptionSize = Utils.textSize(
 			@shareInfo.description,


Rethrow error if the error coming from router is not "Route missing"

--- assets/scripts/app/routes.coffee
+++ assets/scripts/app/routes.coffee
@@ -43,7 +43,10 @@
     try
       @_super(url)
     catch error
-      @_super('/not-found') if error.message.match(/No route matched the URL/)
+      if error.message.match(/No route matched the URL/)
+        @_super('/not-found')
+      else
+        throw(error)

 # TODO: don't reopen Ember.Route to add events, there should be
 #       a better way (like "parent" resource for everything inside map)


fixed createButton

fixed createButton

--- client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -40,7 +40,8 @@
               loader                  :
                 color                 : "#ffffff"
                 diameter              : 24
-
+              callback                : =>
+                @registerDomain()
             checkButton              :
               title                   : "Check Domain"
               style                   : "cupid-green hidden"
@@ -156,7 +157,7 @@
     splittedDomain    = domainName.match(/([\w\-]+)\.(.*)/)
     domain            = splittedDomain[1]
     tld               = splittedDomain[2]
-
+
     {createButton, checkButton, registerButton} = form.buttons
     @clearSuggestions()
     KD.remote.api.JDomain.getTldPrice tld, (tldPrice) =>


updating to use window.addEventListener instead of jquery, feels weird

--- src/components/confirm-leave-mixin.cjsx
+++ src/components/confirm-leave-mixin.cjsx
@@ -4,16 +4,18 @@
   # getFlux: -> {store, actions}
   # getId: -> id
   componentDidMount: ->
-    $(window).on("beforeunload", @confirmLeave)
+    window.addEventListener 'beforeunload', @confirmLeave
   componentWillUnmount: ->
-    $(window).off("beforeunload", @confirmLeave)
+    window.removeEventListener "beforeunload", @confirmLeave

     {store, actions} = @getFlux()
     # TODO add isValid() to the CrudConfig so we can save
     if store.isChanged(@getId())
       alert 'Your changes will be lost because you did not save.  Come back soon!'

-  confirmLeave: ->
+  confirmLeave: (e) ->
     {store} = @getFlux()
     if store.isChanged(@getId())
-      return "You have unsaved changes.  Are you sure you want to leave?"
+      msg = "You have unsaved changes.  Are you sure you want to leave?"
+      (e or window.event).returnValue = msg     # Gecko and Trident
+      msg                                        # Gecko and WebKit


allow cli user to be specified with MJ_USER environment variable

--- app/cli/index.coffee
+++ app/cli/index.coffee
@@ -16,9 +16,10 @@
   command.action ->
     realArguments = arguments
     options = _.last arguments
-    if options.user
-      command.password "password: ", (password) ->
-        signInOp {email: options.user, password}, (error, user) ->
+    email = options.user || process.env.MJ_USER
+    if email
+      command.password "password for #{email}: ", (password) ->
+        signInOp {email, password}, (error, user) ->
           exit error if error
           options.user = user
           realAction.apply this, realArguments


Correct a longstanding typo in DOM tests

--- tests/dom_tests/dom_tests.coffee
+++ tests/dom_tests/dom_tests.coffee
@@ -76,7 +76,7 @@
       assertStartPosition = (element1, element2) ->
         assert.equal element1.getClientRects()[0].left, element2.getClientRects()[0].left
         assert.equal element1.getClientRects()[0].top, element2.getClientRects()[0].top
-      stub document.body, "style", "static"
+      stub document.body.style, "position", "static"
       linkHints = activateLinkHintsMode()
       hintMarkers = getHintMarkers()
       assertStartPosition document.getElementsByTagName("a")[0], hintMarkers[0]


update fr.coffee v.04 (#4975)

update fr.coffee v.04 (#4975)

--- app/locale/fr.coffee
+++ app/locale/fr.coffee
@@ -1376,52 +1376,52 @@
 #    student_age_range_to: "to"
     create_class: "Créer une Classe"
     class_name: "Nom de la classe"
-#    teacher_account_restricted: "Your account is a teacher account and cannot access student content."
-#    account_restricted: "A student account is required to access this page."
-#    update_account_login_title: "Log in to update your account"
+    teacher_account_restricted: "Votre compte est un compte d'enseignant et ne peut pas accéder au contenu des élèves."
+    account_restricted: "Un compte étudiant est requis pour accéder à cette page."
+    update_account_login_title: "Connectez-vous pour mettre à jour votre compte"
     update_account_title: "Votre compte a besoin d'attention !"
-#    update_account_blurb: "Before you can access your classes, choose how you want to use this account."
+    update_account_blurb: "Avant de pouvoir accéder à vos cours, choisissez comment vous voulez utiliser ce compte."
     update_account_current_type: "Type de compte courant :"
     update_account_account_email: "Email/nom d'utilisateur du compte :"
     update_account_am_teacher: "Je suis un professeur"
-#    update_account_keep_access: "Keep access to classes I've created"
+    update_account_keep_access: "Garder l'accès aux classes que j'ai créées"
     update_account_teachers_can: "Les comptes professeurs peuvent :"
     update_account_teachers_can1: "Créer/manager/ajouter des classes"
 #    update_account_teachers_can2: "Assign/enroll students in courses"
-#    update_account_teachers_can3: "Unlock all course levels to try out"
-#    update_account_teachers_can4: "Access new teacher-only features as we release them"
-#    update_account_teachers_warning: "Warning: You will be removed from all classes that you have previously joined and will not be able to play as a student."
+    update_account_teachers_can3: "Déverrouillez tous les niveaux de cours pour essayer"
+    update_account_teachers_can4: "Accédez aux nouvelles fonctionnalités réservées aux enseignants au fur et à mesure que nous les publions"
+    update_account_teachers_warning: "Avertissement : Vous serez retiré de toutes les classes auxquelles vous avez déjà participé et vous ne pourrez pas jouer en tant qu'étudiant."
 #    update_account_remain_teacher: "Remain a Teacher"
 #    update_account_update_teacher: "Update to Teacher"
     update_account_am_student: "Je suis un étudiant"
-#    update_account_remove_access: "Remove access to classes I have created"
+    update_account_remove_access: "Supprimer l'accès aux classes que j'ai créées"
     update_account_students_can: "Les comptes étudiant peuvent :"
     update_account_students_can1: "Rejoindre les cours"
-#    update_account_students_can2: "Play through courses as a student and track your own progress"
-#    update_account_students_can3: "Compete against classmates in arenas"
-#    update_account_students_can4: "Access new student-only features as we release them"
-#    update_account_students_warning: "Warning: You will not be able to manage any classes that you have previously created or create new classes."
-#    unsubscribe_warning: "Warning: You will be unsubscribed from your monthly subscription."
+    update_account_students_can2: "Jouez à travers les cours en tant qu'étudiant et suivez vos propres progrès."
+    update_account_students_can3: "Affrontez vos camarades de classe dans les arénas."
+    update_account_students_can4: "Accédez aux nouvelles fonctionnalités réservées aux étudiants au fur et à mesure que nous les publions"
+    update_account_students_warning: "Avertissement : Vous ne pourrez pas gérer les classes que vous avez déjà créées ou en créer de nouvelles."
+    unsubscribe_warning: "Avertissement : Vous serez désabonné de votre abonnement mensuel."
 #    update_account_remain_student: "Remain a Student"
 #    update_account_update_student: "Update to Student"
-#    need_a_class_code: "You'll need a Class Code for the class you're joining:"
-#    update_account_not_sure: "Not sure which one to choose? Email"
-#    update_account_confirm_update_student: "Are you sure you want to update your account to a Student experience?"
-#    update_account_confirm_update_student2: "You will not be able to manage any classes that you have previously created or create new classes. Your previously created classes will be removed from CodeCombat and cannot be restored."
-#    instructor: "Instructor: "
-#    youve_been_invited_1: "You've been invited to join "
-#    youve_been_invited_2: ", where you'll learn "
-#    youve_been_invited_3: " with your classmates in CodeCombat."
-#    by_joining_1: "By joining "
-#    by_joining_2: "will be able to help reset your password if you forget or lose it. You can also verify your email address so that you can reset the password yourself!"
-#    sent_verification: "We've sent a verification email to:"
+    need_a_class_code: "Vous aurez besoin d'un code de classe pour la classe à laquelle vous vous inscrivez :"
+    update_account_not_sure: "Vous ne savez pas lequel choisir ? Email"
+    update_account_confirm_update_student: "Êtes-vous sûr de vouloir mettre à jour votre compte pour une expérience Etudiant ?"
+    update_account_confirm_update_student2: "Vous ne pourrez pas gérer les classes que vous avez déjà créées ou en créer de nouvelles. Vos classes précédemment créées seront supprimées de CodeCombat et ne pourront pas être restaurées."
+    instructor: "Instructeur : "
+    youve_been_invited_1: "Vous avez été invité à rejoindre "
+    youve_been_invited_2: ", où vous apprendrez "
+    youve_been_invited_3: " avec tes camarades de classe dans CodeCombat."
+    by_joining_1: "En rejoignant "
+    by_joining_2: "pourra vous aider à réinitialiser votre mot de passe si vous l'oubliez ou le perdez. Vous pouvez également vérifier votre adresse e-mail afin de pouvoir réinitialiser le mot de passe vous-même !"
+    sent_verification: "Nous avons envoyé un e-mail de vérification à :"
     you_can_edit: "Vous pouvez modifier votre adresse email dans "
     account_settings: "Paramètres du compte"
     select_your_hero: "Sélectionnez votre héros"
-#    select_your_hero_description: "You can always change your hero by going to your Courses page and clicking \"Change Hero\""
+    select_your_hero_description: "Tu peux toujours changer ton héros en allant sur la page des cours et en cliquant sur \"Change Hero\""
     select_this_hero: "Sélectionnez ce héros"
-#    current_hero: "Current Hero:"
-#    current_hero_female: "Current Hero:"
+    current_hero: "Héros actuel :"
+    current_hero_female: "Héros actuel :"
 #    web_dev_language_transition: "All classes program in HTML / JavaScript for this course.  Classes that have been using Python will start with extra JavaScript intro levels to ease the transition.  Classes that are already using JavaScript will skip the intro levels."
 #    course_membership_required_to_play: "You'll need to join a course to play this level."
 #    license_required_to_play: "Ask your teacher to assign a license to you so you can continue to play CodeCombat!"


Patched up tonnes of minor language mistakes (Dutch-Nederlands)

--- app/locale/nl.coffee
+++ app/locale/nl.coffee
@@ -219,7 +219,7 @@
     thang_title: "Thang Editor"
     thang_description: "Maak eenheden, beschrijf hun default logica, graphics en audio. Momenteel is enkel het importeren van vector graphics geëxporteerd in Flash ondersteund."
     level_title: "Level Editor"
-    level_description: "Bevat het gereedschap om te programmeren, audio te uploaden en aangepaste logica om alle soorten levels te maken. Het is alles wat wijzelf ook gebruiken!"
+    level_description: "Bevat het programma om te programmeren, audio te uploaden en aangepaste logica om alle soorten levels te maken. Het is alles wat wijzelf ook gebruiken!"
     security_notice: "Veel belangrijke elementen in deze editors zijn momenteel niet actief. Met dat wij de veiligheid van deze systemen verbeteren, zullen ook deze elementen beschikbaar worden. Indien u deze elementen al eerder wil gebruiken, "
     contact_us: "contacteer ons!"
     hipchat_prefix: "Je kan ons ook vinden in ons"
@@ -267,16 +267,16 @@
   about:
     who_is_codecombat: "Wie is CodeCombat?"
     why_codecombat: "Waarom CodeCombat?"
-    who_description_prefix: "hebben samen CodeCombat opgericht in 2013. We creerden ook "
+    who_description_prefix: "hebben samen CodeCombat opgericht in 2013. We creëerden ook "
     who_description_suffix: "en in 2008, groeide het uit tot de #1 web en iOS applicatie om Chinese en Japanse karakters te leren schrijven."
     who_description_ending: "Nu is het tijd om mensen te leren programmeren."
-    why_paragraph_1: "Tijdens het maken van Skritter wist George niet hoe hij moest programmeren. Hij constant gefrustreerd doordat hij zijn ideeën niet kon verwezelijken. Nadien probeerde hij te studeren, maar te lessen gingen te traag. Ook zijn huisgenoot wou opnieuw studeren en stopte met lesgeven. Hij probeerde Codecademy maar was al snel \"verveeld\". Iedere week starte een andere vriend met Codecademy, met telkens als resultaat dat hij/zij vrij snel met de lessen stopte. We realiseerde ons dat het hetzelfde probleem was zoals we al eerder hadden opgelost met Skritter: mensen leren iets via langzame en intensieve lessen, terwijl ze het eigenlijk zo snel mogelijk nodig hebben via uitgebreide oefeningen. Wij weten hoe dat op te lossen."
+    why_paragraph_1: "Tijdens het maken van Skritter wist George niet hoe hij moest programmeren en was hij constant gefrustreerd doordat hij zijn ideeën niet kon verwezelijken. Nadien probeerde hij te studeren maar de lessen gingen te traag. Ook zijn huisgenoot wou opnieuw studeren en stopte met lesgeven. Hij probeerde Codecademy maar was al snel \"verveeld\". Iedere week startte een andere vriend met Codecademy, met telkens als resultaat dat hij/zij vrij snel met de lessen stopte. We realiseerden ons dat het hetzelfde probleem was zoals we al eerder hadden opgelost met Skritter: mensen leren iets via langzame en intensieve lessen, terwijl ze het eigenlijk zo snel mogelijk nodig hebben via uitgebreide oefeningen. Wij weten hoe dat op te lossen."
     why_paragraph_2: "Wil je leren programmeren? Je hebt geen lessen nodig. Je moet vooral veel code schrijven en je amuseren terwijl je dit doet."
     why_paragraph_3_prefix: "Dat is waar programmeren om draait. Het moet tof zijn. Niet tof zoals"
     why_paragraph_3_italic: "joepie een medaille"
     why_paragraph_3_center: "maar tof zoals"
     why_paragraph_3_italic_caps: "NEE MAMA IK MOET DIT LEVEL AF MAKEN!"
-    why_paragraph_3_suffix: "Dat is waarom CodeCombat een multiplayergame is, en niet zomaar lessen gegoten in spelformaat. We zullen niet stoppen totdat jij niet meer kan stoppen--maar deze keer, is dat een goed."
+    why_paragraph_3_suffix: "Dat is waarom CodeCombat een multiplayergame is, en niet zomaar lessen gegoten in spelformaat. We zullen niet stoppen totdat jij niet meer kan stoppen--maar deze keer, is dat iets goeds."
     why_paragraph_4: "Als je verslaafd gaat zijn aan een spel, dan is het beter om hieraan verslaafd te raken en een tovenaar van het technisch tijdperk te worden."
     why_ending: "En hallo, het is gratis."
     why_ending_url: "Start nu met toveren!"
@@ -297,15 +297,15 @@
     practices_title: "Goede Respectvolle gewoonten"
     practices_description: "Dit zijn onze beloften aan u, de speler, en iets minder juridische jargon."
     privacy_title: "Privacy"
-    privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen geld verdienen dankzij aanwerving in verloop van tijd, maar je mag op je twee oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar explicit met akkoord gaat."
+    privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen geld verdienen dankzij aanwerving in verloop van tijd, maar je mag op je twee oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar expliciet mee akkoord gaat."
     security_title: "Beveiliging"
     security_description: "We streven ernaar om jouw persoonlijke informatie veilig te bewaren. Onze website is open en beschikbaar voor iedereen, opdat ons beveiliging systeem kan worden nagekeken en geoptimaliseerd door iedereen die dat wil. Dit alles is mogelijk doordat we volledig open source en transparant zijn."
     email_title: "E-mail"
     email_description_prefix: "We zullen je niet overspoelen met spam. Door"
     email_settings_url: "jouw e-mail instellingen"
-    email_description_suffix: "of via urls in de emails die wij verzanden, kan je jouw instellingen wijzigen en te alle tijden uitschrijven."
+    email_description_suffix: "of via urls in de emails die wij verzenden, kan je jouw instellingen wijzigen en ten allen tijden uitschrijven."
     cost_title: "Kosten"
-    cost_description: "Momenteel, CodeCombat is 100% gratis! Één van onze doestellingen is om dit zo te houden, opdat zoveel mogelijk mensen kunnen spelen, onafhankelijk van waar je leeft of wie je bent. Als het financieel moeilijker wordt, kan het mogelijk zijn dat we gaan beginnen met abonnementen of een prijs zetten op bepaalde zaken, maar we streven ernaar om dit te verkomen. Met een beetje geluk zullen we dit voor altijd kunnen garanderen met:"
+    cost_description: "Momenteel is CodeCombat 100% gratis! Één van onze doestellingen is om dit zo te houden, opdat zoveel mogelijk mensen kunnen spelen, onafhankelijk van waar je leeft of wie je bent. Als het financieel moeilijker wordt, kan het mogelijk zijn dat we gaan beginnen met abonnementen of een prijs zetten op bepaalde zaken, maar we streven ernaar om dit te voorkomen. Met een beetje geluk zullen we dit voor altijd kunnen garanderen met:"
     recruitment_title: "Aanwervingen"
     recruitment_description_prefix: "Hier bij CodeCombat, ga je ontplooien tot een krachtige tovenoor-niet enkel virtueel, maar ook in het echt."
     url_hire_programmers: "Niemand kan snel genoeg programmeurs aanwerven"
@@ -320,20 +320,20 @@
     code_title: "Code - MIT"
     code_description_prefix: "Alle code in het bezit van CodeCombat of aanwezig op codecombat.com, zowel in de GitHub respository of in de codecombat.com database, is erkend onder de"
     mit_license_url: "MIT licentie"
-    code_description_suffix: "Dit geld zowel voor code in Systemen en Componenten dat publiekelijk is gemaakt met als doelstellingen het maken van levels."
+    code_description_suffix: "Dit geldt ook voor code in Systemen en Componenten dat publiekelijk is gemaakt met als doelstellingen het maken van levels."
     art_title: "Art/Music - Creative Commons "
     art_description_prefix: "Alle gemeenschappelijke inhoud valt onder de"
     cc_license_url: "Creative Commons Attribution 4.0 Internationale Licentie"
-    art_description_suffix: "Gemeenschappelijke inhoud is alles dat algemeen verkrijgen is bij CodeCombat voor het doel levels te maken. Dit omvat:"
+    art_description_suffix: "Gemeenschappelijke inhoud is alles dat algemeen verkrijgbaar is bij CodeCombat voor het doel levels te maken. Dit omvat:"
     art_music: "Muziek"
     art_sound: "Geluid"
     art_artwork: "Artwork"
     art_sprites: "Sprites"
     art_other: "Eender wat en al het creatief werk dat niet als code aanzien wordt en verkrijgbaar is bij het aanmaken van levels."
-    art_access: "Momenteel is er geen universeel en gebruiksvriendelijk voor het ophalen van deze assets. In het algemeen, worden deze opgehaald via de links zoals gebruikt door de website. Contacteer ons voor assitentie, of help ons met de website uit te breiden en de assets bereikbaarder maken."
+    art_access: "Momenteel is er geen universeel en gebruiksvriendelijk systeem voor het ophalen van deze assets. In het algemeen, worden deze opgehaald via de links zoals gebruikt door de website. Contacteer ons voor assitentie, of help ons met de website uit te breiden en de assets bereikbaarder te maken."
     art_paragraph_1: "Voor toekenning, gelieve de naam en link naar codecombat.com te plaatsen waar dit passend is voor de vorm waarin het voorkomt. Bijvoorbeeld:"
     use_list_1: "Wanneer gebruikt in een film of een ander spel, voeg codecombat.com toe in de credits."
-    use_list_2: "Wanneer toegepast op een website, inclusief een link naar het gebruik, bijvorbeeld onderaan een afbeelding. Of in een algemene webpagina waar je eventueel ook andere Create Commons werken en open source software vernoemd die je gebruikt op de website. Iets dat alreeds duidelijk is gespecificeerd met CodeCombat, zoals een blog artikel, dat CodeCombat vernoemt, heeft geen aparte vermelding nodig."
+    use_list_2: "Wanneer toegepast op een website, inclusief een link naar het gebruik, bijvoorbeeld onderaan een afbeelding. Of in een algemene webpagina waar je eventueel ook andere Create Commons werken en open source software vernoemd die je gebruikt op de website. Iets dat alreeds duidelijk is gespecificeerd met CodeCombat, zoals een blog artikel, dat CodeCombat vernoemt, heeft geen aparte vermelding nodig."
     art_paragraph_2: "Wanneer de gebruikte inhoud is gemaakt door een gebruiker van codecombat.com, vernoem hem/haar in plaats van ons. en volg verspreidingsaanwijzingen van die brons als die er zijn."
     rights_title: "Rechten Voorbehouden"
     rights_desc: "Alle rechten zijn voorbehouden voor de Levels. Dit omvat:"


Update ru.coffee

Update ru.coffee

--- app/locale/ru.coffee
+++ app/locale/ru.coffee
@@ -66,11 +66,11 @@
     no_ie: "CodeCombat не работает в IE8 или более старых версиях. Нам очень жаль!"
     no_mobile: "CodeCombat не приспособлен для работы на мобильных устройствах и может не работать!"
     play: "Играть"
-    old_browser: "Ой, Ваш браузер слишком старый для игры CodeCombat. Извините!"
-    old_browser_suffix: "Вы все равно можете попробовать, но скорее всего это не будет работать"
-#    campaign: "Campaign"
+    old_browser: "Ой, ваш браузер слишком стар для запуска CodeCombat. Извините!"
+    old_browser_suffix: "Вы всё равно можете попробовать, но, скорее всего, это не будет работать."
+    campaign: "Кампания"
     for_beginners: "Новичкам"
-    multiplayer: "Командная игра"
+    multiplayer: "Мультиплеер"
     for_developers: "Разработчикам"

   play:
@@ -214,17 +214,17 @@
     hud_continue: "Продолжить (Shift+Пробел)"
     spell_saved: "Заклинание сохранено"
     skip_tutorial: "Пропуск (Esc)"
-#    editor_config: "Editor Config"
-#    editor_config_title: "Editor Configuration"
-#    editor_config_keybindings_label: "Key Bindings"
-#    editor_config_keybindings_default: "Default (Ace)"
-#    editor_config_keybindings_description: "Adds additional shortcuts known from the common editors."
-#    editor_config_invisibles_label: "Show Invisibles"
-#    editor_config_invisibles_description: "Displays invisibles such as spaces or tabs."
-#    editor_config_indentguides_label: "Show Indent Guides"
-#    editor_config_indentguides_description: "Displays vertical lines to see indentation better."
-#    editor_config_behaviors_label: "Smart Behaviors"
-#    editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes."
+    editor_config: "Настройки редактора"
+    editor_config_title: "Настройки редактора"
+    editor_config_keybindings_label: "Сочетания клавиш"
+    editor_config_keybindings_default: "По умолчанию (Ace)"
+    editor_config_keybindings_description: "Добавляет дополнительные сочетания, известные из популярных редакторов."
+    editor_config_invisibles_label: "Показывать непечатные символы"
+    editor_config_invisibles_description: "Отображение непечатных символов, таких как пробелы или табуляции."
+    editor_config_indentguides_label: "Показывать направляющие отступов"
+    editor_config_indentguides_description: "Отображение вертикальных линий для лучшего обзора отступов."
+    editor_config_behaviors_label: "Умное поведение"
+    editor_config_behaviors_description: "Автозавершать квадратные, фигурные скобки и кавычки."

   admin:
     av_title: "Админ панель"
@@ -539,16 +539,16 @@
     warmup: "Разминка"
     vs: "против"

-#  multiplayer_launch:
-#    introducing_dungeon_arena: "Introducing Dungeon Arena"
-#    new_way: "March 17, 2014: The new way to compete with code."
-#    to_battle: "To Battle, Developers!"
-#    modern_day_sorcerer: "You know how to code? That's badass. You're a modern-day sorcerer! Isn't about time that you used your magic coding powers to command your minions in epic combat? And we're not talking robots here."
-#    arenas_are_here: "CodeCombat head-to-head multiplayer arenas are here."
-#    ladder_explanation: "Choose your heroes, enchant your human or ogre armies, and climb your way over defeated fellow Wizards to reach the top of the ladders–then challenge your friends in our glorious, asynchronous multiplayer coding arenas. If you're feeling creative, you can even"
-#    fork_our_arenas: "fork our arenas"
-#    create_worlds: "and create your own worlds."
-#    javascript_rusty: "JavaScript a bit rusty? Don't worry; there's a"
-#    tutorial: "tutorial"
-#    new_to_programming: ". New to programming? Hit our beginner campaign to skill up."
-#    so_ready: "I Am So Ready for This"
+  multiplayer_launch:
+    introducing_dungeon_arena: "Представляем Арену подземелья"
+    new_way: "17 марта 2014: Новый способ соревноваться с помощью кода."
+    to_battle: "В бой, разработчики!"
+    modern_day_sorcerer: "Вы знаете, как программировать? Это круто. Вы волшебник наших дней! Разве не время, чтобы вы использовали свои магические силы программирования для управления миньонами в эпичной битве? И мы не говорим здесь роботы."
+    arenas_are_here: "Мультиплеерные арены CodeCombat на равных уже здесь."
+    ladder_explanation: "Выбирайте своих героев, зачаровывайте свои армии людей или огров, и взберитесь через поверженных коллег-Волшебников на вершину ладдеров–затем бросьте вызов своим друзьям в наших славных, асинхронно-мультиплеерных аренах прогрммирования. Если вы ощущаете себя творческим, можете даже"
+    fork_our_arenas: "сделать форк наших арен"
+    create_worlds: "и создавать свои собственные миры."
+    javascript_rusty: "Подзабыли JavaScript? Не беспокойтесь; есть"
+    tutorial: "обучение"
+    new_to_programming: ". Новичок в программировании? Пройдите нашу кампанию для новичков, чтобы повысить навык."
+    so_ready: "Я полностью готов для этого"


Accept arrays of messages to send to client

--- services/real-time/app/coffee/HttpApiController.coffee
+++ services/real-time/app/coffee/HttpApiController.coffee
@@ -4,5 +4,9 @@
 module.exports = HttpApiController =
 	sendMessage: (req, res, next) ->
 		logger.log {message: req.params.message}, "sending message"
-		WebsocketLoadBalancer.emitToRoom req.params.project_id, req.params.message, req.body
+		if Array.isArray(req.body)
+			for payload in req.body
+				WebsocketLoadBalancer.emitToRoom req.params.project_id, req.params.message, payload
+		else
+			WebsocketLoadBalancer.emitToRoom req.params.project_id, req.params.message, req.body
 		res.send 204 # No content


Client: fix grammar mistake

Client: fix grammar mistake

--- client/DevTools/views/welcomepage.coffee
+++ client/DevTools/views/welcomepage.coffee
@@ -24,7 +24,7 @@
     """

Koding DevTools

-
Koding DevTools is an extensible Framework built on top of Koding's own KDFramework that allows you to extend the Koding platform by building apps that other Koding users can install on their VM's. Our users have built installers for software packages like Wordpress, Drupal, Django, etc.

+
Koding DevTools is an extensible framework built on top of Koding's own KDFramework that allows you to extend the Koding platform by building apps that other Koding users can install on their VMs. Our users have built installers for software packages like Wordpress, Drupal, Django, etc.

To learn how to get started with writing your first KD App using DevTools, start at this handy guide that will teach you how to use DevTools via the Koding UI or via CLI (command line interface).


AvatarArea: Disable nominate icon

AvatarArea: Disable nominate icon

--- client/Main/avatararea/avatararea.coffee
+++ client/Main/avatararea/avatararea.coffee
@@ -30,10 +30,6 @@
         title    : 'Your groups'
       delegate   : @groupSwitcherPopup

-    @nominateIcon = new KDCustomHTMLView
-      cssClass   : 'nominateicon'
-      click      : -> new NominateModal
-
     @once 'viewAppended', =>
       mainView = KD.getSingleton 'mainView'
       mainView.addSubView @groupSwitcherPopup
@@ -54,7 +50,6 @@

   pistachio: ->
     """
-    {{> @nominateIcon}}
     {{> @avatar}}

{{> @profileLink}}


flux/teams: modify getter

flux/teams: modify getter

--- client/app/lib/flux/teams/getters.coffee
+++ client/app/lib/flux/teams/getters.coffee
@@ -103,7 +103,7 @@
 adminInvitations = [
   allInvitations
   (allInvitations) ->
-    allInvitations.filter (value) -> value.get('role') is 'admin'
+    allInvitations.filter (value) -> value.get 'canEdit'
 ]

 newInvitations = [


Material: Fix varying alias in WebGL1

Material: Fix varying alias in WebGL1

--- engine/material.coffee
+++ engine/material.coffee
@@ -308,6 +308,8 @@
                     out vec4 glOutColor;
                     #define gl_FragColor glOutColor
                     void main''')
+        else
+            fragment = '#define VARYING varying\n'+fragment
         gl.shaderSource fragment_shader, fragment
         gl.compileShader fragment_shader


fix book metadata to use prefixed attributes

--- scripts/gh-book/opf-file.coffee
+++ scripts/gh-book/opf-file.coffee
@@ -96,17 +96,18 @@

         container = @$xml.find('metadata')
         selector = tagName.replace(':', '\\:')
-        template = "<#{tagName}"
+
+        element = @$xml[0].createElement(tagName)

         _.forIn attributes, (value, key) ->
-          selector += "[#{key}=\"#{value}\"]"
-          template += " #{key}=\"#{value}\""
-
-        @$xml.find(selector).remove()
-        template += "></#{tagName}>"
+          selector += "[#{key.replace(':', '\\:')}=\"#{value}\"]"
+          element.setAttributeNS('http://www.idpf.org/2007/opf', key, value)
+
+        container.find(selector).remove()

         container.append('    ')
-        $(template).text(value).appendTo(container)
+        element.textContent = value
+        container[0].appendChild(element)
         container.append('\n')
         if not skipSave
           setNavModel({})
@@ -117,12 +118,12 @@
         tagHelper(
           'date',
           @get('datePublished'),
-          {'event': 'publication'},
+          {'opf:event': 'publication'},
           true
         )

       @on 'change:language', (model, value, options) =>
-        tagHelper('dc:language', value, {type: "dcterms:RFC4646"})
+        tagHelper('dc:language', value, {'xsi:type': "dcterms:RFC4646"})
       @on 'change:description', (model, value, options) =>
         tagHelper('dc:description', value)
       @on 'change:rights', (model, value, options) =>
@@ -142,18 +143,19 @@

         container = @$xml.find('metadata')
         selector = tagName.replace(':', '\\:')
-        template = "<#{tagName}"
+        template = @$xml[0].createElement(tagName)

         _.forIn attributes, (value, key) ->
-          selector += "[#{key}=\"#{value}\"]"
-          template += " #{key}=\"#{value}\""
+          selector += "[#{key.replace(':', '\\:')}=\"#{value}\"]"
+          template.setAttributeNS('http://www.idpf.org/2007/opf', key, value)

         existing = @$xml.find(selector).remove()
-        template += "></#{tagName}>"

         _.each values, (value) ->
+          element = template.cloneNode()
           container.append('    ')
-          $(template).text(value).appendTo(container)
+          element.textContent = value
+          container[0].appendChild(element)
           container.append('\n')

         setNavModel({})
@@ -163,7 +165,7 @@
         tagGroupHelper(
           'dc:subject',
           value,
-          {type: "http:\\github.com/Connexions/rhaptos.cnxmlutils/rhaptos/cnxmlutils/schema"}
+          {'xsi:type': "http:\\github.com/Connexions/rhaptos.cnxmlutils/rhaptos/cnxmlutils/schema"}
         )
       @on 'change:rightsHolders', (model, value, options) =>
         tagGroupHelper(
@@ -200,7 +202,8 @@

         container = @$xml.find('metadata')

-        template = "<dc:creator id=\"#{type}\"></dc:creator>"
+        template = @$xml[0].createElement('dc:creator')
+
         refinesTemplate = "<meta refines=\"##{type}\" property=\"role\" scheme=\"marc:relators\"></meta>"

         i = 1
@@ -212,10 +215,12 @@
           fileAs = creator.split(/[ ](?=[^ ]+$)/).reverse().join(', ')

           container.append('    ')
-          newCreator = $(template).text(creator)
-            .attr('id', id)
-            .attr('file-as', fileAs)
-            .appendTo(container)
+          newCreator = template.cloneNode()
+          newCreator.setAttribute('id', id)
+          newCreator.setAttributeNS('http://www.idpf.org/2007/opf', 'opf:file-as', fileAs)
+          newCreator.textContent = creator
+          container[0].appendChild(newCreator)
+
           container.append('\n    ')
           $(refinesTemplate)
             .attr('refines', '#' + id)


Fix formatting errors

Fix formatting errors

Many dashes were switched with underscores (and vice-versa). Cleaning them up as I find them.

--- src/scripts/hubotagainsthumanity.coffee
+++ src/scripts/hubotagainsthumanity.coffee
@@ -24,27 +24,27 @@
   "_____. It's a trap!",
   "_____. That's how I want to die.",
   "(Draw 2, Pick 3) _____ + _____ = _____",
-  "(Draw 2, Pick 3) I went from – to _____, all thanks to.",
+  "(Draw 2, Pick 3) I went from ______ to _____, all thanks to.",
   "(Pick 2) _____ would be woefully incomplete without _____.",
-  "(Pick 2) – is a slippery slope that leads to _____.",
-  "(Pick 2) An international tribunal has found – guilty of _____.",
-  "(Pick 2) An the Academy Award for – goes to _____.",
+  "(Pick 2) _____ is a slippery slope that leads to _____.",
+  "(Pick 2) An international tribunal has found _____ guilty of _____.",
+  "(Pick 2) An the Academy Award for _____ goes to _____.",
   "(Pick 2) Before _____, all we had was_____.",
-  "(Pick 2) Dear Sir or Madam, We regret to inform you that the office of – has denied your request for _____.",
-  "(Pick 2) For my next trick, I will pull – out of _____.",
-  "(Pick 2) I never truly understood – until I encountered _____.",
+  "(Pick 2) Dear Sir or Madam, We regret to inform you that the office of _____ has denied your request for _____.",
+  "(Pick 2) For my next trick, I will pull _____ out of _____.",
+  "(Pick 2) I never truly understood _____ until I encountered _____.",
   "(Pick 2) I spent my whole life working toward _____, only to have it ruined by _____.",
   "(Pick 2) If God didn't want us to enjoy _____, he wouldn't have given us _____.",
   "(Pick 2) In a pinch, _____ can be a suitable substitute for _____.",
   "(Pick 2) In a world ravaged by _____, our only solace is _____.",
-  "(Pick 2) In M. Night Shamalan's new movie, Bruce Willis discovers that _____ had really been – all along."
+  "(Pick 2) In M. Night Shamalan's new movie, Bruce Willis discovers that _____ had really been _____ all along."
   "(Pick 2) Lifetime presents _____, the story of _____.",
-  "(Pick 2) Michael Bay's new three_____hour action epic pits – against _____.",
+  "(Pick 2) Michael Bay's new three-hour action epic pits _____ against _____.",
   "(Pick 2) Rumor has it that Vladimir Putin's favorite dish is – stuffed with _____.",
   "(Pick 2) Step 1: _____ Step 2_____ Step 3: Profit.",
   "(Pick 2) That's right I killed _____. How you ask? _____.",
   "(Pick 2) What's the next superhero/sidekick duo?",
-  "(Pick 2) When I was tripping on acid – turned into _____.",
+  "(Pick 2) When I was tripping on acid _____ turned into _____.",
   "(Pick 2) You haven't truly lived until you've experienced – and – at the same time.",
   "A romantic candlelit dinner would be incomplete without _____.",
   "After blacking out during New Year's Eve, I was awoken by _____.",
@@ -60,7 +60,7 @@
   "Coming to Broadway this season, _____: The Musical.",
   "Dear Abby, I'm having some trouble with ______ and would like your advice.",
   "During his midlife crisis, my dad got really into _____.",
-  "During Picasso's often_____overlooked Brown Period, he produced hundreds of paintings of _____.",
+  "During Picasso's often-overlooked Brown Period, he produced hundreds of paintings of _____.",
   "During sex, I like to think about _____.",
   "Every Christmas my uncle gets drunk and tells the story about _____.",
   "Everyone down on the ground! We don't want to hurt anyone. We're just here for _____.",
@@ -73,7 +73,7 @@
   "I'm sorry professor, but I couldn't complete my homework because of _____.",
   "In 1,000 years when paper money is but a distant memory, _____ will be our currency.",
   "In an attempt to reach a wider audience, the Smithsonian Museum of Natural History has opened an interactive exhibit on _____.",
-  "In his new self_____produced album, Kanye West raps over the sounds of _____.",
+  "In his new self-produced album, Kanye West raps over the sounds of _____.",
   "In his newest and most difficult stunt, David Blaine must escape from _____.",
   "In its new tourism campaign, Detroit proclaims that it has finally eliminated _____.",
   "In L.A. county Jail, word is you can trade 200 cigarettes for _____.",
@@ -89,9 +89,9 @@
   "Major League Baseball has banned _____ for giving players an unfair advantage.",
   "Maybe she's born with it. Maybe it's _____.",
   "Members of New York's social elite are paying thousands of dollars just to experience _____.",
-  "MTV's new reality show features eight washed_____up celebrities living with _____.",
+  "MTV's new reality show features eight washed-up celebrities living with _____.",
   "My country, 'tis of thee, sweet land of _____.",
-  "My mom freaked out when she looked at my browser history and found _____.com/_____.",
+  "My mom freaked out when she looked at my browser history and found _____.com.",
   "My new favorite porn star is Joey “_____” McGee.",
   "Next from J.K. Rowling: Harry Potter and the Chamber of _____.",
   "Next on ESPN2: The World Series of _____.",
@@ -106,11 +106,11 @@
   "The healing process began when I joined a support group for victims of _____.",
   "The socialist governments of Scandanavia have declared that access to – is a basic human right.",
   "The votes are in, and the new high school mascot is _____.",
-  "This holiday season, Tim Allen must overcome his fear of – to save Christmas.",
+  "This holiday season, Tim Allen must overcome his fear of _____ to save Christmas.",
   "This is the way the world ends This is the way the world ends Not with a bang but with _____.",
   "This is your captain speaking. Fasten your seatbelts and prepare for _____.",
   "This month's Cosmo: 'Spice up your sex life by bringing ______ into the bedroom.'",
-  "This reason on Man vs. Wild, Bear Grylls must survive in the depths of the Amazon with only _____ and his wits.",
+  "This season on Man vs. Wild, Bear Grylls must survive in the depths of the Amazon with only _____ and his wits.",
   "Tonight on 20/20: What you don't know about _____ could kill you.",
   "TSA guidelines now prohibit _____ on airplanes.",
   "Wake up, America. Christmas is under attack by secular liberals and their _____.",
@@ -149,7 +149,7 @@
   "What's the next Happy Meal toy?",
   "What's there a ton of in heaven?",
   "When all else fails, I can always masturbate to _____.",
-  "When I am a billionaire, I shall erect a 50_____foot statue to commemorate _____.",
+  "When I am a billionaire, I shall erect a 50-foot statue to commemorate _____.",
   "When I am President of the United States, I will create the Department of _____.",
   "When I pooped, what came out of my butt?",
   "When Pharaoh remained unmoved, Moses called down a Plague of _____.",
@@ -260,7 +260,7 @@
   "Anal beads.",
   "Another goddamn vampire movie.",
   "Another shitty year.",
-  "Another shop of morphine.",
+  "Another shot of morphine.",
   "Apologizing.",
   "Appreciative snapping.",
   "Arnold Schwarzenegger.",
@@ -794,9 +794,9 @@
 ]

 module.exports = (robot) ->
-  robot.respond /card(?: me)?(?: )(\d+)?/i, (msg) ->
+  robot.hear /card(?: me)?(?: )(\d+)?/i, (msg) ->
     count = if msg.match[1]? then parseInt(msg.match[1], 10) else 1
     msg.send msg.random answers for i in [1..count]

-  robot.respond /q(?:uestion)? card/i, (msg) ->
+  robot.hear /q(?:uestion)? card/i, (msg) ->
     msg.send msg.random questions


Port Unmount to Process API

Port Unmount to Process API

--- components/Unmount.coffee
+++ components/Unmount.coffee
@@ -15,16 +15,13 @@
   c.outPorts.add 'error',
     datatype: 'object'
     required: false
-
-  noflo.helpers.MapComponent c, (data, groups, out) ->
+  c.process (input, output) ->
+    return unless input.hasData 'container'
+    container = input.getData 'container'
     load.getReact (err, React) ->
       if err
-        c.outPorts.error.send err
-        c.outPorts.error.disconnect()
+        output.done err
         return
-      out.send React.unmountComponentAtNode data
-  ,
-    inPort: 'container'
-    outPort: 'unmounted'
-
-  c
+      output.sendDone
+        unmontainer: React.unmountComponentAtNode container
+    return


use custom mediaView in videoplayer

use custom mediaView in videoplayer

--- coffee/src/shells/videolink.shell.coffee
+++ coffee/src/shells/videolink.shell.coffee
@@ -201,12 +201,17 @@

   initializeRemixMediaView: =>
+    @mediaView = new @model.module.MediaView
+      model: @model
+      eventhub: @eventhub
+      playOnReady: false
+
     @remixMediaView = new acorn.player.TimedMediaRemixView
       eventhub: @eventhub
+      mediaView: @mediaView
       model: @model

-    mediaView = @remixMediaView.mediaView
-    @listenTo mediaView, 'Media:Progress', (view, elapsed, total) =>
+    @listenTo @mediaView, 'Media:Progress', (view, elapsed, total) =>
       elapsed += @model.timeStart()
       @timeRangeView.progress elapsed, {silent: true}

@@ -288,18 +293,19 @@
     if changes.timeStart? and changes.timeStart isnt @model.timeStart()
       seekOffset = 0
     else if changes.timeEnd? and changes.timeEnd isnt @model.timeEnd()
-      seekOffset = Infinity # will be bounded to duration after changes
+      seekOffset = Infinity

     @model.set changes

     # unless user paused the video, make sure it is playing
-    unless @remixMediaView.mediaView.isInState 'pause'
-      @remixMediaView.mediaView.play()
+    unless @mediaView.isInState 'pause'
+      @mediaView.play()

     if seekOffset?
-      # bound between 0 <= seekOffset <= @duration() -2
-      seekOffset = Math.max(0, Math.min(seekOffset, @model.duration() - 2))
-      @remixMediaView.mediaView.seek seekOffset
+      seekOffset = Math.min(seekOffset, @model.timeEnd() - 2)
+      seekOffset = Math.max(@model.timeStart(), seekOffset)
+      console.log 'seekOffset: ' + seekOffset
+      @mediaView.seek seekOffset

     @eventhub.trigger 'change:shell', @model, @

@@ -307,7 +313,7 @@
   onChangeProgress: (progress) =>
     # if slider progress differs from player progress, seek to new position
     progress = progress - @model.timeStart()
-    @remixMediaView.mediaView.seek progress
+    @mediaView.seek progress

   onChangeLoops: (changed) =>
@@ -315,8 +321,8 @@
     @model.loops(loops)

     # restart player loops
-    if @remixMediaView.mediaView.isPlaying()
-      @remixMediaView.mediaView.seek 0
+    if @mediaView.isPlaying()
+      @mediaView.seek 0


Clean up, don't allocate an extra date

Clean up, don't allocate an extra date

--- timeAsyncMethod.coffee
+++ timeAsyncMethod.coffee
@@ -17,11 +17,9 @@
 			)

 		timer = new metrics.Timer(key)
-		start = new Date()

 		realMethod.call this, firstArgs..., (callbackArgs...) ->
-			timer.done()
-			elapsedTime = new Date() - start
+			elapsedTime = timer.done()
 			if logger?
 				logger.log {key, elapsedTime}, "[Metrics] timed async method call"
 			callback.apply this, callbackArgs


Fix path

Fix path

--- components/auction_reminder/index.coffee
+++ components/auction_reminder/index.coffee
@@ -92,7 +92,7 @@
     Cookies.set('closeAuctionReminder', true)
     track.click 'Clicked Auction Reminder'
     analytics.snowplowStruct 'auction_reminder', 'click', @auctionId, 'feature'
-    location.assign("feature/#{@auctionId}")
+    location.assign("/feature/#{@auctionId}")

 module.exports = (callBack) ->
   if Cookies.get 'firstAuctionReminderSeen'


Fixed promise error handling in example.

The error handling would send the 500 status with the error, but let execution continue and also try to send the (non-existing, since there was an error) `order.gui.snippet`.

--- example/src/app.coffee
+++ example/src/app.coffee
@@ -76,9 +76,6 @@
     (id) ->
       # Fetch order by id
       klarna.fetch id
-    # Error
-    (error) ->
-      res.status(500).send error
   )

   # 3) If success -> return snippet
@@ -88,6 +85,10 @@
       console.log "Snippet received"
       # Return snippet
       res.send order.gui.snippet
+  )
+
+  # 4) If error -> return error status
+  .catch(
     # Error
     (error) ->
       res.status(500).send error


revert to using cb_fatal in only one place, just in case.

--- src/smc-util/synctable.coffee
+++ src/smc-util/synctable.coffee
@@ -312,7 +312,7 @@
                 # 2. Now actually do the changefeed query.
                 @_reconnect(cb)
         ], (err) =>
-            cb_fatal(err, cb)
+            cb?(err)
         )

     _reconnect: (cb) =>
@@ -518,10 +518,8 @@
         else
             @__is_saving = true
             @__save (err) =>
-                cb_fatal err, (e) =>
-                    @__is_saving = false
-                    cb?(e)
-
+                @__is_saving = false
+                cb?(err)

     __save: (cb) =>
         if @_state == 'closed'


sagews: fixing changing name -- issue #1057

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -315,15 +315,17 @@
         add_strategy_link      : undefined
         remote_strategy_button : undefined

-    handle_change : (field) ->
-        value = ReactDOM.findDOMNode(@refs[field]).value
+    handle_change : (evt, field) ->
+        # value = ReactDOM.findDOMNode(@refs[field]).value
+        value = evt.target.value
         if field in ['first_name', 'last_name'] and not value and (not @props.first_name or not @props.last_name)
             # special case -- don't let them make their name empty -- that's just annoying (not enforced server side)
             return
         @props.redux.getActions('account').setState("#{field}": value)

-    save_change : (field) ->
-        @props.redux.getTable('account').set("#{field}": ReactDOM.findDOMNode(@refs[field]).value)
+    save_change : (evt, field) ->
+        value = evt.target.value
+        @props.redux.getTable('account').set("#{field}": value)

     render_add_strategy_link: ->
         if not @state.add_strategy_link
@@ -454,15 +456,15 @@
                 label    = 'First name'
                 value    = {@props.first_name}
                 ref      = 'first_name'
-                onChange = {=>@handle_change('first_name')}
-                onBlur   = {=>@save_change('first_name')}
+                onChange = {(e)=>@handle_change(e, 'first_name')}
+                onBlur   = {(e)=>@save_change(e, 'first_name')}
                 />
             <TextSetting
                 label    = 'Last name'
                 value    = {@props.last_name}
                 ref      = 'last_name'
-                onChange = {=>@handle_change('last_name')}
-                onBlur   = {=>@save_change('last_name')}
+                onChange = {(e)=>@handle_change(e, 'last_name')}
+                onBlur   = {(e)=>@save_change(e, 'last_name')}
                 />
             <EmailAddressSetting
                 email_address = {@props.email_address}


log line is removed

log line is removed

--- client/landing/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/landing/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -177,7 +177,6 @@

   updateBucket:(item, constructorName, id)->
     data = item.getData()
-    console.log(item)
     group = data.group or data.anchors
     group.unshift {
       bongo_:


put back control group

put back control group

--- src/desktop/components/split_test/running_tests.coffee
+++ src/desktop/components/split_test/running_tests.coffee
@@ -40,6 +40,7 @@
     weighting: 'equal'
     outcomes: [
       'bio'
+      'control'
       'market'
     ]
   autosuggest_search:


Handle sorting external assignments

Handle sorting external assignments

--- tutor/src/components/scores/student-data-sorter.coffee
+++ tutor/src/components/scores/student-data-sorter.coffee
@@ -39,6 +39,8 @@
             else
               record.completed_on_time_exercise_count
           percent(progress, record.exercise_count)
+    when 'external' # events are also of type 'external' and require no special handling
+      record.status
     when 'concept_coach'
       switch dataType
         when 'score'


qemux86.coffee: Remove archived:true instruction since we are now shipping an image and not a directory

Signed-off-by: Praneeth <fea9c151a4839e984ce3a4de9f1aa1eeae9d990b@gmail.com>

--- qemux86.coffee
+++ qemux86.coffee
@@ -39,7 +39,6 @@
 		fstype: 'resin-sdcard'
 		version: 'yocto-jethro'
 		deployArtifact: 'resin-image-qemux86.resin-sdcard'
-		archive: true
 		compressed: true


Book can change color

Book can change color

--- public/dreamcatcher/controllers/books_controller.coffee
+++ public/dreamcatcher/controllers/books_controller.coffee
@@ -2,6 +2,8 @@

   init: ->
     @initSelectMenu()
+    $('.book').each (i, el) =>
+      @closeBook $(el)

   initSelectMenu:  ->
     $('#new-list').selectmenu {
@@ -28,6 +30,17 @@
     $('.open', bookEl).show()
     $('.closed', bookEl).hide()

+  changeBookColor: (el) ->
+    color = el.attr 'class'
+    bookEl = el.closest '.book'
+    bookEl.attr 'class', 'book'
+    bookEl.addClass color
+
+  showMore: (el) ->
+    bookEl = el.closest '.book'
+    $('.more-settings', bookEl).toggle()
+
+
   '.book .control-panel .color click': (el) ->
     @showPage el, 'color'

@@ -40,10 +53,19 @@
   '.book .open .back click': (el) ->
     @showPage el, 'control'

+
+  '.book .color-panel .swatches li click': (el) ->
+    @changeBookColor el
+
+
   '.book .control-panel .confirm click': (el) ->
     @closeBook el

   '.book .closed .edit click': (el) ->
     @openBook el
+
+  '.book .more click': (el) ->
+    @showMore el
+


* [AdvanceFile] update file.stat on missing stat

--- src/advance.coffee
+++ src/advance.coffee
@@ -67,28 +67,28 @@

   _getStreamSync: (aFile)->
     stat = aFile.stat
-    stat = @_loadStatSync(aFile) unless stat
+    stat = aFile.stat = @_loadStatSync(aFile) unless stat
     if stat.isDirectory()
       @_getDirStreamSync(aFile)
     else
       super aFile
   _getStream: (aFile, cb)->
     stat = aFile.stat
-    stat = @_loadStatSync(aFile) unless stat
+    stat = aFile.stat = @_loadStatSync(aFile) unless stat
     if stat.isDirectory()
       @_getDirStream aFile, cb
     else
       super aFile, cb
   _getBufferSync: (aFile)-> # return the array of files
     stat = aFile.stat
-    stat = @_loadStatSync(aFile) unless stat
+    stat = aFile.stat = @_loadStatSync(aFile) unless stat
     if stat.isDirectory()
       @_getDirBufferSync aFile
     else
       super aFile
   _getBuffer: (aFile, cb)-> # return the array of files
     stat = aFile.stat
-    stat = @_loadStatSync(aFile) unless stat
+    stat = aFile.stat = @_loadStatSync(aFile) unless stat
     if stat.isDirectory()
       @_getDirBuffer aFile, cb
     else


account: don't throw an error for fetchPlansAndSubscriptions

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -1280,6 +1280,9 @@
   fetchPlansAndSubscriptions: secure (client, options, callback) ->
     JPaymentPlan = require './payment/plan'

+    [options, callback] = [callback, options] unless callback
+    options ?= {}
+
     @fetchSubscriptions$ client, options, (err, subscriptions) ->
       return callback err  if err


:memo: Add konami code spec

:memo: Add konami code spec

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -351,6 +351,7 @@
         elementA.addEventListener 'y-command-ctrl-up', (e) -> events.push('y-ctrl-keyup')
         elementA.addEventListener 'x-command-ctrl-up', (e) -> events.push('x-ctrl-keyup')
         elementA.addEventListener 'y-command-y-up-ctrl-up', (e) -> events.push('y-up-ctrl-keyup')
+        elementA.addEventListener 'abc-secret-code-command', (e) -> events.push('abc-secret-code')

         keymapManager.add "test",
           ".a":
@@ -358,6 +359,7 @@
             "ctrl-y ^ctrl": "y-command-ctrl-up"
             "ctrl-x ^ctrl": "x-command-ctrl-up"
             "ctrl-y ^ctrl-y ^ctrl": "y-command-y-up-ctrl-up"
+            "a b c ^b ^a ^c": "abc-secret-code-command"

       it "dispatches the command when a matching keystroke precedes it", ->
         keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
@@ -385,6 +387,25 @@
         keymapManager.handleKeyboardEvent(buildKeyupEvent('ctrl', target: elementA))
         advanceClock(keymapManager.getPartialMatchTimeout())
         expect(events).toEqual ['y-keydown']
+
+      it "dispatches commands with multiple keyup keystrokes specified", ->
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('a', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('b', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('c', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('a', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('b', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('c', target: elementA))
+        advanceClock(keymapManager.getPartialMatchTimeout())
+        expect(events).toEqual []
+
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('a', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('b', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('c', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('b', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('a', target: elementA))
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('c', target: elementA))
+        advanceClock(keymapManager.getPartialMatchTimeout())
+        expect(events).toEqual ['abc-secret-code']

     it "only counts entire keystrokes when checking for partial matches", ->
       element = $$ -> @div class: 'a'


fix lint errr

fix lint errr

--- spec/motion-general-spec.coffee
+++ spec/motion-general-spec.coffee
@@ -1131,7 +1131,7 @@
         beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', true)

         describe "softwrap = false, firstColumnIsVisible = true", ->
-          beforeEach -> set cursor: [0, 3];
+          beforeEach -> set cursor: [0, 3]
           it "move to column 0 of screen line", -> ensure "g 0", cursor: [0, 0]

         describe "softwrap = false, firstColumnIsVisible = false", ->
@@ -1195,7 +1195,6 @@
           it "move to first-char of screen line", ->
             set cursorScreen: [0, 3]; ensure "g ^", cursorScreen: [0, 1]
             set cursorScreen: [1, 3]; ensure "g ^", cursorScreen: [1, 1] # skip softwrap indentation.
-
     describe "the g $ keybinding", ->
       describe "allowMoveToOffScreenColumnOnScreenLineMotion = true(default)", ->
         beforeEach -> settings.set('allowMoveToOffScreenColumnOnScreenLineMotion', true)


fix spec

fix spec

--- spec/project-folder-spec.coffee
+++ spec/project-folder-spec.coffee
@@ -220,7 +220,8 @@

   describe "view::openInNewWindow", ->
     it "open selected project in new window", ->
-      spyOn(view, "getSelectedItem").andReturn(normalDir1)
+      selectedItem = {itemPath: normalDir1, itemType: 'directory'}
+      spyOn(view, "getSelectedItem").andReturn(selectedItem)
       spyOn(atom, "open")
       view.openInNewWindow()
       expect(atom.open).toHaveBeenCalledWith({pathsToOpen: [normalDir1], newWindow: true})


fshelper: working version of group uploading, WIP

--- client/finder/fs/fshelper.coffee
+++ client/finder/fs/fshelper.coffee
@@ -213,15 +213,19 @@
       "#{KD.config.uploadsUri}/#{KD.whoami().getId()}/#{name}"

     upload : (name, content, type, slug, callback)->
-      vmController = KD.getSingleton 'vmController'
-      # TODO : change before merging
-      args = {name, bucket: "groups", "groupName", content}
-      vmController.run
+      # groups
+      args = {name, bucket : "groups", path : "buckets", content}
+
+      #user
+      # args = {name,  content}
+
+      KD.getSingleton('vmController').run
         method    : 's3.store'
         withArgs  : args
       , (err, res)->
-        if err then callback err
-        else callback null, FSHelper.s3.get name
+        console.log "upload", err, res
+        # if err then callback err
+        # else callback null, FSHelper.s3.get name

     remove : (name, callback)->
       vmController = KD.getSingleton 'vmController'


More logging

More logging

--- services/filestore/app/coffee/S3PersistorManager.coffee
+++ services/filestore/app/coffee/S3PersistorManager.coffee
@@ -111,6 +111,7 @@
 			secret: settings.filestore.s3.secret
 			bucket: bucketName
 		s3Client.list prefix:key, (err, data)->
+			logger.log data: data, key: key, bucketName: bucketName, "got file list"
 			keys = _.map data.Contents, (entry)->
 				return entry.Key
 			s3Client.deleteMultiple keys, callback


avoid clobbering global _ in loop

avoid clobbering global _ in loop

--- services/track-changes/app/coffee/UpdatesManager.coffee
+++ services/track-changes/app/coffee/UpdatesManager.coffee
@@ -158,7 +158,7 @@
 				users[update.meta.user_id] = true

 		jobs = []
-		for user_id, _ of users
+		for user_id of users
 			do (user_id) ->
 				jobs.push (callback) ->
 					WebApiManager.getUserInfo user_id, (error, userInfo) ->


atom updates

atom updates

--- files/atom/config.cson
+++ files/atom/config.cson
@@ -2,7 +2,8 @@
   "atom-beautify":
     general:
       _analyticsUserId: "ca3b458a-9279-4470-91ff-ab6f2bfeba74"
-  "autocomplete-plus": {}
+  "autocomplete-plus":
+    confirmCompletion: "tab always, enter when suggestion explicitly selected"
   "bracket-matcher":
     autocompleteCharacters: [
       "()"
@@ -112,7 +113,6 @@
     displayHelpText: false
     leinPath: "/usr/local/bin/lein"
     refreshOnReplStart: false
-    showInlineResults: false
   "terminal-plus":
     core: {}
   "tree-view":


adds the rest of what is needed for link to project builder Talk board...

--- app/pages/lab/landing-page.cjsx
+++ app/pages/lab/landing-page.cjsx
@@ -20,6 +20,7 @@
       buildHelp: 'Project Builder Help'
       policies: 'Project Builder Policies'
       bestPractices: 'Best Practices Guide'
+      buildTalk: 'Project Builder Talk'

 module.exports = React.createClass
   displayName: 'ProjectBuilderLandingPage'
@@ -60,6 +61,7 @@
           <Link to="/lab-how-to"><Translate content="labLanding.links.buildHelp" /></Link>
           <Link to="/lab-policies"><Translate content="labLanding.links.policies"></Translate></Link>
           <Link to="/lab-best-practices/introduction"><Translate content="labLanding.links.bestPractices" /></Link>
+          <Link to="/talk/18"><Translate content="labLanding.links.buildTalk" /></Link>
         </div>
       </div>
     </div>


User settings

User settings

--- app/routes/authenticated.coffee
+++ app/routes/authenticated.coffee
@@ -37,9 +37,8 @@
       mixpanel = @get "mixpanel"
       mixpanel.identify user.get "id"
       mixpanel.peopleSet
-        id: user.get "id"
-        username: user.get "username"
-        email: user.get "email"
+        "$name": user.get "username"
+        "$email": user.get "email"


Changed default for Vega Messages to internalRelease: false

--- app/schemas/models/level.coffee
+++ app/schemas/models/level.coffee
@@ -104,7 +104,7 @@
   move: c.object {title: 'Move', description: 'Tell the Thang to move.', required: ['target'], default: {target: {}, duration: 500}},
     target: _.extend _.cloneDeep(PointSchema), {title: 'Target', description: 'Target point to which the Thang will move.', default: {x: 20, y: 20}}
     duration: {title: 'Duration', description: 'Number of milliseconds over which to move, or 0 for an instant move.', type: 'integer', minimum: 0, format: 'milliseconds'}
-  tutorial: c.object {title: 'Tutorial', description: 'Move Vega around on the screen as a step by step tutorial to explain things.', default: {internalRelease: true}},
+  tutorial: c.object {title: 'Tutorial', description: 'Move Vega around on the screen as a step by step tutorial to explain things.', default: {internalRelease: false}},
     internalRelease: {title: 'Internal Release', description: 'Only show this step to admins or accounts with @codecombat emails', type: 'boolean'}
     position: c.shortString(
       title: 'Dialog Position'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -43,6 +43,7 @@
   ###*
   Get the object closest to this one.

+  @name
   @param {Object|Array|String} selector An object or set of objects to find the closest from.
   ###
   closest: (selector) ->


bounded

bounded

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -30,7 +30,7 @@
 @constructor

 @param {Object} I Instance variables
-@param {Object} self Reference to including object
+@param {Core} self Reference to including object
 ###

 Bounded = (I, self) ->