fake activity: added fake activity feed fetcher methods

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -151,7 +151,67 @@

       eventSuffix = "#{@getFeedFilter()}_#{@getActivityFilter()}"

-      if @getFeedFilter() is "Public"
+      group = KD.getSingleton('groupsController').getCurrentGroup().slug
+      {roles} = KD.config
+
+      @on "StartFetchingFakeActivityComment", (activityId, commentId)=>
+        return if commentId > 2
+        timeoutValue = parseInt(Math.random()*100000,10)
+        setTimeout =>
+            eventName = "activity_#{activityId}_C#{commentId}_fetch"
+            @off "#{eventName}_succcess"
+
+            # if fetching comments fails, then shut down the event
+            @once "#{eventName}_fail", ()=>
+              @off "activity_#{activityId}_fetch_success"
+
+            # if fetching success, add them to activity feed
+            @once eventName, (activities)=>
+              @listActivities activities, callback, true
+              @emit "StartFetchingFakeActivityComment", activityId, commentId+1
+            @getFakes(activityId, commentId)
+
+          , timeoutValue
+
+      @on "StartFetchingFakeActivity", (activityId=0)=>
+        return if activityId > 7
+        @isLoading = true
+        timeoutValue = parseInt(Math.random()*10000,10)
+
+        eventName = "activity_#{activityId}_fetch"
+        @off "#{eventName}_succcess"
+
+        setTimeout =>
+            @once "#{eventName}_fail", ()=>
+              @off "#{eventName}_succcess"
+              @emit "StartFetchingFakeActivity", activityId+1
+
+            @once "#{eventName}_success", (activities)=>
+              @listActivities activities, (sanitizedCache)=>
+                callback sanitizedCache
+                @emit "StartFetchingFakeActivityComment", activityId, 1
+
+              @emit "StartFetchingFakeActivity", activityId+1
+            #fetch fake activity
+            @getFakes(activityId)
+
+          , timeoutValue
+
+
+      if group == "koding" and  "guest" in roles
+        # this is a hack, find a better way to hide inner navigation
+        $('.common-inner-nav').hide()
+        eventName = "activity_fetch"
+        @once "#{eventName}_fail", ()->
+          console.log "ERR : static main page activities will not work"
+
+        @once "#{eventName}_success", (activities)=>
+          activities.overview.reverse()  if activities.overview
+          @listActivities activities, callback
+          @emit "StartFetchingFakeActivity", 1
+        @getFakes()
+
+      else if @getFeedFilter() is "Public"
         @once "publicFeedFetched_#{eventSuffix}", (cache)=>
           reset()
           @extractCacheTimeStamps cache
@@ -173,6 +233,28 @@
     if isReady
     then fetch()
     else groupsController.once 'groupChanged', fetch
+
+  listActivities:(activities, callback, update=false)->
+    @sanitizeCache activities, (err, sanitizedCache)=>
+      @extractCacheTimeStamps sanitizedCache
+      @listController.listActivitiesFromCache sanitizedCache, 0 , {type : "slideDown", duration : 100}, update
+      callback sanitizedCache
+
+  getFakes:(activityId=0, commentId=0, likeId=0)->
+    activityName = "activity"
+    activityName += "_#{activityId}" unless activityId is 0
+    activityName += "_C#{commentId}" unless commentId is 0
+    activityName += "_L#{commentId}" unless likeId is 0
+
+    $.ajax({
+      type: "GET"
+      url: "js/activity/#{activityName}.json"
+      dataType: "json"
+      cache: "false"
+    }).done (json) =>
+      @emit "#{activityName}_fetch_success", json
+    .fail ()=>
+      @emit "#{activityName}_fetch_fail"

   fetchPublicActivities:(options = {})->
     {CStatusActivity} = KD.remote.api


tweak to random 2015

tweak to random 2015

Former-commit-id: 542b1f21deb37a678de393e1a5c5e6594209fb93 [formerly d6f61706e9d2de40abd32205d4c8e87cfcbbf00f] [formerly 9a3e2ccf577592a50a1c9fa7acad44ed8561acd1 [formerly 0224b0b94589d7e4a73420c23c73f7a6e2cf3c01]] [formerly 59b0c440ae42e04568c74b09de8cb049d21e909c [formerly a2d9c5d4e24b45dd3f6b354acc3b823ce1296159] [formerly d7663c7236e71dc83241b6e570fb1ef541a92b42 [formerly d7663c7236e71dc83241b6e570fb1ef541a92b42 [formerly 7277131d7be9e7c63a2434b8346ec518ad799561]]]]
Former-commit-id: 4499797df9b3df5b7e8c6f54384c61e37e80541d [formerly 6a26036dfc85f540f77abbbf9bd39d32abfd9d07] [formerly b0dd8e799af83da4e885015d1d437fbbb0cf7bf4 [formerly ce9eecfa5077d48e8077f8c866348c3da171f838]]
Former-commit-id: 3fe86d1f34b9071a0779850a9663b6b0f3ad1f37 [formerly eeed0e5d9fa05ba51b935d226428f65c939c986e]
Former-commit-id: 6e23713a836aaa0db19d782976db93f30bb6ef6e
Former-commit-id: b1718cd51dc00a2a6165d5122d32f4281c01d62d

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -134,11 +134,11 @@

   cluster_options :
     'Under Review':
-      homie_histo_title: "PCs' opinions"
+      homie_histo_title: "PCs' ratings"
     'Rejected':
-      homie_histo_title: "PCs' opinions"
+      homie_histo_title: "PCs' ratings"
     'Accepted':
-      homie_histo_title: "PCs' opinions"
+      homie_histo_title: "PCs' ratings"

 ##################
 # Schools demo


Stickify the various parts of the artist page

--- apps/artist/client/index.coffee
+++ apps/artist/client/index.coffee
@@ -19,6 +19,7 @@
 mediator = require '../../../lib/mediator.coffee'
 RelatedGenesView = require '../../../components/related_links/types/artist_genes.coffee'
 RelatedRepresentationsGenesView = require '../../../components/related_links/types/artist_representations.coffee'
+Sticky = require '../../../components/sticky/index.coffee'

 module.exports.ArtistView = class ArtistView extends Backbone.View
   events:
@@ -44,6 +45,8 @@
     @setupRelatedArtists()
     @setupRelatedShows()
     @setupRelatedPosts()
+
+    @sticky = new Sticky

     # Track pageview
     analytics.track.impression 'Artist page', id: @model.id
@@ -90,6 +93,7 @@
     ArtworkFilter.init el: @$('#artwork-section'), model: @model
     @listenToOnce mediator, 'artwork_filter:filter:sync', (model) ->
       @$('.artist-header-empty').remove() if model.get('total')
+      _.defer => @sticky.add @$('#artwork-filter')

   setupArtworksFillwidth: ->
     @fadeInSection @$('#artist-fillwidth-section')
@@ -130,7 +134,7 @@

   setupRelatedShows: ->
     @listenToOnce @model.relatedShows, 'reset', (collection) =>
-      @fadeInSection @$('#artist-related-shows-section') if collection.length
+      @setupRelatedSection @$('#artist-related-shows-section') if collection.length
     new RelatedShowsView
       el: @$('#artist-related-shows')
       collection: @model.relatedShows
@@ -148,11 +152,16 @@

   setupRelatedPosts: ->
     @listenToOnce @model.relatedPosts, 'reset', (collection) =>
-      @fadeInSection @$('#artist-related-posts-section') if collection.length
+      @setupRelatedSection @$('#artist-related-posts-section') if collection.length
     new RelatedPostsView
       el: @$('#artist-related-posts')
       collection: @model.relatedPosts
       numToShow: 4
+
+  setupRelatedSection: ($el) ->
+    $section = @fadeInSection $el
+    @sticky.add $section.find('.artist-related-section-header')
+    $el

   fadeInSection: ($el) ->
     $el.show()
@@ -170,7 +179,7 @@
   renderRelatedArtists: (type) =>
     $section = @$("#artist-related-#{type.toLowerCase()}-section")
     if @model["related#{type}"]?.models.length
-      @fadeInSection $section
+      @setupRelatedSection $section
       new ArtistFillwidthList(
         el: @$("#artist-related-#{type.toLowerCase()}")
         collection: @model["related#{type}"]


limit height of additional information

limit height of additional information

--- apps/artwork/client/view.coffee
+++ apps/artwork/client/view.coffee
@@ -13,6 +13,7 @@
 BelowTheFoldView          = require './below-the-fold.coffee'
 trackArtworkImpressions   = require("../../../components/analytics/impression_tracking.coffee").trackArtworkImpressions
 MonocleView               = require './monocles.coffee'
+BlurbView                 = require '../../../components/blurb/view.coffee'
 Sale                      = require '../../../models/sale.coffee'
 ZigZagBanner              = require '../../../components/zig_zag_banner/index.coffee'
 Auction                   = require './mixins/auction.coffee'
@@ -247,6 +248,13 @@
       numToShow: 2
       canBeEmpty: false

+    if ($blurb = $('.artwork-additional-information')).length
+      new BlurbView
+        updateOnResize : true
+        lineCount      : 2
+        el             : $blurb
+      $blurb.css maxHeight: 'none'
+
     @$('.ari-right').css
       'min-height': @$('.ari-left').height()


Addressed some minor PR comments.

Addressed some minor PR comments.

--- common/lib/xmodule/xmodule/js/src/crowdsource_hinter/display.coffee
+++ common/lib/xmodule/xmodule/js/src/crowdsource_hinter/display.coffee
@@ -27,10 +27,6 @@

   $: (selector) ->
     $(selector, @el)
-
-  jq_escape: (string) =>
-    # Escape a string for jquery selector use.
-    return string.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&')

   bind: =>
     @$('input.vote').click @vote
@@ -99,7 +95,7 @@
   render: (content) ->
     if content
       # Trim leading and trailing whitespace
-      content = content.replace /^\s+|\s+$/g, ""
+      content = content.trim()

     if content
       @el.html(content)


Update color_filter_view.coffee

Update color_filter_view.coffee

--- components/commercial_filter/filters/color/color_filter_view.coffee
+++ components/commercial_filter/filters/color/color_filter_view.coffee
@@ -49,10 +49,10 @@
     selected = @params.get('color')
     if selected
       @$checkmark.addClass(selected)
-      @$clear.show()
+      @$clear.css('visibility', 'visible')
       @$checkmark.show()
     else
-      @$clear.hide()
+      @$clear.css('visibility', 'hidden')
       @$checkmark.hide()

     # Add empty states


Use 1 and 2 in stead of true and false *puke*

--- client.coffee
+++ client.coffee
@@ -33,7 +33,7 @@
 	round = Db.shared.ref 'rounds', round_no

 	Dom.div ->
-		Dom.style fontSize: '150%', fontWeight: 'bold', textShadow: '0 1px 0 #fff', textAlign: 'center', padding: '4px 10px 10px 10px'
+		Dom.style fontSize: '300%', fontWeight: 'bold', textShadow: '0 1px 0 #fff', textAlign: 'center', padding: '4px 10px 10px 10px'
 		Dom.text Util.stringToQuestion(round.get('question'))

 	if round.get('finished')
@@ -45,12 +45,13 @@
 				Dom.text Plugin.userName(user.key())
 	else
 		ranking = Db.personal.ref('rounds', round_no)
+		my_vote = Db.shared.get('votes', Plugin.userId()) || null

-		Ui.bigButton 'I have!', ->
-			Server.call 'registerVote', Plugin.userId(), true
+		Ui.bigButton (if my_vote == 1 then "Hello" else "") + 'I have!', ->
+			Server.call 'registerVote', Plugin.userId(), 1

-		Ui.bigButton 'I have not!', ->
-			Server.call 'registerVote', Plugin.userId(), false
+		Ui.bigButton (if my_vote == 2 then "Hello" else "") + 'I have not!', ->
+			Server.call 'registerVote', Plugin.userId(), 2

 renderRoundList = ->
 	renderRoundItem = (round) ->


eliminate bifurcation of 'resources' vs 'directory'

--- client.coffee
+++ client.coffee
@@ -54,7 +54,6 @@
     @resource_constructors = @create_resource_constructors(options.resources)

     @resources = {}
-    @directory = {}
     @create_directory(options.directory, @resource_constructors)

@@ -68,13 +67,13 @@
           # generate it.  This is the expected case for directories coming
           # from a Patchboard Server.
           url = options.url
-          @directory[key] = new constructors[options.resource](url: url)
+          @resources[key] = new constructors[options.resource](url: url)
         else if options.path
           # When using a Patchboard definition for a third party API, you may
           # choose to specify paths in the directory, instead of full URLs,
           # to avoid the redundancy.
           url = @api.service_url + options.path
-          @directory[key] = new constructors[options.resource](url: url)
+          @resources[key] = new constructors[options.resource](url: url)
         else if options.template
           # Patchboard can use path templates to provide support for
           # insufficiently hyperlinked APIs.  First we create methods for


add required_contexts

add required_contexts

--- src/github-deployments.coffee
+++ src/github-deployments.coffee
@@ -152,7 +152,8 @@
         ref: ref,
         task: 'deploy',
         environment: target,
-        payload: {user: username, room: room}
+        payload: {user: username, room: room},
+        required_contexts: [],
         description: "#{username} created deployment for #{app}@#{ref} to #{target}"
       }


config change

config change

--- web/src/cs/config.coffee
+++ web/src/cs/config.coffee
@@ -1,6 +1,6 @@
 configs = {
-	mobile:  { initial: 10, max: 20}
-	tablet:  { initial: 10, max: 50}
+	mobile:  { initial: 2, max: 20}
+	tablet:  { initial: 2, max: 50}
 	desktop: { initial: 10, max: 40}
 }
 storedConfig = null


:wat:

:wat:

--- app/assets/javascripts/admin/moi_tree.js.coffee
+++ app/assets/javascripts/admin/moi_tree.js.coffee
@@ -149,13 +149,6 @@
     else
       @showChildren(node)
     @update(node)
-
-  draw: ->
-    d3.select(@selector).html("")
-    @createD3Elements()
-    @drawSVG()
-    @drawLinks()
-    @drawWithoutParent()

   paintNode: (node) ->
     node.attr('r', 4)


fix bounding box in sheet admin home

fix bounding box in sheet admin home

--- app/assets/javascripts/admin/sheet_index.coffee
+++ app/assets/javascripts/admin/sheet_index.coffee
@@ -64,7 +64,7 @@
   parse: (sheet, bounds) ->
     # define rectangle geographical bounds
     # data comes: W, S, E, N
-    bounds = Utils.parseBbox(sheet["bbox"])
+    sbounds = Utils.parseBbox(sheet["bbox"])

     N = sbounds.getNorth()
     S = sbounds.getSouth()
@@ -115,4 +115,4 @@
     @map.fitBounds(l.getBounds())

 $ ->
-  window._s = new Sheet+  window._s = new Sheet


cache circle radius computations, this should be factored out cleanly into the plot and made available as a parametr to render so that other renderers can do the same

--- static/coffee/renderers/glyph/circle.coffee
+++ static/coffee/renderers/glyph/circle.coffee
@@ -21,7 +21,11 @@
     if @mget('nonselection_glyphspec')
       spec = _.extend({}, @mget('glyphspec'), @mget('nonselection_glyphspec'))
       @nonselection_glyphprops = @init_glyph(spec)
-    ##duped in many classes
+    @have_new_data = false
+    @old_ow = null
+    @old_oh = null
+    @old_dx = null
+    @old_dy = null

   init_glyph : (glyphspec) ->
     glyph_props = new glyph_properties(
@@ -43,10 +47,23 @@
     for i in [0..@mask.length-1]
       @mask[i] = true
       @selected_mask[i] = false
+    @have_new_data = true

   _render: (plot_view) ->
     [@sx, @sy] = @plot_view.map_to_screen(@x, @glyph_props.x.units, @y, @glyph_props.y.units)
-    @radius = @distance(@data, 'x', 'radius', 'edge')
+
+    ow = @plot_view.view_state.get('outer_width')
+    oh = @plot_view.view_state.get('outer_height')
+    dx = @plot_view.x_range.get('end') - @plot_view.x_range.get('start')
+    dy = @plot_view.y_range.get('end') - @plot_view.y_range.get('start')
+
+    if @have_new_data or ow != @old_ow or oh != @old_oh or dx != @old_dx or dy != @old_dy
+      @radius = @distance(@data, 'x', 'radius', 'edge')
+      @have_new_data = false
+      @old_ow = ow
+      @old_oh = oh
+      @old_dx = dx
+      @old_dy = dy

     ow = @plot_view.view_state.get('outer_width')
     oh = @plot_view.view_state.get('outer_height')


Muda as cores do gráfico de distribuição por GND.

--- app/assets/javascripts/angular_app/directives/gnd_graph.js.coffee
+++ app/assets/javascripts/angular_app/directives/gnd_graph.js.coffee
@@ -22,6 +22,7 @@
                 .x((d) -> d.key)
                 .y((d) -> d.y)
                 .values((d) -> d)
+                .color(['#E67E22', '#1ABC9C', '#9B59B6', '#2980B9', '#7F8C8D', '#34495E', '#E74C3C', '#F1C40F'])

       chart.legend.align = false
       chart.tooltipContent((key, x, y, e, graph) ->


Do not destroy and re-draw the icon when its updated just update the data

--- client/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -205,10 +205,12 @@
   createAppIcon:(app, appData)->

     log "ADDING:", appData
-    @appIcons[appData.name]?.destroy()
-    @appItemContainer.addSubView @appIcons[appData.name] = new StartTabAppThumbView
-      delegate : @
-    , appData
+    if @appIcons[appData.name]?
+      @appIcons[appData.name].setData appData
+    else
+      @appItemContainer.addSubView @appIcons[appData.name] = new StartTabAppThumbView
+        delegate : @
+      , appData

   createAppsIcons:(apps)->
     for app, appData of apps


Fix specialist spec

Fix specialist spec

--- components/inquiry_questionnaire/test/views/specialist.coffee
+++ components/inquiry_questionnaire/test/views/specialist.coffee
@@ -99,14 +99,14 @@
         @loggedOutUser.get('name').should.equal 'Foo Bar'
         @loggedOutUser.get('email').should.equal 'foo@bar.com'

-        Backbone.sync.callCount.should.equal 4
+        Backbone.sync.callCount.should.equal 3

         Backbone.sync.args[0][1].url
           .should.containEql '/api/v1/admins/available_representatives'
         Backbone.sync.args[1][1].url()
-          .should.containEql '/api/v1/me/artwork_inquiry_request'
+          .should.containEql "/api/v1/me/anonymous_session/#{@loggedOutUser.id}"
         Backbone.sync.args[2][1].url()
-          .should.containEql "/api/v1/me/anonymous_session/#{@loggedOutUser.id}"
+          .should.containEql "/api/v1/profile"

         # Next
         @view.state.current().should.equal 'after_specialist'


fix for refreshApps method

fix for refreshApps method

--- client/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -23,13 +23,6 @@
       @updateRecentFileViews()

     @loader = new KDLoaderView size : width : 16
-
-    refreshApps = ->
-      @removeAppIcons()
-      @showLoader()
-      @appsController.refreshApps (err, apps)=>
-        @hideLoader()
-        @refreshButton.hideLoader()

     @refreshButton = new KDButtonViewWithMenu
       cssClass    : "editor-button refresh-apps-button"
@@ -38,14 +31,13 @@
       iconClass   : "refresh"
       loader      :
         diameter  : 16
+      callback    : @bound "refreshApps"
       menu        :
         "Restore shortcuts":
           cssClass: ""
           callback: =>
             KD.getSingleton('kodingAppsController').putDefaultShortcutsBack()
-            do refreshApps.bind this
-
-      callback    : refreshApps.bind this
+            @refreshApps()

     @addAnAppButton = new KDButtonView
       cssClass    : "editor-button new-app-button"
@@ -61,6 +53,13 @@

     @recentFilesWrapper = new KDView
       cssClass : 'file-container'
+
+  refreshApps: ->
+    @removeAppIcons()
+    @showLoader()
+    @appsController.refreshApps (err, apps)=>
+      @hideLoader()
+      @refreshButton.hideLoader()

   showLoader:->


Much better

Much better

--- client/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -107,10 +107,6 @@
   viewAppended:->

     super
-
-    unless @appsController._loadedOnce
-      @appsController.syncAppStorageWithFS()
-      @appsController._loadedOnce = yes

     @addRealApps()
     @addSplitOptions()
@@ -155,6 +151,11 @@
     @removeAppIcons()
     @showLoader()
     @appsController.fetchApps (err, apps)=>
+
+      unless @appsController._loadedOnce and Object.keys(apps).length > 0
+        @appsController.syncAppStorageWithFS()
+        @appsController._loadedOnce = yes
+
       @decorateApps apps

   decorateApps:(apps)->


Fixed code duplication

Fixed code duplication

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -114,12 +114,16 @@

         if /NetworkOut/i.test error

+          limitReachedNotice = "
+
You've reached your outbound network
+            usage limit for this week.

+          "
+
           paymentController.subscriptions (err, subscription) =>

             if subscription.planTitle is 'free'
               @customErrorMessage = "
-
You've reached your outbound network usage
-                limit for this week.

+                #{limitReachedNotice}
                 Please upgrade your plan or
                 contact support for further
                 assistance.
@@ -127,8 +131,7 @@

             else
               @customErrorMessage = "
-
You've reached your outbound network usage
-                limit for this week.

+                #{limitReachedNotice}
                 Please contact support
                 for further assistance.
               "


Removed family_history.js.coffee

Removed family_history.js.coffee

--- app/assets/javascripts/family_history.js.coffee
+++ app/assets/javascripts/family_history.js.coffee
@@ -1,22 +0,0 @@
-###*
-@namespace scoping into the hquery namespace
-###
-this.hQuery ||= {}
-
-
-# =require core.coffee
-###*
-This represents all  family history.
-@class
-@augments hQuery.CodedEntry
-@exports Family_History as hQuery.Family_History
-###
-class hQuery.Family_History extends hQuery.CodedEntry
-  constructor: (@json) ->
-    super(@json)
-
-
-  ###*
-  @returns {CodedValue}
-  ###
-  relationship: ->  new hQuery.createCodedValue @json['relationshipToPatient']


fake change to test sprint.ly integration, fixes #16, #34

--- common.coffee
+++ common.coffee
@@ -88,6 +88,7 @@
     max: 200
   }
 })
+
 share.Dishes.attachSchema(Schemas.Dish);


Apps: Quitted app on compile and run requested.

--- client/landing/app/Applications/Ace.kdapplication/AppView.coffee
+++ client/landing/app/Applications/Ace.kdapplication/AppView.coffee
@@ -91,9 +91,13 @@
   compileAndRun: ->
     manifest = KodingAppsController.getManifestFromPath @getData().path
     return @ace.notify "Not found an app to compile", null, yes unless manifest?.name
-    KD.getSingleton('kodingAppsController').compileApp manifest.name, (err) =>
+
+    appManager = KD.getSingleton "appManager"
+    appManager.quitByName manifest.name
+
+    KD.getSingleton("kodingAppsController").compileApp manifest.name, (err) =>
       @ace.notify "Trying to run old version..." if err
-      KD.getSingleton('appManager').open manifest.name
+      appManager.open manifest.name

   toggleFullscreen: ->
     mainView = KD.getSingleton "mainView"


Overwriting is clearer

Overwriting is clearer

--- core/app/backbone/modules/discussion_modal_on_frontend.coffee
+++ core/app/backbone/modules/discussion_modal_on_frontend.coffee
@@ -1,4 +1,4 @@
-# HACK: We store the background page url by intercepting "navigate" (called
+# HACK: We store the background page url by overwriting "navigate" (called
 # when navigating from one page in our app to another page in our app using Backbone)
 # and "loadUrl" (called when coming to a page from another site, directly, or
 # when using the browser navigation features). We then check if the page is not the


Start of drag/drop support

Start of drag/drop support

--- client/message.coffee
+++ client/message.coffee
@@ -110,6 +110,18 @@
 scrollToLater = null

 Template.submessage.onRendered ->
+  ## Drag and drop support.
+  id = @data._id
+  group = @data.group
+  focus = @$('.focusButton')
+  focus.attr 'draggable', true
+  focus.on 'dragstart', (e) ->
+    url = "coauthor:#{id}"  ## xxx actually ignored!
+    #url = pathFor 'message',
+    #  group: group
+    #  message: id
+    e.dataTransfer.setData 'text/plain', url
+
   ## Fold deleted messages by default on initial load.
   @folded.set true if @data.deleted
   #@$.children('.panel').children('.panel-body').find('a[href|="/gridfs/fs/"]')


Strip top-level chrome folders.

Strip top-level chrome folders.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -140,7 +140,17 @@
   # Traverses the bookmark hierarchy, and returns a flattened list of all bookmarks.
   traverseBookmarks: (bookmarks) ->
     results = []
-    bookmarks.forEach (folder) => @traverseBookmarksRecursive folder, results
+    bookmarks.forEach (folder) =>
+      # folder is a chrome virtual root folder.
+      folder.children.forEach((folder) =>
+        # folder is one of the chrome, built-in folders ("Other Bookmarks", "Mobile Bookmarks", ...).
+        folder.children.forEach((bookmark) =>
+          # bookmark is a user-defined folder, or itself a bookmark.
+          @traverseBookmarksRecursive bookmark, results))
+          #
+          # We do not add the (possible) bookmark itself.  The user has used the "/" key to begin searching
+          # within bookmark folders.  It's not clear what that even means for top-level bookmarks.
+          # results.push bookmark
     results

   # Recursive helper for `traverseBookmarks`.


Re-work completions: only offer actual search URL matches.

When filter suggestions from other completers, most notably the history
completer, we only keep suggestions which match the current searchUrl
and completer.  Here, we also *replace* the URL of the suggestion.  With
duplicate elimination, multiple history entries (e.g. those generated
with various "Search Tools" settings on Google) will be collapsed to
one.  This matters because, with custom search engines, we don't show
the URL, so the user can't see differences in the URL.  Without this,
the user can be presented with a list of apparently identical
completions.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -456,24 +456,26 @@
     # This filter is applied to all of the suggestions from all of the completers, after they have been
     # aggregated by the MultiCompleter.
     filter = (suggestions) ->
-      suggestions.filter (suggestion) ->
-        # We only keep suggestions which either *were* generated by this search engine, or *could have
-        # been* generated by this search engine (and match the current query).
-        suggestion.isSearchSuggestion or suggestion.isCustomSearch or
-          (
-            terms = Utils.extractQuery searchUrl, suggestion.url
-            terms and RankingUtils.matches queryTerms, terms
-          )
+      # We only keep suggestions which either *were* generated by this search engine, or *could have
+      # been* generated by this search engine (and match the current query).
+      for suggestion in suggestions
+        if suggestion.isSearchSuggestion or suggestion.isCustomSearch
+          suggestion
+        else
+          terms = Utils.extractQuery searchUrl, suggestion.url
+          continue unless terms and RankingUtils.matches queryTerms, terms
+          suggestion.url = Utils.createSearchUrl terms, searchUrl
+          suggestion

     # If a previous suggestion still matches the query, then we keep it (even if the completion engine may not
-    # return it for the current query).  This allows the user to pick suggestions they've previously seen by
-    # typing fragments of their text, without regard to whether the completion engine can continue to complete
-    # the actual text of the query.
+    # return it for the current query).  This allows the user to pick suggestions that they've previously seen
+    # by typing fragments of their text, without regard to whether the completion engine can continue to
+    # complete the actual text of the query.
     previousSuggestions =
       if queryTerms.length == 0
         []
       else
-        for url, suggestion of @previousSuggestions[searchUrl]
+        for _, suggestion of @previousSuggestions[searchUrl]
           continue unless RankingUtils.matches queryTerms, suggestion.title
           # Reset various fields, they may not be correct wrt. the current query.
           extend suggestion, relevancy: null, html: null, queryTerms: queryTerms


Improve consistency and fix relevancy calculation.

Problem:
  - The word relevancy calculation is case sensitive, all other matching
    is case insensitive.

Improve consistency:
  - Highlighting of elements in the vomnibox is case insensitive; this
    does not match the case sensitive relevancy calculation.

Fix:
  - Bookmark titles, in particular, tend to contain capital letters.
    Unless the query term also contains the relevant capital letters,
    then the term was scored at zero, pushing what seem to be good
    matches to the bottom of the list.  In fact, they were only included
    in the list at all because they happen to pass the
    `RankingUtils.matches()` test.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -254,7 +254,7 @@
   matches: (queryTerms, url, title) ->
     for term in queryTerms
       regexp = RegexpCache.get(term)
-      return false unless title.match(regexp) || url.match(regexp)
+      return false unless (title && title.match(regexp)) || (url && url.match(regexp))
     true

   # Returns a number between [0, 1] indicating how often the query terms appear in the url and title.
@@ -264,8 +264,8 @@
     titleScore = 0.0
     for term in queryTerms
       queryLength += term.length
-      urlScore += 1 if url.indexOf(term) >= 0
-      titleScore += 1 if title && title.indexOf(term) >= 0
+      urlScore += 1 if RankingUtils.matches([term], url, null)
+      titleScore += 1 if RankingUtils.matches([term], null, title)
     urlScore = urlScore / queryTerms.length
     urlScore = urlScore * RankingUtils.normalizeDifference(queryLength, url.length)
     if title


Convert authentication

Convert authentication

--- src/api/authentication.coffee
+++ src/api/authentication.coffee
@@ -2,51 +2,46 @@
 crypto = require 'crypto'
 logger = require 'winston'

-exports.authenticate = `function *authenticate(next) {
+exports.authenticate = (next) ->

-  var header = this.request.header;
-  var email = header['auth-username'];
-  var authTS = header['auth-ts'];
-  var authSalt = header['auth-salt'];
-  var authToken = header['auth-token'];
+  header = this.request.header
+  email = header['auth-username']
+  authTS = header['auth-ts']
+  authSalt = header['auth-salt']
+  authToken = header['auth-token']

-  // check if request is recent
-  var requestDate = new Date(Date.parse(authTS));
+  # check if request is recent
+  requestDate = new Date Date.parse authTS

-  var to = new Date();
-  to.setSeconds(to.getSeconds() + 2);
-  var from = new Date();
-  from.setSeconds(from.getSeconds() - 2);
+  to = new Date()
+  to.setSeconds(to.getSeconds() + 2)
+  from = new Date()
+  from.setSeconds(from.getSeconds() - 2)

-  if (requestDate < from || requestDate > to) {
-    // request expired
-    logger.info('API request made by ' +email+ ' from ' +this.request.host+ ' has expired, denying access');
-    this.status = 'unauthorized';
-    return;
-  }
+  if requestDate < from or requestDate > to
+    # request expired
+    logger.info "API request made by #{email} from #{this.request.host} has expired, denying access"
+    this.status = 'unauthorized'
+    return

-  var user = yield User.findOne({ email: email }).exec();
-  this.authenticated = user;
+  user = yield User.findOne(email: email).exec()
+  this.authenticated = user

-  if (!user) {
-    // not authenticated - user not found
-    logger.info('No user exists for ' +email+ ', denying access to API, request originated from ' +this.request.host);
-    this.status = 'unauthorized';
-    return;
-  }
+  if not user
+    # not authenticated - user not found
+    logger.info "No user exists for #{email}, denying access to API, request originated from #{this.request.host}"
+    this.status = 'unauthorized'
+    return

-  hash = crypto.createHash('sha512');
-  hash.update(user.passwordHash);
-  hash.update(authSalt);
-  hash.update(authTS);
+  hash = crypto.createHash 'sha512'
+  hash.update user.passwordHash
+  hash.update authSalt
+  hash.update authTS

-  if (hash.digest('hex') === authToken) {
-    // authenticated
-    yield next;
-  } else {
-    // not authenticated - token mismatch
-    logger.info('API token did not match expected value, denying access to API, the request was made by ' +email+ ' from ' +this.request.host);
-    this.status = 'unauthorized';
-  }
-
-}`
+  if authToken is hash.digest 'hex'
+    # authenticated
+    yield next
+  else
+    # not authenticated - token mismatch
+    logger.info "API token did not match expected value, denying access to API, the request was made by #{email} from #{this.request.host}"
+    this.status = 'unauthorized'


use a layout to render subparts of the view

--- core/app/backbone/views/autocompleted_add_to_channel_view.coffee
+++ core/app/backbone/views/autocompleted_add_to_channel_view.coffee
@@ -2,7 +2,7 @@

 updateWindowHeight = ->  window.updateHeight() if window.updateHeight

-class window.AutoCompletedAddToChannelView extends Backbone.Factlink.PlainView
+class window.AutoCompletedAddToChannelView extends Backbone.Marionette.Layout
   tagName: "div"
   className: "add-to-channel"
   events:
@@ -16,6 +16,10 @@
     "click .show-input-button": "showInput"
     "mouseenter .auto_complete>div": "selectAddNew"
     "mouseleave .auto_complete>div": "deActivateAddNew"
+
+  regions:
+    'added_channels': 'div.added_channels_container'
+    'auto_completes': 'div.auto_complete_container'

   activeChannelKey: -> @_activeChannelKey

@@ -38,11 +42,8 @@
   onRender: ->
     @$(".auto_complete ul").preventScrollPropagation()

-    @_added_channels_view.render()
-    @$("div.added_channels_container").html @_added_channels_view.el
-
-    @_auto_completes_view.render()
-    @$("div.auto_complete_container").html @_auto_completes_view.el
+    @added_channels.show(@_added_channels_view)
+    @auto_completes.show(@_auto_completes_view)

     updateWindowHeight()

@@ -178,12 +179,9 @@
   showAddNew: -> @$el.removeClass "hide-add-new"
   isAddNewVisible: -> not @$el.hasClass("hide-add-new")

-
   clearAutoComplete: ->
     @_auto_completes_view.closeList()
     @_auto_completes_view.search_collection.reset []
     @$(".auto_complete").addClass "empty"
     @deActivateAddNew()
-    @showAddNew()
-
-  onClose: -> @_auto_completes_view.close()+    @showAddNew()


mark notify

mark notify

--- gulp/tasks/server.coffee
+++ gulp/tasks/server.coffee
@@ -16,7 +16,7 @@
   ], (event) ->
     gulp.src(event.path)
     .pipe $.connect.reload()
-    .pipe $.notify( message:'reload')
+    #.pipe $.notify( message:'reload')

   # Watch .scss files


Simpler condition since can_destroy was improved

--- core/app/backbone/views/sub_comments/react_sub_comment.coffee
+++ core/app/backbone/views/sub_comments/react_sub_comment.coffee
@@ -38,7 +38,7 @@
       if @model().get('save_failed') == true
         _a ['button', 'button-danger', onClick: @_save, style: {float: 'right'} ],
           'Save failed - Retry'
-      else if @model().can_destroy()
+      if @model().can_destroy()
         window.ReactDeleteButton
           model: @model()
           onDelete: -> @model.destroy wait: true


fixed tests

fixed tests


:open_hands:

--- scripts/help.coffee
+++ scripts/help.coffee
@@ -56,8 +56,6 @@

 module.exports = (robot) ->
   robot.respond /help(?:\s+(.*))?$/i, (msg) ->
-    cache = ''
-    url = ''
     cmds = renamedHelpCommands(robot)
     filter = msg.match[1]

@@ -70,13 +68,13 @@

     cmds = cmds.join "\n"

-    if cache == cmds
-      msg.send "Full list of commands: " + url
+    if cmds.length < 1000
+      msg.send "```\n#{cmds}\n```"
     else
-      cache = cmds
       gist {content: cmds}, (err, resp, data) ->
-        url = data.html_url
-        msg.send "Full list of commands: " + url
+        if filter
+          return msg.send "Here are '#{filter}' commands: #{data.html_url}"
+        msg.send "Full list of commands: #{data.html_url}"

 renamedHelpCommands = (robot) ->


エスケープ

エスケープ

--- scripts/kimu.coffee
+++ scripts/kimu.coffee
@@ -2,4 +2,4 @@
   robot.hear /きむら/, (msg) ->
     msg.send "りゅうた"
   robot.hear /やれやれだぜ/, (msg) ->
-    msg.send "¯\_(ツ)_/¯"+    msg.send "¯\\_(ツ)_/¯"


Check sidebar width before updating filter editor


Made live reload work from inside a docker container.

--- web/Gruntfile.coffee
+++ web/Gruntfile.coffee
@@ -68,8 +68,8 @@
           port: 9000
           middleware: (connect, options) ->
             [
-
-              require('connect-livereload')(src: "http://#{require('ip').address()}:35729/livereload.js?snipver=1"),
+              # Either grap our ip from environment variable or from ip library.
+              require('connect-livereload')(src: "http://#{process.env.IP || require('ip').address()}:35729/livereload.js?snipver=1"),
               mountFolder(connect, '.tmp'),
               mountFolder(connect, 'app')
             ]


Have hubot show which hash was synced

Have hubot show which hash was synced

--- scripts/sync.coffee
+++ scripts/sync.coffee
@@ -18,25 +18,26 @@
 module.exports = (robot) ->
   robot.respond /sync/i, (msg) ->

+    run = (cmd, done) ->
+      Child.exec cmd, options, (err, stdout) ->
+        if err
+          msg.send err.message.trim()
+          msg.send "'#{cmd}' failed"
+          return
+
+        done(stdout);
+
     pull = ->
       msg.send 'Pulling from github'
-      Child.exec 'git pull origin master', options, (err) ->
-        if err
-          msg.send err.message.trim()
-          msg.send 'git pull failed'
-          return
-
-        install()
+      run 'git pull origin master', install

     install = ->
       msg.send 'Running npm install'
-      Child.exec 'npm install', options, (err) ->
-        if err
-          msg.send err.message.trim()
-          msg.send 'npm install failed'
-          return
+      run 'npm install', show

-        msg.send 'Sync successful. I regret nothing!'
+    show = ->
+      run 'git rev-parse --short HEAD', (stdout) ->
+        msg.send "Successfully synced #{stdout.trim()}. I regret nothing!"
         setTimeout die, 1000

     die = ->


I single handedly fixed a bug without anyone's help.

I'm so good at programming

--- scripts/vote.coffee
+++ scripts/vote.coffee
@@ -128,8 +128,8 @@

   # Election driver
   robot.respond /(vote|freedom) ([^\s]+)\s?(.*)?$/, (msg) ->
-    action = msg.match[1].trim().toLowerCase()
-    arg = msg.match[2]?.trim()
+    action = msg.match[2].trim().toLowerCase()
+    arg = msg.match[3]?.trim()

     switch action
       # Vote on a current issue


NODEWORDS

NODEWORDS

--- scripts/word.coffee
+++ scripts/word.coffee
@@ -18,8 +18,12 @@
 #   jeremy-green

 module.exports = (robot) ->
-  robot.hear /wor(f|d)?/i, (msg) ->
-    imageMe msg
+  robot.hear /(words?|worf)/i, (msg) ->
+    subject = msg.match[1]
+    if subject is 'worf'
+      imageMe msg
+    else
+      msg.send 'NODEWORDS!!'

 imageMe = (msg) ->
   msg.http('http://ajax.googleapis.com/ajax/services/search/images')


Color change

Color change

--- site/app.coffee
+++ site/app.coffee
@@ -148,7 +148,6 @@
     yr = (Math.round(row.epiweek / 100))
     if yr == 2015 and wk >= 40
       mappedData[row.location][wk] = row.value
-    if yr == 2016 and wk <= 39
       mappedData[row.location][wk] = row.value
   for region in HHS_REGIONS
     weeks = nonInfluenzaWeekData[region]
@@ -460,7 +459,7 @@
             @mapData[row.location] = row
         @renderMap()
       handler = getEpidataHander(callback)
-      Epidata_nowcast_multi(handler, LOCATIONS, 201540, 201710)
+      Epidata_nowcast_multi(handler, LOCATIONS, 201540, 201639)
     handler = getEpidataHander(callback)
     Epidata_nowcast_single(handler, 'nat')


Fix illegal requirement

Fix illegal requirement

Signed-off-by: Manson Li <f2a598d7717cc6757bef28b84a9ab02e5efc6048@matchstick.tv>

--- lib/platform/PlatformFirefoxOS.coffee
+++ lib/platform/PlatformFirefoxOS.coffee
@@ -17,10 +17,10 @@
 net                     = rekuire "net"
 child_process           = rekuire "child_process"

-{ Log }                 = rekuire "castaway/util/Log"
+{ Log }                 = rekuire "log/Log"
 { Platform }            = rekuire "platform/Platform"
 { JsonMessageEmitter }  = rekuire "util/JsonMessageEmitter"
-{ HashSet }             = rekuire "lib/HashSet"
+{ HashSet }             = rekuire "util/HashSet"
 { Config }              = rekuire "dial/Config"

 class PlatformFirefoxOS extends Platform


add hourly weather report

add hourly weather report

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -2,6 +2,17 @@
 # TODO: make class in './lib' and use 'module.exports'
 WEATHER_API = "http://api.wunderground.com/api"
 WEATHER_API_KEY = process.env.weather_api_key
+
+# use 'Weather Underground' API
+# place like 'Japan/Kyoto'
+getHourlyWeather = (place, callback) ->
+  url = "#{WEATHER_API}/#{WEATHER_API_KEY}/hourly/q/#{place}.json"
+  console.log "getWeatherForcast : '#{url}'"
+  request.get {url: url}, (err, r, body) ->
+    if err
+      console.dir err
+    else
+      callback(JSON.parse(body))

 # use 'Weather Underground' API
 # place like 'Japan/Kyoto'
@@ -69,8 +80,47 @@
   catch ex
     console.dir ex

+hourlyWeather = (cb) ->
+  try
+    getHourlyWeather 'Japan/Kyoto', (res) ->
+      limit = new Date().getTime() + (24*60*60*1000)
+      startHour = res.hourly_forecast[0]['FCTTIME'].hour
+      weathers = []
+      for w in res.hourly_forecast
+        spentHours = w['FCTTIME'].hour - startHour
+        time = new Date(w['FCTTIME'].epoch * 1000)
+        if spentHours % 3 == 0 && time.getTime() < limit
+          data = {
+            temp: w.temp.metric,
+            cond: w.condition,
+            pop: w.pop,
+            icon: weatherIcon(w.icon),
+            time: time,
+          }
+          weathers.push formatWeather(data)
+      cb "今日の天気は... \n" + weathers.join("\n")
+  catch ex
+    console.dir ex
+
+formatWeather = (w) ->
+  msg = []
+  if w.time.getHours() == 0
+    msg.push "#{w.time.getMonth()+1}/#{w.time.getDate()} (#{dayOfWeek w.time})\n"
+  msg.push w.time.getHours()
+  msg.push w.icon
+  msg.push "#{w.temp}℃  / #{w.pop}%"
+  msg.join " "
+
+DAYS_OF_WEEK = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
+dayOfWeek = (date) ->
+  DAYS_OF_WEEK[date.getDay()]
+
 # weather news
-controller.hears ['weahter', '天気'], MENTIONS, (bot, message) ->
+controller.hears ['明日の天気'], MENTIONS, (bot, message) ->
   weatherReport (report) ->
     bot.reply message, report

+controller.hears ['今日の天気'], MENTIONS, (bot, message) ->
+  hourlyWeather (report) ->
+    bot.reply message, report
+


Tests: Trigger build.

Tests: Trigger build.

--- tests/src/helpers/environmenthelpers.coffee
+++ tests/src/helpers/environmenthelpers.coffee
@@ -82,3 +82,4 @@
       .waitForElementNotVisible '.env-modal.paid-plan', 250000
       .waitForElementVisible    'a[href="/IDE/koding-vm-1/my-workspace"]', 25000
       .end()
+


remove conflictt

remove conflictt

--- client/apps/edit/components/section_image_collection/index.coffee
+++ client/apps/edit/components/section_image_collection/index.coffee
@@ -231,7 +231,6 @@
                         src: item.image
                         className: 'esic-artwork'
                       }
-<<<<<<< HEAD
                     div {className: 'esic-caption', key: 'caption-' + i },
                       p {},
                         strong {}, @formatArtistNames item
@@ -240,15 +239,6 @@
                         if item.date
                           span { className: 'date' }, ", " + item.date if item.date
                       p {}, item.partner.name if item.partner.name
-=======
-                    p {},
-                      strong {}, @formatArtistNames item
-                    p { className: 'esic-artwork' },
-                      span { className: 'title' }, item.title if item.title
-                      if item.date
-                        span { className: 'date' }, ", " + item.date if item.date
-                    p {}, item.partner.name if item.partner.name
->>>>>>> 8a935313dc18c6fe2f4b30e9a4d7254dd70cdf2b
                     button {
                       className: 'edit-section-remove button-reset esic-img-remove'
                       key: 'remove-' + i


removing extra collection declaration

removing extra collection declaration

--- client/controllers/incidentReports/suggestedIncidentsModal.coffee
+++ client/controllers/incidentReports/suggestedIncidentsModal.coffee
@@ -82,7 +82,6 @@
   stageModals(instance, modal, false)

 Template.suggestedIncidentsModal.onCreated ->
-  @incidentCollection = new Meteor.Collection(null)
   @hasBeenWarned = new ReactiveVar(false)
   @loading = new ReactiveVar(true)
   @content = new ReactiveVar('')


myteam: teammates: minor changes

myteam: teammates: minor changes

--- client/home/lib/myteam/components/hometeamteammates/member.coffee
+++ client/home/lib/myteam/components/hometeamteammates/member.coffee
@@ -22,7 +22,7 @@

   getMenuItems: (role) ->

-    items=[]
+    items = []

     if role is 'owner'
       items.push { title: 'MAKE MEMBER', key: 'makemember', onClick: @props.handleRoleChange.bind(this, @props.member, 'member') }


Track view mode changes (closes #915)

Track view mode changes (closes #915)

--- components/logged_in_navigation/components/view_mode/index.coffee
+++ components/logged_in_navigation/components/view_mode/index.coffee
@@ -1,4 +1,5 @@
 mediator = require '../../../../lib/mediator.coffee'
+analytics = require '../../../../lib/analytics.coffee'

 glyphs =
   grid: 'grid-three-up'
@@ -16,6 +17,10 @@
     state.set 'view_mode', mode = $target.data 'mode'
     $indicator.attr 'data-glyph', glyphs[mode]

+    analytics.track.click 'View mode changed',
+      label: 'Mode'
+      value: $target.data 'mode'
+
     window.location.reload()

   null


Fix resizing issue on collapse

Fix resizing issue on collapse

--- lib/unified/php-debug-unified-view.coffee
+++ lib/unified/php-debug-unified-view.coffee
@@ -66,7 +66,9 @@

   setConnected: (isConnected) =>
-    @panel?.item?.style.height = @panel?.item?.clientHeight + 'px'
+    if (@panel?.item?.clientHeight > 0)
+      @panel?.item?.style.height = @panel?.item?.clientHeight + 'px'
+
     if isConnected
       @connectStatus.text('Connected')
     else


Make a JSONP call to the embed API.

Make a JSONP call to the embed API.

--- src/main/webapp/coffeescripts-hidden/load.coffee
+++ src/main/webapp/coffeescripts-hidden/load.coffee
@@ -12,6 +12,16 @@
 googleAnalyticsAccount = "UA-35269224-4"
 apiDomain = "local.anchortab.com"
 jqVersion = "1.8.2"
+
+##
+## LIB FUNCTIONS
+##
+# http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values
+getParam = (name) ->
+  results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href)
+  if ! results
+    return 0;
+  results[1] || 0

 ##
 ## BOOTSTRAPPING FUNCTIONS
@@ -95,9 +105,15 @@
   # safely without things getting hairy. If we find out in the future that things are wonky about that
   # then we'll have to refactor some of this code, I suppose.
   $ = jQuery # In case we're in compability mode.
+  tabId = getParam "tabId"
+  tabJson = "http://" + apiDomain + "/api/v1/embed/" + tabId

-  #$.ajax
-    #url: 'https://anchortab.com/api/v1/embed/abc123'
+  $.ajax
+    url: tabJson
+    dataType: 'jsonp'
+    success: displayTab
+    error: (xhr, status, error) ->
+      console.error(error)

 # Do the neccicary mojo to make sure we load up our goods after the
 # window is loaded. This includes making sure all our dependancies


client: jekyll: docs: improved tf nav creation

--- docs/js/nav-terraform.coffee
+++ docs/js/nav-terraform.coffee
@@ -15,24 +15,26 @@
     then raw.split(': ').pop()
     else raw

-  createUl = (doc, parent) ->
+  createSubNavItems = (doc, parent, prop) ->

-    ul = $ "
"
-    if index = doc?.index
+    if index = doc.index
       isActive = pageUrl is index.url
-      title = "
#{getTitle index.title}
"
-      parent.append title
+      parent.append "
#{getTitle index.title}
"
+    else if prop is 'd'
+      parent.append "
Data Sources
"
+    else if prop is 'r'
+      parent.append "
Resources
"

-    unless doc.title
-      for own prop of doc
-        createUl doc[prop], ul
-      parent.find('li').last().append ul
-    else
+    if doc.title
       unless /index\.html\/?/.test doc.url
         isActive = pageUrl is doc.url
-
         parent.append "
#{getTitle doc.title}
"

+    else
+
+      ul = $ '
'
+      createSubNavItems value, ul, key  for own key, value of doc
+      parent.find('li').last().append ul

     return ul

@@ -50,12 +52,17 @@

   window.TERRAFORM_DOCS_MAP = docsMap

-  createUl docsMap, MAIN_UL
+  createSubNavItems docsMap, MAIN_UL

-  # expandParents active  if (active = $('li.active')).length
-  $('aside li ul').hide()
+  if pageUrl is '/docs/home'
+    $('aside li ul ul').hide()
+  else
+    $('aside li ul').hide()
+
   $('li.active > ul').show()
+  $('li.active > ul > li.separator > ul').show()
   $('li.active').parents('ul').show()
+

   $('li a[href="#"]').on 'click', (event) ->
     event.preventDefault()


These can go too

These can go too

--- SingularityUI/app/application.coffee
+++ SingularityUI/app/application.coffee
@@ -97,14 +97,6 @@
         @state = new State

         resources = [{
-            collection_key: 'requestsPending'
-            collection: RequestsPending
-            error_phrase: 'pending requests'
-        }, {
-            collection_key: 'requestsCleaning'
-            collection: RequestsCleaning
-            error_phrase: 'cleaning requests'
-        }, {
             collection_key: 'tasksActive'
             collection: TasksActive
             error_phrase: 'active tasks'


cut logging message

cut logging message

--- node_modules/hubot-twitter-egosearch/scripts/egosearch.coffee
+++ node_modules/hubot-twitter-egosearch/scripts/egosearch.coffee
@@ -110,14 +110,12 @@

   robot.brain.on 'loaded', =>
     robot.logger.debug 'loaded'
-    robot.logger.debug robot.brain.data.egosearch
     loadQueriesFromBrain robot
     loopSearching robot

   robot.respond /egosearch (?:add|new|start) (.+)/i, (msg) ->
     robot.logger.debug 'add search'
     createQuery robot, msg, msg.match[1], msg.message.user
-    robot.logger.debug robot.brain.data.egosearch

   robot.respond /egosearch (?:rm|remove|del|delete|stop) (.+)/i, (msg) ->
     deleteQuery robot, msg, msg.match[1]


Switch to using the favicon as the test image

--- coffee/offline.coffee
+++ coffee/offline.coffee
@@ -19,14 +19,15 @@
   checks:
     xhr:
       url: ->
+        # This can be any endpoint, even one that will 404.
         "/offline-test-request/#{ Math.floor(Math.random() * 1000000000) }"

     image:
       url: ->
         # This can be any image:
-        "img/tiny-image.gif?_=#{ Math.floor(Math.random() * 1000000000) }"
-
-    active: 'xhr'
+        "/favicon.ico?_=#{ Math.floor(Math.random() * 1000000000) }"
+
+    active: 'image'

   checkOnLoad: false


Update SaveProgress module

Update SaveProgress module

- Add `container` element
- Add `_eraseProgress` method
- Remove unnecessary each block
- Change `elements` to `element`

--- components/saveProgress.coffee
+++ components/saveProgress.coffee
@@ -5,7 +5,8 @@
 #
 # *************************************
 #
-# options.elements - the element containing text to save
+# options.element - the element whose value to save
+# options.container - the element containing input elements
 # options.dataAttribute - the data attribute of the Local Storage key
 #
 # *************************************
@@ -24,7 +25,8 @@

   init = (options) ->
     _settings = $.extend(
-      elements: $('.js-saveProgress')
+      element: $('.js-saveProgress')
+      container: $('.js-saveProgress-container')
       dataAttribute: 'saveprogress'
     , options)

@@ -32,14 +34,24 @@
     _setEventHandlers()

   # -------------------------------------
+  #   Erase Progress
+  # -------------------------------------
+
+  _eraseProgress = (container) ->
+    container.find(_settings.element).each ->
+      key = $(@).data(_settings.dataAttribute)
+
+      localStorage.removeItem(key)
+
+  # -------------------------------------
   #   Restore Progress
   # -------------------------------------

   _restoreProgress = ->
-    _settings.elements.each ->
+    _settings.element.each ->
       element = $(@)
       key = element.data(_settings.dataAttribute)
-      value = localStorage.getItem key
+      value = localStorage.getItem(key)

       element.val(value)

@@ -48,14 +60,15 @@
   # -------------------------------------

   _setEventHandlers = ->
-    _settings.elements.each ->
+    _settings.element.on 'input', ->
       element = $(@)
+      key = element.data(_settings.dataAttribute)
+      value = element.val()

-      element.on 'input', ->
-        key = element.data(_settings.dataAttribute)
-        value = element.val()
+      _storeProgress( key, value )

-        _storeProgress( key, value )
+    _settings.container.on 'submit', (event) ->
+      _eraseProgress($(@))

   # -------------------------------------
   #   Store Progress


Recalculate top positions incase they have changed

--- components/sticky/index.coffee
+++ components/sticky/index.coffee
@@ -6,12 +6,11 @@
     @$window = $(window)
     @$document = $(document)

+    @$window.resize _.debounce(_.bind(@rebuild, this), 250)
+    @$document.on 'ajaxStop', => _.defer => @rebuild()
+
     return if Stickyfill?
     require './vendor/stickyfill.js'
-
-    # rebuild the sticky menu when ajax events stop firing
-    # defer to wait after render
-    @$document.on 'ajaxStop', => _.defer => @rebuild()

   add: ($el) ->
     _.defer =>
@@ -24,7 +23,7 @@
     $el.attr 'style', "position: -webkit-sticky; position: sticky; top: #{@viewportTop()}px"

   headerHeight: ->
-    $('#main-layout-header').height()
+    (@$header ?= $('#main-layout-header')).height()

   visibleArea: ->
     @$window.height() - @headerHeight()
@@ -33,4 +32,11 @@
     if isTouchDevice() then 0 else @headerHeight()

   rebuild: ->
-    Stickyfill?.rebuild()
+    return unless Stickyfill?
+
+    top = @viewportTop()
+    Stickyfill.stickies.map (sticky) =>
+      sticky.css.top = top
+      $(sticky.node).css 'top', top
+
+    Stickyfill.rebuild()


Archive button no longer appears in sent folder

Summary: Archive button doesnt appear in sent folder

Test Plan: tested manually

Reviewers: evan, bengotow

Reviewed By: bengotow

Maniphest Tasks: T3428

Differential Revision: https://phab.nylas.com/D1987

--- internal_packages/thread-list/lib/thread-list-quick-actions.cjsx
+++ internal_packages/thread-list/lib/thread-list-quick-actions.cjsx
@@ -32,7 +32,7 @@
     newProps.thread.id isnt @props?.thread.id

   _shouldDisplayArchiveButton: =>
-    if @props.categoryId != CategoryStore.getStandardCategory('archive')?.id and @props.categoryId != CategoryStore.getStandardCategory('trash')?.id
+    if @props.categoryId not in [CategoryStore.getStandardCategory('archive')?.id, CategoryStore.getStandardCategory('trash')?.id, CategoryStore.getStandardCategory('sent')?.id]
       if AccountStore.current().usesLabels()
         if @props.thread.labels.length == 1 and (@props.thread.labels[0].name == "archive" or @props.thread.labels[0].name == "all")
           return false


Electron: Tray: mount: use fat-arrow to reach context

--- elektron/src/trayicon.coffee
+++ elektron/src/trayicon.coffee
@@ -107,7 +107,7 @@

-  handleMount: (machine) -> ->
+  handleMount: (machine) -> =>

     selectedDir  = electron.dialog.showOpenDialog
       properties : [ 'openDirectory' ]


ManagedProvider::create implementation

ManagedProvider::create implementation

--- workers/social/lib/social/models/computeproviders/managed.coffee
+++ workers/social/lib/social/models/computeproviders/managed.coffee
@@ -14,3 +14,30 @@
   @ping = (client, options, callback)->

     callback null, "Managed VMs rulez #{ client.r.account.profile.nickname }!"
+
+
+  @create = (client, options, callback)->
+
+    { label, queryString, ipAddress } = options
+    { r: { group, user, account } } = client
+
+    provider = 'managed'
+
+    { guessNextLabel, fetchUserPlan, fetchUsage } = require './computeutils'
+
+    guessNextLabel { user, group, label, provider }, (err, label)->
+      fetchUserPlan client, (err, userPlan)->
+        fetchUsage client, options, (err, usage)->
+
+          return callback err  if err?
+
+          meta =
+            type          : 'managed'
+            storage_size  : 0 # sky is the limit.
+            alwaysOn      : no
+
+          callback null, {
+            meta, label, credential: client.r.user.username
+            postCreateOptions: { queryString, ipAddress }
+          }
+


update beforeRender hook for 0.2.x

update beforeRender hook for 0.2.x

--- root/init.coffee
+++ root/init.coffee
@@ -19,7 +19,7 @@
 exports.before = (sprout, done) ->
   # before hook

-exports.before_render = (sprout, done) ->
+exports.beforeRender = (sprout, done) ->
   # before_render hook

 exports.after = (sprout, done) ->


Display client-side error messages in Browser.

--- community/browser/app/scripts/services/Frame.coffee
+++ community/browser/app/scripts/services/Frame.coffee
@@ -75,7 +75,7 @@
                 @isLoading = no
                 @hasErrors = yes
                 @response = null
-                @errorText = "Unknown error"
+                @errorText = result.message or "Unknown error"
                 if result.length > 0 and result[0].code
                   @errorText = result[0].code
                   @detailedErrorText = result[0].message if result[0].message


Add test for resize component

Add test for resize component


save identity in LocalStorage

save identity in LocalStorage

--- zechat/static/app.coffee
+++ zechat/static/app.coffee
@@ -89,12 +89,30 @@
     @ws.send(JSON.stringify(data))

+class zc.Persist extends Backbone.Marionette.Controller
+
+  initialize: ->
+    @key = @options.key
+    @model = @options.model
+    value = localStorage.getItem(@key)
+    if value
+      @model.set(JSON.parse(value))
+    @model.on('change', _.bind(@save, @))
+
+  save: ->
+    localStorage.setItem(@key, JSON.stringify(@model))
+
+
 zc.initialize = (options) ->
   app = zc.app = new Backbone.Marionette.Application

   app.identity = new Backbone.Model
     fingerprint: 'foo'
   app.message_col = new Backbone.Collection
+
+  app.persist_identity = new zc.Persist
+    key: 'identity'
+    model: app.identity

   zc.set_identity = (fingerprint) ->
     app.identity.set('fingerprint', fingerprint)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Workspace: In chat messages multiple links are supported now.

--- client/CollaborativeWorkspace/panes/chatpane.coffee
+++ client/CollaborativeWorkspace/panes/chatpane.coffee
@@ -106,12 +106,19 @@
     @lastChatItem.timeAgo.setData new Date timestamp

   checkEmbeddableContent: ->
-    element = @lastMessage.getElement()
-    content = element.innerHTML
-    isUrl   = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/.test content
+    element  = @lastMessage.getElement()
+    content  = element.innerHTML
+    words    = content.split " "
+    urlRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
+    hasUrl   = no

-    if isUrl
-      element.innerHTML = "#{content}"
+    for word, index in words
+      if urlRegex.test word
+        hasUrl = yes
+        words.splice index, 1, "#{word}"
+
+    if hasUrl
+      element.innerHTML = words.join " "
       element.classList.add "tw-chat-media"

   scrollToTop: ->


account: if credentials has rawContent in it dont show edit option

--- client/account/lib/accountcredentiallistitem.coffee
+++ client/account/lib/accountcredentiallistitem.coffee
@@ -32,7 +32,7 @@
     @addMenuItem 'Show', delegate.lazyBound 'showItemContent', this  if owner

     # Don't show the edit button for aws credentials in list. Gokmen'll on it.
-    @addMenuItem 'Edit', delegate.lazyBound 'editItem', this  unless provider is 'aws'
+    @addMenuItem 'Edit', delegate.lazyBound 'editItem', this  unless provider is 'aws' or @getData().fields?

     @addMenuItem 'Delete', delegate.lazyBound 'deleteItem', this


reactivity: embedbox: remove useless embedly store

--- client/activity/lib/flux/stores/embedlystore.coffee
+++ client/activity/lib/flux/stores/embedlystore.coffee
@@ -1,23 +0,0 @@
-KodingFluxStore = require 'app/flux/store'
-actions         = require '../actions/actiontypes'
-toImmutable     = require 'app/util/toImmutable'
-immutable       = require 'immutable'
-
-module.exports = class EmbedlyStore extends KodingFluxStore
-
-  @getterPath = 'EmbedlyStore'
-
-
-  getInitialState: -> immutable.Map()
-
-
-  initialize: ->
-
-    @on actions.LOAD_EMBEDLY_URL_SUCCESS, @handleLoadSuccess
-
-
-  handleLoadSuccess: (currentState, { data }) ->
-
-    data = data[0]
-    currentState.set data.url, data
-


payment: PaymentController#fetchSubscriptionsWithPlans

--- client/app/MainApp/payment/paymentcontroller.coffee
+++ client/app/MainApp/payment/paymentcontroller.coffee
@@ -1,24 +1,12 @@
 class PaymentController extends KDController
-
-  getGroup = ->
-    KD.getSingleton('groupsController').getCurrentGroup()
-
-  getBalance: (type, callback)->
-
-    { JPaymentPlan } = KD.remote.api
-
-    if type is 'user'
-      JPaymentPlan.getUserBalance callback
-    else
-      JPaymentPlan.getGroupBalance callback

   fetchPaymentMethods: (callback) ->

     { dash } = Bongo

-    methods       = null
+    methods = null
     preferredPaymentMethod = null
-    appStorage    = new AppStorage 'Account', '1.0'
+    appStorage = new AppStorage 'Account', '1.0'
     queue = [

       -> appStorage.fetchStorage (err) ->
@@ -62,14 +50,11 @@
       { JPaymentSubscription } = KD.remote.api

       if type is 'group'
-        getGroup().checkPayment (err, subs) =>
+        KD.getGroup().checkPayment (err, subs) =>
           findActiveSubscription subs, planCode, callback
       else
         JPaymentSubscription.fetchUserSubscriptions (err, subs) ->
           findActiveSubscription subs, planCode, callback
-
-  deleteVM: (vmInfo, callback) -> debugger
-  # views

   fetchPlanByCode: (planCode, callback) ->

@@ -83,7 +68,7 @@

     switch type
       when 'group', 'expensed'
-        getGroup().fetchPaymentInfo callback
+        KD.getGroup().fetchPaymentInfo callback
       when 'user'
         JPaymentPlan.fetchAccountDetails callback

@@ -117,6 +102,24 @@

     return form

+  debitSubscription: ->
+    subscription.debit pack, (err, nonce) =>
+      return  if KD.showError err
+
+      @emit 'SubscriptionDebited', subscription
+
+      callback null, nonce
+
+  fetchSubscriptionsWithPlans: (tags, callback) ->
+    [callback, tags] = [tags, callback]  unless callback
+
+    KD.whoami().fetchPlansAndSubscriptions tags, (err, plansAndSubs) =>
+      return callback err  if err
+
+      { subscriptions } = @groupPlansBySubscription plansAndSubs
+
+      callback null, subscriptions
+
   groupPlansBySubscription: (plansAndSubscriptions = {}) ->

     { plans, subscriptions } = plansAndSubscriptions
@@ -130,4 +133,3 @@
       subscription.plan = plansByCode[subscription.planCode]

     { plans, subscriptions }
-


Remove onboarding view once a participant is added

--- client/ide/lib/views/chat/idechatmessagepane.coffee
+++ client/ide/lib/views/chat/idechatmessagepane.coffee
@@ -152,6 +152,8 @@

Start your collaboration session by adding someone.

       """

+    channel.once 'AddedToChannel', @bound 'removeOnboarding'
+

   handleOnboardingViewClick: (e) ->

@@ -243,7 +245,7 @@

   participantAdded: (participant) ->

-    @onboarding?.destroy()
+    @removeOnboarding()

     appManager = kd.getSingleton 'appManager'
     appManager.tell 'IDE', 'setMachineUser', [participant.profile.nickname]
@@ -263,3 +265,9 @@
     item.checkIfItsTooTall()  for item in @listController.getListItems()
     @scrollView.wrapper.emit 'MutationHappened'
     @scrollDown()
+
+
+  removeOnboarding: ->
+
+    @onboarding?.destroy()
+    delete @onboarding


Store is generic

Store is generic


Display snapped player's names

Display snapped player's names

fixed the [object Object] nonsense being printed

--- src/Game.coffee
+++ src/Game.coffee
@@ -53,10 +53,10 @@
       $('#info').fadeOut 200, =>
           $('#info').html(' Game has started!  Go go go! Topic is '+ @topic+'.').fadeIn(300)
       for player in data.players
-        @players[player] = new Player(@viz.two, player)
+        @players[player.uuid] = new Player(@viz.two, player.name)

     @socket.on 'game over', (data) ->
-      winners = data.winners.join(', ')
+      winners = data.winners.map((p) -> p.name).join(', ')

       $('#info').fadeOut 200, =>
           $('#info').html(' Game Over!  Congratulations to winners: '+ "<marquee>#{winners}</marquee>").fadeIn(300)
@@ -64,16 +64,16 @@

     @socket.on 'snap', (data) =>
       @player.addPoints data.d_score
-      snappedPlayers = data.player.filter (name) => name isnt @player.name
+      snappedPlayers = data.player.filter (p) => p.name isnt @player.name
       for player in snappedPlayers
-        @player.snap(@players[player])
+        @player.snap(@players[player.uuid])
+      playerNames = snappedPlayers.map (p) -> p.name

       # Play a random snap sound
       sound = "#snap" + Math.floor(Math.random() * 12)
       $(sound).trigger("play")

-      $('#wordList').html()
-      $('#wordList').append(''+data.word+' ('+snappedPlayers+')
')
+      $('#wordList').append(" #{data.word} (#{playerNames.join(', ')})
")
       $('#score').html(@player.score)

     @socket.on 'disconnect', () =>


added post button

added post button

--- core/app/backbone/views/react_feed_selection.coffee
+++ core/app/backbone/views/react_feed_selection.coffee
@@ -45,6 +45,8 @@
                 id: 'challenge-description'
                 ref: 'textarea'
                 placeholder: 'Describe the challenge'
+              _button ["button-confirm"],
+                "post"

         ]


don't show remove button on channels of other people

--- core/app/backbone/views/subchannel_item_view.coffee
+++ core/app/backbone/views/subchannel_item_view.coffee
@@ -13,6 +13,9 @@
     e.stopPropagation()
     false

+  onRender: ->
+    @$('i.close').hide() unless currentChannel.user().get('id') == currentUser.get('id')
+
   clickHandler: (e) ->
     return  if e.metaKey or e.ctrlKey or e.altKey
     mp_track "Channel: Click on subchannel",


introducing new raw, command escaping

introducing new raw, command escaping

--- src/MKTS.coffee
+++ src/MKTS.coffee
@@ -390,6 +390,7 @@
       else
         # debug '@2', pattern, no
         self._leave state
+        ### TAINT shouldn't throw error but issue warning remark ###
         throw new Error "too many right fences: #{rpr event}" if state[ 'count' ] < 0
     return event
   #.........................................................................................................
@@ -656,7 +657,7 @@
             text = rpr text
       else
         text = ''
-      if type in [ '.', '!', ] or type in MKTS.FENCES.xleft
+      if type in [ '.', '!', ] or type in @FENCES.xleft
         first             = type
         last              = name
         pre               = '█'
@@ -667,7 +668,7 @@
         pre               = ''
         post              = '█'
       event_txt         = first + last + ' ' + text
-      event_tex         = MKTS.fix_typography_for_tex event_txt, @options
+      event_tex         = @fix_typography_for_tex event_txt, S.options
       ### TAINT use mkts command ###
       send [ 'tex', """{\\mktsStyleBold\\color{violet}{%
         \\mktsStyleSymbol#{pre}}#{event_tex}{\\mktsStyleSymbol#{post}}}""" ]


introducing new raw, command escaping

introducing new raw, command escaping


synctable: it never actually tried to reconnect again

--- src/smc-util/synctable.coffee
+++ src/smc-util/synctable.coffee
@@ -280,9 +280,6 @@
             return
         if @_state == 'connected'
             dbg("already connected")
-            return
-        if @_state == 'reconnecting'
-            dbg("_reconnecting right now already")
             return
         if not @_anonymous and not @_client.is_signed_in()
             dbg("waiting for sign in before connecting")


add error reporting

add error reporting

--- content/pages/_components/ide/compiler/index.coffee
+++ content/pages/_components/ide/compiler/index.coffee
@@ -10,7 +10,9 @@

 exports.compile = (files, complete) ->
   addLocalDeps files
-  addRemoteFiles files, () ->
+  addRemoteFiles files, (err) ->
+    if err
+      return complete err
     try
       complete null, compile files
     catch e
@@ -46,9 +48,13 @@

   superagent.post("http://wzrd.in/multi").send(pkg).end (err, response) ->

-    console.log err
-    console.log response
+    if err
+      return complete err
+
+    unless response.body
+      return complete new Error response.text

+
     for moduleName of response.body
       files.push {
         path: if remoteDeps[moduleName] is "*" then moduleName else moduleName + "@" + remoteDeps[moduleName],


Simplify.

Simplify.

--- core/app/backbone/factlink/cross_fade_region.coffee
+++ core/app/backbone/factlink/cross_fade_region.coffee
@@ -3,8 +3,6 @@
 class Backbone.Factlink.CrossFadeRegion extends Backbone.Marionette.Region

   defaultFadeTime = 560
-
-  initialize: -> @on 'close', -> @$el?.stop()

   crossFade: (newView) ->
     if @currentView
@@ -12,11 +10,10 @@
     else
       @show(newView)

-  open: (view) ->
-    @$el.stop().hide().html(view.el)
-        .fadeIn(@_fadeTime())
+  open: (view) -> @$el.stop().hide().html(view.el).fadeIn(@_fadeTime())

   resetFade: -> @$el?.stop().fadeOut(@_fadeTime(), => @reset())

   _fadeTime: -> @options?.fadeTime || defaultFadeTime

+  onClose: -> @$el?.stop()


Extracted "fadeOut" method in CrossFadeRegion

--- core/app/backbone/factlink/cross_fade_region.coffee
+++ core/app/backbone/factlink/cross_fade_region.coffee
@@ -2,15 +2,17 @@

 class Backbone.Factlink.CrossFadeRegion extends Backbone.Marionette.Region

+  fadeOut: (callback) -> @$el?.stop().fadeOut(@_fadeTime())
+
   crossFade: (newView) ->
     if @currentView
-      @$el.stop().fadeOut(@_fadeTime(), => @show newView)
+      @fadeOut => @show newView
     else
       @show(newView)

   open: (view) -> @$el.stop().hide().html(view.el).fadeIn(@_fadeTime())

-  resetFade: -> @$el?.stop().fadeOut(@_fadeTime(), => @reset())
+  resetFade: -> @fadeOut => @reset()

   _fadeTime: ->
     if @options?.fadeTime?


Fix CurrentSelectionAddress goof

Fix CurrentSelectionAddress goof

--- src/atom/command-interpreter/current-selection-address.coffee
+++ src/atom/command-interpreter/current-selection-address.coffee
@@ -4,6 +4,4 @@
 module.exports =
 class CurrentSelectionAddress extends Address
   getRange: (editor) ->
-    lastRow = editor.getLastBufferRow()
-    column = editor.getBufferLineLength(lastRow)
-    new Range([lastRow, column], [lastRow, column])+    editor.getSelection().getBufferRange()


Update command regex, closes #15

Update command regex, closes #15

--- src/aotw.coffee
+++ src/aotw.coffee
@@ -181,8 +181,8 @@
     robot.hear /^\s*aotw\s*$/i, (msg) ->
         msg.send "Invalid command, say \"aotw help\" for help"

-    robot.hear /^\s*aotw(.*) (.*)/i, (msg) ->
-        cmd = msg.match[0].split(" ")[1]
+    robot.hear /^\s*aotw (.*)/i, (msg) ->
+        cmd = msg.match[1]
         switch cmd
             when "current" then checkMessage msg, aotw.printCurrentAotw
             when "help" then aotw.printHelp msg


Route wird sofort gesucht, sobald Quelle und Ziel gewählt oder verändert wurden.

--- src/main/coffee/script.coffee
+++ src/main/coffee/script.coffee
@@ -1,5 +1,8 @@
 require(["jquery", "styles", "openlayers/OpenLayers", "underscore"], ($, styles) ->

+  # globales AntScout-Object erstellen
+  @AntScout = {}
+  destination = null
   directionsLayer = new OpenLayers.Layer.Vector(
     "Directions"
     {
@@ -65,6 +68,7 @@
         retrieveWays()
   })
   selectFeatureControl = new OpenLayers.Control.SelectFeature([nodesLayer, waysLayer]);
+  source = null

   $(() ->
     map = new OpenLayers.Map "map"
@@ -152,8 +156,15 @@
     )
     directionsLayer.addFeatures([sourceFeature, targetFeature])

+  AntScout.updatePath = (path) ->
+    console.debug("Updating path")
+    drawDirections
+
   enable = (elementId) ->
     $("##{ elementId }").prop("disabled", false)
+
+  selectedNodeId = () ->
+    nodesLayer.selectedFeatures.length > 0 and nodesLayer.selectedFeatures[0].attributes.node.id

   retrieveNode = (id) ->
     $.get "node/#{ id }",
@@ -180,12 +191,10 @@
         nodesLayer.addFeatures(features)
         map.zoomToExtent(nodesLayer.getDataExtent())

-  retrieveDirections = () ->
-    destination = $("#destination").val()
-    source = $("#source").val()
+  retrieveDirections = (source, destination) ->
     $.get "/directions", {
+      source
       destination
-      source
       },
       (directions) ->
         displayDirections(directions)
@@ -197,15 +206,13 @@
         addWaysToLayer(ways, waysLayer)
         map.zoomToExtent(waysLayer.getDataExtent())

-  setNode = (input) ->
-    id = nodesLayer.selectedFeatures.length > 0 and nodesLayer.selectedFeatures[0].attributes.node.id
-    input.val(id) if id?
+  setNodeAsDestination = () ->
+    destination = selectedNodeId()
+    retrieveDirections(source, destination) if source? and destination?

   setNodeAsSource = () ->
-    setNode($("#source"))
-
-  setNodeAsDestination = () ->
-    setNode($("#destination"))
+    source = selectedNodeId()
+    retrieveDirections(source, destination) if source? and destination?

   toggleDisabledProperty = (elementId) ->
     if $("##{ elementId }").prop("disabled") is true


Remove reference to workspaceView in Atom::unloadEditorWindow

--- src/atom.coffee
+++ src/atom.coffee
@@ -238,6 +238,7 @@
     @state.set('packageStates', @packages.packageStates)
     @saveSync()
     @workspaceView.remove()
+    @workspaceView = null
     @project.destroy()
     @windowEventHandler?.unsubscribe()
     @windowState = null


Activity: fixed an error while glancing a channel by putting a guard to make sure we have the selected item of the open pane.

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -103,7 +103,7 @@
     app  = appManager.get 'Activity'
     item = app.getView().sidebar.selectedItem

-    return  unless item.count
+    return  unless item?.count
     # no need to send updatelastSeenTime or glance
     # when checking publicfeeds
     return  if typeConstant is 'group'


reactivity: remove incorrectly merged code

--- client/activity/lib/flux/actions/channel.coffee
+++ client/activity/lib/flux/actions/channel.coffee
@@ -390,16 +390,6 @@

     for userId in userIds
       dispatch ADD_PARTICIPANTS_TO_CHANNEL_SUCCESS, { channelId, userId }
-
-
-addParticipantsByNames = (channelId, names) ->
-
-  users = kd.singletons.reactor.evaluateToJS getters.allUsers
-  participants = (user for userId, user of users when names.indexOf(user.profile.nickname) > -1)
-  accountIds   = (user.socialApiId for user in participants)
-  userIds      = (user._id for user in participants)
-
-  addParticipants channelId, accountIds, userIds

 addParticipantsByNames = (channelId, names) ->


Add city to search

Add city to search

--- client/apps/edit/components/layout/index.coffee
+++ client/apps/edit/components/layout/index.coffee
@@ -146,7 +146,7 @@
     $('#autolink-status').addClass('searching').html('Linking...')
     $('#edit-content__overlay').addClass('disabled')
     linkableText = @getLinkableText()
-    searchTypes = ['artist', 'profile', 'show', 'gene']
+    searchTypes = ['artist', 'profile', 'show', 'gene', 'city']
     searchQueryParam = searchTypes.map((t) -> "type[]=#{t}").join("&")
     async.mapLimit linkableText, 5, ((findText, cb) =>
       text = findText.split('==').join('')
@@ -159,9 +159,10 @@
             return cb()
           result = res.body._embedded.results[0]
           name = result.title
-          if levenshtein.get(name, text) > 2
+          if levenshtein.get(name, text, { useCollator: true}) > 2
             # result string distance was more than threshold, rejecting the match
-            return @article.replaceLink(findText, text)
+            @article.replaceLink(findText, text)
+            return cb()
           link = @findLinkFromResult(result)
           newLink = @getNewLink(link, name)
           @article.replaceLink(findText, newLink)


if you want something done well, either big or small...

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -12,6 +12,34 @@

 class Zotero.BetterBibTeX.DateParser
   parseDateToObject: (date, options) -> (new Zotero.BetterBibTeX.DateParser(date, options)).date
+  parseDateToArray: (date, options) -> (new Zotero.BetterBibTeX.DateParser(date, options)).array()
+
+  toArray: (suffix = '') ->
+    date = {}
+    for d in ['year', 'month', 'day', 'empty']
+      date[d] = @date["#{d}#{suffix}"]
+
+    Zotero.BetterBibTeX.debug('CSL: arraydate', {parsed: @, date, suffix})
+
+    return [ 0, 0 ] if date.empty
+
+    return null unless date.year
+
+    arr = [ date.year ]
+    if date.month
+      arr.push(date.month)
+      arr.push(date.day) if date.day
+    return arr
+
+  array: ->
+    date1 = @toArray()
+    return null unless date1
+
+    date2 = @toArray('_end')
+
+    return {'date-parts': [date1, date2]} if date2
+
+    return {'date-parts': date1 }

   constructor: (date, options = {}) ->
     @zoteroLocale ?= Zotero.locale.toLowerCase()
@@ -703,15 +731,7 @@
         Zotero.BetterBibTeX.CSL.parseParticles(name)
     }
     parseDateToObject: (sandbox, date, locale) -> Zotero.BetterBibTeX.DateParser::parseDateToObject(date, {locale, verbatimDetection: true})
-    parseDateToArray: (sandbox, date, locale) ->
-      parsed = Zotero.BetterBibTeX.DateParser::parseDateToObject(date, {locale, verbatimDetection: true})
-      Zotero.BetterBibTeX.JurisMDateParser.convertDateObjectToArray(parsed)
-      if Array.isArray(parsed['date-parts'])
-        if Array.isArray(parsed['date-parts'][0])
-          parsed['date-parts'] = (((if isNaN(parseInt(d)) then d else parseInt(d)) for d in datepart) for datepart in parsed['date-parts'])
-        else
-          parsed['date-parts'] = ((if isNaN(parseInt(d)) then d else parseInt(d)) for d in parsed['date-parts'])
-      return parsed
+    parseDateToArray: (sandbox, date, locale) -> Zotero.BetterBibTeX.DateParser::parseDateToArray(date, {locale, verbatimDetection: true})
   }

   for own name, endpoint of @endpoints


tests(install/invokeInstallExecutable): wrap `execFileSync` into a spy

Wrap `fakeChildProcess.execFileSync` into `sinon.spy` in every test so that
the `.reset()` call in `beforeEach` doesn't break.

--- test/install/invokeInstallExecutable.spec.coffee
+++ test/install/invokeInstallExecutable.spec.coffee
@@ -36,7 +36,7 @@
 		fakeChildProcess.execFileSync.should.have.been.calledOnce

 	it "returns an Error when 'execFileSync' throws", ->
-		fakeChildProcess.execFileSync = ( file, args, opts ) ->
+		fakeChildProcess.execFileSync = sinon.spy ( file, args, opts ) ->
 			throw new Error "Something went wrong!"
 		result = invokeInstallExecutable "fake.jar", "fake-mod"
 		result.should.be.an.instanceof Error


update sage connection coffeescript test suite since now message has type

--- salvus/tests/test_sage.coffee
+++ salvus/tests/test_sage.coffee
@@ -1,5 +1,5 @@
 ####################################################
-#
+#
 # Test NodeJS TCP interface to a sage_server
 #
 # ASSUMPTION: there is a TCP sage server on localhost, port 6000
@@ -14,13 +14,14 @@
 message = require("message")

 exports.test_2plus2 = (test) ->
-    test.expect(7)
+    test.expect(10)

     pid = null
     conn = new sage.Connection
         host: HOST
         port: PORT
-        recv: (mesg) ->
+        recv: (type, mesg) ->
+            test.equal(type, 'json')
             switch mesg.event
                 when "session_description"
                     test.ok(mesg.pid?, "got back a pid")
@@ -39,4 +40,4 @@
         cb: ->
             test.ok(true)  # connected
             conn.send(message.start_session(limits:{walltime:10}))
-
+


fixes dropdown hover bug

fixes dropdown hover bug

--- components/dropdown/client/dropdown_view.coffee
+++ components/dropdown/client/dropdown_view.coffee
@@ -31,7 +31,7 @@
     @$el.addClass 'dropdown--is_active'

   closeDropdown: (e) =>
-    @$el.removeClass 'dropdown--is_active' unless @$el.is(':hover')
+    @$el.removeClass 'dropdown--is_active' unless @$el.filter( -> $(this).is(":hover")).length

   toggleDropdown: =>
     @$el.toggleClass 'dropdown--is_active'


Fix reference

Fix reference

--- components/email/client/gallery_insights.coffee
+++ components/email/client/gallery_insights.coffee
@@ -19,7 +19,7 @@
     @renderCTA => @setupCTAWaypoints()

   inGIArticlePage: ->
-    sd.GALLERY_INSIGHTS_CHANNEL is sd.ARTICLE?.get('channel_id')
+    sd.GALLERY_INSIGHTS_CHANNEL is sd.ARTICLE?.channel_id

   inGIVerticalPage: ->
     sd.SECTION?.id is sd.GALLERY_INSIGHTS_SECTION_ID


Wiring things together

Wiring things together

--- src/dfhm.coffee
+++ src/dfhm.coffee
@@ -24,6 +24,7 @@

     dummyImage.src = event.target.result

+# We've picked a file - load that badboy up
 onFileSelected = (event) ->
     if event.target.files[0]
         file = event.target.files[0]
@@ -64,7 +65,7 @@
 init = ->
     if window.File and window.FileReader
         #Set up the listeners for the different groups
-        #$('#test-loader').change onFileSelected
+        $('#image-loader').change onFileSelected
         #canvas = $('#output')[0]
         $('#dropdown-height, #dropdown-width').children('.dropdown-menu').find('a').click onDimensionChange

@@ -73,6 +74,7 @@
             setActiveField $(event.target).parent('li')

     else
+        # TODO - provide sensible errors
         alert "noes"

 $(document).ready init


Cleanup click dict

Cleanup click dict

--- src/draw.coffee
+++ src/draw.coffee
@@ -8,7 +8,7 @@
     assert !world.has_children()
     #console.log "draw_path #{world}"
     dict = {
-      class: ['draw_path', "#{world}"]
+      klass: ['draw_path', "#{world}"]
       stroke: world.get 'stroke'
       fill: world.get 'fill'
     }
@@ -31,12 +31,12 @@
   draw_world = (world) ->
     #console.log "draw_world #{world}"
     paths = draw_children(world) or draw_path(world)
+    clicker = world.get_raw 'click'
     dict = {
       klass: ['draw_world', "#{world}"] # TODO: use 'class' properly
       transform: world.get 'transform'
+      click: -> clicker(world) if clicker?
     }
-    clicker = world.get_raw 'click'
-    dict['click'] = -> clicker(world) if clicker?# and !world.has_children()
     SVG.g dict, paths

   #console.log world


Add additional debug logging

Add additional debug logging

--- scripts/jira-linkifier.coffee
+++ scripts/jira-linkifier.coffee
@@ -23,11 +23,13 @@

 module.exports = (robot) ->
   robot.respond /watch jira project ?(.+)?/i, (msg) ->
+    console.log("Recieved request to watch project")
     shortcode = msg.match[1]

     projects = robot.brain.get('jira-projects') or []
     projects.push shortcode
     robot.brain.set 'jira-projects', projects
+    console.log("Added project to memory")

     robot.hear new Regexp(shortcode + "-([0-9]*)", "i"), (mention) ->
       console.log("Starting to check on " + shortcode + - mention.match[1])
@@ -42,6 +44,7 @@

       # mention.send("Issue at: https://jira.brandnetworksinc.com/browse/" + shortcode + "-" + mention.match[1])

+    console.log("Added watch for project")
     msg.send "Watching that project for you"

   robot.respond /stop watching ?(.+)?/i, (msg) ->


:lipstick: Remove extra newline

:lipstick: Remove extra newline

--- src/list.coffee
+++ src/list.coffee
@@ -74,7 +74,6 @@
       console.log "#{@devPackagesDirectory.cyan} (#{devPackages.length})"
       @logPackages(devPackages)

-
   listBundledPackages: (options) ->
     nodeModulesDirectory = path.join(config.getResourcePath(), 'node_modules')
     packages = @listPackages(nodeModulesDirectory, options)


fix moddle filename error reporting

fix moddle filename error reporting

--- src/load.coffee
+++ src/load.coffee
@@ -21,6 +21,6 @@
     m.require absrequest
   catch e
     if e instanceof SyntaxError and e.location
-      location = path.relative process.cwd(), absrequest
+      location = path.relative process.cwd(), e.filename
       throw new Error "#{location}:#{e.location.first_line}:#{e.location.first_column} #{e.message}"
     else throw e


move doFirstConnection below its definition

--- src/main.coffee
+++ src/main.coffee
@@ -70,7 +70,6 @@
 #        unless proxyURL is 'DIRECT'
 #            process.env.HTTP_PROXY ?= "http://#{proxyURL.split(' ')[1]}"
 #        doFirstConnection() if proxiesReturned is 2
-    doFirstConnection()
     # The above should be uncommented as soon as we upgrade to a version of Electron that incorporates https://github.com/atom/electron/pull/2054

     # Create the browser window.
@@ -79,7 +78,7 @@
         height: 590
         "min-width": 620
         "min-height": 420
-        icon: path.join __dirname 'icons' 'icon.png'
+        icon: path.join __dirname, 'icons', 'icon.png'
     }

     # and load the index.html of the app. this may however be yanked
@@ -121,6 +120,8 @@
             else
                 syncrecent()
             reconnectCount++
+
+    doFirstConnection()

     # client deals with window sizing
     mainWindow.on 'resize', (ev) -> ipcsend 'resize', mainWindow.getSize()


always delete chrome cookie on start.

always delete chrome cookie on start.

This closes #2

--- src/main.coffee
+++ src/main.coffee
@@ -23,15 +23,13 @@
     rtokenpath:  paths.rtokenpath
     cookiespath: paths.cookiespath

+if fs.existsSync paths.chromecookie
+    fs.unlinkSync paths.chromecookie
+
 plug = (rs, rj) -> (err, val) -> if err then rj(err) else rs(val)

 logout = ->
     promise = client.logout()
-    rm = (path) -> Q.Promise((rs, rj) -> fs.unlink(path, plug(rs, rj))).fail (err) ->
-        if err.code == 'ENOENT' then null else Q.reject(err)
-    promise = promise.then ->
-        rm paths.chromecookie
-    promise.fail (e) -> console.log e
     promise.then (res) ->
       argv = process.argv
       spawn = require('child_process').spawn


make options.spawn deprecated

make options.spawn deprecated

--- src/main.coffee
+++ src/main.coffee
@@ -27,7 +27,6 @@
     @param {Object|String} [config] config object or config directory containing config info
     @param {Boolean} [options.reset] reset previously-generated settings before generation
     @param {String} [options.env] set environment (production|development|...)
-    @param {Boolean} [options.spawn] if true, spawns child process of loopback
     return {Promise(LoopbackInfo)}
     ###
     @run: (loopbackDefinitions, config, options = {}) ->
@@ -94,6 +93,7 @@
     @launchLoopback: (spawnChildProcess) ->

         if spawnChildProcess
+            console.error('`options.spawn` is deprecated. It cannot set admin access token.')
             new LoopbackProcessLauncher().launch()
         else
             new LoopbackServer().launch()


fix ending push source with `send()` using symbol


Add create TODO

Add create TODO

--- src/main.coffee
+++ src/main.coffee
@@ -17,6 +17,8 @@
 assert sprites = world.find_child('sprites'), "can not find sprites"
 current = turtles.find_child()
 world.send('inspect', current)
+
+# TODO: use a 'create' event to automatically make sprites
 turtles.map_children (turtle) ->
   sprite = sprites.call('NewFromTurtle', turtle)
   layout.add_child sprite


main: Put checking loop into a Governor class

--- src/main.coffee
+++ src/main.coffee
@@ -1,8 +1,58 @@
+
+debug = require('debug')('guv:main')

 heroku = require './heroku'
 rabbitmq = require './rabbitmq'
 scale = require './scale'
 config = require './config'
+
+checkAndScale = (cfg, role, callback) ->
+  rabbitmq.getStats cfg, (err, queues) ->
+    role.current_jobs = null
+    return callback err if err
+    console.log err, queues
+
+    role.current_jobs = queues[role.amqp_queue]
+    return callback new Error "Could not get data for queue: #{role.amqp_queue}" if not role.current_jobs?
+    workers = scale.scale cfg, role.current_jobs
+
+    heroku.setWorkers cfg, role.heroku_dyno, workers, (err) ->
+      role.current_workers = null
+      return callback err if err
+
+      state = {}
+      role.workers = workers
+      state[role.id] = role
+      return callback null, state
+
+
+class Governor
+  constructor: (c) ->
+    @config = config.defaults c
+
+    @interval = null
+
+  start: () ->
+    runFunc = () =>
+
+    seconds = 60*1000
+    interval = runInterval runFunc, 30*seconds
+    runFunc() # do first iteration right now
+
+  stop: () ->
+    clearInterval @interval if @interval
+
+  runOnce: (callback) ->
+
+    # FIXME: unhardcode, add support for multiple processes/queues
+    role =
+      id: 'processing'
+      amqp_queue: 'worker.JOB'
+      heroku_dyno: 'processing'
+      current_workers: null
+      current_jobs: null
+    checkAndScale @config, role, callback
+

 exports.main = () ->
   cfg =
@@ -11,21 +61,9 @@
     worker_maximum: 1
     heroku_app: 'imgflo'

-  cfg = config.defaults cfg
+  heroku.dryrun = true
+  guv = new Governor cfg
+  guv.runOnce (err, state) ->
+    throw err if err
+    console.log state

-  rabbitmq.getStats cfg, (err, queues) ->
-    throw err if err
-    console.log queues
-
-    # TODO: should be declared in config
-    jobs = queues['fbp']
-    workers = scale.scale cfg, jobs
-
-    console.log 'target', cfg.target
-    console.log 'jobs', jobs
-    console.log 'workers', workers
-
-    heroku.dryrun = true
-    # TODO: should be declared in config
-    heroku.setWorkers cfg, 'processing', workers, (err) ->
-      console.log 'heroku.setWorkers', err


Tweaks

Tweaks

--- src/main.coffee
+++ src/main.coffee
@@ -32,8 +32,8 @@
     angle: 0
     size: 10
     mass: 11
-    speed: 100
-    angularSpeed: 150
+    speed: 120
+    angularSpeed: 160
     minX: 0
     maxX: 385
     minY: 0
@@ -166,7 +166,7 @@
     toToToEnemy -= delta

     if toEnemy <= 0
-        enemy = spawnEnemy[Math.floor(random(0, 4))](player.size - 5, player.size + 5)
+        enemy = spawnEnemy[Math.floor(random(0, 4))](player.size * 0.8, player.size * 1.2)
         enemies.push(enemy)

         toEnemy = toToEnemy


activity: send public channel socialApiChannelId while sending new posts on topic channel

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -97,6 +97,11 @@
     {channel}    = @getOptions()
     {body}       = options

+    # when somebody posts on a topic channel, for creating it both in public and topic channels
+    # channelId must be set to current group's socialApiChannelId
+    if channel.typeConstant is 'topic'
+      options.channelId = KD.currentGroup.socialApiChannelId
+
     if channel.typeConstant is 'topic' and not body.match ///\##{channel.name}///
       body += " ##{channel.name} "


Prevent loading of conversation if the message is single

--- client/app/actions/layout_action_creator.coffee
+++ client/app/actions/layout_action_creator.coffee
@@ -189,18 +189,21 @@
         message        = MessageStore.getByID messageID
         if message?
             onMessage message
-        updated = Date.now()
-        XHRUtils.fetchConversation conversationID, (err, rawMessages) ->
-
-            if err?
-                LayoutActionCreator.alertError err
-            else
-                # This prevent to override local updates with older ones from
-                # server
-                rawMessages.forEach (msg) ->
-                    msg.updated = updated
-                MessageActionCreator.receiveRawMessages rawMessages
-                onMessage rawMessages[0]
+
+        unless MessageStore.getConversationsLength().get(conversationID) is 1
+
+            updated = Date.now()
+            XHRUtils.fetchConversation conversationID, (err, rawMessages) ->
+
+                if err?
+                    LayoutActionCreator.alertError err
+                else
+                    # This prevent to override local updates with older ones
+                    # from server
+                    rawMessages.forEach (msg) ->
+                        msg.updated = updated
+                    MessageActionCreator.receiveRawMessages rawMessages
+                    onMessage rawMessages[0]

     showComposeNewMessage: (panelInfo, direction) ->
         # if there isn't a selected account (page loaded directly),


payment: expose a dummy method to payment controller

--- client/app/lib/payment/paymentcontroller.coffee
+++ client/app/lib/payment/paymentcontroller.coffee
@@ -1,6 +1,7 @@
 kd      = require 'kd'
 remote  = require('../remote').getInstance()
 globals = require 'globals'
+whoami = require 'app/util/whoami'

 module.exports = class PaymentController extends kd.Controller
@@ -34,6 +35,19 @@
     @api().subscribeGroup params, (err, plan) =>
       callback err, plan
       @emit 'GroupPlanUpdated'  unless err
+
+
+  # waiting for the team free plan to be exposed, because free_month is for
+  # only accounts. ~Umut
+  removeGroupPlan: (callback) ->
+
+    whoami().fetchEmail (err, email) =>
+      params =
+        token: 'a'
+        planTitle: 'free'
+        email: email
+
+      @api().subscribeGroup params, callback

   subscriptions: (callback) ->


managed: Added missing new keyword

managed: Added missing new keyword

--- client/app/lib/providers/managed/helpers.coffee
+++ client/app/lib/providers/managed/helpers.coffee
@@ -130,7 +130,7 @@
         # Route to the IDE
         kd.utils.defer -> router.handleRoute "/IDE/#{jMachine.slug}"

-        machine = Machine { machine: jMachine }
+        machine = new Machine { machine: jMachine }

         # Get the klient kite, and the info from that klient so we
         # can popup a Provider specific modal


Added VMListPane to IDEFilesTabView.

Added VMListPane to IDEFilesTabView.

--- client/ide/views/tabview/idefilestabview.coffee
+++ client/ide/views/tabview/idefilestabview.coffee
@@ -15,6 +15,15 @@
     filesPane.addSubView new FinderPane
     @tabView.addPane filesPane

+    vmsPane    = new KDTabPaneView
+      name     : 'VMs'
+      closable : no
+
+    vmsPane.addSubView new VMListPane
+    @tabView.addPane  vmsPane
+
+
+    ################################
     actionsPane  = new KDTabPaneView
       name     : 'Actions'
       closable : no


fixed creation of group specific tags even if there is a non-group tag. also needed to manually remove the index slug_1 on jTags collection (needs to be done when going prod)

--- workers/social/lib/social/models/tag.coffee
+++ workers/social/lib/social/models/tag.coffee
@@ -142,7 +142,9 @@
           fin =(i)-> if i is tagRefs.length-1 then queue.next()
           tagRefs.forEach (tagRef, i)->
             if tagRef?.$suggest?
+              {group} = client.context
               newTag = {title: tagRef.$suggest.trim()}
+              newTag.group = group if group isnt 'koding'
               JTag.one newTag, (err, tag)->
                 if err
                   callbackForEach err