Radically simplified creation and inner state of BuildInvites.

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -369,10 +369,7 @@

     @invites = @komp =>
       if not @previous_successful_build() and @outcome() == "success" and VM.project()
-        users = {}
-        for user in VM.project().users()
-          users[user.login] = user.email()
-        new CI.inner.BuildInvite users
+        new CI.inner.BuildInvite VM.project().users()

   feature_enabled: (feature_name) =>
     @feature_flags()[feature_name]
@@ -768,27 +765,24 @@

   # Select all
   all: () =>
-    for name in @team
-      @inviting[name] true
+    for user in @users
+      @inviting[user.login] true

   # Select none
   none: () =>
-    for name in @team
-      @inviting[name] false
-
-  constructor: (team, json={}) ->
+    for user in @users
+      @inviting[user.login] false
+
+  constructor: (users, json={}) ->
     super json
-    @team = []
-    @email = {}
+    @users = users
     @inviting = {}
     # Prepopulate the list of team members to invite with the ones
     # whose email addresses we already know.
-    for name, email of team
-      @team.push name
-      @email[name] = @observable email
-      @inviting[name] = @observable !!email
+    for user in users
+      @inviting[user.login] = @observable !!user.email()

   send: () =>
-    for name, email of @inviting when email()
-      VM.project().invite_team_member name, email
-
+    for user of @users when inviting[user.login] and user.email()
+      VM.project().invite_team_member user.login, user.email()
+


Rollback change

Rollback change


Update html lang attribute when initing the locales


Resolve filePath in Project.buildBuffer

Resolve filePath in Project.buildBuffer

--- src/app/project.coffee
+++ src/app/project.coffee
@@ -267,6 +267,7 @@
   #
   # Returns the {Buffer}.
   buildBuffer: (filePath, initialText) ->
+    filePath = @resolve(filePath) if filePath?
     buffer = new TextBuffer({project: this, filePath, initialText})
     @addBuffer(buffer)
     @trigger 'buffer-created', buffer


Changed score history to reflect the best score, no the average

--- src/pool.coffee
+++ src/pool.coffee
@@ -227,13 +227,13 @@
         ## Store the average
         scores = (a.score for a in @_scoredGenes)
         @average = evo.util.mean scores
-        @_history.push @average

         ## Sort the _scoredGenes by score
         @_scoredGenes = @_scoredGenes.sort (a,b)-> a.score - b.score

         ## add the top scoring genes
         top_pool = @_scoredGenes.reverse()[0..@config.ratios.top * @config.size]
+        @_history.push @top_pool.score

         ## Amount remaining to breed
         size = @config.size # - top_pool.length


Remove src from NODE_PATH

Remove src from NODE_PATH

--- src/atom-window.coffee
+++ src/atom-window.coffee
@@ -46,7 +46,6 @@
   setupNodePath: (resourcePath) ->
     paths = [
       'exports'
-      'src'
       'node_modules'
     ]


Add data function to vuejs component atom snippet

Signed-off-by: Salim Djerbouh <64b2b6d12bfe4baae7dad3d018f8cbf6b0e7a044@caddydz.me>

--- src/atom/snippets.cson
+++ src/atom/snippets.cson
@@ -1,21 +1,21 @@
 '.source.php':
-  'comment':
-    'prefix' : 'comment'
-    'body': '/**\n * $1\n * @param $2\n * @var $3\n * @return $4\n */$5'
+	'comment':
+		'prefix' : 'comment'
+		'body': '/**\n * $1\n * @param $2\n * @var $3\n * @return $4\n */$5'
 '.blade.php':
-  'bstext':
-    'prefix' : 'bstext'
-    'body': '
\n\t$2:\n\t<input type="text" class="form-control" name="$1" id="$1">\n
\n$3'
-  'Vue JS Router':
-    'prefix' : 'router'
-    'body': '<router-link to="${1:url}" exact>${2:title}</router-link>\n\t$3'
-  'Localization retrieval':
-    'prefix' : 'translate'
-    'body': '{{ __(\'${1}\') }}'
+	'bstext':
+		'prefix' : 'bstext'
+		'body': '
\n\t$2:\n\t<input type="text" class="form-control" name="$1" id="$1">\n
\n$3'
+	'Vue JS Router':
+		'prefix' : 'router'
+		'body': '<router-link to="${1:url}" exact>${2:title}</router-link>\n\t$3'
+	'Localization retrieval':
+		'prefix' : 'translate'
+		'body': '{{ __(\'${1}\') }}'
 '.source.js':
-  'VueJS component':
-    'prefix' : 'component'
-    'body': 'Vue.component (\'${1:name}\', {\n\tprops: [\n\t\t\'${2:property}\',\n\t],\n\ttemplate: `\n\t\t${3:html}\n\t`,\n\tmethods: {\n\t\t${4:method_name}() {\n\t\t\t${5:// code}\n\t\t}\n\t}\n});\n$6'
-  'new vue':
-    'prefix' : 'new vue'
-    'body': 'new Vue({\n\tel: \'${1:#app}\',\n\tdata: {\n\t\t${2:name} : \'${3:value}\',\n\t}\n});\n$7'
+	'VueJS component':
+		'prefix' : 'component'
+		'body': 'Vue.component (\'${1:name}\', {\n\tprops: [\n\t\t\'${2:property}\',\n\t],\n\ttemplate: `\n\t\t${3:html}\n\t`,\n\tdata: function(){\n\t\treturn {\n\t\t\t${4:key}: ${5:value}\n\t\t}\n\t}\n\tmethods: {\n\t\t${6:method_name}() {\n\t\t\t${7:// code}\n\t\t}\n\t}\n});\n$8'
+	'new vue':
+		'prefix' : 'new vue'
+		'body': 'new Vue({\n\tel: \'${1:#app}\',\n\tdata: {\n\t\t${2:name} : \'${3:value}\',\n\t}\n});\n$7'


Retry initial websocket request in case of error

--- src/quip.coffee
+++ src/quip.coffee
@@ -11,6 +11,7 @@
 class QuipHubot extends Adapter
   constructor: (robot) ->
     @robot = robot
+    @retries = 0
     super

   send: (envelope, strings...) ->
@@ -28,13 +29,20 @@
     return @robot.logger.error "No access token provided to Hubot" unless options.token

     @client = new Quip.Client {accessToken: options.token}
-    url = @client.getWebsocket(@.websocketUrl)
+    @client.getWebsocket(@.websocketUrl)

   websocketUrl: (error, response) =>
-    @robot.logger.error error if error
-    @socketUrl = response.url
-    @robot.name = "https://quip.com/" + response.user_id
-    @connect()
+    if error and @retries < 5
+      @robot.logger.error error
+      @retries++
+      @logger.info "Trying again in %ds", @retries * 1000
+      setTimeout =>
+        @client.getWebsocket(@.websocketUrl)
+      , @retries * 1000
+    else
+      @socketUrl = response.url
+      @robot.name = "https://quip.com/" + response.user_id
+      @connect()

   messageSent: (error, response) =>
     @robot.logger.error error if error


copy-paste error

copy-paste error

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -86,9 +86,9 @@
     Zotero.debug('[better' + '-' + 'bibtex] ' + str, level)

 Zotero.BetterBibTeX.extensionConflicts = ->
-  AddonManager.getAddonByID('zoteromaps@zotero.org', (maps) ->
-    return unless maps
-    return if Services.vc.compare(zutilo.version, '1.0.10') > 0
+  AddonManager.getAddonByID('zoteromaps@zotero.org', (extension) ->
+    return unless extension
+    return if Services.vc.compare(extension.version, '1.0.10') > 0
     Zotero.BetterBibTeX.removeTranslators()
     Zotero.BetterBibTeX.disabled = '''
       Better BibTeX has been disabled because it has detected conflicting extension "zotero-maps" 1.0.10 or
@@ -101,9 +101,9 @@
     Zotero.BetterBibTeX.flash('Better BibTeX has been disabled', Zotero.BetterBibTeX.disabled)
   )

-  AddonManager.getAddonByID('zutilo@www.wesailatdawn.com', (zutilo) ->
-    return unless zutilo
-    return if Services.vc.compare(zutilo.version, '1.2.10.1') > 0
+  AddonManager.getAddonByID('zutilo@www.wesailatdawn.com', (extension) ->
+    return unless extension
+    return if Services.vc.compare(extension.version, '1.2.10.1') > 0
     Zotero.BetterBibTeX.removeTranslators()
     Zotero.BetterBibTeX.disabled = '''
       Better BibTeX has been disabled because it has detected conflicting extension "zutilo" 1.2.10.1 or
@@ -120,9 +120,9 @@
     Zotero.BetterBibTeX.flash('Better BibTeX has been disabled', Zotero.BetterBibTeX.disabled)
   )

-  AddonManager.getAddonByID('{359f0058-a6ca-443e-8dd8-09868141bebc}', (recoll) ->
-    return unless recoll
-    return if Services.vc.compare(recoll.version, '1.2.3') > 0
+  AddonManager.getAddonByID('{359f0058-a6ca-443e-8dd8-09868141bebc}', (extension) ->
+    return unless extension
+    return if Services.vc.compare(extension.version, '1.2.3') > 0
     Zotero.BetterBibTeX.removeTranslators()
     Zotero.BetterBibTeX.disabled = '''
       Better BibTeX has been disabled because it has detected conflicting extension "recoll-firefox" 1.2.3 or


Make grunt configs for staging, production

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -205,16 +205,17 @@
             target_with_inlined_content = target_content.replace replacement.placeholder, input_content_stringified
             grunt.file.write(target_filename, target_with_inlined_content)

-  grunt.registerTask 'jail_iframe', []
-  grunt.registerTask 'compile',  [ 'clean',
-    'copy:build',  'copy:extension_events', 'coffee', 'copy:config_development',
-    'sass', 'cssUrlEmbed', 'cssmin',
-    'concat', 'mocha', 'uglify', 'code_inliner',
-    'shell:gzip_js_files', 'copy:dist'
-  ]
-
-  grunt.registerTask 'default', ['compile']
-  grunt.registerTask 'server',  ['compile']
+  grunt.registerTask 'preprocessor',  [
+    'clean', 'copy:build', 'copy:extension_events', 'coffee', 'sass', 'cssUrlEmbed', 'cssmin', ]
+
+  grunt.registerTask 'postprocessor', [
+    'concat', 'mocha', 'uglify', 'code_inliner', 'shell:gzip_js_files', 'copy:dist' ]
+
+  grunt.registerTask 'compile_develop',   [ 'preprocessor', 'copy:config_development', 'postprocessor' ]
+  grunt.registerTask 'compile_staging',   [ 'preprocessor', 'copy:config_staging',     'postprocessor' ]
+  grunt.registerTask 'compile_production',[ 'preprocessor', 'copy:config_production',  'postprocessor' ]
+
+  grunt.registerTask 'default', ['compile_develop']

   grunt.loadNpmTasks 'grunt-contrib-sass'
   grunt.loadNpmTasks 'grunt-contrib-uglify'


Improvement: abort grunt on compression failure

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -62,6 +62,7 @@
           'build/js/loader/loader_common.min.js':       ['build/js/loader/loader_common.js']
     shell:
       gzip_js_files:
+        options: failOnError: true
         command: compress_command('build/js/loader/loader_common.min.js')
     copy:
       dist_loader_aliases:


Coffeescript files have to be compiled before any other

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -68,7 +68,7 @@
       all: ['test/*.html']
     watch:
       files: ['src/js/**/*', 'src/css/**/*', 'test/**/*', 'grunt.js', 'libs/**/*.js', 'plugins/**/*.js', 'wrap/*.js']
-      tasks: ['jshint', 'qunit', 'coffee', 'concat', 'less', 'copy']
+      tasks: ['coffee', 'jshint', 'qunit', 'concat', 'less', 'copy']
     jshint:
       all: ['grunt.js', 'src/js/**/*.js', 'test/**/*.js']
       options:
@@ -123,7 +123,7 @@
           'dist/server/easyXDM/easyXDM.min.js':             ['libs/easyXDM.js']
           'dist/easyXDM/easyXDM.min.js':                    ['libs/easyXDM.js']

-  grunt.registerTask 'default', ['jshint', 'qunit', 'less', 'coffee', 'concat', 'uglify', 'copy']
+  grunt.registerTask 'default', ['coffee', 'jshint', 'qunit', 'less', 'concat', 'uglify', 'copy']
   grunt.registerTask 'server',  ['coffee', 'concat', 'uglify', 'less', 'copy']

   grunt.loadNpmTasks 'grunt-contrib-less'


Update gruntfile to remove the various DEPRECATED bits.

This can't be merged until the chrome+firefox extensions have been
updated by users, and that might take a few weeks.

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -37,12 +37,6 @@
           'build/js/jail_iframe/wrap/last.js'
         ]
         dest: 'build/jail_iframe.js'
-      loader_DEPRECATED:
-        src: [
-          'build/js/loader/loader_common.js'
-          'build/js/loader/loader_basic.js'
-        ]
-        dest: 'build/factlink.js'
       loader_basic:
         src: [
           'build/js/loader/loader_common.js'
@@ -66,9 +60,6 @@
         src: 'build/css/basic.min.css'
         dest: 'build/jail_iframe.js' # See jail_iframe/initializers/style.coffee

-      jail_iframe_loader_DEPRECATED:
-        src: 'build/jail_iframe.js'
-        dest: 'build/factlink.js'
       jail_iframe_loader_basic:
         src: 'build/jail_iframe.js'
         dest: 'build/factlink_loader_basic.js'
@@ -79,9 +70,6 @@
         src: 'build/jail_iframe.js'
         dest: 'build/factlink_loader_bookmarklet.js'

-      jail_iframe_loader_DEPRECATED_min:
-        src: 'build/jail_iframe.min.js'
-        dest: 'build/factlink.min.js'
       jail_iframe_loader_basic_min:
         src: 'build/jail_iframe.min.js'
         dest: 'build/factlink_loader_basic.min.js'
@@ -121,7 +109,6 @@
           'build/factlink.stop_annotating.min.js':    ['build/factlink.stop_annotating.js']
           'build/factlink.start_highlighting.min.js': ['build/factlink.start_highlighting.js']
           'build/factlink.stop_highlighting.min.js':  ['build/factlink.stop_highlighting.js']
-          'build/factlink.min.js':                    ['build/factlink.js']
           'build/factlink_loader_basic.min.js':       ['build/factlink_loader_basic.js']
           'build/factlink_loader_publishers.min.js':  ['build/factlink_loader_publishers.js']
           'build/factlink_loader_bookmarklet.min.js': ['build/factlink_loader_bookmarklet.js']


add use case when rebadylize badyl in bottom state

--- jquery-badyl.coffee
+++ jquery-badyl.coffee
@@ -172,6 +172,9 @@
 						else if @rebadylizeFromState is "unstable"
 							@switchState "unstable",
 								top: "#{@unstableTop}px"
+						else if windowScrollTop > @badylOffsetTop + @badylInnerHeight - windowHeight
+							@switchState "fixBottom"
+
 					when "top"
 						if windowScrollTop >= @badylOffsetTop + @badylInnerHeight - windowHeight
 							@switchState "fixBottom"


Adds onError to images

Adds onError to images

--- src/controls/Markdown/UploadImageButton.coffee
+++ src/controls/Markdown/UploadImageButton.coffee
@@ -23,6 +23,7 @@
   markdownImage: (resp) ->
     img = new Image
     img.src = resp.url if resp.url?
+    img.onError = "this.onerror=null;this.src='http://placehold.it/300x100?text=Image%20is%20processing';"
     img.alt=  resp.alt if resp.alt?
     img.width = resp.width if resp.width?
     img.height = resp.height if resp.height?
@@ -59,4 +60,4 @@
     )`
 )

-module.exports = UploadImageButton+module.exports = UploadImageButton


tap map

tap map

--- js/views/map.coffee
+++ js/views/map.coffee
@@ -17,6 +17,7 @@
       "tap .category-filter": "categories"
       "pagehide": "removeTooltips"
       "tap a": "removeTooltips"
+      "tap #map": "removeTooltips"

     mapElement: "map"
     routeLayer: null


The scope lookup was nonsense in the compilation stage, so added a global types lookup table based on ids

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -198,6 +198,7 @@
     @scopes = [topScope]
     @savedScopes = []
     @classParams = newMap()
+    @types = []
     @isMalformed = no

   macros: ->
@@ -438,29 +439,15 @@
       if declaration.type and declaration.type not instanceof TempType
         throw new Error "assignType: #{name} already has a type"
       declaration.type = type
+      @types[declaration.id] = type
     else
       throw new Error "assignType: #{name} is not declared"

+  typeForId: (id) ->
+    @types[id]
+
   currentDeclarations: ->
     cloneMap @_scope()
-
-  addToDeferredNames: (binding) ->
-    @_deferrableDefinition().deferredBindings.push binding
-
-  addToDeferred: (binding) ->
-    @_scope().deferredBindings.push binding
-
-  addToDefinedNames: (binding) ->
-    @_currentDefinition()?.definedNames?.push binding
-
-  definedNames: ->
-    @_currentDefinition()?.definedNames ? []
-
-  deferredNames: ->
-    @_definition().deferredBindings
-
-  deferredBindings: ->
-    @_scope().deferredBindings

   declareArity: (name, arity) ->
     @declare name, arity: arity
@@ -486,6 +473,24 @@

   actualType: (name) ->
     (type = @type name) and (type not instanceof TempType) and type
+
+  addToDeferredNames: (binding) ->
+    @_deferrableDefinition().deferredBindings.push binding
+
+  addToDeferred: (binding) ->
+    @_scope().deferredBindings.push binding
+
+  addToDefinedNames: (binding) ->
+    @_currentDefinition()?.definedNames?.push binding
+
+  definedNames: ->
+    @_currentDefinition()?.definedNames ? []
+
+  deferredNames: ->
+    @_definition().deferredBindings
+
+  deferredBindings: ->
+    @_scope().deferredBindings

   declarationId: (name) ->
     (@_declaration name)?.id
@@ -2216,21 +2221,13 @@
     # log symbol, contextType
     if contextType and contextType not instanceof TempType
       type = freshInstance ctx, contextType
-      {
-        id: ctx.declarationId symbol
-        type: type
-        translation: nameTranslate ctx, atom, symbol, type, ctx.arity symbol
-      }
+      nameTranslate ctx, atom, symbol, type
     # Inside function only defer compilation if we don't know arity
     else if ctx.isInsideLateScope() and (ctx.isDeclared symbol) or contextType instanceof TempType
       # Typing deferred, use an impricise type var
       type = toConstrained ctx.freshTypeVariable star
       ctx.addToDeferredNames {name: symbol, type: type}
-      {
-        id: ctx.declarationId symbol
-        type: type
-        translation: nameTranslate ctx, atom, symbol, type, ctx.arity symbol
-      }
+      nameTranslate ctx, atom, symbol, type
     else
       # log "deferring in rhs for #{symbol}"
       ctx.doDefer atom, symbol
@@ -2244,15 +2241,19 @@
       else
         (jsBinary "instanceof", exp, (validIdentifier symbol)))]

-nameTranslate = (ctx, atom, symbol, type, arity) ->
-  if atom.label is 'const'
-    switch symbol
-      when 'True' then 'true'
-      when 'False' then 'false'
-      else
-        (jsAccess (validIdentifier symbol), "_value")
-  else
-    (irReference symbol, type, arity)
+nameTranslate = (ctx, atom, symbol, type) ->
+  id = ctx.declarationId symbol
+  arity = ctx.arity symbol
+  translation =
+    if atom.label is 'const'
+      switch symbol
+        when 'True' then 'true'
+        when 'False' then 'false'
+        else
+          (jsAccess (validIdentifier symbol), "_value")
+    else
+      (irReference symbol, id, type, arity)
+  {id, type, translation}

 numericalCompile = (ctx, symbol) ->
   translation = if symbol[0] is '~' then (jsUnary "-", symbol[1...]) else symbol
@@ -2423,8 +2424,8 @@
       translateIr ctx, op
   (jsCall op, (join classParams, (translateIr ctx, args)))

-addConstraintsFrom = (ctx, {name, type}, to) ->
-  typed = ctx.type name
+addConstraintsFrom = (ctx, {id, type}, to) ->
+  typed = ctx.typeForId id
   if typed and (_empty to.constraints) and _notEmpty typed.type.constraints
     inferredType = freshInstance ctx, typed
     sub = matchType inferredType.type, (substitute ctx.substitution, type).type
@@ -2432,14 +2433,14 @@
   else
     to

-irReference = (name, type, arity) ->
-  {ir: irReferenceTranslate, name, type, arity}
-
-irReferenceTranslate = (ctx, {name, type, arity}) ->
+irReference = (name, id, type, arity) ->
+  {ir: irReferenceTranslate, name, id, type, arity}
+
+irReferenceTranslate = (ctx, {name, id, type, arity}) ->
   if ctx.isMethod name, type
     translateIr ctx, (irMethod type, name)
   else
-    finalType = addConstraintsFrom ctx, {name, type}, substitute ctx.substitution, type
+    finalType = addConstraintsFrom ctx, {id, type}, substitute ctx.substitution, type
     classParams = dictsForConstraint ctx, finalType.constraints
     if classParams.length > 0
       (irFunctionTranslate ctx,


resurrection.activity: use suggested tag's title for back reference

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -50,15 +50,15 @@
         {data, type} = token
         if type is "tag"
           if data instanceof JTag then tags.push id: data.getId()
-          else if data.$suggest?  then suggestedTags.push data
+          else if data.$suggest?  then suggestedTags.push data.$suggest

     daisy queue = [
       =>
-        tagCreateJobs = suggestedTags.map (data) ->
+        tagCreateJobs = suggestedTags.map (title) ->
           ->
-            JTag.create title: data.$suggest, (err, tag) ->
+            JTag.create {title}, (err, tag) ->
               tags.push id: tag.getId()
-              createdTags[tag.title] = tag
+              createdTags[title] = tag
               tagCreateJobs.fin()

         dash tagCreateJobs, ->


activity: define `ActivityLikeCount::update` method

--- client/Social/Activity/views/likecount.coffee
+++ client/Social/Activity/views/likecount.coffee
@@ -81,14 +81,19 @@
     KD.remote.cacheable origins, callback

+  update: ->
+
+    {actorsCount} = @getData().interactions.like
+    if actorsCount then @show() else @hide()
+
+    @updatePartial actorsCount
+
+
   viewAppended: ->

     super

-    {interactions: {like: {actorsCount}}} = @getData()
-    if actorsCount then @show() else @hide()
+    @update()

-  pistachio: ->
-
-    "{{ #(interactions.like.actorsCount)}}"
+  pistachio: -> ""


PermissionsView: Save permissions when toggle state is changed.

--- client/admin/lib/views/permissionsform.coffee
+++ client/admin/lib/views/permissionsform.coffee
@@ -124,6 +124,7 @@
       cssClass
       itemClass    : PermissionSwitch
       defaultValue : isChecked ? no
+      callback     : => @save()
     }

     if current in ['admin','owner']
@@ -213,3 +214,8 @@
     createReducedList selectedRoleValues

+  save: ->
+
+    @group.updatePermissions @reducedList(), (err,res) =>
+      return showError err if err
+      new KDNotificationView title: 'Group permissions have been updated.'


Proper fix for the reporting

Proper fix for the reporting

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -462,8 +462,6 @@
   if matchNode 'match', node
     [op, args...] = inside node
     return trueMacros[op.token] hoistableWheres, args...
-
-  noHoistables hoistableWheres

   switch node.type
     when 'comment' then 'null'
@@ -508,10 +506,11 @@
             if isReference node
               defLocation = lookupIdentifier name, node
               if not defLocation
+                names = findMissing hoistableWheres, name
                 if nonstrict
-                  warnings.push "#{name} used but not defined"
+                  warnings.push "#{names.join ', '} used but not defined"
                 else
-                  throw new Error "#{name} used but not defined"
+                  throw new Error "#{names.join ', '} used but not defined"
             escapedName

 isList = (node) ->
@@ -544,6 +543,17 @@

 expandMacro = (macro) ->
   macros[macro]?() ? macro
+
+findMissing = (graphs, name) ->
+  names = []
+  for graph in (graphs or [])
+    if name in graph.names
+      names.push (setToArray graph.missing)...
+      continue
+  if names.length > 0
+    names
+  else
+    [name]

 validIdentifier = (name) ->
   [firstChar] = name


collaboration: allow passing callback

collaboration: allow passing callback

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -768,10 +768,10 @@
       @setMachineUser [target], no  if target

-  setMachineSharingStatus: (status) ->
+  setMachineSharingStatus: (status, callback) ->

     @listChatParticipants (accounts) =>
-      @setMachineUser accounts, status
+      @setMachineUser accounts, status, callback

   setMachineUser: (accounts, share = yes, callback = kd.noop) ->


Fixed explicit typing of functions and added a test

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -640,7 +640,7 @@
   # TODO: should we support bare records?
   #   [a: 2 b: 3]
   form.tea = new Constrained (concatMap _constraints, elems),
-    applyKindFn (tupleType arity), (tea for {tea} in elems)...
+    applyKindFn (tupleType arity), (tea.type for {tea} in elems)...

   if ctx.assignTo()
     combinePatterns compiledElems
@@ -726,6 +726,9 @@
 #     [x, xs...] = elems
 #     (call_ (token_ 'cons-array'), [x, (arrayToConses xs)])

+typeConstrainedCompile = (call) ->
+  [type, constraints...] = _arguments call
+  new Constrained (typeConstraintsCompile constraints), typeCompile type

 typeCompile = (expression) ->
   throw new Error "invalid typeCompile args" unless expression
@@ -973,7 +976,7 @@
       malformed call, 'Missing function result'
     else
       [docs, defs] = partition isComment, defs
-      if defs.length % 2 == 0
+      if isTypeConstraint defs[0]
         [type, body, wheres...] = defs
       else
         [body, wheres...] = defs
@@ -985,7 +988,7 @@
         ctx.declareArity ctx.definitionName(), paramNames
         # Explicit typing
         if type
-          explicitType = quantifyUnbound ctx, typeCompile type
+          explicitType = quantifyUnbound ctx, typeConstrainedCompile type
           ctx.assignType ctx.definitionName(), explicitType

       paramTypeVars = map (-> ctx.freshTypeVariable star), params
@@ -1363,7 +1366,7 @@
 constraintsFromInstance = (ctx, constraint) ->
   {className, type} = constraint
   for instance in (ctx.classNamed className).instances
-    substitution = match instance.type, constraint
+    substitution = matchType instance.type.type, constraint.type
     if not lookupInMap substitution, "could not unify"
       return map ((c) -> substitute substitution, c), instance.constraints
   null
@@ -1466,7 +1469,7 @@

 nameCompile = (ctx, atom, symbol) ->
   contextType = ctx.type symbol
-  # log "nameCompile", symbol, ctx.isInsideLateScope(), contextType, ctx.isDeclared symbol
+  # log "nameCompile", symbol, ctx.isInsideLateScope(), (printType contextType), ctx.isDeclared symbol
   if exp = ctx.assignTo()
     if atom.label is 'const'
       if contextType
@@ -1614,6 +1617,9 @@

 listOf = (args) ->
   args.join ', '
+
+isTypeConstraint = (expression) ->
+  (isCall expression) and (':' is _symbol _operator expression)

 isComment = (expression) ->
   (isCall expression) and ('#' is _symbol _operator expression)
@@ -2287,7 +2293,8 @@
 inSet = (set, name) ->
   set.values[name]

-isSetEmpty = (set) ->
+isSetEmpty =
+isMapEmpty = (set) ->
   set.size is 0

 mapSet =
@@ -2401,15 +2408,15 @@
     newMapWith variable.name, type

 # Returns a substitution
-match = (ctx, t1, t2) ->
+matchType = (ctx, t1, t2) ->
   if t1 instanceof TypeVariable and kindsEq (kind t1), (kind t2)
     newMapWith t1.name, t2
   else if t1 instanceof TypeConstr and t2 instanceof TypeConstr and
     t1.name is t2.name
       emptySubstitution()
   else if t1 instanceof TypeApp and t2 instanceof TypeApp
-    s1 = match t1.op, t2.op
-    s2 = match t1.arg, t2.arg
+    s1 = matchType t1.op, t2.op
+    s2 = matchType t1.arg, t2.arg
     s3 = mergeSubs s1, s2
     s3 or
       newMapWith "could not unify", [(printType t1), (printType t2)]
@@ -2617,7 +2624,7 @@
   else if type instanceof ClassContraint
     "(#{type.className} #{printType type.type})"
   else if type instanceof Constrained
-    "(: #{printType type.type} {#{(map printType, type.constraints).join ' '}})"
+    "(: #{(map printType, join [type.type], type.constraints).join ' '})"
   else if type instanceof TempType
     "(. #{printType type.type})"
   else if Array.isArray type
@@ -2794,6 +2801,7 @@
   compiledDefinitions = definitionList (ctx = new Context), pairs terms
   compiledExpression = topLevelExpression ctx, expression
   types: ctx._scope()
+  subs: filterMap ((name) -> name is 'could not unify'), ctx.substitution
   ast: ast
   compiled: library + compiledDefinitions + compiledExpression

@@ -2884,6 +2892,8 @@
     return
   try
     log (collapse toHtml compiled.ast)
+    if not isMapEmpty compiled.subs
+      log compiled.subs
     if result isnt (eval compiled.compiled)
       log "Wrong result", result
   catch e
@@ -2966,7 +2976,13 @@
         {} False
         xx True))
     {x ..xs} {1}"""
-    "(tail? xs)", no
+  "(tail? xs)", no
+
+  'typed function'
+  """f (fn [x y]
+    (: (Fn Bool String Bool))
+    x)"""
+  """(f True "a")""", yes

   'fib'
   """fibonacci (fn [month] (adults month))


Adding missing replacemant for cyrillic char


TEST:WELCOME Delete Welcome Helper

TEST:WELCOME Delete Welcome Helper

--- client/test/lib/helpers/welcomehelpers.coffee
+++ client/test/lib/helpers/welcomehelpers.coffee
@@ -1,93 +0,0 @@
-helpers = require '../helpers/helpers.js'
-teamsHelpers = require '../helpers/teamshelpers.js'
-myTeamLink = "#{helpers.getUrl(yes)}/Home/my-team"
-
-module.exports =
-
-  seeStackView: (browser) ->
-    stackLink = '.WelcomeStacksView ul.bullets li:nth-of-type(1)'
-    browser
-      .pause 3000
-      .waitForElementVisible '.WelcomeStacksView', 20000
-      .click stackLink
-      .waitForElementVisible '.StackEditor-OnboardingModal', 20000
-
-
-  seeTeamView: (browser) ->
-    teamLink = '.WelcomeStacksView ul.bullets li:nth-of-type(2)'
-    browser
-      .pause 3000
-      .waitForElementVisible '.WelcomeStacksView', 20000
-      .click teamLink
-      .waitForElementVisible '.HomeAppView--section.send-invites', 20000
-
-
-  seeKDInstall: (browser) ->
-    browser
-      .waitForElementVisible '.WelcomeStacksView', 2000
-      .click '.WelcomeStacksView ul.bullets li:nth-of-type(3)'
-      .waitForElementVisible '.HomeAppView--section.kd-cli', 20000
-      .pause 3000
-
-
-  seePendingStackView: (browser) ->
-    pendingStack = '.WelcomeStacksView ul.bullets li:nth-of-type(1)'
-    browser
-      .waitForElementVisible '.WelcomeStacksView', 20000
-      .assert.containsText   pendingStack, 'Your Team Stack is Pending'
-
-
-  seePersonalStackView: (browser, targetUser) ->
-    stackLink = '.WelcomeStacksView ul.bullets li:nth-of-type(2)'
-    browser
-      .pause 3000
-      .waitForElementVisible '.WelcomeStacksView', 20000
-      .click stackLink
-      .waitForElementVisible '.StackEditor-OnboardingModal', 20000
-
-
-  seePricingDetails: (browser) ->
-    browser
-      .pause 2000
-      .click '.HomeAppView--button:nth-of-type(2)', (result) ->
-        if result.state is 'success'
-          helpers.switchBrowser browser, 'http://www.koding.com/pricing'
-
-
-  seeMembers: (browser) ->
-    browser
-      .pause 2000
-      .click '.HomeAppView--button:nth-of-type(1)'
-      .waitForElementVisible '.HomeAppView--section.team-settings', 20000
-      .assert.containsText '.HomeAppView--sectionHeader', 'Team Settings'
-
-
-  seePaymentHistory: (browser) ->
-    browser
-      .scrollToElement '.HomeAppView--billing-form'
-      .click '.HomeAppView--button.custom-link-view.primary:nth-of-type(2)'
-      .pause 3000
-      .assert.containsText '.HomeAppView--sectionHeader', 'Payment History'
-      .waitForElementVisible 'div.HomeAppView--section.HomeTeamInvoicesList', 20000
-
-
-  removeCard: (browser) ->
-    browser
-      .scrollToElement '.HomeAppView--billing-form'
-      .click '.HomeAppView--button.custom-link-view.primary:nth-of-type(1)'
-      .pause 3000
-
-
-  gotoSettingsMenu: (browser, menuItemSelector) ->
-    menuSelector      = '.SidebarMenu.kdcontextmenu .kdlistitemview-contextitem.default'
-    teamnameSelector  = '#kdmaincontainer.with-sidebar #main-sidebar .logo-wrapper .team-name'
-
-    browser
-      .waitForElementVisible '#main-sidebar', 20000
-      .click '#main-sidebar'
-      .waitForElementVisible teamnameSelector, 20000
-      .click teamnameSelector
-      .waitForElementVisible menuSelector, 2000
-      .pause 3000
-      .click menuItemSelector
-      .pause 3000


Fix where compilation and quiting definition list if required to speed up compilation

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -207,14 +207,20 @@
     @classParams = newMap()
     @types = []
     @isMalformed = no
+    @importedModules = newSet()
     @_requested = newMap()

   req: (moduleName, names) ->
     addToMap @_requested, moduleName, names
+    if not inSet @importedModules, moduleName
+      @_requesting = yes

   requested: ->
     if @_requested.size > 0
       @_requested
+
+  isRequesting: ->
+    @_requesting

   markMalformed: ->
     @isMalformed = yes
@@ -1418,7 +1424,13 @@
       malformed ctx, lhs, 'missing value in definition'
       # TODO: take into account fakes (using better pairs function) and new lines
       rhs = fake_()
-    definitionPairCompile ctx, lhs, rhs)
+    compiled = definitionPairCompile ctx, lhs, rhs
+    if ctx.isRequesting()
+      break
+    compiled)
+
+  if ctx.isRequesting()
+    []

   shouldRecompile = yes
   while shouldRecompile
@@ -1482,6 +1494,7 @@

         # have to promote constraints from just compiled dependencies
         depConstraints = concat (for dep in binding.deps or [] when not dep.defining and depCanonicalType = ctx.finalType dep.id, dep.scopeIndex
+          # TODO: add origin
           constraintsFromCanonicalType ctx, depCanonicalType, dep.type)
         allConstraints = (join binding.constraints or [], depConstraints)

@@ -2345,8 +2358,7 @@
       jsNoop()

 findDefinitionsIncludingDeps = (ctx, names) ->
-  # console.log names, ((findDeps ctx) names), (setToArray ((findDeps ctx) names)), (lookupInMap ctx.auxiliaries(), 'lines').deps if 'lines' in names
-  findDefinitions ctx, setToArray (findDeps ctx) names
+  findDefinitions ctx, setToArray (findDeps ctx) unique names

 findDefinitions = (ctx, names) ->
   auxiliaries = ctx.auxiliaries()
@@ -5671,6 +5683,7 @@
   topScope.classes = concatMaps topScope.classes, classes
   ctx.scopeIndex += compiledModule.savedScopes.length
   ctx.nameIndex += compiledModule.nameIndex
+  addToSet ctx.importedModules, moduleName
   ctx

 collectRequiresFor = (name) ->
@@ -5913,6 +5926,9 @@

 # Utils

+unique = (list) ->
+  setToArray arrayToSet list
+
 join = (seq1, seq2) ->
   seq1.concat seq2


Set json setting to true

Set json setting to true

--- src/star.coffee
+++ src/star.coffee
@@ -28,6 +28,7 @@
     process.stdout.write '\u2B50  ' if process.platform is 'darwin'
     process.stdout.write "Starring #{packageName} "
     requestSettings =
+      json: true
       url: "#{config.getAtomPackagesUrl()}/#{packageName}/star"
       headers:
         authorization: token


Write logged output to standard error

Write logged output to standard error

--- src/test.coffee
+++ src/test.coffee
@@ -53,7 +53,7 @@
       @spawn cmd, cmdArgs, cmdOptions, (code) ->
         try
           loggedOutput = fs.readFileSync(logFilePath, 'utf8')
-          process.error.write("#{loggedOutput}\n") if loggedOutput
+          process.stderr.write("#{loggedOutput}\n") if loggedOutput

         if code is 0
           process.stdout.write 'Tests passed\n'.green


Remove unused require

Remove unused require

--- src/test.coffee
+++ src/test.coffee
@@ -3,7 +3,6 @@
 optimist = require 'optimist'

 Command = require './command'
-fs = require './fs'

 module.exports =
 class Test extends Command


increase gutters

increase gutters

--- apps/user_profile/client/collection.coffee
+++ apps/user_profile/client/collection.coffee
@@ -16,7 +16,7 @@
       collection: new Artworks
       artworkSize: 'larger'
       numberOfColumns: 3
-      gutterWidth: 40
+      gutterWidth: 80
     @$el.infiniteScroll @nextPage
     @artworkCollection.on 'change:name', @renderName
     @nextPage()


sanity check access flag parsing

sanity check access flag parsing

--- src/util.coffee
+++ src/util.coffee
@@ -55,7 +55,7 @@
   (bytecode_string.charCodeAt(i) & 0xFF for i in [0...bytecode_string.length])

 root.parse_flags = (flag_byte) ->
-  {
+  flags = {
     public:       flag_byte & 0x1
     private:      flag_byte & 0x2
     protected:    flag_byte & 0x4
@@ -70,6 +70,10 @@
     abstract:     flag_byte & 0x400
     strict:       flag_byte & 0x800
   }
+  # quick sanity check
+  s = root.sum(1 for f in ['public','private','protected'] when flags[f] != 0)
+  throw new Error "Too many access flags, invalid classfile parse" if s > 1
+  flags

 class root.BytesArray
   constructor: (@raw_array) ->


fix rerender header

fix rerender header

--- app/controllers/server/applicationController.coffee
+++ app/controllers/server/applicationController.coffee
@@ -10,7 +10,7 @@

   @beforeAction 'bootstrap'#, only: 'welcome'

-  @beforeAction 'authCheck'
+  #@beforeAction 'authCheck'
   authCheck: ->
     userId = @request.cookies.user
     if userId == null || userId == undefined
@@ -33,9 +33,9 @@
     # for every model you add, you can add it to the bootstrap
     # dataset by using this async helper.
     _.series [
-      # (next) => App.Session.all (error, sessions) =>
-      #   data.sessions = sessions
-      #   next()
+      (next) => App.Session.all (error, sessions) =>
+        data.sessions = sessions
+        next()
       # (next) => App.Tasklist.all (error, tasklists) =>
       #   data.tasklists = tasklists
       #   next()


Temporarily turn ssl off for the droneshare app (till #70 fixed)

--- src/scripts/services/dapiServices.coffee
+++ src/scripts/services/dapiServices.coffee
@@ -44,7 +44,7 @@
     base = if useLocalServer
       'http://localhost:8080'
     else
-      'https://api.3drobotics.com'
+      'http://api.droneshare.com'
       # Normally api.3drobotics.com is recommended, but if you'd like cookies used for your application's login info
       # you can create a CNAME under your domain that points to api.3drobotics.com and reference the CNAME here.


fix(task-repeater): test renamed

fix(task-repeater): test renamed

--- src/spec/repeater-task-queue.spec.coffee
+++ src/spec/repeater-task-queue.spec.coffee
@@ -16,7 +16,7 @@
     @client = new SphereClient sphereConfig

-  it 'should finally resolve after three tries', (done) ->
+  it 'should finally resolve after four tries', (done) ->

     callsMap = {
       0: { statusCode: 500, message: 'ETIMEDOUT' }


test(InventorySy): restockableInDays test refactor

--- src/spec/sync/inventory-sync.spec.coffee
+++ src/spec/sync/inventory-sync.spec.coffee
@@ -110,51 +110,44 @@
       expect(update.actions[0].action).toBe 'setExpectedDelivery'
       expect(update.actions[0].expectedDelivery).toBeUndefined()

-    it 'should add restockableInDays', ->
+    iit 'no differences in restockableInDays', ->
+      ie =
+        id: '234'
+        sku: 'bcd'
+        quantityOnStock: 3
+        restockableInDays: 8
+      update = @sync.buildActions(ie, ie).getUpdatePayload()
+      expect(update).toBeUndefined()
+      updateId = @sync.buildActions(ie, ie).getUpdateId()
+      expect(updateId).toBe '234'
+
+    iit 'more differences in restockableInDays', ->
       ieNew =
         sku: 'ijk'
-        quantityOnStock: 5
-        restockableInDays: 7
-        version: 1
+        quantityOnStock: 3
+        restockableInDays: 20
       ieOld =
         sku: 'ijk'
-        quantityOnStock: 5
-        version: 1
+        quantityOnStock: 3
+        restockableInDays: 4
       update = @sync.buildActions(ieNew, ieOld).getUpdatePayload()
       expect(update).toBeDefined()
       expect(update.actions[0].action).toBe 'setRestockableInDays'
-      expect(update.actions[0].restockableInDays).toBe 7
-
-    it 'should update restockableInDays', ->
+      expect(update.actions[0].restockableInDays).toBe 20
+
+    iit 'less differences in restockableInDays', ->
       ieNew =
         sku: 'oik'
-        quantityOnStock: 0
-        restockableInDays: 3
-        version: 1
+        quantityOnStock: 3
+        restockableInDays: 5
       ieOld =
         sku: 'oik'
-        quantityOnStock: 0
-        restockableInDays: 7
-        version: 1
+        quantityOnStock: 3
+        restockableInDays: 15
       update = @sync.buildActions(ieNew, ieOld).getUpdatePayload()
       expect(update).toBeDefined()
       expect(update.actions[0].action).toBe 'setRestockableInDays'
-      expect(update.actions[0].restockableInDays).toBe 3
-
-    it 'should remove restockableInDays', ->
-      ieNew =
-        sku: 'oik'
-        quantityOnStock: 0
-        version: 1
-      ieOld =
-        sku: 'oik'
-        quantityOnStock: 0
-        version: 1
-        restockableInDays: 10
-      update = @sync.buildActions(ieNew, ieOld).getUpdatePayload()
-      expect(update).toBeDefined()
-      expect(update.actions[0].action).toBe 'setRestockableInDays'
-      expect(update.actions[0].restockableInDays).toBeUndefined()
+      expect(update.actions[0].restockableInDays).toBe 5

     describe 'actionsMapCustom', ->
       ieNew =


Allow creating VMs if they are free.

Allow creating VMs if they are free.

--- client/app/MainApp/paymentcontroller.coffee
+++ client/app/MainApp/paymentcontroller.coffee
@@ -304,7 +304,7 @@
     if needBilling
       modal.buttons.Billing.show()
     else
-      if subscription.status is 'canceled'
+      if subscription.status is 'canceled' and amount > 0
         modal.buttons.ReActivate.show()
       else
         modal.buttons.Yes.show()


IDEView: Explicitly throw errors on createPane_.

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -201,11 +201,11 @@

   createPane_: (view, paneOptions, paneData) ->

-      return new Error 'Missing argument for createPane_ helper'
     if not view or not paneOptions
+      return  throw new Error 'Missing argument for createPane_ helper'

     unless view instanceof KDView
-      return new Error 'View must be an instance of KDView'
+      return  throw new Error 'View must be an instance of KDView'

     if view instanceof IDEEditorPane
       paneOptions.name = @trimUntitledFileName paneOptions.name


IDE: Say good bye to open source.

IDE: Say good bye to open source.

--- client/ide/views/statusbar/statusbar.coffee
+++ client/ide/views/statusbar/statusbar.coffee
@@ -27,11 +27,6 @@
       partial  : ''
       cssClass : 'icon help'
       click    : -> new HelpSupportModal
-
-    @addSubView new KDCustomHTMLView
-      partial  : ''
-      cssClass : 'icon github'
-      click    : -> KD.utils.createExternalLink 'https://github.com/koding/IDE'

     @addSubView new KDCustomHTMLView
       partial  : ''


Add color defaults

Add color defaults

--- src/view.coffee
+++ src/view.coffee
@@ -121,6 +121,10 @@
       unless option of @opts
         @opts[option] = DEFAULT_OPTIONS[option]

+    for color of DEFAULT_OPTIONS.colors
+      unless color of @opts.colors
+        @opts.colors[color] = DEFAULT_OPTIONS.colors[color]
+
   # Simple method for clearing caches
   clearCache: ->
     @beginDraw()


cistrans: working towards fancier transitions

--- cistrans/cistrans.coffee
+++ cistrans/cistrans.coffee
@@ -398,13 +398,15 @@
               chr = data.pmark[td].chr
               title = "#{td} (chr #{chr}, #{onedig(pos)} cM)"
               d3.select("text#pxgtitle").text(title)
-              if lastMarker isnt ""
+              if lastMarker is ""
+                  plotPXG td
+              else
                   markerClick[lastMarker] = 0
                   d3.select("#circle#{lastMarker}").attr("opacity", 0).attr("fill",purple).attr("stroke","none")
+                  revPXG td
               lastMarker = td
               markerClick[td] = 1
               d3.select(this).attr("opacity", 1).attr("fill",altpink).attr("stroke",purple)
-              plotPXG td

     plotPXG = (marker) ->
       d3.selectAll(".plotPXG").remove()
@@ -466,6 +468,7 @@
               .data(means)
               .enter()
               .append("line")
+              .attr("class", "PXGvert")
               .attr("y1", top[2])
               .attr("y2", bottom[2])
               .attr("x1", (d,i) -> return if(chr is "X") then pxgXscaleX(i) else pxgXscaleA(i))
@@ -477,6 +480,7 @@
               .data(genotypes)
               .enter()
               .append("text")
+              .attr("class", "PXGgeno")
               .text((d) -> d)
               .attr("y", bottom[2] + pad.bottom*0.25)
               .attr("x",  (d,i) -> return if(chr is "X") then pxgXscaleX(i) else pxgXscaleA(i))
@@ -485,6 +489,7 @@
               .data(["Female", "Male"])
               .enter()
               .append("text")
+              .attr("class", "PXGsex")
               .attr("id", "sextext")
               .text((d) -> d)
               .attr("y", bottom[j] + pad.bottom*0.75)
@@ -529,6 +534,7 @@
          .data(means)
          .enter()
          .append("line")
+         .attr("class", "PXGmeans")
          .attr("x1", (d,i) ->
             return if chr is "X" then pxgXscaleX(i)-jitterAmount*2 else pxgXscaleA(i)-jitterAmount*2)
          .attr("x2", (d,i) ->
@@ -539,6 +545,52 @@
          .attr("stroke-width", 4)
          .on("mouseover", efftip)
          .on("mouseout", -> d3.selectAll("#efftip").remove())
+
+
+    revPXG = (marker) ->
+      # calculate group averages
+      chr = data.pmark[marker].chr
+      if(chr is "X")
+        means = [0,0,0,0]
+        n = [0,0,0,0]
+        male = [0,0,1,1]
+        genotypes = ["RR", "BR", "BY", "RY"]
+        sexcenter = [(pxgXscaleX(0) + pxgXscaleX(1))/2,
+                     (pxgXscaleX(2) + pxgXscaleX(3))/2]
+      else
+        means = [0,0,0,0,0,0]
+        n = [0,0,0,0,0,0]
+        male = [0,0,0,1,1,1]
+        genotypes = ["BB", "BR", "RR", "BB", "BR", "RR"]
+        sexcenter = [pxgXscaleA(1), pxgXscaleA(4)]
+      for i of data.individuals
+         g = Math.abs(data.geno[marker][i])
+         sx = data.sex[i]
+         if(data.pmark[marker].chr is "X")
+           x = sx*2+g-1
+         else
+           x = sx*3+g-1
+         means[x] += probe_data.pheno[i]
+         n[x]++
+      for i of means
+        means[i] /= n[i]
+
+      d3.selectAll("line.PXGvert")
+         .data(genotypes)
+      d3.selectAll("line.PXGmeans")
+         .data(means)
+      d3.selectAll("text.PXGgeno")
+      d3.selectAll("text.PXGsex")
+         .attr("x", (d, i) -> sexcenter[i])
+
+      d3.selectAll("circle.plotPXG")
+         .transition().duration(1000)
+        .attr("cx", (d,i) ->
+              g = Math.abs(data.geno[marker][i])
+              sx = data.sex[i]
+              if(data.pmark[marker].chr is "X")
+                return pxgXscaleX(sx*2+g-1)+jitter[i]
+              pxgXscaleA(sx*3+g-1)+jitter[i])

   chrindex = {}
   for c,i in data.chrnames


Mobile users will now show up online.

Mobile users will now show up online.

--- ext/user_heartbeat.coffee
+++ ext/user_heartbeat.coffee
@@ -8,6 +8,13 @@
   if clientId && userId && message.channel.match(/meta\/connect/ig)
     console.log "User ID: #{userId}"
     startHeartbeat(userId,clientId)
+
+  if clientId && message.channel.match(/meta\/subscribe/ig)
+    privateChannelMatch = /^\/users\/(.{24})\/private$/ig.exec(message.subscription)
+    if privateChannelMatch
+      userId = privateChannelMatch[1]
+      console.log "User ID: #{userId}"
+      startHeartbeat(userId,clientId) if userId

   callback(message)


use shaded texture for shapeways model

use shaded texture for shapeways model

--- fabricator/archive.coffee
+++ fabricator/archive.coffee
@@ -32,7 +32,7 @@
     console.log "Getting texture"
     httpGetOpts =
       bufferType: "buffer"
-      url: "#{config.imageUrl}/map?box=#{boxParams}&outsize=2000,2000"
+      url: "#{config.imageUrl}/map?box=#{boxParams}&outsize=2000,2000&shading=true"
     http.get httpGetOpts, (err, result) =>
       if err?
         console.log "Failed getting texture @ #{httpGetOpts.url}"


Migration: Removed unknown data from unknown method

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -536,7 +536,7 @@

       # embedly supports many error types. we could display those to the user
       when 'error'
-        log 'Embedding error ', data.error_type, data.error_message
+        log 'Embedding error '
         return 'error'

       else


utils: escape backslashes if it exists

utils: escape backslashes if it exists

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -52,6 +52,8 @@
   applyMarkdown: (text, options = {})->

     return null unless text
+
+    text = text.replace '\\', '\\\\'

     options.gfm       ?= true
     options.pedantic  ?= false


Revert "SelectionManager#findNodeAndOffsetForLocation clamps to DOM nodes"

This reverts commit 23304d00848cd704e04fdaac9ddf254f06a2db55.

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -173,14 +173,9 @@
     [container, offset]

   findNodeAndOffsetForLocation: (location) ->
-    index = Math.min(location.index, @getBlockCount())
     offset = 0
-
-    for currentNode in @getNodesForIndex(index)
+    for currentNode in @getNodesForIndex(location.index)
       length = nodeLength(currentNode)
-      lastNode = currentNode
-      lastOffset = offset
-
       if location.offset <= offset + length
         if currentNode.nodeType is Node.TEXT_NODE
           node = currentNode
@@ -189,15 +184,10 @@
         else if not node
           node = currentNode
           nodeOffset = offset
-
       offset += length
       break if offset > location.offset

-    if node?
-      nodeOffset = lastOffset if index < location.index
-      [node, nodeOffset]
-    else
-      [lastNode, lastOffset]
+    [node, nodeOffset]

   getNodesForIndex: (index) ->
     nodes = []
@@ -220,16 +210,6 @@
         nodes.push(node)

     nodes
-
-  getBlockCount: ->
-    walker = DOM.walkTree(@element)
-    blockCount = 0
-
-    while walker.nextNode()
-      node = walker.currentNode
-      blockCount++ if nodeIsBlockStartComment(node)
-
-    blockCount

   getLocationRangeAtPoint: ([clientX, clientY]) ->
     if document.caretPositionFromPoint


Sortable destruction bug fix.

Sortable destruction bug fix.

--- client/collection.coffee
+++ client/collection.coffee
@@ -36,7 +36,7 @@
   # Do not proceed if user is not collection author
   if collection?.author._id isnt Meteor.personId()
     # Remove sortable functionality in case it was previously enabled
-    $(@findAll '.collection-publications').sortable "destroy"
+    $(@findAll '.collection-publications.ui-sortable').sortable "destroy"
     return

   $(@findAll '.collection-publications').sortable


fix(windows): Bad merge

fix(windows): Bad merge

--- src/browser/database-manager.coffee
+++ src/browser/database-manager.coffee
@@ -64,7 +64,7 @@

   closeDatabaseConnections: ->
     for path, val of @_databases
-      @_closeDatabaseConnection(path)
+      @closeDatabaseConnection(path)

   deleteAllDatabases: ->
     Object.keys(@_databases).forEach (path) =>


DOC: add docs to grade service

DOC: add docs to grade service

--- src/app/common/services/outcome-service.coffee
+++ src/app/common/services/outcome-service.coffee
@@ -22,23 +22,50 @@

     calculateTargets: (unit, source, taskStatusFactor) ->
       outcomes = {}
+      # For each learning outcome (LO) -- produce a map with grades containing task scores
+      # calculated from alignment details, task target grade, and task status factor.
+      #
+      # The Task Status Factor for projects will be a value between 0 and 1
+      # In the unit the taskStatusFactor will always be 1 (100%) to show potential values
+      # for the unit -- in effect removing the task status from unit calculations
       _.each unit.ilos, (outcome) ->
+        # Add grade map for this LO to outcomes map
         outcomes[outcome.id] = {
-          0: []
-          1: []
+          # Using 0..3 so that it can be used to calculate the grade scale below
+          0: [] # Pass grade... -- will contain scores for pass grade tasks
+          1: [] # Credit grade... -- etc.
           2: []
           3: []
         }

+      # For each outcome / task alignment...
       _.each source.task_outcome_alignments, (align) ->
+        # Get the task definition
         td = unit.taskDef(align.task_definition_id)
+        # Store a partial score for this task in the relevant outcomes ( outcomes[outcome id][grade] << score )
+        # At this stage it is just rating * taskFactor (1 to 5 times 0 to 1)
         outcomes[align.learning_outcome_id][td.target_grade].push align.rating * taskStatusFactor(td.id)

+      # Finally reduce all of these into one score for each outcome / grade
       _.each outcomes, (outcome, key) ->
+        # For this outcome
         _.each outcome, (tmp, key1) ->
+          # get a scale for the grade
           scale = Math.pow(2, parseInt(key1,10))
+          # Reduce all task partial scores and * grade scale -- replace array with single value
           outcome[key1] = _.reduce(tmp, ((memo, num) -> memo + num), 0) * scale

+      # Returns map of...
+      # {
+      #   <OutcomeID>: {
+      #     0: <score>
+      #     1: <score> ...
+      #   },
+      #   86: {   <--- OutcomeID 86 --> "Programming Principles"
+      #     0: 27 <--- Pass: 27 score (from rating * task status factor * scale reduced)
+      #     1: 53 ...
+      #   },
+      # }
       outcomes

     calculateTaskContribution: (unit, project, task) ->


opt: better log info

opt: better log info

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -22,7 +22,7 @@
 	###
 	constructor: (executor) ->
 		if isLongStackTrace
-			@[$promiseStack] = genTraceInfo()
+			@[$promiseTrace] = genTraceInfo()

 		return if executor == $noop

@@ -211,26 +211,29 @@
 		return if not isObject console

 		hStack = '\n'
-		if isLongStackTrace and p[$promiseStack]
+		if isLongStackTrace and p[$promiseTrace]
+			if p[$settlerTrace]
+				hStack += p[$settlerTrace]
 			while p
-				hStack += p[$promiseStack]
+				hStack += p[$promiseTrace]
 				p = p._pre

 		format = (str) ->
-			if typeof __filename == 'string'
-				str.replace ///.+#{__filename}.+\n///g, ''
+			(if typeof __filename == 'string'
+				str.replace ///.+#{__filename}.+\n?///g, ''
 			else
 				str
-
-		stack = if reason
-			if reason.stack
-				format reason.stack
+			).replace(/\n$/, '')
+
+		console.error 'Unhandled Rejection:', (
+			if reason
+				if reason.stack
+					format reason.stack
+				else
+					reason
 			else
 				reason
-		else
-			reason
-
-		console.error 'Unhandled Rejection:', stack, format(hStack)
+		), format(hStack)

 	isLongStackTrace = false

@@ -397,9 +400,10 @@
 	genTypeError = (msg) ->
 		new TypeError msg

-	genTraceInfo = ->
+	genTraceInfo = (noTitle) ->
 		(new Error).stack
-			.replace 'Error', 'From previous event:'
+			.replace 'Error\n',
+				(if noTitle then '' else ' From previous event:\n')

 	# ************************** Promise Constant **************************

@@ -411,7 +415,8 @@
 	$resolved = 1
 	$pending = 2

-	$promiseStack = '_pStack'
+	$promiseTrace = '_pStack'
+	$settlerTrace = '_sStack'

 	# These are some symbols. They won't be used to store data.
 	$circularChain = 'promise_circular_chain'
@@ -449,7 +454,10 @@
 	 * @return {Function} `(value) -> undefined` A resolve or reject function.
 	###
 	genSettler = (self, state) -> (value) ->
-		settlePromise self, state, value, state == $rejected
+		if isLongStackTrace
+			self[$settlerTrace] = genTraceInfo(true)
+
+		settlePromise self, state, value

 	###*
 	 * Link the promise1 to the promise2.


.

Former-commit-id: 5ff00b028c8f1227a243763fd36154fe2aa8ab70 [formerly 5f5fe80a8d51068473351c930694e538cd817154] [formerly 93b362c0397c3cf5f4cbed3ce4e1a2fc08c06c90 [formerly 95b8cf5716d79ce7865b4007782007dd7586f450]] [formerly fded98f4052dd90d233a0e842dd0da0e89e11692 [formerly 5403a05b5eee31e564aa48c5245db0e2aa0cc1ce] [formerly ddf5f93b23cf840196b274ab7075e15ff9747af2 [formerly ddf5f93b23cf840196b274ab7075e15ff9747af2 [formerly 8c7dfbdc72872a11a8c334962ae127d80d5cba90]]]]
Former-commit-id: cf4c0eaf0997c716402d23f0909b676bf9f8969b [formerly 0e58e7d2ebe61437a61e4fa34a9c0257237ba460] [formerly 076bd9e5e1f3b3a8834cd088eee4cb61acef1b71 [formerly f57eeeb77d0c47045cf43a1ef7507deca9ddf303]]
Former-commit-id: 4b621a2b4e9a615e78121e9c47846118dd59e861 [formerly 21e9e886730af35eb4e2d676d4576ca39a2ca021]
Former-commit-id: 73d47ce6aa170bba191f9500b564bbd5083a336a
Former-commit-id: d009f06ab81a375bd74dade4fb4830a4bdb19c44

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -409,7 +409,7 @@
   slider_pole_labels : effective_ineffective

-customizations['villagb'] = customizations['citysens'] =
+customizations['villagb'] = customizations['citysens'] = customizations['iniciativasciudadanas'] =
   point_labels : pros_contras
   slider_pole_labels : desacuerdo_acuerdo
   homie_histo_title: "Opiniones"


humanities-los configuration

humanities-los configuration

Former-commit-id: 47e88d6adcb26e8c7cd5ddc2af323ac56dd5143d [formerly 0e5ff01473a05515cb58386e77c898022d527e74] [formerly a1d791aa0a10169d79f222ef03befd1782b91cb7 [formerly ef632185784e3c4ceb3abb745ad8732d35c9a9bf]] [formerly 00eadd21800ff56fd40ddb3ddee38d220fc2a010 [formerly a9eec6e284f96d65f3c1e7077c6ec50f6af02b09] [formerly 64d9e925538ae017cbafba1e63112b151658c6e5 [formerly 64d9e925538ae017cbafba1e63112b151658c6e5 [formerly f9abc3859dd6f1896cf6aeeb0840016480d965cd]]]]
Former-commit-id: 2f7860dfb690fd824ceaee94739f4eef4bd3e9ca [formerly 80085785123e0de5c9a5f8d76aabf5d5b7792370] [formerly 179c43752b4c0aec58a51d89f7b1666ac8f43470 [formerly 8bd64cea3362850f9766511b1833800e5f2872b0]]
Former-commit-id: bbd3336ddcb7eca33efd79d3da542a0cd69282e3 [formerly 7ce2ecd5e7b67134b20babd58c5b56cbe117f1e7]
Former-commit-id: 6df55039efa6bf8e2f7d67ccecf684572d65bb3f
Former-commit-id: 2c598a8e51a83fe253aa1e655a25a9cde14376a5

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -113,88 +113,22 @@
 customizations = {}

-################################
-# DEFAULT CUSTOMIZATIONS
-#
-# TODO: refactor config & document
-
-customizations.default =
-
-
-  # Proposal options
-
-  opinion_value: (o) -> o.stance
-
-  show_crafting_page_first: false
-
-  point_labels :
-    pro: 'pro'
-    pros: 'pros'
-    con: 'con'
-    cons: 'cons'
-    your_header: "Give your --valences--"
-    other_header: "Others' --valences--"
-    top_header: "Top --valences--"
-
-  show_slider_feedback: true
-  slider_pole_labels :
-
-    individual:
-      support: 'Support'
-      oppose: 'Oppose'
-      support_sub: ''
-      oppose_sub: ''
-    group:
-      support: 'Supporters'
-      oppose: 'Opposers'
-      support_sub: ''
-      oppose_sub: ''
-
-    # individual:
-    #   support: '+'
-    #   oppose: '–'
-    # group:
-    #   support: '+'
-    #   oppose: '–'
-
-  docking_proposal_header : false
-
-  slider_handle: slider_handle.face
-
-  show_proposer_icon: false
-  collapse_descriptions_at: false
-  homie_histo_filter: false
-
-  # default cluster options
-  # TODO: put them in their own object
-  homie_histo_title: 'Opinions'
-  cluster_order: ['*']
-  archived: false
-  closed: false
-  label: false
-  description: false
-
-  # Other options
-  auth:
-    additional_auth_footer: null
-    user_questions: []
-
-  Homepage : SimpleHomepage
-  ProposalHeader : SimpleProposalHeading
-
-  HomepageHeader : DefaultHeader
-  NonHomepageHeader: ShortHeader
-
-  Footer : DefaultFooter
-
-
-

 #####
 # common options

 # pro/con labels
+
+pro_con =
+  pro: 'pro'
+  pros: 'pros'
+  con: 'con'
+  cons: 'cons'
+  your_header: "Give your --valences--"
+  other_header: "Others' --valences--"
+  top_header: "Top --valences--"
+
 strengths_weaknesses =
   pro: 'strength'
   pros: 'strengths'
@@ -204,7 +138,30 @@
   other_header: "--valences-- observed"
   top_header: "Foremost --valences--"

+challenge_justify =
+  pro: 'justification'
+  pros: 'justifications'
+  con: 'challenge'
+  cons: 'challenges'
+  your_header: "--valences-- you recognize"
+  other_header: "--valences-- identified"
+  top_header: "Foremost --valences--"
+
+
 # slider poles
+
+support_oppose =
+  individual:
+    support: 'Support'
+    oppose: 'Oppose'
+    support_sub: ''
+    oppose_sub: ''
+  group:
+    support: 'Supporters'
+    oppose: 'Opposers'
+    support_sub: ''
+    oppose_sub: ''
+
 yes_no =
   individual:
     support: 'Yes'
@@ -228,6 +185,14 @@
   group:
     support: 'Agree'
     oppose: 'Disagree'
+
+plus_minus =
+  individual:
+    support: '+'
+    oppose: '–'
+  group:
+    support: '+'
+    oppose: '–'

 # application options
 conference_config =
@@ -242,6 +207,62 @@
   homie_histo_title: "PC's ratings"

+################################
+# DEFAULT CUSTOMIZATIONS
+#
+# TODO: refactor config & document
+
+customizations.default =
+
+
+  # Proposal options
+
+  opinion_value: (o) -> o.stance
+
+  show_crafting_page_first: false
+
+  point_labels : pro_con
+
+
+  show_slider_feedback: true
+  slider_pole_labels : support_oppose
+
+
+  docking_proposal_header : false
+
+  slider_handle: slider_handle.face
+
+  show_proposer_icon: false
+  collapse_descriptions_at: false
+  homie_histo_filter: false
+
+  # default cluster options
+  # TODO: put them in their own object
+  homie_histo_title: 'Opinions'
+  archived: false
+  closed: false
+  label: false
+  description: false
+
+  # Other options
+  auth:
+    additional_auth_footer: null
+    user_questions: []
+
+  Homepage : SimpleHomepage
+  ProposalHeader : SimpleProposalHeading
+
+  HomepageHeader : DefaultHeader
+  NonHomepageHeader: ShortHeader
+
+  Footer : DefaultFooter
+
+
+
+
+
+
+
 ##########################
 # SUBDOMAIN CONFIGURATIONS

@@ -276,15 +297,31 @@
 #################
 # humanities-los

+essential_questions =
+  homie_histo_title: "Student responses"
+  slider_pole_labels: agree_disagree
+  point_labels: challenge_justify
+
+monuments =
+  point_labels : strengths_weaknesses
+  slider_pole_labels : ready_not_ready
+  homie_histo_title: "Students' feedback"
+
+
 customizations['humanities-los'] =

+  show_slider_feedback: false
+
   point_labels : strengths_weaknesses
-  show_slider_feedback: false
-
   slider_pole_labels : ready_not_ready
-
   homie_histo_title: "Students' feedback"

+  "cluster/Essential Questions 8-2": essential_questions
+
+  "cluster/Essential Questions 8-1": essential_questions
+
+  "Monuments 8-2" : monuments
+  "Monuments 8-1" : monuments

 #################
 # RANDOM2015


change wording

change wording

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -165,7 +165,7 @@
       when "linux"
         { showItemInFolder } = require "shell"
         showItemInFolder(path)
-        atom.notifications.addInfo("**#{name}**: Opened `#{basename(path)}` in file manager", dismissable: false) if atom.config.get("#{name}.notify")
+        atom.notifications.addInfo("**#{name}**: Opening `#{basename(path)}` in file manager", dismissable: false) if atom.config.get("#{name}.notify")

   openFolder: (path) ->
     { access, F_OK } = require "fs"
@@ -187,7 +187,7 @@
         when "linux"
           { openItem } = require "shell"
           openItem(path)
-          atom.notifications.addInfo("**#{name}**: Opened `#{basename(path)}` in file manager", dismissable: false) if atom.config.get("#{name}.notify")
+          atom.notifications.addInfo("**#{name}**: Opening `#{basename(path)}` in file manager", dismissable: false) if atom.config.get("#{name}.notify")

   spawnCmd: (cmd, args, baseName, fileManager) ->
     { spawn } = require("child_process")
@@ -199,4 +199,4 @@

     open.on "close", ( errorCode ) ->
       if errorCode is 0 and atom.config.get("#{name}.notify")
-        atom.notifications.addInfo("**#{name}**: Opened `#{baseName}` in #{fileManager}", dismissable: false)
+        atom.notifications.addInfo("**#{name}**: Opening `#{baseName}` in #{fileManager}", dismissable: false)


Add KDView appendToSelector and prependToSelector methods to use KDViews with-in native HTML easily

--- client/Framework/core/KDView.coffee
+++ client/Framework/core/KDView.coffee
@@ -209,7 +209,6 @@
   setDomId:(id)->
     @domElement.attr "id",id

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

@@ -255,6 +254,10 @@
       @emit 'viewAppended', @
     @

+  appendToSelector:(selector)->
+    $(selector).append @$()
+    @emit 'viewAppended', @
+
   prepend:(child, selector)->
     @$(selector).prepend child.$()
     if @parentIsInDom
@@ -266,6 +269,10 @@
     if @parentIsInDom
       @emit 'viewAppended', @
     @
+
+  prependToSelector:(selector)->
+    $(selector).prepend @$()
+    @emit 'viewAppended', @

   setPartial:(partial,selector)->
     @$(selector).append partial
@@ -714,12 +721,12 @@
     o.showOnlyWhenOverflowing or= no # this will check for horizontal overflow

     @on "viewAppended", =>
-      # For this to work, the DOM element must have layout box information
-      # associated such as overflow, border-sizing, text-overflow
+      # For this to work, the DOM element must have layout box information
+      # associated such as overflow, border-sizing, text-overflow
       #
       #                                                          Arvid Feb 2013
-
-      isOverflowing = @$(o.selector)[0]?.offsetWidth < @$(o.selector)[0]?.scrollWidth
+
+      isOverflowing = @$(o.selector)[0]?.offsetWidth < @$(o.selector)[0]?.scrollWidth

       if o.showOnlyWhenOverflowing and isOverflowing or not o.showOnlyWhenOverflowing
         @bindTooltipEvents o


payment: update the workflow to not allow people who are not confirmed

--- client/Main/payment/workflow.coffee
+++ client/Main/payment/workflow.coffee
@@ -55,25 +55,47 @@

     { paymentController } = KD.singletons

-    paymentController.creditCard (err, card) =>
-
-      @state.paymentMethod = card
-
-      { UPGRADE, DOWNGRADE, INTERVAL_CHANGE } = PaymentConstants.operation
-
-      switch operation
-        when DOWNGRADE                then @startDowngradeFlow()
-        when UPGRADE, INTERVAL_CHANGE then @startRegularFlow()
-
-      @emit 'WorkflowStarted'
+    paymentController.canUserPurchase (err, canPurchase) =>
+
+      return @showError err  if err
+
+      unless canPurchase
+        return @showError { message: PaymentConstants.error.ERR_USER_NOT_CONFIRMED }
+
+      paymentController.creditCard (err, card) =>
+
+        @state.paymentMethod = card
+
+        { UPGRADE, DOWNGRADE, INTERVAL_CHANGE } = PaymentConstants.operation
+
+        switch operation
+          when DOWNGRADE                then @startDowngradeFlow()
+          when UPGRADE, INTERVAL_CHANGE then @startRegularFlow()
+
+        @emit 'WorkflowStarted'
+
+
+  showError: (err) ->
+
+    { message } = err
+
+    humanReadable = PaymentConstants.error[message]
+    err.message   = humanReadable  if humanReadable
+
+    KD.showError err
+    @modal?.form.submitButton.hideLoader()

   startRegularFlow: ->

     @modal = new PaymentModal { @state }
     @modal.on 'PaymentWorkflowFinished',          @bound 'finish'
-    @modal.on 'PaymentSubmitted',                 @bound 'handlePaymentSubmit'
     @modal.on 'PaymentWorkflowFinishedWithError', @bound 'finishWithError'
+
+    @modal.on 'PaymentSubmitted', (formData) =>
+      isUserConfirmed (err, confirmed) =>
+        return @userIsNotConfirmed err  if err
+        @handlePaymentSubmit formData

     @modal.on 'PaypalButtonClicked', =>
       @state.provider = PaymentConstants.provider.PAYPAL
@@ -164,7 +186,7 @@
     me = KD.whoami()
     me.fetchEmail (err, email) =>

-      return KD.showError err  if err
+      return @showError err  if err

       options.email = email
       options.provider = @state.provider
@@ -215,3 +237,4 @@

     @destroy()

+


Give warning on dublicated email. Add check for existing customer numbers.

--- src/coffee/customerxmlimport.coffee
+++ src/coffee/customerxmlimport.coffee
@@ -47,15 +47,19 @@

       existingCustomers = result.body.results
       email2id = {}
+      usedCustomerNumbers = []
       for ec in existingCustomers
         email2id[ec.email] = ec
+        usedCustomerNumbers.push ec.customerNumber
       console.log "Existing customers: " + _.size(email2id)

       posts = _.map customerData, (data) =>
         customer = data.customer
         paymentInfo = data.paymentInfo
-        if _.has email2id, customer.email
-          Q 'Update of customer is not implemented yet!'
+        if _.contains(usedCustomerNumbers, customer.customerNumber)
+          Q 'Update of customer is not implemented yet - number exists!'
+        else if _.has email2id, customer.email
+          Q 'Update of customer is not implemented yet - email exist!'
           #@resetPassword customer, customer.email, email2id[customer.email]
         else
           @create customer, paymentInfo
@@ -209,7 +213,9 @@
   createCustomerData: (xml, employee, customerNumber, customerGroupName2Id, customerGroup, country) ->
     email = xmlHelpers.xmlVal employee, 'email', xmlHelpers.xmlVal(xml, 'EmailCompany')
     return unless email? # we can't import customers without email
-    return if _.indexOf(@usedEmails, email) isnt -1 # email already used
+    if _.indexOf(@usedEmails, email) isnt -1
+      console.warn "Email #{email} at least twice in XML!"
+      return
     @usedEmails.push email

     streetInfo = @splitStreet xmlHelpers.xmlVal xml, 'Street', ''


Allow to update customer group link too.

--- src/coffee/customerxmlimport.coffee
+++ src/coffee/customerxmlimport.coffee
@@ -60,8 +60,11 @@
           if _.size(email2id[customer.email].addresses or []) is 0
             console.warn "Customer without address"
             #@addAddress(email2id[customer.email], customer.addresses[0])
-          else
-            Q "Update of customer is not implemented yet - email '#{customer.email}' exist!"
+          #if email2id[customer.email].customerGroup?
+          #  @linkCustomerIntoGroup(email2id[customer.email], customer.customerGroup)
+
+          Q "Update of customer is not implemented yet - email '#{customer.email}' exist!"
+
           #@resetPassword customer, customer.email, email2id[customer.email]
           #@ensurePaymentInfo email2id[customer.email], paymentInfo
           #@syncIdenfifier customer, email2id[customer.email]


Fix LESS file finding on Windows

Fix LESS file finding on Windows

--- lib/client.coffee
+++ lib/client.coffee
@@ -5,7 +5,7 @@
 #LESS
 less = remote.require 'less'
 parser = new(less.Parser)({
-  paths: ['less/'],
+  paths: [__dirname + '/../less/'],
   filename: 'style.less'
 });


About: fixed vm count

About: fixed vm count

--- client/landing/About/AppView.coffee
+++ client/landing/About/AppView.coffee
@@ -62,7 +62,7 @@

VMs spinned up

-              1,000,000,000+
+              1,000,000+

           photo by - Isaak Dury


IDE: minor styling

IDE: minor styling

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -112,9 +112,9 @@

   KD.registerRoutes 'IDE',

-    '/:name?/IDE': -> routeToLatestWorkspace()
+    '/:name?/IDE': routeToLatestWorkspace

-    '/:name?/IDE/:machineLabel': -> routeToLatestWorkspace()
+    '/:name?/IDE/:machineLabel': routeToLatestWorkspace

     '/:name?/IDE/:machineLabel/:workspaceSlug': (data) ->


Change defer to explicit delay

Change defer to explicit delay

The current call stack seems to be already clear once when the "Cancel"
is being clicked. An explicit delay allows the modal and backdrop to be
before the revert occurs.

--- coffee/cilantro/ui/workflows/results.coffee
+++ coffee/cilantro/ui/workflows/results.coffee
@@ -308,8 +308,9 @@
             @ui.columns.modal('show')

         cancelColumnChanges: ->
-            c._.defer => @columns.currentView.updateView(
-                c.data.views.getSession())
+            c._.delay =>
+                @columns.currentView.updateView(c.data.views.getSession())
+            , 25

         saveColumns: ->
             c.data.views.getSession().facets = @columns.currentView.facets.clone()


Add feat: #430 Delete month should delete whole month (#493)

--- src/coffee/card.coffee
+++ src/coffee/card.coffee
@@ -167,7 +167,9 @@
       filters: numberInputFilters
     QJ.on @$numberInput, 'payment.cardType', @handle('setCardType')

-    expiryFilters = [(val) -> val.replace /(\s+)/g, '']
+    expiryFilters = [(val) ->
+                        if val.length == 1 and val[0] == '0' then '' else val.replace /(\s+)/g, ''
+                    ]
     expiryFilters.push @validToggler('cardExpiry')

     bindVal @$expiryInput, @$expiryDisplay,


rm displayFinder from passed on props

rm displayFinder from passed on props

--- client/skr/components/LocationChooser.cjsx
+++ client/skr/components/LocationChooser.cjsx
@@ -22,15 +22,14 @@
             })

     render: ->
-        props = _.omit(@props, 'hideSingle')
+        props = _.omit(@props, 'hideSingle', 'displayFinder')

         if @props.hideSingle and Skr.Models.Location.all.length < 2
             return LC.SelectField.renderEmptyColumn(props)

-        if props.displayFinder
+        if @props.displayFinder
             <LC.RecordFinder ref="finder" sm=3 autoFocus
                 commands={@state.commands}
-                collection={Skr.Models.Location.all}
                 query={@query}
                 {...props} />
         else


 Merge branch 'master.sliderbarfix' into 'master'

Master.Sliderbarfix

--- components/sliderbar/KDSliderBarView.coffee
+++ components/sliderbar/KDSliderBarView.coffee
@@ -14,15 +14,16 @@

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

-    options.cssClass    = KD.utils.curry "sliderbar-container", options.cssClass
-    options.minValue   ?= 0
-    options.maxValue   ?= 100
-    options.interval   ?= no
-    options.drawBar    ?= yes
-    options.showLabels ?= yes
-    options.snap       ?= yes
-    options.snapOnDrag ?= no
-    options.width     or= 300
+    options.cssClass      = KD.utils.curry "sliderbar-container", options.cssClass
+    options.minValue      ?= 0
+    options.maxValue      ?= 100
+    options.interval      ?= no
+    options.drawBar       ?= yes
+    options.showLabels    ?= yes
+    options.snap          ?= yes
+    options.snapOnDrag    ?= no
+    options.width         or= 300
+    options.drawOpposite  ?= no

     super options, data

@@ -59,6 +60,20 @@
     @bar.setWidth diff
     @bar.setX "#{left}px"

+  drawOppositeBar:->
+    positions = []
+    positions.push handle.getRelativeX() for handle in @handles
+
+    right     = parseInt(positions.last)
+    diff      = @getWidth() - right
+
+    unless @oppositeBar
+      @addSubView @oppositeBar = new KDCustomHTMLView
+        cssClass : "opposite bar"
+
+    @oppositeBar.setWidth diff
+    @oppositeBar.setX "#{right}px"
+
   _createLabel : (value) =>
     {maxValue, minValue, interval, showLabels} = @getOptions()

@@ -80,6 +95,7 @@
   setValue:(value, handle = @handles.first, updateHandle = yes)->
     handle.setValue value  if updateHandle
     @drawBar()  if @getOption('drawBar')
+    @drawOppositeBar() if @getOption('drawOpposite')
     @setLimits()
     @emit "ValueIsChanging", handle.value
     @emit "ValueChanged", handle
@@ -120,5 +136,6 @@
     @createHandles()
     @setLimits()
     @drawBar()   if @getOption('drawBar')
+    @drawOppositeBar() if @getOption('drawOpposite')
     @addLabels() if @getOption('showLabels')
     @attachEvents()


Update asciidoc-preview.cson

Update asciidoc-preview.cson

--- keymaps/asciidoc-preview.cson
+++ keymaps/asciidoc-preview.cson
@@ -1,5 +1,5 @@
-'.atom-workspace':
-  'ctrl-A': 'asciidoc-preview:toggle'
+'atom-workspace, atom-workspace atom-text-editor':
+  'ctrl-shift-a': 'asciidoc-preview:toggle'
   'ctrl-alt-a': 'asciidoc-preview:toggle-render-on-save-only'

 '.asciidoc-preview':


Fix SubscribeModal tests

Fix SubscribeModal tests

--- test/app/views/core/SubscribeModal.spec.coffee
+++ test/app/views/core/SubscribeModal.spec.coffee
@@ -247,16 +247,16 @@
           yield @payPalButton.click()
           expect(@modal.hide).toHaveBeenCalled()
           expect(@getTrackerEventNames()).toDeepEqual(
-            [ "Start Lifetime Purchase", "Finish Lifetime Purchase" ])
-
-      describe 'when using Stripe', ->
-        it 'calls hide()', wrapJasmine ->
-          spyOn(@modal, 'hide')
-          @modal.onClickLifetimeButton()
-          yield @modal.onClickStripeButton()
-          expect(@modal.hide).toHaveBeenCalled()
-          expect(@getTrackerEventNames()).toDeepEqual(
-            [ "Start Lifetime Purchase", "Finish Lifetime Purchase" ])
+            [ "SubscribeModal Lifetime Button Click", "Start Lifetime Purchase", "Finish Lifetime Purchase" ])
+
+      describe 'when using Stripe', ->
+        it 'calls hide()', wrapJasmine ->
+          spyOn(@modal, 'hide')
+          @modal.onClickLifetimeButton()
+          yield @modal.onClickStripeButton()
+          expect(@modal.hide).toHaveBeenCalled()
+          expect(@getTrackerEventNames()).toDeepEqual(
+            [ "SubscribeModal Lifetime Button Click", "Start Lifetime Purchase", "Finish Lifetime Purchase" ])

     describe 'when the purchase response is 402', ->
       beforeEach ->
@@ -268,7 +268,7 @@
           yield @modal.onClickStripeButton()
           expect(@modal.state).toBe('declined')
           expect(@getTrackerEventNames()).toDeepEqual(
-            [ "Start Lifetime Purchase", "Fail Lifetime Purchase" ])
+            [ "SubscribeModal Lifetime Button Click", "Start Lifetime Purchase", "Fail Lifetime Purchase" ])

     describe 'when the purchase response is 422', ->
       beforeEach ->
@@ -280,4 +280,4 @@
           yield @payPalButton.click()
           expect(@modal.state).toBe('error')
           expect(@getTrackerEventNames()).toDeepEqual(
-            [ "Start Lifetime Purchase", "Fail Lifetime Purchase" ])
+            [ "SubscribeModal Lifetime Button Click", "Start Lifetime Purchase", "Fail Lifetime Purchase" ])


Config: Make linter happy

Config: Make linter happy

--- lib/config.coffee
+++ lib/config.coffee
@@ -14,38 +14,32 @@
         default: '{}'
     kernelspec:
         title: "Kernel Spec"
-        description: 'This field is autogenerated on every launch in up-to-date environments,
-        also you can regenerate this with hydrogen:update-kernels command.
-
-
-        ```
-        Kernel specs as reported by:
-          $ jupyter kernelspecs list --json
-            or
-          $ ipython kernelspecs list --json
-        ```
-
-        If this commands fails in your environment you can use this field to specify your kernels, like:
-        ```
-        {
-          "kernelspecs": {
-            "ijavascript": {
-              "spec": {
-                "display_name": "IJavascript",
-                "env": {},
-                "argv": [
-                  "node",
-                  "/home/user/node_modules/ijavascript/lib/kernel.js",
-                  "--protocol=5.0",
-                  "{connection_file}"
-                ],
-                "language": "javascript"
-              },
-              "resources_dir": "/home/user/node_modules/ijavascript/images"
+        description: 'This field is autogenerated on every launch in up-to-date
+            environments, also you can regenerate this with
+            `hydrogen:update-kernels` command. Kernel specs as reported by
+            `jupyter kernelspecs list --json` or
+            `ipython kernelspecs list --json`. If these commands fail in your
+            environment, you can use this field to specify your kernels, like:
+            ```
+            {
+              "kernelspecs": {
+                "ijavascript": {
+                  "spec": {
+                    "display_name": "IJavascript",
+                    "env": {},
+                    "argv": [
+                      "node",
+                      "/home/user/node_modules/ijavascript/lib/kernel.js",
+                      "--protocol=5.0",
+                      "{connection_file}"
+                    ],
+                    "language": "javascript"
+                  },
+                  "resources_dir": "/home/user/node_modules/ijavascript/images"
+                }
+              }
             }
-          }
-        }
-        ```'
+            ```'
         type: 'string'
         default: '{}'
     languageMappings:
@@ -63,6 +57,8 @@
         default: '{}'
     startupCode:
         title: "Startup Code"
-        description: 'This code will be executed on kernel startup. Format: `{"kernel": "your code \\nmore code"}`. Example: `{"Python 2": "%matplotlib inline"}`'
+        description: 'This code will be executed on kernel startup.
+            Format: `{"kernel": "your code \\nmore code"}`.
+            Example: `{"Python 2": "%matplotlib inline"}`'
         type: 'string'
         default: '{}'


only show pytest panel if error status-bar blink green if success during 500ms

--- lib/atom-python-test.coffee
+++ lib/atom-python-test.coffee
@@ -48,7 +48,7 @@
     @tmp = require('tmp');

     @atomPythonTestView.clear()
-    @atomPythonTestView.toggle()
+    @atomPythonTestView.destroy()

     stdout = (output) ->
       atomPythonTestView = AtomPythonTest.atomPythonTestView
@@ -57,6 +57,15 @@

     exit = (code) ->
       atomPythonTestView = AtomPythonTest.atomPythonTestView
+
+      if atomPythonTestView.message.includes("success-line") #pytest retrun succes
+        statutbar = document.getElementsByClassName('status-bar')[0]
+        statutbar.style.background = "green"
+        setTimeout ->
+          statutbar.style.background = "" # show green status bar while one second  on sucess
+        , 500
+      else
+        atomPythonTestView.toggle() #show panel if pytest is not success

       junitViewer = require('junit-viewer')
       parsedResults = junitViewer.parse(AtomPythonTest.testResultsFilename.name)


Disabled logging

Disabled logging

--- lib/debugger.coffee
+++ lib/debugger.coffee
@@ -11,7 +11,7 @@
 NodeDebuggerView = require './node-debugger-view'
 jumpToBreakpoint = require './jump-to-breakpoint'

-log = (msg) -> console.log(msg)
+log = (msg) -> #console.log(msg)

 class ProcessManager extends EventEmitter
   constructor: (@atom = atom)->


fix inconsistent naming (flag to flags)

fix inconsistent naming (flag to flags)

--- lib/doc_tags.coffee
+++ lib/doc_tags.coffee
@@ -54,23 +54,23 @@
     section:     'type'

   accessor:
-    section:     'flag'
+    section:     'flags'
     markdown:    'is an accessor'
   async:
-    section:     'flag'
+    section:     'flags'
     markdown:    'is asynchronous'
   asynchronous:  'async'
   getter:
-    section:     'flag'
+    section:     'flags'
     markdown:    'is a getter'
   recursive:
-    section:     'flag'
+    section:     'flags'
     markdown:    'is recursive'
   refactor:
-    section:     'flag'
+    section:     'flags'
     markdown:    'needs to be refactored'
   setter:
-    section:     'flag'
+    section:     'flags'
     markdown:    'is a setter'

   alias:
@@ -199,6 +199,6 @@
   throws:        'throw'

   defaultNoValue:
-    section:     'flag'
+    section:     'flags'
   defaultHasValue:
-    section:     'metadata'+    section:     'metadata'


Tidy up snippets file and add snippet for Watchman

--- src/configs/snippets.cson
+++ src/configs/snippets.cson
@@ -37,20 +37,45 @@
 ".source.makefile":
 	"Windows check":
 		prefix: "iswin"
-		body: '# Windows: CMD.exe\nifdef SystemRoot\n\tIS_WINDOWS = 1\nendif\n\n# Windows: MinGW / COMMAND.COM\nifdef SYSTEMROOT\n\tIS_WINDOWS = 1\nendif\n\n'
+		body: """
+			# Windows: CMD.exe
+			ifdef SystemRoot
+				IS_WINDOWS = 1
+			endif
+
+			# Windows: MinGW / COMMAND.COM
+			ifdef SYSTEMROOT
+				IS_WINDOWS = 1
+			endif
+			"""
+
+	Watchman:
+		prefix: "watch"
+		body: """
+			watch:;   @watchman -- trigger . _ '${1:fixtures/*}' -- make all >/dev/null
+			unwatch:; @watchman -- watch-del . >/dev/null
+			"""

 # Cascading Style Sheets
 ".source.css":
-	"Input Placeholder":
+	Placeholder:
 		prefix: "placeholder"
-		body: "::-webkit-input-placeholder{    $1}\n::-ms-input-placeholder{        $1}\n:-ms-input-placeholder{         $1}\n::-moz-placeholder{opacity: 1;  $1}\n:-moz-placeholder{              $1}\n::placeholder{                  $1}\n:placeholder-shown{             $1}\n"
+		body: """
+			::-webkit-input-placeholder{    $1}
+			::-ms-input-placeholder{        $1}
+			:-ms-input-placeholder{         $1}
+			::-moz-placeholder{opacity: 1;  $1}
+			:-moz-placeholder{              $1}
+			::placeholder{                  $1}
+			:placeholder-shown{             $1}
+			"""

 	Comment:
 		prefix: "/"
 		body: "/** $1 */"

-	"Font-face":
+	Font:
 		prefix: "font"
 		body: """
 			@font-face {


Got rid of the makeNode() and makeDigit() methods.

--- lib/finger_tree.coffee
+++ lib/finger_tree.coffee
@@ -50,15 +50,6 @@
       asDigit:  -> new T.Digit3 @a, @b, @c
       norm: -> @v

-    makeNode = (args...) ->
-      type = switch args.length
-        when 2 then T.Node2
-        when 3 then T.Node3
-        else
-          raise new Error "Illegal number of arguments: #{args.length}"
-
-      new type args...
-

     # A digit in a finger tree.
     @Digit1 = class
@@ -129,17 +120,6 @@
       init: -> new T.Digit3 @a, @b, @c

       norm: -> norm(@a).plus(norm(@b)).plus(norm(@c)).plus(norm(@d))
-
-    makeDigit = (args...) ->
-      type = switch args.length
-        when 1 then T.Digit1
-        when 2 then T.Digit2
-        when 3 then T.Digit3
-        when 4 then T.Digit4
-        else
-          raise new Error "Illegal number of arguments: #{args.length}"
-
-      new type args...

     # An empty finger tree.
@@ -169,8 +149,8 @@
       reduceLeft:  (z, op) -> op z, @a
       reduceRight: (op, z) -> op @a, z

-      after:  (x) -> new T.Deep makeDigit(x), (-> T.Empty), makeDigit(@a)
-      before: (x) -> new T.Deep makeDigit(@a), (-> T.Empty), makeDigit(x)
+      after:  (x) -> new T.Deep new T.Digit1(x), (-> T.Empty), new T.Digit1(@a)
+      before: (x) -> new T.Deep new T.Digit1(@a), (-> T.Empty), new T.Digit1(x)

       first: -> @a
       last:  -> @a
@@ -209,14 +189,18 @@
       after: (x) ->
         if @l.constructor == T.Digit4
           { a, b, c, d } = @l
-          new T.Deep makeDigit(x, a), (=> @m().after(makeNode(b, c, d))), @r
+          new T.Deep(new T.Digit2(x, a),
+                     (=> @m().after(new T.Node3(b, c, d))),
+                     @r)
         else
           new T.Deep @l.after(x), @m, @r

       before: (x) ->
         if @r.constructor == T.Digit4
           { a, b, c, d } = @r
-          new T.Deep @l, (=> @m().before(makeNode(a, b, c))), makeDigit(d, x)
+          new T.Deep(@l,
+                     (=> @m().before(new T.Node3(a, b, c))),
+                     new T.Digit2(d, x))
         else
           new T.Deep @l, @m, @r.before(x)


修正AjaxValidator不可用的BUG

修正AjaxValidator不可用的BUG

--- src/core/validator.coffee
+++ src/core/validator.coffee
@@ -172,19 +172,21 @@
 		data: null

 	_validate: (data, entity, callback) ->
-		sendData = @_data
-		if not sendData? or sendData is "{{data}}"
-			sendData = data
-		else if typeof sendData is "object"
-			realSendData = {}
-			for p, v of sendData
-				if v is "{{data}}" then v = data
-				realSendData[p] = v
-			sendData = realSendData
+		url = @_url.replace("{{data}}", data)
+		if url is @_url
+			sendData = @_data
+			if not sendData? or sendData is "{{data}}"
+				sendData = data
+			else if typeof sendData is "object"
+				realSendData = {}
+				for p, v of sendData
+					if v is "{{data}}" then v = data
+					realSendData[p] = v
+				sendData = realSendData

 		options =
 			async: !!callback
-			url: @_url
+			url:  url
 			data: sendData
 			method: @_method


:art: Reorganize CSV editor methods

:art: Reorganize CSV editor methods

--- lib/csv-editor.coffee
+++ lib/csv-editor.coffee
@@ -20,7 +20,6 @@

     Tablr ?= require './tablr'
     @options ?= {}
-    @subscriptions = new CompositeDisposable
     @emitter = new Emitter
     @setPath(filePath)

@@ -36,6 +35,135 @@

     @emitter.emit 'did-change-path', @getPath()
     @emitter.emit 'did-change-title', @getTitle()
+
+  getTitle: ->
+    if sessionPath = @getPath()
+      path.basename(sessionPath)
+    else
+      'untitled'
+
+  getLongTitle: ->
+    if sessionPath = @getPath()
+      fileName = path.basename(sessionPath)
+      directory = atom.project.relativize(path.dirname(sessionPath))
+      directory = if directory.length > 0 then directory else path.basename(path.dirname(sessionPath))
+      "#{fileName} - #{directory}"
+    else
+      'untitled'
+
+  getPath: -> @file?.getPath()
+
+  getURI: -> @getPath()
+
+  isDestroyed: -> @destroyed
+
+  isModified: -> @editor?.isModified() ? false
+
+  copy: ->
+    new CSVEditor({filePath: @getPath(), options: _.clone(@options), @choice})
+
+  destroy: ->
+    return if @destroyed
+
+    if @editor?
+      @saveLayout()
+      @editor.destroy()
+
+    @fileSubscriptions?.dispose()
+    @editorSubscriptions?.dispose()
+    @destroyed = true
+    @emitter.emit('did-destroy', this)
+    @emitter.dispose()
+
+  save: =>
+    @saveAs(@getPath())
+
+  saveAs: (path) ->
+    new Promise (resolve, reject) =>
+      options = _.clone(@options)
+      options.columns = @editor.getColumns() if options.header
+
+      @setPath(path)
+      @saveLayout()
+
+      csv.stringify @editor.getTable().getRows(), options, (err, data) =>
+        return reject(err) if err?
+
+        @preventFileChangeEvents()
+        fs.writeFile path, data, (err) =>
+          if err?
+            @allowFileChangeEvents()
+            return reject(err)
+          resolve()
+
+  saveConfig: (@choice) ->
+    filePath = @getPath()
+    Tablr.csvConfig.set(filePath, 'options', @options)
+    if @options.remember and @choice?
+      Tablr.csvConfig.set(filePath, 'choice', @choice)
+
+  saveLayout: ->
+    @layout = @getCurrentLayout()
+
+    Tablr.csvConfig.set(@getPath(), 'layout', @layout)
+
+  shouldPromptToSave: (options) ->
+    @editor?.shouldPromptToSave(options) ? false
+
+  onDidOpen: (callback) ->
+    @emitter.on 'did-open', callback
+
+  onDidDestroy: (callback) ->
+    @emitter.on 'did-destroy', callback
+
+  onDidConflict: (callback) ->
+    @emitter.on 'did-conflict', callback
+
+  onDidChange: (callback) ->
+    @emitter.on 'did-change', callback
+
+  onDidChangeModified: (callback) ->
+    @emitter.on 'did-change-modified', callback
+
+  onDidChangePath: (callback) ->
+    @emitter.on 'did-change-path', callback
+
+  onDidChangeTitle: (callback) ->
+    @emitter.on 'did-change-title', callback
+
+  applyChoice: ->
+    return if @choiceApplied
+    if @choice?
+      switch @choice
+        when 'TextEditor' then @openTextEditor(@options)
+        when 'TableEditor' then @openTableEditor(@options)
+
+    @choiceApplied = true
+
+  openTextEditor: (@options={}) ->
+    filePath = @getPath()
+    atom.workspace.openTextFile(filePath).then (editor) =>
+      pane = atom.workspace.paneForItem(this)
+      @emitter.emit('did-open', {editor, options: _.clone(@options)})
+      @saveConfig('TextEditor')
+      @destroy()
+
+      pane.activateItem(editor)
+
+  openTableEditor: (@options={}) ->
+    @openCSV().then (@editor) =>
+      @subscribeToEditor()
+
+      @emitter.emit 'did-open', {@editor, options: _.clone(options)}
+      @emitter.emit 'did-change-modified', @editor.isModified()
+
+      @saveConfig('TableEditor')
+      @editor
+
+  subscribeToEditor: ->
+    @editorSubscriptions = new CompositeDisposable
+    @editorSubscriptions.add @editor.onDidChangeModified (status) =>
+      @emitter.emit 'did-change-modified', status

   subscribeToFile: ->
     @fileSubscriptions?.dispose()
@@ -94,130 +222,6 @@
     # @fileSubscriptions.add @file.onWillThrowWatchError (errorObject) =>
     #   console.log 'error', errorObject

-  getTitle: ->
-    if sessionPath = @getPath()
-      path.basename(sessionPath)
-    else
-      'untitled'
-
-  getLongTitle: ->
-    if sessionPath = @getPath()
-      fileName = path.basename(sessionPath)
-      directory = atom.project.relativize(path.dirname(sessionPath))
-      directory = if directory.length > 0 then directory else path.basename(path.dirname(sessionPath))
-      "#{fileName} - #{directory}"
-    else
-      'untitled'
-
-  getPath: -> @file?.getPath()
-
-  getURI: -> @getPath()
-
-  isDestroyed: -> @destroyed
-
-  isModified: -> @editor?.isModified() ? false
-
-  copy: ->
-    new CSVEditor({filePath: @getPath(), options: _.clone(@options), @choice})
-
-  shouldPromptToSave: (options) ->
-    @editor?.shouldPromptToSave(options) ? false
-
-  onDidOpen: (callback) ->
-    @emitter.on 'did-open', callback
-
-  onDidDestroy: (callback) ->
-    @emitter.on 'did-destroy', callback
-
-  onDidConflict: (callback) ->
-    @emitter.on 'did-conflict', callback
-
-  onDidChange: (callback) ->
-    @emitter.on 'did-change', callback
-
-  onDidChangeModified: (callback) ->
-    @emitter.on 'did-change-modified', callback
-
-  onDidChangePath: (callback) ->
-    @emitter.on 'did-change-path', callback
-
-  onDidChangeTitle: (callback) ->
-    @emitter.on 'did-change-title', callback
-
-  applyChoice: ->
-    return if @choiceApplied
-    if @choice?
-      switch @choice
-        when 'TextEditor' then @openTextEditor(@options)
-        when 'TableEditor' then @openTableEditor(@options)
-
-    @choiceApplied = true
-
-  openTextEditor: (@options={}) ->
-    filePath = @getPath()
-    atom.workspace.openTextFile(filePath).then (editor) =>
-      pane = atom.workspace.paneForItem(this)
-      @emitter.emit('did-open', {editor, options: _.clone(@options)})
-      @saveConfig('TextEditor')
-      @destroy()
-
-      pane.activateItem(editor)
-
-  openTableEditor: (@options={}) ->
-    @openCSV().then (@editor) =>
-      @subscriptions.add @editor.onDidChangeModified (status) =>
-        @emitter.emit 'did-change-modified', status
-
-      @emitter.emit 'did-open', {@editor, options: _.clone(options)}
-      @emitter.emit 'did-change-modified', @editor.isModified()
-
-      @saveConfig('TableEditor')
-      @editor
-
-  destroy: ->
-    return if @destroyed
-
-    if @editor?
-      @saveLayout()
-      @editor.destroy()
-
-    @fileSubscriptions?.dispose()
-    @destroyed = true
-    @emitter.emit('did-destroy', this)
-    @emitter.dispose()
-
-  save: =>
-    @saveAs(@getPath())
-
-  saveAs: (path) ->
-    new Promise (resolve, reject) =>
-      options = _.clone(@options)
-      options.columns = @editor.getColumns() if options.header
-
-      @setPath(path)
-      @saveLayout()
-
-      csv.stringify @editor.getTable().getRows(), options, (err, data) =>
-        return reject(err) if err?
-
-        @preventFileChangeEvents()
-        fs.writeFile path, data, (err) =>
-          if err?
-            @allowFileChangeEvents()
-            return reject(err)
-          resolve()
-
-  saveConfig: (@choice) ->
-    filePath = @getPath()
-    Tablr.csvConfig.set(filePath, 'options', @options)
-    if @options.remember and @choice?
-      Tablr.csvConfig.set(filePath, 'choice', @choice)
-
-  saveLayout: ->
-    @layout = @getCurrentLayout()
-
-    Tablr.csvConfig.set(@getPath(), 'layout', @layout)
-
   getCurrentLayout: ->
     columns: @editor.getScreenColumns().map (column) =>
       conf = {}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -153,3 +153,5 @@
     include: (modules...) ->
       for Module in modules
         self.extend Module(I, self)
+
+      return self


Setting For AutoSwitch

Setting For AutoSwitch

--- lib/codekit-commands.coffee
+++ lib/codekit-commands.coffee
@@ -17,6 +17,11 @@
     autoQuit:
       title: 'Auto Quit Codekit'
       description: 'Auto quit Codekit when Atom window is closed'
+      type: 'boolean'
+      default: true
+    autoSwitch:
+      title: 'Auto Switch Codekit Project'
+      description: 'Auto switch Codekit project on file change'
       type: 'boolean'
       default: true

@@ -48,11 +53,17 @@
       applescript.execString(script)

   previewProject: ->
-    script = 'tell application "CodeKit" to preview in browser'
+    script = 'tell application \"CodeKit\" to preview in browser'
     applescript.execString(script)

   refreshProject: ->
-    script = 'tell application "CodeKit" to refresh browsers'
+    newPanel = atom.workspace.getActivePaneItem()
+    if newPanel
+      if newPanel.buffer
+        if newPanel.buffer.file
+          script = "tell application \"CodeKit\" to select project containing path \"#{newPanel.buffer.file.path}\""
+          applescript.execString(script)
+    script = 'tell application \"CodeKit\" to refresh browsers'
     applescript.execString(script)

   addProject: ->
@@ -63,11 +74,12 @@
     applescript.execString(script)

   switchProject: (newPanel) ->
-    if newPanel
-      if newPanel.buffer
-        if newPanel.buffer.file
-          script = "tell application \"CodeKit\" to select project containing path \"#{newPanel.buffer.file.path}\""
-          applescript.execString(script)
+    if atom.config.get('codekit-commands.autoSwitch')
+        if newPanel
+          if newPanel.buffer
+            if newPanel.buffer.file
+              script = "tell application \"CodeKit\" to select project containing path \"#{newPanel.buffer.file.path}\""
+              applescript.execString(script)

   pauseProject: ->
     if atom.config.get('codekit-commands.autoPause')


prevent missing ascender from breaking layout

--- lib/font/afm.coffee
+++ lib/font/afm.coffee
@@ -13,8 +13,8 @@
     @charWidths = (@glyphWidths[characters[i]] for i in [0..255])

     @bbox = (+e for e in @attributes['FontBBox'].split /\s+/)
-    @ascender = +@attributes['Ascender']
-    @decender = +@attributes['Descender']
+    @ascender = +(@attributes['Ascender'] or 0)
+    @decender = +(@attributes['Descender'] or 0)
     @lineGap = (@bbox[3] - @bbox[1]) - (@ascender - @decender)

   parse: ->


Add Selection based support for powershell

Tested and working correctly.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -494,6 +494,9 @@
       args: (context) -> [context.filepath]

   PowerShell:
+    "Selection Based":
+      command: "powershell"
+      args: (context) -> [context.getCode()]
     "File Based":
       command: "powershell"
       args: (context) -> [context.filepath.replace /\ /g, "` "]