prevent autocomplete for /;\s/ (temporary fix)

--- lib/atom-ternjs-provider.coffee
+++ lib/atom-ternjs-provider.coffee
@@ -33,6 +33,11 @@
     catch e then return false
     return true

+  excludeTest: (editor, bufferPosition) ->
+    line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
+    return false if line[line.length - 1].match(/;|\s/)
+    return true
+
   checkPrefix: (prefix) ->
     return '' if prefix.match(/(\s|;|\.|\"|\')$/) or prefix.replace(/\s/g, '').length is 0
     prefix
@@ -42,7 +47,7 @@
     @manager.documentation.hide()

   getSuggestions: ({editor, bufferPosition, scopeDescriptor, prefix}) ->
-    if !@isValidPrefix(prefix) and !@force
+    if (!@excludeTest(editor, bufferPosition) and !@force) or (!@isValidPrefix(prefix) and !@force)
       @manager.documentation.hide()
       return []
     prefix = @checkPrefix(prefix)


Remove unnecessary group

Remove unnecessary group

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -30,7 +30,7 @@
   suppressForClasses: []
   shouldDisplaySuggestions: false
   manualActivationStrictPrefixes: null
-  prefixRegex: /(\b|(['"~`!@#\$%^&*\(\)\{\}\[\]=\+,/\?>]))((\w+[\w-]*)|([.:;[{(< ]+))$/
+  prefixRegex: /(\b|['"~`!@#\$%^&*\(\)\{\}\[\]=\+,/\?>])((\w+[\w-]*)|([.:;[{(< ]+))$/
   wordPrefixRegex: /^\w+[\w-]*$/

   constructor: ->
@@ -254,7 +254,7 @@

   getPrefix: (editor, bufferPosition) ->
     line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
-    @prefixRegex.exec(line)?[3] or ''
+    @prefixRegex.exec(line)?[2] or ''

   getDefaultReplacementPrefix: (prefix) ->
     if @wordPrefixRegex.test(prefix)


Fixed a bug in haithera species

Fixed a bug in haithera species

--- src/definitions/creature-species.coffee
+++ src/definitions/creature-species.coffee
@@ -112,7 +112,7 @@
 	symbol: 'spaceBeeMonarch'
 	weight: 2 / 10000

-class classes.Haithera extends classes.Species
+class classes.Haithera extends Species
 	name: 'haithera'
 	symbol: 'haithera'
 	weight: 400


:racehorse: Eliminate uneccesary calls to isRangeCommentedOrString

--- lib/bracket-matcher-view.coffee
+++ lib/bracket-matcher-view.coffee
@@ -137,7 +137,7 @@
         @editor.backspace()

   findMatchingEndPair: (startPairPosition, startPair, endPair) ->
-    return if startPair is endPair
+    return if startPair is endPair or @isScopeCommentedOrString(@editor.scopeDescriptorForBufferPosition(startPairPosition).getScopesArray())

     scanRange = new Range(
       startPairPosition.traverse(ONE_CHAR_FORWARD_TRAVERSAL),
@@ -159,7 +159,7 @@
     endPairPosition

   findMatchingStartPair: (endPairPosition, startPair, endPair) ->
-    return if startPair is endPair
+    return if startPair is endPair or @isScopeCommentedOrString(@editor.scopeDescriptorForBufferPosition(endPairPosition).getScopesArray())

     scanRange = new Range(
       endPairPosition.traverse(MAX_ROWS_TO_SCAN_BACKWARD_TRAVERSAL),


fix comeontom's override

fix comeontom's override

--- lib/build/ios-build-view.coffee
+++ lib/build/ios-build-view.coffee
@@ -56,7 +56,7 @@
           title: "Select .#{each.suffix} File"
           filters: [{name: ".#{each.suffix} file", extensions: [each.suffix]}]
         .then (destPath) ->
-          if each.relative?
+          if each.relative
             each.view.setText path.relative(atom.project.path, destPath[0])
           else
             each.view.setText destPath[0]
@@ -71,10 +71,10 @@
     @version.setText "1.0.0"
     @build.setText "1"

-    path = atom.project.getPath()+"/resource/ios"
-    @icon.attr 'src',"#{path}/test.png"
-    @mobileprovision.setText "#{path}/comeontom_dis1210.mobileprovision"
-    @p12.setText "#{path}/Distribution.p12"
+    test_path = atom.project.getPath()+"/resource/ios"
+    @icon.attr 'src',"#{test_path}/test.png"
+    @mobileprovision.setText "#{test_path}/comeontom_dis1210.mobileprovision"
+    @p12.setText "#{test_path}/Distribution.p12"
     @password.setText "123456"
     @scheme.setText "chameleon-bundled"
     @BundleIdentifier.setText "com.foreveross.comeontom"


better logic for ramp method, reset all shaders

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -540,8 +540,10 @@
     return if !normalized
     [angle, anchor, options] = normalized
     @panels[anchor][1].style[css.transform] = @_transform angle
-    for i in [2...@panels[anchor].length]
-      @panels[anchor][i].style[css.transform] = @_transform 0
+
+    for panel, i in @panels[anchor]
+      if i > 1
+        @panels[anchor][i].style[css.transform] = @_transform 0

       if @shading
         @_setShader i, anchor, 0


_ prefix @shading

_ prefix @shading

--- oridomi.coffee
+++ oridomi.coffee
@@ -398,13 +398,14 @@
           @[k] = v
       @

-    @_queue = []
-    @panels = {}
-    @stages = {}
-    @lastOp = anchor: anchorList[0]
-    @_xLast = @_yLast = 0
-    {@shading} = @_settings
-    if @shading
+    @_queue   = []
+    @panels   = {}
+    @stages   = {}
+    @lastOp   = anchor: anchorList[0]
+    @_xLast   = @_yLast = 0
+    @_shading = @_settings.shading
+
+    if @_shading
       @_shaders    = {}
       shaderProtos = {}
       shaderProto  = createEl 'shader'
@@ -417,7 +418,7 @@
     for anchor in anchorList
       @panels[anchor] = []
       @stages[anchor] = cloneEl stageProto, false, 'stage' + capitalize anchor
-      if @shading
+      if @_shading
         @_shaders[anchor] = {}
         if anchor in anchorListV
           @_shaders[anchor][side] = [] for side in anchorListV
@@ -476,7 +477,7 @@
             else
               panel.style[anchorSet[0]] = '-100%'

-          if @shading
+          if @_shading
             for a, i in anchorSet
               @_shaders[anchor][a][panelN] = panel.children[0].children[i + 1]

@@ -604,7 +605,7 @@
     # Loop through the panels in this anchor and set the transition duration to the new speed.
     for panel, i in @panels[@lastOp.anchor]
       panel.style[css.transitionDuration] = speed + 'ms'
-      if @shading
+      if @_shading
         for side in shaderPair
           @_shaders[@lastOp.anchor][side][i].style[css.transitionDuration] = speed + 'ms'

@@ -620,7 +621,7 @@
     # With hard shading, opacity is reduced and `angle` is based on the global
     # `lastAngle` so all panels' shaders share the same direction. Soft shaders
     # have alternating directions.
-    if @shading is 'hard'
+    if @_shading is 'hard'
       opacity *= .15
       if @lastOp.angle < 0
         angle = abs
@@ -681,7 +682,7 @@

     for panel, i in @panels[@lastOp.anchor]
       panel.style[css.transform] = @_transform 0, @lastOp.anchor
-      @_setShader i, @lastOp.anchor, 0 if @shading
+      @_setShader i, @lastOp.anchor, 0 if @_shading

     @

@@ -940,7 +941,7 @@
       # Set the CSS transformation.
       panel.style[css.transform] = @_transform deg, anchor, options.fracture
       # Apply shaders.
-      if @shading and !(i is 0 and options.sticky) and Math.abs(deg) isnt 180
+      if @_shading and !(i is 0 and options.sticky) and Math.abs(deg) isnt 180
         @_setShader i, anchor, deg

     @
@@ -954,7 +955,7 @@

     for panel, i in @panels[anchor]
       panel.style[css.transform] = @_transform angle, anchor
-      @_setShader i, anchor, 0 if @shading
+      @_setShader i, anchor, 0 if @_shading

     @

@@ -967,7 +968,7 @@
     # For all but the first two panels, set the angle to 0.
     for panel, i in @panels[anchor]
       @panels[anchor][i].style[css.transform] = @_transform 0, anchor if i > 1
-      @_setShader i, anchor, 0 if @shading
+      @_setShader i, anchor, 0 if @_shading

     @


stageEl -> stageHolder

stageEl -> stageHolder

--- oridomi.coffee
+++ oridomi.coffee
@@ -737,10 +737,11 @@
     for eventPair in eventPairs
       for eString in eventPair
         unless eString is 'TouchLeave' and not mouseLeaveSupport
-          @stageEl[listenFn] eString.toLowerCase(), @['_on' + eventPair[0]], false
+          @stageHolder[listenFn] eString.toLowerCase(), @['_on' + eventPair[0]], false
         else
-          @stageEl[listenFn] 'mouseout', @['_onMouseOut'], false
+          @stageHolder[listenFn] 'mouseout', @_onMouseOut, false
           break
+    @

   # This method is called when a finger or mouse button is pressed on the element.


change bad network message to be less false

--- src/smc-webapp/init_app.coffee
+++ src/smc-webapp/init_app.coffee
@@ -268,12 +268,12 @@
                 reconnect
                     type: "error"
                     timeout: 10
-                    message: "Your internet connection is unstable/down or SMC is temporarily not available. Therefore SMC is not working."
+                    message: "Your connection is unstable or SMC is temporarily not available."
             else if attempt >= 10
                 reconnect
                     type: "info"
                     timeout: 10
-                    message: "Your internet connection could be weak or the SMC service is temporarily unstable. Proceed with caution."
+                    message: "Your connection could be weak or the SMC service is temporarily unstable. Proceed with caution."
     else
         reconnection_warning = null


Create pending tests for missing storage backends

--- test/model.test.coffee
+++ test/model.test.coffee
@@ -20,12 +20,16 @@
       name: 'Redis'
       constructor: storage.redis
       connStr: process.env.REDIS_URL
+  else
+    it 'should be tested with a redis URL as ${REDIS_URL}'

   if process.env.DATABASE_URL?
     storageClasses.push
       name: 'Postgres'
       constructor: storage.postgres
       connStr: process.env.DATABASE_URL
+  else
+    it 'should be tested with a Postgres database at ${DATABASE_URL}'

   for storageClass in storageClasses
     describe "with #{storageClass.name} storage", ->


Make blur event on text editor element work with shadow DOM disabled

When the shadow DOM is enabled, this happens organically because the
focus is abstracted across the shadow boundary. Without that abstraction
boundary, we need to pretend that a blur of the hidden input is actually
a blur of the entire editor.

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@gmail.com>

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -95,6 +95,7 @@
       lineOverdrawMargin: @lineOverdrawMargin
     )
     @component = React.renderComponent(@componentDescriptor, @rootElement)
+    @component.refs.input.getDOMNode().addEventListener 'blur', => @dispatchEvent(new FocusEvent('blur', bubbles: false))

   unmountComponent: ->
     return unless @component?.isMounted()


Make resin.spec tests runnable independently

--- tests/integration/resin.spec.coffee
+++ tests/integration/resin.spec.coffee
@@ -1,6 +1,6 @@
 m = require('mochainon')

-{ resin, getSdk, sdkOpts, credentials } = require('./setup')
+{ resin, getSdk, sdkOpts, credentials, givenLoggedInUser } = require('./setup')

 describe 'Resin SDK', ->

@@ -36,6 +36,8 @@
 		m.chai.expect(resin.errors).to.exist

 	describe 'interception Hooks', ->
+
+		givenLoggedInUser()

 		beforeEach ->
 			resin.interceptors = []


fix https://github.com/sagemathinc/cocalc/issues/3069 -- + new" page jumps vertically

--- src/smc-webapp/project_new.cjsx
+++ src/smc-webapp/project_new.cjsx
@@ -253,10 +253,6 @@
     componentWillReceiveProps: (newProps) ->
         if newProps.default_filename != @props.default_filename
             @setState(filename: newProps.default_filename)
-
-    componentDidUpdate: ->
-        if not @state.extension_warning
-            ReactDOM.findDOMNode(@refs.project_new_filename).focus()

     default_filename: ->
         return require('./account').default_filename()


relaxed comparison tolerance in tests a little

one of the tests fails in browsers by a narrow margin

--- src/vocabulary/vhelpers.coffee
+++ src/vocabulary/vhelpers.coffee
@@ -82,14 +82,14 @@
       else
         x is y

-eps = 1e-13 # comparison tolerance for approx()
+eps = 1e-11 # comparison tolerance for approx()

 numApprox = (x, y) ->
   x is y or Math.abs(x - y) < eps

 # approx() is like match(), but it is tolerant to precision errors;
 # used for comparing expected and actual results in doctests
-approx = approx = (x, y) ->
+approx = (x, y) ->
   if x instanceof APLArray
     if not (y instanceof APLArray) then false
     else


Fall back to ${ATOM_REPOS_HOME} or ${HOME}/github.

For consistency with apm develop.

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -58,7 +58,9 @@
     path.join(path.dirname(packagePath), packageName)

   getPackagesDirectory: ->
-    atom.config.get 'core.projectHome'
+    atom.config.get('core.projectHome') or
+      process.env.ATOM_REPOS_HOME or
+      path.join(process.env.HOME, 'github')

   validPackagePath: ->
     if fs.existsSync(@getPackagePath())


draw barres

draw barres

--- intervals.coffee
+++ intervals.coffee
@@ -313,8 +313,8 @@
       ctx.stroke()
       ctx.lineWidth = 1

-  draw_finger_position = ({string, fret}, options) ->
-    {is_root, color} = options || {}
+  draw_finger_position = ({string, fret}, options={}) ->
+    {is_root, color} = options
     y = v_gutter + above_fretboard + (fret - 1) * fret_height + fret_height / 2
     ctx.fillStyle = color or (if is_root then 'red' else 'white')
     ctx.strokeStyle = color or (if is_root then 'red' else 'black')
@@ -325,12 +325,37 @@
     ctx.stroke()
     ctx.strokeStyle = 'black'

-draw_fingerboard = (finger_positions) ->
+draw_fingerboard = (positions, options={}) ->
+  {barres} = options
   draw_strings()
   draw_frets()
-  if finger_positions
+  if barres and (pos for pos in positions when pos.fret > 0).length >= 4
+    fret_rows = for fn in FretNumbers
+      (for sn in StringNumbers
+        if _.find(positions, (pos)-> pos.string == sn and pos.fret > fn)
+          '.'
+        else if _.find(positions, (pos)-> pos.string == sn and pos.fret < fn)
+          '-'
+        else if _.find(positions, (pos) -> pos.string == sn and pos.fret == fn)
+          'x'
+        else
+          ' ').reverse().join('')
+    barres = []
+    for fp, fn in fret_rows
+      continue if fn == 0
+      m = fp.match(/^[^x]*(x[\.x]+x)$/)
+      barres.push fret: fn, string: m[0].length - m[1].length, string_count: m[1].length if m
+    for {fret, string, fret, string_count} in barres
+      y = v_gutter + above_fretboard + (fret - 1) * fret_height + fret_height / 2
+      ctx.beginPath()
+      ctx.arc h_gutter + string * string_spacing, y, string_spacing / 2, Math.PI/2, Math.PI*3/2, false
+      ctx.arc h_gutter + (string + string_count - 1) * string_spacing, y, string_spacing / 2
+        , Math.PI*3/2, Math.PI/2, false
+      ctx.fillStyle = 'rgba(0,0,0, 0.5)'
+      ctx.fill()
+  if positions
     fretted_strings = []
-    for position in finger_positions
+    for position in positions
       fretted_strings[position.string] = true
       draw_finger_position position, position
     for string_number in StringNumbers
@@ -451,7 +476,7 @@
     pitch = pitch_number_for_position(finger_position) % 12
     pitch_fingers[pitch] = finger_position

-  colors = ['red', 'blue', 'green', 'orange']
+  degree_colors = ['red', 'blue', 'green', 'orange']
   other_colors = ['rgba(255,0,0 ,.1)', 'rgba(0,0,255, 0.1)', 'rgba(0,255,0, 0.1)', 'rgba(255,0,255, 0.1)']

   grid cols: 3, rows: 4
@@ -463,22 +488,22 @@
     ctx.fillStyle = 'rgb(128, 128, 128)'
     ctx.fillText "#{chord.name} Chords", diagram_gutter / 2, header_height / 2

-    for ix in [0...12]
-      pitch = (ix * 5 + 3) % 12
+    pitches = ((i * 5 + 3) % 12 for i in [0...12])
+    for pitch in pitches
       root_fingering = pitch_fingers[pitch]
       chord_name = "#{NoteNames[pitch]}#{chord.abbr}"
       cell ->
-        fingerings = []
+        fingering = []
         for semitones, degree_index in chord.pitch_classes
           for {string, fret} in intervals_from(root_fingering, semitones)
-            fingerings.push {string, fret, degree_index}
-        fingerings = best_fingering_for(chord, pitch) if best_fingering
-        f.color = colors[f.degree_index] for f in fingerings
+            fingering.push {string, fret, degree_index}
+        fingering = best_fingering_for(chord, pitch) if best_fingering
+        position.color = degree_colors[position.degree_index] for position in fingering
         ctx.font = '20px Impact'
         ctx.font = '5pt Times' if draw_diagrams
         ctx.fillStyle = 'rgb(10,20,30)'
         ctx.fillText chord_name, h_gutter, -3
-        draw_fingerboard fingerings
+        draw_fingerboard fingering, barres: best_fingering

 chord_book = (options) ->
   page_count = options.pages


refactoring

refactoring

--- lib/views/remote-list-view.coffee
+++ lib/views/remote-list-view.coffee
@@ -17,9 +17,7 @@

   parseData: ->
     items = @data.split("\n")
-    remotes = []
-    for item in items
-      remotes.push {name: item} unless item is ''
+    remotes = items.filter((item) -> item isnt '').map (item) -> { name: item }
     if remotes.length is 1
       @confirmed remotes[0]
     else


Select2 to work properly anywhere

Select2 to work properly anywhere

--- app/assets/javascripts/admin/nomenclature_changes.js.coffee
+++ app/assets/javascripts/admin/nomenclature_changes.js.coffee
@@ -27,10 +27,10 @@
           text: tc.full_name + ' ' + tc.name_status
         results: formatted_taxon_concepts
   }
-  multiTaxonSelect2Options = {
+  window.multiTaxonSelect2Options = {
     multiple: true,
     initSelection: (element, callback) =>
-      id = $(element).val().match(/{(.*)}/)[1]
+      id = $(element).val().match(/({|\[)(.*)(}|\])/)[2]
       # Reset value attribute to let Select2 work properly when submitting the values again
       $(element).attr('value','')
       if (id != null && id != '')
@@ -39,12 +39,12 @@
         name_status = $(element).data('name-status')
         result = []
         for id, i in ids
-          result.push({id: id, text: names[i] + ' ' + name_status})
+          result.push({id: id.trim(), text: names[i] + ' ' + name_status})
         callback(result)
   }

   $('.taxon-concept').select2(window.defaultTaxonSelect2Options)
-  $('.taxon-concept-multiple').select2($.extend({}, window.defaultTaxonSelect2Options, multiTaxonSelect2Options))
+  $('.taxon-concept-multiple').select2($.extend({}, window.defaultTaxonSelect2Options, window.multiTaxonSelect2Options))
   $('.taxon-concept').on('change', (event) ->
     return false unless event.val
     $.when($.ajax( '/admin/taxon_concepts/' + event.val + '.json' ) ).then(( data, textStatus, jqXHR ) =>


Erroneous logging statement.

Erroneous logging statement.

--- app/assets/javascripts/drops_calculator/chartView.js.coffee
+++ app/assets/javascripts/drops_calculator/chartView.js.coffee
@@ -108,7 +108,6 @@

   bind: =>
     $(window).on('resize',  _.debounce( =>
-      console.log "What"
       @render(@context)
     , 300))


refactor code into a shared function between all three file types.

--- app/assets/javascripts/hanuman/cloudinary-uploads.js.coffee
+++ app/assets/javascripts/hanuman/cloudinary-uploads.js.coffee
@@ -1,10 +1,48 @@
+
+addTexareaForUpload = (file, data, idx) ->
+
+  # this code is seting the value of the hidden fields for the uploaded images.
+  dataObj = JSON.parse(data._response.jqXHR.responseText);
+  fileValue = dataObj.resource_type+"/"+dataObj.type+"/"+"v"+dataObj.version+"/"+dataObj.public_id+"."+dataObj.format+"#"+dataObj.signature
+
+  if file == "photo"
+    regex = /\[observation_photos_attributes]\[\d+]\[photo]/
+    nameAttr = data.cloudinaryField.replace(/\[observation_photos_attributes]\[\d+]\[photo]/, "[observation_photos_attributes][" + idx + "][description]")
+    hiddenNameAttr = data.cloudinaryField.replace(/\[observation_photos_attributes]\[\d+]\[photo]/, "[observation_photos_attributes][" + idx + "][photo]")
+  else if file == "document"
+    regex = /\[observation_documents_attributes]\[\d+]\[document]/
+    nameAttr = data.cloudinaryField.replace(/\[observation_documents_attributes]\[\d+]\[document]/, "[observation_documents_attributes][" + idx + "][description]")
+    hiddenNameAttr = data.cloudinaryField.replace(/\[observation_documents_attributes]\[\d+]\[document]/, "[observation_documents_attributes][" + idx + "][document]")
+    fileValue = dataObj.resource_type+"/"+dataObj.type+"/"+"v"+dataObj.version+"/"+dataObj.public_id+"#"+dataObj.signature
+  else if file == "video"
+    regex = /\[observation_videos_attributes]\[\d+]\[video]/
+    nameAttr = data.cloudinaryField.replace(/\[observation_videos_attributes]\[\d+]\[video]/, "[observation_videos_attributes][" + idx + "][description]")
+    hiddenNameAttr = data.cloudinaryField.replace(/\[observation_videos_attributes]\[\d+]\[video]/, "[observation_videos_attributes][" + idx + "][video]")
+
+  # this is simply appending the textarea and the hidden input. I am adding the hidden input right next to the img video-preview
+  $("."+file+"-preview").append "
"
+  $("."+file+"-preview").append "<textarea rows=2 cols=55 style='margin:20px 0 20px 0;' placeholder='Add "+file+" description here...' name="+nameAttr+"></textarea>"
+  $("."+file+"-preview").append "<input class='"+file+"-hidden-input' value="+fileValue+" type='hidden'  name="+hiddenNameAttr+">"
+  $("."+file+"-preview").append "
"
+
+
+  # this is finding the hidden field that was placed on the dom with mismatched name attributes and removing them because I already placed the correct hidden input next to preview
+  fileHiddenFields = $('form input[type=hidden]')
+  fileHiddenFields.each (i, obj) ->
+    name = $(obj).attr('name')
+    className = $(obj).attr('class')
+    if name.match(regex) != null && className !=  file+"-hidden-input"
+      $(obj).remove()
+
+
+
 $ ->
   # instantiate cloudinary file upload (direct upload)
   if $.fn.cloudinary_fileupload != undefined
     $('input.cloudinary-fileupload[type=file]').cloudinary_fileupload()

   # ***** PHOTOS *****
-  idx = 0
+  photoIdx = 0
   $('.cloudinary-fileupload.survey-photo-upload').bind 'cloudinarydone', (e, data) ->
     # need to wrap this .append in a div class=photo-preview-container
     $('.photo-preview').append $.cloudinary.image(data.result.public_id,
@@ -12,36 +50,16 @@
       version: data.result.version
       crop: 'fill'
       width: 350)
+    addTexareaForUpload("photo", data, photoIdx)
+    photoIdx += 1

-    # this code is seting the value of the hidden fields for the uploaded images.
-    dataObj = JSON.parse(data._response.jqXHR.responseText);
-    imgValue = dataObj.resource_type+"/"+dataObj.type+"/"+"v"+dataObj.version+"/"+dataObj.public_id+"."+dataObj.format+"#"+dataObj.signature
-    nameAttr = data.cloudinaryField.replace(/\[observation_photos_attributes]\[\d+]\[photo]/, "[observation_photos_attributes][" + idx + "][description]")
-    hiddenNameAttr = data.cloudinaryField.replace(/\[observation_photos_attributes]\[\d+]\[photo]/, "[observation_photos_attributes][" + idx + "][photo]")
-
-    # this is simply appending the textarea and the hidden input. I am adding the hidden input right next to the img photo-preview
-    $('.photo-preview').append "
"
-    $('.photo-preview').append "<textarea rows=2 cols=55 style='margin:20px 0 20px 0;' placeholder='Add image caption here...' name="+nameAttr+"></textarea>"
-    $('.photo-preview').append "<input class='photo-hidden-input' value="+imgValue+" type='hidden'  name="+hiddenNameAttr+">"
-    $('.photo-preview').append "
"
-
-
-    #  this is finding the hidden field that was placed on the dom with mismatched name attributes and removing them because I already placed the correct hidden input next to preview
-    photoHiddenFields = $('form input[type=hidden]')
-    photoHiddenFields.each (i, obj) ->
-      name = $(obj).attr('name')
-      className = $(obj).attr('class')
-      if name.match(/\[observation_photos_attributes]\[\d+]\[photo]/) != null && className !=  "photo-hidden-input"
-        $(obj).remove()
-
-    idx = idx + 1
-
-
+  # handle errors
   $('.cloudinary-fileupload.survey-photo-upload').bind 'fileuploadfail', (e, data) ->
     # append error message
     $('.photo-upload-error').append "
 Failed to upload photo, please try again
"
     $(".survey-photo-upload").on 'click', ->
       $('.photo-upload-error').find('p').remove()
+  # progress bar
   $('.cloudinary-fileupload.survey-photo-upload').bind 'fileuploadprogress', (e, data) ->
     # implement progress indicator
     $('.photo-progress-bar').css('width', Math.round((data.loaded * 100.0) / data.total) + '%')
@@ -49,51 +67,45 @@

   # ***** VIDEOS *****
+  videoIdx = 0
   $('.cloudinary-fileupload.survey-video-upload').bind 'cloudinarydone', (e, data) ->
     $('.video-preview').append $.cloudinary.video(data.result.public_id,
       format: data.result.format
       version: data.result.version
       crop: 'fill'
       width: 350)
-    # need to add textarea for caption for each photo, attach photo identifier to text area so we can come back and rename apprpriately
+    addTexareaForUpload("video", data, videoIdx)
+    videoIdx += 1

-    # loop through the hidden fields, match the photo and description textarea with the hidden input and name text area apprpriately
-
-    # rename the hidden fields as they have
-
+  # handle errors
   $('.cloudinary-fileupload.survey-video-upload').bind 'fileuploadfail', (e, data) ->
-    # append error message
     $('.video-upload-error').append "
 Failed to upload video, please try again
"
     $(".survey-video-upload").on 'click', ->
       $('.video-upload-error').find('p').remove()
-
+  # progress bar
   $('.cloudinary-fileupload.survey-video-upload').bind 'fileuploadprogress', (e, data) ->
     # implement progress indicator
     $('.video-progress-bar').css('width', Math.round((data.loaded * 100.0) / data.total) + '%')

-# ***** DOCUMENTS *****
-$('.cloudinary-fileupload.survey-document-upload').bind 'cloudinarydone', (e, data) ->
-  # need to wrap this .append in a div class=photo-preview-container
-  $('.document-preview').append $.cloudinary.image(data.result.public_id,
-    format: data.result.format
-    version: data.result.version
-    crop: 'fill'
-    width: 350)
-  # need to add textarea for caption for each photo, attach photo identifier to text area so we can come back and rename apprpriately
-
-  # loop through the hidden fields, match the photo and description textarea with the hidden input and name text area apprpriately
-
-  # rename the hidden fields as they have
-
-$('.cloudinary-fileupload.survey-document-upload').bind 'fileuploadfail', (e, data) ->
-  # append error message
-  $('.document-upload-error').append "
 Failed to upload document, please try again
"
-
-  $(".survey-document-upload").on 'click', ->
-    $('.document-upload-error').find('p').remove()
-
-$('.cloudinary-fileupload.survey-document-upload').bind 'fileuploadprogress', (e, data) ->
-  # implement progress indicator
-  $('.document-progress-bar').css('width', Math.round((data.loaded * 100.0) / data.total) + '%')
+  # ***** DOCUMENTS *****
+  docIdx = 0
+  $('.cloudinary-fileupload.survey-document-upload').bind 'cloudinarydone', (e, data) ->
+    # need to wrap this .append in a div class=photo-preview-container
+    $('.document-preview').append $.cloudinary.image(data.result.public_id,
+      format: data.result.format
+      version: data.result.version
+      crop: 'fill'
+      width: 350)
+    addTexareaForUpload("document", data, docIdx)
+    docIdx += 1
+  # handles errors
+  $('.cloudinary-fileupload.survey-document-upload').bind 'fileuploadfail', (e, data) ->
+    $('.document-upload-error').append "
 Failed to upload document, please try again
"
+    $(".survey-document-upload").on 'click', ->
+      $('.document-upload-error').find('p').remove()
+  # progress bar
+  $('.cloudinary-fileupload.survey-document-upload').bind 'fileuploadprogress', (e, data) ->
+    # implement progress indicator
+    $('.document-progress-bar').css('width', Math.round((data.loaded * 100.0) / data.total) + '%')


Add a command

Add a command

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -54,7 +54,7 @@
     if process.platform is 'win32'
       console.log directory
       options = {cwd: directory, maxBuffer: Infinity}
-      child_process.exec "C:/psmodules/7z.exe -r #{assetName} #{sourceName}", options, (error, stdout, stderr) ->
+      child_process.exec "C:/psmodules/7z.exe a -r #{assetName} #{sourceName}", options, (error, stdout, stderr) ->
         if error?
           logError("Zipping #{sourceName} failed", error, stderr)
         callback(error)


Fix Message.Utils.Reply

Fix Message.Utils.Reply

--- client/app/utils/message_utils.coffee
+++ client/app/utils/message_utils.coffee
@@ -94,8 +94,8 @@

     # Extract a reply address from a `message` object.
     getReplyToAddress: (message) ->
-        reply = message.get 'replyTo'
-        from = message.get 'from'
+        reply = message?.get 'replyTo'
+        from = message?.get 'from'
         if (reply? and reply.length isnt 0)
             return reply
         else
@@ -129,6 +129,7 @@
             attachments     : Immutable.List()
             accountID       : account.id
             isDraft         : true
+            textOnly        : ''
             from            : [
                     name: account.name
                     address: account.address
@@ -154,9 +155,13 @@
         if html? and not text? and not message.composeInHTML
             text = toMarkdown html

-        if (inReplyTo = props.inReplyTo)
+        inReplyTo = props.inReplyTo
+        inReplyTo = null if inReplyTo and _.isString inReplyTo
+        if inReplyTo
+            replyID = inReplyTo.get 'id'
+            html = inReplyTo?.get 'html'
             _.extend message,
-                inReplyTo: [replyID = inReplyTo.get 'id']
+                inReplyTo: inReplyTo
                 references: (inReplyTo.get('references') or []).concat replyID

         isSignature = !!!_.isEmpty(signature = account.signature)
@@ -172,7 +177,6 @@
             signature
             isSignature
         }
-
         switch props.action
             when ComposeActions.REPLY
                 @setMessageAsReply options
@@ -213,7 +217,7 @@
         message.cc = []
         message.bcc = []
         message.subject = @getReplySubject inReplyTo
-        message.text = separator + @generateReplyText(text) + "\n"
+        message.text = separator + @generateReplyText(inReplyTo) + "\n"
         message.html = """
         #{COMPOSE_STYLE}

@@ -221,6 +225,7 @@

         if isSignature
             @addSignature message, signature
+
         message.html += """

#{separator} …

#{html}

@@ -259,7 +264,7 @@
         message.bcc = []

         message.subject = @getReplySubject inReplyTo
-        message.text = separator + @generateReplyText(text) + "\n"
+        message.text = separator + @generateReplyText(inReplyTo) + "\n"
         message.html = """
             #{COMPOSE_STYLE}

@@ -370,7 +375,9 @@

     # Generate reply text by adding `>` before each line of the given text.
-    generateReplyText: (text) ->
+    generateReplyText: (message) ->
+        unless (text = message?.get('text'))
+            return ''
         text = text.split '\n'
         res  = []
         text.forEach (line) ->
@@ -534,11 +541,11 @@

     # Add a reply prefix to the current subject. Do not add it again if it's
     # already there.
-    getReplySubject: (inReplyTo) ->
-        subject =  inReplyTo.get('subject') or ''
-        replyPrefix = t 'compose reply prefix'
-        if subject.indexOf(replyPrefix) isnt 0
-            subject = "#{replyPrefix}#{subject}"
+    getReplySubject: (message) ->
+        subject =  message?.get('subject') or ''
+        prefix = t 'compose reply prefix'
+        if subject.indexOf(prefix) isnt 0
+            subject = "#{prefix}#{subject}"
         subject

     # To keep HTML markup light, create the contact tooltip dynamicaly


Add copy examples button (fix #162)

Add copy examples button (fix #162)

Merge commit pull request from fredeom:162_copysample

--- client/components/EditablePage.coffee
+++ client/components/EditablePage.coffee
@@ -7,11 +7,35 @@

 import callApi from '../lib/callApi'

-SimplePage = (props) ->
-

-

+class SimplePage extends React.Component
+    updatePreElements: () =>
+        preElements = @statementEl.querySelectorAll("pre")
+        preElements.forEach (preElement) ->
+            copyBtn = document.createElement "button"
+            copyBtn.className = "btn btn-default btn-xs"
+            copyBtn.appendChild document.createTextNode "скопировать"
+            copyBtn.onclick = () =>
+                navigator.clipboard.writeText preElement.textContent
+                copyBtn.textContent = "скопировано"
+                copyBtn.className = "btn btn-success btn-xs"
+                clearSuccess = () =>
+                    copyBtn.className = "btn btn-default btn-xs"
+                    copyBtn.textContent = "скопировать"
+                setTimeout clearSuccess, 1000
+            preElement.before(copyBtn)
+
+    componentDidMount: () =>
+        @updatePreElements()
+
+    componentDidUpdate: (prevProps) =>
+        if @props.material.content != prevProps.material.content
+            @updatePreElements()
+
+    render: () ->
+

+
 @statementEl = el}>
+

-

 class EditablePage extends React.Component
     constructor: (props) ->


use CamelCase

use CamelCase

--- client/lanes/lib/ModuleSupport.coffee
+++ client/lanes/lib/ModuleSupport.coffee
@@ -5,7 +5,7 @@
         _.extend(klass,this)

     @include: (obj) ->
-        special = this::extended_properties || []
+        special = this::extendedProperties || []
         if obj.initialize
             fn = @::initialize
             @::initialize = ->


Use the same list of paragraph selectors for default selectors and article container selectors

--- js-library/app/js/jail_iframe/classes/paragraph_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/paragraph_buttons.coffee
@@ -24,8 +24,14 @@
     elementsLeft = elements[10..]
     setTimeout (=> @_addParagraphButtonsBatch(elementsLeft)), 200

+  _paragraphSelectors: ->
+    ['p', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'dd', 'dt']
+
+  _prefixedParagraphSelectors: (prefix) ->
+    (prefix + ' ' + selector for selector in @_paragraphSelectors())
+
   _defaultSelector: ->
-    "p, h2, h3, h4, h5, h6, li, dd, dt"
+    @_paragraphSelectors().join(',')

   _articleContainerSelector: ->
     selectors = [
@@ -38,7 +44,7 @@
       $element = $(s)
       # Only match if selector is unique
       if $element.length == 1 && $element.is(':visible')
-        return "#{s} p, #{s} h2, #{s} h3, #{s} h4, #{s} h5, #{s} h6, #{s} li, #{s} dd, #{s} dt"
+        return @_prefixedParagraphSelectors(s).join(',')

     null


added failing test in reference to #10

added failing test in reference to #10

--- coffee/test/test_humanize.spec.coffee
+++ coffee/test/test_humanize.spec.coffee
@@ -3,6 +3,11 @@
     it 'should pass', ->
     	expect(Humanize).toBeDefined()
     	expect(Humanize.intword(123456789, 6)).toEqual('123.45M')
+
+describe 'thousands as word', ->
+    it 'should pass', ->
+    	expect(Humanize).toBeDefined()
+    	expect(Humanize.intword(100, 2)).toEqual('0.1k')

 describe 'Ordinal value of numbers Test Suite', ->


Make calculations use cellFrame

Make calculations use cellFrame

--- framer/Components/GridComponent.coffee
+++ framer/Components/GridComponent.coffee
@@ -40,7 +40,7 @@
 			@render()

 	@define "cellWidth",
-		get: -> (@width - (@spacing.horizontal * (@columns - 1))) / @columns
+		get: -> (@width - (@spacing.horizontal * (@columns - 1))) / @columns

 	@define "cellHeight",
 		get: -> (@height - (@spacing.vertical * (@rows - 1))) / @rows
@@ -75,16 +75,10 @@

 		for row in [@rows-1..0]
 			for column in [@columns-1..0]
-
-				frame =
-					x: @cellX(column)
-					y: @cellY(row)
-					width: @cellWidth
-					height: @cellHeight

 				cell = new Layer
 					parent: @
-					frame: frame
+					frame: @cellFrame(column, row)
 					name: "Cell #{column}:#{row}"

 				@renderCell(cell, row, column)


Cleaning release `dir` before building. (EVERYTHING YOU PUT IN THE WILL BE DELETED)

--- src/toaster/core/builder.coffee
+++ src/toaster/core/builder.coffee
@@ -157,7 +157,15 @@
           file.item.getinfo()
           file.item.compile_to_disk()

+  clear:->
+    # clear release folder
+    fsu.rm_rf @config.release_dir
+    fsu.mkdir_p @config.release_dir
+
   compile:( header_code, footer_code )->
+    # clear release folder
+    @clear()
+
     # loop through all ordered files
     file.compile_to_disk() for file, index in @files
     @copy_vendors_to_release()
@@ -165,6 +173,9 @@

   optimize:( header_code, footer_code )->
     log 'Optimizing project...'
+
+    # clear release folder
+    @clear()

     paths = {}
     layers = []


Avoid leaving empty blocks in the document

--- src/trix/models/document.coffee
+++ src/trix/models/document.coffee
@@ -63,9 +63,17 @@

   removeTextAtLocationRange: edit (locationRange) ->
     return if locationRange.isCollapsed()
+
     if locationRange.isInSingleIndex()
-      @blockList = @blockList.editObjectAtIndex locationRange.index, (block) ->
-        block.copyWithText(block.text.removeTextAtRange([locationRange.start.offset, locationRange.end.offset]))
+      index = locationRange.index
+      block = @blockList.getObjectAtIndex(index)
+      text = block.text.removeTextAtRange([locationRange.start.offset, locationRange.end.offset])
+
+      @blockList = if text.isEmpty()
+        @blockList.removeObjectAtIndex(index)
+      else
+        @blockList.replaceObjectAtIndex(block.copyWithText(text), index)
+
     else
       range = @rangeFromLocationRange(locationRange)
       blockList = @blockList.removeObjectsInRange(range)


Reverse project plots

Reverse project plots

--- static/scripts/plottings.coffee
+++ static/scripts/plottings.coffee
@@ -47,7 +47,7 @@
                                 0

         _preparePlot: (plot) ->
-            _.map _.last(plot, @limit), (item, num) -> [num, item]
+            _.map _.last(plot.reverse(), @limit), (item, num) -> [num, item]

         _checkDatasets: (datasets) ->
             datasets.length and (_.any datasets, (@item) ->


enh: cleaner visibility distance setup

enh: cleaner visibility distance setup

--- 3d/lines-app.coffee
+++ 3d/lines-app.coffee
@@ -8,14 +8,17 @@
 controls = undefined
 stepsPerMs = 10 / 1000

+visibilityDistance = 10000
+
 showStats = false

 palette = [0xfe8f0f, 0xf7325e, 0x7dc410, 0xfef8cf, 0x0264ed]

 requestStop = false

+
 class WorkerFlyingCurves
-  constructor: ->
+  constructor: (startZ=4000, endZ=-4000) ->
     @worker = new Worker "./tubing_worker_browser.js"
     @worker.addEventListener "message", (e)=>@_onMsg(e)

@@ -23,14 +26,15 @@
     @scale = scale = 30
     @group = new THREE.Object3D
     @chunks = []
-    @zMin = -4000 / scale
-    @zMax = 4000 / scale
+    @zMin = endZ / scale
+    @zMax = startZ / scale
     @lastChunkZ = 0

     @group.scale.set scale, scale, scale
     @ready = false
     @taskId2dummyChunks = {}
     @nextTaskId = 0
+    @chunkSize = 500
     #continue initialization after the worker is ready
     @loadFDL "$3b2o$2bobob2o$2bo5bo$7b2o$b2o$bo5bo$2b2obobo$5b2obo"

@@ -122,7 +126,7 @@

     tube.addAttribute 'position', new THREE.BufferAttribute(blueprint.v, 3)
     tube.addAttribute 'index', new THREE.BufferAttribute(blueprint.idx, 1)
-    #tube.computeBoundingSphere() #do we need it?
+    tube.computeBoundingSphere() #do we need it?
     return  tube

   #remove all tubes, return to the initial state.
@@ -144,9 +148,9 @@
     @worker.postMessage
       cmd: "init"
       pattern: pattern
-      chunkSize: 500
+      chunkSize: @chunkSize
       skipSteps: 1
-      size: 128
+      size: 100
       # _finishInitialize invoked on responce

   step: (dz) ->
@@ -185,29 +189,29 @@

   #
   camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 10500)
-  camera.position.set 500, 0, -1750
+  camera.position.set 300, 0, -1550
   scene = new THREE.Scene()
-  scene.fog = new THREE.Fog 0x050505, 2000, 10500
+  scene.fog = new THREE.Fog 0x000505, visibilityDistance*0.85, visibilityDistance
   #scene.add new THREE.AmbientLight 0x444444

   controls = new THREE.TrackballControls  camera

   controls.rotateSpeed = 1.0
-  controls.zoomSpeed = 1.2
+  controls.zoomSpeed = 2.2
   controls.panSpeed = 0.8

   controls.noZoom = false
   controls.noPan = false

   controls.staticMoving = true
-  controls.dynamicDampingFactor = 0.3
+  controls.dynamicDampingFactor = 0.9

   controls.keys = [ 65, 83, 68 ]

   #controls.addEventListener 'change', render

   #curves = new ChunkedFlyingCurves
-  curves = new WorkerFlyingCurves
+  curves = new WorkerFlyingCurves visibilityDistance, -0.5*visibilityDistance

   lines = new THREE.Object3D
   lines.add curves.group


separated blueprint-generation code

separated blueprint-generation code

--- 3d/lines-app.coffee
+++ 3d/lines-app.coffee
@@ -66,7 +66,7 @@
     return chunk

-  createTube: (xys, i, xy0)->
+  makeTubeBlueprint: (xys, i, xy0)->
     jumpTreshold = @jumpTreshold
     vs = new Float32Array xys.length*4*3 #x,y,z; 4 vertices
     ixs =  new Uint16Array (xys.length-1)*2*3*4 #2 triangles
@@ -139,13 +139,21 @@
           j1 = (j+1)%4
           pushQuad vindex-4+j, vindex+j, vindex-4+j1, vindex+j1

-    if curIx isnt ixs.length
-      #then throw new Error "Not all indices filled"
-      console.log "Indices skip: #{ixs.length - curIx}"
-      ixs = ixs.subarray 0, curIx
     if curV isnt vs.length then throw new Error "Not all vertices filled"

+    blueprint =
+      v: vs
+      v_used: curV
+      idx: ixs
+      idx_used: curIx
+    return blueprint
+
+  createTube: (xys, i, xy0)->
+    blueprint = @makeTubeBlueprint xys, i, xy0
     tube = new THREE.BufferGeometry()
+
+    vs = blueprint.v.subarray 0, blueprint.v_used
+    ixs = blueprint.idx.subarray 0, blueprint.idx_used

     tube.addAttribute 'position', new THREE.BufferAttribute(vs, 3)
     tube.addAttribute 'index', new THREE.BufferAttribute(ixs, 1)


don't have terms agreement on edit profile

--- @client/auth.coffee
+++ @client/auth.coffee
@@ -513,7 +513,7 @@

       # Considerit terms
-      if auth.form in ['create account', 'create account via invitation'] || (auth.form in ['user questions', 'edit profile'] && !@local.agreed_to_terms)
+      if auth.form in ['create account', 'create account via invitation'] || (auth.form in ['user questions'] && !@local.agreed_to_terms)

         if !@local.agreed_to_terms?
           @local.agreed_to_terms = !!current_user.tags['considerit_terms']
@@ -1150,7 +1150,7 @@

       save @local

-    if auth.ask_questions && auth.form not in ['login', 'reset password', 'verify email'] && !@local.agreed_to_terms
+    if auth.ask_questions && auth.form not in ['login', 'reset password', 'verify email', 'edit profile'] && !@local.agreed_to_terms
       @local.errors.push translator('auth.validation.agree_to_terms', "To proceed, you must agree to the terms")

     if @local.errors.length == 0


More verbose Loco's initialization

More verbose Loco's initialization

--- spec_coffee/dummy/initializers/loco.coffee
+++ spec_coffee/dummy/initializers/loco.coffee
@@ -10,15 +10,38 @@
 App.Views.Shared = {}

 loco = new App.Loco
-  turbolinks: false                  # false by default
+  # set to true if you if have enabled turbolinks
+  turbolinks: false                   # false by default
+
+  # your browser's app will periodically be checking for new notifications via ajax polling
   notifications:
-    enable: false                    # false by default
-    pollingTime: 3000                # 3000 ms by default
-    log: true                        # false by default
-    ssl: false                       # your current protocol by default
-    location: 'notification-center'  # 'notification-center' by default
-    size: 10                         # 100 by default
-  locale: 'en'                       # 'en' by default
-  postInit: ->
+    enable: false                     # false by default
+    #pollingTime: 3000                # 3000 ms by default
+
+    # display upcoming notifications in browser's console e.g. for debugging
+    #log: true                        # false by default
+
+    #ssl: false                       # your current protocol by default
+
+    # location must the same as where you mount Loco in routes.rb
+    #location: 'notification-center'  # 'notification-center' by default
+
+    # max number of notifications that is fetched at once via ajax pooling
+    # must be the same as notifications_size defined in initializers/loco.rb
+    # next batch of notifications will be fetched immediately after max size is reached
+    #size: 100                        # 100 by default
+
+    # after this time your current namespace controller / controller instance method
+    # disconnectedForTooLong: will be called with the 'time since disconnection' passed as an argument
+    #allowedDisconnectionTime: 10     # 10 by default [sec]
+  #locale: 'en'                       # 'en' by default
+
+  # if provided - loco will be using absolute path instead of site-root-relative path in all xhr requests
+  #protocolWithHost: 'https://example.com'
+
+  # this method is called at the end, after given controller methods has been called
+  # at this time Loco's instance is initialized and you can use it to change behaviour of your browser app
+  # e.g. polling interval -> App.Env.loco.getWire().setPollingTime <time>
+  #postInit: ->

 loco.init()


Tweaking the change hub text

Tweaking the change hub text

--- app/assets/javascripts/darkswarm/controllers/account_sidebar_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/account_sidebar_controller.js.coffee
@@ -11,6 +11,6 @@
   $scope.emptyCart = (href, ev)->
     console.log href
     if $(ev.delegateTarget).hasClass "empties-cart"
-      location.href = href if confirm "Changing your collection date will clear your cart."
+      location.href = href if confirm "Changing your Hub will clear your cart."
     else
       location.href = href


Fix #204

Fix #204

--- lib/git.coffee
+++ lib/git.coffee
@@ -83,8 +83,7 @@
     cwd: repo.getWorkingDirectory()
     stdout: (data) -> stdout _prettifyDiff(data)

-# Two-fold, refresh index as well as status
-gitRefreshIndex = ->
+gitRefresh = ->
   atom.project.getRepositories().forEach (r) -> r?.refreshStatus()
   gitCmd
     args: ['add', '--refresh', '--', '.']
@@ -168,8 +167,8 @@
 # Returns a {Promise} that resolves to a repository like object
 getRepo = ->
   new Promise (resolve, reject) ->
-    getRepoForCurrentFile().then (repo) ->
-      resolve(repo) if repo isnt null
+    getRepoForCurrentFile().then (repo) -> resolve(repo)
+    .catch (e) ->
       repos = atom.project.getRepositories().filter (r) -> r?
       if repos.length is 0
         reject("No repos found")
@@ -179,19 +178,24 @@
         resolve(repos[0])

 getRepoForCurrentFile = ->
-  project = atom.project
-  path = atom.workspace.getActiveTextEditor()?.getPath()
-  directory = project.getDirectories().filter((d) -> d.contains(path))[0]
-  if directory?
-    return project.repositoryForDirectory(directory)
-  else
-    return Promise.resolve(null)
+  new Promise (resolve, reject) ->
+    project = atom.project
+    path = atom.workspace.getActiveTextEditor()?.getPath()
+    directory = project.getDirectories().filter((d) -> d.contains(path))[0]
+    if directory?
+      project.repositoryForDirectory(directory).then (repo) ->
+        submodule = repo.repo.submoduleForPath(path)
+        if submodule? then resolve(submodule) else resolve(repo)
+      .catch (e) ->
+        reject(e)
+    else
+      reject "no current file"

 module.exports.cmd = gitCmd
 module.exports.stagedFiles = gitStagedFiles
 module.exports.unstagedFiles = gitUnstagedFiles
 module.exports.diff = gitDiff
-module.exports.refresh = gitRefreshIndex
+module.exports.refresh = gitRefresh
 module.exports.status = gitStatus
 module.exports.reset = gitResetHead
 module.exports.add = gitAdd


display fix for loader (will not load when there is no actual embed request sent to embed.ly)

--- client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
+++ client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
@@ -30,6 +30,7 @@
     @makeActive()

     # KDListView -> EmbedBoxLinksView -> EmbedBox .embedUrl
+    @getDelegate().getDelegate().getDelegate().embedLoader.hide()
     @getDelegate().getDelegate().getDelegate().embedUrl @linkUrl, {}, (embedData)=>
       if embedData.favicon_url? then @setFavicon embedData.favicon_url


Remove mobile/components/cookies

Remove mobile/components/cookies

--- src/mobile/components/cookies/index.coffee
+++ src/mobile/components/cookies/index.coffee
@@ -1,18 +0,0 @@
-_ = require 'underscore'
-{ NODE_ENV } = require('sharify').data
-IS_TEST_ENV = not _.contains(['production', 'staging', 'development'], NODE_ENV)
-methods = ['get', 'set', 'expire']
-
-cookies = ->
-  if IS_TEST_ENV
-    _.reduce methods, (memo, method) ->
-      memo[method] = (->)
-      memo
-    , {}
-  else
-    require 'cookies-js'
-
-module.exports = _.reduce methods, (memo, method) ->
-  memo[method] = -> cookies()[method] arguments...
-  memo
-, {}


body.coffee indent problem

body.coffee indent problem

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -118,7 +118,8 @@
             $el = $(el)
             href = $el.attr('href')

-            if href.substr(0, 1) is '#' and href.length > 1 and $el.data('type') isnt 'footnote-ref'
+            if href.substr(0, 1) is '#' and href.length > 1 and
+               $el.data('type') isnt 'footnote-ref'
               try
                 $target = $temp.find(href)


adding click event

adding click event

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -28,6 +28,7 @@
       'click [data-type="solution"] > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
       'click .solution              > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
       'keydown .media-body' : 'removeDataTextAttribute'
+      'click .media-body' : 'removeDataTextAttribute'

     removeDataTextAttribute: () ->
       $('.media-body').removeAttr('data-text')


Modified sortedLanguages helper to be compatible with tap-i18n 0.5.0

--- i18n_ui.coffee
+++ i18n_ui.coffee
@@ -1,6 +1,11 @@
 # transform getLanguages into ordered array
 sortedLanguages = ->
-  _.sortBy _.values TAPi18n.getLanguages(), (language) -> language.code
+  langauges = TAPi18n.getLanguages()
+  # map language tag into the object
+  for key,val of langauges
+    langauges[key].tag = key
+  # return array sorted alphabetically by tag
+  _.sortBy _.values langauges, (language) -> language.tag

 Template.i18n_dropdown.events


Fixed bug in sponsor date range logic

Fixed bug in sponsor date range logic

--- site/server/lists.coffee
+++ site/server/lists.coffee
@@ -76,7 +76,7 @@
     sponsor_start = moment.utc(collection.sponsor_start)
     sponsor_end = moment.utc(collection.sponsor_end)
     now = moment.utc()
-    if sponsor_start.diff(now) <= 0 and sponsor_start.diff(now) >= 0
+    if sponsor_start.diff(now) <= 0 and sponsor_end.diff(now) >= 0
       # let continue on
       sponsor.text_format = sponsor.format is 'text'
       sponsor.image_format = sponsor.format is 'image'
@@ -193,7 +193,7 @@
     sponsor_start = moment.utc(essay.sponsor_start)
     sponsor_end = moment.utc(essay.sponsor_end)
     now = moment.utc()
-    if sponsor_start.diff(now) <= 0 and sponsor_start.diff(now) >= 0
+    if sponsor_start.diff(now) <= 0 and sponsor_end.diff(now) >= 0
       # let continue on
       sponsor.text_format = sponsor.format is 'text'
       sponsor.image_format = sponsor.format is 'image'


Move talk path

Move talk path

--- app/controllers/classify.coffee
+++ app/controllers/classify.coffee
@@ -83,7 +83,7 @@
     talk = answer.data 'talk'

     if talk
-      url = "http://talk.galaxyzoo.org/objects/#{ @subject.zooniverse_id }"
+      url = "http://talk.galaxyzoo.org/#/subjects/#{ @subject.zooniverse_id }"
       window.open url, '_blank'
       window.focus()


Fix mounting hole numeration in custom pattern generator

--- src/pattern/default/custom.coffee
+++ src/pattern/default/custom.coffee
@@ -95,6 +95,7 @@
   pattern.center -bodyPosition[0].x, -bodyPosition[0].y

   pinNumber = 0
+  mountingHole = 1
   copperPads pattern, element
   i = 1
   loop


Snap cursor to map grid

Snap cursor to map grid

--- app/scripts/client/webgl.coffee
+++ app/scripts/client/webgl.coffee
@@ -150,7 +150,7 @@
     handleMouseOut: (e) =>
         @mouseOver = false

-    showCursor: ->
+    showCursor: (point) ->
         if not @cursor
             console.log 'foo'
             geometry = new THREE.PlaneGeometry 1, 1, 1
@@ -162,10 +162,16 @@
             }
             @cursor = new THREE.Mesh geometry, material
             @cursor.scale.set 2, 2, 1
-            @cursor.position.set 0, 0, 0.5
+            @cursor.position.set 0, 0, 0
             @scene.add @cursor

-        @cursor.position.set @intersection.point.x, @intersection.point.y, 0.5
+        # Snap to nearest grid
+        gridCellWidth = 2
+        gridCellHeight = 2
+        point.x = (Math.floor(point.x / gridCellWidth) * gridCellWidth) + 1
+        point.y = (Math.floor(point.y / gridCellHeight) * gridCellHeight) + 1
+
+        @cursor.position.set point.x, point.y, 0
         @cursor.material.visible = true
         @cursor.material.needsUpdate

@@ -187,7 +193,7 @@

             if intersection
                 @intersection = intersection
-                @showCursor()
+                @showCursor(@intersection.point)
             else
                 @removeCursor()
         else


Do not show 'main' when dbg.list(), since there is already one.

--- lib/debug.coffee
+++ lib/debug.coffee
@@ -178,6 +178,7 @@
     output = new DevToolsBooster
     output['DEBUG'] = new Booster(@, 'main', relist)
     for opt of @h
+      continue if opt is 'main'
       output[opt] = new Booster(@h[opt], 'extra', relist)
     console.table output


:fire: unused

:fire: unused

--- lib/entry.coffee
+++ lib/entry.coffee
@@ -33,8 +33,6 @@
   isDestroyed: ->
     @destroyed

-  setURI: (@URI) ->
-
   isValid: ->
     return false if @isDestroyed()


Add ability to add base64 URI images

Add ability to add base64 URI images

--- lib/image.coffee
+++ lib/image.coffee
@@ -13,8 +13,13 @@
     if Buffer.isBuffer(src)
       data = src
     else
-      data = fs.readFileSync src
-      return unless data
+      if src[0..4] is 'data:' and src.indexOf(';base64,') > -1
+        base64String = src.split(';base64,')[1]
+        data = new Buffer(base64String, 'base64')
+
+      else
+        data = fs.readFileSync src
+        return unless data

     if data[0] is 0xff and data[1] is 0xd8
       return new JPEG(data, label)


allow resetting zip state

allow resetting zip state

--- lib/index.coffee
+++ lib/index.coffee
@@ -15,11 +15,13 @@
   initialize = ->
     zip = archiver type, options
     initialized = true
-  reset = ->
-    fileList = []
-    fileStack = []

   return {
+    reset: ->
+      zip = archiver type, options
+      fileList = []
+      fileStack = []
+
     setOptions: (opt) ->
       extend(options, opt)


Remove redundant ready state event emitter

--- lib/index.coffee
+++ lib/index.coffee
@@ -256,7 +256,6 @@

   connectHelper:(callback)->
     @mq.once 'connected', =>
-      @emit 'ready'
       callback?()

     @channelName = createBongoName @resourceName


hide/show folders and files instead of removing them, increases speed

--- lib/mylyn.coffee
+++ lib/mylyn.coffee
@@ -66,11 +66,10 @@
       @save()
       task

-  reloadTreeView:()=>
+  reloadTreeView:(updateRoots)=>
       @view.treeViewOpenPromise
                 .then (view)=>
                       selectedPath = view.selectedEntry()?.getPath()
-                      view.updateRoots()
                       if selectedPath
                         view.selectEntryForPath selectedPath
                       @rebuild()
@@ -248,12 +247,15 @@
     span = $(e).find("span").first()
     path = $(span).attr("data-path")
     if !isAllowed(path)
-        $(e).remove()
+        #$(e).remove()
+        $(e).hide()
         #$(e).replaceWith($('<h5>' + e.innerHTML + '</h5>'));
         #$(e).addClass("mylyn-hidden")

         #$(e).addClass("status-ignored")
         #$(e).removeClass("entry")
+    else
+        $(e).show()
     #else

     #    $(e).removeClass("mylyn-hidden")
@@ -275,7 +277,7 @@
       files.each (i,e)=>@hideFile(e)

   listenForEvents:(entry)->
-      if entry.onDidExpand
+      if entry.onDidExpand&&!@isRebuilding
           entry.onDidExpand((d)=>@rebuild())

       if entry.entries
@@ -284,12 +286,17 @@
           #@out("entr")(entry.entries)
           #entry.entries.forEach @listenForEvents
   rebuild:()=>
-
+    @isRebuilding = true
     @hideDirs()
     @hideFiles()
+    # if @mylyn.filterOn
+    #   @view.treeView.roots.forEach (root)=>
+    #       if root.expand
+    #           console.log "EXPAND"
+    #           #root.expand(true)
     @view.treeView.roots.forEach (root)=>@listenForEvents root.directory
     rootDir = @view.treeView.roots[0].directory
-
+    @isRebuilding = false
   getDomFiles: =>$(".tree-view [is='tree-view-file']")

@@ -317,7 +324,12 @@
     @mylyn.filterOn = !@mylyn.filterOn
     if @mylyn.filterOn
       @mylyn.enabled = true
-    @reloadTreeView()
+      @reloadTreeView()
+    else
+      @view.treeViewOpenPromise
+                .then (view)=>
+                      selectedPath = view.selectedEntry()?.getPath()
+                      view.updateRoots()

 module.exports =


expose workspace and plotpane

expose workspace and plotpane

--- lib/ink.coffee
+++ lib/ink.coffee
@@ -41,5 +41,7 @@
     Spinner: Spinner
     Result: Result
     Console: Console
+    Workspace: Workspace
+    PlotPane: PlotPane
     highlights: highlights
     tree: tree


! fix centerToAbsolutePosition

! fix centerToAbsolutePosition

--- lib/map.coffee
+++ lib/map.coffee
@@ -32,8 +32,8 @@
         callback();

   centerToAbsolutePosition: (centerX, centerY) =>
-    ax = -(0.5 + centerX) * @fieldSize * @scale + @viewportWidth;
-    ay = -(0.5 + centerY) * @fieldSize * @scale + @viewportHeight;
+    ax = -(0.5 + centerX) * @fieldSize * @scale + @viewportWidth / 2;
+    ay = -(0.5 + centerY) * @fieldSize * @scale + @viewportHeight / 2;
     return[ax, ay];

   # centerDataPosition: (oldWidth, oldHeight) =>


make login button primary to better recognize when disabled

--- src/shared/component/login.coffee
+++ src/shared/component/login.coffee
@@ -156,7 +156,7 @@
                   k.a {
                     className: classNames(
                       'btn'
-                      'btn-default'
+                      'btn-primary'
                       {disabled: that.hasBeenClicked() and that.hasErrors()}
                     )
                     onClick: that.handleClick


* remove unnided test

* remove unnided test

--- test/test_template_engine.coffee
+++ test/test_template_engine.coffee
@@ -1,209 +0,0 @@
-###
-Test suite for node only
-###
-
-_ = require 'lodash'
-
-fs = require 'fs'
-vm = require 'vm'
-
-util = require 'util'
-
-lib_path = GLOBAL?.lib_path || ''
-
-require('node-jsx').install extension: '.jsx'
-
-{ jsdom } = require 'jsdom'
-
-# change to app, for test
-Clinch = require "#{lib_path}app"
-
-# our external plugins
-clinch_jade   = require 'clinch.jade'
-clinch_jsx    = require 'clinch.jsx'
-clinch_csbx   = require 'clinch.csbx'
-clinch_coffee = require 'clinch.coffee'
-
-fixtureRoot  = __dirname + "/fixtures"
-fixturesJade = fixtureRoot + '/jade_powered'
-fixturesReact = fixtureRoot + '/react_powered'
-fixturesWebShims = fixtureRoot + '/web_modules'
-
-JadePowered = require "#{fixturesJade}"
-
-React = ReactTestUtils = document = window = null
-
-describe 'Clinch and template engines:', ->
-
-  clinch_obj = package_config = null
-
-  beforeEach ->
-    clinch_obj = new Clinch()
-    clinch_obj.addPlugin clinch_jade
-              .addPlugin clinch_jsx
-              .addPlugin clinch_csbx
-              .addPlugin clinch_coffee
-
-  describe 'jade:', ->
-
-    jade_expected = """
-                    \n

-
Hello Bender!!!

-

-                    """
-
-    it 'should work in node', ->
-
-      jade_obj = new JadePowered()
-      res = jade_obj.renderData name : 'Bender'
-      res.should.to.be.equal jade_expected
-
-    it 'should work in browser (emulation)', (done) ->
-
-      # looks strange, but its just  analog
-      jade_runtime_file = "#{__dirname}/../node_modules/jade/runtime.js"
-      jade_runtime = fs.readFileSync jade_runtime_file, 'utf8'
-      vm.runInNewContext jade_runtime, jade_sandbox = window : {}
-      # looks starnge, but its ok for browser
-      jade_sandbox.jade = jade_sandbox.window.jade
-
-      ###
-      so, we are should to stub 'fs' and 'jade'
-      looks little ugly, but its fee for untouched sources,
-      think about it as taxes - nobody like it, but every should to pay
-      ###
-      package_config =
-        package_name : 'my_package'
-        bundle :
-          JadePowered : fixturesJade
-        replacement :
-          fs : fixturesWebShims + '/noops'
-          jade : fixturesWebShims + '/noops'
-
-      res_fn = (err, code) ->
-        expect(err).to.be.null
-
-        # this is browser emulation
-        vm.runInNewContext code, jade_sandbox
-        {JadePowered} = jade_sandbox.my_package
-
-        jade_obj = new JadePowered()
-        res = jade_obj.renderData name : 'Bender'
-        res.should.to.be.equal jade_expected
-
-        done()
-
-      # here we are build our package, its what you need for browser
-      clinch_obj.buildPackage package_config, res_fn
-
-  describe 'react:', ->
-
-    react_expected = """
-                    \n

-
Hello Bender!!!

-

-                    """
-
-    beforeEach ->
-      global.document   = jsdom ''
-      global.window     = global.document.parentWindow
-      global.navigator  = global.window.navigator
-
-      React  = require "react/addons"
-      ReactTestUtils = React.addons.TestUtils
-
-      global.react = React
-
-    afterEach ->
-      delete global.window
-      delete global.document
-      delete global.navigator
-
-    it 'should display the window objects', ->
-      global.window.should.exist
-      global.document.should.exist
-
-    it 'should work in node (as `coffee`)', ->
-      ReactPowered = require "#{fixturesReact}/component.coffee"
-      greater = ReactTestUtils.renderIntoDocument ReactPowered name : 'Bender'
-      expect(greater.refs.p.props.children).to.be.equal "Hello Bender!!!"
-
-    it 'should work in node (as `jsx`)', ->
-      ReactPowered = require "#{fixturesReact}/component.jsx"
-      greater = ReactTestUtils.renderIntoDocument ReactPowered name : 'Bender'
-      expect(greater.refs.p.props.children).to.be.eql ["Hello ", "Bender", "!!!"]
-
-    it 'should work in browser (emulation) (as `.coffee`)', (done) ->
-
-      package_config =
-        package_name : 'my_package'
-        bundle :
-          ReactPowered : "#{fixturesReact}/component.coffee"
-        replacement :
-          react : fixturesWebShims + '/react'
-
-      res_fn = (err, code) ->
-        expect(err).to.be.null
-
-        # this is browser emulation
-        vm.runInNewContext code, react_sandbox = global
-
-        {ReactPowered} = react_sandbox.my_package
-
-        greater = ReactTestUtils.renderIntoDocument ReactPowered name : 'Bender'
-        expect(greater.refs.p.props.children).to.be.eql "Hello Bender!!!"
-
-        done()
-
-      # here we are build our package, its what you need for browser
-      clinch_obj.buildPackage package_config, res_fn
-
-    it 'should work in browser (emulation) (as `.jsx`)', (done) ->
-
-      package_config =
-        package_name : 'my_package'
-        bundle :
-          ReactPowered : "#{fixturesReact}/component.jsx"
-        replacement :
-          react : fixturesWebShims + '/react'
-
-      res_fn = (err, code) ->
-        expect(err).to.be.null
-
-        # this is browser emulation
-        vm.runInNewContext code, react_sandbox = global
-
-        {ReactPowered} = react_sandbox.my_package
-
-        greater = ReactTestUtils.renderIntoDocument ReactPowered name : 'Bender'
-        expect(greater.refs.p.props.children).to.be.eql ["Hello ", "Bender", "!!!"]
-
-        done()
-
-      # here we are build our package, its what you need for browser
-      clinch_obj.buildPackage package_config, res_fn
-
-    it 'should work in browser (emulation) (as `.csbx`)', (done) ->
-
-      package_config =
-        package_name : 'my_package'
-        bundle :
-          ReactPowered : "#{fixturesReact}/component.csbx"
-        replacement :
-          react : fixturesWebShims + '/react'
-
-      res_fn = (err, code) ->
-        expect(err).to.be.null
-
-        # this is browser emulation
-        vm.runInNewContext code, react_sandbox = global
-
-        {ReactPowered} = react_sandbox.my_package
-
-        greater = ReactTestUtils.renderIntoDocument ReactPowered name : 'Bender'
-        expect(greater.refs.p.props.children).to.be.eql ["Hello ", "Bender", "!!!"]
-
-        done()
-
-      # here we are build our package, its what you need for browser
-      clinch_obj.buildPackage package_config, res_fn


Fix flex file listing bar for mobile

Fix flex file listing bar for mobile

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -2170,8 +2170,8 @@
             {@render_deleted()}
             {@render_error()}
             {@render_activity()}
-

-

+

+

                     <ProjectFilesSearch
                         project_id          = {@props.project_id}
                         key                 = {@props.current_path}
@@ -2187,17 +2187,17 @@
                         public_view         = {public_view} />

                 {

                         {@render_new_file()}

 if not public_view}
-

+

                     <ProjectFilesPath current_path={@props.current_path} actions={@props.actions} />

-                {

+                {

                     <UsersViewing project_id={@props.project_id} />

 if not public_view}
-                {

+                {

                     <ProjectFilesButtons
                         show_hidden  = {@props.show_hidden ? false}
                         current_path = {@props.current_path}


prettify

prettify

--- lib/quest.coffee
+++ lib/quest.coffee
@@ -21,16 +21,13 @@
     options.headers['content-type'] = 'application/json'
     options.body = JSON.stringify options.json
   jar: (options) ->
-    cookies = options.jar.getCookies options
-    cookie_string = _(cookies).map (c) -> c.toValueString()
-    cookie_string = cookie_string.join '; '
+    cookie_string = _(options.jar.getCookies options).map((c) -> c.toValueString()).join '; '
     options.headers.cookie = if not options.headers.cookie? then '' else "#{options.headeers.cookie}; "
     options.headers.cookie = "#{options.headers.cookie}#{cookie_string}"
+handle_options = (options) -> _(_(handle).values()).map (handler) -> handler options

 is_uri = (uri) -> /^https?:\/\//.test uri
 normalize_uri = (options) -> options.uri = "http://#{options.uri}" if not is_uri options.uri
-
-handle_options = (options) -> _(_(handle).values()).map (handler) -> handler options

 should_redirect = (options, resp) ->
   299 < resp.statusCode < 400 and (options.followAllRedirects or (options.followRedirects and
@@ -60,24 +57,18 @@
   _(options).defaults parsed_uri
   _(options.headers).defaults
     'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_3) AppleWebKit/537.16 (KHTML, like Gecko) Chrome/24.0.1297.0 Safari/537.16'
-
   handle_options options
-
   if options.body?
     options.body = new Buffer options.body
     options.headers['content-length'] = options.body.length
+  options.method = options.method.toUpperCase()

-  options.method = options.method.toUpperCase()
   req = request_module.request options, (resp) ->
-    the_request_info = {}
-    _(the_request_info).extend req
-    _(the_request_info).extend options
-    resp.request = the_request_info
+    resp.request = _(_({}).extend(req)).extend options

     cookies = resp?.headers?['set-cookie']
     if options.jar isnt false and cookies?
       options.jar.setCookies if _(cookies).isArray() then cookies else [cookies]
-
     if should_redirect options, resp
       return req.emit 'error', 'Exceeded max redirects' if options.maxRedirects is 0
       redirect_options = {}
@@ -90,9 +81,8 @@
       redirect_options.uri = url.resolve options.href, redirect_options.uri if not is_uri redirect_options.uri
       return quest redirect_options, cb

+    body = ''
     resp.setEncoding 'utf-8'
-    body = ''
-
     resp.on 'data', (part) -> body += part if part?
     resp.on 'end', (part) ->
       body += part if part?
@@ -112,7 +102,5 @@
   jar.add = jar.setCookie
   jar.get = (uri) -> jar.getCookies url.parse uri
   jar
-
 quest.cookie = cookiejar.Cookie
-
 module.exports = quest


Updated Logger description for failed domain matching

--- src/middleware/tlsAuthentication.coffee
+++ src/middleware/tlsAuthentication.coffee
@@ -49,7 +49,7 @@
 				yield next;
 			}else{
 				this.response.status = "unauthorized";
-				logger.info("Certificate Authentication Failed: Certificate Name did not match your Client Domain");
+				logger.info("Certificate Authentication Failed: the certificate's common name did not match any client's domain attribute");
 			}
 		} else {
 			this.response.status = "unauthorized";


Tests: Activity: Post: Fixed post comment helper for post with tests .

--- tests/src/helpers/helpers.coffee
+++ tests/src/helpers/helpers.coffee
@@ -172,6 +172,8 @@
       .click                    activitySelector + ' [testpath=CommentInputView]'
       .setValue                 activitySelector + ' [testpath=CommentInputView]', comment
       .waitForElementVisible    activitySelector + ' .comment-container .comment-input-wrapper', 20000
+      .click                    activitySelector + ' .has-markdown' # blur
+      .pause                    3000 # content preview
       .click                    activitySelector + ' .comment-container button[testpath=post-activity-button]'

     if shouldAssert


Tests: Helpers: fixed changeName helper.

--- tests/src/helpers/helpers.coffee
+++ tests/src/helpers/helpers.coffee
@@ -428,6 +428,7 @@
       .waitForElementVisible   saveButtonSelector, 20000
       .click                   saveButtonSelector
       .refresh()
+      .pause                   5000 #required
       .waitForElementVisible   inputSelector, 20000
       .getValue                inputSelector, (result) ->
         assert.equal           result.value, newName


test for #1416: don't omit one 'new' when compiling 'new new'

--- test/function_invocation.coffee
+++ test/function_invocation.coffee
@@ -491,3 +491,7 @@
   fn = -> (f) -> f()
   nonce = {}
   eq nonce, (fn() -> nonce)
+
+test "#1416: don't omit one 'new' when compiling 'new new'", ->
+  obj = new new Function "this.foo = 3"
+  eq obj.foo, 3


test is self-reliant thanks to fixtures - removing erroneous comment

--- test/integration/dataset.coffee
+++ test/integration/dataset.coffee
@@ -15,7 +15,7 @@

   before (done) ->
     wd40.fill '#username', 'ehg', ->
-      wd40.fill '#password', 'testing', -> wd40.click '#login', done # This test relies on a Cheese dataset created in api.coffee!!
+      wd40.fill '#password', 'testing', -> wd40.click '#login', done
   context 'when I click on an Apricot dataset', ->
     before (done) ->
       # wait for tiles to fade in


Fixed bug with send --> env.send in refresh token endpoint

--- src/presentationLayer/auth/index.coffee
+++ src/presentationLayer/auth/index.coffee
@@ -25,7 +25,7 @@
 						if not provider.oauth2?.refresh
 							return next new env.utilities.check.Error "refresh token not supported for " + req.params.provider
 						oa = new env.utilities.oauth.oauth2(provider, keyset.parameters)
-						oa.refresh req.body.token, keyset, send(res,next)
+						oa.refresh req.body.token, keyset, env.send(res,next)

 		# iframe injection for IE
 		env.server.get env.config.base + '/auth/iframe', (req, res, next) ->


Adjust minutes and seconds counters to have a zero on the left when they are less than 10.

--- assets/javascripts/timer.coffee
+++ assets/javascripts/timer.coffee
@@ -27,6 +27,9 @@
   allTheTime -= minutes * 60
   seconds = parseInt(allTheTime)

+  minutes = '0' + minutes if minutes < 10
+  seconds = '0' + seconds if seconds < 10
+
   document.getElementById('seconds').innerHTML = seconds
   document.getElementById('minutes').innerHTML = minutes


Check if data is an object

Check if data is an object

I couldn't reproduce it locally, but I got this exception once. This
implementation will need to be rewritten after upgrading to newest
ember, so I'll leave it as it is for now.

--- assets/scripts/app/store.coffee
+++ assets/scripts/app/store.coffee
@@ -41,7 +41,7 @@
     recordCache = @get('recordCache')

     if clientId != undefined
-      if data = dataCache[clientId]
+      if (data = dataCache[clientId]) && (typeof data == 'object')
         for key, value of hash
           if ( descriptor = Object.getOwnPropertyDescriptor(data, key) ) && descriptor.set
             Ember.set(data, key, value)


Keep mass fixed, make particle radius dependent on velocity

--- js/particles.coffee
+++ js/particles.coffee
@@ -97,15 +97,14 @@
       @pos.y = Math.min(Math.max(@pos.y, 0), canvas.height)
       @vel.y = -@vel.y * 0.5

-    @mass = Math.min(Math.max(@vel.mag()/1, 0.1), 1)
-
   draw: (ctx) =>
     ctx.save()
     ctx.beginPath()

     ctx.translate(@pos.x, @pos.y)

-    ctx.ellipse(0, 0, 5 * @mass, 5 * @mass, 0, 0, 2*Math.PI, false)
+    velocity = Math.min(Math.max(@vel.mag(), .5), 5)
+    ctx.ellipse(0, 0, velocity, velocity, 0, 0, 2*Math.PI, false)
     ctx.fillStyle = @color
     ctx.fill()

@@ -119,7 +118,7 @@
     @canvas = @$canvas[0]
     @$canvas.mousemove(@update_mouse_pos)
     @$canvas.mousedown(@explode_particles)
-    @particles = @generate_particles(1)
+    @particles = @generate_particles(50)
     @tick = window.performance.now()

   generate_particles: (num) ->


fix typo

fix typo

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -390,13 +390,13 @@
     when 'forward' then ({position}) -> position.isGreaterThan(fromPoint)
     when 'backward' then ({position}) -> position.isLessThan(fromPoint)

-  for row in scanRows when tokenizeLine = getTokenizedLineForRow(editor, row)
+  for row in scanRows when tokenizedLine = getTokenizedLineForRow(editor, row)

     column = 0
     results = []

-    tokenIterator = tokenizeLine.getTokenIterator()
-    for tag in tokenizeLine.tags
+    tokenIterator = tokenizedLine.getTokenIterator()
+    for tag in tokenizedLine.tags
       tokenIterator.next()
       if tag > 0
         length = switch


:art: Make Views work with PanelView

:art: Make Views work with PanelView

--- lib/views.coffee
+++ lib/views.coffee
@@ -1,29 +1,29 @@
 Views = {
-  currentFile:(View) ->
+  currentFile:(Linter) ->
     Root = document.createElement 'div'
     Root.innerHTML = 'Current File'
     Root.classList.add 'linter-tab'
     Root.classList.add 'active'
     Root.addEventListener 'click', ->
-      View.BarProject.Root.classList.remove 'active'
+      Linter.View.BarProject.Root.classList.remove 'active'
       Root.classList.add 'active'
-      View.Type = 'file'
-      View.render()
+      Linter.Panel.Type = 'file'
+      Linter.View.render()
     Root.appendChild document.createTextNode ' '
     Child = document.createElement 'span'
     Child.classList.add 'badge-flexible'
     Child.textContent = '0'
     Root.appendChild Child
     {Root, Child}
-  wholeProject: (View) ->
+  wholeProject: (Linter) ->
     Root = document.createElement 'div'
     Root.innerHTML = 'Project'
     Root.classList.add 'linter-tab'
     Root.addEventListener 'click', ->
-      View.BarCurrent.Root.classList.remove 'active'
+      Linter.View.BarCurrent.Root.classList.remove 'active'
       Root.classList.add 'active'
-      View.Type = 'project'
-      View.render()
+      Linter.Panel.Type = 'project'
+      Linter.View.render()
     Root.appendChild document.createTextNode ' '
     Child = document.createElement 'span'
     Child.classList.add 'badge'


webapp: add link to explain what octave-workspace files are

--- src/smc-webapp/editor-data/generic.cjsx
+++ src/smc-webapp/editor-data/generic.cjsx
@@ -17,7 +17,10 @@
     fit  : 'You may be able to use this file from Python using the [fitparse](https://github.com/dtcooper/python-fitparse) library.'
     odt  : 'Please download this file to your computer and open it using [OpenOffice Writer](https://www.openoffice.org/product/writer.html).'
     sobj : 'You can load an sobj file into **SageMath** by typing `load("filename.sobj")`.'
-    'noext-octave-workspace' : 'This is a data file that contains the state of your Octave workspace.'
+    'noext-octave-workspace' : '''
+                               This is a data file that contains the state of your Octave workspace.
+                               Read more: [Saving-Data-on-Unexpected-Exits](https://www.gnu.org/software/octave/doc/v4.2.1/Saving-Data-on-Unexpected-Exits.html).
+                               '''
     'noext-a.out' : 'This is a binary executable, which you can run in a Terminal by typing ./a.out.'

 DataGeneric = rclass


Fix specs.

Fix specs.

--- src/spec/integration/import.spec.coffee
+++ src/spec/integration/import.spec.coffee
@@ -176,7 +176,7 @@
         expect(_.size result).toBe 1
         expect(result[0]).toBe '[row 2] Product updated.'
         @client.productProjections.staged(true).where("productType(id=\"#{@productType.id}\")").fetch()
-      .then (result) ->
+      .then (result) =>
         expect(_.size result.body.results).toBe 1
         p = result.body.results[0]
         expect(p.masterVariant.attributes[0]).toEqual {name: TEXT_ATTRIBUTE_NONE, value: 'bar'}
@@ -190,13 +190,13 @@
         expect(p.variants[0].attributes[2]).toEqual {name: LTEXT_ATTRIBUTE_COMBINATION_UNIQUE, value: {en: 'CU2'}}
         expect(p.variants[0].attributes[3]).toEqual {name: NUMBER_ATTRIBUTE_COMBINATION_UNIQUE, value: 10}
         expect(p.variants[0].attributes[4]).toEqual {name: ENUM_ATTRIBUTE_SAME_FOR_ALL, value: {key: 'enum2', label: 'Enum2'}}
-        expect(p.variants[0].attributes[5]).toEqual {name: REFERENCE_ATTRIBUTE_PRODUCT_TYPE_NONE, value: {id: productType.id, typeId: 'productType'}}
+        expect(p.variants[0].attributes[5]).toEqual {name: REFERENCE_ATTRIBUTE_PRODUCT_TYPE_NONE, value: {id: @productType.id, typeId: 'productType'}}
         expect(p.variants[1].attributes[0]).toEqual {name: TEXT_ATTRIBUTE_NONE, value: 'bar'}
         expect(p.variants[1].attributes[1]).toEqual {name: SET_ATTRIBUTE_TEXT_UNIQUE, value: ['cinque', 'sei']}
         expect(p.variants[1].attributes[2]).toEqual {name: LTEXT_ATTRIBUTE_COMBINATION_UNIQUE, value: {en: 'CU3'}}
         expect(p.variants[1].attributes[3]).toEqual {name: NUMBER_ATTRIBUTE_COMBINATION_UNIQUE, value: 10}
         expect(p.variants[1].attributes[4]).toEqual {name: ENUM_ATTRIBUTE_SAME_FOR_ALL, value: {key: 'enum2', label: 'Enum2'}}
-        expect(p.variants[1].attributes[5]).toEqual {name: REFERENCE_ATTRIBUTE_PRODUCT_TYPE_NONE, value: {id: productType.id, typeId: 'productType'}}
+        expect(p.variants[1].attributes[5]).toEqual {name: REFERENCE_ATTRIBUTE_PRODUCT_TYPE_NONE, value: {id: @productType.id, typeId: 'productType'}}
         done()
       .catch (err) -> done _.prettify(err)
       .done()
@@ -280,8 +280,8 @@
       .then (result) ->
         expect(_.size result.body.results).toBe 1
         p = result.body.results[0]
-        expect(p.masterVariant.attributes[0]).toEqual {name: SET_ATTRIBUTE_TEXT_UNIQUE, value: ['bar']}
-        expect(p.masterVariant.attributes[1]).toEqual {name: SET_ATTRIBUTE_ENUM_NONE, value: [{key: 'enum1', label: 'Enum1'}]}
+        expect(p.masterVariant.attributes[0]).toEqual {name: SET_ATTRIBUTE_ENUM_NONE, value: [{key: 'enum1', label: 'Enum1'}]}
+        expect(p.masterVariant.attributes[1]).toEqual {name: SET_ATTRIBUTE_TEXT_UNIQUE, value: ['bar']}
         expect(p.masterVariant.attributes[2]).toEqual {name: NUMBER_ATTRIBUTE_COMBINATION_UNIQUE, value: 100}
         done()
       .catch (err) -> done _.prettify(err)


Prevent dragndrop between calls of same organ

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -24,6 +24,11 @@
       return false

   initCallSelectionDragNDrops = () ->
+    onSameForm = (elem1, elem2) ->
+      elem1_form = elem1.parents('form:first').first()
+      elem2_form = elem2.parents('form:first').first()
+      elem1_form.attr('id') == elem2_form.attr('id')
+
     $('.call-for-application.open .member-card').draggable
       revert: 'invalid'

@@ -33,6 +38,9 @@
         $(@).append(ui.draggable.removeAttr('style'))
         $(@).find('.member-card:even').removeClass('no-margin')
         $(@).find('.member-card:odd').addClass('no-margin')
+      accept: (draggable) ->
+        return false unless draggable.hasClass 'member-card'
+        onSameForm $(@), draggable

     $('.call-for-application.open .member-card-empty').droppable
       activeClass: 'highlight-drop-area'
@@ -40,7 +48,8 @@
         return false unless draggable.hasClass 'member-card'
         return false if $(@).find('.member-card').length > 0
         return false if $(@).hasClass('no-deputy')
-        true
+        onSameForm $(@), draggable
+
       drop: (event, ui) ->
         $(@).prepend(ui.draggable)
         ui.draggable.position


Splitting long line of javascript

Splitting long line of javascript

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -549,7 +549,9 @@
     """)

     updateUsStatus = ($el, us, usStatusById) ->
-        $el.find(".us-status").text(usStatusById[us.status].name).css('color', usStatusById[us.status].color)
+        usStatusDom = $el.find(".us-status")
+        usStatusDom.text(usStatusById[us.status].name)
+        usStatusDom.css('color', usStatusById[us.status].color)

     link = ($scope, $el, $attrs) ->
         $ctrl = $el.controller()


Issue 3610: Team requirement mislabeled

Issue 3610: Team requirement mislabeled

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -1038,12 +1038,6 @@
             lines:
                 fillColor : "rgba(102,153,51,0.3)"
         })
-        team_increment_line = _.map(dataToDraw.milestones, (ml) -> -ml["team-increment"])
-        data.push({
-            data: _.zip(milestonesRange, team_increment_line)
-            lines:
-                fillColor : "rgba(153,51,51,0.3)"
-        })
         client_increment_line = _.map dataToDraw.milestones, (ml) ->
             -ml["team-increment"] - ml["client-increment"]
         data.push({
@@ -1051,7 +1045,12 @@
             lines:
                 fillColor : "rgba(255,51,51,0.3)"
         })
-
+        team_increment_line = _.map(dataToDraw.milestones, (ml) -> -ml["team-increment"])
+        data.push({
+            data: _.zip(milestonesRange, team_increment_line)
+            lines:
+                fillColor : "rgba(153,51,51,0.3)"
+        })
         colors = [
             "rgba(0,0,0,1)"
             "rgba(120,120,120,0.2)"
@@ -1107,10 +1106,10 @@
                         return $translate.instant("BACKLOG.CHART.REAL", ctx)
                     else if flotItem.seriesIndex == 3
                         ctx = {sprintName: dataToDraw.milestones[xval].name, value: Math.abs(yval)}
-                        return $translate.instant("BACKLOG.CHART.INCREMENT_TEAM", ctx)
+                        return $translate.instant("BACKLOG.CHART.INCREMENT_CLIENT", ctx)
                     else
                         ctx = {sprintName: dataToDraw.milestones[xval].name, value: Math.abs(yval)}
-                        return $translate.instant("BACKLOG.CHART.INCREMENT_CLIENT", ctx)
+                        return $translate.instant("BACKLOG.CHART.INCREMENT_TEAM", ctx)
             }
         }


Display direction as uppercase letter in road list.

--- views/roads.coffee
+++ views/roads.coffee
@@ -22,5 +22,5 @@
                                   'data-icon': "trphcm-#{direction}",
                                   href: "/roads/#{road.roadId}/#{direction}",
                                   ->
-                                    h1 "#{road.name} #{direction}"
+                                    h1 "#{road.name} #{direction[0].toUpperCase()}"
                                     p "#{road.cameraCount} Cameras"


test(engine): fix scheduler test in centos6

--- packages/engine/test/scheduler/push.coffee
+++ packages/engine/test/scheduler/push.coffee
@@ -1,7 +1,9 @@

 schedule = require '../../src/schedulers/native'
+{tags} = require '../test'

 describe 'scheduler', ->
+  return unless tags.api

   describe 'push return', ->


Make request path parsing more robust

Make request path parsing more robust

- Ignore query strings. This fixes static web apps that expect to be
  able to pass around data in query strings
- URI decode the path. This allows accessing files with reserved URI
  characters in them (particularily spaces)

--- request.coffee
+++ request.coffee
@@ -1,3 +1,5 @@
+url = require('url')
+
 # Captures the tree and repo path from a request path
 treePathRegexp = /^([^\/]+)\/(.+)$/

@@ -8,10 +10,13 @@
   @path = null

   constructor: (config, nodeReq) ->
+    # Ignore the query
+    fullPath = url.parse(nodeReq.url).pathname
+
     # Find the repo we're requesting
     for httpRoot, repo of config.repos
       # Don't do this in the regexp because escaping is annoying
-      if nodeReq.url.indexOf(httpRoot) == 0
+      if fullPath.indexOf(httpRoot) == 0
         @repo = repo
         @httpRoot = httpRoot
         break
@@ -21,7 +26,8 @@
       return

     # Parse the remaining URL components
-    treeAndPath = treePathRegexp.exec(nodeReq.url[(@httpRoot.length)..])
+    decodedPath = decodeURIComponent(fullPath[(@httpRoot.length)..])
+    treeAndPath = treePathRegexp.exec(decodedPath)

     [unused, @tree, @path] = treeAndPath if treeAndPath


Fix point import

Fix point import

--- cli/points2db.coffee
+++ cli/points2db.coffee
@@ -20,7 +20,7 @@
         lat: lat
         lng: lng

-Point.drop().then -> db.syncSchemas ->
+db.orm.query('SET FOREIGN_KEY_CHECKS = 0', {raw: true}).then -> Point.drop().then -> db.syncSchemas ->
     waitInc = 0
     for point in pointDatas.points
         # callback generator


Update atom packages

Update atom packages

--- public/.atom/packages.cson
+++ public/.atom/packages.cson
@@ -6,6 +6,7 @@
   "emmet"
   "file-type-icons"
   "git-time-machine"
+  "graphql-autocomplete"
   "hyperclick"
   "intentions"
   "js-hyperclick"
@@ -19,10 +20,10 @@
   "linter-write-good"
   "linter-xo"
   "linter"
-  "merge-conflicts"
   "minimap"
   "package-sync"
   "pigments"
+  "prettier-atom"
   "regex-railroad-diagram"
   "sort-lines"
   "svg-preview"


Remove trailing . or ; from url

Remove trailing . or ; from url

--- client/events.coffee
+++ client/events.coffee
@@ -252,6 +252,8 @@
       else
         nice = value
         url = "http://#{nice}"
+      # remove trailing . or ; from url
+      url = url.replace /(\.|;)$/, ""
       return "#{nice}"
     return new Handlebars.SafeString(d)


flattening admin events

flattening admin events

--- client/events.coffee
+++ client/events.coffee
@@ -74,7 +74,9 @@
   if Meteor.user()
     event_ids = Meteor.user().profile.event_queue or []
   else
-    event_ids = Meteor.users.find("profile.admin": true)?.profile?.events or []
+    event_ids = []
+    not_flat = Meteor.users.find("profile.admin": true).map (admin) -> admin?.profile?.events or []
+    event_ids.concat.apply(event_ids, not_flat)
   Events.find(_id: {$in: event_ids}).fetch()

 # user template


Added pause prompt

Added pause prompt

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -1,5 +1,6 @@
 Model = require './model'

+pauseTemplate = require '../views/templates/vex/requestPause'
 unpauseTemplate = require '../views/templates/vex/requestUnpause'
 runTemplate = require '../views/templates/vex/requestRun'
 removeTemplate = require '../views/templates/vex/requestRemove'
@@ -62,6 +63,13 @@
     ###
     promptX opens a dialog asking the user to confirm an action and then does it
     ###
+    pomptPause: (callback) =>
+        vex.dialog.confirm
+            message: pauseTemplate id: @get "id"
+            callback: (confirmed) =>
+                return unless confirmed
+                @pause().done callback
+
     promptUnpause: (callback) =>
         vex.dialog.confirm
             message: unpauseTemplate id: @get "id"


util: meta-bug fix: elementHasTag should always return a boolean

--- core/utils.coffee
+++ core/utils.coffee
@@ -26,7 +26,7 @@
     el?.classList.add "hidden"

   elementHasTag: (el, tagName) ->
-    !!el.tagName?.toLowerCase() is tagName.toLowerCase()
+    Boolean(el.tagName?.toLowerCase() is tagName.toLowerCase())

   elementIsVisible: (el) ->
     return false  if el.offsetWidth <= 0 or el.offsetHeight <= 0


gfm syntax highlighting now uses hljs

gfm syntax highlighting now uses hljs

--- core/utils.coffee
+++ core/utils.coffee
@@ -103,21 +103,11 @@
       gfm: true
       pedantic: false
       sanitize: true
-      highlight:(text)->
-        # log "highlight callback called"
-        # if hljs?
-        #   requirejs (['js/highlightjs/highlight.js']), ->
-        #     requirejs (["highlightjs/languages/javascript"]), ->
-        #       try
-        #         hljs.compileModes()
-        #         _text = hljs.highlightAuto text
-        #         log "hl",_text,text
-        #         return _text.value
-        #       catch err
-        #         log "Error applying highlightjs syntax", err
-        # else
-        #   log "hljs not found"
-          return text
+      highlight:(text,lang)->
+        if hljs.LANGUAGES[lang]?
+          hljs.highlight(lang,text).value
+        else
+          text

     text = Encoder.htmlDecode text


Only call save it method exists

Only call save it method exists

--- lib/autosave.coffee
+++ lib/autosave.coffee
@@ -6,13 +6,14 @@
     @migrateOldAutosaveConfig()

     rootView.on 'focusout', ".editor:not(.mini)", (event) =>
-      @autosave(event.targetView()?.getModel())
+      editSession = event.targetView()?.getModel()
+      @autosave(editSession)

-    rootView.on 'pane:before-item-destroyed', (event, item) =>
-      @autosave(item)
+    rootView.on 'pane:before-item-destroyed', (event, paneItem) =>
+      @autosave(paneItem)

-  autosave: (editSession) ->
-    editSession?.save() if config.get('autosave.enabled')
+  autosave: (paneItem) ->
+    paneItem?.save?() if config.get('autosave.enabled')

   migrateOldAutosaveConfig: ->
     enabled = config.get('core.autosave')


Grep for js files as well as coffee files.

--- lib/beantest.coffee
+++ lib/beantest.coffee
@@ -17,6 +17,19 @@
     console.log "[beantest] Change in file detected. Rerunning tests...\n"
     shouldRun = true

+# We don't want compiled .js tests to trigger another run.
+removeCompiled = (fileNames) ->
+  truncatedCache = {}
+  for fileName in fileNames
+    truncated = fileName.substring 0, fileName.lastIndexOf '.'
+    if truncated of truncatedCache
+      # keep coffee version
+      truncatedCache[truncated] = "#{truncated}.coffee"
+    else
+      # keep whatever we see
+      truncatedCache[truncated] = fileName
+  return (truncatedCache[fileName] for own fileName of truncatedCache)
+
 beantest = (persistent=true) ->
   if shouldRun and canRun
     exec "#{EXECUTE_WITH} #{RUN_PATH}", (error, stdout, stderr) ->
@@ -24,12 +37,13 @@
       canRun = true
     shouldRun = canRun = false
   return unless persistent
-  exec 'find . | grep "\\.coffee$"', (error, stdout, stderr) ->
+  exec "cd #{process.cwd()} && find . | grep '\\.\\(js\\|coffee\\)$'", (error, stdout, stderr) ->
     fileNames = stdout.split('\n')
+    fileNames = removeCompiled fileNames
     for fileName in fileNames
       if fileName not of seen
         seen[fileName] = true
         fs.watchFile fileName, { interval: REFRESH_RATE }, runTest
-    setTimeout beantest, REFRESH_RATE
+    setTimeout beantest, REFRESH_RATE if fileNames.length > 0

 @beantest = beantest


Question marks to or.

Question marks to or.

--- lib/boutique.coffee
+++ lib/boutique.coffee
@@ -3,8 +3,8 @@
 class Boutique

   constructor: (@format, options) ->
-    @skipOptional = options?.skipOptional ? false
-    @skipTemplated = options?.skipTemplated ? true
+    @skipOptional = options?.skipOptional or false
+    @skipTemplated = options?.skipTemplated or true

   # Traverses the AST tree and provides its complete representation.
   represent: (ast, cb) ->
@@ -36,7 +36,7 @@
     @handlePrimitive element.primitive

   handlePrimitive: ({value, type}) ->
-    type = type ? if Array.isArray value then 'object' else 'string'
+    type = type or (if Array.isArray value then 'object' else 'string')

     if type is 'object'
       @format.representObject @handleProperties value


Fix redirect after sample delete

Fix redirect after sample delete

--- web/scripts/app.coffee
+++ web/scripts/app.coffee
@@ -344,7 +344,7 @@
         @post '/samples/:sample/delete', ->
             @app.api.delete_sample @params.sample,
                 success: =>
-                    @redirect config.RESOURCES_PREFIX + '/sample'
+                    @redirect config.RESOURCES_PREFIX + '/samples'
                     @success "Deleted sample '#{@params.name}'"
                 error: (code, message) => @error message
             return


hmm rather set limit very high

hmm rather set limit very high

--- lib/eslogger.coffee
+++ lib/eslogger.coffee
@@ -150,7 +150,8 @@
         '@timestamp': {
           order: 'asc'
         }
-      }
+      },
+      size: 10000
     }
     json = JSON.stringify(query)
     # console.log json


Testing compilation

Testing compilation

--- lib/friebyrd.coffee
+++ lib/friebyrd.coffee
@@ -1,4 +1,11 @@
-root = this
-_ = root._ || require 'underscore'
+((root) ->
+  root = this
+  _ = root._ || require 'underscore'
+
+  fail = () -> []
+  succeed = (result) -> [result]
+
+)(this)

+


generate: Prettier multiline string

generate: Prettier multiline string

--- lib/generate.coffee
+++ lib/generate.coffee
@@ -168,7 +168,16 @@
     fs.writeFile outputBase + "_maps.h", declarec.generateStringMap("graph_nodeMap", def.nodeMap, extractId), (err) ->
       throw err  if err

-    fs.writeFile outputFile, cmdStreamToCDefinition(data, target) + "\n#define MICROFLO_EMBED_GRAPH" + "\n#include \"microflo.h\"" + "\n#include \"main.hpp\"" + "\n#include \"componentlib.hpp\"", (err) ->
+    includes = """
+
+    #define MICROFLO_EMBED_GRAPH 1
+    #include \"microflo.h\"
+    #include \"main.hpp\"
+    #include \"componentlib.hpp\"
+
+    """
+
+    fs.writeFile outputFile, cmdStreamToCDefinition(data, target) + includes, (err) ->
       throw err  if err

 module.exports =


Add Racket support

Add Racket support

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -231,6 +231,14 @@
       command: "Rscript"
       args: (context) -> [context.filepath]

+  Racket:
+    "Selection Based":
+      command: "racket"
+      args: (context) -> ['-e', context.getCode()]
+    "File Based":
+      command: "racket"
+      args: (context) -> [context.filepath]
+
   RSpec:
     "Selection Based":
       command: "ruby"


Revert "Fix CoffeeScript selection based run"

This reverts commit 4729bf6586f4da8b110844f756cc4369718b6b29.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -49,7 +49,7 @@
   CoffeeScript:
     "Selection Based":
       command: "coffee"
-      args: (context)  -> ['-e', '--cli', context.getCode()]
+      args: (context)  -> ['-e', context.getCode()]
     "File Based":
       command: "coffee"
       args: (context) -> [context.filepath]
@@ -57,7 +57,7 @@
   'CoffeeScript (Literate)':
     "Selection Based":
       command: "coffee"
-      args: (context)  -> ['-e', '--cli', context.getCode()]
+      args: (context)  -> ['-e', context.getCode()]
     "File Based":
       command: "coffee"
       args: (context) -> [context.filepath]


Removed selection-based parsing.

Removed selection-based parsing.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -124,9 +124,6 @@
       args: (filename) -> [filename]

   R:
-    "Selection Based":
-      command: "Rscript"
-      args: (code)  -> ['-e', code]
     "File Based":
       command: "Rscript"
       args: (filename) -> [filename]


:new: Implement get{All, ActiveFile}Messages

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -10,7 +10,7 @@
 class MessageRegistry
   constructor: (@linter)->
     @count = File: 0, Project: 0
-    @messages = new Map()
+    @messages = new Map() # Messages = Map<Linter, Array<messages>>
     @emitter = new Emitter
     @subscriptions = new CompositeDisposable
     @subscriptions.add atom.workspace.onDidChangeActivePaneItem =>
@@ -28,8 +28,17 @@
     @messages.delete(linter)
     @emitter.emit 'did-change', @messages

-  get: ->
-    return @messages
+  getAllMessages: ->
+    toReturn = []
+    @messages.forEach (messages) =>
+      toReturn = toReturn.concat(messages)
+    return toReturn
+
+  getActiveFileMessages: ->
+    toReturn = []
+    @messages.forEach (messages) =>
+      toReturn = toReturn.concat(messages.filter((entry) -> entry.currentFile))
+    return toReturn

   onDidChange: (callback) ->
     return @emitter.on 'did-change', callback


Fix redis environment variables

Fix redis environment variables

--- coffee/server.coffee
+++ coffee/server.coffee
@@ -5,9 +5,9 @@

 MongoClient = require('mongodb').MongoClient

-redisPort = process.env.DOTCLOUD_DATA_REDIS_PORT or 6379
-redisHost = process.env.DOTCLOUD_DATA_REDIS_HOST or 'localhost'
-redisPass = process.env.DOTCLOUD_DATA_REDIS_PASSWORD or null
+redisPort = process.env.DOTCLOUD_CACHE_REDIS_PORT or 6379
+redisHost = process.env.DOTCLOUD_CACHE_REDIS_HOST or 'localhost'
+redisPass = process.env.DOTCLOUD_CACHE_REDIS_PASSWORD or null

 redisClient = require('redis').createClient redisPort, redisHost, auth_pass: redisPass