:racehorse: Allocate fewer Points in MarkerIndex::dump()

--- src/marker-index.coffee
+++ src/marker-index.coffee
@@ -126,11 +126,9 @@
     end

   dump: (offset, snapshot) ->
-    childEnd = offset
-    for child in @children
-      childStart = childEnd
-      childEnd = childStart.traverse(child.extent)
-      child.dump(childStart, snapshot)
+    for child in @children
+      offset = child.dump(offset, snapshot)
+    offset

   findContaining: (point, set) ->
     childEnd = Point.zero()
@@ -140,6 +138,7 @@
       continue if childEnd.compare(point) < 0
       break if childStart.compare(point) > 0
       child.findContaining(point.traversalFrom(childStart), set)
+    return

   findIntersecting: (start, end, set) ->
     if start.isZero() and end.compare(@extent) is 0
@@ -157,6 +156,7 @@
         Point.min(child.extent, end.traversalFrom(childStart)),
         set
       )
+    return

   hasEmptyRightmostLeaf: ->
     @children[@children.length - 1].hasEmptyRightmostLeaf()
@@ -252,6 +252,7 @@
         snapshot[id].range.end = end
       else
         snapshot[id].range = Range(offset, end)
+    end

   findContaining: (point, set) ->
     addSet(set, @ids)
@@ -374,6 +375,7 @@
     for id, {range: {start, end}, isExclusive} of snapshot
       @insert(id, start, end)
       @setExclusive(id, isExclusive)
+    return

   ###
   Section: Private
@@ -382,6 +384,7 @@
   condenseIfNeeded: ->
     while @rootNode.children?.length is 1
       @rootNode = @rootNode.children[0]
+    return

 assertValidId = (id) ->
   unless typeof id is 'string'


Simplify MarkerIndex::findContaining

Simplify MarkerIndex::findContaining

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@gmail.com>

--- src/marker-index.coffee
+++ src/marker-index.coffee
@@ -61,44 +61,14 @@
       childStart = childStart.traverse(child.extent)
     end

-  findContaining: (start, end) ->
-    # We break this query into subqueries on our children. For any child that
-    # intersects the query range, we ask the child for markers containing the
-    # subset of the query range covered by that child.
-    #
-    # The search is slightly different depending on whether the search range
-    # is empty or not. If the range is empty, we need to query children that
-    # start or end at the point we're searching for and take the union of
-    # the subquery results. Otherwise, we stop searching earlier, at the first
-    # child that contains the end of our search range and take the intersection
-    # of the subquery results.
-    containingIds = null
-    searchRangeEmpty = start.compare(end) is 0
-
+  findContaining: (point, set) ->
     childStart = Point.zero()
     for child in @children
-      if searchRangeEmpty
-        break if childStart.compare(end) > 0
-      else
-        break if childStart.compare(end) >= 0
-
       childEnd = childStart.traverse(child.extent)
-
-      if childEnd.compare(start) > 0 or childEnd.compare(end) >= 0
-        intersectionStart = Point.max(start, childStart)
-        intersectionEnd = Point.min(end, childEnd)
-        childContainingIds = child.findContaining(intersectionStart.traversalFrom(childStart), intersectionEnd.traversalFrom(childStart))
-        if containingIds?
-          if searchRangeEmpty
-            containingIds = setUnion(containingIds, childContainingIds)
-          else
-            containingIds = setIntersection(containingIds, childContainingIds)
-        else
-          containingIds = childContainingIds
-
+      if point.compare(childStart) >= 0 and point.compare(childEnd) <= 0
+        child.findContaining(point.traversalFrom(childStart), set)
+      break if childEnd.compare(point) > 0
       childStart = childEnd
-
-    containingIds

   toString: (indentLevel=0) ->
     indent = ""
@@ -134,7 +104,8 @@
   getEnd: (id) ->
     @extent if @ids.has(id)

-  findContaining: (start, end) -> @ids
+  findContaining: (point, set) ->
+    @ids.forEach (id) -> set.add(id)

   toString: (indentLevel=0) ->
     indent = ""
@@ -165,16 +136,16 @@
   getEnd: (id) ->
     @rootNode.getEnd(id)

-  findContaining: (start, end=start) ->
-    new Set(@rootNode.findContaining(start, end))
+  findContaining: (start, end) ->
+    containing = new Set
+    @rootNode.findContaining(start, containing)
+    if end? and end.compare(start) isnt 0
+      containingEnd = new Set
+      @rootNode.findContaining(end, containingEnd)
+      containing.forEach (id) -> containing.delete(id) unless containingEnd.has(id)
+    containing

-setIntersection = (a, b) ->
+intersectSets = (a, b) ->
   intersection = new Set
   a.forEach (item) -> intersection.add(item) if b.has(item)
   intersection
-
-setUnion = (a, b) ->
-  union = new Set
-  a.forEach (item) -> union.add(item)
-  b.forEach (item) -> union.add(item)
-  union


fixed fetchData

fixed fetchData

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -91,7 +91,7 @@
   this.body = data
 }`

-exports.fetchData = `function *(path) {
+exports.fetchData = fetchData = `function *(path) {
   var options = {
     url: 'http://' + statsd_server.host + path
   };


Do not set name to class

Do not set name to class

--- client/Framework/components/tabs/KDTabPaneView.coffee
+++ client/Framework/components/tabs/KDTabPaneView.coffee
@@ -38,7 +38,7 @@

   viewAppended:()->
     {name} = @getOptions()
-    @setClass "kdtabpaneview #{name or ''}"
+    @setClass "kdtabpaneview"# #{name or ''}" Why do we need something like crazy?
     super

   setTitle:(title)->


[Group Notifications] open group link in another window

--- client/app/CommonViews/linkviews/grouplinkview.coffee
+++ client/app/CommonViews/linkviews/grouplinkview.coffee
@@ -7,7 +7,8 @@
   render:->
     {slug} = @getData()
     @setAttribute "href", "/#{slug}"
+    @setAttribute "target", "_blank"
     super

   pistachio:->
-    super "{{#(title)}}"+    super "{{#(title)}}"


[Group Notifications] override click event

--- client/app/CommonViews/linkviews/grouplinkview.coffee
+++ client/app/CommonViews/linkviews/grouplinkview.coffee
@@ -12,3 +12,5 @@

   pistachio:->
     super "{{#(title)}}"
+
+  click:-> #super has @utils.stopDOMEvent


WelcomeAppView: Update steps when any stack is generated.

--- client/stacks/lib/views/welcome/welcomeappview.coffee
+++ client/stacks/lib/views/welcome/welcomeappview.coffee
@@ -1,8 +1,9 @@
-kd                 = require 'kd'
-collectCredentials = require 'app/util/collectCredentials'
-{ BULLETS }        = require './boxes'
+kd                  = require 'kd'
+{ BULLETS }         = require './boxes'
+collectCredentials  = require 'app/util/collectCredentials'

-module.exports = class WelcomeAppView extends kd.CustomScrollView
+
+module.exports      = class WelcomeAppView extends kd.CustomScrollView

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

@@ -28,20 +29,28 @@
       cssClass : 'bullets clearfix'

     { groupsController, computeController } = kd.singletons
-    { stacks }                              = computeController

-    view  = this
+    { stacks }  = computeController
+    @isAdmin    = no

-    groupsController.ready -> computeController.ready ->
+    groupsController.ready => computeController.ready =>

       { providers, variables } = collectCredentials()

-      groupsController.getCurrentGroup().fetchMyRoles (err, roles) ->
+      groupsController.getCurrentGroup().fetchMyRoles (err, roles) =>
         return  kd.warn err  if err
-        isAdmin = 'admin' in (roles ? [])
-        if isAdmin
-        then view.putAdminBullets()
-        else view.putUserBullets()
+        @isAdmin = 'admin' in (roles ? [])
+        @putBullets()
+
+    unless stacks.length
+      computeController.on 'RenderStacks', @bound 'putBullets'
+
+
+  putBullets: ->
+
+    if @isAdmin
+    then @putAdminBullets()
+    else @putUserBullets()

   putAdminBullets: ->


NEW: using new ARt.Canvas.FontLoader to load icomoon

--- source/Art/Engine/FullScreenApp.coffee
+++ source/Art/Engine/FullScreenApp.coffee
@@ -1,7 +1,8 @@
 Foundation = require 'art-foundation'

-{merge, Promise, parseQuery, log, ConfigRegistry} = Foundation
+{merge, Promise, parseQuery, log, ConfigRegistry, isPlainObject} = Foundation
 {Meta, Link} = Foundation.Browser.DomElementFactories
+{FontLoader} = require 'art-canvas'

 module.exports = class FullScreenApp

@@ -41,8 +42,16 @@

   @init: (config = {})=>
     ConfigRegistry.configure config
-    @writeDom config
+    {fontFamilies} = config
+    if isPlainObject fontFamilies
+      waitUntilFontsLoaded = fontFamilies
+      fontFamilies = Object.keys fontFamilies
+
+    @writeDom merge config, {fontFamilies}
     @getDomReadyPromise()
+    .then ->
+      FontLoader.allFontsLoaded waitUntilFontsLoaded if waitUntilFontsLoaded
+

   @writeDom: ({title, styleSheets, scripts, fontFamilies, meta, link, manifest})->


Kick out extraPageParams and simplify(?) stuff

--- resources/assets/coffee/react/profile-page/main.coffee
+++ resources/assets/coffee/react/profile-page/main.coffee
@@ -121,8 +121,6 @@

   render: =>
-    withMePage = @state.userPage.initialRaw.trim() != '' || @props.withEdit
-
     if @props.user.is_bot
       profileOrder = ['me']
     else
@@ -130,29 +128,101 @@

     profileOrder.push 'account_standing' if !_.isEmpty @state.user.account_history

-    extraPageParams =
-      me:
-        extraClass: ('hidden' if !withMePage)
+    if @state.userPage.initialRaw.trim() == '' && !@props.withEdit
+      _.pull profileOrder, 'me'
+
+    div className: 'osu-layout osu-layout--full',
+      el ProfilePage.Header,
+        user: @state.user
+        stats: @state.user.statistics
+        currentMode: @state.currentMode
+        withEdit: @props.withEdit
+        rankHistory: @props.rankHistory
+
+      div
+        className: "hidden-xs page-extra-tabs #{'page-extra-tabs--floating' if @state.tabsSticky}"
+
+        div
+          className: 'js-sticky-header'
+          'data-sticky-header-target': 'page-extra-tabs'
+
+        div
+          className: 'page-extra-tabs__padding js-sync-height--target'
+          'data-sync-height-id': 'page-extra-tabs'
+
+        div
+          className: 'page-extra-tabs__floatable js-sync-height--reference js-switchable-mode-page--scrollspy-offset'
+          'data-sync-height-target': 'page-extra-tabs'
+          if profileOrder.length > 1
+            div className: 'osu-page',
+              div
+                className: 'page-mode page-mode--page-extra-tabs'
+                ref: (el) => @tabs = el
+                for m in profileOrder
+                  a
+                    className: "page-mode__item #{'js-sortable--tab' if @isSortablePage m}"
+                    key: m
+                    'data-page-id': m
+                    onClick: @tabClick
+                    href: "##{m}"
+                    el ProfilePage.ExtraTab,
+                      page: m
+                      currentPage: @state.currentPage
+                      currentMode: @state.currentMode
+
+      div
+        className: 'osu-layout__section osu-layout__section--extra'
+        div
+          className: 'osu-layout__row'
+          ref: (el) => @pages = el
+          @extraPage name for name in profileOrder
+
+
+  _tabsStick: (_e, target) =>
+    newState = (target == 'page-extra-tabs')
+    @setState(tabsSticky: newState) if newState != @state.tabsSticky
+
+
+  extraPage: (name) =>
+    {extraClass, props, component} = @extraPageParams name
+    topClassName = 'js-switchable-mode-page--scrollspy js-switchable-mode-page--page'
+    topClassName += ' js-sortable--page' if @isSortablePage name
+    props.withEdit = @props.withEdit
+    props.name = name
+
+    @extraPages ?= {}
+
+    div
+      key: name
+      'data-page-id': name
+      className: "#{topClassName} #{extraClass}"
+      ref: (el) => @extraPages[name] = el
+      el component, props
+
+
+  extraPageParams: (name) =>
+    switch name
+      when 'me'
         props:
           userPage: @state.userPage
           user: @state.user
         component: ProfilePage.UserPage

-      recent_activity:
+      when 'recent_activity'
         props:
           pagination: @state.showMorePagination
           recentActivity: @state.recentActivity
           user: @state.user
         component: ProfilePage.RecentActivity

-      kudosu:
+      when 'kudosu'
         props:
           user: @state.user
           recentlyReceivedKudosu: @state.recentlyReceivedKudosu
           pagination: @state.showMorePagination
         component: ProfilePage.Kudosu

-      top_ranks:
+      when 'top_ranks'
         props:
           user: @state.user
           scoresBest: @state.scoresBest
@@ -161,7 +231,7 @@
           pagination: @state.showMorePagination
         component: ProfilePage.TopRanks

-      beatmaps:
+      when 'beatmaps'
         props:
           user: @state.user
           favouriteBeatmapsets: @state.favouriteBeatmapsets
@@ -176,7 +246,7 @@
           pagination: @state.showMorePagination
         component: ProfilePage.Beatmaps

-      medals:
+      when 'medals'
         props:
           achievements: @props.achievements
           userAchievements: @props.userAchievements
@@ -184,7 +254,7 @@
           user: @state.user
         component: ProfilePage.Medals

-      historical:
+      when 'historical'
         props:
           beatmapPlaycounts: @state.beatmapPlaycounts
           scoresRecent: @state.scoresRecent
@@ -193,80 +263,10 @@
           pagination: @state.showMorePagination
         component: ProfilePage.Historical

-      account_standing:
+      when 'account_standing'
         props:
           user: @state.user
         component: ProfilePage.AccountStanding
-
-    div className: 'osu-layout osu-layout--full',
-      el ProfilePage.Header,
-        user: @state.user
-        stats: @state.user.statistics
-        currentMode: @state.currentMode
-        withEdit: @props.withEdit
-        rankHistory: @props.rankHistory
-
-      div
-        className: "hidden-xs page-extra-tabs #{'page-extra-tabs--floating' if @state.tabsSticky}"
-
-        div
-          className: 'js-sticky-header'
-          'data-sticky-header-target': 'page-extra-tabs'
-
-        div
-          className: 'page-extra-tabs__padding js-sync-height--target'
-          'data-sync-height-id': 'page-extra-tabs'
-
-        div
-          className: 'page-extra-tabs__floatable js-sync-height--reference js-switchable-mode-page--scrollspy-offset'
-          'data-sync-height-target': 'page-extra-tabs'
-          if profileOrder.length > 1
-            div className: 'osu-page',
-              div
-                className: 'page-mode page-mode--page-extra-tabs'
-                ref: (el) => @tabs = el
-                for m in profileOrder
-                  continue if m == 'me' && !withMePage
-
-                  a
-                    className: "page-mode__item #{'js-sortable--tab' if @isSortablePage m}"
-                    key: m
-                    'data-page-id': m
-                    onClick: @tabClick
-                    href: "##{m}"
-                    el ProfilePage.ExtraTab,
-                      page: m
-                      currentPage: @state.currentPage
-                      currentMode: @state.currentMode
-
-      div
-        className: 'osu-layout__section osu-layout__section--extra'
-        div
-          className: 'osu-layout__row'
-          ref: (el) => @pages = el
-          for name in profileOrder
-            @extraPage name, extraPageParams[name]
-
-
-  _tabsStick: (_e, target) =>
-    newState = (target == 'page-extra-tabs')
-    @setState(tabsSticky: newState) if newState != @state.tabsSticky
-
-
-  extraPage: (name, {extraClass, props, component}) =>
-    topClassName = 'js-switchable-mode-page--scrollspy js-switchable-mode-page--page'
-    topClassName += ' js-sortable--page' if @isSortablePage name
-    props.withEdit = @props.withEdit
-    props.name = name
-
-    @extraPages ?= {}
-
-    div
-      key: name
-      'data-page-id': name
-      className: "#{topClassName} #{extraClass}"
-      ref: (el) => @extraPages[name] = el
-      el component, props

   showMore: (e, {showMoreLink}) =>


trim newline

trim newline

Signed-off-by: Adam Stokes <0a364f4bf549cc82d725fa7fd7ed34404be64079@ubuntu.com>

--- scripts/debpkg.coffee
+++ scripts/debpkg.coffee
@@ -18,7 +18,7 @@
   robot.http(fetchUrl).get() (err, res, body) ->
     if err != null or res.statusCode != 200
       return msg.send("Unable to query for #{pkgname}, try again Señor.")
-    for pkgLine in body.split('\n')
+    for pkgLine in body.trim().split('\n')
       [name, version, series, arch] = pkgLine.split('|')
       msg.send "#{name} | #{version} | <#{series}>"
   return


Add sign-in area to sidebar

Add sign-in area to sidebar

--- core/app/backbone/views/facts/react_discussion.coffee
+++ core/app/backbone/views/facts/react_discussion.coffee
@@ -59,6 +59,9 @@

   render: ->
     _div ['discussion'],
+      _div ['discussion-login'],
+        ReactSidebarLogin()
+
       _div ['top-annotation'],
         _div ['top-annotation-text'],
           if @model().get('displaystring')


sachin head is now emoji

sachin head is now emoji

--- scripts/sachin.coffee
+++ scripts/sachin.coffee
@@ -25,7 +25,7 @@
 ]

 justhead = [
-  "https://github.com/sagasu/offshore-team/blrawob/master/img/Sachin.png"
+  ":sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin:"
 ]

 module.exports = (robot) ->
@@ -40,5 +40,5 @@
     count = msg.match[2] || 5
     elements = [1..count]
     Math.floor(Math.random() * cuteLinks.length)
-    msg.send "https://github.com/sagasu/offshore-team/blob/master/img/Sachin.png?raw=true" for element in elements
+    msg.send ":sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin: :sachin:" for element in elements


Fix bug with including underscore.js.

Fix bug with including underscore.js.

--- coffee/Resources/init.coffee
+++ coffee/Resources/init.coffee
@@ -1,6 +1,10 @@
 # Include lib.
-for file in ['buildable', 'arrayFilters', 'underscore']
+for file in ['buildable', 'arrayFilters']
   Ti.include('lib/' + file + '.js')
+
+# Include vendor.
+for file in ['underscore']
+  Ti.include('vendor/' + file + '.js')

 # Include expansions.
 for file in ['citiesAndKnights']


fixed memory leak

fixed memory leak

--- scripts/trello.coffee
+++ scripts/trello.coffee
@@ -108,6 +108,7 @@
     , (err) ->
       console.error err
       msg.send "ERROR: " + err
+  , check_interval_ms

 parse_max_cards = (list) ->
   match = /\((\d+)\)$/.exec list.name


project updated

project updated

--- source/module/seeker.coffee
+++ source/module/seeker.coffee
@@ -31,17 +31,15 @@
     source = "./temp/seeker/#{title}.json"
     list = _.sortBy list, 'time'

-    unless yield $$.isExisted source
-      yield $$.write source, list
-      return list
-
     sourceList = yield $$.read source
+    sourceList or= []

     res = _.differenceBy list, sourceList, 'url'

     list = _.concat sourceList, list
     list = _.uniqBy list, 'url'
     list = _.sortBy list, 'time'
+    list = _.reverse list
     if list.length > 100 then list = list[0...100]
     yield $$.write source, list


Fixes for TabController

Fixes for TabController

--- lib/ST/TabController.coffee
+++ lib/ST/TabController.coffee
@@ -1,142 +1,139 @@
-ST.class 'TabController', 'ViewController', ->
-  @constructor ->
-    @_super()
-    @view = ST.View.create()
-    @view.setDelegate this
+#require ST/Controller
+
+ST.class 'TabController', 'Controller', ->
+  @initializer ->
+    @super()
+    @_view = ST.View.create()

-    @tabView = ST.TabView.create()
-    @tabView.bind 'switchedTab', this, 'tabViewSwitchedTab'
-    @tabView.setCanClose @methodFn('canCloseTab')
-    @tabView.setTruncateLength @tabTruncateLength
-    @tabView.bind 'closedTab', this, 'tabViewClosedTab'
-    @view.addChild @tabView
+    @_tabView = ST.TabView.create()
+    @_tabView.bind 'switchedTab', this, 'tabViewSwitchedTab'
+    @_tabView.setCanClose @methodFn('canCloseTab')
+    @_tabView.bind 'closedTab', this, 'tabViewClosedTab'
+    @_view.addChild @_tabView

-    @contentView = ST.View.create()
-    @view.addChild @contentView
+    @_contentView = ST.View.create()
+    @_view.addChild @contentView

-    @tabControllers = ST.IndexedList.create()
-    @activeTab = null
-    @hideSingleTab = false
-    @tabTruncateLength = false
+    @_tabControllers = ST.List.create()
+    @_activeTab = null
+    @_hideSingleTab = false

   @property 'activeTab'
-  @property 'tabView', 'retain', 'readonly'
-  @property 'tabTruncateLength'
   @property 'hideSingleTab'
+  @retainedProperty 'tabView'

   @destructor ->
-    @tabView.unbindAll this if @tabView
-    @releaseMembers 'tabView', 'contentView', 'tabControllers'
-    @_super()
+    @_tabView.unbindAll this
+    @super()

   @method 'setActiveTab', (newTab) ->
-    if @activeTab
-      @activeTab.view.trigger 'hiding'
-      @contentView.removeChild @activeTab.view
-      @activeTab.view.trigger 'hid'
+    if @_activeTab
+      @_activeTab.view.trigger 'hiding'
+      @_contentView.removeChild @_activeTab.view
+      @_activeTab.view.trigger 'hid'

-    return unless @tabControllers.has newTab
+    return unless @_tabControllers.has newTab
     $.scrollTo 0,0

-    @activeTab = newTab
+    @_activeTab = newTab

-    @activeTab.view.trigger 'showing'
-    @contentView.addChild @activeTab.view
-    @activeTab.view.trigger 'showed'
+    @_activeTab.view.trigger 'showing'
+    @_contentView.addChild @_activeTab.view
+    @_activeTab.view.trigger 'showed'

   @method 'viewLoaded', (view) ->
     @updateTabView()
-    if @tabControllers.count()
-      @setActiveTab @tabControllers.first()
+    if @_tabControllers.count()
+      @activeTab @_tabControllers.first()

   @method 'canCloseTab', (tab, index) ->
-    controller = @tabControllers.objectAtIndex index
+    controller = @_tabControllers.objectAtIndex index
     !!(controller && controller.closedTab)

   @method 'tabViewClosedTab', (tabView, tab, index) ->
-    controller = @tabControllers.objectAtIndex index
+    controller = @_tabControllers.objectAtIndex index
     if controller && controller.closedTab
       controller.closedTab()

   @method 'tabViewSwitchedTab', (tabView, oldIndex, newIndex) ->
-    @setActiveTab @tabControllers.objectAtIndex(newIndex)
+    @activeTab @_tabControllers.objectAtIndex(newIndex)

   @method 'updateTabView', ->
-    self = this;
+    self = this

-    return unless @view.loaded
+    return unless @view.loaded()

     if @hideSingleTab && @tabControllers.count() <= 1
-      @tabView.unload() if @tabView.loaded
+      @tabView.unload() if @tabView.loaded()
     else
-      @tabView.load() unless @tabView.loaded
+      @tabView.load() unless @tabView.loaded()

       tabs = []
-      @tabControllers.each (controller) ->
+      @_tabControllers.each (controller) ->
         tabs.push self.tabForController(controller)
-      @tabView.setTabs tabs
+      @tabView.tabs tabs

-    @tabView.setActiveTab(
-      Math.max(@tabControllers.indexOfObject(@activeTab), 0)
+    @tabView.activeTab(
+      Math.max(@_tabControllers.indexOfObject(@_activeTab), 0)
     )

   @method 'tabForController', (controller) ->
     controller.tabTitle || ''

   @method 'emptyTabs', ->
-    if @activeTab
-      @view.removeChild @activeTab.view
-      @activeTab = null
-    @tabControllers.empty()
+    if @_activeTab
+      @_view.removeChild @_activeTab.view()
+      @_activeTab = null
+    @_tabControllers.empty()

   @method 'addTab', (tab) ->
     tc = this

     tab.setTabTitle = (tabTitle) ->
-      @tabTitle = tabTitle
+      @_tabTitle = tabTitle
       tc.updateTabView()

-    if !@tabControllers.count() && @view.loaded
-      @setActiveTab tab
+    if !@_tabControllers.count() && @view.loaded
+      @_activeTab tab

-    @tabControllers.add tab
-    @updateTabView()
+    @_tabControllers.add tab
+    @_updateTabView()

   @method 'addAndReleaseTab', (tab) ->
-    @addTab tab
+    @_addTab tab
     tab.release()

   @method 'insertTabAtIndex', (tab, index) ->
     tc = this

     tab.setTabTitle = (tabTitle) ->
-      @tabTitle = tabTitle
+      @_tabTitle = tabTitle
       tc.updateTabView()

-    @tabControllers.insertAt index, tab
+    @_tabControllers.insertAt index, tab

-    this.updateTabView();
+    @updateTabView()

   @method 'insertTabBefore', (tab, before) ->
     if before && before.index != null
-      @insertTabAtIndex tab, before.index
+      @_insertTabAtIndex tab, before.index

   @method 'insertTabAfter', (tab, after) ->
-    if after && after.index >= 0 && after.index < (this.tabControllers.count() - 1)
-      @insertTabAtIndex tab, after.index + 1
+    if after && after.index >= 0 && after.index < @_tabControllers.count() - 1
+      @_insertTabAtIndex tab, after.index + 1
     else
-      @addTab tab
+      @_addTab tab

   @method 'removeTab', (tab) ->
-    if @activeTab == tab
-      @setActiveTab tab.next || tab.prev || null
-    @tabControllers.remove tab
-    @updateTabView()
+    if @_activeTab == tab
+      @activeTab tab.next || tab.prev || null
+    @_tabControllers.remove tab
+    @_updateTabView()

   @method 'unloadTabs', ->
-    self = this;
-    activeTab = @activeTab
-    @setActiveTab null
-    @tabControllers.each (controller) ->
+    self = this
+    activeTab = @_activeTab
+    @activeTab null
+    @_tabControllers.each (controller) ->
       controller.view.unload()
-    @setActiveTab activeTab+    @activeTab activeTab


:bug: Pathspec error on current branch checkout

--- lib/branch-list-view.coffee
+++ lib/branch-list-view.coffee
@@ -33,7 +33,7 @@

   confirmed: ({name}) ->
-    @checkout name
+    @checkout name.match(/\*?(.*)/)[1]
     @cancel()

   checkout: (branch) ->


Minor change

Minor change

--- lib/broadcast-server.coffee
+++ lib/broadcast-server.coffee
@@ -70,7 +70,7 @@

   stop: ->
     if !@server?
-      console.error 'Broadcast has not started'
+      console.log 'Broadcast has not started'
       return

     @stopSocketIOServer()


filter works

filter works

--- src/query/filter.coffee
+++ src/query/filter.coffee
@@ -171,7 +171,10 @@
     throw new Error('invalid range') if isNaN(@range[0]) or isNaN(@range[1])

   toString: ->
-    return "#{@attribute} is within #{@range[0]} and #{@range[1]}"
+    [r0, r1] = @range
+    r0 = r0.toISOString() if r0 instanceof Date
+    r1 = r1.toISOString() if r1 instanceof Date
+    return "#{@attribute} is within #{r0} and #{r1}"

   valueOf: ->
     return { type: @type, attribute: @attribute, range: @range }


removed two tap event

removed two tap event

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -8,7 +8,7 @@
     TOUCH_TIMEOUT = undefined
     HOLD_DELAY = 650
     GESTURES = ["touch",
-                "tap", "singleTap", "twoTap" "doubleTap", "hold",
+                "tap", "singleTap", "doubleTap", "hold",
                 "swipe", "swiping", "swipeLeft", "swipeRight", "swipeUp", "swipeDown",
                 "rotate", "rotating", "rotateLeft", "rotateRight",
                 "pinch", "pinching", "pinchIn", "pinchOut",


goal: { clicks: 3, bricks: 4, ticks: 12 }  ordering

--- games/tutorial.coffee
+++ games/tutorial.coffee
@@ -62,8 +62,7 @@
       Start using program bricks to create their own programs.
       Limit actions to 2 to avoid brute force non-run-loop solutions.
       '
-      goal: { clicks: 1, bricks: 1, ticks: 6 }
-      goal: { clicks: 1, ticks: 8, bricks: 1 }
+      goal: { clicks: 1, bricks: 1, ticks: 8 }
       action_limit: 2
       sprites: [
         { kind: 'gate', actions: {interrupt: ['_victory']}, position: [7,0] }
@@ -80,7 +79,7 @@
     }
     {
       name: 'Use "Interrupt" Program to Bounce Off Walls'
-      goal: { clicks: 1, ticks: 10, bricks: 1 }
+      goal: { clicks: 3, bricks: 4, ticks: 12 }
       bricks: 1
       sprites: [
         { kind: 'wall', position: [5,0] }
@@ -94,7 +93,7 @@
     }
     {
       name: 'Click "Interrupt" to Add Bricks to that Program'
-      goal: { clicks: 2, ticks: 10, bricks: 2 }
+      goal: { clicks: 3, bricks: 4, ticks: 12 }
       sprites: [
         { kind: 'wall', position: [5,0] }
         { kind: 'gate', actions: {interrupt: ['_victory']}, position: [4,4] }
@@ -103,11 +102,7 @@
     }
     {
       name: 'Can You Solve the Final Complex Maze?'
-      goal: {
-        clicks: 6
-        ticks: 20
-        bricks: 6
-      }
+      goal: { clicks: 3, bricks: 4, ticks: 12 }
       sprites: [
         {kind: 'gate', actions: {interrupt: ['_victory']}}
         {kind: 'wall'}
@@ -129,7 +124,7 @@
     {
       name: 'Replay Levels to Enable Edit Mode. Click to Add Walls.'
       edit_mode: true
-      goal: { clicks: 2, ticks: 10, bricks: 2 }
+      goal: { clicks: 3, bricks: 4, ticks: 12 }
       sprites: [
         { kind: 'gate', actions: {interrupt: ['_victory']} }
         { kind: 'turtle' }
@@ -137,7 +132,7 @@
     }
     {
       name: 'Click on Pond to Add Walls'
-      goal: { clicks: 1, ticks: 10, bricks: 1 }
+      goal: { clicks: 3, bricks: 4, ticks: 12 }
       bricks: 1
       sprites: [
         { kind: 'gate', actions: {interrupt: ['_victory']}, position: [4,4] }


Revert "Don't use class in specs"

Revert "Don't use class in specs"

This reverts commit 2d50a56c32dc095d650f5a1bc1787bd3d7c6ad43.

--- spec/ComponentLoader.coffee
+++ spec/ComponentLoader.coffee
@@ -11,15 +11,17 @@
   root = 'noflo'
   urlPrefix = '/'

-Split =
-  getComponent: ->
-    new noflo.Component
+class Split extends noflo.Component
+  constructor: ->
+    options =
       inPorts:
         in: {}
       outPorts:
         out: {}
       process: (input, output) ->
         output.sendDone input.get 'in'
+    super options
+Split.getComponent = -> new Split

 Merge = ->
   inst = new noflo.Component
@@ -170,13 +172,15 @@
         done()

   describe 'register a component at runtime', ->
-    Split =
-      getComponent: ->
-        new noflo.Component
+    class Split extends noflo.Component
+      constructor: ->
+        options =
           inPorts:
             in: {}
           outPorts:
             out: {}
+        super options
+    Split.getComponent = -> new Split
     instance = null
     l.libraryIcons.foo = 'star'
     it 'should be available in the components list', ->


uri encode email and project names used in shared url

--- services/web/app/coffee/handlers/ProjectHandler.coffee
+++ services/web/app/coffee/handlers/ProjectHandler.coffee
@@ -106,9 +106,9 @@
 							project:
 								name: project.name
 								url: "#{Settings.siteUrl}/project/#{project._id}?" + [
-										"project_name=#{project.name}"
-										"user_first_name=#{project.owner_ref.first_name}"
-										"new_email=#{email}"
+										"project_name=#{encodeURIComponent(project.name)}"
+										"user_first_name=#{encodeURIComponent(project.owner_ref.first_name)}"
+										"new_email=#{encodeURIComponent(email)}"
 										"r=#{project.owner_ref.referal_id}" # Referal
 										"rs=ci" # referral source = collaborator invite
 									].join("&")


Display information about typechecking

Display information about typechecking

Fixes that the panel probably has not been attached yet when
typechecking as the first action

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -137,8 +137,10 @@
     editor = @getEditor()
     @saveFile editor
     uri = editor.getURI()
+    @messages.attach()
+    @messages.show()
+    @messages.clear()
     @messages.setTitle 'Idris: Typechecking...'
-    @messages.clear()

     successHandler = ({ responseType, msg }) =>
       @messages.attach()
@@ -495,7 +497,7 @@
     @messages.clear()
     @messages.setTitle '<i class="icon-bug"></i> Idris Errors', true

-    # display the general error message
+    # display the general error message
     if err.message?
       @messages.add new PlainMessageView
         raw: true


Fix for thing instancing.

Fix for thing instancing.

--- packages/landsofillusions/adventure/adventure-things.coffee
+++ packages/landsofillusions/adventure/adventure-things.coffee
@@ -78,6 +78,7 @@
         # Look into our cache if we already instantiated this thing.
         thingId = _.thingId thing
         thingClass = _.thingClass thing
+        thingInstance = null

         if thingEntries = @_things[thingId]
           thingEntry = _.find thingEntries, (thingEntry) => thingEntry.class is thingClass
@@ -86,7 +87,7 @@
         unless thingInstance
           # We don't have an instance ready, so we'll have to create it. We do so in a non-reactive
           # context so that reruns of this autorun don't invalidate instance's autoruns.
-          thingInstance = Tracker.nonreactive => new thing
+          thingInstance = Tracker.nonreactive => new thingClass
           @_things[thingId] ?= []
           @_things[thingId].push
             class: thingClass


Error msg

Error msg

--- server/methods.coffee
+++ server/methods.coffee
@@ -97,7 +97,7 @@
         params = _.extend params, filters
       wrap 'charges', 'list', params
     else
-      throw new Meteor.Error 404, "No customer"
+      throw new Meteor.Error 404, "Customer not found.  Cannot list charges."

   #


latency compensation - create an destination bucket placeholder when copying bucket

--- server/methods.coffee
+++ server/methods.coffee
@@ -55,6 +55,9 @@
   'storage/buckets/copy': (source, destination) ->
     ifBucketDoesNotExist destination, ->
       StorageBuckets.update {name: source}, $set: isLocked: true
+      StorageBuckets.insert
+        name: destination
+        isLocked: true
       Agent.copyStorageBucket source, destination

   restartTag: (tag) ->


Use relative path in spec require

Use relative path in spec require

--- spec/status-bar-spec.coffee
+++ spec/status-bar-spec.coffee
@@ -1,7 +1,7 @@
 $ = require 'jquery'
 _ = require 'underscore'
 RootView = require 'root-view'
-StatusBar = require 'status-bar/lib/status-bar-view'
+StatusBar = require '../lib/status-bar-view'
 fsUtils = require 'fs-utils'
 path = require 'path'
 {$$} = require 'space-pen'


Use `atom.activatePackage` directly in specs

--- spec/wrap-guide-spec.coffee
+++ spec/wrap-guide-spec.coffee
@@ -6,7 +6,7 @@
   beforeEach ->
     window.rootView = new RootView
     rootView.open('sample.js')
-    window.loadPackage('wrap-guide')
+    atom.activatePackage('wrap-guide')
     rootView.attachToDom()
     editor = rootView.getActiveView()
     wrapGuide = rootView.find('.wrap-guide').view()


Correct removing event.

Correct removing event.

--- lib/highlighted-area-view.coffee
+++ lib/highlighted-area-view.coffee
@@ -14,9 +14,14 @@
   attach: =>
     @editorView.underlayer.append(this)
     @subscribe @editorView, "selection:changed", @handleSelection
-    @subscribe @editorView, 'core:close', @destroy
+    atom.workspaceView.on 'pane:item-removed', @destroy

   destroy: =>
+    found = false
+    for editor in atom.workspaceView.getEditorViews()
+      found = true if editor.id is @editorView.id
+    return if found
+    atom.workspaceView.off 'pane:item-removed', @destroy
     @unsubscribe()
     @remove()
     @detach()


miniform autoexpands when adding sublist

--- listrr/static/coffee/list.coffee
+++ listrr/static/coffee/list.coffee
@@ -123,7 +123,10 @@
         li.append sublist
         li.removeClass 'leaf'
         form_container = $ '.new_form_container', sublist
-        form_elems = init_new_li_form form_container
+        [form_elems, jx_callback, submit_callback] = init_new_li_form form_container
+        form_setup form_elems, jx_callback, submit_callback
+        form_elems.btn_submit.unbind()
+        form_elems.btn_submit.attr 'type', 'submit'
         bind_sublist_miniform_cancel li, sublist, form_elems

 edit_textform_fix = (fn_reset, elems, jx_callback, submit_callback) ->
@@ -202,7 +205,7 @@
         new_li_title = title

     form = $ 'form', container
-    return init_miniform form, jx_callback, submit_callback
+    return [init_miniform(form, jx_callback, submit_callback), jx_callback, submit_callback]

 init_list_item = (li) ->
     api_url = li.data 'api_url'


fix date validation

fix date validation

--- app/assets/javascripts/frontend/form-validation.js.coffee
+++ app/assets/javascripts/frontend/form-validation.js.coffee
@@ -87,7 +87,11 @@
     expDate = question.data("date-max")
     diff = @compareDateInDays(val, expDate)

-    if not @toDate(val).isValid() or diff > 0
+    if not @toDate(val).isValid()
+      @addErrorMessage(question, "Not a valid date")
+      return
+
+    if diff > 0
       @addErrorMessage(question, "Date cannot be after #{expDate}")

   validateMinDate: (question) ->
@@ -99,7 +103,11 @@
     expDate = question.data("date-max")
     diff = @compareDateInDays(val, expDate)

-    if not @toDate(val).isValid() or diff > 0
+    if not @toDate(val).isValid()
+      @addErrorMessage(question, "Not a valid date")
+      return
+
+    if diff > 0
       @addErrorMessage(question, "Date cannot be before #{expDate}")

   validateBetweenDate: (question) ->
@@ -114,7 +122,11 @@
     diffStart = @compareDateInDays(val, expDateStart)
     diffEnd = @compareDateInDays(val, expDateEnd)

-    if not @toDate(val).isValid() or diffStart < 0 or diffEnd > 0
+    if not @toDate(val).isValid()
+      @addErrorMessage(question, "Not a valid date")
+      return
+
+    if diffStart < 0 or diffEnd > 0
       @addErrorMessage(question, "Date should be between #{expDateStart} and #{expDateEnd}.")

   validateNumber: (question) ->
@@ -217,6 +229,5 @@
       #if $(".question-block[data-answer='overseas_sales-total-overseas-sales'] > .conditional-question").hasClass("show-question")
       #  @validateTotalOverseas()

-
     return @validates


Use new load/ready promises to better delay perf reports

--- app/js/jail_iframe/classes/performance_measurement.coffee
+++ app/js/jail_iframe/classes/performance_measurement.coffee
@@ -26,13 +26,14 @@
       (padder + o.offset).substr(-offset_col_width)
     ).join('\n')

-FactlinkJailRoot.core_loaded_promise.then( ->
+FactlinkJailRoot.core_loaded_promise
+.then( -> FactlinkJailRoot.load_promise)
+.then( ->  FactlinkJailRoot.delay 1000)
+.then( ->
   if window.performance && window.performance.timing
     'fetchStart responseEnd domLoading domInteractive domContentLoadedEventEnd domComplete loadEventEnd'
       .split(' ').forEach (timing_event) ->
         add_existing_timing_event timing_event, window.performance.timing[timing_event]
-).then( ->
-  FactlinkJailRoot.delay 1000
 ).then( ->
   if FactlinkJailRoot.can_haz.log_jslib_loading_performance
     console.log get_perf_summary()


Some tweaks in main.

Some tweaks in main.

--- main.coffee
+++ main.coffee
@@ -118,29 +118,18 @@

 # Hallway middleware
 app.use ((request, response) ->
+	# Basic stuff
 	request.uonline =
 		now: new Date()
 		pjax: request.header('X-PJAX')?
 		moment: moment
 		plural: plural
-	# Transaction wrapper
-	request.uonline.tx = transaction(dbConnection)
-	request.uonline.tx_id = Math.random()*1000|0
-	console.log "opened tx ##{request.uonline.tx_id}"
-	endTransaction = ->
-		console.log "ending tx ##{request.uonline.tx_id}, state is: #{request.uonline.tx.state()}"
-		if request.uonline.tx.state() isnt 'closed'
-			console.log 'closing tx'
-			request.uonline.tx.commit()  # no sync() here?
-			console.log 'closed tx, state:', request.uonline.tx.state()
-		else
-			console.log 'not closing'
-	response.on 'finish', endTransaction
-	response.on 'close', endTransaction
+
 	# Read session data
 	user = lib.user.sessionInfoRefreshing.sync(null,
-		request.uonline.tx, request.cookies.sessid, config.sessionExpireTime, true)
+		dbConnection, request.cookies.sessid, config.sessionExpireTime, true)
 	request.uonline.user = user
+
 	# utility
 	writeDisplayRace = (x) ->
 		tmp = {
@@ -153,29 +142,57 @@
 		}
 		key = "#{x.race}-#{x.gender}"
 		x.displayRace = tmp[key]
+
 	# Read character data
-	character = lib.game.getCharacter.sync null, request.uonline.tx, request.uonline.user.character_id
+	character = lib.game.getCharacter.sync null, dbConnection, request.uonline.user.character_id
 	if character?
 		writeDisplayRace(character)
 	request.uonline.character = character
+
 	# Read all user's characters data
-	characters = lib.game.getCharacters.sync null, request.uonline.tx, request.uonline.user.id
+	characters = lib.game.getCharacters.sync null, dbConnection, request.uonline.user.id
 	if characters?
 		characters.forEach writeDisplayRace
 	request.uonline.characters = characters
+
 	# CSP
 	if !process.env.NOCSP
 		response.header 'Content-Security-Policy', "default-src 'self'; style-src 'self' 'unsafe-inline'"
+
 	# Anti-clickjacking
 	response.header 'X-Frame-Options', 'DENY'
+
 	# PJAX
 	response.header 'X-PJAX-URL', request.url
+
 	# Necessary, or it will pass shit to callback
 	return
 ).asyncMiddleware()

 # Middlewares
+
+openTransaction = ((request, response) ->
+	request.uonline.tx = transaction(dbConnection)
+	request.uonline.tx_id = Math.random()*1000|0
+	# Attach profiler?
+	log = ->
+	if process.env.SQLPROF is 'true'
+		log = (x) -> console.log x
+
+	log "opened tx ##{request.uonline.tx_id}"
+	endTransaction = ->
+		log "ending tx ##{request.uonline.tx_id}, state is: #{request.uonline.tx.state()}"
+		if request.uonline.tx.state() isnt 'closed'
+			log 'closing tx'
+			request.uonline.tx.commit.sync(request.uonline.tx)
+			log 'closed tx, state:', request.uonline.tx.state()
+		else
+			log 'not closing'
+	response.on 'finish', endTransaction
+	response.on 'close', endTransaction
+).asyncMiddleware()
+

 mustBeAuthed = (request, response, next) ->
 	if request.uonline.user.loggedIn is true
@@ -210,14 +227,19 @@
 		response.render template, request.uonline

+redirect = (code, url) ->
+	(request, response) ->
+		response.redirect(code, url)
+
+
 fetchCharacter = ((request, response) ->
-	character = lib.game.getCharacter.sync null, request.uonline.tx, request.uonline.user.character_id
+	character = lib.game.getCharacter.sync null, dbConnection, request.uonline.user.character_id
 	request.uonline.character = character
 ).asyncMiddleware()

 fetchCharacterFromURL = ((request, response) ->
-	request.uonline.fetched_character = lib.game.getCharacter.sync null, request.uonline.tx, request.params.name
+	request.uonline.fetched_character = lib.game.getCharacter.sync null, dbConnection, request.params.name
 ).asyncMiddleware()

@@ -225,7 +247,7 @@
 	id = parseInt(request.params.id, 10)
 	if isNaN(id)
 		throw new Error '404'
-	chars = lib.game.getCharacter.sync null, request.uonline.tx, id
+	chars = lib.game.getCharacter.sync null, dbConnection, id
 	if not chars?
 		throw new Error '404'
 	for i of chars
@@ -235,7 +257,7 @@

 fetchItems = ((request, response) ->
-	items = lib.game.getCharacterItems.sync null, request.uonline.tx, request.uonline.user.character_id
+	items = lib.game.getCharacterItems.sync null, dbConnection, request.uonline.user.character_id
 	request.uonline.equipment = items.filter (x) -> x.equipped
 	request.uonline.equipment.shield = request.uonline.equipment.find (x) -> x.type == 'shield'
 	request.uonline.equipment.right_hand = request.uonline.equipment.find (x) -> x.type.startsWith 'weapon'
@@ -246,19 +268,19 @@

 fetchLocation = ((request, response) ->
 	try
-		location = lib.game.getCharacterLocation.sync null, request.uonline.tx, request.uonline.user.character_id
+		location = lib.game.getCharacterLocation.sync null, dbConnection, request.uonline.user.character_id
 		#request.uonline.pic = request.uonline.picture  if request.uonline.picture?  # TODO: LOLWHAT
 	catch e
 		console.error e.stack
-		location = lib.game.getInitialLocation.sync null, request.uonline.tx
-		lib.game.changeLocation.sync null, request.uonline.tx, request.uonline.user.character_id, location.id
+		location = lib.game.getInitialLocation.sync null, dbConnection
+		lib.game.changeLocation.sync null, dbConnection, request.uonline.user.character_id, location.id
 	request.uonline.location = location
 	return
 ).asyncMiddleware()

 fetchArea = ((request, response) ->
-	area = lib.game.getCharacterArea.sync null, request.uonline.tx, request.uonline.user.character_id
+	area = lib.game.getCharacterArea.sync null, dbConnection, request.uonline.user.character_id
 	request.uonline.area = area
 	return
 ).asyncMiddleware()
@@ -266,14 +288,14 @@

 fetchUsersNearby = ((request, response) ->
 	tmpUsers = lib.game.getNearbyUsers.sync null,
-		request.uonline.tx, request.uonline.user.id, request.uonline.character.location
+		dbConnection, request.uonline.user.id, request.uonline.character.location
 	request.uonline.players_list = tmpUsers
 	return
 ).asyncMiddleware()

 fetchMonstersNearby = ((request, response) ->
-	tmpMonsters = lib.game.getNearbyMonsters.sync null, request.uonline.tx, request.uonline.character.location
+	tmpMonsters = lib.game.getNearbyMonsters.sync null, dbConnection, request.uonline.character.location
 	request.uonline.monsters_list = tmpMonsters
 	request.uonline.monsters_list.in_fight = tmpMonsters.filter((m) -> m.fight_mode)
 	request.uonline.monsters_list.not_in_fight = tmpMonsters.filter((m) -> not m.fight_mode)
@@ -282,7 +304,7 @@

 #fetchStats = ((request, response) ->
-#	chars = lib.game.getUserCharacters.sync null, request.uonline.tx, request.uonline.userid
+#	chars = lib.game.getUserCharacters.sync null, dbConnection, request.uonline.userid
 #	for i of chars
 #		request.uonline[i] = chars[i]
 #	return
@@ -290,7 +312,7 @@

 fetchStatsFromURL = ((request, response) ->
-	chars = lib.game.getUserCharacters.sync null, request.uonline.tx, request.params.username
+	chars = lib.game.getUserCharacters.sync null, dbConnection, request.params.username
 	if not chars?
 		throw new Error '404'
 	for i of chars
@@ -301,7 +323,7 @@

 fetchBattleGroups = ((request, response) ->
 	if request.uonline.character.fight_mode
-		participants = lib.game.getBattleParticipants.sync null, request.uonline.tx, request.uonline.user.character_id
+		participants = lib.game.getBattleParticipants.sync null, dbConnection, request.uonline.user.character_id
 		our_side = participants
 			.find((p) -> p.character_id is request.uonline.user.character_id)
 			.side
@@ -323,7 +345,7 @@
 	throw new Error 'Emulated error.'

 app.get '/explode_db/', (request, response) ->
-	request.uonline.tx.query.sync request.uonline.tx, 'SELECT * FROM "Emulated DB error."'
+	dbConnection.query.sync dbConnection, 'SELECT * FROM "Emulated DB error."'

 app.get '/', (request, response) ->
@@ -346,8 +368,8 @@
 	mustNotBeAuthed,
 	setInstance('login'),
 	(request, response) ->
-		if lib.user.accessGranted.sync null, request.uonline.tx, request.body.username, request.body.password
-			sessid = lib.user.createSession.sync null, request.uonline.tx, request.body.username
+		if lib.user.accessGranted.sync null, dbConnection, request.body.username, request.body.password
+			sessid = lib.user.createSession.sync null, dbConnection, request.body.username
 			response.cookie 'sessid', sessid
 			response.redirect 303, '/'
 		else
@@ -368,7 +390,7 @@
 	(request, response) ->
 		usernameIsValid = lib.validation.usernameIsValid(request.body.username)
 		passwordIsValid = lib.validation.passwordIsValid(request.body.password)
-		userExists = lib.user.userExists.sync(null, request.uonline.tx, request.body.username)
+		userExists = lib.user.userExists.sync(null, dbConnection, request.body.username)
 		if (usernameIsValid is true) and (passwordIsValid is true) and (userExists is false)
 			result = lib.user.registerUser.sync(
 				null
@@ -424,7 +446,7 @@
 	mustBeAuthed,
 	(request, response) ->
 		lib.user.closeSession.sync null,
-			request.uonline.tx, request.uonline.user.sessid
+			dbConnection, request.uonline.user.sessid
 		response.redirect 303, '/'  # force GET

@@ -440,7 +462,7 @@
 	setInstance('new_character'),
 	(request, response) ->
 		nameIsValid = lib.validation.characterNameIsValid(request.body.character_name)
-		alreadyExists = lib.character.characterExists.sync(null, request.uonline.tx, request.body.character_name)
+		alreadyExists = lib.character.characterExists.sync(null, dbConnection, request.body.character_name)

 		if nameIsValid and not alreadyExists
 			charid = lib.character.createCharacter.sync(
@@ -477,7 +499,7 @@
 app.post '/action/go',
 	mustBeAuthed,
 	(request, response) ->
-		result = lib.game.changeLocation.sync null, request.uonline.tx, request.uonline.user.character_id, request.body.to
+		result = lib.game.changeLocation.sync null, dbConnection, request.uonline.user.character_id, request.body.to
 		if result.result != 'ok'
 			console.error "Location change failed: #{result.reason}"
 		response.redirect 303, '/game/'
@@ -485,47 +507,53 @@

 app.post '/action/attack',
 	mustBeAuthed,
-	(request, response) ->
-		lib.game.goAttack.sync null, request.uonline.tx, request.uonline.user.character_id
-		response.redirect 303, '/game/'
+	(request, response, next) ->
+		lib.game.goAttack.sync null, dbConnection, request.uonline.user.character_id
+		next()
+	,
+	redirect(303, '/game/')

 app.post '/action/escape',
 	mustBeAuthed,
-	(request, response) ->
-		lib.game.goEscape.sync null, request.uonline.tx, request.uonline.user.character_id
-		response.redirect 303, '/game/'
+	(request, response, next) ->
+		lib.game.goEscape.sync null, dbConnection, request.uonline.user.character_id
+		next()
+	,
+	redirect(303, '/game/')

 app.post '/action/hit',
 	mustBeAuthed,
-	(request, response) ->
+	(request, response, next) ->
 		lib.game.hitOpponent.sync(
-			null, request.uonline.tx,
+			null, dbConnection,
 			request.uonline.user.character_id,
 			request.body.id,
 			request.body.with_item_id
 		)
-		response.redirect 303, '/game/'
+		next()
+	,
+	redirect(303, '/game/')

 app.get '/ajax/isNickBusy/:nick',
 	(request, response) ->
 		response.json
 			nick: request.params.nick
-			isNickBusy: lib.user.userExists.sync null, request.uonline.tx, request.params.nick
+			isNickBusy: lib.user.userExists.sync null, dbConnection, request.params.nick

 app.get '/ajax/isCharacterNameBusy/:name',
 	(request, response) ->
 		response.json
 			name: request.params.name
-			isCharacterNameBusy: lib.character.characterExists.sync null, request.uonline.tx, request.params.name
+			isCharacterNameBusy: lib.character.characterExists.sync null, dbConnection, request.params.name

 app.post '/ajax/cheatFixAll',
 	(request, response) ->
-		request.uonline.tx.query.sync request.uonline.tx,
+		dbConnection.query.sync dbConnection,
 			'UPDATE items '+
 				'SET strength = '+
 				'(SELECT strength_max FROM items_proto '+
@@ -538,7 +566,7 @@
 app.post '/action/unequip',
 	mustBeAuthed,
 	(request, response) ->
-		request.uonline.tx.query.sync request.uonline.tx,
+		dbConnection.query.sync dbConnection,
 			'UPDATE items '+
 				'SET equipped = false '+
 				'WHERE id = $1 AND owner = $2',
@@ -549,7 +577,7 @@
 app.post '/action/equip',
 	mustBeAuthed,
 	(request, response) ->
-		request.uonline.tx.query.sync request.uonline.tx,
+		dbConnection.query.sync dbConnection,
 			'UPDATE items '+
 				'SET equipped = true '+
 				'WHERE id = $1 AND owner = $2',
@@ -560,20 +588,20 @@
 app.post '/action/switchCharacter',
 	mustBeAuthed,
 	(request, response) ->
-		lib.character.switchCharacter.sync null, request.uonline.tx, request.uonline.user.id, request.body.id
+		lib.character.switchCharacter.sync null, dbConnection, request.uonline.user.id, request.body.id
 		response.redirect 303, 'back'

 app.post '/action/deleteCharacter',
 	mustBeAuthed,
 	(request, response) ->
-		lib.character.deleteCharacter.sync null, request.uonline.tx, request.uonline.user.id, request.body.id
+		lib.character.deleteCharacter.sync null, dbConnection, request.uonline.user.id, request.body.id
 		response.redirect 303, '/account/'

 app.get '/state/',
 	(request, response, next) ->
-		players = request.uonline.tx.query.sync request.uonline.tx,
+		players = dbConnection.query.sync dbConnection,
 			"SELECT *, (sess_time > NOW() - $1 * INTERVAL '1 SECOND') AS online FROM uniusers",
 			[config.sessionExpireTime]
 		request.uonline.userstate = players.rows


import fix

import fix

--- client/app/views/main.coffee
+++ client/app/views/main.coffee
@@ -1,5 +1,5 @@
 BaseView = require 'lib/BaseView'
-appIframeTemplate = require 'templates/home_app_iframe'
+appIframeTemplate = require 'templates/application_iframe'
 AppCollection = require 'collections/application'
 NavbarView = require 'views/navbar'
 AccountView = require 'views/account'


Remove unneeded lines.

Remove unneeded lines.

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -33,9 +33,6 @@
   if machine
     workspace = storage.getValue "LatestWorkspace_#{machine.uid}"

-  unless machine and workspace
-    workspace = storage.getValue 'LatestWorkspace'
-
   return  unless workspace

   { machineLabel, workspaceSlug, channelId } = workspace


#23 change phone number

#23 change phone number

--- client/phoneconnector.coffee
+++ client/phoneconnector.coffee
@@ -1,6 +1,6 @@

 Template.phoneconnector.helpers
-  inboundNumber: '+491771789420'
+  inboundNumber: '+491771789334'
   phonecode: -> Meteor.user()?.profile?.phonecode
   hidePhoneConnector: ->
     if Router.current().route.path(this) == '/profile'


:memo:

:memo:

--- lib/register-manager.coffee
+++ lib/register-manager.coffee
@@ -100,4 +100,5 @@
     else if text.lastIndexOf("\r") is text.length - 1
       'linewise'
     else
+      # [FIXME] should characterwise or line and character
       'character'


less indent

less indent

--- resources/assets/coffee/react/beatmapset-page/header.coffee
+++ resources/assets/coffee/react/beatmapset-page/header.coffee
@@ -190,27 +190,28 @@

   renderAvailabilityInfo: =>
-    if currentUser.id? && @hasAvailabilityInfo()
-      href = if @props.beatmapset.availability.more_information == 'rule_violation'
-               "#{route('wiki.show', locale: currentLocale, path: 'Rules')}#beatmap-submission-rules"
-             else
-               @props.beatmapset.availability.more_information
-
-      div
-        className: 'beatmapset-header__availability-info',
-        if @props.beatmapset.availability.download_disabled
-          osu.trans 'beatmapsets.availability.disabled'
-        else if @props.beatmapset.availability.more_information == 'rule_violation'
-          osu.trans 'beatmapsets.availability.rule_violation'
-        else
-          osu.trans 'beatmapsets.availability.parts-removed'
-
-        if href?
-          div className: 'beatmapset-header__availability-link',
-            a
-              href: href
-              target: '_blank'
-              osu.trans 'beatmapsets.availability.more-info'
+    return unless currentUser.id? && @hasAvailabilityInfo()
+
+    href = if @props.beatmapset.availability.more_information == 'rule_violation'
+              "#{route('wiki.show', locale: currentLocale, path: 'Rules')}#beatmap-submission-rules"
+            else
+              @props.beatmapset.availability.more_information
+
+    div
+      className: 'beatmapset-header__availability-info',
+      if @props.beatmapset.availability.download_disabled
+        osu.trans 'beatmapsets.availability.disabled'
+      else if @props.beatmapset.availability.more_information == 'rule_violation'
+        osu.trans 'beatmapsets.availability.rule_violation'
+      else
+        osu.trans 'beatmapsets.availability.parts-removed'
+
+      if href?
+        div className: 'beatmapset-header__availability-link',
+          a
+            href: href
+            target: '_blank'
+            osu.trans 'beatmapsets.availability.more-info'

   renderDownloadButtons: =>


better error message if they use ^ for exponentiation

--- coffeequate/src/parse.coffee
+++ coffeequate/src/parse.coffee
@@ -14,6 +14,8 @@
 		# Take a string and return a Terminal that that string represents.
 		# E.g. "2" -> Constant(2)
 		# E.g. "v" -> Variable(2)
+		if /\^/.test(string)
+			throw new Error("Unexpected carat (^). Coffeequate uses ** for exponentiation")
 		string = string.trim()
 		terminals = require("terminals")
 		if /^-?\d+(\.\d+)?$/.test(string) or /^-?\d+(\.\d+)?\/\d+(\.\d+)?$/.test(string)


Fixup to use month button

Fixup to use month button

--- spec/skr/screens/time-tracking/TimeTrackingSpec.coffee
+++ spec/skr/screens/time-tracking/TimeTrackingSpec.coffee
@@ -3,12 +3,12 @@
     beforeEach (done) ->
         @screen = LT.renderComponent(Skr.Screens.TimeTracking)
         _.defer =>
-            @screen.entries.entries.once 'sync', (te) ->
-                done()
+            @screen.entries.entries.once 'sync', (te) -> done()
             Lanes.Models.Sync.restorePerform =>
-                _.dom(@screen).qs('input[value=month]').change(target: {value: 'month'})
+                _.dom(@screen).qs('button[value=month]').click()
+

     it "renders monthly totals", ->
-        expect(true).toEqual(true)
-        totals = _.pluck _.dom(@screen).qsa('.day.summary'), 'textContent'
-        expect(totals).toEqual(["", "3.25", "13.25", "", ""])
+        _.dom(@screen).qs('button[value=month]').click()
+        totals = _.pluck _.dom(@screen).qsa('.day.summary > div'), 'textContent'
+        expect(totals).toEqual(["28.75", "14.50", "1.50", "2.00"])


Remove unnecessary fat arrow

Remove unnecessary fat arrow

--- lib/utilities/string.coffee
+++ lib/utilities/string.coffee
@@ -20,7 +20,7 @@

     re = new RegExp(strRe,'gi')

-    str.replace re, (match) => mapObj[match.toLowerCase()]
+    str.replace re, (match) -> mapObj[match.toLowerCase()]

   escapeQueryParam: (str) ->


fix esc keyboard shortcut - fixes #1699

fix esc keyboard shortcut - fixes #1699

--- app/assets/javascripts/keyboard_shortcuts.js.coffee
+++ app/assets/javascripts/keyboard_shortcuts.js.coffee
@@ -15,10 +15,13 @@

     if !active.is('input, textarea, select') or event.which == 27
       switch event.which
-        when 71, 27 # G or ESC for groups search dropdown
+        when 71 # G for groups search dropdown
           $('#groups>a').click()
           $('#groups').find('.group-dropdown-search').focus()
           event.preventDefault()
+        when 27 # ESC for closing groups search dropdown (if it's open)
+          if $('#groups').hasClass('open')
+            $('#groups>a').click()
         when 78 # N for notifications dropdown
           $('#notifications-container>a').click()
         when 83 # S for search box


Mark all episodes as viewed from anime pages.

--- app/assets/javascripts/routes/anime_route.js.coffee
+++ app/assets/javascripts/routes/anime_route.js.coffee
@@ -30,6 +30,8 @@
         libraryEntry = @store.createRecord 'libraryEntry',
           status: newStatus
           anime: @currentModel
+      if newStatus == "Completed"
+        libraryEntry.set 'episodesWatched', libraryEntry.get('anime.episodeCount')
       @saveLibraryEntry libraryEntry

     setLibraryRating: (newRating) ->


Remove debuging console.log

Remove debuging console.log

--- app/assets/javascripts/views/daterangepicker.coffee
+++ app/assets/javascripts/views/daterangepicker.coffee
@@ -52,7 +52,6 @@

     @$('.daterangepicker-input').on 'show.daterangepicker', (ev, picker) =>
       if Ember.empty(@get('start')) && Ember.empty(@get('end'))
-        console.log 'aaaaaddddd'
         picker.setStartDate moment()
         picker.setEndDate moment()


Added clearLine method to Player class

Added clearLine method to Player class

--- player.coffee
+++ player.coffee
@@ -34,6 +34,11 @@
     lastPos: ->
       @positions[@positions.length - 1]

+    clearLine: ->
+      @current_line.points = []
+      @canvas.remove(@current_line)
+      @inCanvas = false
+
     currentLine: ->
       @current_line.points.slice() # Copy array


Replace `String::contains` with `String::includes` in git.coffee

--- lib/git.coffee
+++ lib/git.coffee
@@ -50,7 +50,7 @@
       files = _prettify(data)
     stderr: (data) ->
       # edge case of no HEAD at initial commit
-      if data.toString().contains "ambiguous argument 'HEAD'"
+      if data.toString().includes "ambiguous argument 'HEAD'"
         files = [1]
       else
         new StatusView(type: 'error', message: data.toString())


Fix colliding fuzzy matches, skip fuzzy duplicates altogether

--- lib/index.iced
+++ lib/index.iced
@@ -60,7 +60,7 @@
   for item, index in array
     if isScalar item
       item
-    else if fuzzyOriginals && (bestMatch = findMatchingObject(item, index, fuzzyOriginals)) && bestMatch.score > 40
+    else if fuzzyOriginals && (bestMatch = findMatchingObject(item, index, fuzzyOriginals)) && bestMatch.score > 40 && !originals[bestMatch.key]?
       originals[bestMatch.key] = item
       bestMatch.key
     else


Escape incoming messages before any processes to avoid any malicious hijinks

--- app/assets/javascripts/backbone/views/show_activity.js.coffee
+++ app/assets/javascripts/backbone/views/show_activity.js.coffee
@@ -7,6 +7,7 @@
     activity = @options.activity.toJSON()
     activity.created_at = Kandan.Helpers.Utils.time_to_string(new Date(activity.created_at))
     console.log("Rendering activity:")
+    activity.content = _.escape(activity.content)
     console.log(activity)
     if activity.action != "message"
       @compiled_template = JST['user_notification']({activity: activity})


Use the same list of paragraph selectors for default selectors and article container selectors

--- app/js/jail_iframe/classes/paragraph_buttons.coffee
+++ app/js/jail_iframe/classes/paragraph_buttons.coffee
@@ -24,8 +24,14 @@
     elementsLeft = elements[10..]
     setTimeout (=> @_addParagraphButtonsBatch(elementsLeft)), 200

+  _paragraphSelectors: ->
+    ['p', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'dd', 'dt']
+
+  _prefixedParagraphSelectors: (prefix) ->
+    (prefix + ' ' + selector for selector in @_paragraphSelectors())
+
   _defaultSelector: ->
-    "p, h2, h3, h4, h5, h6, li, dd, dt"
+    @_paragraphSelectors().join(',')

   _articleContainerSelector: ->
     selectors = [
@@ -38,7 +44,7 @@
       $element = $(s)
       # Only match if selector is unique
       if $element.length == 1 && $element.is(':visible')
-        return "#{s} p, #{s} h2, #{s} h3, #{s} h4, #{s} h5, #{s} h6, #{s} li, #{s} dd, #{s} dt"
+        return @_prefixedParagraphSelectors(s).join(',')

     null


Added support for saving the recipe to a server.

--- src/Views/RecipeView.coffee
+++ src/Views/RecipeView.coffee
@@ -35,20 +35,22 @@
 		app.navigate "/#{@recipe.get("id")}/edit", true

 	save: ->
-		updates = "{"
-		changed = false
 		if $("input[name=title]").val()!=""
+			@recipe = new Recipe() if @has("id")==""
 			@recipe.set({'title': $("input[name=title]").val() })
 		if $("textarea[name=ingredients]").val()!=""
 			@recipe.set({'ingredients': $("textarea[name=ingredients]").val().replace(/\n\r?/g, "
") })
 		if $("textarea[name=instructions]").val()!=""
 			@recipe.set({'instructions': $("textarea[name=instructions]").val().replace(/\n\r?/g, "
") })
-
-		updates += "}"
-		#@render
-
-		console.log @recipe
-		console.log @recipe.changedAttributes()
+
+		@recipe.save(null, {
+			success: (model, response) ->
+				loc = window.location.href.split("/")
+				loc[loc.length-1] = model.id if loc[loc.length-1]=="new"
+				loc.pop()					 if loc[loc.length-1]=="edit"
+				window.location = loc.join("/")
+		})
+

 	back: ->
@@ -75,8 +77,8 @@
 	# return: an editable version of the view
 	make_editable: ->
 		title = @has "title"
-		ingredients = (@has "ingredients").replace(/
/, "\n")
-		instructions = (@has "instructions").replace(/
/, "\n")
+		ingredients = (@has "ingredients").replace(/
/g, "\n")
+		instructions = (@has "instructions").replace(/
/g, "\n")

 		editbtn = 		"


After the entry opening animation has finished, set its height to 'auto'.

This way, if images in the entry are lazy-loaded after the animation has finished, the entry height will automatically grow to adapt to the newly loaded entry.

Previous to this entry height was fixed, which meant that if images finished loading after the animation finished, not all content would fit in the allocated viewport space.

--- app/assets/javascripts/ng-services/ng-AnimationsSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-AnimationsSvc.js.coffee
@@ -6,19 +6,34 @@
 ['$rootScope',
 ($rootScope)->

-  #---------------------------------------------
-  # Animate opening an entry, by transitioning its height from zero to its final value.
-  #---------------------------------------------
-  open_entry: (entry)->
-    # Temporarily make entry content visible (height > 0) to measure its height
-    $("#entry-#{entry.id}-summary").css('height', 'auto')
-    height_auto = $("#entry-#{entry.id}-summary").outerHeight()
-    # Set height back to 0px and animate the transition to its final height
-    $("#entry-#{entry.id}-summary").css('height', '0').animate {height: height_auto, 'padding-top': 15, 'padding-bottom': 15}, 300
+  #--------------------------------------------
+  # PRIVATE FUNCTION - Set an entry height to 'auto'.
+  # This way if images in the entry are lazy-loaded after the open animation is finished,
+  # the entry height will adjust instantaneously.
+  #--------------------------------------------
+  entry_auto_height = ->
+    $(this).css('height', 'auto')

-  #---------------------------------------------
-  # Animate closing an entry, by transitioning its height from its current value to zero
-  #---------------------------------------------
-  close_entry: (entry)->
-    $("#entry-#{entry.id}-summary").animate {height: 0, 'padding-top': 0, 'padding-bottom': 0}, 300
+  service =
+
+    #---------------------------------------------
+    # Animate opening an entry, by transitioning its height from zero to its final value.
+    #---------------------------------------------
+    open_entry: (entry)->
+      # Temporarily make entry content visible (height > 0) to measure its height
+      $("#entry-#{entry.id}-summary").css('height', 'auto')
+      height_auto = $("#entry-#{entry.id}-summary").outerHeight()
+      # Set height back to 0px and animate the transition to its final height
+      $("#entry-#{entry.id}-summary")
+        .css('height', '0')
+        .animate {height: height_auto, 'padding-top': 15, 'padding-bottom': 15}, 300, 'swing', entry_auto_height
+
+    #---------------------------------------------
+    # Animate closing an entry, by transitioning its height from its current value to zero
+    #---------------------------------------------
+    close_entry: (entry)->
+      $("#entry-#{entry.id}-summary")
+        .animate {height: 0, 'padding-top': 0, 'padding-bottom': 0}, 300, 'swing'
+
+  return service
 ]


array_extensions

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -190,7 +190,6 @@
 each element in the array.
 @param {Object} [context] Optional context parameter to be
 used as `this` when calling the iterator function.
-
 @returns {Array} this to enable method chaining.
 ###
 Array::each = (iterator, context) ->


Ignore windows without paths

Ignore windows without paths

--- src/atom-application.coffee
+++ src/atom-application.coffee
@@ -191,11 +191,11 @@
   windowForPath: (pathToOpen) ->
     return null unless pathToOpen

-    for atomWindow in @windows
+    for atomWindow in @windows when atomWindow.pathToOpen?
       if pathToOpen is atomWindow.pathToOpen
         return atomWindow

-      if atomWindow.pathToOpen? and pathToOpen.indexOf(path.join(atomWindow.pathToOpen, path.sep)) is 0
+      if pathToOpen.indexOf(path.join(atomWindow.pathToOpen, path.sep)) is 0
         return atomWindow

     null


Make sure Atom terminates after message is sent.

--- src/atom-application.coffee
+++ src/atom-application.coffee
@@ -14,8 +14,9 @@
 class AtomApplication
   @open: (options) ->
     client = net.connect {path: socketPath}, ->
-      client.write(JSON.stringify(options))
-      app.terminate()
+      client.write JSON.stringify(options), ->
+        client.end()
+        app.terminate()

     client.on 'error', -> new AtomApplication(options)


Remove empty parameter list in order to pass linter

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -791,7 +791,7 @@

     @blobStore.save()

-  saveState: () ->
+  saveState: ->
     return Promise.resolve() unless @enablePersistence
     state = @serialize()


(#11) Change dollar sign auto-pairing rules

--- lib/autocomplete/syntax.coffee
+++ lib/autocomplete/syntax.coffee
@@ -17,13 +17,13 @@
       return

     cursor = editor.getCursorBufferPosition()
-    if editor?.buffer?.lines[cursor.row][cursor.column] is '$'
-      editor.moveRight()
-    else if editor?.buffer?.lines[cursor.row][cursor.column - 1] is '$'
-      editor.insertText('$')
-    else
-      editor.insertText('$$')
-      editor.moveLeft()
+    if editor?.buffer?.lines[cursor.row][cursor.column - 1] is ' '
+      if editor?.buffer?.lines[cursor.row].length is cursor.column or \
+          editor?.buffer?.lines[cursor.row][cursor.column] is ' '
+        editor.insertText('$$')
+        editor.moveLeft()
+        return
+    editor.insertText('$')

   backquote: ->
     editor = atom.workspace.getActiveTextEditor()


:bug: Fix undefined reference to syntheticEvent

--- src/command-registry.coffee
+++ src/command-registry.coffee
@@ -248,7 +248,7 @@
       break if propagationStopped
       currentTarget = currentTarget.parentNode ? window

-    @emitter.emit 'did-dispatch', syntheticEvent
+    @emitter.emit 'did-dispatch', dispatchedEvent

     matched


pubnub: fix client-side toggling

pubnub: fix client-side toggling

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -270,12 +270,12 @@
       callback null, owner

   togglePubnub: ->
-    isPubnubEnabled = localStorage.isPubnubEnabled or KD.config.pubnub.enabled
+    isPubnubEnabled = localStorage.isPubnubEnabled is "true" or KD.config.pubnub.enabled
     localStorage.isPubnubEnabled = !isPubnubEnabled
     location.reload()

   isPubnubEnabled: ->
-    localStorage.isPubnubEnabled or KD.config.pubnub.enabled
+    localStorage.isPubnubEnabled is "true" or KD.config.pubnub.enabled

 Object.defineProperty KD, "defaultSlug",


fixed typo

fixed typo

--- client/app/locales/fr.coffee
+++ client/app/locales/fr.coffee
@@ -6,7 +6,7 @@
     "leave google email label": "Votre adresse Gmail"
     "leave google email placeholder": "vous@gmail.com"
     "leave google connect label": "Connectez votre compte Google"
-    "leave google step2 title": "Puis, copiez-collez le code affiché dans la fenêtre dans ce champs : "
+    "leave google step2 title": "Puis, copiez-collez le code affiché dans la fenêtre dans ce champ : "
     "leave google choice title": "Félicitations, votre Cozy est connecté à votre compte Google ! Quelles données souhaitez-vous importer ?"
     "leave google choice photo": "Vos photos"
     "leave google choice calendar": "Vos calendriers"
@@ -21,11 +21,11 @@
     "import calendar running": "Import de vos calendriers en cours…"
     "import photo complete": "L’import de vos photos est un succès !"
     "import contact complete": "Vos contacts sont à présent importés !"
-    "import calendar complete": "L’import de vos calendrier s’est parfaitement déroulé !"
+    "import calendar complete": "L’import de vos calendriers s’est parfaitement déroulé !"
     "import amount photos": " photos importées sur  "
     "import amount events": " évènements importés sur "
     "import amount contacts": " contacts importés sur "
-    "import success message": "Félicitations, toutes vos données ont été importé avec succès de Google dans votre Cozy. Vous pouvez à présent les consulter et les modifier via vos applications Cozy. Accédez aux applications depuis la page d’accueil : "
+    "import success message": "Félicitations, toutes vos données ont été importées avec succès de Google dans votre Cozy. Vous pouvez à présent les consulter et les modifier via vos applications Cozy. Accédez aux applications depuis la page d’accueil : "
     "gmail account synced": "Votre compte GMail est à présent lié à votre Cozy."
     "confirm": "Confirmer"
     "google auth_code": "Code d'autorisation Google"


Fixed typo convertir array

Fixed typo convertir array

--- source/quo.css.coffee
+++ source/quo.css.coffee
@@ -19,7 +19,8 @@
   @param  {string} Name of stylesheet class
   ###
   $$.fn.addClass = (values) ->
-    @each -> @classList.add value for value in values.split " "
+    @each ->
+      @classList.add value for value in _arrayOf values

   ###
@@ -28,7 +29,8 @@
   @param  {string} Name of stylesheet class
   ###
   $$.fn.removeClass = (values) ->
-    @each -> @classList.remove value for value in values.split " "
+    @each ->
+gi      @classList.remove value for value in _arrayOf values

   ###
@@ -37,7 +39,8 @@
   @param  {string} Name of stylesheet class
   ###
   $$.fn.toggleClass = (values) ->
-    @each -> @classList.toggle value for value in values.split " "
+    @each ->
+      @classList.toggle value for value in _arrayOf values

   ###
@@ -86,3 +89,7 @@
   # ---------------------------------------------------------------------------
   _computedStyle = (element, property) ->
     document.defaultView.getComputedStyle(element, "")[property]
+
+  _arrayOf = (values) ->
+    values = [values] unless Array.isArray(values)
+    values


Do not show aborded ajax requests.

Do not show aborded ajax requests.

--- app/assets/javascripts/app/lib/app_post/ajax.js.coffee
+++ app/assets/javascripts/app/lib/app_post/ajax.js.coffee
@@ -38,6 +38,9 @@
       if !status && !detail
         detail = 'General communication error, maybe internet is not available!'

+      # do not show aborded requests
+      return if status is 0
+
       # do not show any error message on wrong login
       return if status is 422


Introduced a re-updating of selectors' date scopes when new scoped parameter is added. Should fix the bug mentioned in #666.

--- app/assets/javascripts/backend/interventions.js.coffee
+++ app/assets/javascripts/backend/interventions.js.coffee
@@ -75,16 +75,12 @@
       for id, attributes of list
         E.interventions.unserializeRecord(form, attributes, prefix + id + '_', updater_id)

-    updateParcelsScopes: (newTime) ->
+    updateDateScopes: (newTime) ->
       $("input.scoped-parameter").each (index, item) ->
-        console.log newTime
         scopeUri = decodeURI($(item).data("selector"))
         re =  /(scope\[availables\]\[\]\[at\]=)(.*)(&)/
-        console.log scopeUri
         scopeUri = scopeUri.replace(re, "$1"+newTime+"$3")
-        console.log scopeUri
         $(item).attr("data-selector", encodeURI(scopeUri))
-        console.log $(item).data('selector')

     # Ask for a refresh of values depending on given field
     refresh: (origin) ->
@@ -133,6 +129,11 @@
   $(document).on 'cocoon:after-insert', ->
     $('input[data-map-editor]').each ->
       $(this).mapeditor()
+    $("input.intervention-started-at").each ->
+      $(this).each ->
+        #date = $(this).data("datetimepicker").getFormattedDate()
+        date = $(this).val()
+        E.interventions.updateDateScopes(date)

   $(document).on 'mapchange', '*[data-intervention-updater]', ->
     $(this).each ->
@@ -155,7 +156,7 @@
     $(this).each ->
       #date = $(this).data("datetimepicker").getFormattedDate()
       date = $(this).val()
-      E.interventions.updateParcelsScopes(date)
+      E.interventions.updateDateScopes(date)

   # $(document).on 'change', '*[data-procedure-global="at"]', ->
   #   $(this).each ->


CKEDITOR is not defined fix

CKEDITOR is not defined fix

--- app/assets/javascripts/fluent/admin/ckeditor.js.coffee
+++ app/assets/javascripts/fluent/admin/ckeditor.js.coffee
@@ -1,5 +1,6 @@
 $(document).ready ->
   $(document).on 'page:done', ->
-    $.each CKEDITOR.instances, (index, instance) ->
-      if $('#'+instance.name).length > 0 && $('#'+instance.name).css('visibility') != 'hidden'
-       CKEDITOR.replace(instance.name)+    if typeof(CKEDITOR) != "undefined"
+      $.each CKEDITOR.instances, (index, instance) ->
+        if $('#'+instance.name).length > 0 && $('#'+instance.name).css('visibility') != 'hidden'
+         CKEDITOR.replace(instance.name)


Fix referenced field

Fix referenced field

--- app/assets/javascripts/kuroko2/job_timelines.js.coffee
+++ app/assets/javascripts/kuroko2/job_timelines.js.coffee
@@ -15,7 +15,7 @@
     new vis.Timeline(
       container.get(0),
       response.data,
-      $.extend(options, {start: response.start_at, end: response.end_at})
+      $.extend(options, {start: response.start, end: response.end})
     )

   $.get(datasetPath, callback)


Check for 3d transform support and add class to slider container.

--- app/assets/javascripts/lib/components/slider.js.coffee
+++ app/assets/javascripts/lib/components/slider.js.coffee
@@ -23,14 +23,16 @@
       $.extend config, args
       @$el = $(config.el)
       @slides = config.slides
-      @slides_container = '.js-slides-container'
+      @slides_container = @$el.find('.js-slides-container')
       @init() unless @$el.length is 0

     init: ->
-      @$el.find(@slides_container).append('

')
+      @slides_container.append('

')
       @$el.find('.js-slider-controls')
         .append('1 of '+$(@slides).length+'')
         .append('1 of '+$(@slides).length+'')
+      if _has3d()
+        @slides_container.addClass('supports-transform')

       @_setupSlideClasses()

@@ -100,3 +102,23 @@

     _resetSlideClasses: ->
       $(@slides+'.is-current, '+@slides+'.is-next, '+@slides+'.is-prev').removeClass('is-current is-next is-prev')
+
+    _has3d = ->
+      el = document.createElement("p")
+      has3d = undefined
+      transforms =
+        webkitTransform: "-webkit-transform"
+        OTransform: "-o-transform"
+        msTransform: "-ms-transform"
+        MozTransform: "-moz-transform"
+        transform: "transform"
+
+
+      # Add it to the body to get the computed style.
+      document.body.insertBefore el, null
+      for t of transforms
+        if el.style[t] isnt `undefined`
+          el.style[t] = "translate3d(1px,1px,1px)"
+          has3d = window.getComputedStyle(el).getPropertyValue(transforms[t])
+      document.body.removeChild el
+      has3d isnt `undefined` and has3d.length > 0 and has3d isnt "none"


Send the new object layer to the back

Send the new object layer to the back

--- src/svg-document.coffee
+++ src/svg-document.coffee
@@ -152,4 +152,4 @@
   _createObjectLayer: ->
     @objectLayer = @svg.nested()
     @setSize(1024, 1024)
-    @objectLayer
+    @objectLayer.back()


fix validation

fix validation

--- app/assets/javascripts/uploadcare/files/base.js.coffee
+++ app/assets/javascripts/uploadcare/files/base.js.coffee
@@ -95,6 +95,7 @@
       @onInfoReady.add @__runValidators

     __runValidators: (info) =>
+      info = info || @__fileInfo()
       try
         for v in @validators
             v(info)
@@ -134,7 +135,7 @@
       if not @__apiPromise
         @__apiPromise = @__extendApi @apiDeferred.promise()

-        @__runValidators(@__fileInfo())
+        @__runValidators()
         if @apiDeferred.state() == 'pending'
           op = @__startUpload()
           op.done(@__completeUpload)


Fix bug: in date/time span validation

Fix bug: in date/time span validation

--- app/assets/javascripts/views/circles/tasks/form.coffee
+++ app/assets/javascripts/views/circles/tasks/form.coffee
@@ -1,5 +1,8 @@
 class Lale.DateTime
   constructor: (date_field, time_field)->
+    # after init, the input element is still empty,
+    # but the datepicker getValue call already returns a valid date object
+    @dateFilled = date_field.val() != ''
     @value = this.buildDate(date_field, time_field)

   buildDate: (date_field, time_field)->
@@ -10,7 +13,7 @@
     date

   valid:->
-    @value instanceof Date
+    @dateFilled && (@value instanceof Date)

   parseDate: (field)->
     field.datetimepicker('getValue')


9324: Fix bubble-up error on delete click

--- app/assets/javascripts/views/form_items_view.js.coffee
+++ app/assets/javascripts/views/form_items_view.js.coffee
@@ -5,11 +5,11 @@

   events:
     'click .add-group': 'show_new_group_modal'
+    'click .form-item-group > .inner': 'show_edit_group_modal'
     'click .form-item-group > .inner .edit': 'show_edit_group_modal'
-    'click .form-item-group > .inner': 'show_edit_group_modal'
     'click .form-item-group > .inner .delete': 'delete_item'
+    'click .form-item-question': 'go_to_question'
     'click .form-item-question > .inner .delete': 'delete_item'
-    'click .form-item-question': 'go_to_question'

   initialize: (params) ->
     this.draggable = new ELMO.Views.FormItemsDraggableListView({parent_view: this}) if params.can_reorder
@@ -54,7 +54,8 @@
     @form_item_being_edited.find('> .inner').replaceWith(data)

   delete_item: (event) ->
-    event.preventDefault()
+    event.preventDefault() # Don't follow link (it's just '#')
+    event.stopPropagation() # Don't bubble up or go_to_question/show_edit_group_modal may get called.

     $link = $(event.currentTarget)
     return unless confirm $link.data('message')


remove js log

remove js log

--- app/assets/javascripts/views/shared/top_menu.js.coffee
+++ app/assets/javascripts/views/shared/top_menu.js.coffee
@@ -4,6 +4,5 @@

   render: ->
     $(@el).html @template(user: @model.attributes)
-    console.log @model
     this


map ui/ui to toolbar plugin, so the correct adopt is called.

--- scripts/configs/aloha.coffee
+++ scripts/configs/aloha.coffee
@@ -36,6 +36,7 @@
       map:
         '*':
           'semanticblock/semanticblock-plugin': 'semanticblock/lib/semanticblock-plugin'
+          'ui/ui': 'toolbar/toolbar-plugin'

     bundles:
       ghbook: '../../../../scripts/aloha'


updates script regex

updates script regex

--- src/cool-ascii-faces.coffee
+++ src/cool-ascii-faces.coffee
@@ -1,5 +1,5 @@
 # Description:
-#  Hubot script that displays cool ascii faces
+#  Displays cool ascii faces
 #
 # Dependencies:
 #   None
@@ -11,7 +11,7 @@
 #   Hubot face me - Returns a random ascii face
 #
 # Notes
-#   See jargon array for list of trigger phrases
+#   None
 #
 # Author:
 #   Morgan Wigmanich <okize123@gmail.com> (https://github.com/okize)
@@ -19,5 +19,6 @@
 getRandomFace = require 'cool-ascii-faces'

 module.exports = (robot) ->
-  robot.respond /face me/, (msg) ->
+
+  robot.respond /face me/i, (msg) ->
     msg.send getRandomFace()


The service initializer is now valid again

Fixes #9

--- blueprints/service/files/app/initializers/__name__.coffee
+++ blueprints/service/files/app/initializers/__name__.coffee
@@ -1,4 +1,4 @@
-<%= classifiedModuleName %>Initializer
+<%= classifiedModuleName %>Initializer =
   name: '<%= dasherizedModuleName %>'
   initialize: (container, app) ->
     app.inject 'route', '<%= camelizedModuleName %>', 'service:<%= camelizedModuleName %>'


minor reorders in the visual core

minor reorders in the visual core

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -215,10 +215,13 @@
     # Method also does some optimizations, such as scripts unifying.
     compressContext: (context) ->
         assert compression = "visual:compression"
+        identify = @constructor.identify().underline
         sources = _.toArray context.sources or Array()
         scripts = _.toArray context.scripts or Array()
         emptySources = "context JS sources are empty"
         u = (val) -> val.match(RegExp "^(.+)/(.+)$")[2]
+        message = "Do context compression sequence in %s"
+        logger.debug message.yellow, identify.toString()
         assert context.sheets = _.unique context.sheets
         assert context.invokes = _.unique context.invokes
         assert context.styles = _.unique context.styles
@@ -264,16 +267,16 @@
         assert _.isFunction(@prelude), noPrelude
         assert _.isObject context = stock or {}
         @energizeContext.call @, context, symbol
-        run = (fn) -> fn symbol, context, request
+        run = (fn) => fn symbol, context, request
         run prelude = this.downstream prelude: =>
             context.snapshot = try _.keys context
             assert @deployContext context, symbol
             assert @inlineAutocalls context, symbol
+            context.inline -> assert try @root = $root
             context.inline -> @emit "installed", this
             context.inline -> (@root.eco ?= []).push @
             context.inline -> this.externals.push "root"
             context.inline -> this.externals.push "eco"
-            context.inline -> assert try @root = $root
             context.inline -> assert @broadcast = ->
                 this.root.emit.apply $root, arguments
             assert context = @compressContext context


fix the compression cache bugs, probably introduces by the spinoff

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -212,7 +212,8 @@
         digest = hasher.update(joined).digest "hex"
         beauty = nconf.get("visual:beautify") or no
         disablers = mangle: false, compress: false
-        context.sources = c if c = @ccache?[digest]
+        assert ccache = @constructor ?= new Object()
+        context.sources = c if c = try ccache[digest]
         return context if (try context.sources and c)
         assert sources = _.reject sources, _.isEmpty
         assert minify = require("uglify-js").minify
@@ -220,8 +221,8 @@
         processing.output = beautify: yes if beauty
         try _.extend processing, disablers if beauty
         minified = minify sources, processing or {}
-        (@ccache ?= {})[digest] = [minified.code]
-        context.sources = @ccache[digest]; context
+        ccache[digest] = [minified.code] # cache it
+        context.sources = ccache[digest]; context

     # Assemble a new remoting context for the current service. This
     # creates a proper empty context that conforms to the necessary


refactor json broker

refactor json broker

--- library/nucleus/content.coffee
+++ library/nucleus/content.coffee
@@ -105,8 +105,8 @@
         json = "application/json".toLowerCase()
         sent = response.headersSent or false
         assert dump = JSON.stringify.bind JSON
-        doesHtml = try response.accepts /html/
-        spaces = if doesHtml then 4 else null
+        doesHtml = response.accepts /html/, /xhtml/
+        spaces = if doesHtml then 4 else undefined
         response.setHeader type, json unless sent
         jsoned = (x) -> dump x, undefined, spaces
         return @output response, jsoned content


fix memory leak for al-repeat

fix memory leak for al-repeat

--- src/directive/repeat.coffee
+++ src/directive/repeat.coffee
@@ -252,6 +252,9 @@
                                 node.CD.destroy()
                                 for el in node.element_list
                                     dom_removes.push el
+                                node.next = null
+                                node.prev = null
+                                node.element_list = null

                             applyList = []
                             # change positions and make new children
@@ -336,6 +339,8 @@
                             nodes = nodes2

                             self.rawUpdateDom dom_removes, dom_inserts
+                            dom_removes.length = 0
+                            dom_inserts.length = 0

                             #applying
                             skippedAttrs = env.skippedAttr()
@@ -367,7 +372,8 @@
                                 if node
                                     node.active = true

-                            dom_removes = for node in nodes
+                            dom_removes = []
+                            for node in nodes
                                 if node.active
                                     continue
                                 if node.prev
@@ -376,8 +382,10 @@
                                     node.next.prev = node.prev
                                 node_del node
                                 node.CD.destroy()
-                                node.element
-
+                                dom_removes.push node.element
+                                node.next = null
+                                node.prev = null
+                                node.element = null

                             applyList = []
                             # change positions and make new children
@@ -458,6 +466,8 @@
                             nodes = nodes2

                             self.rawUpdateDom dom_removes, dom_inserts
+                            dom_removes.length = 0
+                            dom_inserts.length = 0

                             #applying
                             skippedAttrs = env.skippedAttr()


Fix tests for pending state

Fix tests for pending state

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -870,7 +870,6 @@

           runs ->
             expect(editor1.isDestroyed()).toBe true
-            expect(editor2.isDestroyed()).toBe false
             expect(tabBar.tabForItem(editor1)).not.toExist()
             expect($(tabBar.tabForItem(editor2)).find('.title')).toHaveClass 'temp'

@@ -886,24 +885,11 @@
             triggerMouseEvent('mousedown', tabBar.tabForItem(editor2), which: 1)
             expect($(tabBar.tabForItem(editor2)).find('.title')).not.toHaveClass 'temp'

-      describe 'when opening views that do not have file paths', ->
-
-        it 'creates a permanent tab', ->
-          settingsView = null
-          waitsForPromise ->
-            atom.packages.activatePackage('settings-view').then ->
-              atom.workspace.open('atom://config').then (o) ->
-                settingsView = o
-
-          runs ->
-            expect(tabBar.tabForItem(settingsView)).toExist()
-            expect($(tabBar.tabForItem(settingsView)).find('.title')).not.toHaveClass 'temp'
-
-      describe 'when editing a file', ->
-        it 'makes the tab permanent', ->
+      describe 'when editing a file in pending state', ->
+        it 'makes the item and tab permanent', ->
           editor1 = null
           waitsForPromise ->
-            atom.workspace.open('sample.txt').then (o) ->
+            atom.workspace.open('sample.txt', pending: true).then (o) ->
               editor1 = o
               pane.activateItem(editor1)
               editor1.insertText('x')
@@ -916,7 +902,7 @@
         it 'makes the tab permanent', ->
           editor1 = null
           waitsForPromise ->
-            atom.workspace.open(path.join(temp.mkdirSync('tabs-'), 'sample.txt')).then (o) ->
+            atom.workspace.open(path.join(temp.mkdirSync('tabs-'), 'sample.txt'), pending: true).then (o) ->
               editor1 = o
               pane.activateItem(editor1)
               editor1.save()
@@ -966,7 +952,7 @@
         it "makes the tab permanent in the other pane", ->
           editor1 = null
           waitsForPromise ->
-            atom.workspace.open('sample.txt').then (o) -> editor1 = o
+            atom.workspace.open('sample.txt', pending: true).then (o) -> editor1 = o

           runs ->
             pane.activateItem(editor1)


Adds a declaration on navigation js script for running navigation responsiveness

--- coffee/navigation-bar.coffee
+++ coffee/navigation-bar.coffee
@@ -38,6 +38,8 @@
       else if plugin[_]? and $.type(plugin[_]) == 'function'
         plugin[_].apply plugin, args

+  $('.navigation-bar').responsiveNavBar()
+
   ResponsiveNavBar.version = "1.0.0"

 ) jQuery, window, document


Support attaching behaviour to multiple collections

--- collection-behaviours.coffee
+++ collection-behaviours.coffee
@@ -19,8 +19,11 @@

 class CollectionBehaviours

-  @attach: (collection, args...) ->
-    attach.apply collection, args
+  @attach: (collections, args...) ->
+    if Match.test collections, Mongo.Collection
+      collections = [collections]
+
+    attach.apply collection, args for collection in collections

   @configure: (name, options) ->
     if name of behaviours


Add more defensive code if a marker can't be retrieved

--- lib/mixins/decoration-management.coffee
+++ lib/mixins/decoration-management.coffee
@@ -93,6 +93,7 @@
   decorateMarker: (marker, decorationParams) ->
     return unless marker?
     marker = @getMarker(marker.id)
+    return unless marker?

     if !decorationParams.scope? and decorationParams.class?
       cls = decorationParams.class.split(' ').join('.')


Reset position of fixed comments on seek

--- lib/videojs-HTML5-niconicoplayer.coffee
+++ lib/videojs-HTML5-niconicoplayer.coffee
@@ -322,6 +322,8 @@
         layoutedComment = 0
         layoutedFixedComment = 0

+        fixedComments = []
+
         for chat, index in chats
           if 'ue' in chat.styles or 'shita' in chat.styles
             layoutFixedComment chat, index


support for start state

support for start state

--- public/javascripts/tetris.coffee
+++ public/javascripts/tetris.coffee
@@ -178,6 +178,11 @@
     shape.y = data.y
     shape.rotation = data.rotation

+  channel.bind 'start', (data) ->
+    console.log('start got', data.id)
+    shapeClass = Shape.types[data.type]
+    gs.addEntity( new shapeClass(id: data.id, x:data.x, y:data.y, rotation: data.rotation, color: data.color, fixed: true) )
+
   level = new Level()
   gs.addEntity(level)
   pusher.connection.bind 'connected', ->


fix tracker info hash parsing

fix tracker info hash parsing

Conflicts:

	src/tracker/reki.coffee

--- src/tracker/reki.coffee
+++ src/tracker/reki.coffee
@@ -76,12 +76,14 @@

 decodeURLtoHex = (str) ->
   output = ''
-  for i in [0..str.length-1]
+  i = 0
+  while i < str.length
     if (str.charCodeAt i)== 37
       output += str[i+1] + str[i+2]
       i += 2
     else
       output += (str.charCodeAt i).toString 16
+    i += 1
   return output.toLowerCase()

 HextoDec = (str) ->


For @all, send notifications to all room users.

--- packages/rocketchat-lib/server/sendMessage.coffee
+++ packages/rocketchat-lib/server/sendMessage.coffee
@@ -82,10 +82,13 @@
 			message.mentions?.forEach (mention) ->
 				mentionIds.push mention._id

+			# @all?
+			toAll = mentionIds.indexOf('all') > -1
+
 			if mentionIds.length > 0
 				usersOfMention = Meteor.users.find({_id: {$in: mentionIds}}, {fields: {_id: 1, username: 1}}).fetch()

-				if room.t is 'c' and mentionIds.indexOf('all') is -1
+				if room.t is 'c' and !toAll
 					for usersOfMentionItem in usersOfMention
 						if room.usernames.indexOf(usersOfMentionItem.username) is -1
 							Meteor.runAsUser usersOfMentionItem._id, ->
@@ -95,7 +98,7 @@
 				Update all other subscriptions of mentioned users to alert their owners and incrementing
 				the unread counter for mentions and direct messages
 				###
-				if mentionIds.indexOf('all') > -1
+				if toAll
 					# all users except sender if mention is for all
 					RocketChat.models.Subscriptions.incUnreadForRoomIdExcludingUserId message.rid, user._id, 1
 				else
@@ -105,7 +108,7 @@
 				query =
 					statusConnection: {$ne: 'online'}

-				if mentionIds.indexOf('all') > -1
+				if toAll
 					if room.usernames?.length > 0
 						query.username =
 							$in: room.usernames
@@ -116,9 +119,20 @@
 					query._id =
 						$in: mentionIds

-				usersOfMentionIds = _.pluck(usersOfMention, '_id');
-				if usersOfMentionIds.length > 0
-					for usersOfMentionId in usersOfMentionIds
+				userIdsToNotify = _.pluck(usersOfMention, '_id')
+
+				# If the message is @all, send a notification to all online room users except for the sender.
+				if toAll and room.usernames?.length > 0
+					onlineUsersOfRoom = Meteor.users.find({
+							username: {$in: room.usernames},
+							_id: {$ne: user._id}
+							status: {$in: ['online', 'away', 'busy']}},
+						{fields: {_id: 1, username: 1}})
+						.fetch()
+					userIdsToNotify = _.union userIdsToNotify, _.pluck(onlineUsersOfRoom, '_id')
+
+				if userIdsToNotify.length > 0
+					for usersOfMentionId in userIdsToNotify
 						RocketChat.Notifications.notifyUser usersOfMentionId, 'notification',
 							title: "@#{user.username} @ ##{room.name}"
 							text: message.msg
@@ -143,7 +157,7 @@
 								type: room.t
 								name: room.name
 							query:
-								userId: $in: usersOfMentionIds
+								userId: $in: userIdsToNotify

 		###
 		Update all other subscriptions to alert their owners but witout incrementing


add guard check before send fuzzy organization search query.

--- src/app/addressbook/addressbook.coffee
+++ src/app/addressbook/addressbook.coffee
@@ -16,7 +16,8 @@

   $scope.$watch "queryString", (oldVal, newVal) ->
     return if oldVal is newVal
-
+    return unless oldVal
+    return unless newVal
     q =
       name:
         $matches: newVal


Workspace: Fixed a typo.

Workspace: Fixed a typo.

--- client/landing/app/CommonViews/workspace/workspace.coffee
+++ client/landing/app/CommonViews/workspace/workspace.coffee
@@ -19,7 +19,7 @@

     @init()

-  init: -> @bound "createPanel"
+  init: -> @createPanel()

   createPanel: (callback = noop) ->
     panelOptions          = @getOptions().panels[@lastCreatedPanelIndex]


adding module tag for docs

adding module tag for docs

--- src/engine_collision.coffee
+++ src/engine_collision.coffee
@@ -1,8 +1,9 @@
 ###*
-(Module) The Collision module provides some simple collision detection methods to engine.
+The Collision module provides some simple collision detection methods to engine.

 @name Collision
 @fieldOf Engine
+@module
 ###
 Engine.Collision = (I, self) ->
   ###*


Don't escape html in plain-text email.

Don't escape html in plain-text email.

--- emails/activity_summary.coffee
+++ emails/activity_summary.coffee
@@ -23,7 +23,7 @@
 <% } %>Activity summary on InterTwinkles for <%- start.toDateString() %>:

 View full details<%- todos ? " and respond" : "" %> here:
-<%- show_url %>/
+<%= show_url %>/

 """ + [