fix Gruntfile.coffee

fix Gruntfile.coffee

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -54,16 +54,8 @@
                 src: ['<%= paths.src.js %>*.coffee']
                 dest: '<%= paths.out.jsCustom %>'
         coffee_jshint:
-            options: globals: ['window','module','$']
+            options: globals: ['window','module','define','require']
             all: ['<%= paths.src.js %>*.coffee']
-        'closure-compiler':
-            custom:
-                closurePath: '.'
-                js: '<%= paths.out.jsCustom %>*.js'
-                jsOutputFile: '<%= paths.out.js %>gw2t.js'
-                noreport: true
-                options:
-                    compilation_level: 'ADVANCED_OPTIMIZATIONS'
         concat:
             js:
                 src: ['<%= paths.out.jsVendor %>jquery.min.js', '<%= paths.out.jsVendor %>jquery-plugins.js']
@@ -121,7 +113,7 @@
                         middlewares.unshift require('grunt-connect-rewrite/lib/utils').rewriteRequest
                         middlewares
                     keepalive: true
-                    base:
+                    base:
                         [ '<%= paths.out.base %>' ]

         watch:
@@ -172,7 +164,7 @@
     grunt.registerTask 'js', ['clean:js','mkdir:js','coffee:multi','js:vendor','js:minify','js:concat']

     # css
-    grunt.registerTask 'css:sass', ['mkdir:cssTemp', 'sass:main']
+    grunt.registerTask 'css:sass', ['mkdir:cssTemp', 'sass:main']
     grunt.registerTask 'css:prefix', ['autoprefixer:main']
     grunt.registerTask 'css:minify', ['cssmin:main']

@@ -183,7 +175,7 @@
     grunt.registerTask 'test', ['coffee_jshint', 'nodeunit:all']

     # build
-    grunt.registerTask 'build:js', ['test','js']
+    grunt.registerTask 'build:js', ['js']
     grunt.registerTask 'build:img', ['img']
     grunt.registerTask 'build:css', ['css']
     grunt.registerTask 'build', ['build:js', 'build:css', 'build:img']
@@ -203,4 +195,4 @@
         console.log "  #{'grunt serve'.cyan}     - serve the assets on 127.0.0.1:8080"
         console.log ""
         console.log "  #{'grunt test'.cyan}      - run all unit tests"
-    grunt.registerTask 'help', ['default']+    grunt.registerTask 'help', ['default']


Tarvis Test

Tarvis Test

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -43,14 +43,6 @@
 			working: [
 				'<%= settings.tempDirectory %>'
 				'<%= settings.distDirectory %>'
-			]
-			# Used for those that desire plain old JavaScript
-			jslove: [
-				'**/*.coffee'
-				'!**/.components/**'
-				'!**/.temp/**'
-				'!**/bower_components/**'
-				'!**/node_modules/**'
 			]

 		# Compiles CoffeeScript (.coffee) files to JavaScript (.js)
@@ -255,25 +247,6 @@
 					'.temp/scripts/views.js': '.temp/**/*.html'
 				options:
 					trim: '<%= settings.tempDirectory %>'
-
-		prompt:
-			jslove:
-				options:
-					questions: [
-						{
-							config: 'coffee.jslove.compile'
-							type: 'input'
-							message: 'Are you sure you wish to convert all CoffeeScript (.coffee) files to JavaScript (.js)?' + '\n' + 'This cannot be undone.'.red + ': (y/N)'
-							default: false
-							filter: (input) ->
-								confirmed = /^y(es)?/i.test input
-
-								if not confirmed
-									grunt.fatal 'exiting jslove'
-
-								return confirmed
-						}
-					]

 		# RequireJS optimizer configuration for both scripts and styles
 		# This configuration is only used in the 'prod' build
@@ -586,13 +559,3 @@
 		'build'
 		'karma'
 	]
-
-	# Compiles all CoffeeScript files in the project to JavaScript then deletes all CoffeeScript files
-	# Used for those that desire plain old JavaScript
-	# Enter the following command at the command line to execute this build task:
-	# grunt jslove
-	grunt.registerTask 'jslove', [
-		'prompt:jslove'
-		'coffee:jslove'
-		'clean:jslove'
-	]


stop creating archived zips

stop creating archived zips

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -44,13 +44,13 @@
         extMin:
             src: [ "build/jquery.fancytree.*.min.js" ]

-    compress:
-        dist:
-            options:
-                archive: "archive/<%= pkg.name %>-<%= pkg.version %>.zip"
-            files: [
-                {expand: true, cwd: "dist/", src: ["**/*"], dest: ""}
-                ]
+    # compress:
+    #     dist:
+    #         options:
+    #             archive: "archive/<%= pkg.name %>-<%= pkg.version %>.zip"
+    #         files: [
+    #             {expand: true, cwd: "dist/", src: ["**/*"], dest: ""}
+    #             ]

     concat:
         core:
@@ -398,7 +398,7 @@
       "copy:dist"
       "clean:build"
       "replace:release"
-      "compress:dist"
+      # "compress:dist"
       # "tagrelease"
       # "bumpup:prerelease"
       ]


Copy CNAME into dist/ so it will be included on gh-pages

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -49,6 +49,10 @@
       schemas:
         files: [
           expand: true, src: ['schema/*.json'], dest: 'dist/', filter: 'isFile'
+        ]
+      cname:
+        files: [
+          src: ['CNAME'], dest: 'dist/'
         ]

     'gh-pages':


tidy gruntfile

tidy gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,3 +1,5 @@
+fs = require('fs')
+
 # Install directory of all third party-assets
 vendor = 'vendor/'
 # Directory to build the site to
@@ -6,10 +8,10 @@
 js = '.js'
 css = '.css'
 coffee = '.coffee'
-# Bash wildcard patterns to match all
-# JS files
+# Bash wildcard patterns to match all...
+# ...JS files
 js_src = 'js/**/*.js'
-# HTML templates
+# ...HTML templates
 templates = 'templates/**/*.html'

 # Library paths
@@ -37,13 +39,11 @@
   js_url: 'gamma/js/js-url.min'
   modernizr_custom: 'gamma/js/modernizr.custom.70736'

-tests = [
-  'metadata'
-]
+tests = fs.readdirSync('test/specs')

 test_map = {}
 for test in tests
-  test_map['test/build/' + test + js] = 'test/specs/' + test + coffee
+  test_map['test/build/' + test.replace('coffee', 'js')] = 'test/specs/' + test

 # Array to hold values of library map
 lib_list = []


No grunt watch here

No grunt watch here

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -35,7 +35,6 @@
   # Grunt plugins used for testing
   @loadNpmTasks 'grunt-mocha-test'
   @loadNpmTasks 'grunt-coffeelint'
-  @loadNpmTasks 'grunt-contrib-watch'
   @loadNpmTasks 'grunt-shell-spawn'

   # Our local tasks


Remove unneeded custom postMessage on window.opener

--- atom/renderer/lib/override.coffee
+++ atom/renderer/lib/override.coffee
@@ -92,10 +92,7 @@
   throw new Error('prompt() is and will not be supported.')

 openerId = ipcRenderer.sendSync 'ATOM_SHELL_GUEST_WINDOW_MANAGER_GET_OPENER_ID'
-if openerId?
-  window.opener = BrowserWindowProxy.getOrCreate(openerId)
-  window.opener.postMessage = (message, targetOrigin='*') ->
-    ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPENER_POSTMESSAGE', message, targetOrigin, location.origin
+window.opener = BrowserWindowProxy.getOrCreate(openerId) if openerId?

 ipcRenderer.on 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', (event, sourceId, message, sourceOrigin) ->
   # Manually dispatch event instead of using postMessage because we also need to


MemberItemView: Don't create kick button for koding group.

--- client/admin/lib/views/members/memberitemview.coffee
+++ client/admin/lib/views/members/memberitemview.coffee
@@ -1,6 +1,7 @@
 kd                     = require 'kd'
 JView                  = require 'app/jview'
 whoami                 = require 'app/util/whoami'
+isKoding               = require 'app/util/isKoding'
 AvatarView             = require 'app/commonviews/avatarviews/avatarview'
 KDButtonView           = kd.ButtonView
 KDListItemView         = kd.ListItemView
@@ -83,6 +84,8 @@
     buttons = buttonSet[@loggedInUserRole.slug][@memberRole.slug]

     buttons.forEach (button) =>
+      return if isKoding() and button.slug is 'kick'
+
       buttonView = new KDButtonView
         cssClass : kd.utils.curry 'solid compact outline', button.extraClass
         title    : button.buttonTitle


Modify the DOM instead of setting the 'data' prop via jQuery to mark a section as 'progressive-loaded'

Defect:
This was exposed as an issue by another bug which was loading the entire page on partial load and re-loading the javascript.
The result was an infinite loop of reloading the content.

Fix:
Modifying the DOM of the element we don't replace (the main placeholder) ensure we don't run into this class of issues.

--- vendor/assets/javascripts/progressive_render.js.coffee
+++ vendor/assets/javascripts/progressive_render.js.coffee
@@ -11,9 +11,8 @@
 load_missing_content = ->
   $('[data-progressive-render-placeholder=true]').each ->
     $this = $(this)
-    # Don't re-process div's we've already seen
-    return if $this.data('progressive-render-setup')
-    $this.data('progressive-render-setup', true)
+    # Remove the designation on this partial from the DOM so we don't attempt to re-load it later.
+    $this.attr('data-progressive-render-placeholder', false)

     # Start the load
     $.ajax url: $this.data('progressive-render-path'), cache: false, success: (response) ->


Correctly handle keywords with quotes

Correctly handle keywords with quotes

--- src/scripts/modules/search/advanced/advanced.coffee
+++ src/scripts/modules/search/advanced/advanced.coffee
@@ -36,9 +36,10 @@
         _.map obj, (limit, index) ->
           if not limit.value then return

+          # Split keywords into multiple keyword:value pairs
           if limit.name is 'keywords'
-            # split into multiple keyword:value pairs
-            keywords = _.map limit.value.split(' '), (value) -> {name: 'keyword', value: value}
+            keywords = limit.value.match(/(?:[^\s"]+|"[^"]*")+/g)
+            keywords = _.map keywords, (value) -> {name: 'keyword', value: value}
             return format(keywords)

           if /\s/g.test(limit.value) and not /"/g.test(limit.value)


Check for absolute paths

Check for absolute paths

--- lib/models/result-model.coffee
+++ lib/models/result-model.coffee
@@ -9,7 +9,8 @@
       @processResultString(response)

     getFilePath: ->
-      return path.join(@cwd, @fileName)
+      filePath = if @fileName.startsWith('/') then @fileName else path.join(@cwd, @fileName)
+      return filePath

     processResultString: (response) ->
       @resultString = response
@@ -33,4 +34,4 @@

     generateView: ->
       return ResultItemView.setup(@)
-      +


fixing off-by-one error in compile_splat_arguments

--- nodes.coffee
+++ nodes.coffee
@@ -378,7 +378,7 @@
           args[i - 1]: "${prev.substr(0, prev.length - 2)}, $code]"
           continue
         else if i > 1 and prev.substr(8, 1) is '[' and prev.substr(prev.length - 2, 1) is ']'
-          args[i - 1]: "${prev.substr(0, prev.length - 3)}, $code])"
+          args[i - 1]: "${prev.substr(0, prev.length - 2)}, $code])"
           continue
         else
           code: "[$code]"


bugfix: build files if i18n.json files changes for case we don't have project-tap.i18n to trigger build

--- lib/plugin/compile-i18n.coffee
+++ lib/plugin/compile-i18n.coffee
@@ -550,6 +550,9 @@
   if not compileStep.archMatches 'browser'
     return

+  # Build the project files
+  buildFilesOnce compileStep
+
   # See the above note titled: templatesRegistrationsNeeded and the process of
   # tap-i18n package-specific templates registration
   if templatesRegistrationsNeeded


Groups: Defensive check for group logo upload

--- client/Dashboard/views/grouplogosettings.coffee
+++ client/Dashboard/views/grouplogosettings.coffee
@@ -37,8 +37,9 @@
     @addSubView @uploadButton

     @group.on "update", =>
-      resized = KD.utils.proxifyUrl @group.customize.logo, proxifyOptions
-      @groupLogoView.setCss 'background-image', "url(#{resized})"
+      if @group.customize?.logo
+        resized = KD.utils.proxifyUrl @group.customize.logo, proxifyOptions
+        @groupLogoView.setCss 'background-image', "url(#{resized})"

   showUploadView: ->
     @groupLogoView.hide()


resurrection.activity: load embed on activity edit

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -87,6 +87,7 @@
   edit: (activity) ->
     @setData activity
     @input.setContent activity.body, activity
+    @embedBox.loadEmbed activity.link.link_url
     @submit.setTitle "Update"

   reset: ->


activity/flux: add channel load action by name

--- client/activity/lib/flux/actions/channel.coffee
+++ client/activity/lib/flux/actions/channel.coffee
@@ -1,8 +1,39 @@
 kd                = require 'kd'
 actionTypes       = require './actiontypes'
 fetchChatChannels = require 'activity/util/fetchChatChannels'
+isKoding          = require 'app/util/isKoding'

 dispatch = (args...) -> kd.singletons.reactor.dispatch args...
+
+###*
+ * Action to load channel with given slug.
+ *
+ * @param {string} name - slug of the channel
+###
+loadChannelByName = (name) ->
+
+  { LOAD_CHANNEL_BY_NAME_BEGIN
+    LOAD_CHANNEL_BY_NAME_FAIL
+    LOAD_CHANNEL_SUCCESS } = actionTypes
+
+  type = if isKoding()
+    switch name
+      when 'Public'    then 'group'
+      when 'Changelog' then 'announcement'
+      else 'topic'
+  else 'topic'
+
+  name = name.toLowerCase()
+
+  dispatch LOAD_CHANNEL_BY_NAME_BEGIN, { name, type }
+
+  kd.singletons.socialapi.channel.byName { name, type }, (err, channel) ->
+    if err
+      dispatch LOAD_CHANNEL_BY_NAME_FAIL, { err }
+      return
+
+    dispatch LOAD_CHANNEL_SUCCESS, { channelId: channel.id, channel }
+

 ###*
  * Action to load followed private messages.
@@ -50,6 +81,7 @@

 module.exports = {
+  loadChannelByName
   loadFollowedPrivateChannels
   loadFollowedPublicChannels
 }


client: use PerfectScrollbar inside Scroller component

--- client/app/lib/components/scroller/index.coffee
+++ client/app/lib/components/scroller/index.coffee
@@ -1,6 +1,8 @@
 kd = require 'kd'
 React = require 'kd-react'
 Waypoint = require 'react-waypoint'
+
+PerfectScrollbar = require 'app/components/perfectscrollbar'

 module.exports = class Scroller extends React.Component

@@ -27,10 +29,10 @@

   render: ->

-    <div className='Scrollable' ref='scrollContainer'>
+    <PerfectScrollbar className='Scrollable' ref='scrollContainer'>
       {@renderTopWaypoint()}
       {@props.children}
       {@renderBottomWaypoint()}
-    </div>
+    </PerfectScrollbar>


Make it simple

Make it simple

--- client/landing/app/CommonViews/FormViews.coffee
+++ client/landing/app/CommonViews/FormViews.coffee
@@ -1,3 +1,5 @@
+# FIXME (gokmen) Needs to be rw
+
 class AbstractPersonalFormView extends KDFormView
   constructor:(options, data)->
     memberData = data
@@ -86,8 +88,7 @@
       {{> @nameView}}
       {{> @firstName}}{{> @lastName}}
     """
-    # {{> @cancelButton}}{{> @saveButton}}
-
+
   resetInputValue:->
     {profile} = @memberData
     @firstName.setValue Encoder.htmlDecode profile.firstName
@@ -143,13 +144,13 @@
     {@memberData} = options
     {profile} = @memberData

-    defaultPlaceHolder = "You haven't entered anything in your bio yet. Why not add something now?"
+    @defaultPlaceHolder = "You haven't entered anything in your bio yet. Why not add something now?"

     @aboutInput = new KDInputView
       cssClass      : 'about editable hitenterview active'
       type          : 'textarea'
-      defaultValue  : if profile.about is defaultPlaceHolder then '' else Encoder.htmlDecode profile.about
-      placeholder   : if profile.about isnt defaultPlaceHolder then null else Encoder.htmlDecode profile.about
+      defaultValue  : if profile.about is @defaultPlaceHolder then '' else Encoder.htmlDecode profile.about
+      placeholder   : if profile.about isnt @defaultPlaceHolder then null else Encoder.htmlDecode profile.about
       name          : 'about'

     @aboutInfo = new PersonalAboutView
@@ -157,7 +158,7 @@
         title            : "Click to edit"
         placement        : "left"
         offset           : 5
-      defaultPlaceHolder : defaultPlaceHolder
+      defaultPlaceHolder : @defaultPlaceHolder
     , @memberData

     @windowController = @getSingleton 'windowController'
@@ -170,7 +171,7 @@

   resetInputValue:->
     {profile} = @memberData
-    @aboutInput.setValue if profile.about is "You haven't entered anything in your bio yet. Why not add something now?" then '' else Encoder.htmlDecode profile.about
+    @aboutInput.setValue if profile.about is @defaultPlaceHolder then '' else Encoder.htmlDecode profile.about

   formCallback:(formData)->
     {profile} = @memberData
@@ -193,17 +194,12 @@
           duration  : 500
         @unsetClass 'active'

-class PersonalAboutView extends KDCustomHTMLView
+class PersonalAboutView extends JView
   constructor:(options, data)->
     super
     {profile} = @getData()
     profile.about or= options.defaultPlaceHolder

-  viewAppended:->
-    super
-    @setTemplate @pistachio()
-    @template.update()
-
   pistachio:->
     """
       <p>{{ @utils.applyTextExpansions #(profile.about) }}</p>
@@ -270,14 +266,7 @@
           duration  : 500
         @unsetClass 'active'

-class LocationView extends KDCustomHTMLView
-  constructor:(options, data)->
-    super
-
-  viewAppended:->
-    @setTemplate @pistachio()
-    @template.update()
-
+class LocationView extends JView
   pistachio:->
     """
       {{ @getFirstLocation #(locationTags)}}
@@ -307,7 +296,6 @@

         joinedTags   = plainNewTags.concat plainOldTags

-        # log formData.skillTags, newTags, oldTags, joinedTags
         @memberData.addTags formData.skillTags, (err)=>
           if err
             log "An error occured:", err


Ticker: disable ticker

Ticker: disable ticker

--- workers/social/lib/social/models/activityticker.coffee
+++ workers/social/lib/social/models/activityticker.coffee
@@ -135,6 +135,7 @@
         do kallback

   @_fetch = (requestOptions = {}, callback)->
+    return callback null, []
     {options, client} = requestOptions
     sources = constructorNames
     as      = relationshipNames


Add support for spec env in lit. hs.

Add support for spec env in lit. hs.

--- src/haskell.coffee
+++ src/haskell.coffee
@@ -614,7 +614,7 @@

   macros: haskellGrammar.macros
   patterns: [
-      begin: /^((\\)begin)({)code(})(\s*\n)?/
+      begin: /^((\\)begin)({)(code|spec)(})(\s*\n)?/
       beginCaptures:
         1:
           name: 'support.function.be.latex'
@@ -622,9 +622,9 @@
           name: 'punctuation.definition.function.latex'
         3:
           name: 'punctuation.definition.arguments.begin.latex'
-        4:
+        5:
           name: 'punctuation.definition.arguments.end.latex'
-      end: /^((\\)end)({)code(})/
+      end: /^((\\)end)({)\4(})/
       endCaptures:
         1:
           name: 'support.function.be.latex'


New helper: $map.

New helper: $map.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -4,6 +4,17 @@

 $asArray = (val) -> if $_.isArray val then val else [val]
 $asFunction = (val) -> if $_.isFunction val then val else -> val
+
+$each = $_.each
+
+$first = (collection, def) ->
+  if (n = collection.length)?
+    return collection[0] unless n is 0
+  else
+    return value for own _, value of collection
+
+  # Nothing was found, returns the `def` value.
+  def

 $removeValue = (array, value) ->
   index = array.indexOf value
@@ -88,7 +99,7 @@
     # Values are grouped by namespace.
     valuesByNamespace = Object.create null

-    $_.each items, (item) ->
+    $each items, (item) ->
       return unless not cond? or cond.call item

       if bind?
@@ -104,7 +115,7 @@
       # Computes the current value.
       value = val.call item

-      (valuesByNamespace[namespace] ?= []).push value
+      (valuesByNamespace[namespace] ?= {})[item.key] = value

     # Stops here if no values were computed.
     return if do ->
@@ -124,9 +135,9 @@
         value: if value is undefined then $_.clone init else value
       }
       changed = if event is 'enter'
-        fn.call ctx, values_, []
+        fn.call ctx, values_, {}
       else
-        fn.call ctx, [], values_
+        fn.call ctx, {}, values_

       # Notifies watchers unless it is known the value has not
       # changed.
@@ -152,21 +163,21 @@

     throw new Error 'cannot use keys and rules' unless $_.isEmpty rules

-    $_.each keys, (key) -> collection.on "key=#{key}", processOne
+    $each keys, (key) -> collection.on "key=#{key}", processOne

     # Handles existing items.
     process 'enter', collection.getRaw keys
   else if not $_.isEmpty rules
     # Matching is done the rules.

-    $_.each rules, (rule) -> collection.on "rule=#{rule}", process
+    $each rules, (rule) -> collection.on "rule=#{rule}", process

     # TODO: Inefficient, is there another way?
     rules = do -> # Minor optimization.
       tmp = Object.create null
       tmp[rule] = true for rule in rules
       tmp
-    $_.each collection.getRaw(), (item) ->
+    $each collection.getRaw(), (item) ->
       processOne 'enter', item if item.rule of rules
   else
     # No matching done.
@@ -192,6 +203,15 @@

 #=====================================================================

+$map = (options) ->
+  options.init = []
+
+  $watch this, options, (entered, exited) ->
+    $each entered, (value, key) => @value[key] = value
+    $each exited, (value, key) => delete @value[key]
+
+#---------------------------------------------------------------------
+
 # Creates a set of value from various items.
 $set = (options) ->
   # Contrary to other helpers, the default value is the key.
@@ -202,12 +222,12 @@
   $watch this, options, (entered, exited) ->
     changed = false

-    for value in entered
+    $each entered, (value) =>
       if @value.indexOf value is -1
         @value.push value
         changed = true

-    for value in exited
+    $each exited, (value) =>
       changed = true if $removeValue @value, value

     changed
@@ -251,8 +271,8 @@
   $watch this, options, (entered, exited) ->
     prev = @value

-    @value = add @value, value for value in entered
-    @value = sub @value, value for value in exited
+    $each entered, (value) => @value = add @value, value
+    $each exited, (value) => @value = sub @value, value

     @value isnt prev

@@ -277,10 +297,7 @@
   $watch this, options, (entered, exited) ->
     prev = @value

-    if not $_.isEmpty entered
-      @value = entered[0]
-    else
-      @value = def unless keepLast
+    @value = $first entered, (if keepLast then @value else def)

     @value isnt prev


Export helpers.group().

Export helpers.group().

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -26,7 +26,7 @@
 #     {destination: 'b', data: [3], callback: ['f3']}]
 #
 # Returns new array of objects.
-group = (items, key) ->
+exports.group = (items, key) ->
   map = {}
   result = []
   counter = 0


Render discussion activities

Render discussion activities

--- core/app/backbone/views/react_feed_activities.coffee
+++ core/app/backbone/views/react_feed_activities.coffee
@@ -51,12 +51,35 @@

   render: ->
     switch @model().get("action")
+      when "created_fact"
+        ReactCreatedFactActivity model: @model()
       when "created_comment"
         ReactCreatedCommentActivity model: @model()
       when "created_sub_comment"
         ReactCreatedSubCommentActivity model: @model()
       when "followed_user"
         ReactFollowedUserActivity model: @model()
+
+ReactCreatedFactActivity = React.createBackboneClass
+  displayName: 'ReactCreatedFactActivity'
+  mixins: [UpdateOnFeaturesChangeMixin]
+
+  render: ->
+    user = new User @model().get('user')
+    fact = new Fact @model().get('fact')
+    fact_link = fact.fact_show_link()
+
+    ReactGenericActivity {
+        model: user
+        time: @model().get('created_at')
+        href: fact_link.href
+        target: fact_link.target
+        activity_header_action: [
+          _span ["feed-activity-description"],
+            "posted a challenge"
+        ]
+      },
+      fact.get('displaystring')

 ReactCreatedCommentActivity = React.createBackboneClass


Layout on Screen resize if parent is null


Remove not needed popovers.

Remove not needed popovers.

--- app/assets/javascripts/app/controllers/navigation.js.coffee
+++ app/assets/javascripts/app/controllers/navigation.js.coffee
@@ -186,6 +186,9 @@

       @$('.search').addClass('focused')

+      # remove not needed popovers
+      $('.popover').remove()
+
       # check if search is needed
       term = @$('#global-search').val().trim()
       return if !term
@@ -198,6 +201,10 @@
       @delay(
         =>
           @$('.search').removeClass('focused')
+
+          # remove not needed popovers
+          $('.popover').remove()
+
           @renderResult()
         320
       )


Hide instead of delete dismissable Bootstrap alerts

--- app/assets/javascripts/avalon_playlists/playlists.js.coffee
+++ app/assets/javascripts/avalon_playlists/playlists.js.coffee
@@ -80,3 +80,9 @@
   new_text = $('.human_friendly_visibility_'+new_val).attr('title')
   $('.visibility-help-text').text(new_text)
 )
+
+# Hide instead of delete Bootstrap's dismissable alerts
+$('.mejs-form-alert').on('close.bs.alert', (e) ->
+  e.preventDefault()
+  $(this).slideUp()
+)


Add MediaLib.FlashInserter

Add MediaLib.FlashInserter

--- app/assets/javascripts/media_lib/inserter_factory.js.coffee
+++ app/assets/javascripts/media_lib/inserter_factory.js.coffee
@@ -43,6 +43,9 @@
       when 'gallery_image'
         if model.type is 'image'
           new MediaLib.HeatGalleryImageInserter uploader, model
+      when 'flash'
+        if model.type is 'application'
+          new MediaLib.FlashInserter uploader, model
       else
         throw new Error('Undefined Inserter')

@@ -295,3 +298,10 @@
           description: @model.description
     ed = tinyMCE.get @model.uploaderId
     ed.execCommand 'mceInsertContent', false, content
+
+class MediaLib.FlashInserter extends MediaLib.BaseInserter
+  insert: ($uploader) ->
+    $uploader.find('.js-uploader-img [name="movie"]').attr 'value', @model.static_url
+
+    $uploader.find('.js-uploader-input').val @model.static_name
+    $uploader.find('.js-uploader-input').trigger 'change'


プレビューのアイコンサイズを半分に修正

プレビューのアイコンサイズを半分に修正

--- app/assets/javascripts/views/character_modal_view.js.coffee
+++ app/assets/javascripts/views/character_modal_view.js.coffee
@@ -57,6 +57,19 @@

   render: ->
     @$el.modal('show')
+    # HACK: ダイアログを表示して500ms程度待たないと画像のサイズが取得できなかった
+    f = ->
+      if @readImageSizeflag
+        img = $('#character-modal-costume-selector .active img')
+        if img.width() > 0
+          $('#character-modal-character').css
+            width: "#{img.width() / 2}px"
+            height: "#{img.height() / 2}px"
+          @readImageSizeflag = false
+        else
+          setTimeout(_.bind(f, @), 50)
+    if @readImageSizeflag
+      setTimeout(_.bind(f, @), 1)

   onChange: (model, options)->
     @onChangeName(@model, @model.get('name'))
@@ -93,7 +106,14 @@
       alt: model.costume()
     $('#character-modal-character img').replaceWith(img)
     @$el.find('#character-modal-costume-selector a.thumbnail').removeClass('active')
-    @$el.find("#character-modal-costume-selector img[alt=\"#{model.costume()}\"]").parent().addClass('active')
+    thumb = @$el.find("#character-modal-costume-selector img[alt=\"#{model.costume()}\"]")
+    thumb.parent().addClass('active')
+    if thumb.width() > 0
+      $('#character-modal-character').css
+        width: "#{thumb.width() / 2}px"
+        height: "#{thumb.height() / 2}px"
+    else
+      @readImageSizeflag = true

   onSelectCostume: (e) ->
     e.preventDefault()


Refactor to install git packages directly into ~/.atom/packages

Signed-off-by: Michelle Tilley <19f7a7eb6786def4c39d6d86c40af5dbc6589663@github.com>

--- src/install.coffee
+++ src/install.coffee
@@ -1,10 +1,10 @@
 path = require 'path'

+_ = require 'underscore-plus'
 async = require 'async'
 CSON = require 'season'
-_ = require 'underscore-plus'
 yargs = require 'yargs'
-CSON = require 'season'
+Git = require 'git-utils'
 semver = require 'npm/node_modules/semver'
 temp = require 'temp'
 hostedGitInfo = require 'hosted-git-info'
@@ -23,7 +23,6 @@
   constructor: ->
     @atomDirectory = config.getAtomDirectory()
     @atomPackagesDirectory = path.join(@atomDirectory, 'packages')
-    @atomGitPackagesDirectory = path.join(@atomDirectory, 'git-packages')
     @atomNodeDirectory = path.join(@atomDirectory, '.node-gyp')
     @atomNpmPath = require.resolve('npm/bin/npm-cli')
     @atomNodeGypPath = require.resolve('npm/node_modules/node-gyp/bin/node-gyp')
@@ -390,7 +389,6 @@
   createAtomDirectories: ->
     fs.makeTreeSync(@atomDirectory)
     fs.makeTreeSync(@atomPackagesDirectory)
-    fs.makeTreeSync(@atomGitPackagesDirectory)
     fs.makeTreeSync(@atomNodeDirectory)

   # Compile a sample native module to see if a useable native build toolchain
@@ -490,27 +488,51 @@
   getHostedGitInfo: (name) ->
     hostedGitInfo.fromUrl(name)

-  installGitPackage: (gitPackageInfo, options, callback) ->
+  installGitPackage: (packageUrl, gitPackageInfo, options, callback) ->
+    tasks = []
+
     cloneDir = temp.mkdirSync("atom-git-package-clone-")
-    tasks = []
-
-    tasks.push (cb) => @cloneGitPackage gitPackageInfo, cloneDir, cb
-    tasks.push (cb) => @installGitPackageDependencies(cloneDir, options, cb)
-    tasks.push ({name}, cb) =>
-      targetDir = path.join(@atomGitPackagesDirectory, name)
-      fs.cp cloneDir, targetDir, cb
-
-    async.waterfall tasks, callback
+
+    tasks.push (data, next) =>
+      @cloneGitPackage gitPackageInfo, cloneDir, (err) ->
+        next(err, data)
+
+    tasks.push (data, next) =>
+      console.log()
+      @installGitPackageDependencies cloneDir, options, (err) ->
+        next(err, data)
+
+    tasks.push (data, next) =>
+      @getRepositoryHeadSha cloneDir, (err, sha) ->
+        data.sha = sha
+        next(err, data)
+
+    tasks.push (data, next) ->
+      metadataFilePath = CSON.resolve(path.join(cloneDir, 'package'))
+      CSON.readFile metadataFilePath, (err, metadata) ->
+        data.metadataFilePath = metadataFilePath
+        data.metadata = metadata
+        next(err, data)
+
+    tasks.push (data, next) ->
+      data.metadata.apmInstallSource =
+        type: "git"
+        source: packageUrl
+        sha: data.sha
+      CSON.writeFile data.metadataFilePath, data.metadata, (err) ->
+        next(err, data)
+
+    tasks.push (data, next) =>
+      {name} = data.metadata
+      targetDir = path.join(@atomPackagesDirectory, name)
+      fs.cp cloneDir, targetDir, next
+
+    iteratee = (currentData, task, next) -> task(currentData, next)
+    async.reduce tasks, {}, iteratee, callback

   installGitPackageDependencies: (directory, options, callback) =>
-    tasks = []
-
     options.cwd = directory
-    tasks.push (cb) => @installDependencies(options, cb)
-    tasks.push (cb) -> CSON.readFile(CSON.resolve(path.join(directory, 'package')), cb)
-
-    async.waterfall tasks, (err, metadata) ->
-      callback(err, metadata)
+    @installDependencies(options, callback)

   cloneGitPackage: (packageInfo, cloneDir, callback) ->
     Develop = require './develop'
@@ -531,6 +553,14 @@
         else
           callback()

+  getRepositoryHeadSha: (repoDir, callback) ->
+    try
+      repo = Git.open(repoDir)
+      sha = repo.getReferenceTarget("HEAD")
+      callback(null, sha)
+    catch err
+      callback(err)
+
   run: (options) ->
     {callback} = options
     options = @parseOptions(options.commandArgs)
@@ -553,7 +583,7 @@
       gitPackageInfo = @getHostedGitInfo(name)

       if gitPackageInfo
-        @installGitPackage gitPackageInfo, options, options.callback
+        @installGitPackage name, gitPackageInfo, options, callback
       else if name is '.'
         @installDependencies(options, callback)
       else # is registered package


Use singular type in InteractorsView

Use singular type in InteractorsView

--- core/app/backbone/views/interactors_view.coffee
+++ core/app/backbone/views/interactors_view.coffee
@@ -26,9 +26,15 @@
         multipleNotDisplayed: (@collection.totalRecords - @collection.length)>1
       @render()

+  singularType: ->
+    switch @collection.type
+      when 'believes' then 'believe'
+      when 'disbelieves' then 'disbelieve'
+      when 'doubts' then 'doubt'
+
   templateHelpers: =>
     multiplicity = if @collection.totalRecords > 1 then 'plural' else 'singular'
-    translation = "fact_#{@collection.type}_past_#{multiplicity}_action"
+    translation = "fact_#{@singularType()}_past_#{multiplicity}_action"

     past_action: Factlink.Global.t[translation]


Add a description for each package

Add a description for each package

--- package.cson
+++ package.cson
@@ -1 +1,2 @@
-'main': 'lib/tree'
+'main': './lib/tree'
+'description': 'Display, add, move, and delete the files and folders in the current project.'


Code cleanup.

Code cleanup.

--- app/assets/javascripts/app/lib/app_post/i18n.js.coffee
+++ app/assets/javascripts/app/lib/app_post/i18n.js.coffee
@@ -7,12 +7,17 @@
   @translateContent: ( string, args... ) ->
     if _instance == undefined
       _instance ?= new _Singleton
-    _instance.translate_content( string, args )
+    _instance.translateContent( string, args )
+
+  @translatePlain: ( string, args... ) ->
+    if _instance == undefined
+      _instance ?= new _Singleton
+    _instance.translatePlain( string, args )

   @translateInline: ( string, args... ) ->
     if _instance == undefined
       _instance ?= new _Singleton
-    _instance.translate_inline( string, args )
+    _instance.translateInline( string, args )

   @translateTimestamp: ( args ) ->
     if _instance == undefined
@@ -116,11 +121,11 @@
           App.Translation.refresh( { id: object[0], source: object[1], target: object[2], locale: @locale } )
     )

-  translate_inline: ( string, args... ) =>
-    @translate( string, args... )
+  translateInline: ( string, args... ) =>
+    @escape( @translate( string, args... ) )

-  translate_content: ( string, args... ) =>
-    translated = @translate( string, args... )
+  translateContent: ( string, args... ) =>
+    translated = @escape( @translate( string, args... ) )
 #    replace = '' + translated + ''
     if App.Config.get( 'Translation' )
       replace = '' + translated + ''
@@ -129,6 +134,9 @@
       replace += ''
     else
       translated
+
+  translatePlain: ( string, args... ) =>
+    @translate( string, args... )

   translate: ( string, args... ) =>

@@ -146,9 +154,6 @@
     # search %s
     for arg in args
       translated = translated.replace(/%s/, arg)
-
-    # escape
-    translated = @escape(translated)

     @log 'i18n', 'debug', 'translate', string, args, translated


fix the instance in which there is no slider__control--next when updateCount is run

--- app/assets/javascripts/lib/components/slider.js.coffee
+++ app/assets/javascripts/lib/components/slider.js.coffee
@@ -166,7 +166,7 @@
       .end().next().addClass('is-next')

     _updateCount: ->
-      currentHTML = $('.slider__control--next').html()
+      currentHTML = $('.slider__control--next').html() || ""
       nextIndex = @current_slide + 1
       prevIndex = @current_slide - 1


Use height + vertical margin instead of outerHeight for cellHeight

--- app/assets/javascripts/player/renderers/base.js.coffee
+++ app/assets/javascripts/player/renderers/base.js.coffee
@@ -23,7 +23,8 @@
     sample = $('M')
     @$el.parent().append(sample)
     @cellWidth = sample.width()
-    @cellHeight = sample.find('span span').outerHeight()
+    span = sample.find('span span')
+    @cellHeight = span.height() + span.css('padding-top') + span.css('padding-bottom')
     sample.remove()
     @fixTerminalElementSize()
     @fixPlayerContainerSize()


Fire at one player at the time

Fire at one player at the time

--- app/scripts/components/behaviors/shoot_on_sight.coffee
+++ app/scripts/components/behaviors/shoot_on_sight.coffee
@@ -42,10 +42,14 @@
       return if @lastShotAt < @shootConfig.cooldown

     self = this
+    targets = []
     Crafty(@shootConfig.targetType).each ->
       angle = self._determineAngle(this)
       if Math.abs(angle - self.rotation) < self.shootConfig.sightAngle
-        self._shoot(this)
+        targets.push this
+    if targets.length > 0
+      target = _.sample(targets)
+      @_shoot(target)

   _determineAngle: (entity) ->
     angle = Math.atan2(@y - entity.y, @x - entity.x)


cleaned up & updated some comments

cleaned up & updated some comments

--- src/lib/dns.coffee
+++ src/lib/dns.coffee
@@ -73,10 +73,6 @@
                     @log.error {fn:sig+'[error]', err:err, answer:answer}
                     req.DNSErr = err

-                # TODO: find out why some requests appear to be getting lost!
-                #       (you can tell something is off cause a whole lot more
-                #       appears when using node method)
-
                 req.on 'end', =>
                     unless success
                         @log.warn {fn:sig+'[fail]', q:q, err:req.DNSErr}
@@ -90,7 +86,6 @@
                         @log.warn {fn:sig+'[fail]', q:q, err:err}
                         @sendErr res
                     else
-                        # addrs.forEach (a)-> res.answer.push ip2type(q.name, ttl)(a)
                         res.answer.push (addrs.map ip2type(q.name, ttl, QTYPE_NAME[q.type]))...
                         @log.debug {fn:sig+'[success]', answer:res.answer, q:q.name}
                         res.send()
@@ -110,8 +105,7 @@
             ttl = Math.floor(Math.random() * 3600) + 30
             @log.debug "received question", {q:q}

-            # for now we only handle A types.
-            # TODO: handle AAAA for IPv6!
+            # TODO: make sure we correctly handle AAAA
             # if q.type != NAME_QTYPE.A
             #     @log.debug "only support 'A' types ATM, deferring request!", {q:q}
             #     @oldDNSLookup(q, res)


Fix language lookup for file extensions

Fix language lookup for file extensions

--- src/library.coffee
+++ src/library.coffee
@@ -21,7 +21,7 @@
   'yaml': 'yml'
 extensionToLanguage = {}
 for lang, ext of languageExtensions
-  extensionToLanguage[ext] = lang
+  extensionToLanguage[".#{ext}"] = lang

 replaceMarker = (str, marker, value) ->
   marker = '#'+marker.toUpperCase()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -118,8 +118,8 @@
   @name meter
   @methodOf Metered#
   @param {Object} options The meter configuration options
-  @param {String} borderColor - Color of the meter's border
-  @param {Number} borderWidth - Width of the meter's border
+  @param {String} borderColor Color of the meter's border
+  @param {Number} borderWidth Width of the meter's border
     color - Color of the meter's inner rectangle
     nameColor - Color of the property name displayed above the meter
     font - Size and style of the meter's font


Wrap code example in backticks

Wrap code example in backticks

--- src/project.coffee
+++ src/project.coffee
@@ -308,7 +308,7 @@
   # * regex: A RegExp to search with
   # * replacementText: Text to replace all matches of regex with
   # * filePaths: List of file path strings to run the replace on.
-  # * iterator: A Function callback on each file with replacements. ({filePath, replacements}) ->
+  # * iterator: A Function callback on each file with replacements. `({filePath, replacements}) ->`
   replace: (regex, replacementText, filePaths, iterator) ->
     deferred = Q.defer()


routing: Complete sentence

routing: Complete sentence

--- src/routing.coffee
+++ src/routing.coffee
@@ -6,9 +6,9 @@
 #
 # TODO: split into two pieces
 # a) a Router, which implements message routing
-# with a message-queue based interface for listing and manipulating bindings.
+# with a message-queue based protocol for listing and manipulating bindings.
 # b) a Binder mixin for MessageBroker inteface,
-# which sends
+# which sends messsages on this protocol for add/removeBinding() and listBindings()
 #
 # This allows a single Router to exist in the network. It holds the canonical state of which
 # queues/topics are bound to eachother, and multiple processes can query and manipulate these.


fix(service): fix possible internal npe on de-register

--- src/service.coffee
+++ src/service.coffee
@@ -287,8 +287,7 @@
           if wrapped.handlers[address]
             index = wrapped.handlers[address].indexOf(callback)
             wrapped.handlers[address].splice(index, 1) if index > -1
-          if wrapped.handlers[address].length < 1
-            wrapped.handlers[address] = undefined
+            wrapped.handlers[address] = undefined if wrapped.handlers[address].length < 1
           return #void
         deconstructor.displayName = "#{CONSTANTS.MODULE}/#{CONSTANTS.COMPONENT}: wrapped.registerHandler (deconstructor)"
         return deconstructor


Don't fail if there is no entry in HUBOT_TWEETER_ACCOUNTS

--- src/tweeter.coffee
+++ src/tweeter.coffee
@@ -35,14 +35,16 @@
 config =
   consumer_key: process.env.HUBOT_TWITTER_CONSUMER_KEY
   consumer_secret: process.env.HUBOT_TWITTER_CONSUMER_SECRET
-  accounts: JSON.parse(process.env.HUBOT_TWEETER_ACCOUNTS) if process.env.HUBOT_TWEETER_ACCOUNTS?
+  accounts_json: process.env.HUBOT_TWEETER_ACCOUNTS

 unless config.consumer_key
   console.log "Please set the HUBOT_TWITTER_CONSUMER_KEY environment variable."
 unless config.consumer_secret
   console.log "Please set the HUBOT_TWITTER_CONSUMER_SECRET environment variable."
-unless config.accounts
+unless config.accounts_json
   console.log "Please set the HUBOT_TWEETER_ACCOUNTS environment variable."
+
+config.accounts = JSON.parse(config.accounts_json || "{}")

 module.exports = (robot) ->
   robot.respond /tweet\@([^\s]+)$/i, (msg) ->


Use metadata helper in super class

Use metadata helper in super class

--- src/upgrade.coffee
+++ src/upgrade.coffee
@@ -68,12 +68,7 @@
         @installedAtomVersion = version if semver.valid(version)
         callback()
     else
-      config.getResourcePath (resourcePath) =>
-        try
-          {version} = require(path.join(resourcePath, 'package.json')) ? {}
-          version = @normalizeVersion(version)
-          @installedAtomVersion = version if semver.valid(version)
-        callback()
+      @loadInstalledAtomMetadata(callback)

   getLatestVersion: (pack, callback) ->
     requestSettings =


Removed old Perl junk

Removed old Perl junk

--- src/wordnet.coffee
+++ src/wordnet.coffee
@@ -67,13 +67,7 @@
       detach.push word.substring(0, length - 3) if word.endsWith("est")
       detach.push word.substring(0, length - 2) if word.endsWith("est")

-
-
-    $self->removeDuplicates(\@detach);
-    return @detach;
-}
-
-
+  unique(detach)


Sidebar: Do not switch workspace on asynchronous event

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -652,7 +652,6 @@
       if state.status is Running
         machine.status.state = Running
         if appManager.getFrontApp().mountedMachineUId is machine.uid
-          @selectWorkspace { workspace, machine }
           delete @watchedMachines[machine._id]

     computeController.on "public-#{machine._id}", callback


Client: Activity: Added extra className to buttonMenuItemsList

--- client/app/lib/components/buttonwithmenu/index.coffee
+++ client/app/lib/components/buttonwithmenu/index.coffee
@@ -7,7 +7,10 @@

   WINDOW_OFFSET = 100

-  @defaultProps = { items: [], isMenuOpen: no}
+  @defaultProps =
+    items: []
+    isMenuOpen: no
+    listClass:''

   constructor: (props) ->

@@ -58,10 +61,10 @@

   render: ->
-

+

       <button ref="button" onClick={@bound 'onButtonClick'}></button>
       <Portal isOpened={@state.isMenuOpen} closeOnOutsideClick={yes} closeOnEsc={yes} onClose={@bound 'onMenuClose'}>
-

+

           {@renderListMenu()}

       </Portal>


Aligned requires

Aligned requires

--- client/app/lib/providers/computeplansmodalpaid.coffee
+++ client/app/lib/providers/computeplansmodalpaid.coffee
@@ -1,14 +1,14 @@
-globals = require 'globals'
-showError = require '../util/showError'
-kd = require 'kd'
-KDButtonView = kd.ButtonView
-KDSelectBox = kd.SelectBox
-KDView = kd.View
-ComputePlansModal = require './computeplansmodal'
-CustomLinkView = require '../customlinkview'
+globals                 = require 'globals'
+showError               = require '../util/showError'
+kd                      = require 'kd'
+KDButtonView            = kd.ButtonView
+KDSelectBox             = kd.SelectBox
+KDView                  = kd.View
+ComputePlansModal       = require './computeplansmodal'
+CustomLinkView          = require '../customlinkview'
 CustomPlanStorageSlider = require './customplanstorageslider'
-trackEvent = require 'app/util/trackEvent'
-remote = require('../remote').getInstance()
+trackEvent              = require 'app/util/trackEvent'
+remote                  = require('../remote').getInstance()

 module.exports = class ComputePlansModalPaid extends ComputePlansModal


Landing: handle oauth links correctly

Landing: handle oauth links correctly

--- client/landing/site.landing/coffee/core/routes.coffee
+++ client/landing/site.landing/coffee/core/routes.coffee
@@ -102,7 +102,7 @@
     # the routes below are subdomain routes
     # e.g. team.koding.com/Invitation
     '/Invitation/:token'   : handleInvitation
-    '/Account/Oauth'       : handleOauth
+    '/Home/Oauth'          : handleOauth
     # '/Welcome'             : handleTeamOnboardingRoute.bind this, 'welcome'
     '/Join'                : handleTeamOnboardingRoute.bind this, 'join'
     '/Authenticate/:step?' : handleTeamOnboardingRoute.bind this, 'stacks'


success arg does not exist

success arg does not exist

--- lib/db.coffee
+++ lib/db.coffee
@@ -57,7 +57,7 @@
       id = props.title.replace(/\s+/g, '').toLowerCase()
       projects[id] = props

-      @writeFile projects, (success) ->
+      @writeFile projects, () ->
         atom.notifications?.addSuccess "#{props.title} has been added"
         callback?(id)


Fixing Query.

Fixing Query.

--- source/stores/websocket.coffee
+++ source/stores/websocket.coffee
@@ -19,7 +19,7 @@
     @socket = new ws(path)
     @socket.addEventListener 'message', @route
     @socket.addEventListener 'open', -> callback()
-    setInterval (=> @socket.send('keepalive')), 2000
+    setInterval (=> @query('keepalive')), 2000

   # Closes the websocket connection
   close: -> @socket.close()
@@ -41,7 +41,7 @@
   # @private
   query: (type,data,callback)->
     id = @uid()
-    @map[id] = callback
+    @map[id] = callback if callback
     @socket.send JSON.stringify {id: id, type: type, data: data}

   # Lists component names contained in this store


Исправлено: подсветка кнопок для ul и ol перепутана

--- app/assets/javascripts/sitemplate_wysihat_cfg.js.coffee
+++ app/assets/javascripts/sitemplate_wysihat_cfg.js.coffee
@@ -242,7 +242,7 @@
         editor.handler.saveState()
         editor.toggleUnorderedList()
       query: (editor) ->
-        editor.orderedListSelected()
+        editor.unorderedListSelected()
     }
     ol: {
       name: 'ol',
@@ -259,7 +259,7 @@
         editor.handler.saveState()
         editor.toggleOrderedList()
       query: (editor) ->
-        editor.unorderedListSelected()
+        editor.orderedListSelected()
     }
     image: {
       name: 'image',


Client: Activity: DekstopNotifications: Updated isPermitted method of desktopNotifications to check default permission type

--- client/app/lib/desktopnotificationscontroller.coffee
+++ client/app/lib/desktopnotificationscontroller.coffee
@@ -17,7 +17,7 @@
 isPermitted = (callback) ->
   if Notification.permission is 'granted'
     callback()
-  else if Notification.permission is 'denied'
+  else if Notification.permission is 'default'
     Notification.requestPermission (permission) ->
       callback()  if permission is 'granted'


Collab: remove experimental notice

Collab: remove experimental notice

--- client/ide/lib/views/chat/idechatsettingspane.coffee
+++ client/ide/lib/views/chat/idechatsettingspane.coffee
@@ -269,7 +269,6 @@
       {{> @everyone}}

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

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


AppManager: Added quitByName method.

AppManager: Added quitByName method.

--- client/landing/app/MainApp/ApplicationManager.coffee
+++ client/landing/app/MainApp/ApplicationManager.coffee
@@ -225,6 +225,16 @@

     for own name, apps of @appControllers
       @quit app  for app in apps.instances
+
+  quitByName: (name, closeAllInstances = yes) ->
+    appController = @appControllers[name]
+    return  unless appController
+
+    if closeAllInstances
+      instances = appController.instances
+      @quit instances[0] while instances.length > 0
+    else
+      @quit appController.instances[appController.lastActiveIndex]

   get:(name)->


Add domain disposal on socket disconnect

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -154,6 +154,7 @@
         assert _.isFunction o = Marshal.serialize
         assert _.isFunction i = Marshal.deserialize
         assert session = socket?.handshake?.session
+        socket.on "disconnect", -> try guarded.dispose()
         assert _.isObject guarded = @guarded method, socket
         assert _.isFunction g = guarded.run.bind guarded
         s = (f) => session.save -> f.apply this, arguments


Fix param passing via g

Fix param passing via g

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -79,8 +79,8 @@
                 logger.error message.red, error.stack
                 try socket.disconnect?()
             assert _.isFunction g = guarded.run.bind guarded
-            execute = => g => method.apply this, i arguments
-            respond = => g => callback.apply this, o arguments
+            execute = (a...) => g => method.apply this, i(a)
+            respond = (a...) => g => callback.apply this, o(a)
             respond.socket = socket; execute args..., respond
         method.origin = this; return method


Fix incorrect var

Fix incorrect var

--- services/real-time/app/coffee/WebsocketController.coffee
+++ services/real-time/app/coffee/WebsocketController.coffee
@@ -108,8 +108,8 @@
 							for comment in ranges?.comments or []
 								comment.op.c = encodeForWebsockets(comment.op.c)
 							for change in ranges?.changes or []
-								change.op.i = encodeForWebsockets(comment.op.i) if change.op.i
-								change.op.d = encodeForWebsockets(comment.op.d) if change.op.d
+								change.op.i = encodeForWebsockets(change.op.i) if change.op.i
+								change.op.d = encodeForWebsockets(change.op.d) if change.op.d
 						catch err
 							logger.err {err, project_id, doc_id, fromVersion, ranges, client_id: client.id}, "error encoding range uri component"
 							return callback(err)


activity: remove deprecated cade

activity: remove deprecated cade

--- client/Social/Activity/views/activitylistitem.coffee
+++ client/Social/Activity/views/activitylistitem.coffee
@@ -129,8 +129,6 @@

   viewAppended:->

-    return if @getData().constructor is KD.remote.api.CStatusActivity
-
     JView::viewAppended.call this

     @setAnchors()


Client: Activity: Added renderCommentForm method. it renders comment input and send comment button

--- client/activity/lib/components/feeditem/index.coffee
+++ client/activity/lib/components/feeditem/index.coffee
@@ -113,6 +113,24 @@
       @setState { comment: value, hasValue: yes }, ->
         textInput.focus()

+
+  renderCommentForm: ->
+
+

+      <AutoSizeTextarea
+        ref         = 'textInput'
+        onFocus     = { @bound 'onFocus' }
+        onBlur      = { @bound 'onBlur' }
+        placeholder = 'Type your comment'
+        onChange    = { @bound 'handleCommentInputChange' }
+        value       = {@state.comment}
+        className   = 'FeedItem-commentInput'/>
+      <button
+        className={@getPostButtonClassNames()}
+        onClick={ @bound 'postComment' }>SEND</button>
+

+
+
   render: ->
     { message } = @props


Load STOCKOR customer by default for debugging

--- client/skr/screens/customer-maint/CustomerMaint.cjsx
+++ client/skr/screens/customer-maint/CustomerMaint.cjsx
@@ -17,6 +17,11 @@
         commands: new Lanes.Screens.Commands(this, modelName: 'customer')

     modelForAccess: 'customer'
+
+    componentDidMount: ->
+        _.defer =>
+            @getDOMNode().querySelector('input[name=code]').value = "STOCKOR"
+            @refs.finder.loadCurrentSelection()

     render: ->
         <div className="customer-maint">


Fixing localstorage check. Breaks on some Android phones

--- shared/store/local_storage_store.coffee
+++ shared/store/local_storage_store.coffee
@@ -28,4 +28,7 @@
     @_keys = {}

   @canHaz = ->
-    window?.localStorage?
+    try
+      window?.localStorage?
+    catch e
+      return false


activity: fix channel unread counts are not reset with glance in other open tabs

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -127,7 +127,14 @@
     @removeItem id

-  handleGlanced: (update) -> @selectedItem?.setUnreadCount? update.unreadCount
+  handleGlanced: (update) ->
+    { channel } = update
+
+    return  unless channel
+
+    return  unless item = @itemsById[channel.id]
+
+    item.setUnreadCount? update.unreadCount

   glanceChannelWorkspace: (channel) ->


Refine special case of calling super() within an iterator.

--- port/CalendarMonthWithHeadings.coffee
+++ port/CalendarMonthWithHeadings.coffee
@@ -24,8 +24,8 @@

   # The control's current culture.
   culture: Control.iterator ( culture ) ->
-    # Inside iterator, so can't do simply result = super culture
-    result = @constructor.superclass::culture.call( this, culture )
+    # Inside iterator, so can't simply do result = super culture
+    result = @constructor.__super__.culture culture
     if culture isnt undefined
       @$monthAndYear().culture culture
       @$daysOfWeek().culture culture


Inlined alert function

Inlined alert function

--- core/app/backbone/modules/notification_center.coffee
+++ core/app/backbone/modules/notification_center.coffee
@@ -25,10 +25,6 @@

   FactlinkApp.alertsRegion.show alertsView

-  alert = (type, message) ->
-    alerts.add new NotificationCenter.Alert message: message, type: type
-
-  NotificationCenter.info    = -> alert('info',   arguments...)
-  NotificationCenter.success = -> alert('success',arguments...)
-  NotificationCenter.error   = -> alert('error',  arguments...)
-
+  NotificationCenter.info    = -> alerts.add new NotificationCenter.Alert message: message, type: 'info'
+  NotificationCenter.success = -> alerts.add new NotificationCenter.Alert message: message, type: 'success'
+  NotificationCenter.error   = -> alerts.add new NotificationCenter.Alert message: message, type: 'error'


If error occurred when fetching compiledApp force to refresh from FS, and show a notification about that.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -154,9 +154,12 @@
     appPath = getAppPath manifest
     indexJsPath = "#{appPath}/index.js"
     @kiteController.run "cat #{escapeFilePath indexJsPath}", (err, response)=>
-      if err then warn err
+      if err
+        new KDNotificationView
+          title    : "App list is out-dated, refreshing apps..."
+          duration : 2000
+        @refreshApps noop
       callback err, response
-

   # #
   # MISC


ContentSearch: Handle error.

ContentSearch: Handle error.

--- client/ide/views/contentsearch/idecontentsearch.coffee
+++ client/ide/views/contentsearch/idecontentsearch.coffee
@@ -19,8 +19,9 @@
   search: ->
     vmController = KD.getSingleton 'vmController'
     @searchText  = Encoder.XSSEncode @input.getValue()
+    vmController.run "ag '#{@searchText}' '#{@rootPath}' -C 3 --ackmate --stats", (err, res) =>
+      return @showWarning 'Something went wrong, please try again.', yes  if err or res.stderr

-    vmController.run "ag '#{@searchText}' -C 3 --ackmate --stats", (err, res) =>
       @formatOutput res.stdout, @bound 'createResultsView'

   formatOutput: (output, callback = noop) ->


Revert removal of closeModal function, _events get cleared before onClose

--- client/src/views/report_edit_visualisation_view.coffee
+++ client/src/views/report_edit_visualisation_view.coffee
@@ -6,7 +6,7 @@
   template: Handlebars.templates['report_edit_visualisation.hbs']

   events:
-    "click .close": "close"
+    "click .close": "closeModal"
     "click .save": "save"
     "change select[name='visualisation']": 'updateVisualisationType'

@@ -33,9 +33,12 @@
   save: ->
     @visualisation.save()

+  closeModal: ->
+    @trigger('close')
+    @close()
+
   onClose: ->
     $('body').removeClass('stop-scrolling')
-    @trigger('close')
     @closeSubViews()
     @stopListening()


Stopgap solution for totalScore being string

--- app/schemas/models/level_session.coffee
+++ app/schemas/models/level_session.coffee
@@ -235,7 +235,7 @@
               totalScore:
                 title: 'Opponent total score'
                 description: 'The totalScore of a user when the match was computed'
-                type: 'number'
+                type: ['number','string']
               metrics:
                 type: 'object'
                 properties:


Activity: includes file updated

Activity: includes file updated

--- client/Social/Activity/includes.coffee
+++ client/Social/Activity/includes.coffee
@@ -15,15 +15,15 @@
   "views/feedcoverphotoview.coffee",
   "views/activityactions.coffee",
   "views/activitylistheader.coffee",
-  "views/activitysplitview.coffee",
-  "views/activityitemchild.coffee",
-  "views/discussionactivityactions.coffee",
-  "views/tutorialactivityactions.coffee",
-  "views/newmemberbucket.coffee",
+  # "views/activitysplitview.coffee",
+  # "views/discussionactivityactions.coffee",
+  # "views/tutorialactivityactions.coffee",
+  # "views/newmemberbucket.coffee",
   "views/activitytickeritem.coffee",
   "views/referalbox.coffee",
-  "views/activitylistcontainer.coffee",
+  # "views/activitylistcontainer.coffee",
   "views/filterwarning.coffee",
+  "views/activitymenuitem.coffee",

   "views/embedbox.coffee",
   "views/embedboxwidget.coffee",
@@ -83,8 +83,7 @@
   "ContentDisplays/ContentDisplayScoreBoard.coffee",

   # Activity List Items
-  "ListItems/ActivityListItem.coffee",
-  "ListItems/ActivityListItemStatusUpdate.coffee",
+  "ListItems/activitylistitem.coffee",

   "styl/activity.styl"
   "styl/activity.responsive.styl"


Client: Fix boost amounts displaying wrong.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -350,8 +350,9 @@
         when "accuracy" then "Acc."
         when "evasion" then "Eva."
         else stat
+      multiplier = (2 + amount) / 2
       amount = "+#{amount}"  if amount > 0
-      abbreviatedStat = "#{amount} (x#{(2 + amount) / 2}) #{abbreviatedStat}"
+      abbreviatedStat = "#{amount} (x#{multiplier}) #{abbreviatedStat}"
       $effect = @addPokemonEffect($pokemon, "boost #{stat}", abbreviatedStat)
       if amount < 0
         $effect.addClass('negative')


Client: Fix bugs with unattaching hazards.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -600,7 +600,7 @@
       $effect.text(text)
     $effect

-  unattachTeam: (slot, effect, done) =>
+  unattachTeam: (player, attachment, done) =>
     $battlePane = @$('.battle_pane')
     switch attachment
       when "StealthRockAttachment"
@@ -680,7 +680,6 @@
       done()
       return

-    $image = $pokemon.find('.sprite img')
     move($image).set('top', '100%').duration('.25s').ease('ease-in').end()
     move($image).set('opacity', 0).end ->
       $image.addClass('hidden')


Fix Fetch : save currentURL not to fetch twice the same data

--- client/app/stores/message_store.coffee
+++ client/app/stores/message_store.coffee
@@ -3,6 +3,7 @@
 AppDispatcher = require '../app_dispatcher'

 AccountStore = require './account_store'
+
 SocketUtils = require '../utils/socketio_utils'

 {ActionTypes, MessageFlags, MessageFilter, FlagsConstants} =
@@ -32,6 +33,7 @@

     _queryRef = 0
     _nextUrl = null
+    _currentUrl = null
     _noMore = false

     _currentMessages = Immutable.OrderedMap()
@@ -475,12 +477,12 @@

         sequence = _messagesWithInFlights()
         sequence = sequence.filter inMailbox mailboxID
-
         if _filterType is 'flag'
             sequence = sequence.filter _matchFlag

         else if _filterType is 'date'
             sequence = sequence.filter _matchRangeDate
+

         # We dont filter for _filterType from and dest because it is
         # complicated by collation and name vs address.
@@ -612,28 +614,31 @@
         after: _filterAfter
         hasNextPage: not _noMore

+    getCurrentURL: ->
+        mailboxID = AccountStore.getSelectedMailbox().get 'id'
+        sort = if _filterType in ['from', 'dest']
+            encodeURIComponent "+#{_filterType}"
+        else
+            encodeURIComponent "#{_sortOrder}#{_sortField}"
+
+        url = "mailbox/#{mailboxID}/?sort=#{sort}"
+        if _filterType is 'flag' and _filterValue isnt '-'
+            url += "&flag=#{_filterValue}"
+
+        if _filterBefore isnt '-'
+            url += "&before=#{encodeURIComponent _filterBefore}"
+
+        if _filterAfter isnt '-'
+            url += "&after=#{encodeURIComponent _filterAfter}"
+        return url
+
     getNextUrl: ->
-        if _noMore
+        if _nextUrl
+            return _nextUrl
+        else if _noMore or _currentUrl is (url = @getCurrentURL())
             return null
-        else if _nextUrl
-            _nextUrl
         else
-            mailboxID = AccountStore.getSelectedMailbox().get 'id'
-            sort = if _filterType in ['from', 'dest']
-                encodeURIComponent "+#{_filterType}"
-            else
-                encodeURIComponent "#{_sortOrder}#{_sortField}"
-
-            url = "mailbox/#{mailboxID}/?sort=#{sort}"
-            if _filterType is 'flag' and _filterValue isnt '-'
-                url += "&flag=#{_filterValue}"
-
-            if _filterBefore isnt '-'
-                url += "&before=#{encodeURIComponent _filterBefore}"
-
-            if _filterAfter isnt '-'
-                url += "&after=#{encodeURIComponent _filterAfter}"
-
+            _currentUrl = url
             return url


remove detail rows when filtering location table

--- client/controllers/locationList.coffee
+++ client/controllers/locationList.coffee
@@ -59,6 +59,10 @@
     }

 Template.locationList.events
+  "keyup .reactive-table-input": (event, template) ->
+    if event.target.value.length
+      template.$("tr").removeClass("details-open")
+      template.$("tr.tr-details").remove()
   "click #event-locations-table th": (event, template) ->
     template.$("tr").removeClass("details-open")
     template.$("tr.tr-details").remove()


client: home: reduce chatlio view to single section

--- client/home/lib/utilities/index.coffee
+++ client/home/lib/utilities/index.coffee
@@ -1,11 +1,10 @@
-kd                                     = require 'kd'
-sectionize                             = require '../commons/sectionize'
-headerize                              = require '../commons/headerize'
-HomeUtilitiesKD                        = require './homeutilitieskd'
-HomeUtilitiesTryOnKoding               = require './homeutilitiestryonkoding'
-HomeUtilitiesDesktopApp                = require './homeutilitiesdesktopapp'
-HomeUtilitiesCustomerFeedback          = require './homeutilitiescustomerfeedback'
-HomeUtilitiesCustomerFeedbackSecondary = require './homeutilitiescustomerfeedbacksecondary'
+kd                            = require 'kd'
+sectionize                    = require '../commons/sectionize'
+headerize                     = require '../commons/headerize'
+HomeUtilitiesKD               = require './homeutilitieskd'
+HomeUtilitiesTryOnKoding      = require './homeutilitiestryonkoding'
+HomeUtilitiesDesktopApp       = require './homeutilitiesdesktopapp'
+HomeUtilitiesCustomerFeedback = require './homeutilitiescustomerfeedback'

 module.exports = class HomeUtilities extends kd.CustomScrollView

@@ -26,11 +25,4 @@

     @wrapper.addSubView headerize  'Integrations'

-    chatlio          = sectionize 'Customer Feedback', HomeUtilitiesCustomerFeedback
-    chatlioSecondary = sectionize 'Customer Feedback Secondary', HomeUtilitiesCustomerFeedbackSecondary, { cssClass: 'hidden' }
-
-    @wrapper.addSubView chatlio
-    @wrapper.addSubView chatlioSecondary
-
-    chatlio.on 'ChatlioActivated',   -> chatlioSecondary.show()
-    chatlio.on 'ChatlioDeactivated', -> chatlioSecondary.hide()
+    @wrapper.addSubView sectionize 'Customer Feedback', HomeUtilitiesCustomerFeedback


Update RootView.deserialize specs so they break on a version mismatch

--- spec/app/root-view-spec.coffee
+++ spec/app/root-view-spec.coffee
@@ -31,7 +31,7 @@
         viewState = rootView.serialize()
         rootView.deactivate()

-        window.rootView = RootView.deserialize(viewState)
+        window.rootView = deserialize(viewState)
         rootView.attachToDom()

         expect(rootView.getEditors().length).toBe 2
@@ -55,7 +55,7 @@

           viewState = rootView.serialize()
           rootView.deactivate()
-          window.rootView = RootView.deserialize(viewState)
+          window.rootView = deserialize(viewState)
           rootView.attachToDom()

           expect(rootView.getEditors().length).toBe 4
@@ -92,7 +92,7 @@

           viewState = rootView.serialize()
           rootView.deactivate()
-          window.rootView = RootView.deserialize(viewState)
+          window.rootView = deserialize(viewState)

           rootView.attachToDom()
           expect(rootView.getEditors().length).toBe 0


ticking items off gokmen's list…  temporary fix for broken images in Apps

--- client/landing/app/MainApp/main.coffee
+++ client/landing/app/MainApp/main.coffee
@@ -1,82 +1,101 @@
 #Broker.channel_auth_endpoint = KD.config.apiUri+'/1.0/channel/auth';
 #Broker.channel_auth_endpoint = 'http://localhost:8008/auth'

-do ->
-  mainController             = new MainController
-  firstLoad                  = yes
-  connectionLostModal        = null
-  connectionLostNotification = null
-
-  destroyNotification = ->
-    connectionLostNotification?.destroy()
-    connectionLostNotification = null
-
-  # destroyConnectionSetNotification = ->
-  #   connectionSetNotification?.destroy()
-  #   connectionSetNotification = null
-
-  destroyModal = (reconnected = no)->
-    connectionLostModal?.destroy()
-    destroyNotification()
-    if reconnected isnt no
-      new KDNotificationView
-        title         : "Reconnected"
-        type          : "tray"
-        content       : "Server connection has been reset, you can continue working."
-        duration      : 3000
-    else
-      connectionLostNotification = new KDNotificationView
-        title         : "Trying to reconnect..."
-        type          : "tray"
-        closeManually : no
-        content       : "Server connection has been lost, changes will not be saved until server reconnects, please back up locally."
-        duration      : 0
-      connectionLostModal = null
-
-  showModal = ->
-    return if connectionLostModal
-    destroyNotification()
-    connectionLostModal = new KDBlockingModalView
-      title   : "Server connection lost"
-      content : """
-

-          Your internet connection may be down, or our server is.

-          If you have unsaved work please close this dialog and
back up your unsaved work locally until the connection is re-established.

-           Trying to reconnect...
-

-        """
-      height  : "auto"
-      overlay : yes
-      buttons :
-        "Close and Refresh later" :
-          style     : "modal-clean-red"
-          callback  : -> destroyModal()
-
-    connectionLostModal.once "KDObjectWillBeDestroyed", -> destroyModal()
-
-  ###
-  # CONNECTIVITY EVENTS
-  ###
-
-  KD.remote.on 'loggedInStateChanged', (account)->
-    KD.socketConnected()
-    mainController.accountChanged account
-    AccountMixin.init(KD.remote.api)
-
-
-  KD.remote.on 'connected', ->
-    if firstLoad
-      log 'kd remote connected'
-      firstLoad = no
-    else
-      log 'kd remote re-connected'
-      destroyModal yes
-
-  KD.remote.on 'disconnected', ->
-    # to avoid modal to appear on page refresh
-    __utils.wait 500, showModal
-
-  KD.remote.connect()
+# THIS OVERRIDE NEEDS TO WAIT
+# NOT YET TOTALLY INTEROPERABLE
+# WITH Encoder.htmlDecode
+
+# Encoder.htmlEncode = do->
+
+#    htmlMap =
+#      '&' : 'amp'
+#      '<' : 'lt'
+#      '"' : 'quot'
+#      '<' : 'lt'
+#      '>' : 'gt'
+#      "'" : '#39'
+#      '`' : '#96'
+#      '!' : '#33'
+#      '@' : '#36'
+#      '%' : '#37'
+#      '(' : '#40'
+#      ')' : '#41'
+#      '=' : '#61'
+#      '+' : '#43'
+#      '{' : '#123'
+#      '}' : '#125'
+#      '[' : '#91'
+#      ']' : '#93'
+
+#    (str)-> str.replace /(&(?!\w\w+;)|'|<|>|"|'|`|\!|\@|\$|\%|\(|\)|\=|\+|\{|\}|\[|\])/g, (match)-> "&#{htmlMap[match]};"
+
+mainController = new MainController
+
+
+KD.remote.on 'loggedInStateChanged', (account)->
+  KD.socketConnected()
+  mainController.accountChanged account
+  AccountMixin.init(KD.remote.api)
+
+# Pistachio.MODE = if KD.env is 'dev' then 'development' else 'production'
+
+#$.cookie 'clientId', localStorage.clientId
+
+# Cacheable = new Cacheable
+
+# setTimeout ->
+#   appManager.openApplication("Chat")
+# ,5000
+
+firstLoad = yes
+connectionLostModalId = null
+connectionLostNotification = null
+
+KD.remote.on 'connected', -> console.log('kd connected')
+
+KD.remote.on 'disconnected', ->
+  #
+  # CONNECTIVITY NOTIFICATIONS PART START
+  #
+  setTimeout -> # to avoid modal to appear on page refresh
+
+    if connectionLostNotification?
+      connectionLostNotification.destroy()
+      connectionLostNotification = null
+
+    # THE MODAL APPEARS WHEN CONNECTION IS LOST
+    unless connectionLostModalId?
+      connectionLostModalId = KDModalController.createAndShowNewModal
+        type    : 'blocking'
+        title   : "Server connection lost"
+        content : """
+

+            Your internet connection may be down, or our server is.

+            If you have unsaved work please close this dialog and
back up your unsaved work locally until the connection is re-established.

+             Trying to reconnect...
+

+          """
+        height  : "auto"
+        overlay : yes
+        buttons :
+          "Close and Refresh later" :
+            style     : "modal-clean-red"
+            callback  : ()->
+              @propagateEvent KDEventType:'KDModalShouldClose'
+              connectionLostModalId = null
+              if connectionLostNotification?
+                connectionLostNotification.destroy()
+                connectionLostNotification = null
+
+              updateModalActive = no
+
+              # THE NOTIFICATION APPEARS WHEN MODAL WAS CLOSED BEFORE CONNECTION RE-ESTABLISHES
+              connectionLostNotification = new KDNotificationView
+                title    : "Server Connection Has Been Lost"
+                content  : "Trying to reconnect..., changes will not be saved until server reconnects, please back up locally."
+                duration : 0
+
+KD.remote.connect -> console.log 'koding is now connected to the backend'

   #initConnectionEvents conn


Wait for $el before rebinding form

Wait for $el before rebinding form

--- client/lanes/views/FormBindings.coffee
+++ client/lanes/views/FormBindings.coffee
@@ -3,7 +3,7 @@
     constructor: (@view, config={})->
         _.bindAll(this,'onFieldChange')
         @selector = if config.selector then "#{config.selector} " else ""
-        @view.on( "change:el",     this.rebindForm,    this )
+        @view.on( "change:$el",    this.rebindForm,    this )
         @view.on( "change:model",  this.rebindModel,   this )
         @view.on( "remove",        this.teardown,      this )
         this.rebindModel( @view, @view.model )


workers/social: remove active items model

--- workers/social/lib/social/models/activeitems.coffee
+++ workers/social/lib/social/models/activeitems.coffee
@@ -1,149 +0,0 @@
-Bongo          = require "bongo"
-{Relationship} = require "jraphical"
-_              = require "underscore"
-
-JAccount       = require "./account"
-JTag           = require "./tag"
-Cache          = require "../cache/main"
-
-{secure, daisy, Base, signature} = Bongo
-
-module.exports = class ActiveItems extends Base
-  @share()
-  JGroup         = require './group'
-  @set
-    sharedMethods   :
-      static        :
-        fetchTopics : [
-          (signature Function)
-          (signature Object, Function)
-        ]
-        fetchUsers  : [
-          (signature Function)
-          (signature Object, Function)
-        ]
-
-  nameMapping =
-    user    :
-      klass : JAccount
-      as    : ["creator", "follower"]
-    topic   :
-      klass : JTag
-      as    : ["developer", "follower", "post"]
-      select: status : $nin : ["deleted", "synonym"]
-
-  # Returns topics in following order:
-  #   * Active topics in the last day
-  #   * Random topics
-  @fetchTopics = secure (client, options, callback)->
-    options.name  = "topic"
-    options.group = client.context.group
-    @fetchRandomTopics callback, options
-
-  # Returns users in following order:
-  #   * Client's followers who are online
-  #   * Active users in the last day
-  #   * Random users
-  @fetchUsers = secure (client, options, callback)->
-    [callback, options] = [options, callback]  unless callback
-    options.client      = client
-    options.group       = client.context.group
-
-    # options.fallbackFn  = @fetchRandomUsers
-
-    # JGroup.canListMembers client, (err, hasPermission)=>
-    #   if err or not hasPermission
-    #     return callback new Error "Not allowed to list members of this group"
-        # Cache.fetch "activeItems.fetchUsers", (@_fetchUsers.bind this), options, callback
-
-    @fetchRandomUsers callback
-
-  @fetchRandomUsers = (callback)->
-    JAccount.some {"type":"registered"}, {limit:10}, callback
-
-  @fetchRandomTopics = (callback, options)->
-    group = options.group or "koding"
-    {select: selector} = nameMapping.topic
-    selector.group = group
-    JTag.some selector, {limit:10, sort:"counts.followers":-1}, callback
-
-  @_fetchUsers = (options={}, callback)->
-    {client}   = options
-    {delegate} = client.connection
-
-    delegate._fetchMyOnlineFollowingsFromGraph client, {}, (err, onlineMembers)=>
-      return callback err                  if err
-      return callback null, onlineMembers  if onlineMembers.length >= 10
-
-      missing     = 10 - onlineMembers.length
-      existingIds = onlineMembers.map (member)-> member._id
-
-      @fetchItems {name: "user", count:missing, nin:existingIds}, (err, activeMembers)->
-        return callback err  if err
-        members = onlineMembers.concat activeMembers
-        callback null, members
-
-  # DO NOT USE...THIS WILL BRING DOWN KODING: SA
-  #
-  # General method that returns popular items in the last day. If none
-  # exists, it returns random items.
-  #
-  # Popularity is determined by number of entries in 'relationships'
-  # collection that match the criteria passed to it.
-  @fetchItems = (options, callback) ->
-    [callback, options] = [options, callback]  unless callback
-    {name, nin} = options
-    mapping     = nameMapping[name]
-    {klass, as, select} = mapping
-
-    greater = (new Date(Date.now() - 1000*60*60*24))
-
-    matcher     = {
-      sourceName : klass.name
-      as         : $in  : as
-      timestamp  : $gte : greater
-      targetName : $ne : "CFolloweeBucketActivity"
-    }
-
-    matcher.sourceId = $nin : nin  if nin
-
-    limit = options.limit or 10
-
-    Relationship.getCollection().aggregate {$match: matcher},
-      {$group:{_id:"$sourceId", total:{$sum:1}}},
-      {$limit:limit},
-    , (err, items)->
-      return callback err  if err
-
-      items = _.sortBy items, (item)-> item.sum
-      items = items.reverse()
-      items = items[0..10]
-
-      instances = []
-      daisy queue = items.map (item) ->
-        ->
-          klass.one _id: item._id, (err, instance)->
-            if not err and instance
-              instances.push instance
-            queue.next()
-
-      queue.push ->
-        # If there are not enough popular results, we return random items.
-        missing = 10 - instances.length
-        if missing > 0
-          existingIds = instances.map (i) -> i._id
-          existingIds = nin.concat existingIds  if nin
-
-          selector = {}
-          selector = select if select
-          selector.sourceId = {$nin : existingIds}
-          klass.some selector, {limit:missing}, (err, randomInstances)->
-            if not err and randomInstances
-              instances = instances.concat randomInstances
-            queue.next()
-        else
-          queue.next()
-
-      queue.push ->
-        instances = _.uniq instances, (i)-> i._id
-        callback null, instances


graph: spelling mistake

graph: spelling mistake

--- workers/social/lib/social/models/graph/fetch.coffee
+++ workers/social/lib/social/models/graph/fetch.coffee
@@ -78,7 +78,7 @@
         if @bucketNames()[value.type]
           oldSnapshot = JSON.parse(value.snapshot)
           oldSnapshot._id = randomId
-          oldSnapshot.subscribeable = false
+          oldSnapshot.bongo_.subscribable = false
           value.snapshot = JSON.stringify oldSnapshot

         @cacheObjects[randomId] = value


[social][graph] fixed query and getting secret groups logic

--- workers/social/lib/social/models/graph/graph.coffee
+++ workers/social/lib/social/models/graph/graph.coffee
@@ -81,31 +81,34 @@
   getSecretGroups:(client, callback)->
     JGroup = require '../group'
     JGroup.some
-      privacy: "private"
-      visibility: "hidden"
+      $or : [
+        { privacy: "private" }
+        { visibility: "hidden" }
+      ]
       slug:
         $nin: ["koding"] # we need koding even if its private
     , {}, (err, groups)=>
       if err then return callback err
       else
+        if groups.length < 1 then callback null, []
         secretGroups = []
         checkUserCanReadActivity = race (i, {client, group}, fin)=>
           group.canReadActivity client, (err, res)=>
-            secretGroups.push group.slug unless err
+            secretGroups.push group.slug if err
             fin()
-        , =>
-          callback null, secretGroups
+        , -> callback null, secretGroups
         for group in groups
           checkUserCanReadActivity {client: client, group: group}

   removePrivateContent:(client, groupId, contents, callback)->
-      @getSecretGroups client, (err, secretGroups)=>
-        if err then return callback err
-        else
-          filteredContent = []
-          for content in contents
-            filteredContent.push content if content.group not in secretGroups
-          callback null, filteredContent
+    if contents.length < 1 then return callback null, contents
+    @getSecretGroups client, (err, secretGroups)=>
+      if err then return callback err
+      if secretGroups.length < 1 then return callback null, contents
+      filteredContent = []
+      for content in contents
+        filteredContent.push content if content.group not in secretGroups
+      return callback null, filteredContent

   neo4jFacets = [
     "JLink"


groups: silence the error of group bot message create procedure

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -462,10 +462,7 @@
               console.log 'roles are added'
               queue.next()
         ->
-          # CtF hacked because we need client to create a post
-          group.createGroupBotAndPostMessage client, (err) ->
-            return callback err if err
-            console.log 'bot is added and posted its first message'
+          group.createGroupBotAndPostMessage client, ->
             queue.next()
       ]


jgroup : style fix.

jgroup : style fix.

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1405,10 +1405,9 @@
       JInvitation.some
         group  : @slug
         type   : type
-        status :
-          $in  : status
-        , options
-        , callback
+        status : $in : status
+      , options
+      , callback

   fetchInvitationsFromGraph: permit 'send invitations',
     success: (client, type, options, callback)->


Automatic atom changes

Automatic atom changes

--- atom/atom.symlink/config.cson
+++ atom/atom.symlink/config.cson
@@ -11,7 +11,7 @@
     showInvisibles: true
     tabType: "soft"
     fontSize: 12
-    fontFamily: "Droid Sans Mono for Powerline"
+    fontFamily: "Hack"
     autoIndentOnPaste: false
   core:
     audioBeep: false
@@ -131,6 +131,28 @@
     preferredLineLength: 120
 ".gfm.source .code":
   softWrap: false
+".js.regexp.source":
+  editor:
+    preferredLineLength: 120
+".css.less.source":
+  "multi-wrap-guide":
+    columns: [
+      90
+    ]
+  editor:
+    preferredLineLength: 90
+".basic.html.text":
+  editor:
+    showInvisibles: true
+".console.python.text":
+  editor:
+    tabLength: 4
+".python.regexp.source":
+  editor:
+    preferredLineLength: 80
+".python.text.traceback":
+  editor:
+    tabLength: 4
 ".css.scss.source":
   "multi-wrap-guide":
     columns: [
@@ -139,68 +161,46 @@
   "go-plus":
     goPath: "~/Development/Go"
     goInstallation: "/usr/local/opt/go/libexec/bin"
-".python.text.traceback":
-  editor:
-    tabLength: 4
-".python.regexp.source":
-  editor:
-    preferredLineLength: 80
-".console.python.text":
-  editor:
-    tabLength: 4
-".basic.html.text":
-  editor:
-    showInvisibles: true
-".css.less.source":
-  "multi-wrap-guide":
-    columns: [
-      90
-    ]
-  editor:
-    preferredLineLength: 90
-".js.regexp.source":
-  editor:
-    preferredLineLength: 120
-".js.source":
-  editor:
-    preferredLineLength: 80
-    softWrapAtPreferredLineLength: true
-  "multi-wrap-guide":
-    columns: [
-      80
-    ]
-  "wrap-guide":
-    enabled: false
-".python.source":
-  editor:
-    preferredLineLength: 80
-    tabLength: 4
-    showInvisibles: true
-".bibtex.text":
-  editor:
-    preferredLineLength: 100
-".haml.text":
-  "wrap-guide":
-    enabled: false
-".git-commit.text":
-  editor:
-    preferredLineLength: 72
-  "multi-wrap-guide":
-    columns: [
-      72
-    ]
-".gfm.source":
-  editor:
-    softWrap: true
-  "wrap-guide":
-    enabled: false
-".html.source":
-  editor:
-    softWrap: false
-    showIndentGuide: false
 ".julia.source":
   editor:
     preferredLineLength: 100
     softWrap: false
     tabLength: 2
     scrollPastEnd: true
+".html.source":
+  editor:
+    softWrap: false
+    showIndentGuide: false
+".gfm.source":
+  editor:
+    softWrap: true
+  "wrap-guide":
+    enabled: false
+".git-commit.text":
+  editor:
+    preferredLineLength: 72
+  "multi-wrap-guide":
+    columns: [
+      72
+    ]
+".haml.text":
+  "wrap-guide":
+    enabled: false
+".bibtex.text":
+  editor:
+    preferredLineLength: 100
+".python.source":
+  editor:
+    preferredLineLength: 80
+    tabLength: 4
+    showInvisibles: true
+".js.source":
+  editor:
+    preferredLineLength: 80
+    softWrapAtPreferredLineLength: true
+  "multi-wrap-guide":
+    columns: [
+      80
+    ]
+  "wrap-guide":
+    enabled: false


moved throttle closer to invocation

moved throttle closer to invocation

--- core/app/backbone/collections/search_collection.coffee
+++ core/app/backbone/collections/search_collection.coffee
@@ -1,6 +1,3 @@
-throttle = (method) ->
-  _.throttle method, 300, leading: false
-
 class window.SearchCollection extends Backbone.Collection
   constructor: (args...)->
     super(args...)
@@ -10,6 +7,7 @@
     @query = ''
     @reset []

+  throttle = (method) -> _.throttle method, 300, leading: false
   searchFor: throttle (query) ->
     query = $.trim(query)
     return if query == @query


Test for a single decoration.

Test for a single decoration.

--- spec/editor-marker-spec.coffee
+++ spec/editor-marker-spec.coffee
@@ -39,9 +39,18 @@
       editorDecorator(editor)
       expect(editorView.find '.line.line-stackframe').toHaveLength 0

-  it "does nothing if the file doesn't appear in the active trace", ->
-    trace.activate()
+  describe 'with an active trace', ->

-    withEditorOn 'context.txt', ->
-      editorDecorator(editor)
-      expect(editorView.find '.line.line-stackframe').toHaveLength 0
+    beforeEach -> trace.activate()
+
+    it "does nothing if the file doesn't appear in the active trace", ->
+      withEditorOn 'context.txt', ->
+        editorDecorator(editor)
+        expect(editorView.find '.line.line-stackframe').toHaveLength 0
+
+    it 'decorates stackframe lines in applicable editors', ->
+      withEditorOn 'bottom.rb', ->
+        editorDecorator(editor)
+        decorated = editorView.find '.line.line-stackframe'
+        expect(decorated).toHaveLength 1
+        expect(decorated.text()).toEqual("  puts 'this is the stack line'")


Change to show all reservations

Change to show all reservations

--- server/controllers/shopmanagement/reservations.coffee
+++ server/controllers/shopmanagement/reservations.coffee
@@ -6,7 +6,7 @@
 exports.list = (req, res) ->
   shopId = req.session.currentShop.shopId

-  Reservation.find({redeemed: true}).populate('deal').populate('deal.shop').exec( (err, reservations) ->
+  Reservation.find({}).populate('deal').populate('deal.shop').exec( (err, reservations) ->

     shopReservations = []


Browser: syntax highlighting for (i)like operator.

--- community/browser/app/scripts/codemirror-cypher.coffee
+++ community/browser/app/scripts/codemirror-cypher.coffee
@@ -72,7 +72,7 @@
   indentUnit = config.indentUnit
   curPunc = undefined
   funcs = wordRegexp(["str", "min", "labels", "max", "type", "lower", "upper", "length", "type", "id", "coalesce", "head", "last", "nodes", "relationships", "extract", "filter", "tail", "range", "reduce", "abs", "round", "sqrt", "sign", "replace", "substring", "left", "right", "ltrim", "rtrim", "trim", "collect", "distinct", "split", "toInt", "toFloat"])
-  preds = wordRegexp(["all", "any", "none", "single", "not", "in", "has", "and", "or"])
+  preds = wordRegexp(["all", "any", "none", "single", "not", "in", "has", "and", "or", "like", "ilike"])
   keywords = wordRegexp(["start", "merge", "load", "csv", "using", "periodic commit", "on create", "on match", "match", "index on", "drop", "where", "with", "limit", "skip", "order", "by", "return", "create", "delete", "set", "unique", "unwind"])
   operatorChars = /[*+\-<>=&|~]/
   startState: (base) ->


JS: update app/application/controller.coffee

--- core/htdocs_source/app/application/controller.coffee
+++ core/htdocs_source/app/application/controller.coffee
@@ -1,7 +1,6 @@
-import Em from "components-ember"
+import Controller, { inject as injectCtrl } from '@ember/controller'
+import { alias } from '@ember/object/computed'

-Controller = Em.ArrayController.extend
-    needs: ["openxpki"]
-    user: Em.computed.alias "controllers.openxpki.model.user"
-
-export default Controller
+export default Controller.extend
+    openxpki: injectCtrl()      # injects OpenxpkiController
+    user: alias "openxpki.model.user"


Tile view uses Project factory

Tile view uses Project factory

--- spec/javascripts/backbone/views/tile_view_spec.js.coffee
+++ spec/javascripts/backbone/views/tile_view_spec.js.coffee
@@ -1,10 +1,9 @@
 describe "ProjectMonitor.Views.TileView", ->
   describe "one tile view", ->
     beforeEach ->
-      model = new Backbone.Model()
-      model.set build: new ProjectMonitor.Models.Build(name: 'Project Monitor', aggregate: false, statuses: [true, false, true], last_build: "4d")
+      @model = BackboneFactory.create('project')

-      view = new ProjectMonitor.Views.TileView(model: model)
+      view = new ProjectMonitor.Views.TileView(model: @model)
       setFixtures(view.render().$el)

     it "should include build view", ->
@@ -12,14 +11,13 @@

   describe "four tile view", ->
     beforeEach ->
-      model = new Backbone.Model()
-      model.set build: new ProjectMonitor.Models.Build(name: 'Project Monitor', aggregate: false, statuses: [true, false, true], last_build: "4d")
-      model.set tracker: new ProjectMonitor.Models.Tracker(velocity: 9, variance: 10, delivered: 9, open: 5, velocities: [12, 15, 20, 40, 10])
-      model.set new_relic: new ProjectMonitor.Models.NewRelic(times: [10, 20, 50, 80, 100, 90, 80, 90, 40, 40])
-      model.set airbrake: new ProjectMonitor.Models.Airbrake(error_count: 9, last_error: "RuntimeError: Workflow")
+      @model = BackboneFactory.create('project')

-      view = new ProjectMonitor.Views.TileView(model: model)
+      view = new ProjectMonitor.Views.TileView(model: @model)
       setFixtures(view.render().$el)
+
+    it "should include the project id", ->
+      expect($("section").parent().data("project_id")).toEqual(987)

     it "should include build view", ->
       expect($("section")).toContain("article.build")


Update expected active panel name

Update expected active panel name

--- spec/settings-view-spec.coffee
+++ spec/settings-view-spec.coffee
@@ -13,11 +13,11 @@

   describe "serialization", ->
     it "remembers which panel was visible", ->
-      settingsView.showPanel('Packages')
+      settingsView.showPanel('Themes')
       newSettingsView = new SettingsView(settingsView.serialize())
       settingsView.remove()
       newSettingsView.attachToDom()
-      expect(newSettingsView.activePanelName).toBe 'Packages'
+      expect(newSettingsView.activePanelName).toBe 'Themes'

     it "shows the previously active panel if it is added after deserialization", ->
       settingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')


Use Workspace.onDidOpen in test, rather than spying on .open

--- spec/settings-view-spec.coffee
+++ spec/settings-view-spec.coffee
@@ -118,22 +118,14 @@
       expect(settingsView.refs.panels.querySelector('#panel-2')).toBeVisible()

   describe "when the package is activated", ->
-    lastOpenResult = null
-
     openWithCommand = (command) ->
-      atom.commands.dispatch(atom.views.getView(atom.workspace), command)
-      waitsFor ->
-        atom.workspace.getActivePaneItem()?
       waitsFor (done) ->
-        process.nextTick(done)
-      waitsForPromise -> lastOpenResult
+        openSubscription = atom.workspace.onDidOpen ->
+          openSubscription.dispose()
+          done()
+        atom.commands.dispatch(atom.views.getView(atom.workspace), command)

     beforeEach ->
-      originalOpen = atom.workspace.open.bind(atom.workspace)
-      spyOn(atom.workspace, 'open').andCallFake(
-        (args...) => lastOpenResult = originalOpen(args...)
-      )
-
       jasmine.attachToDOM(atom.views.getView(atom.workspace))
       waitsForPromise ->
         atom.packages.activatePackage('settings-view')


Try to fix specs on CI

Try to fix specs on CI

--- spec/settings-view-spec.coffee
+++ spec/settings-view-spec.coffee
@@ -75,6 +75,8 @@
       atom.commands.dispatch(atom.views.getView(atom.workspace), command)
       waitsFor ->
         atom.workspace.getActivePaneItem()?
+      waitsFor (done) ->
+        process.nextTick(done)

     beforeEach ->
       jasmine.attachToDOM(atom.views.getView(atom.workspace))
@@ -135,7 +137,7 @@
         waitsForPromise ->
           atom.workspace.open('atom://config').then (s) -> settingsView = s

-        waits 1
+        waitsFor (done) -> process.nextTick(done)
         runs ->
           expect(settingsView.activePanelName).toBe 'Settings'

@@ -172,7 +174,8 @@
         waitsForPromise ->
           atom.workspace.open('atom://config').then (s) -> settingsView = s

-        waits 1
+        waitsFor (done) -> process.nextTick(done)
+
         runs ->
           expect(settingsView.activePanelName).toBe 'Settings'

@@ -190,6 +193,9 @@

       it "calls the dispose method on all panels", ->
         openWithCommand('settings-view:open')
+
+        waitsFor (done) -> process.nextTick(done)
+
         runs ->
           settingsView = atom.workspace.getActivePaneItem()
           panels = [


Fix spec. Use atom.getUserInitScriptPath() to detect what file is expected to backup

--- spec/sync-settings-spec.coffee
+++ spec/sync-settings-spec.coffee
@@ -145,7 +145,7 @@
           , (err, res) ->
             expect(res.files['styles.less']).not.toBeDefined()

-      it "back up the user init.coffee file", ->
+      it "back up the user init script file", ->
         atom.config.set('sync-settings.syncInit', true)
         run (cb) ->
           SyncSettings.backup cb
@@ -153,9 +153,9 @@
           run (cb) =>
             SyncSettings.createClient().gists.get({id: @gistId}, cb)
           , (err, res) ->
-            expect(res.files['init.coffee']).toBeDefined()
-
-      it "don't back up the user init.coffee file", ->
+            expect(res.files[path.basename(atom.getUserInitScriptPath())]).toBeDefined()
+
+      it "don't back up the user init script file", ->
         atom.config.set('sync-settings.syncInit', false)
         run (cb) ->
           SyncSettings.backup cb
@@ -163,7 +163,7 @@
           run (cb) =>
             SyncSettings.createClient().gists.get({id: @gistId}, cb)
           , (err, res) ->
-            expect(res.files['init.coffee']).not.toBeDefined()
+            expect(res.files[path.basename(atom.getUserInitScriptPath())]).not.toBeDefined()

       it "back up the user snippets", ->
         atom.config.set('sync-settings.syncSnippets', true)


:lipstick: Remove semicolon

:lipstick: Remove semicolon

--- spec/toggle-quotes-spec.coffee
+++ spec/toggle-quotes-spec.coffee
@@ -66,7 +66,8 @@
         expect(buffer.lineForRow(1)).toBe 'console.log("Hello World");'
         expect(editor.getCursors()[0].getBufferPosition()).toEqual [0, 16]
         expect(editor.getCursors()[1].getBufferPosition()).toEqual [1, 16]
-        editor.undo();
+
+        editor.undo()
         expect(buffer.lineForRow(0)).toBe 'console.log("Hello World");'
         expect(buffer.lineForRow(1)).toBe "console.log('Hello World');"
         expect(editor.getCursors()[0].getBufferPosition()).toEqual [0, 16]


reactivity: dropbox: correct dropbox height if it does not fit the window height

--- client/activity/lib/components/dropbox/portaldropbox.coffee
+++ client/activity/lib/components/dropbox/portaldropbox.coffee
@@ -5,6 +5,8 @@
 Dropbox = require './dropboxbody'

 module.exports = class PortalDropbox extends React.Component
+
+  MIN_HEIGHT = 100

   @defaultProps =
     left : 0
@@ -34,20 +36,35 @@
     { width, height, top, left } = @inputDimensions

     dropbox       = $ React.findDOMNode @refs.dropbox
+    scrollable    = dropbox.find '.Dropbox-scrollable'
+
+    # reset scrollable height before calculations
+    # to have initial dropbox height
+    scrollable.css { height : 'auto' }
+
+    scrollHeight  = scrollable.height()
     dropboxHeight = dropbox.height()
     winHeight     = $(window).height()
     winWidth      = $(window).width()
+
+    # check if dropbox is too close to window border
+    # and therefore type should be changed to reverse
     if type is 'dropup'
-      type = 'dropdown'  if top - dropboxHeight < 0
+      type = 'dropdown'  if top < MIN_HEIGHT
     else
-      type = 'dropup'  if top + height + dropboxHeight > winHeight
+      type = 'dropup'  if top + height + MIN_HEIGHT > winHeight

+    # calculate vertical coordinates of dropbox
+    # and height delta if it doesn't fit the window height
     css = { width, top : 'auto', bottom : 'auto' }
     if type is 'dropup'
-      css.bottom = winHeight - top
+      css.bottom  = winHeight - top
+      heightDelta = dropboxHeight - top
     else
-      css.top    = top + height
+      css.top     = top + height
+      heightDelta = css.top + dropboxHeight - winHeight

+    # calculate horizontal coordinates
     rightDelta = @props.right
     leftDelta  = @props.left
     if rightDelta?
@@ -55,10 +72,16 @@
     else
       css.left  = left + leftDelta

+    # update dropbox position and css class depending on the type
     dropbox
       .css css
       .toggleClass 'Dropup', type is 'dropup'
       .toggleClass 'Dropdown', type is 'dropdown'
+
+    # update scrollable container height
+    # if dropbox doesn't fit the window height
+    scrollHeight = if heightDelta > 0 then scrollHeight - heightDelta else 'auto'
+    scrollable.css { height : scrollHeight }

   render: ->


Remove focus

Remove focus

--- spec/server/functional/user.spec.coffee
+++ spec/server/functional/user.spec.coffee
@@ -568,7 +568,7 @@
     expect(classroom.get('deletedMembers')[0].toString()).toEqual(user.id)
     done()

-  fit 'returns 401 if no cookie session', utils.wrap (done) ->
+  it 'returns 401 if no cookie session', utils.wrap (done) ->
     yield utils.logout()
     [res, body] = yield request.delAsync {uri: "#{getURL(urlUser)}/1234"}
     expect(res.statusCode).toBe(401)


Preserve sidebar during user selections

Preserve sidebar during user selections

--- annotran/static/scripts/annotator/sidebar.coffee
+++ annotran/static/scripts/annotator/sidebar.coffee
@@ -65,7 +65,8 @@
   _setupDocumentEvents: ->
     @element.on 'click', (event) =>
       if !@selectedTargets?.length
-        this.hide()
+        # don't hide sidebar
+        return this
     return this

   _setupSidebarEvents: ->


Return the faked box.json

Return the faked box.json

--- code/serv.coffee
+++ code/serv.coffee
@@ -271,6 +271,7 @@
         . ./code/box_lib.sh &&
         create_user #{user_name} &&
         create_user_directories #{user_name}
+        sh ./code/templates/box.json.template
         """
   # insecure - sanitise user_name
   exec cmd, callback