also for about me

also for about me

--- app/assets/javascripts/discourse/components/pagedown_editor.js.coffee
+++ app/assets/javascripts/discourse/components/pagedown_editor.js.coffee
@@ -20,5 +20,5 @@
   didInsertElement: ->
     $wmdInput = $('#wmd-input')
     $wmdInput.data('init', true)
-    @editor = new Markdown.Editor(Discourse.Utilities.markdownConverter())
+    @editor = new Markdown.Editor(Discourse.Utilities.markdownConverter(sanitize: true))
     @editor.run()


Client: Activity: Updated getPlaceHolder method to add isGroupChannel check

--- client/activity/lib/components/channelparticipantavatars/index.coffee
+++ client/activity/lib/components/channelparticipantavatars/index.coffee
@@ -241,7 +241,7 @@

     placeholder  = 'type a @username and hit enter'

-    if @isGroupAdmin()
+    if @isGroupAdmin() and @isGroupChannel()
       placeholder = 'type a @username or email'

     return placeholder


Client: Activity: isUserGroupAdmin method added to channellParticipantAvatars

--- client/activity/lib/components/channelparticipantavatars/index.coffee
+++ client/activity/lib/components/channelparticipantavatars/index.coffee
@@ -12,6 +12,8 @@
 DropboxInputMixin           = require 'activity/components/dropbox/dropboxinputmixin'
 getGroup                    = require 'app/util/getGroup'
 validator                   = require 'validator'
+showErrorNotification       = require 'app/util/showErrorNotification'
+

 module.exports = class ChannelParticipantAvatars extends React.Component

@@ -28,7 +30,11 @@

     super

-    @state = { addNewParticipantMode: no, showAllParticipants: no, value: '' }
+    @state =
+      value                 : ''
+      isGroupAdmin          : no
+      showAllParticipants   : no
+      addNewParticipantMode : no

   getDataBindings: ->
@@ -45,6 +51,8 @@

   componentDidMount: ->
+
+    @isUserGroupAdmin()

     document.addEventListener 'mousedown', @bound 'handleOutsideMouseClick'

@@ -205,12 +213,19 @@
     channel.setChannelParticipantsDropdownVisibility yes

-  isGroupAdmin: ->
-
-    accountId    = whoami()._id
-    groupAdminId = @props.channelThread.getIn ['channel','accountOldId']
-
-    return accountId is groupAdminId
+  isUserGroupAdmin: ->
+
+    { groupsController } = kd.singletons
+
+    groupsController.ready =>
+      currentGroup = groupsController.getCurrentGroup()
+      currentGroup.fetchMyRoles (err, roles) =>
+        return showErrorNotification err  if err
+
+        if 'admin' in (roles ? [])
+          return @setState isGroupAdmin: yes
+        else
+          return @setState isGroupAdmin: no

   isGroupChannel: ->
@@ -224,7 +239,7 @@

     DropboxInputMixin.onEnter.call this, event

-    if @isGroupAdmin() and @isGroupChannel()
+    if @state.isGroupAdmin and @isGroupChannel()

       value        = event.target.value.trim()
       isValidEmail = validator.isEmail value
@@ -241,7 +256,7 @@

     placeholder  = 'type a @username and hit enter'

-    if @isGroupAdmin() and @isGroupChannel()
+    if @state.isGroupAdmin and @isGroupChannel()
       placeholder = 'type a @username or email'

     return placeholder


Fix unauthorized head

Fix unauthorized head

--- coffee/controller/auth.coffee
+++ coffee/controller/auth.coffee
@@ -7,7 +7,7 @@
   scheme = {}

   scheme.authenticate = (request, reply) ->
-    return reply boom.unauthorized('Unauthorized', 'cookie') if not request.state.session
+    return reply boom.unauthorized('Unauthorized') if not request.state.session
     return reply null, credentials: request.state.session

   scheme.payload = (request, next) ->


Wave 12-13

Wave 12-13

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -88,6 +88,11 @@
         "TIE Aggressor Expansion Pack": "Pack de Expansión Tie Agresor"
         "Scurrg H-6 Bomber Expansion Pack": "Pack de Expansión Bombardero Scurrg H-6"
         "Auzituck Gunship Expansion Pack": "Pack de Expansión Cañonera Auzituck"
+        "TIE Silencer Expansion Pack": "Pack de Expansión Silenciador TIE"
+        "Alpha-class Star Wing Expansion Pack": "Pack de Expansión Ala Estelar clase Alfa"
+        "Resistance Bomber Expansion Pack": "Pack de Expansión Bombardero de la Resistencia"
+        "Phantom II Expansion Pack": "Pack de Expansión Fantasma II"
+        "Kimogila Fighter Expansion Pack": "Pack de Expansión Caza M12-L Kimogila"
     ui:
         shipSelectorPlaceholder: "Selecciona una nave"
         pilotSelectorPlaceholder: "Selecciona un piloto"
@@ -202,6 +207,8 @@
     exportObj.renameShip 'TIE Striker', 'Fustigador TIE'
     exportObj.renameShip 'Upsilon-class Shuttle', 'Lanzadera clase Ípsilon'
     exportObj.renameShip 'TIE Aggressor', 'TIE Agresor'
+    exportObj.renameShip 'TIE Silencer', 'Silenciador TIE'
+    exportObj.renameShip 'Alpha-class Star Wing', 'Ala Estelar clase Alfa'
     exportObj.renameShip 'A-Wing', 'Ala-A'
     exportObj.renameShip 'B-Wing', 'Ala-B'
     exportObj.renameShip 'E-Wing', 'Ala-E'
@@ -216,6 +223,8 @@
     exportObj.renameShip 'T-70 X-Wing', 'T-70 Ala-X'
     exportObj.renameShip 'U-Wing', 'Ala-U'
     exportObj.renameShip 'Auzituck Gunship', 'Cañonera Auzituck'
+    exportObj.renameShip 'B/SF-17 Bomber', 'Bombardero B/SF-17'
+    exportObj.renameShip 'Sheathipede-class Shuttle', 'Lanzadera clase Sheathipede'
     exportObj.renameShip 'M3-A Interceptor', 'Interceptor M3-A'
     exportObj.renameShip 'StarViper', 'Víbora Estelar'
     exportObj.renameShip 'Aggressor', 'Agresor'
@@ -227,6 +236,7 @@
     exportObj.renameShip 'Quadjumper', 'Saltador Quad'
     exportObj.renameShip 'C-ROC Cruiser', 'Crucero C-ROC'
     exportObj.renameShip 'Scurrg H-6 Bomber', 'Bombardero Scurrg H-6'
+    exportObj.renameShip 'M12-L Kimogila Fighter', 'Caza M12-L Kimogila'

     pilot_translations =
         "Wedge Antilles":
@@ -1060,19 +1070,32 @@
         'Dalan Oberos (Kimogila)':
             text: '''At the start of the Combat phase, you may acquire a target lock on an enemy ship inside your bullseye firing arc at Range 1-3 .'''
         'Fenn Rau (Sheathipede)':
-            text: '''When an enemy ship inside your firing arc at Range 1-3 becomes the active ship during the Combat phase, if you are not stressed, you may receive 1 stress token.  If you do, that ship cannot spend tokens to modify its dice when attacking this round.'''
+            ship: "Lanzadera clase Sheathipede"
+            text: '''Cuando una nave enemiga que tengas a alcance 1-3 y esté situada dentro de tu arco de fuego pase a ser la nave activa durante la fase de Combate, si no estás bajo tensión, puedes recibir 1 ficha de Tensión. Si lo haces, esa nave no podrá gastar fichas para modificar sus dados cuando ataque en esta ronda.'''
         'Ezra Bridger (Sheathipede)':
-            text: """When defending, if you are stressed, you may change up to 2 of your %FOCUS% results to %EVADE% results."""
+            ship: "Lanzadera clase Sheathipede"
+            text: """Cuando te defiendas, si estás bajo tensión, puedes cambiar hasta 2 de tus resultados %FOCUS% por resultados %EVADE%."""
         '"Zeb" Orrelios (Sheathipede)':
-            text: '''When defending, you may cancel %CRIT% results before %HIT% results.'''
+            ship: "Lanzadera clase Sheathipede"
+            text: '''Cuando te defiendas, puedes anular resultados %CRIT% antes de anular resultados %HIT%.'''
         'AP-5':
-            text: '''When you perform the coordinate action, after you choose a friendly ship and before it performs a free action, you may receive 2 stress tokens to remove 1 stress token from it.'''
+            ship: "Lanzadera clase Sheathipede"
+            text: '''Cuando realices la acción de coordinación, después de que elijas a una nave aliada y antes de que esa nave realice su acción gratuita, puedes recibir 2 fichas de Tensión para quitarle 1 ficha de Tensión a esa nave.'''
+        'Crimson Squadron Pilot ':
+            name: "Piloto del Escuadrón Carmesí"
+            ship: "Bombardero B/SF-17"
         '"Crimson Leader"':
-            text: '''When attacking, if the defender is inside your firing arc, you may spend 1 %HIT% or %CRIT% result to assign the "Rattled" Condition to the defender.'''
+            name: '"Piloto del Escuadrón Carmesí"'
+            ship: "Bombardero B/SF-17"
+            text: '''Cuando ataques, si el defensor está situado dentro de tu arco de fuego, puedes gastar 1 resultado %HIT% o %CRIT% para asignar el estado "Estremecido" al defensor.'''
         '"Crimson Specialist"':
-            text: '''When placing a bomb token you dropped after revealing your maneuver dial, you may place the bomb token anywhere on the play area touching your ship.'''
+            name: '"Especialista Carmesí"'
+            ship: "Bombardero B/SF-17"
+            text: '''Cuando coloques una ficha de Bomba que has soltado después de revelar tu selector de maniobras, puedes colocarla en cualquier lugar de la zona de juego donde quede en contacto con tu nave.'''
         '"Cobalt Leader"':
-            text: '''When attacking, if the defender is at range 1 of a bomb token, the defender rolls 1 fewer defense die, to a minimum of 0.'''
+            name: '"Jefe Cobalto"'
+            ship: "Bombardero B/SF-17"
+            text: '''Cuando ataques, si el defensor está situado a alcance 1 de una ficha de Bomba, el defensor tira 1 dado de defensa menos (hasta un mínimo de 0).'''
         'Kylo Ren (TIE Silencer)':
             text: '''The first time you are hit by an attack each round, deal the "I'll Show You the Dark Side" Condition card to the attacker.'''
         'Test Pilot "Blackout"':
@@ -1754,9 +1777,12 @@
         'Harpoon Missiles':
             text: '''Attack (target lock): Discard this card to perform this attack.%LINEBREAK%If this attack hits, assign the "Harpooned!" Condition to the defender.'''
         'Ordnance Silos':
-            text: '''B/SF-17 Bomber only.%LINEBREAK%When you equip this card, place 3 ordnance tokens on each other equipped %BOMB% Upgrade card. When you are instructed to discard an Upgrade card, you may discard 1 ordnance token on that card instead.'''
+            name: "Silos de munición"
+            ship: "Bombardero B/SF-17"
+            text: '''Sólo bombardero B/SF-17.%LINEBREAK%Cuando te equipes con esta carta, coloca 3 fichas de Munición de artillería encima de cada otra carta de mejora %BOMB% que tengas equipada. Cuando debas descartar una carta de Mejora, en vez de eso puedes descartar 1 ficha de Munición de artillería que esté encima de esa carta.'''
         'Trajectory Simulator':
-            text: '''You may launch bombs using the (%STRAIGHT% 5) template instead of dropping them.  You cannot launch bombs with the "Action:" header in this way.'''
+            name: "Simulador de trayectorias"
+            text: '''Puedes lanzar bombas utilizando la plantilla de maniobra (%STRAIGHT% 5) en vez de soltarlas. No puedes lanzar de esta manera bombas que tengan el encabezado "Acción:".'''
         'Jamming Beam':
             text: '''Attack: Attack 1 ship.%LINEBREAK%If this attack hits, assign the defender 1 jam token.  Then cancel all dice results.'''
         'Linked Battery':
@@ -1886,7 +1912,9 @@
             name: "Escudo de rayos pulsátil"
             text: '''Sólo Escoria y Rebelde.%LINEBREAK%Durante la fase Final, puedes recibir 1 ficha de Iones para recuperar 1 ficha de Escudos (pero no puedes exceder tu valor de Escudos). Sólo puedes equipar esta carta si tu valor de Escudos es 1.'''
         'Deflective Plating':
-            text: '''B/SF-17 Bomber only.%LINEBREAK%When a friendly bomb token detonates, you may choose not to suffer its effects.  If you do, roll an attack die.  On a %HIT% result, discard this card.'''
+            name: "Blindaje deflector de impactos"
+            ship: "Bombardero B/SF-17"
+            text: '''Sólo bombardero B/SF-17.%LINEBREAKCuando una bomba aliada se detone, puedes elegir no sufrir sus efectos. Si lo haces, tira un dado de ataque. Si sacas %HIT%, descarta esta carta.'''

     title_translations =
         "Slave I":
@@ -2094,7 +2122,9 @@
         'Os-1 Arsenal Loadout':
             text: '''Alpha-class Star Wing only.%LINEBREAK%Your upgrade bar gains the %TORPEDO% and %MISSILE% icons.%LINEBREAK%You may perform attacks with %TORPEDO% and %MISSILE% secondary weapons against ships you have locked even while you have a weapons disabled token.'''
         'Crossfire Formation':
-            text: '''B/SF-17 Bomber only.%LINEBREAK%When defending, if there is at least 1 other friendly Resistance ship at Range 1-2 of the attacker with the Crossfire Formation Upgrade card, you may add 1 %FOCUS% result to your roll.'''
+            name: "Formación de fuego cruzado"
+            ship: "Bombardero B/SF-17"
+            text: '''Sólo bombardero B/SF-17.%LINEBREAK%Cuando te defiendas, si hay por lo menos 1 otra nave aliada de la Reistencia situada a alcance 1-2 del atacante, puedes añadir 1 resultado %FOCUS% a tu tirada.'''

     condition_translations =
         '''I'll Show You the Dark Side''':
@@ -2116,6 +2146,7 @@
         'Harpooned!':
             text: '''When you are hit by an attack, if there is at least 1 uncanceled %CRIT% result, each other ship at Range 1 suffers 1 damage.  Then discard this card and receive 1 facedown Damage card.%LINEBREAK%When you are destroyed, each ship at Range 1 suffers 1 damage.%LINEBREAK%Action: Discard this card.  Then roll 1 attack die.  On a %HIT% or %CRIT% result, suffer 1 damage.'''
         'Rattled':
-            text: '''When you suffer damage from a bomb, you suffer 1 additional critical damage. Then, remove this card.%LINEBREAK%Action: Roll 1 attack die. On a %FOCUS% or %HIT% result, remove this card.'''
+            name: "Estremecido"
+            text: '''Cuando sufras daño normal o daño crítico causado por una bomba, sufres 1 punto adicional de daño crítico. Luego, retira esta carta%LINEBREAK%Acción: Tira 1 dado de ataque. Si sacas %FOCUS% o %HIT%, retira esta carta.'''

     exportObj.setupCardData basic_cards, pilot_translations, upgrade_translations, modification_translations, title_translations, condition_translations


fix scroll to el

fix scroll to el

--- app/assets/javascripts/ab_admin/core/utils.js.coffee
+++ app/assets/javascripts/ab_admin/core/utils.js.coffee
@@ -69,6 +69,7 @@
           selectElement.add option, null

 $.fn.scrollToEl = (speed='slow') ->
+  return unless $(this)[0]
   $('html, body').animate({scrollTop: $(this).offset().top}, speed)

 $.fn.toHref = ->


Add event to add new playlist option

Add event to add new playlist option

--- app/assets/javascripts/add-playlist-option.js.coffee
+++ app/assets/javascripts/add-playlist-option.js.coffee
@@ -1,9 +1,32 @@
-  $("#post_playlist_id").select2({
-    language: {
-      noResults: () ->
-        term = $('.select2-search__field')[0].value || Playlist
-        return 'Add '+term+ ''
-    }
-    escapeMarkup:
-      (markup) -> return markup
-    });
+addnew = 'Add new playlist'
+
+$('#post_playlist_id').append('<option>Add new playlist</option>')
+
+matchWithNew = (params, data) ->
+  params.term = params.term || ''
+  if (data.text.indexOf(addnew) != -1 || data.text.indexOf(params.term) != -1)
+    return data;
+  return null;
+
+formatAddNew = (data) ->
+  if ($('.select2-search__field')[0])
+    term = $('.select2-search__field')[0].value || ''
+  term = addnew + term
+  if (data.text.indexOf('Add new playlist') != -1 )
+    return '
+               '+term+'
+            '
+  return data.text
+
+$("#post_playlist_id").select2({
+  templateResult: formatAddNew
+  escapeMarkup:
+    (markup) -> return markup
+  matcher: matchWithNew
+  })
+  .on('select2:select',
+    (evt) ->
+      choice = evt.params.data.text
+      if (choice.indexOf(addnew) != -1)
+        alert('boop')
+  )


Click to hide spoilers again

Click to hide spoilers again

--- app/assets/javascripts/backbone/views/post.js.coffee
+++ app/assets/javascripts/backbone/views/post.js.coffee
@@ -75,8 +75,10 @@

         $(this).find('.innerSpoiler').css('visibility', 'hidden')
         $(this).click ->
-          $(this).addClass('revealed')
-          $(this).find('.innerSpoiler').css('visibility', 'visible')
+          if $(this).hasClass('revealed')
+            $(this).removeClass('revealed').find('.innerSpoiler').css('visibility', 'hidden')
+          else
+            $(this).addClass('revealed').find('.innerSpoiler').css('visibility', 'visible')

   # Apply referral code to Amazon links
   applyAmazonReferralCode: ->


Use textContent instead of innerText

Use textContent instead of innerText

--- app/assets/javascripts/placeholder_element.js.coffee
+++ app/assets/javascripts/placeholder_element.js.coffee
@@ -10,7 +10,7 @@
     @setPlaceholder()

   getText: () ->
-    text = @element.innerText
+    text = @element.textContent
     if text == @placeholder || text == '' then '' else text

   supressEnterKey: (e) ->
@@ -18,12 +18,12 @@
       e.preventDefault()

   clearPlaceholder: ->
-    if @element.innerText == @placeholder
-      @element.innerText = ''
+    if @element.textContent == @placeholder
+      @element.textContent = ''
       @element.classList.remove('placeholder')

   setPlaceholder: ->
-    if @element.innerText.trim() == ''
-      @element.innerText = @placeholder
+    if @element.textContent.trim() == ''
+      @element.textContent = @placeholder
       @element.classList.add('placeholder')


TeamPlans: update plan details for default plan trial

--- workers/social/lib/social/models/computeproviders/teamplans.coffee
+++ workers/social/lib/social/models/computeproviders/teamplans.coffee
@@ -24,16 +24,13 @@
     restrictions       : BASIC_RESTRICTIONS

   trial                :
-    member             : 3  # max number, can be overwritten in group data
-                            # by a super-admin (an admin in Koding group)
-    validFor           : 21 # in days (1 month)
-    instancePerMember  : 1  # allows one instance per member
-    allowedInstances   : [ 't2.nano' ]
-    maxInstance        : 3 # maximum instance count for this group (total)
-    storagePerInstance : 5  # means 25GB storage for this plan in total (max).
-                            # 5 member x 1 instancePerMember = 5 instances
-                            # 5GB per instance x 5 instances = 25GB in total
-    restrictions       : BASIC_RESTRICTIONS
+    member             : 20
+    validFor           : 21
+    instancePerMember  : 20
+    allowedInstances   : []
+    maxInstance        : 100
+    storagePerInstance : 50
+    restrictions       : {}

   basic                :
     member             : 5  # max number, can be overwritten in group data


removed concat

removed concat

--- grunt/concat.coffee
+++ grunt/concat.coffee
@@ -1,4 +0,0 @@
-module.exports = (grunt) ->
-	dist:
-		src: '.tmp/**/*.js'
-		dest: 'dist/fixtable-angular.js'


Tooltips: hide put back

Tooltips: hide put back

--- components/tooltip/KDTooltip.coffee
+++ components/tooltip/KDTooltip.coffee
@@ -60,10 +60,10 @@

   hide: (event)->
     return unless @visible
-    # super
-    # @getDomElement().remove()
-    # KD.singleton("windowController").removeLayer this
-    # @visible = no
+    super
+    @getDomElement().remove()
+    KD.singleton("windowController").removeLayer this
+    @visible = no

   update:(o = @getOptions(), view = null)->
     unless view


fix #266

fix #266

Text color is determined by current fill, which the renderers may change
in draw_legend(). Need to reset label_props before every text draw.

--- bokehjs/src/coffee/renderer/annotation/legend.coffee
+++ bokehjs/src/coffee/renderer/annotation/legend.coffee
@@ -100,7 +100,6 @@
       )
       ctx.fill()
       ctx.stroke()
-      @label_props.set(ctx, @)
       legend_spacing = @mget('legend_spacing')
       for legend_name, idx in @legend_names
         yoffset = idx * @label_height
@@ -111,6 +110,7 @@
         x2 = x1 + @glyph_width
         y1 = @box_coords[1] + yoffset + yspacing
         y2 = y1 + @glyph_height
+        @label_props.set(ctx, @)
         ctx.fillText(legend_name, x, y)
         for renderer in @model.resolve_ref(@legends[legend_name])
           view = @plot_view.renderers[renderer.id]


improve logging

improve logging

--- config/initializers/mailjobs.coffee
+++ config/initializers/mailjobs.coffee
@@ -50,7 +50,7 @@

     Mailbox.find mailboxId, (error, mailbox) ->
         if error
-            console.error "Check error.... The mailbox doesn't exist"
+            console.error "Check error... The mailbox doesn't exist"
         else
             job = jobs.create "check mailbox",
                 mailboxId: mailboxId
@@ -63,14 +63,14 @@
                 logComplete job
                 mailbox.fetchFinished (error) ->
                     if error
-                        console.error "Check error...."
+                        mailbox.log "Check error..."
                     else
-                        console.log "Check successful !"
+                        mailbox.log "Check successful !"

             job.on 'failed', () ->
                 logFailed job
                 mailbox.fetchFailed (error) ->
-                    console.error "Mail check failed."
+                    mailbox.log "Mail check failed."

             job.on 'progress', (progress) ->
                 logProgress job, progress if progress isnt lastProgress
@@ -121,14 +121,14 @@
             logComplete job
             mailbox.importSuccessfull (error) ->
                 if error
-                    console.error "Import error...."
+                    mailbox.log "Import error..."
                 else
-                    console.log "Import successful !"
+                    mailbox.log "Import successful !"

         job.on 'failed', () ->
             logFailed job
             mailbox.importFailed (error) ->
-                console.error "Import failed...."
+                mailbox.log "Import failed...."

         job.on 'progress', (progress) ->
             if progress isnt lastProgress
@@ -143,11 +143,11 @@

     Mailbox.find mailboxId, (error, mailbox) ->
         if error
-            console.error "Import error.... The mailbox doesn't exist"
+            console.error "Import error... The mailbox doesn't exist"
         else
             mailbox.setupImport (error) ->
                 if error
                     mailbox.importError ->
-                        console.error "Could not prepare the import. Aborting."
+                        mailbox.log "Could not prepare the import. Aborting."
                 else
                     createJobs mailbox, mailboxId


Tidy up #1833.

Tidy up #1833.

--- content_scripts/ui_component.coffee
+++ content_scripts/ui_component.coffee
@@ -12,7 +12,7 @@
     # Default to everything hidden while the stylesheet loads.
     styleSheet.innerHTML = "iframe {display: none;}"

-    UIComponent::styleSheetGetter ?= new AsyncDataFetcher @fetchStyles
+    UIComponent::styleSheetGetter ?= new AsyncDataFetcher @fetchFileContents "content_scripts/vimium.css"
     @styleSheetGetter.use (styles) -> styleSheet.innerHTML = styles

     @iframeElement = DomUtils.createElement "iframe"
@@ -116,21 +116,26 @@
             window.removeEventListener "focus", handler
             refocusSourceFrame()

-  fetchStyles: (callback) ->
-    stylesheetRequest = new XMLHttpRequest()
+  # Fetch a Vimium file/resource (such as "content_scripts/vimium.css").
+  # We try making an XMLHttpRequest request.  That can fail (see #1817), in which case we fetch the
+  # file/resource via the background page.
+  fetchFileContents: (file) -> (callback) ->
+    request = new XMLHttpRequest()

-    stylesheetRequest.onload = ->
-      return stylesheetRequest.onerror() unless stylesheetRequest.status == 200
-      callback stylesheetRequest.responseText
+    request.onload = ->
+      if request.status == 200
+        callback request.responseText
+      else
+        request.onerror()

-    stylesheetRequest.onerror = ->
+    request.onerror = ->
       chrome.runtime.sendMessage
         handler: "fetchFileContents"
-        fileName: "content_scripts/vimium.css"
+        fileName: file
       , callback

-    stylesheetRequest.open "GET", (chrome.runtime.getURL "content_scripts/vimium.css"), true
-    stylesheetRequest.send()
+    request.open "GET", (chrome.runtime.getURL file), true
+    request.send()

 root = exports ? window


Set correct initial state for header links

--- project/coffee/view/base/Header.coffee
+++ project/coffee/view/base/Header.coffee
@@ -43,6 +43,21 @@
 		@$navLinkContribute = @$el.find('.contribute-btn')
 		@$infoBtn           = @$el.find('.info-btn')
 		@$closeBtn          = @$el.find('.close-btn')
+
+		null
+
+	setCodeWordInitialStates : =>
+
+		state = @getSectionColour()
+
+		console.log "state", state
+
+		@$logo
+			.add(@$navLinkAbout)
+			.add(@$navLinkContribute)
+			.add(@$infoBtn)
+			.add(@$closeBtn)
+			.attr('data-codeword-initial-state', state)

 		null

@@ -136,6 +151,7 @@

 	animateTextIn : =>

+		@setCodeWordInitialStates()
 		@onAreaChange @CD().nav.current.area

 		null


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -18,6 +18,6 @@

   equals obj.I.maxHealth, 150

-test "should set both name and maxName", ->
+test "should set both <> and maxName", ->

 module()


Removed some archaic console.logs

Removed some archaic console.logs

--- test/queries.coffee
+++ test/queries.coffee
@@ -159,8 +159,6 @@
         assert.equal docName, @docName
         assert.deepEqual query, {x:5}
         called++
-        console.log(cName, docName, data, index, query);
-        console.log(data.op)
         no

       @collection.queryPoll {x:5}, opts, (err, emitter) =>


add describe(...) snipet for javascript

add describe(...) snipet for javascript

--- backup/snippets.cson
+++ backup/snippets.cson
@@ -64,6 +64,14 @@
                 $2
             });
             """
+    'describe(...) block':
+        prefix: 'des'
+        body:
+            """
+            describe ('$1', function () {
+                $2
+            });
+            """
     'function () {}':
         prefix: 'f'
         body: 'function ($1) {$2}'


Track login events.

Track login events.

--- services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
+++ services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
@@ -10,6 +10,7 @@
 basicAuth = require('basic-auth-connect')
 UserHandler = require("../User/UserHandler")
 UserSessionsManager = require("../User/UserSessionsManager")
+Analytics = require "../Analytics/AnalyticsManager"

 module.exports = AuthenticationController =
 	login: (req, res, next = (error) ->) ->
@@ -37,6 +38,7 @@
 						return next(error) if error?
 						req.session.justLoggedIn = true
 						logger.log email: email, user_id: user._id.toString(), "successful log in"
+						Analytics.recordEvent user._id, "user-logged-in"
 						res.json redir: redir
 				else
 					AuthenticationController._recordFailedLogin()


LightweightKoding: Added mixpanel to all pages.

--- server/lib/crawler/staticpages/analytics.coffee
+++ server/lib/crawler/staticpages/analytics.coffee
@@ -35,4 +35,9 @@
       f=d.getElementsByTagName("script")[0];s.src="//d37gvrvc0wt4s1.cloudfront.net/js/1/rollbar.min.js";s.async=!0;
       f.parentNode.insertBefore(s,f);};if(w.addEventListener){w.addEventListener("load",i,!1);}else{w.attachEvent("onload",i);}})(window,document);
     </script>
+
+    <!-- MIXPANEL -->
+    <script>
+      (function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f);b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2}})(document,window.mixpanel||[]);mixpanel.init("d35a8d0b14e284f32ab5380590c6848a");
+    </script>
 """


possible fix ..

possible fix ..

--- app/assets/javascripts/components/commits_table_component.js.coffee
+++ app/assets/javascripts/components/commits_table_component.js.coffee
@@ -11,7 +11,7 @@
     end     = page * perPage

     @get('commits.content').slice(start, end)
-  ).property('arrangedContent.[]', 'page', 'perPage')
+  ).property('arrangedContent.[]', 'page')

   pages: (->
     result = parseInt(@get('commits.content.length') / @get('perPage'))


code cleanup

code cleanup

--- app/assets/javascripts/hanuman/hanuman-cloudinary-uploads.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-cloudinary-uploads.js.coffee
@@ -141,7 +141,7 @@

     $(e.target).siblings('.progress').find('.document-progress-bar').removeAttr("style")
     $(e.target).siblings('.progress').addClass('hidden')
-
+
     # this if statement sets the pdf file's extension to png for a preview in upload
     # publicId = data.result.public_id
     if data.result.format == "pdf"
@@ -155,13 +155,13 @@
     if data.result.format != "pdf" && data.result.format != "png" && data.result.format != "jpg"
       $('.document-preview img:last').attr('src', '/assets/file-icon.png')

-  # validating upload format. If fomat to supported then handle error
+    # validating upload format. If fomat to supported then handle error
     if data.result.format != undefined
-      uploadFormat = "."+data.result.format
+      uploadFormat = "." + data.result.format
     else
       uploadFormat = data.result.public_id.match(/\.[0-9a-z]+$/i)[0]
     permittenFormats = [".jpg", ".png", ".gif", ".jpeg", ".xls", ".xlsx", ".pdf", ".doc", ".docx", ".txt", ".tif", ".tiff", ".zip", ".eml", ".kmz", ".ppt", ".pptx"]
-    permited  = permittenFormats.find (f) ->
+    permited = permittenFormats.find (f) ->
                  f == uploadFormat
     if permited == undefined
       $(e.target).siblings('.document-upload-error').append "
 document format NOT supported
"


Disabling and enabling of radio buttons.

--- c3/app/assets/javascripts/s4_careYouProvide/g10_breaksInCare.coffee
+++ c3/app/assets/javascripts/s4_careYouProvide/g10_breaksInCare.coffee
@@ -13,6 +13,7 @@

     $("tbody").on "click", "input[value='Delete']", ->
         $("tr input[type='button']").attr("disabled", "true").addClass("disabled")
+        $("input[type='radio']").attr("disabled", "true").addClass("disabled")
         $(".form-steps").children().attr("disabled", "true").addClass("disabled")

         tr = $(this).closest("tr")
@@ -21,6 +22,7 @@
         $(".breaks-prompt").slideDown ->
             $("input[value='No']").on "click", ->
                 $("tr input[type='button']").removeAttr("disabled").removeClass("disabled")
+                $("input[type='radio']").removeAttr("disabled", "true").removeClass("disabled")
                 $(".form-steps").children().removeAttr("disabled").removeClass("disabled")

                 $(".breaks-prompt").slideUp()
@@ -33,6 +35,7 @@
                     success: (data) ->
                         $("label[for='answer']").text(data.answer)
                         $("tr input[type='button']").removeAttr("disabled").removeClass("disabled")
+                        $("input[type='radio']").removeAttr("disabled", "true").removeClass("disabled")
                         $(".form-steps").children().removeAttr("disabled").removeClass("disabled")

                         $(".breaks-prompt").slideUp()
@@ -51,6 +54,7 @@

                     error: ->
                         $("tr input[type='button']").removeAttr("disabled").removeClass("disabled")
+                        $("input[type='radio']").removeAttr("disabled", "true").removeClass("disabled")
                         $(".form-steps").children().removeAttr("disabled").removeClass("disabled")

                         $(".breaks-prompt").slideUp()


:art: Remove whitespace in line ends, resolve #5

--- coffee/hatemile/implementation/AccessibleEventImplementation.coffee
+++ coffee/hatemile/implementation/AccessibleEventImplementation.coffee
@@ -412,7 +412,7 @@
     visit(body, (element) ->
       return (hasEvent(element, 'drag') or hasEvent(element, 'dragstart') or hasEvent(element, 'dragend'))
     , this, @makeAccessibleDragEvents)
-    visit(body, (element) ->
+    visit(body, (element) ->
       return (hasEvent(element, 'drop') or hasEvent(element, 'dragenter') or hasEvent(element, 'dragleave') or hasEvent(element, 'dragover'))
     , this, @makeAccessibleDropEvents)
     return


Internationalized the CKEditor in coordination with tkh_admin_panel.

--- app/assets/javascripts/ckeditor/config.js.coffee
+++ app/assets/javascripts/ckeditor/config.js.coffee
@@ -1,5 +1,5 @@
 CKEDITOR.editorConfig = (config) ->
-  # config.language = "fr"
+  config.language = tkh_locale
   config.uiColor = "#AADC6E"
   config.width = '575'
   config.height = '450'


Change reference for new marker placement on scrubber

--- app/assets/javascripts/me-add-to-playlist.coffee
+++ app/assets/javascripts/me-add-to-playlist.coffee
@@ -134,8 +134,9 @@
               later_markers.first().before(new_marker)
             else
               $('#markers').append(new_marker)
-            offset_percent = Math.round(if isNaN(parseFloat(offset)) then 0 else (100*offset / currentPlayer.media.duration))
-            $('.mejs-time-rail').append('')
+            offset_percent = if isNaN(parseFloat(offset)) then 0 else Math.min(1, offset / currentPlayer.media.duration)
+            offset = Math.round($('.mejs-time-total').width() * offset_percent)+'px'
+            $('.mejs-time-rail').append('')
             new_marker.find('button.edit_marker').click(enableMarkerEditForm);
             new_marker.find('.edit_avalon_marker').on('ajax:success', handle_edit_save).on 'ajax:error', (e, xhr, status, error) ->
               alert 'Request failed.'


adds marker

adds marker

--- app/assets/javascripts/views/mapping_view.coffee
+++ app/assets/javascripts/views/mapping_view.coffee
@@ -49,7 +49,12 @@

   renderPath: ->
     @map.removeLayer(@boundary) if @boundary?
-    @boundary = L.polyline(@farm.points)
+
+    if @farm.points.length is 1
+      @boundary = L.marker(@farm.points[0])
+    else
+      @boundary = L.polyline(@farm.points)
+
     @boundary.addTo(@map)

   renderPolygon: ->


fix for tooltip sticking

fix for tooltip sticking

fixes CNVS-9098

test plan:
- be sure to turn on draft state
- verify that as a teacher you still can't unpublish assignments or
  quizzes that have submissions
- verify that on the assignment and quizzes index page tooltips go away
  when moving over and then off of the publish icon (before and after
  publishing an assignment

Change-Id: If24c991daa1f5e215b7c1fcb7088378f7df67906
Reviewed-on: https://gerrit.instructure.com/25772
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Caleb Guanzon <54fa1f30d451e4f3c341a8f3c47c9215fd0be9c6@instructure.com>
Product-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>

--- app/coffeescripts/views/PublishButtonView.coffee
+++ app/coffeescripts/views/PublishButtonView.coffee
@@ -90,7 +90,7 @@

     disable: ->
       @$el.addClass @disabledClass
-      @$el.attr('data-tooltip', '')
+      #@$el.attr('data-tooltip', '')

     enable: ->
       @$el.removeClass @disabledClass
@@ -166,7 +166,7 @@
       @$desc.addClass 'screenreader-only'
       @$el.attr 'aria-describedby', descId

-      # unpublishable
+      # unpublishable
       if !@model.get('unpublishable')? or @model.get('unpublishable')
         @enable()
         @$el.attr 'title', options.text


dom_text_hiliter.coffee - Added much debug output. (Commented out for now.) - Added ability to look up nodes (when doing a hilite), if node is not referenced in the task definition - Added ability to globally undo all hiliting activity. See clean() method.   (No clear semantics for using this together with regular undo, so use with care.)

--- app/lib/dom_text_matcher/dom_text_hiliter.coffee
+++ app/lib/dom_text_matcher/dom_text_hiliter.coffee
@@ -11,7 +11,7 @@
     if indices?
       if typeof indices is 'number' then indices = [indices]
     else
-       indices = [0 ... len]
+      indices = [0 ... len]
     sel = window.getSelection()
     sel.removeAllRanges()
 #    (sel.addRange results.matches[index].range) for index in indices when 0 <= index < len
@@ -40,13 +40,19 @@
     hiliteTemplate ?= @standardHilite

     len = task.ranges.length
+#    console.log "Got " + len + " ranges."
     if indices?
       if typeof indices is 'number' then indices = [indices]
     else
+#      console.log "Got no indices to start with"
       if len
+#        console.log "Creating full indices"
         indices = [0 ... len]
       else
-         return
+#        console.log "Nothing to do"
+        return
+#    console.log "Indices are: "
+#    console.log indices

     # Prepare data structures for undo
     toInsert = []
@@ -71,6 +77,14 @@
       do (path, matches) =>
 #        console.log "Doing new node."
         node = matches[0].element.pathInfo.node
+        unless node?
+          console.log "Node missing. Looking it up..."
+          node = @domMapper.lookUpNode matches[0].element.pathInfo.path
+          console.log "Found. "
+#          console.log node
+#        else
+#          console.log "Node is:"
+#          console.log node
         # Calculate a normalized set of ranges
         ranges = @uniteRanges ({start: match.startCorrected, end: match.endCorrected, yields: match.yields } for match in matches)
         clone = node.cloneNode()
@@ -85,7 +99,7 @@
             node: clone
             before: hl
           toRemove.push hl
- #         console.log "Done full cut."
+#          console.log "Done full cut."
         else
             # Unfortunately, we need to mess around the insides of this element
             index = 0
@@ -145,24 +159,42 @@
       insert: toInsert
       remove: toRemove

+    @allActive.undo.insert.push toInsert...
+    @allActive.undo.remove.push toRemove...
+
+    null
+
   # Call this to undo a highlighting task
   #
   # It's your responsibility to only call this if a highlight is at place.
   # (Altought calling it more than once will do no harm.)
   # Pass in the task that was used with the highlighting.
   undo: (task) ->
-    unless task?.undo? then return
+    unless task?.undo?
+#      console.log "Nothing to undo"
+      return
+#    console.log "Undo hilite: " + task.undo.insert.length + " insertions, " + task.undo.remove.length + " deletions."
     insert.before.parentNode.insertBefore insert.node, insert.before for insert in task.undo.insert
+    task.undo.insert = []
     remove.parentNode.removeChild remove for remove in task.undo.remove
-    delete task.undo
+    task.undo.remove = []
+
+  clean: ->
+    console.log "Hilite cleanup"
+    @undo @allActive
+    console.log "Done"

   # ===== Private methods (never call from outside the module) =======

   constructor: (domTextMapper) ->
+    @domMapper = domTextMapper
     hl = document.createElement "span"
     hl.setAttribute "style", "background-color: yellow; color: black; border-radius: 3px; box-shadow: 0 0 2px black;"
     @standardHilite = hl
+    @allActive = undo:
+      insert: []
+      remove: []

   hilite: (node, template) ->
     parent = node.parentNode


lint(mail-rules): Fix build failure from linter issue

--- internal_packages/preferences/lib/tabs/preferences-mail-rules.cjsx
+++ internal_packages/preferences/lib/tabs/preferences-mail-rules.cjsx
@@ -84,18 +84,19 @@
       className="rule-list"
       showEditIcon={true}
       items={@state.rules}
-      itemContent={ (rule) ->
-        if rule.disabled
-          return
{rule.name}

-        else
-          return rule.name
-      }
+      itemContent={@_renderListItemContent}
       onCreateItem={@_onAddRule}
       onDeleteItem={@_onDeleteRule}
       onItemEdited={@_onRuleNameEdited}
       selected={@state.selectedRule}
       onSelectItem={@_onSelectRule} />

+  _renderListItemContent: (rule) ->
+    if rule.disabled
+      return
{rule.name}

+    else
+      return rule.name
+
   _renderDetail: =>
     rule = @state.selectedRule


copy changes

copy changes

--- client/Dashboard/views/administrationview.coffee
+++ client/Dashboard/views/administrationview.coffee
@@ -123,12 +123,12 @@
                     type    : 'restart'
                   'restart' :
                     title   : 'Shutdown in'
-                    content : 'We are upgrading the platform. Please save your work.'
+                    content : 'We are upgrading our platform. Please save your work.'
                     duration: 300
                     type    : 'restart'
                   'reload'  :
                     title   : 'Koding is updated!'
-                    content : 'Please refresh your browser to be able to use the newest features of Koding.'
+                    content : 'Please refresh your browser and enjoy the latest features.'
                     duration: 10
                     type    : 'reload'

@@ -155,7 +155,7 @@
             Description     :
               label         : "Message Details"
               type          : "text"
-              placeholder   : "We are upgrading the platform. Please save your work."
+              placeholder   : "We are upgrading our platform. Please save your work."
             Duration        :
               label         : "Timer duration"
               type          : "text"


InitialView: since we have multiple stack flow now, removed the 1 stack template limit

--- client/admin/lib/views/stacks/initialview.coffee
+++ client/admin/lib/views/stacks/initialview.coffee
@@ -18,13 +18,7 @@
     @createStackButton = new kd.ButtonView
       title    : 'Create new Stack'
       cssClass : 'solid compact green action hidden'
-      callback : (event) =>
-        if @_stackTemplatesLength >= 1 and not event?.shiftKey
-          @showWarning "Multiple Stack Template support will be
-                        provided soon. You still can modify your
-                        existing stack templates."
-        else
-          @emit 'CreateNewStack', skipOnboarding: yes
+      callback : @lazyBound 'emit', 'CreateNewStack'

     @stackTemplateList = new StackTemplateListView

@@ -43,6 +37,7 @@
       @emit 'NoTemplatesFound'  if stackTemplates.length is 0
       @_stackTemplatesLength = stackTemplates.length
       @createStackButton.show()
+      @emit 'ready'

   reload: ->


Tests: A test case for calculateAndFetchMoreIfNeeded method

--- client/app/test/kodinglistcontroller.test.coffee
+++ client/app/test/kodinglistcontroller.test.coffee
@@ -342,3 +342,27 @@
       listController.fetch {}, kd.noop

       expect(spy).toHaveBeenCalled()
+
+
+  describe '::calculateAndFetchMoreIfNeeded', ->
+
+    it 'should continue to fetch items', (done) ->
+
+      items           = [ 'kodinguser', 'kodinguser2', 'kodinguser3', 'kodinguser4', 'kodinguser5' ]
+
+      fetcherMethod   = (query, options, callback) ->
+        limit   = if not options.skip then 1 else options.limit + 1
+        result  = items.slice (options.skip or 0), limit
+        callback null, result
+
+      listController  = new KodingListController { fetcherMethod, limit : 1 }
+
+      listController.getView().setHeight 500
+      listController.getListView().setHeight 300
+
+      listController.loadItems()
+
+      kd.utils.wait 333, ->
+        { length } = listController.getListView().items
+        expect(length).toBeGreaterThan 1
+        done()


Prevent default properly

Prevent default properly

--- source/javascripts/jquery.windoze.js.coffee
+++ source/javascripts/jquery.windoze.js.coffee
@@ -46,14 +46,15 @@
           .addClass(if klass then klass.join(' ').replace(/\./g, ''))
           .appendTo($(document.body))
     show: (e) ->
+      e && e.preventDefault()
       @$modal.show()
       @$overlay.show()
       if @duration.modal then setTimeout $.proxy(@showModal, @), @duration.modal else @showModal()
       if @duration.overlay then setTimeout $.proxy(@showOverlay, @), @duration.overlay else @showOverlay()
       @bindModalEvents()
       @$modal.find(':input').eq(0).focus()
-      e.preventDefault()
     hide: (e) ->
+      e && e.preventDefault()
       m = @$modal
       o = @$overlay
       if m.find(':focus').length then return
@@ -62,7 +63,6 @@
       if @duration.modal then setTimeout $.proxy(@hideModal, @), @duration.modal else @hideModal()
       if @duration.overlay then setTimeout $.proxy(@hideOverlay, @), @duration.overlay else @hideOverlay()
       @unbindModalEvents()
-      e.preventDefault()
     showModal: ->
       @$modal.addClass('wdz-active')
     showOverlay: ->


document admin - setup the jquery chain on page load

--- app/assets/javascripts/admin/documents.js.coffee
+++ app/assets/javascripts/admin/documents.js.coffee
@@ -6,7 +6,12 @@
   $('#event-id').chained('#event-type')
   $('#event-id-search').chained('#event-type-search')

-  $('#event-type-search').click( (e) ->
+  eventTypeSearch = $('#event-type-search')
+
+  if eventTypeSearch.val()
+    $('#document-type').chained('#event-type-search')
+
+  eventTypeSearch.click( (e) ->
     documentType = $('#document-type')
     if e.target.value
       documentType.chained('#event-type-search')


Activate cache by default

Activate cache by default

--- app/assets/javascripts/ember/coderwall.js.coffee
+++ app/assets/javascripts/ember/coderwall.js.coffee
@@ -4,6 +4,8 @@
 #= require      ./ember-routemanager
 #= require      sorted-array
 #= require_self
+
+Ember.ENV.CP_DEFAULT_CACHEABLE = true

 window.Coderwall = Ember.Application.create()


rm unused require()-s

rm unused require()-s

--- client/ide.coffee
+++ client/ide.coffee
@@ -1,5 +1,4 @@
 require './menu'
-about = require './about'
 prefs = require './prefs'
 prefsUI = require './prefs-ui'
 prefsKeys = require './prefs-keys'
@@ -7,7 +6,7 @@
 {Editor} = require './editor'
 {Session} = require './session'
 keymap = require './keymap'
-{esc, delay, qw, join} = require './util'
+{esc, delay, join} = require './util'

 class @IDE
   constructor: ->


Removed more logging.

Removed more logging.

--- picoCTF-web/web/coffee/management.coffee
+++ picoCTF-web/web/coffee/management.coffee
@@ -25,7 +25,6 @@
     #get_all_problems
     apiCall "GET", "/api/admin/problems/submissions"
     .done ((api) ->
-      console.log api.data
       @setState React.addons.update @state,
         submissions: $set: api.data
     ).bind this


test response message

test response message

--- app/assets/javascripts/me-add-to-playlist.coffee
+++ app/assets/javascripts/me-add-to-playlist.coffee
@@ -36,7 +36,7 @@
               end_time: $('#playlist_item_end').val()
           success: (response) ->
             $('#add_to_playlist_alert')[0].className = 'alert alert-success'
-            $('#add_to_playlist_result_message')[0].innerHTML = response.responseJSON.message
+            $('#add_to_playlist_result_message')[0].innerHTML = response.message
             $('#add_to_playlist_alert').show(300)
             $('#add_to_playlist').hide(500)
           error: (response) ->


removing commented lines of javascript

removing commented lines of javascript

--- app/assets/javascripts/testing_grounds.js.coffee
+++ app/assets/javascripts/testing_grounds.js.coffee
@@ -41,8 +41,6 @@
   # Draw the diagram area.
   svg = d3.select(container)
     .append('svg')
-      # .attr('width', width + margin.right + margin.left)
-      # .attr('height', height + margin.top + margin.bottom)
       .attr('viewBox', "0 0 #{innerWidth} #{innerHeight}")
       .attr('preserveAspectRatio', 'xMidYMid meet')
       .attr('pointer-events', 'all')
@@ -342,7 +340,6 @@
   $('.testing-ground-view').each (idx, viewEl) ->
     if $('.loading', viewEl).length
       showTree($(viewEl).data('url'), viewEl)
-      # svg = showTopology($(viewEl).data('url'), viewEl)

   # Set up the network editors.


alert methods in the base

alert methods in the base

--- app/assets/javascripts/views/base_view.js.coffee
+++ app/assets/javascripts/views/base_view.js.coffee
@@ -27,4 +27,7 @@

 	alert: (msg)-> alertify.alert msg
 	notifyError: (msg)-> alertify.error msg
-	notifySuccess: (msg)-> alertify.success msg+	notifySuccess: (msg)-> alertify.success msg
+	confirm: (msg)->
+		alertify.set { buttonFocus: "cancel" }
+		alertify.confirm msg


Fix nested calls to silent()

Fix nested calls to silent()

--- commandant.coffee
+++ commandant.coffee
@@ -72,9 +72,12 @@

   # Execute a function without recording any command executions.
   silent: (fn) ->
-    @_silent = true
-    result = fn()
-    @_silent = false
+    if @_silent
+      result = fn()
+    else
+      @_silent = true
+      result = fn()
+      @_silent = false

     return result


added forgotten error handler call

added forgotten error handler call

--- modules/gui/platforms/node/lib/HttpContext.coffee
+++ modules/gui/platforms/node/lib/HttpContext.coffee
@@ -44,7 +44,11 @@
                 @__POOL__ = []

                 @lookup @data.libloader, @identity, (err, ctx)=>
+                    return done(err) if err
+
                     ctx.getCompiledCode (err, script)=>
+                        return done(err) if err
+
                         @__SCRIPT = script

                         done()


Troubleshoot: a simple modal is added for showing troubleshoot results

--- client/Main/avatararea/avatarareaiconmenu.coffee
+++ client/Main/avatararea/avatarareaiconmenu.coffee
@@ -5,6 +5,11 @@
     super

     @setClass "account-menu"
+
+    @troubleshootIcon = new AvatarAreaIconLink
+      cssClass   : "help acc-dropdown-icon"
+      attributes :
+        title    : "Troubleshoot"

     @helpIcon    = new AvatarAreaIconLink
       cssClass   : "help acc-dropdown-icon"
@@ -15,6 +20,10 @@
       KD.singletons.helpController.showHelp this
       KD.utils.stopDOMEvent event
       @animation?.destroy()
+
+    @troubleshootIcon.click = (event) =>
+      @_modal?.destroy()
+      @_modal = new TroubleshootModal

     @notificationsPopup = new AvatarPopupNotifications
       cssClass : "notifications"
@@ -37,6 +46,7 @@

   pistachio:->
     """
+    {{> @troubleshootIcon}}
     {{> @helpIcon}}
     {{> @notificationsIcon}}
     """
@@ -93,3 +103,53 @@
     notifications.filter (notification) ->
       snapshot = JSON.parse Encoder.htmlDecode notification.snapshot
       snapshot.anchor.constructorName in activityNameMap
+
+class TroubleshootModal extends KDModalView
+
+  constructor: (options = {}, data) ->
+    super options, data
+    KD.troubleshoot()
+    KD.singleton("troubleshoot").on "troubleshootCompleted", @bound "update"
+    status =
+      bongo :
+        status : "waiting"
+      broker:
+        status : "waiting"
+      kiteBroker:
+        status : "waiting"
+      osKite:
+        status : "waiting"
+      webServer :
+        status : "waiting"
+      connection :
+        status : "waiting"
+
+    @update status
+
+  update: (response) ->
+    @destroySubViews()
+    @bongo = new KDCustomHTMLView
+      partial : "Bongo Server Status : #{response?.bongo.status}"
+
+    @broker = new KDCustomHTMLView
+      partial : "Broker Status : #{response?.broker.status}"
+
+    @kiteBroker = new KDCustomHTMLView
+      partial : "Kite-Broker Status : #{response?.kiteBroker.status}"
+
+    @osKite = new KDCustomHTMLView
+      partial : "OS-Kite Status : #{response?.osKite.status}"
+
+    @webServer = new KDCustomHTMLView
+      partial : "Webserver Status : #{response?.webServer?.status}"
+
+    @connection = new KDCustomHTMLView
+      partial : "Internet Connection Status : #{response?.connection?.status}"
+
+    @addSubView @bongo
+    @addSubView @broker
+    @addSubView @kiteBroker
+    @addSubView @osKite
+    @addSubView @webServer
+    @addSubView @connection
+


activity: define `MessageEventHelper::removeReply` method

--- client/Social/Activity/messageeventhelper.coffee
+++ client/Social/Activity/messageeventhelper.coffee
@@ -68,3 +68,15 @@

     message.emit "AddReply", reply
     message.emit "update"
+
+
+  removeReply: (message, {replyId}) ->
+
+    for item in message.replies
+      reply = item  if replyId is item.getId()
+
+    message.replies = message.replies.filter (reply) -> reply.getId() isnt replyId
+    message.repliesCount--
+
+    message.emit "RemoveReply", reply
+    message.emit "update"


Activity: sidebar pinned item finalized

Activity: sidebar pinned item finalized

--- client/Social/Activity/sidebar/pinneditem.coffee
+++ client/Social/Activity/sidebar/pinneditem.coffee
@@ -9,21 +9,25 @@

     data = @getData()

-    @avatar = new AvatarView
+    origin =
+      id              : data.account._id
+      constructorName : data.account.constructorName
+
+    @avatar = new AvatarView {
       size       : width : 30, height : 30
       cssClass   : "avatarview"
       showStatus : yes
-    , KD.whoami()
+      origin
+    }

-    @actor = new ProfileTextView {}, KD.whoami()
-
-    @lastMessage = new KDCustomHTMLView
-      cssClass  : 'user-numbers'
-      pistachio : "{{ #(body)}}"
-    , data
+    @actor = new ProfileTextView {origin}

-  viewAppended:->
-    @addSubView @avatar
-    @addSubView @actor
-    @addSubView @lastMessage
+  viewAppended: JView::viewAppended
+
+
+  pistachio: ->
+    """
+    {{> @avatar}}{{> @actor}}
+    {span.user-numbers{ #(body)}}
+    """


activity: obsolete variable declarations

--- client/Social/Activity/views/settingsview.coffee
+++ client/Social/Activity/views/settingsview.coffee
@@ -48,8 +48,6 @@

   getOwnerMenu: ->

-    account              = KD.whoami()
-    {activityController} = KD.singletons
     @addMenuItem 'Edit Post', => @emit 'ActivityEditIsClicked'
     @addMenuItem 'Delete Post', => @confirmDeletePost()


The where compilation is broken

The where compilation is broken

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2349,7 +2349,7 @@
       jsNoop()

 findDefinitionsIncludingDeps = (ctx, names) ->
-  findDefinitions ctx, reverse setToArray (findDeps ctx) names
+  findDefinitions ctx, setToArray (findDeps ctx) names

 findDefinitions = (ctx, names) ->
   auxiliaries = ctx.auxiliaries()


Fix compiling deferring, now finally correct as it was before I mucked around it with predeclaration

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1441,7 +1441,7 @@
     while (_notEmpty ctx.deferred()) and deferredCount < ctx.deferred().length
       prevSize = ctx.deferred().length
       [expression, dependencyName, lhs, rhs] = deferred = ctx.deferred().shift()
-      if (ctx.isFinallyDeclared dependencyName)
+      if (ctx.isDeclared dependencyName)
         compiledPairs.push definitionPairCompile ctx, lhs, rhs
         deferredCount = 0
       else
@@ -3006,7 +3006,6 @@
   inferredType = freshInstance ctx, canonicalType
   ctx.extendSubstitution matchType inferredType.type, (substitute ctx.substitution, type).type
   reduceConstraints ctx, inferredType.constraints
-  console.log (printType (substitute ctx.substitution, inferredType))
   (substitute ctx.substitution, inferredType).constraints

 irReference = (name, type, scopeIndex) ->
@@ -5268,7 +5267,6 @@

 findMatchingDefinitionsOnType = (type, definitionLists) ->
   ctx = new Context
-  console.log definitionLists
   [typed, untyped] = unzip (for definitions, i in definitionLists
     isValid = (name, def) ->
       def.type? and not def.type.TempType


Fix super class constraints

Fix super class constraints

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1424,7 +1424,8 @@
     else
       constraints = typeConstraintsCompile ctx, _terms constraintSeq

-    superClasses = map (({className}) -> className), constraints
+    superClasses = map (quantifyConstraintFor paramNames), constraints
+    superClassNames = map (({className}) -> className), constraints

     # TODO: defer if not all declared to prevent cycles in classes
     #   allDeclared = (ctx.isClass c for c in superClasses)
@@ -1450,11 +1451,20 @@
           ctx.addClass name, classConstraint, superClasses, freshedDeclarations
           declareMethods ctx, classConstraint, freshedDeclarations

-          translateDict name, (keysOfMap freshedDeclarations), superClasses
+          translateDict name, (keysOfMap freshedDeclarations), superClassNames
         else
           jsNoop()
     else
       jsNoop()
+
+quantifyConstraintFor = (names) -> (constraint) ->
+  new ClassConstraint constraint.className,
+    new Types (for {name} in constraint.types.types
+      index = names.indexOf name
+      # TODO: attach to the syntax
+      # if index is -1
+      #   malformed param, 'Superclass param must occur in class\'s params'
+      new QuantifiedVar index)

 findClassType = (ctx, params, className, paramNames, methods) ->
   kinds = mapMap (-> undefined), (arrayToSet paramNames)
@@ -1588,9 +1598,7 @@
   "#{instanceName}_#{methodName}"

 findSuperClassInstances = (ctx, instanceTypes, classDefinition) ->
-  toConstraint = (superName) ->
-    new ClassConstraint superName, instanceTypes
-  superConstraints = map toConstraint, classDefinition.supers
+  superConstraints = substituteList instanceTypes.types, classDefinition.supers
   instanceDictFor ctx, constraint for constraint in superConstraints

@@ -1993,7 +2001,7 @@
 constraintsFromSuperClasses = (ctx, constraint) ->
   {className, types} = constraint
   join [constraint], concat (for s in (ctx.classNamed className).supers
-    constraintsFromSuperClasses ctx, new ClassConstraint s, types)
+    constraintsFromSuperClasses ctx, substitute types.types, s)

 constraintsFromInstance = (ctx, constraint) ->
   {className, type} = constraint
@@ -2394,8 +2402,8 @@

 findSuperClassChain = (ctx, className, targetClassName) ->
   for s in (ctx.classNamed className).supers
-    if s is targetClassName
-      return [s]
+    if s.className is targetClassName
+      return [s.className]
     else if chain = findSuperClassChain ctx, s, targetClassName
       return join [s], chain
   undefined
@@ -4943,6 +4951,35 @@
   """
   "(count (magic \\C (Map)))", 1

+  'super classes with less params'
+  """
+    Bag (class [b i]
+      length (fn [bag]
+        (: (Fn b Num)))
+      id (fn [item]
+        (: (Fn i i))))
+
+    Map (class [m k v]
+      {(Bag m v)}
+      put (fn [key value map]
+        (: (Fn k v m m))))
+
+    map-bag (instance (Bag (Map k v) v)
+      length (macro [map]
+        (: (Fn (Map k v) Num))
+        (Js.access map "size"))
+      id (fn [x] x))
+
+    map-map (instance (Map (Map k v) k v)
+      put (macro [key value map]
+        (: (Fn k v (Map k v) (Map k v)))
+        (Js.call (Js.access map "set") {key value})))
+
+    magic (fn [key map]
+      (put key 42 map))
+  """
+  "(length (magic \\C (Map)))", 1
+
   'nested pattern matching'
   """
     f (fn [x]


Dont relabel names as params

Dont relabel names as params

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1467,7 +1467,7 @@
       # Syntax - used params in function body
       # !! TODO: possibly add to nameCompile instead, or defer to IDE
       isUsedParam = (expression) ->
-        (isName expression) and (_symbol expression) in paramNames
+        (isName expression) and expression.label isnt 'name' and (_symbol expression) in paramNames
       labelUsedParams = (expression) ->
         map (syntaxNameAs ctx, '', 'param'), filterAst isUsedParam, expression
       map labelUsedParams, join docs, (if body then join [body], wheres else wheres)


Use dash for negation instead of tilde

Use dash for negation instead of tilde

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -83,7 +83,7 @@
 constantLabeling = (atom) ->
   {symbol} = atom
   labelMapping atom, [
-    ['numerical', -> /^~?(\d+|Infinity)/.test symbol]
+    ['numerical', -> /^-?(\d+|Infinity)/.test symbol]
     ['label', -> isLabel atom]
     ['string', -> /^"/.test symbol]
     ['char', -> /^\\/.test symbol]
@@ -2585,7 +2585,7 @@
 constantToSource = (value) ->
   switch typeof value
     when 'boolean' then (tokenize (if value then 'True' else 'False'))[0]
-    when 'number' then (tokenize (if value < 0 then "~#{Math.abs value}" else "#{value}"))[0]
+    when 'number' then (tokenize (if value < 0 then "-#{Math.abs value}" else "#{value}"))[0]
     when 'string' then (tokenize JSON.stringify value)[0]
     when 'object'
       kind = Object.prototype.toString.call(value).slice(8, -1)
@@ -3070,7 +3070,7 @@
   pattern: precs: []

 numericalCompile = (ctx, atom, symbol) ->
-  translation = if symbol[0] is '~' then (jsUnary "-", symbol[1...]) else symbol
+  translation = symbol#if symbol[0] is '-' then (jsUnary "-", symbol[1...]) else symbol
   type: toConstrained markOrigin numType, atom
   translation: translation
   pattern: literalPattern ctx, translation
@@ -6494,7 +6494,7 @@
     ~ (macro [x]
       (: (Fn Num Num))
       (Js.unary "-" x))
-    x ~42
+    x -42
   """
   "(~ x)", 42


MachineStateModal: Added Terminated state.

--- client/ide/views/modals/machinestatemodal.coffee
+++ client/ide/views/modals/machinestatemodal.coffee
@@ -42,7 +42,7 @@

     @createStateLabel()

-    if @state in ['Stopped', 'Running', 'NotInitialized']
+    if @state in ['Stopped', 'Running', 'NotInitialized', 'Terminated']
       @createStateButton()
     else if @state in [ 'Starting', 'Building', 'Stopping' ]
       @createLoading()
@@ -57,6 +57,7 @@
       Running        : 'up and running.'
       Building       : 'is building now.'
       NotInitialized : 'is turned off.'
+      Terminated     : 'is turned off.'
       NotFound       : 'This machine does not exist.' # additional class level state to show a modal for unknown routes.

     @label     = new KDCustomHTMLView


Textarea: JSDoc fix and code styling.

Textarea: JSDoc fix and code styling.

--- src/lib/components/textarea/Textarea.coffee
+++ src/lib/components/textarea/Textarea.coffee
@@ -33,7 +33,8 @@
   ###*
     Decore our textarea from `goog.ui.Textarea`.
     This is the painless way to resize the textarea to fit its content.
-    @private
+
+    @protected
   ###
   decorate_: ->
     @textarea_ = new goog.ui.Textarea @getOptions().value
@@ -42,6 +43,7 @@

   ###*
     Sets the value and calls the resize method to adjust height of the textarea.
+
     @export
     @override
   ###
@@ -52,7 +54,8 @@

   ###*
-    Helper method to resize textarea.
+    Helper method to resize textarea to fit height for the value inside.
+
     @export
   ###
   resize: ->


Docs: Describe named transitions that are pre-defined

--- lib/assets/javascripts/up/motion.js.coffee
+++ lib/assets/javascripts/up/motion.js.coffee
@@ -58,7 +58,7 @@

   \#\#\#\# Named animations

-  The following animations come pre-defined:
+  The following animations are pre-defined:

   | `fade-in`          | Changes the element's opacity from 0% to 100% |
   | `fade-out`         | Changes the element's opacity from 100% to 0% |
@@ -150,7 +150,7 @@
   Completes all animations and transitions for the given element
   by jumping to the last animation frame instantly. All callbacks chained to
   the original animation's promise will be called.
-
+
   Does nothing if the given element is not currently animating.

   @method up.motion.finish
@@ -174,18 +174,24 @@
       u.error("Did not return a promise with .then and .resolve methods: %o", origin)

   ###*
-  Performs a transition between two elements.
-
-  The following transitions  are pre-registered:
-
-  - `cross-fade`
-  - `move-up`
-  - `move-down`
-  - `move-left`
-  - `move-right`
-  - `none`
-
-  You can also compose a transition from two animation names
+  Performs an animated transition between two elements.
+  Transitions are implement by performing two animations in parallel,
+  causing one element to disappear and the other to appear.
+
+  \#\#\#\# Named transitions
+
+  The following transitions are pre-defined:
+
+  | `cross-fade` | Fades out the first element. Simultaneously fades in the second element. |
+  | `move-up`    | Moves the first element upwards until it exits the screen at the top edge. Simultaneously moves the second element upwards from beyond the bottom edge of the screen until it reaches its current position. |
+  | `move-down`  | Moves the first element downwards until it exits the screen at the bottom edge. Simultaneously moves the second element downwards from beyond the top edge of the screen until it reaches its current position. |
+  | `move-left`  | Moves the first element leftwards until it exists the screen at the left edge. Simultaneously moves the second element leftwards from beyond the right  edge of the screen until it reaches its current position. |
+  | `move-right` | Moves the first element rightwards until it exists the screen at the right edge. Simultaneously moves the second element rightwards from beyond the left edge of the screen until it reaches its current position. |
+  | `none`       | A transition that has no visible effect. Sounds useless at first, but can save you a lot of `if` statements. |
+
+  You can define additional named transitions using [`up.transition`](#up.transition).
+
+  You can also compose a transition from two [named animations](#named-animations).
   separated by a slash character (`/`):

   - `move-to-bottom/fade-in`


fix: atom ide config option

fix: atom ide config option

--- configs/atom.cson
+++ configs/atom.cson
@@ -1,6 +1,8 @@
 "*":
   "atom-beautify":
     general: {}
+  "atom-ide-ui":
+    use: {}
   "atom-package-deps":
     ignored: [
       "linter-package-json-validator"


feat(engine): schema executed before templated

--- packages/engine/test/actions/assert.coffee
+++ packages/engine/test/actions/assert.coffee
@@ -90,6 +90,8 @@
           -> true
         ,
           -> new Promise (resolve) -> resolve false
+        ,
+          -> true
         ]
       .should.be.rejectedWith
         code: 'NIKITA_ASSERT_UNTRUE'


copied required query collection methods to JTag to fix fetching and searching topics for guests

--- workers/social/lib/social/models/tag.coffee
+++ workers/social/lib/social/models/tag.coffee
@@ -13,12 +13,9 @@
   {permit}    = require './group/permissionset'

   @trait __dirname, '../traits/followable'
-  @trait __dirname, '../traits/filterable'
   @trait __dirname, '../traits/taggable'
   @trait __dirname, '../traits/protected'
   @trait __dirname, '../traits/slugifiable'
-  @trait __dirname, '../traits/restrictedquery'
-  # @trait __dirname, '../traits/groupable'
   @trait __dirname, '../traits/grouprelated'

   @share()
@@ -247,6 +244,64 @@
                         content.flushSnapshot tagId, (err)->
                           if err then console.log err

+  makeGroupSelector =(group)->
+    if Array.isArray group then $in: group else group
+
+  @one$ = permit 'browse content by tag',
+    success:(client, uniqueSelector, options, callback)->
+      uniqueSelector.group = makeGroupSelector client.context.group
+      @one uniqueSelector, options, callback
+
+  @some$ = permit 'browse content by tag',
+    success:(client, selector, options, callback)->
+      selector.group = makeGroupSelector client.context.group
+      @some selector, options, callback
+
+  @count$ = permit 'browse content by tag',
+    success:(client, selector, callback)->
+      [callback, selector] = [selector, callback]  unless callback
+      selector ?= {}
+      selector.group = makeGroupSelector client.context.group
+      @count selector, callback
+
+  @cursor$ = permit 'browse content by tag',
+    success:(client, selector, options, callback)->
+      selector.group = makeGroupSelector client.context.group
+      @cursor selector, options, callback
+
+  @each$ = permit 'browse content by tag',
+    success:(client, selector, fields, options, callback)->
+      selector.group = makeGroupSelector client.context.group
+      @each selector, fields, options, callback
+
+  @byRelevance = permit 'browse content by tag',
+    success: (client, seed, options, callback)->
+      [callback, options] = [options, callback] unless callback
+      {limit, blacklist, skip}  = options
+      limit     ?= 10
+      blacklist or= []
+      blacklist = blacklist.map(ObjectId)
+      cleanSeed = seed.replace(/[^\w\s-]/).trim() #TODO: this is wrong for international charsets
+      startsWithSeedTest = RegExp '^'+cleanSeed, "i"
+      startsWithOptions = {limit, blacklist, skip}
+      @findSuggestions client, startsWithSeedTest, startsWithOptions, (err, suggestions)=>
+        if err
+          callback err
+        else if limit is suggestions.length
+            callback null, suggestions
+        else
+          containsSeedTest = RegExp cleanSeed, 'i'
+          containsOptions =
+            skip      : skip
+            limit     : limit-suggestions.length
+            blacklist : blacklist.concat(suggestions.map (o)-> o.getId())
+          @findSuggestions client, containsSeedTest, containsOptions, (err, moreSuggestions)->
+            if err
+              callback err
+            else
+              allSuggestions = suggestions.concat moreSuggestions
+              callback null, allSuggestions
+
 #
 # class JLicense extends JTag
 #


Fixed problem with old CoffeeScript version

--- cs_app/app.coffee
+++ cs_app/app.coffee
@@ -142,7 +142,7 @@
           range = _.map(_.filter(range.split('bytes=')[1].split('-'), (r) -> parseInt(r) and not _.isNaN(r)), (r) -> parseInt r)
           start = parseInt range[0] if range.length > 0
           end = parseInt range[1] if range.length > 1
-      catch
+      catch e
         return res.send 500

     res.writeHead 200, { 'Content-Length': end - start, 'Content-Range': 'bytes ' + start + '-' + end + '/' + filesize }


nsca -> opsview

nsca -> opsview

--- server/lib/server/services-presence.coffee
+++ server/lib/server/services-presence.coffee
@@ -2,7 +2,7 @@

 koding = require './bongo'

-opsview = require 'koding-nsca-wrapper'
+opsview = require 'koding-opsview-wrapper'

 parseServiceKey = require 'koding-service-key-parser'

@@ -58,7 +58,7 @@
   protocol = KONFIG.broker.webProtocol ? 'https:'

   genericServices = allServices[service]
-
+
   { services: s } = genericServices

   services =


loading fixtures from generated default octopress public website directory.

--- spec/javascripts/helpers/SpecHelper.coffee
+++ spec/javascripts/helpers/SpecHelper.coffee
@@ -1,4 +1,5 @@
 beforeEach ->
-  jasmine.getFixtures().fixturesPath = 'source/_includes/custom/lunr-search/'
-  loadFixtures "search-results-template.html"
+  #jasmine.getFixtures().fixturesPath = 'source/_includes/custom/lunr-search/'
+  jasmine.getFixtures().fixturesPath = 'spec/javascripts/fixtures/app/public/'
+  loadFixtures "index.html"


Temporarily disable /contact server test

--- spec/server/functional/contact.spec.coffee
+++ spec/server/functional/contact.spec.coffee
@@ -3,7 +3,8 @@
 request = require '../request'
 User = require '../../../server/models/User'

-describe 'POST /contact', ->
+# TODO: need to update this test since /contact calls external Close.io API now
+xdescribe 'POST /contact', ->

   beforeEach utils.wrap (done) ->
     spyOn(sendwithus.api, 'send')


activity: transform tag if it is followed by a common punctuation character

--- client/Social/Activity/views/activitylistitem.coffee
+++ client/Social/Activity/views/activitylistitem.coffee
@@ -106,11 +106,15 @@
       pre  = text[offset - 1]
       post = text[offset + match.length]

-      return match  if (pre?.match /\S/)  and offset isnt 0
-      return match  if (post?.match /\S/) and (offset + match.length) isnt text.length
+      switch
+        when (pre?.match /\S/) and offset isnt 0
+          return match
+        when post?.match /[,.;:!?]/
+          break
+        when (post?.match /\S/) and (offset + match.length) isnt text.length
+          return match

       href = KD.utils.groupifyLink "/Activity/Topic/#{tag}", yes
-
       return "[##{tag}](#{href})"


Client: Activity: Updated setPaddedClassName to add/remove padded class correctly

--- client/activity/lib/components/chatpane/index.coffee
+++ client/activity/lib/components/chatpane/index.coffee
@@ -28,7 +28,7 @@
     @shouldScrollToBottom = yes  if isMessageBeingSubmitted

     scrollContainer = React.findDOMNode @refs.scrollContainer
-    scrollContainer?.classList.remove 'padded'
+    @resetPaddedClassName()

   onTopThresholdReached: (event) ->
@@ -92,9 +92,9 @@
     scrollContainerClientHeight = scrollContainer.clientHeight
     channelInfoContainerHeight  = 0

-    return scrollContainer.classList.add 'padded' if shouldPaddedClass and scrollContainer
+    return scrollContainer.classList.add 'padded' if options.force and scrollContainer

-    return  if scrollContainerClientHeight is 0 or listHeight is 0
+    return  if scrollContainerClientHeight is 0 and listHeight is 0

     if channelInfoContainer
       channelInfoContainerHeight = channelInfoContainer.offsetHeight


activity: format blockquotes

activity: format blockquotes

--- client/Social/Activity/views/activitylistitem.coffee
+++ client/Social/Activity/views/activitylistitem.coffee
@@ -89,6 +89,7 @@

     fns = [
       @bound 'transformTags'
+      @bound 'formatBlockquotes'
       KD.utils.applyMarkdown
     ]

@@ -108,6 +109,21 @@
       return match  if (post?.match /\S/) and (offset + match.length) isnt text.length
       return "[##{tag}](#{prefix}/Activity/Topic/#{tag})"

+
+  formatBlockquotes: (text = '') ->
+
+    parts = text.split '```'
+    for part, index in parts
+      blockquote = index %% 2 is 1
+
+      if blockquote
+        if match = part.match /^\w+/
+          [lang] = match
+          part = "\n#{part}"  unless hljs.getLanguage lang
+
+        parts[index] = "\n```#{part}\n```\n"
+
+    parts.join ''

   setAnchors: ->


Use ? instead or

Use ? instead or

--- client/app/Applications/Ace.kdapplication/ace.coffee
+++ client/app/Applications/Ace.kdapplication/ace.coffee
@@ -39,15 +39,15 @@
     @setSyntax()
     @setEditorListeners()
     @appStorage.fetchStorage (storage)=>
-      @setUseSoftTabs         @appStorage.getValue('useSoftTabs')         or yes
-      @setShowGutter          @appStorage.getValue('showGutter')          or yes
-      @setUseWordWrap         @appStorage.getValue('useWordWrap')         or no
-      @setShowPrintMargin     @appStorage.getValue('showPrintMargin')     or no
-      @setHighlightActiveLine @appStorage.getValue('highlightActiveLine') or yes
-      @setShowInvisibles      @appStorage.getValue('showInvisibles')      or no
+      @setUseSoftTabs         @appStorage.getValue('useSoftTabs')         ? yes
+      @setShowGutter          @appStorage.getValue('showGutter')          ? yes
+      @setUseWordWrap         @appStorage.getValue('useWordWrap')         ? no
+      @setShowPrintMargin     @appStorage.getValue('showPrintMargin')     ? no
+      @setHighlightActiveLine @appStorage.getValue('highlightActiveLine') ? yes
+      @setShowInvisibles      @appStorage.getValue('showInvisibles')      ? no
       @setSoftWrap            @appStorage.getValue('softWrap')            or 'off'
-      @setFontSize            @appStorage.getValue('fontSize')            or 12
-      @setTabSize             @appStorage.getValue('tabSize')             or 4
+      @setFontSize            @appStorage.getValue('fontSize')            ? 12
+      @setTabSize             @appStorage.getValue('tabSize')             ? 4

   setEditorListeners:->


move translate section up above the w.format method

--- src/components/widgets/hr-payroll-summary/hr-payroll-summary.directive.coffee
+++ src/components/widgets/hr-payroll-summary/hr-payroll-summary.directive.coffee
@@ -181,6 +181,13 @@
   # Chart formating function
   # --------------------------------------
   $scope.drawTrigger = $q.defer()
+
+  periodName = if ($scope.widget.metadata.hist_parameters && $scope.widget.metadata.hist_parameters.period) then $scope.widget.metadata.hist_parameters.period.toLowerCase() else 'monthly'
+  $translate('impac.widget.settings.time_period.period.' + periodName).then(
+    (translation) ->
+      $scope.period_translation = _.capitalize(translation.toLowerCase())
+  )
+
   w.format = ->
     if $scope.isDataFound && $scope.hasElements()

@@ -257,14 +264,6 @@

   w.settings.push(selectedElementsSetting)

-  # translate
-  periodName = if ($scope.widget.metadata.hist_parameters && $scope.widget.metadata.hist_parameters.period) then $scope.widget.metadata.hist_parameters.period.toLowerCase() else 'monthly'
-  $translate('impac.widget.settings.time_period.period.' + periodName).then(
-    (translation) ->
-      $scope.period_translation = _.capitalize(translation.toLowerCase())
-  )
-
-
   # Widget is ready: can trigger the "wait for settigns to be ready"
   # --------------------------------------
   $scope.widgetDeferred.resolve(settingsPromises)


also turn off event handler on document

also turn off event handler on document

--- resources/assets/coffee/react/_components/comments-manager.coffee
+++ resources/assets/coffee/react/_components/comments-manager.coffee
@@ -41,6 +41,7 @@

   componentWillUnmount: =>
     $.unsubscribe ".#{@id}"
+    $(document).off ".#{@id}"

   render: =>


MachineSettingsModal: Change tab order.

MachineSettingsModal: Change tab order.

--- client/app/lib/providers/machinesettingsmodal.coffee
+++ client/app/lib/providers/machinesettingsmodal.coffee
@@ -23,8 +23,8 @@
   { title: 'Specs',         viewClass: MachineSettingsSpecsView     }
   { title: 'Disk Usage',    viewClass: MachineSettingsDiskUsageView }
   { title: 'Domains',       viewClass: MachineSettingsDomainsView   }
+  { title: 'VM Sharing',    viewClass: MachineSettingsVMSharingView }
   { title: 'Snapshots',     viewClass: MachineSettingsSnapshotsView }
-  { title: 'VM Sharing',    viewClass: MachineSettingsVMSharingView }
   { title: 'Advanced',      viewClass: MachineSettingsAdvancedView  }
   { title: 'Common guides', viewClass: MachineSettingsGuidesView    }
 ]


Show playlist name or file name when artist/track not available

--- frontend/app/js/player-ctrl.coffee
+++ frontend/app/js/player-ctrl.coffee
@@ -24,6 +24,10 @@
   ctrl.nowPlaying = (data) ->
     if data.Artist && data.Title
       "#{data.Artist} - #{data.Title}"
+    else if data.Name
+      data.Name
+    else
+      data.file

   ctrl.friendlyQuality = (mpdAudioString, bitrate) ->
     return unless mpdAudioString


Updated ochn_high

Updated ochn_high

--- server/materials/data/level_about.coffee
+++ server/materials/data/level_about.coffee
@@ -856,7 +856,7 @@
         Более того, хотя я формально ничего не задаю, но конечно заниматься из дома также надо, одних очных занятий, скорее всего,
         вам будет недостаточно.

-
+
Занятия по воскресеньям для старших уровней немного отличаются по формату: мы решаем разные старые контесты, а потом обсуждаем задачи, см. отдельный текст про это.

Про регистрацию на занятия и компьютеры

@@ -871,14 +871,9 @@
     page("\nПро очные занятия для старших уровней ", String.raw"""

Про очные занятия для старших уровней

-
Для старших уровней (примерно уровень 3 и выше, но см. еще следующий абзац) по воскресеньям проводятся дополнительные занятия. Сейчас (сентябрь 2020 г.) пока очных занятий нет,
-        дополнительные занятия проводятся в дистанционном режиме. А именно, я заранее готовлю некоторый контест (обычно — какой-нибудь старый div2-раунд с codeforces), вы его заранее
-        в виртуальном режиме решаете в удобное для вас время, а потом в воскресенье мы в дистанционном режиме обсуждаем задачи и обсуждаем какую-нибудь еще информацию,
-        в первую очередь про нижегородские олимпиады. Для участия надо состоять в группе занятий на cf и в чате занятий в Телеграмме. Напишите мне ваш логин на cf и логин (или телефон) в Телеграмме,
-        я вас добавлю туда и туда. Конкретная информация про занятия каждой недели будет в группе в Телеграмме.

-
Я ожидаю на занятиях учеников уровня 3 и выше, хотя, если у вас уровень ниже 3, но вы хотите участвовать — пожалуйста, но будьте готовы к тому, что может быть сложновато.
-        Занятия проводятся в первую очередь для нижегородских школьников, и часть обсуждений будут касаться нижегородских олимпиад, но если вы не являетесь нижегородским школьников,
-        то тоже можете присоединяться, в дистанционном режиме мне не жалко :) Только естественно надо, чтобы у вас были оплачены занятия на алгопроге, если они для вас платные.

+
Для старших уровней (примерно уровень 3 и выше, но см. еще следующий абзац) по воскресеньям проводятся дополнительные занятия. А именно, я заранее готовлю некоторый контест (обычно — какой-нибудь старый div2-раунд с codeforces), в воскресенье во время обычного занятия вы его решаете, потом мы обсуждаем задачи и обсуждаем какую-нибудь еще информацию,
+        в первую очередь про нижегородские олимпиады.

+
Я ожидаю на занятиях учеников уровня 3 и выше, хотя, если у вас уровень ниже 3, но вы хотите участвовать — пожалуйста, но будьте готовы к тому, что может быть сложновато.

     """, {id: "ochn_high"})

 module20927_29 = () ->


landing: added latest and sandbox envs

landing: added latest and sandbox envs

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -333,14 +333,18 @@
     { hostname } = location
     mainDomains = ['dev.koding.com', 'sandbox.koding.com', 'latest.koding.com', 'prod.koding.com']
     groupName = if hostname in mainDomains then 'koding'
+    if hostname.endsWith("asdf")
     else if hostname.indexOf('.dev.koding.com') isnt -1
     then hostname.replace('.dev.koding.com', '').split('.').last
+    else if hostname.indexOf('.sandbox.koding.com') isnt -1
+    then hostname.replace('.sandbox.koding.com', '').split('.').last
+    else if hostname.indexOf('.latest.koding.com') isnt -1
+    then hostname.replace('.latest.koding.com', '').split('.').last
     else if hostname.indexOf('.koding.com') isnt -1
     then hostname.replace('.koding.com', '').split('.').last
     else 'koding'

     return groupName
-

   checkIfGroupExists: (groupName, callback) ->

@@ -523,4 +527,4 @@
       type      : 'POST'
       success   : -> location.href = formData.redirect
       error     : ({responseText}) ->
-        new KDNotificationView title : responseText+        new KDNotificationView title : responseText


Revert "Landing: add confirmToken helper function"

This reverts commit 6c4c35ae28e9cfd2873c6b5a2f03777277b144c7.

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -194,20 +194,6 @@
         new KDNotificationView title : responseText

-  confirmToken: (token, callbacks = {}) ->
-
-    _csrf = Cookies.get '_csrf'
-
-    $.ajax
-      url     : '/-/confirm'
-      data    : { token, _csrf }
-      type    : 'GET'
-      success : callbacks.success or ->
-        location.href = '/'
-      error   : callbacks.error or ({responseText}) ->
-        new KDNotificationView { title : responseText }
-
-
   routeIfInvitationTokenIsValid: (token, callbacks) ->

     $.ajax


Fix tests

Fix tests

--- services/notifications/test/unit/coffee/NotificationsTests.coffee
+++ services/notifications/test/unit/coffee/NotificationsTests.coffee
@@ -66,7 +66,7 @@
 			@countStub.callsArgWith(1, null, 0)

 			@notifications.addNotification user_id, @stubbedNotification, (err)=>
-				@insertStub.calledWith(@expectedDocument).should.equal true
+				assert.deepEqual(@insertStub.lastCall.args[0], @expectedDocument)
 				done()

 		it 'should fail insert of existing notification key', (done)->
@@ -100,7 +100,7 @@

 				@notifications.addNotification user_id, @stubbedNotification, (err)=>
 					@insertStub.callCount.should.equal 1
-					@insertStub.calledWith(@expectedDocument).should.equal true
+					assert.deepEqual(@insertStub.lastCall.args[0], @expectedDocument)
 					done()

 		describe 'when the notification has a nonsensical expires field', () ->
@@ -149,12 +149,15 @@
 			@updateStub.callsArgWith(2, null)

 			@notifications.removeNotificationKey user_id, notification_key, (err)=>
-				searchOps =
+				searchOps = {
 					user_id:ObjectId(user_id)
 					key: notification_key
-				updateOperation =
+				}
+				updateOperation = {
 					"$unset": {templateKey:true}
-				@updateStub.calledWith(searchOps, updateOperation).should.equal true
+				}
+				assert.deepEqual(@updateStub.args[0][0], searchOps)
+				assert.deepEqual(@updateStub.args[0][1], updateOperation)
 				done()

 	describe 'removeNotificationByKeyOnly', ->


rename dropdown -> dropbox

rename dropdown -> dropbox

--- services/web/public/coffee/settings/DropboxSettingsManager.coffee
+++ services/web/public/coffee/settings/DropboxSettingsManager.coffee
@@ -17,11 +17,11 @@
 					@tab.empty()
 					if !@ide.isAllowedToDoIt "owner"
 					else if !@project.get('features').dropbox
-						ga('send', 'event', 'subscription-funnel', 'askToUpgrade', "dropdown")
+						ga('send', 'event', 'subscription-funnel', 'askToUpgrade', "dropbox")
 						accountManager.askToUpgrade @ide,
 							onUpgrade: =>
 								@checkIfUserIsLinkedToDropbox()
-								ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "dropdown")
+								ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "dropbox")
 					else
 						@checkIfUserIsLinkedToDropbox()


switched to marionette view instead of plain class

--- core/app/backbone/factlink/tooltip_definition.coffee
+++ core/app/backbone/factlink/tooltip_definition.coffee
@@ -13,49 +13,47 @@

 Backbone.Factlink ||= {}

-class HoverState extends Backbone.Model
+class Hovermodel extends Backbone.Model
   defaults:
     inTooltip: false
     inTarget: false

   hovered: -> @get('inTarget') || @get('inTooltip')

+class Backbone.Factlink.TooltipDefinition extends Backbone.Marionette.View
+  _closingtimeout: 500

-class Backbone.Factlink.TooltipDefinition
-  closingtimeout: 500
+  modelEvents:
+    'change': '_toggleTooltip'

-  constructor: (options) ->
-    @_options = options
-    @state = new HoverState
-    @state.on 'change', => @toggleTooltip()
+  initialize: -> @model = new Hovermodel

   render: ->
-    @_options.$container.hoverIntent
-      timeout: @closingtimeout
-      selector: @_options.selector
-      over: => @state.set inTarget: true
-      out:  => @state.set inTarget: false
-    @$target = @_options.$container.find(@_options.selector)
+    @$target = @options.$container.find(@options.selector)
+    @$target.hoverIntent
+      timeout: @_closingtimeout
+      over: => @model.set inTarget: true
+      out:  => @model.set inTarget: false

-  close: ->
-    @removeTooltip()
-    @_options.$container.off(".hoverIntent")
+  onClose: ->
+    @_removeTooltip()
+    @options.$container.off(".hoverIntent")

-  removeTooltip: =>
-    @_options.removeTooltip @_options.$container, @$target, @_$tooltip
+  _openTooltip: ->
+    @_$tooltip = @options.makeTooltip @options.$container, @$target
+    @_$tooltip.hoverIntent
+      timeout: @_closingtimeout
+      over: => @model.set inTooltip: true
+      out:  => @model.set inTooltip: false
+
+  _removeTooltip: ->
+    @options.removeTooltip @options.$container, @$target, @_$tooltip
     delete @_$tooltip

-  start_tooltip: ->
-    @_$tooltip = @_options.makeTooltip @_options.$container, @$target
-    @_$tooltip.hoverIntent
-      timeout: @closingtimeout
-      over: => @state.set inTooltip: true
-      out:  => @state.set inTooltip: false
-
-  toggleTooltip: ->
+  _toggleTooltip: ->
     if @_$tooltip
-      @removeTooltip() unless @state.hovered()
+      @_removeTooltip() unless @model.hovered()
     else
-      @start_tooltip() if @state.hovered()
+      @_openTooltip() if @model.hovered()


fixed reading id of undefined

fixed reading id of undefined

--- src/lib/controllers/user_controller.coffee
+++ src/lib/controllers/user_controller.coffee
@@ -59,7 +59,7 @@
   res.redirect '/'

 get_user_uploaded = (req, res) ->
-  my_user_id = req.user.id
+  my_user_id = req.user and req.user.id
   user_id = req.params.user_id

   models.User.find(
@@ -84,7 +84,6 @@
       images.push current_row

     if my_user_id == parseInt(user_id)
-      console.log "this is happening!"
       title = 'My Images'
       your_or_their = "Your Images"
     else


Disable font face in PDFs for IE 11 as well

--- services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
@@ -12,7 +12,7 @@
 			INDICATOR_DELAY2: 250  # time until the indicator starts animating

 			constructor: (@url, @options) ->
-				if navigator.userAgent?.indexOf("Edge/") >= 0
+				if navigator.userAgent?.indexOf("Edge/") >= 0 or navigator.userAgent?.indexOf("rv:11.0") >= 0 # IE 11
 					# Microsoft Edge does not work well with font-face (Sept 2016)
 					PDFJS.disableFontFace = true
 				if @options.disableAutoFetch


do not show loader for multiple calendar deletion

--- client/app/views/menu.coffee
+++ client/app/views/menu.coffee
@@ -73,12 +73,11 @@
     onCalendarMultipleRemove: ->
         message = t 'confirm delete selected calendars'
         if confirm(message)
-            @showLoading()
             $('.calendar-actions:checked').each ->
                 calendarName = @value
                 tag = app.tags.getByName calendarName
-                app.calendars.remove calendarName, =>
-                    @hideLoading()
+                app.calendars.remove calendarName
+
         # remove additional menu if only 1 calendar is left
         if $('#menu-items .calendar-name').length < 2
             $('#multiple-actions').addClass 'hidden'


prevented autocompletion in the beginning of a line

--- client/autocompletion.coffee
+++ client/autocompletion.coffee
@@ -7,7 +7,7 @@
     tid = setTimeout(
       ->
         tid = null; c = cm.getCursor(); s = cm.getLine c.line; i = c.ch
-        if s[i - 1] !in [' ', keymap.getPrefixKey()] then requestAutocompletion s, i
+        if i && s[i - 1] !in [' ', keymap.getPrefixKey()] then requestAutocompletion s, i
         return
       500
     )


Made mincha earlier on Ereb Pesach

Made mincha earlier on Ereb Pesach

--- source/javascripts/index/mincha.coffee
+++ source/javascripts/index/mincha.coffee
@@ -186,6 +186,7 @@
     when @hebrew_date.isEreb9Ab() && !@hebrew_date.isShabbat() then minutes_before_event(@sunset, 55)
     when @tonight_is_yom_tob() && @hebrew_date.isShabbat() then minutes_before_event(@sunset, 23)
     when @hebrew_date.is6thDayOfPesach() then @today().hour(18).minute(30)
+    when @hebrew_date.isErebPesach() then minutes_before_event(@sunset, if @hebrew_date.isErebShabbat() then 47 else 30)
     when @hebrew_date.isShabbat() then @mincha_on_shabbat()
     when @yom_tob_that_we_can_pray_at_plag() then (if @hebrew_date.isErebShabbat() then @mincha_on_ereb_shabbat() else minutes_before_event(@plag(), 30))
     when @hebrew_date.isYomTob() && !@yom_tob_that_we_can_pray_at_plag() then minutes_before_event(@sunset, if @hebrew_date.isErebShabbat() then 40 else 25)


display latest chametz times on Ereb Pesach. fixes #29

--- source/javascripts/index/mincha.coffee
+++ source/javascripts/index/mincha.coffee
@@ -19,9 +19,10 @@
   today: -> moment(@sunset)
   tonight_is_yom_tob: -> @hebrew_date.isErebYomTob() || @hebrew_date.is1stDayOfYomTob()
   yom_tob_that_we_can_pray_at_plag: -> @hebrew_date.is7thDayOfPesach() || @hebrew_date.is1stDayOfShabuot()
-  last_time_for_shema: -> @_last_time_for_shema ?= moment.min(
-      moment(@zmanim.magen_abraham_dawn).add((@zmanim.magen_abraham_dusk - @zmanim.magen_abraham_dawn) / 4000, 'seconds'),
-      moment(@zmanim.sunrise).subtract(36, 'minutes').add((@zmanim.sunset - @zmanim.sunrise) / 4000, 'seconds'),
+  last_time_for_shema: -> @_last_time_for_shema ?= @shaa_zemani(3)
+  shaa_zemani: (hour) -> moment.min(
+      moment(@zmanim.magen_abraham_dawn).add((@zmanim.magen_abraham_dusk - @zmanim.magen_abraham_dawn) / 1000 * hour / 12, 'seconds'),
+      moment(@zmanim.sunrise).subtract(36, 'minutes').add((@zmanim.sunset - @zmanim.sunrise) / 1000 * hour / 12, 'seconds'),
     )
   shema_is_before_9_am: -> @last_time_for_shema().isBefore(@today().hour(if (@hebrew_date.is1stDayOfPesach() || @hebrew_date.is2ndDayOfPesach()) then 10 else 9).minute(0))
   mincha_minutes_before_sunset_on_shabbat: -> if @hebrew_date.isEreb9Ab() then 100 else 45
@@ -208,8 +209,13 @@
     $(".#{@chag()}").removeClass("hidden").find('.time').html(@today().hour(0).minute(0).format('h:mm A'))
     @set_date()
   announcement: -> @_announcement ?= (
-    a = window.announcements[@hebrew_date.getHebrewYear().getYearFromCreation()]
-    a = a[@hebrew_date.weekOfYear()] if a?
+    if @hebrew_date.isErebPesach()
+      latest_time_to_eat = @shaa_zemani(4).format('h:mm A')
+      latest_time_to_burn = @shaa_zemani(5).format('h:mm A')
+      "Stop eating חָמֵץ before #{latest_time_to_eat}#{if @hebrew_date.isShabbat() then " on Friday"  else ""}<br>Burn חָמֵץ before #{latest_time_to_burn}#{if @hebrew_date.isShabbat() then " on שַׁבָּת" else ""}"
+    else
+      a = window.announcements[@hebrew_date.getHebrewYear().getYearFromCreation()]
+      a = a[@hebrew_date.weekOfYear()] if a?
     )

 window.mincha_and_arbit = (day_iterator) ->
@@ -225,6 +231,7 @@
     schedule.taanit_schedule()
   if schedule.hebrew_date.isShabbat()
     $('.sedra').html(schedule.sedra())
+  if schedule.hebrew_date.isShabbat() || schedule.hebrew_date.isErebPesach()
     $(".announcement.jumbotron").removeClass('hidden').html(schedule.announcement()) if schedule.announcement()?
   else if schedule.hebrew_date.isErebHoshanaRaba()
     schedule.tiqun_leil_hoshana_raba_schedule()


new buttons added

new buttons added

--- lib/atom-python-test-view.coffee
+++ lib/atom-python-test-view.coffee
@@ -12,7 +12,9 @@
           @button outlet: 'closeBtn', class: 'btn inline-block-tight right', click: 'destroy', style: 'float: right', =>
             @span class: 'icon icon-x'
           @button outlet: 'clearBtn', class: 'btn inline-block-tight right', click: 'clear', style: 'float: right', =>
-            @span class: 'icon icon-zap'
+            @span class: 'icon icon-trashcan'
+          @button outlet: 'clearBtn', class: 'btn inline-block-tight right', click: 'clear', style: 'float: right', =>
+            @span class: 'icon icon-history'
         @pre class: 'output', outlet: 'output'
         # TODO: create history button


Show an error if the source couldn't be read

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -130,14 +130,21 @@
   renderMarkdown: ->
     @showLoading() unless @loaded
     @getMarkdownSource().then (source) => @renderMarkdownText(source) if source?
+    .catch (reason) =>
+      @showError({message: reason})

   getMarkdownSource: ->
     if @file?.getPath()
-      @file.read()
+      @file.read().then (source) =>
+        if source is null
+          Promise.reject("#{@file.getBaseName()} could not be found")
+        else
+          Promise.resolve(source)
+      .catch (reason) => Promise.reject(reason)
     else if @editor?
       Promise.resolve(@editor.getText())
     else
-      Promise.resolve(null)
+      Promise.reject()

   getHTML: (callback) ->
     @getMarkdownSource().then (source) =>


UI : fix sliding issues

UI : fix sliding issues

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -32,8 +32,6 @@
   Views: {}

 $.fn.fadeSlideLeft = (px, speed,fn) ->
-  if $(@).css('right') == '-'+px+'px'
-    $(@).animate {right : '+='+px}, speed || 400, -> $.isFunction(fn) && fn.call(this)
+  $(@).stop().animate {right : '0px'}, speed || 400, -> $.isFunction(fn) && fn.call(this)
 $.fn.fadeSlideRight = (px, speed,fn) ->
-  if $(@).css('right') == '0px'
-    $(@).animate {right : '-='+px}, speed || 400, -> $.isFunction(fn) && fn.call(this)+  $(@).stop().animate {right : '-'+px+'px'}, speed || 400, -> $.isFunction(fn) && fn.call(this)


[JS] testing beforeunload stuff

[JS] testing beforeunload stuff

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -422,11 +422,13 @@
   loseChangesMessage = "You have unsaved changes! If you leave the page now, some answers will be lost. Stay on the page for a minute in order for everything to be saved or use the buttons at the bottom of the page."

   window.onbeforeunload = ->
+    console.log("beforeunload-----------------1")
     if !$(".page-read-only-form").length
       if changesUnsaved then loseChangesMessage else undefined

   if window.addEventListener isnt undefined
     window.addEventListener "beforeunload", (e) ->
+      console.log("beforeunload-----------------2")
       return undefined unless changesUnsaved

       e.returnValue = loseChangesMessage


fixing merge

fixing merge

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
 # This is a manifest file that'll be compiled into application.js, which will include all the files
 # listed below.
 #


Add the blue color

Add the blue color

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -48,7 +48,7 @@
     palette: [
       [
         'black'
-        'white'
+        '#0FF'
         'blanchedalmond'
       ]
       [