Minor code cleaning.

Minor code cleaning.

--- app/coffee/controllers/backlog.coffee
+++ app/coffee/controllers/backlog.coffee
@@ -28,7 +28,6 @@
     $scope.$on "milestones:loaded", (ctx, data) ->
         if data.length > 0
             $rootScope.sprintId = data[0].id
-

 @BacklogController.$inject = ['$scope', '$rootScope', '$routeParams', 'resource']

@@ -115,7 +114,6 @@

         # HACK: because django-filter does not works properly
         # $scope.unassingedUs = data
-        #
         $scope.unassingedUs = _.filter unassingedUs, (item) ->
             return (item.project is $rootScope.projectId and item.milestone is null)

@@ -189,7 +187,6 @@
             tag.selected = false
         else
             tag.selected = true
-
         filterUsBySelectedTags()

     # Signal Handlign
@@ -199,7 +196,6 @@

 # Backlog milestones controller.
-
 @BacklogMilestonesController = ($scope, $rootScope, rs) ->
     # Local scope variables
     $scope.sprintFormOpened = false
@@ -225,8 +221,6 @@
         rs.getMilestones($rootScope.projectId).then (data) ->
             # HACK: because django-filter does not works properly
             # $scope.milestones = data
-            #
-            #
             $scope.milestones = _.filter data, (item) ->
                 item.project == $rootScope.projectId

@@ -259,7 +253,6 @@

 # One backlog milestone controller
-
 @BacklogMilestoneController = ($scope, rs) ->
     calculateStats = ->
         pointIdToOrder = greenmine.utils.pointIdToOrder($scope.constants.points)


Add spec to ensure snippet itself is not modified

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -115,6 +115,13 @@
               line 1
               \tline 2$1
               $2
+            """
+
+          "multiline with indented placeholder tabstop":
+            prefix: "t4"
+            body: """
+              line ${1:1}
+                ${2:body...}
             """

           "nested tab stops":
@@ -409,6 +416,41 @@
             expect(markers[1].getBufferRange().start).toEqual row: 4, column: 4
             expect(markers[1].getBufferRange().end).toEqual markers[1].getBufferRange().start

+          it "does not change the relative positioning of the tab stops when inserted multiple times", ->
+            editor.setCursorScreenPosition([2, Infinity])
+            editor.insertNewline()
+            editor.insertText 't4'
+            atom.commands.dispatch editorElement, 'snippets:expand'
+
+            markers = editor.getMarkers()
+            expect(markers.length).toBe 2
+            expect(markers[0].getBufferRange().start).toEqual row: 3, column: 9
+            expect(markers[0].getBufferRange().end).toEqual row: 3, column: 10
+            expect(markers[1].getBufferRange().start).toEqual row: 4, column: 6
+            expect(markers[1].getBufferRange().end).toEqual row: 4, column: 13
+
+            atom.commands.dispatch editorElement, 'snippets:next-tab-stop'
+            editor.insertText 't4'
+            atom.commands.dispatch editorElement, 'snippets:expand'
+
+            markers = editor.getMarkers()
+            expect(markers.length).toBe 4
+            expect(markers[2].getBufferRange().start).toEqual row: 4, column: 11
+            expect(markers[2].getBufferRange().end).toEqual row: 4, column: 12
+            expect(markers[3].getBufferRange().start).toEqual row: 5, column: 8
+            expect(markers[3].getBufferRange().end).toEqual row: 5, column: 15
+
+            editor.setText('') # Clear editor
+            editor.insertText 't4'
+            atom.commands.dispatch editorElement, 'snippets:expand'
+
+            markers = editor.getMarkers()
+            expect(markers.length).toBe 6
+            expect(markers[4].getBufferRange().start).toEqual row: 0, column: 5
+            expect(markers[4].getBufferRange().end).toEqual row: 0, column: 6
+            expect(markers[5].getBufferRange().start).toEqual row: 1, column: 2
+            expect(markers[5].getBufferRange().end).toEqual row: 1, column: 9
+
       describe "when multiple snippets match the prefix", ->
         it "expands the snippet that is the longest match for the prefix", ->
           editor.insertText('t113')


Use favicon for battle stats

Use favicon for battle stats

--- app/assets/javascripts/battles.coffee
+++ app/assets/javascripts/battles.coffee
@@ -156,7 +156,7 @@
   $("#{id} .other-info").append(" ")
   $("#{id} .other-info").append(" ")
   $("#{id} .other-info").append(" ")
-  $("#{id} .other-info").append("  MV:#{unit.move} EV:#{unit.evade} DEF:#{unit.defense} | DMG:#{unit.damage} ACC:#{unit.accuracy} Range:#{unit.range_min}-#{unit.range_max}")
+  $("#{id} .other-info").append("  #{unit.move}  #{unit.evade}/#{unit.defense}  #{unit.damage}/#{unit.accuracy}/#{unit.range_min}-#{unit.range_max} ")

 clearUnitInfoBoxes = ->
   for id in ["#selected-unit", "#targeted-unit"]


Reduced the minimuminput to 0 to autopopulate autocompletes

--- app/assets/javascripts/core.js.coffee
+++ app/assets/javascripts/core.js.coffee
@@ -17,7 +17,7 @@
         $field.select2(
             width: "off",
             multiple: $field.data("multiple"),
-            minimumInputLength: 1
+            minimumInputLength: 0
             placeholder: $field.data("placeholder")
             ajax:
                 url: $field.data("url")


add refresh list; add multiple undos;

add refresh list; add multiple undos;

--- app/assets/javascripts/file.js.coffee
+++ app/assets/javascripts/file.js.coffee
@@ -184,7 +184,7 @@
       $.post(window.routes.remove_files_path.replace('/:path', item.data('path')), {
         _method: 'delete'
       }).success (d) ->
-        window.available_undos = [d]
+        window.available_undos.unshift d
         load_file_list item.closest('ul.column')
       .error ->
         update_footer -2
@@ -195,11 +195,17 @@
     if window.available_undos.length > 0
       undo = window.available_undos[0].undo
       $.post(undo.action, undo.parameters)
+      .complete ->
+        window.available_undos = window.available_undos.splice(1)
       .success ->
-        window.available_undos = window.available_undos.splice(1)
         load_file_list window.selected_items[0].find('ul.column')
       .error ->
         update_footer -3
+
+  # refresh list
+  refresh = ->
+    if window.selected_items.length > 0
+      load_file_list window.selected_items[0].find('ul.column')

   menu_items =
     for_files: ['Open--','Move to Trash--','Get Info','Rename'],
@@ -217,7 +223,7 @@

   menu_items_clicked =
     for_files: [null, move_to_trash, null, null],
-    for_lists: [undo, null, null, null],
+    for_lists: [undo, refresh, null, null],

   $(document).bind 'contextmenu', (e) ->
     if $('#context_menu').css('z-index') != '0'


add empty comp question function

add empty comp question function

--- app/assets/javascripts/game.js.coffee
+++ app/assets/javascripts/game.js.coffee
@@ -81,13 +81,16 @@

     else alert('Fuck off')

+  comp_question = ->
+#    TODO 1. get list of properties, group by count 2. Detect optimal cut 3. Build question
+    true

   proc_question_panel = ->
     data = if state == states['player1'] # comp turn
       """

Comp turn

-
+        #{comp_question()}
       """
     else
       """


Coffeescript tweaks

Coffeescript tweaks

--- app/assets/javascripts/notices.coffee
+++ app/assets/javascripts/notices.coffee
@@ -1,7 +1,2 @@
-$(document).ready ->
-  setTimeout (->
-    $(".alert").fadeOut 350
-    return
-  ), 2500
-  return
-
+$ ->
+  setTimeout (-> $(".alert").fadeOut(350)), 2500


modifying hquery assignment to work in spider monkey for map reduce queries

--- app/assets/javascripts/patient.coffee
+++ app/assets/javascripts/patient.coffee
@@ -1,6 +1,7 @@
+this.hQuery ||= {}
 # =require core.coffee
 # =require medication.coffee
-this.hQuery ||= {}
+
 # =require condition.coffee
 # =require encounter.coffee
 # =require procedure.coffee


modified reports.coffee to allow the (select if none) option to pass through the jQuery filter.

--- app/assets/javascripts/reports.coffee
+++ app/assets/javascripts/reports.coffee
@@ -26,13 +26,19 @@
     else
       $('#report_intro_id').empty()

+
   units = $('#report_unit_id').html()
   $('#report_subject_id').change ->
     subject = $('#report_subject_id :selected').text()
     escaped_subject = subject.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
     options = $(units).filter("optgroup[label='#{escaped_subject}']").html()
+    # if options
+    #   $('#report_unit_id').html(options)
+    # else
+    #   $('#report_unit_id').empty()
     if options
-      $('#report_unit_id').html(options)
+      $('#report_unit_id').children().not(':first').detach()
+      $('#report_unit_id').append(options)
     else
       $('#report_unit_id').empty()

@@ -41,8 +47,13 @@
     subject = $('#report_subject_id :selected').text()
     escaped_subject = subject.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
     options = $(units).filter("optgroup[label='#{escaped_subject}']").html()
+    # if options
+    #   $('#report_initial_id').html(options)
+    # else
+    #   $('#report_initial_id').empty()
     if options
-      $('#report_initial_id').html(options)
+      $('#report_initial_id').children().not(':first').detach()
+      $('#report_initial_id').append(options)
     else
       $('#report_initial_id').empty()

@@ -51,8 +62,13 @@
     subject = $('#report_subject_id :selected').text()
     escaped_subject = subject.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
     options = $(units).filter("optgroup[label='#{escaped_subject}']").html()
+    # if options
+    #   $('#report_ending_id').html(options)
+    # else
+    #   $('#report_ending_id').empty()
     if options
-      $('#report_ending_id').html(options)
+      $('#report_ending_id').children().not(':first').detach()
+      $('#report_ending_id').append(options)
     else
       $('#report_ending_id').empty()

@@ -61,7 +77,12 @@
     subject = $('#report_subject_id :selected').text()
     escaped_subject = subject.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
     options = $(futures).filter("optgroup[label='#{escaped_subject}']").html()
+    # if options
+    #   $('#report_future_id').html(options)
+    # else
+    #   $('#report_future_id').empty()
     if options
-      $('#report_future_id').html(options)
+      $('#report_future_id').children().not(':first').detach()
+      $('#report_future_id').append(options)
     else
       $('#report_future_id').empty()


OutputStream must be exposed on window so plugins can listen it

--- app/assets/javascripts/task.js.coffee
+++ app/assets/javascripts/task.js.coffee
@@ -1,8 +1,9 @@
 #= require_tree ./task
 #= require_self

+@OutputStream = new Stream
+
 jQuery ->
-  OutputStream = new Stream
   OutputStream.addEventListener 'status', (status) ->
     $('[data-status]').attr('data-status', status)


Addition of Content to Draft

Addition of Content to Draft

--- app/pages/about/acknowledgements.cjsx
+++ app/pages/about/acknowledgements.cjsx
@@ -13,6 +13,10 @@

     You can find a list of publications written using the Zooniverse on the Publications tab (link).

+    If you would like to reference the project builder, information for this is below:
+
+    *Bowyer, A.; Lintott, C.; Hines, G.; Allen, C.; Paget, E. Panoptes, a project building tool for citizen science. 2015; in press. [Google Scholar](url)*
+
     If you have any questions about how to acknowledge the Zooniverse, such as referencing a particular individual or custom code, get in touch (link).
 '''


Trigger 'vtex.sku.selected' after a 'vtex.sku.select'

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -77,7 +77,6 @@
 		@bindEvents()
 		if @skus.length == 1
 			@selectSku(@skus[0])
-			@triggerProductEvent 'vtex.sku.selected', @skus[0]

 		# Seleciona dimensoes que tem somente um valor possivel
 		if @options.selectSingleDimensionsOnOpening
@@ -227,6 +226,7 @@
 	selectSku: (sku) =>
 		for dimension in @dimensions
 			@selectDimensionValue(dimension.name, sku.dimensions[dimension.name])
+		@triggerProductEvent 'vtex.sku.selected', sku

 	selectDimensionValue: (dimensionName, valueName) =>
 		@finditemDimensionValueInput(dimensionName, valueName).prop('checked', true).trigger('change')


improve mysql error

improve mysql error

--- app/templates/gulp/tasks/mysql.coffee
+++ app/templates/gulp/tasks/mysql.coffee
@@ -16,8 +16,10 @@

   exec cmd, (err, stdout, stderr) ->
    console.log stdout
-   console.log stdout
-   console.log err
+   console.log stderr
+
+   if err
+     console.log err


remove comments

remove comments


SplitView: defaults improved for better performance

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -7,16 +7,16 @@

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

-    options.type      or= "vertical"        # "vertical" or "horizontal"
-    options.resizable  ?= yes               # yes or no
-    options.sizes     or= ['50%','50%']     # an Array of Strings such as ["50%","50%"] or ["500px","150px",null] and null for the available rest area
-    options.minimums  or= [0,0]             # an Array of Strings
-    options.maximums  or= ['100%','100%']   # an Array of Strings
-    options.views     or= null              # an Array of KDViews
-    options.fixed     or= []                # an Array of Booleans
-    options.duration  or= 200               # a Number in miliseconds
-    options.separator or= null              # a KDView instance or null for default separator
-    options.colored    ?= no
+    options.type      or= "vertical"         # "vertical" or "horizontal"
+    options.resizable  ?= yes                # yes or no
+    options.sizes     or= [.5, .5]           # an Array of Strings such as ["50%","50%"] or ["500px","150px",null] and null for the available rest area
+    options.minimums  or= [0, 0]             # an Array of Strings
+    options.maximums  or= ['100%', '100%']   # an Array of Strings
+    options.views     or= []                 # an Array of KDViews
+    options.fixed     or= []                 # an Array of Booleans
+    options.duration  or= 200                # a Number in miliseconds
+    options.separator or= null               # a KDView instance or null for default separator
+    options.colored    ?= no                 # a Boolean
     options.type        = options.type.toLowerCase()
     options.cssClass    = KD.utils.curry "kdsplitview kdsplitview-#{options.type}", options.cssClass


whitespace change for consistency with old

--- app/controllers/subject_viewer.coffee
+++ app/controllers/subject_viewer.coffee
@@ -72,10 +72,11 @@
       if ExperimentServer.currentCohort?
         @classification.metadata.cohort = ExperimentServer.currentCohort
       @html template @classification
+
       @active = Math.floor @classification.subject.location.standard.length / 2
       @activate @active
+
       @onClassificationChange()
-
     else
       @html ''


Auto clan schema (#6106)

Auto clan schema (#6106)

* Clan kind schema

* Add displayName

* Initial auto clan schema changes

Co-authored-by: Andrew Jakubowicz <a294cb40eaf0c6b8a0361e0e350bb8d2fe25d268@gmail.com>


Fix downloading good solutions (#42)

Fix downloading good solutions (#42)

Merge pull request #105 from DanilaZhebryakov/master

--- server/api/setupApi.coffee
+++ server/api/setupApi.coffee
@@ -369,8 +369,13 @@
     app.get '/api/submitSource/:id', ensureLoggedIn, wrap (req, res) ->
         submit = await Submit.findById(req.params.id)
         if not req.user?.admin and ""+req.user?.userKey() != ""+submit.user
-            res.status(403).send('No permissions')
-            return
+            if submit.quality == 0
+                res.status(403).send('No permissions')
+                return
+            result = await Result.findByUserAndTable(req.user?.userKey(), submit.problem)
+            if not result or result.solved <= 0
+                res.status(403).send('No permissions')
+                return
         mimeType = fileType(Buffer.from(submit.sourceRaw))?.mime || "text/plain"
         res.contentType(mimeType)
         res.send(submit.sourceRaw)


DateTime

DateTime

- Fixed date picker for dates > 30th.

--- src/elements/DateTime/DateTime.coffee
+++ src/elements/DateTime/DateTime.coffee
@@ -985,9 +985,13 @@
 				# CUI.debug "click on date table", ev.getTarget()
 				if $target.closest(".cui-date-time-day").length
 					data = DOM.data($target.closest("td,.cui-td")[0])
+
+					# order here is important, we need to set the month
+					# before we set the date!
+					@__current_moment.year(data.year)
+					@__current_moment.month(data.month)
 					@__current_moment.date(data.date)
-					@__current_moment.month(data.month)
-					@__current_moment.year(data.year)
+
 					@updateCalendar(@__current_moment)

 					if not CUI.__ng__


added filter icon

added filter icon

--- src/base/Icon/Icon.coffee
+++ src/base/Icon/Icon.coffee
@@ -54,6 +54,7 @@
 		failed: "fa-warning"
 		no_right: "fa-slack"
 		file: if CUI.__ng__ then "fa-file" else "fa-file-o"
+		filter: "fa-filter"
 		refresh: "fa-refresh"
 		zip: "fa fa-file-archive-o"
 		rotate_right: "fa-rotate-right"


No need to set content type for requests without body

--- assets/scripts/lib/travis/ajax.coffee
+++ assets/scripts/lib/travis/ajax.coffee
@@ -23,11 +23,13 @@
     options.url = "#{endpoint}#{url}"
     options.type = method
     options.dataType = options.dataType || 'json'
-    options.contentType = options.contentType || 'application/json; charset=utf-8'
     options.context = this

     if options.data && method != 'GET'
       options.data = JSON.stringify(options.data)
+
+    if method != 'GET' && method != 'HEAD'
+      options.contentType = options.contentType || 'application/json; charset=utf-8'

     success = options.success || (->)
     options.success = (data) =>


refactor roughDate filter to always return exact date with rough date appended afterwards

--- src/coffee/filters.coffee
+++ src/coffee/filters.coffee
@@ -26,28 +26,33 @@
   Filters.filter 'roughDate', Array '$filter', (filters) -> (str) ->
     date = new Date str
     now = new Date()
+    rDate;

     minutesAgo = (now.getTime() - date.getTime()) / 60 / 1000
+    hoursAgo = minutesAgo / 60
+    daysAgo = hoursAgo / 24
+
+    if hoursAgo < 48 && (now.getDate()-1 == date.getDate())
+      rDate = "yesterday"
+
+    if hoursAgo < now.getHours()
+      if hoursAgo.toFixed() == "1"
+        rDate = "today, one hour ago"
+      else
+        rDate = "today, #{ hoursAgo.toFixed() } hours ago"
+
+    if minutesAgo < 60
+      rDate =  "today, #{ minutesAgo.toFixed() } minutes ago"
+
+    if minutesAgo < 2
+      rDate = "one minute ago"

     if minutesAgo < 1
-      return "a moment ago"
+      rDate = "a moment ago"

-    if minutesAgo < 2
-      return "one minute ago"
-
-    if minutesAgo < 60
-      return "today, #{ minutesAgo.toFixed() } minutes ago"
-
-    hoursAgo = minutesAgo / 60
-
-    if hoursAgo < now.getHours()
-      return "today, #{ hoursAgo.toFixed() } hours ago"
-
-    daysAgo = hoursAgo / 24
-
-    if hoursAgo > (1 + (now.getHours() / 24))
-      return filters('date')(str)
+    if rDate
+      rDate = filters('date')(str) + " (" +rDate + ")"
     else
-      return "yesterday"
+      rDate = filters('date')(str)

   return Filters


fix(sphere-node-product-csv-sync): Change misleading error message (#216)

--- src/coffee/mapping.coffee
+++ src/coffee/mapping.coffee
@@ -78,7 +78,7 @@

   ensureValidSlug: (slug, rowIndex, appendix = '') ->
     unless _.isString(slug) and slug.length > 2
-      @errors.push "[row #{rowIndex}:#{CONS.HEADER_SLUG}] Can't generate valid slug out of '#{slug}'!"
+      @errors.push "[row #{rowIndex}:#{CONS.HEADER_SLUG}] Can't generate valid slug out of '#{slug}'! If you did not provide slug in your file, please do so as slug could not be auto-generated from the product name given."
       return
     @slugs or= []
     currentSlug = "#{slug}#{appendix}"


Use absolute path.

Use absolute path.

--- src/compilers/sass.coffee
+++ src/compilers/sass.coffee
@@ -36,7 +36,7 @@

   includePaths = [
     path.join process.cwd(), 'node_modules'
-    path.dirname opts.filename
+    path.dirname opts.absolutePath
   ]

   try


:art:

:art:

--- build/tasks/generate-asar-task.coffee
+++ build/tasks/generate-asar-task.coffee
@@ -8,12 +8,21 @@
   grunt.registerTask 'generate-asar', 'Generate asar archive for the app', ->
     done = @async()

+    unpack = [
+      '*.node'
+      'ctags-darwin'
+      'ctags-linux'
+      'ctags-win32.exe'
+      '\\.ctags'
+    ]
+    unpack = "{#{unpack.join(',')}}"
+
     appDir = grunt.config.get('atom.appDir')
     unless fs.existsSync(appDir)
       grunt.log.error 'The app has to be built before generating asar archive.'
       return done(false)

-    asar.createPackageWithOptions appDir, path.resolve(appDir, '..', 'app.asar'), {unpack: '{*.node,ctags-darwin,ctags-linux,ctags-win32.exe,\\.ctags}'}, (err) ->
+    asar.createPackageWithOptions appDir, path.resolve(appDir, '..', 'app.asar'), {unpack}, (err) ->
       return done(err) if err?

       rm appDir


Use the softWrap property directly

Use the softWrap property directly

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -49,10 +49,10 @@
         @updateWrappedScreenLines()

     @observeConfig 'editor.preferredLineLength', callNow: false, =>
-      @updateWrappedScreenLines() if @getSoftWrap() and atom.config.get('editor.softWrapAtPreferredLineLength')
+      @updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')

     @observeConfig 'editor.softWrapAtPreferredLineLength', callNow: false, =>
-      @updateWrappedScreenLines() if @getSoftWrap()
+      @updateWrappedScreenLines() if @softWrap

   copy: ->
     newDisplayBuffer = atom.create(new DisplayBuffer({@buffer, tabLength: @getTabLength()}))
@@ -88,8 +88,10 @@
   # visible - A {Boolean} indicating of the tokenized buffer is shown
   setVisible: (visible) -> @tokenizedBuffer.setVisible(visible)

+  # Deprecated: Use the softWrap property directly
   setSoftWrap: (@softWrap) -> @softWrap

+  # Deprecated: Use the softWrap property directly
   getSoftWrap: -> @softWrap

   # Set the number of characters that fit horizontally in the editor.
@@ -98,7 +100,7 @@
   setEditorWidthInChars: (editorWidthInChars) ->
     previousWidthInChars = @editorWidthInChars
     @editorWidthInChars = editorWidthInChars
-    if editorWidthInChars isnt previousWidthInChars and @getSoftWrap()
+    if editorWidthInChars isnt previousWidthInChars and @softWrap
       @updateWrappedScreenLines()

   getSoftWrapColumn: ->
@@ -422,7 +424,7 @@
   # Returns a {Number} representing the `line` position where the wrap would take place.
   # Returns `null` if a wrap wouldn't occur.
   findWrapColumn: (line, softWrapColumn=@getSoftWrapColumn()) ->
-    return unless @getSoftWrap()
+    return unless @softWrap
     return unless line.length > softWrapColumn

     if /\s/.test(line[softWrapColumn])


Softwrap always takes into account the vertical scrollbar width

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -356,8 +356,10 @@
       if editorWidthInChars isnt previousWidthInChars and @softWrap
         @updateWrappedScreenLines()

+  # Returns the editor width in characters for soft wrap.
   getEditorWidthInChars: ->
-    width = @getWidth()
+    width = @width ? @getScrollWidth()
+    width -= @getVerticalScrollbarWidth()
     if width? and @defaultCharWidth > 0
       Math.floor(width / @defaultCharWidth)
     else


These methods aren't supposed to be overwritten

--- src/django-formset.coffee
+++ src/django-formset.coffee
@@ -60,7 +60,7 @@
       newFormElem = @template.clone().removeClass("empty-form")

       newForm = new $.fn.djangoFormset.Form(newFormElem, this)
-      newForm.initFormIndex(@totalForms.val())
+      newForm._initFormIndex(@totalForms.val())
       @totalForms.val(parseInt(@totalForms.val()) + 1)

       newFormElem.insertAfter(@insertAnchor)
@@ -80,7 +80,7 @@
       @totalForms.val(parseInt(@totalForms.val()) - 1)
       @forms.splice(index, 1)
       for form, i in @forms
-        form.updateFormIndex(i)
+        form._updateFormIndex(i)

       if @forms.length == 0
         @insertAnchor = @template
@@ -125,11 +125,11 @@
       )
       return

-    initFormIndex: (index) ->
+    _initFormIndex: (index) ->
       @_replaceFormIndex("__prefix__", index)
       return

-    updateFormIndex: (index) ->
+    _updateFormIndex: (index) ->
       @_replaceFormIndex('\\d+', index)
       return


- fix js

- fix js

--- app/assets/javascripts/spree/cart.js.coffee
+++ app/assets/javascripts/spree/cart.js.coffee
@@ -184,7 +184,7 @@
     url: $('#shipping_method').data('url')
     data: params_shippment
     beforeSend: ( jqXHR ) ->
-      $('#methods').html ''
+      $('#methods .time-load').show()
     success: (data) ->
       checkAdjustments()
       if data? && data.length


removing problematic and unnecessary render call for dashboard

Former-commit-id: f0d1e235d31bb9a7427bb267c683030936d56017 [formerly 2eb73908e0f78f42265dc7037e2b9307b08dc766] [formerly 597560528c99552e409a0516261bf84fcbcecb37 [formerly 597560528c99552e409a0516261bf84fcbcecb37 [formerly a820d0c991931528f14adacb882eab7f495619ec]]]
Former-commit-id: b6bd7856f2fa627e135e53ba69c5bab4875d5c96 [formerly a6b29ac135c07733211d88334f0bf55618d4c880]
Former-commit-id: caf769327eee4d440de061cda06b9205aecb5c89
Former-commit-id: 82eb36de44623859ae69d4fd47899a5e2186d6af

--- app/assets/javascripts/views/appview.coffee
+++ app/assets/javascripts/views/appview.coffee
@@ -22,7 +22,6 @@

   render : () ->
     @usermanagerview.render()
-    @dashboardview.render()
     @proposals_manager.render()

     # kick off events for the current path


fix sql query checking last access time

fix sql query checking last access time

--- app/coffee/ProjectPersistenceManager.coffee
+++ app/coffee/ProjectPersistenceManager.coffee
@@ -72,7 +72,7 @@
 		job = (cb)->
 			keepProjectsFrom = new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT)
 			q = {}
-			q[db.op.gt] = keepProjectsFrom
+			q[db.op.lt] = keepProjectsFrom
 			db.Project.findAll(where:{lastAccessed:q})
 				.then((projects) ->
 					cb null, projects.map((project) -> project.project_id)


fix exception on open create us lightbox

--- app/coffee/modules/common/estimation.coffee
+++ app/coffee/modules/common/estimation.coffee
@@ -57,6 +57,7 @@
                         totalPoints: @calculateTotalPoints()
                         roles: @calculateRoles()
                         editable: @isEditable
+                        loading: false
                     }
                     mainTemplate = "common/estimation/us-estimation-points-per-role.html"
                     template = $template.get(mainTemplate, true)


Edit Aardvark text

Edit Aardvark text

Removed parentheses from scientific name

--- app/lib/field-guide-content/aardvark.coffee
+++ app/lib/field-guide-content/aardvark.coffee
@@ -3,7 +3,7 @@
     Aardvarks are the last survivors of a group of primitive ungulates. They have a stocky body with thick skin that ranges in color from yellowish to pinkish and is sparsely covered with bristly hair. Aardvarks have short, powerful legs with sharp claws (four on the forefeet and five on each hind foot), a prominently arched back, a short neck, long pointed ears, and a long muscular tail that tapers to a point. Their head is elongated with a long snout. They are well adapted to feed on termites and ants with nostrils that can be sealed and a protractile tongue.
   '''

-  scientificName: '(Orycteropus afer)'
+  scientificName: 'Orycteropus afer'
   mainImage: 'assets/fieldguide-content/mammals/aardvark/aardvark-feature.jpg'

   conservationStatus: 'Least Concern' # Options are Least Concern, Near Threatened, Vulnerable, and Endangered; all have their own little icons as well.


working on issues now

working on issues now

--- app/scripts/alchemy/core/updateGraph.coffee
+++ app/scripts/alchemy/core/updateGraph.coffee
@@ -45,6 +45,8 @@
         .attr "y1", 0
         .attr "x2", 0
         .attr "y2", 0
+
+    # draw nodes and edges
     alchemy.drawing.drawedges(alchemy.edge)
     alchemy.drawing.drawnodes(alchemy.node)


validator depends on jquery

validator depends on jquery

--- src/textorum/tinymce/validator.coffee
+++ src/textorum/tinymce/validator.coffee
@@ -28,6 +28,7 @@
   helper = require('../helper')
   {RNGParser} = require('../relaxng/parse')
   objects = require('../relaxng/objects')
+  $ = require('jquery')

   class TextorumValidator
     constructor: (@validationOutputSelector, @editor, alternateStart) ->


comments and log messages for @nathakits to learn/work on

--- assets/coffeescript/header_animation.coffee
+++ assets/coffeescript/header_animation.coffee
@@ -3,22 +3,29 @@
   last_scroll_top = 0
   delta = 40
   navbar_height = $('.Header-container').outerHeight()
+  console.log "navbar_height: #{ navbar_height }"

   $(window).scroll (event)=>
     did_scroll = true

   has_scrolled = ()=>
     st = $(this).scrollTop()
-    if Math.abs(last_scroll_top - st) <= delta
+    console.log "scroll top position: #{ st }"
+
+    if Math.abs(last_scroll_top - st) < delta
+      # if we move less than delta early return (do nothing)
       return

     if st < $('.Header').outerHeight()
+      # reveal everything
       $('.Header-container').removeClass('Header-container-show').removeClass('Header-container-hide')
       # coordinates = "translate3d(0px,"+st+"px,0px)"
       # $('.Header-container').css("transform",coordinates)
     else if st > last_scroll_top and st > navbar_height
+      console.log "scrolling down past header --> hide everything"
       $('.Header-container').removeClass('Header-container-show').addClass('Header-container-hide')
     else if st + $(window).height() < $(document).height()
+      console.log "show nav tabs --> document height: #{ $(document).height() } \n window height: #{ $(window).height() }"
       $(".Header-container").removeClass('Header-container-hide').addClass('Header-container-show')

     last_scroll_top = st


oops -- remove some log

oops -- remove some log

--- salvus/page/syncstring.coffee
+++ salvus/page/syncstring.coffee
@@ -112,8 +112,8 @@
             f         : f
             max_tries : 30
             factor    : 1.5
-            name      : "SyncString"
-            log       : (m) -> console.log(m)
+            #name      : "SyncString"
+            #log       : (m) -> console.log(m)
             cb        : cb

     _sync: (cb) =>


groups: CRUD methods for group bundles

groups: CRUD methods for group bundles

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -78,7 +78,8 @@
         'resolvePendingRequests','fetchVocabulary', 'fetchMembershipStatuses',
         'setBackgroundImage', 'removeBackgroundImage', 'fetchAdmin', 'inviteByEmail',
         'inviteByEmails', 'inviteByUsername', 'kickMember', 'transferOwnership',
-        'remove', 'sendSomeInvitations', 'fetchNewestMembers', 'countMembers'
+        'remove', 'sendSomeInvitations', 'fetchNewestMembers', 'countMembers',
+        'fetchBundle', 'createBundle', 'destroyBundle'
       ]
     schema          :
       title         :
@@ -106,12 +107,15 @@
           customType      :
             type          : String
             default       : 'defaultImage'
-            enum          : ['Invalid type', [ 'defaultImage', 'customImage', 'defaultColor', 'customColor']]
+            enum          : ['invalid type', [ 'defaultImage', 'customImage', 'defaultColor', 'customColor']]
           customValue     :
             type          : String
             default       : '1'
           customOptions   : Object
     relationships   :
+      bundle        :
+        targetType  : 'JGroupBundle'
+        as          : 'owner'
       permissionSet :
         targetType  : JPermissionSet
         as          : 'owner'
@@ -1175,3 +1179,32 @@
       unless err
         for admin in admins
           admin.sendNotification event, contents
+
+  destroyBundle: (callback) ->
+    @fetchBundle (err, bundle) =>
+      return callback err  if err?
+      return callback new KodingError 'Bundle not found!'  unless bundle?
+
+      bundle.remove callback
+
+  destroyBundle$: permit 'change bundle',
+    success: (client, callback) -> @destroyBundle callback
+
+  createBundle: (callback) ->
+    @fetchBundle (err, bundle) =>
+      return callback err  if err?
+      return callback new KodingError 'Bundle exists!'  if bundle?
+
+      JGroupBundle = require '../bundle/groupbundle'
+
+      bundle = new JGroupBundle
+      bundle.save (err) =>
+        return callback err  if err?
+
+        @addBundle bundle, callback
+
+  createBundle$: permit 'change bundle',
+    success: (client, callback) -> @createBundle callback
+
+  fetchBundle$: permit 'change bundle',
+    success: (client, rest...) -> @fetchBundle rest...


build date with timezone again fro travis

--- test/front/coffee/suggest.spec.coffee
+++ test/front/coffee/suggest.spec.coffee
@@ -234,31 +234,31 @@

     callbackResponse = [
       {
-      "begin":new Date(2012,3,19,15,35).getTime()
+      "begin":new Date("Thu, 19 Avr 2012 15:35:00 +0200").getTime()
       "title":"title 1"
       "location":"location 1"
       "tags":["MYTAG","MYTAG2"]
       "description":"lorem"
       },
       {
-      "begin":new Date(2012,3,19,15,35).getTime()
+      "begin":new Date("Thu, 19 Avr 2012 15:35:00 +0200").getTime()
       "title":"title 2"
       "location":"location 2"
       "tags":["MYTAG2"]
       },
       {
-      "begin":new Date(2012,3,19,15,35).getTime()
+      "begin":new Date("Thu, 19 Avr 2012 15:35:00 +0200").getTime()
       "title":"title 3"
       "location":"location 3"
       "tags":["MYTAG"]
       },
       {
-      "begin":new Date(2012,3,19,15,35).getTime()
+      "begin":new Date("Thu, 19 Avr 2012 15:35:00 +0200").getTime()
       "title":"title 4"
       "location":"location 4"
       },
       {
-      "begin":new Date(2012,3,19,15,35).getTime()
+      "begin":new Date("Thu, 19 Avr 2012 15:35:00 +0200").getTime()
       "title":"title 5"
       "location":"location 5"
       }


Fix export if any operation has category -1 "None";

--- server/controllers/all.coffee
+++ server/controllers/all.coffee
@@ -88,9 +88,13 @@
     all.operations ?= []
     for o in all.operations
         if o.categoryId?
-            if not categoryMap[o.categoryId]?
-                throw 'unexpected category id'
-            o.categoryId = categoryMap[o.categoryId]
+            cid = o.categoryId
+            if +cid is -1
+                o.categoryId = undefined
+            else if not categoryMap[cid]?
+                log.warn "unexpected category id: #{cid}"
+            else
+                o.categoryId = categoryMap[cid]
         # Strip away id
         o.id = undefined


use pick on collection_view initialize as well

--- src/chaplin/views/collection_view.coffee
+++ src/chaplin/views/collection_view.coffee
@@ -93,10 +93,11 @@
       # Start observing the collection
       @addCollectionListeners()

-      # Apply options
-      @renderItems = options.renderItems if options.renderItems?
-      @itemView = options.itemView       if options.itemView?
-      @filter options.filterer           if options.filterer?
+      # Apply options to view instance
+      _(this).extend _.pick options, ['renderItems', 'itemView']
+
+      # Apply a filter if one provided
+      @filter options.filterer if options.filterer?

     # Binding of collection listeners
     addCollectionListeners: ->


published plans should show modal regardless of open state

--- src/components/course-calendar/plan.cjsx
+++ src/components/course-calendar/plan.cjsx
@@ -130,8 +130,8 @@

       # alias modal hide to also make plan look un-selected
       @refs.trigger.hide = ->
+        hide()
         syncClosePlan()
-        hide()

   syncOpenPlan: ->
     @setIsViewingStats(true) unless @state.isViewingStats
@@ -169,7 +169,7 @@
     {isPublishing} = @state
     {plan} = item

-    if isPublishing
+    if isPublishing and not plan.isPublished
       planModal = <CoursePlanPublishingDetails
         plan={plan}
         courseId={courseId}
@@ -267,7 +267,7 @@
       label = @renderLabel(rangeDuration, durationLength, plan, index, offset)

       renderFn = 'renderEditPlan'
-      renderFn = 'renderOpenPlan' if plan.isOpen and plan.isPublished or (isPublishing) or (publishStatus is 'completed')
+      renderFn = 'renderOpenPlan' if plan.isPublished or (isPublishing) or (publishStatus is 'completed')

       @[renderFn](planStyle, planClasses, label, index)
     )


Refactor event extension with triggerEvent.

--- src/js/jquery.extension.hlf.event.coffee
+++ src/js/jquery.extension.hlf.event.coffee
@@ -84,8 +84,7 @@
       $trigger
         .removeData attr('timer')
         .removeData attr('intentional')
-    debugLog 'truemouseleave'
-    $trigger.trigger 'truemouseleave'
+    triggerEvent 'truemouseleave', $trigger
     return yes

   performCheck = (event, $trigger, state) ->
@@ -96,11 +95,7 @@
     mouse.x.previous = event.pageX
     mouse.y.previous = event.pageY
     if state.intentional is yes and event.type is 'mouseover'
-      debugLog 'truemouseenter'
-      $trigger.trigger new $.Event(
-        'truemouseenter',
-        { pageX: mouse.x.current, pageY: mouse.y.current }
-      )
+      triggerEvent 'truemouseenter', $trigger
     state.timer.cleared = yes
     $trigger.data attr('intentional'), state.intentional
     $trigger.data attr('timer'), state.timer
@@ -108,6 +103,17 @@
   track = (event) ->
     mouse.x.current = event.pageX
     mouse.y.current = event.pageY
+
+  triggerEvent = (name, $trigger) ->
+    switch name
+      when 'truemouseenter' then event =
+        new $.Event name, { pageX: mouse.x.current, pageY: mouse.y.current }
+      when 'truemouseleave' then event = name
+      else event = null
+    if event?
+      debugLog name
+      $trigger.trigger event
+    else return no

   $.event.special.truemouseenter =
     setup: (data, namespaces) ->


HIRE-224: bugfix

HIRE-224: bugfix

--- src/main/java/app/profile/profile.coffee
+++ src/main/java/app/profile/profile.coffee
@@ -15,13 +15,14 @@
   dateFormat = $('.datepicker').attr("data-date-format").toUpperCase()
   $scope.profile = profile
   $scope.projects = projects
+  console.log($scope.profile.projectAssociations)
   if $scope.profile.projectAssociations?
 		    $scope.projectData = $scope.profile.projectAssociations.map (project) ->
 		      data = {}
-		      data.locations = project.locations.slice() if project.locations?
-		      data.technologies = project.technologies.slice() if project.technologies?
-		      data.start = project.start
-		      data.end = project.end
+		      data.locations = project.project.locations.slice() if project.project.locations?
+		      data.technologies = project.project.technologies.slice() if project.project.technologies?
+		      data.start = moment(project.project.start).format(dateFormat)
+		      data.end = moment(project.project.end).format(dateFormat)
 		      return data
   else $scope.projectData = []
   if moment(profile.workExperience).isValid()
@@ -142,7 +143,6 @@

   $scope.loadProjectDefaults = (index) ->
     data = {}
-    console.log($scope.profile.projectAssociations[index].project.locations)
     if $scope.profile.projectAssociations[index].project.locations?
     	data.locations = $scope.profile.projectAssociations[index].project.locations.slice()
     if $scope.profile.projectAssociations[index].project.technologies?
@@ -154,8 +154,6 @@
     $scope.projectData.splice(index, 1, data)
     $scope.loadInitialProjectDefaults(index)
     return
-
-

   $scope.$watch 'files', ->
     $scope.upload $scope.files


Cast remark's vfile to string

Cast remark's vfile to string

Another change on the API was the change of the return type.  It now return a vfile instead of a string.

Old:
https://github.com/wooorm/remark/blob/d1c52a3b28223e55aef9067ab725330019c87aae/doc/remark.3.md#remarkprocessfile-options-done

New:
https://github.com/wooorm/unified#processorprocessfilevalue-options-done

--- src/beautifiers/remark.coffee
+++ src/beautifiers/remark.coffee
@@ -36,7 +36,7 @@
     return new @Promise((resolve, reject) ->
       try
         remark = require 'remark'
-        cleanMarkdown = remark().process(text, options)
+        cleanMarkdown = remark().process(text, options).toString()
         resolve cleanMarkdown
       catch err
         @error("Remark error: #{err}")


Extract process logic and introduce streams entry point

--- src/coffee/stockimport.coffee
+++ src/coffee/stockimport.coffee
@@ -146,6 +146,9 @@
       .on 'error', (error) ->
         reject "#{LOG_PREFIX}Problem in parsing CSV: #{error}"

+  performStream: (chunk, cb) ->
+    _processBatches(chunk).then -> cb()
+
   _getHeaderIndexes: (headers, csvHeaders) ->
     Promise.all _.map csvHeaders.split(','), (h) =>
       cleanHeader = h.trim()
@@ -208,31 +211,34 @@
         ElasticIo.returnSuccess msg, next
       Promise.resolve "#{LOG_PREFIX}elastic.io messages sent."
     else
-      batchedList = _.batchList(stocks, 30) # max parallel elem to process
-      Promise.map batchedList, (stocksToProcess) =>
-        ie = @client.inventoryEntries.all().whereOperator('or')
-        @logger.debug stocksToProcess, 'Stocks to process'
-        uniqueStocksToProcessBySku = _.reduce stocksToProcess, (acc, stock) ->
-          foundStock = _.find acc, (s) -> s.sku is stock.sku
-          acc.push stock unless foundStock
-          acc
-        , []
-        _.each uniqueStocksToProcessBySku, (s) =>
-          @summary.emptySKU++ if _.isEmpty s.sku
-          # TODO: query also for channel?
-          ie.where("sku = \"#{s.sku}\"")
-        ie.sort('sku').fetch()
-        .then (results) =>
-          @logger.debug results, 'Fetched stocks'
-          queriedEntries = results.body.results
-          @_createOrUpdate stocksToProcess, queriedEntries
-        .then (results) =>
-          _.each results, (r) =>
-            switch r.statusCode
-              when 201 then @summary.created++
-              when 200 then @summary.updated++
-          Promise.resolve()
-      , {concurrency: 1} # run 1 batch at a time
+      @_processBatches(stocks)
+
+  _processBatches: (stocks) ->
+    batchedList = _.batchList(stocks, 30) # max parallel elem to process
+    Promise.map batchedList, (stocksToProcess) =>
+      ie = @client.inventoryEntries.all().whereOperator('or')
+      @logger.debug stocksToProcess, 'Stocks to process'
+      uniqueStocksToProcessBySku = _.reduce stocksToProcess, (acc, stock) ->
+        foundStock = _.find acc, (s) -> s.sku is stock.sku
+        acc.push stock unless foundStock
+        acc
+      , []
+      _.each uniqueStocksToProcessBySku, (s) =>
+        @summary.emptySKU++ if _.isEmpty s.sku
+        # TODO: query also for channel?
+        ie.where("sku = \"#{s.sku}\"")
+      ie.sort('sku').fetch()
+      .then (results) =>
+        @logger.debug results, 'Fetched stocks'
+        queriedEntries = results.body.results
+        @_createOrUpdate stocksToProcess, queriedEntries
+      .then (results) =>
+        _.each results, (r) =>
+          switch r.statusCode
+            when 201 then @summary.created++
+            when 200 then @summary.updated++
+        Promise.resolve()
+    , {concurrency: 1} # run 1 batch at a time

   _match: (entry, existingEntries) ->
     _.find existingEntries, (existingEntry) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_fps_counter.coffee
+++ src/engine_fps_counter.coffee
@@ -20,7 +20,7 @@
     showFPS: true
     FPSColor: "#FFF"

-  framerate = Framerate
+  framerate = Framerate()
     noDOM: true

   self.bind "overlay", (canvas) ->


Add more hook languages into dredd init

Add more hook languages into dredd init

--- src/interactive-config.coffee
+++ src/interactive-config.coffee
@@ -36,6 +36,8 @@
       "ruby"
       "python"
       "nodejs"
+      "php"
+      "go"
     ]
   }


remove unused test

remove unused test

--- src/layout/test_layout.coffee
+++ src/layout/test_layout.coffee
@@ -80,7 +80,7 @@
     t.end()

   test "find words", (t) ->
-    t.ok inspector = sprite.get('inspector'), 'made inspector'
+    #t.ok inspector = sprite.get('inspector'), 'made inspector'
     t.ok language = sprite.get('language'), 'language'
     t.ok words = Object.keys(language), 'words'
     t.ok "forward" in words, 'has a command'


added NIA takedown detection

added NIA takedown detection

--- src/lib/requestfactory.coffee
+++ src/lib/requestfactory.coffee
@@ -197,11 +197,14 @@
                 process.exit 0
                 return false

+            if body.indexOf('NIA takedown in progress. Stand by.') > -1
+                return new Error 'Request rejected by Ingress Server.'
+
             if body.indexOf('but your computer or network may be sending automated queries') > -1
-                return new Error 'request rejected'
+                return new Error 'Request rejected. Please try changing IP.'

             if body.trim().length is 0
-                return new Error 'Empty server response'
+                return new Error 'Empty server response. Please try adding more accounts.'

             return new Error 'unknown server response'


Remove duplicate WS_CONNECTED messages

Remove duplicate WS_CONNECTED messages

--- src/listeners/wsClient.coffee
+++ src/listeners/wsClient.coffee
@@ -42,7 +42,6 @@
       ifExtension.tx {type: 'WS_DISCONNECTED'}
       _fSocketConnected = false
     _socketio.on 'connect', socketConnected
-    _socketio.on 'reconnect', socketConnected
     _socketio.on 'disconnect', socketDisconnected
     _socketio.on 'error', socketDisconnected
     _socketio.on 'MSG', _rxMsg


Silence change events when clearing the model

--- src/scripts/models/search-results.coffee
+++ src/scripts/models/search-results.coffee
@@ -53,7 +53,7 @@

     fetch: () ->
       # Reset search results
-      @clear().set(@defaults)
+      @clear({silent: true}).set(@defaults)
       @set('loaded', false)

       return super(arguments...)


paper-snowflake-emulator-selector: Remove coffeescript source


added some missing error handling

added some missing error handling


fix logout from homepage causes everything to disappear

Former-commit-id: f34e5df4b99b998a080c90f036a2ee84bdf70a97 [formerly c07c942677027bf25067500e34690665c7e225e7] [formerly bc87084a28632cf6cdffb89821d5ee2f44a80a2f [formerly b4d2df56c4936e37ae01bc69de69abba5037807d]] [formerly 246913d0e01714798b30af1cd5a6d759dbc01909 [formerly 24ce788fb10d12b9c945179ee07aae9f6d686fcf] [formerly 140a28e94c7007514036a84b1f68e7a53c79ca52 [formerly 140a28e94c7007514036a84b1f68e7a53c79ca52 [formerly 25433da127b244c643e8139d8f53f89b52169258]]]]
Former-commit-id: 11bb3b465b9e3e0bed761a2d31866f7a5a61742d [formerly 0545ecfe68c3b91f45d4894b0bfb44dcbb2472f0] [formerly 4b9e363ad71d727b9b7622e87bf845287506b8f5 [formerly 20676a33be29a1ab3386638779f3e82218475f8d]]
Former-commit-id: 66671a7ba27c45e5398ba3e64eac9b805e01e9cc [formerly f6a54f31a35475a133cb7cc07b4184cd7e2bd787]
Former-commit-id: 180649df3aff0e87795509a3fe3d044d5a2bb86a
Former-commit-id: a663e1e49c41e2e2f1abc89ae1afb3ea2fb19351


Texture: Choose image texture format depending on type and support. Alpha textures require reexporting with newer addon.

--- engine/texture.coffee
+++ engine/texture.coffee
@@ -45,9 +45,11 @@
             @wrap='R' # Clamp, Repeat or Mirrored
             @use_mipmap # TODO: currently not exported!
             @filter # enable bilinear filtering
+            @use_alpha
         } = options
         @filter ?= true
         @use_mipmap ?= true
+        @use_alpha ?= false
         @gl_target = 3553 # gl.TEXTURE_2D
         @gl_tex = null
         @bound_unit = -1
@@ -284,8 +286,15 @@
                 gl.pixelStorei gl.UNPACK_FLIP_Y_WEBGL, true
                 # TODO: Use gl.RGB when there's no alpha?
                 # Would other format be better?
-                gl.texImage2D gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
-                    gl.UNSIGNED_BYTE, @image
+                if @use_alpha
+                    gl.texImage2D gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
+                        gl.UNSIGNED_BYTE, @image
+                else if @context.is_webgl2
+                    gl.texImage2D gl.TEXTURE_2D, 0, gl.RGB565, gl.RGB,
+                        gl.UNSIGNED_SHORT_5_6_5, @image
+                else
+                    gl.texImage2D gl.TEXTURE_2D, 0, gl.RGB, gl.RGB,
+                        gl.UNSIGNED_BYTE, @image
                 if @use_mipmap
                     gl.generateMipmap gl.TEXTURE_2D
             when 'video'


remove iit from master

remove iit from master

--- lineman/spec-e2e/discussion_spec.coffee
+++ lineman/spec-e2e/discussion_spec.coffee
@@ -9,7 +9,7 @@
     threadHelper.addComment('hi this is my comment')
     expect(threadHelper.mostRecentComment().getText()).toContain('hi this is my comment')

-  iit 'replies to a comment', ->
+  it 'replies to a comment', ->
     threadHelper.addComment('original comment right heerrr')
     threadHelper.replyLinkOnMostRecentComment().click()
     threadHelper.addComment('hi this is my comment')


readded linkify support to motions.js

readded linkify support to motions.js

--- app/assets/javascripts/motions.js.coffee
+++ app/assets/javascripts/motions.js.coffee
@@ -153,12 +153,24 @@
       $(".control-group").addClass("error")
   )

-# NOTE (Jon): We should implement a better method for scoping javascript to specific pages
-# http://stackoverflow.com/questions/6167805/using-rails-3-1-where-do-you-put-your-page-specific-javascript-code
+# linkify links: this should be placed in a framework asap
 $ ->
-  if $("#motion").length > 0
-    $("#description").html(linkify_html($("#description").html()))
-    $(".comment-body").each(-> $(this).html(linkify_html($(this).html())))
+  selectors = [
+    "#motion h2",
+    "#motion #description",
+    "#votes .statement",
+    ".comment-body",
+    ".proposal-text",
+    ".position-message"
+  ]
+
+  selectors = for selector in selectors
+    string = "body.discussions " + selector
+
+  selector = selectors.join ", "
+
+  $(selector).each ->
+    $(this).html(linkify_html($(this).html()))

 # adds bootstrap popovers to vote buttons
 $ ->


update isAnnotationComplete

update isAnnotationComplete

--- app/classifier/tasks/dropdown/index.cjsx
+++ app/classifier/tasks/dropdown/index.cjsx
@@ -78,22 +78,8 @@
       value: []

     isAnnotationComplete: (task, annotation) ->
-      requiredSelects = Object.keys(task.selects).filter (i) -> task.selects[i].required
-
-      return true if not requiredSelects.length
-
-      select = (i) ->
-        return i if annotation.value[i]?.value isnt null and annotation.value[i]?.value isnt undefined
-      selectsCompleted = requiredSelects.map select
-
-      compareArrays = (requiredSelects, selectsCompleted) ->
-        areEqual = true
-        for i in [0..requiredSelects.length]
-          if requiredSelects[i] isnt selectsCompleted[i]
-            areEqual = false
-        areEqual
-
-      compareArrays(requiredSelects, selectsCompleted)
+      task.selects.every (select, i) ->
+        not select.required or annotation.value[i]?.value?

     testAnnotationQuality: (unknown, knownGood) ->
       distance = levenshtein.get unknown.value.toLowerCase(), knownGood.value.toLowerCase()


change default to show global info

change default to show global info

--- src/smc-util/db-schema.coffee
+++ src/smc-util/db-schema.coffee
@@ -237,7 +237,7 @@
                     page_size         : 50
                     standby_timeout_m : 10
                     default_file_sort : 'time'
-                    show_global_info  : false
+                    show_global_info  : true
                 first_name      : ''
                 last_name       : ''
                 terminal        :


misc console tweakage...

misc console tweakage...

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -379,6 +379,7 @@
     pause_rendering: (immediate) =>
         if @_rendering_is_paused
             return
+        #console.log 'pause_rendering'
         @_rendering_is_paused = true
         if not @_render_buffer?
             @_render_buffer = ''
@@ -394,6 +395,7 @@
     unpause_rendering: () =>
         if not @_rendering_is_paused
             return
+        #console.log 'unpause_rendering'
         @_rendering_is_paused = false
         f = () =>
             @render(@_render_buffer)
@@ -439,7 +441,7 @@

         e.on 'copy', =>
             @unpause_rendering()
-            setTimeout(@focus, 0)  # must happen in next cycle or copy will not work due to loss of focus.
+            setTimeout(@focus, 5)  # must happen in next cycle or copy will not work due to loss of focus.

     _init_colors: () =>
         colors = Terminal.color_schemes[@opts.color_scheme].colors
@@ -479,7 +481,7 @@
                 when 188       # "control-shift-<"
                     @_decrease_font_size()
                     return false
-        if (ev.metaKey or ev.ctrlKey) and (ev.keyCode in [17, 86, 91, 93, 223, 224])  # command or control key (could be a paste coming)
+        if (ev.metaKey or ev.ctrlKey or ev.altKey) and (ev.keyCode in [17, 86, 91, 93, 223, 224])  # command or control key (could be a paste coming)
             #console.log("resetting hidden textarea")
             #console.log("clear hidden text area paste bin")
             # clear the hidden textarea pastebin, since otherwise
@@ -488,7 +490,7 @@
             # NOTE: we could do this on all keystrokes.  WE restrict as above merely for efficiency purposes.
             # See http://stackoverflow.com/questions/3902635/how-does-one-capture-a-macs-command-key-via-javascript
             @textarea.val('')
-        if @_rendering_is_paused and not (ev.ctrlKey or ev.metaKey)
+        if @_rendering_is_paused and not (ev.ctrlKey or ev.metaKey or ev.altKey)
             @unpause_rendering()

     _increase_font_size: () =>
@@ -794,7 +796,7 @@

         pb.on 'paste', =>
             pb.val('')
-            setTimeout(f,0)
+            setTimeout(f,5)

     #######################################################################
     # Public API
@@ -992,7 +994,7 @@
         @terminal.focus()

         $(@terminal.element).addClass('webapp-console-focus').removeClass('webapp-console-blur')
-        setTimeout((()=>delete @_focusing), 0)   # critical!
+        setTimeout((()=>delete @_focusing), 5)   # critical!

     set_title: (title) ->
         @opts.set_title?(title)


put in some basic check that password has at least 6 characters at least

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -175,6 +175,11 @@
             password : ''  # more secure...

     save_editing: ->
+        if @state.password.length < 6
+            @setState
+                state : 'edit'
+                error : 'Password must be at least 6 characters long.'
+            return
         @setState
             state : 'saving'
         webapp_client.change_email
@@ -246,7 +251,7 @@
         if @props.email_address
             return "Change email address"
         else
-            return "Set email address"
+            return "Set email address and password"

     render: ->
         <LabeledRow label='Email address'  style={if @props.disabled then {color:"#666"}}>
@@ -349,7 +354,7 @@
                         strength     : 0

     is_submittable: ->
-        return @state.new_password and @state.new_password != @state.old_password and (not @state.zxcvbn? or @state.zxcvbn?.score > 0)
+        return @state.new_password.length >= 6 and @state.new_password and @state.new_password != @state.old_password and (not @state.zxcvbn? or @state.zxcvbn?.score > 0)

     change_button: ->
         if @is_submittable()


autosave fallout: don't save files before they are loaded -- otherwise sometimes browser suddenly can't open a file since it autosaved during open

--- src/smc-webapp/syncdoc.coffee
+++ src/smc-webapp/syncdoc.coffee
@@ -206,6 +206,7 @@
         @_syncstring.once 'init', =>
             @emit('connect')   # successful connection
             @_syncstring.wait_until_read_only_known (err) =>  # first time open a file, have to look on disk to load it -- this ensures that is done
+                @_fully_loaded = true
                 opts.cb(err, @)

         @_syncstring.on 'change', => # only when change is external
@@ -233,6 +234,9 @@
         cb?()

     _save: (cb) =>
+        if not @_fully_loaded
+            cb?()
+            return
         async.series([@_syncstring.save, @_syncstring.save_to_disk], cb)

     save: (cb) =>
@@ -331,7 +335,6 @@

         @_syncstring.once 'init', (err) =>
             if err
-                window.err = err
                 if err.code == 'EACCES'
                     err = "You do not have permission to read '#{@filename}'."
                 @editor.show_startup_message(err, 'danger')
@@ -340,6 +343,7 @@
             @_syncstring.wait_until_read_only_known (err) =>
                 @editor.show_content()
                 @editor._set(@_syncstring.get())
+                @_fully_loaded = true
                 @codemirror.setOption('readOnly', false)
                 @codemirror1.setOption('readOnly', false)
                 @codemirror.clearHistory()  # ensure that the undo history doesn't start with "empty document"
@@ -456,8 +460,8 @@
         cb?()

     _save: (cb) =>
-        if not @codemirror?
-            cb() # nothing to do -- not initialized yet...
+        if not @codemirror? or not @_fully_loaded
+            cb() # nothing to do -- not initialized/loaded yet...
             return
         @_syncstring.set(@codemirror.getValue())
         async.series [@_syncstring.save, @_syncstring.save_to_disk], (err) =>


refactor: Use better variable name

refactor: Use better variable name

--- src/transaction-runner.coffee
+++ src/transaction-runner.coffee
@@ -629,13 +629,13 @@
         # version of the code.) In that case, we want to save the new validation
         # output, but we want to keep at least the original array of Gavel errors.
         results = transaction.results or {}
-        for own sectionName, validatorOutput of gavelResult when sectionName isnt 'version'
+        for own sectionName, rawValidatorOutput of gavelResult when sectionName isnt 'version'
           # Section names are 'statusCode', 'headers', 'body' (and 'version', which is irrelevant)
           results[sectionName] ?= {}

           # We don't want to modify the object and we want to get rid of some
           # custom Gavel.js types ('clone' will keep just plain JS objects).
-          validatorOutput = clone(validatorOutput)
+          validatorOutput = clone(rawValidatorOutput)

           # If transaction already has the 'results' object, ...
           if results[sectionName].results


Fix context indexes

Fix context indexes

--- framer/Context.coffee
+++ framer/Context.coffee
@@ -56,6 +56,11 @@
 		@perspectiveOriginY = options.perspectiveOriginY

 		@reset()
+
+		if options.hasOwnProperty("index")
+			@index = options.index
+		else
+			@index = @id

 	reset: ->

@@ -322,6 +327,12 @@
 				@_perspectiveOriginY = value
 				@_updatePerspective()

+	@define "index",
+		get: -> @_element?.style["z-index"] or 0 or 0
+		set: (value) ->
+			return unless @_element
+			@_element.style["z-index"] = value
+
 	toInspect: ->

 		round = (value) ->


move specific tasks to execute right away

--- functions/main.coffee
+++ functions/main.coffee
@@ -218,15 +218,21 @@
     $('.status').removeClass('hidden')
     window.Championify.run()

+
+###*
+* Execute ASAP
+###
+setupPlatform()
+$('#browseTitle').text(window.browseTitle)
+window.Championify.setVersion()
+$('.options [data-toggle="tooltip"]').tooltip()
+
+
 ###*
  * Executes on Page Load.
 ###
 $(document).ready ->
   runUpdates()
-  setupPlatform()
-  $('#browseTitle').text(window.browseTitle)
-  window.Championify.setVersion()
-  $('.options [data-toggle="tooltip"]').tooltip()
   findInstallPath()


Kill historical tasks view table and pagination when there are no historical tasks (Fixes #35)

--- SingularityUI/app/views/request.coffee
+++ SingularityUI/app/views/request.coffee
@@ -110,11 +110,21 @@
                     ]

                 @historicalTasksView.setElement $('.historical-tasks-paginated')[0]
+
                 @historicalTasksView.render()
+                @postHistoricalTasksViewRender()

-                @historicalTasks.on 'sync', => @historicalTasksView.render()
+                @historicalTasks.on 'sync', =>
+                    @historicalTasksView.render()
+                    @postHistoricalTasksViewRender()

                 @setupEvents()
+
+    postHistoricalTasksViewRender: ->
+        $teebleOuter = $(@historicalTasksView.el)
+        $empty = $teebleOuter.find('.teeble_empty')
+        if $empty.length
+            $teebleOuter.html('
No historical tasks.
')

     setupEvents: ->
         @$el.find('[data-action="viewJSON"]').unbind('click').click (event) ->


[fix] Replace Google Contacts OAuth button by anchor

Having a button was causing a strange issue with preact, main form's button was having onclick attribute contained in the custom view, after opening the Google Contacts connect form two times.

--- server/konnectors/googlecontacts.coffee
+++ server/konnectors/googlecontacts.coffee
@@ -39,13 +39,13 @@
     customView: """

<%t konnector customview googlecontacts 4 %>

<%t konnector customview googlecontacts 1 %>

-    <button id="connect-google"
+    <a id="connect-google" role="button"
     title="<%t konnector customview googlecontacts 2 %>" class="btn"
        onclick="window.open('#{GoogleToken.getAuthUrl()}',
        'Google OAuth',
        'toolbars=0,width=700,height=600,\
        left=200,top=200,scrollbars=1,resizable=1');"
-       ><%t konnector customview googlecontacts 2 %></button>
+       ><%t konnector customview googlecontacts 2 %>

<%t konnector customview googlecontacts 3 %>

     """
     fields:


Fix styles active again

Fix styles active again

--- source/assets/javascripts/app.js.coffee
+++ source/assets/javascripts/app.js.coffee
@@ -325,7 +325,7 @@
   $('.wish').addClass('pushed')
   $('#styles .styles').show defaultSpeed, ->
     $('#styles .styles').addClass('visible')
-    if $(window).scrollTop < $('#styles .styles').height()
+    if $(window).scrollTop() < $('#styles .styles').height()
       stylesActive()
     showWish = ->
       $('.wish').removeClass('pushed')


Fix duplicate targets being added on kpi date change

--- src/components/kpi/kpi.directive.coffee
+++ src/components/kpi/kpi.directive.coffee
@@ -86,6 +86,7 @@
         # Linked methods
         # -------------------------
         $scope.addTargetToWatchable = (watchable)->
+          return if _.has($scope.targets, watchable)
           (newTarget = {})[$scope.getTargetPlaceholder(watchable).mode] = ''
           ($scope.targets[watchable] ||= []).push(newTarget)


spacebar to hide dots

spacebar to hide dots

--- app/assets/javascripts/building.coffee
+++ app/assets/javascripts/building.coffee
@@ -103,9 +103,14 @@
 			e.preventDefault()
 		)

+		$("body").keydown (e)->
+			switch e.which
+				when 32 then tagger.hideDotted(e)
+
 		$("body").keyup (e)->
 			# console.log "key", e.which
 			switch e.which
+				when 32 then tagger.showDotted(e)
 				when 49 then tagger.submitNoFlag(e)
 				when 97 then tagger.submitNoFlag(e)
 				when 50 then tagger.submitFixFlag(e)
@@ -529,12 +534,23 @@
 			# 	l.bindPopup(out.join("<br />"))
 		).addData json

-	# onMapClick: (e) =>
-	# 	@popup
-	# 		.setLatLng(e.latlng)
-	# 		.setContent("You clicked the map at " + e.latlng.toString())
-	# 		.openOn(@map)
-
+	showDotted: (e) =>
+		console.log "showDotted"
+		@geo.setStyle (feature) ->
+			color: '#b00'
+			weight: 5
+			opacity: 1
+			dashArray: '1,16'
+			fill: false
+
+	hideDotted: (e) =>
+		console.log "hideDotted"
+		@geo.setStyle (feature) ->
+			color: '#b00'
+			opacity: 0
+			fill: false
+
+
 	# deep copy method
 	# see: http://coffeescriptcookbook.com/chapters/classes_and_objects/cloning
 	clone: (obj) ->


fixed poly shuffler for length == 1

fixed poly shuffler for length == 1

--- app/assets/javascripts/building.coffee
+++ app/assets/javascripts/building.coffee
@@ -418,6 +418,7 @@
 		@showNextPolygon()

 	shufflePolygons: (a) ->
+		if a.length == 1 return a
 		# from: http://coffeescriptcookbook.com/chapters/arrays/shuffling-array-elements
 		# From the end of the list to the beginning, pick element `i`.
 		for i in [a.length-1..1]


Initialize moment to english or swedish

Initialize moment to english or swedish

Fixes #104

--- app/assets/javascripts/posts.js.coffee
+++ app/assets/javascripts/posts.js.coffee
@@ -6,6 +6,7 @@
   $('time').each (index, elem) ->
     $elem = $(elem)
     datetime = $elem.attr 'datetime'
+    moment.locale(I18n.locale)
     $elem.text moment(new Date(datetime)).fromNow()


Add spinner to iocaine button and refactor a little the directive

--- app/coffee/modules/tasks/detail.coffee
+++ app/coffee/modules/tasks/detail.coffee
@@ -262,7 +262,7 @@
 module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", TaskStatusButtonDirective])

-TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm) ->
+TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
     template = _.template("""
       <fieldset title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!">
         <label for="is-iocaine" class="clickable button button-gray is-iocaine">Iocaine</label>
@@ -271,32 +271,39 @@
     """)

     link = ($scope, $el, $attrs, $model) ->
-        render = _.once (us) ->
+        render = _.once (task) ->
             $el.html(template())

-        refresh = (us) ->
-            if us?.is_iocaine
+        refresh = (task) ->
+            if task?.is_iocaine
                 $el.find('.is-iocaine').addClass('active')
             else
                 $el.find('.is-iocaine').removeClass('active')

-        $scope.$watch $attrs.ngModel, (us) ->
-            return if not us
-            render(us)
-            refresh(us)
+        $scope.$watch $attrs.ngModel, (task) ->
+            return if not task
+            render(task)
+            refresh(task)

         $scope.$on "$destroy", ->
             $el.off()

         $el.on "click", ".is-iocaine", (event) ->
-            us = $model.$modelValue.clone()
-            us.is_iocaine = not us.is_iocaine
-            $model.$setViewValue(us)
+            task = $model.$modelValue.clone()
+            task.is_iocaine = not task.is_iocaine
+            $model.$setViewValue(task)
+            $loading.start($el.find('label'))
+
             promise = $tgrepo.save($model.$modelValue)
             promise.then ->
+                $confirm.notify("success")
                 $rootscope.$broadcast("history:reload")
+
             promise.then null, ->
                 $confirm.notify("error")
+
+            promise.finally ->
+                $loading.finish($el.find('label'))

     return {
         link: link
@@ -304,4 +311,4 @@
         require: "ngModel"
     }

-module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", TaskIsIocaineButtonDirective])
+module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", TaskIsIocaineButtonDirective])


fixed logs page ordering

fixed logs page ordering

--- lib/actions/views/logs.coffee
+++ lib/actions/views/logs.coffee
@@ -43,8 +43,9 @@
   arr.sort (a,b) ->
     a0 = a[0]
     b0 = b[0]
-    a0_error = (a0.exception || a0.fail_reason)
-    ret = if a0_error == (b0.exception || b0.fail_reason)
+    a0_error = a0.error?
+    b0_error = b0.error?
+    ret = if a0_error == b0_error
       b0.start - a0.start
     else
       if a0_error then -1 else 1


localizing apply/cancel

localizing apply/cancel

--- pivot.coffee
+++ pivot.coffee
@@ -170,6 +170,8 @@
                 selectNone: "Select None"
                 tooMany: "(too many to list)"
                 filterResults: "Filter values"
+                apply: "Apply"
+                cancel: "Cancel"
                 totals: "Totals" #for table renderer
                 vs: "vs" #for gchart renderer
                 by: "by" #for gchart renderer
@@ -755,13 +757,14 @@

                     finalButtons = $("<p>").appendTo(valueList)

-                    $("<button>", {type: "button"}).text("Apply")
-                        .appendTo(finalButtons).bind "click", ->
-                            if valueList.find(".changed").removeClass("changed").length
-                                refresh()
-                            closeFilterBox()
-
-                    $("<button>", {type: "button"}).text("Cancel")
+                    if values.length <= opts.menuLimit
+                        $("<button>", {type: "button"}).text(opts.localeStrings.apply)
+                            .appendTo(finalButtons).bind "click", ->
+                                if valueList.find(".changed").removeClass("changed").length
+                                    refresh()
+                                closeFilterBox()
+
+                    $("<button>", {type: "button"}).text(opts.localeStrings.cancel)
                         .appendTo(finalButtons).bind "click", ->
                             valueList.find(".changed:checked")
                                 .removeClass("changed").prop("checked", false)


fix bug when show terminal

fix bug when show terminal

--- assets/scripts/controllers/task.coffee
+++ assets/scripts/controllers/task.coffee
@@ -58,9 +58,9 @@
                 if jobInfo.socket
                   jobInfo.socket.emit 'detach',from: [id]
           else
-            worker.doms.terminal.style.visibility = 'visible'
+            $(worker.doms.terminal).show('fast')
         else if worker.doms.terminal?
-            worker.doms.terminal.style.visibility = 'hidden'
+            $(worker.doms.terminal).hide('fast')
       else
         worker.doms = {}
         # create twinkle light


Don't pass through the host header

Don't pass through the host header

--- proxy.coffee
+++ proxy.coffee
@@ -67,7 +67,7 @@
       options =
         uri: url
         method: req.method
-        headers: req.headers
+        headers: _.omit(req.headers or {}, 'host')
         followRedirect: not config.passRedirects

       reqDomain.run ->


Implement exit codes != 0

Implement exit codes != 0

For more details: https://groups.google.com/forum/#!topic/nodejs/zF7GEoPccqw

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -4,7 +4,13 @@
 mocha = require 'gulp-mocha'
 rimraf = require 'rimraf'

+errorOccurred = no
+process.once 'exit', (code) ->
+  if errorOccurred and code == 0
+    process.exit 1
+
 handleError = (err) ->
+  errorOccurred = yes
   gutil.log err
   @emit 'end'


Remove EcmaScript5 strict, switch to new externs.

Please bower install and npm install.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -46,7 +46,6 @@

 dirs =
   googBaseJs: 'bower_components/closure-library/closure/goog'
-  nodeJsExterns: 'bower_components/closure-compiler-externs'
   watch: ['app', 'bower_components/este-library/este']

 gulp.task 'stylus', ->
@@ -81,17 +80,16 @@
     compilerPath: paths.compiler
     compilerFlags:
       closure_entry_point: 'app.main'
-      language_in: 'ECMASCRIPT5_STRICT'
       externs: paths.externs

 gulp.task 'compile-serverapp', ->
+  console.log paths.externs.concat este.getNodeJsExterns()
+
   este.compile paths.js, 'app/server/build',
     compilerPath: paths.compiler
     compilerFlags:
       closure_entry_point: 'server.main'
-      language_in: 'ECMASCRIPT5_STRICT'
-      externs: paths.externs
-        .concat este.getExterns dirs.nodeJsExterns
+      externs: paths.externs.concat este.getNodeJsExterns()
       # To have compiled code readable, so we can trace errors.
       debug: true
       formatting: 'PRETTY_PRINT'


Use absolute root path for opts.config.root

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -2,13 +2,18 @@
 gulp = require 'gulp'
 GulpTaskLoader = require 'gulp-task-loader'
 require 'require-yaml'
+relpath = require 'relative-path'

 # Load configuration.
 config = require './config.yml'

+# Convert config.root to absolute path
+absroot = relpath config.root
+console.log absroot
+
 # Configure gulp-task-loader
 opts =
-  root: config.root,
+  root: absroot,
   dir: config.tasks,
   exts: config.ext,
   config: config


Don't crash gulp on errors

Don't crash gulp on errors

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -17,6 +17,10 @@
     'src/coffee/app.coffee'
   ]

+printError = (err) ->
+  console.log err
+  @emit 'end'
+
 gulp.task 'coffee', ->
   bundle = transform (files) ->
     browserify(
@@ -27,6 +31,7 @@

   gulp.src(paths.entry)
     .pipe(bundle)
+    .on('error', printError)
     .pipe(sourcemaps.init loadMaps: true)
       .pipe(uglify())
       .pipe(rename extname: '.min.js')
@@ -37,6 +42,7 @@
   gulp.src(paths.less)
     .pipe(sourcemaps.init())
       .pipe(less plugins: [cleancss])
+      .on('error', printError)
       .pipe(rename extname: '.min.css')
     .pipe(sourcemaps.write())
     .pipe(gulp.dest paths.css)


little changes in the location

little changes in the location

--- library/nucleus/service.coffee
+++ library/nucleus/service.coffee
@@ -179,9 +179,10 @@
         current = => @$location or automatic
         automatic = _.head(@resources)?.unescape()
         return current() if arguments.length is 0
-        isLocation = _.isString location
-        noLocation = "The location is not a string"
-        throw new Error noLocation unless isLocation
+        isEmpty = "the location must not be empty"
+        noLocation = "the location is not a string"
+        assert _.isString(location), noLocation
+        assert not _.isEmpty(location), isEmpty
         @$location = location.toString(); this

     # This is a very basic method that adds the specified regular


update maxSearchResults for a pane

update maxSearchResults for a pane

--- src/scripts/models/paneViewModel.coffee
+++ src/scripts/models/paneViewModel.coffee
@@ -62,7 +62,7 @@
       loadingTemplateName: 'cyclops.paneItemsLoading'
       loading: false
       headerTemplateName: 'cyclops.paneHeader'
-      maxSearchResults: 5
+      maxSearchResults: 20
       searchComparer: (item, query) ->
          return ko.unwrap(item.name).toLowerCase().indexOf(query) > -1
     }, options


touch event support

touch event support

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -106,10 +106,14 @@
       cube[side].style[css.transform] = "rotate3d(#{ rotate3d }) translate3d(0, 0, #{ @size / 2 }px)"
       cube.el.appendChild cube[side]

-    for eventType in ['MouseDown', 'MouseMove', 'MouseUp', 'MouseOut'] then do (eventType) =>
-      cube.el.addEventListener eventType.toLowerCase(), (e) =>
-        @['_on' + eventType] e, cube
-      , true
+    eventPairs = [['TouchStart', 'MouseDown'], ['TouchMove', 'MouseMove'],
+      ['TouchEnd', 'MouseUp'], ['TouchLeave', 'MouseOut']]
+
+    for eventPair in eventPairs
+      for eString in eventPair then do (fn = '_on' + eventPair[0], cube) =>
+        cube.el.addEventListener eString.toLowerCase(), (e) =>
+          @[fn] e, cube
+        , true

     cube

@@ -184,22 +188,25 @@
     "translate(0, 0) translateZ(-#{ @size }px) rotate3d(1, 0, 0, #{ deg }deg)"

-  _onMouseDown: (e, cube) ->
+  _onTouchStart: (e, cube) ->
     e.preventDefault()
     @_touchStarted = true
     e.currentTarget.classList.add 'no-tween'
-    cube.y1 = e.pageY
-
-
-  _onMouseMove: (e, cube) ->
+    if e.type is 'mousedown'
+      cube.y1 = e.pageY
+    else
+      cube.y1 = e.touches[0].pageY
+
+
+  _onTouchMove: (e, cube) ->
+    return unless @_touchStarted
     e.preventDefault()
-    return unless @_touchStarted
     cube.diff = (e.pageY - cube.y1) * @_touchCoefficient
     cube.yDelta = cube.yLast - cube.diff
     @_setSides cube

-  _onMouseUp: (e, cube) ->
+  _onTouchEnd: (e, cube) ->
     @_touchStarted = false
     mod = cube.yDelta % 90
     if mod < 45
@@ -211,15 +218,15 @@
         cube.yLast = cube.yDelta - (90 - mod)

     if cube.yLast % 90 isnt 0
-      console.log 'wtf'
       cube.yLast -= cube.yLast % 90

     cube.el.classList.remove 'no-tween'
     cube.el.style[transform] = @_getTransform cube.yLast

-  _onMouseOut: (e, cube) ->
+  _onTouchLeave: (e, cube) ->
     return unless @_touchStarted
-    @_onMouseUp e, cube if e.toElement and not cube.el.contains e.toElement
-
-
+    @_onTouchEnd e, cube if e.toElement and not cube.el.contains e.toElement
+
+
+


Updated localization test classes for AMD

--- test/_examples/component-imports.coffee
+++ test/_examples/component-imports.coffee
@@ -1,7 +1,4 @@
-module_name = 'knockback-localization'
-module_name = 'knockback' if (require.toUrl(module_name).split('./..').length is 1)
-
-kb = if not @kb and (typeof(require) isnt 'undefined') then require(module_name) else @kb
+if not @kb then (try kb = require('knockback-localization') catch e then kb = require('knockback')) else (kb = @kb)
 _ = kb._
 Backbone = kb.Backbone
 ko = kb.ko


activity: fix the order of "comment form" classes in includes.coffee

--- client/Social/Activity/includes.coffee
+++ client/Social/Activity/includes.coffee
@@ -47,6 +47,8 @@
   "views/tags/TagViews.coffee"
   "views/tags/TagAutoCompleteController.coffee"

+  "views/comments/newcommentform.coffee"
+  "views/comments/editcommentform.coffee"
   "views/comments/commentcount.coffee"
   "views/comments/commentlikeview.coffee"
   "views/comments/commentview.coffee"
@@ -55,8 +57,6 @@
   "views/comments/commentsettingsbutton.coffee"
   "views/comments/commentdeletemodal.coffee"
   "views/comments/commentlistitemview.coffee"
-  "views/comments/newcommentform.coffee"
-  "views/comments/editcommentform.coffee"

   "views/inputview.coffee"
   "views/inputwidget.coffee"


Fixes on FSWatcher

Fixes on FSWatcher

--- client/app/MainApp/fs/fswatcher.coffee
+++ client/app/MainApp/fs/fswatcher.coffee
@@ -33,30 +33,26 @@
       method     : 'fs.readDirectory'
       vmName     : @vmName
       withArgs   :
-        onChange : (change)=>
-          do _.throttle =>
-            @onChange @path, change
-          , 500
+        onChange : (change)=> @onChange @path, change
         path     : FSHelper.plainPath @path
         watchSubdirectories : @getOption 'recursive'
     , (err, response)=>

       if not err and response?.files
-        # files = FSHelper.parseWatcher @vmName, @path, response.files
+        files = FSHelper.parseWatcher @vmName, @path, response.files
         FSWatcher.registerWatcher @getFullPath(), response.stopWatching
         callback? err, files
-
       else
         callback? err, null

   onFileAdded:(change)->
-    warn "File added:", change.file.fullPath
+    # warn "File added:", change.file.fullPath

   onFolderAdded:(change)->
-    warn "Folder added:", change.file.fullPath
+    # warn "Folder added:", change.file.fullPath

   onFileRemoved:(change)->
-    warn "File removed:", change.file.fullPath
+    # warn "File removed:", change.file.fullPath

   onChange:(path, change)->


Providers.Config: AWS: add regions as selection with default regions

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -27,9 +27,22 @@
         label              : "Secret Key"
         placeholder        : "aws secret key"
         type               : "password"
-      regions              :
-        label              : "Regions"
-        placeholder        : "use ',' for multiple regions"
+      region               :
+        label              : 'Region'
+        type               : 'selection'
+        placeholder        : 'Region'
+        defaultValue       : 'us-east-1'
+        values             : [
+          { title: 'US East (N. Virginia) (us-east-1)',         value: 'us-east-1' }
+          { title: 'US West (Oregon) (us-west-2)',              value: 'us-west-2' }
+          { title: 'US West (N. California) (us-west-1)',       value: 'us-west-1' }
+          { title: 'EU (Ireland) (eu-west-1)',                  value: 'eu-west-1' }
+          { title: 'EU (Frankfurt) (eu-central-1)',             value: 'eu-central-1' }
+          { title: 'Asia Pacific (Singapore) (ap-southeast-1)', value: 'ap-southeast-1' }
+          { title: 'Asia Pacific (Sydney) (ap-southeast-2)',    value: 'ap-southeast-2' }
+          { title: 'Asia Pacific (Tokyo) (ap-northeast-1)',     value: 'ap-northeast-1' }
+          { title: 'South America (Sao Paulo) (sa-east-1)',     value: 'sa-east-1' }
+        ]

   koding                   :
     name                   : "Koding"


client: add require instancetypes

client: add require instancetypes

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -28,6 +28,7 @@
     title                  : 'AWS'
     color                  : '#F9A900'
     description            : 'Amazon Web Services'
+    instanceTypes          : require './instance-types/aws'
     advancedFields         : [
                               'subnet', 'sg', 'vpc',
                               'ami', 'acl', 'cidr_block',
@@ -83,6 +84,7 @@
     title                  : 'Google Cloud'
     color                  : '#357e99'
     description            : 'Google compute engine'
+    instanceTypes          : require './instance-types/gce'
     credentialFields       :
       projectId            :
         label              : 'Project Id'
@@ -106,6 +108,7 @@
     title                  : 'Azure'
     color                  : '#ec06be'
     description            : 'Azure'
+    instanceTypes          : require './instance-types/azure'
     credentialFields       :
       accountId            :
         label              : 'Account Id'
@@ -121,6 +124,7 @@
     title                  : 'Digitalocean'
     color                  : '#7abad7'
     description            : 'Digitalocean droplets'
+    instanceTypes          : require './instance-types/do'
     credentialFields       :
       clientId             :
         label              : 'Client Id'


Add/remove grid data row when editing

Add/remove grid data row when editing

--- client/lanes/components/grid/Grid.cjsx
+++ client/lanes/components/grid/Grid.cjsx
@@ -72,6 +72,7 @@
     onEditCancel: (model) ->
         if model.isNew()
             @query.results.removeRow(@state.selectedIndex)
+            this.refs.grid.data.splice(0, 1)
         this.hideEditor()

     onEditSave: (model) ->
@@ -118,6 +119,7 @@
             position: 'top',
             onAddRecord: =>
                 model = @query.results.addBlankRow(0)
+                this.refs.grid.data.unshift([])
                 @setState(selectedIndex: 0, selectedModel: model)
         <Lanes.Components.Grid.Toolbar key="toolbar" {...props} />


fix bugs in KeydownEvents so that correct keyup events will be handled

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -463,13 +463,7 @@
 KeydownEvents =
   handledEvents: {}

-  stringify: (event) ->
-    JSON.stringify
-      metaKey: event.metaKey
-      altKey: event.altKey
-      ctrlKey: event.ctrlKey
-      keyIdentifier: event.keyIdentifier
-      keyCode: event.keyCode
+  stringify: (event) -> event.keyCode

   push: (event) ->
     @handledEvents[@stringify event] = true
@@ -481,6 +475,12 @@
     value = @handledEvents[detailString]
     delete @handledEvents[detailString]
     value
+
+  clear: -> @handledEvents = {}
+
+handlerStack.push
+  _name: "KeydownEvents-cleanup"
+  blur: (event) -> KeydownEvents.clear() if event.target == window; true

 #
 # Sends everything except i & ESC to the handler in background_page. i & ESC are special because they control


Send pending find mistakes metric to graphite

--- server/cron/sendMetrics.coffee
+++ server/cron/sendMetrics.coffee
@@ -1,9 +1,14 @@
-import {mongoCallbacksCount} from '../mongo/MongooseCallbackManager'
 import {GROUPS} from '../../client/lib/informaticsGroups'
+
 import {START_SUBMITS_DATE} from '../api/dashboard'
+
 import send from '../metrics/graphite'
 import notify from '../metrics/notify'
+
+import FindMistake from '../models/FindMistake'
 import Result from "../models/result"
+
+import {mongoCallbacksCount} from '../mongo/MongooseCallbackManager'

 sendGraphite = () ->
     queries =
@@ -21,6 +26,10 @@
     metrics = {"websockets": mongoCallbacksCount()}
     await send(metrics)

+sendPendingFindMistakes = () ->
+    metrics = {"pendingFindMistakes": await FindMistake.findNotApprovedCount()}
+    await send(metrics)
+
 sendWarnings = () ->
     endDate = new Date(new Date() - 5 * 60 * 1000)
     query = {ps: 1, lastSubmitTime: {$lt: endDate}, total: 1}
@@ -34,3 +43,4 @@
     await sendWarnings()
     await sendGraphite()
     await sendWebSocketsCount2Graphite()
+    await sendPendingFindMistakes()


Scenario false, prints verbose (skipped) msg.

--- cakes.coffee
+++ cakes.coffee
@@ -85,9 +85,13 @@
 ###

 exports.Scenario = ->
-	if arguments[0]
-		dic 'describe', "\n    Scenario: %s".green, arguments
+	unless arguments[0]
+		arguments = _.toArray(arguments)
+		arguments.shift()  # removes false
+		arguments[1] = ->  # removes call body
+		arguments[0] = ('(skipped) '+arguments[0]).grey

+	dic 'describe', "\n    Scenario: %s".green, arguments

 gwt = (label, args, options)->
 	[title, cb] = args_wash args


Add more details to json2qif script.

Add more details to json2qif script.

--- json2qif.coffee
+++ json2qif.coffee
@@ -1,4 +1,6 @@
 fs = require 'fs'
+try
+  categorizer = require './categorizer'

 jsonFile = process.argv[2]

@@ -40,16 +42,25 @@
 qif += "D#{date.toISOString().substr(0,10)}"
 qif += "POpening Balance\n"
 qif += "T#{statements[0].openingBalance/100}\n"
+qif += "CR\n"
 qif += "^\n"
 for statement in statements
   for row in statement.rows
     #console.log "+ #{row.in} - #{row.out} = #{row.balance}"
-    desc = row.details ? row.description
-    desc = desc.replace /\n/g, " | "
-    desc = desc.replace /\s+/g, " "
+    notes = row.details ? ""
+    notes = notes.replace row.description, ""
+    notes = notes.replace /(^\s+|\s+$)/g, ""
+    notes = notes.replace /\n/g, " | "
+    notes = notes.replace /\s+/g, " "
+    row.notes = notes
     qif += "D#{row.date.toISOString().substr(0,10)}\n"
     qif += "T#{(row.in - row.out)/100}\n"
-    qif += "P#{desc}\n"
+    qif += "P#{row.description}\n"
+    qif += "M#{row.notes}\n"
+    category = categorizer?.categorize(row)
+    if category
+      qif += "L#{category}\n"
+    qif += "CR\n"
     qif += "^\n"

 fs.writeFileSync "#{jsonFile.replace(/.json$/, "")}.qif", qif


Added UnicodeSyntax snippets

Added UnicodeSyntax snippets

--- snippets/language-haskell.cson
+++ snippets/language-haskell.cson
@@ -28,9 +28,6 @@
   'Right Arrow':
     'prefix': '>'
     'body': '${1:expression} -> ${0:expression}'
-  'Type Signature':
-    'prefix': '::'
-    'body': '${1:name} :: ${0:Type}'
   'case … of …':
     'prefix': 'case'
     'body': '''
@@ -93,6 +90,38 @@
       where
       \t${0:definitions}
       '''
+  ###### UNICODE ######
+  '.source.haskell':
+    'Proportion':
+      'prefix': '::'
+      'body': '∷' #0x2237
+    'Rightwards double arrow':
+      'prefix': '=>'
+      'body': '⇒'
+    'For all':
+      'prefix': 'forall'
+      'body': '∀' #0x2200
+    'Rightwards arrow':
+      'prefix': '->'
+      'body': '→' #  0x2192
+    'Leftwards arrow':
+      'prefix': '<-'
+      'body': '←' #0x2190
+    'Leftwards arrow-tail':
+      'prefix': '-<'
+      'body': '↢' #0x2919
+    'Rightwards arrow-tail':
+      'prefix': '>-'
+      'body': '↣' #0x291A
+    'Leftwards double arrow-tail':
+      'prefix': '-<<'
+      'body': '⤛' # 0x291B
+    'Rightwards double arrow-tail':
+      'prefix': '>>-'
+      'body': '⤜' # 0x291C
+    'Black star':
+      'prefix': '*'
+      'body': '★' #0x2605
 '.source.haskell:not(.comment)':
   'Guard':
     'prefix': '|'


all font properties now propagate from .font( ) and passing just a string sets the family

--- src/core/methods/attach.coffee
+++ src/core/methods/attach.coffee
@@ -81,17 +81,25 @@
         print.warning stringFormat(str, "\"" + s + "\"", s), s
         vars.self[s] user[s]

-    # Set all font families and weights, if calling .font()
-    if key is "font" and validObject(user) and "family" of user and typeof user.family is "string"
-      checkFamily = (o) ->
+    # Set all font properties, if calling .font()
+    if key is "font" and typeof user is "string"
+      user =
+        family: user
+    if key is "font"
+      checkFont = (o, a, v, start) ->
         if validObject(o)
-          if "family" of o
-            o.family.value = o.family.process(user.family, vars)
+          if a of o
+            if validObject(o[a])
+              o[a].value = if o[a].process then o[a].process(v) else v
+            else
+              o[a] = v
           else
             for m of o
-              checkFamily o[m]
+              checkFont o[m], a, v, false if m isnt "font" or start isnt true
         return
-      checkFamily vars
+      for fontAttr, fontAttrValue of user
+        fontAttrValue = fontAttrValue.value if validObject(fontAttrValue)
+        checkFont vars, fontAttr, fontAttrValue, true if fontAttrValue

     # Object
     checkObject vars, key, vars, key, user


:lipstick:

:lipstick:

--- src/highlight-component.coffee
+++ src/highlight-component.coffee
@@ -33,8 +33,9 @@
         # render _without_ the flash class first, then re-render with the
         # flash class. Otherwise there will be no flash.
         clearTimeout(@flashTimeout)
+        setImmediate => @setState(flash: newProps.decoration.flash)
+        @flashTimeout = null
         @state.flash = null
-        setImmediate => @setState(flash: newProps.decoration.flash)
       else
         @state.flash = newProps.decoration.flash


bugfix

bugfix

--- src/hubot-scfes-command.coffee
+++ src/hubot-scfes-command.coffee
@@ -35,17 +35,17 @@
     )
     msg.reply "registerd " + next_date

-  robot.respond /scfes levelup time (\d+)( \w+)?/, (msg) ->
+  robot.respond /scfes levelup time (\d+)( (\w+))?/, (msg) ->
     now_exp = parseInt msg.match[1]
-    difficulty = msg.match[2]
+    difficulty = msg.match[3]
     if not difficulty
       difficulty = "ex"

     msg.reply "next levelup is " + scfes.getNextLevelupTime(now_exp, difficulty)

-  robot.respond /scfes levelup count (\d+)( \w+)?/, (msg) ->
+  robot.respond /scfes levelup count (\d+)( (\w+))?/, (msg) ->
     now_exp = parseInt msg.match[1]
-    difficulty = msg.match[2]
+    difficulty = msg.match[3]
     if not difficulty
       difficulty = "ex"


Warn about components using legacy APIs

Warn about components using legacy APIs

--- src/lib/ComponentLoader.coffee
+++ src/lib/ComponentLoader.coffee
@@ -8,6 +8,7 @@
 fbpGraph = require 'fbp-graph'
 {EventEmitter} = require 'events'
 registerLoader = require './loader/register'
+platform = require './Platform'

 class ComponentLoader extends EventEmitter
   constructor: (baseDir, options = {}) ->
@@ -86,6 +87,9 @@
       instance.baseDir = @baseDir if name is 'Graph'
       instance.componentName = name if typeof name is 'string'

+      if instance.isLegacy()
+        platform.deprecated "Component #{name} uses legacy NoFlo APIs. Please port to Process API"
+
       @setIcon name, instance
       callback null, instance


Fixed: Potential location.ctrlType XSS

Fixed: Potential location.ctrlType XSS

--- lib/Site.coffee
+++ lib/Site.coffee
@@ -330,6 +330,7 @@
     p = config.upstream + p

   # path = path.replace(config._outputCtrlQueryRe,'') if ctrlType
+  ctrlType = '' if ctrlType != 'iframe' && ctrlType != 'raw'

   return {
     target:{


Ability to supply stdin

Ability to supply stdin

Notice: there is no line feed supplied at the end,
so one needs to explicitly add it.

--- lib/ghci.coffee
+++ lib/ghci.coffee
@@ -135,7 +135,9 @@
       @ghci.stderr.resume()
       return true
     else
-      return false
+      @ghci.stdin.write lines.join(EOL)
+      @emitter.emit 'response', "> \"#{lines.join('␊')}\"#{EOL}"
+      return true

   historyBack: (current) ->
     if @history.item is @history.back.length


mark full line

mark full line

--- lib/init.coffee
+++ lib/init.coffee
@@ -41,6 +41,7 @@
                 type: 'Warning'
                 text: match[2]
                 filePath
-                range: [[line - 1, 0], [line - 1, 1]]
+                # make range the full line
+                range: helpers.rangeFromLineNumber(textEditor, line - 1, 0)
               })
           return toReturn


coffeescript 2

coffeescript 2

--- lib/init.coffee
+++ lib/init.coffee
@@ -77,7 +77,7 @@
         type: 'loadIndexFile',
         path: document.getElementById('lupa-index-file').value
     })
-})
+)
 lastState = {}

 update1 = ->


dynamic preview of functions on hover

dynamic preview of functions on hover

--- lib/init.coffee
+++ lib/init.coffee
@@ -70,12 +70,15 @@

 el.addEventListener('mouseout',
     (e) ->
+        if lastPos
+            editor.scrollToBufferPosition lastPos, {center: true}
         #return
         decorations.forEach (d) ->
             d.destroy()
 )

+lastPos = null

 el.addEventListener('mouseover',
     (e) ->
@@ -97,6 +100,8 @@
             item.className = 'my-line-class'
             decoration = editor.decorateMarker(marker, {item, type: 'highlight', class: 'my-line-class'})
             console.log "deco", decoration, marker
+            lastPos = editor.getCursorBufferPosition()
+            editor.scrollToBufferPosition pos, {center: true}
             decorations.push(decoration)
 )

@@ -122,6 +127,7 @@
         if line
             pos = [~~line - 1, 0]
             editor.setCursorBufferPosition pos
+            editor.scrollToBufferPosition pos, {center: true}

         path = e.target.getAttribute('data-path')
         console.log("path, open file:", path)


remove old code

remove old code

--- lib/init.coffee
+++ lib/init.coffee
@@ -66,10 +66,7 @@
 (It requires lupaProject.json file.)

sss

-atom.workspace.addLeftPanel(item: el)
-
-
+    style='padding: 10px; width:240px;overflow:scroll;'>

"

 decorations = []


Account for delayed activation of linter

--- lib/lint.coffee
+++ lib/lint.coffee
@@ -12,7 +12,7 @@

   activate: () =>
     @grammars = ['C', 'C++']
-    @subscriptions = new CompositeDisposable
+    if not @subscriptions then @subscriptions = new CompositeDisposable

   deactivate: () =>
     @subscriptions.dispose()
@@ -88,6 +88,7 @@
     module.exports.lint editor, temp, editor.getPath()

   consumeLinter: (indieRegistry) =>
+    module.exports.activate()
     module.exports.linter = indieRegistry.register({name: 'PROS GCC Linter'})
     @subscriptions.add module.exports.linter
     atom.workspace.observeTextEditors (editor) =>


download/upload test #1

download/upload test #1

--- lib/main.coffee
+++ lib/main.coffee
@@ -69,7 +69,7 @@

   # on file Download
   session.on 'will-download', (event, item, downloadWebContents) ->
-    # event.preventDefault();
+    event.preventDefault();
     downloadFolder = "#{settings.dirUpload.dir}\\download"
     if not fs.existsSync downloadFolder
       fs.mkdirSync downloadFolder


Update console error message

Update console error message

--- lib/main.coffee
+++ lib/main.coffee
@@ -155,7 +155,7 @@
             ]
           else
             # Leaving this here to allow people to report the errors
-            console.log(error.stack)
+            console.log('linter-sass-lint', error)
             return [
               type: 'Error'
               text: 'Unexpected parse error in file'


use new jade api for filenames

use new jade api for filenames

--- lib/main.coffee
+++ lib/main.coffee
@@ -13,11 +13,10 @@

     # Jade compile to function string
     jopts =
-      filename: opt.filename
       client: true
       compileDebug: opt.development
     contents = String contents # for fs buffers
-    src = String jade.compile contents, jopts
+    src = String jade.compile contents, opt.filename, jopts

     if opt.rivets # wrap with rivets conveience function
       src = """


uri regex shouldn't be global

uri regex shouldn't be global

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

 configUri = 'atom://config'
-uriRegex = /config\/([a-z]+)\/?([a-z]+)?/gi
+uriRegex = /config\/([a-z]+)\/?([a-z]+)?/i

 createSettingsView = (params) ->
   SettingsView ?= require './settings-view'
@@ -10,13 +10,6 @@

 openPanel = (panelName, uri) ->
   settingsView ?= createSettingsView({uri: configUri})
-
-  # There appears to be a bug that regex.exec doesn't assign, or lazily assigns,
-  # so even if there is a match, it's null unless I immediately call it a second
-  # time. Probably should dig deeper on this to figure out exactly why it's
-  # happening here and file an issue on either Atom or Electron, as appropriate.
-  # TODO: remove if this strange behavior goes away with an Electron update.
-  match = uriRegex.exec(uri)
   match = uriRegex.exec(uri)

   panel = match?[1]


Rename the `caller` command `to-caller`.

--- lib/main.coffee
+++ lib/main.coffee
@@ -15,7 +15,7 @@
       text = (s.getText() for s in (selections or [])).join ''
       atom.emit 'stacktrace:accept-trace', trace: text

-    atom.workspaceView.command 'stacktrace:caller', ->
+    atom.workspaceView.command 'stacktrace:to-caller', ->
       NavigationView.current()?.navigateToCaller()

     atom.workspaceView.command 'stacktrace:follow-call', ->


selenium: if BROWSER_KEEP env exists, don't close browser window

--- test/integration/helper.coffee
+++ test/integration/helper.coffee
@@ -13,7 +13,10 @@
         browser.get login_url, done

   after (done) ->
-    browser.quit done
+    unless process.env.BROWSER_KEEP?
+      browser.quit done
+    else
+      done()

 exports.wd40 = wd40
 exports.browser = browser


Only adjusting the y if we don’t have constraintValues

--- framer/Components/FlowComponent.coffee
+++ framer/Components/FlowComponent.coffee
@@ -515,9 +515,10 @@
 				continue if child is footer
 				child.setParentPreservingConstraintValues(body)
 				headerHeight = header?.height or 0
-				if child.constraintValues?.top?
-					child.constraintValues.top -= headerHeight
-					child.layout()
+				if child.constraintValues?
+					if child.constraintValues?.top?
+						child.constraintValues?.top -= headerHeight
+						child.layout()
 				else
 					child.y -= headerHeight