Refs #3. Start working on another test

Refs #3. Start working on another test

--- test/continuous_deployment.coffee
+++ test/continuous_deployment.coffee
@@ -73,64 +73,28 @@
       @api.routes.post '/free/slug', (req, res) ->
         res.send
           free: true
-
-    describe 'GitHub repo does not exist', ->
-      it 'create new repo', (done) ->
-        # TODO
-        done()
-
-    describe 'GitHub repo already exists', ->
-      it 'use existing repo', (done) ->
-        @timeout(5000)
-
-        @api.routes.post '/apps/exists', (req, res) ->
-          res.send
-            exists: false
-
-        @api.routes.post '/suggest/slug', (req, res) ->
-          res.send
-            slug: 'suggested-slug'
-
-        @api.routes.post '/deploy/new', (req, res) ->
-          res.send
-            success: true
-            url: 'http://example-subdomain.closeheatapp.com'
-
-        prompts = [
-          {
-            question: 'What subdomain would you like to choose'
-            answer: 'example-subdomain'
-          }
-          {
-            question: 'Would you like to use your existing'
-            answer: 'y'
-          }
-        ]
-
-        opts =
-          prompts: prompts
-          git: '../test/fixtures/git/dist/default'
-
-        command('publish', opts).then (stdout) ->
-          assertStdout stdout,
-            """
-            TEST: Executing 'git remote --verbose'
-            You are about to publish a new website.
-            ? What subdomain would you like to choose at SUBDOMAIN.closeheatapp.com? (you will be able to add top level domain later) (suggested-slug)
-            ? What subdomain would you like to choose at SUBDOMAIN.closeheatapp.com? (you will be able to add top level domain later) example-subdomain
-            TEST: Executing 'git remote --verbose'
-            ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? (Y/n)
-            ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? Yes
-            #{success('example-org/example-repo')}
-            """
-          done()
   #
+  #   describe 'GitHub repo does not exist', ->
   #     it 'create new repo', (done) ->
+  #       # TODO
+  #       done()
+  #
+  #   describe 'GitHub repo already exists', ->
+  #     it 'use existing repo', (done) ->
   #       @timeout(5000)
   #
-  #       @api.routes.post '/deploy/existing', (req, res) ->
+  #       @api.routes.post '/apps/exists', (req, res) ->
+  #         res.send
+  #           exists: false
+  #
+  #       @api.routes.post '/suggest/slug', (req, res) ->
+  #         res.send
+  #           slug: 'suggested-slug'
+  #
+  #       @api.routes.post '/deploy/new', (req, res) ->
   #         res.send
   #           success: true
+  #           url: 'http://example-subdomain.closeheatapp.com'
   #
   #       prompts = [
   #         {
@@ -139,27 +103,72 @@
   #         }
   #         {
   #           question: 'Would you like to use your existing'
-  #           answer: 'n'
-  #         }
-  #         {
-  #           question: 'What is the GitHub repository would you like to create for this website?'
-  #           answer: 'example-new-repo'
+  #           answer: 'y'
   #         }
   #       ]
   #
-  #       # also add origin to remotes
-  #       command('publish', prompts).then (stdout) ->
+  #       opts =
+  #         prompts: prompts
+  #         git: '../test/fixtures/git/dist/default'
+  #
+  #       command('publish', opts).then (stdout) ->
   #         assertStdout stdout,
   #           """
+  #           TEST: Executing 'git remote --verbose'
   #           You are about to publish a new website.
-  #           TEST: Executing 'git remote --verbose'
   #           ? What subdomain would you like to choose at SUBDOMAIN.closeheatapp.com? (you will be able to add top level domain later) (suggested-slug)
   #           ? What subdomain would you like to choose at SUBDOMAIN.closeheatapp.com? (you will be able to add top level domain later) example-subdomain
   #           TEST: Executing 'git remote --verbose'
   #           ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? (Y/n)
-  #           ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? No
-  #           ? What is the GitHub repository would you like to create for this website? Ex. Nedomas/NAME?
-  #           ? What is the GitHub repository would you like to create for this website? Ex. Nedomas/NAME? example-new-repo
-  #           #{success('example-new-repo')}
+  #           ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? Yes
+  #           #{success('example-org/example-repo')}
   #           """
   #         done()
+  #
+    it 'create new repo', (done) ->
+      @timeout(5000)
+
+      @api.routes.post '/apps/exists', (req, res) ->
+        res.send
+          exists: false
+
+      @api.routes.post '/suggest/slug', (req, res) ->
+        res.send
+          slug: 'suggested-slug'
+  #
+  #       @api.routes.post '/deploy/new', (req, res) ->
+  #         res.send
+  #           success: true
+  #           url: 'http://example-subdomain.closeheatapp.com'
+
+      prompts = [
+        {
+          question: 'What subdomain would you like to choose'
+          answer: 'example-subdomain'
+        }
+        {
+          question: 'Would you like to use your existing'
+          answer: 'n'
+        }
+        {
+          question: 'What is the GitHub repository would you like to create for this website?'
+          answer: 'example-new-repo'
+        }
+      ]
+
+      # also add origin to remotes
+      command('publish', prompts).then (stdout) ->
+        assertStdout stdout,
+          """
+          You are about to publish a new website.
+          TEST: Executing 'git remote --verbose'
+          ? What subdomain would you like to choose at SUBDOMAIN.closeheatapp.com? (you will be able to add top level domain later) (suggested-slug)
+          ? What subdomain would you like to choose at SUBDOMAIN.closeheatapp.com? (you will be able to add top level domain later) example-subdomain
+          TEST: Executing 'git remote --verbose'
+          ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? (Y/n)
+          ? Would you like to use your existing example-org/example-repo GitHub repository repo for continuos delivery? No
+          ? What is the GitHub repository would you like to create for this website? Ex. Nedomas/NAME?
+          ? What is the GitHub repository would you like to create for this website? Ex. Nedomas/NAME? example-new-repo
+          #{success('example-new-repo')}
+          """
+        done()


nest session content in .session property and simplify object assignment

--- src/render/renderPageWithComponent.coffee
+++ src/render/renderPageWithComponent.coffee
@@ -12,7 +12,8 @@
     res.render = (componentPath, options = {}) ->
       defaults =
         isUser: req.isUser
-      options = _.extend defaults, req.session, options
+        session: req.session
+      options = _.extend defaults, options
       unless options.isUser?
         options.isUser = req.isUser
       if /:/.test componentPath


groups: more avatar wip

groups: more avatar wip

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -130,18 +130,19 @@
         forms     :
           "Avatar":
             title : "Select an Avatar"
-            partial : ""
+            partial : ""
             callback :(formData)=>
               {inputs, buttons} = modal.modalTabs.forms["Avatar"]
+
               fileData = inputs['Drop Image here'].fileList.items[0]?.data
-              log fileData
+              log 'fileData is',fileData

               if fileData
-                log 'uploading to s3'
+                log 'uploading to s3 should happen here. IMPLEMENT ME!'
                 # upload file to S3

                 # add image URL to data.avatar
-                log 'closing modal'
+                log 'closing modal now.'
                 modal.destroy()

             buttons:


Disable return value in Alchemy.preview.js functions

--- app/assets/javascripts/alchemy/alchemy.preview.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.preview.js.coffee
@@ -64,6 +64,7 @@
           scrollTop: $this.offset().top - offset
           scrollLeft: $this.offset().left - offset
         , 400
+        return

       clickElement: (e) ->
         $this = $(this)
@@ -79,6 +80,7 @@
           else
             $elementsWindow.dialog "open"
         $this.trigger("Alchemy.SelectElement")
+        return

       getStyle: (state) ->
         self = Alchemy.ElementSelector


highlighter: dont select when selection started outside of article

--- app/assets/javascripts/app/controllers/layout_ref.coffee
+++ app/assets/javascripts/app/controllers/layout_ref.coffee
@@ -895,7 +895,7 @@
     # don't go into highlight mode
     # just toggle the selected
     if !selection.isCollapsed
-      @toggleHighlightAtSelection $(selection.anchorNode).closest @articles.selector
+      @toggleHighlightAtSelection selection, $(selection.anchorNode).closest @articles.selector
     else
       # show color
       @highlightIcon.css('fill', @colors[@activeColorIndex].color)
@@ -919,7 +919,9 @@
     @setColor()

   onMouseUp: (e) =>
-    @toggleHighlightAtSelection $(e.currentTarget).closest @articles.selector
+    selection = rangy.getSelection()
+
+    @toggleHighlightAtSelection selection, $(e.currentTarget).closest @articles.selector

   #
   # toggle Highlight
@@ -930,18 +932,27 @@
   # - or highlights the selection
   # - clears the selection

-  toggleHighlightAtSelection: (article) ->
-    selection = rangy.getSelection()
+  toggleHighlightAtSelection: (selection, article) ->

     if @highlighter.selectionOverlapsHighlight selection
       @highlighter.unhighlightSelection()
-    else
-      @highlighter.highlightSelection @highlightClass,
-        selection: selection
-        containerElementId: article.get(0).id
-
-      # remove selection
-      selection.removeAllRanges()
+      return @storeHighlights()
+
+    # selection.anchorNode = element in which the selection started
+    # selection.focusNode = element in which the selection ended
+    #
+    # check if the start node is inside of the article or the article itself
+    startNode = @$(selection.anchorNode)
+
+    if !(article.is(startNode) or article.contents().is(startNode))
+      return selection.removeAllRanges()
+
+    @highlighter.highlightSelection @highlightClass,
+      selection: selection
+      containerElementId: article.get(0).id
+
+    # remove selection
+    selection.removeAllRanges()

     @storeHighlights()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -45,7 +45,7 @@
         return "rgba(#{channels.r}, #{channels.g}, #{channels.b}, #{channels.a})"

       toString: ->
-        return (if channels.a == 1 then self.toHex() else self.rgba())
+        return (if channels.a == 1 then toHex([channels.r, channels.g, channels.b]) else self.rgba())

     return self
 )()


StackTemplateListController: Fix "isDefault" issue

--- client/app/lib/stacks/stacktemplatelistcontroller.coffee
+++ client/app/lib/stacks/stacktemplatelistcontroller.coffee
@@ -100,6 +100,7 @@

     for item in @getListItems()
       item.isDefaultView.hide()
+      item.getData().isDefault = no

       if item.getData()._id is stackTemplateId
         item.getData().isDefault = yes


working...

working...

--- views/coffeescript/filters.coffee
+++ views/coffeescript/filters.coffee
@@ -54,13 +54,21 @@
     if relsHiddenByUser.contains(rel)
       hideRel(rel)
       otherNode = rel.other(node)
-      if (!areConnected(otherNode, node, activeRels))
+
+      # (here we could use memoization to improve performance: return if (isHidden(otherNode)))
+
+      if (!areConnected(node, otherNode, activeRels))
         # No connections to otherNode exists, so hide otherNode and its subgraph
         hideSubGraph(node, activeRels)

-areConnected = (nodeA, nodeB, rels) ->
-  # ask server about this..?
-
+areConnected = (nodeA, nodeB, allowedRels) ->
+  for rel in nodeA.rels
+    if allowedRels.contains(rel)
+      otherNode = rel.other(nodeA)
+      if otherNode == nodeB
+        return true
+      return areConnected(otherNode, nodeB)
+  return false

 hideSubGraph = (startNode, rels) ->
   # TODO


require moment and js-routes

require moment and js-routes

--- vendor/assets/javascripts/fustrate.coffee
+++ vendor/assets/javascripts/fustrate.coffee
@@ -1,2 +1,5 @@
+#= require js-routes
+#= require moment
+#
 #= require_directory ./fustrate
 #= require_tree ./fustrate


Addresses #107 though I'm not sure this is correct. Assignments with only proposed hours entered should probably be reassignable. And deleteable? Maybe this is actually okay.

--- app/assets/javascripts/models/assignment.js.coffee
+++ app/assets/javascripts/models/assignment.js.coffee
@@ -27,7 +27,7 @@

   isDeletable: ->
     0 is @work_weeks.reduce (total, element) ->
-      total += parseInt(element.get("actual_hours") or 0, 10) + parseInt(element.get("estimated_hours") or 0, 10)
+      total += parseInt(element.get("actual_hours") or 0, 10)
       total
     , 0


Blur = datetime picker blur

Blur = datetime picker blur

--- vendor/assets/javascripts/modal.js.coffee
+++ vendor/assets/javascripts/modal.js.coffee
@@ -13,7 +13,7 @@
         field = $(section).find("input[name='#{name}']")
         errorSpan = $("
#{message}
")
         $(field).addClass('invalid').after errorSpan
-        ['keyup', 'change'].each ->
+        ['keyup', 'change', 'blur'].each ->
           $(field).one this, -> $(this).siblings(".error").remove()

     return false


Update app/assets/js/controllers/editor_controller.coffee

Downloaded themes had a ".tmtheme" file extension. It should be ".tmTheme".

Fixes this issue https://github.com/aziz/tmTheme-Editor/issues/11

--- app/assets/js/controllers/editor_controller.coffee
+++ app/assets/js/controllers/editor_controller.coffee
@@ -189,7 +189,7 @@
     plist = json2plist($scope.jsonTheme)
     #console.log plist
     blob = new Blob([plist], {type: "text/plain"})
-    saveAs blob, "#{$scope.jsonTheme.name}.tmtheme"
+    saveAs blob, "#{$scope.jsonTheme.name}.tmTheme"

   $scope.save_theme = ->
     $scope.update_general_colors()


去掉删除流程按钮

去掉删除流程按钮

--- app/assets/javascripts/operation_flow_editor/course_wares/oe_flow_list.coffee.cjsx
+++ app/assets/javascripts/operation_flow_editor/course_wares/oe_flow_list.coffee.cjsx
@@ -84,10 +84,7 @@

                       设计
                     </BSButton>
-                    <BSButton bssize='xs' onClick={@remove}>
-
-                      删除
-                    </BSButton>
+


payment: fix typo

payment: fix typo

--- client/app/MainApp/payment/paymentformmodal.coffee
+++ client/app/MainApp/payment/paymentformmodal.coffee
@@ -33,7 +33,6 @@
     @paymentForm = new PaymentForm

     @forwardEvent @paymentForm, 'PaymentInfoSubmitted'
-paymentInfo
     @addSubView @paymentForm

     @paymentForm.hide()


Collaboration: Remove feature flag.

Collaboration: Remove feature flag.

--- client/ide/lib/views/statusbar/idestatusbar.coffee
+++ client/ide/lib/views/statusbar/idestatusbar.coffee
@@ -67,7 +67,7 @@
         return  if @hasClass 'loading'
         return  unless appManager.frontApp.isMachineRunning()

-        appManager.tell 'IDE', 'showChat'  unless @_collabDisabled
+        appManager.tell 'IDE', 'showChat'

     @addSubView @avatars = new KDCustomHTMLView cssClass : 'avatars fr hidden'


infobox quirks

infobox quirks

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -277,6 +277,8 @@
     @embedBox.resetEmbedAndHide()
     @previousURL = ""
     @initialRequest = yes
+    @inputLinkInfoBoxPermaHide = off
+    @inputLinkInfoBox.hide()

     super


CredentialHelper: show error once create fails

--- workers/social/testhelper/models/computeproviders/credentialhelper.coffee
+++ workers/social/testhelper/models/computeproviders/credentialhelper.coffee
@@ -51,6 +51,8 @@

     addToRemoveList client, credential.identifier  if credential

+    console.log ">>> ERROR ON CREATE CREDENTIAL", err  if err
+
     callback err, { credential }


CredentialHelper: no need to remove a credential which is not exists

--- workers/social/testhelper/models/computeproviders/credentialhelper.coffee
+++ workers/social/testhelper/models/computeproviders/credentialhelper.coffee
@@ -49,7 +49,7 @@

   JCredential.create client, options, (err, credential) ->

-    addToRemoveList client, credential.identifier
+    addToRemoveList client, credential.identifier  if credential

     callback err, { credential }


less lazy

less lazy

--- resources/assets/coffee/react/_components/comment.coffee
+++ resources/assets/coffee/react/_components/comment.coffee
@@ -96,11 +96,8 @@
   render: =>
     el Observer, null, () =>
       @children = getChildren(@props)
-      comment = store.comments.get(@props.comment.id)
-      parent = store.comments.get(comment.parent_id)
-      user = @userFor(comment)
-      @comment = comment #lazy
-      @user = user #lazy
+      parent = store.comments.get(@props.comment.parent_id)
+      user = @userFor(@props.comment)

       modifiers = @props.modifiers?[..] ? []
       modifiers.push 'top' if @props.depth == 0
@@ -120,11 +117,11 @@
             div className: 'comment__float-container comment__float-container--left hidden-xs',
               @renderVoteButton()

-          @renderUserAvatar()
+          @renderUserAvatar user

           div className: 'comment__container',
             div className: 'comment__row comment__row--header',
-              @renderUsername()
+              @renderUsername user

               if parent?
                 span
@@ -139,15 +136,15 @@
             if @state.editing
               div className: 'comment__editor',
                 el CommentEditor,
-                  id: comment.id
-                  message: comment.message
+                  id: @props.comment.id
+                  message: @props.comment.message
                   modifiers: @props.modifiers
                   close: @closeEdit
-            else if comment.message_html?
+            else if @props.comment.message_html?
               div
                 className: 'comment__message',
                 dangerouslySetInnerHTML:
-                  __html: comment.message_html
+                  __html: @props.comment.message_html

             div className: 'comment__row comment__row--footer',
               if @canHaveVote()
@@ -157,7 +154,7 @@

               div
                 className: 'comment__row-item comment__row-item--info'
-                dangerouslySetInnerHTML: __html: osu.timeago(comment.created_at)
+                dangerouslySetInnerHTML: __html: osu.timeago(@props.comment.created_at)

               @renderPermalink()
               @renderReplyButton()
@@ -170,7 +167,7 @@

             @renderReplyBox()

-        if @props.showReplies && comment.replies_count > 0
+        if @props.showReplies && @props.comment.replies_count > 0
           div
             className: repliesClass
             @children.map @renderComment
@@ -178,9 +175,9 @@
             el DeletedCommentsCount, { comments: @children, showDeleted: uiState.isShowDeleted }

             el CommentShowMore,
-              parent: comment
+              parent: @props.comment
               comments: @children
-              total: comment.replies_count
+              total: @props.comment.replies_count
               modifiers: @props.modifiers
               label: osu.trans('comments.load_replies') if @children.length == 0
               ref: @loadMoreRef
@@ -218,13 +215,13 @@

   renderEditedBy: =>
-    if !@isDeleted() && @comment.edited_at?
-      editor = userStore.get(@comment.edited_by_id)
+    if !@isDeleted() && @props.comment.edited_at?
+      editor = userStore.get(@props.comment.edited_by_id)
       div
         className: 'comment__row-item comment__row-item--info'
         dangerouslySetInnerHTML:
           __html: osu.trans 'comments.edited',
-            timeago: osu.timeago(@comment.edited_at)
+            timeago: osu.timeago(@props.comment.edited_at)
             user:
               if editor.id?
                 osu.link(laroute.route('users.show', user: editor.id), editor.username, classNames: ['comment__link'])
@@ -235,7 +232,7 @@
   renderPermalink: =>
     div className: 'comment__row-item',
       a
-        href: laroute.route('comments.show', comment: @comment.id)
+        href: laroute.route('comments.show', comment: @props.comment.id)
         className: 'comment__action comment__action--permalink'
         osu.trans('common.buttons.permalink')

@@ -280,7 +277,7 @@
     if @state.showNewReply
       div className: 'comment__reply-box',
         el CommentEditor,
-          parent: @comment
+          parent: @props.comment
           close: @closeNewReply
           modifiers: @props.modifiers

@@ -300,8 +297,8 @@
       div className: 'comment__row-item',
         el ReportComment,
           className: 'comment__action'
-          comment: @comment
-          user: @userFor(@comment)
+          comment: @props.comment
+          user: @userFor(@props.comment)

   renderRestore: =>
@@ -314,30 +311,30 @@
           osu.trans('common.buttons.restore')

-  renderUserAvatar: =>
-    if @user.id?
+  renderUserAvatar: (user) =>
+    if user.id?
       a
         className: 'comment__avatar js-usercard'
-        'data-user-id': @user.id
-        href: laroute.route('users.show', user: @user.id)
-        el UserAvatar, user: @user, modifiers: ['full-circle']
+        'data-user-id': user.id
+        href: laroute.route('users.show', user: user.id)
+        el UserAvatar, user: user, modifiers: ['full-circle']
     else
       span
         className: 'comment__avatar'
-        el UserAvatar, user: @user, modifiers: ['full-circle']
-
-
-  renderUsername: =>
-    if @user.id?
+        el UserAvatar, user: user, modifiers: ['full-circle']
+
+
+  renderUsername: (user) =>
+    if user.id?
       a
-        'data-user-id': @user.id
-        href: laroute.route('users.show', user: @user.id)
+        'data-user-id': user.id
+        href: laroute.route('users.show', user: user.id)
         className: 'js-usercard comment__row-item comment__row-item--username comment__row-item--username-link'
-        @user.username
+        user.username
     else
       span
         className: 'comment__row-item comment__row-item--username'
-        @user.username
+        user.username

   # mobile vote button
@@ -406,7 +403,7 @@

   renderCommentableMeta: =>
     return unless @props.showCommentableMeta
-    meta = commentableMetaStore.get(@comment.commentable_type, @comment.commentable_id)
+    meta = commentableMetaStore.get(@props.comment.commentable_type, @props.comment.commentable_id)

     if meta.url
       component = a


Couchmuffin findManyById fixed

Couchmuffin findManyById fixed

--- server/src/Component/Builtin/Database/CouchMuffin.coffee
+++ server/src/Component/Builtin/Database/CouchMuffin.coffee
@@ -94,9 +94,16 @@
         idsWithPrefix = []
         idsWithPrefix = ("#{@_keyPrefix}#{value}" for value in ids)

-        @_dataSource.bucket.getMulti idsWithPrefix, (error, result) ->
+        @_dataSource.bucket.getMulti idsWithPrefix, (error, result) =>
             return callback error if error? and !_.isNumber error
-            return callback null, result
+            if result
+                newResult = {}
+                for id of result
+                    if @_type?
+                        newResult[id.substr @_type.length + 1, id.length] = result[id]
+                    else
+                        newResult = result
+                return callback null, newResult

     # Remove a single record using the primary key
     # @param {array|string} ids The records id within an array of Strings


writen function to set the user email safely

--- services/web/app/coffee/Features/User/UserUpdater.coffee
+++ services/web/app/coffee/Features/User/UserUpdater.coffee
@@ -1,6 +1,7 @@
 mongojs = require("../../infrastructure/mongojs")
 db = mongojs.db
 ObjectId = mongojs.ObjectId
+UserLocator = require("./UserLocator")

 module.exports = UserUpdater =
 	updateUser: (query, update, callback = (error) ->) ->
@@ -10,3 +11,17 @@
 			query = _id: query

 		db.users.update query, update, callback
+
+
+	changeEmailAddress: (user_id, newEmail, callback)->
+		self = @
+		UserLocator.findById user_id, (error, user) ->
+			if user?
+				return callback({message:"User with that email already exists."})
+			self.updateUser user_id.toString(), {
+				$set: { "email": newEmail},
+			}, (err) ->
+				if err?
+					return callback(err)
+				callback()
+


Tweak effective_regions plugin to accept JSON response

--- app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee
+++ app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee
@@ -55,10 +55,10 @@
       dataType: 'json'
       data: { effective_regions: regionData, effective_menus: menuData }
       async: false
-      complete: (data) ->
-        $('.effective-menu').effectiveMenuEditor('saveComplete')
-        if data.responseText == 'refresh'
-          location.reload(true)
+      complete: (response) ->
+        data = response['responseJSON'] || {}
+        $('.effective-menu').effectiveMenuEditor('saveComplete', data['effective_menus'])
+        location.reload(true) if data['refresh'] == true
 }

 Exit = {


Update `shipping-rate-selection` to use `Order.shippingAddress` directly

--- app/assets/javascripts/sprangular/directives/shippingRatesSelection.coffee
+++ app/assets/javascripts/sprangular/directives/shippingRatesSelection.coffee
@@ -21,8 +21,8 @@

       order.updateTotals()

-    $scope.$watch('order.actualShippingAddress()', ->
-      $scope.address = $scope.order.actualShippingAddress()
+    $scope.$watch('order.shippingAddress', ->
+      $scope.address = $scope.order.shippingAddress
     , true)

     validateAddress = (address) ->


using screen broke the kdevent for the screen used webterm instances. fixed using the icon control code with a random date timestamp.

--- client/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
+++ client/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
@@ -315,10 +315,13 @@
               terminal.inputHandler.useApplicationKeypad false
           "?s": ignored "save mode values"
         "]": catchParameters /()(.*?)(\x07|\x1B\\)/, switchParameter 0, # OSC
-          0: (params) -> terminal.setTitleCallback? params.raw[1]
-          1: ignored "icon name"
-          2: (params) -> terminal.setTitleCallback? params.raw[1]
-          100: (params) -> terminal.eventHandler? params.raw[1]
+          0:   (params) -> terminal.setTitleCallback? params.raw[1]
+          # it handles the code:
+          # echo -e "\e]1;$DATA;$(date +%s%N)\e\\"
+          # if the data has semicolon in it, it splits the data, we should join them again.
+          1:   (params) -> terminal.eventHandler?     params.raw[1..-2].join ';' # trimming random timestamp
+          2:   (params) -> terminal.setTitleCallback? params.raw[1]
+          100: (params) -> terminal.eventHandler?     params.raw[1..].join ';' # deprecated

   return new WebTerm.ControlCodeReader(terminal, initCursorControlHandler(),
     new WebTerm.ControlCodeReader(terminal, initEscapeSequenceHandler(),


Fix error when the same tableName is synced on many stores.

--- app/assets/javascripts/action_cable_notifications/store.coffee
+++ app/assets/javascripts/action_cable_notifications/store.coffee
@@ -84,18 +84,25 @@
       console.warn "[syncToChannel]: Channel specified doesn't have an identifier"
       return false

-    if @collections[collection.name] < 0
+    if !@collections[collection.name]
       console.warn "[syncToChannel]: Collection does not exists in the store"
       return false

+    if collection.channel == channel
+      console.warn "[syncToChannel]: Collection is already been synced with channel '#{channelId}'"
+      return false
+
+    channel.collections = [] unless channel.collections
+
+    existingCollection = _.find(channel.collections, {tableName: collection.tableName})
+    if existingCollection
+      console.warn "[syncToChannel]: Table '#{collection.tableName}' is already being synced with channel '#{channelId}' in collection '#{existingCollection.name}'"
+
+      # Copies data from existing collection to the new collection
+      collection.data = _.cloneDeep(existingCollection.data)
+
     if @channels[channelId]
-      channelInfo = @channels[channelId]
-      if _.find(channelInfo.collections, {name: collection.name})
-        console.warn "[syncToChannel]: Collection '#{collection.name}' is already being synced with channel '#{channelId}'"
-        return false
-      else
-        collection.syncToChannel(channel)
-        channelInfo.collections.push collection
+      @channels[channelId].collections.push collection
     else
       # Initialize channelInfo
       @channels[channelId] =
@@ -114,7 +121,8 @@
       channel.disconnected = channelDisconnected(@channels[channelId]).bind(this)
       channel.isConnected = channelIsConnected(@channels[channelId]).bind(this)

-      # Assigns channel to collection and turns on Sync
-      collection.syncToChannel(channel)
+    # Assigns channel to collection and turns on Sync
+    collection.syncToChannel(channel)
+    channel.collections.push collection

     return true


Check before destroy, if task is already stored.

--- app/assets/javascripts/app/lib/app_post/task_manager.js.coffee
+++ app/assets/javascripts/app/lib/app_post/task_manager.js.coffee
@@ -356,6 +356,9 @@

     # destory task in backend
     delete @tasksToUpdate[ task.key ]
+
+    # if task isnt already stored on backend
+    return if !task.id
     App.Taskbar.destroy(task.id)
     return


Do not bubble change events to parent form elements

--- app/assets/javascripts/components/question_component.js.coffee
+++ app/assets/javascripts/components/question_component.js.coffee
@@ -27,6 +27,7 @@

   change: ->
     Ember.run.debounce(this, this._saveModel, @get('model'), 200)
+    false

   _saveModel: (model) ->
     model.save()


OX-138 direction of movement while hitting space depends now on last direction

--- app/assets/javascripts/libs/flycam2.coffee
+++ app/assets/javascripts/libs/flycam2.coffee
@@ -29,6 +29,7 @@
     @hasChanged = true
     @activePlane = PLANE_XY
     @rayThreshold = [10, 10, 10, 100]
+    @spaceDirection = 1

   #reset : ->
   #  @zoomSteps=[1,1,1]
@@ -95,20 +96,30 @@
   setDirection : (direction) ->
     @direction = direction

+  setSpaceDirection : ->
+    ind = @getIndices @activePlane
+    if @direction[ind[2]] <= 0
+      @spaceDirection = -1
+    else
+      @spaceDirection = 1
+
+  getSpaceDirection : ->
+    @spaceDirection
+
   move : (p) -> #move by whatever is stored in this vector
     if @activePlane == PLANE_XY
       # BAD consider the different resolution in z-direction
       @setGlobalPos([@globalPosition[0]+p[0], @globalPosition[1]+p[1], @globalPosition[2]+2*p[2]])
     else
       @setGlobalPos([@globalPosition[0]+p[0], @globalPosition[1]+p[1], @globalPosition[2]+p[2]])
-    # update the direction whenever the user moves
-    @lastDirection = @direction
-    @direction = [0.8 * @lastDirection[0] + 0.2 * p[0], 0.8 * @lastDirection[1] + 0.2 * p[1], 0.8 * @lastDirection[2] + 0.2 * p[2]]
-
+
   moveActivePlane : (p) ->
     ind = @getIndices @activePlane
     f = Math.pow(2, @integerZoomSteps[@activePlane])
-    @move([p[ind[0]]*f, p[ind[1]]*f, p[ind[2]]*f])
+    # change direction of the value connected to space, based on the last direction
+    delta = [p[ind[0]]*f, p[ind[1]]*f, p[ind[2]]*f]
+    delta[ind[2]] *= @spaceDirection
+    @move(delta)

   toString : ->
     position = @globalPosition
@@ -122,11 +133,17 @@
     @texturePosition[planeID]

   setGlobalPos : (position) ->
+    p = [position[0] - @globalPosition[0], position[1] - @globalPosition[1], position[2] - @globalPosition[2]]
     @globalPosition = position
     @hasChanged = true
+    # update the direction whenever the user moves
+    @lastDirection = @direction
+    @direction = [0.8 * @lastDirection[0] + 0.2 * p[0], 0.8 * @lastDirection[1] + 0.2 * p[1], 0.8 * @lastDirection[2] + 0.2 * p[2]]

   setActivePlane : (activePlane) ->
     @activePlane = activePlane
+    # setSpaceDirection when entering a new viewport
+    @setSpaceDirection()

   getActivePlane : ->
     @activePlane


close dropdown on resize/orientationchange

--- app/assets/javascripts/mtl/dropdown.coffee
+++ app/assets/javascripts/mtl/dropdown.coffee
@@ -15,4 +15,8 @@
     $el = $(this)
     updateAttrs($el).dropdown()

+closeAll = ->
+  setTimeout -> $('[data-mtl-dropdown]').dropdown('close')
+
 if Turbolinks? then $(document).on('turbolinks:load', init) else $(init)
+$(window).on 'resize orientationchange', closeAll


OX-427 scaleinfoclass fixed

OX-427 scaleinfoclass fixed

--- app/assets/javascripts/oxalis/model.coffee
+++ app/assets/javascripts/oxalis/model.coffee
@@ -2,7 +2,7 @@
 ./model/binary : Binary
 ./model/route : Route
 ./model/user : User
-./model/scaleinfo : ScaleInfoClass
+./model/scaleinfo : ScaleInfo
 ./model/flycam2d : Flycam2d
 ./model/flycam3d : Flycam3d
 ../libs/request : Request


When the tagger loses focus, close the candidates box.

--- app/assets/javascripts/views/tagger.coffee
+++ app/assets/javascripts/views/tagger.coffee
@@ -25,6 +25,9 @@
   hasText: (->
     @get('value.length') > 0
   ).property('value')
+
+  focusOut: ->
+    @set 'wantsCandidates', false

   keyDown: (e) ->
     pv = @get 'parentView'


@connect > @call

@connect > @call

--- lib/assets/javascripts/webrtc_rails/main.js.coffee
+++ lib/assets/javascripts/webrtc_rails/main.js.coffee
@@ -42,7 +42,7 @@
         =>
           unless @_callAnswerReceived
             if @_webRTCReconnecting
-              @connect(remoteUserIdentifier)
+              @call(remoteUserIdentifier)
             else
               @onWebRTCConnectFailed(WebRTC.TIMEOUT)
         5000
@@ -98,7 +98,7 @@
       @_sendValue('setMyToken')
       if @_wantWebRTCReconnecting
         @_wantWebRTCReconnecting = false
-        @connect(@_remoteUserIdentifier)
+        @call(@_remoteUserIdentifier)

     @_webSocket.onclose = (event) =>
       unless @_isWebSocketReconnectingStarted
@@ -288,7 +288,7 @@
     if @_isCaller
       @_webRTCReconnecting = true
       if @_webSocket.readyState == WebSocket.OPEN
-        @connect(@_remoteUserIdentifier)
+        @call(@_remoteUserIdentifier)
       else
         @_wantWebRTCReconnecting = true


made smart login even smarter

made smart login even smarter

--- addon/services/user-session.coffee
+++ addon/services/user-session.coffee
@@ -30,6 +30,7 @@
   rank: alias "employee.role"
   isLoggedIn: equal "state", "login-success"
   accountLoggedIn: ifPresent "account.permalink"
+  username: alias "session.username"
   namespace: alias "account.namespace"
   permalink: alias "account.permalink"
   host: alias "account.host"
@@ -69,7 +70,9 @@
       @set "simwmsUserSession", null
       @set "state", "login-failed"
     .finally => @
-  cookieLogin: ->
+  cookieLogin: (userToken) ->
+    if Ember.isPresent userToken
+      @set "simwmsUserSession", userToken
     @store.find "session", "cookie-session"
     .then (session) =>
       @set "session", session
@@ -78,13 +81,12 @@
       @set "simwmsUserSession", null
       @set "state", "login-failed"
     .finally => @
-  smartLogin: ({email, accountToken, password}) ->
-    @login({email, password})
-    .then (session) =>
-      if accountToken?
-        @accountLogin accountToken
-      else
-        session
+  smartLogin: ({email, accountToken, password, userToken}) ->
+    @cookieLogin(userToken)
+    .then =>
+      @login({email, password})
+    .then =>
+      @accountLogin accountToken
     .finally => @
   accountLogout: ->
     @set "simwmsAccountSession", null


social: deal with when not user, but no user is found

--- workers/social/lib/social/models/passwordrecovery.coffee
+++ workers/social/lib/social/models/passwordrecovery.coffee
@@ -130,6 +130,8 @@
     JUser.one {email}, (err, user)=>
       if err
         callback err
+      else unless user
+        callback { message: 'User not found'}
       else
         certificate = new JPasswordRecovery {
           email


social: remove commented out code

social: remove commented out code

--- workers/social/lib/social/models/passwordrecovery.coffee
+++ workers/social/lib/social/models/passwordrecovery.coffee
@@ -166,14 +166,7 @@
         return callback UNKNOWN_ERROR  if err or not user
         user.confirmEmail (err)->
           return callback UNKNOWN_ERROR  if err
-          certificate.update {$set: status: 'redeemed'}, (err) ->
-            return callback err if err
-
-            # Email.queue user.username, {
-            #   to       : certificate.email
-            #   subject  : Email.types.WELCOME
-            # }, {firstName: user.username}, (err)->
-            #   console.error err  if err
+          certificate.update {$set: status: 'redeemed'}, callback

   @invalidate =(query, callback)->
     query.status = 'active'


Also catch internal server errors

Also catch internal server errors

--- node_modules_koding/koding-payment/lib/main.coffee
+++ node_modules_koding/koding-payment/lib/main.coffee
@@ -87,7 +87,10 @@
             # See https://docs.recurly.com/api/basics/validation-errors for XML format
             errors = []
             for err in r.errors.error
-              errors.push Error(err['$']['symbol'], err['_'], err['$']['field'])
+              if err['$']
+                errors.push Error(err['$']['symbol'], err['_'], err['$']['field'])
+              else
+                errors.push Error(err, err)
             callback errors, {}
           else
             callback no, r, nextUrl


Add some function-group comments to live.coffee

--- app/assets/javascripts/live.coffee
+++ app/assets/javascripts/live.coffee
@@ -1,4 +1,4 @@
-
+# --- Time-string helper functions ---
 twoDigit = (num) ->
   if num > 9
     return "" + num
@@ -13,7 +13,9 @@
   t[0] = t[1] // 60
   t[1] = t[1]  % 60
   twoDigit(t[0]) + ":" + twoDigit(t[1]) + ":" + twoDigit(t[2])
-
+# ------------
+
+# --- Host-object. Contains all global values for the cue player ---
 @host =
   play: off
   ticker_start: new Date().getTime()
@@ -22,10 +24,11 @@
   ticker_over: 0
   current_cue: $()
   live_indicator: $()
+# ------------

+# --- Refresh/restore time displays in cues and the master clock view ---
 restore_t = (part_t) ->
   seconds = part_t.data('seconds-original')
-  part_t.data('seconds', seconds)
   part_t.html(seconds_string(seconds))
   seconds

@@ -36,7 +39,9 @@
   t_string = seconds_string(host.ticker_remaining)
   host.live_indicator.html(t_string)
   t_string
+# ------------

+# --- Cleanly set host.play variable. Updates the play/pause button ---
 set_play = (play, ppbutton = $('#live-started .controls .pp')) ->
   host.play = play
   if play
@@ -45,7 +50,9 @@
   else
     ppbutton.removeClass('play')
     ppbutton.html('play_circle_filled')
+# ------------

+# --- Go-To functions. Jump to next/prev/custom cue ---
 go_to = (part, refresh = true) ->
   host.current_cue.attr('id', '')
   restore host.current_cue # Restore previous cue
@@ -68,12 +75,15 @@
     go_to prev, refresh
   else
     go_to host.current_cue, refresh
+# ------------

+# --- Ticker functions (advances the clock every second) ---
 schedule_tick = (tick_f) ->
   actual = (new Date().getTime()) - host.ticker_start
+  # Next tick in 1 second +/- correction of any accumulated error
   setTimeout tick_f, 1000 - (actual - host.ticker_elapsed)

-tick = ->
+tick = -> # Process tick
   if host.play
     host.ticker_elapsed += 1000
     host.ticker_remaining-- if host.ticker_remaining # Ensure that it's not made negative
@@ -97,7 +107,10 @@
   set_play off
   actual = (new Date().getTime()) - host.ticker_start
   host.ticker_over = actual - host.ticker_elapsed
+# ------------

+# --- Load. Executed when the page is loaded (via Tubolinks or otherwise) ---
+# Defines all hooks for controls and cue buttons, and initializes the views
 load = ->
   $('#start-live-show').click ->
     # Enter live mode
@@ -144,3 +157,4 @@

 # Turbolinks
 $(document).on('turbolinks:load', load)
+# ------------


Fix wait of score to be in correct sequence

--- app/scripts/lib/LazerScript.coffee
+++ app/scripts/lib/LazerScript.coffee
@@ -175,7 +175,7 @@
     (sequence) =>
       @_verify(sequence)
       score = @level.finishStage()
-      @wait(15 * 2000)().then =>
+      @wait(15 * 2000)(sequence).then =>
         score.destroy()

   disableWeapons: ->


Removed checkSolved method

Removed checkSolved method

--- app/scripts/views/game_view.coffee
+++ app/scripts/views/game_view.coffee
@@ -31,4 +31,3 @@

     updateStats: =>
       @$('#moves_count').html @model.get('moves')
-      @checkSolved()


bug when adding bold within a link

bug when adding bold within a link

--- app/views/CNeditor/CNeditor.coffee
+++ app/views/CNeditor/CNeditor.coffee
@@ -1487,6 +1487,8 @@
                     span = document.createElement(startSegment.nodeName)
                     if startSegment.className != ''
                         span.className = startSegment.className
+                    if startSegment.nodeName == 'A'
+                        span.href = startSegment.href
                     span = frag1.appendChild(span)
                     span.appendChild(frag1.firstChild)
                     rg.setEndAfter(startSegment)


some cleanup

some cleanup

--- client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
+++ client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
@@ -80,64 +80,6 @@
       click       : (e)=>
         @emit "moreLinkClicked"

-  # pistachio:->
-
-  #   participants = @getData()
-  #   {hasMore, totalCount, group, separator} = @getOptions()
-
-  #   @createMoreLink()
-  #   l = if @getData().length < 4 then @getData().length else 3
-  #   tmpl = ""
-  #   for i in [0...l]
-  #     tmpl += "{{> @participant#{i}}}"
-  #     tmpl += separator if i isnt l-1
-
-  #   if totalCount > @getData().length
-  #     tmpl += " and {{> @more}}"
-
-  #   tmpl += " became a member."
-  #   # tmpl += "{{> @loader }}"
-
-
-# SLIGHTLY OLD
-
-# class NewMemberBucketData extends KDObject
-
-#   constructor:(options, @buckets)->
-
-#     @bongo_ = {}
-#     @meta   = @buckets[0].meta
-#     @bongo_.constructorName = "NewMemberBucketData"
-#     super
-
-# class NewMemberBucketView extends JView
-
-#   constructor: (options = {}, data)->
-
-#     options.cssClass = "new-member"
-
-#     super
-
-#     @group = new LinkGroup {}, @getData().buckets.map (bucket)-> bucket.anchor
-
-#   viewAppended:->
-
-#     super
-
-#     @timer = @utils.wait 800, =>
-#       @$('.fx').removeClass "out hidden"
-#       @timer = @utils.wait 400, =>
-#         @$('.fx').addClass "hidden"
-
-#   pistachio:->
-#     """
-#
-#
-#     {{> @group}}
-#     became member.
-#     """
-
-

 # OLD


mixpanel fix

mixpanel fix

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -330,11 +330,11 @@
     @populateActivity {},\
       KD.utils.getTimedOutCallbackOne
         name      : "populateActivity",
-        onSuccess : -> KD.logToMixpanel "refresh activity feed success"
+#        onSuccess : -> KD.logToMixpanel "refresh activity feed success"
         onTimeout : @recover.bind this

   recover:->
-    KD.logToMixpanel "activity feed render failed; recovering"
+    #KD.logToMixpanel "activity feed render failed; recovering"

     @isLoading = no


activity feed: fix bug where opening content display by itself causes reconnect

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -67,7 +67,7 @@
   loadView:->
     @getView().feedWrapper.ready (controller)=>
       @attachEvents @getView().feedWrapper.controller
-      @ready @bound "refresh"
+      @ready @bound "populateActivity"
     @emit 'ready'

   resetAll:->


remove scrolling from news

remove scrolling from news

--- apps/exhibition/client/tips.coffee
+++ apps/exhibition/client/tips.coffee
@@ -1,4 +1,4 @@
 module.exports.initTips = ->
   WH = $(window).height();
   SH = $('body')[0].scrollHeight;
-  $('html, body').stop().animate({scrollTop: SH - WH}, 20000, 'swing')
+  # $('html, body').stop().animate({scrollTop: SH - WH}, 20000, 'swing')


Remove reset button from bbcode editor.

Remove reset button from bbcode editor.

--- resources/assets/coffee/react/_components/bbcode-editor.coffee
+++ resources/assets/coffee/react/_components/bbcode-editor.coffee
@@ -39,8 +39,6 @@
           div className: 'bbcode-editor__buttons bbcode-editor__buttons--actions',
             div className: 'bbcode-editor__button bbcode-editor__button--cancel',
               @actionButton @_cancel, osu.trans('common.buttons.cancel')
-            div className: 'bbcode-editor__button bbcode-editor__button--hide-on-preview',
-              @actionButton @_reset, osu.trans('common.buttons.reset')
             div className: 'bbcode-editor__button bbcode-editor__button--hide-on-write',
               @renderPreviewHideButton()
             div className: 'bbcode-editor__button bbcode-editor__button--hide-on-preview',
@@ -85,12 +83,6 @@

     @body.value = @props.rawValue
     @sendOnChange(event: event, type: 'cancel')
-
-
-  _reset: (event) =>
-    @body.value = @props.rawValue
-    @sendOnChange(event: event, type: 'reset')
-    @body.focus()

   _save: (event) =>


Fix missing mode from links in beatmap top/own score

It was updated to the wrong props.

--- resources/assets/coffee/react/beatmapset-page/score-top.coffee
+++ resources/assets/coffee/react/beatmapset-page/score-top.coffee
@@ -39,7 +39,7 @@
           a
             className: "#{bn}__username js-usercard"
             'data-user-id': props.score.user.id
-            href: route 'users.show', user: props.score.user.id, mode: props.beatmap.playmode
+            href: route 'users.show', user: props.score.user.id, mode: props.score.mode
             props.score.user.username

           div
@@ -49,7 +49,7 @@

           a
             href: route 'rankings',
-              mode: props.beatmap.playmode
+              mode: props.score.mode
               country: props.score.user.country_code
               type: 'performance'
             el FlagCountry,


Check that input element exists before focusing

--- js/controls/textcontrol/textcontrol.coffee
+++ js/controls/textcontrol/textcontrol.coffee
@@ -154,8 +154,10 @@
   fieldType: 'text'

   setFocus: ->
-    @getInputElement().focus()
-    @setSelectionRange( @value.length, @value.length ) if @typeChr(@value) == 's'
+    _elem = @getInputElement()
+    if _elem?
+      _elem.focus()
+      @setSelectionRange( @value.length, @value.length ) if @typeChr(@value) == 's'

   drawMarkup: ->
     @base()


FinderController: lets create method creates a new instance each time when its called

--- client/finder/AppController.coffee
+++ client/finder/AppController.coffee
@@ -11,59 +11,69 @@
     super options, data

   create: (options = {}) ->
+
     options.useStorage        ?= yes
     options.addOrphansToRoot  ?= no
     options.addAppTitle       ?= yes
     options.bindMachineEvents ?= yes
     options.delegate          ?= this

-    @controller = new NFinderController options
-    finderView  = @controller.getView()
+    controller = new NFinderController options
+    finderView = controller.getView()

     finderView.addSubView @getAppTitleView()  if options.addAppTitle
-    finderView.addSubView @getUploader()
+    finderView.addSubView @createUploader controller

-    return @controller
+    return controller
+

   getAppTitleView: ->
-    return new KDCustomHTMLView
+
+    new KDCustomHTMLView
       cssClass : "app-header"
       partial  : "Ace Editor"

-  getUploader: ->
-    @uploaderPlaceholder = new KDView
-      domId       : "finder-dnduploader"
-      cssClass    : "hidden"

-    @uploaderPlaceholder.addSubView @uploader = new DNDUploader
+  createUploader: (controller)->
+
+    uploaderPlaceholder = new KDView
+      domId       : 'finder-dnduploader'
+      cssClass    : 'hidden'
+
+    uploaderPlaceholder.addSubView uploader = new DNDUploader
       hoverDetect : no
-      delegate    : this
+      delegate    : controller

-    {treeController} = @controller
-    treeController.on 'dragEnter', @bound "onDrag"
-    treeController.on 'dragOver' , @bound "onDrag"
+    onDrag = ->

-    @uploader
-      .on "dragleave", =>
-        @uploaderPlaceholder.hide()
+      unless controller.treeController.internalDragging
+        uploaderPlaceholder.show()
+        uploader.unsetClass 'hover'

-      .on "drop", =>
-        @uploaderPlaceholder.hide()
+    {treeController} = controller
+    treeController.on 'dragEnter', onDrag
+    treeController.on 'dragOver' , onDrag
+
+    finderView = controller.getView()
+    finderView.on 'dragenter', onDrag
+    finderView.on 'dragover' , onDrag
+
+    uploader
+      .on 'dragleave', ->
+        uploaderPlaceholder.hide()
+
+      .on 'drop', ->
+        uploaderPlaceholder.hide()

       .on 'uploadProgress', ({ file, percent }) ->
         filePath = "[#{file.machine.uid}]#{file.path}"
         treeController.nodes[filePath]?.showProgressView percent

-      .on "uploadComplete", ({ parentPath }) =>
-        @controller.expandFolders FSHelper.getPathHierarchy parentPath
+      .on 'uploadComplete', ({ parentPath }) ->
+        controller.expandFolders parentPath

-      .on "cancel", =>
-        @uploader.setPath()
-        @uploaderPlaceholder.hide()
+      .on 'cancel', ->
+        uploader.setPath()
+        uploaderPlaceholder.hide()

-    return @uploaderPlaceholder
-
-  onDrag: ->
-    return  if @controller.treeController.internalDragging
-    @uploaderPlaceholder.show()
-    @uploader.unsetClass "hover"
+    return uploaderPlaceholder


Removed github request permissions scope (this was requesting full read/write on user info); No scope means read-only.

--- client/views/login/services.coffee
+++ client/views/login/services.coffee
@@ -36,9 +36,6 @@

 		serviceConfig = {}

-		if this.service is 'github'
-			serviceConfig.requestPermissions = ['user']
-
 		Meteor[loginWithService] serviceConfig, (error) ->
 			if error?.error is 'github-no-public-email'
 				alert "You don't have any email as public email in your GitHub account"


chagne activities according to selectedTicket

--- coffee/services/dataAdapter.coffee
+++ coffee/services/dataAdapter.coffee
@@ -112,6 +112,7 @@
       set: (n) ->
         if @_selectedTicket isnt n
           @_selectedTicket = n
+          @activities = @_data[n.url].activities if n and @_data[n.url]
           @fireEvent(@SELECTED_TICKET_CHANGED, @, n)

     # selected activity.


REDUCED TO 100!

REDUCED TO 100!

--- app/assets/javascripts/collections/clouds/users.js.coffee
+++ app/assets/javascripts/collections/clouds/users.js.coffee
@@ -2,7 +2,7 @@

   model: Cloudsdale.Models.User
   url: ->
-    if @topic.get('member_count') > 250
+    if @topic.get('member_count') > 100
       return "/v1/#{@topic.type}s/#{@topic.id}/users/online.json"
     else
       return "/v1/#{@topic.type}s/#{@topic.id}/users.json"


Patch handlers not reverted on cancel edit in Leaflet.Draw

--- app/assets/javascripts/leaflet/reactive_measure.js.coffee
+++ app/assets/javascripts/leaflet/reactive_measure.js.coffee
@@ -297,8 +297,18 @@
 #Patch Leaflet.Draw
 L.EditToolbar.Edit.include
   __removeHooks: L.EditToolbar.Edit::removeHooks
-
+  __revertLayer: L.EditToolbar.Edit::_revertLayer
+
+  # Patch missing event
   removeHooks: ->
     @__removeHooks.apply @, arguments
     if @_map
-      @_map.off 'draw:editvertex', @_updateTooltip, @+      @_map.off 'draw:editvertex', @_updateTooltip, @
+
+  # Patch handlers not reverted on cancel edit. See https://github.com/Leaflet/Leaflet.draw/issues/532
+  _revertLayer: (layer) ->
+    id = L.Util.stamp layer
+    @__revertLayer.apply @, arguments
+    layer.editing.latlngs = this._uneditedLayerProps[id].latlngs
+    layer.editing._poly._latlngs = this._uneditedLayerProps[id].latlngs
+    layer.editing._verticesHandlers[0]._latlngs = this._uneditedLayerProps[id].latlngs


video: fix channel video session fetch process

--- client/app/lib/videocollaboration/services/opentok.coffee
+++ client/app/lib/videocollaboration/services/opentok.coffee
@@ -86,18 +86,27 @@

     { id } = channel

-    return callback @sessions[id]  if @sessions[id]
+    kallback = (sessionId) =>
+      { apiKey } = globals.config.tokbox
+      session = @sessions[channel.id] = OT.initSession apiKey, sessionId
+      session.sessionId = sessionId
+      return callback session

-    helper.generateSession channel, (session) =>
+    # return cached OT.session if that session exists.
+    if session = @sessions[id]
+      return callback session

-      { sessionId } = session
-      { apiKey } = globals.config.tokbox
+    # initate a OT.Session with channel's sessionId if it's present.
+    else if sessionId = helper.getChannelSessionId channel
+      kallback sessionId

-      session = @sessions[channel.id] = OT.initSession apiKey, sessionId
-
-      session.sessionId = sessionId
-
-      callback session
+    # first generate a sessionId, then assign that sessionId to channel, and
+    # then initiate a new OT.Session.
+    else
+      helper.generateSession channel, (result) =>
+        { sessionId } = result
+        helper.setChannelVideoSession channel, sessionId, (err) =>
+          kallback sessionId

   ###*


TabHandler: Change "handleCloseAction"s method name

--- client/ide/lib/views/tabview/ideapplicationtabview.coffee
+++ client/ide/lib/views/tabview/ideapplicationtabview.coffee
@@ -125,7 +125,7 @@
     @splitRegions = null

-  handleCloseIcon: (pane) ->
+  handleCloseAction: (pane) ->

     @emit 'PaneRemovedByUserAction', pane


Change the function of prev button and next button in my course page secend time

--- common/lib/xmodule/xmodule/js/src/sequence/display.coffee
+++ common/lib/xmodule/xmodule/js/src/sequence/display.coffee
@@ -6,7 +6,7 @@
     @id = @el.data('id')
     @modx_url = @el.data('course_modx_root')
     #20151026b Analysis the url and get the parameter
-    @url_split_array = @el[0].baseURI.split('/')
+    @url_split_array = window.location.href.split('/')
     @prev_button_para = @url_split_array[@url_split_array.length-1]
     @initProgress()
     @bind()


guestcleanerworker: use `console.log`

guestcleanerworker: use `console.log`

--- workers/guestcleaner/lib/guestcleaner/guestcleanerwoker.coffee
+++ workers/guestcleaner/lib/guestcleaner/guestcleanerwoker.coffee
@@ -69,7 +69,7 @@
         accounts.forEach (account) =>
           queue = [
             ->
-              console.debug "Removing " + account.profile.nickname
+              console.log "Removing " + account.profile.nickname
               queue.next()
             ->
               # delete user cookie
@@ -120,7 +120,7 @@
                 if err then console.error err
                 queue.next()
             ->
-              console.debug "Removed " + account.profile.nickname
+              console.log "Removed " + account.profile.nickname
               queue.next()
           ]
           daisy queue


Add support for programmatically changing the account in the account-switcher component

--- src/scripts/models/accountSwitcherViewModel.coffee
+++ src/scripts/models/accountSwitcherViewModel.coffee
@@ -117,13 +117,16 @@
       @currentAccountAlias(newValue.alias)

     _setCurrentAccount = () =>
+      alias = @currentAccountAlias();
       possibleAccounts = @flatOrgList().filter (acct) =>
-        acct.alias.toLowerCase() == @currentAccountAlias().toLowerCase()
+        acct.alias.toLowerCase() == alias.toLowerCase()

       if possibleAccounts.length == 1
         @impersonatedOrg possibleAccounts[0]
-      else
-        throw 'There is more than one account with the same alias, this should not be possible check the data please.'
+      else if possibleAccounts.length > 1
+        throw "There is more than one account with the alias '#{alias}'. This is not allowed please check the list of accounts."
+      else
+        throw "There is no account with the alias '#{alias}'."

     if !@loading()
@@ -137,6 +140,10 @@
     @impersonateOrg = (acct)  =>
       @impersonatedOrg(acct)
       @toggleHandler()
+
+
+    @currentAccountAlias.subscribe (newAlias) =>
+      _setCurrentAccount()

     ###############################


BONNIE-882 Adds type check

BONNIE-882 Adds type check

--- Src/coffeescript/cql-execution/src/datatypes/uncertainty.coffee
+++ Src/coffeescript/cql-execution/src/datatypes/uncertainty.coffee
@@ -5,15 +5,24 @@
     if obj instanceof Uncertainty then obj else new Uncertainty(obj)

   constructor: (@low = null, @high) ->
-    gt = (a, b) -> if typeof a.after is 'function' then a.after b else a > b
+    gt = (a, b) ->
+      if typeof a != typeof b
+        return null
+      if typeof a.after is 'function' then a.after b else a > b
     if typeof @high is 'undefined' then @high = @low
     if @low? and @high? and gt(@low, @high) then [@low, @high] = [@high, @low]

   isPoint: () ->
     # Note: Can't use normal equality, as that fails for Javascript dates
     # TODO: Fix after we don't need to support Javascript date uncertainties anymore
-    lte = (a, b) -> if typeof a.sameOrBefore is 'function' then a.sameOrBefore b else a <= b
-    gte = (a, b) -> if typeof a.sameOrBefore is 'function' then a.sameOrAfter b else a >= b
+    lte = (a, b) ->
+      if typeof a != typeof b
+        return null
+      if typeof a.sameOrBefore is 'function' then a.sameOrBefore b else a <= b
+    gte = (a, b) ->
+      if typeof a != typeof b
+        return null
+      if typeof a.sameOrBefore is 'function' then a.sameOrAfter b else a >= b
     @low? and @high? and lte(@low, @high) and gte(@low, @high)

   equals: (other) ->
@@ -21,7 +30,9 @@
     ThreeValuedLogic.not ThreeValuedLogic.or(@lessThan(other), @greaterThan(other))

   lessThan: (other) ->
-    lt = (a, b) -> if typeof a.before is 'function' then a.before b else a < b
+    lt = (a, b) ->
+      if typeof a != typeof b then return null
+      if typeof a.before is 'function' then a.before b else a < b
     other = Uncertainty.from other
     bestCase = not @low? or not other.high? or lt(@low, other.high)
     worstCase = @high? and other.low? and lt(@high, other.low)


Completed +finishadd (temporary command until IPN callbacks are utilized)

--- bot.iced
+++ bot.iced
@@ -119,13 +119,20 @@
 					console.error err
 					bot.sendMessage chatterID, "The database ran into an error"
 		when "+finishadd"
-			for payment in pendingPayments
-				if payment.steamID is chatterID
+			await checkIfRegistered chatterID, defer(registered)
+			if registered is undefined
+				return bot.sendMessage chatterID, "The database ran into an error"
+			unless registered
+				return bot.sendMessage chatterID, "You must register before you can add funds. Do this by sending '+register'."
+
+			await Users_collection.findOne {id: chatterID}, defer(err, user)
+			for transaction in user.history
+				if transaction.status is "pending"
 					break
-			unless payment?
+			unless transaction?
 				return bot.sendMessage chatterID, "You have no +add requests pending"

-			requester "https://moolah.ch/api/pay/check/#{payment.tx}", (error, response, body) ->
+			requester "https://moolah.ch/api/pay/check/#{transaction.tx}", (error, response, body) ->
 				unless !error and response.statusCode is 200
 					console.error "#{Date.now().toString()} - #{error}, #{response}, #{body}"
 					return bot.sendMessage chatterID, "Moolah ran into an error processing your request"
@@ -134,7 +141,30 @@
 				catch e
 					return bot.sendMessage chatterID, "Moolah returned invalid JSON"
 				if body.status is "complete"
+					# Payment was successful
+					# Update the funds in the DB
+					for transaction, transactionIndex in user.history
+						if transaction.status is "pending"
+							transaction.status = "complete"
+							user.history[transactionIndex] = transaction
+					await Users_collection.update {id: chatterID}, {$set: {history: user.history}, $inc: {funds: transaction.amount}}, {w:1}, defer(err)
+					if err
+						console.error err
+						return bot.sendMessage chatterID, "The database ran into an error"
+
 					bot.sendMessage chatterID, "Payment processed successfully; You can now tip with it"
+				else if body.status is "cancelled"
+					# User didn't send the funds within 30 minutes
+					# Cancel the request in the DB
+					for transaction, transactionIndex in user.history
+						if transaction.status is "pending"
+							user.history.splice transactionIndex, 1
+					await Users_collection.update {id: chatterID}, {$set:{history: user.history}}, {w:1}, defer(err)
+					if err
+						console.error err
+						return bot.sendMessage chatterID, "The database ran into an error"
+					# Notify the user
+					bot.sendMessage chatterID, "Balance was not paid within 30 minutes so the transaction was cancelled"
 				else
 					bot.sendMessage chatterID, "Your payment is currently '#{body.status}'"


open second dialog for crop

open second dialog for crop

--- app/assets/javascripts/uploadcare/widget/tabs/preview-tab-multiple.js.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/preview-tab-multiple.js.coffee
@@ -118,23 +118,19 @@
       @__updateFileInfo(fileEl, info)

       if info.isImage
-        preview = @__find('file-preview-wrap', fileEl).html($('<img>')
+        @__find('file-preview-wrap', fileEl).html($('<img>')
           .attr('src', "#{info.cdnUrl}-/scale_crop/90x90/center/")
           .css(
             width: 'auto'
             height: 45
           )
-        )
-        if @settings.crop
-          preview
-            .addClass('uploadcare__zoomable')
-            .on 'click', =>
-              s = $.extend({}, @settings)
-              s.multiple = false
-              s.tabs = 'preview'
-              uploadcare.openDialog(file, 'preview', s).done (newFile) =>
-                @dialogApi.fileColl.replace(file, newFile)
-                uploadcare.openDialog(@dialogApi.fileColl.get(), 'preview', @settings)
+        ).on 'click', =>
+          s = $.extend({}, @settings, {
+            multiple: false
+            tabs: 'preview'
+          })
+          uploadcare.openDialog(file, 'preview', s, true).done (newFile) =>
+            @dialogApi.fileColl.replace(file, newFile)

     __fileFailed: (file, error, info) =>
       fileEl = @__fileToEl(file)


Add beforeunload event

Add beforeunload event

--- backend/app/assets/javascripts/comable/admin/application.coffee
+++ backend/app/assets/javascripts/comable/admin/application.coffee
@@ -11,6 +11,20 @@
   # ---
   # functions
   # ---
+
+  initialize_beforeunload_event = ->
+    $form = $('form[method!="get"]')
+    $form.change(->
+      $(window).on('beforeunload', (event) ->
+        # TODO: Install 'i18n-js' gem
+        confirmation_message = 'The changes not saved. Are you sure you want to move from this page?'
+        (event || window.event).returnValue = confirmation_message # for Gecko and Trident
+        confirmation_message                                       # for Gecko and WebKit
+      )
+    )
+    $form.submit(->
+      $(window).off('beforeunload')
+    )

   initialize_vertical_navigation = ->
     $vnavigation = $('.vnavigation').find('ul')
@@ -76,6 +90,7 @@
   # main
   # ---

+  initialize_beforeunload_event()
   initialize_vertical_navigation()

   if $('#comable-affix').length != 0


Set base_url in the iffe instead of on window

--- backend/app/assets/javascripts/spree/backend/taxonomy.js.coffee
+++ backend/app/assets/javascripts/spree/backend/taxonomy.js.coffee
@@ -1,4 +1,5 @@
 last_rollback = null
+base_url = null

 handle_ajax_error = (XMLHttpRequest, textStatus, errorThrown) ->
   $.jstree.rollback(last_rollback)
@@ -70,11 +71,10 @@
     $.jstree.rollback(last_rollback)
     last_rollback = null

-root = exports ? this
-root.setup_taxonomy_tree = (taxonomy_id) ->
+@setup_taxonomy_tree = (taxonomy_id) ->
   if taxonomy_id != undefined
     # this is defined within admin/taxonomies/edit
-    root.base_url = Spree.routes.taxonomy_taxons_path
+    base_url = Spree.routes.taxonomy_taxons_path

     Spree.ajax
       url: base_url.replace("/taxons", "/jstree"),


Client: Activity:Updated messageLikeSummary component to check actorsCount

--- client/activity/lib/components/common/messagelikesummary.coffee
+++ client/activity/lib/components/common/messagelikesummary.coffee
@@ -6,8 +6,15 @@
 module.exports = class MessageLikeSummary extends React.Component

   render: ->
-

-      {summarizeLikes @props.message}
+
+    { message, className } = @props
+
+    actorsCount = message.getIn ['interactions', 'like', 'actorsCount']
+
+    return null  unless actorsCount
+
+

+      {summarizeLikes message}


Track team join form actions

Track team join form actions

--- client/landing/site.landing/coffee/team/tabs/teamjointab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamjointab.coffee
@@ -99,19 +99,25 @@

     { username } = formData
     success      = =>
+      track 'submitted join a team form'
       KD.utils.storeNewTeamData 'join', formData
       KD.utils.joinTeam
         error : ({responseText}) =>
           if /TwoFactor/.test responseText
+            track 'needed two factor authentication'
             @form.showTwoFactor()
           else
+            track 'failed to join a team'
             new KDNotificationView title : responseText

     if @alreadyMember then success()
     else
       KD.utils.usernameCheck username,
-        success : success
+        success : ->
+          track 'entered a valid username'
+          success()
         error   : ({responseJSON}) =>
+          track 'entered an invalid username'

           unless responseJSON
             return new KDNotificationView
@@ -123,3 +129,10 @@
           else                    "Sorry, there is a problem with \"#{username}\"!"

           new KDNotificationView title : msg
+
+
+track = (action) ->
+
+  category = 'TeamJoin'
+  label    = 'JoinTab'
+  KD.utils.analytics.track action, { category, label }


Social: fix PR comments

Social: fix PR comments

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -843,9 +843,10 @@
         # load test, we dont want test user's posts to be seen by others
         if username.indexOf("ktu-") is 0
           account.markUserAsExemptUnsafe client, yes, (err, res)->
+            return callback err if err?
             queue.next()
         else
-            queue.next()
+          queue.next()
       ->

         callback error, {account, recoveryToken, newToken}


Social: update session with group name

Social: update session with group name

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -883,7 +883,7 @@
       callback null

   @changeUsernameByAccount = (options, callback)->
-    { account, username, clientId, isRegistration } = options
+    { account, username, clientId, isRegistration, groupName } = options
     account.changeUsername { username, isRegistration }, (err) =>
       return callback err   if err?
       return callback null  unless clientId?
@@ -893,7 +893,7 @@
           return callback createKodingError "Could not update your session"

         if session?
-          session.update { $set: { clientId: newToken, username }}, (err) ->
+          session.update { $set: { clientId: newToken, username, groupName }}, (err) ->
             return callback err  if err?
             callback null, newToken
         else
@@ -1027,7 +1027,14 @@

       =>
         if aNewRegister and username?
-          options = { account, username, clientId, isRegistration: yes }
+          options = {
+            account        : account
+            username       : username
+            clientId       : clientId
+            isRegistration : yes
+            groupName      : client.context.group
+          }
+
           @changeUsernameByAccount options, (err, newToken_) =>
             return callback err  if err
             newToken = newToken_
@@ -1047,9 +1054,10 @@
           queue.next()

       ->
+        # why do we need to create a separate client?
         _client =
           connection : delegate : account
-          context    : group    : 'koding'
+          context    : group    : client.context.group

         ComputeProvider.createGroupStack _client, (err)->
           if err?


Groups: Guest conversion is not interrupted for the cases where the joinee group quota is exceeded

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -691,11 +691,12 @@
     newToken  = null
     invite    = null
     user      = null
+    quotaExceedErr = null

     queue = [
       =>
         @validateAll userFormData, (err) =>
-          return callback err  if err?
+          return callback err  if err
           queue.next()
       =>
         # password is autogenerated before here
@@ -708,7 +709,7 @@
         if username? and email?
           options = { account, oldUsername, email, username }
           @changeEmailByUsername options, (err) =>
-            return callback err  if err?
+            return callback err  if err
             queue.next()
         else process.nextTick -> queue.next()
       ->
@@ -724,7 +725,7 @@
         if username?
           options = { account, username, clientId, isRegistration: yes }
           @changeUsernameByAccount options, (err, newToken_) =>
-            return callback err  if err?
+            return callback err  if err
             newToken = newToken_
             queue.next()
         else process.nextTick -> queue.next()
@@ -735,11 +736,15 @@
           queue.next()
       =>
         @addToGroups account, invite, user.email, (err) =>
-          return callback err  if err?
+          # for the cases where the quota exceeded, we must progress on registering user
+          # and then show the quota exceeded message to new user
+          if err
+            if err.code is 999 then quotaExceedErr = err
+            else return callback err
           queue.next()
       =>
         @removeFromGuestsGroup account, (err) =>
-          return callback err  if err?
+          return callback err  if err
           queue.next()
       ->
         accountModifier = $set:
@@ -765,7 +770,7 @@
         JAccount.emit "AccountRegistered", account, referrer
         queue.next()
       =>
-        callback null, newToken
+        callback quotaExceedErr, newToken
         queue.next()
     ]


SocialWorker: unify comments

SocialWorker: unify comments

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1026,7 +1026,7 @@
     if typeof username isnt 'string'
       return callback new KodingError 'Username must be a string!'

-    # lower casing username is necessary to prevent conflicts with JName documents
+    # lower casing username is necessary to prevent conflicts with other JModels
     username       = username.toLowerCase()
     email          = emailsanitize email
     sanitizedEmail = emailsanitize email, { excludeDots: yes, excludePlus: yes }


Remove sort by creator

Remove sort by creator

--- resources/assets/coffee/react/beatmaps/search-sort.coffee
+++ resources/assets/coffee/react/beatmaps/search-sort.coffee
@@ -41,7 +41,6 @@
     options = [
       {id: 'title', name: 'title'},
       {id: 'artist', name: 'artist'},
-      {id: 'creator', name: 'creator'},
       {id: 'difficulty', name: 'difficulty'},
       {id: 'ranked', name: 'ranked'},
       {id: 'rating', name: 'rating'},


tests for table content

tests for table content

[#95231438]

--- spec/javascripts/acceptance/projects_index_page.js.coffee
+++ spec/javascripts/acceptance/projects_index_page.js.coffee
@@ -100,3 +100,27 @@
   click('.navbar-left a:last')
   andThen ->
     equal(currentPath(), 'admin.users')
+
+test "checks correct values for project", ->
+  findFirstProjectRejected = ->
+    find(".table-bordered tr:eq(1) td:eq(1)").text()
+
+  findFirstProjectPending = ->
+    find(".table-bordered tr:eq(1) td:eq(2)").text()
+
+  findFirstProjectAccepted = ->
+    find(".table-bordered tr:eq(1) td:eq(3)").text()
+
+  findFirstProjectTotal = ->
+    find(".table-bordered tr:eq(1) td:eq(4)").text()
+
+  findFirstProjectStatus = ->
+    find(".table-bordered tr:eq(1) td:eq(5)").text()
+
+  visit '/projects'
+  andThen ->
+    equal(findFirstProjectRejected(), '2')
+    equal(findFirstProjectPending(), '3')
+    equal(findFirstProjectAccepted(), '1')
+    equal(findFirstProjectTotal(), '6')
+    equal(findFirstProjectStatus(), 'good')


Extendend Delivery.js for auto-update of fields through AJAX calls on Selector.js selection.

--- app/assets/javascripts/backend/deliveries.js.coffee
+++ app/assets/javascripts/backend/deliveries.js.coffee
@@ -44,7 +44,8 @@
             unit.html(data.unit_name)
           else
             unit.html('#')
-          item.find(".item-variant-name").html(data.variant.name)
+          if data.variant
+            item.find(".item-variant-name").html(data.variant.name)
           pop = item.find(".item-population")
           total = item.find(".item-population-total")
           if data.population
@@ -64,6 +65,35 @@
             pop.attr('step', 1)
           else if data.population_counting is 'decimal'
             pop.removeAttr('step')
+
+          item.find('*[data-when-item]').each ->
+            if typeof data[$(this).data('when-item')] != "undefined"
+              if typeof $(this).data("when-set-value") != "undefined"
+                if $(this).data("when-set-value") == "RECORD_VALUE"
+                  newVal = data[$(this).data("when-item")]
+                  newVal = newVal.toLowerCase() if typeof newVal == "string"
+                  $(this).val(newVal)
+                else
+                  $(this).val($(this).data("when-set-value"))
+
+              if typeof $(this).data("when-prop-value") != "undefined"
+                $(this).prop($(this).data("when-prop-value"), true)
+
+              if typeof $(this).data("when-display-value") != "undefined"
+                if $(this).data("when-display-value") == true
+                  $(this).show()
+                else
+                  $(this).hide()
+
+            else
+              if typeof $(this).data("when-prop-value") != "undefined"
+                $(this).prop($(this).data("when-prop-value"), false)
+
+              if typeof $(this).data("when-display-value") != "undefined"
+                if $(this).data("when-display-value") == true
+                  $(this).hide()
+                else
+                  $(this).show()

           # shape = item.find(options.population_field or ".item-shape")
           # if data.shape


fixing channel subscription callbacks

fixing channel subscription callbacks

--- app/assets/javascripts/pirates/socketHandler.coffee
+++ app/assets/javascripts/pirates/socketHandler.coffee
@@ -57,7 +57,7 @@
     @packetQueue.push packet

   onSubscribe = (event) =>
-    console.log "Subscribed to channel '#{@channelName}'"
+    console.log "Subscribed to simulation channel"

   onSubscribeFail = (event) =>
     Console.logError "Fehler bei der Kommunikation, bitte lade die Seite neu!"
@@ -65,7 +65,7 @@

   @initialize = () ->
-    @channel = webSocket.subscribe 'simulation', @onSubscribe, @onSubscribeFail
+    @channel = webSocket.subscribe 'simulation', onSubscribe, onSubscribeFail
     @channel.bind 'step', addToQueue
     @lifeTime = 0
     @packetQueue = []


trys to fix 1041

trys to fix 1041

--- app/assets/javascripts/pirates/socketHandler.coffee
+++ app/assets/javascripts/pirates/socketHandler.coffee
@@ -12,7 +12,7 @@

 # establish connection
 #window.webSocket = new WebSocketRails 'localhost:3000/websocket'
-window.webSocket = new WebSocketRails 'www-ps.informatik.uni-kiel.de/web-piraten/websocket'
+window.webSocket = new WebSocketRails('www-ps.informatik.uni-kiel.de/web-piraten/websocket', false)
 webSocket.on_open = onOpen
 webSocket.on_close = onClose
 webSocket.on_error = onError


Remove debug console statements.

Remove debug console statements.

--- app/assets/javascripts/reservation_popups.js.coffee
+++ app/assets/javascripts/reservation_popups.js.coffee
@@ -121,11 +121,8 @@
     "#{result.join("-").replace("Z","")}-00:00"
   position_popup: (x, y)->
     @popup.show()
-    console.log("Yo")
-    console.log("X: #{x} Y: #{y} Window Scroll: #{window.scrollY}")
     if(y < window.scrollY)
       y = window.scrollY+10
-      console.log(y)
     # Change behavior for phones
     @popup.attr("style","")
     if $("body").width() <= 480
@@ -138,11 +135,8 @@
       @popup.css("margin-left",-1)
       @popup.css("margin-top",-1)
     else
-      console.log("Popup X: #{@popup.offset().left} Popup Y: #{@popup.offset().top}")
-      console.log("X: #{x} Y: #{y}")
       @popup.css("top", y)
       @popup.css("left", x+10)
-      console.log("Popup X: #{@popup.offset().left} Popup Y: #{@popup.offset().top}")
     return
   populate_reservation_popup: (room_element, start_time, end_time, event) ->
     $(".popup").hide()


changed back to hover

changed back to hover

--- public/scripts/backbone-schedule/views/scheduler/calendar-max-section.coffee
+++ public/scripts/backbone-schedule/views/scheduler/calendar-max-section.coffee
@@ -59,7 +59,7 @@
 							html: true
 							content: popover_content
 							placement: 'bottom'
-							trigger: 'click'
+							trigger: 'hover'
 							animation: false


Add keymods for mania

Add keymods for mania

--- resources/assets/coffee/react/beatmapset-page/scoreboard.coffee
+++ resources/assets/coffee/react/beatmapset-page/scoreboard.coffee
@@ -44,7 +44,7 @@
     modsClassName += ' beatmapset-scoreboard__mods--initial' if _.isEmpty @props.enabledMods

     mods = if @props.playmode == 'mania'
-      ['NM', 'EZ', 'NF', 'HT', 'HR', 'SD', 'PF', 'DT', 'NC', 'FI', 'HD', 'FL']
+      ['NM', '4K', '5K', '6K', '7K', '8K', '9K', 'EZ', 'NF', 'HT', 'HR', 'SD', 'PF', 'DT', 'NC', 'FI', 'HD', 'FL']
     else
       ['NM', 'EZ', 'NF', 'HT', 'HR', 'SD', 'PF', 'DT', 'NC', 'HD', 'FL']


Fixed resize

Fixed resize

--- resources/assets/coffee/react/landing-page/landing-graph.coffee
+++ resources/assets/coffee/react/landing-page/landing-graph.coffee
@@ -45,6 +45,8 @@
   ).y0(height).y1((d) ->
     yScale d.users_osu
   )
+# Define the graph
+svg = null

 modelStats = (data) ->
   # Define svg canvas


Fix re-addition of scoreTypes to Campaign denormalization

--- app/views/editor/campaign/CampaignEditorView.coffee
+++ app/views/editor/campaign/CampaignEditorView.coffee
@@ -109,7 +109,6 @@
       continue if not campaignLevel
       $.extend campaignLevel, _.omit(level.attributes, '_id')
       campaignLevel.rewards = @formatRewards level
-      delete campaignLevel[key] for key in ['tasks', 'requiredCode', 'suspectCode', 'allowedHeroes', 'scoreTypes']  # Migrate these out of the campaigns 2016-10-12
       # Save campaign to level if it's a main 'hero' campaign so HeroVictoryModal knows where to return.
       # (Not if it's a defaulted, typeless campaign like game-dev-hoc or auditions.)
       campaignLevel.campaign = @campaign.get 'slug' if @campaign.get('type') is 'hero'
@@ -316,7 +315,7 @@
         LevelsNode.levels[level.get('original')] = level
         @settings.supermodel.registerModel level
       mapped = ({label: r.get('name'), value: r.get('original')} for r in collection.models)
-
+
       # Sort the results. Prioritize names that start with the search term, then contain the search term.
       lowerPriority = _.clone(mapped)
       lowerTerm = req.term.toLowerCase()


Save folder expansion state to local storage

--- services/web/public/coffee/app/ide/file-tree/controllers/FileTreeFolderController.coffee
+++ services/web/public/coffee/app/ide/file-tree/controllers/FileTreeFolderController.coffee
@@ -2,10 +2,11 @@
 	"base"
 ], (App) ->
 	App.controller "FileTreeFolderController", ["$scope", "ide", "$modal", ($scope, ide, $modal) ->
-		$scope.expanded = false
+		$scope.expanded = $.localStorage("folder.#{$scope.entity.id}.expanded") or false

 		$scope.toggleExpanded = () ->
 			$scope.expanded = !$scope.expanded
+			$.localStorage("folder.#{$scope.entity.id}.expanded", $scope.expanded)

 		$scope.onDrop = (events, ui) ->
 			source = $(ui.draggable).scope().entity


Workspace: Added panel, workspace, file params to savedCallback.

--- client/landing/app/CommonViews/workspace/panes/collaborativeeditorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativeeditorpane.coffee
@@ -53,7 +53,7 @@
     else
       @ref.child("WaitingSaveRequest").set yes

-    @getOptions().saveCallback? @firepad.getText()
+    @getOptions().saveCallback? @panel, @workspace, file, @firepad.getText()

   getValue: ->
     return @codeMirrorEditor.getValue()


Minor cleanup in BaseFactWheelView

Minor cleanup in BaseFactWheelView

--- core/app/backbone/views/base_fact_wheel_view.coffee
+++ core/app/backbone/views/base_fact_wheel_view.coffee
@@ -155,8 +155,8 @@

   bindTooltips: ->
     if @options.respondsToMouse
-      $("div.tooltip", @$el).remove()
-      @$el.find(".authority").tooltip title: "This number represents the amount of thinking " + "spent by people on this Factlink"
+      @$("div.tooltip").remove()
+      @$(".authority").tooltip title: "This number represents the amount of thinking " + "spent by people on this Factlink"

       # Create tooltips for each opinionType (believe, disbelieve etc)
       for key, opinionType of @model.get('opinion_types')


add render event to dropdown

add render event to dropdown

--- modules/dropdown/main/index.coffee
+++ modules/dropdown/main/index.coffee
@@ -131,13 +131,12 @@
   dropdownContent: (dropdownContent) ->
     if arguments.length
       setupDropdown = dropdownContentToSetupDropdown dropdownContent
-      @_.dropdownContent = dropdownContent
       @_ = hx.shallowMerge @_, {
         setupDropdown,
         dropdownContent
       }
-      debugger
       @render()
+      this
     else
       @_.dropdownContent

@@ -157,6 +156,8 @@

   render: ->
     @_.setupDropdown @_.dropdown.node()
+    @emit 'render'
+    this

   show: (cb) ->
     _ = @_


Debouncing PaginationCollection._reset, fixes #1051

--- app/assets/javascripts/admin/models/pagination_collection.coffee
+++ app/assets/javascripts/admin/models/pagination_collection.coffee
@@ -39,6 +39,9 @@
     @listenTo(@fullCollection, "add", @_passthroughEvent("add"))
     @listenTo(@fullCollection, "remove", @_passthroughEvent("remove"))
     @listenTo(@fullCollection, "sync", @_passthroughEvent("sync"))
+
+    @_reset = _.debounce(@_reset, 50)
+    return

   add : ->
@@ -158,6 +161,7 @@

   _reset : ->
+    console.log("reset")
     @_resetModels()
     @models = @currentModels.slice(
       @state.currentPage * @state.pageSize,


Fix signup modal for anonymous users on unit map (#103)

--- app/views/core/CreateAccountModal/CreateAccountModal.ozar.coffee
+++ app/views/core/CreateAccountModal/CreateAccountModal.ozar.coffee
@@ -94,9 +94,7 @@
       when 'teacher'
         startSignupTracking()
         @signupState.set({ path: 'teacher', screen: if @euConfirmationRequiredInCountry() then 'eu-confirmation' else 'basic-info' })
-      else
-        if /^\/play/.test(location.pathname) and me.showIndividualRegister()
-          @signupState.set({ path: 'individual', screen: 'segment-check' })
+      # else, default value of screen is used, i.e. 'choose-account-type'
     if @signupState.get('screen') is 'segment-check' and not @signupState.get('path') is 'student' and not @segmentCheckRequiredInCountry()
       @signupState.set screen: 'basic-info'


Fix README typo

Fix README typo

funtion -> function

--- readme-src.coffee
+++ readme-src.coffee
@@ -304,7 +304,7 @@
 """

 doc.fn "Bacon.fromBinder(subscribe)", """
-The parameter `subscribe` is a function that accepts a `sink` which is a function that your `subscribe` funtion can "push" events to.
+The parameter `subscribe` is a function that accepts a `sink` which is a function that your `subscribe` function can "push" events to.

 For example:


Hashtags link to search

Hashtags link to search

--- lib/assets/javascripts/helpers/formatting.js.coffee
+++ lib/assets/javascripts/helpers/formatting.js.coffee
@@ -79,5 +79,9 @@
       jsonml[1]['data-view'] = 'ExternalLink'
       jsonml

-    markdown.toHTML(text, 'Tent', { footnotes: inline_mention_urls, preprocessors: [externalLinkPreprocessor] })
+    markdown.toHTML(text, 'Tent', {
+      footnotes: inline_mention_urls
+      hashtagURITemplate: @fullPath('/search') + '?q=%23{hashtag}'
+      preprocessors: [externalLinkPreprocessor]
+    })


Workspace: Users button added to header buttons container.

--- client/app/CommonViews/workspace/panel/collaborativepanel.coffee
+++ client/app/CommonViews/workspace/panel/collaborativepanel.coffee
@@ -15,7 +15,7 @@

   createHeaderButtons: ->
     super
-    @header.addSubView new KDCustomHTMLView
+    @headerButtonsContainer.addSubView new KDCustomHTMLView
       cssClass : "users"
       tooltip  :
         title  : "Show Users"


Speed up rendering of glossary

Speed up rendering of glossary

--- app/assets/javascripts/application/glossary_list.js.coffee
+++ app/assets/javascripts/application/glossary_list.js.coffee
@@ -79,7 +79,7 @@
       glossaryAnchor = $('<tbody/>',
         id: 'glossary-table-' + letter,
         class: 'glossary-table-anchor'
-      ).hide().fadeIn().appendTo(@glossaryTable)
+      ).appendTo(@glossaryTable)
       glossaryAnchor.append($('<tr/>').append($('<td/>').append($('<strong/>').text(letter))))

   # @private
@@ -223,10 +223,10 @@
             firstLetter = sourceEntry.source_copy.substr(0, 1).toUpperCase()
             if firstLetter.match(/[A-Z]/)
               $('#glossary-table-' + firstLetter)
-                .after($(HoganTemplates['glossary/glossary_entry'].render(context)).hide().fadeIn())
+                .after($(HoganTemplates['glossary/glossary_entry'].render(context)))
             else
               $('#glossary-table-\\#')
-                .after($(HoganTemplates['glossary/glossary_entry'].render(context)).hide().fadeIn())
+                .after($(HoganTemplates['glossary/glossary_entry'].render(context)))

         for domLocaleEntry in $('.glossary-table-locale-entry')
           locale = $(domLocaleEntry).data('locale')
@@ -275,7 +275,7 @@
                       .hide().removeClass("text-info text-success").addClass("text-error").fadeIn(500)
           )(domLocaleEntry, addLocaleEntryUrl, rejectLocaleEntryUrl, locale, localeId))

-        @glossaryTable.find("input, textarea, button, a").click (e) ->
+        @glossaryTable.find("input, textarea, button, a").click (e) ->
           e.stopPropagation()

         new TableAccordion(@glossaryTable)


deletes appear to be restored

deletes appear to be restored

--- app/assets/javascripts/backbone/helpers/channels.js.coffee
+++ app/assets/javascripts/backbone/helpers/channels.js.coffee
@@ -81,17 +81,12 @@

   @deleteChannelByTabIndex: (tabIndex, deleted)->
     # gotcha, 0 index being passed a natural index from the html views
-    #tabIndex = tabIndex-1
-    console.log("deleteChannelByTabIndex", tabIndex);
     deleted = deleted || false
     channelId = @getChannelIdByTabIndex(tabIndex)
     if channelId == 'undefined'
       throw "NO CHANNEL ID";
-    console.log("deleteing channelId", channelId);
     channel = new Kandan.Models.Channel({id: channelId})
     return @confirmAndDeleteChannel(channel, tabIndex) if not deleted
-    console.log "TAB INDEX", tabIndex
-    $("#kandan").tabs("remove", tabIndex)

   @channelExists: (channelId)->


ensure file dropzone fills up entire space

fixes CNVS-18347

The dropzone section of the files area wasn't expanding to take up the
entire space available. This fixes that with a some simple css.

Test Plan
Given I'm a user that can upload files
And I'm uploading a file in new files
When I drag a file into the filesystem area
Then the dropzone overlay should appear
And you should be able to upload by dragging onto any of the whitespace
area

Change-Id: Iaad8a5f5ae460220735acf0393bf0b98344486f9
Reviewed-on: https://gerrit.instructure.com/49225
Tested-by: Jenkins
Reviewed-by: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>
QA-Review: Clare Strong <40d6bb3ed7fef516c73274131d5a837c16de74fd@instructure.com>
Product-Review: Sterling Cobb <7a57d889ea7f266299434459898a90c7f6a1ca4d@instructure.com>

--- app/coffeescripts/react_files/components/ShowFolder.coffee
+++ app/coffeescripts/react_files/components/ShowFolder.coffee
@@ -119,7 +119,7 @@
           @state.errorMessages.map (error) ->
             div className: 'muted', error.message
       return div({ref: 'emptyDiv'}) unless @props.currentFolder
-      div role: 'grid',
+      div role: 'grid', style: {flex: "1 1 auto"},

         div {
           ref: 'accessibilityMessage'


Renamed options -> exportOptions in .getCroppedImageData

--- src/cropit.coffee
+++ src/cropit.coffee
@@ -295,14 +295,14 @@
   isZoomable: ->
     @zoomer.isZoomable()

-  getCroppedImageData: (options) ->
+  getCroppedImageData: (exportOptions) ->
     return null unless @imageSrc

     exportDefaults =
       type: 'image/png'
       quality: .75
       originalSize: false
-    options = $.extend {}, exportDefaults, options
+    exportOptions = $.extend {}, exportDefaults, exportOptions

     croppedSize =
       w: @previewSize.w
@@ -315,7 +315,7 @@
         @imageSize.h * @zoom < @previewSize.h
       croppedSize.h = @imageSize.h * @zoom

-    exportZoom = if options.originalSize then 1 / @zoom else @options.exportZoom
+    exportZoom = if exportOptions.originalSize then 1 / @zoom else @options.exportZoom

     canvas = $ '<canvas />'
       .attr
@@ -330,7 +330,7 @@
       @zoom * exportZoom * @imageSize.w,
       @zoom * exportZoom * @imageSize.h

-    canvas.toDataURL options.type, options.quality
+    canvas.toDataURL exportOptions.type, exportOptions.quality

   getImageState: ->
     src: @imageSrc


:memo: doc Cursor.isBetweenWordAndNonWord()

--- src/cursor.coffee
+++ src/cursor.coffee
@@ -137,6 +137,11 @@
     range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
     /^\s+$/.test @editSession.getTextInBufferRange(range)

+  # Public: Returns whether the cursor is currently between a word and non-word
+  # character. The non-word characters are defined by the
+  # `editor.nonWordCharacters` config value.
+  #
+  # Returns a Boolean.
   isBetweenWordAndNonWord: ->
     return false if @isAtBeginningOfLine() or @isAtEndOfLine()

@@ -148,7 +153,6 @@
       _.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after)
     else
       false
-

   # Public: Returns whether this cursor is between a word's start and end.
   isInsideWord: ->
@@ -418,7 +422,6 @@
   getCurrentWordBufferRange: (options={}) ->
     startOptions = _.extend(_.clone(options), allowPrevious: false)
     endOptions = _.extend(_.clone(options), allowNext: false)
-
     new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions))

   # Public: Returns the buffer Range for the current line.


when there are lots of items in the navigation, hovering breaks things

--- src/js/app.coffee
+++ src/js/app.coffee
@@ -37,7 +37,6 @@
     $('body').on('mouseover', 'nav', @openMenu)

     $('body nav').on('click', '.menu', @closeMenu)
-
     $('nav .nav-item-list [href]').on('click', @closeMenu)

     $('body').on 'mousemove', (e) =>
@@ -46,7 +45,6 @@
       else
         $('body nav').removeClass('near-menu')

-    $('nav .nav-item .nav-item-icon').on 'mouseover', @openNavItem
     $('nav .nav-item .nav-item-title').on 'click', @openNavItem

   openNavItem: (e) ->


Proposed fix for jQuery 1.9 "SYNTAX ERROR, UNRECOGNIZED EXPRESSION"

Because of security-motivated changes to $(html) in jQuery 1.9, if the ajax response for more content begins with any character other than < (even if there are only spaces and/or carriage returns before the first <), waypoints-infinite will cause jquery to throw "SYNTAX ERROR, UNRECOGNIZED EXPRESSION".

This change fixes the problem by implementing the new best practice described here:
http://jquery.com/upgrade-guide/1.9/#jquery-htmlstring-versus-jquery-selectorstring

Also see:

http://bugs.jquery.com/ticket/13223

--- shortcuts/infinite-scroll/waypoints-infinite.coffee
+++ shortcuts/infinite-scroll/waypoints-infinite.coffee
@@ -86,7 +86,7 @@

         # Load items from the next page.
         $.get $(options.more).attr('href'), (data) ->
-          $data = $ data
+          $data = $ $.parseHTML(data)
           $more = $ options.more
           $newMore = $data.find options.more
           $container.append $data.find options.items


Removes login_style setting of custom oauth.

--- packages/rocketchat-lib/server/methods/removeOAuthService.coffee
+++ packages/rocketchat-lib/server/methods/removeOAuthService.coffee
@@ -18,3 +18,4 @@
 		RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_button_label_text"
 		RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_button_label_color"
 		RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_button_color"
+		RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_login_style"


Add indexes Subscriptions mobilePushNotifications and desktopNotifications

--- packages/rocketchat-lib/server/models/Subscriptions.coffee
+++ packages/rocketchat-lib/server/models/Subscriptions.coffee
@@ -9,6 +9,8 @@
 		@tryEnsureIndex { 'unread': 1 }
 		@tryEnsureIndex { 'ts': 1 }
 		@tryEnsureIndex { 'ls': 1 }
+		@tryEnsureIndex { 'desktopNotifications': 1 }, { sparse: 1 }
+		@tryEnsureIndex { 'mobilePushNotifications': 1 }, { sparse: 1 }

 	# FIND ONE


Whitespace clean-up.

Whitespace clean-up.

--- src/element.coffee
+++ src/element.coffee
@@ -61,6 +61,7 @@
     # @return {Object} the velocity vector.
     getVelocity : () ->
         return @direction.normalize().scale(@speed)
+
     # Apply an impulse force to the element.
     applyImpulse : (impulse) ->
         velocity = @getVelocity().add(impulse)


hijack and route clicks on inner elements of a tags

--- src/client-reusable/history.coffee
+++ src/client-reusable/history.coffee
@@ -8,18 +8,35 @@
       throw new Error 'missing argument'

     el.addEventListener 'click', (event) ->
-      unless event.target.tagName is 'A'
-        return
+      target = event.target
+
+      # console.log 'click',
+      #   tagName: target.tagName
+      #   bypass: target.dataset.bypass?
+      #   href: target.getAttribute('href')
+      #   absoluteHref: target.href
+      #   root: getBrowserUrlProtocolHost()
+      #   bubbles: event.bubbles
+      #   isRelative: event.target.href.slice(0, getBrowserUrlProtocolHost().length) is root
+
+      # find the containing A tag
+      while target? and target.tagName isnt 'A'
+        target = target.parentNode
+        unless target?
+          return
+
+      # now we have an A tag
+
       # does the element have attribute `data-bypass` ?
-      if event.target.dataset.bypass?
+      if target.dataset.bypass?
         return

       # don't break the users ability to open links in a new tab !
       if event.ctrlKey or event.metaKey
         return

-      href = event.target.getAttribute('href')
-      absoluteHref = event.target.href
+      href = target.getAttribute('href')
+      absoluteHref = target.href

       root = getBrowserUrlProtocolHost()

@@ -29,6 +46,10 @@
         return

       relative = absoluteHref.slice(root.length)
+
+      # TODO
+      # this is triggered twice per click. why ?
+      console.log 'relative anchor click', relative

       cb
         event: event
@@ -82,6 +103,7 @@
     # (every time a relative anchor is clicked the path cursor is updated)
     onRelativeAnchorClick window, (data) ->
       data.event.preventDefault()
+      data.event.stopPropagation()
       console.log 'relative anchor click', data.relative
       setBrowserPath data.relative
       pathCursor.set(data.relative)


fix(draft): delay draft re-opening w/ errors to allow time to close

--- src/flux/stores/draft-store.coffee
+++ src/flux/stores/draft-store.coffee
@@ -372,9 +372,12 @@
       # We delay so the view has time to update the restored draft. If we
       # don't delay the modal may come up in a state where the draft looks
       # like it hasn't been restored or has been lost.
+      #
+      # We also need to delay because the old draft window needs to fully
+      # close. It takes windows currently (June 2016) 100ms to close by
       _.delay =>
         @_notifyUserOfError({draftClientId, threadId, errorMessage})
-      , 100
+      , 300

   _isPopout: ->
     NylasEnv.getWindowType() is "composer"


:bug: fix close button in DocDetailContainer component not working

--- src/renderer/components/doc-slide-wrapper-component.coffee
+++ src/renderer/components/doc-slide-wrapper-component.coffee
@@ -116,6 +116,7 @@
     top: 7
     left: 8
     cursor: 'pointer'
+    zIndex: '1'

   close_icon:
     borderWidth: 0
@@ -138,6 +139,7 @@
     paddingTop: 30
     paddingBottom: 30
     boxSizing: 'border-box'
+    zIndex: '0'

 DocSlideWrapperComponent.contextTypes =
   ctx: React.PropTypes.any


* [bug] pass the this to _getPerms

* [bug] pass the this to _getPerms

--- src/mixins/none-cache-roles.coffee
+++ src/mixins/none-cache-roles.coffee
@@ -105,7 +105,7 @@

   Model::getPerms = (lastVisited = 0)->
     if isFunc Model._getPerms
-      Model._getPerms(aRoles, lastVisited)
+      Model._getPerms(@, aRoles, lastVisited)
     else
       Model.getPerms @[rolesFieldName], lastVisited