stash

stash

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -109,7 +109,7 @@
         # Note: the callbacks are not ordered. In this code, `f` is
         # never called: `qr.shift 2 f; cb = -> qr.shift 1 cb`
         shift: (n, k) =>
-            cb = =>
+            cb = =>
                 if @results.length > n or @ended
                     @off 'end', cb
                     ret = @results[0...n]
@@ -144,14 +144,13 @@
         fetch_next: =>
             if not @ended
                 try
-                    @cursor.next (error, row) =>
-                        if error?
-                            if error.message is 'No more rows in the cursor.'
-                                @ended = true
-                                @trigger 'end', @
-                            else
-                                @set_error error
-                        else
+                    @driver_handler.cursor_next @cursor,
+                        end: () =>
+                            @ended = true
+                            @trigger 'end', @
+                        error: (error) =>
+                            @set_error error
+                        row: (row) =>
                             if @discard_results
                                 return
                             @results.push row
@@ -185,7 +184,7 @@
         limit: 40 # How many results we display per page // Final for now
         line_height: 13 # Define the height of a line (used for a line is too long)
         size_history: 50
-
+
         max_size_stack: 100 # If the stack of the query (including function, string, object etc. is greater than @max_size_stack, we stop parsing the query
         max_size_query: 1000 # If the query is more than 1000 char, we don't show suggestion (codemirror doesn't highlight/parse if the query is more than 1000 characdd_ters too

@@ -381,7 +380,7 @@
         #   size: size of the collapsible panel we want // If not specified, we are going to try to figure it out ourselves
         #   no_animation: boolean (do we need to animate things or just to show it)
         #   is_at_bottom: boolean (if we were at the bottom, we want to scroll down once we have added elements in)
-        #   delay_scroll: boolean, true if we just added a query - It speficied if we adjust the height then scroll or do both at the same time
+        #   delay_scroll: boolean, true if we just added a query - It speficied if we adjust the height then scroll or do both at the same time
         adjust_collapsible_panel_height: (args) =>
             that = @
             if args?.size?
@@ -513,7 +512,7 @@
                 returns = returns.concat return_values
                 for parent_value in parent_values
                     parents[parent_value] = true
-
+
             if full_tag isnt '('
                 for parent_value of parents
                     if not suggestions[parent_value]?
@@ -702,7 +701,7 @@
         render: =>
             @$el.html @template()
             @$('.input_query_full_container').html @input_query_template()
-
+
             # Check if the browser supports the JavaScript driver
             # We do not support internet explorer (even IE 10) and old browsers.
             if navigator?.appName is 'Microsoft Internet Explorer'
@@ -733,7 +732,7 @@
             # If driver not conneced
             if @driver_connected is false
                 @error_on_connect()
-
+
             return @

         # This method has to be called AFTER the el element has been inserted in the DOM tree, mostly for codemirror
@@ -754,7 +753,7 @@
             if @state.current_query?
                 @codemirror.setValue @state.current_query
             @codemirror.focus() # Give focus
-
+
             # Track if the focus is on codemirror
             # We use it to refresh the docs once the reql_docs.json is loaded
             @state.focus_on_codemirror = true
@@ -801,7 +800,7 @@
             ',': true
             ';': true
             ']': true
-
+
         handle_click: (event) =>
             @handle_keypress null, event

@@ -857,7 +856,7 @@
                         next_char = @get_next_char()
                         if next_char is char_to_insert # Next char is a single quote
                             if num_quote%2 is 0
-                                if last_element_incomplete_type is 'string' or last_element_incomplete_type is 'object_key' # We are at the end of a string and the user just wrote a quote
+                                if last_element_incomplete_type is 'string' or last_element_incomplete_type is 'object_key' # We are at the end of a string and the user just wrote a quote
                                     @move_cursor 1
                                     event.preventDefault()
                                     return true
@@ -868,7 +867,7 @@
                                 # Let's add the closing/opening quote missing
                                 return true
                         else
-                            if num_quote%2 is 0 # Next char is not a single quote and the user has an even number of quotes.
+                            if num_quote%2 is 0 # Next char is not a single quote and the user has an even number of quotes.
                                 # Let's keep a number of quote even, so we add one extra quote
                                 last_key = @get_last_key(stack)
                                 if last_element_incomplete_type is 'string'
@@ -962,7 +961,7 @@
                         is_parsing_string = true
                         string_delimiter = char
                         continue
-
+
                     result_inline_comment = @regex.inline_comment.exec query.slice i
                     if result_inline_comment?
                         to_skip = result_inline_comment[0].length-1
@@ -1012,7 +1011,7 @@
                         is_parsing_string = true
                         string_delimiter = char
                         continue
-
+
                     result_inline_comment = @regex.inline_comment.exec query.slice i
                     if result_inline_comment?
                         to_skip = result_inline_comment[0].length-1
@@ -1087,7 +1086,7 @@
                         # create_suggestion is going to fill to_complete and to_describe
                         #to_complete: undefined
                         #to_describe: undefined
-
+
                     # Create the suggestion/description
                     @create_suggestion
                         stack: stack
@@ -1242,7 +1241,7 @@
                                         @current_highlighted_extra_suggestion--
                                     else
                                         @current_highlighted_extra_suggestion++
-
+
                                     if @current_highlighted_extra_suggestion >= @extra_suggestions.length
                                         @current_highlighted_extra_suggestion = -1
                                     else if @current_highlighted_extra_suggestion < -1
@@ -1267,7 +1266,7 @@
                                             string_delimiter = "'"
                                             move_outside = true
                                         suggestion = string_delimiter+@extra_suggestions[@current_highlighted_extra_suggestion]+string_delimiter
-
+
                                     @write_suggestion
                                         move_outside: move_outside
                                         suggestion_to_write: suggestion
@@ -1340,7 +1339,7 @@

             # We are scrolling in history
             if @history_displayed_id isnt 0 and event?
-                # We catch ctrl, shift, alt and command
+                # We catch ctrl, shift, alt and command
                 if event.ctrlKey or event.shiftKey or event.altKey or event.which is 16 or event.which is 17 or event.which is 18 or event.which is 20 or (event.which is 91 and event.type isnt 'keypress') or event.which is 92 or event.type of @mouse_type_event
                     return false

@@ -1426,7 +1425,7 @@
                 # create_suggestion is going to fill to_complete and to_describe
                 #to_complete: undefined
                 #to_describe: undefined
-
+
             # If we are in the middle of a function (text after the cursor - that is not an element in @char_breakers or a comment), we just show a description, not a suggestion
             result_non_white_char_after_cursor = @regex.get_first_non_white_char.exec(query_after_cursor)

@@ -1637,7 +1636,7 @@
                                 stack_stop_char = ['{']
                                 continue

-                            # Check for a for loop
+                            # Check for a for loop
                             result_regex = @regex.loop.exec query.slice i
                             if result_regex isnt null
                                 element.type = 'loop'
@@ -1646,7 +1645,7 @@
                                 body_start = i+result_regex[0].length
                                 stack_stop_char = ['{']
                                 continue
-
+
                             # Check for return
                             result_regex = @regex.return.exec query.slice i
                             if result_regex isnt null
@@ -2344,7 +2343,7 @@

             @$('.suggestion_name_list').show()
             @$('.arrow').show()
-
+
         # If want to show suggestion without moving the arrow
         show_suggestion_without_moving: =>
             @$('.arrow').show()
@@ -2405,7 +2404,7 @@
             @remove_highlight_suggestion()
             @$('.suggestion_name_li').eq(id).addClass 'suggestion_name_li_hl'
             @$('.suggestion_description').html @description_template @extend_description @current_suggestions[id]
-
+
             @$('.suggestion_description').show()

         remove_highlight_suggestion: =>
@@ -2564,7 +2563,7 @@
             @raw_query = @codemirror.getValue()

             @query = @clean_query @raw_query # Save it because we'll use it in @callback_multilples_queries
-
+
             # Execute the query
             try
                 @state.query_result?.discard()
@@ -2590,7 +2589,9 @@
                     broken_query: true

         toggle_executing: (executing) =>
-            if executing is true
+            if executing == @executing
+                return
+            else if executing is true
                 @executing = true
                 @timeout_show_abort = setTimeout =>
                     #TODO Delay a few ms
@@ -2790,7 +2791,7 @@
             is_parsing_string = false
             stack = []
             start = 0
-
+
             position =
                 char: 0
                 line: 1
@@ -2824,7 +2825,7 @@
                     if result_multiple_line_comment?
                         to_skip = result_multiple_line_comment[0].length-1
                         continue
-
+

                     if char of @stop_char.opening
                         stack.push char
@@ -3201,7 +3202,7 @@
             return @templates.td_value
                 col: col
                 cell_content: @templates.td_value_content data
-
+
         compute_data_for_type: (value,  col) =>
             data =
                 value: value
@@ -3255,7 +3256,7 @@
                     data_cell['need_comma'] = true

                 result += @templates.data_inline data_cell
-
+
             return result

         # Build the table
@@ -3307,7 +3308,7 @@
             @copy_parent_results()

             previous_keys = @parent.container.state.last_keys # Save previous keys. @last_keys will be updated in @json_to_table
-            if Object::toString.call(@results) is '[object Array]'
+            if Object::toString.call(@results) is '[object Array]'
                 if @results.length is 0
                     @$el.html @templates.wrapper content: @template_no_result()
                 else
@@ -3375,7 +3376,7 @@

         template: Handlebars.templates['dataexplorer_result_raw-template']

-        init_after_dom_rendered: =>
+        init_after_dom_rendered: =>
             height = @$('.raw_view_textarea')[0].scrollHeight
             if height > 0
                 @$('.raw_view_textarea').height(height)
@@ -3389,7 +3390,7 @@

         template:
             Handlebars.templates['dataexplorer_result_profile-template']
-
+
         initialize: (args) =>
             super args
             ZeroClipboard.setDefaults
@@ -3425,7 +3426,7 @@

         render: =>
             @copy_parent_results()
-
+
             if not @ATN_profile?
                 @$el.html @template {}
             else
@@ -3689,7 +3690,7 @@
         dataexplorer_history_template: Handlebars.templates['dataexplorer-history-template']
         dataexplorer_query_li_template: Handlebars.templates['dataexplorer-query_li-template']
         className: 'history_container'
-
+
         size_history_displayed: 300
         state: 'hidden' # hidden, visible
         index_displayed: 0
@@ -3804,7 +3805,8 @@
                 is_at_bottom: 'true'

     class DriverHandler
-        constructor: ({@container}) ->
+        constructor: (container) ->
+            @container = container

         close_connection: =>
             if @connection?.open is true
@@ -3815,13 +3817,31 @@
         run_with_new_connection: ({query, callback, connection_error}) =>
             @close_connection()

+            @container.toggle_executing true
             driver.connect (error, connection) =>
                 if error?
+                    @container.toggle_executing false
                     connection_error error
                 connection.removeAllListeners 'error' # See issue 1347
-                connection.on 'error', connection_cb
+                connection.on 'error', (error) =>
+                    @container.toggle_executing false
+                    connection_error error
                 @connection = connection
-                cb(error, connection)
+                query.private_run connection, (error, result) =>
+                    @container.toggle_executing false
+                    callback error, result
+
+        cursor_next: ({cursor, error, row, end}) =>
+            @container.toggle_executing true
+            cursor.next (err, row_) =>
+                @container.toggle_executing false
+                if error?
+                    if error.message is 'No more rows in the cursor.'
+                        end()
+                    else
+                        error err
+                else
+                    row row_

         remove: =>
             @close_connection()
@@ -3955,7 +3975,7 @@
             # We build the tree in a recursive way
             return json_to_node = (value) ->
                 value_type = typeof value
-
+
                 output = ''
                 if value is null
                     return template.span


pwnfx-showif can replace elements, so their fields don't get submitted.

--- app/assets/javascripts/pwn-fx.js.coffee
+++ app/assets/javascripts/pwn-fx.js.coffee
@@ -397,6 +397,8 @@
 #
 # Attributes:
 #   data-pwnfx-showif: an identifier that connects the source <input>
+#   data-pwnfx-showif-replace: the name of a tag that will be used to replace
+#       the hidden element (default: don't replace the hidden element)
 #   data-pwnfx-showif-class: the CSS class that is added to hidden elements;
 #       (default: hidden)
 #   data-pwnfx-showif-is: the value that the <input> has to match for this
@@ -407,20 +409,32 @@
   constructor: (element, identifier, scopeId) ->
     hiddenClass = element.getAttribute('data-pwnfx-showif-class') || 'hidden'
     showValue = element.getAttribute 'data-pwnfx-showif-is'
-    sourceSelector = "[data-pwnfx-showif-source=\"#{identifier}\"]"
-
-    oldValue = null
+    sourceSelector = "[data-pwnfx-showif-source=\"#{identifier}\"]"
+
+    replacementTag = element.getAttribute 'data-pwnfx-showif-replace'
+    if replacementTag
+      replacement = document.createElement replacementTag
+      replacement.setAttribute 'class', hiddenClass
+    else
+      replacement = null
+
+    isHidden = false
     onChange = (event) ->
-      console.log ['change', event.target]
       value = event.target.value
-      return true if value == oldValue
-      oldValue = value
-
-      console.log [value, showValue]
-      if value == showValue
-        element.classList.remove hiddenClass
+      willHide = value != showValue
+      return if isHidden == willHide
+      isHidden = willHide
+
+      if replacement
+        if willHide
+          element.parentElement.replaceChild replacement, element
+        else
+          replacement.parentElement.replaceChild element, replacement
       else
-        element.classList.add hiddenClass
+        if willHide
+          element.classList.add hiddenClass
+        else
+          element.classList.remove hiddenClass
       true

     scope = PwnFx.resolveScope scopeId, element


Correctly set the affix bottom

Correctly set the affix bottom

--- app/assets/javascripts/sheets.js.coffee
+++ app/assets/javascripts/sheets.js.coffee
@@ -65,7 +65,7 @@
         navOuterHeight = $('.navbar-fixed-top').height()
         return (this.top = offsetTop - navOuterHeight - sideBarMargin)
       bottom: () ->
-        return (this.bottom = parseInt($(document.body).css('padding-bottom')))
+        return ( this.bottom = $(document.body).outerHeight(true) - $('#main-bar').offset().top - $('#main-bar').outerHeight(true) )
   )
   $('[data-spy="scroll"]').each( () ->
     $spy = $(this).scrollspy('refresh')


eliminado codigo innecesario

eliminado codigo innecesario

--- app/assets/javascripts/tables.js.coffee
+++ app/assets/javascripts/tables.js.coffee
@@ -2,8 +2,7 @@
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
 init_datatables = ->
-	id = $('.data-table').attr('id')
-	$('#' + id).dataTable
+	$('.data-table').dataTable
     sDom: "<'row'<'col-xs-6'T><'col-xs-6'f>r>t<'row'<'col-xs-6'i><'col-xs-6'p>>"
     oLanguage:
       sUrl: "datatables.spanish.txt"


Only tracks user information when logged in

--- app/assets/javascripts/tracks.js.coffee
+++ app/assets/javascripts/tracks.js.coffee
@@ -14,9 +14,6 @@
     name     = $this.data('track-event-name')
     _paq.push(['trackEvent', category, action, name])

-  proposal_show_page: ->
-    $("#js-tracking").data('proposal-show')
-
   track_proposal: ->
     page_title = App.Tracks.page_title()
     proposal_rank = $('#js-tracking').data('proposal-rank')
@@ -30,9 +27,9 @@
     age                = tracking_data.data('track-age')
     district           = tracking_data.data('track-district')

-    _paq.push(['setCustomDimension', customDimensionId = 1, customDimensionValue = verification_level]);
     if current_user_id?
       _paq.push(['setUserId', current_user_id]);
+      _paq.push(['setCustomDimension', customDimensionId = 1, customDimensionValue = verification_level]);
       _paq.push(['setCustomDimension', customDimensionId = 2, customDimensionValue = current_user_id])
       _paq.push(['setCustomDimension', customDimensionId = 3, customDimensionValue = sex])
       _paq.push(['setCustomDimension', customDimensionId = 4, customDimensionValue = age])
@@ -40,6 +37,9 @@

   page_title: ->
     $(document).find("title").text()
+
+  proposal_show_page: ->
+    $("#js-tracking").data('proposal-show')

   initialize: ->
     if App.Tracks.tracking_enabled()


remove debugger

remove debugger

--- app/assets/javascripts/zvg/radar.coffee
+++ app/assets/javascripts/zvg/radar.coffee
@@ -139,7 +139,6 @@

         @n_threshold_color('white')({ n: mock_n })
       ).style('fill', (d) =>
-        debugger
         @colors(d.key)
       )


Added member add buttons to user profile

--- app/backbone/views/react_profile.coffee
+++ app/backbone/views/react_profile.coffee
@@ -34,8 +34,11 @@
           _div ['profile-information-item'],
             ReactSocialStatistics model: @model()
             if currentSession.signedIn() && !currentSession.isCurrentUser(@model())
-              ReactFollowUserButton user: @model()
-
+              _div [],
+                ReactFollowUserButton user: @model()
+                ReactGroupButtons
+                  groups: currentSession.user().groups()
+                  user: @model()
         _div ['profile-box-item'],
           _h1 [],
             'Information'


Fix pagination for filtered collections

Fix pagination for filtered collections

--- app/js/collections/notifications.coffee
+++ app/js/collections/notifications.coffee
@@ -31,8 +31,8 @@
   #
   # Returns false if last request returned zero notifications
   paginate: ->
-    return false if @donePaginating
-    data = before: @last()?.get('updated_at')
+    return $.Deferred.reject() if @donePaginating
+    data = before: @oldest_timestamp?.toISOString()
     @fetch(reset: false, remove: false, data: data).done(@checkIfPaginated)

   checkIfPaginated: (data, options, xhr) =>
@@ -45,4 +45,12 @@
   # Internal: Overriden to allow filtering out some models.
   _prepareModel: (attrs, options) ->
     model = super
+
+    # Save timestamp of earliest notification we've seen, regardless of filter.
+    # FIXME: subtracts 1 millisecond because the API will return <=, which includes
+    # the last notification. Need to update the API.
+    updated_at = moment(model.get('updated_at')).subtract('millisecond', 1)
+    if updated_at.isBefore(@oldest_timestamp || moment())
+      @oldest_timestamp = updated_at
+
     model if @filter(model)


Added get route method to app

Added get route method to app


disable client action so i can test the feature via route only

--- vue/src/shared/services/thread_service.coffee
+++ vue/src/shared/services/thread_service.coffee
@@ -33,12 +33,12 @@
             model: discussion
             # newVolume: 'normal'

-    export_thread:
-      name: 'common.action.export'
-      canPerform: ->
-        AbilityService.canExportThread(discussion)
-      perform: ->
-        hardReload LmoUrlService.discussion(discussion, {}, action: 'export', absolute: true)
+    # export_thread:
+    #   name: 'common.action.export'
+    #   canPerform: ->
+    #     AbilityService.canExportThread(discussion)
+    #   perform: ->
+    #     hardReload LmoUrlService.discussion(discussion, {}, action: 'export', absolute: true)

     unignore:
       name: 'common.action.unignore'


[grunt] remove obsolete comment

[grunt] remove obsolete comment

--- modules/gettext.coffee
+++ modules/gettext.coffee
@@ -7,7 +7,6 @@

     ###*
      * Load l10n files and make l10n class available to Grunt-related tasks
-     * @note Do not use 'LC_MESSAGES' in path to locales
      * @todo Since node-gettext doesn't have method for switching between languages AND domains,
      *       use `dgettext('{{locale}}:{{domain'), 'String')` to switch between locales and domains
      *       `/locale/en/{defaultLocale}.po` will result in `en` domain.


deprecated config.add in favour of config.buttons

--- modules/gui/lib/editor/ListEditor.coffee
+++ modules/gui/lib/editor/ListEditor.coffee
@@ -80,6 +80,11 @@
             , ->

                 if config.add
+                    console.warning 'DEPRECATED: config.add is deprecated, use config.buttons.add instead'
+                    config.buttons ?= {}
+                    config.buttons.add = config.add
+
+                if config.buttons
                     @children.push

                         type: 'gui.editor.Editor'
@@ -89,8 +94,7 @@
                             events:
                                 delegate: true

-                        buttons:
-                            add: config.add
+                        buttons: config.buttons

             , config


added css-classes each and add added the event to the call of _show and _hide

--- modules/gui/lib/editor/ListEditor.coffee
+++ modules/gui/lib/editor/ListEditor.coffee
@@ -8,6 +8,7 @@

             super new floyd.Config
                 data:
+                    class: 'ListEditor'
                     itemSelector: '> ul > li'

                 children: [
@@ -16,6 +17,7 @@
                         type: 'gui.editor.Editor'

                         data:
+                            class: 'each'
                             events:
                                 delegate: true

@@ -34,12 +36,12 @@
                             .mouseenter (event)=>
                                 @parent._allow event, ()=>
                                     $(event.currentTarget).append @__root
-                                    @_show()
+                                    @_show(event)

                             .mouseleave (event)=>
                                 @parent.__root.append @__root
-                                @_hide()
+                                @_hide(event)

                         ##
                         _show: ()->
@@ -61,6 +63,7 @@
                         type: 'gui.editor.Editor'

                         data:
+                            class: 'add'
                             events:
                                 delegate: true


auto add notices methods

auto add notices methods

--- lib/client/javascripts/nali/notice.js.coffee
+++ lib/client/javascripts/nali/notice.js.coffee
@@ -2,19 +2,16 @@

   initialize: ->
     @::::expand Notice: @
+    @addMethods()

   prepare: ( params ) ->
     params = message: params if typeof params is 'string'
     params

-  info: ( params ) ->
-    @new( @prepare params ).showInfo()
-
-  warning: ( params ) ->
-    @new( @prepare params ).showWarning()
-
-  error:   ( params ) ->
-    @new( @prepare params ).showError()
+  addMethods: ->
+    for name of @views
+      do ( name ) =>
+        @[ name ] = ( params ) => @new( @prepare params ).show name

 Nali.View.extend


Remove logging statements

Remove logging statements

--- lib/js/integrations/slack_integration.coffee
+++ lib/js/integrations/slack_integration.coffee
@@ -21,24 +21,19 @@
       @slack_channels = (channel for id, channel of @slack.channels when channel.is_member)

     @slack.on 'message', (message)=>
-      console.log 'READING MESSAGE: ', message?.text
       channel = @slack.getChannelGroupOrDMByID(message.channel)
-      console.log 'Channel: ', channel
       #event.emit()

   sendNotification: (player, game)->
-    console.log 'send for channels', @slack_channels
     return if not @config_channels or not @slack_channels
-    console.log 'should send'
+
     channels = @getChannelsToNotify(player)
     message = "#{player} is playing #{game}! Go join them!"
     channel.send(message) for channel in channels

   getChannelsToNotify: (player)=>
-    console.log 'Getting channels to notify'
     userChannels = (channel for channel in @config_channels when channel.indexOf(player) isnt -1)
-    console.log 'slack channels:', @slack_channels.length
     notifyChannels = (channel for channel in @slack_channels when userChannels[channel.name] isnt -1)

     notifyChannels


Ignore invalid modules

Ignore invalid modules

--- build/tasks/output-module-counts.coffee
+++ build/tasks/output-module-counts.coffee
@@ -9,6 +9,8 @@
       return if fileName isnt 'package.json'

       {name, version} = grunt.file.readJSON(absolutePath)
+      return unless name and version
+
       modules[name] ?= {versions: {}, count: 0}
       modules[name].count++
       modules[name].versions[version] = true


update scroll to 50

update scroll to 50

--- documentation/app/static/scripts/modules/process-documentation.coffee
+++ documentation/app/static/scripts/modules/process-documentation.coffee
@@ -50,7 +50,7 @@
           $('body').scrollspy 'refresh'
           $(@elements.nav).find('a[href="'+hash+'"]').parent().addClass 'active'

-      @scrollToHash(hash, 30)
+      @scrollToHash(hash, 50)

       if imgLength is 0
         initOrRefresh()


OAuth: getUrl: use protocol provided in KONFIG

--- workers/social/lib/social/models/oauth.coffee
+++ workers/social/lib/social/models/oauth.coffee
@@ -235,7 +235,7 @@
     if (_provider = @PROVIDERS[provider]) and _provider.enabled
       { context: { group } } = client
       urlOptions.redirectUri = \
-        "http://#{group}.#{KONFIG.hostname}/-/oauth/#{provider}/callback"
+        "#{KONFIG.protocol}//#{group}.#{KONFIG.hostname}/-/oauth/#{provider}/callback"
       _provider.getUrl client, urlOptions, callback
     else
       callback ERROR.NOTSUPPORTED


JComputeStack: destroy: update resources not only for group stacks but also personal stacks

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -299,19 +299,14 @@
     , callback

-  updateGroupResourceUsage = (stack, group, callback) ->
-
-    if stack.getAt 'config.groupStack'
-
-      ComputeProvider = require './computeproviders/computeprovider'
-      instanceCount   = (stack.getAt('machines') ? []).length
-
-      ComputeProvider.updateGroupResourceUsage {
-        group, change: 'decrement', instanceCount
-      }, callback
-
-    else
-      callback null
+  updateGroupResourceUsage = (stack, group, change, callback) ->
+
+    ComputeProvider = require './computeproviders/computeprovider'
+    instanceCount   = (stack.getAt?('machines') ? stack.machines ? []).length
+
+    ComputeProvider.updateGroupResourceUsage {
+      group, change, instanceCount
+    }, callback

   destroy: (callback) ->
@@ -324,7 +319,7 @@

         JMachine = require './computeproviders/machine'

-        updateGroupResourceUsage this, group, =>
+        updateGroupResourceUsage this, group, 'decrement', =>

           machineIds = (machineId for machineId in @machines)


CoffeeScript is magical

CoffeeScript is magical

--- modules/player/commands.coffee
+++ modules/player/commands.coffee
@@ -28,7 +28,7 @@
       return msg.reply 'You must be in a voice channel.' if not msg.member.getVoiceChannel()
       target = Math.round(msg.member.getVoiceChannel().members.length * 0.4)
       if queue.items.length or queue.currentItem
-        if not @permissions.isAdmin msg.author, msg.guild and msg.author.id isnt queue.currentItem.requestedBy.id
+        if not @permissions.isAdmin(msg.author, msg.guild) and msg.author.id isnt queue.currentItem.requestedBy.id

           if msg.author.id in queue.currentItem.voteSkip
             return msg.reply 'Did you really try to skip this song **again**?'


deprecated selector

deprecated selector

--- keymaps/hacker-news.cson
+++ keymaps/hacker-news.cson
@@ -7,5 +7,5 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'.workspace':
+'.atom-workspace':
   'cmd-alt-h': 'hacker-news:top'


Page up disabled on task search page if it can be detected that the current page is the last page

--- SingularityUI/app/components/common/Table.cjsx
+++ SingularityUI/app/components/common/Table.cjsx
@@ -69,13 +69,16 @@
         if @props.customPaging then @props.pageNumber else @state.pageNumber

     pageUpDisabled: ->
-        return (@props.customPaging and @props.isLastPage) or (@state.pageNumber * @state.rowsPerPage >= @props.tableRows.length and not @props.customPaging)
+        return (@props.customPaging and (@props.isLastPage or @props.tableRows.length < @rowsPerPage())) or
+            (@state.pageNumber * @state.rowsPerPage >= @props.tableRows.length and not @props.customPaging)

     pageDown: ->
+        return if @pageNumber() is 1
         return @props.pageDown() if @props.customPaging
         @setState {pageNumber: @state.pageNumber - 1} if @state.pageNumber > 1

     pageUp: ->
+        return if @pageUpDisabled()
         return @props.pageUp() if @props.customPaging
         @setState {pageNumber: @state.pageNumber + 1} unless @pageUpDisabled()


Minor logging corrections

Minor logging corrections

--- library/exposure/session.coffee
+++ library/exposure/session.coffee
@@ -59,7 +59,7 @@
     # The session may or may not exist. Please refer to the `Connect`.
     destory: (sid, callback) ->
         qualified = @namespaced sid
-        message = "Redis session engine error: destroy: %s"
+        message = "Redis session engine error at destroy"
         @redis.del qualified, (error, trailings...) ->
             logger.error message.red, error if error
             return callback error if error
@@ -74,7 +74,7 @@
         encoded = JSON.stringify session
         expire = session?.cookie?.maxAge / 1000 | 0
         expire = 86400 unless expire and expire > 0
-        message = "Redis session engine error: set: %s"
+        message = "Redis session engine error at set"
         @redis.setex qualified, expire, encoded, (error) ->
             logger.error message.red, error if error
             return callback error if error
@@ -86,7 +86,7 @@
     # The session may or may not exist. Please refer to the `Connect`.
     get: (sid, callback) ->
         qualified = @namespaced sid
-        message = "Redis session engine error: get: %s"
+        message = "Redis session engine error at get"
         @redis.get qualified, (error, data, trailings...) ->
             logger.error message.red, error if error
             return callback error if error


remove the cleanup task that has rarely been used

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -42,7 +42,6 @@
 DEFAULT_LIBRARY = "library"
 DEFAULT_ARTIFACT = "artifact"
 DEFAULT_DOCUMENTS = "documents"
-DEFAULT_MODULES = "node_modules"
 DEFAULT_SCOPING = "development"
 DEFAULT_LOGGING = "info"

@@ -59,31 +58,12 @@
     # options are shared among all of the tasks and the entire file!
     # Please refer to `Cakefile` and `CoffeeScript` for information.
     option "-l", "--library [PATH]", "Path to the library sources"
-    option "-m", "--modules [PATH]", "Path to the modules directory"
     option "-a", "--artifact [PATH]", "Path to the artifact directory"
     option "-d", "--documents [PATH]", "Path to the documents directory"
     option "-s", "--scoping [SCOPE]", "The name of the scope to boot kernel"
     option "-i", "--logging [LEVEL]", "The level to use for the logging output"
     option "-w", "--watch", "Watch the library sources and recompile"
     option "-g", "--git-hub-pages", "Publish documents to GitHub pages"
-    option "-n", "--clean-modules", "Remove node_modules during cleanup"
-
-    # This is one of the major tasks in this Cakefile, it implements
-    # the cleanup of everything that could have been generated by the
-    # build system; such as documentation, compiled JavaScript artifact,
-    # and every other piece of generated data that the system knows of.
-    # Please see the implementation for the exact clean up mechanics.
-    task "cleanup", "remove everything generated by build", (options) ->
-        knm = "clean-modules" of options or Object()
-        modules = options.modules or DEFAULT_MODULES
-        artifact = options.artifact or DEFAULT_ARTIFACT
-        documents = options.documents or DEFAULT_DOCUMENTS
-        removing = "Removing everything under %s".yellow
-        note = (path, f) -> logger.warn removing, path; f()
-        note(artifact, -> rmdirSyncRecursive artifact, yes)
-        note(documents, -> rmdirSyncRecursive documents, yes)
-        note(modules, -> rmdirSyncRecursive modules, yes) if knm
-        logger.info "Finished cleaning everything up".green

     # This is one of th e major tasks in this Cakefile, it implements
     # the generation of the documentation for the library, using the


replace hard-string with variable.

replace hard-string with variable.

--- app/assets/javascripts/plugins/graph.js.coffee
+++ app/assets/javascripts/plugins/graph.js.coffee
@@ -116,7 +116,7 @@
       template = 'graphs/logic_mappings_node'
     else if edge_type == 'Link'
       template = 'graphs/links_node'
-    info_list.html(HandlebarsTemplates['graphs/logic_mappings_node'](payload)) if template
+    info_list.html(HandlebarsTemplates[template](payload)) if template
     $("div#d3_context").html(info_list)

   embedEdgeInfo = (edge) ->


Scenario 36. Added

Scenario 36. Added

Scenario 36. Two tags with two attribute

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -374,4 +374,14 @@
     # console.log output
     expectedOutput = fs.readFileSync './test/scenarios/scenario-35-expected.html', 'utf8'
     # console.log expectedOutput
+    expect(output).to.equal(expectedOutput)
+
+  it 'Scenario 36. Two tags with two attribute', ->
+
+    input = fs.readFileSync './test/scenarios/scenario-36-input.cohtml', 'utf8'
+    # console.log input
+    output = toHtml5 input
+    # console.log output
+    expectedOutput = fs.readFileSync './test/scenarios/scenario-36-expected.html', 'utf8'
+    # console.log expectedOutput
     expect(output).to.equal(expectedOutput)


Add tests for multiple selections

Add tests for multiple selections

--- test/selection-manager-test.coffee
+++ test/selection-manager-test.coffee
@@ -100,6 +100,56 @@
         it "Should be selected", ->
           selectionMngr.isSelected(toAdd).should.equal true

+  describe "Multiple selectOnly", ->
+    a = null
+    b = null
+    c = null
+    context = null
+    beforeEach ->
+      a = new GraphPrimitive()
+      b = new GraphPrimitive()
+      c = new GraphPrimitive()
+
+    describe "without a context", ->
+      beforeEach ->
+        context = null
+
+      describe "When 'a' was previously selected", ->
+        beforeEach ->
+          selectionMngr.addToSelection(a, context)
+
+        describe "When selecting 'b' with ctrlKey pressed", ->
+          beforeEach ->
+            ctrlKey = true
+            selectionMngr.selectOnly(b, context, ctrlKey)
+          it "'a' should be selected", ->
+            selectionMngr.isSelected(a, context).should.equal true
+          it "'b' should be selected", ->
+            selectionMngr.isSelected(b, context).should.equal true
+          it "'c' should not be selected", ->
+            selectionMngr.isSelected(c, context).should.equal false
+
+          describe "When selecting  'c' with ctrlKey still pressed", ->
+            beforeEach ->
+              ctrlKey = true
+              selectionMngr.selectOnly(c, context, ctrlKey)
+            it "'a' should be selected", ->
+              selectionMngr.isSelected(a, context).should.equal true
+            it "'b' should be selected", ->
+              selectionMngr.isSelected(b, context).should.equal true
+            it "'c' should be selected", ->
+              selectionMngr.isSelected(c, context).should.equal true
+
+          describe "When selecting  'c' with ctrlKey not pressed", ->
+            beforeEach ->
+              ctrlKey = false
+              selectionMngr.selectOnly(c, context, ctrlKey)
+            it "'a' should not be selected", ->
+              selectionMngr.isSelected(a, context).should.equal false
+            it "'b' should not be selected", ->
+              selectionMngr.isSelected(b, context).should.equal false
+            it "'c' should be selected", ->
+              selectionMngr.isSelected(c, context).should.equal true

   describe "selectOnly", ->
     a = null
@@ -445,7 +495,68 @@
       it "'a' should be selected for inspection", ->
         selectionMngr.isSelectedForInspection(a).should.equal true

-
+  describe "Selecting multiple links for inspection", ->
+    a = null
+    b = null
+    c = null
+    beforeEach ->
+      a = new GraphPrimitive()
+      b = new GraphPrimitive()
+      c = new GraphPrimitive()
+
+    describe "When nothing else is selected", ->
+      it "'a' should be selected for inspection", ->
+        selectionMngr.selectLinkForInspection(a)
+        selectionMngr.isSelectedForInspection(a).should.equal true
+
+    describe "When 'a' is a node already selected for inspection and ctrlKey is pressed", ->
+      beforeEach ->
+        ctrlKey = true
+        selectionMngr.selectNodeForInspection(a)
+        selectionMngr.selectLinkForInspection(b, ctrlKey)
+
+      it "'a' should be selected for inspection", ->
+        selectionMngr.isSelectedForInspection(a).should.equal true
+      it "'b' should be selected for inspection'", ->
+        selectionMngr.isSelectedForInspection(b).should.equal true
+      it "'c' should not be selected for inspection'", ->
+        selectionMngr.isSelectedForInspection(c).should.equal false
+
+      describe "When 'c' is a node selected for inspection and ctrlKey is depressed", ->
+        beforeEach ->
+          ctrlKey = false
+          selectionMngr.selectNodeForInspection(c, ctrlKey)
+        it "'a' should not be selected for inspection", ->
+          selectionMngr.isSelectedForInspection(a).should.equal false
+        it "'b' should not be selected for inspection'", ->
+          selectionMngr.isSelectedForInspection(b).should.equal false
+        it "'c' should be selected for inspection'", ->
+          selectionMngr.isSelectedForInspection(c).should.equal true
+
+
+    describe "When 'a' is a link already selected for inspection and ctrlKey is pressed", ->
+      beforeEach ->
+        ctrlKey = true
+        selectionMngr.selectLinkForInspection(a, ctrlKey)
+        selectionMngr.selectLinkForInspection(b, ctrlKey)
+
+      it "'a' should be selected for inspection", ->
+        selectionMngr.isSelectedForInspection(a).should.equal true
+      it "'b' should be selected for inspection'", ->
+        selectionMngr.isSelectedForInspection(b).should.equal true
+      it "'c' should not be selected for inspection'", ->
+        selectionMngr.isSelectedForInspection(c).should.equal false
+
+      describe "When 'c' is a link selected for inspection and ctrlKey is depressed", ->
+        beforeEach ->
+          ctrlKey = false
+          selectionMngr.selectLinkForInspection(c, ctrlKey)
+        it "'a' should be selected for inspection", ->
+          selectionMngr.isSelectedForInspection(a).should.equal false
+        it "'b' should be selected for inspection'", ->
+          selectionMngr.isSelectedForInspection(b).should.equal false
+        it "'c' should not be selected for inspection'", ->
+          selectionMngr.isSelectedForInspection(c).should.equal true

   describe "clearSelectionFor", ->
     a = null


left a reminder to set a date for a real time data

--- app/assets/javascripts/views/navigation.coffee
+++ app/assets/javascripts/views/navigation.coffee
@@ -64,6 +64,7 @@
       # OBA gives a response with an invalid content type, force it to json
       $.ajax
         url: "/oba/where/arrivals-and-departures-for-stop/#{response.oba_id}_#{first_transit_leg.from.stopId.id}.json"
+        # TODO: put the actual time in here, defaults to NOW
         data: { key: 'TEST' }
         success: (real_time) ->
           Bus.events.trigger 'real_time:complete', real_time.data.arrivalsAndDepartures


test(macAutocomplete): Fix tests not working on Firefox

--- test/unit/autocomplete.spec.coffee
+++ test/unit/autocomplete.spec.coffee
@@ -7,6 +7,11 @@
   $sniffer         = null
   changeInputValue = null

+  getMenuText = ->
+    item = document.querySelector(".mac-menu-item")
+
+    return item.innerText or item.textContent
+
   beforeEach module("Mac")
   beforeEach module("template/autocomplete.html")
   beforeEach module("template/menu.html")
@@ -64,7 +69,7 @@
       changeInputValue element, "fo"
       $rootScope.$digest()

-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

     it "should use local label, value object", ->
       $rootScope.source = [
@@ -76,7 +81,7 @@
       $rootScope.$digest()

       changeInputValue element, "f"
-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

     it "should use a url string and work exactly like mac-autocomplete-url", ->
       $httpBackend.when("GET", "/api/autocomplete?q=f").respond({data})
@@ -100,7 +105,7 @@
       changeInputValue element, "f"
       $rootScope.$digest()

-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

     it "should use a callback function returned by an invoked function", ->
       $rootScope.source = (val) ->
@@ -112,7 +117,7 @@
       changeInputValue element, "f"
       $rootScope.$digest()

-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

   describe "label", ->
     it "should use default 'name' label", ->
@@ -125,7 +130,7 @@
       $rootScope.$digest()

       changeInputValue element, "f"
-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

     it "should not be able to find anything", ->
       $rootScope.source = [
@@ -137,7 +142,7 @@
       $rootScope.$digest()

       changeInputValue element, "f"
-      expect(document.querySelector(".mac-menu-item").innerText).not.toBe "foo"
+      expect(getMenuText()).not.toBe "foo"

     it "should use 'label' as the key", ->
       $rootScope.source = [
@@ -149,7 +154,7 @@
       $rootScope.$digest()

       changeInputValue element, "f"
-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

     it "should evaulate expression on label attribute correctly", ->
       $rootScope.source = [
@@ -163,7 +168,7 @@
       $rootScope.$digest()

       changeInputValue element, "f"
-      expect(document.querySelector(".mac-menu-item").innerText).toBe "foo"
+      expect(getMenuText()).toBe "foo"

   describe "updateItem", ->
     it "should convert key to label", ->


Removed scrolling behavior

Removed scrolling behavior

--- app/controllers/interactive/my_galaxies.coffee
+++ app/controllers/interactive/my_galaxies.coffee
@@ -128,23 +128,17 @@
     subject = _.find @samples, (sample) => sample.classification_id == galaxy_id
     data.push subject

-    console.log 'Galaxy ID: ', galaxy_id
-    console.log 'Data: ', data
-
     subject_viewer = new SubjectViewer({el: '.dialog-content'})
     subject_viewer.receiveData data

     $('.dialog-underlay').show()
-    $('body').css 'overflow', 'hidden'
-    $(window).unbind 'scroll'
+    $('.dialog footer').hide()

-    $('.dialog-underlay').click ->
-      $(@).hide()
-      $('body').css 'overflow', 'auto'
-      $(window).scroll ->
-        @$.scrollTop($(window).scrollTop).scrollLeft($(window).scrollLeft)
+    $('.dialog-closer').click ->
+      $('.dialog-underlay').hide()
+      $('.dialog footer').show()

-    console.log 'Subject Viewer', subject_viewer
+


Add workaround for table caption selection bug.

--- app/lib/dom_text_mapper/dom_text_mapper.coffee
+++ app/lib/dom_text_mapper/dom_text_mapper.coffee
@@ -3,6 +3,7 @@
   USE_THEAD_TBODY_WORKAROUND = true
   USE_TABLE_TEXT_WORKAROUND = true
   USE_OL_WORKAROUND = true
+  USE_CAPTION_WORKAROUND = true
   CONTEXT_LEN = 32

   @instances: []
@@ -449,7 +450,8 @@

     if node.nodeType is Node.ELEMENT_NODE and node.hasChildNodes() and
         ((USE_THEAD_TBODY_WORKAROUND and node.tagName.toLowerCase() in ["thead", "tbody"]) or
-        (USE_OL_WORKAROUND and node.tagName.toLowerCase() is "ol"))
+        (USE_OL_WORKAROUND and node.tagName.toLowerCase() is "ol") or
+        (USE_CAPTION_WORKAROUND and node.tagName.toLowerCase() is "caption"))
       # This is a thead or a tbody, and selection those is problematic,
       # because if the WebKit bug.
       # (Sometimes it selects nothing, sometimes it selects the whole table.)


- Added workaround for	a WebKit selection bug (selecting <OL>s fails, too)  - Added method	to auto-detect some selection problem (no automatic fix, but makes debugging easier.)

--- app/lib/dom_text_mapper/dom_text_mapper.coffee
+++ app/lib/dom_text_mapper/dom_text_mapper.coffee
@@ -2,6 +2,7 @@

   USE_THEAD_TBODY_WORKAROUND = true
   USE_TABLE_TEXT_WORKAROUND = true
+  USE_OL_WORKAROUND = true
   CONTEXT_LEN = 32

   @instances: []
@@ -108,7 +109,8 @@
   # Select the given path (for visual identification), and optionally scroll to it
   selectPath: (path, scroll = false) ->
     info = @path[path]
-    node = info.node ? @lookUpNode info.path
+    node = info?.node
+    node or= @lookUpNode info.path
     @selectNode node, scroll

   performUpdateOnNode: (node, escalating = false) ->
@@ -360,7 +362,7 @@
     xpath

   # This method is called recursively, to collect all the paths in a given sub-tree of the DOM.
-  collectPathsForNode: (node) ->
+  collectPathsForNode: (node, invisible = false) ->
     # Step one: get rendered node content, and store path info, if there is valuable content
     cont = @getNodeContent node, false
     if cont.length
@@ -370,13 +372,17 @@
         content: cont
         length: cont.length
         node : node
+      if invisible
+        console.log "Something seems to be wrong. I see visible content @ " + path + ", while some of the ancestor nodes reported empty contents. Probably a new selection API bug...."
+    else
+      invisible = true

     # Step two: cover all children.
     # Q: should we check children even if the goven node had no rendered content?
-    # I seem to remember that the answer is yes, but I don't remember why.
+    # A: I seem to remember that the answer is yes, but I don't remember why.
     if node.hasChildNodes()
       for child in node.childNodes
-        @collectPathsForNode child
+        @collectPathsForNode child, invisible
     null

   getBody: -> (@rootWin.document.getElementsByTagName "body")[0]
@@ -435,8 +441,9 @@
     # To work around this, when told to select specific nodes, we have to
     # do various other things. See bellow.

-    if USE_THEAD_TBODY_WORKAROUND and node.nodeType is Node.ELEMENT_NODE and
-        node.tagName.toLowerCase() in ["thead", "tbody"] and node.hasChildNodes()
+    if node.nodeType is Node.ELEMENT_NODE and node.hasChildNodes() and
+        ((USE_THEAD_TBODY_WORKAROUND and node.tagName.toLowerCase() in ["thead", "tbody"]) or
+        (USE_OL_WORKAROUND and node.tagName.toLowerCase() is "ol"))
       # This is a thead or a tbody, and selection those is problematic,
       # because if the WebKit bug.
       # (Sometimes it selects nothing, sometimes it selects the whole table.)


updating selection on every mousemove is now an option

--- bokehjs/src/coffee/tool/box_select_tool.coffee
+++ bokehjs/src/coffee/tool/box_select_tool.coffee
@@ -11,6 +11,7 @@
   class BoxSelectToolView extends Tool.View
     initialize: (options) ->
       super(options)
+      @select_every_mousemove = @mget('select_every_mousemove')
       #@select_callback = _.debounce((() => @_select_data()), 50)
       #@listenTo(@model, 'change', @select_callback)

@@ -36,7 +37,8 @@
       SetBasepoint: "_start_selecting",
       UpdatingMouseMove: "_selecting",
       deactivated: "_stop_selecting",
-      DragEnd : "_select_data"
+      #DragEnd : "_select_data"
+      DragEnd : "_dragend",
       }

     pause:()->
@@ -77,10 +79,15 @@
       @mset({'current_x': x, 'current_y': y})
       [@xrange, @yrange] = @_get_selection_range(x, y)
       @trigger('boxselect', @xrange, @yrange)
-      @plot_view.render_overlays(true)
+      if @every_select
+        @_select_data()
+      else
+        @plot_view.render_overlays(true)
       return null

-
+    _dragend : () ->
+      console.log("dragend")
+      @_select_data()
     _select_data: () ->
       if not @basepoint_set
         return
@@ -139,6 +146,7 @@
         renderers: []
         select_x: true
         select_y: true
+        select_every_mousemove: true
         data_source_options: {} #backbone options for save on datasource
       }


shuffleのファンクションがunkになってた…

shuffleのファンクションがunkになってた…

--- cbt/app/scripts/controllers/exam.coffee
+++ cbt/app/scripts/controllers/exam.coffee
@@ -16,7 +16,6 @@
     $scope.max = 0
     $http.get("json/" + $routeParams.examName + ".json").success (data) ->
       questions = []
-      console.log(data.questions)
       for q in data.questions
         switch q.answers
           when 0
@@ -47,8 +46,6 @@
             if $scope.check[id].toString() is answer.toString()
               score++
           else
-            console.log(typeof $scope.check[id])
-            console.log($scope.check[id])
             $scope.check[id].sort()
             rights = $scope.check[id].toString()
             tmp = []
@@ -73,6 +70,7 @@
    ]

 shuffle = (array) ->
+  console.log("呼んだ?")
   m = array.length
   t = 0
   i = 0
@@ -82,5 +80,5 @@
     t = array[m]
     array[m] = array[i]
     array[i] = t
-    return array
+  return array


Add Spec T004-006,007

Add Spec T004-006,007

--- spec/src/coffee/label_category/models/t004_label_category_spec.coffee
+++ spec/src/coffee/label_category/models/t004_label_category_spec.coffee
@@ -89,7 +89,7 @@
         @label.destroy()
         should.strictEqual @root.get("label"), undefined

-  context "004: get_label_text()", ->
+  context "004: #get_label_text()", ->
     beforeEach ->
       @root = new @LabelCategory.Models.LabelCategory()
       @fake_label =
@@ -126,4 +126,84 @@
           .get("childrens")["G"]
           .get_label_text().should.equal "A/B/C/D/E/F/G"

-
+  context "005: #children()", ->
+    beforeEach ->
+      @root = new @LabelCategory.Models.LabelCategory
+      @fake_label =
+        set: sinon.spy()
+        on:  sinon.spy()
+
+    context "001: A/B/C/D/E", ->
+      beforeEach ->
+        @root.parse_labels_recursive_func "A/B/C/D/E", @fake_label
+
+      it "001: get target category", ->
+        @root.children("A").get("name").should.equal "A"
+        @root.children("A").children("B").get("name").should.equal "B"
+        @root.children("A").children("B").children("C").get("name").should.equal "C"
+        @root.children("A").children("B").children("C").children("D").get("name").should.equal "D"
+        @root.children("A").children("B").children("C").children("D").children("E").get("name").should.equal "E"
+
+      it "002: is not found", ->
+        should.strictEqual @root.children("B"), undefined
+
+  context "006: #parent()", ->
+    beforeEach ->
+      @root = new @LabelCategory.Models.LabelCategory
+      @fake_label =
+        spy: sinon.spy()
+        on:  sinon.spy()
+
+    context "001: A/B/C/D/E", ->
+      beforeEach ->
+        @root.parse_labels_recursive_func "A/B/C/D/E", @fake_label
+
+      it "001: #parent", ->
+        @root.children("A").parent().children("A").get("name").should.equal "A"
+        @root.children("A").children("B").children("C").parent().parent().get("name").should.equal "A"
+        @root
+          .children("A")
+          .children("B")
+          .children("C")
+          .children("D")
+          .children("E")
+          .parent()
+          .parent()
+          .parent()
+          .parent()
+          .children("C")
+          .children("D")
+          .children("E")
+          .parent()
+          .get("name").should.equal "D"
+
+      it "002: root's parent is undefined", ->
+        should.strictEqual @root.parent(), undefined
+
+  context "007: childrens watching", ->
+    beforeEach ->
+      @root = new @LabelCategory.Models.LabelCategory
+      @fake_label =
+        spy: sinon.spy()
+        on:  sinon.spy()
+
+    context "001: destroy with no child", ->
+      beforeEach ->
+        @root.parse_labels_recursive_func "1/1/1", @fake_label
+        @root.parse_labels_recursive_func "1/1/2", @fake_label
+        @root.parse_labels_recursive_func "1/1/3", @fake_label
+        @root.parse_labels_recursive_func "1/1/4", @fake_label
+        @root.parse_labels_recursive_func "1/1/5", @fake_label
+        # TODO: can it check fake_labels' destroy count?
+
+      it "001: check", ->
+        destroy_event_spy = sinon.spy()
+        @root.on "destroy", destroy_event_spy
+        @root.children("1").children("1").children("1").destroy()
+        @root.children("1").children("1").children("2").destroy()
+        @root.children("1").children("1").children("3").destroy()
+        @root.children("1").children("1").children("4").destroy()
+        @root.children("1").children("1").children("5").destroy()
+        destroy_event_spy.called.should.equal true
+
+


Account: email notifications section redesigned WIP

--- client/Account/views/emailnotifications.coffee
+++ client/Account/views/emailnotifications.coffee
@@ -30,17 +30,13 @@

     globalValue = if user.getAt("emailFrequency.global") is on then 'ON' else 'OFF'

-    turnedOffHint = new KDView
-      partial : """
-      <cite>
-        Email notifications are turned off. You won't receive any emails about anything.
-      </cite>
-      """
+    turnedOffHint = new KDCustomHTMLView
+      partial : "Email notifications are turned off. You won't receive any emails about anything."
       cssClass: "no-item-found #{if globalValue is 'ON' then 'hidden'}"

     @addSubView turnedOffHint

-    @getDelegate().addSubView global = new KDMultipleChoice
+    @getDelegate().addSubView global = new KodingSwitch
       cssClass      : "dark in-account-header"
       defaultValue  : globalValue
       callback      : (state)=>
@@ -64,7 +60,7 @@

       field.current = user.getAt("emailFrequency.#{flag}")

-      field.formView.addSubView field.switch = new KDMultipleChoice
+      field.formView.addSubView field.switch = new KodingSwitch
         cssClass      : 'dark'
         defaultValue  : if field.current is on then 'ON' else 'OFF'
         callback      : (state)->
@@ -78,9 +74,7 @@
             fields[@getData()].loader.hide()
             if err
               @fallBackToOldState()
-              new KDNotificationView
-                duration : 2000
-                title    : 'Failed to change state'
+              KD.notify_ 'Failed to change state'

         , flag


compute controller: don't trigger state changes from .info method

--- client/Main/providers/computecontroller.coffee
+++ client/Main/providers/computecontroller.coffee
@@ -295,10 +295,6 @@
     if stateEvent
       @eventListener.addListener stateEvent, machine._id
       return Promise.resolve()
-
-    @eventListener.triggerState machine,
-      status      : machine.status.state
-      percentage  : 0

     call = @kloud.info { machineId: machine._id }


Teamwork: Implemented user avatars area on chat pane.

--- client/Teamwork/Views/teamworkworkspace.coffee
+++ client/Teamwork/Views/teamworkworkspace.coffee
@@ -37,7 +37,7 @@
       @sendSystemMessage data

     KD.singleton("windowController").addUnloadListener "window", =>
-      @workspaceRef.remove()
+      @workspaceRef.remove()  if @amIHost()

     @chatView.sendWelcomeMessage()  if @amIHost()

@@ -121,9 +121,9 @@
     else
       Panel::showHintModal.call @getActivePanel()

-  manageUserAvatars: (userStatus) ->
-    for own nickname, status of userStatus
-      if status is "online"
+  manageUserAvatars: (users) ->
+    for own nickname, data of users
+      if data.status is "online"
         unless @avatars[nickname]
           @createUserAvatar @users[nickname]
       else
@@ -139,8 +139,8 @@

     avatarView   = new AvatarStaticView
       size       :
-        width    : 25
-        height   : 25
+        width    : 30
+        height   : 30
       tooltip    :
         title    : followText
       click      : =>
@@ -176,11 +176,9 @@

   removeUserAvatar: (nickname) ->
     avatarView = @avatars[nickname]
-    avatarView.setClass "fade-out"
-    avatarView.once "transitionend", =>
-      avatarView.destroy()
-      delete @avatars[nickname]
-      @avatarsView.unsetClass "has-user" if @avatars.length is 0
+    avatarView.destroy()
+    delete @avatars[nickname]
+    @avatarsView.unsetClass "has-user" if @avatars.length is 0

   sendSystemMessage: (messageData) ->
     return unless @getOptions().enableChat


Change 'open by default' number for ranking page

--- client.coffee
+++ client.coffee
@@ -752,7 +752,7 @@
 						Dom.style fontWeight: 'bold'
 					#log 'users: ', Plugin.users, ', length: ', Plugin.users.count().peek()
 					# To Do expand voor scores
-					if expanded.get() || Plugin.users.count().peek() <= 6
+					if expanded.get() || Plugin.users.count().peek() <= 10
 						team.iterate 'users', (user) !->
 							Dom.div !->
 								if parseInt(user.key())  isnt Plugin.userId()
@@ -778,7 +778,7 @@
 						Dom.div !->
 							Dom.style fontSize: '75%', marginTop: '6px'
 							Dom.text "Tap for details"
-				if Plugin.users.count().peek() > 6
+				if Plugin.users.count().peek() > 10
 					Dom.onTap !->
 						expanded.set(!expanded.get())
 		, (team) -> [team.get('ranking')]


Added Circle

Added Circle

drawing a flag also draws a circle around it. Renderlocation is added
but commented out because it causes a lot of spam.

--- client.coffee
+++ client.coffee
@@ -295,6 +295,7 @@
 	###
 	loadOpenStreetMap()
 	setupMap()
+	#renderLocation();

 loadOpenStreetMap = ->
 	# Only insert these the first time
@@ -350,28 +351,14 @@
 # Add flags to the map
 renderFlags = ->
 	log "rendering flags"
-	###
-	if Geoloc.isSubscribed()
-		Obs.observe -> # Creates new observe scope, because state changes a lot this spams the console a bit
-			state = Geoloc.track()
-			location = state.get('latlong');
-			if location?
-				location = location.split(',')
-				marker = L.marker(L.latLng(location[0], location[1]))
-				marker.bindPopup("Hier ben ik nu!" + "
 accuracy: " + state.get('accuracy'))
-				if window.flagCurrentLocation
-					map.removeLayer window.flagCurrentLocation
-				marker.addTo(map)
-				window.flagCurrentLocation = marker
-			else
-				log 'location could not be found'
-	###
 	Db.shared.observeEach 'game', 'flags', (flag) !->
 		if mapReady() and map?
 			if not window.flagMarkers?
 				log "flagMarkers list reset"
 				window.flagMarkers = [];
 			location = L.latLng(flag.get('location', 'lat'), flag.get('location', 'lng'))
+			log "Added circle"
+			renderCircle(location.lat, location.lng, {})
 			log "Added flag"
 			marker = L.marker(location)
 			marker.bindPopup("lat: " + location.lat + "
long: " + location.lng)
@@ -415,4 +402,31 @@
 # Check if the map can be used
 mapReady = ->
 	return L? and map?
-
+#Commented out because causes a lot of spam needs to be fixed!
+###
+renderLocation = ->
+	if Geoloc.isSubscribed()
+		Obs.observe -> # Creates new observe scope, because state changes a lot this spams the console a bit
+			state = Geoloc.track()
+			location = state.get('latlong');
+			if location?
+				location = location.split(',')
+				if mapReady()
+					marker = L.marker(L.latLng(location[0], location[1]))
+					marker.bindPopup("Hier ben ik nu!" + "
 accuracy: " + state.get('accuracy'))
+					if window.flagCurrentLocation
+						map.removeLayer window.flagCurrentLocation
+					marker.addTo(map)
+					window.flagCurrentLocation = marker
+			else
+				log 'location could not be found'
+###
+renderCircle = (lat, long, team)->
+	#Render the flag
+	circle = L.circle([lat, long], 500, {
+		color: 'red',
+		fillColor: '#f03',
+		fillOpacity: 0.5
+	}).addTo(window.map);
+
+


Simplify continueTurn a bit.

Simplify continueTurn a bit.

--- server/battle.coffee
+++ server/battle.coffee
@@ -258,8 +258,7 @@
   continueTurn: =>
     @determineTurnOrder()
     while @priorityQueue.length > 0
-      {pokemon} = @priorityQueue.shift()
-      {id} = @getOwner(pokemon)
+      {id, pokemon} = @priorityQueue.shift()
       continue  if pokemon.isFainted()

       action = @popAction(id)


Revert "This was causing infinite loop for not valid Develop routes"

This reverts commit 53e3030fd086025aaacb93611612dc46d48a7a85.

--- client/app/MainApp/kodingappscontroller.coffee
+++ client/app/MainApp/kodingappscontroller.coffee
@@ -132,8 +132,7 @@
       @_loadedOnce = yes
       if err or not Array.isArray files or files.length is 0
         @putAppsToAppStorage {}
-        unless err
-          callback()  for callback in @_fetchQueue
+        callback()  for callback in @_fetchQueue
         @_fetchQueue = []
       else
         apps = @filterAppsFromFileList files


flux/environment: listen revive compute ctrl revive events

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

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -20,6 +20,21 @@
 _eventsCache = { machine: {}, stack: no }

+_bindMachineEvents = (machine) ->
+
+  return  unless id = machine?._id
+
+  { computeController, reactor } = kd.singletons
+
+  if handler = _eventsCache.machine[id]
+    return computeController.off "revive-#{id}", handler
+
+  _eventsCache.machine[id] = handler = (newMachine) ->
+    reactor.dispatch actions.MACHINE_UPDATED, { id, machine: newMachine }
+
+  computeController.on "revive-#{id}", handler
+
+
 _bindStackEvents = ->

   return  if _eventsCache.stack is yes
@@ -87,6 +102,9 @@
       machines = computeController.storage.get 'machines'

       reactor.dispatch actions.LOAD_USER_ENVIRONMENT_SUCCESS, machines
+
+      machines.forEach _bindMachineEvents
+
       resolve machines


Button for manual reconnecting

Button for manual reconnecting

--- src/plugins/preview.coffee
+++ src/plugins/preview.coffee
@@ -9,6 +9,7 @@

         <button class=\"start btn\"></button>
         <button class=\"stop btn\"></button>
+        <button class=\"connect btn\"></button>

@@ -19,6 +20,7 @@
     @$preview = @$connector.find '.preview'
     @$startButton = @$connector.find '.start'
     @$stopButton = @$connector.find '.stop'
+    @$connButton = @$connector.find '.connect'

     dataflow.addPlugin
       id: 'preview'
@@ -31,7 +33,9 @@
       @runtime.start()
     @$stopButton.click =>
       @runtime.stop()
+    @$startButton.hide()
     @$stopButton.hide()
+    @$connButton.hide()

   setRuntime: (@runtime) ->
     #@$preview.append @runtime.getElement()
@@ -58,9 +62,18 @@

   preparePreview: (preview, callback) ->
     @runtime.connect preview
+    @$connButton.click =>
+      @preparePreview preview, ->
+
     @runtime.once 'connected', =>
       @$connector.find('h2 span').html @runtime.getAddress()
+      @$connButton.hide()
+      @$startButton.show()
       callback()
+    @runtime.once 'disconnected', =>
+      @$connButton.show()
+      @$startButton.hide()
+      @$stopButton.hide()

 plugin = Dataflow::plugin 'preview'
 Dataflow::plugins['preview'] = new NoFloPreview


Client: Sidebar: Added setActiveLeavingSharedMachineId action

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -276,6 +276,13 @@
   { reactor, computeController } = kd.singletons

   reactor.dispatch actions.SET_ACTIVE_INVITATION_MACHINE_ID, { id }
+
+
+setActiveLeavingSharedMachineId = (id) ->
+
+  { reactor, computeController } = kd.singletons
+
+  reactor.dispatch actions.SET_ACTIVE_LEAVING_SHARED_MACHINE_ID, { id }

 module.exports = {
@@ -297,4 +304,5 @@
   setMachineListItem
   unsetMachineListItem
   setActiveInvitationMachineId
+  setActiveLeavingSharedMachineId
 }


video: model add helper

video: model add helper

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -5,6 +5,7 @@
 OpenTokService  = require './services/opentok'
 ParticipantType = require './types/participant'
 ViewModel       = require './viewmodel'
+helper          = require './helper'

 module.exports = class VideoCollaborationModel extends kd.Object


video: update model docs

video: update model docs

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -267,6 +267,10 @@
       error   : (err) =>

+  ###*
+   * Action for ending VideoCollaborationSession. Only can be called from
+   * admins of this session.
+  ###
   end: ->

     @stopSession
@@ -279,7 +283,6 @@
    * `OT.Publisher` instance. Sets active participant to user.
    *
    * @param {OT.Publisher} publisher
-   * @emits VideoCollaborationModel~VideoCollaborationActive
   ###
   handlePublishSuccess: (publisher) ->

@@ -290,6 +293,9 @@
     @changeActiveParticipant getNick()

+  ###*
+   * Handler for successful video stopping.
+  ###
   handleStopSuccess: ->

     @setState { publishing: off }


a dirty make up for index searches

a dirty make up for index searches

--- coffee/docset/db.coffee
+++ coffee/docset/db.coffee
@@ -7,7 +7,22 @@
             client: 'sqlite3'
             connection:
                 filename: @fileName
+        @s = @k('searchIndex')
     getIndexCount: ->
-        @k('searchIndex').count 'name'
+        @s.count 'name'
         .then (rows)->
-            rows[0]['count("name")']+            rows[0]['count("name")']
+    matchExactly: (key)->
+        @s.where 'name', key
+    matchHead: (string)->
+        @s.where 'name', "#{string}%"
+    matchTail: (string)->
+        @s.where 'name', "%#{string}"
+    matchMiddle: (string)->
+        @s.where 'name', "%#{string}%"
+    matchDeep: (string, depth)->
+        match = ''
+        for char, i in string
+            match += '%' if i % depth
+            match += char
+        @s.where 'name', match


+ new cursor(w-resize, e-resize) to sign the max and min value exceed.

--- client/app/scripts/directives/iSlidable.coffee
+++ client/app/scripts/directives/iSlidable.coffee
@@ -78,7 +78,21 @@
     .directive('iSlidable', ['$iReactable', ($iReactable) ->
         dragManager = new DragManager()
         aProcessEvent = (ngModelCtrl, options, scope, element, attrs, nextValueFn) ->
+            _$body = $('body')
+            oldCursor = _$body.css('cursor')
             options.hint = "Drag it to adjust @#{attrs.ngModel}'s value"
+            _updateCursor = (value)->
+                el = $(element)
+                if value >= options.range.max
+                    _$body.css(cursor: 'w-resize')
+                    el.css(cursor: 'w-resize')
+                else if value <= options.range.min
+                    _$body.css(cursor: 'e-resize')
+                    el.css(cursor: 'e-resize')
+                else
+                    _$body.css(cursor: 'col-resize')
+                    el.css(cursor: 'col-resize')
+                return
             _reset = ->
                 now = new Date()
                 if now - options.lastClick < 500
@@ -86,14 +100,14 @@
                         ngModelCtrl.$setViewValue(options.defaultValue);
                         ngModelCtrl.$render();
                     )
-                    #model.assign(scope, options.defaultValue)
+                    _updateCursor(options.defaultValue)
                 options.lastClick = now
                 return

             _onDrag = ({ x_start, y_start, x_delta, y_delta }) ->
                 options.range.mulStep = Math.floor(x_delta / 5)
-                #value = model(scope) #scope[options.bind]
                 value = nextValueFn(ngModelCtrl.$modelValue, options, false)
+                _updateCursor(value)
                 scope.$apply( ()->
                     ngModelCtrl.$setViewValue(value)
                 )
@@ -101,6 +115,8 @@

             _stopDragging = ->
                 element.removeClass('active')
+                _updateCursor(ngModelCtrl.$modelValue)
+                _$body.css('cursor', oldCursor)

             _startDragging = (e) ->
                 if !options.readonly


Terminal: TerminalStartTab: rename vms with machines, remove vmController calls

--- client/Terminal/terminalstarttab.coffee
+++ client/Terminal/terminalstarttab.coffee
@@ -4,7 +4,7 @@

     super

-    @vmWrapper = new KDCustomHTMLView tagName : 'ul'
+    @machineWrapper = new KDCustomHTMLView tagName : 'ul'

     @message = new KDCustomHTMLView cssClass : 'terminal-bottom-message'
     @message.hide()
@@ -17,13 +17,12 @@
   viewAppended: (force = no) ->

     super
-    @fetchVMs force
+    @fetchMachines force
     @prepareMessage()

-  fetchVMs: (force = no) ->
+  fetchMachines: (force = no) ->

-    {vmController, kontrol} = KD.singletons

     vmController.fetchVMs force, (err, vms)=>
       if err
@@ -50,14 +49,10 @@
       for own alias, kite of osKites
         if kite.recentState
           @vmWrapper[alias]?.handleVMInfo kite.recentState
-
-    vmController.on 'vm.progress.start', ({alias, update}) => @vmWrapper[alias]?.handleVMStart update
-    vmController.on 'vm.progress.stop',  ({alias, update}) => @vmWrapper[alias]?.handleVMStop update
-    vmController.on 'vm.state.info',     ({alias, state})  => @vmWrapper[alias]?.handleVMInfo state
-    vmController.on 'vm.progress.error', ({alias, error}) => @vmWrapper[alias]?.handleVMError error
+  listMachines: (machines)->

-  listVMs:(vms)->
+

     @vmWrapper.destroySubViews()
     vms.forEach (vm)=>
@@ -68,15 +63,8 @@
       appView.forwardEvent @vmWrapper[alias], 'VMItemClicked'

-  listVMSessions: (vms) ->
-    {vmController, kontrol} = KD.singletons
-    vmList = {}
-    vms.forEach (vm) -> vmList[vm.hostnameAlias] = vm
+  listMachineSessions: (machines) ->

-    terminalKites =
-      if KD.useNewKites
-      then kontrol.kites.terminal
-      else vmController.terminalKites

     delegate = @getDelegate()


minor: typo

minor: typo

--- client/activity/lib/routehandler.coffee
+++ client/activity/lib/routehandler.coffee
@@ -46,7 +46,7 @@
         return ctx.handleRoute ''

 ###*
- * Renders with reacth router.
+ * Renders with react router.
 ###
 handleReactivity = ({ query }, router) ->


Klient: on close fallback to original url instead of discovered one

--- client/app/lib/kite/kites/klient.coffee
+++ client/app/lib/kite/kites/klient.coffee
@@ -55,10 +55,10 @@
         machineUId = @getOption 'correlationName'
         machine    = cc.findMachineFromMachineUId(machineUId)

-        if not machine or not machine.isRunning()
-          @disconnect()
-
-    @terminalSessions = []
+        @transport.options.url = @_baseURL  if @_baseURL
+        @disconnect()  if not machine or not machine.isRunning()
+
+    @terminalSessions   = []

   # setTransport is used to override the setTransport method in KodingKite
@@ -66,8 +66,13 @@
   # Kite can go over our internal userproxy
   setTransport: (@transport) ->

-    { url } = @transport.options
-    proxifyTransportUrl url, (newurl) =>
+    { url, checkAlternatives } = @transport.options
+
+    # keep a local copy of proxified version
+    proxifyTransportUrl url, no,  (newurl) => @_baseURL = newurl
+
+    # ask for the alternatives or proxified version
+    proxifyTransportUrl url, checkAlternatives, (newurl) =>

       @transport.options.url = newurl


only show editing controls when actually editing

--- client/lanes/components/shared/ImageAsset.cjsx
+++ client/lanes/components/shared/ImageAsset.cjsx
@@ -11,6 +11,10 @@
         asset: 'props'

     listenNetworkEvents: true
+
+    mixins: [
+        Lanes.React.Mixins.ReadEditingState
+    ]

     bindEvents: ->
         model: "change:#{@props.name} change:#{@props.name}_data"
@@ -29,6 +33,18 @@
         return null unless @props.label
         {@props.label}

+    Edit: ->
+        return null unless @isEditingRecord()
+        <form>
+
+
+                    {if @asset.isPresent then 'Change' else 'Add'}
+
+                <input id='file' className="file" type="file"
+                    onChange={@handleImageChange} />
+
+        </form>
+
     render: ->
         Component = if @asset.hasImage then @renderImage else @blankImage
         className = _.classnames('image-asset', @props.className, {
@@ -40,13 +56,5 @@
         >
             <@Label />
             <Component />
-            <form>
-
-
-                        {if @asset.isPresent then 'Change' else 'Add'}
-
-                    <input id='file' className="file" type="file"
-                        onChange={@handleImageChange} />
-
-            </form>
+            <@Edit />
         </BS.Col>


global records cell/ null cell styling

global records cell/ null cell styling

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -495,8 +495,8 @@
               v = info.metric.options[v]
             when 'summary'
               if info.d.global
-                cell.classList.add('spreadsheet-global-record-cell')
                 if Editing
+                  cell.classList.add('spreadsheet-global-record-cell')
                   del = cell.appendChild(document.createElement('a'))
                   del.className = 'button mini global-record white icon-text'
                   delicon = del.appendChild(document.createElement('span'))
@@ -554,9 +554,9 @@
               if Editing && info.c != 'slot' && info.c != Key.id
                 generateAdd(info, td)
               else if !info.n
-                td.classList.add('spreadsheet-global-record-cell')
                 td.appendChild(document.createTextNode(info.category.not))
                 if Editing && info.c == 'slot' && Key.id != 'slot'
+                  td.classList.add('spreadsheet-global-record-cell')
                   add = td.appendChild(document.createElement('a'))
                   add.className = 'button mini white global-record icon-text'
                   addicon = add.appendChild(document.createElement('span'))


Don't show top-level materials in record view

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -411,7 +411,7 @@
             records[r] = populateRecord(record)

           nor = undefined
-          for s, slot of volume.containers when Top != !slot.top
+          for s, slot of volume.containers when Top != !slot.top && slot.id != volume.top.id
             recs = slot.records
             any = false
             for rr in recs when (row = records[rr.id])


Fix ss data display

Fix ss data display

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -484,7 +484,9 @@
             else
               if (info.metric.type == 'void' && info.d) || (info.metric.type == 'bool' && v)
                 cell.className = 'icon ' + if Editing && Key.id != info.c then 'trash' else 'bullet'
-              v = ''
+                v = ''
+              else if info.metric.type == 'bool'
+                v = ''
           if info.metric.long
             cell.classList.add('white-space-pre')
           if v?


Tests: Messages: PrivateMessages: Added refreshPageAndSeeTheConversationInSidebar test.

--- client/test/lib/messages/privatemessage.coffee
+++ client/test/lib/messages/privatemessage.coffee
@@ -17,5 +17,21 @@
     messagesHelpers.startConversation(browser, messageUser)
     browser.end()

+
+  refreshPageAndSeeTheConversationInSidebar: (browser) ->
+
+    messageUser =
+      userName  : 'sinan'
+      fullName  : 'Sinan Yasar'
+
     helpers.beginTest(browser)

+    isStarted = messagesHelpers.startConversation(browser, messageUser)
+
+    if isStarted then browser.end()
+    else
+      browser
+        .refresh()
+        .waitForElementVisible  '.activity-sidebar .messages', 20000
+        .assert.containsText    '.activity-sidebar .messages', messageUser.fullName  # Assertion
+        .end()


missed an imageTimestamp -> heartbeat

missed an imageTimestamp -> heartbeat

--- server/router.coffee
+++ server/router.coffee
@@ -33,7 +33,7 @@
           file.on 'end', ->
       busboy.on 'finish', ->
         if Object.keys(images).length
-          req.imageTimestamp = timestamp
+          req.heartbeat = timestamp
           res.statusCode = 204
         else
           res.statusCode = 400
@@ -46,7 +46,7 @@
     {roomId} = @params
     Rooms.update { _id: roomId }, {
       $set:
-        heartbeat: @request.imageTimestamp
+        heartbeat: @request.heartbeat
         images: @request.images
     }, (err, result) ->
       console.log err if err


Add failing assertion for #9

Add failing assertion for #9

--- spec/javascripts/role_jquery_spec.coffee
+++ spec/javascripts/role_jquery_spec.coffee
@@ -48,3 +48,5 @@

   it 'should work with $.fn.is', ->
     expect( $('@some_role').is('@some_role') ).toBe(true)
+    expect( $('@role_one').is('@role_one') ).toBe(true)
+    expect( $('@role_two').is('@role_one') ).toBe(true)


Show Channel Name

Show Channel Name

--- src/matteruser.coffee
+++ src/matteruser.coffee
@@ -210,7 +210,8 @@

         user = @robot.brain.userForId mmPost.user_id
         user.room = mmPost.channel_id
-
+        user.room_name = msg.data.channel_display_name
+
         text = mmPost.message
         if msg.data.channel_type == 'D'
           if !///^@?#{@robot.name} ///i.test(text) # Direct message


Fixed small bug where method didn't exist in migration of code

--- src/scripts/payroll.coffee
+++ src/scripts/payroll.coffee
@@ -10,11 +10,16 @@

 moment = require 'moment'
 schedule = require 'node-schedule'
+ADMINS = ['aaronsky', 'reid', 'ryan']

 module.exports = (robot) ->
   HEADERS = require('../helpers/constants').HEADERS
   Logger = require('../helpers/logger')(robot)
   Organization = require('../models/organization').get()
+
+  isAdminUser = (user) ->
+    return user? and user in ADMINS
+
   # Weeks ‘start’ on Sunday morning.

   dailyReport = (reports) ->


Factor out PValidator for now

Factor out PValidator for now

--- library/membrane/validate.coffee
+++ library/membrane/validate.coffee
@@ -67,149 +67,6 @@
         assert _.isFunction(context.prototype.run), noRun
         assert _.isFunction(context.prototype.chain), noChain
         @$vcontext = context; return this
-
-# This is an ABC service intended to be used only as a compund. It
-# provides a complete validation solution for request parameters. The
-# important difference is this validation system supports asynchronous
-# validators which is what differs it from existent solutions. This
-# validation system is a one-stop-shop for checking all the inputs!
-module.exports.RValidator = class RValidator extends Validator
-
-    # This is a marker that indicates to some internal subsystems
-    # that this class has to be considered abstract and therefore
-    # can not be treated as a complete class implementation. This
-    # mainly is used to exclude or account for abstract classes.
-    @abstract yes
-
-    # This method is a default implementation of the renderer that
-    # will be called when the validation has failed. You can easily
-    # override it in either your service or in an external compound.
-    # By default it renders JSON object with errors mapped to params.
-    renderParamValidation: (results, request, response) ->
-        notRequest = "a #{request} is not a request"
-        notResponse = "a #{response} is not a respnonse"
-        assert _.isObject(response), notResponse
-        assert _.isObject(request), notRequest
-        message = (error) -> error.message
-        strings = _.map results, message
-        response.statusCode = 400
-        map = _.object _.keys(results), strings
-        return @reject response, params: map
-
-    # Given the request with possible validation contexts appended
-    # run all the validator contexts in parallel and wait for the
-    # completion. If no validation mistakes found, run continuation.
-    # If some mistakes are found, however, run `@renderValidation`.
-    validateParameters: (request, response, continuation) ->
-        notRequest = "a #{request} is not a request"
-        notResponse = "a #{response} is not a response"
-        notContinuation = "a #{continuation} is not function"
-        transformer = (o) -> (c) -> o.run (e) -> c null, e
-        assert _.isFunction(continuation), notContinuation
-        assert _.isObject(response), notResponse
-        assert _.isObject(request), notRequest
-        vcontexts = request.params.vcontexts or {}
-        transformed =  _.map _.values(vcontexts), transformer
-        transformed = _.object _.keys(vcontexts), transformed
-        async.parallel transformed, (error, results) =>
-            assert not error, "internal valdation error"
-            errors = _.any _.values(results), _.isObject
-            hasRender = _.isFunction @renderParamValidation
-            assert hasRender, "no method to render validation"
-            params = [results, request, response, continuation]
-            return @renderParamValidation params... if errors
-            return continuation.bind(this)()
-
-    # Create a validation context for the parameter designated by
-    # the `name` and add it to the current request. If the `message`
-    # is supplied then it will be forced as an error messages. Use
-    # this method to automatically obtain contex for the parameter.
-    param: (request, name, message) ->
-        context = @constructor.validationContext?()
-        context = Primitive unless _.isObject context
-        assert request; value = request.params?[name]
-        vcontexts = request.params.vcontexts ?= {}
-        return obtain if obtain = vcontexts[name]
-        created = new context value, message
-        vcontexts[name] = created; created
-
-# This is an ABC service intended to be used only as a compund. It
-# provides a complete validation solution for request headers. The
-# important difference is this validation system supports asynchronous
-# validators which is what differs it from existent solutions. This
-# validation system is a one-stop-shop for checking all the inputs!
-module.exports.HValidator = class HValidator extends Validator
-
-    # This is a marker that indicates to some internal subsystems
-    # that this class has to be considered abstract and therefore
-    # can not be treated as a complete class implementation. This
-    # mainly is used to exclude or account for abstract classes.
-    @abstract yes
-
-    # This method is a default implementation of the renderer that
-    # will be called when the validation has failed. You can easily
-    # override it in either your service or in an external compound.
-    # By default it renders JSON object with errors mapped to params.
-    renderHeaderValidation: (results, request, response) ->
-        notRequest = "a #{request} is not a request"
-        notResponse = "a #{response} is not a respnonse"
-        assert _.isObject(response), notResponse
-        assert _.isObject(request), notRequest
-        message = (error) -> error.message
-        strings = _.map results, message
-        response.statusCode = 400
-        map = _.object _.keys(results), strings
-        return @reject response, headers: map
-
-    # Given the request with possible validation contexts appended
-    # run all the validator contexts in parallel and wait for the
-    # completion. If no validation mistakes found, run continuation.
-    # If some mistakes are found, however, run `@renderValidation`.
-    validateHeaders: (request, response, continuation) ->
-        notRequest = "a #{request} is not a request"
-        notResponse = "a #{response} is not a response"
-        notContinuation = "a #{continuation} is not function"
-        transformer = (o) -> (c) -> o.run (e) -> c null, e
-        assert _.isFunction(continuation), notContinuation
-        assert _.isObject(response), notResponse
-        assert _.isObject(request), notRequest
-        vcontexts = request.headers.vcontexts or {}
-        transformed =  _.map _.values(vcontexts), transformer
-        transformed = _.object _.keys(vcontexts), transformed
-        async.parallel transformed, (error, results) =>
-            assert not error, "internal valdation error"
-            errors = _.any _.values(results), _.isObject
-            hasRender = _.isFunction @renderHeaderValidation
-            assert hasRender, "no method to render validation"
-            params = [results, request, response, continuation]
-            return @renderHeaderValidation params... if errors
-        return continuation.bind(this)()
-
-    # Create a validation context for the parameter designated by
-    # the `name` and add it to the current request. If the `message`
-    # is supplied then it will be forced as an error messages. Use
-    # this method to automatically obtain contex for the parameter.
-    header: (request, name, message) ->
-        context = @constructor.validationContext?()
-        context = Primitive unless _.isObject context
-        assert request; value = request.headers?[name]
-        vcontexts = request.headers.vcontexts ?= {}
-        return obtain if obtain = vcontexts[name]
-        created = new context value, message
-        vcontexts[name] = created; created
-
-# This is an ABC service intended to be used only as a compund. It
-# provides a complete validation solution for the plain objects. The
-# important difference is this validation system supports asynchronous
-# validators which is what differs it from existent solutions. This
-# validation system is a one-stop-shop for checking all the inputs!
-module.exports.PValidator = class PValidator extends Validator
-
-    # This is a marker that indicates to some internal subsystems
-    # that this class has to be considered abstract and therefore
-    # can not be treated as a complete class implementation. This
-    # mainly is used to exclude or account for abstract classes.
-    @abstract yes

     # Given the request with possible validation contexts appended
     # run all the validator contexts in parallel and wait for the
@@ -241,3 +98,133 @@
         return obtain if obtain = vcontexts[name]
         created = new context value, message
         vcontexts[name] = created; created
+
+# This is an ABC service intended to be used only as a compund. It
+# provides a complete validation solution for request parameters. The
+# important difference is this validation system supports asynchronous
+# validators which is what differs it from existent solutions. This
+# validation system is a one-stop-shop for checking all the inputs!
+module.exports.RValidator = class RValidator extends Validator
+
+    # This is a marker that indicates to some internal subsystems
+    # that this class has to be considered abstract and therefore
+    # can not be treated as a complete class implementation. This
+    # mainly is used to exclude or account for abstract classes.
+    @abstract yes
+
+    # This method is a default implementation of the renderer that
+    # will be called when the validation has failed. You can easily
+    # override it in either your service or in an external compound.
+    # By default it renders JSON object with errors mapped to params.
+    renderParamValidation: (results, request, response) ->
+        notRequest = "a #{request} is not a request"
+        notResponse = "a #{response} is not a respnonse"
+        assert _.isObject(response), notResponse
+        assert _.isObject(request), notRequest
+        message = (error) -> error.message
+        strings = _.map results, message
+        response.statusCode = 400
+        map = _.object _.keys(results), strings
+        return @reject response, params: map
+
+    # Given the request with possible validation contexts appended
+    # run all the validator contexts in parallel and wait for the
+    # completion. If no validation mistakes found, run continuation.
+    # If some mistakes are found, however, run `@renderValidation`.
+    validateParameters: (request, response, continuation) ->
+        notRequest = "a #{request} is not a request"
+        notResponse = "a #{response} is not a response"
+        notContinuation = "a #{continuation} is not function"
+        transformer = (o) -> (c) -> o.run (e) -> c null, e
+        assert _.isFunction(continuation), notContinuation
+        assert _.isObject(response), notResponse
+        assert _.isObject(request), notRequest
+        vcontexts = request.params.vcontexts or {}
+        transformed =  _.map _.values(vcontexts), transformer
+        transformed = _.object _.keys(vcontexts), transformed
+        async.parallel transformed, (error, results) =>
+            assert not error, "internal valdation error"
+            errors = _.any _.values(results), _.isObject
+            hasRender = _.isFunction @renderParamValidation
+            assert hasRender, "no method to render validation"
+            params = [results, request, response, continuation]
+            return @renderParamValidation params... if errors
+            return continuation.bind(this)()
+
+    # Create a validation context for the parameter designated by
+    # the `name` and add it to the current request. If the `message`
+    # is supplied then it will be forced as an error messages. Use
+    # this method to automatically obtain contex for the parameter.
+    param: (request, name, message) ->
+        context = @constructor.validationContext?()
+        context = Primitive unless _.isObject context
+        assert request; value = request.params?[name]
+        vcontexts = request.params.vcontexts ?= {}
+        return obtain if obtain = vcontexts[name]
+        created = new context value, message
+        vcontexts[name] = created; created
+
+# This is an ABC service intended to be used only as a compund. It
+# provides a complete validation solution for request headers. The
+# important difference is this validation system supports asynchronous
+# validators which is what differs it from existent solutions. This
+# validation system is a one-stop-shop for checking all the inputs!
+module.exports.HValidator = class HValidator extends Validator
+
+    # This is a marker that indicates to some internal subsystems
+    # that this class has to be considered abstract and therefore
+    # can not be treated as a complete class implementation. This
+    # mainly is used to exclude or account for abstract classes.
+    @abstract yes
+
+    # This method is a default implementation of the renderer that
+    # will be called when the validation has failed. You can easily
+    # override it in either your service or in an external compound.
+    # By default it renders JSON object with errors mapped to params.
+    renderHeaderValidation: (results, request, response) ->
+        notRequest = "a #{request} is not a request"
+        notResponse = "a #{response} is not a respnonse"
+        assert _.isObject(response), notResponse
+        assert _.isObject(request), notRequest
+        message = (error) -> error.message
+        strings = _.map results, message
+        response.statusCode = 400
+        map = _.object _.keys(results), strings
+        return @reject response, headers: map
+
+    # Given the request with possible validation contexts appended
+    # run all the validator contexts in parallel and wait for the
+    # completion. If no validation mistakes found, run continuation.
+    # If some mistakes are found, however, run `@renderValidation`.
+    validateHeaders: (request, response, continuation) ->
+        notRequest = "a #{request} is not a request"
+        notResponse = "a #{response} is not a response"
+        notContinuation = "a #{continuation} is not function"
+        transformer = (o) -> (c) -> o.run (e) -> c null, e
+        assert _.isFunction(continuation), notContinuation
+        assert _.isObject(response), notResponse
+        assert _.isObject(request), notRequest
+        vcontexts = request.headers.vcontexts or {}
+        transformed =  _.map _.values(vcontexts), transformer
+        transformed = _.object _.keys(vcontexts), transformed
+        async.parallel transformed, (error, results) =>
+            assert not error, "internal valdation error"
+            errors = _.any _.values(results), _.isObject
+            hasRender = _.isFunction @renderHeaderValidation
+            assert hasRender, "no method to render validation"
+            params = [results, request, response, continuation]
+            return @renderHeaderValidation params... if errors
+        return continuation.bind(this)()
+
+    # Create a validation context for the parameter designated by
+    # the `name` and add it to the current request. If the `message`
+    # is supplied then it will be forced as an error messages. Use
+    # this method to automatically obtain contex for the parameter.
+    header: (request, name, message) ->
+        context = @constructor.validationContext?()
+        context = Primitive unless _.isObject context
+        assert request; value = request.headers?[name]
+        vcontexts = request.headers.vcontexts ?= {}
+        return obtain if obtain = vcontexts[name]
+        created = new context value, message
+        vcontexts[name] = created; created


Refactor the validation structure

Refactor the validation structure

--- library/membrane/validate.coffee
+++ library/membrane/validate.coffee
@@ -148,6 +148,31 @@
             return @renderValidation params... if errors
             return continuation.bind(this)()

+    # Create a validation context for the parameter designated by
+    # the `name` and add it to the current request. If the `message`
+    # is supplied then it will be forced as an error messages. Use
+    # this method to automatically obtain contex for the parameter.
+    param: (request, name, message) ->
+        context = @constructor.validationContext?()
+        context = Context unless _.isObject context
+        assert request; value = request.params?[name]
+        vcontexts = (request.vcontexts ?= {})
+        return obtain if obtain = vcontexts[name]
+        created = new context value, message
+        vcontexts[name] = created; created
+
+# An abstract base class that inherts from the standard Validator
+# and adds specific functionality on top of it. This functionality
+# is related to using the validation subsystem from outside of a
+# request/response context. Designed to be used by the providers.
+module.exports.PValidator = class PValidator extends Validator
+
+    # This is a marker that indicates to some internal subsystems
+    # that this class has to be considered abstract and therefore
+    # can not be treated as a complete class implementation. This
+    # mainly is used to exclude or account for abstract classes.
+    @abstract yes
+
     # Given the request with possible validation contexts appended
     # run all the validator contexts in parallel and wait for the
     # completion. Once the validation has been completed, call the
@@ -170,19 +195,6 @@
             return continuation.bind(this) errors, results

     # Create a validation context for the parameter designated by
-    # the `name` and add it to the current request. If the `message`
-    # is supplied then it will be forced as an error messages. Use
-    # this method to automatically obtain contex for the parameter.
-    param: (request, name, message) ->
-        context = @constructor.validationContext?()
-        context = Context unless _.isObject context
-        assert request; value = request.params?[name]
-        vcontexts = (request.vcontexts ?= {})
-        return obtain if obtain = vcontexts[name]
-        created = new context value, message
-        vcontexts[name] = created; created
-
-    # Create a validation context for the parameter designated by
     # the `name` and add it to the supplied storage. If the `message`
     # is supplied then it will be forced as an error messages. Use
     # this method to automatically obtain contex for the parameter.