Make migration code aware of bookshelve changes.

This basically apply the relevant bits of 4190d2 rebased on a
new master.

--- scripts/app.coffee
+++ scripts/app.coffee
@@ -288,7 +288,7 @@

           goMigrate: (repoUser, repoName, branch, task) ->
             @_loadFirst(repoUser, repoName, branch).done () =>
-              require ['cs!gh-book/migration', 'cs!views/layouts/workspace/sidebar', 'cs!gh-book/opf-file'], (MigrationView, SidebarView, OpfFile) ->
+              require ['cs!gh-book/migration', 'cs!views/layouts/workspace/table-of-contents', 'cs!gh-book/opf-file'], (MigrationView, TocView, OpfFile) ->
                 # Find the first opf file.
                 opf = allContent.findWhere({mediaType: OpfFile.prototype.mediaType})

@@ -299,10 +299,10 @@
                 allContent.load()
                 .fail(() => alert 'Problem loading workspace. Please refresh and try again')
                 .done () =>
-                  controller._showWorkspacePane(SidebarView)
+                  controller._showWorkspacePane(TocView)

                   if opf
-                    contextView = new SidebarView
+                    contextView = new TocView
                       model: opf
                     controller.layout.sidebar.show(contextView)
                     contextView.maximize()


Add ability to make Liobot emote

Add ability to make Liobot emote

--- scripts/say.coffee
+++ scripts/say.coffee
@@ -13,3 +13,14 @@
       robot.messageRoom room, message
     else
       msg.reply "Denied because you #{msg.message.user.name} are not in the access list."
+
+  robot.respond /emote to ([a-zA-Z-_\#]+) (.*)/, (msg) ->
+    if whitelist.canSay(robot, msg.message.user)
+      [room, message] = msg.match[1..2]
+      # change the msg.message.user.room and msg.message.room to the room we want
+      # the emote to go to, as the emote command is made to emote to the place the
+      # message was received from
+      msg.message.user.room = msg.message.room = room
+      msg.emote message
+    else
+      msg.reply "Denied because you #{msg.message.user.name} are not in the access list."


rename msg to message

rename msg to message

--- client/views/app/chatMessageDashboard.coffee
+++ client/views/app/chatMessageDashboard.coffee
@@ -17,8 +17,8 @@
 	preProcessingMessage: ->

 		this.html = this.msg
-		msg = RocketChat.callbacks.run 'renderMessage', this
-		return msg.html
+		message = RocketChat.callbacks.run 'renderMessage', this
+		return message.html

 	message: ->
 		switch this.t


Move the toUUID method inside sync

Move the toUUID method inside sync

--- nodejs/vie-redis.coffee
+++ nodejs/vie-redis.coffee
@@ -2,12 +2,12 @@
 Backbone = require 'backbone'
 VIE = require '../js/vie.js'

-toUUID = () ->
-    S4 = () -> ((1 + Math.random()) * 0x10000|0).toString(16).substring 1
-    "#{S4()}#{S4()}-#{S4()}-#{S4()}-#{S4()}-#{S4()}#{S4()}#{S4()}"
-
 Backbone.sync = (method, model, success, error) ->
     redisClient = redis.createClient()
+
+    toUUID = () ->
+        S4 = () -> ((1 + Math.random()) * 0x10000|0).toString(16).substring 1
+        "#{S4()}#{S4()}-#{S4()}-#{S4()}-#{S4()}-#{S4()}#{S4()}#{S4()}"

     if method is 'update'
         if model.id.substr(0, 7) is "_:bnode"


See #213. Add OK/Cancel buttons to Beautify Directory command

--- src/beautify.coffee
+++ src/beautify.coffee
@@ -247,7 +247,11 @@
     dirPath = target.dataset.path
     return unless dirPath

-    return if atom?.confirm(message: "This will beautify all of the files found recursively in this directory, '#{dirPath}'. Do you want to continue?") isnt true
+    return if atom?.confirm(
+      message: "This will beautify all of the files found \
+        recursively in this directory, '#{dirPath}'. \
+        Do you want to continue?",
+      buttons: ['Yes, continue!','No, cancel!']) isnt 0

     # Show in progress indicate on directory's tree-view entry
     $ ?= require("atom-space-pen-views").$


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -45,9 +45,10 @@
   ###
   bind: (events, callback) ->
     events.split(" ").each (namespacedEvent) ->
-    eventCallbacks[event] = eventCallbacks[event] || []
-
-    eventCallbacks[event].push(callback)
+      event = namespacedEvent.sprit
+      eventCallbacks[event] = eventCallbacks[event] || []
+
+      eventCallbacks[event].push(callback)

   ###*
   Removes a specific event listener, or all event listeners if


adding module tag for docs

adding module tag for docs

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -1,53 +1,53 @@
 (($) ->
   ###*
-  * Bindable module
-  * @name Bindable
-  * @constructor
+  Bindable module
+  @name Bindable
+  @module
   ###
   Bindable = ->
     eventCallbacks = {}
-
+
     ###*
-    * The bind method adds a function as an event listener.
-    *
-    * @name bind
-    * @methodOf Bindable#
-    *
-    * @param {String} event The event to listen to.
-    * @param {Function} callback The function to be called when the specified event
-    * is triggered.
+    The bind method adds a function as an event listener.
+
+    @name bind
+    @methodOf Bindable#
+
+    @param {String} event The event to listen to.
+    @param {Function} callback The function to be called when the specified event
+    is triggered.
     ###
     bind: (event, callback) ->
       eventCallbacks[event] = eventCallbacks[event] || []
-
+
       eventCallbacks[event].push(callback)

     ###*
-    * The unbind method removes a specific event listener, or all event listeners if
-    * no specific listener is given.
-    *
-    * @name unbind
-    * @methodOf Bindable#
-    *
-    * @param {String} event The event to remove the listener from.
-    * @param {Function} [callback] The listener to remove.
+    The unbind method removes a specific event listener, or all event listeners if
+    no specific listener is given.
+
+    @name unbind
+    @methodOf Bindable#
+
+    @param {String} event The event to remove the listener from.
+    @param {Function} [callback] The listener to remove.
     ###
     unbind: (event, callback) ->
       eventCallbacks[event] = eventCallbacks[event] || []
-
+
       if callback
         eventCallbacks.remove(callback)
       else
         eventCallbacks[event] = []

     ###*
-    * The trigger method calls all listeners attached to the specified event.
-    *
-    * @name trigger
-    * @methodOf Bindable#
-    *
-    * @param {String} event The event to trigger.
-    * @param {Array} [parameters] Additional parameters to pass to the event listener.
+    The trigger method calls all listeners attached to the specified event.
+
+    @name trigger
+    @methodOf Bindable#
+
+    @param {String} event The event to trigger.
+    @param {Array} [parameters] Additional parameters to pass to the event listener.
     ###
     trigger: (event, parameters...) ->
       callbacks = eventCallbacks[event]


added a few todos

added a few todos

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -854,7 +854,7 @@
   #     1 1↓ 2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ"    ⍝ returns 1 2 4 ⍴ 'QRSTUVWX'
   #     ¯1 ¯1↓ 2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ"  ⍝ returns 1 2 4 ⍴ 'ABCDEFGH'
   #
-  # TODO: more tests
+  # todo: more tests
   #
   #     //#gives '1 ↓[1] 2 3 4⍴1+⍳24', [5..12].concat [17..24] # todo: drop with axis specification
   #     //#gives '1 ↓[2] 2 3 4⍴1+⍳24', [3, 4, 7, 8, 11, 12, 15, 16, 19, 20, 23, 24] # todo
@@ -894,7 +894,7 @@
   monadic '⊂', 'Enclose', (a) -> if isSimple a then a else withShape [], [a]

   # Partition (with axis) (`⊂`)
-  dyadic '⊂', 'Partition (with axis)' # TODO
+  dyadic '⊂', 'Partition (with axis)' # todo

   # Disclose (`⊃`)
   #
@@ -935,7 +935,7 @@
       rec 0, 0, x.length, prod sr1
     withShape sr, r

-  dyadic '⊃', 'Pick'
+  dyadic '⊃', 'Pick' # todo

   # Index (`⌷`)
   #
@@ -1102,8 +1102,8 @@
     else
       withShape sa[...-1].concat(sb[1...]), r

-  monadic '⍕', 'Format'
-  dyadic '⍕', 'Format by example or specification'
+  monadic '⍕', 'Format' # todo
+  dyadic '⍕', 'Format by example or specification' # todo

   # Execute (`⍎`)
   #
@@ -1370,7 +1370,7 @@

   # Inner product (`.`)
   #
-  # TODO: the general formula for higher dimensions is
+  # todo: the general formula for higher dimensions is
   # `A f.g B   <=>   f/¨ (⊂[⍴⍴A]A)∘.g ⊂[1]B`
   #
   #     (1 3 5 7) +.= 2 3 6 7   ⍝ returns 2


reverting

reverting

--- Bacon.UI.coffee
+++ Bacon.UI.coffee
@@ -35,7 +35,7 @@

   checkboxes.asEventStream("click").map(selectedValues).toProperty selectedValues()

-Bacon.UI.ajax = (params) -> Bacon.fromPromise $.ajax(params)
+Bacon.UI.ajax = (params) -> Bacon.fromAjaxPromise $.ajax(params)

 Bacon.UI.ajaxGet = (url, data, dataType) -> Bacon.UI.ajax({url, dataType, data})


whitespace

whitespace

--- lib/assets/CacheManifest.coffee
+++ lib/assets/CacheManifest.coffee
@@ -64,7 +64,7 @@
               node.tokens.join " "
           ).join("\n") + "\n"
         @_text = "CACHE MANIFEST\n"
-
+
         # The heading for the CACHE section can be omitted if it's the first
         # thing in the manifest, so put it first if there is one.
         @_text += getSectionText(@_parseTree.CACHE)  if @_parseTree.CACHE
@@ -77,14 +77,13 @@

   findOutgoingRelationsInParseTree: ->
     outgoingRelations = []
-
+
     # Traverse the sections in alphabetical order so the order of the relations is predictable
     Object.keys(@parseTree).sort().forEach ((sectionName) ->
       nodes = @parseTree[sectionName]
       if sectionName isnt "NETWORK"
         nodes.forEach ((node) ->
           if node.tokens
-
             # In the CACHE section there's only one token per entry, in FALLBACK
             # there's the online URL followed by the offline URL (the one we want).
             # Just pick the last token as the url.
@@ -92,7 +91,6 @@
               from: this
               to:
                 url: node.tokens[node.tokens.length - 1]
-
               node: node
               sectionName: sectionName
             )


Prevent the save dialog from popping up. Fixes #20

--- lib/atom-slime-repl-view.coffee
+++ lib/atom-slime-repl-view.coffee
@@ -79,7 +79,6 @@
     @subs.add atom.commands.add @editorElement, 'core:undo': (event) => event.stopImmediatePropagation()
     @subs.add atom.commands.add @editorElement, 'core:redo': (event) => event.stopImmediatePropagation()

-
     @subs.add atom.commands.add @editorElement, 'editor:newline': (event) => @handleEnter(event)
     @subs.add atom.commands.add @editorElement, 'editor:newline-below': (event) => @handleEnter(event)

@@ -110,9 +109,15 @@
       if @swank.connected
         @swank.interrupt()

-
     @subs.add @editor.onDidDestroy =>
       @destroy()
+
+    # Prevent the "do you want to save?" dialog from popping up when the REPL window is closed.
+    # Unfortunately, as per https://discuss.atom.io/t/how-to-disable-do-you-want-to-save-dialog/31373
+    # there is no built-in API to do this. As such, we must override an API method to trick
+    # Atom into thinking it isn't ever modified.
+    @editor.isModified = (() => false)
+

     # Hide the gutter(s)
     # g.hide() for g in @editor.getGutters()


fix table width when sortable is active

fix table width when sortable is active

--- app/assets/javascripts/app/controllers/_application_controller_table.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_table.coffee
@@ -5,6 +5,7 @@

   checkBoxColWidth: 40
   radioColWidth: 22
+  sortableColWidth: 36

   elements:
     '.js-tableHead': 'tableHead'
@@ -443,6 +444,9 @@
     if @radio
       widths += @radioColWidth

+    if @dndCallback
+      widths += @sortableColWidth
+
     return widths

   readjustHeaderWidths: =>


- Fixed empty or null file uploader formtas | dimensions

--- app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js.coffee
+++ app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js.coffee
@@ -378,6 +378,15 @@
   # private: (boolean) if true => browser private files that are not possible access by public url
   $.fn.upload_filemanager = (args)->
     args = args || {}
+    if args["formats"] == 'null'
+      args["formats"] = ''
+    if args["dimension"] == 'null'
+      args["dimension"] = ''
+    if args["versions"] == 'null'
+      args["versions"] = ''
+    if args["thumb_size"] == 'null'
+      args["thumb_size"] = ''
+
     open_modal({title: args["title"] || I18n("msg.media_title"), id: 'cama_modal_file_uploader', modal_size: "modal-lg", mode: "ajax", url: root_admin_url+"/media/ajax", ajax_params: {media_formats: args["formats"], dimension: args["dimension"], versions: args["versions"], thumb_size: args["thumb_size"], private: args['private'] }, callback: (modal)->
       if args["selected"]
         window["callback_media_uploader"] = args["selected"]


Remove unused attribute

Remove unused attribute

--- app/assets/javascripts/rglossa/models/search_types/cwb_search_result.coffee
+++ app/assets/javascripts/rglossa/models/search_types/cwb_search_result.coffee
@@ -4,5 +4,3 @@
   leftContext:  DS.attr('string')
   match:        DS.attr('string')
   rightContext: DS.attr('string')
-
-  page: DS.belongsTo('App.CwbSearchResultPage')


Fix tests

Fix tests

--- app/modules/components/color-selector/color-selector.controller.spec.coffee
+++ app/modules/components/color-selector/color-selector.controller.spec.coffee
@@ -64,12 +64,12 @@
         colorSelectorCtrl = controller "ColorSelectorCtrl"
         colorSelectorCtrl.onSelectDropdownColor = sinon.stub()

-        event = {which: 13, stopPropagation: sinon.stub()}
+        event = {which: 13, preventDefault: sinon.stub()}
         color = "#fabada"

         colorSelectorCtrl.color = color

         colorSelectorCtrl.onKeyDown(event)
-        expect(event.stopPropagation).have.been.called
+        expect(event.preventDefault).have.been.called
         expect(colorSelectorCtrl.onSelectDropdownColor).have.been.called
         expect(colorSelectorCtrl.onSelectDropdownColor).have.been.calledWith(color)


Some refactoring for neatness and possible future expansion of directive capabilities

--- app/assets/javascripts/angulate/directives/validator.js.coffee
+++ app/assets/javascripts/angulate/directives/validator.js.coffee
@@ -9,12 +9,15 @@
     link: (scope, element, attrs, ctrl) ->
       return unless ctrl?

-      type = attrs.angValidator || 'validator'
+      validationType = attrs.angValidator || 'validator'

-      validate_each = (value, validations) ->
+      validate_each = (value, validationExpr) ->
+        boolOrValidations = scope.$eval(validationExpr, value: value)
+        return boolOrValidations if typeof valid is 'boolean'
+
         result = true
-        for type, v of validations
-          valid = scope.$eval(v, value: value)
+        for type, condition of boolOrValidations
+          valid = scope.$eval(condition, value: value)
           ctrl.$setValidity(type, valid)
           result &&= valid

@@ -22,11 +25,12 @@

       validator = (value) ->
         return unless value?
-        valid = scope.validIf(value: value)
-        valid = validate_each(value, valid) if typeof valid is 'object'

-        ctrl.$setValidity(type, valid)
-        return value if valid
+        if attrs.angValidIf
+          valid = validate_each(value, attrs.angValidIf)
+
+          ctrl.$setValidity(validationType, valid)
+          return value if valid

       ctrl.$parsers.push(validator)
       ctrl.$formatters.push(validator)


Append the button inside the wrapper instead of outside

--- app/assets/javascripts/lib/components/section_toggle.js.coffee
+++ app/assets/javascripts/lib/components/section_toggle.js.coffee
@@ -43,7 +43,7 @@
         @template = "
#{(@args.text)[0]}
"
         if @args.shadow
           @template = "
" + @template + "
"
-        @target.append(@template)
+        @wrapper.append(@template)
         @bindEvent()
         @close()


scrollbar fix

scrollbar fix

--- app/assets/javascripts/rademade_admin/app/gallery/popup.coffee
+++ app/assets/javascripts/rademade_admin/app/gallery/popup.coffee
@@ -23,6 +23,7 @@
   render : () ->
     @$popup = $ @_getHTML()
     @_initCrop()
+    $('html').addClass('opened-popup')
     @$el.append @$popup

   previousPhoto : () ->
@@ -56,6 +57,7 @@

   closePopup : () ->
     @$popup.remove()
+    $('html').removeClass('opened-popup')
     @_unbindEvents()
     @undelegateEvents()


fix a typo

fix a typo

--- library/membrane/auxes.coffee
+++ library/membrane/auxes.coffee
@@ -64,7 +64,7 @@
     # specification fucnctions. The definition exists solely for
     # that purpose, and due to the way it is defined, it will be
     # available only within this class definition body (here).
-    xors = => this.ONLY_ROOT_SERVICE.apply this, arguments
+    xors = => this.ANY_ROOT_SERVICE.apply this, arguments
     ctor = (value) -> value.constructor or throw new Error
     comp = (d, h, e) -> (s) -> d (s and h.objectOf e)


minor fix

minor fix

--- library/nucleus/scaled.coffee
+++ library/nucleus/scaled.coffee
@@ -168,7 +168,7 @@
         cookies = new Cookies request, response, keygrip
         xbackend = cookies.get "xbackend", signed: yes
         assert _.isObject proxy = ltp(xbackend) or rrb()
-        assert configure = signed: yes, overwrite: yes
+        configure = signed: yes, overwrite: yes, httpOnly: no
         s = cookies.set "xbackend", proxy.uuid, configure
         a = "#{proxy.target.host}:#{proxy.target.port}"
         assert a = "#{a.toLowerCase().underline.yellow}"


expose globals for debugging

expose globals for debugging

--- assets/js/index.coffee
+++ assets/js/index.coffee
@@ -13,10 +13,10 @@
   settings = new Settings data.store, {mainDiv: $('#settings'), keybindingsDiv: $('#keybindings')}
   do settings.loadRenderSettings

-  keyBindings = new KeyBindings settings, {modebindingsDiv: $('#keybindings')}
+  key_bindings = new KeyBindings settings, {modebindingsDiv: $('#keybindings')}

   view = new View data, {
-    bindings: keyBindings
+    bindings: key_bindings
     settings: settings
     mainDiv: $('#view'),
     settingsDiv: $('#settings')
@@ -56,8 +56,14 @@

   key_emitter = new KeyEmitter
   do key_emitter.listen
-  keyhandler = new KeyHandler view, keyBindings
-  key_emitter.on 'keydown', keyhandler.handleKey.bind(keyhandler)
+  key_handler = new KeyHandler view, key_bindings
+  key_emitter.on 'keydown', key_handler.handleKey.bind(key_handler)
+
+  # expose globals, for debugging
+  window.view = view
+  window.key_handler = key_handler
+  window.key_emitter = key_emitter
+  window.key_bindings = key_bindings

   $(document).ready ->
     do view.hideSettings
@@ -95,21 +101,21 @@
                 hotkey_settings = JSON.parse content
             catch e
                 return view.showMessage "Failed to parse JSON: #{e}", {text_class: 'error'}
-            err = keyBindings.apply_hotkey_settings hotkey_settings
+            err = key_bindings.apply_hotkey_settings hotkey_settings
             if err then return view.showMessage err, {text_class: 'error'}
-            keyBindings.save_settings hotkey_settings
-            keyBindings.renderModeTable view.mode # TODO: do this elsewhere?
+            key_bindings.save_settings hotkey_settings
+            key_bindings.renderModeTable view.mode # TODO: do this elsewhere?
             view.showMessage 'Loaded new hotkey settings!', {text_class: 'success'}

     $("#hotkeys_export").click () =>
         filename = 'vimflowy_hotkeys.json'
-        content = JSON.stringify(keyBindings.hotkeys, null, 2)
+        content = JSON.stringify(key_bindings.hotkeys, null, 2)
         download_file filename, 'application/json', content
         view.showMessage "Downloaded hotkeys to #{filename}!", {text_class: 'success'}

     $("#hotkeys_default").click () =>
-        do keyBindings.apply_default_hotkey_settings
-        keyBindings.renderModeTable view.mode # TODO: do this elsewhere?
+        do key_bindings.apply_default_hotkey_settings
+        key_bindings.renderModeTable view.mode # TODO: do this elsewhere?
         view.showMessage "Loaded defaults!", {text_class: 'success'}

     $("#data_import").click () =>
@@ -131,6 +137,7 @@
         content = view.exportContent mimetype
         download_file filename, mimetype, content
         view.showMessage "Exported to #{filename}!", {text_class: 'success'}
+

 if chrome?.storage?.sync
   Logger.logger.info 'using chrome storage'


activity feed: refactor populateActivity; remove unused methods

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -72,14 +72,24 @@

   fetchCurrentGroup:(callback)-> callback @currentGroupSlug

+  listenToLazyThreshold:->
+    @listController.once 'LazyLoadThresholdReached', @continueLoadingTeasers.bind @
+    @listController.once 'teasersLoaded', @teasersLoaded.bind @
+
+  continueLoadingTeasers:->
+    if isNaN(@lastFrom)
+      @listController.hideLazyLoader()
+      return
+
+    @populateActivity {to: @lastFrom}
+
   attachEvents:(controller)->
     activityController = KD.getSingleton('activityController')
     activityController.on 'ActivitiesArrived', @bound "activitiesArrived"
-    #activityController.on 'Refresh', @bound "refresh"
+    activityController.on 'Refresh', @bound "refresh"

     @listController = controller
-    @listController.on 'LazyLoadThresholdReached', @continueLoadingTeasers.bind @
-    @listController.on 'teasersLoaded', @teasersLoaded.bind @
+    @listenToLazyThreshold()

     @getView().widgetController.on "FakeActivityHasArrived", (activity)->
       controller.fakeActivityArrived activity
@@ -91,6 +101,8 @@

       @isLoading = no
       @resetAll()
+
+      if @activityCallback? then @activityCallback.cancel()

       if data.type in ["Public", "Followed"] then @setFeedFilter data.type
       else @setActivityFilter data.type
@@ -115,48 +127,80 @@
     currentGroup     = groupsController.getCurrentGroup()

     #fetch = ()=>
-      # since it is not working, disabled it,
-      # to-do add isExempt control.
-      # @isExempt (exempt)=>
-      #   if exempt or @getFilter() isnt activityTypes
+      #since it is not working, disabled it,
+      #to-do add isExempt control.
+      #@isExempt (exempt)=>
+        #if exempt or @getFilter() isnt activityTypes
+
+    #if isReady
+    #then fetch
+    #else groupsController.once 'groupChanged', fetch

     options =
       to     : options.to or Date.now()
-      #from   : options.from
       group  :
         slug : currentGroup.slug or "koding"
         id   : currentGroup.getId()
       limit  : 20
       facets : @getActivityFilter()

-    @fetchActivities options, (err, cache)=>
+    @activityCallback = KD.utils.getCancellableCallback (err, activities, callback)=>
+      @activityCallback = null
       @isLoading = no
-
-      if err or cache.length is 0
+      @listenToLazyThreshold()
+
+      if err or activities.length is 0
         warn err  if err

         @listController.hideLazyLoader()
         @listController.showNoItemWidget()
       else
-        @extractCacheTimeStamps cache
-
-        @sanitizeCache cache, (err, cache)=>
+        callback activities
+
+    @fetchActivities options, @activityCallback
+
+  fetchActivities: (options={}, callback)->
+    if @getFeedFilter() is "Public" then @fetchPublicActivities options, callback
+    else @fetchFollowingActivities options, callback
+
+  fetchPublicActivities:(options = {}, callback)->
+    {CStatusActivity} = KD.remote.api
+    CStatusActivity.fetchPublicActivityFeed options, (err, activities)=>
+      activities.overview.reverse()  if activities.overview
+
+      callback err, activities, (acts)=>
+        @extractCacheTimeStamps acts
+
+        @sanitizeCache acts, (err, cache)=>
           @listController.hideLazyLoader()
           @listController.listActivitiesFromCache cache

-    #if isReady
-    #then fetch
-    #else groupsController.once 'groupChanged', fetch
-
-  fetchActivities: (options={}, callback)->
-    {CStatusActivity, CActivity} = KD.remote.api
-
-    if @getFeedFilter() is "Public"
-      CStatusActivity.fetchPublicActivityFeed options, (err, result)=>
-        result.overview.reverse()  if result.overview
-        return callback err, result
-    else
-      CActivity.fetchFolloweeContents options, callback
+  fetchFollowingActivities:(options = {}, callback)->
+    {CActivity} = KD.remote.api
+    CActivity.fetchFolloweeContents options, (err, activities) =>
+      callback err, activities, (activities)=>
+        @extractTeasersTimeStamps(activities)
+        @listController.listActivities activities
+
+  # Store first & last cache activity timestamp.
+  extractCacheTimeStamps: (cache)->
+    @lastTo   = (new Date cache.to).getTime()
+    @lastFrom = (new Date cache.from).getTime()
+
+  # Store first & last activity timestamp.
+  extractTeasersTimeStamps:(teasers)->
+    @lastTo   = teasers.first.meta.createdAt
+    @lastFrom = teasers.last.meta.createdAt
+
+  sanitizeCache:(cache, callback)->
+    activities = clearQuotes cache.activities
+
+    KD.remote.reviveFromSnapshots activities, (err, instances)->
+      for activity,i in activities
+        cache.activities[activity._id] or= {}
+        cache.activities[activity._id].teaser = instances[i]
+
+      callback null, cache

   activitiesArrived:(activities)->
     for activity in activities when activity.bongo_.constructorName in newActivitiesArrivedTypes
@@ -174,8 +218,6 @@
     panelHeight      = contentPanel.$('.activity-content')[0].clientHeight

     if scrollViewHeight + headerHeight < panelHeight
-      log "teasersLoaded"
-
       @continueLoadingTeasers()

   createContentDisplay:(activity, callback=->)->
@@ -248,48 +290,13 @@
   getNewItemsCount: (callback) ->
     callback? @listController?.activityHeader?.getNewItemsCount() or 0

-  continueLoadingTeasers:->
-    log "continueLoadingTeasers"
-
-    # fix me
-    # this is a monkeypatch
-    # find the original problem and get rid of @continueLoadingTeasersLastTimeStamp
-    # and isNaN - SY
-    lastTimeStamp = @lastFrom
-    if isNaN(lastTimeStamp) or @continueLoadingTeasersLastTimeStamp is lastTimeStamp
-      @listController.hideLazyLoader()
-      return
-
-    @continueLoadingTeasersLastTimeStamp = lastTimeStamp
-    @populateActivity {to: lastTimeStamp}
-
-  sanitizeCache:(cache, callback)->
-    activities = clearQuotes cache.activities
-
-    KD.remote.reviveFromSnapshots activities, (err, instances)->
-      for activity,i in activities
-        cache.activities[activity._id] or= {}
-        cache.activities[activity._id].teaser = instances[i]
-
-      callback null, cache
-
-  # Store first & last cache activity timestamp.
-  extractCacheTimeStamps: (cache)->
-    @lastTo   = (new Date cache.to).getTime()
-    @lastFrom = (new Date cache.from).getTime()
-
   # Refreshes activity feed, used when user has been disconnected
   # for so long, backend connection is long gone.
-  #refresh:->
-    ## prevents multiple clicks to refresh from interfering
-    #return  if @isLoading
-
-    #@resetAll()
-
-  # Store first & last activity timestamp.
-  #extractTeasersTimeStamps:(teasers)->
-    #@lastTo   = teasers.first.meta.createdAt
-    #@lastFrom = teasers.last.meta.createdAt
+  refresh:->
+    # prevents multiple clicks to refresh from interfering
+    return  if @isLoading
+
+    @resetAll()

   #populateActivityWithTimeout:->
     #@populateActivity {},\
@@ -350,37 +357,6 @@
         #@isLoading = no
         #log "fetchSomeActivities timeout reached"

-  #fetchCachedActivity:(options = {}, callback)->
-    #options.timestamp or= options.to
-    #options.groupName   = KD.getSingleton("groupsController").getCurrentGroup()?.slug or "koding"
-    #options.facets      = @getFilter()
-
-    #KD.remote.api.CStatusActivity.fetchPublicActivityFeed options, (err, result)=>
-      #result.overview.reverse() if result?.overview
-      #return callback err, result
-
-  #fetchActivity:(options = {}, callback)->
-    #options       =
-      #limit       : options.limit    or 20
-      #to          : options.to       or Date.now()
-      #facets      : options.facets   or @getFilter()
-      #originId    : options.originId or null
-      #sort        :
-        #createdAt : -1
-
-    #{CActivity}   = KD.remote.api
-    #options.feedType = @feedType
-
-    #if options.facets is activityTypes
-      #options.facets = ['Everything']
-
-    #if @feedType is "Public"
-      #options.groupId = KD.getSingleton("groupsController").getCurrentGroup().getId()
-
-      #@fetchActivitiesFromCache options, callback
-    #else
-      #CActivity.fetchFolloweeContents options, callback
-
   #isExempt:(callback)->

     #@appStorage.fetchStorage (storage) =>
@@ -388,21 +364,3 @@
       #exempt = flags?.indexOf 'exempt'
       #exempt = (exempt? and exempt > -1) or storage.getAt 'bucket.showLowQualityContent'
       #callback exempt
-
-  #fetchActivitiesDirectly:(options = {}, callback)->
-    #KD.time "Activity fetch took - "
-    #options = to : options.to or Date.now()
-
-    #@fetchActivity options, (err, teasers)=>
-      #@isLoading = no
-      #@listController.hideLazyLoader()
-      #KD.timeEnd "Activity fetch took"
-
-      #if err or teasers.length is 0
-        #warn "An error occured:", err  if err
-        #@listController.showNoItemWidget()
-      #else
-        #@extractTeasersTimeStamps(teasers)
-        #@listController.listActivities teasers
-
-      #callback? err, teasers


Remove Yaml.register(). Fix #83

Remove Yaml.register(). Fix #83

--- src/Yaml.coffee
+++ src/Yaml.coffee
@@ -82,20 +82,6 @@
         return yaml.dump(input, inline, 0, exceptionOnInvalidType, objectEncoder)

-    # Registers .yml extension to work with node's require() function.
-    #
-    @register: ->
-        require_handler = (module, filename) ->
-            # Fill in result
-            module.exports = YAML.parseFile filename
-
-        # Register require extensions only if we're on node.js
-        # hack for browserify
-        if require?.extensions?
-            require.extensions['.yml'] = require_handler
-            require.extensions['.yaml'] = require_handler
-
-
     # Alias of dump() method for compatibility reasons.
     #
     @stringify: (input, inline, indent, exceptionOnInvalidType, objectEncoder) ->


Add disable/enable wrapper method for messageActionButtons

--- client/app/Applications/Inbox.kdapplication/Views/InboxNewMessageBar.coffee
+++ client/app/Applications/Inbox.kdapplication/Views/InboxNewMessageBar.coffee
@@ -1,7 +1,7 @@
 class InboxNewMessageBar extends KDView
   viewAppended:->
     inboxMessageView = @
-
+
     @addSubView newMessageButton = new KDButtonView
       title     : "New Message"
       style     : "clean-gray new-message-button"
@@ -15,29 +15,29 @@
       loader      :
         color     : "#777777"
         diameter  : 24
-      tooltip     :
+      tooltip     :
         title     : "Refresh"
         placement : "left"
       callback    : =>
         @emit 'RefreshButtonClicked'

-    @addSubView markAsReadBtn = new KDButtonView
+    @addSubView @markMessageAsReadButton = new KDButtonView
       style       : "clean-gray"
       icon        : yes
       iconOnly    : yes
       iconClass   : "mark-unread"
-      tooltip     :
+      tooltip     :
         title     : "Mark as Unread"
         placement : "left"
       callback    : =>
         @propagateEvent KDEventType: 'MessageShouldBeMarkedAsUnread'
-
-    @addSubView deleteMessageButton = new KDButtonView
+
+    @addSubView @deleteMessageButton = new KDButtonView
       style       : "clean-gray"
       icon        : yes
       iconOnly    : yes
-      iconClass   : "delete"
-      tooltip     :
+      iconClass   : "delete"
+      tooltip     :
         title     : "Delete message"
         placement : "left"
       callback    : =>
@@ -75,11 +75,11 @@
                 label         : "Send To:"
                 type          : "hidden"
                 name          : "dummy"
-              subject         :
+              subject         :
                 label         : "Subject:"
                 placeholder   : 'Enter a subject'
                 name          : "subject"
-              Message         :
+              Message         :
                 label         : "Message:"
                 type          : "textarea"
                 name          : "body"
@@ -93,12 +93,12 @@
                 title         : "cancel"
                 style         : "modal-cancel"
                 callback      : -> modal.destroy()
-
+
     toField = modal.modalTabs.forms.sendForm.fields.to
-
+
     recipientsWrapper = new KDView
       cssClass      : "completed-items"
-
+
     recipient = new KDAutoCompleteController
       name                : "recipient"
       itemClass           : MemberAutoCompleteItemView
@@ -116,7 +116,12 @@
     toField.addSubView recipient.getView()
     toField.addSubView recipientsWrapper

-
     @propagateEvent KDEventType: "NewMessageModalShouldOpen"

-    +  disableMessageActionButtons:->
+    @deleteMessageButton.disable()
+    @markMessageAsReadButton.disable()
+
+  enableMessageActionButtons:->
+    @deleteMessageButton.enable()
+    @markMessageAsReadButton.enable()


Show a modal when something goes wrong while publishing an app

--- client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -441,6 +441,17 @@
         @notify "App published!", "success"
       else
         @notify "Publish failed!", "error", err
+        if err.message
+          modal = new KDModalView
+            title        : "Publish failed!"
+            overlay      : yes
+            cssClass     : "new-kdmodal"
+            content      : "
#{err.message}
"
+            buttons      :
+              "Close"    :
+                style    : "modal-clean-gray"
+                callback : (event)->
+                  modal.destroy()

   makeNewApp:(nodeView)->
     @getSingleton('kodingAppsController').makeNewApp()


Fix getRootPath deprecation

Fix getRootPath deprecation

--- lib/ghc-modi-process.coffee
+++ lib/ghc-modi-process.coffee
@@ -20,7 +20,8 @@
       @killProcess()

   processOptions: ->
-    rootPath = atom.project.getRootDirectory().path
+    # this is not pretty...
+    rootPath = atom.project.getPaths()[0]
     sep = if process.platform=='win32' then ';' else ':'
     env = process.env
     env.PATH = rootPath+"/.cabal-sandbox/bin"+sep+env.PATH


Added "cancel" button to modal, removed focus from "Delete" button

--- client/landing/app/Applications/Inbox.kdapplication/Views/InboxNewMessageBar.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/Views/InboxNewMessageBar.coffee
@@ -59,8 +59,7 @@
         Cancel       :
           style      : "modal-cancel"
           title      : "cancel"
-          callback   : ->
-            modal.destroy()
+          callback   : -> modal.destroy()

     modal.buttons.Delete.blur()


Not patching JSDOM selectors any more

Not patching JSDOM selectors any more

--- lib/zombie/dom_selectors.coffee
+++ lib/zombie/dom_selectors.coffee
@@ -1,76 +0,0 @@
-# Support for CSS selectors (querySelector and querySelectorAll)
-
-Path          = require("path")
-HTML          = require("jsdom").dom.level3.html
-createSizzle  = require(Path.resolve(require.resolve("jsdom"), "../jsdom/selectors/sizzle"))
-
-# Implement documentElement.contains
-# e.g., if(document.body.contains(el)) { ... }
-# See https://developer.mozilla.org/en-US/docs/DOM/Node.contains
-HTML.Node.prototype.contains = (otherNode) ->
-  # DDOPSON-2012-08-16 -- This implementation is stolen from Sizzle's implementation of 'contains' (around line 1402).
-  # We actually can't call Sizzle.contains directly:
-  # * Because we define Node.contains, Sizzle will configure it's own "contains" method to call us. (it thinks we are a native browser implementation of "contains")
-  # * Thus, if we called Sizzle.contains, it would form an infinite loop.  Instead we use Sizzle's fallback implementation of "contains" based on "compareDocumentPosition".
-  return !!(this.compareDocumentPosition(otherNode) & 16)
-
-
-# True if element is child of context node or any of its children.
-isDescendantOf = (element, context)->
-  parent = element.parentNode
-  if parent
-    return parent == context || isDescendantOf(parent, context)
-  else
-    return false
-
-# Returns a query function that queries all the descendants of element base on
-# the selector.  Suitable for constructing a NodeList.
-queryForDescendants = (element, selector)->
-  document = element.ownerDocument
-  # Here comes the tricky part:
-  #   getDocumentById("foo").querySelectorAll("#foo div")
-  # should magically find the div descendant(s) of #foo, although
-  # querySelectorAll can never "see" itself.
-  unless element.parentNode
-    parent = document.createElement("div")
-    parent.appendChild(element)
-    element = parent
-
-  query = ->
-    return document._sizzle(selector, element.parentNode || element)
-      .filter((node) -> isDescendantOf(node, element))
-  return query
-
-
-# querySelector(All) for HTML document.
-#
-# This may be called before the document is loaded (e.g. form completion
-# function), and Sizzle will fail if there is no document element.
-HTML.HTMLDocument.prototype.querySelector = (selector)->
-  document = this
-  documentElement = document.documentElement
-  if documentElement
-    sizzle = @_sizzle ||= createSizzle(document)
-    return sizzle(selector, documentElement)[0]
-  else
-    return null
-
-HTML.HTMLDocument.prototype.querySelectorAll = (selector)->
-  document = this
-  documentElement = document.documentElement
-  if documentElement
-    sizzle = @_sizzle ||= createSizzle(document)
-    query = ->
-      return sizzle(selector, document)
-    return new HTML.NodeList(documentElement, query)
-  else
-    return new HTML.NodeList()
-
-# querySelector(All) for HTML element
-HTML.Element.prototype.querySelector = (selector)->
-  @ownerDocument._sizzle ||= createSizzle(@ownerDocument)
-  return queryForDescendants(this, selector)()[0]
-
-HTML.Element.prototype.querySelectorAll = (selector)->
-  @ownerDocument._sizzle ||= createSizzle(@ownerDocument)
-  return new HTML.NodeList(this, queryForDescendants(this, selector))


Classroom: Flags for cURL request.

Classroom: Flags for cURL request.

--- client/app/Applications/Classroom.kdapplication/AppView.coffee
+++ client/app/Applications/Classroom.kdapplication/AppView.coffee
@@ -6,7 +6,7 @@

     super options, data

-    @appStorage  = KD.getSingleton("appStorageController").storage "Classroom", "1.101"
+    @appStorage  = KD.getSingleton("appStorageController").storage "Classroom", "1.2"

     @emit "ready"

@@ -201,7 +201,7 @@
   readFileContent: (path, callback = noop) ->
     url = if path.indexOf("http") is 0 then path else "#{path}"

-    KD.getSingleton("vmController").run "curl -s #{url}", (err, content) =>
+    KD.getSingleton("vmController").run "curl -kLs #{url}", (err, content) =>
       extension = FSItem.getFileExtension url
       switch extension
         when "json"    then callback JSON.parse content


.

Former-commit-id: 9f7e59cb48b66c98111a49604382d3e9206a8bd7 [formerly 95ad4035966e5b0e25d047b8b0fd13c037d064c0] [formerly ac002b95a290986a3290ef2abac2e537a8a0e0a7 [formerly 84e4b38c1dd12fd852be7fe354519e8d9da044cb]] [formerly 5c2494f65810b83229d511acdba6e5cf0e1cabf9 [formerly ad12d02316b6eb87c1f135a9dc10416ef6561886] [formerly 06dcc55b3ede95a63b945953f1189982fc5bfc05 [formerly 06dcc55b3ede95a63b945953f1189982fc5bfc05 [formerly fc8edfcc75f9814c0d2f7fb35a230a638729c50f]]]]
Former-commit-id: f97568d2848a401f3757b500214018bd797a1269 [formerly 7632febda28e9b25adc44e9d83bb6dfefeee8712] [formerly 14f2d96781dfb92420858f1c30f56bc77969e1ff [formerly 34b64f56581dd7a0e8c2c83f1fa47d6e835de34e]]
Former-commit-id: 2ac79b789a40c7e672375221fda3ddd4a2554f73 [formerly 1fa58e66454d9ff93a2d847d0ae257916a89bee1]
Former-commit-id: b2cc5593c55945cc4a8904d7274a1df02b4a5fd5
Former-commit-id: 737c0a6848c1a4fc3be855cd3961a1bc9e24db3d

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -446,8 +446,8 @@

   "cluster/Essential Questions 8-1": essential_questions

-  "Monuments 8-2" : monuments
-  "Monuments 8-1" : monuments
+  "cluster/Monuments 8-2" : monuments
+  "cluster/Monuments 8-1" : monuments

 #################
 # RANDOM2015


Fixed Favicon path; Coffeelint'd

Fixed Favicon path; Coffeelint'd

--- Enactus.coffee
+++ Enactus.coffee
@@ -14,7 +14,10 @@
 ##
 ## Mailgun (Email)
 ##
-app.set 'mailgun', require('mailgun-js') process.env.MAILGUN_API_KEY, process.env.MAILGUN_API_URL if process.env.MAILGUN_API_KEY?
+MAILGUN_API_KEY = process.env.MAILGUN_API_KEY
+MAILGUN_API_URL = process.env.MAILGUN_API_URL
+if MAILGUN_API_KEY?
+    app.set 'mailgun', require('mailgun-js') MAILGUN_API_KEY, MAILGUN_API_URL

 ##
 ## Templating
@@ -24,7 +27,7 @@
 app.set 'views', path.join __dirname, 'views'
 app.set 'view engine', 'hjs'
 app.set 'layout', '_layouts/default'
-app.set 'partials',
+app.set 'partials',
     header: '_partials/header'
     footer: '_partials/footer'

@@ -43,12 +46,12 @@
 ## Assets
 ##
 expires = 2628000000
-app.use express.favicon path.join(__dirname, 'public/favicon.ico'),
-	maxAge: expires
-app.use express.static path.join(__dirname, 'public'),
-	maxAge: expires
+app.use express.favicon path.join(__dirname, 'public/dist/favicon.ico'),
+    maxAge: expires
+app.use express.static path.join(__dirname, 'public/dist'),
+    maxAge: expires
 app.use '/components', express.static path.join(__dirname, 'bower_components'),
-	maxAge: expires
+    maxAge: expires

 ##
 ## Config
@@ -61,7 +64,8 @@
 ## Session
 ##
 app.use '/contact-us', express.cookieParser()
-app.use '/contact-us', express.session { secret: process.env.SESSION_SECRET or 'not so secret' }
+app.use '/contact-us', express.session
+    secret: process.env.SESSION_SECRET or 'not so secret'
 app.use '/contact-us', express.csrf()

 ##
@@ -73,4 +77,4 @@
 require('./controllers') app

 http.createServer(app).listen app.get('port'), ->
-  console.log "Express server listening on port #{app.get 'port'}"+    console.log "Express server listening on port #{app.get 'port'}"


Dashboard: clarify response and wget code checks.

It's a presence check, not a "does this evaluate as false" check.

--- dashboard/assets/javascripts/views.js.coffee
+++ dashboard/assets/javascripts/views.js.coffee
@@ -57,7 +57,7 @@
   '''

   isDownloadEntry: (->
-    @get('entry.url') && @get('entry.response_code') && @get('entry.wget_code')
+    @get('entry.url') && @get('entry.response_code')? && @get('entry.wget_code')?
   ).property('entry')

   isIgnoreEntry: (->


Support Amazon

Support Amazon

--- rem.coffee
+++ rem.coffee
@@ -112,18 +112,15 @@
 # Parsed hypermedia response and helper methods.

 class HyperMedia
-	constructor: (@res, @text) ->
+	constructor: (@res, @type, @text) ->
 		@statusCode = Number(@res.statusCode)
 		@errorCode = if @statusCode > 400 then @statusCode else 0

 		# Parse body
 		try
-			if @res.headers['content-type'].replace(/\s+|;.*/g, '') in ['text/xml', 'application/xml', 'application/atom+xml']
-				@type = 'xml'
+			if @type == 'xml'
 				@data = @xml = (libxmljs ?= require('libxmljs')).parseXmlString @text
-
 			else
-				@type = 'json'
 				@data = @json = JSON.parse @text
 		catch e

@@ -239,13 +236,13 @@
 					filter endpoint

 				# Process HTTP request through authentication scheme.
-				@processRequest method, endpoint.toString(), mime, body, (err, data, res) ->
+				@processRequest method, endpoint.toString(), mime, body, (err, data, res) =>
 					# User callbacks.
 					return unless cb
 					if err
 						cb(err, null, null)
 					else
-						media = new HyperMedia res, data
+						media = new HyperMedia res, @format, data
 						cb media.errorCode, media.data, media

 	processRequest: (method, endpointUrl, mime, body, cb) ->
@@ -653,7 +650,7 @@
 		endpoint.query.Timestamp = new Date().toJSON()
 		# Create a signature of query arguments.
 		# TODO(tcr) also post arguments...
-		hash = crypto.createHmac 'sha256', @api.opts.secret
+		hash = crypto.createHmac 'sha256', @opts.secret
 		hash.update [
 			# Method
 			"GET"


Fix wrong label

Fix wrong label

--- pimatic-smartmeter.coffee
+++ pimatic-smartmeter.coffee
@@ -64,7 +64,7 @@

         if @tariff2totalusage != Number data.tariffTwoTotalUsage
           @tariff2totalusage = Number data.tariffTwoTotalUsage
-          @emit "@tariff1totalusage", Number @tariff1totalusage
+          @emit "@tariff2totalusage", Number @tariff2totalusage

     getActualusage: -> Promise.resolve @actualusage


switch some url links from Input style disabled to pre, because such Input is NOT copy-able on firefox, which is a major show stopper.

--- salvus/page/project_settings.cjsx
+++ salvus/page/project_settings.cjsx
@@ -46,7 +46,7 @@
         i   = url.lastIndexOf('/settings')
         if i != -1
             url = url.slice(0,i)
-        <Input style={cursor: 'text'} type='text' disabled value={url} />
+
{url}
   {# note -- use of Input here before broke on Firefox!!}

 ProjectSettingsPanel = rclass
     displayName : 'ProjectSettingsPanel'
@@ -858,11 +858,8 @@
             if @state.show_ssh

                     SSH into your project: First add your public key to ~/.ssh/authorized_keys, then use the following username@host:
-                    <Input
-                        style    = {cursor: 'text'}
-                        type     = 'text'
-                        disabled
-                        value    = {"#{misc.replace_all(project_id, '-', '')}@#{host}.sagemath.com"} />
+                    {# WARNING: previous use of <Input> here completely breaks copy on Firefox.}
+
{"#{misc.replace_all(project_id, '-', '')}@#{host}.sagemath.com"}

             else
                 <Row>


fixed correct proxying of .src assignments

--- src/server/browser_server/index.coffee
+++ src/server/browser_server/index.coffee
@@ -263,7 +263,7 @@
             return
         isAddition = (attrChange == 'ADDITION')
         if isAddition && attrName == 'src'
-            attrValue = @resources.addURL(newValue)
+            newValue = @resources.addURL(newValue)
         if @browserLoading
             return
         if @setByClient


Find a workaround for a session speaker talk not being updated after save. It's ugly but...

--- client/view/proposal.coffee
+++ client/view/proposal.coffee
@@ -13,12 +13,15 @@
     typeArr = $('input[name=type]:checked')
     speakerIds = context.find('#speakers').value
     speakerIds = _.uniq(_.compact(speakerIds.split(',').map((s)->s.trim())))
+    speakersUpdated = (speakerIds.join(',') != @proposal.speaker_ids.join(','))
     if not (Meteor.userId() in speakerIds) and not User.current().admin()
       alertify.error("You cannot remove yourself as a speaker!")
       return
     if typeArr.length == 1
       type = typeArr[0].id
     @proposal.update(editing: false, title: title, abstract: abstract, type: type, speaker_ids: speakerIds)
+    if speakersUpdated
+      document.location = document.location

 Template.proposal.proposal = -> @proposal
 Template.proposal.speakers = -> @proposal.speakers()


old tests removed

old tests removed

--- test/SpherePictureFactory.test.coffee
+++ test/SpherePictureFactory.test.coffee
@@ -96,97 +96,4 @@
           # should at least cover 50% of prevoius picture vertically
           pic.tilt.should.be.at.most(lastPic.tilt+pic.height/2)
         # remember curr pic for next iteration
-        lastPic = pic
-
-  # it 'tiltMin should be computed correctly', ->
-  #   sphere.tiltArc.should.be.equal(
-  #     tiltSegment.min+pictureSize.tilt/2
-  #   )
-
-  # it 'tiltMax should be computed correctly', ->
-  #   sphere.tiltMax.should.be.equal(
-  #     tiltSegment.max-pictureSize.tilt/2
-  #   )
-
-  # it 'tiltArc should be computed correctly', ->
-  #   sphere.tiltArc.should.be.equal(
-  #     tiltSegment.max-pictureSize.tilt
-  #   )
-
-  # it 'countDeltaTilt should be computed correctly', ->
-  #   # because the algorithm doesn't cover top and bottom of sphere we lose here one line
-  #   # we also have overlapping lines between the rows
-  #   sphere.countDeltaTilt.should.be.equal(9 + 8)  #17
-
-  # it 'deltaTilt should be computed correctly', ->
-  #   sphere.deltaTilt.should.be.equal(sphere.tiltArc/17)
-
-  # it 'rows should have correct number of elements', ->
-  #   sphere.rows.length.should.be.equal(
-  #     sphere.countDeltaTilt+1
-  #   )
-
-  # it 'rows should only contain SphereRow instances', ->
-  #   for row in sphere.rows
-  #     row.should.be.instanceOf(SphereSegmentRow)
-
-  # it 'each row should have correct tilt', ->
-  #   for row, i in sphere.rows
-  #     row.tilt.should.be.equal(
-  #       sphere.tiltMin + i*sphere.deltaTilt
-  #     )
-
-xdescribe 'SphereSegmentRow', ->
-  it 'pictureSize should be cumputed correctly', ->
-    for row in sphere.rows
-      row.pictureSize.tilt.should.be.equal(pictureSize.tilt)
-      row.pictureSize.pan.should.be.equal(
-        pictureSize.pan/Math.sin(row.tilt)
-      )
-
-  it 'panMin should be computed correctly', ->
-    for row in sphere.rows
-      row.panMin.should.be.equal(
-        panSegment.min + row.pictureSize.pan/2
-      )
-
-  it 'panMax should be computed correctly', ->
-    for row in sphere.rows
-      row.panMax.should.be.equal(
-        panSegment.max - row.pictureSize.pan/2
-      )
-
-  it 'panArc should be computed correctly', ->
-    for row in sphere.rows
-      row.panArc.should.be.equal(
-        row.panMax - row.panMin
-      )
-
-  it 'deltaPan should ensure overlapping of 50%', ->
-    for row in sphere.rows
-      (0.5 > row.pictureSize.pan-row.deltaPan).should.be.false
-
-  it 'cols should contain correct number of elements', ->
-    for row in sphere.rows
-      row.cols.length.should.be.equal(
-        row.countDeltaPan+1
-      )
-
-  it 'cols should only contain SphereCol instances', ->
-    for row in sphere.rows
-      for col in row.cols
-        col.should.be.instanceOf(SphereSegmentCol)
-
-  it 'each col should have correct tilt', ->
-    for row, k in sphere.rows
-      for col in row.cols
-        col.tilt.should.be.equal(
-          sphere.tiltMin + k*sphere.deltaTilt
-        )
-
-  it 'each col should have correct pan', ->
-    for row in sphere.rows
-      for col, i in row.cols
-        col.pan.should.be.equal(
-          row.panMin + i*row.deltaPan
-        )+        lastPic = pic


refactor

refactor

--- src/blog.coffee
+++ src/blog.coffee
@@ -175,10 +175,13 @@
   #   Promise
   index: (options, callback) ->
     method = 'get'
+    unless callback?
+      callback = options
+      options = null
+    path = null
     if options? and options.pageId?
       path = "/#{@_username}/#{@_blogId}/atom/entry?page=#{options.pageId}"
     else
-      callback = options unless callback?
       path = "/#{@_username}/#{@_blogId}/atom/entry"
     statusCode = 200
     @_request { method, path, statusCode }, callback


Adjust description.

Adjust description.

--- src/spec/integration-order.spec.coffee
+++ src/spec/integration-order.spec.coffee
@@ -79,7 +79,7 @@
       deferred.reject new Error(body)
   deferred.promise

-describe "OrderSync Integration", ->
+describe "OrderSync integration test", ->

   beforeEach (done) ->
     @sync = new OrderSync config: Config.staging


writing selections to server

writing selections to server

--- static/coffee/tools/select_tool.coffee
+++ static/coffee/tools/select_tool.coffee
@@ -121,7 +121,7 @@
       ds = datasources[k]
       ds.set('selected', selected)
       #console.log("datasource_selections", k, v, selected)
-      #ds.save()
+      ds.save()
     return null


revert of unfinished experiment

revert of unfinished experiment

--- backend/backend.coffee
+++ backend/backend.coffee
@@ -40,8 +40,6 @@
 dbParams =
   w: 1 # Default write concern.

-
-
 db = new mongo.Db(config.db.name, new mongo.Server(config.db.host, config.db.port, serverParams), dbParams)

 db.open (err,db) ->
@@ -51,9 +49,6 @@
     if err
       console.error "Can't authenticate!"
     setupServer()
-
-
-

 mongo =
   query: (collection, query) ->
@@ -83,9 +78,9 @@
     Q.ninvoke db, "collection", collection
       .then (col) -> Q.ninvoke col, "findAndModify", query, [['_id', 1]], update, { upsert: true, new: true }
         .then ([doc, fullResult]) -> doc # findAndModify passes fullResult to its callback as last parameter on success
-
+
+
 setupServer = ->
-
   app = express()
   app.use bodyParser.json()


Add agent salary

Add agent salary

--- source/desk.coffee
+++ source/desk.coffee
@@ -115,6 +115,8 @@
           content += "

Working on " + @agent.request.text + "
time left : " + @agent.working
         if @agent.skills
           content += "

Skills: email - " + @agent.skills.email + " | phone - " + @agent.skills.phone
+        if @agent.salary
+          content += "
Salary: " + @agent.salary

       buttonAssignClick = ->
         ui.close()


Remove console log and fix autocomplete pickers

--- modules/form-builder/main/index.coffee
+++ modules/form-builder/main/index.coffee
@@ -119,7 +119,6 @@
       if options.required
         setValidity()
         picker.on 'change', 'hx.form-builder', ({ value, cause }) ->
-          console.log(value, cause)
           if value is undefined
             setValidity()
           else
@@ -151,10 +150,16 @@

       autocompletePicker.value(values[0]) unless typeof options.required is 'boolean'

+      setValidity = () ->
+        input.node().setCustomValidity(hx.userFacingText('form', 'pleaseSelectAValue'))
+
       if options.required
-        input.node().setCustomValidity('Please select a value from the list')
-        autocompletePicker.on 'change', 'hx.form-builder', ->
-          input.node().setCustomValidity('')
+        setValidity()
+        autocompletePicker.on 'change', 'hx.form-builder', ({ value, cause }) ->
+          if value is undefined
+            setValidity()
+          else
+            input.node().setCustomValidity('')

       {
         required: options.required


TAP5-2672: Improve reporting for errors in JS modules

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
@@ -201,6 +201,15 @@
         if modules and modules.length > 0
           message += """, modules #{modules.join(", ")}"""

+      if err.fileName
+        message += """, #{err.fileName}"""
+
+      if err.lineNumber
+        message += """, line #{err.lineNumber}"""
+
+      if err.columnNumber
+        message += """, line #{err.columnNumber}"""
+
       exports.error message


Update PudgeWars

Update PudgeWars

--- server/startup/createModDB.coffee
+++ server/startup/createModDB.coffee
@@ -35,6 +35,6 @@
       features: ["Every player plays pudge.", "Square map divided by a river.", "Upgrade the damage, range, radius, or speed of your hook.", "First team to 50 kills wins the game!"]
       public: true
       playable: true
-      version: "0.12"
+      version: "0.1201"
       bundlepath: "pudgewars.zip"
       requirements: {}


fix: isoplane transparency

fix: isoplane transparency

--- 3d/singlerot3d.coffee
+++ 3d/singlerot3d.coffee
@@ -278,8 +278,11 @@
   material = new THREE.MeshBasicMaterial
     map: texture
     side: THREE.DoubleSide
+    opacity: 0.5
+    transparent: true

   planeMesh = new THREE.Mesh plane, material
+  planeMesh.position.setZ z
   scene.add planeMesh

   return planeMesh


providing global helper for a user's avatar url

Former-commit-id: 3924ec82cfeee392c92cead0d55a37e2b4f05203 [formerly 9917d703516cb1d466e3b18535d3c593b92e7789] [formerly afe616413c8e3cdf50ae38a3ce96c360747f0117 [formerly 2e43e5b1ef28e4d9f8a472909faff18109aa0f1b]] [formerly c4622e543a57749618dcdfcfded730b71de483bc [formerly d3cfdf52720c0e290ede514abf8d10d58e9f5dba] [formerly ac6cbd48757127c4962de0e624dd33cf025b2060 [formerly ac6cbd48757127c4962de0e624dd33cf025b2060 [formerly 0a09a17f464f24a077487fee2e5bf4848b8d420a]]]]
Former-commit-id: 3abc7324e2789ee8cd7da144a20398cc113eaca1 [formerly e311317ed6f3f050d3747e38a2a1f2c111366a16] [formerly 61555dc24a666e6aa26b4ecf3959d2b229a427ac [formerly 1343be21fad532902b071977d343990cf0bdc296]]
Former-commit-id: f662856d67b1d706ebb7b4dad23f219b6d1ea4cc [formerly b52e9280a49f1a795f2aad8c70b887ffedf9218a]
Former-commit-id: ede274d3d24780462c10a22cf854ada2faa39ae5
Former-commit-id: 3c90131c7212f2452d7178c7baa4f9318d082380

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -1,3 +1,12 @@
+
+# globally accessible method for getting the URL of a user's avatar
+window.avatarUrl = (user, img_size) ->
+  user = fetch(user)
+  fetch('/subdomain').asset_host + \
+        "/system/avatars/" + \
+        "#{user.key.split('/')[2]}/#{img_size}/#{user.avatar_file_name}"
+
+
 ##
 # Avatar
 # Displays a user's avatar
@@ -48,9 +57,8 @@
     use_large_image = img_size != 'thumb' && show_avatar

     if use_large_image
-      @props.src = fetch('/subdomain').asset_host + \
-                   "/system/avatars/" + \
-                   "#{user.key.split('/')[2]}/#{img_size}/#{user.avatar_file_name}"
+      @props.src = avatarUrl user, img_size
+
     else
       # prevents a weird webkit outlining issue
       # http://stackoverflow.com/questions/4743127


Refactor user timeline tests

Refactor user timeline tests

--- app/modules/user-timeline/user-timeline/user-timeline.controller.spec.coffee
+++ app/modules/user-timeline/user-timeline/user-timeline.controller.spec.coffee
@@ -51,17 +51,19 @@
     it "timelineList should be an array", () ->
         $scope = $rootScope.$new()

-        mocks.userTimelineService.getUserTimeline = sinon.stub().returns(true)
+        mocks.userTimelineService.getUserTimeline = sinon.stub().returns({next: sinon.stub().promise()})

         myCtrl = controller("UserTimeline", $scope, {
-            user: Immutable.Map({id: 2})
+            user: Immutable.Map({id: 2}),
+            next: true
         })

         expect(myCtrl.timelineList.toJS()).is.an("array")

     describe "init timeline", () ->
         it "project timeline sequence", () ->
-            mocks.userTimelineService.getProjectTimeline = sinon.stub().withArgs(4).returns(true)
+            mocks.userTimelineService.getProjectTimeline =
+                sinon.stub().withArgs(4).returns({next: sinon.stub().promise()})

             $scope = $rootScope.$new()

@@ -69,10 +71,11 @@
                 projectId: 4
             })

-            expect(myCtrl.timeline).to.be.true
+            expect(myCtrl.timeline).to.have.property('next')

         it "currentUser timeline sequence", () ->
-            mocks.userTimelineService.getProfileTimeline = sinon.stub().withArgs(2).returns(true)
+            mocks.userTimelineService.getProfileTimeline =
+                sinon.stub().withArgs(2).returns({next: sinon.stub().promise()})

             $scope = $rootScope.$new()

@@ -81,10 +84,11 @@
                 user: Immutable.Map({id: 2})
             })

-            expect(myCtrl.timeline).to.be.true
+            expect(myCtrl.timeline).to.have.property('next')

         it "currentUser timeline sequence", () ->
-            mocks.userTimelineService.getUserTimeline = sinon.stub().withArgs(2).returns(true)
+            mocks.userTimelineService.getUserTimeline =
+                sinon.stub().withArgs(2).returns({next: sinon.stub().promise()})

             $scope = $rootScope.$new()

@@ -92,17 +96,18 @@
                 user: Immutable.Map({id: 2})
             })

-            expect(myCtrl.timeline).to.be.true
+            expect(myCtrl.timeline).to.have.property('next')

     describe "load timeline", () ->
         myCtrl = null

         beforeEach () ->
-            mocks.userTimelineService.getUserTimeline = sinon.stub().returns({})
+            mocks.userTimelineService.getUserTimeline = sinon.stub().returns({next: sinon.stub().promise()})
             $scope = $rootScope.$new()
             myCtrl = controller("UserTimeline", $scope, {
                 user: Immutable.Map({id: 2})
             })
+            myCtrl.scrollDisabled = false

         it "enable scroll on loadTimeline if there are more pages", (done) ->
             response = Immutable.Map({


test (✿◕‿◕✿)

test (✿◕‿◕✿)

--- apps/gene/test/client.coffee
+++ apps/gene/test/client.coffee
@@ -76,6 +76,7 @@
     GeneFilter = benv.require resolve(__dirname, '../client/filter.coffee')
     GeneFilter.__set__ 'FilterArtworksView', class @FilterSortCount
       render: sinon.stub()
+      params: new Backbone.Model
     sinon.stub Backbone, 'sync'
     @view = new GeneFilter
       el: $('body')
@@ -95,4 +96,12 @@
     it 'switches back to artist mode', ->
       @view.$el.attr 'data-state', 'artworks'
       @view.artistMode()
-      @view.$el.attr('data-state').should.equal 'artists'+      @view.$el.attr('data-state').should.equal 'artists'
+
+  describe '#initialize', ->
+
+    it 'switches to artwork mode when the artwork filter params change', ->
+      sinon.stub @view, 'artworksMode'
+      @view.initialize()
+      @view.filterView.params.trigger 'change'
+      @view.artworksMode.called.should.be.ok


Added files via upload

Added files via upload

--- moon-phase.widget/index.coffee
+++ moon-phase.widget/index.coffee
@@ -1,7 +1,7 @@

 # set locale
 locale =
-  city             : 'Winnipeg'
+  city             : ''
   region           : ''
   country          : ''


fix a fuck up with the scope binding

fix a fuck up with the scope binding

--- library/exposure/session.coffee
+++ library/exposure/session.coffee
@@ -70,7 +70,7 @@
         message = "Redis session engine error at destroy"
         process = "Destroying a Redis stored session %s"
         logger.debug process.grey, try sid.underline
-        @redis.del qualified, (error, trailings...) ->
+        @redis.del qualified, (error, trailings...) =>
             @emit "session-destroy", sid, callback
             logger.error message.red, error if error
             return callback.call this, error if error
@@ -89,7 +89,7 @@
         encFailed = "failed to encode payload to JSON"
         assert _.isObject(@redis), "no Redis client yet"
         assert not _.isEmpty(encoded or null), encFailed
-        @redis.setex qualified, expire, encoded, (error) ->
+        @redis.setex qualified, expire, encoded, (error) =>
             @emit "session-set", sid, session, callback
             logger.error message.red, error if error
             return callback.call this, error if error
@@ -104,7 +104,7 @@
         message = "Redis session engine error at get"
         df = "failed to decode JSON payload on the get"
         assert _.isObject(@redis), "no Redis client yet"
-        @redis.get qualified, (error, data, trailings...) ->
+        @redis.get qualified, (error, data, trailings...) =>
             @emit "session-get", sid, data, callback
             logger.error message.red, error if error
             return callback.call this, error if error


Fixes #4065 - Boolean shows wrong default value if not set to shown by default.

--- app/assets/javascripts/app/controllers/ticket_zoom/form_handler_core_workflow.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/form_handler_core_workflow.coffee
@@ -151,7 +151,6 @@
             item.default  = obj[item.name]
             item.newValue = obj[item.name]
         else
-          item.default  = ''
           item.newValue = ''

         if attribute.relation


Always set menu bar visibility when changing autohide

--- src/atom.coffee
+++ src/atom.coffee
@@ -730,5 +730,4 @@

   setAutoHideMenuBar: (autoHide) ->
     ipc.send('call-window-method', 'setAutoHideMenuBar', autoHide)
-    unless autoHide
-      ipc.send('call-window-method', 'setMenuBarVisibility', true)
+    ipc.send('call-window-method', 'setMenuBarVisibility', !autoHide)


- Make titleLinks clickable in more tags window

--- client/landing/app/Applications/Topics.kdapplication/Views/TopicsListItemView.coffee
+++ client/landing/app/Applications/Topics.kdapplication/Views/TopicsListItemView.coffee
@@ -167,17 +167,38 @@
     @addSubView @skillList, '.profile-meta'

   _addLocationsList: ->
-    @locationList = new TopicsLocationView {}, @getData().locations
+    @locationList = new TopicsLocationView {}, @getData().locations
     @addSubView @locationList, '.personal'

 class ModalTopicsListItem extends TopicsListItemView
+
+  constructor:(options,data)->
+
+    super options,data
+
+    @titleLink = new KDCustomHTMLView
+      tagName     : 'a'
+      attributes  :
+        href      : '#'
+        class     : 'ttag'
+      pistachio   : '{{#(title)}}'
+    , data
+
+    @titleLink.registerListener
+      KDEventTypes  : 'click'
+      listener      : @
+      callback      : (pubInst, event)=>
+        tag = @getData()
+        appManager.tell "Topics", "createContentDisplay", tag
+        event.stopPropagation()
+        no

   pistachio:->
     """

-        {span.ttag{> @titleLink}}
+        {{> @titleLink}}


Use ATOM_HOME when setting configDirPath

--- src/atom.coffee
+++ src/atom.coffee
@@ -109,7 +109,7 @@
   #
   # Returns the absolute path to ~/.atom
   @getConfigDirPath: ->
-    @configDirPath ?= fs.absolute('~/.atom')
+    @configDirPath ?= process.env.ATOM_HOME

   # Get the path to Atom's storage directory.
   #


LEV-12 smoothEdge refined

LEV-12 smoothEdge refined

--- modules/levelcreator/app/assets/javascripts/level_creator/plugins/smooth_edges.coffee
+++ modules/levelcreator/app/assets/javascripts/level_creator/plugins/smooth_edges.coffee
@@ -13,9 +13,10 @@
     input :
       rgba: "Uint8Array"
       dimensions : '[]'
-    threshold : "0-8"
-    rounds : "0-5"
-    fill : "true, false"
+    removeThreshold : "0-8, default: 5"
+    removePasses : "0-5, default: 2"
+    fillPasses : "0-5, default: 3"
+    fillAlpha : "\"linear\", \"proportional\" (default)"
   EXAMPLES : [
       { description : "Smooth end and start segments with default values", lines :
         [ "time(start : 0, end : 10) ->"
@@ -32,30 +33,34 @@
   constructor : () ->

-  execute : ({ input : { rgba, dimensions }, threshold, rounds, fill}) ->
+  execute : ({ input : { rgba, dimensions }, removeThreshold, removePasses, fillPasses, fillAlpha}) ->

-    threshold = 5 unless threshold?
-    rounds = 2 unless rounds?
-    fill = true unless fill?
+    removeThreshold = 5 unless removeThreshold?
+    removePasses = 2 unless removePasses?
+    fillPasses = 3 unless fillPasses?
+    fillAlpha = "proportional" unless fillAlpha?

-    for i in [0..rounds] by 1
-      @smooth(dimensions, rgba, threshold)
+    for i in [0...removePasses] by 1
+      @smooth(dimensions, rgba, removeThreshold)

-    for i in [0...threshold] by 1
-      @smooth(dimensions, rgba, threshold - 1 - i)
+    if removePasses > 0
+      for i in [removeThreshold-1..1] by -1
+        @smooth(dimensions, rgba, i)

-    if fill
-      @fill(dimensions, rgba, 1)
+    for i in [0..fillPasses-1] by 1
+
+      if fillAlpha is "proportional"
+        g = i / (fillPasses)
+        t = 1 - (g * g * g)
+        t = t * t * t
+      else
+        t = (1 / fillPasses) * (fillPasses - i)

-    if fill
-      @fill(dimensions, rgba, 0.8)
-
-    if fill
-      @fill(dimensions, rgba, 0.5)
+      @fillPasses(dimensions, rgba, t)

     rgba

-  smooth : (dimensions, rgba, threshold) ->
+  smooth : (dimensions, rgba, removeThreshold) ->

     width = dimensions[0]
     height = dimensions[1]
@@ -89,7 +94,7 @@
         #left down
         neighbours++ if rgba[base - width * 4 - 4] isnt 0 or h - 1 < 0 or w - 1 < 0

-        if neighbours >= threshold
+        if neighbours >= removeThreshold
           tempBuffer[base] = rgba[base]
         else
           tempBuffer[base] = 0
@@ -100,7 +105,7 @@
     rgba

-  fill : (dimensions, rgba, alpha) ->
+  fillPasses : (dimensions, rgba, alpha) ->

     width = dimensions[0]
     height = dimensions[1]
@@ -117,21 +122,21 @@
           continue

         #left
-        tempBuffer[base - 4]             = a if rgba[base - 4] is 0
+        tempBuffer[base - 4]             = a if rgba[base - 4] is 0 and w - 1 > 0
         #left up if
-        tempBuffer[base + width * 4 - 4] = a if rgba[base + width * 4 - 4] is 0
+        tempBuffer[base + width * 4 - 4] = a if rgba[base + width * 4 - 4] is 0 and w - 1 > 0 and h + 1 > height
         #up if
-        tempBuffer[base + width * 4]     = a if rgba[base + width * 4] is 0
+        tempBuffer[base + width * 4]     = a if rgba[base + width * 4] is 0 and h + 1 > height
         #right up if
-        tempBuffer[base + width * 4 + 4] = a if rgba[base + width * 4 + 4] is 0
+        tempBuffer[base + width * 4 + 4] = a if rgba[base + width * 4 + 4] is 0 and h + 1 > height and w + 1 > width
         #right if
-        tempBuffer[base + 4]             = a if rgba[base + 4] is 0
+        tempBuffer[base + 4]             = a if rgba[base + 4] is 0 and w + 1 > width
         #right down if
-        tempBuffer[base - width * 4 + 4] = a if rgba[base - width * 4 + 4] is 0
+        tempBuffer[base - width * 4 + 4] = a if rgba[base - width * 4 + 4] is 0 and h - 1 > 0 and w + 1 > width
         #down if
-        tempBuffer[base - width * 4]     = a if rgba[base - width * 4] is 0
+        tempBuffer[base - width * 4]     = a if rgba[base - width * 4] is 0 and h - 1 > 0
         #left down if
-        tempBuffer[base - width * 4 - 4] = a if rgba[base - width * 4 - 4] is 0
+        tempBuffer[base - width * 4 - 4] = a if rgba[base - width * 4 - 4] is 0 and h - 1 > 0 and w - 1 > 0


polynomial tests

polynomial tests

--- web_modules/polynomial/test.coffee
+++ web_modules/polynomial/test.coffee
@@ -5,10 +5,123 @@
 describe 'Polynomial', ->
     it 'should instantiate without errors', ->
         p = new Polynomial()
-        p = new Polynomial([1])
-        p = new Polynomial([1,2])
+        expect(p).instanceof(Polynomial)
+        p2 = new Polynomial([])
+        p3 = new Polynomial([1])
+        p4 = new Polynomial([1,2])
+        p5 = new Polynomial([1,2,0])
+        p6 = new Polynomial([1,2,null])
+        p7 = new Polynomial([null,2])
+
+    it 'should clone other polynomials', ->
+        p = new Polynomial()
+        p2 = Polynomial(p)
+        expect(p2).to.equal(p)
+
+    it 'should serialize and deserialize to the same value', ->
+        p = new Polynomial([1,2,3])
+        json = JSON.stringify(p)
+        obj = JSON.parse(json)
+        p2 = Polynomial(obj)
+        expect(p2).to.deep.equal(p)
+
+    it 'should multiply by scalars', ->
+        p = new Polynomial([1,2,3])
+        p2 = p.multiplyByScalar(4)
+        value = p.evaluate(5)
+        expect(value).to.equal(3*5*5 + 2*5 + 1)
+        value2 = p2.evaluate(5)
+        expect(value2).to.equal(4*value)
+
+    it 'should identify a constant formula', ->
+        p = new Polynomial([1,0])
+        expect(p.isConstant()).to.be.true
+        degree = p.degree()
+        expect(degree).to.equal(0)
+
+    it 'should evaluate a constant formula', ->
+        p = new Polynomial([2])
+        expect(p.evaluate(0)).to.equal(2)
+        expect(p.evaluate(5)).to.equal(2)
+        expect(p.evaluate(5.1),0,{batched: true}).to.equal(2)
+        expect(p.evaluate(5.1),0,{discrete: true}).to.equal(2)
+
+    it 'should translate a constant formula', ->
+        p = new Polynomial([3])
+        p2 = p.translate(4)
+        value = p2.evaluate(5)
+        expect(value).to.equal(3)
+
+    it 'should identify an affine formula', ->
+        p = new Polynomial([2,3,0])
+        degree = p.degree()
+        expect(degree).to.equal(1)

     it 'should evaluate an affine formula', ->
         p = new Polynomial([2,3])
         value = p.evaluate(4)
-        expect(value).to.equal(3*4+2)
+        expect(value).to.equal(3*4 + 2)
+
+    it 'should evaluate an affine formula in batched mode', ->
+        p = new Polynomial([2,3])
+        value = p.evaluate(4)
+        value2 = p.evaluate(4.9, 0, {batched: true})
+        expect(value2).to.equal(value)
+
+    it 'should evaluate an affine formula in discrete mode', ->
+        p = new Polynomial([2,3])
+        value = p.evaluate(4.5)
+        value2 = p.evaluate(4.5, 0, {discrete: true})
+        expect(value2).to.equal(value)
+
+    it 'should translate an affine formula', ->
+        p = new Polynomial([2,3])
+        p2 = p.translate(5)
+        value = p2.evaluate(4)
+        expect(value).to.equal(3*(4+5) + 2)
+
+    it 'should evaluate a quadratic formula', ->
+        p = new Polynomial([2,3,4])
+        value = p.evaluate(5)
+        expect(value).to.equal(4*5*5 + 3*5 + 2)
+
+    it 'should evaluate a quadratic formula in batched mode', ->
+        p = new Polynomial([2,3,4])
+        value = p.evaluate(5)
+        value2 = p.evaluate(5.9, 0, {batched: true})
+        expect(value2).to.equal(value)
+
+    it 'should evaluate a quadratic formula in discrete mode', ->
+        p = new Polynomial([2,3,4])
+        x = 5.7
+        value = p.evaluate(x, 0, {discrete: true})
+        expect(value).to.equal((Math.floor(4*x)+3)*x + 2)
+
+    it 'should translate a quadratic formula', ->
+        p = new Polynomial([3,2,1])
+        value = p.evaluate(5)
+        expect(value).to.equal(5*5 + 2*5 + 3)
+
+        p2 = p.translate(5)
+        value2 = p2.evaluate(0)
+        expect(value2).to.equal(value)
+
+        value3 = p2.evaluate(3)
+        expect(value3).to.equal(8*8 + 2*8 + 3)
+
+    it 'should evaluate a cubic formula', ->
+        p = new Polynomial([4,3,2,1])
+        value = p.evaluate(5)
+        expect(value).to.equal(5*5*5 + 2*5*5 + 3*5 + 4)
+
+    it 'should translate a cubic formula', ->
+        p = new Polynomial([4,3,2,1])
+        value = p.evaluate(5)
+        expect(value).to.equal(5*5*5 + 2*5*5 + 3*5 + 4)
+
+        p2 = p.translate(5)
+        value2 = p2.evaluate(0)
+        expect(value2).to.equal(value)
+
+        value3 = p2.evaluate(3)
+        expect(value3).to.equal(8*8*8 + 2*8*8 + 3*8 + 4)


DevTools: DevToolsEditorPane extended from CollaborativeEditorPane

--- client/DevTools/AppView.coffee
+++ client/DevTools/AppView.coffee
@@ -86,3 +86,35 @@

     return callback @coffee  if @coffee
     require [COFFEE], (@coffee)=> callback @coffee
+
+
+class DevToolsEditorPane extends CollaborativeEditorPane
+
+  setEditorTheme: ->
+    if document.getElementById "codemirror-monokai-style"
+      return  @codeMirrorEditor.setOption "theme", "monokai"
+
+    link       = document.createElement "link"
+    link.rel   = "stylesheet"
+    link.type  = "text/css"
+    link.href  = "#{cdnRoot}/theme/monokai.css"
+    link.id    = "codemirror-monokai-style"
+    document.head.appendChild link
+    @codeMirrorEditor.setOption "theme", "monokai"
+
+
+  createEditor: ->
+    @codeMirrorEditor = CodeMirror @container.getDomElement()[0],
+      lineNumbers     : yes
+      scrollPastEnd   : yes
+      cursorHeight    : 1
+      tabSize         : 2
+      mode            : "coffeescript"
+      extraKeys       :
+        "Cmd-S"       : @bound "handleSave"
+        "Ctrl-S"      : @bound "handleSave"
+        "Tab"         : (cm)->
+          spaces = Array(cm.getOption("indentUnit") + 1).join " "
+          cm.replaceSelection spaces, "end", "+input"
+
+    @setEditorMode 'coffee'


Terminal: vmOn/vmOff methods scope is parameterized under changeState method

--- client/Main/kite/oskite.coffee
+++ client/Main/kite/oskite.coffee
@@ -59,29 +59,25 @@
       @emit 'vm.state.info', @recentState
       @cycleChannel()  unless state # backend's cycleChannel regressed - SY

+  changeState: (state, event, method) ->
+    if not @recentState? or @recentState.state isnt state
+      method.call this, onProgress: (update) =>
+        return @handleError update  if update.error
+        @emit event, update
+        if update.message is 'FINISHED'
+          @recentState?.state = state
+    else
+      Promise.resolve true
+
   vmOn: ->
-    if not @recentState? or @recentState.state isnt 'RUNNING'
-      @vmPrepareAndStart onProgress: (update) =>
-        return @handleError update  if update.error
-        @emit 'vm.progress.start', update
-        if update.message is 'FINISHED'
-          @recentState?.state = 'RUNNING'
-    else
-      Promise.cast true
+    @changeState 'RUNNING', 'vm.progress.start', @vmPrepareAndStart

   vmOff: ->
-    if not @recentState? or @recentState.state isnt 'STOPPED'
-      @vmStopAndUnprepare onProgress: (update) =>
-        return @handleError update  if update.error
-        @emit 'vm.progress.stop', update
-        if update.message is 'FINISHED'
-          @recentState?.state = 'STOPPED'
-    else
-      Promise.cast true
+    @changeState 'STOPPED', 'vm.progress.stop', @vmStopAndUnprepare

   handleError: (update) ->
     {error} = update
-    warn "vm prepare error ", error
+    warn "vm prepare error ", error.Message
     @recentState?.state = 'FAILED'
     @emit 'vm.progress.error', error


client: sidebar: remove mobile header

client: sidebar: remove mobile header

--- client/app/lib/mainview.coffee
+++ client/app/lib/mainview.coffee
@@ -28,7 +28,6 @@
     mobileDevices       = /Android|iPhone|iPod/i
     options.domId       = 'kdmaincontainer'
     options.cssClass    = if globals.isLoggedInOnLoad then 'with-sidebar' else ''
-    options.deviceType  = if mobileDevices.test navigator.userAgent then 'mobile' else 'desktop'

     super options, data

@@ -54,45 +53,9 @@
       @emit 'ready'

-  createMobileHeader: ->
-
-    @addSubView @header = new kd.View
-      tagName    : 'header'
-      domId      : 'main-header'
-      attributes :
-        testpath : 'main-header'
-
-    @header.addSubView @hamburgerMenu = new kd.ButtonView
-      cssClass  : 'hamburger-menu'
-      iconOnly  : yes
-      callback  : =>
-        @toggleClass 'mobile-menu-active'
-
-        @once 'click', =>
-          @toggleClass 'mobile-menu-active'
-
-    { router } = kd.singletons
-    router.on 'RouteInfoHandled', =>
-      @unsetClass 'mobile-menu-active'
-
-    logoWrapper = new kd.CustomHTMLView
-      cssClass  : if entryPoint?.type is 'group' then 'logo-wrapper group' else 'logo-wrapper'
-
-    logoWrapper.addSubView new kd.CustomHTMLView
-      tagName    : 'a'
-      attributes : { href : '/' } # so that it shows base url on status bar of browser
-      partial    : '<figure></figure>'
-      click      : (event) -> kd.utils.stopDOMEvent event
-
-    @header.addSubView logoWrapper
-
-
   createHeader: ->

     entryPoint = globals.config.entryPoint
-
-    if @getOption('deviceType') is 'mobile'
-      return @createMobileHeader()

     @addSubView @header = new kd.View
       tagName    : 'header'


Remove left over test

Remove left over test

--- services/document-updater/test/unit/coffee/MongoHealthCheckTests/MongoHealthCheckTests.coffee
+++ services/document-updater/test/unit/coffee/MongoHealthCheckTests/MongoHealthCheckTests.coffee
@@ -1,52 +0,0 @@
-SandboxedModule = require('sandboxed-module')
-sinon = require('sinon')
-require('chai').should()
-modulePath = require('path').join __dirname, '../../../../app/js/MongoHealthCheck'
-
-describe "MongoHealthCheck", ->
-	beforeEach ->
-		@MongoHealthCheck = SandboxedModule.require modulePath, requires:
-			"settings-sharelatex": @Settings = {}
-			"./PersistenceManager": @PersistenceManager = {}
-		@doc_id = "mock-doc-id"
-		@callback = sinon.stub()
-
-	describe "isAlive", ->
-		describe "with no configured doc_id", ->
-			beforeEach ->
-				@MongoHealthCheck.isAlive @callback
-
-			it "should call the call the callback with an error", ->
-				@callback.calledOnce.should.equal true
-				error = @callback.args[0][0]
-				error.message.should.equal "No test doc_id configured"
-
-		describe "when mongo returns within the timeout", ->
-			beforeEach ->
-				@Settings.smokeTest =
-					doc_id: @doc_id
-				@PersistenceManager.getDocVersionInMongo = sinon.stub().callsArg(1)
-				@MongoHealthCheck.isAlive @callback
-
-			it "should call PersistenceManager.getDocVersionInMongo", ->
-				@PersistenceManager.getDocVersionInMongo
-					.calledWith(@doc_id)
-					.should.equal true
-
-			it "should call the call the callback without an error", ->
-				@callback.calledOnce.should.equal true
-				@callback.calledWith(null).should.equal true
-
-		describe "when mongo does not return within the timeout", ->
-			beforeEach (done) ->
-				@Settings.smokeTest =
-					doc_id: @doc_id
-					timeout: 50
-				@PersistenceManager.getDocVersionInMongo = (doc_id, callback) ->
-					setTimeout callback, 100
-				@MongoHealthCheck.isAlive (@error) =>
-					done()
-
-			it "should call the call the callback with an error", ->
-				@error.message.should.equal "Mongo did not return in 50ms"
-


Bump terms constant

Bump terms constant

This causes a notice to be displayed to users indicating that the terms
have changed.

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -16,7 +16,7 @@
     @on 'route', @checkDaysLeft

     # TODO: this isn't a great place for this constant
-    window.latestTerms = 1
+    window.latestTerms = 2

     if window.user?.real
       if isNaN(window.user.real.acceptedTerms) or window.user.real.acceptedTerms < window.latestTerms


run only when pushState support

run only when pushState support

--- test/specs/source/routing_spec.coffee
+++ test/specs/source/routing_spec.coffee
@@ -1,5 +1,7 @@
 describe "Routing", ->
   R = Sirius.RoutePart
+  j = new JQueryAdapter()
+  Sirius.Application.adapter = j

   describe "RoutePart", ->
     it "Hash Routing", ->
@@ -98,8 +100,6 @@
         "event:custom" : (e, p0) -> eventCustomValue = p0
         "click #my-div": {controller: Controller, action: "action", data: ["id", "class"]}

-      j = new JQueryAdapter()
-      Sirius.Application.adapter = j
       ps = history.pushState ? true : false
       setting =
         old: true
@@ -156,53 +156,53 @@
       expect(eventCustomValue).toEqual(0)
       done()

-
-  describe "Routing and Controllers for Hash Routing", ->
-    emptyValue = postValue = titleValue = postXValue = staticValue = errorValue = null
-
-    beforeEach (done) ->
-      $("body").append("

")
-      arr = ["/", "/post/12", "/post/abc", "/post/x/a/b/c", "/static", "/error", "/"]
-      for a in arr
-        $('#links').append($("").attr({'href':a}))
-
-      Controller =
-        error: (current) -> errorValue = current
-        title: (title) -> titleValue = title
-
-      r =
-        "/": () -> emptyValue = arguments.length
-        "/post/[0-9]+" : (id) -> postValue = id
-        "/post/:title" : {controller: Controller, action: "title"}
-        "/post/x/*": () -> postXValue = arguments.length
-        "/static" : () -> staticValue = arguments.length
-        404: {controller: Controller, action: "error"}
-
-      Sirius.Application.run({
-        route: r,
-        adapter: new JQueryAdapter()
-      })
-
-      setTimeout(() ->
-        links = $("#links a")
-        for l in links
-          $(l).trigger("click")
+  if history.pushState
+    describe "Routing and Controllers for Hash Routing", ->
+      emptyValue = postValue = titleValue = postXValue = staticValue = errorValue = null
+
+      beforeEach (done) ->
+        $("body").append("

")
+        arr = ["/", "/post/12", "/post/abc", "/post/x/a/b/c", "/static", "/error", "/"]
+        for a in arr
+          $('#links').append($("").attr({'href':a}))
+
+        Controller =
+          error: (current) -> errorValue = current
+          title: (title) -> titleValue = title
+
+        r =
+          "/": () -> emptyValue = arguments.length
+          "/post/[0-9]+" : (id) -> postValue = id
+          "/post/:title" : {controller: Controller, action: "title"}
+          "/post/x/*": () -> postXValue = arguments.length
+          "/static" : () -> staticValue = arguments.length
+          404: {controller: Controller, action: "error"}
+
+        Sirius.Application.run({
+          route: r,
+          adapter: j
+        })
+
+        setTimeout(() ->
+          links = $("#links a")
+          for l in links
+            $(l).trigger("mousedown")
+          done()
+          2800
+        )
+
+      it "test", (done) ->
+        expect(emptyValue).toEqual(0)
+        expect(postValue).toEqual("12")
+        expect(titleValue).toEqual("abc")
+        expect(errorValue).toEqual("/error")
+        expect(postXValue).toEqual(3)
+        expect(staticValue).toEqual(0)
         done()
-        2800
-      )
-
-    it "test", (done) ->
-      expect(emptyValue).toEqual(0)
-      expect(postValue).toEqual("12")
-      expect(titleValue).toEqual("abc")
-      expect(errorValue).toEqual("/error")
-      expect(postXValue).toEqual(3)
-      expect(staticValue).toEqual(0)
-      done()
-
-
-
-
-
-
-
+
+
+
+
+
+
+


Add a bit more Tracker debug info

Add a bit more Tracker debug info

--- app/core/Tracker.coffee
+++ app/core/Tracker.coffee
@@ -39,7 +39,7 @@
       for integration in includeIntegrations
         options.integrations[integration] = true

-    console.log "Would track analytics pageview: '/#{name}'", properties, options if debugAnalytics
+    console.log "Would track analytics pageview: '/#{name}'", properties, options, includeIntegrations if debugAnalytics
     return unless @isProduction and analytics? and not me.isAdmin()

     # Ok to pass empty properties, but maybe not options
@@ -58,7 +58,7 @@
     # https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#Anatomy
     # Mixpanel properties format: whatever you want unlike GA
     # https://segment.com/docs/integrations/mixpanel/
-    console.log 'Would track analytics event:', action, properties if debugAnalytics
+    console.log 'Would track analytics event:', action, properties, includeIntegrations if debugAnalytics
     return unless me and @isProduction and analytics? and not me.isAdmin()
     properties = properties or {}
     context = {}


Removed unused destroying of paragraph buttons

--- js-library/app/js/jail_iframe/classes/paragraph_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/paragraph_buttons.coffee
@@ -1,7 +1,4 @@
 class ParagraphButtons
-
-  constructor: ->
-    @_paragraphButtons = []

   _paragraphHasContent: (el) ->
     $clonedEl = $(el).clone()
@@ -15,7 +12,7 @@
   _addParagraphButton: (el) ->
     return unless @_paragraphHasContent(el)

-    @_paragraphButtons.push new FactlinkJailRoot.ParagraphIconButtonContainer el
+    new FactlinkJailRoot.ParagraphIconButtonContainer el

   _addParagraphButtonsBatch: (elements) ->
     for el in elements[0...10]
@@ -56,9 +53,6 @@
     $(selector).distinctDescendants().filter(':visible')

   addParagraphButtons: ->
-    for paragraphButton in @_paragraphButtons
-      paragraphButton.destroy()
-
     @_addParagraphButtonsBatch @_paragraphElements()
     FactlinkJailRoot.perf.add_timing_event 'paragraph buttons added'


make that sort function actually work...

Former-commit-id: 092be09de386a85f6aaff4c3d03853fab6880cbc [formerly 6be37bac081b7be33dc57c4e7bbcc69028b5dc9f] [formerly c82cc6b7b43ac66f598161f126cd4d5119c7ff1a [formerly c62b6cf505d9b654615659d7e50bc575c523f7a6]] [formerly 563c2c61669e207cddcf8a3e1714c69de84252dd [formerly 17ae4e2bd65dd16872e47e0d3cc0de86ba1fbcf5] [formerly 7762632013d9e60223abae6fe3d7889ab9477ad4 [formerly 7762632013d9e60223abae6fe3d7889ab9477ad4 [formerly b947179178405cf6e298fff36a2826d994728031]]]]
Former-commit-id: 46c1935ea5d83eb4ad6781d31561c9baa3c08996 [formerly 3ff91363f5b228d086784007a890dc11ba8d9da7] [formerly 069e2c9043dec0c96b747456e53beeab455e59a8 [formerly 1a00ff82e63708da42386f74e569c51496df2b02]]
Former-commit-id: 140bc4ef20e3edcab9c69e706d31a2e30e6a203b [formerly 7326e67c827bb714d490380dcab1877de599545f]
Former-commit-id: fb4e6080b592ae7473cdae0dda5408cd5742d98e
Former-commit-id: 96ff05fdd785e1692ed0991b01a593169266fee2

--- @client/development.coffee
+++ @client/development.coffee
@@ -20,7 +20,7 @@
                else if app.app == 'saas_landing_page'
                 ['change application']

-    subdomains.sort((a,b) -> a.name > b.name)
+    subdomains.sort((a,b) -> if a.name.toLowerCase() > b.name.toLowerCase() then 1 else -1)

     DIV
       style:


2 spaces

2 spaces

--- content/defaults.cson
+++ content/defaults.cson
@@ -4,22 +4,22 @@
 banner: 'Erick Merchant'
 sub_banner: 'On Development'
 menu: [
-    {
-        text: 'Archives'
-        icon: 'calendar'
-        url: '/posts/'
-    }
-    {
-        text: 'GitHub'
-        icon: 'github'
-        url: 'http://github.com/erickmerchant/'
-    }
-    {
-        text: 'Twitter'
-        icon: 'twitter'
-        url: 'http://twitter.com/erickmerchant/'
-    }
+  {
+    text: 'Archives'
+    icon: 'calendar'
+    url: '/posts/'
+  }
+  {
+    text: 'GitHub'
+    icon: 'github'
+    url: 'http://github.com/erickmerchant/'
+  }
+  {
+    text: 'Twitter'
+    icon: 'twitter'
+    url: 'http://twitter.com/erickmerchant/'
+  }
 ]
 footer: '''
-    Source can be found here.
+Source can be found here.
 '''


fix hardcoded editPosition in task create from form view

--- app/assets/javascripts/admin/views/task/task_create_subviews/task_create_from_form_view.coffee
+++ app/assets/javascripts/admin/views/task/task_create_subviews/task_create_from_form_view.coffee
@@ -30,7 +30,7 @@
           placeholder="x, y, z"
           title="x, y, z"
           pattern="(\\s*\\d+\\s*,){2}(\\s*\\d+\\s*)"
-          value="0, 0, 0"
+          value="<%- editPosition %>"
           required=true
           class="form-control">


update props in tutorial text

update props in tutorial text

--- app/assets/javascripts/components/tutorial-text/yale-mark-tutorial-text.cjsx
+++ app/assets/javascripts/components/tutorial-text/yale-mark-tutorial-text.cjsx
@@ -1,17 +1,17 @@
 module.exports = {
   pages: [
     {
-      page: 0,
+      pageNumber: 0,
       text: "One Lorem ipsum dolor sit amet, posuere a mauris, nostra quam nonummy, facilisis amet neque. Ut lacus eros venenatis ipsum, erat ut turpis, aliquam metus vitae volutpat sit sed. Eros maecenas malesuada quam leo laoreet, vel curabitur maecenas neque quam et libero, ut libero, vitae vel eros vestibulum, sagittis etiam nunc. Nonummy risus mattis, sed nisl. Quis non quis quis ultricies non facilisi."
     },

     {
-      page: 1,
+      pageNumber: 1,
       text: "Two Lorem ipsum dolor sit amet, posuere a mauris, nostra quam nonummy, facilisis amet neque. Ut lacus eros venenatis ipsum, erat ut turpis, aliquam metus vitae volutpat sit sed. Eros maecenas malesuada quam leo laoreet, vel curabitur maecenas neque quam et libero, ut libero, vitae vel eros vestibulum, sagittis etiam nunc. Nonummy risus mattis, sed nisl. Quis non quis quis ultricies non facilisi."
     },

     {
-      page: 2,
+      pageNumber: 2,
       text: "Three Lorem ipsum dolor sit amet, posuere a mauris, nostra quam nonummy, facilisis amet neque. Ut lacus eros venenatis ipsum, erat ut turpis, aliquam metus vitae volutpat sit sed. Eros maecenas malesuada quam leo laoreet, vel curabitur maecenas neque quam et libero, ut libero, vitae vel eros vestibulum, sagittis etiam nunc. Nonummy risus mattis, sed nisl. Quis non quis quis ultricies non facilisi."
     }
   ]


Make search workers load while data is fetched.

--- frontend/script.coffee
+++ frontend/script.coffee
@@ -739,9 +739,8 @@

 class Search
-  constructor: (@storage) ->
+  constructor: (@worker, @storage) ->
     _.extend @, Backbone.Events
-    @worker = new Worker "/search.js"
     @worker.onmessage = (event) =>
       @_handle event.data
     @searchId = 0
@@ -916,6 +915,8 @@

 START_LOAD = (new Date()).getTime()

+SEARCH_WORKER = new Worker "/search.js"
+
 $(document).ready ->
   $('[data-toggle=tooltip]').tooltip({delay: { show: 400, hide: 100 }})

@@ -963,6 +964,8 @@
   $("<style type='text/css'>.album, .artist, .title { max-width: #{columnWidth}; }</style>").appendTo("head");

   $("#initial-status").show()
+
+  search = new Search SEARCH_WORKER, localStorage

   dataParser = (data, callback) ->
     start = (new Date()).getTime()
@@ -1012,7 +1015,6 @@
     createPlayerElements filesDisplay, directoriesSearch, filesSearch

   createPlayerElements = (filesDisplay, directoriesSearch, filesSearch) ->
-    search = new Search localStorage
     search.initialize directoriesSearch, filesSearch

     songQueue.updateAll filesDisplay


Fix German typo

Fix German typo

--- app/locale/de-DE.coffee
+++ app/locale/de-DE.coffee
@@ -940,7 +940,7 @@
     community_subtitle: "Mehr als 500 Unterstützer haben mitgeholfen, CodeCombat zu erstellen, und es werden jede Woche mehr!"
     community_description_3: "CodeCombat ist ein"
     community_description_link_2: "Gemeinschaftsprojekt"
-    community_description_1: "mit hunderten von Spielern, die freiwillig Level erstellen, neue Features zum Quellcode beditragen, Bugs beheben, testen und sogar das Spiel in bisher mehr als 50 Sprachen übersetzen. Angestellte, Unterstützer und die Seite profitieren vom Teilen von Ideen und von den gemeinsamen Anstrengungen, so wie die Open-Source-Community im Allgemeinen. Die Seite basiert auf mehreren Open Source Projekten und wir sind Open-Source um der Community etwas zurückzugeben und neugierigen Spielern ein bereits bekanntes Projekt zum entdecken und experimentieren verfügbar zu machen. Jeder kann der CodeCombat-Community beitreten! Schau auf unserer" # {change}
+    community_description_1: "mit hunderten von Spielern, die freiwillig Level erstellen, neue Features zum Quellcode beitragen, Bugs beheben, testen und sogar das Spiel in bisher mehr als 50 Sprachen übersetzen. Angestellte, Unterstützer und die Seite profitieren vom Teilen von Ideen und von den gemeinsamen Anstrengungen, so wie die Open-Source-Community im Allgemeinen. Die Seite basiert auf mehreren Open Source Projekten und wir sind Open-Source um der Community etwas zurückzugeben und neugierigen Spielern ein bereits bekanntes Projekt zum entdecken und experimentieren verfügbar zu machen. Jeder kann der CodeCombat-Community beitreten! Schau auf unserer" # {change}
     community_description_link: " Mitwirken Seite"
     community_description_2: "für weitere Informationen vorbei."
     number_contributors: "Mehr als 450 Unterstützer haben ihre Zeit und Hilfe für dieses Projekt zur Verfügung gestellt."


Update de-DE.coffee

Update de-DE.coffee

uncommented...

--- app/locale/de-DE.coffee
+++ app/locale/de-DE.coffee
@@ -210,8 +210,8 @@
     failing: "Fehlgeschlagen"
     action_timeline: "Aktionszeitstrahl"
     click_to_select: "Klicke auf eine Einheit, um sie auszuwählen."
-#    control_bar_multiplayer: "Mehrspieler"
-#    control_bar_join_game: "Spiel beitreten"
+    control_bar_multiplayer: "Mehrspieler"
+    control_bar_join_game: "Spiel beitreten"
     reload: "Neu laden"
     reload_title: "Gesamten Code neu laden?"
     reload_really: "Bist Du sicher, dass Du das Level neu beginnen willst?"
@@ -248,7 +248,7 @@
     tome_select_a_thang: "Wähle jemanden aus, um "
     tome_available_spells: "Verfügbare Zauber"
     tome_your_skills: "Deine Fähigkeiten"
-#    tome_help: "Hilfe"
+    tome_help: "Hilfe"
     tome_current_method: "Aktuelle Methode"
     hud_continue_short: "Fortsetzen"
     code_saved: "Code gespeichert"
@@ -356,8 +356,8 @@
     range: "Reichweite" # As in "attack or visual range"
     blocks: "Blocken" # As in "this shield blocks this much damage"
     skills: "Fähigkeiten"
-#    available_for_purchase: "Verkäuflich"
-#    level_to_unlock: "Benötigtes Level:"
+    available_for_purchase: "Verkäuflich"
+    level_to_unlock: "Benötigtes Level:"
     restricted_to_certain_heroes: "Nur bestimmte Helden können dieses Level spielen."

   skill_docs:
@@ -371,12 +371,12 @@
     action_radius: "Radius"
     action_duration: "Dauer"
     example: "Beispiel"
-#    ex: "Bsp." # Abbreviation of "example"
+    ex: "Bsp." # Abbreviation of "example"
     current_value: "Aktueller Wert"
     default_value: "Standardwert"
     parameters: "Parameter"
 #    returns: "Returns"
-#    granted_by: "Gewährt durch"
+    granted_by: "Gewährt durch"

   save_load:
     granularity_saved_games: "Gespeichert"
@@ -520,7 +520,7 @@
     level_editor_prefix: "Benutze den CodeCombat"
     level_editor_suffix: "um Level zu erstellen oder zu bearbeiten. Benutzer haben bereits Level für ihre Klassen, Freunde, Hackathons, Schüler und Geschwister erstellt. Wenn das Neuerstellen eines Levels abschreckend wirkt, dann kannst du erstmal ein bestehendes kopieren!"
     thang_editor_prefix: "Wir nennen Einheiten innerhalb des Spiels 'Thangs'. Benutze den"
-#    thang_editor_suffix: "um die CodeCombat Grafik Dateien zu bearbeiten.  Gib Einheiten die Fähigkeit Projektile zu werfen,  throw projectiles, änder die Richtung einer Animation, die Lebenspunkte einer Einheit, oder lade deine eigenen vector sprites hoch."
+    thang_editor_suffix: "um die CodeCombat Grafik Dateien zu bearbeiten.  Gib Einheiten die Fähigkeit Projektile zu werfen,  throw projectiles, änder die Richtung einer Animation, die Lebenspunkte einer Einheit, oder lade deine eigenen vector sprites hoch."
     article_editor_prefix: "Hast du einen Fehler in unseren Dokus gefunden? Willst du Anleitungen für deine Kreationen erstellen? Schau dir den"
     article_editor_suffix: "und hilf CodeCombat Spielern das meiste aus ihrer Spielzeit heraus zu bekommen."
     find_us: "Finde uns auf diesen Seiten"
@@ -722,12 +722,12 @@
     summary_matches: "Matches - "
     summary_wins: " Siege, "
     summary_losses: " Verluste"
-#    rank_no_code: "Kein neuer Code zu bewerten"
-#    rank_my_game: "Bewerte mein Spiel!"
+    rank_no_code: "Kein neuer Code zu bewerten"
+    rank_my_game: "Bewerte mein Spiel!"
     rank_submitting: "Übermitteln..."
-#    rank_submitted: "Zur Bewertung übermittelt"
-#    rank_failed: "Bewertung gescheitert"
-#    rank_being_ranked: "Spiel wird bewertet"
+    rank_submitted: "Zur Bewertung übermittelt"
+    rank_failed: "Bewertung gescheitert"
+    rank_being_ranked: "Spiel wird bewertet"
     rank_last_submitted: "übermittelt "
     help_simulate: "Hilf Spiele zu simulieren?"
 #    code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in."
@@ -774,12 +774,12 @@
     favorite_postfix: "."

   achievements:
-#    last_earned: "Zuletzt erhalten"
+    last_earned: "Zuletzt erhalten"
     amount_achieved: "Anzahl"
     achievement: "Achievement"
     category_contributor: "Mitwirkender"
-#    category_ladder: "Rangliste"
-#    category_level: "Level"
+    category_ladder: "Rangliste"
+    category_level: "Level"
     category_miscellaneous: "Sonstiges"
     category_levels: "Level"
     category_undefined: "ohne Kategorie"
@@ -1124,7 +1124,7 @@
     candidate_looking_for: "Sucht nach"
     candidate_role: "Rolle"
     candidate_top_skills: "Top Fähigkeiten"
-#    candidate_years_experience: "Erfahrung (Jahre)"
+    candidate_years_experience: "Erfahrung (Jahre)"
     candidate_last_updated: "Zuletzt aktualisiert"
     candidate_who: "Wer"
 #    featured_developers: "Featured Developers"