:white_check_mark: Write specs for cache

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -176,6 +176,42 @@
             body: """
               $0one${1} ${2:two} three${3}
             """
+
+    it "parses snippets once, reusing cached ones on subsequent queries", ->
+      spyOn(Snippets, "getBodyParser").andCallThrough()
+
+      editor.insertText("t1")
+      simulateTabKeyEvent()
+
+      expect(Snippets.getBodyParser).toHaveBeenCalled()
+      expect(editor.lineTextForBufferRow(0)).toBe "this is a testvar quicksort = function () {"
+      expect(editor.getCursorScreenPosition()).toEqual [0, 14]
+
+      Snippets.getBodyParser.reset()
+
+      editor.setText("")
+      editor.insertText("t1")
+      simulateTabKeyEvent()
+
+      expect(Snippets.getBodyParser).not.toHaveBeenCalled()
+      expect(editor.lineTextForBufferRow(0)).toBe "this is a test"
+      expect(editor.getCursorScreenPosition()).toEqual [0, 14]
+
+      Snippets.getBodyParser.reset()
+
+      Snippets.add __filename,
+        ".source.js":
+          "invalidate previous snippet":
+            prefix: "t1"
+            body: "new snippet"
+
+      editor.setText("")
+      editor.insertText("t1")
+      simulateTabKeyEvent()
+
+      expect(Snippets.getBodyParser).toHaveBeenCalled()
+      expect(editor.lineTextForBufferRow(0)).toBe "new snippet"
+      expect(editor.getCursorScreenPosition()).toEqual [0, 11]

     describe "when the snippet body is invalid or missing", ->
       it "does not register the snippet", ->


Typo and fix wrong logger in konnector init module

--- server/init/konnectors.coffee
+++ server/init/konnectors.coffee
@@ -34,7 +34,7 @@
                     createKonnectors konnectorsToCreate, callback

-# Reset konnector importing flags: isImporting flage is set to false if value
+# Reset konnector importing flags: isImporting flag is set to false if value
 # is true. This happens when the app is crashing while importing.
 konnectorResetValue = (konnector, callback) ->

@@ -58,7 +58,7 @@
     for name, konnectorModule of konnectorModules
         unless konnectorHash[konnectorModule.slug]?
             konnectorsToCreate.push konnectorModule
-            console.info "New konnector to init: #{name}"
+            log.info "New konnector to init: #{name}"

     return konnectorsToCreate


Implemented issue #394 - email filter issues - unable to set additional ticket attributes.

--- app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee
+++ app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee
@@ -2,64 +2,94 @@
 class App.UiElement.postmaster_set
   @defaults: ->
     groups =
-      general:
+      ticket:
         name: 'Ticket'
         options: [
           {
-            value:    'x-zammad-ticket-priority_id'
+            value:    'priority_id'
             name:     'Priority'
             relation: 'TicketPriority'
-          },
-          {
-            value:    'x-zammad-ticket-state_id'
+          }
+          {
+            value:    'state_id'
             name:     'State'
             relation: 'TicketState'
-          },
-          {
-            value:    'x-zammad-ticket-customer_id'
+          }
+          {
+            value:    'customer_id'
             name:     'Customer'
             relation: 'User'
             tag:      'user_autocompletion'
-            disableCreateUser: true,
-          },
-          {
-
-            value:    'x-zammad-ticket-group_id'
+            disableCreateUser: true
+          }
+          {
+            value:    'group_id'
             name:     'Group'
             relation: 'Group'
-          },
-          {
-            value:    'x-zammad-ticket-owner_id'
+          }
+          {
+            value:    'owner_id'
             name:     'Owner'
             relation: 'User'
             tag:      'user_autocompletion'
-            disableCreateUser: true,
-          },
+            disableCreateUser: true
+          }
+        ]
+      article:
+        name: 'Article'
+        options: [
+          {
+            value:    'x-zammad-article-internal'
+            name:     'Internal'
+            options:  { true: 'yes', false: 'no'}
+          }
+          {
+            value:    'x-zammad-article-type_id'
+            name:     'Type'
+            relation: 'TicketArticleType'
+          }
+          {
+            value:    'x-zammad-article-sender_id'
+            name:     'Sender'
+            relation: 'TicketArticleSender'
+          }
+        ]
+      expert:
+        name: 'Expert'
+        options: [
           {
             value:    'x-zammad-ignore'
             name:     'Ignore Message'
             options:  { true: 'yes', false: 'no'}
-          },
+          }
         ]
-      expert:
-        name: 'Article'
-        options: [
-          {
-            value:    'x-zammad-article-internal'
-            name:     'Internal'
-            options:  { true: 'yes', false: 'no'}
-          },
-          {
-            value:    'x-zammad-article-type_id'
-            name:     'Type'
-            relation: 'TicketArticleType'
-          },
-          {
-            value:    'x-zammad-article-sender_id'
-            name:     'Sender'
-            relation: 'TicketArticleSender'
-          },
-        ]
+
+    # add additional ticket attributes
+    for row in App.Ticket.configure_attributes
+      exists = false
+      for item in groups.ticket.options
+        if item.value is row.name
+          exists = true
+
+        # do not support this types
+        else if row.tag is 'datetime' || row.tag is 'date' || row.tag is 'tag'
+          exists = true
+
+      # ignore passwords and relations
+      if !exists && row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids'
+
+        # ignore readonly attributes
+        if !row.readonly
+          item =
+            value:    row.name
+            name:     row.display
+            relation: row.relation
+            tag:      row.tag
+            options:  row.options
+          groups.ticket.options.push item
+
+    for item in groups.ticket.options
+      item.value = "x-zammad-ticket-#{item.value}"

     groups

@@ -91,7 +121,6 @@
     item.find('.js-attributeSelector select').bind('change', (e) =>
       key = $(e.target).find('option:selected').attr('value')
       elementRow = $(e.target).closest('.js-filterElement')
-
       @rebuildAttributeSelectors(item, elementRow, key, attribute)
       @buildValue(item, elementRow, key, groups, undefined, undefined, attribute)
     )
@@ -130,7 +159,7 @@
     for groupName, meta of groups
       for entry in meta.options
         if entry.value is key
-          config = entry
+          config = clone(entry)
     if !config.tag
       if config.relation || config.options
         config['tag'] = 'select'


Added 500ms wait before click, changed selector definition

--- client/test/lib/ide/layout.coffee
+++ client/test/lib/ide/layout.coffee
@@ -86,24 +86,29 @@
     settingsTabSelector     = "#{tabSelector} .settings"
     collapseButton          = '.application-tab-handle-holder .general-handles'
     collapsedWindowSelector = '.kdview.kdscrollview.kdsplitview-panel.panel-0.floating'
-    settingsHeaderSelector  = '.kdsplitview-panel.panel-0 .settings-header'
+    settingsHeaderSelector  = '.kdtabhandle-tabs .settings.kddraggable.active'

     user = helpers.beginTest(browser)
     helpers.waitForVMRunning(browser)

     browser
       .waitForElementVisible        fileTabSelector, 20000
+      .pause                        3000 # wait for file tree to load
       .moveToElement                fileTabSelector, 5, 5
       .waitForElementVisible        collapseButton, 20000
       .click                        collapseButton
+      .pause                        500
       .waitForElementVisible        collapsedWindowSelector, 20000
       .click                        fileTabSelector
+      .pause                        500
       .waitForElementNotPresent     collapsedWindowSelector, 20000
       .click                        collapseButton
+      .pause                        500
       .waitForElementVisible        collapsedWindowSelector, 20000
       .click                        settingsTabSelector
+      .pause                        500
       .waitForElementNotPresent     collapsedWindowSelector, 20000
-      .assert.containsText          settingsHeaderSelector, 'Editor Settings'
+      .waitForElementVisible        settingsHeaderSelector, 20000
       .end()

@@ -111,7 +116,8 @@

     sidebarSelector         = '.kdview.with-sidebar .logo-wrapper'
     collapseButton          = '.logo-wrapper .sidebar-close-handle'
-    collapsedWindowSelector = '.kdview.with-sidebar.desktop.collapsed'
+    collapsedWindowSelector = '.kdview.with-sidebar#kdmaincontainer.collapsed'
+    hiddenExpandSelector    = '.sidebar-close-handle.hidden .icon'

     user = helpers.beginTest(browser)
     helpers.waitForVMRunning(browser)
@@ -121,8 +127,12 @@
       .moveToElement             sidebarSelector, 5, 5
       .waitForElementVisible     collapseButton, 20000
       .click                     collapseButton
+      .pause                     500
       .waitForElementVisible     collapsedWindowSelector, 20000
       .moveToElement             sidebarSelector, 5, 5
+      .pause                     2000 # wait for hidden button to be displayed
+      .click                     hiddenExpandSelector
+      .pause                     500
       .waitForElementNotPresent  collapsedWindowSelector, 20000
       .end()


Tests: IDE: Search: Search content in active tab.

--- client/test/lib/ide/search.coffee
+++ client/test/lib/ide/search.coffee
@@ -37,7 +37,7 @@
         return browser.end() unless lowercase

         tabHandleSelector = ".panel-1 div[title='#{fileName}']"
-        editorSelector    = ".panel-1 .kdtabpaneview.#{lowercase} .ace_content"
+        editorSelector    = ".panel-1 .kdtabpaneview.active.#{lowercase} .ace_content"

         browser
           .waitForElementVisible     matchedWordSelector, 20000


setAudience protocol conform

setAudience protocol conform

--- pub/work/src/js/actions/WorkActions.coffee
+++ pub/work/src/js/actions/WorkActions.coffee
@@ -25,6 +25,8 @@
     Dispatcher.handleViewAction {type:'newItem', index, item}

   setItem: ({id,version},key,val) ->
+    if key is "audience"
+      return @setAudience {id},val
     version += 1
     key = key.split('_').join '-'
     set = "#{key}": val


Simplify browser-specific extension install logic

--- core/app/backbone/views/react_install_extension_button.coffee
+++ core/app/backbone/views/react_install_extension_button.coffee
@@ -25,17 +25,7 @@
         disabled: true],
         'Factlink already installed.'
     else
-      _div [],
-        _div ['visible-when-chrome'],
-          _a ['button-success', extra_class, href: extension_link],
-            _span [icon_class]
-            'Install Factlink for ' + browserName.capitalize()
-        _div ['visible-when-firefox'],
-          _a ['button-success', extra_class, href: extension_link],
-            _span [icon_class]
-            'Install Factlink for ' + browserName.capitalize()
-        _div ['visible-when-safari'],
-          _a ['button-success', extra_class, href: extension_link],
-            _span [icon_class]
-            'Install Factlink for ' + browserName.capitalize()
+      _a ['button-success', extra_class, href: extension_link],
+        _span [icon_class]
+        'Install Factlink for ' + browserName.capitalize()


FIx rendering bugs with multiline sockets

--- src/controller.coffee
+++ src/controller.coffee
@@ -1915,8 +1915,6 @@
     @hiddenInput.addEventListener event, =>
       @highlightFlashShow()
       if @cursorAtSocket()
-        @populateSocket @getCursor(), @hiddenInput.value
-
         @redrawTextInput()

         # Update the dropdown size to match
@@ -2039,7 +2037,7 @@
         endPosition - startPosition, @view.opts.textHeight

     else
-      @cursorCtx.fillRect startPosition, textFocusView.bounds[startRow].y + @view.opts.textPadding +
+      @cursorCtx.fillRect startPosition, textFocusView.bounds[startRow].y + @view.opts.textPadding,
         textFocusView.bounds[startRow].right() - @view.opts.textPadding - startPosition, @view.opts.textHeight

       for i in [startRow + 1...endRow]


Don't overwrite options var and pass on 'go' param

--- coach/src/user/login-gateway.cjsx
+++ coach/src/user/login-gateway.cjsx
@@ -38,15 +38,15 @@
     openWindow: (windowImpl = window, options = {type: 'login'}) ->
       width  = Math.min(1000, windowImpl.screen.width - 20)
       height = Math.min(800, windowImpl.screen.height - 30)
-      options = ["toolbar=no", "location=" + (if windowImpl.opera then "no" else "yes"),
+      windowOptions = ["toolbar=no", "location=" + (if windowImpl.opera then "no" else "yes"),
         "directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=no",
         "width=" + width, "height=" + height,
         "top="   + (windowImpl.screen.height - height) / 2,
         "left="  + (windowImpl.screen.width - width)   / 2].join()

       url = @urlForLogin()
-      url += '?go=signup' if options.type is 'signup'
-      CURRENT_WINDOW = windowImpl.open(url, 'oxlogin', options)
+      url += '&go=signup' if options.type is 'signup'
+      CURRENT_WINDOW = windowImpl.open(url, 'oxlogin', windowOptions)

   propTypes:


SocialWorker: remove follow and unfollow shared methods

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -98,13 +98,6 @@
       instance:
         modify:
           (signature Object, Function)
-        follow: [
-          (signature Function)
-          (signature Object, Function)
-        ]
-        unfollow: [
-          (signature Function)
-        ]
         fetchAppStorage:
           (signature Object, Function)
         setEmailPreferences:


account: normalize callback signature for #fetchInvitationByGroupSlug

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -1371,17 +1371,17 @@
       JGroup = require './group'
       JGroup.one { slug }, (err, groupObj)->
         return callback err  if err
-        callback null, invite, groupObj
+        callback null, { invite, group: groupObj }

   acceptInvitation: secure (client, slug, callback)->
-    @fetchInvitationByGroupSlug slug, (err, invite, groupObj)=>
+    @fetchInvitationByGroupSlug slug, (err, { invite, group })=>
       return callback err  if err
       groupObj.approveMember this, (err)->
         return callback err  if err
         invite.update $set:status:'accepted', callback

   ignoreInvitation: secure (client, slug, callback)->
-    @fetchInvitationByGroupSlug slug, (err, invite)->
+    @fetchInvitationByGroupSlug slug, (err, { invite })->
       return callback err  if err
       invite.update $set:status:'ignored', callback


Social: emit an event when we mark a user as exempt

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -61,6 +61,7 @@
         # { name: 'updateInstance' }
         { name: 'notification' }
         { name : "RemovedFromCollection" }
+        { name : "markedAsExempt" }

       ]
     sharedMethods :
@@ -961,11 +962,11 @@
     # mark user as troll in social api
     @markUserAsExemptInSocialAPI client, exempt, (err, data)=>
       return callback new ApiError err  if err
-      @update $set: {isExempt: exempt}, (err, result)->
+      @update $set: {isExempt: exempt}, (err, result)=>
         if err
           console.error 'Could not update user exempt information'
           return callback err
-
+        @emit "markedAsExempt", @
         callback null, result

   markUserAsExemptInSocialAPI: (client, exempt, callback)->


Be sure when a message is selected enableMessageActionButtons

--- client/landing/app/Applications/Inbox.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/AppController.coffee
@@ -102,7 +102,7 @@
           paneView.addSubView detail
           paneView.detail = detail
           item.paneView = paneView
-
+        newMessageBar.enableMessageActionButtons()
         @selectMessage data, item, paneView

     newMessageBar.registerListener


Preview: Fix path issue.

Preview: Fix path issue.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -943,8 +943,7 @@

     if FSHelper.isPublicPath file.path
       nickname    = @collaborationHost or nick()
-      prefix      = "[#{@mountedMachineUId}]/home/#{nickname}/Web/"
-      [temp, src] = file.path.split prefix
+      [temp, src] = FSHelper.plainPath(file.path).split '/Web'
       @createNewBrowser "#{@mountedMachine.domain}/#{src}"
     else
       @notify 'File needs to be under ~/Web folder to preview.', 'error'


Disable a flaky ORA Javascript test.

Disable a flaky ORA Javascript test.

--- common/lib/xmodule/xmodule/js/spec/combinedopenended/display_spec.coffee
+++ common/lib/xmodule/xmodule/js/spec/combinedopenended/display_spec.coffee
@@ -81,7 +81,7 @@
       expect(window.setTimeout).toHaveBeenCalledWith(@combined.poll, 10000)
       expect(window.queuePollerID).toBe(5)

-    it 'polling stops properly', =>
+    xit 'polling stops properly', =>
       fakeResponseDone = state: "done"
       spyOn($, 'postWithPrefix').andCallFake (url, callback) -> callback(fakeResponseDone)
       @combined.poll()


simplify unusual unit test

simplify unusual unit test


More concise generation of logged out ID's

--- src/kissmetrics-anon.coffee
+++ src/kissmetrics-anon.coffee
@@ -189,9 +189,7 @@
   constructor: (key) ->
     @storage = new KissmetricsStorage 'kissmetricsAnon'

-    unless person = @storage.get()
-      person = @createID()
-      @storage.set person
+    @storage.set(person = @createID()) unless person = @storage.get()

     super key, person


:wrench: make search case insensitive

:wrench: make search case insensitive

--- src/vdom-trees.coffee
+++ src/vdom-trees.coffee
@@ -24,7 +24,7 @@
   matchedItemsProp = Bacon.combineWith (items, searchStr) ->
     if searchStr
       items.filter (item) ->
-        item.relPath.search(searchStr) isnt -1
+        item.relPath.toLowerCase().search(searchStr) isnt -1
     else
       items
   , itemsProp, searchBus.toProperty('')


Reuse regexps when comparing results in fuzzy_sort

--- coffee/storage_freak.coffee
+++ coffee/storage_freak.coffee
@@ -7,32 +7,12 @@
   # $.getScript "assets/json2.js" if StorageFreak.JSON_support() is false

   class StorageFreak
-    # Sorts by exact query matching
-    absolute_sort = (query, a, b) ->
-      re_abs = @_createAccentIndependentRE(query, 'absolute')
-
-      ((abs_match_a, abs_match_b) ->
-        return -1 if abs_match_a
-        return  1 if abs_match_b)(@config.match_func(re_abs, a.name),
-                                  @config.match_func(re_abs, b.name))
-
-    # Sorts by matching an exact token of the query
-    token_sort = (query, a, b) ->
-      re_t = @_createAccentIndependentRE(query, 'token')
-
-      ((token_match_a, token_match_b) ->
-          return -1 if token_match_a && !token_match_b
-          return  1 if token_match_b && !token_match_a)(@config.match_func(re_t, a.name),
-                                                        @config.match_func(re_t, b.name))
-
-    # Sorts by matching query prefix
-    prefix_sort = (query, a, b) ->
-      re_p = @_createAccentIndependentRE(query, 'prefix')
-
-      ((prefix_match_a, prefix_match_b) ->
-        return -1 if prefix_match_a && !prefix_match_b
-        return  1 if prefix_match_b && !prefix_match_a)(@config.match_func(re_p, a.name),
-                                                        @config.match_func(re_p, b.name))
+    # Sorts by regular expression matching
+    regexp_sort = (re, a, b) ->
+      ((re_match_a, re_match_b) ->
+        return -1 if re_match_a && !re_match_b
+        return  1 if re_match_b && !re_match_a)(@config.match_func(re, a.name),
+                                                @config.match_func(re, b.name))

     # Sorts by aphabetical order
     lexicographical_sort = (a, b) ->
@@ -43,14 +23,11 @@
     # Compares a to b with respect to the query given by the user
     # Uses in order absolute, token, prefix and lexicographical sorting
     fuzzy_sort = (query, a, b) ->
-      abs_result = absolute_sort.call(@, query, a, b)
-      return abs_result   if abs_result
-
-      token_result = token_sort.call(@, query, a, b)
-      return token_result if token_result
-
-      prefix_result = prefix_sort.call(@, query, a, b)
-      return prefix_result if prefix_result
+      @sort_criteria_res ||= @_createSortingREs(query)
+
+      for re in @sort_criteria_res
+        regexp_result = regexp_sort.call(@, re, a, b)
+        return regexp_result if regexp_result

       lexicographical_sort(a, b)

@@ -170,7 +147,7 @@
       for id, name of @_data
         results.push({id: id, name: name}) if match_func(re, name)

-      results = results.sort ((a, b) => sort_func(query, a, b))
+      results = results.sort ((a, b) -> sort_func(query, a, b))
       results.slice 0, @config.maxResultsNum

     _getRemoteData: (query, options)->
@@ -235,13 +212,20 @@
         re = new RegExp "#{value[0]}|#{value[1]}", 'ig'
         query = query.replace re, "(?:#{value[0]}|#{value[1]})"

-      return query
+      query

     _createQueryRE: (query, type = 'infix')->
       return new RegExp "#{query}", 'ig'        if type == 'infix'
       return new RegExp "^#{query}", 'ig'       if type == 'prefix'
       return new RegExp "\\b#{query}\\b", 'ig'  if type == 'token'
       return new RegExp "^#{query}$", 'ig'      if type == 'absolute'
+
+    _createSortingREs: (query)->
+      re_abs = @_createQueryRE(query, 'absolute')
+      re_t = @_createQueryRE(query, 'token')
+      re_p = @_createQueryRE(query, 'prefix')
+
+      [re_abs, re_t, re_p]

     ## REFACTOR THOSE
     ## MAYBE REMOVE THOSE??


investigation wip

investigation wip

--- jsapp/xlform/src/view.rowSelector.coffee
+++ jsapp/xlform/src/view.rowSelector.coffee
@@ -11,6 +11,7 @@
   class viewRowSelector.RowSelector extends $baseView
     events:
       "click .js-close-row-selector": "shrink"
+
     initialize: (opts)->
       @options = opts
       @ngScope = opts.ngScope
@@ -19,6 +20,7 @@
       @line = @$el.find(".line")
       if opts.action is "click-add-row"
         @expand()
+      return

     expand: ->
       @$el.parents('.survey-editor__null-top-row--hidden').removeClass('survey-editor__null-top-row--hidden')
@@ -29,6 +31,7 @@
         evt.preventDefault()
         $namer_form.submit()
       @$('input').eq(0).focus()
+      return

     show_namer: () ->
       $surveyViewEl = @options.surveyView.$el
@@ -59,6 +62,7 @@
           if $(evt.target).closest('.line.expanded').length == 0
             evt.preventDefault()
             @$('input').eq(0).focus()
+      return

     show_picker: (evt) ->
       evt.preventDefault()
@@ -131,6 +135,8 @@
         # user makes selection by pressing ENTER
         if evt.which == $ENTER
           currentListRow.eq(columnIndex).children().eq(rowIndex).trigger('click')
+
+      return

     shrink: ->
       # click .js-close-row-selector
@@ -145,14 +151,19 @@
       @line.animate height: "0"
       if @reversible
         @button.removeClass('btn--hidden')
+      return
+
     hide: ->
+      console.log('rowSelector hide')
       @button.removeClass('btn--hidden')
       @line.empty().removeClass("expanded").css "height": 0
       @line.parents(".survey-editor__null-top-row")
           .removeClass("expanded")
           .addClass("survey-editor__null-top-row--hidden")
+      return

     onSelectNewQuestionType: (evt)->
+      console.log('onSelectNewQuestionType')
       @question_name = @line.find('input').val()
       $rowSelect = $('select.skiplogic__rowselect')
       if $rowSelect.data('select2')
@@ -186,7 +197,13 @@
       newRow = survey.addRow(rowDetails, options)
       newRow.linkUp(warnings: [], errors: [])
       @hide()
-
+      return
+
+    ###
+    # Scrolls the newly opened element into the screen if it is being opened
+    # below the fold. Calling the function doesn't cause the scrolling
+    # to happen unless it passes the checks.
+    ###
     scrollFormBuilder: (scrollBy)->
       $row = @$el.parents('.survey__row')
       if !$row.length
@@ -196,5 +213,6 @@

       if $row.height() + $row.position().top + 50 > $fbC.height() + $fbC.prop('scrollTop')
         $fbC.animate scrollTop: scrollBy
+      return

   viewRowSelector


Scope t * shortcuts to outline mode.

Scope t * shortcuts to outline mode.

--- keymaps/foldingtext-markdown.cson
+++ keymaps/foldingtext-markdown.cson
@@ -2,10 +2,10 @@
   'ctrl-cmd-c': 'foldingtext-markdown:copy-markdown'
   'ctrl-cmd-v': 'foldingtext-markdown:paste-markdown'

-'ft-outline-editor':
+'ft-outline-editor.outlineMode':
   't p': 'foldingtext-markdown:make-paragraph'
   't h': 'foldingtext-markdown:make-header'
   't c': 'foldingtext-markdown:make-code-block'
   't q': 'foldingtext-markdown:make-block-quote'
   't o': 'foldingtext-markdown:make-ordered-list'
-  't u': 'foldingtext-markdown:make-bullet-list'+  't u': 'foldingtext-markdown:make-bullet-list'


Helpful message when the web server cannot start listening.

--- src/web-server.coffee
+++ src/web-server.coffee
@@ -68,22 +68,47 @@

     ++@_notYetListening

-    # Helpful message.
+    errorHandler = (error) ->
+      # `address()` can only be used once listening.
+      address = do ->
+        return socket if socket?
+        "#{host}:#{port}"
+
+      # Prints a (hopefully) helpful message if the server could not
+      # listen.
+      console.log "[WARN] WebServer could not listen on #{address}"
+      switch error.code
+        when 'EACCES'
+          console.log '       Access denied.'
+          if port < 1024
+            console.log '       Ports < 1024 are often reserved to privileges users.'
+        when 'EADDRINUSE'
+          console.log '       Address already in use.'
+
+      # This server will never start listening.
+      --@_notYetListening
+
+    # Registers the error handler.
+    server.on 'error', errorHandler
+
     server.once 'listening', =>
+      # Removes the error handler.
+      server.removeListener 'error', errorHandler
+
+      # Prints a helpful message.
       address = server.address()
       if $_.isObject address
         {address, port} = address
         address = "#{address}:#{port}"
-
       console.log "WebServer listening on #{address}"

       # If the web server is listening on all addresses, fire the
       # `listening` event.
       @emit 'listening' unless --@_notYetListening

-    # Forwards events to this object.
-    $_.each $events, (event) =>
-      server.on event, (args...) => @emit event, args...
+      # Forwards events to this object.
+      $_.each $events, (event) =>
+        server.on event, (args...) => @emit event, args...

     # Does not return anything.
     undefined


Thought I fixed a small bug but actually made it worse

--- templates/example-aerix/javascript/invoice-view.coffee
+++ templates/example-aerix/javascript/invoice-view.coffee
@@ -57,12 +57,12 @@
     @$('div#invoice-body table tbody').empty()
     for index, itemObj of @model.get("invoiceItems")
       $row = $itemPrototype.clone()
-      itemObj.subtotal = @model.itemSubtotal(itemObj)
+      itemObj.subTotal = @model.itemSubtotal(itemObj)
       @_mapObjToDOM itemObj, $row
       @$("div#invoice-body table tbody").append $row
     # Table footer part
     footerAggregate = {}
-    footerAggregate.subTotal = @model.subTotal()
+    footerAggregate.subtotal = @model.subTotal()
     footerAggregate.vat = @model.VAT(footerAggregate.subtotal)
     footerAggregate.total = footerAggregate.subtotal + footerAggregate.vat
     @_mapObjToDOM footerAggregate, @$("div#invoice-body table tfoot")


make the autocomplete wider

make the autocomplete wider

--- app/assets/javascripts/admin/nomenclature_changes.js.coffee
+++ app/assets/javascripts/admin/nomenclature_changes.js.coffee
@@ -1,7 +1,7 @@
 $(document).ready ->
   defaultTaxonSelect2Options = {
     placeholder: 'Start typing scientific name'
-    width: '200px'
+    width: '300px'
     minimumInputLength: 3
     quietMillis: 500,
     initSelection: (element, callback) =>


refs #311 自分の表ページだけ編集ボタンの表示切替を表示する

refs #311 自分の表ページだけ編集ボタンの表示切替を表示する

--- app/assets/javascripts/components/sheets/sheet_list.js.cjsx
+++ app/assets/javascripts/components/sheets/sheet_list.js.cjsx
@@ -8,6 +8,7 @@
       renderAds: UserStore.renderAds()
       viewport: EnvironmentStore.findBy 'viewport'
       reverse: EnvironmentStore.findBy 'reverseSheet'
+      currentUser: {}

   onChangeViewPortAndReverse: =>
     @setState
@@ -15,7 +16,9 @@
       reverse: EnvironmentStore.findBy 'reverseSheet'

   onChangeCurrentUser: =>
-    @setState renderAds: UserStore.renderAds()
+    @setState
+      renderAds: UserStore.renderAds()
+      currentUser: UserStore.get()

   onChangeSheet: =>
     @setState sheets: SheetStore.get()
@@ -103,7 +106,7 @@
         /> if @state.renderAds
       }
       {
 if @state.renderAds}
-      <button onClick={@onClickSelect} className='uk-button uk-button-primary'>編集ボタン表示切替</button>
+      {<button onClick={@onClickSelect} className='uk-button uk-button-primary'>編集ボタン表示切替</button> if @state.currentUser.iidxid is @props.user.iidxid}

           {


Support turbolinks

Support turbolinks

--- app/assets/javascripts/glimpse/views/performance_bar.coffee
+++ app/assets/javascripts/glimpse/views/performance_bar.coffee
@@ -125,13 +125,13 @@
 updateStatus = (html) ->
   $('#serverstats').html html

-pjaxStart = null
-$(document).on 'pjax:start', (event) ->
-  pjaxStart = event.timeStamp
+ajaxStart = null
+$(document).on 'pjax:start, page:fetch', (event) ->
+  ajaxStart = event.timeStamp

-$(document).on 'pjax:end', (event, xhr) ->
-  pjaxEnd    = event.timeStamp
-  total      = pjaxEnd - pjaxStart
+$(document).on 'pjax:end, page:change', (event, xhr) ->
+  ajaxEnd    = event.timeStamp
+  total      = ajaxEnd - ajaxStart
   serverTime = if xhr then parseInt(xhr.getResponseHeader('X-Runtime')) else 0

   # Defer to include the timing of pjax hook evaluation
@@ -139,23 +139,27 @@
     now = new Date().getTime()
     bar = new PerformanceBar
       timing:
-        requestStart: pjaxStart,
-        responseEnd: pjaxEnd,
-        domLoading: pjaxEnd,
+        requestStart: ajaxStart,
+        responseEnd: ajaxEnd,
+        domLoading: ajaxEnd,
         domInteractive: now
       isLoaded: -> true
-      start: -> pjaxStart
+      start: -> ajaxStart
       end: -> now

     bar.render serverTime

-    console.log 'why u called'
-    span = $('<span>', {'class': 'tooltip', title: 'PJAX navigation time'})
+    if $.fn.pjax?
+      tech = 'PJAX'
+    else
+      tech = 'Turbolinks'
+
+    span = $('<span>', {'class': 'tooltip', title: "#{tech} navigation time"})
       .text(PerformanceBar.formatTime(total))
     span.tipsy({ gravity: 'n' })
     updateStatus span

-    pjaxStart = null
+    ajaxStart = null
   , 0

 $ ->


Change default version snippet to use XQuery 3.1

--- snippets/language-xquery.cson
+++ snippets/language-xquery.cson
@@ -52,10 +52,10 @@
     'body': 'module namespace ${1:ns} = "${2:http://www.example.com}";\n\n${3}'
   'xquery':
     'prefix': 'xquery'
-    'body': 'xquery version "${1:3.0}";\n\n${2}'
+    'body': 'xquery version "${1:3.1}";\n\n${2}'
   'tei':
     'prefix': 'tei'
     'body': 'declare namespace tei="http://www.tei-c.org/ns/1.0";\n${2}'
   'output':
     'prefix': 'output'
-    'body': 'declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";\ndeclare option output:method "${1:xml}";\ndeclare option output:media-type "${2:application/xml}";'+    'body': 'declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";\ndeclare option output:method "${1:xml}";\ndeclare option output:media-type "${2:application/xml}";'


#38 loading all data again

#38 loading all data again

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -105,33 +105,20 @@

   pullBatch : (batch) ->

-    if @alreadyRequested? or @dataLayerName == "color"
-      return
-
-    @alreadyRequested = true
+    # Loading a bunch of buckets
+
+    @batchCount++
+
     transmitBuffer = []
-    transmitBuffer.push(
-      0
-      0
-      5376
-      5376
-      1408
-    )
-
-    # Loading a bunch of buckets
-
-#    @batchCount++
-
-#    transmitBuffer = []
-#    for bucket in batch
-#      zoomStep = bucket[3]
-#      transmitBuffer.push(
-#        zoomStep
- #       if @fourBit and zoomStep == 0 then 1 else 0
-#        bucket[0] << (zoomStep + @cube.BUCKET_SIZE_P)
-  #      bucket[1] << (zoomStep + @cube.BUCKET_SIZE_P)
-   #     bucket[2] << (zoomStep + @cube.BUCKET_SIZE_P)
-    #  )
+    for bucket in batch
+      zoomStep = bucket[3]
+      transmitBuffer.push(
+        zoomStep
+        if @fourBit and zoomStep == 0 then 1 else 0
+        bucket[0] << (zoomStep + @cube.BUCKET_SIZE_P)
+        bucket[1] << (zoomStep + @cube.BUCKET_SIZE_P)
+        bucket[2] << (zoomStep + @cube.BUCKET_SIZE_P)
+      )

     # Measuring the time until response arrives to select appropriate preloading strategy
     roundTripBeginTime = new Date()


Don't forget the tab count on <Space>.

Don't forget the tab count on <Space>.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -283,6 +283,7 @@
       @updateVisibleMarkers @tabCount

     else if event.keyCode == keyCodes.space and @markerMatcher.shouldRotateHints event
+      @tabCount = previousTabCount
       HintCoordinator.sendMessage "rotateHints"

     else


authorizeをpromise化

authorizeをpromise化


profile setting btn goes to profile settings page

--- app/directives/group-page-toolbar/group-page-toolbar.coffee
+++ app/directives/group-page-toolbar/group-page-toolbar.coffee
@@ -10,6 +10,9 @@
       $scope.openSidenav = ->
         $rootScope.$broadcast('open sidenav')

+      $scope.openProfileSettings = ->
+        $location.path('/profile_settings')
+
       $scope.openFeedbackForm = ->
         $window.location.href = 'https://docs.google.com/forms/d/1-_zDQzdMmq_WndQn2bPUEW2DZQSvjl7nIJ6YkvUcp0I/viewform?usp=send_form';


Improve home page party elements

Improve home page party elements

Closes #176.

--- static/js/home.coffee
+++ static/js/home.coffee
@@ -24,7 +24,6 @@
                 data.activity_percentage = 100
             el = $(@template data)
             @$el.append el
-            el.tooltip()

 members_view = new MostActiveMembersView()

@@ -44,7 +43,8 @@
     add_all_items: =>
         max_score = ACTIVITY_BAR_CAP
         @$el.empty()
-        sorted = @collection.sortBy (x) -> x.get 'name'
+        sorted = @collection.sortBy (x) -> x.get 'member_count'
+        sorted.reverse()
         _.each sorted, (model) =>
             data = model.toJSON()
             data.thumbnail_url = model.get_logo_thumbnail 48, 48
@@ -59,5 +59,6 @@
                 data.activity_percentage = 100
             el = $(@template data)
             @$el.append el
+            el.find('.party-thumbnail img').tooltip()

 parties_view = new MostActivePartiesView()


Changed inviteteams test suit position in parallel-sets

--- scripts/test-instance/parallel-sets.coffee
+++ scripts/test-instance/parallel-sets.coffee
@@ -27,6 +27,7 @@
     { name: 'logout' }
     { name: 'unittests' }
     { name: 'account accountsettings' }
+    { name: 'teams inviteteams' }
   ]

   [
@@ -72,7 +73,6 @@
   [
     { name: 'teams teams' }
     { name: 'teams stack' }
-    { name: 'teams inviteteams' }
   ]

   [


Fixed disposing of tree view context menu items

--- lib/controllers/config-controller.coffee
+++ lib/controllers/config-controller.coffee
@@ -13,6 +13,7 @@
     @patterns = @projectController.getPatterns(@config.patterns);
     @lastTime = null;
     @disposables = new CompositeDisposable();
+    @ctxMenuDisposables = null;

     cssSelector = 'atom-workspace';

@@ -133,6 +134,7 @@
     @killRunningProcesses();
     @clearControllers();
     @disposables.dispose();
+    @ctxMenuDisposables?.dispose();

   clearControllers: ->
     for processController in @processControllers


Referral: implement decreaseLeftSpace function

--- workers/social/lib/social/models/referral/index.coffee
+++ workers/social/lib/social/models/referral/index.coffee
@@ -265,6 +265,18 @@
         return callback new KodingError "#{vm} is not found" unless vm
         callback null, vm

+  decreaseLeftSpace = (size, callback)->
+    isCampaingValid (err, status)->
+      return callback err if err
+      return callback null unless status
+
+      # change value with negative
+      size = -size if size > 0
+      JStorage = require '../storage'
+      JStorage.update
+        name: CAMPAIGN_NAME
+      , $inc : "content.diskSpaceLeftMB" : size
+      , callback
   do =>
     JAccount.on 'AccountRegistered', (me, referrerCode)->
       return console.error "Account is not defined in event" unless me


[data][tests] force-limit `data.site.shortName` length according to Chrome requirements

--- source/data/index.test.coffee
+++ source/data/index.test.coffee
@@ -41,7 +41,7 @@
       templates: t.String
   site: t.struct
     name: t.String
-    shortName: t.String
+    shortName: r.Maxlength(12)(t.String)
     version: t.String
     description: t.String
     homepage: r.Absoluteurl


Updated Coffeescript template for model

Updated Coffeescript template for model

--- lib/generators/templates/model.js.coffee
+++ lib/generators/templates/model.js.coffee
@@ -1,6 +1,12 @@
 # for more details see: http://emberjs.com/guides/models/defining-models/

 <%= application_name.camelize %>.<%= class_name %> = DS.Model.extend<%= "()" if attributes.length == 0 %>
-<% attributes.each_index do |idx| -%>
-  <%= attributes[idx][:name].camelize(:lower) %>: DS.attr('<%= attributes[idx][:type] %>')
+<% attributes.each do |attribute| -%>
+  <%= attribute[:name].camelize(:lower) %>: <%=
+  if %w(references belongs_to).member?(attribute[:type])
+    "DS.belongsTo '%s.%s'" % [application_name.camelize, attribute[:name].camelize]
+  else
+    "DS.attr '%s'" % attribute[:type]
+  end
+  %>
 <% end -%>


Remove some passthrough methods

Remove some passthrough methods

--- src/gameobject.coffee
+++ src/gameobject.coffee
@@ -29,33 +29,4 @@
   update: (delta) ->
     @children.update delta

-  ###
-  @description Add an object to the draw/update loop
-  @param {Shape} object
-  ###
-  add: (object) ->
-    @children.add object
-    console.log @children.length
-    object.parent = this
-
-  ###
-  @description Permanently remove an object from the draw/update loop
-  @param {Shape} object
-  ###
-  remove: (objectOrIndex) ->
-    @children.remove objectOrIndex
-
-  ###
-  @description Passthrough to @children Pool
-  @param {Number|Object} index Object/index to activate
-  ###
-  activate: (index) ->
-    @children.activate index
-
-  ###
-  @description Passthrough to @children Pool
-  ###
-  deactivate: (index) ->
-    @children.deactivate index
-
 module.exports = GameObject


Show window when switching accounts if it isnt visible

--- internal_packages/account-sidebar/lib/sidebar-commands.coffee
+++ internal_packages/account-sidebar/lib/sidebar-commands.coffee
@@ -5,15 +5,22 @@

 class SidebarCommands

+  @_focusAccounts: (accounts) ->
+    SidebarActions.focusAccounts(accounts)
+    NylasEnv.show() unless NylasEnv.isVisible()
+
   @_registerCommands: ->
     commands = {}
+
     allKey = "application:select-account-0"
-    commands[allKey] = => SidebarActions.focusAccounts(AccountStore.accounts())
+    commands[allKey] = @_focusAccounts.bind(@, AccountStore.accounts())
+
     [1..8].forEach (index) =>
       account = AccountStore.accounts()[index - 1]
       return unless account
       key = "application:select-account-#{index}"
-      commands[key] = => SidebarActions.focusAccounts([account])
+      commands[key] = @_focusAccounts.bind(@, [account])
+
     NylasEnv.commands.add('body', commands)

   @_registerMenuItems: ->


Use atom.commands

Use atom.commands

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -16,8 +16,10 @@
       @div class: 'message', outlet: 'message'

   initialize: ->
-    atom.workspaceView.command "package-generator:generate-package", => @attach('package')
-    atom.workspaceView.command "package-generator:generate-syntax-theme", => @attach('theme')
+    atom.commands.add 'atom-workspace',
+      'package-generator:generate-package': => @attach('package')
+      'package-generator:generate-syntax-theme': => @attach('theme')
+
     @miniEditor.hiddenInput.on 'focusout', => @detach()
     @on 'core:confirm', => @confirm()
     @on 'core:cancel', => @detach()


send desired part size to start multipart request


Added whitespace between methods

Added whitespace between methods

--- js-library/src/js/util/modal.coffee
+++ js-library/src/js/util/modal.coffee
@@ -27,11 +27,13 @@
   hide: ->
     unbindClick()
     iFrame.fadeOut 'fast'
+
     Factlink.trigger 'modalClosed'

   show: ->
     bindClick()
     iFrame.fadeIn('fast')
+
   highlightNewFactlink: (fact, id, opinions) ->
     fct = Factlink.selectRanges(Factlink.search(fact), id, opinions)

@@ -48,7 +50,9 @@
     created_message.render()

     fct
+
   stopHighlightingFactlink: (id) ->
     $("span.factlink[data-factid=#{id}]").each (i, val) ->
       $(val).contents().unwrap()
+
   trigger: (e) -> Factlink.trigger(e)


Only show 1Password button for iOS

Only show 1Password button for iOS

--- packages/rocketchat-ui-login/login/form.coffee
+++ packages/rocketchat-ui-login/login/form.coffee
@@ -68,7 +68,7 @@
 		return RocketChat.settings.get('Accounts_PasswordPlaceholder') or t("Password")

 	hasOnePassword: ->
-		return OnePassword?.findLoginForUrl?
+		return OnePassword?.findLoginForUrl? && device?.platform?.toLocaleLowerCase() is 'ios'

 Template.loginForm.events
 	'submit #login-card': (event, instance) ->


set lru cache to 10 hours

set lru cache to 10 hours

--- services/spelling/app/coffee/ASpell.coffee
+++ services/spelling/app/coffee/ASpell.coffee
@@ -6,8 +6,9 @@
 settings = require("settings-sharelatex")
 Path = require("path")

-cache = LRU(10000)
 OneMinute = 60 * 1000
+opts = {max:10000, maxAge: OneMinute * 60 * 10}
+cache = LRU(opts)

 cacheFsPath = Path.resolve(settings.cacheDir, "spell.cache")
 cacheFsPathTmp = cacheFsPath + ".tmp"


Request TIFFs as PNGs since TIFF browser support is lacking

--- src/imgflo-url.coffee
+++ src/imgflo-url.coffee
@@ -39,6 +39,7 @@

   format ?= extension
   format = format.toLowerCase() if format? and typeof format is 'string'
+  format = 'png' if format? and format is 'tif'
   graph = "#{graph}.#{format}" if format?

   query = "?#{qs.stringify(params)}"


Upgrade remote list view

Upgrade remote list view

--- lib/views/remote-list-view.coffee
+++ lib/views/remote-list-view.coffee
@@ -1,4 +1,5 @@
-{$$, BufferedProcess, SelectListView} = require 'atom'
+{BufferedProcess} = require 'atom'
+{$$, SelectListView} = require 'atom-space-pen-views'

 git = require '../git'
 OutputView = require './output-view'
@@ -8,7 +9,7 @@
 class ListView extends SelectListView
   initialize: (@data, @mode, @setUpstream=false, @tag='') ->
     super
-    @addClass 'overlay from-top'
+    @show()
     @parseData()

   parseData: ->
@@ -20,10 +21,18 @@
       @confirmed remotes[0]
     else
       @setItems remotes
-      atom.workspaceView.append this
       @focusFilterEditor()

   getFilterKey: -> 'name'
+
+  show: ->
+    @panel ?= atom.workspace.addModalPanel(item: this)
+    @panel.show()
+
+  cancelled: -> @hide()
+
+  hide: ->
+    @panel?.hide()

   viewForItem: ({name}) ->
     $$ ->


feat(navbar): inject up button from state

--- modules/scripts/directives/navbar.coffee
+++ modules/scripts/directives/navbar.coffee
@@ -17,21 +17,21 @@
     bpNavbarTitle: '@'
   compile: (elem, attrs, transcludeFn) ->

+    getTitleFromState = (state) ->
+      state.data?.title or
+      state.name.charAt(0).toUpperCase() + state.name.slice(1)
+
     ios = if bpConfig.platform is 'android' then no else yes

     (scope, element, attrs) ->
+      state = $state.current

       element.attr
         role: 'navigation'

       transcludeFn scope, (clone) ->
         unless attrs.bpNavbarTitle?
-          attrs.bpNavbarTitle =
-            $state.current.data?.title or
-            (do ->
-            name = $state.current.name
-            name.charAt(0).toUpperCase() + name.slice(1)
-            )
+          attrs.bpNavbarTitle = getTitleFromState state

         $title = $compile("
           <bp-navbar-title role='heading'>
@@ -39,6 +39,15 @@
           </bp-navbar-title>")(scope)

         $actions = clone.filter 'bp-action'
+
+        if state.data?.up? and not attrs.bpNavbarNoUp?
+          upState = $state.get state.data.up
+          upTitle = getTitleFromState upState
+          $up = $compile("
+            <bp-action class='bp-button-back' bp-sref='#{upState.name}'>
+              #{upTitle}
+            </bp-action>") scope
+          $actions = $up.add $actions

         if $actions.length <= 2

@@ -54,7 +63,7 @@
             unless scope.navbarTitle then $timeout ->
               difference = $scndAction.outerWidth() - $frstAction.outerWidth()
               if difference isnt 0 and $frstAction.length
-                $spacer = $("
+                $spacer = angular.element("
                   <div style='
                     -webkit-box-flex:10;
                     max-width:#{Math.abs(difference)}px


Handle uncategorized AST nodes.

Handle uncategorized AST nodes.

--- src/javascript.coffee
+++ src/javascript.coffee
@@ -247,7 +247,7 @@
             return @opts.categories.command.color
         else
           category = @lookupCategory node
-          return category.color
+          return category?.color or 'command'

     getSocketLevel: (node) -> helper.ANY_DROP


mixpanel: remove ?

mixpanel: remove ?

--- client/Main/mixpanel.coffee
+++ client/Main/mixpanel.coffee
@@ -1,7 +1,6 @@
 if KD.config.logToExternal then do ->
   KD.getSingleton('mainController').on "AccountChanged", (account) ->
-    return  unless KD.isLoggedIn()
-    return  unless account
+    return  unless KD.isLoggedIn() or account or mixpanel

     account.fetchEmail (err, email)->
       console.log err  if err
@@ -11,8 +10,8 @@
       {firstName, lastName, nickname} = profile

       # register user to mixpanel
-      mixpanel?.identify nickname
-      mixpanel?.people.set
+      mixpanel.identify nickname
+      mixpanel.people.set
         "$username"     : nickname
         "$first_name"   : firstName
         "$last_name"    : lastName
@@ -20,10 +19,13 @@
         "$created"      : createdAt
         "Status"        : type
         "Randomizer"    : KD.utils.getRandomNumber 4
-      mixpanel?.name_tag "#{nickname}.kd.io"
+      mixpanel.name_tag "#{nickname}.kd.io"

 # Access control wrapper around mixpanel object.
 KD.mixpanel = (args...)->
-  mixpanel?.track args...  if KD.config.logToExternal
+  return  unless mixpanel
+  mixpanel.track args...  if KD.config.logToExternal

-KD.mixpanel.alias = (args...)-> mixpanel?.alias args...
+KD.mixpanel.alias = (args...)->
+  return  unless mixpanel
+  mixpanel.alias args...


klient: Moved bindKlientEvents call to handle more use cases.

A first bindKlientEvents is called if the machine is already running,
and no machine state modal needs to be created. If however, the machine
is not loaded (even if it's on), a machineStateModal is loaded which
handles all of that. When it's done, it calls handleIDEBecameReady, and
as part of that bindKlientEvents.

This way we will resubscribe anytime we disconnect.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -134,7 +134,6 @@

     @createInitialView withFakeViews
     @bindCollapseEvents()
-    @bindKlientEvents()

     {@finderPane, @settingsPane} = @workspace.panel.getPaneByName 'filesPane'

@@ -175,10 +174,13 @@
    * Listen for any `clientSubscribe` events that we care about.
    * Currently just `openFiles`, which triggers the IDE to open
    * a new file.
+   *
+   * @param {Machine} machine
   ###
-  bindKlientEvents: ->
-
-    @once 'MachineDidMount', (machine) =>
+  bindKlientEvents: (machine) ->
+
+    kite = machine.getBaseKite()
+    kite.ready =>
       kem = new KlientEventManager {}, machine
       kem.on 'openFiles', ({ eventName, files }) =>

@@ -491,6 +493,7 @@
           @mountMachine machineItem
           baseMachineKite.fetchTerminalSessions()
           @prepareCollaboration()
+          @bindKlientEvents machineItem

         else
           unless @machineStateModal
@@ -981,6 +984,8 @@
     else
       finderController.reset()

+    # when MachineStateModal calls this func, we need to rebind Klient.
+    @bindKlientEvents machine
     machine.getBaseKite().fetchTerminalSessions()

     @fetchSnapshot (snapshot) =>


Setup fake activity notification for newly registered user

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -2,6 +2,8 @@
 _                             = require 'lodash'
 kd                            = require 'kd'
 $                             = require 'jquery'
+kookies                       = require 'kookies'
+
 KDBlockingModalView           = kd.BlockingModalView
 KDCustomHTMLView              = kd.CustomHTMLView
 KDModalView                   = kd.ModalView
@@ -438,8 +440,7 @@
             }

             if state is NotInitialized
-              @machineStateModal.once 'MachineTurnOnStarted', =>
-                kd.getSingleton('mainView').activitySidebar.initiateFakeCounter()
+              @setupFakeActivityNotification()

           @once 'IDEReady', => @prepareCollaboration()

@@ -1329,3 +1330,12 @@
             @quit()

     @showModal options
+
+
+  setupFakeActivityNotification: ->
+
+    return  unless kookies.get('newRegister') is 'true'
+
+    @machineStateModal?.once 'MachineTurnOnStarted', ->
+      kookies.expire 'newRegister', path: '/'
+      kd.getSingleton('mainView').activitySidebar.initiateFakeCounter()


Add isKoding check

Add isKoding check

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -1674,7 +1674,10 @@
           style    : 'solid light-gray medium'
           title    : 'OK'
           callback : =>
-            reactor.dispatch actionTypes.SHARED_VM_INVITATION_REJECTED, @mountedMachine._id
+
+            unless isKoding()
+              reactor.dispatch actionTypes.SHARED_VM_INVITATION_REJECTED, @mountedMachine._id
+
             @modal.destroy()
             @quit()


Add JSDoc to "mergeSplitView" method.

Add JSDoc to "mergeSplitView" method.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -321,8 +321,10 @@
       view.holderView[closeHandleMethod]()
       view.ensureSplitHandlers()

-
-  mergeSplitView: ->
+  ###*
+   * @param {boolean=} quite  Don't dispatch the `SplitViewWasMerged` event if it is `yes`
+  ###
+  mergeSplitView: (quite = no) ->

     tabView     = @activeTabView
     panel       = tabView.parent.parent
@@ -1666,6 +1668,8 @@
     @forEachSubViewInIDEViews_ (pane) =>
       @removePaneFromTabView pane  if pane.isInitial

+    @mergeSplitView()
+

   setTargetTabView: (tabView) ->


Small optimization to prevent text segment jitter in the common case.

--- client/lib/annotator.coffee
+++ client/lib/annotator.coffee
@@ -206,9 +206,21 @@
       padding: 0
       margin: 0

-    # To make code simpler, we apply padding all around the text segment DOM element,
-    # but then we have to counteract text content position change by set negative
-    # margin. With this, text content stays in place, but DOM element gets a
+    # Optimization if position is to the right and down of the segment. We do this
+    # because modifying both margin and padding slightly jitters text segment around
+    # because of rounding to pixel coordinates (text is scaled and rotated so margin
+    # and padding values do not fall nicely onto pixel coordinates).
+    if segment.boundingBox.left <= position.left and segment.boundingBox.top <= position.top
+      $dom.css
+        paddingRight: padding
+        paddingBottom: padding
+      return
+
+    # Otherwise we apply padding all around the text segment DOM element and do not
+    # really care where the mouse position is, we have to change both margin and
+    # padding anyway.
+    # We counteract text content position change introduced by padding by setting
+    # negative margin. With this, text content stays in place, but DOM element gets a
     # necessary padding.
     $dom.css
       marginLeft: -left


Made notification shown only for 3 seconds (instead of 7).

--- client/notifications.coffee
+++ client/notifications.coffee
@@ -44,7 +44,7 @@
       Notify.documents.remove @data._id
     @_timeout = null
   ,
-    7000 # ms
+    3000 # ms

   # Pause the timeout while user is hovering over the notification
   $notification.on 'mouseenter.notification', (e) =>


made sure to not store empty colour configurations

--- client/prefs-colours.coffee
+++ client/prefs-colours.coffee
@@ -159,7 +159,7 @@
   v = {}
   for g, i in G
     m = model[i]
-    for p in props when m[p] != (scheme[g.t]?[p] || '') then (v[g.t] ?= {})[p] = m[p]
+    for p in props when (m[p] || '') != (scheme[g.t]?[p] || '') then (v[g.t] ?= {})[p] = m[p]
   v

 @save = -> prefs.hi getModelAsObject(); return


Introducing environments. Not yet ready.

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -501,8 +501,7 @@

 		# Max Age
 		# The caching time limit that is sent to the client
-		# Defaults to false if not on production, otherwise one day
-		maxAge: null
+		maxAge: false

 		# -----------------------------
@@ -526,10 +525,6 @@

 		# -----------------------------
 		# Other
-
-		# Environment
-		# Whether or not we are in production or development
-		env: process.env.NODE_ENV or 'development'

 		# Node Path
 		# The location of our node executable
@@ -554,8 +549,7 @@

 		# Check Version
 		# Whether or not to check for newer versions of DocPad
-		# Defaults to true if not on production, otherwise false
-		checkVersion: null
+		checkVersion: true

 		# Collections
 		# A hash of functions that create collections
@@ -564,6 +558,21 @@
 		# Events
 		# A hash of event handlers
 		events: null  # {}
+
+
+		# -----------------------------
+		# Environment Configuration
+
+		# Environment
+		# Whether or not we are in production or development
+		env: process.env.NODE_ENV or 'development'
+
+		# Environments
+		# Environment specific configuration to over-ride the global configuration
+		environments:
+			production:
+				checkVersion: false
+				maxAge: 86400000

 	# Get Environment


Remove a stray console.log.

Remove a stray console.log.

--- src/client-compile.coffee
+++ src/client-compile.coffee
@@ -258,9 +258,7 @@
         if @options.verbose
             @options.logCb.apply @, arguments

-
     defaultLog: (verb, message) ->
-        console.log.apply @, arguments
         verb = ' ' + verb while verb.length < 10
         console.log "  \u001b[90m #{verb} : \u001b[0m\u001b[36m%s\u001b[0m", message


CLI refactoring

CLI refactoring

--- src/CLI.coffee
+++ src/CLI.coffee
@@ -1,29 +1,71 @@
 # command-line interface for restatic
-
-Environment = require("./Environment.coffee")
-SiteParser = require("./SiteParser.coffee")

 ansi = require("ansi")
 cursor = ansi(process.stdout)

-SiteParser = new SiteParser()
-Environment = new Environment()
+SiteParser = require("./SiteParser.coffee")
+Environment = require("./Environment.coffee")

-config = Environment.prepare(process.argv.splice(2))
+siteParser = new SiteParser()
+environment = new Environment()

-unless config is false
-    Extractor = require(config.extractor)
-    Extractor = new Extractor()
-    switch config.mode
-        when "fetch"
-            cursor.green().write("Restatic started fetching data from spreadsheet defined in ").blue().write(config.source + "restatic.json").reset().write(" to ").blue().write(config.target).write("\n").reset()
-            Extractor.extract config.apiKey, config.delimiter, config.target, config.excludable, (data, target, excludable) ->
-                Environment.storeResult data, config.source
-        when "process"
-            cursor.green().write("Restatic started parsing html files from ").blue().write(config.source).reset().write(" to ").blue().write(config.target).green().write(" using ").blue().write(config.extractorName).write("\n").reset()
-            Environment.loadData config.source, config.target, config.excludable, SiteParser.parse
-        else
-            cursor.green().write("Restatic started parsing html files from ").blue().write(config.source).reset().write(" to ").blue().write(config.target).write("\n").reset()
-            Extractor.extract config.apiKey, config.delimiter, config.target, config.excludable, SiteParser.parse
-else
-    cursor.green().write("Usage: parse google docs spreadsheet content using 'restatic source_folder target_folder' or 'restatic -d' if you want to generate site from actual location to folder _site '\n").reset()    if config.finished is false+options = process.argv.splice(2)
+config = environment.prepare(options)
+
+# helper UI
+printFetchInfo = ->
+    cursor
+        .green().write("Restatic started fetching data from spreadsheet defined in ")
+        .blue().write(config.source + "restatic.json")
+        .reset().write(" to ")
+        .blue().write(config.target)
+        .reset().write("\n")
+
+printProcessInfo = ->
+    cursor
+        .green().write("Restatic started parsing html files from ")
+        .blue().write(config.source)
+        .reset().write(" to ")
+        .blue().write(config.target)
+        .green().write(" using ")
+        .blue().write(config.extractorName)
+        .reset().write("\n")
+
+printDefaultInfo = ->
+    cursor
+        .green().write("Restatic started parsing html files from ")
+        .blue().write(config.source)
+        .reset().write(" to ")
+        .blue().write(config.target)
+        .reset().write("\n")
+
+printUsage = ->
+    cursor
+        .green().write("Usage: ")
+        .write("parse google docs spreadsheet content using 'restatic source_folder target_folder'")
+        .write(" or ")
+        .write("'restatic -d' if you want to generate site from actual location to folder _site")
+        .reset().write("\n")
+
+##########################################################################################
+# main logic
+
+if not config
+    printUsage()
+    process.exit 1
+
+# TODO: here should be robust error reporting
+Extractor = require(config.extractor)
+extractor = new Extractor()
+
+switch config.mode
+    when "fetch"
+        printFetchInfo()
+        extractor.extract config.apiKey, config.delimiter, config.target, config.excludable, (data, target, excludable) ->
+            environment.storeResult data, config.source
+    when "process"
+        printProcessInfo()
+        environment.loadData config.source, config.target, config.excludable, siteParser.parse
+    else
+        printDefaultInfo()
+        extractor.extract config.apiKey, config.delimiter, config.target, config.excludable, siteParser.parse


Fixed CPU timing (empty R/W cycles). More instructions.

--- src/CPU.coffee
+++ src/CPU.coffee
@@ -82,7 +82,8 @@

     resetVariables: ->
         @cycle = 0
-        @pageCrossed = false
+        @emptyReadCycle = no
+        @emptyWriteCycle = no
         @requestedInterrupt = null

     resetMemory: ->
@@ -98,6 +99,8 @@
     step: ->
         @resolveInterrupt()
         operation = @readOperation()
+        @emptyReadCycle = operation.emptyReadCycle
+        @emptyWriteCycle = operation.emptyWriteCycle
         addressingMode = operation.addressingMode
         instruction = operation.instruction
         address = @computeAddress addressingMode
@@ -141,19 +144,17 @@
         @instructionsTable[instruction](address)

     readByte: (address) ->
-        @tick()
+        @resolveReadCycles()
         @memory.read address

     readWord: (address) ->
-        @resolvePageCrossed()
         (@readByte address + 1) << 8 | @readByte address

     writeByte: (address, value) ->
-        @tick()
+        @resolveWriteCycles()
         @memory.write address, value

     writeWord: (address, value) ->
-        @resolvePageCrossed()
         @writeByte address, value & 0xFF
         @writeByte address + 1, value >> 8

@@ -173,10 +174,17 @@
         result = @popByte()
         result |= @popByte() << 8

-    resolvePageCrossed: ->
-        if @pageCrossed
-            tick()
-            @pageCrossed = false
+    resolveReadCycles: ->
+        tick()
+        if @emptyReadCycle
+            @emptyReadCycle = no
+            @tick()
+
+    resolveWriteCycles: ->
+        tick()
+        if @emptyWriteCycle
+            @emptyWriteCycle = no
+            @tick()

     getStatus: ->
         status = @carryFlag
@@ -261,11 +269,11 @@
         @addressingModesTable[addressingMode] = computation

     getIndexedAddressByte: (base, offset) ->
-        @tick()
+        @emptyReadCycle = yes
         (base + offset) & 0xFF

     getIndexedAddressWord : (base, offset) ->
-        @pageCrossed = base & 0xFF00 != (base + offset) & 0xFF00
+        @emptyReadCycle = yes if base & 0xFF00 != (base + offset) & 0xFF00 # Page crossed
         (base + offset) & 0xFFFF

     getSignedByte: (value) ->
@@ -424,115 +432,114 @@
         @computeZeroFlag result
         @computeNegativeFlag result

-    emptyStackReadCycle: ->
-        tick()
-
     initOperationsTable: ->
         @operationsTable = []

-        @registerOperation 0x69, Instruction.ADC, AddressingMode.Immediate
-        @registerOperation 0x65, Instruction.ADC, AddressingMode.ZeroPage
-        @registerOperation 0x75, Instruction.ADC, AddressingMode.IndexedXZeroPage
-        @registerOperation 0x6D, Instruction.ADC, AddressingMode.Absolute
-        @registerOperation 0x7D, Instruction.ADC, AddressingMode.IndexedXAbsolute
-        @registerOperation 0x79, Instruction.ADC, AddressingMode.IndexedYAbsolute
-        @registerOperation 0x61, Instruction.ADC, AddressingMode.IndexedXIndirect
-        @registerOperation 0x71, Instruction.ADC, AddressingMode.IndirectIndexedY
-
-        @registerOperation 0x29, Instruction.AND, AddressingMode.Immediate
-        @registerOperation 0x25, Instruction.AND, AddressingMode.ZeroPage
-        @registerOperation 0x35, Instruction.AND, AddressingMode.IndexedXZeroPage
-        @registerOperation 0x2D, Instruction.AND, AddressingMode.Absolute
-        @registerOperation 0x3D, Instruction.AND, AddressingMode.IndexedXAbsolute
-        @registerOperation 0x39, Instruction.AND, AddressingMode.IndexedYAbsolute
-        @registerOperation 0x21, Instruction.AND, AddressingMode.IndexedXIndirect
-        @registerOperation 0x31, Instruction.AND, AddressingMode.IndirectIndexedY
+        @registerOperation 0x69, Instruction.ADC, AddressingMode.Immediate,        no,  no
+        @registerOperation 0x65, Instruction.ADC, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0x75, Instruction.ADC, AddressingMode.IndexedXZeroPage, no,  no
+        @registerOperation 0x6D, Instruction.ADC, AddressingMode.Absolute,         no,  no
+        @registerOperation 0x7D, Instruction.ADC, AddressingMode.IndexedXAbsolute, no,  no
+        @registerOperation 0x79, Instruction.ADC, AddressingMode.IndexedYAbsolute, no,  no
+        @registerOperation 0x61, Instruction.ADC, AddressingMode.IndexedXIndirect, no,  no
+        @registerOperation 0x71, Instruction.ADC, AddressingMode.IndirectIndexedY, no,  no
+
+        @registerOperation 0x29, Instruction.AND, AddressingMode.Immediate,        no,  no
+        @registerOperation 0x25, Instruction.AND, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0x35, Instruction.AND, AddressingMode.IndexedXZeroPage, no,  no
+        @registerOperation 0x2D, Instruction.AND, AddressingMode.Absolute,         no,  no
+        @registerOperation 0x3D, Instruction.AND, AddressingMode.IndexedXAbsolute, no,  no
+        @registerOperation 0x39, Instruction.AND, AddressingMode.IndexedYAbsolute, no,  no
+        @registerOperation 0x21, Instruction.AND, AddressingMode.IndexedXIndirect, no,  no
+        @registerOperation 0x31, Instruction.AND, AddressingMode.IndirectIndexedY, no,  no

-        @registerOperation 0x0A, Instruction.ASL, AddressingMode.Accumulator
-        @registerOperation 0x06, Instruction.ASL, AddressingMode.ZeroPage
-        @registerOperation 0x16, Instruction.ASL, AddressingMode.IndexedXZeroPage
-        @registerOperation 0x0E, Instruction.ASL, AddressingMode.Absolute
-        @registerOperation 0x1E, Instruction.ASL, AddressingMode.IndexedXAbsolute
-
-        @registerOperation 0x90, Instruction.BCC, AddressingMode.Relative
-
-        @registerOperation 0xB0, Instruction.BCS, AddressingMode.Relative
-
-        @registerOperation 0xB0, Instruction.BEQ, AddressingMode.Relative
-
-        @registerOperation 0x24, Instruction.BIT, AddressingMode.ZeroPage
-        @registerOperation 0x2C, Instruction.BIT, AddressingMode.Absolute
-
-        @registerOperation 0x30, Instruction.BMI, AddressingMode.Relative
-
-        @registerOperation 0xD0, Instruction.BNE, AddressingMode.Relative
-
-        @registerOperation 0x10, Instruction.BPL, AddressingMode.Relative
-
-        @registerOperation 0x00, Instruction.BRK, AddressingMode.Implied
-
-        @registerOperation 0x50, Instruction.BVC, AddressingMode.Relative
-
-        @registerOperation 0x70, Instruction.BVS, AddressingMode.Relative
-
-        @registerOperation 0x18, Instruction.CLC, AddressingMode.Implied
-
-        @registerOperation 0xD8, Instruction.CLD, AddressingMode.Implied
-
-        @registerOperation 0x58, Instruction.CLI, AddressingMode.Implied
-
-        @registerOperation 0xB8, Instruction.CLV, AddressingMode.Implied
-
-        @registerOperation 0xC9, Instruction.CMP, AddressingMode.Immediate
-        @registerOperation 0xC5, Instruction.CMP, AddressingMode.ZeroPage
-        @registerOperation 0xD5, Instruction.CMP, AddressingMode.IndexedXZeroPage
-        @registerOperation 0xCD, Instruction.CMP, AddressingMode.Absolute
-        @registerOperation 0xDD, Instruction.CMP, AddressingMode.IndexedXAbsolute
-        @registerOperation 0xD9, Instruction.CMP, AddressingMode.IndexedYAbsolute
-        @registerOperation 0xC1, Instruction.CMP, AddressingMode.IndexedXIndirect
-        @registerOperation 0xD1, Instruction.CMP, AddressingMode.IndirectIndexedY
-
-        @registerOperation 0xE0, Instruction.CPX, AddressingMode.Immediate
-        @registerOperation 0xE4, Instruction.CPX, AddressingMode.ZeroPage
-        @registerOperation 0xEC, Instruction.CPX, AddressingMode.Absolute
-
-        @registerOperation 0xC0, Instruction.CPX, AddressingMode.Immediate
-        @registerOperation 0xC4, Instruction.CPX, AddressingMode.ZeroPage
-        @registerOperation 0xCC, Instruction.CPX, AddressingMode.Absolute
-
-        @registerOperation 0xC6, Instruction.DEC, AddressingMode.ZeroPage
-        @registerOperation 0xD6, Instruction.DEC, AddressingMode.IndexedXZeroPage
-        @registerOperation 0xCE, Instruction.DEC, AddressingMode.Absolute
-        @registerOperation 0xDE, Instruction.DEC, AddressingMode.IndexedXAbsolute
-
-        @registerOperation 0xCA, Instruction.DEX, AddressingMode.Implied
-
-        @registerOperation 0x88, Instruction.DEY, AddressingMode.Implied
-
-        @registerOperation 0x49, Instruction.EOR, AddressingMode.Immediate
-        @registerOperation 0x45, Instruction.EOR, AddressingMode.ZeroPage
-        @registerOperation 0x55, Instruction.EOR, AddressingMode.IndexedXZeroPage
-        @registerOperation 0x4D, Instruction.EOR, AddressingMode.Absolute
-        @registerOperation 0x5D, Instruction.EOR, AddressingMode.IndexedXAbsolute
-        @registerOperation 0x59, Instruction.EOR, AddressingMode.IndexedYAbsolute
-        @registerOperation 0x41, Instruction.EOR, AddressingMode.IndexedXIndirect
-        @registerOperation 0x51, Instruction.EOR, AddressingMode.IndirectIndexedY
-
-        @registerOperation 0xC6, Instruction.INC, AddressingMode.ZeroPage
-        @registerOperation 0xD6, Instruction.INC, AddressingMode.IndexedXZeroPage
-        @registerOperation 0xCE, Instruction.INC, AddressingMode.Absolute
-        @registerOperation 0xDE, Instruction.INC, AddressingMode.IndexedXAbsolute
-
-        @registerOperation 0xCA, Instruction.INX, AddressingMode.Implied
-
-        @registerOperation 0x88, Instruction.INY, AddressingMode.Implied
-
-        @registerOperation 0x4C, Instruction.INY, AddressingMode.Absolute
-        @registerOperation 0x6C, Instruction.INY, AddressingMode.Indirect
-
-    registerOperation: (operationCode, instruction, addressingMode) ->
+        @registerOperation 0x0A, Instruction.ASL, AddressingMode.Accumulator,      no,  no
+        @registerOperation 0x06, Instruction.ASL, AddressingMode.ZeroPage,         no,  yes
+        @registerOperation 0x16, Instruction.ASL, AddressingMode.IndexedXZeroPage, no,  yes
+        @registerOperation 0x0E, Instruction.ASL, AddressingMode.Absolute,         no,  yes
+        @registerOperation 0x1E, Instruction.ASL, AddressingMode.IndexedXAbsolute, yes, yes
+
+        @registerOperation 0x90, Instruction.BCC, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0xB0, Instruction.BCS, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0xB0, Instruction.BEQ, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0x24, Instruction.BIT, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0x2C, Instruction.BIT, AddressingMode.Absolute,         no,  no
+
+        @registerOperation 0x30, Instruction.BMI, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0xD0, Instruction.BNE, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0x10, Instruction.BPL, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0x00, Instruction.BRK, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0x50, Instruction.BVC, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0x70, Instruction.BVS, AddressingMode.Relative,         no,  no
+
+        @registerOperation 0x18, Instruction.CLC, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0xD8, Instruction.CLD, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0x58, Instruction.CLI, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0xB8, Instruction.CLV, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0xC9, Instruction.CMP, AddressingMode.Immediate,        no,  no
+        @registerOperation 0xC5, Instruction.CMP, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0xD5, Instruction.CMP, AddressingMode.IndexedXZeroPage, no,  no
+        @registerOperation 0xCD, Instruction.CMP, AddressingMode.Absolute,         no,  no
+        @registerOperation 0xDD, Instruction.CMP, AddressingMode.IndexedXAbsolute, no,  no
+        @registerOperation 0xD9, Instruction.CMP, AddressingMode.IndexedYAbsolute, no,  no
+        @registerOperation 0xC1, Instruction.CMP, AddressingMode.IndexedXIndirect, no,  no
+        @registerOperation 0xD1, Instruction.CMP, AddressingMode.IndirectIndexedY, no,  no
+
+        @registerOperation 0xE0, Instruction.CPX, AddressingMode.Immediate,        no,  no
+        @registerOperation 0xE4, Instruction.CPX, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0xEC, Instruction.CPX, AddressingMode.Absolute,         no,  no
+
+        @registerOperation 0xC0, Instruction.CPX, AddressingMode.Immediate,        no,  no
+        @registerOperation 0xC4, Instruction.CPX, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0xCC, Instruction.CPX, AddressingMode.Absolute,         no,  no
+
+        @registerOperation 0xC6, Instruction.DEC, AddressingMode.ZeroPage,         no,  yes
+        @registerOperation 0xD6, Instruction.DEC, AddressingMode.IndexedXZeroPage, no,  yes
+        @registerOperation 0xCE, Instruction.DEC, AddressingMode.Absolute,         no,  yes
+        @registerOperation 0xDE, Instruction.DEC, AddressingMode.IndexedXAbsolute, yes, yes
+
+        @registerOperation 0xCA, Instruction.DEX, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0x88, Instruction.DEY, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0x49, Instruction.EOR, AddressingMode.Immediate,        no,  no
+        @registerOperation 0x45, Instruction.EOR, AddressingMode.ZeroPage,         no,  no
+        @registerOperation 0x55, Instruction.EOR, AddressingMode.IndexedXZeroPage, no,  no
+        @registerOperation 0x4D, Instruction.EOR, AddressingMode.Absolute,         no,  no
+        @registerOperation 0x5D, Instruction.EOR, AddressingMode.IndexedXAbsolute, no,  no
+        @registerOperation 0x59, Instruction.EOR, AddressingMode.IndexedYAbsolute, no,  no
+        @registerOperation 0x41, Instruction.EOR, AddressingMode.IndexedXIndirect, no,  no
+        @registerOperation 0x51, Instruction.EOR, AddressingMode.IndirectIndexedY, no,  no
+
+        @registerOperation 0xC6, Instruction.INC, AddressingMode.ZeroPage,         no,  yes
+        @registerOperation 0xD6, Instruction.INC, AddressingMode.IndexedXZeroPage, no,  yes
+        @registerOperation 0xCE, Instruction.INC, AddressingMode.Absolute,         no,  yes
+        @registerOperation 0xDE, Instruction.INC, AddressingMode.IndexedXAbsolute, yes, yes
+
+        @registerOperation 0xCA, Instruction.INX, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0x88, Instruction.INY, AddressingMode.Implied,          no,  no
+
+        @registerOperation 0x4C, Instruction.INY, AddressingMode.Absolute,         no,  no
+        @registerOperation 0x6C, Instruction.INY, AddressingMode.Indirect,         no,  no
+
+    registerOperation: (operationCode, instruction, addressingMode, emptyReadCycle, emptyWriteCycle) ->
         @operationsTable[operationCode] =
             instruction: instruction
             addressingMode: addressingMode
+            emptyReadCycle: emptyReadCycle
+            emptyWriteCycle: emptyWriteCycle

 module.exports = CPU


facade.createModel()

facade.createModel()

--- src/lib/facade.coffee
+++ src/lib/facade.coffee
@@ -84,6 +84,21 @@
     ###
     getRepository: (name) ->
         @require("#{name}-repository")
+
+
+
+    ###*
+    create an instance of the given modelName using obj
+
+    @method createModel
+    @param {String} modelName
+    @param {Object} obj
+    @param {Object} [options]
+    @return {BaseModel}
+    ###
+    createModel: (modelName, obj, options) ->
+
+        @createFactory(modelName).createFromObject(obj, options)

     ###*


Fix bug where clicking on picker does not close it

--- src/lib/picker.coffee
+++ src/lib/picker.coffee
@@ -20,6 +20,7 @@
       _.defer( =>
         dom(@container).toggleClass('ql-expanded')
       )
+      return false
     )
     dom(@select).on('change', =>
       if @select.selectedIndex > -1


Fix CoffeeScript example. Use readline-sync. Old ffi thing does not work.

--- coffee/node_readline.coffee
+++ coffee/node_readline.coffee
@@ -1,39 +1,4 @@
-# IMPORTANT: choose one
-RL_LIB = "libreadline"  # NOTE: libreadline is GPL
-#RL_LIB = "libedit"
+readlineSync = require('readline-sync');

-HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history')
-
-rlwrap = {} # namespace for this module in web context
-
-ffi = require('ffi')
-fs = require('fs')
-
-rllib = ffi.Library(RL_LIB, {
-    'readline': ['string', ['string']],
-    'add_history': ['int', ['string']]})
-
-rl_history_loaded = false
-
-exports.readline = rlwrap.readline = (prompt = 'user> ') ->
-  if !rl_history_loaded
-    rl_history_loaded = true
-    lines = []
-    if fs.existsSync(HISTORY_FILE)
-        lines = fs.readFileSync(HISTORY_FILE).toString().split("\n");
-
-    # Max of 2000 lines
-    lines = lines[Math.max(lines.length - 2000, 0)..]
-    rllib.add_history(line) for line in lines when line != ""
-
-  line = rllib.readline prompt
-  if line
-    rllib.add_history line
-    try
-      fs.appendFileSync HISTORY_FILE, line + "\n"
-    catch exc
-      true
-
-  line
-
-# vim: ts=2:sw=2
+exports.readline = (prompt = 'user> ') ->
+  readlineSync.question(prompt)


test.EventSpy: add 'callsSinceLastCheck' method

--- coffee/src/util/test.coffee
+++ coffee/src/util/test.coffee
@@ -152,9 +152,16 @@
   reset: =>
     @triggered = false
     @triggerCount = 0
+    @_callsSinceLastCheck = 0
     @arguments = []

   trigger: =>
     @triggered = true
     @triggerCount++
+    @_callsSinceLastCheck++
     @arguments.push _.toArray arguments
+
+  callsSinceLastCheck: =>
+    calls = @_callsSinceLastCheck
+    @_callsSinceLastCheck = 0
+    calls


logging removed

logging removed

--- client/landing/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
@@ -276,10 +276,8 @@
       {activity} = data
       delete data.activity
       activity.modify data, (err, tutorial)=>
-        log data,activity
         if data.appendToList?
           KD.remote.api.JTutorialList.fetchForTutorialId data.appendToList._id,(existingList)=>
-
               unless existingList
                 KD.remote.api.JTutorialList.create
                   title : "New List"
@@ -296,7 +294,6 @@
                 existingList.addItemById activity._id, (err,tutlist)=>
                   if err then log err
                   else callback? err, tutorial, tutlist
-
         callback? err, tutorial
         unless err
           new KDNotificationView type : "mini", title : "Updated the tutorial successfully"


Workspace: Enabled context menu for host finder.

--- client/app/CommonViews/workspace/panes/collaborativefinderpane.coffee
+++ client/app/CommonViews/workspace/panes/collaborativefinderpane.coffee
@@ -17,7 +17,7 @@
     @finderController = new NFinderController
       nodeIdPath          : "path"
       nodeParentIdPath    : "parentPath"
-      contextMenu         : no
+      contextMenu         : yes
       loadFilesOnInit     : yes
       useStorage          : yes
       treeControllerClass : CollaborativeFinderTreeController


Client: Dashboard: Deleted unnecessary lines

--- client/home/lib/account/credentials/homeaccountcredentialsview.coffee
+++ client/home/lib/account/credentials/homeaccountcredentialsview.coffee
@@ -11,20 +11,12 @@

 module.exports = class HomeAccountCredentialsView extends kd.CustomHTMLView

-
-  DEFAULT_LIST_TEXT = '''
-    Credentials may include arbitrary data that you collect for third party integrations.
-  '''
-
   constructor: (options = {}, data) ->

     super options, data

     @addSubView @top = new kd.CustomHTMLView
       cssClass : 'top'
-
-    @top.addSubView @header = new KDHeaderView
-      title : DEFAULT_LIST_TEXT

     @createFilterView()

@@ -93,9 +85,3 @@
     filter.provider = value  if value
     @listController.filterByProvider filter

-    if provider = providers[value]
-      { listText } = provider
-
-    listText = DEFAULT_LIST_TEXT unless listText
-
-    @header.updateTitle listText


Material: Make fixed_z parameter a uniform.

--- engine/material.coffee
+++ engine/material.coffee
@@ -295,11 +295,12 @@
                     # final position in screen, then the view coord from that
                     projection_matrix_inverse =
                         generator.get_projection_matrix_inverse_name()
-                    modifiers_uniforms.push \
-                        "uniform mat4 #{projection_matrix_inverse};"
+                    modifiers_uniforms.push """
+                        uniform mat4 #{projection_matrix_inverse};
+                        uniform float fixed_z;
+                    """
                     [
-                        "vec4 proj_co = vec4(co.xy,
-                                    #{@data.fixed_z.toFixed(7)}, 1.0);"
+                        "vec4 proj_co = vec4(co.xy, fixed_z, 1.0);"
                         "vec4 view_co = #{projection_matrix_inverse} * proj_co;"
                     ]
                 else [
@@ -408,6 +409,9 @@
         @u_uv_rect = gl.getUniformLocation prog, "uv_rect"
         @u_group_id = gl.getUniformLocation prog, "group_id"
         @u_uv_rect? and gl.uniform4f @u_uv_rect, 0, 0, 1, 1
+        if @data.fixed_z?
+            u_fixed_z = gl.getUniformLocation prog, "fixed_z"
+            u_fixed_z? and gl.uniform1f u_fixed_z, @data.fixed_z

         @u_mesh_center = gl.getUniformLocation prog, "mesh_center"
         @u_mesh_inv_dimensions = \


Slight refactoring of generic code in chart_totals

Since the view is shared by the projects#show page and the
staffplans#show page, it was not telling the two pages apart in the
code, resulting in the proposed part of the hours not being rendered
properly in the graph.

I thus added a test on the page so that the proposed hours are now
rendered prooperly on both pages.

--- app/assets/javascripts/views/_shared/chart_totals_view.js.coffee
+++ app/assets/javascripts/views/_shared/chart_totals_view.js.coffee
@@ -6,11 +6,12 @@
     * @param {!HTMLElement}    @el   Container to add charts to.
   *###
   initialize: (@dates, @models=[], @parentsSelector, @el) ->
-    window.parentsSelector = @parentsSelector
     @maxHeight = @el.parents(@parentsSelector).height() - 20
     @render @dates, @models if @dates and @models and @el
-
-
+    @staffPlanPage = if @models.size == 0
+      @parentsSelector is ".user-select" # Fallback on weaker test if the models array is empty
+    else
+      _.has(_.first @models, "projects")
   ###*
     * Render week hour counter.
     * @param {!Object} date_range Date range meta from User model.
@@ -59,7 +60,8 @@
   * @returns {!Object}          Mapping of data to weeks for a given date range.
 *###
 get_data = (date_range, models) ->
-  # Gompute
+
+  # At this point, models should be either an array of User objects or an array of Project objects
   ww = _.map models, (p) ->
     _.map date_range, (date) ->
       p.work_weeks.find (m) ->
@@ -73,20 +75,17 @@
   ww["#{d.year}-#{d.cweek}"] ?= [dummy(d)] for d in date_range # Fill empty weeks

   # Total hours for each week
-  _.map ww, (hours, key) ->
-    _.reduce hours, (m, o) ->
+  _.map ww, (hours, key) =>
+    _.reduce hours, (m, o) =>
       m.date = o.get "date"
       m.actual    += (parseInt(o.get('actual_hours'),    10) or 0)
       m.estimated += (parseInt(o.get('estimated_hours'), 10) or 0)
-      if window.parentsSelector is ".user-select"
+      if @staffPlanPage
         m.proposed.actual += (parseInt(o.get('actual_hours'),    10) or 0) if o.collection?.parent?.collection?.get(o.get("project_id"))?.get("proposed") || false
+        m.proposed.estimated += (parseInt(o.get('estimated_hours'), 10) or 0) if o.collection?.parent?.collection?.get(o.get("project_id"))?.get("proposed") || false
       else
         assignment = _.detect window._meta.assignments, (assignment) -> assignment.user_id == (o.collection?.parent?.id || -1)
         m.proposed.actual += (parseInt(o.get('actual_hours'),    10) or 0) if assignment?.proposed || false
-      if window.parentsSelector is ".user-select"
-        m.proposed.estimated += (parseInt(o.get('estimated_hours'), 10) or 0) if o.collection?.parent?.collection?.get(o.get("project_id"))?.get("proposed") || false
-      else
-        assignment = _.detect window._meta.assignments, (assignment) -> assignment.user_id == (o.collection?.parent?.id || -1)
         m.proposed.estimated += (parseInt(o.get('estimated_hours'), 10) or 0) if assignment?.proposed || false
       m
     , {id: key, actual: 0, estimated: 0, proposed: {actual: 0, estimated: 0}}


host ip env variable

host ip env variable

--- configs/webpack/base.coffee
+++ configs/webpack/base.coffee
@@ -155,7 +155,7 @@
   mergeWebpackConfigs(productionBase, {postLoaders})

 makeDevelopmentBase = (projectConfig) ->
-  host = projectConfig.host or '0.0.0.0'
+  host = process.env.OX_PROJECT_HOST or projectConfig.host or 'localhost'
   servePath = "http://#{host}:#{projectConfig.devPort}"
   publicPath = "#{servePath}/dist/"
   outputPath = "#{projectConfig.basePath}/"


added "xAttributeOptions" to config schema for DenonAvrPresenceSensor, DenonAvrMasterVolume, and DenonAvrZoneVolume

--- device-config-schema.coffee
+++ device-config-schema.coffee
@@ -4,7 +4,7 @@
     title: "Denon AVR Power Switch"
     description: "Denon AVR Power Switch"
     type: "object"
-    extensions: ["xLink", "xPresentLabel", "xAbsentLabel"]
+    extensions: ["xLink", "xPresentLabel", "xAbsentLabel", "xAttributeOptions"]
     properties:
       interval:
         description: "The time interval in seconds (minimum 10) at which the power state of the AVR will be read"
@@ -20,7 +20,7 @@
     title: "Denon AVR Master Volume"
     description: "Denon AVR Master Volume"
     type: "object"
-    extensions: ["xLink", "xPresentLabel", "xAbsentLabel"]
+    extensions: ["xLink", "xPresentLabel", "xAbsentLabel", "xAttributeOptions"]
     properties:
       interval:
         description: "The time interval in seconds (minimum 10) at which the power state of the AVR will be read"
@@ -44,7 +44,7 @@
     title: "Denon AVR Zone Volume"
     description: "Denon AVR Zone Volume"
     type: "object"
-    extensions: ["xLink", "xPresentLabel", "xAbsentLabel"]
+    extensions: ["xLink", "xPresentLabel", "xAbsentLabel", "xAttributeOptions"]
     properties:
       zone:
         description: "The zone for which volume shall be controlled. If set to MAIN it is equivalent to master volume"


Minor code cleanup

Minor code cleanup

--- web/site/search.coffee
+++ web/site/search.coffee
@@ -106,7 +106,6 @@
       beforeQuery = $scope.query
       query = $scope.query + "|type=container"
       query = searchVolume(vol, query)
-      # query = $scope.query + "|type=container|arg=container_volume_id_i:" + "15"
       $scope.search(query, vol.id)
       $scope.query = beforeQuery

@@ -116,10 +115,13 @@
     $scope.searchBox = ->
       console.log("The search box was :", $scope.searchBoxQuery)
       console.log("The original query was :", $scope.originalQuery)
+
+      # Handle user clearing search box and hitting enter
       if $scope.searchBoxQuery? and $scope.searchBoxQuery != ""
         $scope.originalQuery = $scope.searchBoxQuery
       else
         $scope.originalQuery = "*"
+
       $scope.query = $scope.originalQuery
       $scope.offset = 0
       console.log("NEW SEARCH:", $scope.query)
@@ -147,9 +149,7 @@
         else
           $scope.query = searchAge($scope.query, ageRangeMin, ageRangeMax)

-
-      console.log("QUERY IS: ", $scope.query)
-
+      # Send the search
       promise = page.router.http(page.router.controllers.postSearch,
         {"query" : $scope.query, "offset" : $scope.offset, "limit" : $scope.limit})
       promise.then (res) ->
@@ -186,10 +186,7 @@
         $scope.query = "*"
         $scope.search()
         return
-      # if !$scope.query? or !$scope.query or $scope.query == "" or $scope.query == "null" or $scope.query == "false" or $scope.query == "undefined"
-        # $scope.query = "*"
-      console.log("query before anything:", $scope.query)
-      console.log("RES:", res)
+
       models = createModels(res)
       $scope.partyModels = models.parties
       $scope.volumeModels = models.volumes
@@ -262,7 +259,6 @@
       return res?.facets?.content_type?.buckets[idx].count ? 0

     findResult = (typeName, res) ->
-      console.log("RESULT IS", res)
       if res != null
         groups = (group.groupValue for group in res.grouped.content_type.groups)
         return groups.indexOf(typeName)


Removed unneeded parentheses

Removed unneeded parentheses

--- js-library/app/js/models/fact.coffee
+++ js-library/app/js/models/fact.coffee
@@ -20,9 +20,9 @@

     # Bind the own events
     $(@elements)
-      .on('mouseenter', @focus)
-      .on('mouseleave', @blur)
-      .on('click', (=> @click()))
+      .on 'mouseenter', @focus
+      .on 'mouseleave', @blur
+      .on 'click', => @click()
       .on 'inview', (event, isInView, visiblePart) =>
         @highlight(highlight_time_on_in_view) if ( isInView && visiblePart == 'both' )


Stop scroll to top

Stop scroll to top

--- js/flight.coffee
+++ js/flight.coffee
@@ -69,9 +69,7 @@
     else
       transitionType = @targetPage.getAttribute("data-transition")
       @pageHistory.push(window.location.hash)
-
-    window.scrollTo 0, 1
-
+
     # Delay transition to prevent flickering
     window.setTimeout =>
       if @transitionAnimation is true


Make flight automatically display block the current div on initialisation.

--- js/flight.coffee
+++ js/flight.coffee
@@ -24,6 +24,9 @@

     @currentPanel = document.getElementsByClassName('visible')[0]
     if @currentPanel is undefined then throw new Error "Current panel not set"
+
+    # make first page visible
+    @currentPanel.style.display = "block"

     @detectUserAgent()


If scroller is true then destroy any old instances and create new ones on page transition end.

--- js/flight.coffee
+++ js/flight.coffee
@@ -114,6 +114,14 @@
     @isTransitioning = false
     @currentPanel.removeEventListener "webkitTransitionEnd", @finishTransition, false

+    if @useScroller is true
+
+      if @iScrollInstance? then @iScrollInstance.destroy(); @iScrollInstance = null
+      window.setTimeout =>
+        @iScrollInstance = new iScroll(@targetPanel.getElementsByClassName('wrapper')[0].id)
+        console.log @targetPanel.getElementsByClassName('wrapper')[0].id
+      , 0
+
    # displays pages when transiton is false
    displayPage: =>
     @targetPanel.style.display = "block"


Filter out empty lines etc

Filter out empty lines etc

--- lib/ghc-mod/ghc-modi-process.coffee
+++ lib/ghc-mod/ghc-modi-process.coffee
@@ -279,26 +279,21 @@
       args: args
     .then (lines) =>
       rootDir = @getRootDir buffer
+      rx = /^(.*?):([0-9]+):([0-9]+): *(?:(Warning|Error): *)?/
       lines
       .filter (line) ->
-        res = false
         switch
           when line.startsWith 'Dummy:0:0:Error:'
             atom.notifications.addError line.slice(16)
           when line.startsWith 'Dummy:0:0:Warning:'
             atom.notifications.addError line.slice(18)
-          else
-            res = true
-        return res
+          when line.match(rx)?
+            return true
+          when line.trim().length > 0
+            console.log("ghc-mod says: #{line}")
+        return false
       .map (line) ->
-        match =
-          line.match(/^(.*?):([0-9]+):([0-9]+): *(?:(Warning|Error): *)?/)
-        unless match?
-          #TODO: reject (i.e. throw)
-          console.log("ghc-mod says: #{line}")
-          line = "#{olduri}:0:0:Error: #{line}"
-          match=
-            line.match(/^(.*?):([0-9]+):([0-9]+): *(?:(Warning|Error): *)?/)
+        match = line.match(rx)
         [m, file, row, col, warning] = match
         file = olduri if uri.endsWith(file)
         severity =


Cleanup misleading event names

Cleanup misleading event names

--- client/app/Applications/Inbox.kdapplication/AppController.coffee
+++ client/app/Applications/Inbox.kdapplication/AppController.coffee
@@ -25,9 +25,10 @@
       @prepareMessage formOutput, callback

     nc = KD.getSingleton 'notificationController'
-    nc.on 'PrivateMessageSent', @bound 'refreshMessages'
+    nc.on 'PrivateMessageSent', =>
+      @emit 'PrivateMessageReceived'
     nc.on 'ReplyIsAdded', ({subject:{constructorName}})=>
-      @refreshMessages()  if constructorName is 'JPrivateMessage'
+      @emit 'PrivateMessageReceived'  if constructorName is 'JPrivateMessage'

   fetchMessages:(options, callback)->
     KD.whoami().fetchMail? options, callback
@@ -179,7 +180,7 @@
     @on 'MessageShouldBeSent', ({formOutput,callback})=>
       @prepareMessage formOutput, callback, newMessageBar

-    @on 'RefreshMessages', -> newMessageBar?.emit 'RefreshButtonClicked'
+    @on 'PrivateMessageReceived', -> newMessageBar?.emit 'RefreshButtonClicked'

     newMessageBar.on 'MessageShouldBeDisowned', do =>
       if not @selection
@@ -237,9 +238,6 @@

     KD.remote.api.JPrivateMessage.create messageDetails, callback

-  refreshMessages:->
-    @emit "RefreshMessages"
-
   prepareMessage:(formOutput, callback, newMessageBar)->
     {body, subject, recipients} = formOutput


put update my machine modal button next to stact title

--- client/home/lib/stacks/components/stacktemplateitem/index.coffee
+++ client/home/lib/stacks/components/stacktemplateitem/index.coffee
@@ -2,6 +2,7 @@
 _ = require 'lodash'
 React = require 'kd-react'
 TimeAgo = require 'app/components/common/timeago'
+UnreadCount = require 'app/components/sidebarmachineslistitem/unreadcount'

 module.exports = class StackTemplateItem extends React.Component

@@ -14,6 +15,19 @@
     else
       ADD TO SIDEBAR

+
+  getStackUnreadCount: ->
+
+    @props.stack?.getIn [ '_revisionStatus', 'status', 'code' ]
+
+
+  renderUnreadCount: ->
+
+    return null  unless @getStackUnreadCount()
+
+    <UnreadCount
+      count={@getStackUnreadCount()}
+      onClick={@bound 'handleUnreadCountClick'} />

   render: ->

@@ -30,6 +44,7 @@
         onClick={onOpen}>
         { makeTitle { template, stack } }

+      {@renderUnreadCount()}

         Last updated <TimeAgo from={template.getIn ['meta', 'modifiedAt']} />


Update location only on successful or failed selection and not on every deselect.

This makes transition between two selected highlights nicer, without
location having to go through deselected one.

--- client/lib/annotator.coffee
+++ client/lib/annotator.coffee
@@ -58,6 +58,12 @@
     @wrapper = $(@element)

     @ # For chaining
+
+  _inAnyHighlight: (clientX, clientY) =>
+    for highlight in @getHighlights()
+      return true if highlight.in clientX, clientY
+
+    false

   _inAnySelectedHighlight: (clientX, clientY) =>
     for highlight in @getHighlights() when highlight.isSelected()
@@ -97,7 +103,6 @@

   deselectAllHighlights: =>
     highlight.deselect() for highlight in @getHighlights()
-    @updateLocation()

   updateLocation: =>
     location = null
@@ -171,7 +176,15 @@
   onFailedSelection: (event) =>
     super

+    # If click (mousedown coordinates same as mouseup coordinates) is on a highlight, we do not
+    # do anything because click event will be made as well, which will select the new highlight.
+    # This assures we do not first deselect (and update location to publication location) just
+    # to select another highlight immediately afterwards (and update location again to highlight).
+    return if event and event.previousMousePosition and event.previousMousePosition.pageX - event.pageX == 0 and event.previousMousePosition.pageY - event.pageY == 0 and @_inAnyHighlight event.clientX, event.clientY
+
+    # Otherwise we deselect any existing selected highlight
     @deselectAllHighlights()
+    @updateLocation()

     return # Make sure CoffeeScript does not return anything


Fix `null` core_serial case

Fix `null` core_serial case

--- js/script.coffee
+++ js/script.coffee
@@ -121,7 +121,8 @@
 			body += "#{p.rocket.rocket_name} #{p.rocket.rocket_type}#{if p.rocket.first_stage.cores[0].landing_vehicle then " to #{p.rocket.first_stage.cores[0].landing_vehicle} " else ' EXP '}"
 			for c in p.rocket.first_stage.cores
 			  body += "[#{c.core_serial or 'TBD'}] "
-				body += ""
+				span_id = if c.core_serial then c.core_serial.split(".")[0] else 'tbd'
+				body += ""
 				# body += ""
 			body += "	"
 			#


Restore shortcut description

Restore shortcut description

--- src/setup/pif/create.coffee
+++ src/setup/pif/create.coffee
@@ -8,6 +8,7 @@
   q.TargetPath = 'cmd'
   q.Arguments = '/c gui.bat'
   q.WorkingDirectory = install2
+  q.Description = PACKAGE.description
   q.WindowStyle = 7   # Minimized
   q.save()
   # Pin to TaskBar


figure out what to do for fancy straight quotes

--- resource/translators/unicode_translator.coffee
+++ resource/translators/unicode_translator.coffee
@@ -12,8 +12,10 @@
   if Translator.csquotes
     text = text.replace(/[«‹][\s\u00A0]?/g, '')
     text = text.replace(/[\s\u00A0]?[»›]/g, '')
-    text = text.replace(/“/g, '')
-    text = text.replace(/”/g, '')
+
+    # figure out how to do these safely -- so many quoting conventions...
+    # text = text.replace(/“/g, '')
+    # text = text.replace(/”/g, '')

   for chunk, i in text.split(/(<\/?(?:i|italic|b|sub|sup|pre|sc|span)(?:[^>a-z][^>]*)?>)/i)
     switch


add quotes around filename in error message

--- src/smc-webapp/tasks.coffee
+++ src/smc-webapp/tasks.coffee
@@ -1477,7 +1477,7 @@
                 @set_clean()
             else
                 if err
-                    alert_message(type:"error", message:"Error saving #{@filename} -- #{to_json(err)} -- you may need to refresh your browser or restart your project.")
+                    alert_message(type:"error", message:"Error saving '#{@filename}' -- #{to_json(err)} -- you may need to refresh your browser or restart your project.")

     show: () =>
         @element.show()


Added destroy method for jquery version

Added destroy method for jquery version

--- coffee/chosen.jquery.coffee
+++ coffee/chosen.jquery.coffee
@@ -5,10 +5,14 @@
     # Do no harm and return as soon as possible for unsupported browsers, namely IE6 and IE7
     # Continue on if running IE document type but in compatibility mode
     return this unless AbstractChosen.browser_is_supported()
-    this.each((input_field) ->
+    this.each (input_field) ->
       $this = $ this
-      $this.data('chosen', new Chosen(this, options)) unless $this.hasClass "chzn-done"
-    )
+      chosen = $this.data('chosen')
+      if options == 'destroy'
+        return chosen.destroy() if chosen
+      else
+        return $this.data('chosen', new Chosen(this, options)) unless chosen
+
 })

 class Chosen extends AbstractChosen
@@ -86,6 +90,22 @@
       @search_choices.bind 'click.chosen', (evt) => this.choices_click(evt); return
     else
       @container.bind 'click.chosen', (evt) => evt.preventDefault(); return # gobble click of anchor
+
+  destroy: ->
+    $(document).unbind '.chosen'
+    @container.unbind '.chosen'
+    @search_results.unbind '.chosen'
+    @form_field_jq.unbind '.chosen'
+    @search_field.unbind '.chosen'
+    if @is_multiple
+      @search_choices.unbind '.chosen'
+
+    if @search_field.attr "tabindex"
+      @form_field_jq.attr "tabindex", @search_field.attr("tabindex")
+
+    @container.remove()
+    @form_field_jq.data('chosen', null)
+    @form_field_jq.show()

   search_field_disabled: ->
     @is_disabled = @form_field_jq[0].disabled


ugg closer

ugg closer

--- src/openpgp/ocfb.iced
+++ src/openpgp/ocfb.iced
@@ -92,14 +92,10 @@
   #-------------

   _emit_buf : (buf) ->
-    console.log "emit ->"
     wa = WordArray.from_buffer buf[0...@block_size]
-    console.log @FRE.to_buffer().toString('hex')
-    console.log buf.toString('hex')
     wa.xor @FRE, {n_words : (Math.min wa.words.length, @FRE.words.length) }
     buf = wa.to_buffer()
     @out_bufs.push buf
-    console.log buf.toString('hex')
     @FR = new Buffer buf

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