AppController: Set first panel as active.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -222,8 +222,10 @@
             @machineStateModal.once 'IDEBecameReady', =>
               tabView.removePane_ terminalPane
               @createNewTerminal @machineStateModal.getData()
+              @setActiveTabView @ideViews.first.tabView
         else
           @createNewTerminal machine
+          @setActiveTabView @ideViews.first.tabView

   getMountedMachine: (callback = noop) ->
     KD.getSingleton('computeController').fetchMachines (err, machines) =>


Perf: Analysis avoided when toggle

Perf: Analysis avoided when toggle

--- lib/plugin-state.coffee
+++ lib/plugin-state.coffee
@@ -23,7 +23,11 @@

       @currentFilePathExtension = @currentFilePath.split('.').pop();

+      isNavigatingToCorresponding = @prevCorrespondingFilePath? and @prevCorrespondingFilePath == @currentFilePath
+
       @currentCorrespondingFilePath = @railsRSpecFinder.toggleSpecFile(@currentFilePath)
+
+      @prevCorrespondingFilePath = @currentCorrespondingFilePath

       if !@currentCorrespondingFilePath?
         @setNullState()
@@ -40,7 +44,9 @@

       @specFileToAnalyzeWithoutProjectRoot = @railsRSpecFinder.getFileWithoutProjectRoot(@specFileToAnalyze) if @specFileToAnalyze?

-      @analyze(@specFileToAnalyze) if (@specFileToAnalyze? and @specFileExists)
+      shouldAnalyze = @specFileToAnalyze? and @specFileExists and !isNavigatingToCorresponding
+
+      @analyze(@specFileToAnalyze) if (shouldAnalyze)

       @rspecAnalyzerCommand.onDataParsed (asTree) =>
         @asTree = asTree


fix lint warn

fix lint warn

--- lib/search-input.coffee
+++ lib/search-input.coffee
@@ -123,7 +123,7 @@
     @registerCommands()
     this

-  emitDidCommand: (name, options={})->
+  emitDidCommand: (name, options={}) ->
     options.name = name
     options.input = @editor.getText()
     @emitter.emit('did-command', options)


Support git installed themes

Support git installed themes

Signed-off-by: Michelle Tilley <19f7a7eb6786def4c39d6d86c40af5dbc6589663@github.com>

--- lib/themes-panel.coffee
+++ lib/themes-panel.coffee
@@ -79,16 +79,25 @@
             @div outlet: 'devPackages', class: 'container package-container', =>
               @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading themes…"

+          @section class: 'sub-section git-packages', =>
+            @h3 outlet: 'gitThemesHeader', class: 'sub-section-heading icon icon-paintcan', =>
+              @text 'Git Themes'
+              @span outlet: 'gitCount', class: 'section-heading-count badge badge-flexible', '…'
+            @div outlet: 'gitPackages', class: 'container package-container', =>
+              @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading themes…"
+
   initialize: (@packageManager) ->
     super
     @items =
       dev: new List('name')
       core: new List('name')
       user: new List('name')
+      git: new List('name')
     @itemViews =
       dev: new ListView(@items.dev, @devPackages, @createPackageCard)
       core: new ListView(@items.core, @corePackages, @createPackageCard)
       user: new ListView(@items.user, @communityPackages, @createPackageCard)
+      git: new ListView(@items.git, @gitPackages, @createPackageCard)

     @handleEvents()
     @loadPackages()
@@ -133,11 +142,12 @@
     packages.dev = packages.dev.filter ({theme}) -> theme
     packages.user = packages.user.filter ({theme}) -> theme
     packages.core = packages.core.filter ({theme}) -> theme
+    packages.git = (packages.git or []).filter ({theme}) -> theme

     for pack in packages.core
       pack.repository ?= "https://github.com/atom/#{pack.name}"

-    for packageType in ['dev', 'core', 'user']
+    for packageType in ['dev', 'core', 'user', 'git']
       for pack in packages[packageType]
         pack.owner = ownerFromRepository(pack.repository)
     packages
@@ -146,6 +156,7 @@
     packages.dev.sort(packageComparatorAscending)
     packages.core.sort(packageComparatorAscending)
     packages.user.sort(packageComparatorAscending)
+    packages.git.sort(packageComparatorAscending)
     packages

   loadPackages: ->
@@ -162,6 +173,9 @@

         @communityPackages.find('.alert.loading-area').remove()
         @items.user.setItems(@packages.user)
+
+        @gitPackages.find('.alert.loading-area').remove()
+        @items.git.setItems(@packages.git)

         # TODO show empty mesage per section

@@ -268,7 +282,7 @@
   filterPackageListByText: (text) ->
     return unless @packages

-    for packageType in ['dev', 'core', 'user']
+    for packageType in ['dev', 'core', 'user', 'git']
       allViews = @itemViews[packageType].getViews()
       activeViews = @itemViews[packageType].filterViews (pack) ->
         return true if text is ''
@@ -287,8 +301,9 @@
     @updateSectionCount(@communityThemesHeader, @communityCount, @packages.user.length)
     @updateSectionCount(@coreThemesHeader, @coreCount, @packages.core.length)
     @updateSectionCount(@developmentThemesHeader, @devCount, @packages.dev.length)
-
-    @totalPackages.text "#{@packages.user.length + @packages.core.length + @packages.dev.length}"
+    @updateSectionCount(@gitThemesHeader, @gitCount, @packages.git.length)
+
+    @totalPackages.text "#{@packages.user.length + @packages.core.length + @packages.dev.length + @packages.git.length}"

   updateFilteredSectionCounts: ->
     community = @notHiddenCardsLength(@communityPackages)
@@ -300,8 +315,11 @@
     core = @notHiddenCardsLength(@corePackages)
     @updateSectionCount(@coreThemesHeader, @coreCount, core, @packages.core.length)

+    git = @notHiddenCardsLength(@gitPackages)
+    @updateSectionCount(@gitThemesHeader, @gitCount, git, @packages.git.length)
+
   resetSectionHasItems: ->
-    @resetCollapsibleSections([@communityThemesHeader, @coreThemesHeader, @developmentThemesHeader])
+    @resetCollapsibleSections([@communityThemesHeader, @coreThemesHeader, @developmentThemesHeader, @gitThemesHeader])

   matchPackages: ->
     filterText = @filterEditor.getModel().getText()


生成公钥debug

生成公钥debug

--- lib/utils/gitApi.coffee
+++ lib/utils/gitApi.coffee
@@ -108,16 +108,16 @@
         gitHubFlag: 'new'
         gogsFlag: 'new'
       # 根据github规则，公钥名称暂时写死id_dsa
-      cp = exec "ssh-keygen -t das -C chameleonIDE@github.com -f #{options.home}/.ssh/id_dsa -N", options.options, (error, stdout, stderr) ->
-          if error
-            reject(error)
-          else
-            console.log stdout.toString()
-            console.log stderr.toString()
-            pubKey = fs.readFileSync "#{options.home}/.ssh/id_dsa.pub", encoding:'utf-8'
-            msg.public = pubKey
-            localStorage.installedSshKey = JSON.stringify msg
-            resolve(pubKey)
+      cp = exec "ssh-keygen -t dsa -C chameleonIDE@github.com -f #{options.home}/.ssh/id_dsa -N ''", options.options, (error, stdout, stderr) ->
+        if error
+          reject(error)
+        else
+          console.log stdout.toString()
+          console.log stderr.toString()
+          pubKey = fs.readFileSync "#{options.home}/.ssh/id_dsa.pub", encoding:'utf-8'
+          msg.public = pubKey
+          localStorage.installedSshKey = JSON.stringify msg
+          resolve(pubKey)

   github: ->
     # 上传公钥到服务器


Create draft release with correct target branch

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -31,7 +31,8 @@
     cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json')

   grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', ->
-    switch process.env.JANKY_BRANCH
+    branchName = process.env.JANKY_BRANCH
+    switch branchName
       when 'stable'
         isPrerelease = false
       when 'beta'
@@ -54,7 +55,7 @@

     zipAssets buildDir, assets, (error) ->
       return done(error) if error?
-      getAtomDraftRelease isPrerelease, (error, release) ->
+      getAtomDraftRelease isPrerelease, branchName, (error, release) ->
         return done(error) if error?
         assetNames = (asset.assetName for asset in assets)
         deleteExistingAssets release, assetNames, (error) ->
@@ -128,7 +129,7 @@
     tasks.push(zip.bind(this, buildDir, sourcePath, assetName))
   async.parallel(tasks, callback)

-getAtomDraftRelease = (isPrerelease, callback) ->
+getAtomDraftRelease = (isPrerelease, branchName, callback) ->
   atomRepo = new GitHub({repo: 'atom/atom', token})
   atomRepo.getReleases {prerelease: isPrerelease}, (error, releases=[]) ->
     if error?
@@ -150,9 +151,9 @@
             firstDraft.assets = assets
             callback(null, firstDraft)
       else
-        createAtomDraftRelease(isPrerelease, callback)
-
-createAtomDraftRelease = (isPrerelease, callback) ->
+        createAtomDraftRelease(isPrerelease, branchName, callback)
+
+createAtomDraftRelease = (isPrerelease, branchName, callback) ->
   {version} = require('../../package.json')
   options =
     uri: 'https://api.github.com/repos/atom/atom/releases'
@@ -161,6 +162,7 @@
     json:
       tag_name: "v#{version}"
       prerelease: isPrerelease
+      target_commitish: branchName
       name: version
       draft: true
       body: """


Apply Point.fromObj to Range constructor parameters

--- emulation/atom-emulator.coffee
+++ emulation/atom-emulator.coffee
@@ -5,8 +5,8 @@
   end: null

   constructor: (start, end) ->
-    @start = start
-    @end = end
+    @start = Point.fromObj start
+    @end   = Point.fromObj end

 # Not in prototype, because fromObj is a static method
 Range.fromObj = (obj) ->


file select

file select

--- lib/build/ios-build-view.coffee
+++ lib/build/ios-build-view.coffee
@@ -16,10 +16,10 @@

           @div class: 'form-group', =>
             @label 'Mobileprovision:'
-            @subview 'editor', new EditorView(mini: true)
+            @subview 'mobileprovision', new EditorView(mini: true)
           @div class: 'form-group', =>
             @label 'p12:'
-            @subview 'editor', new EditorView(mini: true)
+            @subview 'p12', new EditorView(mini: true)
           @div class: 'form-group', =>
             @label 'p12 password:'
             @subview 'editor', new EditorView(mini: true)
@@ -31,12 +31,31 @@
             @subview 'editor', new EditorView(mini: true)
           @div class: 'form-group', =>
             @label 'Content Src:'
-            @subview 'editor', new EditorView(mini: true)
+            @subview 'src', new EditorView(mini: true)
+
+  initialize: ->
+    [
+      {view: @mobileprovision, suffix: 'mobileprovision'}
+      {view: @p12, suffix: 'p12'}
+      {view: @src, suffix: 'html'}
+    ]
+    .forEach (each) ->
+      #disable input
+      # each.view.setInputEnabled false
+      #select file
+      each.view.on 'click', ->
+        openFile({
+          title: "Select .#{each.suffix} File"
+          filters: [{name: ".#{each.suffix} file", extensions: [each.suffix]}]
+        })
+        .then (destPath) ->
+          console.log "!!! #{each.view.setText}"
+          each.view.setText(destPath)

   onClickIcon: ->
-    openFile {
+    openFile({
       title: 'Select Icon Image'
       filters: [{name: "png image", extensions: ['png']}]
-    }
-    .then (destPath) ->
+    })
+    .then (destPath) =>
       @icon.attr('src', destPath) if destPath


add padding

add padding

--- lib/carte/client/views/app.cjsx
+++ lib/carte/client/views/app.cjsx
@@ -15,6 +15,8 @@
     Button = require('react-bootstrap/lib/Button')

-      <Content key='content' router={@props.router} />
+

+        <Content key='content' router={@props.router} />
+


:art: refactoring

:art: refactoring

--- lib/cursor-style-manager.coffee
+++ lib/cursor-style-manager.coffee
@@ -2,18 +2,19 @@

 settings = require './settings'
 swrap = require './selection-wrapper'
+isSpecMode = atom.inSpecMode()

 lineHeight = null

-getDomNode = (editorElement, cursor) ->
+getCursorNode = (editorElement, cursor) ->
   cursorsComponent = editorElement.component.linesComponent.cursorsComponent
   cursorsComponent.cursorNodesById[cursor.id]

 # Return cursor style offset(top, left)
 # ---------------------------------------
-getOffset = (submode, selection) ->
+getOffset = (submode, cursor) ->
   {top, left} = {}
-  {cursor, editor} = selection
+  {selection, editor} = cursor
   switch submode
     when 'characterwise', 'blockwise'
       unless selection.isReversed()
@@ -50,16 +51,9 @@
         left += bufferPoint.column
   {top, left}

-setStyleOffset = (cursor, {submode, editorElement}) ->
-  domNode = getDomNode(editorElement, cursor)
-  # This guard is for test spec, not all spec have dom attached.
-  return (new Disposable) unless domNode
-
-  {style} = domNode
-  {left, top} = getOffset(submode, cursor.selection)
+setStyle = (style, {top, left}) ->
   style.setProperty('top', "#{top}em") if top?
   style.setProperty('left', "#{left}ch") if left?
-
   new Disposable ->
     style.removeProperty('top')
     style.removeProperty('left')
@@ -74,11 +68,12 @@
       @refresh()

   destroy: ->
-    @subscriptions.dispose()
+    @subscriptions?.dispose()
     @lineHeightObserver.dispose()
     {@subscriptions, @lineHeightObserver} = {}

   refresh: ->
+    return if isSpecMode
     {submode} = @vimState
     @subscriptions?.dispose()
     @subscriptions = new CompositeDisposable
@@ -86,8 +81,10 @@

     cursors = cursorsToShow = @editor.getCursors()
     if submode is 'blockwise'
-      cursorsToShow = @vimState.blockwiseSelections.map (bs) -> bs.getHead().cursor
+      cursorsToShow = @vimState.getBlockwiseSelections.map (bs) -> bs.getHead().cursor
+    @subscriptions.add(@updateCursorStyle({submode, cursors, cursorsToShow})...)

+  updateCursorStyle: ({submode, cursors, cursorsToShow}) ->
     for cursor in cursors
       if cursor in cursorsToShow
         cursor.setVisible(true) unless cursor.isVisible()
@@ -98,9 +95,11 @@
     # But corresponding cursorsComponent(HTML element) is added in sync.
     # So to modify style of cursorsComponent, we have to make sure corresponding cursorsComponent
     # is available by component in sync to model.
-    @editorElement.component.updateSync()
+    if submode is 'blockwise'
+      @editorElement.component.updateSync()

-    for cursor in cursorsToShow
-      @subscriptions.add setStyleOffset(cursor, {submode, @editorElement})
+    cursorsToShow.map (cursor) =>
+      cursorNode = getCursorNode(@editorElement, cursor)
+      setStyle(cursorNode.style, getOffset(submode, cursor))

 module.exports = CursorStyleManager


JNewStatusUpdate: add defensive checks for new status update

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItem.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItem.coffee
@@ -45,7 +45,7 @@

   addChildView:(data, callback)->
     # return
-    return unless data.bongo_
+    return unless data?.bongo_
     {constructorName} = data.bongo_

     childConstructor =
@@ -69,7 +69,8 @@
   show:(callback)->

     @getData().fetchTeaser? (err, teaser)=>
-      @addChildView teaser, => @slideIn()
+      if teaser
+        @addChildView teaser, => @slideIn()

   slideIn:(callback=noop)->
     @once 'transitionend', callback.bind this


isExempt column, redirect profile page if isExempt

--- client/landing/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
+++ client/landing/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
@@ -4,6 +4,10 @@
     super

     memberData = @getData()
+
+    if memberData.isExempt
+      if not KD.checkFlag 'super-admin'
+        return KD.getSingleton('router').handleRoute "/Activity"

     @avatar = new AvatarStaticView
       size     :


DOM Exception 18 fix on KDRouter

DOM Exception 18 fix on KDRouter

--- client/Framework/core/KDRouter.coffee
+++ client/Framework/core/KDRouter.coffee
@@ -157,5 +157,5 @@
   handleQuery:(query)->
     query = @utils.stringifyQuery query  unless 'string' is typeof query
     return  unless query.length
-    nextRoute = "/#{@currentPath}?#{query}"
+    nextRoute = "#{@currentPath}?#{query}"
     @handleRoute nextRoute


Referral: use app tell instead of using utils

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -161,8 +161,8 @@
       tagName    : 'a'
       attributes :
         href     : '#'
-      click      : (e)->
-        KD.utils.showRedeemReferralPointModal()
+      click      : (e)=>
+        @showRedeemReferralPointModal()
         e.stopPropagation()

     KD.getSingleton("vmController").on "ReferralCountUpdated", =>
@@ -176,6 +176,13 @@
       determinate : yes

   click : -> @showReferrerModal()
+
+
+  showRedeemReferralPointModal:->
+    KD.mixpanel "Referer Redeem Point modal, click"
+
+    appManager = KD.getSingleton "appManager"
+    appManager.tell "Account", "showRedeemReferralPointModal"

   updateReferralCountPartial:->
     KD.remote.api.JReferral.fetchRedeemableReferrals { type: "disk" }, (err, referals)=>
@@ -210,12 +217,7 @@
     KD.mixpanel "Referer modal, click"

     appManager = KD.getSingleton "appManager"
-    appManager.tell "Account", "showReferrerModal",
-      # linkView    : getYourReferrerCode
-      top         : 50
-      left        : 35
-      arrowMargin : 110
-
+    appManager.tell "Account", "showReferrerModal"

   pistachio:->
     """


Revert "Activity: Wait for mainView before referencing the sidebar in mainView"

This reverts commit feb35af98de22f985388997af754db4b34f594c9.

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -43,9 +43,7 @@
     # @addSubView @groupHeader  unless isKoding()
     # @addSubView @sidebar
     { mainView } = KD.singletons
-    mainView.ready =>
-      @sidebar     = mainView.activitySidebar
-
+    @sidebar     = mainView.activitySidebar
     @addSubView @tabs

@@ -60,10 +58,10 @@
   # slug: [slug|id|name]
   open: (type, slug) ->

-    {socialapi, router, notificationController, mainView} = KD.singletons
+    {socialapi, router, notificationController} = KD.singletons
+

     kallback = (data) =>
-
       name = if slug then "#{type}-#{slug}" else type
       pane = @tabs.getPaneByName name

@@ -74,25 +72,23 @@
       then @tabs.showPane pane
       else @createTab name, data

-    mainView.ready =>
+    @sidebar.selectItemByRouteOptions type, slug
+    item = @sidebar.selectedItem

-      @sidebar.selectItemByRouteOptions type, slug
-      item = @sidebar.selectedItem
+    if not item
+      type_ = switch type
+        when 'message' then 'privatemessage'
+        when 'post'    then 'activity'
+        else type

-      if not item
-        type_ = switch type
-          when 'message' then 'privatemessage'
-          when 'post'    then 'activity'
-          else type
+      socialapi.cacheable type_, slug, (err, data) =>
+        if err then router.handleNotFound router.getCurrentPath()
+        else
+          @sidebar.addItem data
+          kallback data

-        socialapi.cacheable type_, slug, (err, data) =>
-          if err then router.handleNotFound router.getCurrentPath()
-          else
-            @sidebar.addItem data
-            kallback data
-
-      else
-        kallback item.getData()
+    else
+      kallback item.getData()

   openNext: ->


client: fix missing require

client: fix missing require

--- client/app/lib/util/shortenUrl.coffee
+++ client/app/lib/util/shortenUrl.coffee
@@ -1,4 +1,5 @@
 $ = require 'jquery'
+showError = require "./showError"

 module.exports = shortenUrl = (url, callback)->
   request = $.ajax "https://www.googleapis.com/urlshortener/v1/url",
@@ -12,5 +13,5 @@
     callback data?.id or url, data

   request.error ({status, statusText, responseText})->
-    error "URL shorten error, returning self as fallback.", status, statusText, responseText
-    callback url+    showError "URL shorten error, returning self as fallback.", status, statusText, responseText
+    callback url


config

config

--- mqtt-gw.coffee
+++ mqtt-gw.coffee
@@ -136,6 +136,13 @@
     sse obj

 addport = (p) ->
+  #console.log "addport #{p} #{options.exclude}"
+  pat = new RegExp(options.exclude,"i");
+  #pat = new RegExp(/USB1/);
+  if p.match pat
+    if plist[p]
+      zap p
+    return
   if not plist[p] or plist[p].state=="closed"
     zap p
     plist[p]={state: "init",p3: false, p3esc: false,p3buf: [],stamp: 0, id:"" }


Client: Activity: Deleted unnecessary lines from simpleChatListItem. These lines moved to MarkUserAsTrollModal and BlockUserModal components

--- client/activity/lib/components/chatlistitem/simplechatlistitem.coffee
+++ client/activity/lib/components/chatlistitem/simplechatlistitem.coffee
@@ -6,6 +6,8 @@
 ChatListItem         = require 'activity/components/chatlistitem'
 MessageTime          = require 'activity/components/chatlistitem/messagetime'
 ActivityLikeLink     = require 'activity/components/chatlistitem/activitylikelink'
+MarkUserAsTrollModal = require 'app/components/markuserastrollmodal'
+BlockUserModal       = require 'app/components/blockusermodal'
 ActivityPromptModal  = require 'app/components/activitypromptmodal'
 classnames           = require 'classnames'

@@ -36,22 +38,8 @@
         <ActivityPromptModal {...@getDeleteItemModalProps()} isOpen={@state.isDeleting}>
           Are you sure you want to delete this post?
         </ActivityPromptModal>
-
-        <ActivityPromptModal {...@getMarkUserAsTrollModalProps()} isOpen={@state.isMarkUserAsTrollModalVisible}>
-          This is what we call 'Trolling the troll' mode.

-          All of the troll's activity will disappear from the feeds, but the troll himself will think that people still gets his posts/comments.

-          Are you sure you want to mark him as a troll?
-        </ActivityPromptModal>
-
-        <ActivityPromptModal {...@getBlockUserModalProps()} isOpen={@state.isBlockUserModalVisible}>
-          This will block user from logging in to Koding(with all sub-groups).

-          You can specify a duration to block user.
-          Entry format: [number][S|H|D|T|M|Y] eg. 1M

-

-            Block User For
-            <input name="duration" type="text" onKeyUp={@bound "onKeyUpBlockingUserTime"} ref="BlockingTimeInput" onChange={@bound "onKeyUpBlockingUserTime"} placeholder="e.g. 1Y 1W 3D 2H..."/>
-

-        </ActivityPromptModal>
+        <MarkUserAsTrollModal {...@getMarkUserAsTrollModalProps()} isOpen={@state.isMarkUserAsTrollModalVisible} />
+        <BlockUserModal {...@getBlockUserModalProps()} isOpen={@state.isBlockUserModalVisible} />


Should be props and it's called pinned

Should be props and it's called pinned

--- resources/assets/coffee/react/beatmap-discussions/new-discussion.coffee
+++ resources/assets/coffee/react/beatmap-discussions/new-discussion.coffee
@@ -98,7 +98,7 @@
             span
               className: buttonCssClasses
               onClick: @toggleSticky
-              title: if @state.sticky then osu.trans('beatmaps.discussions.new.pinned') else osu.trans('beatmaps.discussions.new.unpinned')
+              title: if @props.pinned then osu.trans('beatmaps.discussions.new.pinned') else osu.trans('beatmaps.discussions.new.unpinned')

               span className: 'btn-circle__content',
                 i className: 'fas fa-thumbtack'


discussions now correctly created and modified in the DB

--- client/landing/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/widgetcontroller.coffee
@@ -151,8 +151,6 @@

   discussionWidgetSubmit:(data, callback)->

-    log "stating submit"
-
     if data.activity

       {activity} = data


fixed the implementation of moved

fixed the implementation of moved

--- lib/models/vehicle.coffee
+++ lib/models/vehicle.coffee
@@ -44,7 +44,7 @@
   lonBinding: '.lastMessage.lon'
   headingBinding: '.lastMessage.heading'
   moved: ( ->
-    !!@getPath('_journeys.length')
+    !!@getPath('journeys.length')
   ).property('_journeys.length').cacheable()
   createJourney: (message) ->
     if lastJourney = @getPath('_journeys.lastObject')


feat(filterActions): pass existing and new product to filter function

--- lib/product-import.coffee
+++ lib/product-import.coffee
@@ -202,7 +202,9 @@
           .then (sameForAllAttributes) =>
             @_prepareUpdateProduct(prodToProcess, existingProduct).then (preparedProduct) =>
               synced = @sync.buildActions(preparedProduct, existingProduct, sameForAllAttributes)
-                .filterActions(@filterActions)
+                .filterActions((action) =>
+                  @filterActions(action, existingProduct, preparedProduct)
+                )
               if synced.shouldUpdate()
                 @client.products.byId(synced.getUpdateId()).update(synced.getUpdatePayload())
               else


[pylprof] Better error handling for dialtone

--- lib/pylprof/runner.coffee
+++ lib/pylprof/runner.coffee
@@ -40,21 +40,20 @@
     dialtoneInterval = setInterval (->
       if curAttempt > maxAttempts
         clearInterval dialtoneInterval
-        deferred.reject()
+        deferred.reject('Dialtone max attempts reached')
       else
         try
           child.stdin.write dialtoneCmd for i in [0..100]
         catch err
           clearInterval dialtoneInterval
-          deferred.reject(err)
+          stderr += err.toString()
+          deferred.reject(stderr)

         curAttempt += 1
     ), 1000

     child.stderr.on 'data', (err) ->
-      err = err.toString()
-      stderr += err
-      console.log(err)
+      stderr += err.toString()

     child.stdout.on 'data', (data) ->
       if data.toString().indexOf(dialtoneKey) isnt -1


Klient: fix fat arrow for catch

Klient: fix fat arrow for catch

--- client/Main/kite/kites/klient.coffee
+++ client/Main/kite/kites/klient.coffee
@@ -93,6 +93,6 @@

       @terminalSessions = sessions

-    .catch (err)->
+    .catch (err)=>
       # Reset current sessions if fails
       @terminalSessions = []


Uncaught TypeError: object is not a function [fixes #80400070]

--- client/Social/Activity/routes.coffee
+++ client/Social/Activity/routes.coffee
@@ -1,16 +1,16 @@
 do ->

-  appManager = -> KD.singletons.appManager
-
   activityPane = (callback) ->
-    appManager().open 'Activity', (app) ->
+    {appManager} = KD.singletons
+    appManager.open 'Activity', (app) ->
       view = app.getView()
       view.open 'topic', 'public'
       callback view.tabs.getPaneByName 'topic-public'

   handleChannel = (type, slug, callback) ->
     callback    ?= (app) -> app.getView().open type, slug
-    appManager().open 'Activity', callback
+    {appManager} = KD.singletons
+    appManager.open 'Activity', callback

   KD.registerRoutes 'Activity',

@@ -69,5 +69,5 @@
       if query.tagged?
         KD.getSingleton('router').handleRoute "/Activity/Topic/#{query.tagged}"
       else
-        {router, appManager} = KD.singletons
+        {router} = KD.singletons
         router.handleRoute '/Activity/Public'


app: add an option to disable god mode for isFeatureDisabled

--- client/app/lib/maincontroller.coffee
+++ client/app/lib/maincontroller.coffee
@@ -168,10 +168,16 @@
     @forwardEvents remote, ['disconnected', 'reconnected']

-  isFeatureDisabled: (name) ->
-
-    return no  if checkFlag 'super-admin'
+  isFeatureDisabled: (name, options = {}) ->
+
     return no  unless name
+
+    options.godMode ?= yes
+
+    { godMode } = options
+
+    if godMode
+      return no  if checkFlag 'super-admin'

     {roles}            = globals.config
     {disabledFeatures} = getGroup()


Got rid of the dummy collection

Got rid of the dummy collection

--- server/publications.coffee
+++ server/publications.coffee
@@ -2,8 +2,6 @@
 Houston._HIDDEN_COLLECTIONS = {'users': Meteor.users, 'meteor_accounts_loginServiceConfiguration': undefined}
 ADDED_COLLECTIONS = {}
 # TODO: describe what this is, exactly, and how it differs from Houston._collections.
-
-Dummy = new Meteor.Collection("system.dummy")  # hack.

 Houston._publish = (name, func) ->
   Meteor.publish Houston._houstonize(name), func
@@ -68,7 +66,7 @@
   ADDED_COLLECTIONS[name] = collection

 sync_collections = ->
-  Dummy.findOne()  # hack. TODO: verify this is still necessary
+  Houston._admins.findOne()

   _sync_collections = (meh, collections_db) ->
     collection_names = (col.collectionName for col in collections_db \


ListenProcessErrors: remove debug messages

--- components/ListenProcessErrors.coffee
+++ components/ListenProcessErrors.coffee
@@ -24,8 +24,6 @@
     @runtime = runtime
     @listener = (message) =>
       return unless message.command is 'processerror'
-      console.log "got error message : "
-      console.log JSON.stringify message
       @outPorts.process.send message.payload.id
       @outPorts.message.send message.payload.error
       @outPorts.process.disconnect()


Moo cow

Moo cow

(literally tried to rewrite the whole thing...again)

--- Pen.coffee
+++ Pen.coffee
@@ -38,16 +38,19 @@
     }`
     return "i#{res.join ''}"
   vrs.regs =
-    attribute: /([^\n\ ]*?)=(['"]([^\n'"]*?)['"]|(true|false))/gi, css: /([^\n;: ]+):([^\n]+);/gi
-    define: /define\s*([^\n\ ]+)\s*as\s*([^\n\ ,]+)(\s*(?:global|local)ly)?/i, tag: /<([^\n]*?)>/gi
-    eleme: /<([^\n]*?)>([\S\s]*?)<\/([^\n]*?)>/gi, innerText: />([\S\s]*?)</gi
-  vrs.type = (obj) =>
-    strType = vrs.toString obj
-    vrs.class2Type[strType] or 'object'
-  vrs.str = (reg, flags) =>
-    flags = flags or 'gi'
-    ty = vrs.type reg
-    return `ty === 'string' ? new RegExp(reg, flags) : reg`
+    attribute: /([^\n\ ]*?)=(['"]([^\n'"]*?)['"]|(true|false))/gi
+    css: /([^\n;: ]+):([^\n]+);/gi
+    define: /define\s*([^\n\ ]+)\s*as\s*([^\n\ ,]+)(\s*(?:global|local)ly)?/i
+    tag: /<([^\n]*?)>/gi
+    eleme: /<([^\n]*?)>([\S\s]*?)<\/([^\n]*?)>/gi
+    innerText: />([\S\s]*?)</gi
+  vrs.type = do ->
+    class2Type = {}
+    names = 'Boolean Number String Function Array Date RegExp Undefined Null Error Symbol Promise NamedNodeMap Map NodeList DOMTokenList DOMStringMap CSSStyleDeclaration Document Window'.split /\s+/gi
+    names.forEach((name) => class2Type["[object #{name}]"] = name.toLowerCase())
+    (obj) ->
+      strType = Object::toString.call obj
+      class2Type[strType] or 'object'
   vrs.parser = (regs, flags, num1, num2) ->
     flags = flags or 'gi'
     num1 = num1 or 1
@@ -150,25 +153,342 @@
     vrs.def @, arguments[0]
     return
   pen.ink = pen:: = {}
-  `pen.genId = () => vrs.iterate('0 1 2 3 4 5 6 7 8 9'.split(/\s+/g), 5)`
-  pen.seleted = {}
-  pen.$ = () ->
-    pen.selected = pen.selected or {}
-    if vrs.type(arguments[0]) is 'array'
-      els = arguments[0]; ps = arguments[1] or no
-      els = els.map (el) =>
-        el = if ps then pen document.querySelector el else document.querySelector el
-        pen.selected["element#{++elCount}"] = if el instanceof pen then el.el else el
-        return if el instanceof pen then el.el else el
-      return els
-    else if vrs.type(arguments[0]) is 'object'
+  pen.$ = (el, parseIt = false) ->
+    if parseIt is yes
+      pen doc.querySelector(el)
+    else doc.querySelector el
+  pen.$$ = (el) => document.querySelectorAll el
+  pen.create = pen.createElement = (el, parseIt = false) => if parseIt is yes then pen(doc.createElement el) else doc.createElement el
+  pen.addedFunctions = {}
+  pen.parse =
+    attributes: vrs.parser vrs.regs.attribute, 1, 3
+    css: vrs.parser vrs.regs.css, 1, 2
+    element: (str) ->
+      s = str.search('<')
+      e = str.search('>')+1
+      stTag = str.slice(s, e)
+      s = stTag.search(' ')+1
+      e = stTag.search('>')
+      attribs = stTag.slice(s, e)
+      s = stTag.search('<')+1
+      e = stTag.search(' ')
+      tag = stTag.slice(s, e)
+      s = str.search('>')+1
+      e = str.search('</')
+      text = str.slice(s, e)
+      return [str, stTag, attribs, tag, (if text is '' then null else text)]
+  pen.add = (func, name) ->
+    t = vrs.type func
+    if t is 'object'
+      for fname of func
+        pen.addedFunctions[fname] = func[fname]
+        pen::[fname] = func[fname]
+    else if t is 'function'
+      if not func.name?
+        if name?
+          pen.addedFunctions[name] = func
+          pen::[name] = func
+        else
+          vrs.penError "Pen-add 'no-name'", "function, must have a name. Cannot be anonymous"
+      else
+        pen.addedFunctions[func.name] = func
+        pen::[func.name] = func
+  pen.findInObj = (obj, key, defin) ->
+    if vrs.type(key) is 'string'
+      key = new RegExp key, 'i'
+    for kname of obj
+      if /autoAttachTo|el/i.test(kname) is false
+        if key.test(kname) is true
+          return obj[kname]
+        else if vrs.type(obj[kname]) is 'object'
+          return pen.findInObj obj[kname], key, defin
+    return defin

-    else
-      el = arguments[0];ps = arguments[1] or no
-      el = if ps then pen document.querySelector el else document.querySelector el
-      pen.selected["element#{++elCount}"] = if el instanceof pen then el.el else el
-      return el
-  pen.$$ = () ->
-    el = arguments[0];
+  pen::start = (ele, ops) ->
+    t = vrs.type ops
+    if t is 'string'
+      el = pen.$ ops, yes
+      if /\.|#|\[\]/gi.test ele
+        ele = el.$ ele
+      else
+        ele = el.create ele
+    else
+      @initOptions ops
+    @el = ele
+    t1 = vrs.type @el
+    if @el instanceof Document
+      @body = window['pBody']
+      @head = window['pHead']
+      pen::ready = (cb) ->
+        @on 'DOMContentLoaded', cb
+        return @
+    else if @el instanceof Window
+      @doc = @el.document
+    else if @el instanceof pen
+      for prop of ele
+        @[prop] = ele[prop]
+    else if t1 is 'string'
+      if @el.startsWith('define') is true
+        @define @el
+      else
+        @setup @el
+
+    if @ops.autoAttach is yes
+      @ops.autoAttachTo.append element
+    return @
+  pen::initOptions = (ops) ->
+    @ops =
+      autoAttach: (if ops? and ops.autoAttach? then ops.autoAttach else no)
+      autoAttachTo: (if ops? and ops.autoAttachTo? then ops.autoAttachTo else window['body'])
+      global:
+        parseIt: (if ops? and ops.global? and ops.global.parseIt? then ops.global.parseIt else no)
+        create: (if ops? and ops.global? and ops.global.create? then ops.global.create else 'return child')
+        html:
+          app: (if ops? and ops.global? and ops.global.html? and ops.global.html.app? then ops.global.html.app else no)
+          parse: (if ops? and ops.global? and ops.global.html? and ops.global.html.parse? then ops.global.html.parse else no)
+    return @ops
+  pen::toString = () => @el.outerHTML
+  pen::define = (toDef) ->
+    if vrs.regs.define.test(toDef) is true
+      [func, oname, t] = vrs.regs.define.exec(toDef)[1..3]
+      if t?
+        t = t.trim()
+        if t is 'locally' then vrs[func] else window[oname] = vrs[func]
+      else window[onmae] = vrs[func]
+    return
+  pen::setup = (el) ->
+    t = vrs.type el
+    if t is 'string'
+      if el.startsWith('<')
+        [whole, startTag, attributes, tag, text] = pen.parse.element el
+        attribs = pen.parse.attributes attributes
+        @el = pen.create tag
+      else
+        @el = pen.$ el
+    else
+      @el = el
+    for prop of attribs
+      @attr prop, attribs[prop]
+    if text?
+      @html text, parse: yes
+    @inits()
+    @partialSetup()
+    return
+  pen::partialSetup = (ev) ->
+    @attributes.id = vrs.detectAndReturn 'id', @el
+    @attributes.class = vrs.detectAndReturn 'class', @el
+    szlp = @el.getBoundingClientRect()
+    @size =
+      width: szlp.width, height: szlp.height
+    @inits()
+    switch @tag
+      when 'template'
+        @content = @el.content
+        pen::clone = () ->
+          args = vrs.slice arguments
+          document.importNode [args...]
+      when 'canvas'
+        @ctx = @context = @el.getContext '2d'
+    return @
+  pen::initTag = () ->
+    tag = @tag = if @el.tagName? then @el.tagName.toLowerCase() else 'ios-element'
+    return tag
+  pen::initText = () ->
+    text = @text = @html
+    return text
+  pen::initChildren = () ->
+    children = @Children = if @tag is 'template' then @el.content.children else @el.children
+    return children
+  pen::initParent = () ->
+    parent = @Parent = if @el.parentNode? then @el.parentNode else null
+    return parent
+  pen::initLocalName = () ->
+    @initAttributes()
+    @initTag()
+    res1 = if @attributes.id? then "##{@attributes.id}" else ''
+    res2 = if @attributes.class? then ".#{vrs.slice(@el.classList).join '.'}" else ''
+    str = "#{@tag}#{res1}#{res2}"
+    @localName = str
+    return str
+  pen::initClasses = () ->
+    res = vrs.slice(@el.classList)
+    @Classes = res
+    return res
+  pen::initAttributes = () ->
+    results = vrs.slice @el.attributes
+    ret = results.map (result) =>
+      @attributes[result.name] = result.value
+      return "#{result.name}='#{result.value}'"
+    return ret
+  pen::inits = () ->
+    ret = {}
+    ret.tag = @initTag()
+    ret.text = @initText()
+    ret.children = @initChildren()
+    ret.parent = @initParent()
+    ret.attributes = @initAttributes()
+    ret.classes = @initClasses()
+    ret.localName = @initLocalName()
+    return ret
+  pen::html = (str, ops) ->
+    if ops?
+      app = if ops.app? then ops.app else false
+      parse = if ops.parse? then ops.parse else false
+    else
+      app = if @ops.global.html.app? then @ops.global.html.app else false
+      parse = if @ops.global.html.parse? then @ops.global.html.parse else false
+    @initTag()
+    switch @tag
+      when 'input', 'textarea'
+        return vrs.def 'value', str, this, ops
+      when 'option'
+        vrs.def 'value', str, this, ops
+        return vrs.def 'innerText', str, this, ops
+      else
+        return vrs.def((if parse is true then 'innerHTML' else 'innerText'), str, this, ops)
+    return
+  pen::attr = (attribute, value) ->
+    func = vrs.funcoso this, 'attributes', 'setAttribute'
+    if attribute?
+      if vrs.type(attribute) is 'object'
+        @attributes['class'] = attribute?.id
+        @attributes['id'] = attribute?.class
+        return func(attribute)
+      else if value?
+        @el.setAttribute attribute, value
+        @attributes[attribute] = value
+        @inits()
+        return @
+      else if vrs.type(attribute) is 'string' and not value?
+        attrs = pen.parseAttributes attribute
+        for attr of attrs
+          @el.setAttribute attr, attrs[attr]
+          @attributes[attr] = attrs[attr]
+        @inits()
+        return @
+      else
+        @inits()
+        return @el.getAttribute attribute
+    else
+      return @attributes
+  pen::css = (rule, rules) ->
+    func = vrs.funcoso this, 'style'
+    if rule?
+      switch vrs.type(rule)
+        when 'object' then return func(rule)
+        when 'string'
+          if rules?
+            rule = rule.replace /-(\w{1})/g, (whole, dash) => dash.toUpperCase()
+            @style[rule] = rules
+            @el.style[rule] = rules
+            return @
+          else
+            styles = pen.parseCss rule
+            for style of styles
+              st = styles[style]
+              @style[style] = st
+              @el.style[style] = st
+            return @
+        else
+          return @el.style[rule]
+    else
+      return @style
+  pen::on = (evtp, cb, cp) ->
+    cp ?= false
+    if not @events?
+      @events = {}
+    @events[evtp] = {}
+    @events[evtp].capture = cp
+    @events[evtp][if cb.name isnt '' then cb.name else 'func'] = cb
+    typeEvent = if @el.addEventListener? then 'addEventListener' else if @el.attachEvent? then 'attachEvent' else "on#{evtp}"
+    switch typeEvent
+      when 'addEventListener' then @el[typeEvent](arguments...)
+      when 'attachEvent' then @el[typeEvent](evtp, cb)
+      else @el[typeEvent] = cb
+    return @
+  pen::off = (evtp, cb) ->
+    typeEvent = if @el.removeEventListener? then 'removeEventListener' else if @el.detachEvent? then 'detachEvent' else "on#{evtp}"
+    switch typeEvent
+      when 'removeEventListener' then @el[typeEvent](evtp, cb)
+      when 'detachEvent' then @el[typeEvent](evtp, cb)
+      else @el[typeEvent] = null
+
+    delete @events[evtp]
+    return @
+  pen::is = (tag) => @tag is tag
+  pen::append = (elements...) ->
+    elements.forEach (element) =>
+      if vrs.type(element) is 'string'
+        element = pen.$ element
+      else if element instanceof pen
+        element.Parent = @el
+      elu = (if element instanceof pen then element.el else element)
+      if @tag is 'template'
+        @el.content.appendChild elu
+      else
+        @el.appendChild elu
+    return @
+  pen::appendTo = (element) ->
+    if vrs.type(element) is 'string'
+      element = pen.$ element
+    pen(element).append(@el)
+    return @
+  pen::remove = ->
+    check = if @Parent? then 'Parent' else if @el.parentNode? then 'parentNode' else null
+    if check?
+      @[check].removeChild(@el)
+      @Parent = null
+    else
+      log "Pen-remove-error: There's no parent to remove child: #{@localName} from"
+    return @
+  pen::$ = (element, parseIt) ->
+    result = if @tag is 'template' then @el.content else @el
+    if @ops.global.parseIt is true or parseIt is true
+      return pen result.querySelector(element)
+    else
+      return result.querySelector(element)
+  pen::$$ = (element) ->
+    result = if @tag is 'template' then @el.content else @el
+    result.querySelectorAll(element)
+  pen::create = pen::createElement = (element, ret) ->
+    element = pen(element)
+    @append(element)
+    if /child|parent/gi.test(ret) is true
+      result = "return #{ret}"
+      if result.endsWith("parent") is true
+        return @
+      else
+        return element
+    else
+      return
+  pen::toggle = (classes...) ->
+    for classs in classes
+      @el.classList.toggle classs
+    return @
+  pen::hasClass = (cls) ->
+    @initClasses()
+    for clss in @Classes
+      if clss is cls
+        return true
+    return false
+  pen::hide = () ->
+    if @hidden isnt true
+      @hidden = true
+      @css 'display', 'none'
+    else
+      @hidden = false
+      @css 'display', ''
+    return
+  pen::getSize = () -> {width: @el.getBoundingClientRect().width, height: @el.getBoundingClientRect().height}
+  atribs = ['id', 'class', 'href', 'src', 'contentEditable', 'charset', 'title', 'rows', 'cols', 'style']
+  evps = ['click', 'keyup' ,'keypress','keydown' ,'mouse', 'mouseup', 'error', 'load', 'mouseover', 'mousedown' ,'mouseout', 'contextmenu', 'dblclick' ,'drag', 'dragover', 'drop', 'dropend']
+  atribs.forEach (atrib) ->
+    pen::[atrib] = () ->
+      if str? then @attr atrib, arguments... else @attr atrib
+      return
+  evps.forEach (evp) ->
+    pen::[evp] = () ->
+      if not @events[evp]? then @on(evp, arguments...) else @off(evp, arguments...)
+      return
   pen.vrs = vrs
   return pen


Use SJ Nginx Cache

Use SJ Nginx Cache

--- config/main.kontrol-prod.coffee
+++ config/main.kontrol-prod.coffee
@@ -37,7 +37,7 @@
     enabled     : no
     port        : 1337
   neo4j         :
-    read        : "http://kgraph.sj.koding.com"
+    read        : "http://172.16.3.15"
     write       : "http://kgraph.sj.koding.com"
     port        : 7474
   mongo         : mongo


Fall back to old AWS env vars if present

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -1,4 +1,11 @@
 Path = require "path"
+
+# environment variables renamed for consistency
+# use AWS_ACCESS_KEY_ID-style going forward
+if process.env['AWS_KEY'] && !process.env['AWS_ACCESS_KEY_ID']
+	process.env['AWS_ACCESS_KEY_ID'] = process.env['AWS_KEY']
+if process.env['AWS_SECRET'] && !process.env['AWS_SECRET_ACCESS_KEY']
+	process.env['AWS_SECRET_ACCESS_KEY'] = process.env['AWS_SECRET']

 settings =
 	internal:


Fix display of admin group side menu

Fix display of admin group side menu

--- app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee
+++ app/assets/javascripts/admin/enterprise_groups/controllers/side_menu_controller.js.coffee
@@ -12,3 +12,6 @@
     ]

     $scope.select(0)
+
+    $scope.showItem = (item) ->
+      true


Users could not be reused

Users could not be reused

--- coffee/game/users/user_engine.coffee
+++ coffee/game/users/user_engine.coffee
@@ -39,5 +39,6 @@
       parsedId = parseInt($(e).data('id'))
       if parsedId is id
         delete @active[id]
+        @activeSize--
         $(e).parent().remove()
         return false


set isFoldedUp to false in constructor

set isFoldedUp to false in constructor

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -133,6 +133,7 @@
     @panelHeight = @height / @hPanels

     @lastAngle = 0
+    @isFoldedUp = false
     @anchors = ['left', 'right', 'top', 'bottom']
     @lastAnchor = @anchors[0]
     @panels = {}


Remove template snippets folder

Remove template snippets folder

--- dot-atom/snippets/coffee.cson
+++ dot-atom/snippets/coffee.cson
@@ -1,44 +0,0 @@
-".source.coffee":
-  "Describe block":
-    prefix: "de"
-    body: """
-      describe "${1:description}", ->
-        ${2:body}
-    """
-  "It block":
-    prefix: "i"
-    body: """
-      it "$1", ->
-        $2
-    """
-  "Before each":
-    prefix: "be"
-    body: """
-      beforeEach ->
-        $1
-    """
-  "After each":
-    prefix: "af"
-    body: """
-      afterEach ->
-        $1
-    """
-  "Expectation":
-    prefix: "ex"
-    body: "expect($1).to$2"
-  "Console log":
-    prefix: "log"
-    body: "console.log $1"
-  "Range array":
-    prefix: "ra"
-    body: "[[$1, $2], [$3, $4]]"
-  "Point array":
-    prefix: "pt"
-    body: "[$1, $2]"
-
-  "Key-value pair":
-    prefix: ":"
-    body: '${1:"${2:key}"}: ${3:value}'
-  "Create Jasmine spy":
-    prefix: "spy"
-    body: 'jasmine.createSpy("${1:description}")$2'


`ast#run` must not throw errors, must call cb or reject promise

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -73,7 +73,8 @@
                     callback = options
                     options = {}
                 else
-                    throw new err.RqlDriverError "Second argument to `run` cannot be a function is a third argument is provided."
+                    options new err.RqlDriverError("Second argument to `run` cannot be a function is a third argument is provided.")
+                    return
             # else we suppose that we have run(connection[, options][, callback])
         else if connection?.constructor is Object
             if @showRunWarning is true
@@ -88,11 +89,18 @@
         options = {} if not options?

         # Check if the arguments are valid types
-        for own key of options
-            unless key in ['useOutdated', 'noreply', 'timeFormat', 'profile', 'durability', 'groupFormat', 'binaryFormat', 'batchConf', 'arrayLimit']
-                throw new err.RqlDriverError "Found "+key+" which is not a valid option. valid options are {useOutdated: <bool>, noreply: <bool>, timeFormat: <string>, groupFormat: <string>, binaryFormat: <string>, profile: <bool>, durability: <string>, arrayLimit: <number>}."
-        if net.isConnection(connection) is false
-            throw new err.RqlDriverError "First argument to `run` must be an open connection."
+        try
+            for own key of options
+                unless key in ['useOutdated', 'noreply', 'timeFormat', 'profile', 'durability', 'groupFormat', 'binaryFormat', 'batchConf', 'arrayLimit']
+                    throw new err.RqlDriverError "Found "+key+" which is not a valid option. valid options are {useOutdated: <bool>, noreply: <bool>, timeFormat: <string>, groupFormat: <string>, binaryFormat: <string>, profile: <bool>, durability: <string>, arrayLimit: <number>}."
+            if net.isConnection(connection) is false
+                throw new err.RqlDriverError "First argument to `run` must be an open connection."
+        catch e
+            if typeof callback is 'function'
+                return callback(e)
+            else
+                return new Promise (resolve, reject) =>
+                    reject(e)

         if options.noreply is true or typeof callback is 'function'
             try


bring errors back!

bring errors back!

--- app/assets/javascripts/backbone/lib/components/form/form_view.coffee
+++ app/assets/javascripts/backbone/lib/components/form/form_view.coffee
@@ -48,15 +48,20 @@
         if _.isEmpty(errors) then @removeErrors() else @addErrors errors

     removeErrors: ->
-      @$(".form-group").removeClass("has-error")
+      @$('.form-group').removeClass('has-error')
+      @$('.help-block.error').remove()

     addErrors: (errors = {}) ->
       for name, array of errors
         @addError name, array[0]

     addError: (name, error) ->
-      el = @$("[name='#{name}']:first")
-      el.parents('.form-group').addClass('has-error')
+      $el = @$("[name='#{name}']:first")
+      $parent = $el.parents('.form-group')
+      $parent.addClass('has-error')
+      $sm = $('<span>').addClass('help-block error').text(error)
+      lastSibling = $parent.children(':last')
+      lastSibling.after $sm

     syncStart: (model) ->
       @addOpacityWrapper() if @config.syncing


KDWindowController: remove a console.log

--- core/KDWindowController.coffee
+++ core/KDWindowController.coffee
@@ -221,7 +221,6 @@
     @keyView.unsetClass "mousetrap" if @keyView

   setKeyView:(keyView)->
-    console.log keyView
     keyView?.activateKeyView?()
     return if keyView is @keyView
     # unless keyView


Router: referral links fixed

Router: referral links fixed

--- client/Main/kodingrouter.coffee
+++ client/Main/kodingrouter.coffee
@@ -45,7 +45,7 @@

   handleRoute:(route, options={})->
     @breadcrumb.push route
-
+
     entryPoint = options.entryPoint or KD.config.entryPoint
     frags      = route.split("?")[0].split "/"
     name       = if frags[1] is entryPoint?.slug then frags[2] else frags[1]
@@ -264,8 +264,7 @@

     requireLogout =(fn)->
       mainController.ready ->
-        unless KD.isLoggedIn() then __utils.defer fn
-        else clear()
+        unless KD.isLoggedIn() then __utils.defer fn else clear()

     createSectionHandler = (sec)=>
       ({params:{name, slug}, query})=>
@@ -282,7 +281,7 @@
       '/Landing/:page'         : noop
       '/R/:username'           : ({params:{username}})->
         KD.mixpanel "Visit referrer url, success", {username}
-        noop
+        handleRoot.call this

       '/:name?/Logout'         : ({params:{name}})-> requireLogin -> mainController.doLogout()


Router: top level routes fixed

Router: top level routes fixed

--- client/Main/kodingrouter.coffee
+++ client/Main/kodingrouter.coffee
@@ -29,10 +29,12 @@

     return if @userRoute

-    @handleRoute @defaultRoute,
-      shouldPushState : yes
-      replaceState    : yes
-      entryPoint      : KD.config.entryPoint
+
+    KD.utils.defer =>
+      @handleRoute @defaultRoute,
+        shouldPushState : yes
+        replaceState    : yes
+        entryPoint      : KD.config.entryPoint

   handleRoute: (route, options = {}) ->


Fat arrow fix.

Fat arrow fix.

--- server/code/model/user.coffee
+++ server/code/model/user.coffee
@@ -112,7 +112,7 @@
     @save callback

   getCurrentSubscription: (callback) ->
-    requestRecurlyAPI "/v2/accounts/#{@recurlyAccount}/subscriptions", (err, obj) ->
+    requestRecurlyAPI "/v2/accounts/#{@recurlyAccount}/subscriptions", (err, obj) =>
       if err
         return callback err, null


Remove T&C check from validation; it gets in the way of user migration.

--- server/code/model/user.coffee
+++ server/code/model/user.coffee
@@ -41,7 +41,6 @@
     return 'invalid shortName' unless /^[a-zA-Z0-9-.]{3,24}$/g.test @shortName
     return 'invalid displayName' unless /^[^<>;\b]+$/g.test @displayName
     return 'invalid email' unless /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/gi.test @email[0]
-    return 'please accept the terms and conditions' if isNaN @acceptedTerms

   checkPassword: (password, callback) ->
     User.findByShortName @shortName, (err, user) ->


don't bind now

don't bind now

--- src/paper/decor/attr/text.coffee
+++ src/paper/decor/attr/text.coffee
@@ -13,7 +13,7 @@

   bind: () ->
     super()
-    @clippedBuffer.bind("text").to(@_onChange).now()
+    @clippedBuffer.bind("text").to(@_onChange)

   ###
   ###


reworking new source logic

reworking new source logic

--- methods/articles.coffee
+++ methods/articles.coffee
@@ -14,20 +14,15 @@
           $addToSet: userEventIds: eventId
         Meteor.call("editUserEventArticleCount", eventId, 1)
         existingSource._id
-      else
-        if source.url
-          insertArticle =
-            url: source.url
-            title: source.title
-            userEventIds: [eventId]
-          insertArticle = source
-          insertArticle.addedByUserId = user._id
-          insertArticle.addedByUserName = user.profile.name
-          insertArticle.addedDate = new Date()
-          newId = Articles.insert(insertArticle)
-          Meteor.call("editUserEventLastModified", eventId)
-          Meteor.call("editUserEventArticleCount", eventId, 1)
-          return newId
+      else if source.url
+        source.userEventIds = [eventId]
+        source.addedByUserId = user._id
+        source.addedByUserName = user.profile.name
+        source.addedDate = new Date()
+        newId = Articles.insert(source)
+        Meteor.call("editUserEventLastModified", eventId)
+        Meteor.call("editUserEventArticleCount", eventId, 1)
+        return newId
     else
       throw new Meteor.Error("auth", "User does not have permission to add documents")


KDFramework: KDModalView code cleaning

KDFramework: KDModalView code cleaning

--- components/modals/KDModalView.coffee
+++ components/modals/KDModalView.coffee
@@ -27,7 +27,7 @@
     @setClass "fx"                                if options.fx
     @setTitle options.title                       if options.title
     @setContent options.content                   if options.content
-    @addSubView options.view,".kdmodal-content"   if options.view
+    @addSubView options.view, ".kdmodal-content"  if options.view

     @on 'ModalCancelled', options.cancel          if options.cancel

@@ -136,19 +136,20 @@

   setPositions:->
     @utils.defer =>
-      {position} = @getOptions()
-      newPosition = {}
-
-      newPosition.top = if (position.top?) then position.top else ($(window).height()/2) - (@getHeight()/2)
-      newPosition.left = if (position.left?) then position.left else ($(window).width()/2) - (@modalWidth/2)
-      newPosition.left = $(window).width() - @modalWidth - position.right - 20 if position.right #20 is the padding FIX
-      @$().css newPosition
-      @$().css opacity : 1
+      {top, right, bottom, left} = @getOptions().position
+      newRules = {}
+      height = $(window).height()
+      width  = $(window).width()
+      newRules.top  = if top?  then top  else height/2 - @getHeight()/2
+      newRules.left = if left? then left else width/2  - @modalWidth/2
+      newRules.left = width - @modalWidth - right - 20 if right #20 is the padding FIX
+      newRules.opacity = 1
+      @$().css newRules

   _windowDidResize:->
     @setPositions()
     {winHeight} = @getSingleton('windowController')
-    @$('.kdmodal-content').css 'max-height', winHeight - 200
+    @$('.kdmodal-content').css 'max-height', winHeight - 120
     @setY (winHeight - @getHeight()) / 2 unless @getOptions().position.top

   putOverlay:->


Move to next submit in review mode

Move to next submit in review mode

--- client/components/Review.coffee
+++ client/components/Review.coffee
@@ -48,10 +48,11 @@
         'submit/' + props.result.lastSubmitId

     setResult: (result) ->
-        callApi "/setOutcome/#{@props.result.lastSubmitId}", {
+        callApi "setOutcome/#{@props.result.lastSubmitId}", {
             result,
             comment: @state.commentText
         }
+        @props.handleDone()

     accept: () ->
         @setResult("AC")
@@ -70,7 +71,9 @@
                 if newText.endsWith(tail)
                     newText = newText.substring(0, newText.length - tail.length);
             newText = newText.trim()
-            @setField("commentText", @state.commentText + "\n\n" + newText)
+            if @state.commentText.length
+                newText = @state.commentText + "\n\n" + newText
+            @setField("commentText", newText)

     setField: (field, value) ->
@@ -106,10 +109,15 @@
         super(props)
         @state =
             results: props.data.ok
+        @gotoNext = @gotoNext.bind this
+
+    gotoNext: () ->
+        @setState
+            results: @state.results[...-1]

     render: () ->
         if @state.results.length == 0
             return
Ревьювить больше нечего, обновите страницу

-            <ConnectedReviewResult result={@state.results[@state.results.length-1]}/>
+            <ConnectedReviewResult result={@state.results[@state.results.length-1]} handleDone={@gotoNext}/>


use GET when GETting messages - Doh!

use GET when GETting messages - Doh!

--- server/restapi/restapi.coffee
+++ server/restapi/restapi.coffee
@@ -39,7 +39,7 @@

 # get messages in a room
 Api.addRoute 'rooms/:id/messages', authRequired: true,
-	post: ->
+	get: ->
 		msgs = ChatMessage.find({rid: @urlParams.id, _deleted: {$ne: true}}, {sort: {ts: -1}}, {limit: 50}).fetch()

 		if msgs


AppController: Updated getPaneByChange method.

To return finderPane for finder related change object.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1157,6 +1157,8 @@

     return unless change.context

+    return @finderPane  if change.type is 'FileTreeInteraction'
+
     targetPane = null
     {context}  = change
     {paneType} = context
@@ -1249,6 +1251,7 @@

   getWorkspaceName: (callback) -> callback @workspaceData.name
+

   createChatPaneView: (channel) ->


Collaboration: Implemented a message broadcasting mechanism.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -709,15 +709,19 @@

     @rtm.once 'FileLoaded', (doc) =>
       @rtm.setRealtimeDoc doc
-      nickname      = KD.nick()
-      @participants = @rtm.getFromModel 'participants'
-      @changes      = @rtm.getFromModel 'changes'
+      nickname           = KD.nick()
+      @participants      = @rtm.getFromModel 'participants'
+      @changes           = @rtm.getFromModel 'changes'
+      @broadcastMessages = @rtm.getFromModel 'broadcastMessages'

       unless @participants
         @participants = @rtm.create 'list', 'participants', []

       unless @changes
         @changes = @rtm.create 'list', 'changes', []
+
+      unless @broadcastMessages
+        @broadcastMessages = @rtm.create 'list', 'broadcastMessages', []

       isInList = no

@@ -787,6 +791,7 @@

   syncChange: (change) ->
+
     {context} = change

     return  if not @rtm or not @rtm.isReady or not context
@@ -850,16 +855,24 @@

   listenChangeEvents: ->
-    @changes = @rtm.getFromModel 'changes'
-    @changes?.clear()  if @amIHost
-
     @rtm.bindRealtimeListeners @changes, 'list'
-
-    @rtm.on 'ValuesAddedToList', (list, value) =>
-      @handleChange value.values[0]  if list is @changes
-
-    @rtm.on 'ValuesRemovedFromList', (list, value) =>
-      @handleChange value.values[0]  if list is @changes
+    @rtm.bindRealtimeListeners @broadcastMessages, 'list'
+
+    @rtm.on 'ValuesAddedToList', (list, event) =>
+
+      [value] = event.values
+
+      switch list
+
+        when @changes
+          @handleChange value
+
+        when @broadcastMessages
+          @handleBroadcastMessage value
+
+    @rtm.on 'ValuesRemovedFromList', (list, event) =>
+
+      @handleChange event.values[0]  if list is @changes

   handleChange: (change) ->
@@ -1282,3 +1295,7 @@
     @confirmationModal = new KDModalView modalOptions
     @confirmationModal.once 'KDObjectWillBeDestroyed', =>
       delete @confirmationModal
+
+
+  handleBroadcastMessage: (data) ->
+    log data


Collaboration: Create changes map.

Collaboration: Create changes map.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -717,6 +717,7 @@
         else if @amIHost
           rtm.createFile title
           rtm.once 'FileCreated', (file) =>
+            @rtm.create 'list', @realTimeDoc, 'changes'
             @loadCollaborationFile file.result.id


Collaboration: Handle change objects from realtime api.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -836,3 +836,10 @@
     @rtm.on 'ValuesRemovedFromList', (list, value) =>
       @handleChange value.values[0]  if list is @changes

+
+  handleChange: (change) ->
+    myWatchMap = @rtm.getFromModel @realTimeDoc, "#{KD.nick()}WatchMap"
+    {origin}   = change
+
+    if not myWatchMap or myWatchMap.keys().length is 0 or origin in myWatchMap.keys()
+      log 'i need to handle this change...', change


Remove stray debugging console.log

Remove stray debugging console.log

--- client/lanes/models/Base.coffee
+++ client/lanes/models/Base.coffee
@@ -271,15 +271,9 @@
         attrs = []
         for name, definition of klass::_definition when definition.required
             attrs.push(name)
-
         for name, definition of (klass::associations?.definitions || {}) when definition.required
             attrs.push(name)
-
         klass::requiredAttributes = attrs
-
-        unless _.isEmpty(klass::requiredAttributes)
-            console.log klass.name, klass::requiredAttributes
-

         unless klass.Collection
             @createDefaultCollectionFor(klass)


clarify purpose of redis client

clarify purpose of redis client

--- services/real-time/app.coffee
+++ services/real-time/app.coffee
@@ -11,9 +11,9 @@

 if redisSessionsSettings?.cluster?
 	logger.log {}, "using redis cluster for web sessions"
-	rclient = new ioredis.Cluster(redisSessionsSettings.cluster)
+	sessionRedisClient = new ioredis.Cluster(redisSessionsSettings.cluster)
 else
-	rclient = redis.createClient(redisSessionsSettings)
+	sessionRedisClient = redis.createClient(redisSessionsSettings)

 RedisStore = require('connect-redis')(session)
 SessionSockets = require('session.socket.io')
@@ -30,7 +30,7 @@
 io = require('socket.io').listen(server)

 # Bind to sessions
-sessionStore = new RedisStore(client: rclient)
+sessionStore = new RedisStore(client: sessionRedisClient)
 cookieParser = CookieParser(Settings.security.sessionSecret)
 sessionSockets = new SessionSockets(io, sessionStore, cookieParser, Settings.cookieName)


add health check endpoint and http route logger

--- services/real-time/app.coffee
+++ services/real-time/app.coffee
@@ -25,10 +25,9 @@

 # work around frame handler bug in socket.io v0.9.16
 require("./socket.io.patch.js")
-
 # Set up socket.io server
 app = express()
-Metrics.injectMetricsRoute(app)
+
 server = require('http').createServer(app)
 io = require('socket.io').listen(server)

@@ -37,6 +36,9 @@
 cookieParser = CookieParser(Settings.security.sessionSecret)

 sessionSockets = new SessionSockets(io, sessionStore, cookieParser, Settings.cookieName)
+
+Metrics.injectMetricsRoute(app)
+app.use(Metrics.http.monitor(logger))

 io.configure ->
 	io.enable('browser client minification')
@@ -65,7 +67,7 @@

 rclient = require("redis-sharelatex").createClient(Settings.redis.realtime)

-app.get "/health_check/redis", (req, res, next) ->
+healthCheck = (req, res, next)->
 	rclient.healthCheck (error) ->
 		if error?
 			logger.err {err: error}, "failed redis health check"
@@ -77,7 +79,11 @@
 		else
 			res.sendStatus 200

-Metrics.injectMetricsRoute(app)
+app.get "/health_check", healthCheck
+
+app.get "/health_check/redis", healthCheck
+
+

 Router = require "./app/js/Router"
 Router.configure(app, io, sessionSockets)


fixed console for 0.10

fixed console for 0.10

--- lib/cli/console.coffee
+++ lib/cli/console.coffee
@@ -6,7 +6,7 @@
   repl = require 'repl'
   if Caboose.versions.node.minor is 6
     context = repl.start().context
-  else if Caboose.versions.node.minor is 8
+  else if Caboose.versions.node.minor in [8, 10]
     context = repl.start({
       prompt: "caboose:#{Caboose.app.name}> "
       useColors: true


Add enableLogs to KD to enable logging even in production

--- core/KD.coffee
+++ core/KD.coffee
@@ -25,6 +25,12 @@

 # KD Global
 KD = @KD or {}
+
+noop  = ->
+
+KD.log   = log   = noop
+KD.warn  = warn  = noop
+KD.error = error = noop

 @KD = $.extend (KD), do ->

@@ -151,17 +157,15 @@
       dataType : 'jsonp'
       success : (data)->
         inflated = JSONH.unpack data
-        console.log 'success', inflated
-        console.log Date.now()-start
+        KD.log 'success', inflated
+        KD.log Date.now()-start

-noop  = ->
+  enableLogs:->
+    KD.log   = log   = if console?.log   then console.log.bind(console)   else noop
+    KD.warn  = warn  = if console?.warn  then console.warn.bind(console)  else noop
+    KD.error = error = if console?.error then console.error.bind(console) else noop
+    return "Logs are enabled now."

-if KD.config?.suppressLogs
-  # assign all these to noop:
-  console.log = console.error = console.warn = console.trace = noop
-
-KD.log   = log   = if console?.log   then console.log.bind(console)   else noop
-KD.warn  = warn  = if console?.warn  then console.warn.bind(console)  else noop
-KD.error = error = if console?.error then console.error.bind(console) else noop
+KD.enableLogs() if not KD.config?.suppressLogs

 prettyPrint = noop


added default values to web term styling when a user uses it for the first time

--- client/landing/app/Applications/WebTerm.kdapplication/AppView.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/AppView.coffee
@@ -3,9 +3,9 @@
   setDefaultStyle:->
     @container.unsetClass font.value for font in __webtermSettings.fonts
     @container.unsetClass theme.value for theme in __webtermSettings.themes
-    @container.setClass @appStorage.getValue('font')
-    @container.setClass @appStorage.getValue('theme')
-    @container.$().css fontSize:@appStorage.getValue('fontSize')+'px'
+    @container.setClass @appStorage.getValue('font') or 'ubuntu-mono'
+    @container.setClass @appStorage.getValue('theme') or 'black-on-white'
+    @container.$().css fontSize:@appStorage.getValue('fontSize')+'px' or '14px'
   viewAppended: ->

     @appStorage = new AppStorage 'WebTerm', '1.0'


Test spec store

Test spec store

--- spec/Initialization.coffee
+++ spec/Initialization.coffee
@@ -43,7 +43,7 @@
     it 'should have created the IndexedDB database', (done) ->
       indexedDB = win.overrideIndexedDB or win.indexedDB
       chai.expect(indexedDB).to.be.an 'object'
-      req = indexedDB.open 'noflo-ui', 3
+      req = indexedDB.open 'noflo-ui', 4
       req.onerror = ->
         chai.expect(true).to.equal false
         done()
@@ -62,3 +62,5 @@
       chai.expect(db.objectStoreNames.contains('components')).to.equal true
     it 'should have created the runtime store', ->
       chai.expect(db.objectStoreNames.contains('runtimes')).to.equal true
+    it 'should have created the spec store', ->
+      chai.expect(db.objectStoreNames.contains('specs')).to.equal true


added specs for find-all

added specs for find-all

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -460,6 +460,19 @@

       expect(findView.resultCounter.text()).toEqual('2 of 5')
       expect(editor.getSelectedBufferRange()).toEqual [[1, 6], [1, 10]]
+
+
+    it "selects the all matches when the 'find-and-replace:find-all' event is triggered and correctly focuses the editor", ->
+      expect(findView).toHaveFocus()
+      findView.findEditor.trigger('find-and-replace:find-all')
+      expect(editor.getSelectedBufferRanges()).toEqual [[[1, 27], [1, 22]],[[2, 8], [2, 13]], [[2, 34], [2, 39]], [[3, 16], [3, 21]],[[4,10], [4, 15]],[[5, 16], [5, 21]]]
+      expect(editorView).toHaveFocus()
+
+    it "will re-run search if 'find-and-replace:find-all' is triggered after changing the findEditor's text", ->
+      findView.findEditor.setText 'sort'
+      findView.findEditor.trigger('find-and-replace:find-all')
+
+      expect(editor.getSelectedBufferRanges()).toEqual [[[0, 9], [0, 13]], [[1, 6], [1, 10]],[[8, 11], [8, 15]], [[8, 43], [8, 47]], [[11, 9], [11, 13]]]

     it "replaces results counter with number of results found when user moves the cursor", ->
       editor.moveDown()


Use path.sep in tests

Use path.sep in tests

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2164,7 +2164,7 @@
         expect(zetaEntries).toEqual(["zeta.txt"])

       it "squashes two dir names when the first only contains a single dir", ->
-        betaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha/beta):first')
+        betaDir = $(treeView.roots[0].entries).find(".directory:contains(alpha#{path.sep}beta):first")
         betaDir[0].expand()
         betaEntries = [].slice.call(betaDir[0].children[1].children).map (element) ->
           element.innerText
@@ -2172,7 +2172,7 @@
         expect(betaEntries).toEqual(["beta.txt"])

       it "squashes three dir names when the first and second only contain single dirs", ->
-        epsilonDir = $(treeView.roots[0].entries).find('.directory:contains(gamma/delta/epsilon):first')
+        epsilonDir = $(treeView.roots[0].entries).find(".directory:contains(gamma#{path.sep}delta#{path.sep}epsilon):first")
         epsilonDir[0].expand()
         epsilonEntries = [].slice.call(epsilonDir[0].children[1].children).map (element) ->
           element.innerText


.entries always exist now so just check expanded class

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -38,12 +38,10 @@
       subdir0 = rootEntries.find('> li:eq(0)')
       expect(subdir0).not.toHaveClass('expanded')
       expect(subdir0.find('.name')).toHaveText('dir1')
-      expect(subdir0.find('.entries')).toBeHidden()

       subdir2 = rootEntries.find('> li:eq(1)')
       expect(subdir2).not.toHaveClass('expanded')
       expect(subdir2.find('.name')).toHaveText('dir2')
-      expect(subdir2.find('.entries')).toBeHidden()

       expect(rootEntries.find('> .file:contains(tree-view.js)')).toExist()
       expect(rootEntries.find('> .file:contains(tree-view.txt)')).toExist()
@@ -237,16 +235,13 @@
       subdir = treeView.root.find('.entries > li:contains(dir1)').view()

       expect(subdir).not.toHaveClass('expanded')
-      expect(subdir.find('.entries')).not.toExist()

       subdir.click()

       expect(subdir).toHaveClass('expanded')
-      expect(subdir.find('.entries')).toExist()

       subdir.click()
       expect(subdir).not.toHaveClass('expanded')
-      expect(subdir.find('.entries')).not.toExist()

     it "restores the expansion state of descendant directories", ->
       child = treeView.root.find('.entries > li:contains(dir1)').view()
@@ -256,14 +251,14 @@
       grandchild.click()

       treeView.root.click()
-      expect(treeView.root.find('.entries')).not.toExist()
+      expect(treeView.root).not.toHaveClass('expanded')
       treeView.root.click()

       # previously expanded descendants remain expanded
       expect(treeView.root.find('> .entries > li:contains(dir1) > .entries > li:contains(sub-dir1) > .entries').length).toBe 1

       # collapsed descendants remain collapsed
-      expect(treeView.root.find('> .entries > li:contains(dir2) > .entries')).not.toExist()
+      expect(treeView.root.find('> .entries > li:contains(dir2) > .entries')).not.toHaveClass('expanded')

     it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", ->
       child = treeView.root.entries.find('li:contains(dir1)').view()


Update spec for opening files

Update spec for opening files

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -530,7 +530,7 @@
     beforeEach ->
       jasmine.attachToDOM(workspaceElement)

-    it "selects the files and opens it in the active editor, without changing focus", ->
+    it "selects the files and opens it in the active editor, and changes focus to the file", ->
       treeView.focus()

       waitsForFileToOpen ->
@@ -538,8 +538,9 @@

       runs ->
         expect(sampleJs).toHaveClass 'selected'
-        expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')
-        expect(treeView.list).toHaveFocus()
+        activePaneItem = atom.workspace.getActivePaneItem()
+        expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')
+        expect(atom.views.getView(activePaneItem)).toHaveFocus()

       waitsForFileToOpen ->
         sampleTxt.trigger clickEvent(originalEvent: {detail: 1})
@@ -547,8 +548,9 @@
       runs ->
         expect(sampleTxt).toHaveClass 'selected'
         expect(treeView.find('.selected').length).toBe 1
-        expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt')
-        expect(treeView.list).toHaveFocus()
+        activePaneItem = atom.workspace.getActivePaneItem()
+        expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt')
+        expect(atom.views.getView(activePaneItem)).toHaveFocus()

   describe "when a file is double-clicked", ->
     beforeEach ->


:green_heart: Fixes tests for configurable large-file-warning.

--- spec/workspace-spec.coffee
+++ spec/workspace-spec.coffee
@@ -451,9 +451,13 @@
           waitsForPromise ->
             workspace.open('sample.js').then (e) -> editor = e

-        runs ->
-          expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
-          expect(editor.largeFileMode).toBe true
+          runs ->
+            expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
+            expect(editor.largeFileMode).toBe true
+        else
+          runs ->
+            expect(atom.applicationDelegate.confirm).not.toHaveBeenCalled()
+            expect(editor).not.toBeUndefined()

       it "prompts the user to make sure they want to open a file this big", ->
         atom.config.set "core.warnOnLargeFileLimit", 20


Print errors for packages

Print errors for packages

If you had a package.json with a name that could not be found on npm, it
will tell you.

--- dependencies.coffee
+++ dependencies.coffee
@@ -150,7 +150,7 @@

   @error: (name, err) ->
     # Print standard error message for given package
-    # console.log(name, err.toString())
+    console.error(name, err.toString())

   print: ->
     status = @diff @local, @remote


Update jade template plugin to conform with new template plugin structure

--- src/plugins/jade-template.coffee
+++ src/plugins/jade-template.coffee
@@ -2,6 +2,7 @@
 async = require 'async'
 jade = require 'jade'
 fs = require 'fs'
+path = require 'path'

 {TemplatePlugin} = require './../templates'

@@ -15,14 +16,15 @@
     catch error
       callback error

-JadeTemplate.fromFile = (filename, callback) ->
+JadeTemplate.fromFile = (filename, base, callback) ->
+  fullpath = path.join base, filename
   async.waterfall [
     (callback) ->
-      fs.readFile filename, callback
+      fs.readFile fullpath, callback
     (buffer, callback) ->
       try
         rv = jade.compile buffer.toString(),
-          filename: filename
+          filename: fullpath
         callback null, new JadeTemplate rv
       catch error
         callback error


Updated timed echoes example.

Updated timed echoes example.

--- examples/03-timed-echo.coffee
+++ examples/03-timed-echo.coffee
@@ -1,5 +1,10 @@
 class Example extends fk.client.Sketch
   Vec2 = fk.math.Vec2
+  State = fk.physics.State
+
+  rng = new fk.math.Random()
+
+  cMouse = 0

   setup: ->
 #    console.log "w #{@width} h #{@height}"
@@ -7,6 +12,7 @@
     @timer = new fk.Timer()
     @tempo = new fk.Tempo()

+    # -- Physics --
     @physics = new fk.physics.Physics()

     # use 2D particles
@@ -20,7 +26,10 @@
       particle.setPosition2 @width / 2, ((Math.random() * 2 - 1) * 0.25 + 0.5) * @height
       particle.force.set2 (Math.random() * 2 - 1) * 10.1, 0

-#      particle.setPosition2 Math.random() * @width, Math.random() * @height
+      particle.lifetime = rng.int 60, 120
+      particle.size = 5
+
+    #      particle.setPosition2 Math.random() * @width, Math.random() * @height

     # creates a force that slowly pulls particles up
     @physics.add new fk.physics.Force new Vec2(0, 1), 0.001
@@ -31,13 +40,16 @@
     @attractor = new fk.physics.Attractor new Vec2(), 150, 0.5
 #    @physics.add @attractor

+    # -- Graphics --
+    cMouse = @color 255, 0, 255
+

   draw: ->

     dt = @timer.update()
     beat = @tempo.update dt

-#    console.log "bar: #{@tempo.bar} beat: #{@tempo.beat}"
+    #    console.log "bar: #{@tempo.bar} beat: #{@tempo.beat}"

     if @tempo.onBar and @tempo.bar % 5 == 0
       console.log "beep"
@@ -53,9 +65,12 @@
     # draw
     @background(0)

-    @fill(255)
+#    @fill(255)
     for particle in @physics.particles
-      @rect particle.position.x, particle.position.y, 3, 3
+      if particle.state == State.ALIVE
+        life = 1 - (particle.age / particle.lifetime)
+        @fill 255, life
+        @rect particle.position.x, particle.position.y, particle.size, particle.size

-    @fill 255, 0, 255
+    @fill cMouse
     @rect this.mouseX, this.mouseY, 5, 5


Updating systemd_service resource snippet

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


Make no idea dog appear more often

Make no idea dog appear more often

--- couchbot/scripts/dogatcomputer.coffee
+++ couchbot/scripts/dogatcomputer.coffee
@@ -21,7 +21,7 @@
 module.exports = (robot) ->
   robot.hear /(dunno|I don\'t know|beats me|no idea)/i, (msg)->
     r = Math.random()
-    if r <= 0.05
+    if r <= 0.5
       msg.send noidea

   robot.respond /dunno|I don\'t know|beats me|no idea/i, (msg) ->


save datatables parameters in local storage

--- BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
@@ -104,6 +104,12 @@
           viewContext.renderColumnActions(viewContext, td, cellData, rowData, row, col)
       ]
       order: [@options.order]
+      bStateSave: true
+      fnStateSaveCallback: (oSettings, oData) ->
+      	localStorage.setItem 'DataTables_' + location.hash, JSON.stringify(oData)
+      	return
+      fnStateLoadCallback: (oSettings) ->
+      	JSON.parse localStorage.getItem('DataTables_' + location.hash)
     )

     return


clarify

clarify

--- lib/transforms/split.coffee
+++ lib/transforms/split.coffee
@@ -5,7 +5,7 @@

 class Split extends Transform
   constructor: (@stream_opts, @options) ->
-    delete @stream_opts.objectMode # must take in strings
+    delete @stream_opts.objectMode # must take in strings or buffers
     super @stream_opts
     if not @options? or
     not (_(@options).isString() or @options instanceof RegExp or _(@options).isObject())


time to sleep

time to sleep

--- commands/version-control.coffee
+++ commands/version-control.coffee
@@ -16,7 +16,7 @@

 svnExport = (repo, respond) ->

-    validate repo, ->
+    validateRepo repo, ->

         options = "-q --no-auth-cache --username #{config.svn.username} --password #{config.svn.password}"
         exec "svn export #{options} svn://#{config.svn.url}/#{repo} #{config.svn.path}/#{repo}", (err, stdout, stderr) ->
@@ -28,7 +28,7 @@

 svnLog = (repo, respond) ->

-    validate repo, ->
+    validateRepo repo, ->

         options = '-l 3 --no-auth-cache --username florinda --password chespirito'
         exec "svn log #{options} svn://localhost/#{repo}", (err, stdout, stderr) ->


Bucket helper functions now exist. Score progression step charts are now possible.

--- web/coffee/progression_graphs.coffee
+++ web/coffee/progression_graphs.coffee
@@ -13,8 +13,37 @@

 timestampsToBuckets = (samples, key, seconds) ->
   min = _.first(samples)[key]
+  max = _.last(samples)[key]
+
+  bucketNumber = (number) ->
+    Math.floor((number - min) / seconds)
+
+  continuousBucket = {}
+  maxBuckets = bucketNumber max
+
+  for i in [0..maxBuckets]
+    continuousBucket[i] = []
+
   buckets = _.groupBy samples, (sample) ->
-    (sample[key] - min) / seconds
+    bucketNumber sample[key]
+
+  return _.extend continuousBucket, buckets
+
+maxValuesFromBuckets = (buckets, sampleKey) ->
+  maxValues = []
+
+  lastInsertedValue = 0
+  _.each buckets, (samples) ->
+    values = _.pluck(samples, sampleKey)
+
+    if values.length > 0
+      maxValue = _.max values
+      maxValues.push maxValue
+      lastInsertedValue = maxValue
+    else
+      maxValues.push lastInsertedValue
+
+  return maxValues

 @drawTeamProgressionGraph = (selector) ->
   div = divFromSelector selector
@@ -29,7 +58,6 @@
       graphData.push [submission.time, submission.score]
       lastSubmission = submission

-    console.log(timestampsToBuckets data.data, "time", 600)
     packagedData = google.visualization.arrayToDataTable graphData

     chart = new google.visualization.LineChart(div)


Clean up

Clean up

--- source/behaviors/avoid.coffee
+++ source/behaviors/avoid.coffee
@@ -1,293 +0,0 @@
-
-class Avoid
-
-    constructor: ( options ) ->
-
-        # Merge options & defaults
-        core.extend @, core.defaults options or {},
-
-            # Toggle
-            enabled: yes
-
-            # Obstacles
-            obstacles: []
-
-            # Ray length
-            radius: 200
-
-            # Behavior weighting
-            weight: 1.0
-
-            # Apply probability
-            chance: 1.0
-
-    apply: ( agent ) ->
-
-        line = ( v1, v2, col, label = '', width = 1 ) ->
-            ctx.save()
-            ctx.beginPath()
-            ctx.translate v1.x, v1.y
-            ctx.moveTo 0, 0
-            ctx.lineTo v2.x, v2.y
-            ctx.closePath()
-            ctx.strokeStyle = col
-            ctx.lineWidth = width
-            ctx.stroke()
-
-            mid = v2.clone().scale 0.5
-            ctx.fillStyle = '#ff00ff'
-            ctx.fillText label, mid.x + 5, mid.y
-
-            ctx.restore()
-
-        circ = ( pos, rad, col ) ->
-            ctx.beginPath()
-            ctx.arc( pos.x, pos.y, rad, 0, Math.PI * 2 )
-            ctx.closePath()
-            ctx.strokeStyle = col
-            ctx.lineWidth = 4
-            ctx.stroke()
-
-        # Determine whether or not to apply behavior
-        return if not @enabled or @chance < 1 and Math.random() > @chance
-
-        # Project a feeler out infront of the agent
-        # TODO scale to velocity? (don't normalise first)
-        feeler = agent.vel.clone().norm().scale @radius
-        feeler.rotate 0.2
-
-        #  BEGIN DEBUG
-        line agent, feeler, 'rgba(255,0,255,0.5)', 'v', 5
-        # END DEBUG
-
-        for obstacle in @obstacles
-
-            # Location delta
-            a = Vector.sub obstacle, agent
-
-            # Project delta onto feeler
-            p = Vector.project a, feeler
-
-            b = Vector.sub p, a
-
-            line agent, a, '#00ff00', 'a'
-            line obstacle, b, '#ff00ff', 'b'
-            line agent, p, '#0000ff', 'p'
-
-            # Feeler of infinite length will intersect obstacle
-            intersect = b.mag() < obstacle.radius
-
-            # Feeler of actual length will penetrate obstacle
-            intersect = intersect and @radius + obstacle.radius > p.mag()
-
-            if intersect
-
-                circ obstacle, obstacle.radius, '#ff00ff'
-
-                a.rotate -agent.vel.angle()
-
-                line agent, a, '#00ff00', 'a'
-                console.log a.norm().x
-        ###
-
-        for obstacle in @obstacles
-
-            u = agent.vel.clone().norm()
-            v = u.clone().scale @radius # ?
-            a = Vector.sub obstacle, agent
-            p = ( a.clone().mult u ).mult u
-            b = Vector.sub p, a
-
-            #console.log b.mag(), p.mag(), v.mag()
-
-            if b.mag() < obstacle.radius and p.mag() < v.mag()
-
-                line agent, a, '#00ff00', 'a'
-                line obstacle, b, '#f0f00f', 'b'
-                line agent, p, '#ff0000', 'p'
-
-                # DEBUG
-                circ obstacle, obstacle.radius, '#ff00ff'
-
-                w = a.clone().rotate -Math.PI
-                w.norm()
-
-                # DEBUG
-                line agent, ( w.clone().scale 100 ), '#00ffff'
-
-                m = if a.x < 0 then 1 else -1
-
-
-
-        ###
-
-
-
-
-        ###
-
-        for obstacle in @obstacles
-
-            forward = agent.vel.clone().norm()
-            delta = Vector.sub obstacle, agent
-            dot = delta.dot forward
-
-            if dot < 0
-
-                ray = forward.clone().scale @radius
-                pro = forward.clone().scale dot
-
-                dist = delta.mag()
-
-                if dist < obstacle.radius + @radius
-
-                    force = forward.clone().scale agent.maxSpeed
-
-                    HALF_PI = Math.PI / 2
-                    theta = HALF_PI * delta.sign agent.vel
-                    force.rotate theta
-
-                    force.scale 1 - pro.mag() / ray.mag()
-                    agent.acc.add force
-
-                    force.scale 80
-
-                    # BEGIN DEBUG
-                    ctx.save()
-                    ctx.translate agent.x, agent.y
-                    ctx.moveTo 0, 0
-                    ctx.lineTo force.x, force.y
-                    ctx.strokeStyle = '#ff0000'
-                    ctx.stroke()
-                    ctx.restore()
-                    # END DEBUG
-
-        ###
-
-
-        ###
-
-        range = Number.MAX_VALUE
-
-        for obstacle in @obstacles
-
-            # Delta between agent & obstacle
-            delta = Vector.sub obstacle, agent
-
-            # Compute squared distance
-            distSq = do delta.magSq
-
-            # Combined agent, obstacle & look ahead radii
-            radii = @radius + obstacle.radius + agent.radius
-
-            # Squared radii to avoid sqrt
-            radiiSq = radii * radii
-
-            # Only check objects witin range
-            if distSq < radiiSq
-
-                # Compute the dot (scalar) product
-                scalar = delta.dot agent.vel
-
-                # Heading is the agent's velocity
-                heading = agent.vel.clone()
-
-                offset = delta.sub heading.scale scalar
-
-                ## BEGIN DEBUG
-                agent.ctx.save()
-                agent.ctx.translate agent.x, agent.y
-                agent.ctx.moveTo 0, 0
-                agent.ctx.lineTo offset.x, offset.y
-                agent.ctx.strokeStyle = '#000'
-                agent.ctx.stroke()
-                agent.ctx.restore()
-                ## END DEBUG
-
-                # Is obstacle in front of agent?
-                infront = scalar > 0
-
-                # Compute velocity delta
-                steer = offset.scale -1
-
-                # Limit force
-                steer.limit agent.maxForce
-
-                # Apply weighting
-                steer.scale @weight
-
-                # Apply force
-                agent.acc.add steer
-
-                # Is obstacle in front of agent?
-
-        ###
-
-
-
-
-
-        ###
-
-        # Cast a ray in the direction of the vehicle's velocity
-        forward = agent.vel.clone().norm()
-
-        # Process all obstacles
-        for obstacle in @obstacles
-
-            # Compute the delta between center points
-            delta = Vector.sub obstacle, agent
-
-            # Compute the dot product for projection
-            scalar = delta.dot forward
-
-            # True if obstacle is in front
-            if scalar > 0
-
-                # Cast a ray forward
-                ray = forward.clone().scale @radius
-
-                pro = forward.clone().scale scalar
-
-                # Squared ray length
-                rMagSq = do ray.magSq
-
-                # Squared projection ray length
-                pMagSq = do pro.magSq
-
-                # Squared distance between agent & obstacle
-                distSq = do delta.magSq
-
-                # Combined radius
-                radius = obstacle.radius + agent.radius
-
-                # Optimise by calculating squares
-                radiusSq = radius * radius
-
-                # Is avoidance response is required
-                if distSq < radiusSq and pMagSq < rMagSq
-
-                    console.log +new Date
-
-
-        ###
-
-
-
-
-
-
-
-        # minDistSq = 999999
-        # closest
-
-        # # Find closest obstacle
-        # for obstacle in @obstacles
-
-        #     distSq = agent.distSq obstacle
-
-        #     if distSq < minDistSq
-
-        #         closest = obstacle
-        #         minDistSq = distSq
-


simplify highlight narrow editor via decoration

--- lib/highlighter.coffee
+++ lib/highlighter.coffee
@@ -15,21 +15,24 @@
   regexp: null
   lineMarker: null

-  createMarkerLayerForUi: ->
+  highlightNarrowEditor: ->
     editor = @ui.editor
-    @markerLayerForUi = editor.addMarkerLayer()
-    decorationOptions = {type: 'highlight', class: 'narrow-match-ui'}
-    @decorationLayerForUi = editor.decorateMarkerLayer(@markerLayerForUi, decorationOptions)

-  scanRangesInNarrowEditor: (regExp, fn) ->
-    lines = @ui.editor.buffer.getLines()
-    regExp = cloneRegExp(regExp)
-    for line, row in lines when isNormalItem(item = @ui.items.getItemForRow(row))
+    if @markerLayerForUi?
+      @markerLayerForUi.clear()
+    else
+      @markerLayerForUi = editor.addMarkerLayer()
+      decorationOptions = {type: 'highlight', class: 'narrow-match-ui'}
+      @decorationLayerForUi = editor.decorateMarkerLayer(@markerLayerForUi, decorationOptions)
+
+    regExp = cloneRegExp(@provider.searchRegExp)
+    for line, row in editor.buffer.getLines() when isNormalItem(item = @ui.items.getItemForRow(row))
       regExp.lastIndex = 0
       while match = regExp.exec(item.text)
-        start = new Point(row, match.index + item._lineHeader.length)
-        end = start.translate([0, match[0].length])
-        fn(new Range(start, end))
+        startColumn = match.index + item._lineHeader.length
+        endColumn = startColumn + match[0].length
+        range = [[row, startColumn], [row, endColumn]]
+        @markerLayerForUi.markBufferRange(range, invalidate: 'inside')

   constructor: (@ui) ->
     @provider = @ui.provider
@@ -45,12 +48,7 @@
         # When search and atom-scan did regexp search, it can't use syntax highlight
         # for narrow-editor, so use normal marker decoration to highlight original searchTerm
         if @provider.useRegex
-          @subscriptions.add @ui.onDidRefresh =>
-            @createMarkerLayerForUi() unless @markerLayerForUi?
-            @markerLayerForUi.clear()
-            @scanRangesInNarrowEditor @provider.searchRegExp, (range) =>
-              @markerLayerForUi.markBufferRange(range, invalidate: 'inside')
-
+          @subscriptions.add @ui.onDidRefresh(@highlightNarrowEditor.bind(this))
         @subscriptions.add @ui.onDidStopRefreshing(@refreshAll.bind(this))

     @subscriptions.add @ui.onDidConfirm(@clearCurrentAndLineMarker.bind(this))


1girl -> solo

1girl -> solo

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -66,7 +66,7 @@
       # Do a dummy search
       qs = {
         limit: 1
-        tags: '1girl ' + waifu
+        tags: 'solo ' + waifu
       }
       safebooru.get '/posts.json', { json: true, qs }
       .then (r)=>
@@ -94,7 +94,7 @@
         qs = {
           limit: 1
           random: true
-          tags: '1girl ' + results[0].waifu
+          tags: 'solo ' + results[0].waifu
         }
         safebooru.get '/posts.json', { json: true, qs }
       .then (r)=>


documentation and change some to only listen when addressed

--- src/scripts/hubot-factoid.coffee
+++ src/scripts/hubot-factoid.coffee
@@ -5,10 +5,20 @@
 #   None
 #
 # Configuration:
-#   None
+#   HUBOT_URL - Base url instead of hostname:port, ex: "http://hubot.kodekoan.com"
+#
+# URLS:
+#   /hubot/factoid/:factoid
 #
 # Commands:
-#   None
+#   hubot literal <factoid> - Outputs information about a factoid. Right now only the url
+#   hubot do something - see something random
+#   hubot something random - see somethin random
+#   do something - see something random
+#   something random - Outputs a random factoid
+#   hubot forget <factoid>#<num> - Deletes the <num>th response of <factoid>
+#   hubot forget that - Deletes the last used response
+#   <factoid> - Outputs a random reply that matches <factoid>
 #
 # Notes:
 #   Copyright (c) 2013 Gavin Mogan
@@ -128,23 +138,23 @@
     #res.send require('util').inspect(req.params)

-  robot.hear /^literal(?:\[([*\d]+)\])?\s+(.*)$/, (msg) =>
+  robot.respond /literal(?:\[([*\d]+)\])?\s+(.*)$/, (msg) =>
     page = msg.match[1]
     factoid_name = msg.match[2].trim()
     factoid = robot.factoid.get factoid_name
     if !factoid
       msg.reply "No such factoid"
       return
-    baseurl = process.env.HUBOT_URL || 'http://' + os.hostname() + ':' + robot.server.address().port
+    baseurl = (process.env.HUBOT_URL || 'http://' + os.hostname() + ':' + robot.server.address().port).replace(/\/+$/, '')
     msg.reply baseurl + '/' + robot.name + '/factoid/' + encodeURIComponent(factoid_name)

-  robot.hear /^forget #(\d+)$/, (msg) =>
+  robot.respond /forget #(\d+)$/, (msg) =>
     msg.reply "Sorry, syntax is now \"forget <factoid>#<index from 0>\" or \"forget that\""

-  robot.hear /^forget that$/, (msg) =>
+  robot.resond /forget that$/, (msg) =>
     msg.reply "FIXME - not implemented yet"

-  robot.hear /^forget (.*)#(\d+)$/, (msg) =>
+  robot.respond /forget (.*)#(\d+)$/, (msg) =>
     factoid_name = msg.match[1].trim()
     tid = parseInt(msg.match[2], 10)-1
     factoid = robot.factoid.get factoid_name


Fix duplicate key error

Fix duplicate key error

--- lib/linter-less.coffee
+++ lib/linter-less.coffee
@@ -72,7 +72,7 @@
     parser = new less.Parser(
       verbose: false
       silent: true
-      paths: [@cwd, @config('includePath')...]
+      paths: [@cwd, @getConfig('includePath')...]
       filename: filePath
     )

@@ -81,9 +81,9 @@
       if not err
         try
           tree.toCSS(
-            ieCompat: @config 'ieCompatibilityChecks'
-            strictUnits: @config 'strictUnits'
-            strictMath: @config 'strictMath'
+            ieCompat: @getConfig 'ieCompatibilityChecks'
+            strictUnits: @getConfig 'strictUnits'
+            strictMath: @getConfig 'strictMath'
           )
         catch toCssErr
           err = toCssErr
@@ -96,7 +96,7 @@
         position: [[err.line, err.column], [err.line, err.column]]
       ])

-  config: (key) ->
+  getConfig: (key) ->
     atom.config.get "linter-less.#{key}"

 module.exports = LinterLess


:art: Modify code struct a bit

:art: Modify code struct a bit

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -41,9 +41,9 @@
     @linters.addLinter(linter)

   deleteLinter: (linter) ->
-    if @linters.hasLinter(linter)
-      @linters.deleteLinter(linter)
-      @deleteMessages(linter)
+    return unless @hasLinter(linter)
+    @linters.deleteLinter(linter)
+    @deleteMessages(linter)

   hasLinter: (linter) ->
     @linters.hasLinter(linter)


simplified eq matcher

simplified eq matcher

--- lib/matchers/eq.coffee
+++ lib/matchers/eq.coffee
@@ -1,7 +1,7 @@
 _ = require("underscore")
+s = JSON.stringify

 module.exports = (engine) ->
   engine.defineMatcher "eq",
     match: (actual, expected) -> _.isEqual(actual, expected)
-    failMessage: (actual, expected) -> "expected #{JSON.stringify(actual)} to be equal to #{JSON.stringify(expected)}"
-    reverseFailMessage: (actual, expected) -> "expected #{JSON.stringify(actual)} to not be equal to #{JSON.stringify(expected)}"
+    errorMessage: (expected) -> "be equal to #{s expected}"


fix cli options to separate version and tag parameters

--- lib/modules/cli.coffee
+++ lib/modules/cli.coffee
@@ -12,22 +12,22 @@
     usage: require('./usage')
     version: true
     argv:
-      version:
-        description: 'version id'
+      tag:
+        description: 'tag ID that should be applied to version a new release'
+        alias: 't'
         string: true
       message:
-        description: 'optional message to add to your version commit'
+        description: 'optional message to add to your new tag commit'
         string: true
     dir: path.join(__dirname, 'commands')
     prompt:
-      message: '>'
-      delimiter: '>'
+      override: true
       properties:
-        version:
-          decription: 'Enter new version'
+        tag:
+          decription: 'Enter new tag id'
           type: 'string'
           pattern: /^[0-9]+\.[0-9]+\.[0-9]+$/
-          message: 'Version must be of the form N.N.N (where N is a number)'
+          message: 'Tag must be of the form N.N.N (where N is a number)'
           required: true
           hidden: false
   })
@@ -45,7 +45,7 @@

     if parseInt(minor, 10) % 2
       app.log.warn 'You are using unstable version of node.js. You may experience problems.'
-    app.prompt.override = optimist.argv
+#    app.prompt.override = optimist.argv
     return app.exec app.argv._, callback

 app.exec = (command, callback) ->


Mmmaximum coverage. Again

Mmmaximum coverage. Again

--- lib/prettyprint.coffee
+++ lib/prettyprint.coffee
@@ -24,15 +24,11 @@
 		i++
 	s

-exports.writeln = (text, targetFunction) ->
-	if !targetFunction
-		targetFunction = console.log
+exports.writeln = (text, targetFunction = console.log) ->
 	targetFunction @spaces(offset) + text
 	return

-exports.section = (name, targetFunction) ->
-	if !targetFunction
-		targetFunction = console.log
+exports.section = (name, targetFunction = console.log) ->
 	@writeln name + '...', targetFunction
 	offset += 2
 	offset
@@ -41,14 +37,10 @@
 	offset -= 2
 	offset

-exports.action = (name, targetFunction) ->
-	unless targetFunction?
-		targetFunction = process.stdout.write
+exports.action = (name, targetFunction = process.stdout.write) ->
 	targetFunction @spaces(offset) + name + '...'
 	return

-exports.result = (result, targetFunction) ->
-	unless targetFunction?
-		targetFunction = console.log
+exports.result = (result, targetFunction = console.log) ->
 	targetFunction ' ' + result
 	return