handle root keys in JSON responses

handle root keys in JSON responses

--- app/assets/javascripts/rails_admin_selectize.coffee
+++ app/assets/javascripts/rails_admin_selectize.coffee
@@ -43,6 +43,15 @@

   processResults: (callback) ->
     (response) =>
+      response = if Object.prototype.toString.call(response) is '[object Array]'
+        response
+      else
+        firstArray = null
+        $.each response, ->
+          firstArray = this
+          false
+        firstArray
+
       callback(response)

   dataFor: (query) ->


Troubleshoot: fixed hideFeedback call

Troubleshoot: fixed hideFeedback call

--- client/Main/troubleshoot/troubleshootmodal.coffee
+++ client/Main/troubleshoot/troubleshootmodal.coffee
@@ -56,7 +56,7 @@
     @modalTabs.forms.Troubleshoot.fields.result.hide()

     troubleshoot.on "recoveryCompleted", =>
-      hideFeedback()  if troubleshoot.isSystemOK()
+      @hideFeedback()  if troubleshoot.isSystemOK()

     troubleshoot.once "troubleshootCompleted", =>


VirtualizationController: emit ready after the first time fetchVMs triggers

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -11,9 +11,9 @@

     @osKites = {}

-    KD.getSingleton('mainController')
-      .once('AppIsReady', @bound 'fetchVMs')
-      .on('AccountChanged', => @emit 'VMListChanged')
+    mc = KD.getSingleton('mainController')
+    mc.once 'AppIsReady', => @fetchVMs => @emit 'ready'
+    mc.on   'AccountChanged', => @emit 'VMListChanged'

     @on 'VMListChanged', @bound 'resetVMData'


Added expiry date feature to introduction tooltips.

--- client/app/CommonViews/introductiontooltip.coffee
+++ client/app/CommonViews/introductiontooltip.coffee
@@ -29,6 +29,7 @@

     super options, data

+    @currentTimestamp = Date.now()
     @shouldAddOverlay = no
     @visibleTooltips  = []

@@ -52,7 +53,7 @@

   createInstance: (parentView, data) ->
     assets = @getAssets parentView, data
-    return unless assets
+    return if not assets or @isExpired assets.data.expiryDate

     {parentView, tooltipView} = assets
     tooltip = new IntroductionTooltip {
@@ -67,6 +68,9 @@
       @close parentView.getOptions().introId
       parentView.tooltip.destroy()
       @visibleTooltips.splice @visibleTooltips.indexOf(tooltip), 1
+
+  isExpired: (expiryDate) ->
+    return new Date(expiryDate).getTime() < @currentTimestamp

   getAssets: (parentView, data) ->
     return no if not @introSnippets


Activity: handle q for activity search

Activity: handle q for activity search

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -3,6 +3,7 @@
   KD.registerAppClass this,
     name         : "Activity"
     route        : "/:name?/Activity"
+    searchRoute  : "/Activity?q=:text:"
     hiddenHandle : yes

@@ -134,6 +135,11 @@
       tag = KD.utils.slugify KD.utils.stripTags query.tagged
       @setWarning {text:tag, loading:yes}
       options = filterByTag: tag
+    else if query.q
+      query = KD.utils.stripTags query.q
+      @setWarning {text:query, loading:yes, type:"search"}
+      options = searchText: query
+
     # TODO: populateActivity will fire twice if there is a query (FIXME) C.T.
     @ready => @populateActivity options


Fix conversation.toolbox.trash action

Fix conversation.toolbox.trash action

--- client/app/components/toolbar_conversation.coffee
+++ client/app/components/toolbar_conversation.coffee
@@ -79,6 +79,7 @@
     onDelete: ->
         conversationID = @props.conversationID
         MessageActionCreator.delete {conversationID}
+        @redirect (url = @buildClosePanelUrl 'second')

     onMark: (flag) ->
         conversationID = @props.conversationID


Fixed bracket style guide issue

Fixed bracket style guide issue

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -615,7 +615,7 @@
     # Do we plan to stop machine before snapshot starts? ~ GG
     # machine.getBaseKite( createIfNotExists = no ).disconnect()

-    call = @getKloud().createSnapshot machineId: machine._id, label
+    call = @getKloud().createSnapshot { machineId: machine._id, label }

     .then (res) =>


Change js depending of window with

Change js depending of window with

--- app/assets/javascripts/foundation_extras.js.coffee
+++ app/assets/javascripts/foundation_extras.js.coffee
@@ -2,7 +2,6 @@

   initialize: ->
     $(document).foundation()
-    $(window).trigger "init.zf.sticky"
     $(window).trigger "resize"

     clearSticky = ->
@@ -11,3 +10,15 @@
     $(document).on("page:before-unload", clearSticky)

     window.addEventListener("popstate", clearSticky, false)
+
+    mobile_ui_init = ->
+      $(window).trigger "load.zf.sticky"
+
+    desktop_ui_init = ->
+      $(window).trigger "init.zf.sticky"
+
+    $ ->
+      if $(window).width() < 620
+        do mobile_ui_init
+      else
+        do desktop_ui_init


Fixed an exception when xhr is null

Fixed an exception when xhr is null

Close #39

--- app/assets/javascripts/growlyflash/listener.coffee
+++ app/assets/javascripts/growlyflash/listener.coffee
@@ -33,9 +33,10 @@
     @stack ?= new Stack()
     @process_static() if window.flashes?
     ($ context).on Growlyflash.Listener.EVENTS, (event, xhr) =>
-      xhr ?= event.data?.xhr
-      source = process_from_header(xhr.getResponseHeader(Growlyflash.Listener.HEADER))
-      @stack.push_only_fresh source
+      if xhr ?= event.data?.xhr
+        source = process_from_header(xhr.getResponseHeader(Growlyflash.Listener.HEADER))
+        @stack.push_only_fresh source
+      return

   process_static: ->
     @stack.push alert for alert in process(window.flashes)
@@ -43,4 +44,4 @@

 @Growlyflash.Listener = Listener
 @Growlyflash.listen_on = (context) ->
-  @listener ?= new @Listener(context)+  @listener ?= new @Listener(context)


Email Confirmation : Reset confirmation mail is done, UI needed(modal is done but it has CSS bugs). Preventing user from login is done, modal needs UI improvement. User will get referrer point after referred user verifies email.

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -445,7 +445,7 @@
         when "recover"
           @recoverForm.usernameOrEmail.input.setFocus()
         when "resendEmailToken"
-          @resendEmailConfirmationTokenForm.usernameOrEmail.input.setFocus()
+          @resendForm.usernameOrEmail.input.setFocus()

   getRouteWithEntryPoint:(route)->
     {entryPoint} = KD.config


Corrected the filters event binding

Corrected the filters event binding

--- src/main/client/common/table-options-filter-dropdown.coffee
+++ src/main/client/common/table-options-filter-dropdown.coffee
@@ -20,7 +20,7 @@
       }

       @.$widget = $(@getTemplate()).on('click', $.proxy(@widgetClick, @))
-      @.$widget.find('textarea').each () ->
+      @.$widget.find('input').each () ->
         $(@).on {
           'click.filterdropdown': () -> $(@).select()
           'keyup.filterdropdown': $.proxy(self.widgetKeyup, self)


Sliderbar Component: Added event emits

Sliderbar Component: Added event emits

--- components/sliderbar/KDSliderBarHandleView.coffee
+++ components/sliderbar/KDSliderBarHandleView.coffee
@@ -19,7 +19,11 @@
       valueChange        = ((maxValue - minValue) * relPos) / width
       @setValue value + valueChange
       @snap() if @parent.getOption "snapOnDrag"
-    @on "DragFinished", -> @snap() if @parent.getOption "snap"
+    @on "DragFinished", ->
+      @snap() if @parent.getOption "snap"
+      if value isnt @getOption "value"
+        @emit        "ValueChange"
+        @parent.emit "ValueChange", @

   getPosition:->
     {maxValue, minValue} = @parent.getOptions()
@@ -42,7 +46,6 @@
     @setX "#{@getPosition()}"
     @parent.drawBar() if @parent.getOption('drawBar')
     @parent.setLimits()
-    @emit "ValueChange"

   getSnappedValue:(value)->
     {interval}  = @parent.getOptions()


Load search results even if the query string paramter order changes

--- src/scripts/modules/search/search.coffee
+++ src/scripts/modules/search/search.coffee
@@ -14,7 +14,7 @@
     initialize: () ->
       super()

-      if location.search and location.search isnt '?q='
+      if location.search and location.search.length
         @model = searchResults.load({query: location.search})

       @listenTo(@model, 'change:error', @displayError) if @model


Updated projection object to include certain properties - not exclude

--- src/api/users.coffee
+++ src/api/users.coffee
@@ -40,7 +40,7 @@

   try {

-    projectionRestriction = { "settings": 0, "email": 0, "passwordAlgorithm": 0, "passwordSalt": 0, "passwordHash": 0, "groups": 0 };
+    projectionRestriction = { "firstname": 1, "surname": 1, "msisdn": 1, "token": 1, "locked": 1, "expiry": 1, "_id": 0 };

     var result = yield User.findOne({ token: token }, projectionRestriction).exec();
     if (result === null) {


Fixed tab switching

Fixed tab switching

--- core/app/backbone/views/facts/fact_relation_tabs_view.coffee
+++ core/app/backbone/views/facts/fact_relation_tabs_view.coffee
@@ -17,7 +17,7 @@
     $tabButtons = @$(".tab-control li")
     $tabButtons.removeClass "active"

-    @$(".tab-content > div").hide()
+    @$(".tab-content").hide()
     @$(".tab-control > li").removeClass "tabOpened"
     @_currentTab = `undefined`


fix #4231 -- sagews: Cannot read property 'save_view_state' of undefined

--- src/smc-webapp/editor_react_wrapper.cjsx
+++ src/smc-webapp/editor_react_wrapper.cjsx
@@ -67,10 +67,11 @@
     componentWillUnmount: ->
         @_mounted = false
         window.removeEventListener('resize', @refresh)
-        # These cover all cases for jQuery type overrides.
-        @props.editor.save_view_state?()
-        @props.editor.blur?()
-        @props.editor.hide()
+        if @props.editor?
+            # These cover all cases for jQuery type overrides.
+            @props.editor.save_view_state?()
+            @props.editor.blur?()
+            @props.editor.hide()

     # Refreshes -- cause the editor to resize itself
     refresh: ->


Fix import

Fix import

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -2,7 +2,7 @@

 import "trix/models/location_mapper"
 import "trix/models/point_mapper"
-import "observers/selection_change_observer"
+import "trix/observers/selection_change_observer"

 {getDOMSelection, getDOMRange, setDOMRange, elementContainsNode,
  nodeIsCursorTarget, innerElementIsActive, handleEvent, normalizeRange,


account: remove legals from account settings

--- client/Account/AppController.coffee
+++ client/Account/AppController.coffee
@@ -28,12 +28,6 @@
         { slug: 'Delete', title : "Delete account", listType: "deleteAccount" }
       ]

-    tosAndPrivacyPolicy  :
-      title : "Koding"
-      items : [
-        { slug: 'Terms-of-service', title : "Terms of service", listType: "termsOfService" }
-        { slug: 'Privacy-policy', title : "Privacy policy", listType: "privacyPolicy" }
-      ]

   if KD.utils.oauthEnabled() is yes
     items.personal.items.push({ slug : 'Externals', title : "Linked accounts",     listType: "linkedAccounts" })


pricing: start payment workflow if the required parameters are provided via query string

--- client/Pricing/AppController.coffee
+++ client/Pricing/AppController.coffee
@@ -21,3 +21,13 @@
   loadPaymentProvider: (callback) -> @ready callback

+  handleQuery: (query) ->
+
+    { view } = @getOptions()
+
+    { planTitle, planInterval } = query
+
+    return  unless planTitle and planInterval
+
+    @loadPaymentProvider -> view.continueFrom planTitle, planInterval
+


kitecontroller: fix some OoO bugs

kitecontroller: fix some OoO bugs

--- client/app/MainApp/kite/kite.coffee
+++ client/app/MainApp/kite/kite.coffee
@@ -1,4 +1,4 @@
-class Kite extends KDEventEmitter
+class Kite extends KDObject

   [ NOTREADY, READY ] = [ 0, 1 ]

@@ -14,19 +14,20 @@
     @localStore   = new Store
     @remoteStore  = new Store

-    @entropy = @createId 128
-
     @readyState = NOTREADY

   createId: Bongo.createId

   initChannel: ->
-    @channelName = @getChannelName()
+    @entropy        = @createId 128
+    @qualifiedName  = "kite-#{@kiteName}"
+
+    @channelName  = @getChannelName()

     @channel = KD.remote.mq.subscribe @channelName
     @channel.setAuthenticationInfo
       serviceType       : 'kite'
-      name              : "kite-#{@kiteName}"
+      name              : @qualifiedName
       correlationName   : @correlationName
       clientId          : KD.remote.getSessionToken()

@@ -40,7 +41,9 @@
     @channel.on 'publish'               , @bound 'handleChannelPublish'
     @channel.on 'possibleUnresponsive'  , @bound 'handleSuspectChannel'
     @channel.on 'unresponsive'          , @bound 'handleUnresponsiveChannel'
-    @channel.once 'broker.subscribed'   , @bound 'handleBrokerSubscribed'
+
+  handleBrokerSubscribed:->
+

   cycleChannel: ->
     @setStopPinging()
@@ -50,11 +53,12 @@
     @emit 'destroy'

   pingChannel: (callback) ->
+    return if @stopPinging
     @channel.publish JSON.stringify
       method      : 'ping'
       arguments   : []
       callbacks   : {}
-    @once 'pong', callback  if callback
+    @channel.once 'pong', callback  if callback

   setStartPinging: -> @stopPinging = false

@@ -66,7 +70,7 @@
     @unresponded = 0

     @pingTimeoutId = setTimeout =>
-      @ping()
+      @pingChannel()
     , 5000

   handleChannelMessage: (args) ->
@@ -79,10 +83,8 @@
       when 'pong'             then @bound 'handlePong'
       when 'cycleChannel'     then @bound 'cycleChannel'
       else (@localStore.get method) ? ->
-
-    @unscrubbed = @unscrub args

-    callback.apply this, unscrubbed
+    callback.apply this, @unscrub args

   handleChannelPublish: ->
     clearTimeout @pingTimeoutId           if @pingTimeoutId?
@@ -109,8 +111,9 @@
     return KD.utils.defer callback  if @readyState is READY
     @once 'ready', callback

-  handleReady: ->
+  handleReady: (resourceName)->
     @readyState = READY
+    @channel.exchange = resourceName
     @emit 'ready'

   handleError: (err) ->
@@ -133,7 +136,7 @@
       scrubbed.method or= method
       callback scrubbed

-  unscrub:->
+  unscrub: (args) ->
     scrubber = new Scrubber @localStore
     return scrubber.unscrub args, (callbackId) =>
       unless remoteStore.has callbackId
@@ -146,7 +149,7 @@
     nickname  = delegate?.profile.nickname ?
                 if delegate.guestId then "guest#{delegate.guestId}" ?
                 'unknown'
-    channelName = "#{@entropy}.#{nickname}.#{@kiteName}"
+    channelName = "#{@entropy}.#{nickname}.#{@qualifiedName}"
     return channelName

   tell:(options, callback) ->


fix error handling on event import

fix error handling on event import

--- client/app/views/import_view.coffee
+++ client/app/views/import_view.coffee
@@ -110,19 +110,14 @@
             counter++
             $('.import-counter').html counter

-        addAlarmError = (alarm) ->
+        addError = (element, templatePath) ->
             if $('.import-errors').html().length is 0
                 $('.import-errors').html """
                 <p>#{t 'import error occured for'}:</p>
                 """

             $('.import-errors').append(
-                require('./templates/import_alarm')(alarm.attributes)
-            )
-
-        addEventError = (event) ->
-            $('.import-errors').append(
-                require('./templates/import_event')(event.attributes)
+                require(templatePath)(element.attributes)
             )

         # Show loading spinner.
@@ -144,7 +139,7 @@
                 error: ->
                     # When an element failed to import, an error message is
                     # displayed.
-                    addAlarmError alarm
+                    addAlarmError alarm, './templates/import_alarm'
                     updateCounter()
                     callback()

@@ -162,7 +157,7 @@
                 error: ->
                     # When an element failed to import, an error message is
                     # displayed.
-                    addEventError event
+                    addEventError event, './templates/import_event'
                     updateCounter()
                     callback()


Groups: banner is hidden after invitation code is redeemed

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -350,6 +350,7 @@
           duration  : 2000

         KD.getSingleton('router').clear()
+        @headBanner.hide()

         setTimeout =>
           @hide()


Document the abstract Cacheable class.

Document the abstract Cacheable class.

--- src/cacheable.coffee
+++ src/cacheable.coffee
@@ -1,11 +1,40 @@
 Impromptu = require './impromptu'

+# An abstract class that manages how a method is cached.
 class Cacheable
   constructor: (@impromptu, @name, @options) ->
+
+
+  # The main method.
+  #
+  # Accepts a `fn` with a signature of `err, results`, where `results` is the
+  # cached value. Optionally updates the cache.
+  #
+  # This should be bound to the instance so the method can be passed around
+  # without the instance.
   run: (fn) => throw Impromptu.AbstractError
+
+
+  # Gets the cached value.
+  #
+  # Accepts a `fn` with a signature of `err, results`, where `results` is the
+  # cached value. Does not update the cache.
   get: (fn) -> throw Impromptu.AbstractError
+
+
+  # Updates the cached value.
+  #
+  # Accepts a `fn` with a signature of `err, success`, where `success` is a
+  # boolean indicating whether the cached value was updated.
   set: (fn) -> throw Impromptu.AbstractError
+
+
+  # Clears the cached value.
+  #
+  # Accepts a `fn` with a signature of `err, success`, where `success` is a
+  # boolean indicating whether the value was removed from the cache.
   unset: (fn) -> throw Impromptu.AbstractError
+

 # Expose `Cacheable`.
 exports = module.exports = Cacheable


Rename variables for clarity

Rename variables for clarity

--- client/services/loginservice.coffee
+++ client/services/loginservice.coffee
@@ -2,11 +2,11 @@
 idSignIn = "login-signin-tab"
 idSignup = "login-signup-tab"

-findEmail = (t) ->
-  return trimWhitespace(t.find('.account-email').value)
+findEmail = (template) ->
+  return trimWhitespace(template.find('.account-email').value)

-findPassword = (t) ->
-  return trimWhitespace(t.find('.account-password').value)
+findPassword = (template) ->
+  return trimWhitespace(template.find('.account-password').value)

 class @LoginService
   setupTemplate: ->


jupyter: more merge issues

jupyter: more merge issues

--- src/smc-webapp/jupyter/server-urls.coffee
+++ src/smc-webapp/jupyter/server-urls.coffee
@@ -3,7 +3,7 @@
 ###

 exports.get_server_url = (project_id) ->
-    return "#{window?.smc_base_url ? ''}/#{project_id}/raw/.smc/jupyter"
+    return "#{window?.app_base_url ? ''}/#{project_id}/raw/.smc/jupyter"

 exports.get_blob_url = (project_id, extension, sha1) ->
     return "#{exports.get_server_url(project_id)}/blobs/a.#{extension}?sha1=#{sha1}"


Remove dead test code

Remove dead test code

--- src/smc-webapp/test/smc-react-test.coffee
+++ src/smc-webapp/test/smc-react-test.coffee
@@ -1,270 +0,0 @@
-`DEBUG = true`
-{computed, depends, rtypes, __internals} = require('../app-framework')
-{harvest_import_functions, harvest_own_functions, generate_selectors} = __internals
-
-# 3rd Party Libraries
-expect = require('expect')
-{createSelector} = require('reselect')
-immutable = require('immutable')
-_ = require('underscore')
-
-# SMC Libraries
-misc = require('smc-util/misc')
-
-describe 'rtypes immutable categories', ->
-    it 'Assigns all immutable types the category `IMMUTABLE`', ->
-        expect(rtypes.immutable.category != "IMMUTABLE" or
-                rtypes.immutable.Map.category != "IMMUTABLE" or
-                rtypes.immutable.List.category != "IMMUTABLE" or
-                rtypes.immutable.isRequired.category != "IMMUTABLE" or
-                rtypes.immutable.Map.isRequired.category != "IMMUTABLE" or
-                rtypes.immutable.List.isRequired.category != "IMMUTABLE")
-        .toEqual(false)
-
-create_account_def = () ->
-    name: 'account'
-
-    stateTypes:
-        first_name : rtypes.string
-        last_name  : rtypes.string
-        full_name  : computed rtypes.string
-
-    getInitialState: ->
-        first_name : ""
-        last_name  : ""
-
-    full_name: (first_name, last_name) ->
-        return "#{first_name} #{last_name}"
-
-create_project_def = () ->
-    name: 'project'
-
-    reduxState:
-        account :
-            first_name : rtypes.string # Raw
-            full_name  : rtypes.string # Computed
-
-    stateTypes:
-        use_full_name  : rtypes.bool
-        defined_getter : rtypes.string
-        greetings      : computed rtypes.string
-
-    getInitialState: ->
-        use_full_name : false
-
-    defined_getter: ->
-        return "cake"
-
-    greetings: (first_name, full_name, use_full_name) ->
-        if use_full_name
-            return "Hello #{full_name ? 'friend'}."
-        else
-            return "Hi #{first_name ? 'friend'}."
-
-
-describe 'harvest_import_functions', ->
-    it 'Removes reduxState from the given object', ->
-        store_def = create_project_def()
-        harvest_import_functions(store_def)
-
-        expect store_def.reduxState
-        .toEqual undefined
-
-    it 'Returns an object of functions', ->
-        store_def = create_project_def()
-        result = harvest_own_functions(store_def)
-
-        expect(typeof result).toEqual('object')
-        _.map result, (func, name) =>
-            expect(typeof func).toEqual('function')
-
-    it 'Returns an object with keys identical to the declared state names', ->
-        store_def = create_project_def()
-        original_names = _.keys(_.clone(store_def.reduxState.account))
-        func_names = _.keys(harvest_import_functions(store_def))
-
-        expect(func_names).toEqual(original_names)
-
-describe 'Functions from harvest_import_functions', ->
-    it 'Call the correct store', ->
-        called = false
-        scope =
-            redux:
-                getStore: (name) =>
-                    if name == 'account'
-                        called = true
-        bound_func = harvest_import_functions(create_project_def()).full_name.bind(scope)
-
-        expect(=> bound_func()).toThrow()
-        expect(called).toEqual(true)
-
-
-describe 'harvest_own_functions', ->
-    it 'Removes stateTypes from the given object', ->
-        store_def = create_project_def()
-        harvest_own_functions(store_def)
-
-        expect store_def.stateTypes
-        .toEqual undefined
-
-    it 'Throws an error when a computed definition is not found', ->
-        store_def = create_project_def()
-        delete store_def.greetings
-
-        expect(=> harvest_own_functions(store_def))
-        .toThrow("Computed value 'greetings' in store 'project' was declared but no definition was found.")
-
-    it 'Returns an object of functions', ->
-        store_def = create_project_def()
-        result = harvest_own_functions(store_def)
-
-        expect(typeof result).toEqual('object')
-        _.map result, (func, name) =>
-            expect(typeof func).toEqual('function')
-
-    it 'Returns an object with keys identical to the declared state names', ->
-        store_def = create_project_def()
-        original_names = _.keys(_.clone(store_def.stateTypes))
-        func_names = _.keys(harvest_own_functions(store_def))
-
-        expect(func_names).toEqual(original_names)
-
-describe 'Functions from harvest_own_functions', ->
-    it 'Call @get(function_key) if `function_key` is not defined nor a computed value', ->
-        called_with = ''
-        scope =
-            get: (name) =>
-                called_with = name
-
-        store_def = create_project_def()
-        result = harvest_own_functions(store_def)
-        result.use_full_name.bind(scope)()
-
-        expect(called_with).toEqual('use_full_name')
-
-    it 'Use input functions if defined in the store', ->
-        store_def = create_project_def()
-        original_function = store_def.defined_getter.toString()
-        result = harvest_own_functions(store_def)
-
-        expect(result.defined_getter.toString()).toEqual(original_function)
-
-    it 'Use selectors defined in the store', ->
-        store_def = create_project_def()
-        original_function = store_def.greetings.toString()
-        result = harvest_own_functions(store_def)
-
-        expect(result.greetings.toString()).toEqual(original_function)
-
-describe 'selectors lib', ->
-    it 'selects', ->
-        state = immutable.Map
-            first_name : "Kimberly"
-            last_name  : "Smith"
-        first_name = -> state.get('first_name')
-        last_name  = -> state.get('last_name')
-        full_name  = -> "#{first_name()} #{last_name()}"
-        short_name = -> full_name().slice(0,5)
-
-        selector = createSelector([first_name, last_name], full_name)
-
-        init_val = selector(state)
-        init_recomps = selector.recomputations()
-        state = state.set('first_name', "Katie")
-        expect(first_name()).toEqual("Katie")
-
-        update_once_val = selector(state)
-        update_once_call_2_val = selector(state)
-
-        expect(init_val).toEqual("Kimberly Smith")
-        expect(update_once_val).toEqual("Katie Smith")
-        expect(update_once_call_2_val).toEqual("Katie Smith")
-        expect(selector.recomputations()).toEqual(init_recomps + 1)
-
-    it 'is composable', ->
-        state = immutable.Map
-            first_name : "Kimberly"
-            last_name  : "Smith"
-        first_name = -> state.get('first_name')
-        last_name  = -> state.get('last_name')
-        full_name  = -> "#{first_name()} #{last_name()}"
-        short_name = -> full_name().slice(0,5)
-
-        s0 = createSelector([first_name, last_name], full_name)
-        selector = createSelector(s0, short_name)
-
-        init_val = selector(state)
-        init_recomps = selector.recomputations()
-        state = state.set('first_name', "Katherine")
-        expect(first_name()).toEqual("Katherine")
-
-        update_once_val = selector(state)
-        update_once_call_2_val = selector(state)
-
-        expect(init_val).toEqual("Kimbe")
-        expect(update_once_val).toEqual("Kathe")
-        expect(update_once_call_2_val).toEqual("Kathe")
-        expect(selector.recomputations()).toEqual(init_recomps + 1)
-
-describe 'generate_selectors', ->
-    store =
-        first_name : -> @state.get('first_name')
-        last_name  : -> @state.get('last_name')
-        # Note that the function invocations are necessary unlike when
-        # writing actual stores because of the binding method
-        full_name  : -> "#{@first_name()} #{@last_name()}"
-        short_name : -> @full_name().slice(0,5)
-        setState   : (key, val) -> @state = @state.set(key, val)
-
-    store.state = immutable.Map
-        first_name : "Kimberly"
-        last_name  : "Smith"
-
-    store.full_name.dependency_names = ["first_name", "last_name"]
-    store.short_name.dependency_names = ["full_name"]
-
-    [bound_store] = misc.bind_objects(store, [store])
-
-    first_name_func_string = JSON.stringify(bound_store.first_name)
-    store = generate_selectors(bound_store)
-
-    it 'ignores input functions (those without arguments)', ->
-        expect JSON.stringify(store.first_name)
-        .toEqual first_name_func_string
-
-    it 'computes values from input functions', ->
-        expect store.full_name(store.state)
-        .toEqual "Kimberly Smith"
-
-    it 'computes values from computed values', ->
-        expect store.short_name(store.state)
-        .toEqual "Kimbe"
-
-    it 'memoizes computed values', ->
-        initial_comps = store.full_name.recomputations()
-        val1 = store.full_name(store.state)
-        val2 = store.full_name(store.state)
-        expect val1
-        .toEqual val2
-
-        expect initial_comps
-        .toEqual store.full_name.recomputations()
-
-    it 'recomputes once on one input change', ->
-        initial_comps = store.short_name.recomputations()
-        init_val = store.short_name(store.state)
-        store.state = store.setState('first_name', "Katie")
-
-        expect(store.first_name(store.state)).toEqual("Katie")
-        expect(store.full_name(store.state)).toEqual("Katie Smith")
-
-        update_once_val = store.short_name(store.state)
-        update_once_call_2_val = store.short_name(store.state)
-        update_once_call_3_val = store.short_name(store.state)
-
-        expect(init_val).not.toEqual(update_once_val)
-        expect(update_once_val).toEqual(update_once_call_2_val)
-
-        expect initial_comps + 1
-        .toEqual store.short_name.recomputations()
-


fixed js bug in the admin

fixed js bug in the admin

--- app/assets/javascripts/admin/polygon.coffee
+++ app/assets/javascripts/admin/polygon.coffee
@@ -72,12 +72,13 @@
         # dashArray: '1,16'
         fill: false
       onEachFeature: (feature, layer) ->
+        console.log layer
         str = ""
         str += "ID: " + feature.properties.id + "" if feature.properties?.id
         str += "
" + "User: " + feature.properties.user_id + "" if feature.properties?.user_id
         str += "
" + "Session: " + feature.properties.session_id + "" if feature.properties?.session_id
         str += "
" + feature.properties.flag_value if feature.properties?.flag_value
-        layer.setIcon(p.markerFlag)
+        layer.setIcon(p.markerFlag) if feature.geometry.type == "Point"
         layer.bindPopup(str,
           offset: L.point(0, -30)
         )


Store instance on element data, allow access via jQuery UI style calls

--- app/assets/javascripts/exo/widget.js.coffee
+++ app/assets/javascripts/exo/widget.js.coffee
@@ -6,9 +6,19 @@
     _.extend(@prototype, Backbone.Events) if Backbone?.Events

     @register: (name, klass) ->
-      $.fn[name] = (options = {}) ->
-        (new klass(element, options) for element in this)
-        return # No need to collect results
+      $.fn[name] = (params...) ->
+        for el in this
+          $el = $ el
+          api = $el.data name
+
+          if api
+            [action, args...] = params
+            api[action].apply(api, args) if api[action]?
+          else
+            options = params[0] || {}
+            $el.data(name, new klass($el, options))
+
+        this

     constructor: (el, options) ->
       @cid = _.uniqueId "exo-widget"


bugfix notification colors

bugfix notification colors

--- app/assets/javascripts/notifications.coffee
+++ app/assets/javascripts/notifications.coffee
@@ -52,7 +52,7 @@
 		notificationId = $(this).data('id')
 		console.log 'Hi'
 		# remove coloring of list group item
-		$(this).closest('.card').removeClass('newsColor')
+		$(this).closest('.card').removeClass('bg-post-it-blue')
 		# remove link and icon
 		$(this).remove()
 		# adjust news announcement counter


Change membership.is_owner to membership.is_admin

--- app/coffee/modules/admin/memberships.coffee
+++ app/coffee/modules/admin/memberships.coffee
@@ -266,7 +266,7 @@
         member = $scope.$eval($attrs.tgMembershipsRowAdminCheckbox)
         html = render(member)

-        if member.is_owner
+        if member.is_admin
             $el.find(":checkbox").prop("checked", true)

         $el.on "click", ":checkbox", (event) =>
@@ -275,11 +275,11 @@

             onError = (data) ->
                 member.revert()
-                $el.find(":checkbox").prop("checked", member.is_owner)
-                $confirm.notify("error", data.is_owner[0])
+                $el.find(":checkbox").prop("checked", member.is_admin)
+                $confirm.notify("error", data.is_admin[0])

             target = angular.element(event.currentTarget)
-            member.is_owner = target.prop("checked")
+            member.is_admin = target.prop("checked")
             $repo.save(member).then(onSuccess, onError)

         $scope.$on "$destroy", ->


add w3d configuration

add w3d configuration

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -35,6 +35,22 @@
       weight: 5
       title_video: "Download sample videos"
       link_video: "https://drive.google.com/file/d/0B4Q4b_aBOfU-TmJvSFhFeWp1NEk/view?usp=sharing"
+    w3d:
+      id: 'w3d'
+      title_store: '3d'
+      description_store: "Intel RealSense R200 RGBD camera. Provides stereo RGB images and depth image."
+      tech_specs:
+        sensor: "See Intel R200 Docs for details"
+        fov: "See Intel R200 Docs for details"
+        latency: "See Intel R200 Docs for details"
+        audio: "Record audio via host device microphone"
+      title_cart: "World Camera: 3d"
+      description_cart: "Intel RealSense R200. Provides stereo RGB images and depth image."
+      weight: 17
+      title_video: "Download sample video"
+      link_video: "https://drive.google.com/file/d/0Byap58sXjMVfRi13ZUo2MkJTWUE/view?usp=sharing"
+
+

   return world_cam_data

@@ -273,7 +289,58 @@
         world_camera: world_cam_data.w120
         eye_camera: eye_cam_data.e120b
         discount: edu_discount
-      img: [pupil_img_path,'pupil_w120_e120b.jpg'].join('/')
+      img: [pupil_img_path,'pupil_w120_e120b.jpg'].join('/')
+
+    pupil_w3d_enone:
+      cost: 1390
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.w3d
+        eye_camera: eye_cam_data.enone
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_w3d_enone.jpg'].join('/')
+    pupil_w3d_e120:
+      cost: 1840
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.w3d
+        eye_camera: eye_cam_data.e120
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_w3d_e120.jpg'].join('/')
+    pupil_w3d_e120b:
+      cost: 2540
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.w3d
+        eye_camera: eye_cam_data.e120b
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_w3d_e120b.jpg'].join('/')
+
+    pupil_w3d_enone_edu:
+      cost: 800
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.w3d
+        eye_camera: eye_cam_data.enone
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_w3d_enone.jpg'].join('/')
+    pupil_w3d_e120_edu:
+      cost: 1250
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.w3d
+        eye_camera: eye_cam_data.e120
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_w3d_e120.jpg'].join('/')
+    pupil_w3d_e120b_edu:
+      cost: 1950
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.w3d
+        eye_camera: eye_cam_data.e120b
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_w3d_e120b.jpg'].join('/')
+
     oculusdk2m:
       cost: 800
       title_product: 'Oculus Rift DK2 Add-on'


Removed camera count logic and it's not very useful right now.

--- controllers/http.coffee
+++ controllers/http.coffee
@@ -5,7 +5,13 @@
     # Connect to mongodb.
     mongoose.connect 'mongodb://localhost/cams'

+    # Render the default view.
+    render 'default'
+
+    # This is sorta pointless right now.  The point is the render call is made
+    # in the callback to the `count()` method since it's asynchronous.
+
     # Count those cameras!
-    app.CameraModel.count {}, (err, @count) =>
+    # app.CameraModel.count {}, (err, @count) =>
         # mongoose.disconnect()
-        render 'default'
+        # render 'default'


IDEView: Change event binding procedure

IDEView: Change event binding procedure

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -392,8 +392,11 @@

     terminalPane.ready -> frontApp.setRealtimeManager terminalPane

-    terminalPane.webtermView.on 'ScreenSizeChanged', kd.utils.debounce 100, (size) =>
-      @emitChange terminalPane, { context: { size } }, 'TerminalScreenSizeChanged'
+    { webtermView } = terminalPane
+
+    webtermView.on 'viewAppended', =>
+      webtermView.terminal.on 'ScreenSizeChanged', kd.utils.debounce 100, (size) =>
+        @emitChange terminalPane, { context: { size } }, 'TerminalScreenSizeChanged'

     @createPane_ terminalPane, { name: 'Terminal', isActivePane }


Collaboration: Don't create avatar if exists.

--- client/ide/views/statusbar/statusbar.coffee
+++ client/ide/views/statusbar/statusbar.coffee
@@ -53,6 +53,8 @@

   createParticipantAvatar: (nickname, isOnline) ->
+
+    return  if @participantAvatars[nickname]

     view       = new IDE.StatusBarAvatarView
       origin   : nickname


Set tab size to 2 in editor

Set tab size to 2 in editor

--- client/source/views/templates/editor.coffee
+++ client/source/views/templates/editor.coffee
@@ -46,6 +46,7 @@

     @editorSession = @editor.getSession()
     @editorSession.setMode 'ace/mode/handlebars'
+    @editorSession.setTabSize 2

     @$('pre.code, textarea.code').toggleClass 'hidden'


Handle empty scoring lists properly PP-8

--- client/src/scripts/scoring-list-view.coffee
+++ client/src/scripts/scoring-list-view.coffee
@@ -1,5 +1,6 @@
 Steam.ScoringListView = (_) ->
   _items = do nodes$
+  _hasItems = lift$ _items, (items) -> items.length > 0

   #TODO ugly
   _isLive = node$ yes
@@ -30,7 +31,10 @@

   displayScorings = (scorings) ->
     _items items = map scorings, createItem
-    activateItem head items unless isEmpty items
+    if isEmpty items
+      _.displayEmpty()
+    else
+      activateItem head items

   _predicate = null
   loadScorings = (predicate) ->
@@ -54,5 +58,6 @@
     _isLive yes

   items: _items
+  hasItems: _hasItems
   template: 'scoring-list-view'


removeStale needs to be called on news item

--- app/models/news.coffee
+++ app/models/news.coffee
@@ -84,7 +84,7 @@
     else
       @collection.add news

-  removeStale: (outage) =>
+  removeStale: (news) =>
     current = @get news.id

     if not current


Fix reply button text

Fix reply button text

--- assets/javascripts/views/reply_post_form.js.coffee
+++ assets/javascripts/views/reply_post_form.js.coffee
@@ -26,6 +26,7 @@
     @is_repost = @$container.hasClass('repost-reply-container')

     @is_hidden = @$container.hasClass('hide')
+    @show_text = 'Reply'
     @hide_text = 'Cancel'

     @_initialized = true
@@ -59,7 +60,6 @@
     @focusAfterText()
     if $button = @getReplyButton()
       $text = $('.text', $button)
-      @show_text ?= $text.text()
       $text.text @hide_text

   hide: =>


Fix typo

Fix typo

--- assignments/coffeescript/luhn/luhn_test.spec.coffee
+++ assignments/coffeescript/luhn/luhn_test.spec.coffee
@@ -26,7 +26,7 @@
     luhn = new Luhn(201773)
     expect(luhn.checksum).toEqual(21)

-  it "invalid number", ->
+  it "valid number", ->
     luhn = new Luhn(738)
     expect(luhn.valid).toEqual(false)


Fixed ActionButtonView to not use listenTo for jQuery events

--- core/app/backbone/views/action_button_view.coffee
+++ core/app/backbone/views/action_button_view.coffee
@@ -11,6 +11,7 @@
       @trigger 'click:unchecked'
     @set 'hovering', false

+
 class window.ActionButtonView extends Backbone.Marionette.ItemView
   tagName: 'button'
   template: 'generic/action_button'
@@ -23,26 +24,31 @@

     super

-    @bindInteractionEvents options.$listenToEl || @$el
+    @$listenToEl = options.$listenToEl || @$el
+    @bindInteractionEvents()

-
-  bindInteractionEvents: ($listenToEl)->
-    @listenTo $listenToEl, 'click', @onClick
-    @listenTo $listenToEl, 'mouseenter', @onMouseEnter
-    @listenTo $listenToEl, 'mouseleave', @onMouseLeave
+  bindInteractionEvents: ->
+    @$listenToEl.on 'click', @onClick
+    @$listenToEl.on 'mouseenter', @onMouseEnter
+    @$listenToEl.on 'mouseleave', @onMouseLeave

     @listenTo @model, 'change', @render
     @on 'render', @showCurrentState, @

-  onClick: (e) ->
+  onClose: ->
+    @listenToEl.off 'click', @onClick
+    @listenToEl.off 'mouseenter', @onClick
+    @listenToEl.off 'mouseleave', @onClick
+
+  onClick: (e) =>
     e.preventDefault()
     e.stopPropagation()
     return unless @model.get('loaded')

     @model.onClick()

-  onMouseEnter: -> @model.set 'hovering', true
-  onMouseLeave: -> @model.set 'hovering', false
+  onMouseEnter: => @model.set 'hovering', true
+  onMouseLeave: => @model.set 'hovering', false

   showCurrentState: ->
     hovering = @model.get('hovering')


don't use the atom-dark-ui theme because it breaks our init.coffee script

--- cookbooks/vm/files/default/atom_config.cson
+++ cookbooks/vm/files/default/atom_config.cson
@@ -4,10 +4,6 @@
     fontSize: 14
     lineHeight: 1.4
   core:
-    themes: [
-      "atom-dark-ui"
-      "atom-dark-syntax"
-    ]
   welcome:
     showOnStartup: false
   tabs:


Update listener to correctly reset data before rerender

--- bokehjs/src/coffee/models/annotations/label.coffee
+++ bokehjs/src/coffee/models/annotations/label.coffee
@@ -14,14 +14,18 @@
   bind_bokeh_events: () ->
     if @mget('render_mode') == 'css'
       # dispatch CSS update immediately
-      @listenTo(@model, 'change', @render)
+      @listenTo(@model, 'change', () ->
+        @set_data(@mget('source'))
+        @render())
       @listenTo(@mget('source'), 'change', () ->
-        @set_data()
+        @set_data(@mget('source'))
         @render())
     else
-      @listenTo(@model, 'change', @plot_view.request_render)
+      @listenTo(@model, 'change', () ->
+        @set_data(@mget('source'))
+        @plot_view.request_render())
       @listenTo(@mget('source'), 'change', () ->
-        @set_data()
+        @set_data(@mget('source'))
         @plot_view.request_render())

   _initialize_properties: () ->
@@ -61,8 +65,6 @@

     [sx, sy] = @_map_data()

-    debugger;
-
     if @mget('render_mode') == 'canvas'
       for i in [0...@_text.length]
         @_canvas_text(ctx, i, @_text[i], sx[i] + @_x_offset[i], sy[i] - @_y_offset[i], @_angle[i])


dsync -- clearly I have implemented the protocol incorrectly.

--- salvus/dsync.coffee
+++ salvus/dsync.coffee
@@ -22,7 +22,7 @@

 dmp = new diff_match_patch()

-class DSync
+class DSync0
     constructor: (opts) ->
         opts = defaults opts,
             id   : undefined
@@ -72,15 +72,16 @@
     # Create a list of new edits, then send all edits not yet
     # processed to the other end of the connection.
     push_edits: (cb) =>
-        edits = {edits:@_compute_edits(@shadow, @live)}
+        snapshot = @_copy(@live)
+        edits = {edits:@_compute_edits(@shadow, snapshot)}

         if edits.edits.length > 0
             edits.shadow_version = @shadow_version
             edits.shadow_checksum = @_checksum(@shadow)
             # console.log("#{@id} -- push_edits -- #{misc.to_json(edits)}")
             @edit_stack.push(edits)
-            console.log("#{@id} -- shadow changes: '#{@shadow}' --> '#{@live}'  (version #{@shadow_version+1})")
-            @shadow = @_copy(@live)
+            console.log("#{@id} -- shadow changes: '#{@shadow}' --> '#{snapshot}'  (version #{@shadow_version+1})")
+            @shadow = snapshot
             @shadow_version += 1
         else
             # console.log("#{@id} -- push_edits -- (nothing new)")
@@ -91,6 +92,10 @@
     # Receive and process the edits from the other end of the sync connection.
     recv_edits: (edit_stack, last_version_ack, cb) =>

+        if Math.random() < .5
+            cb?(true)
+            return
+
         #console.log("#{@id} -- recv_edits -- #{misc.to_json(edit_stack)}")
         # Keep only edits that we still need to send.
         @edit_stack = (edits for edits in @edit_stack when edits.shadow_version > last_version_ack)
@@ -98,7 +103,7 @@
         #console.log("#{@id} -- remaining queued edits for next time: ", @edit_stack)
         # process the incoming edits
         for edits in edit_stack
-            console.log("#{@id} -- our shadow version = #{@shadow_version} and client shadow version #{edits.shadow_version}")
+            console.log("#{@id} -- our shadow version = #{@shadow_version} and other shadow version #{edits.shadow_version}")
             # If edits.shadow_version does not equal @shadow_version, then there was a packet duplication or loss.
             if edits.shadow_version < @shadow_version
                 console.log("Duplicate Packet: we have no interest in edits we have already processed.")
@@ -121,7 +126,86 @@

         cb?()

-
+class DSync1
+    constructor: (opts) ->
+        opts = defaults opts,
+            id   : undefined
+            doc  : required
+        if not opts.id?
+            @id = misc.uuid()
+        else
+            @id = opts.id
+
+        @live                  = opts.doc
+        @shadow                = @_copy(@live)
+
+    status: () =>
+        return {'id':@id, 'live':@live, 'shadow':@shadow}
+
+    restart: (reason) =>
+        console.log("*********************************************************")
+        console.log("* THINGS WENT TO HELL. -- #{reason} --  HAVE TO RESTART!!!!! *")
+        console.log("*********************************************************")
+        throw("dang")
+
+    # Copy a document; strings are immutable and the default, so we
+    # just return the object.
+    _copy: (doc) =>
+        return doc
+
+    # Determine array of edits between the two versions of the document
+    _compute_edits: (version0, version1) =>
+        return dmp.patch_make(version0, version1)
+
+    # "Best effort" application of array of edits.
+    _apply_edits: (edits, doc) =>
+        return dmp.patch_apply(edits, doc)[0]
+
+    # Return a checksum of a document
+    _checksum: (doc) =>
+        return doc.length
+
+    # Connect this client to the other end of the connection, the "server".
+    connect: (server) =>
+        @server = server
+
+    # Create a list of new edits, then send all edits not yet
+    # processed to the other end of the connection.
+    push_edits: (cb) =>
+        snapshot = @_copy(@live)
+        edits    = @_compute_edits(@shadow, snapshot)
+
+        if edits.length == 0
+            cb?()
+            return
+
+        @server.recv_edits edits, @_checksum(@shadow), (err) =>
+            if err
+                cb?(err)
+            else
+                @shadow = @_copy(snapshot)
+                cb?()
+
+
+    # Receive and process the edits from the other end of the sync connection.
+    recv_edits: (edits, checksum, cb) =>
+        if Math.random() < .5
+            cb(true)
+            return
+        if checksum != @_checksum(@shadow)
+            # Data corruption in memory or network -- there should be no other way for this to happen.
+            # In this case, we have to just restart everything from scratch.
+            @restart("checksum")
+            cb(true)
+            return
+
+        @shadow = @_apply_edits(edits, @shadow)
+        @live = @_apply_edits(edits, @live)
+        cb?()
+
+
+
+DSync = DSync0
 exports.test1 = () ->
     client = new DSync(doc:"sage", id:"client")
     server = new DSync(doc:"sage", id:"server")
@@ -136,27 +220,22 @@
         console.log(misc.to_json(server.status()))
         console.log("------------------------")

-    status()
-    client.push_edits()
-    server.push_edits()
-    status()
-
+    go = () ->
+        client.push_edits( (err) =>
+            if not err
+                server.push_edits())
+
+    go()
     client.live += "\nmore stuff"
-    status()
-    client.push_edits()
-    server.push_edits()
-    status()
-
-    client.push_edits()
+    go()

     client.live = 'bar1\n' + client.live
     server.live = 'bar2\n'
     status()
     while client.live != server.live
         try
-            client.push_edits()
-            server.push_edits()
             status()
+            go()
         catch e
             status()
             break


Latest Atom configuration

Latest Atom configuration

--- atom/config.cson
+++ atom/config.cson
@@ -63,7 +63,6 @@
     debugLSP: true
     disabledPackages: [
       "background-tips"
-      "file-type-icons"
       "npm-library-description"
     ]
     followSymlinks: true
@@ -81,7 +80,7 @@
     telemetryConsent: "limited"
     themes: [
       "one-light-ui"
-      "atelier-forest-light-syntax"
+      "one-light-syntax"
     ]
     useTreeSitterParsers: true
   docblockr:


Minor atom config updates

Minor atom config updates

--- atom/config.cson
+++ atom/config.cson
@@ -3,12 +3,6 @@
     createDirectories: true
     fuzzyMatch: true
     helmDirSwitch: true
-  "atom-beautify":
-    css: {}
-    general:
-      analytics: false
-    json:
-      indent_size: 2
   "autocomplete-modules":
     babelPluginModuleAlias: true
     webpack: true
@@ -49,13 +43,14 @@
     instantSearch: true
   "language-babel": {}
   linter: {}
+  "linter-elixir-credo":
+    flags: "--strict"
   "linter-eslint":
     fixOnSave: true
   "linter-rubocop":
     disableWhenNoConfigFile: true
   "linter-sass-lint":
     noConfigDisable: true
-  "linter-ui-default": {}
   "markdown-deluxe":
     fontFamily: "Hack"
   "package-sync":


Added some vim keymaps

Added some vim keymaps

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -3,3 +3,6 @@
   'ctrl-d': 'editor:select-word'
 '.platform-linux atom-workspace, .platform-win32 atom-workspace':
   'shift-enter': 'terminal-panel:toggle'
+'atom-text-editor.vim-mode-plus:not(.insert-mode)':
+  'ctrl-d': 'editor:select-word'
+  'ctrl-f': 'find-and-replace:show'


Workspace: Don't show host disconnected modal when session not active.

--- client/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -86,7 +86,8 @@
       # if we can't get the node value then it means user really disconnected.
       KD.utils.wait 1500, =>
         @workspaceRef.once "value", (snapshot) =>
-          @showDisconnectedModal()  unless snapshot.val() or @disconnectedModal
+          unless snapshot.val() or @disconnectedModal or @sessionNotActive
+            @showDisconnectedModal()

     @broadcastRef.on "value", (snapshot) =>
       message = snapshot.val()
@@ -164,6 +165,7 @@
         @startNewSession()

     @container.addSubView notValid
+    @sessionNotActive = yes
     @loader.hide()

   startNewSession: ->


sidebar: remove unused require

sidebar: remove unused require

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/sidebar/components/ownedresourceslist.coffee
+++ client/app/lib/sidebar/components/ownedresourceslist.coffee
@@ -13,8 +13,6 @@

 List = require 'app/components/list'
 Link = require 'app/components/common/link'
-
-SidebarNoStacks = require 'app/components/sidebarstacksection/sidebarnostacks'

 SidebarMachineItem = require './machineitemcontainer'
 OwnedResourceHeader = require './ownedresourceheader'


clean request sequence

clean request sequence

--- config/initializers/requests.coffee
+++ config/initializers/requests.coffee
@@ -2,7 +2,6 @@

 ## Requests

-User.defineRequest "all", requests.allType, requests.checkError
 User.all = (callback) -> User.request "all", callback
 Tree.defineRequest "all", requests.allType, requests.checkError
 TodoList.defineRequest "all", requests.all, requests.checkError
@@ -11,16 +10,8 @@
 todos = -> emit doc.description, doc if not doc.done
 archiveList = -> emit [doc.list, doc.completionDate], doc if doc.done
 todosList = -> emit [doc.list, doc.previousTask], doc if not doc.done
-Task.defineRequest "all", requests.all, requests.checkError
-setTimeout ->
-    Task.defineRequest "archive", archive, requests.checkError
-, 1000
-setTimeout ->
-    Task.defineRequest "todos", todos, requests.checkError
-, 2000
-setTimeout ->
-    Task.defineRequest "archiveList", archiveList, requests.checkError
-, 3000
-setTimeout ->
-    Task.defineRequest "todosList", todosList, requests.checkError
-, 4000
+Task.defineRequest "all", requests.all ->
+    Task.defineRequest "archive", archive ->
+        Task.defineRequest "todos", todos, ->
+            Task.defineRequest "archiveList", archiveList, ->
+                Task.defineRequest "todosList", todosList, requests.checkError


Removed console.log

Removed console.log

--- src/chocolate.coffee
+++ src/chocolate.coffee
@@ -353,7 +353,6 @@
       offset = 0
     if offset < width * -1
       offset = width * -1
-    console.log offset

     translate @thumbnails, offset
     @


Fetching models

Fetching models

--- assets/javascripts/routes/jobs.coffee
+++ assets/javascripts/routes/jobs.coffee
@@ -1 +1,3 @@
 Lever.JobsIndexRoute = Ember.Route.extend
+  model: ->
+    @get('store').find 'job'


Removed debug msg, tweaking

Removed debug msg, tweaking

--- src/creatures.coffee
+++ src/creatures.coffee
@@ -37,14 +37,12 @@

 	constructor: ->
 		super
-		@personality = new (require './personality').FleeFromPlayer 9
+		@personality = new (require './personality').FleeFromPlayer 3

 	tickRate: -> 11

 	aiTick: ->
-		w = @personality.weight(this)
-		(require 'winston').info "Weight: #{w}"
-		if w > 0 then @personality.tick(this)
+		if @personality.weight(this) > 0 then @personality.tick(this)

 		else @tickRate()


VarChar, NVarChar length must not be zero

--- src/data-type.coffee
+++ src/data-type.coffee
@@ -288,7 +288,7 @@
       if parameter.length
         length = parameter.length
       else if parameter.value?
-        length = parameter.value.toString().length
+        length = parameter.value.toString().length || 1
       else if parameter.value is null
         length = 1
       else
@@ -348,7 +348,7 @@
       if parameter.length
         length = parameter.length
       else if parameter.value?
-        length = parameter.value.toString().length
+        length = parameter.value.toString().length || 1
       else if parameter.value is null
         length = 1
       else


Fixed group invitations

Fixed group invitations

--- client/Dashboard/views/groupsinvitationview.coffee
+++ client/Dashboard/views/groupsinvitationview.coffee
@@ -5,7 +5,7 @@
     super options, data

     @getData().fetchMembershipPolicy (err, @policy)=>
-      @addSubView tabHandleContainer = new KDCustomHTMLView
+      @addSubView tabHandleContainer = new KDTabHandleContainer
       @addSubView @tabView = new GroupsInvitationTabView {
         delegate           : this
         tabHandleClass     : GroupTabHandleView


Client: Battle updates are queued, not executed in parallel.

--- client/app/js/collections/battle_collection.coffee
+++ client/app/js/collections/battle_collection.coffee
@@ -7,6 +7,7 @@
       'spectate battle': @spectateBattle
       'join battle': @joinBattle
       'leave battle': @leaveBattle
+    @updateQueue = []
     @on 'remove', (model) ->
       PokeBattle.socket.send('leave battle', model.id)

@@ -16,17 +17,22 @@
       console.log "Received events for #{battleId}, but no longer in battle!"
       return
     battle.notify()
-    @_updateBattle(battle, actions)
-
-  _updateBattle: (battle, actions, wasAtBottom) =>
+    @queueBattleUpdates(battle, actions)
+
+  queueBattleUpdates: (battle, actions) =>
+    hadStuff = (@updateQueue.length > 0)
+    @updateQueue.push(actions...)
+    if !hadStuff then @_updateBattle(battle)
+
+  _updateBattle: (battle, wasAtBottom) =>
     view = battle.view
-    if actions.length == 0
+    if @updateQueue.length == 0
       view.renderUserInfo()
       if wasAtBottom || view.skip? then view.chatView.scrollToBottom()
       if view.skip?                then delete view.skip
       return
     wasAtBottom ||= view.chatView.isAtBottom()
-    action = actions.shift()
+    action = @updateQueue.shift()
     [ type, rest... ] = action
     protocol = (key  for key, value of Protocol when value == type)[0]
     console.log "Received protocol: #{protocol}"
@@ -34,7 +40,7 @@
     done = () =>
       return  if done.called
       done.called = true
-      @_updateBattle.call(this, battle, actions, wasAtBottom)
+      @_updateBattle.call(this, battle, wasAtBottom)

     try
       switch type
@@ -185,7 +191,7 @@
     createBattleWindow(this, battle)
     if log.length > 0
       battle.view.skip = 0
-      @_updateBattle(battle, log, false)
+      @queueBattleUpdates(battle, log)

   joinBattle: (socket, id, user) =>
     battle = @get(id)


Fixed unit test

Fixed unit test

--- test/basicTests.coffee
+++ test/basicTests.coffee
@@ -8,6 +8,8 @@
     equal = _.isEqual(actual, expected, (a,b)->
       if _.isNumber(a) and _.isNumber(b)
         expect(a).to.be.within(b-.01,b+.01)
+        return true
+      if _.isString(a) and _.isString(b) and a.indexOf("Success")==0 and b.indexOf("Success")==0
         return true
       return undefined
     )


enable rest of books

enable rest of books

--- exercises/src/components/tags/book-selection.cjsx
+++ exercises/src/components/tags/book-selection.cjsx
@@ -10,11 +10,8 @@
   'stax-cbio'    : 'Concepts of Biology'
   'stax-econ'    : 'Economics'
   'stax-anp'     : 'Anatomy and Physiology'
-
-
-# Temporarily removed from list until books are ready
-#  'stax-macro'   : 'Macro Economics'
-#  'stax-micro'   : 'Micro Economics'
+  'stax-macro'   : 'Macro Economics'
+  'stax-micro'   : 'Micro Economics'

 BookSelection = React.createClass


Repaired matching.  Rationalized order.

Repaired matching.  Rationalized order.

--- bokehjs/src/coffee/common/base.coffee
+++ bokehjs/src/coffee/common/base.coffee
@@ -82,11 +82,11 @@
   'widget/dialog'

   'transforms/const'
+  'transforms/count'
+  'transforms/cuberoot'
   'transforms/id'
-  'transforms/count'
+  'transforms/interpolate'
   'transforms/seq'
-  'transforms/interpolate'
-  'transforms/cuberoot'
 ], (_, require) ->

   # add some useful functions to underscore
@@ -175,12 +175,13 @@
     Tabs:                     'widget/tabs'
     Dialog:                   'widget/dialog'

+    Const:                    'transforms/const'
+    Count:                    'transforms/count'
     Cuberoot:                 'transforms/cuberoot'
+    Id:                       'transforms/id'
+    Interpolate:              'transforms/interpolate'
     Seq:                      'transforms/seq'
-    Interpolate:              'transforms/const'
-    Const:                    'transforms/interpolate'
-    Count:                    'transforms/count'
-    Id:                       'transforms/id'
+
   mod_cache = {}
   collection_overrides = {}
   Collections = (typename) ->


test filter-0

test filter-0

--- test/filter/all.coffee
+++ test/filter/all.coffee
@@ -531,3 +531,68 @@
             $test.equal result[2].kk, 'version'
             $test.equal result[2].vv, '16.04'
             $test.close()
+
+
+Test('filter-0').run ($test, alight, timeout) ->
+    $test.start 10
+
+    alight.filters.double =
+        init: (scope, dividerKey, env) ->
+            value = null
+            divider = null
+            @.watch dividerKey, (value) ->
+                divider = value
+                update()
+
+            planned = false
+            update = =>
+                if planned
+                    return
+                planned = true
+                @.watch '$onScanOnce', ->
+                    planned = false
+                    if value and divider
+                        env.setValue value + divider + value
+
+            onChange: (input) ->
+                value = input
+                update()
+            onStop: ->
+                onstop += 1
+
+    cd = alight.ChangeDetector
+        name: 'Linux'
+        mid: '+'
+
+    count = 0
+    result = null
+    onstop = 0
+    cd.watch 'name | double mid', (value) ->
+        result = value
+        count += 1
+
+    cd.scan ->
+        $test.equal count, 1
+        $test.equal result, 'Linux+Linux'
+
+        cd.scope.name = 'ubuntu'
+        cd.scan ->
+            $test.equal count, 2
+            $test.equal result, 'ubuntu+ubuntu'
+
+            cd.scope.mid = '-'
+            cd.scan ->
+                $test.equal count, 3
+                $test.equal result, 'ubuntu-ubuntu'
+
+                cd.scope.name = 'debian'
+                cd.scope.mid = '*'
+                cd.scan ->
+                    $test.equal count, 4
+                    $test.equal result, 'debian*debian'
+
+                    $test.equal onstop, 0
+                    cd.destroy()
+                    $test.equal onstop, 1
+
+                    $test.close()


Fix typos

Fix typos

--- test/generation.coffee
+++ test/generation.coffee
@@ -7,9 +7,9 @@
 describe 'generation', ->

   expectUsingIteration = (grammar, expected) ->
-    gemerator = rmutt.compile grammar
+    generator = rmutt.compile grammar
     for result, index in expected
-      expect(gemerator iteration: index)
+      expect(generator iteration: index)
         .to.equal result

   it 'more than one dash in rule identifier', ->
@@ -172,15 +172,15 @@
       obj: "you", "me";
       adv: "patiently", "impatiently";
     """
-    gemerator = rmutt.compile grammar
-
-    expect(gemerator iteration: 0)
+    generator = rmutt.compile grammar
+
+    expect(generator iteration: 0)
       .to.equal 'ate patiently with you'

-    expect(gemerator iteration: 200)
+    expect(generator iteration: 200)
       .to.equal 'waited patiently for me'

-    expect(gemerator iteration: 400)
+    expect(generator iteration: 400)
       .to.equal 'yelled impatiently at you'

   it 't7 - variables', ->
@@ -528,13 +528,13 @@
         .to.equal 'A';

     it 'defined in transpilation', ->
-      gemerator = rmutt.compile @grammar, entry: 'b'
-      expect(gemerator())
+      generator = rmutt.compile @grammar, entry: 'b'
+      expect(generator())
         .to.equal 'B';

     it 'override defined in transpilation', ->
-      gemerator = rmutt.compile @grammar, entry: 'b'
-      expect(gemerator(entry: 'c'))
+      generator = rmutt.compile @grammar, entry: 'b'
+      expect(generator(entry: 'c'))
         .to.equal 'C';

   describe 'external rules', ->


lenght to count func

lenght to count func

--- test/js/totodoo.coffee
+++ test/js/totodoo.coffee
@@ -38,7 +38,7 @@

 			it 'is the lenght of the default list items = 2', (done) ->
 				lis = @driver.findElements(js: 'return document.getElementById("todo-list").getElementsByTagName("li")')
-				expect(lis.length).to.eventually.equal 2
+				expect(lis.count()).to.eventually.equal 2
 				@timeout 4000, done()

 	describe 'List actions', ->


driver copy

driver copy

--- test/js/totodoo.coffee
+++ test/js/totodoo.coffee
@@ -26,8 +26,9 @@
 			@timeout 4000, done()

 		it 'is the name of the default list', (done) ->
+			driver = @driver
 			text = @driver.findElement(id: 'listName').getText()
-			@driver.wait(-> @driver.isElementPresent @driver.findElement(id: 'todo-list').findElement(tagName: 'li'), 10000)
+			@driver.wait(-> driver.isElementPresent @driver.findElement(id: 'todo-list').findElement(tagName: 'li'), 10000)
 			expect(text).to.eventually.equal 'Public list 1'
 			@timeout 10000, done()


Added test for parsing empty lines

Added test for parsing empty lines

--- test/parse_test.coffee
+++ test/parse_test.coffee
@@ -360,6 +360,20 @@
     """
     ret = chalkboard.parse code, @lang, {}
     test.equal ret[0].description, "Hello  \n"
+    test.done()
+
+  "preserve empty line between text bock": (test) ->
+    code = """
+      ###
+      @chalk
+      @description
+      Testing
+
+      whitespaces
+      ###
+    """
+    ret = chalkboard.parse code, @lang, {}
+    test.equal ret[0].description, "Testing  \n  \nwhitespaces  \n"
     test.done()

 exports.jsParseTest =


Fix sidebar when an account doesn't have a standard category

--- internal_packages/account-sidebar/lib/sidebar-section.coffee
+++ internal_packages/account-sidebar/lib/sidebar-section.coffee
@@ -27,8 +27,12 @@
     }

   @standardSectionForAccount: (account) ->
-    return @empty('Mailboxes') if CategoryStore.categories().length is 0
+    if not account
+      throw new Error("standardSectionForAccount: You must pass an account.")
+
     cats = CategoryStore.standardCategories(account)
+    return @empty('Mailboxes') if cats.length is 0
+
     items = _
       .reject(cats, (cat) -> cat.name is 'drafts')
       .map (cat) => SidebarItem.forCategories([cat])
@@ -63,11 +67,13 @@
       categories = CategoryStore.getStandardCategories(accounts, names...)
       continue if categories.length is 0

-      children = accounts.map (acc) ->
+      children = []
+      accounts.forEach (acc) ->
         cat = _.first(_.compact(
           names.map((name) -> CategoryStore.getStandardCategory(acc, name))
         ))
-        SidebarItem.forCategories([cat], name: acc.label)
+        return unless cat
+        children.push(SidebarItem.forCategories([cat], name: acc.label))

       items.push SidebarItem.forCategories(categories, {children})


add test without callback

add test without callback

--- test/sshfs-test.coffee
+++ test/sshfs-test.coffee
@@ -72,4 +72,30 @@
         assert.isFalse u.include result, 'root'
         assert.isFalse u.include result, 'etc'
         assert.isFalse u.include result, 'home'
+  .addBatch
+    'when mounting a server without callback':
+      topic: ->
+        callback = this.callback
+
+        sshfs.mount config.user, config.host, mountPoint
+        setTimeout ->
+          callback()
+        , 1000
+        return
+
+      'we got no error': (err, arg2) ->
+        assert.isNull err
+  .addBatch
+    'when umounting a server':
+      topic: ->
+        callback = this.callback
+
+        sshfs.umount mountPoint, true
+        setTimeout ->
+          callback()
+        , 1000
+        return
+
+      'we got no error': (err, arg2) ->
+        assert.isNull err
   .export(module)


kiss

kiss

Signed-off-by: Carlos Escribano <ab5e2bca84933118bbc9d48ffaccce3bac4eeb64@doofinder.com>

--- test/test_stats.coffee
+++ test/test_stats.coffee
@@ -92,9 +92,9 @@

       stats = getStats()

-      (-> stats.registerClick.apply stats, []).should.throw
-      (-> stats.registerClick.apply stats, [session_id]).should.throw
-      (-> stats.registerClick.apply stats, [session_id, id]).should.throw
+      (-> stats.registerClick()).should.throw
+      (-> stats.registerClick(session_id)).should.throw
+      (-> stats.registerClick(session_id, id)).should.throw

       done()


Fixed test/test_trade

Fixed test/test_trade

--- test/test_trade.coffee
+++ test/test_trade.coffee
@@ -13,10 +13,10 @@

 describe 'TradeEngine', ->
   beforeEach =>
-    TestHelper.clean_state_sync()
+    TestHelper.remove_log(kTestFilename)

   afterEach =>
-    TestHelper.clean_state_sync()
+    TestHelper.remove_log(kTestFilename)

   it 'can perform deposit', (finish) ->
     deferred = Q.defer()


another reply style

another reply style

--- scripts/imaginaria.coffee
+++ scripts/imaginaria.coffee
@@ -28,8 +28,7 @@
           feed.init()
           count = msg.match[1] || 5
           items = feed.getItems(0, count)
-          msg.send item.getTitle() + ": " + item.getPermalink() + " [" + item.getCategories() + "]" for item in items
-          msg.send "<"+item.getPermalink()+"|"+item.getTitle()+">" + " [" + item.getCategories() + "]" for item in items
+          msg.send item.getTitle() + " [" + item.getCategories() + "] : " + item.getPermalink() +  for item in items
         catch e
           console.log(e)
           msg.send "Something's gone awry"


Fixed resent of articles.

Fixed resent of articles.

--- app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
@@ -608,6 +608,9 @@
       done: (r) =>
         @renderDone = false

+        # reset article - should not be resubmited on next ticket update
+        ticket.article = undefined
+
         # reset form after save
         @taskReset()

@@ -673,6 +676,10 @@
     # update title
     if title isnt @ticket.title
       @ticket.title = title
+
+      # reset article - should not be resubmited on next ticket update
+      @ticket.article = undefined
+
       @ticket.save()

       # update taskbar with new meta data


Fixed issue#466 - Search result is not shown if you search for "AAA", "BBB" and "AAA" again.

--- app/assets/javascripts/app/lib/app_post/global_search.coffee
+++ app/assets/javascripts/app/lib/app_post/global_search.coffee
@@ -9,7 +9,8 @@
 class _globalSearchSingleton extends Spine.Module

   constructor: ->
-    @searchResultCache = {}
+    @searchResultCache = undefined
+    @searchResultCacheByKey = {}
     @apiPath = App.Config.get('api_path')

   execute: (params) ->
@@ -20,8 +21,8 @@

     # use cache for search result
     currentTime = new Date
-    if @searchResultCache[cacheKey] && @searchResultCache[cacheKey].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
-      render(@searchResultCache[cacheKey].result)
+    if @searchResultCacheByKey[cacheKey] && @searchResultCacheByKey[cacheKey].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
+      @renderTry(render, @searchResultCacheByKey[cacheKey].result, cacheKey)
       return

     App.Ajax.request(
@@ -31,7 +32,7 @@
       data:
         query: query
         limit: limit
-      processData: true,
+      processData: true
       success: (data, status, xhr) =>
         App.Collection.loadAssets(data.assets)
         result = {}
@@ -48,19 +49,21 @@
           else
             App.Log.error('_globalSearchSingleton', "No such model App.#{item.type}")

-        diff = false
-        if @searchResultCache[cacheKey]
-          diff = difference(@searchResultCache[cacheKey].resultRaw, data.result)
+        @renderTry(render, result, cacheKey)
+    )

-        # cache search result
-        @searchResultCache[cacheKey] =
-          result: result
-          resultRaw: data.result
-          limit: limit
-          time: new Date
+  renderTry: (render, result, cacheKey) =>

-        # if result hasn't changed, do not rerender
-        return if diff isnt false && _.isEmpty(diff)
+    # if result hasn't changed, do not rerender
+    diff = false
+    if @searchResultCache
+      diff = difference(@searchResultCache, result)
+    return if diff isnt false && _.isEmpty(diff)

-        render(result)
-    )
+    # cache search result
+    @searchResultCache = result
+    @searchResultCacheByKey[cacheKey] =
+      result: result
+      time: new Date
+
+    render(result)


Fix: Don't wait for all parents to be loaded to adjust scroll postition in conversation view

--- assets/javascripts/views/post/conversation/parents.js.coffee
+++ assets/javascripts/views/post/conversation/parents.js.coffee
@@ -12,14 +12,9 @@
     reference_post = @postView().post()
     mentions = reference_post.postMentions()

-    num_remaining = mentions.length
-    complete = =>
-      num_remaining--
-      @trigger('ready') if num_remaining == 0
-
     for m in mentions
       do (m) =>
         @fetchPost {entity: m.entity, id: m.post}, (post) =>
           @prependRender([post])
-          complete()
+          @trigger('ready')


:art:

:art:

--- build/tasks/prebuild-less-task.coffee
+++ build/tasks/prebuild-less-task.coffee
@@ -35,39 +35,27 @@
   grunt.registerMultiTask 'prebuild-less', 'Prebuild cached of compiled Less files', ->
     compileBootstrap()

-    prebuiltConfigurations = [
-      ['atom-dark-ui', 'atom-dark-syntax']
-      ['atom-dark-ui', 'atom-light-syntax']
-      ['atom-dark-ui', 'one-dark-syntax']
-      ['atom-dark-ui', 'one-light-syntax']
-      ['atom-dark-ui', 'solarized-dark-syntax']
-      ['atom-dark-ui', 'base16-tomorrow-dark-theme']
-      ['atom-dark-ui', 'base16-tomorrow-light-theme']
+    uiThemes = [
+      'atom-dark-ui'
+      'atom-light-ui'
+      'one-dark-ui'
+      'one-light-ui'
+    ]

-      ['atom-light-ui', 'atom-light-syntax']
-      ['atom-light-ui', 'atom-dark-syntax']
-      ['atom-light-ui', 'one-dark-syntax']
-      ['atom-light-ui', 'one-light-syntax']
-      ['atom-light-ui', 'solarized-dark-syntax']
-      ['atom-light-ui', 'base16-tomorrow-dark-theme']
-      ['atom-light-ui', 'base16-tomorrow-light-theme']
+    syntaxThemes = [
+      'atom-dark-syntax'
+      'atom-light-syntax'
+      'one-dark-syntax'
+      'one-light-syntax'
+      'solarized-dark-syntax'
+      'base16-tomorrow-dark-theme'
+      'base16-tomorrow-light-theme'
+    ]

-      ['one-dark-ui', 'one-dark-syntax']
-      ['one-dark-ui', 'one-light-syntax']
-      ['one-dark-ui', 'atom-dark-syntax']
-      ['one-dark-ui', 'atom-light-syntax']
-      ['one-dark-ui', 'solarized-dark-syntax']
-      ['one-dark-ui', 'base16-tomorrow-dark-theme']
-      ['one-dark-ui', 'base16-tomorrow-light-theme']
-
-      ['one-light-ui', 'one-light-syntax']
-      ['one-light-ui', 'one-dark-syntax']
-      ['one-light-ui', 'atom-light-syntax']
-      ['one-light-ui', 'atom-dark-syntax']
-      ['one-light-ui', 'solarized-dark-syntax']
-      ['one-light-ui', 'base16-tomorrow-dark-theme']
-      ['one-light-ui', 'base16-tomorrow-light-theme']
-    ]
+    prebuiltConfigurations = []
+    uiThemes.forEach (uiTheme) ->
+      syntaxThemes.forEach (syntaxTheme) ->
+        prebuiltConfigurations.push([uiTheme, syntaxTheme])

     directory = path.join(grunt.config.get('atom.appDir'), 'less-compile-cache')


Remove extraneous comment

Remove extraneous comment

--- bokehjs/src/coffee/common/models.coffee
+++ bokehjs/src/coffee/common/models.coffee
@@ -110,7 +110,6 @@
   QUADKEYTileSource:        require '../models/tiles/quadkey_tile_source'
   BBoxTileSource:           require '../models/tiles/bbox_tile_source'

-  # dbl check
   DynamicImageRenderer:     require '../models/tiles/dynamic_image_renderer'
   ImageSource:              require '../models/tiles/image_source'


Get rid of stray semi-colon

Get rid of stray semi-colon

--- bokehjs/src/coffee/widget/slider.coffee
+++ bokehjs/src/coffee/widget/slider.coffee
@@ -36,7 +36,7 @@
   slide: (event, ui) =>
     value = ui.value
     logger.debug("slide value = #{value}")
-    @$( "##{ @mget('id') }" ).val( ui.value );
+    @$( "##{ @mget('id') }" ).val( ui.value )
     @mset('value', value)
     @model.save()


activity: set "hidden" as initial CSS class of `ActivityLikeSummaryView` class

--- client/Social/Activity/views/likesummaryview.coffee
+++ client/Social/Activity/views/likesummaryview.coffee
@@ -2,7 +2,7 @@

   constructor: (options = {}, data) ->

-    options.cssClass = KD.utils.curry "like-summary", options.cssClass
+    options.cssClass = KD.utils.curry "like-summary hidden", options.cssClass

     super options, data

@@ -46,8 +46,6 @@

     super

-    if @getData().interactions.like.actorsCount is 0
-    then @setClass 'hidden'

     names  = []
     strong = (x) -> "<strong>#{x}</strong>"


Docs - removed laravel 3 support and some cleaning

--- scripts/docs.coffee
+++ scripts/docs.coffee
@@ -2,12 +2,12 @@
 #   LioBot gets you tip top information from the Laravel or PHP documentation
 #
 # Commands:
-#   LioBot show <nick?> docs for <type?> <version?> <query>#
+#   LioBot show <nick?> docs for <type?> <version?> <query>
 #   LioBot show docs search engine
 #   LioBot show docs target version
 #
-#   LioBot set docs search engine (google|bing)
-#   LioBot set docs target version ([0-9.]+)
+#   LioBot set docs search engine to (google|bing)
+#   LioBot set docs target version to ([0-9.]+)
 #
 # Notes:
 #   None
@@ -20,79 +20,51 @@
   google: 'https://www.google.com/search'
   bing: 'http://www.bing.com/search'

-BRAIN_KEY = 'docs-search-engine'
+ENGINE_KEY = 'docs-search-engine'
 VERSION_KEY = 'docs-search-version'
+
+TARGET_VERSION = 'master'
+SEARCH_ENGINE = 'bing'

 USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36'
 #Rommie=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17

-getCurrentEngine = (robot) ->
-  robot.brain.get BRAIN_KEY || 'bing'
-
 getSearchUrl = (key) ->
   SEARCH_URLS[key]

-setCurrentEngine = (robot, engine) ->
-  robot.brain.set BRAIN_KEY, engine
+module.exports = (robot) ->

-getTargetVersion = (robot) ->
-  robot.brain.get(VERSION_KEY) || 'master'
+  robot.brain.on 'loaded', =>
+    TARGET_VERSION = robot.brain.data._private[VERSION_KEY] || TARGET_VERSION
+    SEARCH_ENGINE = robot.brain.data._private[ENGINE_KEY] || SEARCH_ENGINE

-setTargetVersion = (robot, version) ->
-  robot.brain.set VERSION_KEY, version
-
-module.exports = (robot) ->
   getQueryUrl = (doctype, version, query) ->
-    version = 3 if version?.match /^3(.*)/i
-
-    TARGET_VERSION = getTargetVersion robot
-
-    if doctype == 'api'
-      if version == 3
-        "site:l3.shihan.me/api #{query}"
-      else if version?
-        "site:laravel.com/api/#{version} #{query}"
-      else
-        "site:laravel.com/api/#{TARGET_VERSION} #{query}"
-    else if doctype == 'lumen'
-      if version?
-        "site:lumen.laravel.com/docs/#{version} #{query}"
-      else
-        "site:lumen.laravel.com/docs #{query}"
-    else if doctype == 'php'
-      "site:php.net/manual/en #{query}"
-    else
-      if version == 3
-        "site:laravel3.veliovgroup.com/docs #{query}"
-      else if version?
-        "site:laravel.com/docs/#{version} #{query}"
-      else
-        "site:laravel.com/docs/#{TARGET_VERSION} #{query}"
+    switch doctype
+      when 'api' then "site:laravel.com/api/#{version || TARGET_VERSION} #{query}"
+      when 'lumen' then "site:lumen.laravel.com/docs/#{version || ''} #{query}"
+      when 'php' then "site:php.net/manual/en #{query}"
+      else "site:laravel.com/docs/#{version || TARGET_VERSION} #{query}"

   fetchResult = (query, location, callback) ->
     robot.http(location)
       .header('User-Agent', USER_AGENT)
       .query(q: query)
       .get() (err, res, body) ->
-        # if res.statusCode is 302
-        #   return fetchResult query, res.headers.location, callback
-
         $ = cheerio.load body

-        if getCurrentEngine(robot) == 'google'
+        if SEARCH_ENGINE is 'google'
           result = $('#rso .g').first()
           # Jump To Link ? Main Link
           url = result.find('.s span.f a').attr('href') ? result.find('h3.r a').attr('href')
-        else # bing
-          result = $('ol#b_results li.b_algo h2')
-          url = result.find('a').attr('href')
+        else
+          url = $('ol#b_results li.b_algo h2').find('a').attr('href')

         callback url if callback

   docFetcher = (msg) ->
     [user, doctype, version, query] = msg.match[1..4]

-    fetchResult getQueryUrl(doctype, version, query), getSearchUrl(getCurrentEngine(robot)), (url) ->
+    fetchResult getQueryUrl(doctype, version, query), getSearchUrl(SEARCH_ENGINE), (url) ->
       return msg.send "No results for \"#{query.substr(0,30)}\"" unless url

       response = url
@@ -106,24 +78,33 @@
       else
         msg.send response

+  setDocsSearchEngine = (msg) ->
+    if whiteList.isTeacher msg.robot, msg.message.user
+      engine = msg.match[1]
+      msg.robot.brain.set ENGINE_KEY, SEARCH_ENGINE = engine
+      msg.send "The docs search engine is set to #{engine}."
+
+  setDocsTargetVersion = (msg) ->
+    if whiteList.isTeacher msg.robot, msg.message.user
+      version = msg.match[1]
+      msg.robot.brain.set VERSION_KEY, TARGET_VERSION = version
+      msg.send "The targeted docs version has been set to #{version}."
+
+  showDocsSearchEngine = (msg) ->
+    msg.send "The current docs search engine is #{SEARCH_ENGINE}."
+
+  showDocsTargetVersion = (msg) ->
+    msg.send "The current targeted docs version id #{TARGET_VERSION}."
+
+
   robot.respond /show (?:([^\s!]+) )?docs for (?:(api|php|lumen) )?(?:([0-9.]+) )?(.+)/i, docFetcher

-  robot.respond /show docs search engine/i, (msg) ->
-    msg.send "The current docs search engine is #{getCurrentEngine robot}."
+  robot.respond /show docs search engine/i, showDocsSearchEngine

-  robot.respond /show docs target version/i, (msg) ->
-    msg.send "The current targeted docs version is #{getTargetVersion robot}."
+  robot.respond /show docs target version/i, showDocsTargetVersion

-#ACL - teacher
-  robot.respond /set docs search engine to (google|bing)/i, (msg) ->
-    if whiteList.isTeacher robot, msg.message.user
-      engine = msg.match[1]
-      setCurrentEngine robot, engine
-      msg.send "The docs search engine is set to #{engine}."
+  #ACL - teacher
+  robot.respond /set docs search engine to (google|bing)/i, setDocsSearchEngine

-#ACL - admin
-  robot.respond /set docs target version to ([0-9.]+)/i, (msg) ->
-    if whiteList.isAdmin robot, msg.message.user
-      version = msg.match[1]
-      setTargetVersion robot, version
-      msg.send "The targeted docs version has been set to #{version}."+  #ACL - teacher
+  robot.respond /set docs target version to ([0-9.]+)/i, setDocsTargetVersion


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -12,7 +12,7 @@
       log I.duration
       I.alpha = 1 - (I.age + 1 / I.duration)

-    if I.duration != -1 && I.age >= I.duration
+    if I.duration != -1 && I.age + 1 >= I.duration
       I.active = false

   return {}


Push information about groups to the client.

--- server/person.coffee
+++ server/person.coffee
@@ -27,6 +27,7 @@
       'givenName'
       'familyName'
       'isAdmin'
+      'inGroups'
     ]

   # A set of fields which are public and can be published to the client
@@ -38,6 +39,7 @@
       givenName: 1
       familyName: 1
       isAdmin: 1
+      inGroups: 1
       work: 1
       education: 1
       publications: 1


KodingKontrol: pass transportOptions to constructor.options if provided

--- client/Main/kite/kodingkontrol.coffee
+++ client/Main/kite/kodingkontrol.coffee
@@ -84,7 +84,7 @@
   getKite: (options = {}) ->

     # Get options
-    { name, correlationName, region,
+    { name, correlationName, region, transportOptions,
       username, environment, queryString } = options

     # If queryString provided try to split it first
@@ -97,7 +97,7 @@

     # Get Kite Proxy, it will be created at this point
     # since its not cached before
-    kite = @getKiteProxy { name, correlationName }
+    kite = @getKiteProxy { name, correlationName, transportOptions }

     # Query kontrol
     @fetchKite


KodingKontrol: fix conflict issue

KodingKontrol: fix conflict issue

--- client/Main/kite/kodingkontrol.coffee
+++ client/Main/kite/kodingkontrol.coffee
@@ -10,20 +10,13 @@
     @regions = {}

   getAuthOptions: ->
-<<<<<<< HEAD
-    autoConnect : no
-    url         : @_kontrolUrl ? KD.config.newkontrol.url
-    auth        :
-      type      : 'sessionID'
-      key       : Cookies.get 'clientId'
-=======
     autoConnect     : no
-    url             : KD.config.newkontrol.url
+    url             : @_kontrolUrl ? KD.config.newkontrol.url
     auth            :
       type          : 'sessionID'
       key           : Cookies.get 'clientId'
     transportClass  : SockJS
->>>>>>> master
+

   reauthenticate: ->
     # disconnect the old kontrol kite


realtime: add comments for timetoken parameter

--- client/Main/realtimecontroller.coffee
+++ client/Main/realtimecontroller.coffee
@@ -192,7 +192,11 @@
         error   : (err) =>
           @handleError err
           callback err
-        timetoken: ((new Date()).getTime() - 1500) * 10000
+        # with each channel subscription pubnub resubscribes to every channel
+        # and some messages are dropped in this resubscription time interval
+        # for this reason for every subscribe request, we are fetching all messages sent
+        # in last 3 seconds
+        timetoken: ((new Date()).getTime() - 3000) * 10000
         restore : yes


Fix for disappearing input entry when switching tabs.

--- packages/artificialengines/mirage/datainput/datainput.coffee
+++ packages/artificialengines/mirage/datainput/datainput.coffee
@@ -71,6 +71,7 @@

   events: -> [
     'change input, change textarea': @onChange
+    'blur input, blur textarea': @onBlur
     'input input, input textarea': @onInput
     'change select': @onChangeSelect
   ]
@@ -80,6 +81,9 @@
       @save $(event.target).is(':checked')
       return

+    @save @_convertValue $(event.target).val() unless @realtime
+
+  onBlur: (event) ->
     @save @_convertValue $(event.target).val() unless @realtime

   onInput: (event) ->


console.logへの私方のミスの修正

console.logへの私方のミスの修正

--- send_message.coffee
+++ send_message.coffee
@@ -265,7 +265,7 @@
             , (err, response, body) ->
               console.log "err: #{err}" if err?
         when "idobata"
-          console.log("form: %j", form)
+          # console.log("form: %j", @form)
           request.post
             url: tg
             headers: @headers


Use Marked's async code highlight API. Enable smartypants

--- src/highlight.coffee
+++ src/highlight.coffee
@@ -1,42 +0,0 @@
-# Use pygments to highlight code blocks in HTML
-highlight = require('pygments').colorize
-async = require 'async'
-ent = require 'ent'
-
-CODE_BLOCK_REGEX = /
(.+)<\/pre>/mi
-
-module.exports =
-  highlightCodeBlocks: (html, callback) ->
-    unless ///<code///.test html
-      # No code blocks, return immediately
-      callback null, html
-      return
-
-    regex = ///
([\s\S]+?)
///img
-    matches = []
-    while match = regex.exec html
-      matches.push match
-
-    # Convert each code block
-    async.map(
-      matches,
-      (item, cb) ->
-        [input, lang, code] = item
-        # Code is HTML encoded, so we need to unescape
-        code = ent.decode code
-        highlight code, lang, 'html', (data) ->
-          # Strip out the HTML wrapper added around the code, and return the
-          #
 and  around the data
-          cb null, data.replace(
-            /^

([\s\S]+)<\/pre><\/div>$/img,
-            (match, p1, offset, str) ->
-              """
#{p1}
"""
-          )
-      (err, results) ->
-        if err then return callback err
-        # Now replace each instance
-        i = -1
-        callback null, html.replace regex, (match) ->
-          i += 1
-          return results[i]
-    )


StatusBar: User presence.

StatusBar: User presence.

--- client/ide/views/statusbar/statusbar.coffee
+++ client/ide/views/statusbar/statusbar.coffee
@@ -11,7 +11,7 @@
     @participantAvatars = {}

     @on 'ShowAvatars',          @bound 'showAvatars'
-    @on 'ParticipantLeft',      @bound 'removeParticipantAvatar'
+    @on 'ParticipantLeft',      @bound 'dimParticipantAvatar'
     @on 'ParticipantJoined',    @bound 'addParticipantAvatar'
     @on 'CollaborationEnded',   @bound 'handleCollaborationEnded'
     @on 'CollaborationStarted', @bound 'handleCollaborationStarted'
@@ -83,17 +83,24 @@
         @createParticipantAvatar nickname, isOnline

-  removeParticipantAvatar: (nickname) ->
+  dimParticipantAvatar: (nickname) ->

-    @participantAvatars[nickname]?.destroy()
-    delete @participantAvatars[nickname]
+    avatar = @participantAvatars[nickname]
+
+    if avatar
+      avatar.setClass   'offline'
+      avatar.unsetClass 'online'

   addParticipantAvatar: (nickname) ->

-    return if @participantAvatars[nickname]
+    oldAvatar = @participantAvatars[nickname]

-    @createParticipantAvatar nickname
+    if oldAvatar
+      oldAvatar.unsetClass 'offline'
+      oldAvatar.setClass   'online'
+    else
+      @createParticipantAvatar nickname, yes

   handleCollaborationEnded: ->


client: stackeditor: initial resize of the template label if title is large

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -211,13 +211,17 @@
       cssClass: 'StackEditorView--header'

     @header.addSubView @inputTitle = new kd.InputView
-      cssClass     : 'template-title'
-      autogrow     : yes
+      cssClass: 'template-title'
+      autogrow: yes
       defaultValue : stackTemplate?.title or 'Default stack template' # can we auto generate cute stack names?
       bind: 'keyup'
       keyup: (e) ->
         { changeTemplateTitle } = EnvironmentFlux.actions
         changeTemplateTitle stackTemplate?._id, e.target.value
+
+    @inputTitle.on 'viewAppended', =>
+      @inputTitle.prepareClone()
+      @inputTitle.resize()

   createOutputView: ->


Fix date with hours display

Fix date with hours display

--- client/templates/editor/date/date.js.coffee
+++ client/templates/editor/date/date.js.coffee
@@ -14,6 +14,8 @@
       editor = share.EditorCache.editors[template.data.family]
       editor.collection.update(template.data._id, {$set: $set})
   )
-  $editor.datepicker("setDate", @data.value)
+  date = @data.value.replace(/\:.+$/i, "") # actually, the format is yy/mm/dd:hh, but ":hh" part should be stripped for datepicker
+  $editor.datepicker("setDate", date)
+  $editor.val(@data.value)

 Template.date.events


Tests: Added scrollToElement for secret key input.

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -429,6 +429,7 @@
     deleteCredentialButton = "#{newCredential} button.delete"
     useCredentialButton    = "#{newCredential} button.verify"
     inUseLabelSelector     = "#{newCredential} .custom-tag.inuse"
+    secretKeyInput         = "#{credentialsPane} .secret-key input"

     { accessKeyId, secretAccessKey } = @getAwsKey()

@@ -449,7 +450,8 @@
           .pause                  500
           .setValue               "#{credentialsPane} .access-key input", accessKeyId
           .pause                  500
-          .setValue               "#{credentialsPane} .secret-key input", secretAccessKey
+          .scrollToElement        secretKeyInput
+          .setValue               secretKeyInput, secretAccessKey
           .pause                  1000
           .click                  '.credential-creation-intro'
           .scrollToElement        saveButtonSelector


Loosen up the intel map regex a bit

Loosen up the intel map regex a bit

--- src/intel-map.coffee
+++ src/intel-map.coffee
@@ -9,7 +9,7 @@
 #
 # Commands:
 #   hubot intelmap for <search>
-#   https://www.ingress.com/intel?ll=<lat>,<lng> - Return Google Maps image and link for intelmap location
+#   ingress.com/intel?ll=<lat>,<lng> - Return Google Maps image and link for intelmap location
 #

 module.exports = (robot) ->
@@ -60,7 +60,7 @@
     location = msg.match[2]
     lookupLatLong msg, location, sendIntelLink

-  robot.hear /https:\/\/www.ingress.com\/intel\?ll=(-?\d+\.\d+,-?\d+\.\d+)/, (msg) ->
+  robot.hear /ingress.com\/intel\?ll=(-?\d+\.\d+,-?\d+\.\d+)/, (msg) ->
     latlng = msg.match[1]
     sendGmapImageLink msg, latlng
     sendGmapLink msg, latlng


フォローしてるユーザの投稿情報をローカルにキャッシュする処理実装

フォローしてるユーザの投稿情報をローカルにキャッシュする処理実装

--- coffee/model/getFollowerItemsCommand.coffee
+++ coffee/model/getFollowerItemsCommand.coffee
@@ -14,17 +14,17 @@
       )

       result.push(mainTableView.createRow(json)) for json in items
-      result.push(mainTableView.createRowForLoadOldEntry(storedTo))
       mainTable.setData result
       @_hideStatusView()

   getFollowerItems:() ->
     @_showStatusView()
-    qiitaUser.getfollowingUserList( (result) =>
-
-      for item in result
-        # _url = "https://qiita.com/api/v1/users/#{item.url_name}/items?per_page=5"
-        _url = "https://qiita.com/api/v1/users/#{item.url_name}/items"
+    qiitaUser.getfollowingUserList( (userList) =>
+      # 1)フォローしてるユーザ情報のuserListを
+      # 順番にループして個々のユーザの投稿情報を取得
+      for item in userList
+        _url = "https://qiita.com/api/v1/users/#{item.url_name}/items?per_page=20"
+        # _url = "https://qiita.com/api/v1/users/#{item.url_name}/items"
         _items = []
         xhr = Ti.Network.createHTTPClient()
         xhr.open("GET",_url)
@@ -32,7 +32,6 @@
           if @.status is 200
             items = JSON.parse(@.responseText)
             if items isnt null
-              Ti.API.info typeof items
               for item in items
                 _items.push item

@@ -42,13 +41,17 @@
         xhr.timeout = 5000
         xhr.send()

-
+      # 1)の処理は非同期で実施されるため、最終的に
+      # 全部のユーザの投稿情報を取得するのに時間がかかるため
+      # ひとまず10秒間まってから、mainTable.setDataの処理と
+      # 取得した投稿情報のローカルへのキャッシュを実施
       setTimeout (=>
         result = []
         _items.sort( (a, b) ->
           (if moment(a.created_at).format("YYYYMMDDHHmm") > moment(b.created_at).format("YYYYMMDDHHmm") then -1 else 1)
         )
-
+        # フォローしてるユーザの投稿情報をローカルにキャッシュ
+        Ti.App.Properties.setString("followerItems",JSON.stringify(_items))
         for _item in _items
           result.push(mainTableView.createRow(_item))


fix splicing while iterating through array #118

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -129,6 +129,15 @@
         return port if port.public is publicPort
     return null

+  # To check if a node has exported ports
+  getExportByNode: (nodeKey, isIn) ->
+    if isIn is true or isIn is undefined
+      for port in @exports.inports
+        return port if port.process is nodeKey
+    if isIn is false or isIn is undefined
+      for port in @exports.outports
+        return port if port.nodeKey is nodeKey
+
   removeExport: (publicPort, isIn) ->
     @checkTransactionStart()

@@ -245,11 +254,15 @@
       if initializer.to.node is node.id
         @removeInitial initializer.to.node, initializer.to.port

-    for exported in @exports
-      continue unless exported
-      [privateNode, privatePort] = exported.private.split '.'
-      if privateNode is id.toLowerCase()
-        @removeExport exported.public
+    inport = @getExportByNode id, true
+    while inport
+      @removeExport inport.public, true
+      inport = @getExportByNode id, true
+
+    outport = @getExportByNode id, false
+    while outport
+      @removeExport outport.public, false
+      outport = @getExportByNode id, false

     for group in @groups
       continue unless group


rtm: fix metric names

rtm: fix metric names

--- client/ide/lib/realtimemanager.coffee
+++ client/ide/lib/realtimemanager.coffee
@@ -10,10 +10,10 @@

     super options, data

-    IDEMetrics.collect 'RealtimeManager.google_api_client', 'request'
+    IDEMetrics.collect 'RealTimeManager.google_api_client', 'request'
     GoogleApiClient.on 'ready', =>
       GoogleApiClient.loadDriveApi =>
-        IDEMetrics.collect 'RealtimeManager.google_api_client', 'ready'
+        IDEMetrics.collect 'RealTimeManager.google_api_client', 'ready'
         @emit 'ready'


IDE: removed unused logic

IDE: removed unused logic

--- client/ide/views/chat/chatview.coffee
+++ client/ide/views/chat/chatview.coffee
@@ -69,13 +69,4 @@

   showChatPane: -> @showPane @chatPane

-
   showSettingsPane: -> @showPane @settingsPane
-
-
-  show: ->
-
-    {windowController} = KD.singletons
-    windowController.addLayer this
-
-    super


Silently skip a data object if false is returned

--- client/lanes/react/mixins/Data.coffee
+++ client/lanes/react/mixins/Data.coffee
@@ -14,6 +14,8 @@
     rebind: (objects, options = {}) ->
         customEvents = _.result(@component, 'bindDataEvents') || {}
         for name, state of objects
+            if false == state
+                continue
             unless state
                 Lanes.warn "#{name} is not set on #{@componentName()}"
                 continue


Trigger ladda on LoginView (since it doesn't use ajax submit)

--- client/source/views/auth/login.coffee
+++ client/source/views/auth/login.coffee
@@ -5,15 +5,20 @@
 mediator = require('chaplin').mediator

 module.exports = class LoginView extends View
-  @mixin FormMixin
-
   template: tpl
-  autoRender: yes
   container: '#bkts-content'
   className: 'loginView'
+
+  events:
+    'submit form': 'submitForm'
+
+  submitForm: ->
+    @$btn?.ladda?('start')

   optionNames: View::optionNames.concat ['next']

   getTemplateData: ->
     if @next
       next: "/#{mediator.options.adminSegment}/#{@next}"
+
+  @mixin FormMixin


Uppercase PORT

Uppercase PORT


show activities on first page load

show activities on first page load

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -30,10 +30,7 @@

     super options

-    if KD.config.useNeo4j
-      @setfilter "Everything"
-    else
-      @currentFilter  = activityTypes
+    @currentFilter  = activityTypes
     @feedType       = "Public"
     @appStorage     = new AppStorage 'Activity', '1.0'
     @isLoading      = no
@@ -234,6 +231,9 @@
     {CActivity}   = KD.remote.api
     if KD.config.useNeo4j
       options.feedType = @feedType
+
+      if options.facets is activityTypes
+        options.facets = ['Everything']

       if @feedType is "Public"
         options.groupId = KD.getSingleton("groupsController").getCurrentGroup().getId()


Move scroller

Move scroller

--- source/assets/javascripts/app.js.coffee
+++ source/assets/javascripts/app.js.coffee
@@ -173,7 +173,7 @@
       moveToPosition buttons.selectAll('g#'+item.id+' > g'), item.initial, scale.initial
       moveToPosition buttons.select('g#'+item.id+'-title'), item.initial, scale.initial
       moveButton item, 0
-    scroller = snap.circle width/2 - 7, height - 46, 13
+    scroller = snap.circle $('header.main').css('padding-left'), height - 46, 13
     scroller.attr { fill: '#b3b3b3' }
     scroller.click ->
       $.scrollTo '.fonts', { duration: defaultSpeed }


Fixed error

Fixed error

--- gitorial/coffee/main.coffee
+++ gitorial/coffee/main.coffee
@@ -63,7 +63,7 @@
                 return

             $ '.user-listing-title'
-            .on 'hover', (e) -> $(this).text += ' +'
+            .on 'hover', (e) -> $(this).text($(this).text() += ' +')
             .on 'click', (e) ->
                 gitorial.tutorials.utils.makeNew(e)
                 return


display homework builder tour

display homework builder tour

--- tutor/src/components/task-plan/index.cjsx
+++ tutor/src/components/task-plan/index.cjsx
@@ -55,6 +55,16 @@
     {courseId, id} = Router.currentParams()
     <PlanShell courseId={courseId} id={id} type='event'/>

+{ default: TourRegion } = require '../tours/region'
+
+PlanBuilder = ({ id, courseId, body: Body, type }) ->
+  <TourRegion
+    id={"#{type}-assignment-editor"} courseId={courseId}
+  >
+    <Body id={id} courseId={courseId} />
+  </TourRegion>
+
+
 PlanShell = React.createClass
   displayName: 'PlanShell'

@@ -85,7 +95,6 @@
     getPlanType(typeName)

   render: ->
-    Type = @getType()
     {courseId} = Router.currentParams()
     id = @getId()

@@ -96,7 +105,9 @@
       id={id}
       store={TaskPlanStore}
       actions={TaskPlanActions}
-      renderItem={-> <Type id={id} courseId={courseId} />}
+      renderItem={=>
+        <PlanBuilder type={@props.type} body={@getType()} id={id} courseId={courseId} />
+      }
     />

 module.exports = {ReadingShell, HomeworkShell, ExternalShell, EventShell}


Registration: enforce register for Apps page

--- client/Social/Apps/AppController.coffee
+++ client/Social/Apps/AppController.coffee
@@ -4,6 +4,7 @@

   KD.registerAppClass this,
     name         : "Apps"
+    enforceLogin : yes
     routes       :
       "/:name?/Apps"             : ({params, query})->
         handler (app)-> app.handleQuery query