Micro-optimization: calculate target value before cycle

--- src/range.coffee
+++ src/range.coffee
@@ -369,8 +369,9 @@
       # the combined length of the textNodes to that point exceeds or
       # matches the value of the offset.
       length = 0
+      targetOffset = this[p + 'Offset']
       for tn in Util.getTextNodes($(node))
-        if (length + tn.nodeValue.length >= this[p + 'Offset'])
+        if (length + tn.nodeValue.length >= targetOffset)
           range[p + 'Container'] = tn
           range[p + 'Offset'] = this[p + 'Offset'] - length
           break


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -160,8 +160,8 @@
         path.transition().duration(1000).attrTween('d', (d,i) ->
             theOldDataInPie = @oldPieData ? piedata
             console.log('@oldPieData in the tween',@oldPieData)
-            @testData = @testData + 1000
-            console.log('@testData in the tween',@testData)
+            tD = @testData + 1000
+            console.log('tD in the tween',tD)
             # Interpolate the arcs in data space
             s0 = undefined
             e0 = undefined


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -2,8 +2,10 @@

     @accessor 'data'

+    @oldPieData: ''
+
     ready: ->
-        window.oldPieData = ''
+        #window.oldPieData = ''

     onData: (data) ->
         #$(@node).fadeOut().fadeIn()


(- tests re) Re-arranging some tests

(- tests re) Re-arranging some tests

--- Paws/Test/utility.tests.coffee
+++ Paws/Test/utility.tests.coffee
@@ -28,11 +28,20 @@
          expect(constructify ->).to.be.a 'function'
          Ctor = constructify ->
          expect(-> new Ctor).to.not.throwException()
+      it 'can take options', ->
+         expect(-> constructify(foo: 'bar') ->).to.not.throwException()

       it 'returns a *new* function, not the constructor-body passed to it', ->
          body = ->
          Ctor = constructify body
          expect(constructify).to.not.be body
+      it 'can pass the `arguments` object intact', ->
+         Ctor = constructify(arguments: 'intact') (args) ->
+            @caller = args.callee.caller
+         it = null; func = null
+         expect(-> (func = -> it = new Ctor)() ).to.not.throwException()
+         expect(it).to.have.property 'caller'
+         expect(it.caller).to.be func
       it "causes constructors it's called on to always return instances", ->
          Ctor = constructify ->
          expect(new Ctor)  .to.be.a Ctor
@@ -55,16 +64,6 @@
          expect(    Ctor()).not.to.be 123
          expect(new Ctor)  .to.be.a Ctor
          expect(    Ctor()).to.be.a Ctor
-
-      it 'can take options', ->
-         expect(-> constructify(foo: 'bar') ->).to.not.throwException()
-      it 'can pass the `arguments` object intact', ->
-         Ctor = constructify(arguments: 'intact') (args) ->
-            @caller = args.callee.caller
-         it = null; func = null
-         expect(-> (func = -> it = new Ctor)() ).to.not.throwException()
-         expect(it).to.have.property 'caller'
-         expect(it.caller).to.be func
       it 'can be configured to *always* return the instance', ->
          Ctor = constructify(return: this) -> return new Array
          expect(new Ctor()).not.to.be.an 'array'


add bootstrap data back in

add bootstrap data back in

--- apps/list/routes.coffee
+++ apps/list/routes.coffee
@@ -4,4 +4,5 @@
 @index = (req, res, next) ->
   spooky.new Articles, 'articles', (err, articles) ->
     return next err if err
+    res.locals.sd.ARTICLES = articles.toJSON()
     res.render 'index', articles: articles.models


activity: add missing log fn

activity: add missing log fn

--- client/activity/lib/views/privatemessage/participantsearchcontroller.coffee
+++ client/activity/lib/views/privatemessage/participantsearchcontroller.coffee
@@ -3,6 +3,7 @@

 module.exports = class ParticipantSearchController extends KDAutoCompleteController

+  {log} = kd
   ### @constant {number} ###
   SHOW_LOADER_TIMEOUT = 1000


Added small comments.

Added small comments.

--- app/assets/javascripts/app/models/user.coffee
+++ app/assets/javascripts/app/models/user.coffee
@@ -48,7 +48,7 @@
   avatar: (size = 40, placement = '', cssClass = '', unique = false, avatar, type = undefined) ->
     size   = parseInt(size, 10)

-    cssClass += " " if cssClass
+    cssClass += ' ' if cssClass
     cssClass += "size-#{ size }"

     if placement
@@ -65,6 +65,7 @@
       vip = false
       data = " data-avatar-id=\"#{avatar.id}\""

+    # set vip flag, ignore if personal avatar is request
     vip = @vip
     if type is 'personal'
       vip = false
@@ -74,7 +75,9 @@
     # use system avatar for system actions
     if @id is 1
       return App.view('avatar_system')()
-    else if !@image || @image is 'none' || unique
+
+    # generate uniq avatar
+    if !@image || @image is 'none' || unique
       width  = 300
       height = 226

@@ -90,13 +93,14 @@
         x: x
         y: y
         initials: @initials()
-    else
-      return App.view('avatar')
-        data: data
-        cssClass: cssClass
-        placement: placement
-        vip: vip
-        url: @imageUrl()
+
+    # generate image based avatar
+    return App.view('avatar')
+      data: data
+      cssClass: cssClass
+      placement: placement
+      vip: vip
+      url: @imageUrl()

   imageUrl: ->
     return if !@image


Fixes sidebar_nav for mobile devices

Fixes sidebar_nav for mobile devices

--- app/assets/javascripts/bsat/sidebar.js.coffee
+++ app/assets/javascripts/bsat/sidebar.js.coffee
@@ -46,17 +46,22 @@
   #
   # Handle Subnavigation
   #
-  $(document).on 'click', '#sidebar .subnav-toggle', (e) ->
+
+  open_subnav = (e) ->
     path = $(e.currentTarget).attr 'href'
     $(e.currentTarget).parents('nav#sidebar').find('ul.nav').addClass 'hidden'
     $(e.currentTarget).parents('nav#sidebar').find(path).removeClass 'hidden'

-
-  $(document).on 'click', '#sidebar .subnav-back', (e) ->
+  close_subnav = (e) ->
     path = $(e.currentTarget).parents('ul').first().attr 'id'
     $(e.currentTarget).parents('nav#sidebar').find('ul.nav').addClass 'hidden'
     $(e.currentTarget).parents('nav#sidebar').find("a[href=##{path}]").parents('ul.nav').removeClass 'hidden'

+  $(document).on 'click', '#sidebar .subnav-toggle', open_subnav
+  $(document).on 'tap', '#sidebar .subnav-toggle', open_subnav
+
+  $(document).on 'click', '#sidebar .subnav-back', close_subnav
+  $(document).on 'tap', '#sidebar .subnav-back', close_subnav

   path = $('nav#sidebar').find('li.active a').parents('ul').attr('id')
   if path


Fix re-draggabilization of performances after partial refresh

--- app/assets/javascripts/competitions.js.coffee
+++ app/assets/javascripts/competitions.js.coffee
@@ -25,7 +25,7 @@
         dataType: 'script',
         context: $(this).closest('div'),
         complete: (request) ->
-          $('.draggable-performance').draggable('destroy').draggable(draggable_attributes)
+          $('.draggable-performance').draggable(draggable_attributes)
         ,
         url: '/jmd/performances/' + ui.draggable.attr('id') + '/retime'
       })


Fixed bug in backbone root router where the unauthorized path sent the wrong parameter to it's view.

--- app/assets/javascripts/routers/root.js.coffee
+++ app/assets/javascripts/routers/root.js.coffee
@@ -17,7 +17,7 @@
     @asserErrorContainerAndRender("servererror")

   unauhorized: ->
-    @asserErrorContainerAndRender("unauhorized")
+    @asserErrorContainerAndRender("unauthorized")

   asserErrorContainerAndRender: (id) ->


Comment out more slide code for now, still breaks functionality

--- app/assets/javascripts/static_pages.js.coffee
+++ app/assets/javascripts/static_pages.js.coffee
@@ -106,39 +106,39 @@
 	# 				$(".menu").data("state", "open")
 	# 			$('.siteContainer').animate(left: $('.menu').data('sitewidth'), 200)

-maxTime = 1000
-maxDistance = 50
-startX = 0
-startTime = 0
-touch = "ontouchend" of document
-startEvent = (if (touch) then "touchstart" else "mousedown")
-moveEvent = (if (touch) then "touchmove" else "mousemove")
-endEvent = (if (touch) then "touchend" else "mouseup")
+# maxTime = 1000
+# maxDistance = 50
+# startX = 0
+# startTime = 0
+# touch = "ontouchend" of document
+# startEvent = (if (touch) then "touchstart" else "mousedown")
+# moveEvent = (if (touch) then "touchmove" else "mousemove")
+# endEvent = (if (touch) then "touchend" else "mouseup")

-jQuery ->
-	$(".siteContainer").bind startEvent, (e) ->
-		startTime = e.timeStamp
-		startX = (if e.originalEvent.touches then e.originalEvent.touches[0].pageX else e.pageX)
+# jQuery ->
+# 	$(".siteContainer").bind startEvent, (e) ->
+# 		startTime = e.timeStamp
+# 		startX = (if e.originalEvent.touches then e.originalEvent.touches[0].pageX else e.pageX)

-	$(".siteContainer").bind endEvent, (e) ->
-		startTime = 0
-		startX = 0
+# 	$(".siteContainer").bind endEvent, (e) ->
+# 		startTime = 0
+# 		startX = 0

-	$(".siteContainer").bind moveEvent, (e) ->
-		currentX = (if e.originalEvent.touches then e.originalEvent.touches[0].pageX else e.pageX)
-		currentDistance = (if (startX is 0) then 0 else Math.abs(currentX - startX))
-		currentTime = e.timeStamp
-		if startTime != 0 && currentTime - startTime < maxTime && currentDistance > maxDistance
-			if($(window).width() <= ResponsiveWindowWidth)
-				if currentX < startX
-					if $(".menu").data("state") == "open"
-						$('.menu').animate(left:"-" + mobileMenuWidth, 200)
-						$('.siteContainer').animate(left:edgeLocation, 200)
-						$('body').css('position', "relative")
-						$(".menu").data("state", "closed")
-				if currentX > startX
-					if $(".menu").data("state") == "closed"
-						$('.menu').animate(left:edgeLocation, 200)
-						$('.siteContainer').animate(left:mobileSiteWidth, 200)
-						$('body').css('position', "fixed")
-						$(".menu").data("state", "open")+# 	$(".siteContainer").bind moveEvent, (e) ->
+# 		currentX = (if e.originalEvent.touches then e.originalEvent.touches[0].pageX else e.pageX)
+# 		currentDistance = (if (startX is 0) then 0 else Math.abs(currentX - startX))
+# 		currentTime = e.timeStamp
+# 		if startTime != 0 && currentTime - startTime < maxTime && currentDistance > maxDistance
+# 			if($(window).width() <= ResponsiveWindowWidth)
+# 				if currentX < startX
+# 					if $(".menu").data("state") == "open"
+# 						$('.menu').animate(left:"-" + mobileMenuWidth, 200)
+# 						$('.siteContainer').animate(left:edgeLocation, 200)
+# 						$('body').css('position', "relative")
+# 						$(".menu").data("state", "closed")
+# 				if currentX > startX
+# 					if $(".menu").data("state") == "closed"
+# 						$('.menu').animate(left:edgeLocation, 200)
+# 						$('.siteContainer').animate(left:mobileSiteWidth, 200)
+# 						$('body').css('position', "fixed")
+# 						$(".menu").data("state", "open")


Show more results

Show more results

--- app/assets/javascripts/users_search.js.coffee
+++ app/assets/javascripts/users_search.js.coffee
@@ -1,5 +1,5 @@
 $ ->
-  # Autocomplete on user search
+  # Autocomplete on employee search
   $queryEmployee = $("#query-employee")
   if $queryEmployee.length
     $queryEmployee.autocomplete
@@ -17,6 +17,12 @@
       minLength: 2
       select: (event, ui) ->
         document.location = ui.item.path
+        open: (event, ui) ->
+          # Add "More results" at the bottom of the list
+          $("
Visa fler resultat
").click (event) ->
+            event.preventDefault()
+            $queryEmployee.closest("form").submit()
+          .appendTo $('ul.search_users')
     .data("ui-autocomplete")._renderItem = (ul, item) ->
       ul.addClass('search_users')
       if $queryEmployee.hasClass("full-search")


Popup shows 'Pending' instead of 'Pending Edit' .

--- expensify/static/js/script.coffee
+++ expensify/static/js/script.coffee
@@ -36,7 +36,7 @@
       $(".td_category").text $(@).find(".field_category").text()
       $(".td_amount").text $(@).find(".field_amount").text()
       $(".td_date").text $(@).find(".field_date").text()
-      $(".td_status").text $(@).find(".field_status").text()
+      $(".td_status").text $(@).find(".field_status span.badge").text()
       $(".td_description").text $(@).find(".description").val()
       if $(@).find(".invoice").val().trim().length > 0
         url_name_arr = $(@).find(".invoice").val().split(";")


improved toggle method to hide content if already showing

--- app/coffee/drm-accordion/drm-accordion.coffee
+++ app/coffee/drm-accordion/drm-accordion.coffee
@@ -31,7 +31,11 @@
 			@.container.on 'click', '.drm-accordion-label', @.toggle

 		toggle: ->
-			$(@).next().slideToggle(drmAccordion.config.speed).siblings('.drm-accordion-inner').slideUp drmAccordion.config.speed
+			content = $(@).next()
+			if ( content.is(':hidden') )
+				content.slideDown(drmAccordion.config.speed).siblings('.drm-accordion-inner').slideUp drmAccordion.config.speed
+			else
+				content.slideUp drmAccordion.config.speed

 		showAll: ->
 			drmAccordion.content.slideDown drmAccordion.config.speed


some fixes

some fixes

--- app/controllers/currentGame_controller.coffee
+++ app/controllers/currentGame_controller.coffee
@@ -5,6 +5,7 @@
   confirmEndMatch: false
   message: ""
   gameOverFlag: false
+  gameStartedFlag: false
   confirmOpenRound: false
   cancelGameConfirm: false
   isActiveGame: Ember.computed.alias('controllers.application.isActiveGame')
@@ -12,6 +13,7 @@
     rounds = @get('rounds')
     authPerson = @get('authPerson')
     game = @get('model')
+    @set('gameStartedFlag', true)
     @recountMatchScores()
     homePerson = game.get('home')
     awayPerson = game.get('away')
@@ -70,8 +72,10 @@
   recountMatchScores: ->
     if @get('gameOverFlag')
       return
+    if !@get('gameStartedFlag')
+      return
     game = @get('model')
-
+
     rounds = game.get('rounds')
     if rounds? or rounds == null
       return
@@ -382,6 +386,7 @@
       @set('cancelGameConfirm', false)
       game = @get('model')
       game.delete()
+      @set('gameOverFlag', true)
       @transitionTo("/games")
     cancelGameConfirm: ->
       @set('cancelGameConfirm', true)


gemfile extension works for real now

gemfile extension works for real now

--- extensions/gemfile/gemfile.coffee
+++ extensions/gemfile/gemfile.coffee
@@ -13,20 +13,22 @@

   startup: (@project) =>
     urls = @project.urls()
-    gemfile = _.detect urls, (url) -> /Gemfile/i.test url
+    {url} = _.detect urls, ({url}) -> /Gemfile/i.test url

-    if gemfile
-      console.log
-        label: "RubyGems"
+    if url
+      @project.settings.extraURLs[@project.url] = [
+        name: "RubyGems"
         url: "http://rubygems.org/"
-        urls: @gemsFromGemFile gemfile
+        type: 'dir'
+      ]
+      @project.settings.extraURLs["http://rubygems.org/"] = @gems url

-  gemsFromGemFile: (url) ->
+  gems: (url) ->
     file = fs.read url
     gems = []

     for line in file.split "\n"
       if gem = line.match(/^\s*gem ['"](.+?)['"]/)?[1]
-        gems.push label: gem, url: "https://rubygems.org/gems/#{gem}"
+        gems.push type: 'file', name: gem, url: "https://rubygems.org/gems/#{gem}"

     gems


Removed useless logging in test.

Removed useless logging in test.

Closes #2.

--- features/steps/clients_steps.coffee
+++ features/steps/clients_steps.coffee
@@ -2,7 +2,6 @@

 module.exports = ->
   @Given /^client "([^"]*)" subscribe to "([^"]*)" via ([^ ]*)$/, (client, topic, protocol, callback) ->
-    console.log @
     @getClient protocol, client, (client) ->
       client.subscribe(topic)
       callback()


reassert FontAwesome after bug introduced with customized fonts

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -701,6 +701,11 @@
   vertical-align: baseline;
   line-height: 1.4; }

+
+#content .fa {
+  font-family: FontAwesome;
+}
+
 body, html {
   height: 100%;
 }


Update pt-BR.coffee

Update pt-BR.coffee

Update finish_signup_p and conversion_warning

--- app/locale/pt-BR.coffee
+++ app/locale/pt-BR.coffee
@@ -1082,10 +1082,10 @@
     thanks_p: "Vamos entrar em contato logo! Se você precisar entrar em contato conosco, use:"
     back_to_classes: "Voltar para Turmas"
     finish_signup: "Termine de criar sua conta de professor:"
-#    finish_signup_p: "Create an account to set up a class, add your students, and monitor their progress as they learn computer science."
+    finish_signup_p: "Crie uma conta para formar uma turma, adicione seus estudantes, e monitore o progresso enquanto eles aprendem ciência da computação."
     signup_with: "Entrar com:"
     connect_with: "Conectar - se com:"
-#    conversion_warning: "WARNING: Your current account is a Student Account. Once you submit this form, your account will be updated to a Teacher Account."
+    conversion_warning: "AVISO: Sua conta atual é uma Conta de Estudante. Ao submeter esse formulário, sua conta será convertida em uma Conta de Professor."
 #    learn_more_modal: "Teacher accounts on CodeCombat have the ability to monitor student progress, assign licenses and manage classrooms. Teacher accounts cannot be a part of a classroom - if you are currently enrolled in a class using this account, you will no longer be able to access it once you update to a Teacher Account."
 #    create_account: "Create a Teacher Account"
 #    create_account_subtitle: "Get access to teacher-only tools for using CodeCombat in the classroom.  Set up a class, add your students, and monitor their progress!"


Update Event

Update Event

--- app/models/Event.coffee
+++ app/models/Event.coffee
@@ -7,9 +7,10 @@

   constructor: (x, y, width, height, owningLevel, settings) ->
-    # settings.physicsType = 'static'
-    # settings.isSensor    = true
+    settings.physicsType = 'static'
+    settings.isSensor    = true

     super x, y, width, height, owningLevel, settings

-    @triggered = false
+    @onTouchEnd   = false
+    @onTouchBegin = false


¼Ή

¼Ή

--- AppStoreClient.coffee
+++ AppStoreClient.coffee
@@ -8,7 +8,6 @@

   # API = "http://itunes.apple.com/search?"
   API = "http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsSearch?"
-  RSS = "https://itunes.apple.com/jp/rss/topsongs/limit=300/explicit=true/xml"
   ANDROID_URL = "http://play.google.com/store/search?q=tsuyoshi+hyuga"
   URL=API#"http://coecorsproxy.appspot.com/"

@@ -126,18 +125,18 @@
   @param {object} callback
   ###
   @getItunesRssData:(callback,errorcallback,obj)->
+    country = obj?.country ? require("AppStoreClient/AppStoreClient").getTzOff()
+    url = "https://itunes.apple.com/"+country.toLowerCase()+"/rss/topsongs/limit=300/explicit=true/xml"
+    unless ENV_PRODUCTION then Ti.API.debug "url=#{url}"
+    client_object = getClientObject errorcallback
+    client_object.onload = ->
+      callback getKeywordToXML @responseXML

-      url = RSS
-      unless ENV_PRODUCTION then Ti.API.debug "url=#{url}"
-      client_object = getClientObject errorcallback
-      client_object.onload = ->
-        callback getKeywordToXML @responseXML
+    client = Ti.Network.createHTTPClient client_object

-      client = Ti.Network.createHTTPClient client_object
+    client.open "GET", url

-      client.open "GET", url
-
-      client.send()
+    client.send()

   ###*
   get data


New theme colors

New theme colors

--- src/theme.coffee
+++ src/theme.coffee
@@ -4,26 +4,26 @@
 module.exports =
   bar: colors.bgBlue
   name: colors.white.bold
-  circular: colors.cyan
-  null: colors.magenta
+  circular: colors.red.bold
+  null: colors.magenta.bold
   array:
-    bracket: colors.white
-    empty: colors.cyan
-  buffer: colors.blue
+    bracket: colors.cyan.bold
+    empty: colors.cyan.bold
+  buffer: colors.blue.bold
   string:
-    value: colors.blue
+    value: colors.blue.bold
     quote: colors.white
-    nl: colors.white
+    nl: colors.white.bold
   object:
-    valid: colors.cyan
-    empty: colors.cyan
-  number: colors.yellow
+    valid: colors.cyan.bold
+    empty: colors.cyan.bold
+  number: colors.yellow.bold
   boolean:
-    true: colors.green
-    false: colors.red
-  function: colors.cyan
-  undefined: colors.magenta
-  other: colors.bgWhite.black
+    true: colors.bgGreen.white.bold
+    false: colors.bgRed.white.bold
+  function: colors.green.bold
+  undefined: colors.magenta.bold
+  other: colors.bgWhite.black.bold
   dots: colors.white
   tabs: colors.white
-  regexp: colors.cyan
+  regexp: colors.yellow.bold


Add contact file, allow inquiry analytics to fire outside artwork page

--- assets/analytics.coffee
+++ assets/analytics.coffee
@@ -29,6 +29,7 @@
   require '../analytics/artworks_filter.js'
   require '../analytics/artist_page.js'
   require '../analytics/home.js'
+  require '../analytics/contact.js'
   require '../analytics/show_page.js'
   require '../analytics/account_creation.js'
   require '../analytics/account_login.js'
@@ -52,7 +53,5 @@
   require '../analytics/pro_buyer.js'
   require '../analytics/recently_viewed_artworks.js'
   require '../analytics/save.js'
-
-  if route.test(/^\/inquiry\/.*/) or route.test(/^\/artwork\/.*/)
-    require '../analytics/embedded_inquiry.js'
-    require '../analytics/inquiry_questionnaire.js'
+  require '../analytics/embedded_inquiry.js'
+  require '../analytics/inquiry_questionnaire.js'


(fix) Create ShareJS DB even if TutorDB already exists.

--- src/utils.coffee
+++ src/utils.coffee
@@ -77,23 +77,26 @@
     config =
       db: config.db
       tables: tables
-    (new Promise (resolve, reject) ->
-      rdbSetup.setup con, config, (err) ->
-        if err
-          reject (err)
-        else
+    Promise.all [
+      (new Promise (resolve, reject) ->
+        rdbSetup.setup con, config, (err) ->
+          if err
+            resolve(err)
+          else
+            resolve()),
+      (new Promise (resolve, reject) ->
+        if !config.sharejs?.rethinkdb?.db
           resolve()
-    ).then( new Promise (resolve, reject) ->
-      if !config.sharejs?.rethinkdb?.db
-        resolve()
-      config =
-        db: config.sharejs.rethinkdb.db
-      rdbSetup.setup con, config, (err) ->
-        if err
-          reject (err)
-        else
-          resolve()
-    )
+        config =
+          db: config.sharejs.rethinkdb.db
+        rdbSetup.setup con, config, (err) ->
+          if err
+            resolve(err)
+          else
+            resolve())
+    ], (err1, err2) ->
+      if err1 or err2
+        return Promise.reject([err1, err2])

   empty: (con, config) ->
     config =


Activity: removed deprecated helper

Activity: removed deprecated helper

--- client/Social/Activity/routes.coffee
+++ client/Social/Activity/routes.coffee
@@ -1,21 +1,4 @@
 do ->
-
-  createContentDisplayHandler = (section, passOptions = no) ->
-
-    ({params:{name, slug}, query}, models, route)->
-
-      {router} = KD.singletons
-      route = name unless route
-      contentDisplay = router.openRoutes[route.split('?')[0]]
-
-      if contentDisplay?
-        KD.singleton('display').hideAllDisplays contentDisplay
-        contentDisplay.emit 'handleQuery', query
-      else if models?
-        router.openContent name, section, models, route, query, passOptions
-      else
-        router.loadContent name, section, slug, route, query, passOptions
-

   handleChannel = (type, slug, callback) ->


Avoid trying to update mentions mark if there is no subscription.

--- client/lib/RoomHistoryManager.coffee
+++ client/lib/RoomHistoryManager.coffee
@@ -48,9 +48,10 @@
 				heightDiff = wrapper.scrollHeight - previousHeight
 				wrapper.scrollTop += heightDiff

-			Meteor.defer ->
-				readMessage.refreshUnreadMark(rid, true)
-				RoomManager.updateMentionsMarksOfRoom subscription.t + subscription.name
+			if ls
+				Meteor.defer ->
+					readMessage.refreshUnreadMark(rid, true)
+					RoomManager.updateMentionsMarksOfRoom subscription.t + subscription.name

 			room.isLoading.set false
 			room.loaded += result?.messages?.length


Hopefully this fixes it finally

Hopefully this fixes it finally

--- frontend/topic_bookmarking.coffee
+++ frontend/topic_bookmarking.coffee
@@ -61,8 +61,8 @@
   @waitForResource /viewtopic\.php.*bookmark=1/

 casper.then ->
-  @test.assertTextNotExists 'Remove from bookmarks'
-  @test.assertTextExists 'Bookmark topic'
+  @test.assertNotExists utils.a_text_xs('Remove from bookmarks')
+  @test.assertExists utils.a_text_xs('Bookmark topic')

 casper.run ->
   @test.done()


Improved wording.

Improved wording.

--- app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
@@ -65,7 +65,7 @@

     # generate form
     configure_attributes = [
-      { name: 'customer_id',        display: 'From',     tag: 'autocompletion', type: 'text', limit: 100, null: false, relation: 'User', class: 'span7', autocapitalize: false, help: 'Select the customer of the Ticket or create one.', link: '»', callback: @userInfo },
+      { name: 'customer_id',        display: 'Customer', tag: 'autocompletion', type: 'text', limit: 100, null: false, relation: 'User', class: 'span7', autocapitalize: false, help: 'Select the customer of the Ticket or create one.', link: '»', callback: @userInfo },
       { name: 'group_id',           display: 'Group',    tag: 'select',   multiple: false, null: false, filter: @edit_form, nulloption: true, relation: 'Group', default: defaults['group_id'], class: 'span7',  },
       { name: 'owner_id',           display: 'Owner',    tag: 'select',   multiple: false, null: true,  filter: @edit_form, nulloption: true, relation: 'User',  default: defaults['owner_id'], class: 'span7',  },
       { name: 'subject',            display: 'Subject',  tag: 'input',    type: 'text', limit: 100, null: false, default: defaults['subject'], class: 'span7', },


Add delete icon for working periods

Add delete icon for working periods

--- app/assets/javascripts/backend/intervention_participations.js.coffee
+++ app/assets/javascripts/backend/intervention_participations.js.coffee
@@ -54,16 +54,16 @@
     $(participationResult).append('')
     $(newLine).append(participationResult)

+    clearPeriod = $('

')
+    $(clearPeriod).append('')
+    $(newLine).append(clearPeriod)
+
     $('#working_times .participations').append(newLine)

     return

   $(document).on "submit", '.edit_intervention_participation', (event) ->
-    # event.preventDefault()
-
-    # $('.edit_intervention_participation').submit()
-
     @workingTimesModal = new ekylibre.modal('#working_times')
     @workingTimesModal.getModal().modal 'hide'


added confirmation to delete on check view

--- app/assets/javascripts/states/monitoring/views/check_group.js.coffee
+++ app/assets/javascripts/states/monitoring/views/check_group.js.coffee
@@ -8,9 +8,15 @@
     events:
       "click button.return_host": (e) ->
         @transition "mon_view_host", {host: @host}
+
       "click #delete_check": (e) ->
-        @check.destroy success: (model, response) =>
-          @transition "mon_view_host", {host: @host}
+        c = @create_partial Bixby.view.Confirm,
+          message: "Are you sure you want to delete '#{@check.name}'?",
+          hidden_cb: (confirmed) =>
+            if confirmed
+              @check.destroy()
+              @transition "mon_view_host", {host: @host}
+        c.render()

     after_render: ->
       super()


whitespace

whitespace

--- src/hinoki.coffee
+++ src/hinoki.coffee
@@ -14,7 +14,7 @@

         dependencies = argumentPart.match(/([^\s,]+)/g)

-        return if dependencies?  then dependencies else []
+        return if dependencies? then dependencies else []

     # find the first instance or factory of the service
     # specified by `id` in `containers`


CHG: fix componentReady helper

CHG: fix componentReady helper

--- frontend/javascripts/helpers.coffee
+++ frontend/javascripts/helpers.coffee
@@ -28,7 +28,10 @@
       $(el).on 'tao:connected.taoReady', (e) ->
         return unless e.target == el
         $(el).off 'tao:connected.taoReady'
-        deferred.resolve()
+        if el.connected
+          deferred.resolve()
+        else
+          deferred.reject()
     deferred.promise()

   $.when(promises...).then -> callback()


remove deprecated buttonTitleTextColor, steroids.app.path acually iz needed

--- testApp/app/controllers/navigationbar.coffee
+++ testApp/app/controllers/navigationbar.coffee
@@ -180,9 +180,8 @@
       tintColor: '#FF0000'
       titleTextColor: '#0000FF'
       buttonTintColor: '#00FF00'
-      buttonTitleTextColor: '#FF7F00'
-      portraitBackgroundImage: '/images/navbar-bg@2x.png'
-      landscapeBackgroundImage: '/images/navbar-bg@2x.png'
+      portraitBackgroundImage: steroids.app.path + '/images/navbar-bg@2x.png'
+      landscapeBackgroundImage: steroids.app.path + '/images/navbar-bg@2x.png'
     }, {
       onSuccess: -> alert "taste the rainbow"
       onFailure: -> alert "failed set nav bar appearance"


workers: remove unused variables

workers: remove unused variables

--- workers/social/lib/social/render/loggedout/kodinghome.coffee
+++ workers/social/lib/social/render/loggedout/kodinghome.coffee
@@ -1,12 +1,7 @@
 module.exports = (options, callback) ->

-  getTitle = require './../title'
-
-  { campaign, account, bongoModels } = options
-  campaign or= 'landing'
-
+  { account } = options
   userAccount   = JSON.stringify account
-  campaignStats = null

   addSiteScripts = require './sitescripts'
   addSiteTags    = require './sitetags'


Update token url

Update token url

--- backend/ide-init.coffee
+++ backend/ide-init.coffee
@@ -45,8 +45,8 @@
   tokenLinkDiv = document.createElement 'div'
   tokenText = document.createTextNode 'Get your token here: '
   tokenLink = document.createElement 'a'
-  tokenLink.title = 'https://learn.co/ile/token'
-  tokenLink.href = 'https://learn.co/ile/token'
+  tokenLink.title = 'https://learn.co/ide/token'
+  tokenLink.href = 'https://learn.co/ide/token'
   tokenLink.setAttribute 'style', 'text-decoration: underline;'
   tokenLink.appendChild document.createTextNode 'https://learn.co/ile/token'
   tokenLinkDiv.appendChild tokenText


* optimalize a little code and aOption could not be object

--- src/custom-factory.coffee
+++ src/custom-factory.coffee
@@ -44,7 +44,7 @@
             if result
               aParentClass[vName] = aClass
               Factory[vName] = aClass unless aParentClass is Factory
-              if aOptions
+              if aOptions?
                 registeredObjects[vName] = aOptions
               else
                 registeredObjects[vName] = -1 #createObject aClass, aBufferSize
@@ -86,7 +86,7 @@
   class CustomFactory
     constructor: (aName, aOptions)->
       if aName instanceof Factory
-        aName.initialize aOptions if aOptions
+        aName.initialize aOptions
         return aName
       if isObject aName
         aOptions = aName
@@ -95,8 +95,11 @@
         if not aName
           # arguments.callee is forbidden if strict mode enabled.
           try vCaller = arguments.callee.caller
-          if vCaller
-            while isInheritedFrom vCaller, Factory
+          if vCaller and isInheritedFrom vCaller, Factory
+            aName = vCaller
+            vCaller = vCaller.caller
+            #get farest hierarchical registered class
+            while isInheritedFrom vCaller, aName
               aName = vCaller
               vCaller = vCaller.caller
             aName = Factory.getNameFromClass(aName) if aName
@@ -113,7 +116,7 @@
         if result instanceof Factory
           result.initialize aOptions
         else if result
-          result = if isObject result then extend(result, aOptions) else aOptions
+          result = if isObject result then extend(result, aOptions) else if aOptions? then aOptions else result
           result = registeredObjects[aName] = createObject Factory[aName], result
         return result
       else


Place tooltip beneath the hovered node instead of above. Fixes #21

--- src/d3-flame-graph.coffee
+++ src/d3-flame-graph.coffee
@@ -146,16 +146,14 @@
         .direction (d) =>
           return 'w' if @x(d.x) + @x(d.dx) / 2 > @width() - 100
           return 'e' if @x(d.x) + @x(d.dx) / 2 < 100
-          return 's' if @y(d.y) < 100
-          return 'n' # otherwise
+          return 's' # otherwise
         .offset (d) =>
           x = @x(d.x) + @x(d.dx) / 2
-          xOffset = @x(d.dx) / 2
-          yOffset = @cellHeight() / 2
+          xOffset = Math.max(Math.ceil(@x(d.dx) / 2), 5)
+          yOffset = Math.ceil(@cellHeight() / 2)
           return [0, -xOffset] if @width() - 100 < x
           return [0,  xOffset] if x < 100
-          return [ yOffset, 0] if @y(d.y) < 100
-          return [-yOffset, 0]
+          return [ yOffset, 0]

       @container.call(@tip)
       @container


Fix gulp watch

Fix gulp watch

--- tasks/dev.coffee
+++ tasks/dev.coffee
@@ -19,7 +19,7 @@
 gulp.task 'run-watch', (cb) ->
   fs.writeFileSync('./dev/dev_enabled', 'dev enabled', 'utf8')
   gulp.watch './stylesheets/*.styl', ['stylus']
-  gulp.watch './functions/browser.coffee', ['coffee']
+  gulp.watch './functions/main.coffee', ['coffee']
   gulp.watch ['./functions/championify.coffee', './functions/helpers.coffee'], ['browserify']

   cmd = path.normalize('../node_modules/.bin/electron') + ' .'


Fix reference to display-buffer-marker

Fix reference to display-buffer-marker

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -10,7 +10,7 @@
 Fold = require './fold'
 Token = require './token'
 Decoration = require './decoration'
-Marker = require './display-buffer-marker'
+Marker = require './marker'
 Grim = require 'grim'

 class BufferToScreenConversionError extends Error


Update news.coffee

Update news.coffee

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,23 +3,8 @@

 export default allNews = () ->
     return news([
-        newsItem("Возможность оплаты не-российскими картами", String.raw"""
-            Появилась возможность оплаты занятий на алгопроге не-российскими картами
-        """),
-
-        newsItem("Школьная олимпиада", String.raw"""
-
Добавлена/обновлена информация про школьную олимпиаду.

-        """),
-
-        newsItem("Командная олимпиада", String.raw"""
-
Добавлена/обновлена информация про командную олимпиаду.

-        """),
-
-        newsItem("Изменение формата очных занятий в лицее 40", String.raw"""
-
28 сентября занятия в лицее 40 не будет. С 4 октября очные занятия в лицее 40 временно будут проходить в очно-дистанционном формате.
-               А именно, вы (ученики лицея) можете приходить на занятие, но я буду присутствовать только
-               удаленно через zoom. Вы сможете решать задачи как обычно, и обращаться ко мне через zoom, если у вас
-               будут возникать вопросы.

+        newsItem("С 23 ноября возобновляются очные занятия в лицее 40", String.raw"""
+
С 23 ноября возобновляются очные занятия в лицее 40, начало в 15.00.

         """),

         newsItem("Дистанционные консультации и контесты по воскресеньям", String.raw"""


Add pattern-matching for comment-lines

Add pattern-matching for comment-lines

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -27,3 +27,15 @@
 	"t",
 	"tr"
 ]
+patterns: [
+
+	# Comments
+	{
+		name: "comment.line.roff"
+		begin: "\\.\\\\\""
+		end: "$"
+		captures:
+			0: {name: "punctuation.definition.begin.comment.roff"}
+	}
+
+]


Fix inconsistent function highlighting in macros

The control characters and arguments of certain formatting macros didn't
share the same colour as the command's name, resulting in uneven colour.
Sections and titles were also left looking like unstyled text, since the
default styling of Atom's themes don't accommodate markup headings.

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -111,7 +111,7 @@
 			end:   "[^\n ]+\n"
 			patterns: [{ include: "$self" }]
 			beginCaptures:
-				1: {name: "punctuation.definition.request.roff"}
+				1: {name: "punctuation.definition.function.request.roff"}
 				2: {name: "entity.function.name.roff"}
 				3: {patterns: [{include: "#params"}]}
 			endCaptures:
@@ -309,7 +309,7 @@
 		patterns: [{

 			# .TH: Title
-			name: "markup.heading.title.man.macro.roff"
+			name: "markup.heading.title.function.man.macro.roff"
 			patterns: [{include: "#params"}]
 			begin: "^([.'])[ \t]*(TH)"
 			end: "$"
@@ -319,7 +319,7 @@
 		},{

 			# .SH: Section heading
-			name: "markup.heading.section.man.macro.roff"
+			name: "markup.heading.section.function.man.macro.roff"
 			patterns: [{include: "#params"}]
 			begin: "^([.'])[ \t]*(SH)"
 			end: "$"
@@ -329,7 +329,7 @@
 		},{

 			# .SS: Subsection
-			name: "markup.heading.subsection.man.macro.roff"
+			name: "markup.heading.subsection.function.man.macro.roff"
 			patterns: [{include: "#params"}]
 			begin: "^([.'])[ \t]*(SS)"
 			end: "$"
@@ -344,7 +344,7 @@
 			end:   "([^\n ]+ ?\n)"
 			patterns: [{ include: "$self" }]
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.name.function.roff"}
 		},{

@@ -354,7 +354,7 @@
 			end:   "([^\n ]+ ?\n)"
 			patterns: [{ include: "$self" }]
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.name.function.roff"}
 		},{

@@ -363,7 +363,7 @@
 			begin: "^([.'])[ \t]*(BI)(?=[ \t\\\\]|$)"
 			end:   "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.function.name.roff"}
 			patterns: [{
 				begin: '(?x)(?<=
@@ -395,7 +395,7 @@
 			begin: "^([.'])[ \t]*(BR)(?=[ \t\\\\]|$)"
 			end:   "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.function.name.roff"}
 			patterns: [{
 				begin: '(?x)(?<=
@@ -426,7 +426,7 @@
 			begin: "^([.'])[ \t]*(IB)(?=[ \t\\\\]|$)"
 			end:   "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.function.name.roff"}
 			patterns: [{
 				begin: '(?x)(?<=
@@ -458,7 +458,7 @@
 			begin: "^([.'])[ \t]*(IR)(?=[ \t\\\\]|$)"
 			end:   "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.function.name.roff"}
 			patterns: [{
 				begin: '(?x)(?<=
@@ -489,7 +489,7 @@
 			begin: "^([.'])[ \t]*(RB)(?=[ \t\\\\]|$)"
 			end:   "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.function.name.roff"}
 			patterns: [{
 				begin: '(?x)(?<=
@@ -519,7 +519,7 @@
 			begin: "^([.'])[ \t]*(RI)(?=[ \t\\\\]|$)"
 			end:   "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.macro.roff"}
+				1: {name: "punctuation.definition.function.macro.roff"}
 				2: {name: "entity.function.name.roff"}
 			patterns: [{
 				begin: '(?x)(?<=


Split hyperlink patterns into something manageable


fix keymap typo

fix keymap typo

--- keymaps/julia-client.cson
+++ keymaps/julia-client.cson
@@ -40,5 +40,5 @@
   'ctrl-alt-j ctrl-alt-r': 'julia-client:start-repl-client'
   'ctrl-j ctrl-s': 'julia-client:start-julia'
   'ctrl-j ctrl-c': 'julia-client:clear-console'
-  'ctrl-j ctrl-c': 'julia-client:reset-loading-indicator'
+  'ctrl-j ctrl-l': 'julia-client:reset-loading-indicator'
   'ctrl-j ctrl-k': 'julia-client:kill-julia'


Added an is_private flag to steam-api-player.

This flag means that the user has not made their profile available.

--- public/src/services/steam-api/steam-api-player-service.coffee
+++ public/src/services/steam-api/steam-api-player-service.coffee
@@ -10,11 +10,14 @@
   ($q, steamApiCaller, SteamDotaMatch) ->
     class SteamPlayer

+      PRIVATE_ID_32 = "4294967295"
+
       constructor: (steamId) ->
         if(steamId.length != 17)
           steamId = SteamPlayer.steamId32To64(steamId)
         @steamid64 = steamId
         @steamid32 = SteamPlayer.steamId64To32(@steamid64)
+        @is_private = (@steamid32 == PRIVATE_ID_32)
         @is_loaded = false

       @steamId64To32 = (steam64Id) ->
@@ -24,26 +27,34 @@
         '765' + (parseInt(steam32Id, 10) + 61197960265728);

       loadSummary: (cache = true) ->
-        steamApiCaller.getPlayerSummaries({steamids: @steamid64}, cache)
-        .then (summaries) =>
-          if summaries.length == 1
-            angular.extend(this, summaries[0])
-            @is_loaded = true
+        if @is_private
+          $q.reject(new Error('The player has not made their profile publicly available to third-party websites.'))
+        else
+          steamApiCaller.getPlayerSummaries({steamids: @steamid64}, cache)
+          .then (summaries) =>
+            if summaries.length == 1
+              angular.extend(this, summaries[0])
+              @is_loaded = true
+            else
+              $q.reject(new Error('Could not find a player with that id.'))

       getMatches: (numMatches = 5, startAtId = null, cache = true) ->
-        steamApiCaller.getMatchHistory({
-          account_id: @steamid64
-          matches_requested: numMatches
-          start_at_match_id: startAtId}, cache)
-        .then (result) ->
-          meta = {
-            num_results: result.num_results
-            total_results: result.total_results
-            results_remaining: result.results_remaining
-          }
-          matches = (new SteamDotaMatch(match.match_id) for match in result.matches)
-          return {
-          matches: matches,
-          meta: meta
-          }
+        if @is_private
+          $q.reject(new Error('The player has not made their profile publicly available to third-party websites.'))
+        else
+          steamApiCaller.getMatchHistory({
+            account_id: @steamid64
+            matches_requested: numMatches
+            start_at_match_id: startAtId}, cache)
+          .then (result) ->
+            meta = {
+              num_results: result.num_results
+              total_results: result.total_results
+              results_remaining: result.results_remaining
+            }
+            matches = (new SteamDotaMatch(match.match_id) for match in result.matches)
+            return {
+            matches: matches,
+            meta: meta
+            }
 ]


add `Serf#stats` test

add `Serf#stats` test

--- test/serf.coffee
+++ test/serf.coffee
@@ -18,6 +18,11 @@
   before (done) ->
     client = Serf.connect {port: 7373}, done

+  it 'should stats', (done) ->
+    client.stats {}, (result) ->
+      assert.equal 'agent-one', result.agent.name
+      done if result.Error is '' then undefined else result.Error
+
   it 'should join', (done) ->
     client.join {Existing: ['127.0.0.1:7947'], Replay: false}, (result) ->
       done if result.Error is '' then undefined else result.Error


intercept ajax problem before login box displayed

--- BackofficeBundle/Resources/public/coffee/setUpCallAjax.coffee
+++ BackofficeBundle/Resources/public/coffee/setUpCallAjax.coffee
@@ -2,6 +2,8 @@
 $(document).ready ->
   $.ajaxSetup
     beforeSend: (xhr, settings) ->
+      @intercept(xhr.success, isLoginForm(xhr.responseText))
+      @intercept(xhr.error, isAccessDenied(xhr.responseText))
       xhrFifo.push(xhr)
       context = settings.context
       displayLoader(context.button) if context != undefined && context.button != undefined
@@ -9,7 +11,11 @@
       for i of xhrFifo
         xhrFifo[i].abort()
       xhrFifo = []
-  $(document).ajaxError (event, jqXHR, settings) ->
-    redirectToLogin() if isAccessDenied(jqXHR.responseText)
-  $(document).ajaxSuccess (event, xhr, settings) ->
-    redirectToLogin() if isLoginForm(xhr.responseText)
+    intercept: (method, condition) ->
+      oldMethod = method
+      method = do ->
+        if condition
+          redirectToLogin()
+        else
+          oldMethod.apply this, arguments
+        return


Adds WPCC to the Syndication tool list

Adds WPCC to the Syndication tool list

--- app/assets/javascripts/syndication/tools.js.coffee
+++ app/assets/javascripts/syndication/tools.js.coffee
@@ -315,6 +315,16 @@
       width: "100%"
       title: 'Money Manager'

+    wpcc:
+      en:
+        path: 'en/tools/workplace-pension-contribution-calculator'
+      cy:
+        path: 'cy/tools/cyfrifiannell-cyfraniadau-pensiwn-gweithle'
+      width: "100%"
+      height: "1000px"
+      include_ga: false
+      title: 'Workplace Pension Contribution Calculator'
+
     default_dimensions:
       width: "680px"
       height: "1000px"


added missing clearImmediate

added missing clearImmediate

--- platforms/remote/lib/Platform.coffee
+++ platforms/remote/lib/Platform.coffee
@@ -41,24 +41,32 @@

             ## setImmediate emulation
             if !window.setImmediate
-                pending = []
+                pending = {}
+                TRIGGERID = 0

-                triggerID = null
                 if window.postMessage
                     window.addEventListener 'message', (e)->
-                        if e.origin is location.origin && triggerID && e.data is triggerID
-                            if pending.length
-                                pending.shift()()
+                        triggerID = e.data
+                        if e.origin is location.origin && pending[triggerID]
+                            pending[triggerID]()
+                            window.clearImmediate triggerID

                 window.setImmediate = (fn)->
-                    if !window.postMessage
-                        triggerID = 'floyd-exec-pending-immediate-trigger-'+floyd.tools.strings.uuid()
-                        pending.push fn
+                    if window.postMessage
+                        triggerID = TRIGGERID++
+                        pending[triggerID] = fn
                         window.postMessage triggerID, location.origin

                     else ## fallback
-                        setTimeout fn, 1
+                        triggerID = setTimeout fn, 1

+                    return triggerID
+
+                window.clearImmediate = (triggerID)->
+                    if window.postMessage
+                        delete pending[triggerID] if pending[triggerID]
+                    else
+                        clearTimeout triggerID

         ##
         ##


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -3,7 +3,7 @@
 test "text moves up vertically", ->
   ;

-test "text fades out"
+test "text fades out", ->

 module()


clearer for loop syntax

clearer for loop syntax

--- code/config.coffee
+++ code/config.coffee
@@ -10,5 +10,6 @@
     config = JSON.parse text
     if config.include?
       @_load_config config.include
-    if key isnt 'include' and not @[key]? then @[key] = value for key, value of config
-
+    for key, value of config
+      do (key, value) =>
+        if key isnt 'include' then @[key] = value


clean up streaming api logic

clean up streaming api logic

--- src/hubot/campfire.coffee
+++ src/hubot/campfire.coffee
@@ -98,15 +98,32 @@

       request = HTTPS.request options, (response) ->
         response.setEncoding("utf8")
+
+        buf = ''
         response.on "data", (chunk) ->
-          if chunk.match(/^\S+/)
-            console.log "#{new Date}: Received #{id} \"#{chunk}\""
-            try
-              chunk.split("\r").forEach (part) ->
-                data = JSON.parse part
+          if chunk == ' '
+            # campfire api sends a ' ' heartbeat every 3s

-                self.emit data.type, data.id, data.created_at, data.room_id, data.user_id, data.body
-                data
+          else if chunk.match(/^\s*Access Denied/)
+            # errors are not json formatted
+            console.log "Error", chunk
+            process.exit(1)
+
+          else
+            # api uses newline terminated json payloads
+            # buffer across tcp packets and parse out lines
+            buf += chunk
+
+            while (offset = buf.indexOf("\r")) > -1
+              part = buf.substr(0, offset)
+              buf = buf.substr(offset + 1)
+
+              if part
+                try
+                  data = JSON.parse part
+                  self.emit data.type, data.id, data.created_at, data.room_id, data.user_id, data.body
+                catch err
+                  console.log(err)

         response.on "end", ->
           console.log "Streaming Connection closed. :("


Remove commented out old code that snuck in to previous commit.

--- server/methods/rentalProducts.coffee
+++ server/methods/rentalProducts.coffee
@@ -1,20 +1,3 @@
-# dateBinarySearch = (dates, date) ->
-#   min = 0
-#   max = dates.length -1
-#
-#   while min <= max
-#     guess = Math.floor((min + max) / 2)
-#
-#     if +dates[guess] == +date
-#       return guess
-#     else
-#       if +dates[guess] < +date
-#         min = guess + 1
-#       else
-#         max = guess - 1
-#
-#   return -1
-
 # Helper function to see if any of the requestedDates are already reserved.
 # reservedDates is an array of sorted dates that
 #   is not guaranteed to be consecutive
@@ -128,46 +111,9 @@
           requestedVariants.push(item._id)
           if requestedVariants.length >= quantity
             break
-            # return {
-            #   variants: requestedVariants,
-            #   quantity: requestedVariants.length,
-            #   message: 'available'
-            # }
-      # if we get to the end of the loop and have more than one requested
-      # variants available, but less than the quantity requested.
-      # if requestedVariants.length >= 1
-      #   return {
-      #     variants: requestedVariants,
-      #     quantity: requestedVariants.length,
-      #     message: 'available'
-      #   }
+
     else
       if _.isEmpty(_.intersection(variant.unavailableDates, requestedDates))
         requestedVariants.push(variant._id)
-        # if requestedVariants.length >= quantity
-        #   return {
-        #     variants: requestedVariants,
-        #     quantity: quantity,
-        #     message: 'available'
-        #   }
-
-    # if requestedVariants.length == quantity
-    #   message = 'available'
-    # else if requestedVariants.length >= 1
-    #   message = 'partial availability'
-    # else
-    #   message = 'unavailable'

     return requestedVariants
-
-
-    #  If variant has inventoryChildren
-      #  Loop through children
-        #  If inventory variant has availibility
-          #  push it into reserveUIDs
-          #  if reserveUIDs.length is == quantity
-            #  break out of loop
-    # Else
-      # if variant has availibility
-        # push variant into reserveUIDs
-


Delay migration of Layout_Login_Header

Delay migration of Layout_Login_Header

--- server/startup/migrations/v36.coffee
+++ server/startup/migrations/v36.coffee
@@ -13,9 +13,11 @@
 			if requestUrl[0] is '/'
 				requestUrl = url.resolve(Meteor.absoluteUrl(), requestUrl)

-			result = HTTP.get requestUrl, npmRequestOptions: {encoding: 'binary'}
-			if result.statusCode is 200
-				RocketChat.Assets.setAsset(result.content, result.headers['content-type'], 'logo')
+			try
+				Meteor.startup ->
+					result = HTTP.get requestUrl, npmRequestOptions: {encoding: 'binary'}
+					if result.statusCode is 200
+						RocketChat.Assets.setAsset(result.content, result.headers['content-type'], 'logo')

 		RocketChat.models.Settings.remove _id: 'Layout_Login_Header'


Add do..while snippets

Add do..while snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -25,6 +25,10 @@
     'prefix': 'while'
     'body': 'while (${1:condition}) {\n\t$2\n}'

+  'do..while':
+    'prefix': 'dowhile'
+    'body': 'do {\n\t$1\n} while (${2:condition});'
+
   # Switch constructs

   'switch':
@@ -390,6 +394,10 @@
     'prefix': 'while'
     'body': 'while ( ${1:condition} ) {\n\t$2\n}'

+  'do..while':
+    'prefix': 'dowhile'
+    'body': 'do {\n\t$1\n} while ( ${2:condition} );'
+
   'array (one-line)':
     'prefix': 'ar'
     'body': 'array( $1 )'


avoid rendering errors with mathjax

avoid rendering errors with mathjax

--- shared/src/helpers/mathjax.coffee
+++ shared/src/helpers/mathjax.coffee
@@ -1,4 +1,4 @@
-_ = require 'underscore'
+_ = require 'lodash'

 MATH_MARKER_BLOCK  = '\u200c\u200c\u200c' # zero-width non-joiner
 MATH_MARKER_INLINE = '\u200b\u200b\u200b' # zero-width space
@@ -9,9 +9,9 @@
 COMBINED_MATH_SELECTOR = "#{MATH_DATA_SELECTOR}, #{MATH_ML_SELECTOR}"

 # Search document for math and [data-math] elements and then typeset them
-typesetDocument = (windowImpl) ->
+typesetDocument = (root, windowImpl) ->
   latexNodes = []
-  for node in windowImpl.document.querySelectorAll(MATH_DATA_SELECTOR)
+  for node in root.querySelectorAll(MATH_DATA_SELECTOR)
     formula = node.getAttribute('data-math')
     # divs should be rendered as a block, others inline
     if node.tagName.toLowerCase() is 'div'
@@ -20,23 +20,34 @@
       node.textContent = "#{MATH_MARKER_INLINE}#{formula}#{MATH_MARKER_INLINE}"
     latexNodes.push(node)

-  unless _.isEmpty(latexNodes)
-    windowImpl.MathJax.Hub.Typeset(latexNodes)
+  windowImpl.MathJax.Hub.Queue(
+    ->
+      if _.isEmpty(latexNodes)
+        return

-  mathMLNodes = _.toArray(windowImpl.document.querySelectorAll(MATH_ML_SELECTOR))
-  unless _.isEmpty(mathMLNodes)
-    # style the entire document because mathjax is unable to style individual math elements
-    windowImpl.MathJax.Hub.Typeset( windowImpl.document )
+      windowImpl.MathJax.Hub.Typeset(latexNodes)
+    ->
+      mathMLNodes = _.toArray(root.querySelectorAll(MATH_ML_SELECTOR))
+      if _.isEmpty(mathMLNodes)
+        return

-  windowImpl.MathJax.Hub.Queue ->
-    # Queue a call to mark the found nodes as rendered so are ignored if typesetting is called repeatedly
-    # uses className += instead of classList because IE
-    for node in latexNodes.concat(mathMLNodes)
-      node.className += " #{MATH_RENDERED_CLASS}"
+      # style the entire document because mathjax is unable to style individual math elements
+      windowImpl.MathJax.Hub.Typeset( root )
+    ->
+      # Queue a call to mark the found nodes as rendered so are ignored if typesetting is called repeatedly
+      # uses className += instead of classList because IE
+      for node in latexNodes
+        node.className += " #{MATH_RENDERED_CLASS}"
+  )

-# Install a debounce around typesetting function so that it will only run once
-# every Xms even if called multiple times in that period
-typesetDocument = _.debounce( typesetDocument, 100)
+_.memoize.Cache = WeakMap
+# memoize'd getter for typeset document function so that each node's
+# typeset has its own debounce
+getTypesetDocument = _.memoize((root, windowImpl) ->
+  # Install a debounce around typesetting function so that it will only run once
+  # every Xms even if called multiple times in that period
+  _.debounce(typesetDocument, 100).bind(null, root, windowImpl)
+)

 # typesetMath is the main exported function.
 # It's called by components like HTML after they're rendered
@@ -44,7 +55,7 @@
   # schedule a Mathjax pass if there is at least one [data-math] or <math> element present

   if windowImpl.MathJax?.Hub?.Queue? and root.querySelector(COMBINED_MATH_SELECTOR)
-    typesetDocument(windowImpl)
+    getTypesetDocument(root, windowImpl)()

 # The following should be called once and configures MathJax.


Add deprecation to Marker::matchesAttributes

--- src/marker.coffee
+++ src/marker.coffee
@@ -502,6 +502,7 @@
     EmitterMixin::on.apply(this, arguments)

   Marker::matchesAttributes = (args...) ->
+    Grim.deprecate("Use Marker::matchesParams instead.")
     @matchesParams(args...)

   Marker::getAttributes = ->


Add in another date util snippet.

Add in another date util snippet.

--- CoffeeScripts/date_time_util.coffee
+++ CoffeeScripts/date_time_util.coffee
@@ -48,5 +48,14 @@

   "#{formattedHour}#{formattedMinute} #{amPM}"

+TN.encodeDate = (date) ->
+  "#{date.getFullYear()}-" +
+    "#{TN.intWithLeadingZeros(date.getMonth() + 1, 2)}-" +
+    "#{TN.intWithLeadingZeros(date.getDate(), 2)}"
+
+TN.decodeDate = (dateString) ->
+  new Date(
+    dateString.slice(0,4), dateString.slice(5,7) - 1, dateString.slice(8))
+
 TN.msPerWeek = 7 * 24 * 60 * 60 * 1000
 TN.msPerYear = TN.msPerWeek * 52


refactoring

refactoring

--- src/lib/Attachment.coffee
+++ src/lib/Attachment.coffee
@@ -12,12 +12,12 @@
     @data = data
     @hash = this._hash(data)

-  _hash: (data) ->
+  _hash: () ->
     md5 = crypto.createHash "md5"
     md5.setEncoding "hex"
     md5.write @data
     md5.end()
-    hash = md5.read()
+    md5.read()

   setFileName: (fileName) ->
     if fileName


added initial wordCount operation

added initial wordCount operation

--- src/lib/background.coffee
+++ src/lib/background.coffee
@@ -648,6 +648,11 @@
   # Route message as if it was sent internally.
   onMessage message, sender, sendResponse

+# Avoid repetitive calls to render the text contents of a Mustache section by passed `callback` the
+# pre-rendered text and safely handling bad returns.
+rendered = (callback) ->
+  -> (text, render) -> callback(render text) ? ''
+
 # Attempt to select a tab in the current window displaying a page whose location begins with
 # `url`.
 # If no existing tab exists a new one is created.
@@ -851,13 +856,9 @@
         log.debug 'Found the following cookies...', cookies

         done null,
-          cookie: ->
-            (text, render) ->
-              # Attempt to find the value for the cookie name.
-              name   = render text
-              cookie = _.findWhere cookies, {name}
-
-              cookie?.value or ''
+          cookie: rendered (text) ->
+            # Attempt to find the value for the cookie name.
+            _.findWhere(cookies, name: text)?.value
           cookies: _.chain(cookies).pluck('name').uniq().value()

     (done) ->
@@ -888,9 +889,8 @@
           description:    response.description    ? ''
           images:         response.images         ? []
           keywords:       response.keywords       ? []
-          lastmodified:   ->
-            (text, render) ->
-              lastModified?.format(render(text) or undefined) ? ''
+          lastmodified:   rendered (text) ->
+            lastModified?.format if text then text
           linkhtml:       response.linkHTML       ? ''
           links:          response.links          ? []
           linktext:       response.linkText       ? ''
@@ -975,75 +975,59 @@
   # modified version of [mustache.js](https://github.com/janl/mustache.js).
   $.extend data, url.attr(),
     # Deprecated since 1.2.5, use `linkstarget` instead.
-    anchortarget:          ->
-      @linkstarget
+    anchortarget:          -> @linkstarget
     # Deprecated since 1.2.5, use `linkstitle` instead.
-    anchortitle:           ->
-      @linkstitle
+    anchortitle:           -> @linkstitle
     bitly:                 bitly.enabled
     bitlyaccount:          ->
       _.findWhere(SHORTENERS, name: 'bitly').oauth.hasAccessToken()
     browser:               browser.title
     browserversion:        browser.version
-    capitalise:            ->
-      do @capitalize
-    capitalize:            ->
-      (text, render) ->
-        utils.capitalize render text
+    capitalise:            -> do @capitalize
+    capitalize:            rendered (text) ->
+      utils.capitalize text
     # Deprecated since 1.0.0, use `menu` instead.
-    contextmenu:           ->
-      @menu
+    contextmenu:           -> @menu
     cookiesenabled:        navigator.cookieEnabled
     count:                 stats.count
     customcount:           stats.customCount
-    datetime:              ->
-      (text, render) ->
-        new Date().format(render(text) or undefined) ? ''
-    decode:                ->
-      (text, render) ->
-        decodeURIComponent(render text) ? ''
+    datetime:              rendered (text) ->
+      new Date().format if text then text
+    decode:                rendered (text) ->
+      decodeURIComponent text
     depth:                 screen.colorDepth
     # Deprecated since 1.0.0, use `linkstarget` instead.
-    doanchortarget:        ->
-      @linkstarget
+    doanchortarget:        -> @linkstarget
     # Deprecated since 1.0.0, use `linkstitle` instead.
-    doanchortitle:         ->
-      @linkstitle
-    encode:                ->
-      (text, render) ->
-        encodeURIComponent(render text) ? ''
+    doanchortitle:         -> @linkstitle
+    encode:                rendered (text) ->
+      encodeURIComponent text
     # Deprecated since 0.1.0.2, use `encode` instead.
     encoded:               ->
       encodeURIComponent @url
-    escape:                ->
-      (text, render) ->
-        _.escape render text
+    escape:                rendered (text) ->
+      _.escape text
     favicon:               ctab.favIconUrl
-    fparam:                ->
-      (text, render) ->
-        url.fparam(render text) ? ''
+    fparam:                rendered (text) ->
+      url.fparam text
     fparams:               nullIfEmpty url.fparam()
-    fsegment:              ->
-      (text, render) ->
-        url.fsegment(parseInt render(text), 10) ? ''
+    fsegment:              rendered (text) ->
+      url.fsegment parseInt text, 10
     fsegments:             url.fsegment()
     googl:                 googl.enabled
     googlaccount:          ->
       _.findWhere(SHORTENERS, name: 'googl').oauth.hasAccessToken()
     # Deprecated since 1.0.0, use `googlaccount` instead.
-    googloauth:            ->
-      do @googlaccount
+    googloauth:            -> do @googlaccount
     java:                  navigator.javaEnabled()
-    length:                ->
-      (text, render) ->
-        render(text).length
+    length:                rendered (text) ->
+      text.length
     linkmarkdown:          ->
       toMarkdown @linkhtml
     linkstarget:           links.target
     linkstitle:            links.title
-    lowercase:             ->
-      (text, render) ->
-        render(text).toLowerCase()
+    lowercase:             rendered (text) ->
+      text.toLowerCase()
     markdowninline:        markdown.inline
     menu:                  menu.enabled
     menuoptions:           menu.options
@@ -1052,22 +1036,19 @@
     notificationduration:  notifications.duration * .001
     offline:               not navigator.onLine
     # Deprecated since 0.1.0.2, use `originalurl` instead.
-    originalsource:        ->
-      @originalurl
+    originalsource:        -> @originalurl
     originaltitle:         tab.title or url.attr 'source'
     originalurl:           tab.url
     os:                    operatingSystem
-    param:                 ->
-      (text, render) ->
-        url.param(render text) ? ''
+    param:                 rendered (text) ->
+      url.param text
     params:                nullIfEmpty url.param()
     plugins:               _.chain(navigator.plugins).pluck('name').uniq().value()
     popular:               $.extend yes, {}, _.findWhere ext.templates, key: stats.popular
     screenheight:          screen.height
     screenwidth:           screen.width
-    segment:               ->
-      (text, render) ->
-        url.segment(parseInt render(text), 10) ? ''
+    segment:               rendered (text) ->
+      url.segment parseInt text, 10
     segments:              url.segment()
     select:                ->
       getCallback 'select'
@@ -1084,48 +1065,49 @@
     selectmarkdown:        ->
       getCallback 'selectmarkdown'
     # Deprecated since 1.0.0, use `shorten` instead.
-    short:                 ->
-      do @shorten
+    short:                 -> do @shorten
     shortcuts:             shortcuts.enabled
     shortcutspaste:        shortcuts.paste
     shorten:               ->
       getCallback 'shorten'
-    tidy:                  ->
-      (text, render) ->
-        render(text).replace(/([ \t]+)/g, ' ').trim()
+    tidy:                  rendered (text) ->
+      text.replace(/([ \t]+)/g, ' ').trim()
     title:                 ctab.title or url.attr 'source'
     toolbarclose:          toolbar.close
     # Deprecated since 1.0.0, use the inverse of `toolbarpopup` instead.
-    toolbarfeature:        ->
-      not @toolbarpopup
+    toolbarfeature:        -> not @toolbarpopup
     # Deprecated since 1.0.0, use `toolbarstyle` instead.
-    toolbarfeaturedetails: ->
-      @toolbarstyle
+    toolbarfeaturedetails: -> @toolbarstyle
     # Deprecated since 1.0.0, use `toolbarkey` instead.
-    toolbarfeaturename:    ->
-      @toolbarkey
+    toolbarfeaturename:    -> @toolbarkey
     toolbarkey:            toolbar.key
     toolbaroptions:        toolbar.options
     toolbarpopup:          toolbar.popup
     # Obsolete since 1.1.0, functionality has been removed.
     toolbarstyle:          no
-    trim:                  ->
-      (text, render) ->
-        render(text).trim()
-    trimleft:              ->
-      (text, render) ->
-        render(text).trimLeft()
-    trimright:             ->
-      (text, render) ->
-        render(text).trimRight()
-    unescape:              ->
-      (text, render) ->
-        _.unescape render text
-    uppercase:             ->
-      (text, render) ->
-        render(text).toUpperCase()
+    trim:                  rendered (text) ->
+      text.trim()
+    trimleft:              rendered (text) ->
+      text.trimLeft()
+    trimright:             rendered (text) ->
+      text.trimRight()
+    unescape:              rendered (text) ->
+      _.unescape text
+    uppercase:             rendered (text) ->
+      text.toUpperCase()
     url:                   url.attr 'source'
     version:               ext.version
+    wordcount:             rendered (text) ->
+      # Oddly, this is the optimal method to calculate the word count on WebKit browsers.
+      count   = 0
+      text    = text.trim()
+      matches = text.match /\s+/g
+
+      if text
+        count++
+        count+= matches.length if matches
+
+      count
     xpath:                ->
       getCallback 'xpath'
     xpathall:             ->


Parse opening hours.

Parse opening hours.

--- src/models.coffee
+++ src/models.coffee
@@ -82,6 +82,16 @@
     class Unit extends SMModel
         resource_name: 'unit'
         translated_attrs: ['name', 'description', 'street_address']
+        parse: (response, options) ->
+            opening_hours = _.filter response.connections, (c) ->
+                c.section == 'opening_hours' and c.type == 0
+            if opening_hours.length > 0
+                response.opening_hours = opening_hours[0].name[p13n.get_language()]
+
+            opening_hours = _.filter response.connections, (c) ->
+                c.section == 'opening_hours' and c.type == 0
+
+            response

     class UnitList extends SMCollection
         model: Unit


neo4j: add new member buckets

neo4j: add new member buckets

--- server/lib/server/graph/fetch.coffee
+++ server/lib/server/graph/fetch.coffee
@@ -16,7 +16,7 @@
     @newMemberBucketIndex = null

   get:(callback)->
-    methods = [@fetchSingles, @fetchTagFollows, @fetchMemberFollows, @fetchInstalls]
+    methods = [@fetchSingles, @fetchTagFollows, @fetchMemberFollows, @fetchInstalls, @fetchNewMembers]
     holder = []
     boundMethods = holder.push method.bind this for method in methods
     async.parallel holder, (err, results)=>


Add default { template } delimiters.

Add default { template } delimiters.

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -20,6 +20,7 @@
   # overridden globally or local to a `Rivets.View` instance.
   config:
     prefix: 'rv'
+    templateDelimiters: ['{', '}']
     rootInterface: '.'
     preloadData: true


Add an orchestration for the publish_to_ilr script

--- src/server.coffee
+++ src/server.coffee
@@ -61,14 +61,29 @@
 dhisToIlr = (out, callback) ->
   out.info "Running dhis-to-ilr ..."
   args = buildArgs()
+  beforeTimestamp = new Date()
   script = spawn('bash', args)
   out.info "Executing bash script #{args.join ' '}"

+  logs = ""
   script.stdout.on 'data', out.info
+  script.stdout.on 'data', (data) -> logs += data.toString() + '\n'
   script.stderr.on 'data', out.error
+  script.stderr.on 'data', (data) -> logs += data.toString() + '\n'

   script.on 'close', (code) ->
     out.info "Script exited with status #{code}"
+    logs += "Script exited with status #{code}"
+
+    out.pushOrchestration
+      name: 'Execute Publish_to_ilr.sh'
+      request:
+        timestamp: beforeTimestamp
+      response:
+        status: code
+        body: logs
+        timestamp: new Date()
+
     callback code is 0


Allowing override of main hoard namespace

--- src/server.coffee
+++ src/server.coffee
@@ -9,6 +9,7 @@
   constructor: (@conf, @cli) ->
     @sPath = @conf.scriptPath
     @fqdn = @conf.fqdn.split('.').join('_')
+    @namespace = @conf.namespace or 'hoard'
     @samplesRun = 0
     @util = Util


Remove logger statements slowing everything down!

--- src/server.coffee
+++ src/server.coffee
@@ -27,7 +27,7 @@
   # connect socket.io client to url, bind to socket.io events and our custom events
   #
   _setup_sio: ->
-    logger.debug "Socket.io connecting to url #{@_url()}"
+    # logger.debug "Socket.io connecting to url #{@_url()}"
     socket = new io(@_url(), {})
     socket.on 'connect', =>
       console.log "connected to socket at #{@_url()}"
@@ -52,12 +52,14 @@
         @_write_file()
         return
       else
+        console.log "writing to file"
+        console.log buffer
         ws.write(buffer, (err, written) =>
           throw err if err
-          # ws.end()
+          buffer = ''
           @_write_file()
         )
-    ), 25
+    ), 15

   #
   # convert halo rgba string to a UART instruction
@@ -66,15 +68,15 @@
   #   # => '4,1,000,110,255,000;'
   #
   _data_to_instruction: (data) ->
-    console.log "called _data_to_instruction"
-    console.log data
+    # console.log "called _data_to_instruction"
+    # console.log data

     # break colors string into array
     colors = data.color.split '\n'
     colors.pop()

     # build our TTY instruction
-    logger.debug "colors: #{colors} length: #{colors.length}"
+    # logger.debug "colors: #{colors} length: #{colors.length}"
     instruction = ''
     instruction += "4,#{i+1},#{color};" for color, i in colors

@@ -87,14 +89,14 @@
   # write socket.io color data to buffer as UART instruction
   #
   _to_buffer: (data) ->
-    logger.info "writing to buffer #{data.color}"
+    # logger.info "writing to buffer #{data.color}"
     buffer = @_data_to_instruction(data)

   #
   # empty the buffer
   #
   _clear_buffer: ->
-    logger.info "clearing buffer"
+    # logger.info "clearing buffer"
     buffer = ''

 module.exports = Server


add corgi bomb

add corgi bomb

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -15,4 +15,10 @@
    robot.respond /corgi me/i, (msg) ->
      msg.http("http://corginator.herokuapp.com/random")
       .get() (err, res, body) ->
-        msg.send JSON.parse(body).corgi+        msg.send JSON.parse(body).corgi
+
+   robot.respond /corgi bomb( (\d+))?/i, (msg) ->
+     count = msg.match[2] || 5
+     msg.http("http://corginator.herokuapp.com/bomb?count=" + count)
+      .get() (err, res, body) ->
+        msg.send corgi for corgi in JSON.parse(body).corgis


feat(queued/process_nodemailer.coffee): 调整nodemailer

--- queued/process_nodemailer.coffee
+++ queued/process_nodemailer.coffee
@@ -16,11 +16,13 @@
   client.get "mail_settings", (err, reply) ->
     if reply
       config = JSON.parse(reply)
+      console.log config
+      console.log config.smtp_server
     else
       console.log 'SMTP configure not found'
       return
     # Build nodemailer transporter
-    transporter = nodemailer.createTransport('smtps://'+encodeURIComponent(config.smtp_login)+':'+config.smtp_password+'@'+config.smtp_server)
+    transporter = nodemailer.createTransport('smtp://'+encodeURIComponent(config.smtp_login)+':'+config.smtp_password+'@'+config.smtp_server)

     # According to the receive group send to mail
     db.collection('users').find({"group":data.receive_group}).toArray (err, users) ->


Improve coffee style

Improve coffee style

--- scripts/players.coffee
+++ scripts/players.coffee
@@ -342,7 +342,7 @@
 , false)

 document.addEventListener "keypress", ((e) ->
-  key = (if typeof window.event is "undefined" then e.which else window.event.keyCode)
+  key = (if not window.event? then e.which else window.event.keyCode)
   if key is 32
     video = document.getElementsByTagName("video")[0]
     if video
@@ -351,5 +351,4 @@
         video.play()
       else
         video.pause()
-  return
 ), true


deleted last line break in plugins

deleted last line break in plugins

--- lib/Bumped.plugins.coffee
+++ lib/Bumped.plugins.coffee
@@ -31,6 +31,7 @@

   print: (err, message, cb) ->
     return cb err if err
+    message = message.replace(/\n$/, '') # deletes last line break
     @bumped.logger.output message
     cb()


Setting a more useful initial zoom for the viewer

--- assets/js/viewer.coffee
+++ assets/js/viewer.coffee
@@ -21,7 +21,7 @@
     @mmToGLCoords = 0.1

     @rotation = new PhiloGL.Vec3(Math.PI*3/2+0.2, 0, 0)
-    @position = new PhiloGL.Vec3(0, -10, -70)
+    @position = new PhiloGL.Vec3(0, -10, -40)
     #@position = new PhiloGL.Vec3(0, 0, -70)

   o3dDefaults:


Add spec for quoted '::'

Add spec for quoted '::'

--- spec/language-haskell-spec.coffee
+++ spec/language-haskell-spec.coffee
@@ -140,6 +140,21 @@
         ]
       ]

+    it 'doesnt get confused by quoted ::', ->
+      g = grammarExpect(grammar, '("x :: String -> IO ()" ++ var)')
+      g.toHaveScopes [['source.haskell']]
+      g.toHaveTokenScopes [
+        [ "("
+        , "\""
+        , "x :: String -> IO ()" : ['string.quoted.double.haskell']
+        , "\""
+        , " "
+        , "++" : ['keyword.operator.haskell']
+        , " "
+        , "var" : ['identifier.haskell']
+        , ")"
+        ]
+      ]

     it 'parses in-line non-parenthesised declarations', ->
       g = grammarExpect(grammar, 'main = putStrLn "Hello World" :: IO ()')


Hound comments fixed

Hound comments fixed

--- lib/conekta-button.coffee
+++ lib/conekta-button.coffee
@@ -125,9 +125,10 @@
             window.console.error 'Missing "Name" Attribute'
             return false

-        if arguments[0].onSuccess? && typeof arguments[0].onSuccess != "function"
-            window.console.error 'Missing "Name" Attribute'
-            return false
+        if arguments[0].onSuccess?
+            if typeof arguments[0].onSuccess != "function"
+                window.console.error "Unexpected type of 'onSuccess'! Expected \"function\", got #{typeof arguments[0].onSuccess}"
+                return false

         params.amount = arguments[0].amount
         params.name = arguments[0].name


Show map type controls

Show map type controls

--- views/map.coffee
+++ views/map.coffee
@@ -5,7 +5,8 @@
       lng: 20.8
     zoom: 3
     disableDefaultUI: true,
-    zoomControl: true
+    zoomControl: true,
+    mapTypeControl: true
   )
   setDayNightCycleOverlay(map)
   loadSessions(map)


For Ctrl-[, also require NOT Alt

For Ctrl-[, also require NOT Alt

See #1906.

It is not obvious that this is in fact correct.  In particular, it's not
clear how `Ctrl-[` should work on Mac keyboards.

--- lib/keyboard_utils.coffee
+++ lib/keyboard_utils.coffee
@@ -53,7 +53,7 @@

   isEscape: (event) ->
     # c-[ is mapped to ESC in Vim by default.
-    (event.keyCode == @keyCodes.ESC) || (event.ctrlKey && @getKeyChar(event) == '[')
+    (event.keyCode == @keyCodes.ESC) || (event.ctrlKey && @getKeyChar(event) == '[' and not event.metaKey)

   # TODO. This is probably a poor way of detecting printable characters.  However, it shouldn't incorrectly
   # identify any of chrome's own keyboard shortcuts as printable.


Update jenkins_summary.coffee

Update jenkins_summary.coffee

--- widgets/jenkins_summary/jenkins_summary.coffee
+++ widgets/jenkins_summary/jenkins_summary.coffee
@@ -14,7 +14,7 @@
         elem.scrollTop = 0
         window.scroll_height = 0
       return
-    ), 100
+    ), 50

   @accessor 'green', Dashing.AnimatedValue


Removed jQuery Dependency from Main

Removed jQuery Dependency from Main

--- js/main.coffee
+++ js/main.coffee
@@ -9,11 +9,11 @@
     jquery: '//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min'
     underscore: '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min'

-require ['jquery', 'underscore', 'particle', 'vector', 'camera', 'time'], ($, _, Particle, Vector, Camera, Time) ->
+require ['underscore', 'particle', 'vector', 'camera', 'time'], (_, Particle, Vector, Camera, Time) ->
   MAX_UPDATE_RATE = 0
   PARTICLE_COUNT = 100

-  canvas = $('canvas')[0]
+  canvas = document.getElementsByTagName('canvas')[0]
   context = canvas.getContext '2d'

   for i in [1..PARTICLE_COUNT]


Style

Style

--- lib/styles/default.coffee
+++ lib/styles/default.coffee
@@ -154,4 +154,4 @@
       if sections.example?
         output += "\n\nExample:\n\n#{humanize.gutterify tag.markdown, 4}" for tag in sections.example

-      segment.comments = output.split '\n'+      segment.comments = output.split '\n'


Fix jshint error

Fix jshint error

--- spec/autocomplete-emojis-spec.coffee
+++ spec/autocomplete-emojis-spec.coffee
@@ -17,7 +17,7 @@
     atom.config.set "autocomplete-plus.autoActivationDelay", completionDelay
     completionDelay += 100 # Rendering delay

-    atom.workspaceView = new WorkspaceView
+    atom.workspaceView = new WorkspaceView()
     atom.workspaceView.openSync "sample.js"
     atom.workspaceView.simulateDomAttachment()


add metric for getdoc bytes

add metric for getdoc bytes

--- app/coffee/RedisManager.coffee
+++ app/coffee/RedisManager.coffee
@@ -41,6 +41,7 @@
 			logger.error {err: error, doc_id: doc_id, docLines: docLines}, error.message
 			return callback(error)
 		docHash = RedisManager._computeHash(docLines)
+		metrics.summary "redis.setDoc", docLines.length, {status: "set"}
 		logger.log {project_id, doc_id, version, docHash, pathname, projectHistoryId}, "putting doc in redis"
 		RedisManager._serializeRanges ranges, (error, ranges) ->
 			if error?


Remove PromiseRenderer from collections subject list

--- app/collections/show-list.cjsx
+++ app/collections/show-list.cjsx
@@ -4,7 +4,6 @@
 Translate = require 'react-translate-component'
 counterpart = require 'counterpart'
 Paginator = require '../talk/lib/paginator'
-PromiseRenderer = require '../components/promise-renderer'
 SubjectViewer = require '../components/subject-viewer'
 Loading = require '../components/loading-indicator'
 {Link} = require 'react-router'
@@ -16,40 +15,24 @@
     error: 'There was an error listing this collection.'
     noSubjects: 'No subjects in this collection.'

-module.exports = React.createClass
-  displayName: 'CollectionShowList'
+SubjectNode = React.createClass

   contextTypes:
     geordi: React.PropTypes.object.isRequired
-    router: React.PropTypes.object.isRequired

-  componentDidMount: ->
-    @fetchCollectionSubjects pick @props.location.query, VALID_COLLECTION_MEMBER_SUBJECTS_PARAMS
+  getInitialState: ->
+    project: null
+
+  componentWillMount: ->
+    @fetchProject(@props.subject)
+      .then (project) =>
+        @setState {project}

-  componentWillReceiveProps: (nextProps, nextContext) ->
-    @fetchCollectionSubjects pick nextProps.location.query, VALID_COLLECTION_MEMBER_SUBJECTS_PARAMS
-
-  fetchCollectionSubjects: (query = null) ->
-    query ?= @props.location.query
-
-    defaultQuery =
-      page: 1
-      page_size: 12
-
-    query = Object.assign {}, defaultQuery, query
-    return @props.collection.get 'subjects', query
-
-  onPageChange: (page) ->
-    nextQuery = Object.assign {}, @props.location.query, { page }
-    @context.router.push
-      pathname: @props.location.pathname
-      query: nextQuery
-
-  handleDeleteSubject: (subject) ->
-    @props.collection.removeLink 'subjects', [subject.id.toString()]
-      .then =>
-        @props.collection.uncacheLink 'subjects'
-        @forceUpdate()
+  fetchProject: (subject) ->
+    projectRequest = if @props.collection?.links.project?
+      @props.collection.get('project')
+    else
+      subject.get('project')

   isOwnerOrCollaborator: ->
     collaboratorOrOwnerRoles = @props.roles.filter (collectionRoles) ->
@@ -62,49 +45,92 @@

     return hasPermission

-  fetchProjectOwner: (subject) ->
-    projectRequest = if @props.collection.links.project?
-      @props.collection.get('project')
-    else
-      subject.get('project')
+  render: ->
+    logClick = @context.geordi?.makeHandler? 'about-menu'
+

+      <SubjectViewer defaultStyle={false} subject={@props.subject} user={@props.user}>
+        {if @isOwnerOrCollaborator()
+          <button type="button" className="collection-subject-viewer-delete-button" onClick={@props.onDelete}>
+
+          </button>}
+          <Link className="subject-link" to={"/projects/#{@state.project?.slug}/talk/subjects/#{@props.subject.id}"} onClick={logClick?.bind(this, 'view-favorite')}>
+
+          </Link>
+      </SubjectViewer>
+

+
+module.exports = React.createClass
+  displayName: 'CollectionShowList'

-    projectRequest.then (project) ->
-      [owner, name] = project.slug.split('/')
-      {owner: owner, name: name, id: subject.id}
+  contextTypes:
+    geordi: React.PropTypes.object.isRequired
+    router: React.PropTypes.object.isRequired
+
+  getInitialState: ->
+    subjects: null
+    error: null
+
+  componentWillMount: ->
+    @fetchCollectionSubjects pick @props.location.query, VALID_COLLECTION_MEMBER_SUBJECTS_PARAMS
+      .then (subjects) =>
+        error = null
+        @setState {subjects, error}
+
+  componentWillReceiveProps: (nextProps, nextContext) ->
+    @fetchCollectionSubjects pick nextProps.location.query, VALID_COLLECTION_MEMBER_SUBJECTS_PARAMS
+      .then (subjects) =>
+        error = null
+        @setState {subjects, error}
+
+  fetchCollectionSubjects: (query = null) ->
+    query ?= @props.location.query
+
+    defaultQuery =
+      page: 1
+      page_size: 12
+
+    query = Object.assign {}, defaultQuery, query
+    return @props.collection.get 'subjects', query
+      .catch (error) =>
+        @setState {error}
+
+  onPageChange: (page) ->
+    nextQuery = Object.assign {}, @props.location.query, { page }
+    @context.router.push
+      pathname: @props.location.pathname
+      query: nextQuery
+
+  handleDeleteSubject: (subject) ->
+    subjects = @state.subjects
+    index = subjects.indexOf subject
+    subjects.splice index, 1
+    @setState {subjects}
+
+    @props.collection.removeLink 'subjects', [subject.id.toString()]
+      .then =>
+        @props.collection.uncacheLink 'subjects'

   render: ->
-    logClick = @context.geordi?.makeHandler? 'about-menu'
-    subjectNode = (subject) =>
-

-        <SubjectViewer defaultStyle={false} subject={subject} user={@props.user}>
-          {if @isOwnerOrCollaborator()
-            <button type="button" className="collection-subject-viewer-delete-button" onClick={@handleDeleteSubject.bind @, subject}>
-
-            </button>}
-          <PromiseRenderer promise={@fetchProjectOwner(subject)}>{ (params) =>
-            <Link className="subject-link" to={"/projects/#{params.owner}/#{params.name}/talk/subjects/#{subject.id}"} onClick={logClick?.bind(this, 'view-favorite')}>
-
-            </Link>
-          }</PromiseRenderer>
-        </SubjectViewer>
-

-
-    pendingFunc = ->
-      <Loading />
-
-    catchFunc = (e) ->
-      <Translate component="p" className="form-help error" content="collectionSubjectListPage.error" />
-
-    thenFunc = (subjects) =>
+    if @state.subjects?

-        {if subjects.length is 0
+        {if @state.subjects.length is 0
           <Translate component="p" content="collectionSubjectListPage.noSubjects" />}

-        {if subjects.length > 0
-          meta = subjects[0].getMeta()
+        {if @state.subjects.length > 0
+          meta = @state.subjects[0].getMeta()

-
{subjects.map(subjectNode)}

+

+              {@state.subjects.map (subject) =>
+                <SubjectNode
+                  key={subject.id}
+                  subject={subject}
+                  user={@props.user}
+                  roles={@props.roles}
+                  onDelete={@handleDeleteSubject.bind @, subject}
+                />
+              }
+

             <Paginator
               page={meta.page}
@@ -112,9 +138,7 @@
               pageCount={meta.page_count} />

}

-
-    <PromiseRenderer
-      promise={@fetchCollectionSubjects()}
-      pending={pendingFunc}
-      catch={catchFunc}
-      then={thenFunc} />
+    else if @state.error
+      <Translate component="p" className="form-help error" content="collectionSubjectListPage.error" />
+    else
+      <Loading />


search for username

search for username

--- client/views/app/sideNav/directMessagesFlex.coffee
+++ client/views/app/sideNav/directMessagesFlex.coffee
@@ -11,14 +11,14 @@
 					# @TODO maybe change this 'collection' and/or template
 					collection: 'UserAndRoom'
 					subscription: 'roomSearch'
-					field: 'name'
+					field: 'username'
 					template: Template.roomSearch
 					noMatchTemplate: Template.roomSearchEmpty
 					matchAll: true
 					filter:
 						type: 'u'
 						_id: { $ne: Meteor.userId() }
-					sort: 'name'
+					sort: 'username'
 				}
 			]
 		}


Changed readable text.

Changed readable text.

--- client/Dashboard/views/permissionsform.coffee
+++ client/Dashboard/views/permissionsform.coffee
@@ -129,6 +129,7 @@
       "JGroupBundle": "Group Bundles"
       "JDomain"     : "Domains"
       "JProxyFilter": "Proxy Filters"
+      "JInvitation" : "Invitations"
     return dictionary[text] or text.charAt(0).toUpperCase()+text.slice(1)

   _getCheckboxName =(module, permission, role)->


Refactor broadcasting set machine user as a function

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -514,6 +514,15 @@
       @statusBar.emit 'ParticipantUnwatched', property

+  broadcastMachineUserChange: (participants, state) ->
+
+    return  if @stateMachine?.state is 'Active'
+
+    type = "#{if state then 'Set' else 'Unset'}MachineUser"
+
+    @broadcastMessage {type, participants}
+
+
   handleSharedMachine: ->

     @unmountMachine @mountedMachine
@@ -929,10 +938,7 @@
     setMachineUser @mountedMachine, usernames, share, (err) =>
       return callback err  if err

-      if @stateMachine.state is 'Active'
-        @broadcastMessage
-          type: "#{if share then 'Set' else 'Unset'}MachineUser"
-          participants: usernames
+      @broadcastMachineUserChange usernames, share

       callback null


Collaboration: Kite: Remove kicked / leaved participants from mounted machine

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -142,6 +142,10 @@
     # remove participant's all data persisted in realtime appInfo
     @removeParticipant username

+    # Remove leaved / kicked participants from the mounted machine
+    @setMachineUser [username], no, (err) ->
+      throwError err  if err
+

   handleParticipantAction: (actionType, changeData) ->

@@ -956,17 +960,11 @@

   endCollaborationForParticipant: (callback) ->

-    socialHelpers.leaveChannel @socialChannel, (err) =>
+    socialHelpers.leaveChannel @socialChannel, (err) ->
       throwError err  if err

     @removeWorkspaceSnapshot()
-
-    unless @mountedMachine.isPermanent()
-      @setMachineUser [nick()], no, (err) =>
-        throwError err  if err
-
     @broadcastMessage type: 'ParticipantWantsToLeave'
-
     callback()


collaboration: use initChannel helper

collaboration: use initChannel helper

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -45,25 +45,10 @@

   initPrivateMessage: (callback) ->

-    {message} = kd.singletons.socialapi
-    nickname  = nick()
-
-    options =
-      type       : 'collaboration'
-      body       : "@#{nickname} initiated the IDE session."
-      purpose    : "#{getCollaborativeChannelPrefix()}#{dateFormat 'HH:MM'}"
-      recipients : [ nickname ]
-      payload    :
-        'system-message' : 'initiate'
-        collaboration    : yes
-
-    message.initPrivateMessage options, (err, channels) =>
-
-      return callback err  if err or (not Array.isArray(channels) and not channels[0])
-
-      [channel] = channels
+    socialHelpers.initChannel (err, channel) =>
+      return callback err  if err
+
       @setSocialChannel channel
-
       @updateWorkspace { channelId : channel.id }
         .then =>
           @workspaceData.channelId = channel.id


Add -H option to fix broken symbolic links error

 From man of ls -H provides: show information for the file the link references rather than for the link itself. So, when some broken links exists it adds the link to the tree with "Link -> Non-Exist" label.

--- client/landing/app/MainApp/fs/fsfolder.coffee
+++ client/landing/app/MainApp/fs/fsfolder.coffee
@@ -21,7 +21,7 @@
     # a = Date.now()
     @kiteController.run
       withArgs  :
-        command : "ls #{FSHelper.escapeFilePath @path} -Llpva --group-directories-first --time-style=full-iso"
+        command : "ls #{FSHelper.escapeFilePath @path} -LHlpva --group-directories-first --time-style=full-iso"
     , (err, response)=>
       # log "------------------------------------------------------------------"
       # log "l flag response in: #{Date.now()-a} msec."


Clean up legend item creation

Clean up legend item creation

--- app/assets/javascripts/lib/views/d3_chart_view.coffee
+++ app/assets/javascripts/lib/views/d3_chart_view.coffee
@@ -132,23 +132,23 @@

     d3.selectAll(".legend-column").each((items) ->
       scope = d3.select(this)
+                .selectAll(".legend-item")
+                .data(items)
+                .enter()
+                .append('div')
+                .attr("class", "legend-item")
+                .classed("hidden", (d) -> d.get('skip'))
+                .classed("target_line", (d) -> d.get('is_target_line'))
+                .on('click', (d) -> legend_click(d))

-      items.forEach((d) =>
-        item = scope.append('div')
-          .attr("class", "legend-item")
-          .classed("hidden", d.get('skip'))
-          .classed("target_line", d.get('is_target_line'))
-          .on('click', legend_click.bind(null, d))
+      scope.append("span")
+        .attr('class', 'rect')
+        .style("background-color", (d) => d.get 'color')

-        item.append("span")
-          .attr('class', 'rect')
-          .style("background-color", () => d.get 'color')
-
-        item.append("span")
-          .text(() ->
-            d.get('label') || I18n.t("output_element_series.#{d.get('key')}")
-          )
-      )
+      scope.append("span")
+        .text((d) ->
+          d.get('label') || I18n.t("output_element_series.#{d.get('key')}")
+        )
     )

   # height of the legend item


for #130 - small js fix for uri space replacement

--- app/assets/javascripts/modules/search_location.coffee
+++ app/assets/javascripts/modules/search_location.coffee
@@ -40,6 +40,6 @@
         .set 'search_form[search_location]', search_location
         .set 'search_form[generated_geolocation]', generated_geolocation
         .toString()
-        .replace '%2B', '%20' # fix $.query tendency to convert space to plus
+        .replace /%2B/g, '%20' # fix $.query tendency to convert space to plus

     link.href = originalBase + changedHref


Fix marking initializer Default preferences.preferences to an empty object, if preferences is null.

--- app/classifier/tasks/drawing/marking-initializer.cjsx
+++ app/classifier/tasks/drawing/marking-initializer.cjsx
@@ -82,7 +82,7 @@
     @onChange()

   handleInitRelease: (e) ->
-    pref = @props.preferences.preferences
+    pref = @props.preferences?.preferences ? {}
     taskDescription = @props.workflow.tasks[@props.annotation.task]
     mark = @props.annotation.value[@props.annotation.value.length - 1]
     MarkComponent = drawingTools[taskDescription.tools[mark.tool].type]


[Backport] regex find links with spaces and format them

--- app/modules/components/wysiwyg/wysiwyg.service.coffee
+++ app/modules/components/wysiwyg/wysiwyg.service.coffee
@@ -78,6 +78,13 @@
             title = linkParams[1] || linkParams[0]

             return '[' + title + '](' + link  + ')'
+
+
+    linkTitleWithSpaces: (text) ->
+        link = /\[[^\]]*\]\(([^\)]*)\)/g # [Title-with-spaces](Title with spaces)
+
+        return text.replace link, (match, p1, offset, str) ->
+            return match.replace /\(.*\)/, '(' + taiga.slugify(p1) + ')'

     replaceUrls: (html) ->
         el = document.createElement( 'html' )
@@ -213,7 +220,8 @@
         }

         text = @.replaceEmojiNameByImgs(text)
-        text = @.pipeLinks(text)
+        text = @.linkTitleWithSpaces(text)
+        text = @.pipeLinks(text)

         md = window.markdownit({
             breaks: true


Server: add new line

Server: add new line

--- servers/lib/server/setsession.coffee
+++ servers/lib/server/setsession.coffee
@@ -31,4 +31,4 @@

     JSession.updateClientIP result.session.clientId, remoteIp, (err)->
       console.log err  if err?
-      next()+      next()


always include builtins module

always include builtins module

--- app/context.coffee
+++ app/context.coffee
@@ -185,7 +185,7 @@

 # the base context contains the loaded modules, and the list of modules to import into every context
 create_base_context = ({modules, imports}={}) ->
-  modules = _.extend({context: exports}, modules)
+  modules = _.extend({context: exports, builtins: Builtins}, modules)
   # TODO find a better home for repl vars
   {modules, imports, repl_vars: {}, prop_vars: {}}


Run intersection test against all polys

Run intersection test against all polys

--- app/js/main.coffee
+++ app/js/main.coffee
@@ -57,10 +57,14 @@
         dragTarget.target.pos = Vec.add where, dragTarget.offset
     keydown: (e) ->
       if e.keyCode is 32 # space
-        triangle = polys[2]
-        square = polys[1]
-        console.log triangle.intersects square

+        # explicit iteration to compare all polys
+        for i in [0..(polys.length-1)]
+          poly = polys[i]
+          for other in polys.slice(i+1)
+            if poly.intersects other
+              poly.hit = true
+              other.hit = true

 class Polygon
   hit: false


Added Go. Tests hook handlers.

Added Go. Tests hook handlers.

--- scripts/test-hooks-handlers.coffee
+++ scripts/test-hooks-handlers.coffee
@@ -82,6 +82,9 @@
   ,
     name: 'perl-hooks-handler'
     repo: 'https://github.com/ungrim97/Dredd-Hooks.git'
+  ,
+    name: 'go-hooks-handler'
+    repo: 'https://github.com/snikch/goodman.git'
 ]

 TRIGGER_KEYWORD = 'tests hook handlers' # inspired by https://help.github.com/articles/closing-issues-via-commit-messages/


Fix initialization of periodic check

Fix initialization of periodic check

--- scripts/watch-web-resources.coffee
+++ scripts/watch-web-resources.coffee
@@ -78,10 +78,12 @@
   resources = robot.brain.get('web_resources')
   room = robot.brain.get('room_web_resources')
   if resources isnt null and room isnt null
-    setInterval(periodicCheck, PERIODIC_CHECKS_INTERVAL, robot, room)
+    periodicCheckId = setInterval(periodicCheck, PERIODIC_CHECKS_INTERVAL, robot, room)

 module.exports = (robot) ->
-  init(robot)
+  robot.brain.on 'loaded', (_) ->
+    if periodicCheckId is null
+      init(robot)

   robot.respond /watch ((?:https?:\/\/)?([^\s]+))$/, (res) ->
     resource = res.match[1]


Bug fix for new moment-timezone package format

--- .Gruntfile.coffee
+++ .Gruntfile.coffee
@@ -88,10 +88,12 @@
   ## Convert timezones into autocompletion list
   fs = require 'fs'
   meta = JSON.parse fs.readFileSync 'node_modules/moment-timezone/data/meta/latest.json', encoding: 'utf8'
-  zones = JSON.parse fs.readFileSync 'node_modules/moment-timezone/data/unpacked/latest.json', encoding: 'utf8'
+  zones = JSON.parse fs.readFileSync 'node_modules/moment-timezone/data/packed/latest.json', encoding: 'utf8'
   timezones =
     for zone in zones.zones
-      name = zone.name
+      #name = zone.name  ## unpacked format
+      name = zone
+      name = name[...name.indexOf '|'] if '|' in name  ## packed format
       if name of meta.zones
         countries = meta.zones[name].countries
         countries = (meta.countries[country].name for country in countries)


fix(web): Fixing compilation error, better manifest depenency

--- apps/actor-web/gulpfile.coffee
+++ apps/actor-web/gulpfile.coffee
@@ -107,7 +107,7 @@
     .pipe gulp.dest './dist/'
     .pipe connect.reload()

-gulp.task 'manifest-prod', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push']
+gulp.task 'manifest:prod', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push'], ->
   gulp.src ['./dist/**/*']
     .pipe manifest {
       hash: true,
@@ -125,7 +125,7 @@
     root: ['./dist/', './']
     livereload: true

-gulp.task 'build', ['manifest-prod']
+gulp.task 'build', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push', 'manifest:prod']

 gulp.task 'build:dev', ['assets', 'browserify:watchify', 'sass', 'html', 'push']


atom: update config

atom: update config

--- .atom/config.cson
+++ .atom/config.cson
@@ -7,6 +7,9 @@
     preferredLineLength: 100
   core:
     audioBeep: false
+    disabledPackages: [
+      "jshint"
+    ]
     projectHome: "~/dev/mnubo/main"
   "exception-reporting":
     userId: "c0191c2d-2e42-2a6b-6160-e625d859884b"
@@ -42,3 +45,13 @@
     onlyConfig: true
     messageType: "warning"
     executablePath: "/usr/local/bin"
+  minimap: {}
+  "linter-jshint":
+    disableWhenNoJshintrcFileInPath: true
+  "atom-typescript": {}
+".source.ts.tsx":
+  editor:
+    preferredLineLength: 100
+".source.ts":
+  editor:
+    preferredLineLength: 100