remove ship it harder, cauise its broke

remove ship it harder, cauise its broke

--- scripts/shipitharder.coffee
+++ scripts/shipitharder.coffee
@@ -1,131 +0,0 @@
-# Description:
-#   More Effective Rodent Motivation
-#
-#   Set the environment variable HUBOT_SHIP_EXTRA_CAPYBARAS (to anything)
-#   for additional motivation
-#
-# Dependencies:
-#   None
-#
-# Configuration:
-#   HUBOT_SHIP_EXTRA_CAPYBARAS
-#
-# Commands:
-#   ship it harder - Display a motivational capybara when a squirrel just wont do!
-#
-# Original Author:
-#   maddox (capybaras added by DaveOps83)
-
-capybaras = [
-"http://www.rainforest-alliance.org/sites/default/files/uploads/4/capybara-family_15762686447_f9f8a0684a_o.jpg",
-"http://www.zooborns.com/.a/6a010535647bf3970b01a3fd3d706c970b-800wi",
-"http://www.porfell.co.uk/assets/PICT0125.jpg",
-"http://www.rainforest-alliance.org/sites/default/files/styles/responsive_breakpoints_theme_rainforest_wide_1x/public/slideshow/header/capybara_0.jpg%253Fitok%253DG1uvWT23",
-"http://thumbs.media.smithsonianmag.com//filer/2e/73/2e738d22-a273-4a77-a76e-c5486b80c309/02_07_2014_capybara_monkey.jpg__800x600_q85_crop.jpg",
-"http://library.sandiegozoo.org/factsheets/capybara/images/capybara.jpg",
-"http://ak-hdl.buzzfed.com/static/enhanced/webdr05/2013/4/29/14/enhanced-buzz-5639-1367258753-0.jpg",
-"http://a-z-animals.com/media/animals/images/original/capybara3.jpg",
-"http://vignette4.wikia.nocookie.net/cookieclicker/images/2/24/Capybara-02.jpg/revision/latest%253Fcb%253D20130810045256",
-"http://a-z-animals.com/media/animals/images/original/capybara5.jpg",
-"https://upload.wikimedia.org/wikipedia/commons/7/70/Capybara_2.jpg",
-"http://www.zooborns.com/.a/6a010535647bf3970b015433fc97e2970c-500wi",
-"https://upload.wikimedia.org/wikipedia/commons/8/85/Capybara_portrait.jpg",
-"http://a.dilcdn.com/bl/wp-content/uploads/sites/8/2013/05/025-624x616.jpg",
-"http://www.dallasnews.com/incoming/20130712-garl001.jpg.ece/BINARY/w595x467/garl001.JPG",
-"http://www.stlzoo.org/files/8813/0798/4082/capybara02.jpg",
-"http://www.zooborns.com/.a/6a010535647bf3970b019b02308fe1970b-800wi",
-"http://animalfactguide.com/wp-content/uploads/2013/01/iStock_000001572797XSmall.jpg",
-"http://sauceio.com/wp-content/uploads/2013/12/capybaras.jpg",
-"http://i.imgur.com/PIbKpeE.jpg",
-"http://40.media.tumblr.com/f9c96eb10e9086bc26b3ec31cf6dc82a/tumblr_mx34xbtS9P1r38hk2o1_1280.jpg",
-"http://i.ytimg.com/vi/-lqhleCf8R0/maxresdefault.jpg",
-"http://api.ning.com/files/2r6NdP2wa8OTUiSd7GiXVIyYcSB3EPPcR5CA1OFCF1dYNwsJ1C9t70GiPI6UMgJSYC2swr7NEOLNSVpcHpnV-aCSThPQ3AAj/Capybarafamily.jpg%253Fwidth%253D720%2526height%253D480",
-"http://i.livescience.com/images/i/000/048/850/i02/capybara-02.jpg%253F1324347800",
-"http://38.media.tumblr.com/84ae77bbf9768f28b7e5494060702006/tumblr_inline_nagg7e01wf1rhuc3f.jpg",
-"https://upload.wikimedia.org/wikipedia/en/b/b4/Capybara_male.jpg",
-"https://capybaraworld.files.wordpress.com/2014/03/donguri-yawns-40-you-can-see-yasushi-behind-her-and-little-baby-io-in-the-background-40.jpg%253Fw%253D750%2526h%253D421",
-"http://jeffkreeftmeijer.com/images/capybaras.jpg",
-"http://www.dapcat.org/wp-content/uploads/2014/03/Capybara-And-Cat-Cat-Picture.jpg",
-"http://media.giphy.com/media/10FvIiZ8W67wNW/giphy.gif",
-"http://40.media.tumblr.com/b8516edadabe88195d7c971a3d6299b7/tumblr_n0q4ewMUyP1sutuwgo1_1280.jpg",
-"http://s3-static-ak.buzzfed.com/static/2014-01/campaign_images/webdr02/13/17/27-ways-the-capybara-is-the-life-of-the-party-1-3528-1389653760-35_big.jpg",
-"http://www.zooborns.com/.a/6a010535647bf3970b019b02308ed6970b-800wi",
-"http://cdn2.arkive.org/media/82/82134A02-1C65-4051-878E-6A3BE12C9067/Presentation.Large/Portrait-of-infant-capybara.jpg",
-"http://40.media.tumblr.com/a54de0a2c94b8703fdfa988bbe130eb8/tumblr_mxuowa3OzO1sutuwgo1_1280.jpg",
-"http://a.dilcdn.com/bl/wp-content/uploads/sites/8/2013/05/147.jpg",
-"http://gianthamster.com/wp-content/uploads/2011/11/2009_04_18_02_CaplinPool_cropped.jpg",
-"http://gianthamster.com/wp-content/uploads/2013/07/2010_03_01_06_sCaplinMelanie.jpg",
-"http://media.mnn.com/assets/images/2015/04/pet-capybara.jpg",
-"https://capybaraworld.files.wordpress.com/2014/03/wn-scent-marking-capybara-straddling-plant-for-a-blog.jpg%253Fw%253D750%2526h%253D757",
-"http://i.dailymail.co.uk/i/pix/2014/02/03/article-2551095-1B2C322900000578-479_634x448.jpg",
-"http://40.media.tumblr.com/tumblr_m22m5htGGG1rtoixio1_500.jpg",
-"http://ngm.nationalgeographic.com/u/TvyamNb-BivtNwcoxtkc5xGBuGkIMh_nj4UJHQKuor8ESQ8FbgLInpSev4L5ezzHLXWAqUhznzvyNA/",
-"https://capybaraworld.files.wordpress.com/2014/04/20-of-romeo-one-of-the-worlds-great-leaders-april-4-2014-014.jpg%253Fw%253D750",
-"http://ak-hdl.buzzfed.com/static/2014-01/enhanced/webdr02/13/12/enhanced-buzz-19448-1389632861-30.jpg",
-"http://cdn0.lostateminor.com/wp-content/uploads/2015/02/Capybaras2.jpg",
-"http://i.dailymail.co.uk/i/pix/2013/06/21/article-2345934-1A6F477E000005DC-563_634x476.jpg",
-"https://media1.giphy.com/media/FvYUkjxRe2U0M/200_s.gif",
-"https://s-media-cache-ak0.pinimg.com/originals/d4/fa/e7/d4fae720ba3c18eff30a8acfc4ac0665.jpg",
-"http://40.media.tumblr.com/0dd167c95dc41206806d9b351c24974c/tumblr_mvqnfd21oc1sutuwgo1_1280.jpg",
-"http://gianthamster.com/wp-content/uploads/2009/05/caplin_profile_pool.jpg",
-"http://ichef-1.bbci.co.uk/news/304/media/images/72748000/jpg/_72748973_capybara-6.jpg",
-"http://thescoopblog.dallasnewsblogs.com/files/2013/07/tumblr_mpe4io1Duy1r03kk7o1_500.jpg",
-"http://41.media.tumblr.com/0d6ca07e0c4d2520ca8822183fd5f3c4/tumblr_my6ghpdKje1sutuwgo1_1280.jpg",
-"http://i.huffpost.com/gen/1241701/thumbs/o-CHEESECAKE-CAPYBARA-PUPPIES-570.jpg%253F16",
-"http://media.mnn.com/assets/images/2015/04/big-capybara.jpg",
-"http://36.media.tumblr.com/0adcb1bb7f9d7ccdbdb2449a3106f047/tumblr_npmr9gPv7G1u6hbi4o1_1280.jpg",
-"https://capybaraworld.files.wordpress.com/2014/04/45-faq-io-5-months-old-male-eating-his-cecotropes-mother-donguri-is-always-near-him-keeping-an-eye-on-him-and-making-sure-he-does-not-get-into-trouble.jpg",
-"http://i.huffpost.com/gen/1241696/thumbs/o-CHEESECAKE-CAPYBARA-PUPPIES-570.jpg%253F16",
-"http://www.zooborns.com/.a/6a010535647bf3970b011570362ae4970b-pi",
-"http://i.dailymail.co.uk/i/pix/2013/06/21/article-2345934-1A6F45DC000005DC-1000_634x476.jpg",
-"https://pbs.twimg.com/media/B9_X-jQIIAEhpcR.jpg",
-"http://s3.amazonaws.com/appendixjournal-images/images/attachments/000/000/189/large/l-Capybara-with-tiny-monkeys-riding-on-its-back.jpg%253F1358735010",
-"http://animal-backgrounds.com/download/2171/640x960/crop/capybara-animal-wallpaper-640x960.jpg",
-"http://cdn2.arkive.org/media/EB/EB0B5A72-3E4F-4717-96D5-1FADB30732F2/Presentation.Large/Female-capybara-suckling-two-infants.jpg",
-"https://capybaraworld.files.wordpress.com/2014/03/yasushi-loves-to-be-petted-august-2012.jpg",
-"http://i.dailymail.co.uk/i/pix/2013/06/21/article-2345934-1A6F4453000005DC-902_634x448.jpg",
-"http://i.dailymail.co.uk/i/pix/2013/06/21/article-2345934-1A6F4666000005DC-175_634x424.jpg",
-"http://i.livescience.com/images/i/000/043/573/i02/CapybaraBaby_Web_11.29.11.jpg%253F1322695173",
-"http://i2.wp.com/www.kittyarmy.com/wp-content/uploads/2013/06/o-CAPYBARA-HUGS-CAT-570.jpg",
-"http://www.newquayzoo.org.uk/userfiles/animals/images/2007_04_ray_wiltshire_4.jpg%253Fw%253D900%2526h%253D500",
-"http://gianthamster.com/wp-content/uploads/2013/09/2013_09_25_11_sGariPool.jpg",
-"http://gianthamster.com/wp-content/uploads/2010/12/V188_sCapybara.jpg",
-"http://lovemeow.com/wp-content/uploads/2015/01/1476035_758673774204483_6795465395002341575_n.jpg",
-"http://ichef.bbci.co.uk/naturelibrary/images/ic/credit/640x395/c/ca/capybara/capybara_1.jpg",
-"http://a-z-animals.com/media/animals/images/original/capybara7.jpg",
-"https://capybaraworld.files.wordpress.com/2014/04/40-uploaded-blog-faqs-snapshot0-romeo-and-tuffn-play-fighting-february-2014-jpeg.jpg%253Fw%253D750",
-"https://capybaraworld.files.wordpress.com/2014/03/snapshot16-toku-mating-momiji-jpeg-40-september-1-2013.jpg%253Fw%253D750%2526h%253D421",
-"https://capybaraworld.files.wordpress.com/2012/09/046.jpg",
-"https://s-media-cache-ak0.pinimg.com/736x/c3/d7/c8/c3d7c84dcf02b06f4fd58d21d0385944.jpg",
-"http://therevscience.com/wp-content/uploads/2013/05/169158_10100718789077725_301237654_o.jpg",
-"https://s-media-cache-ak0.pinimg.com/236x/64/46/ac/6446acbfdef68815b8bb161b16e8fad3.jpg",
-"http://19mvmv3yn2qc2bdb912o1t2n.wpengine.netdna-cdn.com/science/files/2014/01/capybara-bird.jpg",
-"http://r.ddmcdn.com/s_f/o_1/APL/uploads/2015/02/Preposterous-Pets-Season-1-Lifts-Gary-the-Capybara-Put-the-Cute-In-Rodent-1423969398.jpg",
-"http://31.media.tumblr.com/0cb4ac7d09a4f71b38c068206965dd92/tumblr_inline_nkyk5gCgT61s6kzh8.jpg",
-"https://s-media-cache-ak0.pinimg.com/236x/0c/66/dd/0c66ddc6a19d251eb5886097b65bfd96.jpg",
-"http://3.bp.blogspot.com/-Lah2ype_5f8/UuLavIR0D3I/AAAAAAAAAQE/wjQXQjqeAZY/s1600/Capybara%252Bpet.jpg",
-"http://41.media.tumblr.com/929c4e6e7223de7da46bef10ff18824a/tumblr_mt1gf4wjWD1sutuwgo1_1280.jpg",
-"https://s-media-cache-ak0.pinimg.com/736x/92/2d/6d/922d6dd9dc12f8313effe73ab9037623.jpg",
-"http://gianthamster.com/wp-content/uploads/2012/12/2010_05_23_01_sCaplinSnarling.jpg",
-"http://www.godvine.com/pics/capybara/capybara4.jpg",
-"https://capybaraworld.files.wordpress.com/2013/10/061.jpg",
-"http://vignette2.wikia.nocookie.net/wierdnwildcreatures/images/0/04/Capybara_front.jpg/revision/latest%253Fcb%253D20131004015805",
-"http://osdoc.cogsci.nl/2.9/tutorials/img/capybara/meowing-capybara.png",
-"https://s-media-cache-ak0.pinimg.com/236x/d3/6f/63/d36f63315896c2f6a746e223324f3239.jpg",
-"http://www.zooborns.com/.a/6a010535647bf3970b019b02301b9a970c-800wi",
-"http://big.assets.huffingtonpost.com/joecap1.gif",
-"http://assets.nydailynews.com/polopoly_fs/1.1960909!/img/httpImage/image.jpg_gen/derivatives/gallery_1200/joejoe-capybara.jpg",
-"https://upload.wikimedia.org/wikipedia/commons/2/2c/Hydrochoeris_hydrochaeris_Zoo_Praha_2011-3.jpg",
-"http://gianthamster.com/wp-content/uploads/2010/02/2009_09_01_sCaplinPool.jpg"
-]
-
-module.exports = (robot) ->
-
-  # Enable a looser regex if environment variable is set
-  if process.env.HUBOT_SHIP_EXTRA_CAPYBARAS
-    regex = /ship(ping|z|s|ped)?\s*it\s*(the)?\s*hard(er|est)?/i
-  else
-    regex = /ship\s*it\s*harder/i
-
-  robot.hear regex, (msg) ->
-    msg.send msg.random capybaras


use timestore in month view

use timestore in month view

--- src/components/course-calendar/month.cjsx
+++ src/components/course-calendar/month.cjsx
@@ -6,6 +6,7 @@
 BS = require 'react-bootstrap'

 {Calendar, Month, Week, Day} = require 'react-calendar'
+{TimeStore} = require '../../flux/time'

 CourseCalendarHeader = require './header'
 CourseDuration = require './duration'
@@ -29,7 +30,7 @@
     router: React.PropTypes.func

   getInitialState: ->
-    date: @props.startDate or moment()
+    date: @props.startDate or moment(TimeStore.getNow())
     activeAddDate: null

   setDate: (date) ->
@@ -85,8 +86,8 @@

   # render days based on whether they are past or upcoming
   # past days do not allow adding of plans
-  renderDays: (calendarDuration, referenceDay) ->
-    referenceDay ?= moment()
+  renderDays: (calendarDuration, referenceDate) ->
+    referenceDate ?= moment(TimeStore.getNow())

     durationDays = calendarDuration.iterate('days')
     days = []
@@ -95,7 +96,7 @@
     while durationDays.hasNext()
       dayIter = durationDays.next()

-      if dayIter.isAfter(referenceDay, 'day')
+      if dayIter.isAfter(referenceDate, 'day')

         modifiers =
           upcoming: true
@@ -117,7 +118,7 @@
         modifiers =
           past: true

-        if dayIter.isSame(referenceDay, 'day')
+        if dayIter.isSame(referenceDate, 'day')
           modifiers.current = true

       day = <Day date={dayIter} modifiers={modifiers} {...otherProps}/>


Append unavaliable correctly

Append unavaliable correctly

--- public/coffeescripts/portfolio.coffee
+++ public/coffeescripts/portfolio.coffee
@@ -63,24 +63,27 @@
     childInfo = (callback) ->
       if data.itemType == 'collection'
         getChildren('item', data._id).then (res) ->
-          for child in res
-            if $.isArray child.image
-              for elem in child.image
-                data.image.push elem
-            else
-              data.image.push child.image
-            $('.info .child').append $('
-

-

-
' + child.name + '

-

-

-                  <button class="temp childTarg" target=item:' + child._id + '>View</button>
-

-              </row>
-            ')
-          $('.itemInfoOverlay .info .childTarg').one 'click', ->
-            linkTo $(this).attr('target').split(':')[0], $(this).attr('target').split(':')[1]
+          if res.length != 0
+            for child in res
+              if $.isArray child.image
+                for elem in child.image
+                  data.image.push elem
+              else
+                data.image.push child.image
+              $('.info .child').append $('
+

+

+
' + child.name + '

+

+

+                    <button class="temp childTarg" target=item:' + child._id + '>View</button>
+

+                </row>
+              ')
+            $('.itemInfoOverlay .info .childTarg').one 'click', ->
+              linkTo $(this).attr('target').split(':')[0], $(this).attr('target').split(':')[1]
+          else
+            $('.info .child').append $('
Unavaliable
')
           callback()
       else
         $('.info .child').append $('
Unavaliable
')


webterm: Some new control codes.

webterm: Some new control codes.

--- client/landing/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
@@ -248,10 +248,12 @@
             40:   ignored "allow 80 to 132 mode"
             42:   ignored "enable nation replacement character sets"
             45:   ignored "reverse-wraparound mode"
+            47:   -> terminal.changeScreenBuffer 1
             1000: -> terminal.inputHandler.setMouseMode true, true, false
             1001: -> terminal.inputHandler.setMouseMode true, true, false
             1002: -> terminal.inputHandler.setMouseMode true, true, true
             1003: -> terminal.inputHandler.setMouseMode true, true, true
+            1015: ignored "enable urxvt mouse mode"
             1034: ignored "interpret meta key"
             1047: -> terminal.changeScreenBuffer 1
             1048: -> terminal.cursor.savePosition()
@@ -273,10 +275,12 @@
             40:   ignored "disallow 80 to 132 mode"
             42:   ignored "disable nation replacement character sets"
             45:   ignored "no reverse-wraparound mode"
+            47:   -> terminal.changeScreenBuffer 0
             1000: -> terminal.inputHandler.setMouseMode false, false, false
             1001: -> terminal.inputHandler.setMouseMode false, false, false
             1002: -> terminal.inputHandler.setMouseMode false, false, false
             1003: -> terminal.inputHandler.setMouseMode false, false, false
+            1015: ignored "disable urxvt mouse mode"
             1034: ignored "don't interpret meta key"
             1047: -> terminal.changeScreenBuffer 0
             1048: -> terminal.cursor.restorePosition()
@@ -300,12 +304,14 @@
           .addRange(90, 97, (params) -> terminal.setStyle "textColor", params[0] - 90 + 8)
           .addRange(100, 107, (params) -> terminal.setStyle "backgroundColor", params[0] - 100 + 8)
           "r": (params) -> terminal.screenBuffer.scrollingRegion = [(params[0] ? 1) - 1, (params[1] ? terminal.sizeY) - 1]
+          "?r": ignored "restore mode values"
           "p": switchRawParameter 0
             "!": -> # soft reset
               terminal.cursor.setVisibility true
               originMode = false
               terminal.changeScreenBuffer 0
               terminal.inputHandler.useApplicationKeypad false
+          "?s": ignored "save mode values"
         "]": catchParameters /()(.*?)(\x07|\x1B\\)/, switchParameter 0 # OSC
           0: (params) -> terminal.setTitleCallback? params.raw[1]
           1: ignored "icon name"


closes #17 , routes user back to homepage if not logged in on /manage

--- apps/root/routes.coffee
+++ apps/root/routes.coffee
@@ -11,7 +11,6 @@
 sd = require("sharify").data

 @index = (req, res, next) ->
-
   if req.params.block_id
     res.locals.sd.CLIENT_PATH = "block/#{req.params.block_id}"


Rename $UNRECOGNIZED$ symbol, avoiding id ambiguity (#45)


migrate application definitions from etcd. When migration fails, don't throw error because outside of ictu not all bigboat users have apps/instances in etcd.

--- server/db/migrations.coffee
+++ server/db/migrations.coffee
@@ -3,17 +3,14 @@
   if proj
     recursiveUrl = "#{baseKey}/#{proj}?recursive=true"
     EtcdClient.discover recursiveUrl, (err, nodes) ->
-      if err
-        console.error err
-      else
-        handler err, nodes
+      handler err, nodes

 Migrations.add
-  version: 1,
-  name: 'Gets instance info from ETCD.',
+  version: 1
+  name: 'Gets instance info from ETCD.'
   up: ->
     syncWithBaseKey "instances", (err, nodes) ->
-      throw new Error err if err
+      console.error 'Error while syncing instances info, skipping...', err if err

       objects = {}
       if nodes
@@ -42,5 +39,33 @@

       Instances.updateCollection (value for key, value of objects)

-Meteor.startup ->
-  Migrations.migrateTo('latest')
+Migrations.add
+  version: 2
+  name: 'Gets application definitions from ETCD.'
+  up: ->
+    toApp = (node) ->
+      [ignore..., keyBase, project, appName, version] = node.key?.split('/')
+      {project: project, name: appName, key: node.key?.split('/')[0..-2].join '/'}
+
+    syncWithBaseKey "apps", (err, nodes) ->
+      console.error 'Error while syncing apps info, skipping...', err if err
+
+      apps = if nodes then (toApp node for node in nodes) else []
+      Apps.updateCollection apps
+
+      objects = []
+      if nodes
+        for n in nodes
+          [ignore..., keyBase, project, appName, version] = n.key.split('/')
+
+          objects.push
+            key: n.key
+            project: project
+            name: appName
+            version: version
+            def: n.value
+            tags: Helper.extractTags n.value
+
+      ApplicationDefs.updateCollection objects
+
+Meteor.startup -> Migrations.migrateTo('latest')


fix fill resized problem

fix fill resized problem

--- server/helpers/thumb.coffee
+++ server/helpers/thumb.coffee
@@ -65,14 +65,24 @@
             gmRunner = gm(srcPath).options(imageMagick: true)

             if name is 'thumb'
-                gmRunner
-                .resize(300)
-                .crop(300, 300, 0, 0)
-                .write dstPath, (err) ->
+                buildThumb = (width, height) ->
+                    gmRunner
+                    .resize(width, height)
+                    .crop(300, 300, 0, 0)
+                    .write dstPath, (err) ->
+                        if err
+                            callback err
+                        else
+                            thumb.attachFile file, dstPath, name, callback
+
+                gmRunner.size (err, data) ->
                     if err
                         callback err
                     else
-                        thumb.attachFile file, dstPath, name, callback
+                        if data.width > data.height
+                            buildThumb null, 300
+                        else
+                            buildThumb 300, null

             else if name is 'screen'
                 gmRunner.resize(1200, 800)
@@ -118,7 +128,10 @@
                 stream.on 'end', =>
                     thumb.resize rawFile, file, 'thumb', (err) =>
                         fs.unlink rawFile, ->
-                            log.info """
-createThumb #{file.id} / #{file.name}: Thumbnail created
-"""
+                            if err
+                                console.log err
+                            else
+                                log.info """
+    createThumb #{file.id} / #{file.name}: Thumbnail created
+    """
                             callback err


Adds missing process require call.

Adds missing process require call.

--- src/webpage.coffee
+++ src/webpage.coffee
@@ -1,4 +1,5 @@
 _ = require "lodash"
+process = require "node-ish/process"
 events = require "node-ish/events"
 eventnames = [
 	"Alert"


<debug> Error in condition to check driver validity.

--- server/models/driver.coffee
+++ server/models/driver.coffee
@@ -17,7 +17,7 @@
 Sensor = require './sensor'
 Actuator = require './actuator'

-DRIVERS_DIR = '../drivers/'
+DRIVERS_DIR = __dirname + '/../drivers/'

 actuatorsDrivers = null
 sensorsDrivers = null # List of drivers supported by the system - Must be set when server starts.
@@ -171,7 +171,7 @@
 					unless isActuator = driverModule.isActuator or isSensor = driverModule.isSensor # or (!isActuator and !isSensor)
 						callback 'Driver not defining if for Sensors and/or Actuators', null

-					unless driverModule.add and driverModule.remove and driver.update and driver.apply and (!isActuator or driver.init)
+					unless driverModule.add and driverModule.remove and driver.update and driver.init and (!isActuator or driver.apply)
 						callback 'Driver not implementing the interface', null

 					driverModule.init (err) ->


Simplified the cache key calculation

Simplified the cache key calculation

--- src/wordnet.coffee
+++ src/wordnet.coffee
@@ -116,8 +116,7 @@
     lword = word.toLowerCase().replace(/\s+/g, '_')

     if @cache
-      queryPos = pos or 'undefined'
-      query = "lookup:#{lword}:#{queryPos}"
+      query = "lookup:#{input}"
       return callback(hit) if hit = wordnet.cache.get query

     selectedFiles = if ! pos then wordnet.allFiles else wordnet.allFiles.filter (file) -> file.pos == pos


Fix bug in Utils.textSize()

Fix bug in Utils.textSize()

https://github.com/koenbok/Framer/issues/227

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -743,9 +743,11 @@
 		_textSizeNode = document.createElement("div")
 		_textSizeNode.id = "_textSizeNode"

+	# Reset all the previous styles and set the content
+	_textSizeNode.removeAttribute("style")
 	_textSizeNode.innerHTML = text

-	style = _.extend style,
+	style = _.extend _.clone(style),
 		position: "fixed"
 		display: "inline"
 		visibility: "hidden"


No need to pad out delay time

No need to pad out delay time

--- src/coffee/flight.coffee
+++ src/coffee/flight.coffee
@@ -226,9 +226,9 @@
       clearTimeout @timeout

     # delay node removal time to speed
-    delay = (@speed * 1000) + 300
+    delay = (@speed * 1000)
     @timeout = window.setTimeout =>
-      currentPage.style.display = "none"
+      page.style.display = "none"
     , delay


test multiple texts

test multiple texts

--- src/test/xml.coffee
+++ src/test/xml.coffee
@@ -106,6 +106,24 @@
         test.end()
         xml.end()

+    'sequencial text': (æ) ->
+        xml = new Builder
+        xml.on 'end', æ.done
+        xml.on 'data', (tag) -> æ.equal results.shift(), tag
+        results = [
+            '<test>'
+            'in'
+            ' '
+            'here'
+            '</test>'
+        ]
+        test = xml.tag('test')
+        test.text "in",   append:on
+        test.text " ",    append:on
+        test.text "here", append:on
+        æ.equal test.text(), "in here"
+        test.end()
+        xml.end()

     attributes: (æ) ->
         xml = new Builder


Mark nodes as processed inline

Mark nodes as processed inline

This way we don't have to loop over them twice

--- src/components/html.cjsx
+++ src/components/html.cjsx
@@ -58,18 +58,16 @@
       else
         node.textContent = "\u200b\u200b\u200b#{formula}\u200b\u200b\u200b"
       window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, node])
+      # mark node as processed
+      node.classList.add('math-rendered')

     # render MathML with MathJax
     window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, root]) if hasMath

-    # Once MathJax finishes processing, mark all the nodes as
-    # rended and then manually cleanup the MathJax message nodes to prevent
+    # Once MathJax finishes processing, cleanup the MathJax message nodes to prevent
     # React "Invariant Violation" exceptions.
-    # MathJax calls Queued events in order, so this should always execute after typesetting
+    # MathJax calls queued events in order, so this will be called after processing completes
     window.MathJax?.Hub.Queue([ ->
-      _.each nodes, (node) ->
-        node.classList.add('math-rendered')
-
       for nodeId in ['MathJax_Message', 'MathJax_Hidden', 'MathJax_Font_Test']
         el = document.getElementById(nodeId)
         break unless el # the elements won't exist if MathJax didn't do anything


webapp/course: insert the skip button either before return or in peer grading mode, after it is peer collected

--- src/smc-webapp/course/assignments_panel.cjsx
+++ src/smc-webapp/course/assignments_panel.cjsx
@@ -299,14 +299,19 @@
         else
             width = 3
         buttons = []
+        insert_skip_button = =>
+            b2 = @render_skip_grading_button(status)
+            buttons.push(<Col md={width} key='skip_grading'>{b2}</Col>)
+
         for name in STEPS(peer)
             b = @["render_#{name}_button"](status)
             # squeeze in the skip grading button (don't add it to STEPS!)
-            if name == 'return_graded'
-                b2 = @render_skip_grading_button(status)
-                buttons.push(<Col md={width} key='skip_grading'>{b2}</Col>)
+            if !peer and name == 'return_graded'
+                insert_skip_button()
             if b?
                 buttons.push(<Col md={width} key={name}>{b}</Col>)
+                if peer and name == 'peer_collect'
+                    insert_skip_button()

         v.push <Row key='header-control'>
             <Col md=10 mdOffset=2 key='buttons'>


activity: display first two user names if like actor count is bigger than three

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

     {actorsCount, actorsPreview} = @getData().interactions.like

-    linkCount = Math.min actorsPreview.length, 3
+    linkCount = switch
+      when actorsCount > 3 then 2
+      else actorsPreview.length

     for i in [0..linkCount - 1]
       @addSubView new ProfileLinkView null, accounts[i]


activity: removed hackathon widget from activity feed

--- client/activity/lib/views/activitywidgetsbar.coffee
+++ client/activity/lib/views/activitywidgetsbar.coffee
@@ -16,7 +16,6 @@

     super options

-    @addSubView new ActivityAnnouncementWidget
     @addSubView new ActivityGuideWidget
     @addSubView new ActivityTopicsWidget
     @addSubView new ActivityUniversityWidget


sitewide: commoninnernavigation selectitembyname method implemented

--- client/app/CommonViews/CommonInnerNavigation.coffee
+++ client/app/CommonViews/CommonInnerNavigation.coffee
@@ -47,6 +47,13 @@
     mainView.addSubView list
     @instantiateListItems(@getData().items or [])

+  selectItemByName:(name)->
+    item = null
+    for navItem in @itemsOrdered when navItem.getData()?.title is name
+      @selectItem item = navItem
+      break
+    return item
+
 class CommonInnerNavigationList extends KDListView
   constructor : (options = {},data)->
     options.tagName or= "ul"


FinderPane: Method to getOpenFolders from finder.

--- client/ide/lib/workspace/panes/idefinderpane.coffee
+++ client/ide/lib/workspace/panes/idefinderpane.coffee
@@ -30,6 +30,16 @@

       @addSubView fc.getView()
       @bindListeners()
+
+
+  getOpenFolders: (filterRootFolder = yes) ->
+
+    fc              = @finderController
+    machineNodePath = fc.getMachineNode(@mountedMachine.uid).getData().path
+    folderPaths     = fc.treeController.getOpenFolders()
+
+    return folderPaths  unless filterRootFolder
+    return folderPaths.filter (path) -> path isnt machineNodePath

   bindListeners: ->


Remove trailing whitespaces

Remove trailing whitespaces

--- client/landing/app/MainApp/login/recoverform.coffee
+++ client/landing/app/MainApp/login/recoverform.coffee
@@ -20,7 +20,7 @@
       loader      :
         color     : "#ffffff"
         diameter  : 21
-
+
   pistachio:->

     """


Make the first installed user an administrator by default

--- client/source/controllers/install_controller.coffee
+++ client/source/controllers/install_controller.coffee
@@ -10,10 +10,11 @@
     @adjustTitle 'Install'

     newUser = new User
+      roles: [{name: 'administrator'}]

     @view = new FirstUserView
       model: newUser

-    newUser.on 'sync', =>
+    newUser.on 'sync', =>
       mediator.options.needsInstall = no
-      @redirectTo url: '/'+      @redirectTo url: '/'


Guard against detached <trix-editor> elements

--- src/trix/elements/trix_editor_element.coffee
+++ src/trix/elements/trix_editor_element.coffee
@@ -57,8 +57,8 @@
   toolbarElement:
     get: ->
       if @hasAttribute("toolbar")
-        document.getElementById(@getAttribute("toolbar"))
-      else
+        @ownerDocument?.getElementById(@getAttribute("toolbar"))
+      else if @parentElement
         toolbarId = "trix-toolbar-#{@trixId}"
         @setAttribute("toolbar", toolbarId)
         element = makeElement("trix-toolbar", id: toolbarId)
@@ -68,8 +68,8 @@
   inputElement:
     get: ->
       if @hasAttribute("input")
-        document.getElementById(@getAttribute("input"))
-      else
+        @ownerDocument?.getElementById(@getAttribute("input"))
+      else if @parentElement
         inputId = "trix-input-#{@trixId}"
         @setAttribute("input", inputId)
         element = makeElement("input", type: "hidden", id: inputId)
@@ -78,11 +78,11 @@

   name:
     get: ->
-      @inputElement.name
+      @inputElement?.name

   value:
     get: ->
-      @inputElement.value
+      @inputElement?.value
     set: (@defaultValue) ->
       @editorController?.loadHTML(@defaultValue)

@@ -102,7 +102,7 @@
           @documentChangedSinceLastRender = false
           @notify("change")
       when "change", "attachment-add", "attachment-edit", "attachment-remove"
-        @inputElement.value = Trix.serializeToContentType(this, "text/html")
+        @inputElement?.value = Trix.serializeToContentType(this, "text/html")

     if @editorController
       triggerEvent("trix-#{message}", onElement: this, attributes: data)


fix issue

fix issue

--- lib/buildScripts.coffee
+++ lib/buildScripts.coffee
@@ -16,7 +16,7 @@
         buildProcess.stderr.on 'data', (data) -> atom.notifications.addError 'Build cant Finish' , dismissable: true, detail: data

     make: ->
-        notifications.addInfo 'Make started', dismissable: true, detail: 'this Progress Need Some More Time'
+        atom.notifications.addInfo 'Make started', dismissable: true, detail: 'this Progress Need Some More Time'

         # get Config Path
         path = atom.config.get('language-arma-atom.buildScript')


do not overwrite existing content on paste

--- client/apps/edit/components/content/sections/text/index.coffee
+++ client/apps/edit/components/content/sections/text/index.coffee
@@ -156,8 +156,9 @@
       unless unstyled.getType() in ['unstyled', 'LINK', 'header-two', 'header-three', 'unordered-list-item', 'ordered-list-item']
         unstyled = unstyled.set 'type', 'unstyled'
       return unstyled
-    newState = ContentState.createFromBlockArray(convertedHtml, blocksFromHTML.getBlocksAsArray())
-    @onChange(EditorState.push(editorState, newState, 'insert-fragment'))
+    blockMap = ContentState.createFromBlockArray(convertedHtml, blocksFromHTML.getBlocksAsArray()).blockMap
+    newState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), blockMap)
+    @onChange EditorState.push(editorState, newState, 'insert-fragment')
     return true

   makePlainText: () ->


refactoring

refactoring

--- lib/console/view.coffee
+++ lib/console/view.coffee
@@ -78,26 +78,18 @@
     cell.appendChild content
     cell

-  outView: (s) ->
+  streamView: (text, type, icon) ->
     out = document.createElement 'div'
-    out.innerText = s
-    out.classList.add 'stream', 'output'
+    out.innerText = text
+    out.classList.add type, 'stream'
     @cellView out,
-      icon: 'quote'
+      icon: icon

-  errView: (s) ->
-    err = document.createElement 'div'
-    err.innerText = s
-    err.classList.add 'stream', 'error'
-    @cellView err,
-      icon: 'alert'
+  outView: (s) -> @streamView s, 'output', 'quote'

-  infoView: (s) ->
-    err = document.createElement 'div'
-    err.innerText = s
-    err.classList.add 'stream', 'info'
-    @cellView err,
-      icon: 'info'
+  errView: (s) -> @streamView s, 'error', 'alert'
+
+  infoView: (s) -> @streamView s, 'info', 'info'

   inputView: (con) ->
     ed = document.createElement 'atom-text-editor'


remove unused function

remove unused function

--- lib/assets/javascripts/turbograft/turbohead.coffee
+++ lib/assets/javascripts/turbograft/turbohead.coffee
@@ -60,18 +60,6 @@
   task = tasks.shift()
   task(-> asyncSeries(tasks, callback))

-
-asyncParallel = (tasks, callback) ->
-  tasksRemaining = tasks.length
-  return callback() if tasksRemaining == 0
-
-  done = () ->
-    tasksRemaining--
-    if (tasksRemaining == 0)
-      callback()
-
-  tasks.forEach((task) -> task(done))
-
 insertScriptTask = (activeDocument, scriptNode) ->
   # We need to clone script tags in order to ensure that the browser executes them.
   newNode = activeDocument.createElement('SCRIPT')


Fixed: tests

Fixed: tests

--- tests/helpers.coffee
+++ tests/helpers.coffee
@@ -31,9 +31,9 @@
     initializeApplication (app, server) =>
         @app = app
         @app.server = server
-        done(app)
+        done(app, server)

 helpers.stopApp = (done) ->
     setTimeout =>
         @app.server.close done
-    , 1000+    , 1000


variable

variable

--- stuff/atom/language-ublu/grammars/ublu.cson
+++ stuff/atom/language-ublu/grammars/ublu.cson
@@ -11,6 +11,10 @@
 # foldingStopMarker: '' # A regular expression that checks for the end of a foldable area (such as `}`). If both folding markers are matched in the same line, there will be no foldable area.
 patterns: [
   {
+    match: '-\\w+\\b'
+    name: 'option.dashcommand.ublu'
+  }
+  {
     match: '\\bas400\\b'
     name: 'keyword.as400.ublu'
   }
@@ -408,11 +412,11 @@
   }

   {
-    match: '\\b\\@\\@\\w\+\\b'
+    match: '\\@\\@\\w\+\\b'
     name: 'variable.pastetoken.ublu'
   }
   {
-    match: '\\b\\@\\w\+\\b'
+    match: '\\@\\w\+\\b'
     name: 'variable.tuple.ublu'
   }
 ]


indent

indent

--- src/tip/bootstrap.coffee
+++ src/tip/bootstrap.coffee
@@ -240,14 +240,14 @@
   #
   # target - a jquery element
   _getTargetBounds: (target) ->
-      el = target[0]
-
-      if typeof el.getBoundingClientRect == 'function'
-        size = el.getBoundingClientRect()
-      else
-        size =
-          width: el.offsetWidth
-          height: el.offsetHeight
-
-      $.extend({}, size, target.offset())
-
+    el = target[0]
+
+    if typeof el.getBoundingClientRect == 'function'
+      size = el.getBoundingClientRect()
+    else
+      size =
+        width: el.offsetWidth
+        height: el.offsetHeight
+
+    $.extend({}, size, target.offset())
+


:bug: add hacky fix for #408

:bug: add hacky fix for #408

--- lib/view-models/vim-command-mode-input-view.coffee
+++ lib/view-models/vim-command-mode-input-view.coffee
@@ -56,6 +56,10 @@

   confirm: =>
     @value = @editor.getText()
+    # HACK! Adding "\n" to on key events
+    # Useful for norm r<CR>
+    if not @value and event?.originalEvent?.which is 13
+      @value = "\n"
     @viewModel.confirm(@)
     @remove()


Use proper numerus for commits

Use proper numerus for commits

--- gitcommander.coffee
+++ gitcommander.coffee
@@ -60,8 +60,9 @@

 eventTeller =
   "PushEvent": (channel, event) ->
+    numerus = if event.payload.size > 1 then "commits" else "commit"
     ircClient.say channel, "[#{event.created_at}] #{event.actor.login} pushed
-      #{event.payload.size} commit(s) to
+      #{event.payload.size} #{numerus} to
       #{event.repo.name} (#{event.repo.weburl})"
   "ReleaseEvent": (channel, event) ->
     ircClient.say channel, "[#{event.created_at}] #{event.actor.login} published


FIX: Poll on clock on of helpdesk if done via dashboard

--- src/app/helpdesk/states/dashboard/dashboard.coffee
+++ src/app/helpdesk/states/dashboard/dashboard.coffee
@@ -14,7 +14,7 @@
       roleWhitelist: ['Student', 'Tutor', 'Convenor', 'Admin']
   headerServiceProvider.state 'helpdesk', helpdeskDashboardState
 )
-.controller("HelpdeskDashboardCtrl", ($scope, $rootScope, $interval, $state, HelpdeskStats, HelpdeskTicket, HelpdeskSession, HelpdeskSessionModal) ->
+.controller("HelpdeskDashboardCtrl", ($scope, $timeout, $rootScope, $interval, $state, HelpdeskStats, HelpdeskTicket, HelpdeskSession, HelpdeskSessionModal) ->
   # Internal poll interval
   pollInterval = null
   # How long the duration is between each poll, in seconds
@@ -101,6 +101,12 @@
     $scope.helpdeskOpen = not $scope.helpdeskClosed

   #
+  # If clock on event, refresh immediately
+  #
+  $rootScope.$on 'CurrentWorkingSession', (event, session) ->
+    $scope.pollNow()
+
+  #
   # Begins polling for stats
   #
   startPolling = (interval = intervalDuration) ->
@@ -128,8 +134,10 @@
       pollForTickets()
       pollForTutors()
       pollForStats()
+    $scope.pollNow = ->
+      pollFunction()
     # Call poll at least once to start now
-    pollFunction()
+    $scope.pollNow()
     pollInterval = $interval pollFunction, interval * 1000 # milliseconds

   #


Add comments to ConceptPanel for clarity of implementation

--- src/coffee/cilantro/ui/concept/panel.coffee
+++ src/coffee/cilantro/ui/concept/panel.coffee
@@ -16,20 +16,27 @@
             c.publish c.CONCEPT_FOCUS, datum.id

+    # Takes a collection of c.models.ConceptModel objects
     class ConceptPanel extends c.Marionette.Layout
         className: 'concept-panel'

         template: templates.panel

+        # Two primary regions for the concept panel including the search
+        # and the index (listing) of concepts
         regions:
             search: '.search-region'
             index: '.index-region'

         onRender: ->
+            # Pass the collection of concepts to be rendered in the index
             @index.show new index.ConceptIndex
                 collection: @collection
                 collapsable: false

+            # When a search occurs, the index is filtered relative to the
+            # response which contains a listing of IDs that the search
+            # has matched.
             @search.show new ConceptSearch
                 collection: @collection
                 handler: (query, resp) =>


Allow integration test looking up by instance name to be bypassed (if SQL Server Browser not available).

--- test/integration/instance-lookup-test.coffee
+++ test/integration/instance-lookup-test.coffee
@@ -7,6 +7,12 @@

 exports.goodInstance = (test) ->
   config = getConfig()
+
+  if !config.instanceName
+    # Config says don't do this test (probably because SQL Server Browser is not available).
+    console.log('Skipping goodInstance test')
+    test.done()
+    return

   callback = (err, port) ->
     test.ok(!err)


Fixed highlighting of ending tag for custom tags

--- grammars/marko.cson
+++ grammars/marko.cson
@@ -892,7 +892,7 @@
       "1":
         name: "punctuation.definition.tag.begin.close.html"
       "2":
-        name: "support.function.tag.close.html"
+        name: "support.function.marko-tag.close.html"
       "3":
         name: "punctuation.definition.tag.end.close.html"
       "4":


Reorder function highlighting to support nested functions, fixes #97

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -482,10 +482,6 @@
           1: name: 'punctuation.definition.parameters.end.stata'
         patterns: [
           {
-            match: '[\\w&&[^0-9]]\\w{0,31}'
-            name: 'variable.parameter.function.stata'
-          }
-          {
             begin: '\\('
             end: '\\)'
             beginCaptures:
@@ -527,6 +523,10 @@
           {include: '#builtin_variables'}
           {include: '#macro-commands'}
           {include: '#braces-without-error'}
+          {
+            match: '[\\w&&[^0-9]]\\w{0,31}'
+            name: 'variable.parameter.function.stata'
+          }
         ]
       }
     ]


show warning when giving but cursor not in any goal

--- lib/hole-manager.coffee
+++ lib/hole-manager.coffee
@@ -137,8 +137,8 @@
       @agda.executable.agda.stdin.write command

     else
-      console.log goals
-      console.log 'not in any goal'
+      @agda.panelView.setStatus 'Info'
+      @agda.panelView.setContent ['For this command, please place the cursor in a goal']

   giveHandler: (index) ->
     @holes.forEach (hole) =>


Kill logging

Kill logging

--- lib/image-editor.coffee
+++ lib/image-editor.coffee
@@ -6,7 +6,6 @@
 # Essentially, the graphical version of a {EditSession}.
 module.exports =
 class ImageEditor extends Model
-  console.log "registering ImageEditor as representation class"
   atom.registerRepresentationClass(this)

   @properties


Fixes #659

Fixes #659

We have to make sure to not have spaces in the tag to find it.

--- app/assets/javascripts/alchemy/alchemy.autocomplete.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.autocomplete.js.coffee
@@ -26,5 +26,5 @@
   _initializeSelection: (element, callback) ->
     data = []
     $(element.val().split(",")).each ->
-      data.push id: this, text: this
+      data.push id: $.trim(this), text: this
     callback(data)


'api-wiki' glossary link fixed

'api-wiki' glossary link fixed

--- scripts/api-wiki/jobs/glossary.coffee
+++ scripts/api-wiki/jobs/glossary.coffee
@@ -14,6 +14,7 @@

 parseList = (list, path, fileLinks) ->
     wikiPath = path.replace /\.md$/, ''
+    wikiPath = path.replace /.*\//, ''
     lines = list.split '\n'
     for line in lines
         if match = line.match(/^\s*[\-*]\s*\[([^\]]+)\]\(([^\)]+)\)\s*$/)


Update utils.coffee. Add some fullscreen api helper methods

--- app/assets/javascripts/martian_components/utils.coffee
+++ app/assets/javascripts/martian_components/utils.coffee
@@ -32,3 +32,22 @@
       decodeURIComponent(string[1]).replace(/\+/g, ' ')
     else
       null
+
+  @requestFullscreen: (el) ->
+    el.webkitRequestFullscreen?(el)
+    el.mozRequestFullScreen?(el)
+    el.msRequestFullscreen?(el)
+    el.webkitRequestFullscreen?(el)
+
+  @exitFullscreen: ->
+    document.webkitExitFullscreen?()
+    document.mozCancelFullScreen?()
+    document.msExitFullscreen?()
+    document.webkitExitFullscreen?()
+    document.webkitExitFullscreen?()
+
+  @onExitFullscreen: (callback) ->
+    document.addEventListener('webkitfullscreenchange', callback, false)
+    document.addEventListener('mozfullscreenchange', callback, false)
+    document.addEventListener('fullscreenchange', callback, false)
+    document.addEventListener('MSFullscreenChange', callback, false)


chore(merge): fix from not pulling first

--- src/client/scripts/constants/browsers-const.coffee
+++ src/client/scripts/constants/browsers-const.coffee
@@ -97,11 +97,10 @@
 	'
 	'full support': '
 		<rb-icon
-			size="1.65"
-			kind="flag"
+			size="2"
+			kind="check"
 			source="solid"
-			class="full-support"
-			title="Supported Behind Flag">
+			class="full-support">
 		</rb-icon>
 	'
 	'no support': null


Fixed cloud settings dialogs checkboxes

Fixed cloud settings dialogs checkboxes

--- app/assets/javascripts/views/clouds/settings_dialog.js.coffee
+++ app/assets/javascripts/views/clouds/settings_dialog.js.coffee
@@ -74,7 +74,7 @@
     @.$(':checkbox').change ->
       unless $(@).data('preventAjax') == true
         submitData = {}
-        submitData[@name] = if (@value == "on") then true else false
+        submitData[@name] = if @checked then true else false
         $.event.trigger 'change:field:cloud', { element: @, submitData: submitData }

     @.$(':file').on 'change', => @submitAvatar()


Fixes the invalid calculation of page size in Line Wrapper. @startY + options.height will actually = the full remainder of the page, by subrtacting out a line of text it just wrapped a line of text too early as the last line of text could never fit.

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -33,7 +33,7 @@
         @column = 1

         # calculate the maximum Y position the text can appear at
-        @maxY = @startY + options.height - @document.currentLineHeight()
+        @maxY = @startY + options.height

         # make sure we're actually on the page
         # and that the first line of is never by


:bug: [linter-views] Fix a critical duplication bug with Markers When two markers of the same key were created and one was removed, the other was considered removed as well (but it wasn't removed in real, it stayed on the text editor), 'cause they had the same string key. so I've moved from a string key to object key in a WeakMap, so it'll not be the case again. :tada:

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -10,7 +10,7 @@
     @state = @linter.state
     @subscriptions = new CompositeDisposable
     @messages = []
-    @markers = new Map()
+    @markers = new WeakMap()
     @panel = new BottomPanel(@state.scope)
     @bottomContainer = new BottomContainer().prepare(@linter.state)
     @bottomBar = null
@@ -97,7 +97,7 @@
     return unless activeEditor
     added.forEach (message) =>
       return unless message.currentFile
-      @markers.set(message.key, marker = activeEditor.markBufferRange message.range, {invalidate: 'inside'})
+      @markers.set(message, marker = activeEditor.markBufferRange message.range, {invalidate: 'inside'})
       activeEditor.decorateMarker(
         marker, type: 'line-number', class: "linter-highlight #{message.class}"
       )
@@ -112,10 +112,10 @@

   removeMarkers: (messages = @messages) ->
     messages.forEach((message) =>
-      return unless @markers.has(message.key)
-      marker = @markers.get(message.key)
+      return unless @markers.has(message)
+      marker = @markers.get(message)
       marker.destroy()
-      @markers.delete(message.key)
+      @markers.delete(message)
     )

   removeBubble: ->


:memo: Add comment on linter-views::_extractMessages

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -78,6 +78,7 @@
     @linter.h.genValues(Gen).forEach (Entry) ->
       # Entry === Array<Messages>
       Entry.forEach (message) ->
+        # If there's no file prop on message and the panel scope is file then count is as current
         if (not message.file and not isProject) or message.file is activeFile
           counts.file++
           counts.project++


set correct cache headers

set correct cache headers

--- lib/local_server.coffee
+++ lib/local_server.coffee
@@ -34,7 +34,7 @@
         "<!-- roots development configuration -->
         <script>var __livereload = #{@roots.config.live_reload};</script>
         <script src='/__roots__/main.js'></script>"
-      opts.cache_control = { '**': 'private, max-age=0, no-cache' }
+      opts.cache_control = { '**': 'max-age=0, no-cache, no-store' }

     app = charge(@roots.config.output_path(), opts)


:art: minor refactoring

:art: minor refactoring

--- lib/mode-manager.coffee
+++ lib/mode-manager.coffee
@@ -13,10 +13,8 @@

   isMode: (mode, submode=null) ->
     if submode
-      if _.isArray(submode)
-        @mode is mode and (@submode in submode)
-      else
-        @mode is mode and @submode is submode
+      submode = [submode] if _.isString(submode)
+      @mode is mode and (@submode in submode)
     else
       @mode is mode


Update virginmobile.coffee

Update virginmobile.coffee

* Set vendor for each bill
* Increase date delta

--- server/konnectors/virginmobile.coffee
+++ server/konnectors/virginmobile.coffee
@@ -49,7 +49,7 @@
                 log: log
                 model: Bill
                 identifier: 'virgin mobile'
-                dateDelta: 4
+                dateDelta: 17
                 amountDelta: 5
             )
             .args(requiredFields, {}, {})
@@ -114,6 +114,7 @@
                 amount: parseFloat(inv.amount.unite + '.' + inv.amount.centimes)
                 pdfurl: baseURL + inv.invoiceNumber
                 type: "phone"
+                vendor: "Virgin mobile"

             if bill.date? and bill.amount? and bill.pdfurl?
                 bills.fetched.push bill


fixed ear size calculation

fixed ear size calculation

--- evolution.coffee
+++ evolution.coffee
@@ -1281,7 +1281,7 @@
     size = @radius * @maxAcceleration() / @genes.maxAcceleration[2](@)
     max 2, size
   calcEarSize: ->
-    ratio = @auditoryRange() / @genes.auditoryRange[2]
+    ratio = @auditoryRange() / @genes.auditoryRange[2](@)
     size = ratio * @radius / 3
     max size, 1
   calcEyeSize: ->


Fix Review : typo

Fix Review : typo

--- client/app/components/mails_input.coffee
+++ client/app/components/mails_input.coffee
@@ -29,8 +29,8 @@
         return state

     componentWillReceiveProps: (nextProps) ->
-        isFocus = _.isEmpty nextProps.valueLink.value
-        @setState known: value, focus: not isFocus
+        value = nextProps.valueLink.value
+        @setState known: value, focus: not _.isEmpty value

     # Code from the StoreWatch Mixin. We don't use the mixin
     # because we store other things into the state


realtime: add collaboration check for authentication

--- client/app/lib/realtimecontroller.coffee
+++ client/app/lib/realtimecontroller.coffee
@@ -166,7 +166,7 @@
     options = { channelName: pubnubChannelName, channelId }

     # authentication needed for private message channels
-    if typeConstant is 'privatemessage'
+    if typeConstant in ['privatemessage', 'collaboration']
       options.authenticate =
         endPoint : "/api/gatekeeper/subscribe/channel"
         data     : { name: channelName, typeConstant, groupName: group }


updates now!!!!!!

updates now!!!!!!

--- client/assets/scripts/application.coffee
+++ client/assets/scripts/application.coffee
@@ -299,7 +299,7 @@
 					item.weight = data_item.weight
 					elem.find('.rating').html(item.weight)
 			app.nowPlaying()
-			return if data.length == $feed.find('div.media').length
+			# return if data.length == $feed.find('div.media').length

 			saveList()


Do not re-submit on submit timeout, first check if it appeared

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -89,7 +89,8 @@
             headers: {'Content-Type': contentType},
             body,
             followAllRedirects: true,
-            timeout: 30 * 1000
+            timeout: 30 * 1000,
+            maxAttempts: 1
         })
         res = JSON.parse(page)
         if res.res != "ok"
@@ -165,17 +166,24 @@

     submitWithFormData: (user, problemId, contentType, data) ->
         informaticsProblemId = @_informaticsProblemId(problemId)
-        try
-            oldSubmits = await Submit.findByUserAndProblem(user.informaticsId, problemId)
+        success = false
+        for i in [1..8]
             try
-                informaticsUser = await LoggedInformaticsUser.getUser(user.informaticsUsername, user.informaticsPassword)
-                informaticsData = await informaticsUser.submit(informaticsProblemId, contentType, data)
-            finally
-                await downloadSubmits.runForUser(user.informaticsId, 5, 1)
-        catch e
+                oldSubmits = await Submit.findByUserAndProblem(user.informaticsId, problemId)
+                try
+                    informaticsUser = await LoggedInformaticsUser.getUser(user.informaticsUsername, user.informaticsPassword)
+                    informaticsData = await informaticsUser.submit(informaticsProblemId, contentType, data)
+                finally
+                    await downloadSubmits.runForUser(user.informaticsId, 5, 1)
+            catch e
+                logger.info "Error submitting", e.message
             newSubmits = await Submit.findByUserAndProblem(user.informaticsId, problemId)
-            if oldSubmits.length == newSubmits.length
-                throw e
+            if oldSubmits.length != newSubmits.length
+                logger.info "However, submit appeared after downloadSubmits"
+                success = true
+                break
+        if not success
+            throw "Can't submit"

     registerUser: (user) ->
         logger.info "Moving user #{user._id} to unknown group"


fix mermaid config bug

fix mermaid config bug

--- lib/md.coffee
+++ lib/md.coffee
@@ -19,10 +19,12 @@
 loadMermaidConfig = ()->
   # mermaid_config.js
   configPath = path.resolve(atom.config.configDirPath, './markdown-preview-enhanced/mermaid_config.js')
-  mermaidConfigFile = new File(configPath)
-  mermaidConfigFile.create().then (flag)->
-    return require(configPath) if not flag # file already exists
-    mermaidConfigFile.write """
+  try
+    return require(configPath)
+  catch error
+    mermaidConfigFile = new File(configPath)
+    mermaidConfigFile.create().then (flag)->
+      mermaidConfigFile.write """
 'use strict'
 // config mermaid init call
 // http://knsv.github.io/mermaid/#configuration
@@ -34,11 +36,10 @@
 }

 module.exports = config || {startOnLoad: false}
-    """
+"""
     return {startOnLoad: false}

 mermaidAPI.initialize(loadMermaidConfig())
-

 #################################################
 ## Math


Tree now captures correct checked keys

Tree now captures correct checked keys

--- muleview/app/controller/KeysTree.coffee
+++ muleview/app/controller/KeysTree.coffee
@@ -21,11 +21,12 @@
   ]

   onSelectionChange: (me, selected)->
-    return unless selected[0]
-    if @multiMode
-      keys = node.get("fullname") for node in @getTree().getChecked()
-    else
-      keys = selected[0].get("fullname")
+    return unless selected[0] and not @multiMode
+    key = selected[0].get("fullname")
+    Muleview.event "viewChange", key, Muleview.currentRetention
+
+  onCheckChange: ->
+    keys = (node.get("fullname") for node in @getTree().getChecked()).join(",")
     Muleview.event "viewChange", keys, Muleview.currentRetention

   onLaunch: ->
@@ -34,7 +35,7 @@
     @getTree().on
       selectionchange: @onSelectionChange
       itemexpand: @onItemExpand
-      # checkchange: @onCheckChange
+      checkchange: @onCheckChange
       scope: @

     @getNormalModeBtn().on


Small code cleanup.

Small code cleanup.

--- public/assets/chat/chat.coffee
+++ public/assets/chat/chat.coffee
@@ -63,7 +63,8 @@

       @ws.onopen = =>
         console.log('ws connected')
-        @send 'chat_status'
+        @send 'chat_status_customer'
+

       @ws.onmessage = @onWebSocketMessage

@@ -97,11 +98,6 @@
           when 'chat_session_typing'
             return if pipe.data.self_written
             @onAgentTypingStart()
-            if @stopTypingId
-              clearTimeout(@stopTypingId)
-            delay = =>
-              @onAgentTypingEnd()
-            @stopTypingId = setTimeout(delay, 3000)
           when 'chat_session_start'
             switch pipe.data.state
               when 'ok'
@@ -113,7 +109,7 @@
               when 'queue'
                 @onQueue pipe.data.position
                 @session_id = pipe.data.session_id
-          when 'chat_status'
+          when 'chat_status_customer'
             switch pipe.data.state
               when 'online'
                 @onReady()
@@ -170,7 +166,7 @@
       # add message before message typing loader
       if @el.find('.zammad-chat-message--typing').size()
         @lastAddedType = 'typing-placeholder'
-        @el.find('.zammad-chat-message--typing').before messageElement
+        @el.find('.zammad-chat-message--typing').before messageElement
       else
         @lastAddedType = 'message--customer'
         @el.find('.zammad-chat-body').append messageElement
@@ -245,6 +241,10 @@
         position: position

     onAgentTypingStart: =>
+      if @stopTypingId
+        clearTimeout(@stopTypingId)
+      @stopTypingId = setTimeout(@onAgentTypingEnd, 3000)
+
       # never display two typing indicators
       return if @el.find('.zammad-chat-message--typing').size()


[node_modules_koding][bongo] added emiting of new instances on model loading

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -73,6 +73,7 @@
     @loadModels models
     @on 'error', Bongo.throwIt
     @setMaxListeners 0
+    @emit 'newInstance', this

   initBlacklist:do ->
     blacklister = (k, v)->


potential fix for num 8

potential fix for num 8

--- lib/sublime-tabs.coffee
+++ lib/sublime-tabs.coffee
@@ -53,14 +53,13 @@
       @state

   deactivate: ->
+    # tree view
+    @treeView?.deactivate()
+    @treeView = null
+
     # tabs
     @paneSubscription?.off()
     tabBarView.remove() for tabBarView in @tabBarViews ? []
-
-    # tree view
-    SublimeTree.deactivate()
-    @treeView?.deactivate()
-    @treeView = null

   # tree view
   createView: ->


'neft create' bug on windows fixed

'neft create' bug on windows fixed

--- lib/tasks/create.coffee
+++ lib/tasks/create.coffee
@@ -19,6 +19,6 @@
 	fs.removeSync "#{dest}/.git"

 	log "Install modules (may take a while)"
-	cp.execSync "cd #{dest}; npm install"
+	cp.execSync "cd #{dest} & npm install"

 	log.ok "Project created in '#{dest}'"


Allow standalone debug

Allow standalone debug

--- test/assert.coffee
+++ test/assert.coffee
@@ -6,5 +6,5 @@
   count++
   assert.apply @, arguments

-after ->
+after? ->
   console.log "\nAssertions:", count


fix enquiry table

fix enquiry table

--- imports/ui/components/enquiryList/enquiryList.coffee
+++ imports/ui/components/enquiryList/enquiryList.coffee
@@ -6,7 +6,7 @@

 	countEnquiries: ->
 		enquiries = Messages.find { 'recipient.name': 'Support' }, _id: 1
-		enquiries.length || 0
+		enquiries.fetch().length

 Template.enquiryList.onCreated ->


Update tests to trigger command

Update tests to trigger command

Modify the tests to test at a higher level. Instead of testing the
`#sortLines` function directly, test that the everything works as
expected when the command is triggered in the editor.

--- spec/sort-lines-spec.coffee
+++ spec/sort-lines-spec.coffee
@@ -1,87 +1,89 @@
-{sortLines} = require '../lib/sort-lines'
+{WorkspaceView} = require 'atom'

-describe "SortLines", ->
-  describe "sortLines(editor)", ->
-    [editor] = []
+describe "sorting lines", ->
+  [editorView] = []

-    beforeEach ->
-      editor = atom.project.openSync()
+  beforeEach ->
+    atom.workspaceView = new WorkspaceView
+    atom.workspaceView.openSync()
+    atom.packages.activatePackage('sort-lines')
+    editorView = atom.workspaceView.getActiveView()

-    describe "when no lines are selected", ->
-      it "sorts all lines", ->
-        editor.setText """
-          Hydrogen
-          Helium
-          Lithium
-        """
-        editor.setCursorBufferPosition([0, 0])
-        sortLines(editor)
-        expect(editor.getText()).toBe """
-          Helium
-          Hydrogen
-          Lithium
-        """
+  describe "when no lines are selected", ->
+    it "sorts all lines", ->
+      editorView.setText """
+        Hydrogen
+        Helium
+        Lithium
+      """
+      editorView.setCursorBufferPosition([0, 0])
+      editorView.trigger "sort-lines:sort"
+      expect(editorView.getText()).toBe """
+        Helium
+        Hydrogen
+        Lithium
+      """

-    describe "when entire lines are selected", ->
-      it "sorts the selected lines", ->
-        editor.setText """
-          Hydrogen
-          Helium
-          Lithium
-          Beryllium
-          Boron
-        """
-        editor.setSelectedBufferRange([[1,0], [4,0]])
-        sortLines(editor)
-        expect(editor.getText()).toBe """
-          Hydrogen
-          Beryllium
-          Helium
-          Lithium
-          Boron
-        """
+  describe "when entire lines are selected", ->
+    it "sorts the selected lines", ->
+      editorView.setText """
+        Hydrogen
+        Helium
+        Lithium
+        Beryllium
+        Boron
+      """
+      editorView.setSelectedBufferRange([[1,0], [4,0]])
+      editorView.trigger "sort-lines:sort"
+      expect(editorView.getText()).toBe """
+        Hydrogen
+        Beryllium
+        Helium
+        Lithium
+        Boron
+      """

-    describe "when partial lines are selected", ->
-      it "sorts the selected lines", ->
-        editor.setText """
-          Hydrogen
-          Helium
-          Lithium
-          Beryllium
-          Boron
-        """
-        editor.setSelectedBufferRange([[1,3], [3,2]])
-        sortLines(editor)
-        expect(editor.getText()).toBe """
-          Hydrogen
-          Beryllium
-          Helium
-          Lithium
-          Boron
-        """
+  describe "when partial lines are selected", ->
+    it "sorts the selected lines", ->
+      editorView.setText """
+        Hydrogen
+        Helium
+        Lithium
+        Beryllium
+        Boron
+      """
+      editorView.setSelectedBufferRange([[1,3], [3,2]])
+      editorView.trigger "sort-lines:sort"
+      expect(editorView.getText()).toBe """
+        Hydrogen
+        Beryllium
+        Helium
+        Lithium
+        Boron
+      """

-    describe "when there are multiple selection ranges", ->
-      it "sorts the lines in each selection range", ->
-        editor.setText """
-          Hydrogen
-          Helium    # selection 1
-          Beryllium # selection 1
-          Carbon
-          Fluorine  # selection 2
-          Aluminum  # selection 2
-          Gallium
-          Europium
-        """
-        editor.addSelectionForBufferRange([[1, 0], [3, 0]])
-        editor.addSelectionForBufferRange([[4, 0], [6, 0]])
-        sortLines(editor)
-        expect(editor.getText()).toBe """
-          Hydrogen
-          Beryllium # selection 1
-          Helium    # selection 1
-          Carbon
-          Aluminum  # selection 2
-          Fluorine  # selection 2
-          Gallium
-          Europium
-        """
+  describe "when there are multiple selection ranges", ->
+    it "sorts the lines in each selection range", ->
+      editorView.setText """
+        Hydrogen
+        Helium    # selection 1
+        Beryllium # selection 1
+        Carbon
+        Fluorine  # selection 2
+        Aluminum  # selection 2
+        Gallium
+        Europium
+      """
+      editorView.addSelectionForBufferRange([[1, 0], [3, 0]])
+      editorView.addSelectionForBufferRange([[4, 0], [6, 0]])
+      editorView.trigger "sort-lines:sort"
+      expect(editorView.getText()).toBe """
+        Hydrogen
+        Beryllium # selection 1
+        Helium    # selection 1
+        Carbon
+        Aluminum  # selection 2
+        Fluorine  # selection 2
+        Gallium
+        Europium
+      """


Fix :shit: I shouldn't have checked in

Fix :shit: I shouldn't have checked in

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -42,8 +42,8 @@

           window.loadPackage("package-with-module")

-          expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-2"
-          expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-3"
+          expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1"
+          expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2"
           expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()

       describe "when package.json contains a 'keymaps' manifest", ->


Fixes T1731: code in iframes renders with `white-space: normal`

Summary:
Fixes T1731
Chrome by default on a user-agent stylesheet sets white-space to `pre`.
For `pre` elements. This is useful in most cases since whitespace is taken
literally.

Unfortunately when people send out emails with code in them, the `pre`
blocks can get mangled because there may be unnecessary whitespace in
them.

I personally think the default behavior is much better, however, I think
we should have `pre` blocks render with `white-space: normal` because
anyone who's sending an email with code it in will probably use <span> and
<br> and inline styles to ensure it looks correct on most readers. Since
many places (like Outlook) strip whitespace out of HTML, I think we should
mimic that behavior too.

Test Plan: edgehill --test

Reviewers: bengotow

Reviewed By: bengotow

Maniphest Tasks: T1731

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

--- internal_packages/message-list/lib/email-frame.cjsx
+++ internal_packages/message-list/lib/email-frame.cjsx
@@ -60,6 +60,10 @@
     max-width: 840px;
     overflow: hidden;
     -webkit-font-smoothing: antialiased;
+  }
+
+  pre {
+    white-space: normal;
   }

   a {


Adds a test

Adds a test

--- components/article/test/templates.coffee
+++ components/article/test/templates.coffee
@@ -26,3 +26,34 @@
       sd: {}
       asset: ->
     html.should.containEql 'hi'
+
+  it 'renders fullscreen headers with video', ->
+    html = render('index')
+      article: new Article
+        title: 'hi'
+        sections: []
+        hero_section:
+          type: 'fullscreen'
+          background_url: 'http://video.mp4'
+      footerArticles: new Articles
+      crop: (url) -> url
+      moment: moment
+      sd: {}
+      asset: ->
+    html.should.containEql 'article-fullscreen-video-player'
+
+  it 'renders fullscreen headers with static image', ->
+    html = render('index')
+      article: new Article
+        title: 'hi'
+        sections: []
+        hero_section:
+          type: 'fullscreen'
+          background_image_url: 'http://image.jpg'
+      footerArticles: new Articles
+      crop: (url) -> url
+      moment: moment
+      sd: {}
+      asset: ->
+    html.should.containEql 'article-fullscreen-image'
+


html-editor: put spaces around math in %html so that it appears rendered nicely in normal mode.

--- salvus/page/misc_page.coffee
+++ salvus/page/misc_page.coffee
@@ -144,10 +144,10 @@
                 t.find(".#{c}").remove()
             for s in t.find("script[type='math/tex']")
                 a = $(s)
-                a.replaceWith("$#{a.text()}$")
+                a.replaceWith(" $#{a.text()}$ ")
             for s in t.find("script[type='math/tex; mode=display']")
                 a = $(s)
-                a.replaceWith("$$#{a.text()}$$")
+                a.replaceWith(" $$#{a.text()}$$ ")
             return t

 $.fn.extend


Simplify code using newer jQuery features

--- js-library/app/js/jail_iframe/classes/buttons.coffee
+++ js-library/app/js/jail_iframe/classes/buttons.coffee
@@ -1,12 +1,10 @@
 class Button
   constructor: (dom_events={}) ->
     @frame = new FactlinkJailRoot.ControlIframe()
-    @frame.doc.body.innerHTML = @content
-    el = @el=  @frame.doc.body.firstChild
+    @frame.setContent($.parseHTML(@content))
+    @$el =  $(@frame.doc.body.firstChild)
     for event, callback of dom_events
-      el.addEventListener event, callback
-    @frame.resizeFrame()
-    @$el = $(el)
+      @$el.on event, callback

   startLoading: => @$el.addClass "fl-loading"
   stopLoading:  => @$el.removeClass  "fl-loading"


Rename Firefox XHR headers hack, remove XDomainRequest hacking from that Firefox hack and only apply the hack when the browser is Firefox... Did I mention Firefox?

--- lib/assets/javascripts/firehose/long_poll.js.coffee
+++ lib/assets/javascripts/firehose/long_poll.js.coffee
@@ -47,7 +47,7 @@
       timeout:      @_timeout
       success:      @_success
       error:        @_error
-      xhr:          hackedXHR
+      xhr:          hackAroundFirefoxXhrHeadersBug if $.browser is 'mozilla'
       complete:     @_xhrComplete
     console?.log "_request END"

@@ -104,27 +104,66 @@
       # Reconnect with delay
       setTimeout @_request, @_retryDelay

+# NB: This is a stupid hack to deal with CORS short-comings in jQuery in
+# Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338
+# Once jQuery is upgraded to this version we can probably remove this, but be
+# sure you test the crap out of Firefox!
 # Its also worth noting that I had to localize this monkey-patch to the
 # Firehose.LongPoll consumer because a previous global patch on
 # jQuery.ajaxSettings.xhr was breaking regular IE7 loading. Better to localize
 # this anyway to solve that problem and loading order issues.
-hackedXHR = ->
-  # HACK: use XDomainRequest instead for IE8+
-  if window.XDomainRequest? then new XDomainRequest()
-  else
-    # NB: This is a stupid hack to deal with CORS short-comings in jQuery in
-    # Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338
-    # Once jQuery is upgraded to this version we can probably remove this, but be
-    # sure you test the crap out of Firefox!
-    xhr = jQuery.ajaxSettings.xhr()
-    originalFun = xhr.getAllResponseHeaders
-    xhr.getAllResponseHeaders = ->
-      XHR_HEADERS = [
-        "Cache-Control", "Content-Language", "Content-Type"
-        "Expires", "Last-Modified", "Pragma"
-      ]
-      return allHeaders if (allHeaders = originalFun.call xhr)?
-      lines = for name in XHR_HEADERS when xhr.getResponseHeader(name)?
-        "#{name}: #{xhr.getResponseHeader name}"
-      lines.join '\n'
-    xhr
+hackAroundFirefoxXhrHeadersBug = ->
+  xhr = jQuery.ajaxSettings.xhr()
+  originalFun = xhr.getAllResponseHeaders
+  xhr.getAllResponseHeaders = ->
+    XHR_HEADERS = [
+      "Cache-Control", "Content-Language", "Content-Type"
+      "Expires", "Last-Modified", "Pragma"
+    ]
+    return allHeaders if (allHeaders = originalFun.call xhr)?
+    lines = for name in XHR_HEADERS when xhr.getResponseHeader(name)?
+      "#{name}: #{xhr.getResponseHeader name}"
+    lines.join '\n'
+  xhr
+
+# if ( window.XDomainRequest ) {
+#   jQuery.ajaxTransport(function( s ) {
+#     if ( s.crossDomain && s.async ) {
+#       if ( s.timeout ) {
+#         s.xdrTimeout = s.timeout;
+#         delete s.timeout;
+#       }
+#       var xdr;
+#       return {
+#         send: function( _, complete ) {
+#           function callback( status, statusText, responses, responseHeaders ) {
+#             xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop;
+#             xdr = undefined;
+#             complete( status, statusText, responses, responseHeaders );
+#           }
+#           xdr = new XDomainRequest();
+#           xdr.open( s.type, s.url );
+#           xdr.onload = function() {
+#             callback( 200, "OK", { text: xdr.responseText }, "Content-Type: " + xdr.contentType );
+#           };
+#           xdr.onerror = function() {
+#             callback( 404, "Not Found" );
+#           };
+#           if ( s.xdrTimeout ) {
+#             xdr.ontimeout = function() {
+#               callback( 0, "timeout" );
+#             };
+#             xdr.timeout = s.xdrTimeout;
+#           }
+#           xdr.send( ( s.hasContent && s.data ) || null );
+#         },
+#         abort: function() {
+#           if ( xdr ) {
+#             xdr.onerror = jQuery.noop();
+#             xdr.abort();
+#           }
+#         }
+#       };
+#     }
+#   });
+# }


Update headers to use object, not array.

--- lib/assets/javascripts/turbograft/turbolinks.coffee
+++ lib/assets/javascripts/turbograft/turbolinks.coffee
@@ -95,8 +95,10 @@
     xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
     xhr.setRequestHeader 'X-XHR-Referer', referer

-    options.headers ?= []
-    xhr.setRequestHeader options.headers[header].key, options.headers[header].value for header of options.headers
+    options.headers ?= {}
+
+    for k,v of options.headers
+      xhr.setRequestHeader k, v

     xhr.onload = ->
       if xhr.status >= 500


reset the nr of lines to 3

reset the nr of lines to 3

--- core/app/backbone/views/fact_view.coffee
+++ core/app/backbone/views/fact_view.coffee
@@ -27,7 +27,7 @@
     popoverSelector: "ul.top-right"
   ]

-  showLines: 1
+  showLines: 3

   initialize: (opts) ->
     @_currentTab = `undefined`


Rethrow inside the error handler for proper promise chaining

--- src/scripts/optimist-model.service.coffee
+++ src/scripts/optimist-model.service.coffee
@@ -131,10 +131,7 @@
       setPending: true
       updateCallback: updateCallback

-    clean   = @getClean()
-    request = apiCall clean
-
-    request.then (response) =>
+    success = (response) =>
       @_data._lastUpdated = OptimistHelpers.timestamp()

       if handleResponse
@@ -151,12 +148,16 @@

       response

-    request.catch (err) =>
+    failure = (err) =>
       @set
         updates: updates
         setError: true
         error: err
         restoreBackup: rollbackOnFailure
+
+      throw new Error('Unable to apply updates due to API failure')
+
+    apiCall( @getClean() ).then(success, failure)

   Model.prototype.update = (options = {}) ->
     @set


Environments: Fallback state for stacks

Environments: Fallback state for stacks

--- client/Environments/views/environmentsmainscene.coffee
+++ client/Environments/views/environmentsmainscene.coffee
@@ -38,13 +38,16 @@
     {JStack} = KD.remote.api

     JStack.getStacks (err, stacks)=>
-      return KD.showError err  if err
+      warn err  if err
+
+      if not stacks or stacks.length is 0
+        stacks = [{sid:0, group:'koding'}]

       stacks.forEach (stack)=>

         title   = stack.meta?.title
         number  = if stack.sid > 0 then "#{stack.sid}." else "default"
-        title or= "Your #{number} environment stack on #{stack.group}"
+        title or= "Your #{number} stack on #{stack.group}"

         @addSubView new StackView {}, {title, stack}


Environments: Fetch and create stack views based on the data provided by EnvironmentDataProvider

--- client/Environments/views/environmentsmainscene.coffee
+++ client/Environments/views/environmentsmainscene.coffee
@@ -48,24 +48,25 @@

     @fetchStacks()

-  fetchStacks:->
+  fetchStacks: (callback)->

-    {JStack} = KD.remote.api
+    EnvironmentDataProvider.get (data) =>

-    JStack.getStacks (err, stacks)=>
-      warn err  if err
+      {JStack} = KD.remote.api
+      JStack.getStacks (err, stacks)=>
+        warn err  if err

-      group = KD.getGroup().title
-      if not stacks or stacks.length is 0
-        stacks = [{sid:0, group}]
+        group = KD.getGroup().title
+        if not stacks or stacks.length is 0
+          stacks = [{sid:0, group}]

-      stacks.forEach (stack)=>
+        @_stacks = []
+        stacks.forEach (stack, index)=>
+          @_stacks.push @addSubView new StackView \
+            {stack, isDefault: index is 0}, data

-        title   = stack.meta?.title
-        number  = if stack.sid > 0 then "#{stack.sid}." else "default"
-        title or= "Your #{number} stack on #{group}"
+          callback?()  if index is stacks.length - 1

-        @addSubView new StackView {}, {title, stack}

 class StackView extends KDView


AdminAppView: make navigation judgments based on the provided data not for the currentGroup

--- client/admin/lib/views/customviews/adminappview.coffee
+++ client/admin/lib/views/customviews/adminappview.coffee
@@ -9,16 +9,14 @@

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

-    options.testPath = 'groups-admin'
-    data           or= kd.singletons.groupsController.getCurrentGroup()
+    options.testPath   = 'groups-admin'

     super options, data

-    @addSubView @nav  = new kd.TabHandleContainer
-      cssClass: 'AppModal-nav'
-
-    @addSubView @tabs = new AdminMainTabPaneView
-      tabHandleContainer: @nav
+    @addSubView @nav     = new kd.TabHandleContainer
+      cssClass           : 'AppModal-nav'
+    @addSubView @tabs    = new AdminMainTabPaneView
+      tabHandleContainer : @nav
     , data

     @nav.unsetClass 'kdtabhandlecontainer'
@@ -64,15 +62,14 @@

   createTabs: ->

-    data         = @getData()
-    {tabData}    = @getOptions()
-    currentGroup = kd.singletons.groupsController.getCurrentGroup()
+    group        = @getData()
+    { tabData }  = @getOptions()

     items = []

     for own sectionKey, section of tabData

-      if sectionKey is 'koding' and currentGroup.slug isnt 'koding'
+      if sectionKey is 'koding' and group.slug isnt 'koding'
         continue

       for item in section.items
@@ -107,7 +104,7 @@
         cssClass : slug or action
         delegate : this
         action   : action
-      , data
+      , group

     items.forEach (item, i) =>


StacksCustomViews: pass oauth data through all steps

--- client/admin/lib/views/stacks/stackscustomviews.coffee
+++ client/admin/lib/views/stacks/stackscustomviews.coffee
@@ -425,11 +425,12 @@
         whoami().fetchOAuthInfo (err, services) ->
           return  if showError err

-          unless services?[provider]?
+          unless oauth = services?[provider]
             showError "You need to authenticate with #{provider} first."
             kd.singletons.router.handleRoute '/Admin/Integrations'
           else
             data.repo_provider = provider
+            data.oauth_data    = oauth
             callback data

       return container
@@ -438,7 +439,7 @@
     stepLocateFile: (options) =>

       { callback, cancelCallback, data } = options
-      { repo_provider } = data
+      { repo_provider, oauth_data } = data

       container = @views.container 'step-locate-file'

@@ -446,8 +447,7 @@
         stepsHeaderView   :
           steps           : STEPS.REPO_FLOW
           selected        : 2
-        repoListView      :
-          users           : ['gokmen', 'koding']
+        repoListView      : { oauth_data }
         navCancelButton   :
           title           : '< Select another provider'
           callback        : cancelCallback
@@ -515,18 +515,16 @@

     repoList: (options) =>

-      controller          = new kd.ListViewController
-        viewOptions       :
-          itemClass       : StackRepoUserItem
-          cssClass        : 'repo-user-list'
+      controller    = new kd.ListViewController
+        viewOptions :
+          itemClass : StackRepoUserItem
+          cssClass  : 'repo-user-list'

       __view = controller.getListView()
       return { __view, controller }

     repoListView: (options) =>
-
-      { users } = options

       container    = @views.container 'repo-listview'
       loader       = @addTo container,


ContentSearch: Show error notification unless `res.stdOut`.

--- client/ide/views/contentsearch/idecontentsearch.coffee
+++ client/ide/views/contentsearch/idecontentsearch.coffee
@@ -81,7 +81,8 @@
     query = "#{query} #{flags.join ' '}"

     vmController.run query, (err, res) =>
-      return @showWarning 'Something went wrong, please try again.', yes  if err or res.stderr
+      if (err or res.stderr) and not res.stdout
+        return @showWarning 'Something went wrong, please try again.', yes

       @formatOutput res.stdout, @bound 'createResultsView'


Fix the compile App method that I just broke )

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -234,17 +234,15 @@
     if KDApps[name]
       callback null, KDApps[name]
     else
+
       @fetchCompiledApp manifest, (err, script)=>
         if err
-          if not /cat\:/.test err
-            @compileApp name, (err)=>
-              if err
-                new KDNotificationView type : "mini", title : "There was an error, please try again later!"
-                callback err
-              else
-                callback err, KDApps[name]
-          else
-            callback err
+          @compileApp name, (err)=>
+            if err
+              new KDNotificationView type : "mini", title : "There was an error, please try again later!"
+              callback err
+            else
+              callback err, KDApps[name]
         else
           @defineApp name, script
           callback err, KDApps[name]


Remove obsolute import.

Remove obsolute import.

--- src/spec/integration/channels.spec.coffee
+++ src/spec/integration/channels.spec.coffee
@@ -2,8 +2,6 @@
 _.mixin require('sphere-node-utils')._u
 SphereClient = require '../../lib/client'
 Config = require('../../config').config
-{_u} = require('sphere-node-utils')
-

 CHANNEL_KEY = 'OrderXmlFileExport'
 ROLE_ORDER_EXPORT = 'OrderExport'
@@ -43,7 +41,7 @@
       @logger.info @channel, 'New channel created'
       done()
     .fail (error) ->
-      done _u.prettify(error)
+      done _.prettify(error)

   afterEach (done) ->
     @client.channels.byId(@channel.id).delete(@channel.version)
@@ -52,7 +50,7 @@
       expect(result.statusCode).toBe 200
       done()
     .fail (error) ->
-      done _u.prettify(error)
+      done _.prettify(error)

   it 'should update a channel', (done) ->
     @client.channels.byId(@channel.id).update(updateChannel(@channel.version))
@@ -64,7 +62,7 @@
       expect(@channel.roles).toEqual [ROLE_INVENTORY_SUPPLY, ROLE_ORDER_EXPORT]
       done()
     .fail (error) ->
-      done _u.prettify(error)
+      done _.prettify(error)

   it 'should create a new channel with given role and return it', (done) ->
     key = uniqueId "channel"
@@ -76,7 +74,7 @@
       expect(result.body.roles).toEqual [ROLE_ORDER_EXPORT]
       done()
     .fail (error) ->
-      done _u.prettify(err)
+      done _.prettify(err)

   it 'should fetch an existing channel, add given role and return it', (done) ->

@@ -87,7 +85,7 @@
       expect(result.body.roles).toEqual [ROLE_INVENTORY_SUPPLY, ROLE_ORDER_EXPORT]
       done()
     .fail (error) ->
-      done _u.prettify(err)
+      done _.prettify(err)

   it 'should fetch an existing channel and return it', (done) ->

@@ -101,7 +99,7 @@
       expect(result.body.roles).toEqual @channel.roles
       done()
     .fail (error) ->
-      done _u.prettify(err)
+      done _.prettify(err)

   it 'should fail if role value is not supported', (done) ->
     @client.channels.byKeyOrCreate(@channel.key, 'undefined-role')


Use micro-operations in StartTab AppView

--- client/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -18,6 +18,8 @@
       @decorateApps apps

     @appsController.on "AppsDataChanged", @bound "updateAppIcons"
+    @appsController.on "InvalidateApp", @bound "removeAppIcon"
+    @appsController.on "UpdateAppData", @bound "createAppIcon"

     @finderController = KD.getSingleton "finderController"
     @finderController.on 'recentfiles.updated', =>
@@ -197,20 +199,18 @@

   updateAppIcons:(changes)->

-    {removedApps, newApps, force} = changes
-    return @decorateApps()  if force
+    {removedApps, newApps, existingApps, force} = changes
+    return @decorateApps()  if force or existingApps.length is 0
     @removeAppIcon app  for app in removedApps
     @createAppsIcons @appsController.getManifests()  if newApps.length > 0

   createAppIcon:(app, appData)->

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

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


allow ajax modal to pass in params

allow ajax modal to pass in params

--- vendor/assets/javascripts/ajax_modal.coffee
+++ vendor/assets/javascripts/ajax_modal.coffee
@@ -1,6 +1,6 @@
 (($) ->
-  $.ajaxModal = (url) ->
-    $.get url, (data) ->
+  $.ajaxModal = (url, params) ->
+    $.get url, params, (data) ->
       $modal = if $('#modalWindow').size() > 0 then $('#modalWindow') else $('<div>').addClass('modal fade').attr('id', 'modalWindow')
       $modal.html(data).modal().on 'hidden', ->
         $(@).remove()
@@ -11,4 +11,4 @@
 $ ->
   $(document.body).on 'click', 'a.ajax-modal', (e) ->
     e.preventDefault()
-    $.ajaxModal $(@).attr('href')+    $.ajaxModal $(@).attr('href'), $(@).data('params')


Correctly handle > and < in equations

Correctly handle > and < in equations

--- vendor/assets/javascripts/control.js.coffee
+++ vendor/assets/javascripts/control.js.coffee
@@ -220,7 +220,7 @@

   checkEquation: ->
     # Replace tokens with #id#.
-    equation = @stripNbsp(@input.html())
+    equation = @fixHtmlTags(@input.html())
     while (token = equation.match(@TOKEN_REGEX)) != null
       idReg = token[0].match(@ID_REGEX)
       id = idReg[0].replace('data-id="', '').replace('"', '')
@@ -265,11 +265,13 @@

     html.text().substr(0, caretPos)

-  stripNbsp: (string) ->
-    string.replace(/ /g, '')
+  fixHtmlTags: (string) ->
+    string = string.replace(/ /g, '')
+    string = string.replace(/>/g, '>')
+    string = string.replace(/</g, '<')

   fixWhitespace: ->
-    html = @stripNbsp(@input.html())
+    html = @fixHtmlTags(@input.html())
     html = html.replace(/ /g, ' ')
     html = html.replace(/[\s]+/g, ' ')
     @input.html(html)


add a per-client metric

add a per-client metric

--- services/real-time/app/coffee/DocumentUpdaterController.coffee
+++ services/real-time/app/coffee/DocumentUpdaterController.coffee
@@ -14,12 +14,15 @@
 	rclientList: RedisClientManager.createClientList(settings.redis.pubsub, settings.redis.unusedpubsub)

 	listenForUpdatesFromDocumentUpdater: (io) ->
-		for rclient in @rclientList
+		for rclient, i in @rclientList
 			rclient.subscribe "applied-ops"
 			rclient.on "message", (channel, message) ->
 				metrics.inc "rclient", 0.001 # global event rate metric
 				EventLogger.debugEvent(channel, message) if settings.debugEvents > 0
 				DocumentUpdaterController._processMessageFromDocumentUpdater(io, channel, message)
+			do (i) ->
+				rclient.on "message", () ->
+					metrics.inc "rclient-#{i}", 0.001 # per client event rate metric

 	_processMessageFromDocumentUpdater: (io, channel, message) ->
 		SafeJsonParse.parse message, (error, message) ->


More bugfixing.

More bugfixing.

--- services/web/public/coffee/ide/history/HistoryV2Manager.coffee
+++ services/web/public/coffee/ide/history/HistoryV2Manager.coffee
@@ -238,9 +238,15 @@
 			@ide.$filter("orderBy")(labels, [ '-version', '-created_at' ])

 		loadFileAtPointInTime: () ->
-			console.log @$scope.history.selection.pathname, @$scope.history.selection.updates
 			pathname = @$scope.history.selection.pathname
-			toV =  @$scope.history.selection.updates[0].toV
+			if @$scope.history.selection.updates?[0]?
+				toV = @$scope.history.selection.updates[0].toV
+			else
+				for label in @$scope.history.labels or []
+					if label.selected
+						toV = label.version
+			if !toV?
+				return
 			url = "/project/#{@$scope.project_id}/diff"
 			query = ["pathname=#{encodeURIComponent(pathname)}", "from=#{toV}", "to=#{toV}"]
 			url += "?" + query.join("&")


convert saving student id to new route type

This allows it to only listen for errors that it can handle

--- tutor/src/api.coffee
+++ tutor/src/api.coffee
@@ -169,17 +169,17 @@
   apiHelper RosterActions, RosterActions.save, RosterActions.saved, 'PATCH', (id, params) ->
     url: "/api/students/#{id}", payload: params

-
   route 'PUT', "/api/students/{studentId}/undrop",
     actions: RosterActions, trigger: 'undrop', onSuccess: 'undropped'
     errorHandlers:
       already_active: 'onUndropAlreadyActive'

-  apiHelper RosterActions, RosterActions.saveStudentIdentifier,
-    RosterActions.savedStudentIdentifier, 'PATCH', (courseId, studentId) ->
-      student_identifier = RosterStore.getStudentIdentifier(courseId, studentId)
-      url: "/api/students/#{studentId}", payload: {student_identifier}
-  , displayError: false, handleError: RosterActions.recordStudentIdError
+  route 'PATCH', '/api/students/{studentId}',
+    actions: RosterActions, trigger: 'saveStudentIdentifier', onSuccess: 'savedStudentIdentifier',
+    payload: ({courseId, studentId}) ->
+      student_identifier: RosterStore.getStudentIdentifier(courseId, studentId)
+    errorHandlers:
+      student_identifier_has_already_been_taken: 'recordDuplicateStudentIdError'

   apiHelper RosterActions, RosterActions.create, RosterActions.created, 'POST', (courseId, params) ->
     url: "/api/courses/#{courseId}/roster", payload: params


stream test

stream test

--- twitter-tests.coffee
+++ twitter-tests.coffee
@@ -22,3 +22,13 @@
     catch e
         test.equal e.toString(), 'Error: Status is a duplicate.'
     return
+
+Tinytest.addAsync 'stream', (test, onComplete) ->
+    client.stream 'statuses/filter', track: 'javascript', (stream) ->
+        stream.on 'data', (tweet) ->
+            test.equal typeof tweet.text, 'string'
+            onComplete()
+            return
+        stream.on 'error', (error) ->
+            throw error
+        return


[Group Notification Email]  added case fields for group join and left

--- workers/emailnotifications/templates.coffee
+++ workers/emailnotifications/templates.coffee
@@ -26,6 +26,12 @@
   groupApproved           :
     definition            : "group access approval"
     fullDefinition        : "has approved your access request to"
+  groupLeft               :
+    definition            : "group left"
+    fullDefinition        : "has left your group"
+  groupJoined             :
+    definition            : "group join"
+    fullDefinition        : "has joined your group"

 link      = (addr, text)   ->
   """#{text}"""
@@ -155,6 +161,12 @@
       when 'Approved'
         action  = 'has approved your access request to the group'
         preview = ''
+      when 'GroupJoined'
+        action  = 'has joined your group'
+        preview = ''
+      when 'GroupLeft'
+        action  = 'has left your group'
+        preview = ''

     """


don't log this exchange all the time

don't log this exchange all the time

--- workers/social/lib/social/followfeed.coffee
+++ workers/social/lib/social/followfeed.coffee
@@ -28,7 +28,6 @@
   JUser.on 'UserCreated',  (user) ->
     console.log 'a user is created'
     followMQ.exchange user.username, USER_EXCHANGE_OPTIONS, (exchange) ->
-      console.log {exchange}
       # don't leak a channel; gotta love this driver!
       exchange.close()


If an error is returned by the server when loading folders, the animated spinner in the sidebar will disappear.

--- app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
@@ -126,6 +126,8 @@

       $rootScope.folders_loaded = true
     .error (data, status)->
+      reset_timer()
+      $rootScope.folders_loaded = true
       timerFlagSvc.start 'error_loading_folders' if status!=0

   #--------------------------------------------


user: make style consistent

user: make style consistent

--- workers/social/lib/social/models/user.coffee
+++ workers/social/lib/social/models/user.coffee
@@ -296,8 +296,7 @@
   @addToGroup = (account, slug, callback)->
     JGroup.one {slug}, (err, group)->
       if err or not group then callback err
-      else
-        group.approveMember account, callback
+      else group.approveMember account, callback

   @addToGroups = (account, invite, callback)->
     @addToGroup account, 'koding', (err)=>
@@ -306,8 +305,7 @@
         @addToGroup account, invite.group, (err)->
           if err then callback err
           else callback null
-      else
-        callback null
+      else callback null

   @register = secure (client, userFormData, callback)->
     {connection} = client


credentials: destroy modal after button actions are done

--- client/account/lib/views/accountcredentiallistcontroller.coffee
+++ client/account/lib/views/accountcredentiallistcontroller.coffee
@@ -96,18 +96,17 @@

       listView.askForConfirm { credential, bootstrapped }, ({ action, modal }) =>

-        modal['button_Remove'].disable()
-
         switch action

           when 'Remove'
-            @removeCredential item, (err) -> modal.destroy()
+            @removeCredential item, -> modal.destroy()

           when 'DestroyAll'
             @destroyResources credential, (err) =>
               if err
-                modal.buttons.DestroyAll.hideLoader()
-                modal.buttons.Remove.enable()
+                modal['button_DestroyAll'].hideLoader()
+                modal['button_Remove'].hideLoader()
+                modal.destroy()
               else
                 @removeCredential item, -> modal.destroy()


MachineSharePopup: Remove when MachineShareActionTaken notification came.

--- client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
@@ -1,16 +1,18 @@
 kd               = require 'kd'
+nick             = require 'app/util/nick'
+KDView           = kd.View
+remote           = require('app/remote').getInstance()
+whoami           = require '../../util/whoami'
+sinkrow          = require 'sinkrow'
+Machine          = require 'app/providers/machine'
+showError        = require 'app/util/showError'
+envHelpers       = require 'ide/collaboration/helpers/environment'
+AvatarView       = require 'app/commonviews/avatarviews/avatarview'
 KDModalView      = kd.ModalView
-KDCustomHTMLView = kd.CustomHTMLView
-KDView           = kd.View
 KDButtonView     = kd.ButtonView
 KDOverlayView    = kd.OverlayView
-AvatarView       = require 'app/commonviews/avatarviews/avatarview'
-showError        = require 'app/util/showError'
-remote           = require('app/remote').getInstance()
 envDataProvider  = require 'app/userenvironmentdataprovider'
-envHelpers       = require 'ide/collaboration/helpers/environment'
-nick             = require 'app/util/nick'
-sinkrow          = require 'sinkrow'
+KDCustomHTMLView = kd.CustomHTMLView

 module.exports = class SidebarMachineSharePopup extends KDModalView
@@ -25,7 +27,7 @@

     super options, data

-    {@isApproved} = options
+    { @isApproved } = options
     @setClass 'approved'  if @isApproved

     @createArrow()
@@ -37,6 +39,8 @@
     else
       kd.singletons.windowController.addLayer this
       @on 'ReceivedClickElsewhere', @bound 'destroy'
+
+    kd.singletons.notificationController.on 'MachineShareActionTaken', @bound 'destroy'

   createArrow: ->


SidebarMachineSharePopup: Handle IDE management in approve.

--- client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
@@ -92,6 +92,7 @@
       cssClass : 'user-details'

+    # FIXME: cacheable is actually not cacheable, must be fixed before deploy.
     remote.cacheable nickname, (err, accounts) =>

       return showError err  if err
@@ -111,9 +112,30 @@
     @approveButton.showLoader()
     jMachine.approve (err) =>
       return showError err  if err
+      { router, mainView } = kd.singletons

-      kd.singletons.router.handleRoute "/IDE/#{jMachine.uid}/my-workspace"
-      @destroy()
+      doNavigation = =>
+        # route to permanent shared url to open the ide
+        router.handleRoute "/IDE/#{jMachine.uid}/my-workspace"
+
+        # defer sidebar redraw sidebar to properly select workspace
+        kd.utils.defer =>
+          mainView.activitySidebar.redrawMachineList()
+          @destroy()
+
+
+      envDataProvider.fetch =>
+        # if there is an ide instance this means user landed to ide with direct url
+        ideApp = envDataProvider.getIDEFromUId jMachine.uid
+
+        if ideApp
+          ideApp.quit()
+          # i needed to wait 737ms to do the navigation. actually i don't want
+          # to burn more ATP for this case because it's the only case if user
+          # navigates to that url manually by knowing the machine uid and stuff
+          kd.utils.wait 737, => doNavigation()
+        else
+          doNavigation()

   denyShare: ->


Tweak code-of-conduct message (trying to link to #presentaciones channel): try with robot.adapter.client.web.postMessage

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -61,9 +61,7 @@
       add_nicks user.name

   robot.respond /la bienvenida/i, (msg) ->
-    msg.reply welcomeMsg('')
+#    msg.reply welcomeMsg('')
+    robot.adapter.client.web.postMessage(msg.message.room, welcomeMsg(''), {as_user: true, link_names: true})
 #    robot.adapter.client.chat.postMessage(msg.mes   sage.room, welcomeMsg(''), {as_user: true, link_names: true})

-    msg.reply {
-        text: welcomeMsg('')
-      }


Uncomment env conditionals

Uncomment env conditionals

--- spec/integration/startup-spec.coffee
+++ spec/integration/startup-spec.coffee
@@ -1,10 +1,10 @@
 # These tests are excluded by default. To run them from the command line:
 #
 # ATOM_INTEGRATION_TESTS_ENABLED=true apm test
-# return unless process.env.ATOM_INTEGRATION_TESTS_ENABLED
+return unless process.env.ATOM_INTEGRATION_TESTS_ENABLED
 # Integration tests require a fast machine and, for now, we cannot afford to
 # run them on Travis.
-# return if process.env.TRAVIS
+return if process.env.TRAVIS

 fs = require "fs"
 path = require "path"


payment: fix strangely-formatted heredocs

--- client/landing/app/Applications/Groups.kdapplication/Views/productsettingsview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/productsettingsview.coffee
@@ -106,33 +106,37 @@

     {code} = @getData()

-    codeCheck  = """
-                 KD.remote.api.JPaymentSubscription.checkUserSubscription '#{code}', (err, subscriptions)->
-                   if not err and subscriptions.length > 0
-                     console.log "User is subscribed to the plan."
-                 """
-
-    codeGet    = """
-                 KD.remote.api.JPaymentPlan.fetchPlanByCode '#{code}', (err, plan)->
-                   if not err and plan
-                    plan.fetchSubscriptions (err, subs)->
-                      console.log "Subscribers:", subs
-                 """
-
-    codeWidget = """@content = new KDButtonView
-                    cssClass   : "clean-gray test-input"
-                    title      : "Subscribed! View Video"
-                    callback   : ->
-                      console.log "Open video..."
-
-                    @payment = new PaymentWidget
-                      planCode        : '#{code}'
-                      contentCssClass : 'modal-clean-green'
-                      content         : @content
-
-                    @payment.on "subscribed", ->
-                      console.log "User is subscribed."
-                  """
+    codeCheck =
+      """
+      KD.remote.api.JPaymentSubscription.checkUserSubscription '#{code}', (err, subscriptions)->
+        if not err and subscriptions.length > 0
+          console.log "User is subscribed to the plan."
+      """
+
+    codeGet =
+      """
+      KD.remote.api.JPaymentPlan.fetchPlanByCode '#{code}', (err, plan)->
+        if not err and plan
+          plan.fetchSubscriptions (err, subs)->
+            console.log "Subscribers:", subs
+      """
+
+    codeWidget =
+      """
+      @content = new KDButtonView
+        cssClass   : "clean-gray test-input"
+        title      : "Subscribed! View Video"
+        callback   : ->
+          console.log "Open video..."
+
+      @payment = new PaymentWidget
+        planCode        : '#{code}'
+        contentCssClass : 'modal-clean-green'
+        content         : @content
+
+      @payment.on "subscribed", ->
+        console.log "User is subscribed."
+      """

     @embedButton = new KDButtonView
       title    : "View Embed Code"