fixed issue reported by szaby after npm connect-redis update

--- nodejs/palsu.coffee
+++ nodejs/palsu.coffee
@@ -8,7 +8,7 @@
 auth = require 'connect-auth'
 sys = require 'sys'
 require './vie-redis.coffee'
-RedisStore = require 'connect-redis'
+RedisStore = require('connect-redis')(express)
 querystring = require 'querystring'
 #require '../js/auth/auth.strategies/linkedin.js'
 fs = require 'fs'


invitations: if groupName is not accessible over client, do not revoke

--- workers/social/lib/social/models/invitation.coffee
+++ workers/social/lib/social/models/invitation.coffee
@@ -113,7 +113,9 @@

         { _id } = account
         { roles } = permissionAndRoles
-        groupName = client.context.group or 'koding'
+        groupName = client.context.group
+
+        return callback null, this  unless groupName

         hasPermisson = 'admin' in roles or 'owner' in roles
         { inviterId, email, groupName } = invite


Update description helper to pick first paragraph

--- node_modules/application/helpers.coffee
+++ node_modules/application/helpers.coffee
@@ -28,15 +28,23 @@

     episodeTitle: (text, showTitle) ->
       #strip off spaces and "nutv -" from the start
-      text = text.replace(/^.*[-:]\s?/i, '')
+      text = text?.replace(/^.*[-:]\s?/i, '')
       if showTitle?
         regexStr = '(?:\s)*(?:-)*(?:\s)*' + showTitle + '(?:\s)*(?:-)*(?:\s)*'
         regex = new RegExp('(?:\s)*(?:-)*(?:\s)*' + showTitle + '(?:\s)*(?:-)*(?:\s)*', 'i')
-        text = text.replace(regex, '')
+        text = text?.replace(regex, '')

     episodeDescriptionSnippet: (text) ->
-      snippet = text.match(/((?:\w+[,!@#%&:;]?\s){0,15})/i)?[1]
-      if !!snippet then snippet = snippet + '...'
+
+      # get the first paragraph up to a line break
+      snippet = text?.match(/(.*)[\n\Z]/i)?[1]
+
+      words = snippet?.split(' ').filter (item) -> return !!item
+
+      # either return the words as a string or if necessary trim off some of
+      # the words and append ... before returning the words as a string
+      if words?.length > 15 then return words.splice(0, 15).join(' ') + ' ...'
+      words?.join(' ')

     episodeDate: (dateText) ->
       moment(dateText).format('MMMM Do YYYY')


oauth: move checking for foreignAuth existence to start

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -822,7 +822,7 @@
   @persistOauthInfo: (username, clientId, callback)->
     @extractOauthFromSession clientId, (err, foreignAuthInfo, session)=>
       return callback err  if err
-      return callback()    unless foreignAuthInfo
+      return callback()    unless foreignAuthInfo?
       @saveOauthToUser foreignAuthInfo, username, (err)=>
         return callback err  if err
         @clearOauthFromSession session, (err)=>


S70088 - Update IterationPlanningBoardAppSpec for new backlog search bar

--- test/spec/iterationplanningboard/IterationPlanningBoardAppSpec.coffee
+++ test/spec/iterationplanningboard/IterationPlanningBoardAppSpec.coffee
@@ -25,7 +25,7 @@

     createAppWithBacklogData: ->
       userStoryRecord = @createUserStoryRecord
-        Name: 'A User Story'
+        Name: 'A User Story z'
         Iteration: null

       defectRecord = @createDefectRecord
@@ -55,9 +55,8 @@
         )

     filterByBacklogCustomSearchQuery: (query) ->
-      @click(css: '.search-text input').then (el) =>
-        el.sendKeys(query).then =>
-          @click(className: 'search-button')
+      @click(css: '.rally-search-text input').then (el) =>
+        el.sendKeys(query)

     getProgressBar: (columnIndex) ->
       @getColumns()[columnIndex].getProgressBar().down('.progress-bar')
@@ -150,45 +149,45 @@

       expect(contentUpdatedHandlerStub).toHaveBeenCalledOnce()

-#  it 'should exclude filtered artifact types when filtering by custom search query on the backlog column', ->
-#    @createAppWithBacklogData().then =>
-#      columns = @getColumns()
-#
-#      @filterByBacklogCustomSearchQuery('A').then =>
-#        expect(columns[0].getCards().length).toBe 2
-#        @filterByType('hierarchicalrequirement').then =>
-#          expect(columns[0].getCards().length).toBe 1
-#
-#  it 'should filter by artifact type and still filter by custom search query on the backlog column', ->
-#    @createAppWithBacklogData().then =>
-#      columns = @getColumns()
-#
-#      @filterByType('hierarchicalrequirement').then =>
-#        @filterByType('defect').then =>
-#          @filterByBacklogCustomSearchQuery('A').then =>
-#            expect(columns[0].getCards().length).toBe 0
-#
-#  it 'should remove all cards (including deactivated cards) when submitting a search in the backlog column', ->
-#    @createAppWithBacklogData().then =>
-#      columns = @getColumns()
-#      backlogColumn = columns[0]
-#      clearCardsSpy = @spy(backlogColumn, 'clearCards')
-#
-#      @filterByType('hierarchicalrequirement').then =>
-#        @filterByType('defect').then =>
-#          @filterByBacklogCustomSearchQuery('A').then =>
-#            expect(clearCardsSpy).toHaveBeenCalled()
-#            expect(backlogColumn.getCards(true).length).toBe 2
-#            expect(backlogColumn.getCards().length).toBe 0
-#
-#  it 'should correctly clean up deactivated cards', ->
-#    @createAppWithBacklogData().then =>
-#      columns = @getColumns()
-#      @filterByType('defect').then =>
-#        @ajax.whenQuerying('artifact').respondWith [columns[0].getCards()[0].getRecord().data]
-#        @filterByBacklogCustomSearchQuery('Story').then =>
-#          @filterByType('defect').then =>
-#            expect(columns[0].getCards().length).toBe 1
+  it 'should exclude filtered artifact types when filtering by custom search query on the backlog column', ->
+    @createAppWithBacklogData().then =>
+      columns = @getColumns()
+
+      @filterByBacklogCustomSearchQuery('A').then =>
+        expect(columns[0].getCards().length).toBe 2
+        @filterByType('hierarchicalrequirement').then =>
+          expect(columns[0].getCards().length).toBe 1
+
+  it 'should filter by artifact type and still filter by custom search query on the backlog column', ->
+    @createAppWithBacklogData().then =>
+      columns = @getColumns()
+
+      @filterByType('hierarchicalrequirement').then =>
+        @filterByType('defect').then =>
+          @filterByBacklogCustomSearchQuery('A').then =>
+            expect(columns[0].getCards().length).toBe 0
+
+  it 'should remove all cards (including deactivated cards) when submitting a search in the backlog column', ->
+    @createAppWithBacklogData().then =>
+      columns = @getColumns()
+      backlogColumn = columns[0]
+      clearCardsSpy = @spy(backlogColumn, 'clearCards')
+
+      @filterByType('hierarchicalrequirement').then =>
+        @filterByType('defect').then =>
+          @filterByBacklogCustomSearchQuery('A').then =>
+            expect(clearCardsSpy).toHaveBeenCalled()
+            expect(backlogColumn.getCards(true).length).toBe 2
+            expect(backlogColumn.getCards().length).toBe 0
+
+  it 'should correctly clean up deactivated cards', ->
+    @createAppWithBacklogData().then =>
+      columns = @getColumns()
+      @filterByType('defect').then =>
+        @ajax.whenQuerying('artifact').respondWith [columns[0].getCards()[0].getRecord().data]
+        @filterByBacklogCustomSearchQuery('z').then =>
+          @filterByType('defect').then =>
+            expect(columns[0].getCards().length).toBe 1

   it 'should include filtered cards when calculating fullness of the iteration', ->
     iterationData = Helpers.TimeboxDataCreatorHelper.createTimeboxData plannedVelocity: 10


Use Marker::onDidChange

Use Marker::onDidChange

--- lib/bookmarks.coffee
+++ lib/bookmarks.coffee
@@ -82,7 +82,7 @@
   createBookmarkMarker: (bufferRow) ->
     range = [[bufferRow, 0], [bufferRow, 0]]
     bookmark = @displayBuffer().markBufferRange(range, @bookmarkMarkerAttributes(invalidate: 'surround'))
-    @subscribe bookmark, 'changed', ({isValid}) ->
+    @subscribe bookmark.onDidChange ({isValid}) ->
       bookmark.destroy() unless isValid
     @editor.decorateMarker(bookmark, {type: 'gutter', class: 'bookmarked'})
     bookmark


Lock in sidebar width once rendered

Lock in sidebar width once rendered

This keeps it from shifting as entries are hidden from the package
list because of filtering.

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -86,11 +86,11 @@
     @showPanel(@panelToShow) if @panelToShow
     @showPanel('Settings') unless @activePanelName
     @filterEditor.show()
-    @filterEditor.width(@sidebar.width()) if @isOnDom()
+    @sidebar.width(@sidebar.width()) if @isOnDom()

   afterAttach: (onDom) ->
     if onDom and @filterEditor.isVisible() and @sidebar.width() > 0
-      @filterEditor.width(@sidebar.width())
+      @sidebar.width(@sidebar.width())

   serialize: ->
     deserializer: 'SettingsView'


Always call superclass when focusing Settings View

Previously, in some cases, calling focus wasn't causing the
selected panel to correctly focus if the Settings View was
focused by cycling through tabs with a keyboard shortcut,
as described in atom/atom#2416.

This seems to happen only for panels which don't implement
their own focus handler, e.g. the Settings panel and panels
for specific packages. Panels which do have a focus handler
implemented weren't affected.

This change moves the focus() call to the superclass so that
it's always executed (allowing the panel to focus correctly
even if it doesn't have a focus handler), but also allows
the panel's focus handler to be executed afterwards if it
does exist.

Fixes atom/atom#2416

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -153,12 +153,12 @@
     @sidebar.find("[name='#{name}']").addClass('active')

   focus: ->
+    super
+
     # Pass focus to panel that is currently visible
     for panel in @panels.children() when $(panel).isVisible()
       $(panel).view().focus()
       return
-
-    super

   showPanel: (name) ->
     if panel = @getOrCreatePanel(name)


Update blobTransport.coffee

Update blobTransport.coffee

add log ext

--- src/blobTransport.coffee
+++ src/blobTransport.coffee
@@ -22,7 +22,7 @@
   constructor: ({@account, @containerName, @blobName, @maxBlobSize, @maxBlockCount = 48000 , @level = "info"}) ->
     @maxBlobSize = if @maxBlobSize then (@maxBlobSize * MB) else undefined
     @origBlobName = @blobName
-    @blobName = if @maxBlobSize then (@blobName + '-' + @_timestamp()) else @origBlobName
+    @blobName = if @maxBlobSize then (@blobName + '-' + @_timestamp() + ".log") else @origBlobName
     @name = "BlobTransport"
     @cargo = @_buildCargo()
     @client = @_buildClient @account
@@ -104,4 +104,4 @@
 #
 winston.transports.AzureBlob = BlobTransport

-module.exports = BlobTransport+module.exports = BlobTransport


Remove superfluous conditional clauses

Remove superfluous conditional clauses

--- vendor/assets/javascripts/twitter/bootstrap/rails/confirm.coffee
+++ vendor/assets/javascripts/twitter/bootstrap/rails/confirm.coffee
@@ -53,7 +53,7 @@

         if element.get(0).click
           element.get(0).click()
-        else if $.isFunction(document.createEvent)
+        else
           evt = new MouseEvents("click", {
             bubbles : true,
             cancelable : true,
@@ -71,8 +71,6 @@
             relatedTarget : document.body.parentNode
           })
           element.get(0).dispatchEvent(evt)
-        else
-          element.trigger "click"

         $.rails.allowAction = allowAction


Use fat arrow for write callback

Use fat arrow for write callback


updated Generator namespace and namespacePath should not get set on every namespace and class If the namespace already exists in the global namespace tree, it can reuse the existing one. This will be helpful for all the Art.* libraries


Require 2 characters before autocomplete

--- app/assets/javascripts/access_autocomplete.js.coffee
+++ app/assets/javascripts/access_autocomplete.js.coffee
@@ -21,6 +21,7 @@
     if $target.val() == '' or $t.val() == ''
       $target.val($t.val())
   $t.typeahead
+    minLength: 2
     source: (query, process) ->
       $target.val('')
       $.get "#{$('body').data('mountpoint')}autocomplete",


Fixed registration.

Fixed registration.

--- app/assets/javascripts/app/controllers/sla.js.coffee
+++ app/assets/javascripts/app/controllers/sla.js.coffee
@@ -27,5 +27,4 @@
 #App.Config.set( 'slas', Index, 'Routes' )
 #App.Config.set( 'Sla', { prio: 2900, parent: '#admin', name: 'SLAs', target: '#slas', role: ['Admin'] }, 'NavBar' )

-App.Config.set( 'Sla', { prio: 2900, name: 'SLAs', target: '#manage/slas', controller: Index, role: ['Admin'] }, 'NavBarLevel2' )
-
+App.Config.set( 'Sla', { prio: 2900, name: 'SLAs', parent: '#manage', target: '#manage/slas', controller: Index, role: ['Admin'] }, 'NavBarLevel44' )


form for entry input

--- app/assets/javascripts/components/weight_form.coffee
+++ app/assets/javascripts/components/weight_form.coffee
@@ -1,8 +1,20 @@
 @WeightForm = React.createClass
   getInitialState: ->
     user: @props.data
+    id: ''
+    entry: ''
   getDefaultProps: ->
     user: []
+  valid: ->
+    @state.entry
+  handleChange: (e) ->
+    name = e.target.name
+    @setState "#{ name }": e.target.value
+  handleSubmit: (e) ->
+    e.preventDefault()
+    $.post '', { weight: @state }, (data) =>
+      @setState @getInitialState()
+    , 'JSON'
   render: ->
     React.DOM.div
       className: 'users'
@@ -11,4 +23,33 @@
         "Weight Monitor (#{@state.user.name})"
       React.DOM.div
         className: 'row'
-      React.DOM.hr null+      React.DOM.form
+        className: 'form-inline'
+        onSubmit: @handleSubmit
+        React.DOM.div
+          className: 'form-group'
+          React.DOM.input
+            type: 'text'
+            className: 'form-control'
+            placeholder: 'Weight'
+            name: 'entry'
+            value: @state.entry
+            onChange: @handleChange
+          React.DOM.input
+            type: 'date'
+            className: 'form-control'
+            placeholder: 'Date'
+            name: 'date'
+            value: @state.date
+            onChange: @handleChange
+          React.DOM.input
+            type: 'hidden'
+            className: 'form-control'
+            name: 'id'
+            value: @state.user.id
+            onChange: @handleChange
+        React.DOM.button
+          type: 'submit'
+          className: 'btn btn-primary'
+          disabled: !@valid()
+          'Create entry'


BF-181 back to original loading

BF-181 back to original loading

--- app/assets/javascripts/model/binary/pullqueue.coffee
+++ app/assets/javascripts/model/binary/pullqueue.coffee
@@ -47,8 +47,8 @@

     queue = @queue

-    if priority > 50 and zoomStep < 3
-      @insert([bucket[0] >> 1, bucket[1] >> 1, bucket[2] >> 1], zoomStep+1, priority-50)
+#    if priority > 50 and zoomStep < 3
+#      @insert([bucket[0] >> 1, bucket[1] >> 1, bucket[2] >> 1], zoomStep+1, priority-50)

     # Buckets with negative priority are not loaded
     return unless priority >= 0


Feature: "PUT" requests.

Feature: "PUT" requests.

--- app/assets/javascripts/try_api/application.js.coffee
+++ app/assets/javascripts/try_api/application.js.coffee
@@ -106,7 +106,7 @@
     $scope.headers = []
     $scope.global_headers = {}
     $scope.params = []
-
+
     $http.get('/developers/projects.json').success (data) ->
       $scope.project = data.project
       $.each $scope.project.menu_items, () ->
@@ -160,7 +160,17 @@
                   headers: headers
                 .success method.response_handler
                 .error method.response_handler
+              when 'put'
+                fd = new FormData

+                $.each method.parameters, (i) ->
+                  $scope.addParameterToForm fd, this
+
+                $http.put path, fd,
+                  transformRequest: angular.identity
+                  headers: headers
+                .success method.response_handler
+                .error method.response_handler
     $scope.addParameterToForm = (form, parameter) -> # TODO implement multidimentional parameters
       if parameter.type == 'array'
         $.each parameter.values, ->
@@ -174,4 +184,4 @@
                 form.append parameter.name + '[]' + subparameter.name, subparameter.value || ''
       else
         form.append parameter.name, parameter.value || ''
-]+]


in UsersRecordsInterface#fetchMe, only hit 'users#me' if user isn't in local db

--- app/records-interfaces/user-records-interface.coffee
+++ app/records-interfaces/user-records-interface.coffee
@@ -1,4 +1,4 @@
-global.cobudgetApp.factory 'UserRecordsInterface', (config, BaseRecordsInterface, UserModel) ->
+global.cobudgetApp.factory 'UserRecordsInterface', (config, BaseRecordsInterface, $q, UserModel) ->
   class UserRecordsInterface extends BaseRecordsInterface
     model: UserModel
     constructor: (recordStore) ->
@@ -25,4 +25,10 @@
       @remote.post('update_password', params)

     fetchMe: ->
-      @remote.get('me', {})
+      deferred = $q.defer()
+      if @find(global.cobudgetApp.currentUserId)
+        deferred.resolve()
+      else
+        @remote.get('me', {}).then ->
+          deferred.resolve()
+      deferred.promise


String changes in sync-settings.coffee

String changes in sync-settings.coffee

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -6,7 +6,7 @@
 fs = require 'fs'

 # constants
-DESCRIPTION = 'Atom configuration store operated by http://atom.io/packages/sync-settings'
+DESCRIPTION = 'Atom configuration storage operated by http://atom.io/packages/sync-settings'
 REMOVE_KEYS = ["sync-settings"]

 module.exports =
@@ -16,11 +16,11 @@
       type: 'string'
       default: ''
     gistId:
-      description: 'Id of gist to use for configutation store'
+      description: 'ID of gist to use for configuration storage'
       type: 'string'
       default: ''
     extraFiles:
-      description: 'Comnma seperated list of files other than Atom\'s default config files in ~/.atom'
+      description: 'Comma-seperated list of files other than Atom\'s default config files in ~/.atom'
       type: 'array'
       default: []
       items:


engine: comment utils string render

engine: comment utils string render

--- packages/engine/src/utils/string.coffee
+++ packages/engine/src/utils/string.coffee
@@ -39,31 +39,31 @@
         "#{time}ms"
     snake_case: (str) ->
       str.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase()
-    render: (options) ->
-      @log message: "Rendering with #{options.engine}", level: 'DEBUG', module: 'nikita/lib/write'
-      try
-        switch options.engine
-          when 'nunjunks'
-            env = new nunjucks.Environment null, autoescape: false
-            options.filters ?= {}
-            options.filters.isString ?= (obj) -> typeof obj is 'string'
-            options.filters.isArray ?= (obj) -> Array.isArray obj
-            options.filters.isObject ?= (obj) -> typeof obj is 'object' and not Array.isArray obj
-            options.filters.contains ?= (arr, obj) -> if Array.isArray arr then obj in arr else false
-            options.filters.isEmpty ?= (obj) ->
-              return true if !obj? or obj is ''
-              return true if Array.isArray obj and obj.length is 0
-              return true if typeof obj is 'object' and Object.keys(obj).length is 0
-              return false
-            for filter, func of options.filters
-              if typeof func is 'function'
-                env.addFilter filter, func
-              else
-                @log message: "Option filter not a function and ignored", level: 'WARN', module: 'nikita/lib/write'
-            options.content = env.renderString options.content.toString(), options.context
-          else throw Error "Invalid engine: #{options.engine}"
-      catch err
-        throw (if typeof err is 'string' then Error(err) else err)
+    # render: (options) ->
+    #   @log message: "Rendering with #{options.engine}", level: 'DEBUG', module: 'nikita/lib/write'
+    #   try
+    #     switch options.engine
+    #       when 'nunjunks'
+    #         env = new nunjucks.Environment null, autoescape: false
+    #         options.filters ?= {}
+    #         options.filters.isString ?= (obj) -> typeof obj is 'string'
+    #         options.filters.isArray ?= (obj) -> Array.isArray obj
+    #         options.filters.isObject ?= (obj) -> typeof obj is 'object' and not Array.isArray obj
+    #         options.filters.contains ?= (arr, obj) -> if Array.isArray arr then obj in arr else false
+    #         options.filters.isEmpty ?= (obj) ->
+    #           return true if !obj? or obj is ''
+    #           return true if Array.isArray obj and obj.length is 0
+    #           return true if typeof obj is 'object' and Object.keys(obj).length is 0
+    #           return false
+    #         for filter, func of options.filters
+    #           if typeof func is 'function'
+    #             env.addFilter filter, func
+    #           else
+    #             @log message: "Option filter not a function and ignored", level: 'WARN', module: 'nikita/lib/write'
+    #         options.content = env.renderString options.content.toString(), options.context
+    #       else throw Error "Invalid engine: #{options.engine}"
+    #   catch err
+    #     throw (if typeof err is 'string' then Error(err) else err)
     replace_partial: (options) ->
       return unless options.write?.length
       @log message: "Replacing sections of the file", level: 'DEBUG', module: 'nikita/lib/misc/string'
@@ -144,6 +144,6 @@
             else # TODO: honors append
               @log message: "Missing 'to', skip writing", level: 'WARN', module: 'nikita/lib/misc/string'

-nunjucks = require 'nunjucks/src/environment'
+# nunjucks = require 'nunjucks/src/environment'
 quote = require 'regexp-quote'
 crypto = require 'crypto'


Support optional www subdomain for IA queries

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -12,7 +12,7 @@
 IA_GB_TABLE_ID = '1Tg0cm8gXBUwsBGx53GwGhHYiPpt_6YzG-HrR6Ywl'

 HT_REGEX = /^https?:\/\/babel\.hathitrust\.org\/cgi\/pt\?id=(.+)/
-IA_REGEX = /^https?:\/\/archive\.org\/details\/(.+)\/?/
+IA_REGEX = /^https?:\/\/(www\.)?archive\.org\/details\/(.+)\/?/
 GB_REGEX = /^https?:\/\/books\.google\.com\/books\?id=(.+)/
 HDL_REGEX = /^https?:\/\/hdl\.handle\.net\/2027\/(.+)\/?/
 HT_CATALOG_REGEX = /^https?:\/\/catalog\.hathitrust\.org\/Record\/(\d{9})/
@@ -172,7 +172,7 @@
 process_ia = (identifier_string) ->
   console.log 'process_ia'
   match = identifier_string.match(IA_REGEX)
-  ia_id = match[1].split('&')[0]
+  ia_id = match[2].split('&')[0]
   process_ia_id(ia_id, 100)
   ia_query(ia_id)


delete graph.coffee

delete graph.coffee

--- graph.coffee
+++ graph.coffee
@@ -1,27 +0,0 @@
-# graph.coffee
-# Library for graph theory functions
-# J. Hassler Thurston
-# Adapted from Mathematica code written in 2010-2011
-# January 24, 2014
-
-
-
-# graphs:
-# represented by an object, which has vertex and edge attributes
-# the vertices are represented by an array of vertex names
-# the edges are represented by a ragged array, where the ith array contains other vertices connected to the ith vertex
-# e.g. the graph with edges 1->2, 0->3, 4->1, 1->0 would be represented by the object
-# {
-# 	vertices: [1,2,0,3,4],
-# 	edges: [[2,0],[],[3],[],[1]]
-# }
-
-# creates a graph with the given edges
-class Graph
-	constructor: (@edges) ->
-		vertices = (edge[0] for edge in edges if edge[0] not in vertices)
-
-
-
-
-


for easiness of test

for easiness of test

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -239,6 +239,12 @@
       marker.destroy()
     @highlightSearchMarkers = null

+  hasHighlightSearch: ->
+    @highlightSearchMarkers?
+
+  getHighlightSearch: ->
+    @highlightSearchMarkers
+
   highlightSearch: (pattern, scanRange) ->
     ranges = []
     @editor.scanInBufferRange pattern, scanRange, ({range}) ->
@@ -258,6 +264,9 @@
     @rangeMarkers.push(markers...)
     @updateEditorElement()

+  hasRangeMarkers: ->
+    @rangeMarkers.length > 0
+
   getRangeMarkers: (markers) ->
     @rangeMarkers


Ensure only one 'mode' is shown in the status bar

I don't really like the repetition of

  if !$('#status-bar-vim-mode').length

but it works for now.

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -433,7 +433,8 @@
   updateStatusBar: ->
     if !$('#status-bar-vim-mode').length
       atom.packages.once 'activated', ->
-        atom.workspaceView.statusBar?.prependRight("
Command
")
+        if !$('#status-bar-vim-mode').length
+          atom.workspaceView.statusBar?.prependRight("
Command
")

     if @mode is "insert"
       $('#status-bar-vim-mode').html("Insert")


Make nicer annotations

Make nicer annotations

--- src/classify-page.coffee
+++ src/classify-page.coffee
@@ -80,16 +80,21 @@
       Subject.next()

   finishSubject: ->
-    # @classification.set 'marks', @subjectViewer.getMarks()
+    for annotation in @composeClassification()
+      @classification.annotate annotation
     console?.log JSON.stringify @classification
     Subject.next()

   composeClassification: ->
-    annotations = @decisionTree.getValues()
+    annotations = []
+
+    decisionTreeValues = @decisionTree.getValues()
+    for keyAndValue in decisionTreeValues then for key, value of keyAndValue
+      annotations.push {key, value}

     for {mark} in @subjectViewer.markingSurface.tools
-      annotations[mark._taskIndex]._marks ?= []
-      annotations[mark._taskIndex]._marks.push mark
+      annotations[mark._taskIndex].marks ?= []
+      annotations[mark._taskIndex].marks.push mark

     annotations


9379: Shorten calendar long press delay

9379: Shorten calendar long press delay

--- app/assets/javascripts/backbone/views/reservation_calendar_view.coffee
+++ app/assets/javascripts/backbone/views/reservation_calendar_view.coffee
@@ -25,6 +25,7 @@
       selectable: @ruleSet.access_level != "read_only"
       selectOverlap: false
       selectHelper: true
+      longPressDelay: 500
       header:
         left: 'title'
         center: 'agendaDay,agendaWeek,month'


Support removing a token when using NestedAttributes strategy

--- app/assets/javascripts/exo/widgets/auto_complete_token_field.js.coffee
+++ app/assets/javascripts/exo/widgets/auto_complete_token_field.js.coffee
@@ -21,6 +21,8 @@
   exports.NestedAttributesStrategy = (tokens) ->
     token = tokens[0]

+    if !token
+      return @originalInput.value = ""

     if !token.isNew()
       # If token is an existing object than convert nested attributes to ID attribute instead


Adds an 'errors' hash to beforeRender

Adds an 'errors' hash to beforeRender

--- src/client/widget.coffee
+++ src/client/widget.coffee
@@ -106,6 +106,7 @@
         @_renderCount ?= 0
         @_renderCount++
         ret       = {}
+        errors    = {}
         dfd       = @sandbox.data.deferred()
         try
           keys      = _.keys(@datasources)
@@ -118,6 +119,7 @@
             , (err)=>
               handler = @["on#{_.string.capitalize(_.string.camelize(k))}Error"]
               handler = onDataError.bind(@, k) unless _.isFunction(handler)
+              errors[k] = err
               promiseDfd.resolve(handler err)
             promiseDfd

@@ -149,7 +151,7 @@
         catch e
           console.error("Caught error in buildContext", e.message, e)
           dfd.reject(e)
-        dfd
+        [dfd, errors]

       loggedIn: =>
         return false unless @sandbox.data.api.model('me').id?
@@ -176,12 +178,12 @@
       # afterRender
       # Start nested widgets...
       render: (tpl, data)=>
-        ctx = @buildContext.call(@)
+        [ctx, errors] = @buildContext.call(@)
         ctx.fail (err)->
           console.error("Error fetching Datasources ", err.message, err)
         ctx.then (ctx)=>
           try
-            beforeCtx = @beforeRender.call(@, ctx)
+            beforeCtx = @beforeRender.call(@, ctx, errors)
             beforeRendering = $.when(beforeCtx)
             beforeRendering.done (dataAfterBefore)=>
               data = _.extend(dataAfterBefore || ctx, data)


Add a try/catch in the Error.prepareStackTrace implementation to make sure that it never throws. Resolves jashkenas/coffeescript#4391.

--- src/coffee-script.coffee
+++ src/coffee-script.coffee
@@ -349,14 +349,24 @@
 # NodeJS / V8 have no support for transforming positions in stack traces using
 # sourceMap, so we must monkey-patch Error to display CoffeeScript source
 # positions.
+prepareStackTraceOrig = Error.prepareStackTrace
 Error.prepareStackTrace = (err, stack) ->
-  getSourceMapping = (filename, line, column) ->
-    sourceMap = getSourceMap filename
-    answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
-    if answer then [answer[0] + 1, answer[1] + 1] else null
-
-  frames = for frame in stack
-    break if frame.getFunction() is exports.run
-    "  at #{formatSourcePosition frame, getSourceMapping}"
-
-  "#{err.toString()}\n#{frames.join '\n'}\n"
+  try
+    getSourceMapping = (filename, line, column) ->
+      sourceMap = getSourceMap filename
+      answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
+      if answer then [answer[0] + 1, answer[1] + 1] else null
+
+    frames = for frame in stack
+      break if frame.getFunction() is exports.run
+      "  at #{formatSourcePosition frame, getSourceMapping}"
+
+    "#{err.toString()}\n#{frames.join '\n'}\n"
+  catch
+    try
+      if prepareStackTraceOrig?
+        prepareStackTraceOrig err, stack
+      else
+        "#{err.toString()}\n  at #{stack.join '\n  at '}\n"
+    catch
+      null


Add docs

Add docs

--- src/coffee/layout.coffee
+++ src/coffee/layout.coffee
@@ -1,3 +1,9 @@
+###
+SwanKiosk.Layout
+
+build HTMLElements (with inline CSS styles and event listeners) from javascript dictionaries
+###
+
 class SwanKiosk.Layout
   @defaultTag        = 'div'
   @specialAttributes = ['contents', 'tag', 'rawHtml', '_context']


Remove obsolete function

Remove obsolete function

--- src/coffee/sphere.coffee
+++ src/coffee/sphere.coffee
@@ -4,7 +4,7 @@
 utils = require './utils'
 {_u} = require 'sphere-node-utils'

-#TODO refactor use with node connect GET / POST functions only (better use sphere-node-client / sync)
+#TODO use sphere-node-client/sync or GET/POST

 ###
 # Updates asynchronously category in Sphere.
@@ -23,26 +23,6 @@
         deferred.reject message
       else
         message = "Category with id: '#{data.id}' updated."
-        deferred.resolve message
-  deferred.promise
-
-###
-# Updates asynchronously product in Sphere.
-#
-# @param {Object} data Update product request data
-# @return {Object} If success returns promise with success message otherwise rejects with error message
-###
-exports.updateProduct = (rest, data) ->
-  deferred = Q.defer()
-  rest.POST "/products/#{data.id}", data.payload, (error, response, body) ->
-    if error
-      deferred.reject "HTTP error on product update; Error: #{error}; Request body: \n #{utils.pretty data} \n\n Response body: '#{utils.pretty body}'"
-    else
-      if response.statusCode isnt 200
-        message = "Error on product update; Request body: \n #{utils.pretty data} \n\n Response body: '#{utils.pretty body}'"
-        deferred.reject message
-      else
-        message = "Product with id: '#{data.id}' updated."
         deferred.resolve message
   deferred.promise


Fixes #7

Fixes #7

--- src/cohtml-parser.coffee
+++ src/cohtml-parser.coffee
@@ -13,7 +13,7 @@
       tag: [].concat ['!'], CohtmlParser.CommonTokens.urlSafeBase64
       id: CohtmlParser.CommonTokens.urlSafeBase64
       class: CohtmlParser.CommonTokens.urlSafeBase64
-      attribute: CohtmlParser.CommonTokens.urlSafeBase64
+      attribute: [].concat ['$', '?'], CohtmlParser.CommonTokens.urlSafeBase64
       ws: CohtmlParser.CommonTokens.whitespace
       equal: '='
       dquote: '"'


fixes #66

fixes #66

--- src/coffee/controllers/player/player-options-ctrl.coffee
+++ src/coffee/controllers/player/player-options-ctrl.coffee
@@ -165,22 +165,23 @@
       {folder: 'events',      type: 'party' }
       {folder: 'events',      type: 'providence' }
       {folder: 'events',      type: 'tinker' }
-      {folder: 'ingredients', type: 'bread',    requiresName: yes }
-      {folder: 'ingredients', type: 'meat',     requiresName: yes }
-      {folder: 'ingredients', type: 'veg',      requiresName: yes }
-      {folder: 'items',       type: 'body',     requiresName: yes }
-      {folder: 'items',       type: 'charm',    requiresName: yes }
-      {folder: 'items',       type: 'feet',     requiresName: yes }
-      {folder: 'items',       type: 'finger',   requiresName: yes }
-      {folder: 'items',       type: 'hands',    requiresName: yes }
-      {folder: 'items',       type: 'head',     requiresName: yes }
-      {folder: 'items',       type: 'legs',     requiresName: yes }
-      {folder: 'items',       type: 'mainhand', requiresName: yes }
-      {folder: 'items',       type: 'neck',     requiresName: yes }
-      {folder: 'items',       type: 'offhand',  requiresName: yes }
-      {folder: 'items',       type: 'prefix',   requiresName: yes }
-      {folder: 'items',       type: 'suffix',   requiresName: yes }
-      {folder: 'monsters',    type: 'monster',  requiresName: yes }
+      {folder: 'ingredients', type: 'bread',    requiresName: yes, requiresContent: yes }
+      {folder: 'ingredients', type: 'meat',     requiresName: yes, requiresContent: yes }
+      {folder: 'ingredients', type: 'veg',      requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'body',     requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'charm',    requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'feet',     requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'finger',   requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'hands',    requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'head',     requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'legs',     requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'mainhand', requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'neck',     requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'offhand',  requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'prefix',   requiresName: yes, requiresContent: yes }
+      {folder: 'items',       type: 'suffix',   requiresName: yes, requiresContent: yes }
+      {folder: 'monsters',    type: 'monster',  requiresName: yes, requiresContent: yes }
+      {folder: 'npcs',        type: 'trainer',  requiresName: yes }
     ]

     $scope.data = type: _.sample $scope.types
@@ -190,11 +191,12 @@
     $scope.submit = ->
       data = $scope.data
       data._name = data._name?.trim()
-      data.content = data.content?.trim()
+      data.content = data.content?.trim() or ""

       requiresName = $scope.data.type.requiresName
-
-      if not data.content
+      requiresContent = $scope.data.type.requiresContent
+
+      if not data.content and requiresContent
         $mdToast.show template: '<md-toast>You must have content!</md-toast>'
         return


marina otero recently

marina otero recently

--- maps/contributors.coffee
+++ maps/contributors.coffee
@@ -211,7 +211,7 @@
     name: 'Marina Otero Verzier'
     location: 'Rotterdam'
     links: ['[@marina_ov](https://twitter.com/marina_ov)']
-    bio: 'Marina Otero Verzier is an architect based in Rotterdam. She is Head of Research at Het Nieuwe Instituut and Chief Curator of the Oslo Architecture Triennale 2016 together with After Belonging Agency. Previously, she was Director of Global Network Programming at Studio-X and Adjunct Assistant Professor at Columbia University’s Graduate School of Architecture, Planning and Preservation. Her work has been published in _Arquitectura Viva_, _The Avery Review_, _Domus_, and _SQM: The Quantified Home_.'
+    bio: 'Marina Otero Verzier is an architect based in Rotterdam. She is Head of Research at Het Nieuwe Instituut and Chief Curator of the Oslo Architecture Triennale 2016 together with After Belonging Agency. Previously, she was Director of Global Network Programming at Studio-X and Adjunct Assistant Professor at Columbia University’s Graduate School of Architecture, Planning and Preservation. Her work has been recently published in _Arquitectura Viva_, _The Avery Review_, _Domus_, and _SQM: The Quantified Home_.'
     azone_username: ''
     azone_reviews: []
     azone_funds: []


feat($core): adds media and creation conditions on widgets

The `if` and `unless` options can be passed to the `widgets`
function to set conditions on the widgets creation.
The `media` option can be used to set a condition on
widgets activations based on the widow size. The `min` and
`max` shortcuts can be used to automatically creates a
condition similar to the `@media` query in CSS.

--- app/assets/javascripts/awesome_ui/widgets.js.coffee
+++ app/assets/javascripts/awesome_ui/widgets.js.coffee
@@ -1,77 +1,149 @@

+## widgets
+
+# The `widgets` function is both the main module and the function
+# used to register the widgets to apply on a page.
 widgets = (name, selector, options={}, block) ->
+
+  # The options specific to the widget registration and activation are
+  # extracted from the options object.
   events = options.on
+  if_condition = options.if
+  unless_condition = options.unless
+  media_condition = options.media
+
+  delete options.on
+  delete options.if
+  delete options.unless
+  delete options.media
+
+  # Events can be passed as a string with event names separated with spaces.
   events = events.split /\s+/g if typeof events is 'string'
+
+  # The widgets instances are stored in a Hash with the DOM element they
+  # target as key. The instances hashes are stored per widget type.
   instances = widgets.instances[name] ||= new widgets.Hash

-  handler = (e) ->
-    elements = document.querySelectorAll "#{selector}:not(.#{name}-handled)"
+  # This method execute a test condition for the given element. The condition
+  # can be either a function or a value converted to boolean.
+  test_condition = (condition, element) ->
+    if typeof condition is 'function' then condition(element) else !!condition
+
+  # The DOM elements handled by a widget will receive a handled class
+  # to differenciate them from unhandled elements.
+  handled_class = "#{name}-handled"
+
+  # Activated elements will receive a specific class to keep track of their
+  # activation state.
+  activated_class = "#{name}-activated"
+
+  # This method will test if an element can be handled by the current widget.
+  # It will test for both the handled class presence and the widget
+  # conditions. Note that if both the `if` and `unless` conditions
+  # are passed in the options object they will be tested as both part
+  # of a single `&&` condition.
+  can_be_handled = (element) ->
+    res = element.className.indexOf(handled_class) is -1
+    res &&= test_condition(if_condition, element) if if_condition?
+    res &&= not test_condition(unless_condition, element) if unless_condition?
+    res
+
+  # If a media condition have been specified, the widget activation will be
+  # conditionned based on the result of this condition. The condition is
+  # verified each time the `resize` event is triggered.
+  if media_condition?
+    # The media condition can be either a boolean value, a function, or,
+    # to simply the setup, an object with `min` and `max` property containing
+    # the minimal and maximal window width where the widget is activated.
+    if typeof media_condition is 'object'
+      {min, max} = media_condition
+      media_condition = ->
+        res = true
+        res &&= window.innerWidth >= min if min?
+        res &&= window.innerWidth <= max if max?
+        res
+
+    # The media handl is registered on the `resize` event of the `window`
+    # object.
+    media_handler = (element, widget) ->
+      return unless widget?
+
+      condition_matched = test_condition(media_condition, element)
+
+      if condition_matched and not widget.active
+        widget.activate?()
+      else if not condition_matched and widget.active
+        widget.deactivate?()
+
+    window.addEventListener 'resize', ->
+      instances.each_pair (element, widget) ->
+        media_handler element, widget
+
+  # The `handler` function is the function registered on specified event and
+  # will proceed to the creation of the widgets if the conditions are met.
+  handler = ->
+    elements = document.querySelectorAll selector

     Array::forEach.call elements, (element) ->
+      return unless can_be_handled element
+
       res = widgets[name] element, Object.create(options)
-      element.className += " #{name}-handled"
+      element.className += " #{handled_class}"
       instances.set element, res
       block?.call element, element, res

+      # The widgets activation state are resolved at creation
+      media_handler(element, res) if media_condition?
+
+  # For each event specified, the handler is registered as listener.
+  # A special case is the `init` event that simply mean to trigger the
+  # handler as soon a the function is called.
   events.forEach (event) ->
     switch event
-      when 'load'
+      when 'init' then handler()
+      when 'load', 'resize'
         window.addEventListener event, handler
       else
         document.addEventListener event, handler

+# The `instances` of the various widgets, stored by widget type and then
+# mapped with their target DOM element as key.
 widgets.instances = {}

+#### widgets.define
+
+# The `widgets.define` is used to create a new widget usable through the
+# `widgets` method. Basically, a widget is defined using a `name`, and a
+# `block` function that will be called for each DOM elements targeted by
+# the widget.
+#
+# The `block` function should have the following signature:
+#
+#     function(element:HTMLElement, options:Object):Object
+#
+# The `options` object will contains all the options passed to the `widgets`
+# method except the `on`, `if`, `unless` and `media` ones.
 widgets.define = (name, block) -> widgets[name] = block

+#### widgets.release
+
+# The `widgets.release` method can be used to completely remove the widgets
+# of the given `name` from the page.
+# It's the widget responsibility to clean up its dependencies during
+# the `dispose` call.
 widgets.release = (name) ->
   widgets.instances[name].each (value) -> value?.dispose?()

-widgets.tag = (name, attrs={}) ->
-  flatten_object = (object, key) ->
-    new_object = {}
+#### widgets.activate

-    recursion = (object, new_object, prefix='') ->
-      for k,v of object
-        if typeof v is 'object'
-          recursion v, new_object, prefix + k + '-'
-        else
-          new_object[prefix + k] = v
+# Activates all the widgets instances of type `name`.
+widgets.activate = (name) ->
+  widgets.instances[name].each (value) -> value?.activate?()

-    recursion object, new_object
-    new_object
+#### widgets.deactivate

-
-  node = document.createElement name
-  attrs = flatten_object attrs
-
-  if attrs.id?
-    node.id = attrs.id
-    delete attrs.id
-
-  if attrs.class?
-    node.className = attrs.class
-    delete attrs.class
-
-  node.setAttribute(attr, value) for attr, value of attrs
-
-  node
-
-widgets.content_tag = (name, content='', attrs={}) ->
-  [attrs, content] = [content, attrs] if typeof attrs is 'function'
-  [attrs, content] = [content, ''] if typeof content is 'object'
-
-  node = widgets.tag name, attrs
-  node.innerHTML = if typeof content is 'function'
-    res = content.call node
-    res?.outerHTML or String(res)
-  else
-    String(content)
-  node
-
-widgets.tag_html = (name, attrs) -> widgets.tag(name, attrs).outerHTML
-widgets.content_tag_html = (name, content, attrs) -> widgets.content_tag(name,content, attrs).outerHTML
-
-
+# Deactivates all the widgets instances of type `name`.
+widgets.deactivate = (name) ->
+  widgets.instances[name].each (value) -> value?.deactivate?()

 window.widgets = widgets


Removed debug output

Removed debug output

--- app/assets/javascripts/components/checkboxes.coffee
+++ app/assets/javascripts/components/checkboxes.coffee
@@ -20,7 +20,6 @@
   updateCalculatedVariables()

 @toggleGroupInput = (element, group_name, event) ->
-  console.log "toggleGroupInput"
   if element.closest(".custom-control").hasClass("selected") and (element.attr("type") == "checkbox" or event.type == "click")
     element.prop("checked", false)
   else


:art: Move the promise check out of displaySuggestions

It shoudldnt care about promises

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -121,7 +121,9 @@
     return unless providerPromises?.length
     @currentSuggestionsPromise = suggestionsPromise = Promise.all(providerPromises)
       .then(@mergeSuggestionsFromProviders)
-      .then((suggestions) => @displaySuggestions(suggestions, suggestionsPromise, options))
+      .then (suggestions) =>
+        if @currentSuggestionsPromise is suggestionsPromise
+          @displaySuggestions(suggestions, options)

   # providerSuggestions - array of arrays of suggestions provided by all called providers
   mergeSuggestionsFromProviders: (providerSuggestions) ->
@@ -130,10 +132,9 @@
       suggestions
     , []

-  displaySuggestions: (suggestions, suggestionsPromise, options) =>
+  displaySuggestions: (suggestions, options) =>
     suggestions = _.uniq(suggestions, (s) -> s.word)
-    suggestionsAreLatest = @currentSuggestionsPromise is suggestionsPromise
-    if @shouldDisplaySuggestions and suggestions.length and suggestionsAreLatest
+    if @shouldDisplaySuggestions and suggestions.length
       @showSuggestionList(suggestions)
     else
       @hideSuggestionList()


Make selectors more specific when setting the tab content

Reason: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2023

--- app/assets/javascripts/merge_request_tabs.js.coffee
+++ app/assets/javascripts/merge_request_tabs.js.coffee
@@ -133,7 +133,7 @@
     @_get
       url: "#{source}.json"
       success: (data) =>
-        document.getElementById('commits').innerHTML = data.html
+        document.querySelector("div#commits").innerHTML = data.html
         $('.js-timeago').timeago()
         @commitsLoaded = true
         @scrollToElement("#commits")
@@ -144,7 +144,7 @@
     @_get
       url: "#{source}.json" + @_location.search
       success: (data) =>
-        document.getElementById('diffs').innerHTML = data.html
+        document.querySelector("div#diffs").innerHTML = data.html
         $('div#diffs .js-syntax-highlight').syntaxHighlight()
         @diffsLoaded = true
         @scrollToElement("#diffs")
@@ -155,7 +155,7 @@
     @_get
       url: "#{source}.json"
       success: (data) =>
-        document.getElementById('builds').innerHTML = data.html
+        document.querySelector("div#builds").innerHTML = data.html
         $('.js-timeago').timeago()
         @buildsLoaded = true
         @scrollToElement("#builds")


fix selection via enter key to select correct option

--- app/components/eui-popup.coffee
+++ app/components/eui-popup.coffee
@@ -111,13 +111,12 @@
   enterPressed: (event) ->
     event.preventDefault()
     event = @get('event')
-    selection = @get('options')[@get('highlightedIndex')]

     if event == 'select'
-      @set('selection', selection)
+      @set('selection', @get('highlightedOption'))

     else if event == 'action'
-      action = selection.get('action')
+      action = @get('highlightedOption.action')
       @get('targetObject').triggerAction({action})

     @hide()


Thin line to separate canvas from button area.

--- app/assets/javascripts/roofpig/DomHandler.js.coffee
+++ app/assets/javascripts/roofpig/DomHandler.js.coffee
@@ -37,7 +37,7 @@
     @place.html(place_text)

   add_alg_buttons: ->
-    @button_area = $("<div/>", { class: 'button-area' }).height(@div.height() - @div.width()).width(@div.width())
+    @button_area = $("<div/>").height(@div.height() - @div.width()).width(@div.width()).css("border-top": "1px solid #ccc")
     @div.append(@button_area)

     @reset = this._make_button("↩",  "reset")


Remove unused destructuring assignment of unused `Error` subtypes

--- lib/createServiceFilters.coffee
+++ lib/createServiceFilters.coffee
@@ -1,10 +1,8 @@
 {getType, addTo, compact, flatten, merge} = require './util'
 generateFilter = require './generateFilter'
 {
-  FailedArgumentLookupError
   MissingArgumentError
   InvalidArgumentError
-  InvalidServiceDefinitionError
 } = require './errors'

 # generate validations that will be added to the filter stack


Let also other git packages know when index is changed

atom/git-utils Repository does not always detect index changes.
For example tree view does not update when doing

Also close fs watcher when disposing.

--- lib/git-diff-staged-view.coffee
+++ lib/git-diff-staged-view.coffee
@@ -1,5 +1,4 @@
 fs = require 'fs'
-{join} = require 'path'
 {Directory, CompositeDisposable} = require 'atom'
 {repositoryForPath} = require './helpers'

@@ -21,11 +20,13 @@
     @subscriptions = new CompositeDisposable()
     @decorations = {}
     @markers = []
+    @indexWatch = null
     @timeoutId = @repository = @editor = null

   dispose: =>
     @cancelUpdate()
     @removeDecorations()
+    @indexWatch?.close()
     @subscriptions?.dispose()
     @reset()

@@ -50,7 +51,11 @@
       @scheduleUpdate()
       # Watch the directory because index is recreated on every change.
       # Otherwise we loose the watch after the first change.
-      fs.watch join(@repository.path), (__, name)=> @scheduleUpdate() if name is 'index'
+      @indexWatch = fs.watch @repository.path, (__, name)=>
+        # atom/git-utils Repository does not always detect index changes
+        # ... let others know (e.g. tree-view)
+        @repository?.refreshStatus()
+        @scheduleUpdate() if name is 'index'

   cancelUpdate: ->
     clearTimeout(@timeoutId)


Fix dialogmanager only working in debug mode

--- app/models/DialogManager.coffee
+++ app/models/DialogManager.coffee
@@ -4,6 +4,7 @@
   constructor: ->
     @source = document.getElementById 'dialog-template'
     @template = Handlebars.compile(@source.innerText)
+    @canvas = document.getElementById 'game-canvas'
     mediator.dialogManager = @

   showDialog: (data, callback) =>
@@ -17,8 +18,8 @@

     $('#dialog').append(result).
     css('left', 0).
-    css('top', homepageview.canvas.height + 2).
-    css('width', homepageview.canvas.width).
+    css('top', @canvas.height + 2).
+    css('width', @canvas.width).
     fadeIn()


fix a bug with Other media install

fix a bug with Other media install

--- app/modules/new-vm/index.coffee
+++ app/modules/new-vm/index.coffee
@@ -147,7 +147,10 @@

       VDIs = $scope.VDIs = cloneDeep template.template_info.disks
-      if VDIs.length is 0
+      # if the template has no config disk
+      # nor it's Other install media (specific case)
+      # then do NOT display disk and network panel
+      if VDIs.length is 0 and template.name_label isnt 'Other install media'
         $scope.isDiskTemplate = true
         $scope.VIFs.length = 0
       else $scope.isDiskTemplate = false


Redirect if successful

Redirect if successful

--- picoCTF-web/web/coffee/account.coffee
+++ picoCTF-web/web/coffee/account.coffee
@@ -2,7 +2,7 @@
   e.preventDefault()
   apiCall "POST", "/api/user/update_password", $("#password-update-form").serializeObject()
   .done (data) ->
-    apiNotify data
+    apiNotify data, "/account"

 resetPassword = (e) ->
   e.preventDefault()
@@ -10,8 +10,7 @@
   form.token = window.location.hash.substring(1)
   apiCall "POST", "/api/user/confirm_password_reset", form
   .done (data) ->
-    apiNotify data
-
+    apiNotify data, "/login"
 $ ->
   $("#password-update-form").on "submit", updatePassword
   $("#password-reset-form").on "submit", resetPassword


Conditionally render component

Conditionally render component

--- lib/form.coffee
+++ lib/form.coffee
@@ -231,8 +231,9 @@
     message: React.PropTypes.string

   render: ->
-    className = if @context.message? then (@props.className or "error-message")
-    @transferPropsTo div {className}, @context.message
+    if @context.message?
+      className = @props.className or "error-message"
+      return div {className}, @context.message

 Submit = React.createClass
   displayName: "Submit"


More tests.

More tests.

--- tests.coffee
+++ tests.coffee
@@ -46,6 +46,31 @@
 class BasicTestCase extends ClassyTestCase
   @testName: 'blaze-components - basic'

+  FOO_COMPONENT_CONTENT = """
+
Other component: FooComponent

+    <button>Foo1</button>
+

+    <button>Foo2</button>
+

+

+  """
+
+  SUB_COMPONENT_CONTENT = """
+
Main component: SubComponent

+    <button>Foo1</button>
+
SubComponent/SubComponent.foobar/{"top":"42"}/{"top":"42"}/SubComponent

+    <button>Foo2</button>
+
SubComponent/SubComponent.foobar2/{"top":"42"}/{"a":"1","b":"2"}/SubComponent

+
SubComponent/MainComponent.foobar3/{"top":"42"}/{"top":"42"}/SubComponent

+
Subtemplate

+    <button>Foo1</button>
+
SubComponent/SubComponent.foobar/{"top":"42"}/{"top":"42"}/SubComponent

+    <button>Foo2</button>
+
SubComponent/SubComponent.foobar2/{"top":"42"}/{"a":"3","b":"4"}/SubComponent

+
SubComponent/MainComponent.foobar3/{"top":"42"}/{"top":"42"}/SubComponent

+    #{ FOO_COMPONENT_CONTENT }
+  """
+
   trim: (html) =>
     html = html.replace />\s+/g, '>'
     html = html.replace /\s+</g, '<'
@@ -72,31 +97,27 @@
       <button>Foo2</button>

MainComponent/MainComponent.foobar2/{"top":"42"}/{"a":"3","b":"4"}/MainComponent

MainComponent/MainComponent.foobar3/{"top":"42"}/{"top":"42"}/MainComponent

-
Other component: FooComponent

-      <button>Foo1</button>
-

-      <button>Foo2</button>
-

-

+      #{ FOO_COMPONENT_CONTENT }

-
Main component: SubComponent

-      <button>Foo1</button>
-
SubComponent/SubComponent.foobar/{"top":"42"}/{"top":"42"}/SubComponent

-      <button>Foo2</button>
-
SubComponent/SubComponent.foobar2/{"top":"42"}/{"a":"1","b":"2"}/SubComponent

-
SubComponent/MainComponent.foobar3/{"top":"42"}/{"top":"42"}/SubComponent

-
Subtemplate

-      <button>Foo1</button>
-
SubComponent/SubComponent.foobar/{"top":"42"}/{"top":"42"}/SubComponent

-      <button>Foo2</button>
-
SubComponent/SubComponent.foobar2/{"top":"42"}/{"a":"3","b":"4"}/SubComponent

-
SubComponent/MainComponent.foobar3/{"top":"42"}/{"top":"42"}/SubComponent

-
Other component: FooComponent

-      <button>Foo1</button>
-

-      <button>Foo2</button>
-

-

+      #{ SUB_COMPONENT_CONTENT }
     """

+    componentTemplate = BlazeComponent.getComponentTemplate 'FooComponent'
+
+    @assertTrue componentTemplate
+
+    output = Blaze.toHTMLWithData componentTemplate,
+      top: '42'
+
+    @assertEqual @trim(output), @trim FOO_COMPONENT_CONTENT
+
+    componentTemplate = BlazeComponent.getComponentTemplate 'SubComponent'
+
+    @assertTrue componentTemplate
+
+    output = Blaze.toHTMLWithData componentTemplate,
+      top: '42'
+
+    @assertEqual @trim(output), @trim SUB_COMPONENT_CONTENT
+
 ClassyTestCase.addTest new BasicTestCase()


Improved out-link detection

Improved out-link detection

--- lib/html.coffee
+++ lib/html.coffee
@@ -27,7 +27,7 @@
     links = @dom.querySelectorAll "a, link"
     for l in links
       href = l.attributes.getNamedItem 'href'
-      if href
+      if href and href.nodeValue.length
         uri = @crawly.addStartingPoint url.resolve(@uri.href, href.nodeValue), yes
         if uri
           @crawly.registerOutLink @uri, uri
@@ -44,11 +44,12 @@

     sourced = @dom.querySelectorAll "script[src], img"
     for s in sourced
-      uri = @crawly.addStartingPoint url.resolve(@uri.href, s.src), yes
-      if uri
-        @crawly.registerOutLink @uri, uri
-      if uri and uri.alternate_href?
-        s.src = uri.alternate_href
+      if s.src.length
+        uri = @crawly.addStartingPoint url.resolve(@uri.href, s.src), yes
+        if uri
+          @crawly.registerOutLink @uri, uri
+        if uri and uri.alternate_href?
+          s.src = uri.alternate_href

   isStreaming: ()->
     return false


Temporarily Disabling AutoComplete Related to steelbrain/atom-hack#16 Description: Due to the recent changes in AutoComplete-Plus, The API we're using is no longer advised. And produces some long errors at Atom's Startup. As I don't have much time to work on it, I am temporarily supressing the Errors.

--- lib/init.coffee
+++ lib/init.coffee
@@ -1,4 +1,4 @@
-AutoComplete = require "./autocomplete"
+# AutoComplete = require "./autocomplete"
 Linter = require './linter'

 dis = module.exports =
@@ -8,6 +8,7 @@
   activate: ->
     new Linter(dis)
   activateAutoComplete:->
+    return
     atom.packages.activatePackage("autocomplete-plus")
     .then (pkg) =>
       @autocomplete = pkg.mainModule
@@ -21,6 +22,7 @@
         @autocomplete.registerProviderForEditor(provider, editor)
         @providers.push provider
   deactivate: ->
+    return
     @editorSubscription?.dispose()
     @editorSubscription = null


fix lastPos

fix lastPos

--- lib/init.coffee
+++ lib/init.coffee
@@ -126,6 +126,7 @@
         console.log("LINE:", line)
         if line
             pos = [~~line - 1, 0]
+            lastPos = pos
             editor.setCursorBufferPosition pos
             editor.scrollToBufferPosition pos, {center: true}


Fix another typo

Fix another typo

--- lib/main.coffee
+++ lib/main.coffee
@@ -35,7 +35,7 @@
         'text.plain'
         'text.plain.null-grammar'
       ]
-      description: 'List of scopes for languages which previewing is enabled. See [this README](https://github.com/atom/spell-check#spell-check-package-) for more information on finding the correct scope for a specific language.'
+      description: 'List of scopes for languages for which previewing is enabled. See [this README](https://github.com/atom/spell-check#spell-check-package-) for more information on finding the correct scope for a specific language.'
     useGitHubStyle:
       title: 'Use GitHub.com style'
       type: 'boolean'


allow all valid package name characters in config URIs

--- lib/main.coffee
+++ lib/main.coffee
@@ -2,7 +2,7 @@
 settingsView = null

 configUri = 'atom://config'
-uriRegex = /config\/([a-z]+)\/?([a-z]+)?/i
+uriRegex = /config\/([a-z]+)\/?([a-zA-Z0-9_-]+)?/i

 createSettingsView = (params) ->
   SettingsView ?= require './settings-view'


:memo: Fix initSubscriptions method documentation

--- lib/main.coffee
+++ lib/main.coffee
@@ -278,7 +278,7 @@
       disposable.dispose()
     disposable

-  # Internal: Registers
+  # Internal: Registers to the `observeTextEditors` method.
   initSubscriptions: ->
     @subscriptions.add atom.workspace.observeTextEditors (textEditor) =>
       minimap = @minimapForEditor(textEditor)


remove exec dependency

remove exec dependency

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,7 +1,7 @@
 meta = require '../package.json'

 # Dependencies
-{exec, spawn} = require 'child_process'
+{spawn} = require 'child_process'
 os = require 'os'

 module.exports =


checkArgument -> checkRouteOrPath

checkArgument -> checkRouteOrPath

--- lib/activeroute.coffee
+++ lib/activeroute.coffee
@@ -1,10 +1,10 @@
 fr = ir = null

-checkArgument = (arg) ->
+checkRouteOrPath = (arg) ->
   try
     check arg, Match.OneOf RegExp, String
   catch error
-    throw new Error errorMessages.invalidArgument
+    throw new Error errorMessages.invalidRouteNameArgument

 checkRouterPackages = ->
   fr = Package['kadira:flow-router'] ? Package['meteorhacks:flow-router']
@@ -16,7 +16,7 @@
     'No supported router installed. Please install ' +
     'iron:router or meteorhacks:flow-router.'

-  invalidArgument: 'Invalid argument, must be String or RegExp.'
+  invalidRouteNameArgument: 'Invalid argument, must be String or RegExp.'

 share.config = new ReactiveDict 'activeRouteConfig'
 share.config.setDefault
@@ -56,7 +56,7 @@

     return if Meteor.isServer

-    checkArgument routeName
+    checkRouteOrPath routeName

     if ir
       currentRouteName = ir.Router.current()?.route?.getName?()
@@ -71,7 +71,7 @@

     return if Meteor.isServer

-    checkArgument path
+    checkRouteOrPath path

     if ir
       controller = ir.Router.current()


simplified applyPolicy

simplified applyPolicy

--- lib/applyPolicy.coffee
+++ lib/applyPolicy.coffee
@@ -52,12 +52,12 @@
   policyMap = getPolicy services, policy

   # return the original services with the new filters prepended
-  wrappedServices = {}
-  for name of services
-    filters = for filter in policyMap[name]
-      generateFilter filter, services[filter]
-    wrappedServices[name] = services[name]
-    wrappedServices[name].prepend filters
-  return wrappedServices
+  for name, def of services
+    filters = for filterName in policyMap[name]
+      generateFilter filterName, services[filterName]
+
+    services[name].prepend filters
+
+  return services

 module.exports = applyPolicy


Fix incorrect argument type to obtain scope.

--- lib/auto-indent.coffee
+++ lib/auto-indent.coffee
@@ -111,12 +111,12 @@

   # Buffer has stopped changing. Indent as required
   didStopChanging: () ->
-    return unless @autoJsx
+    return unless @autoJsx
     selectedRange = @editor.getSelectedBufferRange()
     highestRow = Math.max selectedRange.start.row, selectedRange.end.row
     if highestRow isnt @highestSelectedRow
       @highestSelectedRow = highestRow
-      scope = @editor.scopeDescriptorForBufferPosition(highestRow,0).getScopesArray()
+      scope = @editor.scopeDescriptorForBufferPosition([highestRow,0]).getScopesArray()
       if 'meta.tag.jsx' in scope
         endPointOfJsx = new Point highestRow,0
         startPointOfJsx =  autoCompleteJSX.getStartOfJSX @editor, endPointOfJsx


Handle reused commands in path parser for last command

--- lib/path.coffee
+++ lib/path.coffee
@@ -45,7 +45,7 @@

                 cmd = c

-            else if c in [" ", ","] or (c is "-" and curArg.length > 0 and curArg[curArg.length-1]!='e') or (c is "." and foundDecimal)
+            else if c in [" ", ","] or (c is "-" and curArg.length > 0 and curArg[curArg.length - 1] isnt 'e') or (c is "." and foundDecimal)
                 continue if curArg.length is 0

                 if args.length is params # handle reused commands
@@ -69,6 +69,14 @@
                 foundDecimal = true if c is '.'

         # add the last command
+        if args.length is params # handle reused commands
+            ret[ret.length] = {cmd,args}
+            args = [+curArg]
+
+            # handle assumed commands
+            cmd = "L" if cmd is "M"
+            cmd = "l" if cmd is "m"
+
         args[args.length] = +curArg if curArg.length > 0
         ret[ret.length] = {cmd,args}


Use our data structures.

Use our data structures.

--- lib/git-service.coffee
+++ lib/git-service.coffee
@@ -368,24 +368,24 @@
     patchText = "#{header}#{lines.join("\n")}\n"
     Promise.resolve(patchText)

-  stagePatch: (patchText, patch) =>
-    data = {}
-    oldPath = patch.oldFile().path()
-    newPath = patch.newFile().path()
+  stagePatch: (fileDiff, patchText) =>
+    data = {}
+    oldPath = fileDiff.getOldPathName()
+    newPath = fileDiff.getNewPathName()
     Git.Repository.open(@repoPath)
     .then (repo) ->
       data.repo = repo
       repo.openIndex()
     .then (index) =>
       data.index = index
-      @indexBlob(oldPath) unless patch.isUntracked()
+      @indexBlob(oldPath) unless fileDiff.isUntracked()
     .then (content) =>
       newContent = JsDiff.applyPatch(content ? '', patchText)
       console.log patchText, newContent
       buffer = new Buffer(newContent)
       oid    = data.repo.createBlobFromBuffer(buffer)

-      if patch.isDeleted()
+      if fileDiff.isDeleted()
         entry = data.index.getByPath(oldPath)
         entry.id = oid
         entry.fileSize = buffer.length
@@ -394,7 +394,7 @@
           oid: oid
           path: newPath
           fileSize: buffer.length
-          mode: patch.newFile().mode()
+          mode: fileDiff.getMode()

       data.index.removeByPath(oldPath) if oldPath != newPath
       data.index.add(entry)
@@ -405,12 +405,12 @@
       console.log error.message
       console.log error.stack

-  unstagePatch: (patchText, patch) =>
+  unstagePatch: (fileDiff, patchText) =>
     patchText = @reversePatch(patchText)

     data = {}
-    oldPath = patch.oldFile().path()
-    newPath = patch.newFile().path()
+    oldPath = fileDiff.getOldPathName()
+    newPath = fileDiff.getNewPathName()
     Git.Repository.open(@repoPath)
     .then (repo) ->
       data.repo = repo
@@ -423,7 +423,7 @@
           blob?.toString()
     .then (content) =>
       newContent = JsDiff.applyPatch(content or '', patchText)
-      if !newContent and patch.isAdded()
+      if !newContent and fileDiff.isAdded()
         @unstagePath(newPath)
       else
         buffer = new Buffer(newContent)
@@ -432,7 +432,7 @@
           oid: oid
           path: newPath
           fileSize: buffer.length
-          mode: patch.newFile().mode()
+          mode: fileDiff.getMode()
         data.index.add(entry)
         data.index.write()
     .then =>


fix test

fix test

--- apps/auction/test/routes.coffee
+++ apps/auction/test/routes.coffee
@@ -93,7 +93,7 @@
       routes.__set__ 'metaphysics', @metaphysics = sinon.stub()

     it 'filters to bids in this auction sorted by losing bids', (done) ->
-      @metaphysics.returns then: (callback) =>
+      @metaphysics.returns catch: => then: (callback) =>
         myActiveBids = callback { me: bidder_positions: [
           { id: 'a', sale_artwork: { sale_id: 'foobar' }, is_winning: true }
           { id: 'b', sale_artwork: { sale_id: 'baz' } }


Adjustable jump speed

Adjustable jump speed

--- assets/coffeescript/main.coffee
+++ assets/coffeescript/main.coffee
@@ -107,7 +107,7 @@

 class Selection
-    constructor: (@reading, @jump=500) -> # half a second default jump
+    constructor: (@reading) -> # half a second default jump
         @clear()

     clear: ->
@@ -144,9 +144,9 @@
         if cur_sample and (offset - prev_sample.time) * direction > (cur_sample.time - offset) * direction
             index += direction
         index
-    next: (direction=1) ->
+    next: (direction, jump) ->
         return unless @valid()
-        @offset += @jump * direction
+        @offset += jump * direction
         @start = @find_closest_sample(@get_start(), @offset, direction)
         @end = @find_closest_sample(@get_end(), @offset + @span, direction)
         @reading.unhighlight()
@@ -613,6 +613,7 @@
         fixations = null
         load_timer = null
         nocache = false
+        selection_jump = 500 # default: half a second

         set_opts = ->
             fixations = $('#i-dt').is(':checked')
@@ -939,6 +940,29 @@
                         callback: (key, options) ->
                             fixfix.data.reading.selection.clear()
                             fixfix.data.reading.unhighlight()
+                    select_speed:
+                        name: "Jump Speed"
+                        items:
+                            selspeed_100ms:
+                                name: "100 ms"
+                                icon: if selection_jump == 100 then "checkmark" else undefined
+                                callback: (key, options) ->
+                                    selection_jump = 100
+                            selspeed_200ms:
+                                name: "200 ms"
+                                icon: if selection_jump == 200 then "checkmark" else undefined
+                                callback: (key, options) ->
+                                    selection_jump = 200
+                            selspeed_500ms:
+                                name: "500 ms"
+                                icon: if selection_jump == 500 then "checkmark" else undefined
+                                callback: (key, options) ->
+                                    selection_jump = 500
+                            selspeed_1000ms:
+                                name: "1000 ms"
+                                icon: if selection_jump == 1000 then "checkmark" else undefined
+                                callback: (key, options) ->
+                                    selection_jump = 1000

         $(document).keydown (evt) ->
             return unless fixfix.reading_file?
@@ -947,10 +971,10 @@
             return true if $target.is('input')
             switch evt.keyCode
                 when 37 # left
-                    fixfix.data.reading.selection.next(-1)
+                    fixfix.data.reading.selection.next(-1, selection_jump)
                     stop(evt)
                 when 39 # right
-                    fixfix.data.reading.selection.next(+1)
+                    fixfix.data.reading.selection.next(+1, selection_jump)
                     stop(evt)


Fixed deal creation after shop signup

Fixed deal creation after shop signup

--- assets/controllers/shops.coffee
+++ assets/controllers/shops.coffee
@@ -30,7 +30,7 @@
           shop.$save(
             (successData) ->
               $scope.global.authenticated = true
-              $scope.global.shop = {name: $scope.shopname, email: $scope.email }
+              $scope.global.shop = successData
               $location.path('/')
             ,
             (errorData) ->
@@ -47,11 +47,11 @@
     $scope.errors = []

     $scope.doLogIn = () ->
-
+
       validationErrors = Validation.loginShop
         email: $scope.email
         password: $scope.password
-
+
       # Verify password and email have been provided
       if validationErrors.length == 0


Fix sort order in solvedByWeel

Fix sort order in solvedByWeel

--- server/models/user.coffee
+++ server/models/user.coffee
@@ -73,7 +73,7 @@

 usersSchema.statics.findByList = (list) ->
-    User.find({userList: list}).sort({active: -1, level: -1, ratingSort: -1})
+    User.find({userList: list}).sort({active: -1, "level.current": -1, ratingSort: -1})

 usersSchema.statics.findAll = (list) ->
     User.find {}


Replace dots in blog titles with dashes

Replace dots in blog titles with dashes

--- models/topicModel.coffee
+++ models/topicModel.coffee
@@ -21,7 +21,7 @@
         url += c
       else if c >= '0' and c <= '9'
         url += c
-      else if c is '(' or c is ')' or c is '.'
+      else if c is '(' or c is ')'
         url += c
       else
         url += '-'


:princess::satellite: Updated at https://danielx.net/editor/

--- tools.coffee
+++ tools.coffee
@@ -41,8 +41,7 @@
         target = e.target

       if validTarget(target)
-        context.withTransform editor.scene.matrix, (context) ->
-          drawRect(context, target)
+        drawRect(context, target, editor.scene.matrix)

       return unless state.activeElement

@@ -104,14 +103,17 @@
     name: "Cut"
     iconURL: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAA6lJREFUeJzt3E2KHHUAxuHX6FLU6MqtG4MLQSe5hCJuXZmPA0TwKCa4Eg/gBVS8gaAHUHDj2g8Ql34snE5qJvX2TE9XVazu54GGWdQ0/Yf6wUv39CQAAAAAAAAAAAAAAADM5LUk1572i4D/o7eT/Jrk84gEztjE8c/pQyRw6nwcIoFTLQ6RcPQuikMkHK3LxiESjs6ucYiEo3HVOETCwds3DpFwsKaKQyQcnKnj2Dw+W/IQMIe54vg7yb0FzwGTEwcU4oBCHFCIAwpxQCEOKMQBhTigEAcU4oBCHFCIAwpxQCEOKMQBhTigEAcU4oBCHFCIAwpxQCEOKMQBhTigEAcU4oBCHFCIAwpxQCEOKMQBxZxx3F3wHDA5cUAhDijEAYU4oBAHFOKAQhxQiAMKcUAhDijEAYU4oBAHFOKAQhxQiAMKcUAhDijEAYU4oBAHFOKAQhxQiAMKcUAhDijEAYU4oBAHFOKAQhxQiAOKOeO4s9wxYHrigEIcUIgDCnFAIQ4oxAGFOKAQBxTigEIcUIgDCnFAIQ4oxAGFOKAQBxTigEIcUIgDCnFAIQ4oxAGFOKAQBxTigEIcUIgDCnFAIQ4oxAGFOKAQBxRzxnF7wXPA5MQBhTigEAcU4oBCHFCIAwpxQCEOKMQBhTigEAcU4oBCHFCIAwpxQCEOKMQBhTigEAcU4oDizYgDqpeTfB9xwBkvDX6eMhJxsHq3kvySszfyFJGIg9W7leT3jN/Q+0QiDlZvGMfwxr4zuOYqkYiD1RuLY1sk35VrxcHB2RbHPpGIg9W7TBzDG/7u4He3RSIOVm+XOHaJRBys3s3sHkeL5HoeRyIOVm+fOIaR3Bs85/Uk30YcrNwUcbRInlvkBDCTKeNokcAqzRHHn0k+TfLGgueAyU0dx09JPs7ZP2iEVZoyjm+SvJfk2UVPADO5meS3TD+jnk/yzCIngJnsG8fYjLqR5JMk7y9yApjJPnGcn1HXkryb5OskfyX5cKlDwByuEsfYjHoxyUdJfszjt3NvL3ICmMmucbQZ9TDJH+eu+2CRE8BMdomjzaivLrgOVukkF8dxmRnlwz8OzkVx7DKjfPjHQdkWx9iMeidmFEdiLA4zCvJkHGPz6PUkD2JGcWSGcZhRMHCS5Oc8OY9eSHI/ZhRH7JX89z1wMwq22MyoL2NGwSObGfVDzCh4xIyCEW/FjIJRw/94aEbBwOY75GYUnHOS5IuYUTDq1af9AgAAAAAAAAAAAAAAAMi/0aQPy7R/vZsAAAAASUVORK5CYII="
     mousedown: (e, editor) ->
+      viewMatrix = editor.scene.matrix.inverse()
+
       active = true
       targetMap = new Map
       canvas = editor.screenElement
       context = canvas.getContext('2d')

-      path = [Point localPosition(e, false)]
+      path = [viewMatrix.transformPoint localPosition(e, false)]

     mousemove: (e, editor) ->
+      viewMatrix = editor.scene.matrix.inverse()
       target = e.target

       if active
@@ -120,23 +122,23 @@
           unless targetMap.has target
             targetMap.set target, []

-            drawRect(context, target)
+            drawRect(context, target, editor.scene.matrix)

         prev = path[path.length - 1]
-        current = Point localPosition(e, false)
+        current = viewMatrix.transformPoint localPosition(e, false)

         line = Line
           start: prev
           end: current

-        drawLine context, line, "purple"
+        drawLine context, line, "purple", editor.scene.matrix

         targetMap.forEach (intersections, target) ->
           rectLines(target).forEach (targetLine, i) ->
             intersection = line.intersects(targetLine)

             if intersection
-              drawCircle context, intersection
+              drawCircle context, intersection, editor.scene.matrix

               intersections.push [path.length, i]
               path.push intersection
@@ -297,7 +299,8 @@

   target.matrix.transformPoint(Point(width/2, height/2))

-drawCircle = (context, p) ->
+drawCircle = (context, p, transform=Matrix.IDENTITY) ->
+  p = transform.transformPoint(p)
   context.beginPath()
   context.arc(p.x, p.y, 5, 0, 2*Math.PI)
   context.fillStyle = "magenta"
@@ -326,18 +329,21 @@
       start: currentPoint
       end: nextPoint

-drawLine = (context, line, color="#8BF") ->
+drawLine = (context, line, color="#8BF", transform=Matrix.IDENTITY) ->
+  p1 = transform.transformPoint(line.start)
+  p2 = transform.transformPoint(line.end)
+
   context.beginPath()
-  context.moveTo(line.start.x, line.start.y)
-  context.lineTo(line.end.x, line.end.y)
+  context.moveTo(p1.x, p1.y)
+  context.lineTo(p2.x, p2.y)
   context.lineWidth = 2
   context.strokeStyle = color
   context.stroke()

-drawRect = (context, target) ->
+drawRect = (context, target, transform=Matrix.IDENTITY) ->
   rectLines(target)
   .forEach (line) ->
-    drawLine context, line
+    drawLine context, line, null, transform

 angleBetween = (a, b, origin=Point.ZERO) ->
   Point.direction(origin, b) - Point.direction(origin, a)


- Cleanup whitespace

- Cleanup whitespace

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -4,7 +4,7 @@
 # #
   {defineProperty} = Object
   deprecated = (methodName)-> warn "#{methodName} is deprecated from KDView if you need it override in your subclass"
-  eventNames =
+  eventNames =
     ///
     ^(
     (dbl)?click|
@@ -29,10 +29,10 @@
     view.parentIsInDom = yes
     view.propagateEvent KDEventType: 'viewAppended'

-# #
+# #
 # INSTANCE LEVEL
-# #
-
+# #
+
   constructor:(options = {},data)->
     o = options
     o.tagName     or= "div"     # a String of a HTML tag
@@ -53,12 +53,12 @@
     o.suffix      or= ""        # a String
     o.tooltip     or= null      # an Object of twipsy options
     super o,data
-
+
     data?.on? 'update', => @render()

     @setInstanceVariables options
     @defaultInit options,data
-
+
     if location.hostname is 'localhost'
       @listenTo
         KDEventTypes        : 'click'
@@ -78,7 +78,7 @@
       listenedToInstance  : @
       callback            : (publishingInstance, child)=>
         @childAppended child
-
+
     @listenTo
       KDEventTypes        : 'viewAppended'
       listenedToInstance  : @
@@ -102,8 +102,8 @@
             unless child.parentIsInDom
               child.parentIsInDom = yes
               child.propagateEvent KDEventType: 'viewAppended'
-
-
+
+
   setTemplate:(tmpl)->
     @template = new Pistachio(@, tmpl)
     @updatePartial @template.html
@@ -111,7 +111,7 @@

   pistachio:(tmpl)->
     "#{@options.prefix}#{tmpl}#{@options.suffix}"
-
+
   setParent:(parent)->
     if @parent?
       error 'View already has a parent'
@@ -121,7 +121,7 @@
         defineProperty @, 'parent', value : parent, configurable : yes
       else
         @parent = parent
-
+
   unsetParent:()->
     delete @parent

@@ -134,9 +134,9 @@
     child.setParent @
     @subViews.push child
     child.propagateEvent KDEventType: 'viewAppended'
-
+
   getTagName:-> @options.tagName || 'div'
-
+
   render:->
     if @template?
       @template.update()
@@ -146,7 +146,7 @@
   setInstanceVariables:(options)->
     {@domId, @parent} = options
     @subViews = []
-
+
   defaultInit:(options,data)->
     @setDomElement options.cssClass
     @setDataId()
@@ -156,18 +156,18 @@
     @setPosition options.position                 if options.position
     @setPartial options.partial                   if options.partial
     @addEventHandlers options
-
+
     if options.pistachio
       @setTemplate options.pistachio
       @template.update()
-
+
     @setDelegate options.delegate                 if options.delegate
     @setLazyLoader options.lazyLoadThreshold      if options.lazyLoadThreshold
-
+

     @setTooltip options.tooltip if options.tooltip
     @setDraggable options.draggable if options.draggable
-
+

     @bindEvents()

@@ -175,9 +175,9 @@
 # VIEW PROPERTY GETTERS
 # #

-  getDomId:()->
+  getDomId:()->
     @domElement.attr "id"
-
+

 # #
 # DOM ELEMENT CREATION
@@ -186,17 +186,17 @@
   setDomElement:(cssClass)->
     cssClass = if cssClass then " #{cssClass}" else ""
     @domElement = $ "<#{@options.tagName} class='kdview#{cssClass}'></#{@options.tagName} >"
-
+
   setDomId:(id)->
     @domElement.attr "id",id
-
+

   setDataId:()->
     @domElement.data "data-id",@getId()

   setDomAttributes:(attributes)->
     @domElement.attr attributes
-
+
   isInDom:do ->
     findUltimateAncestor =(el)->
       ancestor = el
@@ -204,17 +204,17 @@
         ancestor = ancestor.parentNode
       ancestor
     -> findUltimateAncestor(@$()[0]).body?
-
+
 # #
 # TRAVERSE DOM ELEMENT
 # #

   getDomElement:()-> @domElement
-
+
   getElement:-> @getDomElement()[0]

   # shortcut method for @getDomElement()
-  $ :(selector)->
+  $ :(selector)->
     if selector?
       @getDomElement().find(selector)
     else
@@ -229,41 +229,41 @@
     if @parentIsInDom
       child.propagateEvent KDEventType: 'viewAppended'
     @
-
+
   appendTo:(parent, selector)->
     @$().appendTo parent.$(selector)
     if @parentIsInDom
       @propagateEvent KDEventType: 'viewAppended'
     @
-
+
   prepend:(child, selector)->
     @$(selector).prepend child.$()
     if @parentIsInDom
       child.propagateEvent KDEventType: 'viewAppended'
     @
-
+
   prependTo:(parent, selector)->
     @$().prependTo parent.$(selector)
     if @parentIsInDom
       @propagateEvent KDEventType: 'viewAppended'
     @
-
+
   setPartial:(partial,selector)->
     @$(selector).append partial
     @
-
+
   updatePartial: (partial, selector) ->
     @$(selector).html partial
-
+
   # UPDATE PARTIAL EXPERIMENT TO NOT TO ORPHAN SUBVIEWS
-
+
   # updatePartial: (partial, selector) ->
   #   subViews = @getSubViews()
   #   subViewSelectors = for subView in subViews
   #     subView.$().parent().attr "class"
-  #
+  #
   #   @$(selector).html partial
-  #
+  #
   #   for subView,i in subViews
   #     @$(subViewSelectors[i]).append subView.$()

@@ -275,7 +275,7 @@
   setClass:(cssClass)->
     @$().addClass cssClass
     @
-
+
   unsetClass:(cssClass)->
     @$().removeClass cssClass
     @
@@ -299,7 +299,7 @@
     @setClass 'hidden'
     # @$().hide duration
     #@getDomElement()[0].style.display = "none"
-
+
   show:(duration)->
     @unsetClass 'hidden'
     # @$().show duration
@@ -313,7 +313,7 @@
     positionOptions = @getOptions().position
     positionOptions.position = "absolute"
     @$().css positionOptions
-
+
   getWidth:()->
     # pre = Date.now()
     # w = @getDomElement()[0].clientWidth
@@ -354,19 +354,19 @@
     if @parent and @parent.subViews?
       # log "parent subviews spliced"
       @parent.removeSubView @
-
+
     # instance removes itself from DOM
     @getDomElement().remove()
-
+
     if @$overlay?
       @removeOverlay()
-
+
     # call super to remove instance subscriptions
     # and delete instance from KD.instances registry
     super()
     # log delete @listeners
     # log delete @listeningTo
-
+
   destroySubViews:()->
     # (subView.destroy() for subView in @getSubViews())

@@ -407,7 +407,7 @@
   getSubViews:->
     ###
     FIX: NEEDS REFACTORING
-    used in @destroy
+    used in @destroy
     not always sub views stored in @subviews but in @items, @itemsOrdered etc
     see KDListView KDTreeView etc. and fix it.
     ###
@@ -415,7 +415,7 @@
     if @items?
       subViews = subViews.concat [].slice.call @items
     subViews
-
+
   removeSubView:(subViewInstance)->
     for subView,i in @subViews
       if subViewInstance is subView
@@ -431,8 +431,8 @@
 # #
 # EVENT BINDING/HANDLING
 # #
-
-
+
+
   setLazyLoader:(threshold=.75)->
     @getOptions().bind += ' scroll' unless /\bscroll\b/.test @getOptions().bind
     @listenTo
@@ -462,7 +462,7 @@
       eventsToBeBound.join(" ")
     else
       defaultEvents
-
+
     $elm.bind eventsToBeBound, (event)=>
       willPropagateToDOM = @handleEvent event
       event.stopPropagation() unless willPropagateToDOM
@@ -471,9 +471,9 @@
     # if @contextMenu?
     #   $elm.bind "contextmenu",(event)=>
     #     @handleEvent event
-
+
     eventsToBeBound
-
+
   handleEvent:(event)->
     # log event.type
     # thisEvent = @[event.type]? event or yes #this would be way awesomer than lines 98-103, but then we have to break camelcase convention in mouseUp, etc. names....??? worth it?
@@ -516,19 +516,19 @@
   keyDown:(event)->
     # log "override keyDown in your subclass to do something useful"
     yes
-
+
   keyPress:(event)->
     yes
-
+
   dblClick:(event)->
     yes
-
+
   click:(event)->
     yes

   contextMenu:(event)->
     yes
-
+
   mouseMove:(event)->
     yes

@@ -540,7 +540,7 @@
     # log "override mouseDown in your subclass to do something useful"
     (@getSingleton "windowController").setKeyView null
     yes
-
+
   mouseEnter:(event)-> yes
   mouseLeave:(event)-> yes

@@ -558,7 +558,7 @@

     e.preventDefault()
     e.stopPropagation()
-
+
   drop:(event)->

     event.preventDefault()
@@ -568,19 +568,19 @@
   submit:(event)->
     log "override submit in your subclass to do something useful"
     no #propagations leads to window refresh
-
+
   addEventHandlers:(options)->
     for key,value of options
       if eventNames.test key
-        @listenTo
+        @listenTo
           KDEventTypes       : key
           listenedToInstance : @
           callback           : value
-
+
   setDraggable:(options = {})->
-
+
     options = {} if options is yes
-
+
     @dragState =
       containment : options.containment             # a parent KDView
       handle      : options.handle                  # a parent KDView or a child selector
@@ -588,16 +588,16 @@

     handle = if options.handle and options.handle instanceof KDView then handle else @

-    @listenTo
+    @listenTo
       KDEventTypes       : "mousedown"
       listenedToInstance : handle
       callback           : (pubInst, event)=>
-
+
         if "string" is typeof options.handle
           return if $(event.target).closest(options.handle).length is 0
-
+
         top    = parseInt @$().css("top"), 10
-        right  = parseInt @$().css("right"), 10
+        right  = parseInt @$().css("right"), 10
         bottom = parseInt @$().css("bottom"), 10
         left   = parseInt @$().css("left"), 10

@@ -625,25 +625,25 @@
     x    = -x if directionX is "right"
     posY = @dragState[directionY] + y
     posX = @dragState[directionX] + x
-
+
     @$().css directionX, posX unless axis is 'y'
     @$().css directionY, posY unless axis is 'x'
-
+
     @emit "DragInAction", x, y
-
-# #
-# VIEW READY EVENTS
+
+# #
+# VIEW READY EVENTS
 # #

   viewAppended:()->
-
+
   childAppended:(child)->
     # bubbling childAppended event
     @parent?.propagateEvent KDEventType: 'childAppended', child
-
+
   setViewReady:()->
     @viewIsReady = yes
-
+
   isViewReady:()->
     @viewIsReady or no

@@ -662,15 +662,15 @@
 # #
 # DEFAULT CONTEXT MENU OPTIONS, DEPRECATED 2012/5/14 Sinan
 # #
-
+
   # classContextMenu:()->
   #   items = @classContextMenuItems()
   #   items.concat @contextMenuItems if @contextMenuItems?
   #   items
-  #
+  #
   # classContextMenuItems:()->
   #   items = []
-  #
+  #
   # setContextMenuItems:(menuItems)->
   #   @contextMenuItems = menuItems

@@ -684,7 +684,7 @@
 # #
 # HELPER METHODS
 # #
-
+
   putOverlay:(options = {})->

     {isRemovable, cssClass, parent, animated} = options
@@ -692,7 +692,7 @@
     isRemovable ?= yes
     cssClass    ?= "transparent"
     parent      ?= "body"           #body or a KDView instance
-
+
     @$overlay = $ "<div />", class : "kdoverlay #{cssClass} #{if animated then "animated"}"

     if parent is "body"
@@ -728,9 +728,9 @@
         kallback()
     else
       kallback()
-
+
   setTooltip:(o = {})->
-
+
     o.title     or= "Default tooltip title!"
     o.placement or= "above"
     o.offset    or= 0
@@ -739,16 +739,16 @@
     o.animate   or= yes
     o.selector  or= null

-    @listenTo
+    @listenTo
       KDEventTypes        : "viewAppended"
       listenedToInstance  : @
       callback            : =>
         # log "get rid of this timeout there should be an event after template update"
         @utils.wait =>
           @$(o.selector).twipsy o
-
+
   listenWindowResize:->
-
+
     @getSingleton('windowController').registerWindowResizeListener @


Reverting conclusion of #1001 -- Throw should be able to be used as part of an expression.

--- grammar.coffee
+++ grammar.coffee
@@ -77,7 +77,6 @@
   # Pure statements which cannot be expressions.
   Statement: [
     o 'Return'
-    o 'Throw'
     o 'Comment'
     o 'STATEMENT',                              -> new Literal $1
   ]
@@ -98,6 +97,7 @@
     o 'For'
     o 'Switch'
     o 'Class'
+    o 'Throw'
   ]

   # An indented block of expressions. Note that the [Rewriter](rewriter.html)


Remove random errorIds from error messages in legislation.

--- api/v1/legislation.coffee
+++ api/v1/legislation.coffee
@@ -11,11 +11,7 @@
   api.v1.getLegislation = (req, res) ->
     db.collection("legislation").find({sessionId: req.apiRequestSessionId}).sort({number: 1}).toArray (err, results) ->
       if err
-        errorId = Math.random().toString(36).substring(7)
-        console.error("Error " + errorId + ": " + err)
-
         res.jsonp
-          id: errorId,
           error: err
         , 500

@@ -38,11 +34,7 @@

     db.collection("legislation").findOne {sessionId: req.apiRequestSessionId, code: fullCode}, (err, legislation) ->
       if err
-        errorId = Math.random().toString(36).substring(7)
-        console.error("Error " + errorId + ": " + err)
-
         res.jsonp
-          id: errorId,
           error: err
         , 500


should check if nil

should check if nil

--- app/assets/javascripts/type_station/editables/edit_page.js.coffee
+++ app/assets/javascripts/type_station/editables/edit_page.js.coffee
@@ -27,8 +27,8 @@
     tsFieldTypes[field.name] = field.type
     label = "<label for='#{field.name}'>#{field.label}</label>"
     input = switch field.type
-      when "text" then "<input type='text' name='#{field.name}' id='#{field.name}' value='#{modelValues[field.name]}' />"
-      when "textarea" then "<textarea name='#{field.name}' id='#{field.name}' rows='5'>#{modelValues[field.name]}</textarea>"
+      when "text" then "<input type='text' name='#{field.name}' id='#{field.name}' value='#{if modelValues[field.name] then modelValues[field.name] else ''}' />"
+      when "textarea" then "<textarea name='#{field.name}' id='#{field.name}' rows='5'>#{if modelValues[field.name] then modelValues[field.name] else ''}</textarea>"
       when "select"
         select = "<select name='#{field.name}' id='#{field.name}'>"
         for o in field.options


fixed a bug caused by private messaging yourself

--- src/chat/chat.coffee
+++ src/chat/chat.coffee
@@ -191,8 +191,7 @@
       e.context.channel = chat.SERVER_WINDOW

     chan = e.context.channel
-    if conn?.irc.isOwnNick chan
-      return chat.NO_WINDOW unless e.name is 'privmsg'
+    if @_isDirectMessageToUser(conn, chan, e.name)
       from = e.args?[0]
       unless conn.windows[from]
         @createPrivateMessageWindow conn, from
@@ -205,6 +204,12 @@
     if conn.windows[chan]
       return conn.windows[chan]
     return chat.NO_WINDOW
+
+  ##
+  # Direct messages (e.g. /msg) have the channel set to the user's nick.
+  ##
+  _isDirectMessageToUser: (conn, chan, type) ->
+    conn?.irc.isOwnNick(chan) and type is 'privmsg'

   createPrivateMessageWindow: (conn, from) ->
     return conn.windows[from] if conn.windows[from]


Simplify regex patterns

Simplify regex patterns

--- grammars/csound.cson
+++ grammars/csound.cson
@@ -230,7 +230,7 @@
             endCaptures:   0: name: "punctuation.definition.string.end.csound"
             patterns: [
               {
-                match: "(\\(|\\)|[#'])|(\\\\(?:\\(|\\)|[#']))"
+                match: "([#'()])|(\\\\[#'()])"
                 captures:
                   1: name: "invalid.illegal.csound"
                   2: name: "constant.character.escape.csound"
@@ -243,7 +243,7 @@
             end:   "\\}\\}"
             patterns: [
               {
-                match: "(\\(|\\)|[#'])|(\\\\(?:\\(|\\)|[#']))"
+                match: "([#'()])|(\\\\[#'()])"
                 captures:
                   1: name: "invalid.illegal.csound"
                   2: name: "constant.character.escape.csound"


Fix issues with tokenizing prints and printks strings

--- grammars/csound.cson
+++ grammars/csound.cson
@@ -329,11 +329,14 @@
                 name:  "constant.character.escape.csound"
                 match: "\\\\\\\\[aAbBnNrRtT]"
               }
-              {include: "#escapeSequences"}
-              {include: "#formatSpecifiers"}
+              {include: "#bracedStringContents"}
+              {include: "#lineContinuations"}
               {
                 name:  "constant.character.escape.csound"
                 match: "%[!nNrRtT]|[~^]{1,2}"
+              },{
+                name:  "invalid.illegal.csound"
+                match: '[^"\\\\\\n]*\\n'
               }
             ]
           }


Add readscore to score opcodes

Add readscore to score opcodes

--- grammars/csound.cson
+++ grammars/csound.cson
@@ -281,7 +281,7 @@
           }
         ]
       },{
-        begin: '\\b(scoreline(?:_i)?)[ \\t]*(\\{\\{)'
+        begin: '\\b(readscore|scoreline(?:_i)?)[ \\t]*(\\{\\{)'
         end:   '\\}\\}'
         beginCaptures:
           1: name: 'support.function.csound'


Add first-line recognition of classical troff output

--- grammars/output.cson
+++ grammars/output.cson
@@ -1,6 +1,6 @@
 name: "Roff (Intermediate Output)"
 scopeName: "source.ditroff"
-firstLineMatch: "^x\\s*T\\s+(dvi|html|lbp|lj4|ps|pdf|ascii|cp1047|latin1|utf8|X75|X75-12|X100|X100-12)(?=\\s|$)"
+firstLineMatch: "^x\\s*T\\s+(dvi|html|lbp|lj4|ps|post|pdf|ascii|cp1047|latin1|utf8|X75|X75-12|X100|X100-12)(?=\\s|$)"
 patterns: [include: "#main"]

 repository:


Delete current classification global

Delete current classification global

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -54,7 +54,6 @@
         @props.subject.expert_classification_data
       else
         @props.classification
-      window.classification = currentClassification

       if currentClassification is @props.classification and not @props.classification.completed
         currentAnnotation = currentClassification.annotations[currentClassification.annotations.length - 1]


Admins can also activate demo mode

Admins can also activate demo mode

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -8,6 +8,7 @@
 preloadSubject = require '../lib/preload-subject'
 PromiseRenderer = require '../components/promise-renderer'
 TriggeredModalForm = require 'modal-form/triggered'
+isAdmin = require '../lib/is-admin'

 unless process.env.NODE_ENV is 'production'
   mockData = require './mock-data'
@@ -201,7 +202,7 @@
                 set.concat next

       <PromiseRenderer promise={getUserRoles}>{(userRoles) =>
-        if 'owner' in userRoles or 'collaborator' in userRoles or 'expert' in userRoles
+        if isAdmin() or 'owner' in userRoles or 'collaborator' in userRoles or 'expert' in userRoles
           <TriggeredModalForm trigger={

           }>
@@ -218,7 +219,7 @@
                 </TriggeredModalForm>

}

-              {if 'owner' in userRoles or 'collaborator' in userRoles
+              {if isAdmin() or 'owner' in userRoles or 'collaborator' in userRoles

                     <input type="checkbox" checked={@props.demoMode} onChange={@handleDemoModeChange} />{' '}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -3,7 +3,7 @@
 If a duration is specified the object will update that many times. If -1 is
 specified the object will have an unlimited duration.

-This module is
+This module is included by default in a

 enemy = GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Updating sshd_config resource snippet

Updating sshd_config resource snippet

--- snippets/sshd_config.cson
+++ snippets/sshd_config.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe sshd_config':
     'prefix': 'sshd_config'
-    'body': "# http://inspec.io/docs/reference/resources/sshd_config/\ndescribe sshd_config('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/sshd_config/\ndescribe sshd_config do\n  its('${1:setting}') { should${2:_not} eq '${3:value}' }${4}\nend"


fix key binding bug

fix key binding bug

--- band.coffee
+++ band.coffee
@@ -56,7 +56,7 @@
       pitch: keyMap[@key]
       instrument: instrument

-  keymaster 'q,w,e,r,t,y,u,i,o,p,[,],\\,z,x,c,v,b,n,m,2,3,5,6,7,9,0,-,=,backspace', keyPress
+  keymaster 'q,w,e,r,t,y,u,i,o,p,[,],\\,z,x,c,v,b,n,m,2,3,5,6,8,9,0,+,=,backspace', keyPress

   Template.piano.events
     "click li": (e) ->


fuck favicon

fuck favicon


Optimize aliasing

Optimize aliasing


Revert "guests group: map it over to "koding" on the client side"

This reverts commit 205a952a959dad9a48c4ea669ed7fd76f06d0037.

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -66,15 +66,22 @@

   changeGroup:(groupName='', callback=->)->

-    groupName or=
-      if KD.defaultSlug is 'guests'
-      then 'koding'
-      else KD.defaultSlug
-
+    groupName or= KD.defaultSlug
     return callback()  if @currentGroupName is groupName

-    throw new Error 'Cannot change the group!'  if @currentGroupName?
-    unless @currentGroupName is groupName
+    if @currentGroupName?
+      location.reload()
+      # modal = new KDModalView
+      #   title       : 'Change group'
+      #   content     : 'You have requested to change the group.'
+      #   overlay     : yes
+      #   buttons     :
+      #     "Yes, Change group":
+      #       style    : "modal-clean-red"
+      #       callback : (event)=>
+      #         location.reload()
+
+    else unless @currentGroupName is groupName
       KD.remote.cacheable groupName, (err, models)=>
         if err then callback err
         else if models?


home/tryonkoding: fix tryonkoding button initial state

--- client/home/lib/utilities/components/tryonkoding/container.coffee
+++ client/home/lib/utilities/components/tryonkoding/container.coffee
@@ -30,7 +30,7 @@

       allowedDomains = @state.team.get 'allowedDomains'

-      if '*' in allowedDomains.toJS()
+      unless '*' in allowedDomains.toJS()
         @setState
           primaryClassName : 'primary'
           secondaryClassName : 'secondary hidden'


Troubleshoot: Kite Broker status is also checked

--- client/Main/troubleshoot.coffee
+++ client/Main/troubleshoot.coffee
@@ -38,6 +38,8 @@
     KD.remote.once "modelsReady", =>
       bongoStatus = KD.remote.api.JSystemStatus
       @registerItem "bongo", bongoStatus, bongoStatus.healthCheck
+    # register kite
+    @registerItem "kiteBroker", KD.kite.mq, KD.kite.mq.ping
     @vc = KD.singleton "vmController"
     @registerItem "broker", KD.remote.mq
     @registerItem "osKite", @vc


oauth: minor fix

oauth: minor fix

--- client/account/lib/index.coffee
+++ client/account/lib/index.coffee
@@ -32,6 +32,7 @@
       items  : [
         { slug : 'Profile',   title : "User profile",        listType: "username" }
         { slug : 'Email',     title : "Email notifications", listType: "emailNotifications" }
+        { slug : 'Externals', title : "Linked accounts",     listType: "linkedAccounts" }
       ]
     billing :
       title : "Billing"
@@ -53,7 +54,6 @@
       ]

-  NAV_ITEMS.personal.items.push { slug : 'Externals',   title : "Linked accounts", listType: "linkedAccounts" }

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

@@ -94,7 +94,9 @@
       break

-  handleOauthRedirect: ({error, provider}) ->
+  handleOauthRedirect: (options) ->
+
+    { error, provider } = options

     error = null  if error is "null"
     kd.singletons.oauthController.authCompleted error, provider


Update zh-HANT.coffee (#5615)

Update zh-HANT.coffee (#5615)

--- app/locale/zh-HANT.coffee
+++ app/locale/zh-HANT.coffee
@@ -199,7 +199,7 @@
   modal:
     close: "關閉"
     okay: "好"
-#    cancel: "Cancel"
+    cancel: "取消"

   not_found:
     page_not_found: "找不到網頁"


no need to make global

no need to make global

--- core/app/backbone/views/auto_completed_added_channels_view.coffee
+++ core/app/backbone/views/auto_completed_added_channels_view.coffee
@@ -1,4 +1,4 @@
-class window.AutoCompletedAddedChannelView extends Backbone.Marionette.ItemView
+class AutoCompletedAddedChannelView extends Backbone.Marionette.ItemView
   tagName: "li"

   triggers: