Project home page links to all workflows with their display names

--- app/pages/project/home.cjsx
+++ app/pages/project/home.cjsx
@@ -1,25 +1,35 @@
-counterpart = require 'counterpart'
 React = require 'react'
 Markdown = require '../../components/markdown'
+HandlePropChanges = require '../../lib/handle-prop-changes'
+PromiseToSetState = require '../../lib/promise-to-set-state'
+PromiseRenderer = require '../../components/promise-renderer'
 {Link} = require 'react-router'
-Translate = require 'react-translate-component'
-
-counterpart.registerTranslations 'en',
-  project:
-    home:
-      getStarted: 'Get started'

 module.exports = React.createClass
   displayName: 'ProjectHomePage'

+  mixins: [HandlePropChanges, PromiseToSetState]
+
+  propChangeHandlers:
+    project: (project) ->
+      @promiseToSetState workflows: project.link 'workflows'
+
+  getInitialState: ->
+    workflows: []
+
   render: ->
+    linkParams =
+      owner: @props.owner.display_name
+      name: @props.project.display_name
+

         <Markdown className="description">{@props.project.description}</Markdown>

-        <Link to="project-classify" params={owner: @props.owner.display_name, name: @props.project.display_name} className="call-to-action">
-          <Translate content="project.home.getStarted" />
-        </Link>
+        {for workflow in @state.workflows
+          <Link to="project-classify" params={linkParams} query={workflow: workflow.id} key={workflow.id} className="call-to-action">
+            {workflow.display_name}
+          </Link>}


freePositions refactoring

freePositions refactoring

--- app/src/connect-four.coffee
+++ app/src/connect-four.coffee
@@ -43,12 +43,7 @@
   i for e, i in a[0...7] when e is _

 freePositions = (a) ->
-  res = []
-  for columnIndex in openColumns a
-    for rowIndex in [5..0]
-      index = rowIndex*7+columnIndex
-      res.push index if a[index] is _
-  res
+  i for e, i in a[0...42] when e is _

 play = (a, columnIndex, W) ->
   for rowIndex in [5..0]


Prevent divide by zero in pie chart calculation

--- modules/plot/main/coffee/Utils.coffee
+++ modules/plot/main/coffee/Utils.coffee
@@ -34,10 +34,14 @@
   else ''

 arcCurveMinimumRadius = (startRadians, endRadians, padding) ->
+  DELTA = 1e-4
   radians = endRadians - startRadians
   theta = if radians < Math.PI then radians / 2 else Math.PI - radians / 2

-  padding / 2 / Math.sin(theta)
+  if Math.abs(theta) < DELTA
+    return 0
+  else
+    return padding / 2 / Math.sin(theta)

 arcCurve = (x, y, innerRadius, outerRadius, startRadians, endRadians, padding, dontCurveCenter) ->


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -235,7 +235,7 @@
         path.enter().append("path")
             .attr("class", "pie")
             .attr("fill", (d, i) ->
-                return color(i)
+                return color i
             )
             .attr("d", arc)
         #path.transition()


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -464,7 +464,7 @@
         #    .attr("transform", "translate(" + width/2 + "," + height/2 + ")")

         pie = d3.layout.pie()
-            .sort(null)
+            #.sort(null)
             .value((d) -> d.value)

         arc = d3.svg.arc()


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -87,6 +87,7 @@
         .text(sum)
         .attr("filter","url(#dropshadow)")

+      labelr = 250
       arcs.append("svg:text").attr("transform", (d, i) ->
         #procent_val = Math.round(data[i].value/sum * 100)
         #d.innerRadius = (radiuso * (100-procent_val)/100) - radiuso/2 #45  #45=max text size/2


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -174,14 +174,6 @@

         $(@node).children("svg").remove();

-        pie = d3.layout.pie()
-            .sort(null)
-            .value((d) -> d.value)
-
-        arc = d3.svg.arc()
-          .outerRadius(radiuso)
-          .innerRadius(radiusi)
-
         svg = d3.select(@node).append("svg:svg")
             .attr("width", width)
             .attr("height", height)
@@ -192,6 +184,14 @@
         #    .attr("height", height)
         #    .append("g")
         #    .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
+
+        pie = d3.layout.pie()
+            .sort(null)
+            .value((d) -> d.value)
+
+        arc = d3.svg.arc()
+          .outerRadius(radiuso)
+          .innerRadius(radiusi)

         # end Build pie
         #console.log("update pie", data);


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -226,7 +226,7 @@
             .sort(null)
             #.value((d) -> d.value)

-        window.arc = d3.svg.arc()
+        arc = d3.svg.arc()
           .outerRadius(radiuso)
           .innerRadius(radiusi)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -21,7 +21,7 @@
     width = 400 #width
     height = 400 #height
     radiuso = 200 #outer radius
-    radiusi = 200 #inner radius
+    radiusi = 0 #inner radius

     color = d3.scale.ordinal()
       #.domain([1,10])


Added toggle back to the main menu

Added toggle back to the main menu

--- menus/atom-sharper.cson
+++ menus/atom-sharper.cson
@@ -22,6 +22,7 @@
     'submenu': [
       'label': 'OmniSharp'
       'submenu': [
+        { "label": "Toggle", "command": "omnisharp-atom:toggle" },
         { 'label': 'New Application', 'command': 'omnisharp-atom:new-application' }
       ]
     ]


Array#flatten

Array#flatten

--- test/array_extensions.coffee
+++ test/array_extensions.coffee
@@ -19,6 +19,15 @@
   ok a != b, "Original array is not the same array as the copied one"
   ok a.length == b.length, "Both arrays are the same size"
   ok a[0] == b[0] && a[1] == b[1] && a[2] == b[2], "The elements of the two arrays are equal"
+
+test "#flatten", ->
+  array = [[0,1], [2,3], [4,5]]
+
+  flattenedArray = array.flatten()
+
+  equals flattenedArray.length, 6, "Flattened array length should equal number of elements in sub-arrays"
+  equals flattenedArray.first(), 0, "First element should be first element in first sub-array"
+  equals flattenedArray.last(), 5, "Last element should be last element in last sub-array"

 test "#rand", ->
   array = [1,2,3]


Fix missing changes in expected.coffee

Fix missing changes in expected.coffee

--- test/fixtures/tasks/package/expected.coffee
+++ test/fixtures/tasks/package/expected.coffee
@@ -16,13 +16,13 @@
 #     class Foo extends Map
 #       get: (k) ->
 Generator = ->
-  `/* #{file[2]}<Map> line:5 */`
+  `/* #{file[2]}<Map> line:6 */`
   class Map
-    `/* #{file[2]}<Map.static> line:6 */`
+    `/* #{file[2]}<Map.static> line:7 */`
     @static: ->
-    `/* #{file[2]}<Map::constructor> line:7 */`
+    `/* #{file[2]}<Map::constructor> line:8 */`
     constructor: ->
-    `/* #{file[2]}<Map::get> line:8 */`
+    `/* #{file[2]}<Map::get> line:9 */`
     get: (k) ->
     `/* #{file[2]}<Map::set> line:10 */`
     set: (k,v) ->


Use notifications in the "splashscreen" controller

--- testApp/app/controllers/splashscreen.coffee
+++ testApp/app/controllers/splashscreen.coffee
@@ -22,7 +22,7 @@

   @testHide: ->
     steroids.splashscreen.hide {},
-      onSuccess: -> alert "hided"
+      onSuccess: -> notification "hided"
       onFailure: -> alert "failed to show"

   @testShowHide: ->


Framework: dnd fixes on KDView

Framework: dnd fixes on KDView

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -722,14 +722,25 @@
       if options.containment
         dragState.containment = {}
         {view} = options.containment
-        if 'string' is typeof view
-          dragState.containment.viewBounds = @[view].getBounds()
-        dragState.containment.viewBounds or= @parent.getBounds()
-        if 'number' is typeof options.containment.padding
-        then padding = options.containment.padding
-        else padding = 0
-        dragState.containment.padding = \
-            top: padding, right: padding, bottom: padding, left: padding
+
+        bounds = if 'string' is typeof view
+        then @[view].getBounds()
+        else if view instanceof KDView
+        then view.getBounds()
+        else @parent.getBounds()
+
+        dragState.containment.viewBounds = bounds
+
+        padding = top : 0, right : 0, bottom : 0, left : 0
+
+        oPad = options.containment.padding
+        if 'number' is typeof oPad
+        then v = oPad for own p, v of padding
+        else if 'object' is typeof oPad
+        then KD.utils.extend padding, oPad
+
+        dragState.containment.padding = padding
+

       # TODO: should move these lines
       dragState.handle      = options.handle


console.log a bit less

console.log a bit less

--- test/helpers/map_exec.coffee
+++ test/helpers/map_exec.coffee
@@ -7,8 +7,8 @@
   iterator = (item, cbk) ->
     item = "sh -c \"#{item}\""
     exec item, (err, stdout, stderr) ->
-      console.log "ran: #{item}"
-      console.log "got: #{stdout}"
+      # console.log "ran: #{item}"
+      # console.log "got: #{stdout}"
       if err
         console.log "STDERR: #{stderr}"
         console.log err


Remove .only from tests

Remove .only from tests

Change-type: patch
Signed-off-by: Thodoris Greasidis <5791bdc4541e79bd43d586e3c3eff1e39c16665f@balena.io>

--- tests/integration/models/device.spec.coffee
+++ tests/integration/models/device.spec.coffee
@@ -1551,7 +1551,7 @@

 			describe 'balena.models.device.getWithServiceDetails()', ->

-				it.only 'should retrieve the current service details', ->
+				it 'should retrieve the current service details', ->
 					balena.models.device.getWithServiceDetails(@device.id).then (deviceDetails) =>
 						m.chai.expect(deviceDetails).to.deep.match
 							device_name: @device.device_name


proper types in test

proper types in test

--- test/documentation.coffee
+++ test/documentation.coffee
@@ -17,6 +17,21 @@
 BOX = 'asotehusatoehus'

 describe 'Box SSH documentation', ->
+  describe '( GET / )', ->
+    server = null
+
+    before (done) ->
+      server = require 'serv'
+      done()
+
+    it 'documents how to SSH into a box', (done) ->
+      u = baseurl
+      request.get {url:u}, (err, resp, body) ->
+        resp.statusCode.should.equal 200
+        resp.body.should.include 'create'
+        resp.body.should.include 'API'
+        done()
+
   describe '( GET /<box_name> )', ->
     server = null

@@ -36,7 +51,7 @@
       u = baseurl + BOX
       request.get {url:u}, (err, resp, body) ->
         resp.statusCode.should.equal 200
-        resp.body.should.contain 'ssh'
-        resp.body.should.contain '@'
+        resp.body.should.include 'ssh'
+        resp.body.should.include '@'
         done()


white space change

white space change

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -209,7 +209,7 @@
     # In this case, user see modified icon(mark) on tab.
     # Explicitly setting modified start here prevent this
     @setModifiedState(false)
-
+
     if @editorElement.component?
       attachedPromise = Promise.resolve()
     else


guard in case editor is dead

guard in case editor is dead

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -289,8 +289,9 @@

   preview: ->
     @confirm(keepOpen: true).then ({editor, point}) =>
-      @setRowMarker(editor, point)
-      @focus()
+      if editor.isAlive()
+        @setRowMarker(editor, point)
+        @focus()

   isNormalItem: (item) ->
     item? and not item.skip


ignore node_modules in file search

ignore node_modules in file search

--- modules/atom/settings/config.cson
+++ modules/atom/settings/config.cson
@@ -16,6 +16,16 @@
       "welcome"
       "markdown-preview"
       "language-javascript"
+    ]
+    ignoredNames: [
+      ".git"
+      ".hg"
+      ".svn"
+      ".DS_Store"
+      "._*"
+      "Thumbs.db"
+      "desktop.ini"
+      "node_modules"
     ]
     telemetryConsent: "no"
   editor:


fix for padding on images

fix for padding on images

--- lib/assets/javascripts/directives.js.coffee
+++ lib/assets/javascripts/directives.js.coffee
@@ -265,7 +265,7 @@
       for image in scope.settings.images
         img = new Image()
         img.onload = ->
-          pimage = paper.image img.src, image.x or padding, image.y or padding, img.width, img.height
+          pimage = paper.image img.src, image.x+padding or padding, image.y+padding or padding, img.width, img.height
           scope.image_set.push(pimage)
           if scope.settings.editing
             pimage.drag (dx, dy) ->


[Fixes #22] Fixed time input changing to midnight on click.

--- src/directives/time.coffee
+++ src/directives/time.coffee
@@ -46,7 +46,7 @@
           markers: -> inputDOM.setSelectionRange 6, 8

         $scope.$watch "model", (value) ->
-          element.val value if value?
+          updateScopeTime() if value?

         $scope.reset = ->
           $scope.time = new Date()


Add explanation to browserCompatibleDocumentParser

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -167,6 +167,16 @@
     doc.close()
     doc

+  # Use createDocumentUsingParser if DOMParser is defined and natively
+  # supports 'text/html' parsing (Firefox 12+, IE 10)
+  #
+  # Use createDocumentUsingDOM if createDocumentUsingParser throws an exception
+  # due to unsupported type 'text/html' (Firefox < 12, Opera)
+  #
+  # Use createDocumentUsingWrite if:
+  #  - DOMParser isn't defined
+  #  - createDocumentUsingParser returns null due to unsupported type 'text/html' (Chrome, Safari)
+  #  - createDocumentUsingDOM doesn't create a valid HTML document (safeguarding against potential edge cases)
   try
     if window.DOMParser
       testDoc = createDocumentUsingParser '<html><body><p>test'


Picked a few changes from the 'unbreak annotator' project.

--- src/dom_text_mapper.coffee
+++ src/dom_text_mapper.coffee
@@ -195,7 +195,10 @@
     result

   # Return info for a given node in the DOM
-  getInfoForNode: (node) -> @getInfoForPath @getPathTo node
+  getInfoForNode: (node) ->
+    unless node?
+      throw new Error "Called getInfoForNode(node) with null node!"
+    @getInfoForPath @getPathTo node

   # Get the matching DOM elements for a given set of charRanges
   # (Calles getMappingsForCharRange for each element in the givenl ist)
@@ -384,6 +387,8 @@
   getPathTo: (node) ->
     xpath = '';
     while node != @rootNode
+      unless node?
+        throw new Error "Called getPathTo on a node which was not a descendant of @rootNode. " + @rootNode
       xpath = (@getPathSegment node) + '/' + xpath
       node = node.parentNode
     xpath = (if @rootNode.ownerDocument? then './' else '/') + xpath
@@ -612,8 +617,7 @@
   # Returns:
   #    the first character offset position in the content of this node's
   #    parent node that is not accounted for by this node
-  collectPositions: (node, path, parentContent = null,
-      parentIndex = 0, index = 0) ->
+  collectPositions: (node, path, parentContent = null, parentIndex = 0, index = 0) ->
 #    console.log "Scanning path " + path
 #    content = @getNodeContent node, false


Detach child views when detaching apps view

--- lib/assets/javascripts/views/apps.js.coffee
+++ lib/assets/javascripts/views/apps.js.coffee
@@ -4,6 +4,7 @@
   @view_name: 'apps'

   detach: =>
+    @detachChildViews()
     for cid in @collection.model_ids
       Marbles.Model.detach(cid)
     @collection.detach()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -39,7 +39,7 @@
       equals engine.closest(".enemy", player2.position()), enemy2

   @name closest
-
+  @methodOf Engine.selector
   @param {String} selector
   @param {Point} position
   ###


some more coverage

some more coverage

--- test/phabs_admin_test.coffee
+++ test/phabs_admin_test.coffee
@@ -661,6 +661,36 @@
           expect(hubotResponse())
             .to.eql 'Sorry, project1 not found.'

+    context 'and phabricator reports an error', ->
+      beforeEach ->
+        room.robot.brain.data.phabricator.projects = {
+          'Bug Report': { },
+          'project with phid': { phid: 'PHID-PROJ-1234567' },
+        }
+        room.robot.brain.data.phabricator.aliases = {
+          bugs: 'project with phid',
+          bug: 'project with phid'
+        }
+        do nock.disableNetConnect
+        nock(process.env.PHABRICATOR_URL)
+          .get('/api/project.query')
+          .query({
+            'names[0]': 'project1',
+            'api.token': 'xxx'
+          })
+          .reply(500, { message: 'Internal error' })
+
+      afterEach ->
+        room.robot.brain.data.phabricator = { }
+        nock.cleanAll()
+
+      context 'phad remove project1 from #dev', ->
+        hubot 'phad remove project1 from #dev'
+        it 'should reply with proper info', ->
+          expect(hubotResponse())
+            .to.eql 'http error 500'
+
+
     context 'and the feed exists', ->
       beforeEach ->
         room.robot.brain.data.phabricator.projects = {
@@ -801,8 +831,14 @@
           room.robot.brain.data.phabricator = { }
           nock.cleanAll()

-        context 'phad alias project with phid as pwp', ->
-          hubot 'phad alias project with phid as pwp', 'phuser_user'
+        context 'phad del project', ->
+          hubot 'phad del project', 'phuser_user'
           it 'warns the user that he has no permission to use that command', ->
             expect(hubotResponse())
               .to.eql "You don't have permission to do that."
+
+        context 'phad forget project', ->
+          hubot 'phad forget project', 'phuser_user'
+          it 'warns the user that he has no permission to use that command', ->
+            expect(hubotResponse())
+              .to.eql "You don't have permission to do that."


small thing

small thing

--- app/assets/javascripts/line_chart.js.coffee
+++ app/assets/javascripts/line_chart.js.coffee
@@ -50,7 +50,7 @@
     @set_default_boundaries() if not @boundaries
     @calc_ticks() if not @ticks
     @axisX = new Axis({domain: @domain, ticks: @ticks})
-    @axisY = new Axis({domain: @quality})
+    @axisY = new KAxis({domain: @quality})
     @display = new Display
       select: "#navigation"
       boundaries: @boundaries
@@ -175,10 +175,19 @@
       d3.svg.axis()
         .scale(@domain.domain)
         .ticks(@ticks)
-        .orient(@orientation);
+        .orient(@orientation)
+        .tickFormat(@k_format);

   value_at: (position) =>
     @domain.value_at(position)
+
+
+class @KAxis extends Axis
+  constructor: (options={}) ->
+    super(options)
+    @k_format = d3.format("s")
+    @axis.tickFormat(@k_format)
+


viewing angle changes when bearing changes...

but only if tilt has been input

--- app/assets/javascripts/pv_queries.js.coffee
+++ app/assets/javascripts/pv_queries.js.coffee
@@ -9,8 +9,8 @@
     bearing = this.value - 45
     centres_compass = -22
     #use compass div not panel div to apply each rotation in order to separate divs
-    input = this.id
-    $('#' + input).parent().find('.compass').css({
+    icon = $(this).parent().find('.icon')
+    $(this).parent().find('.compass').css({
       '-moz-transform':     'translateX(' + centres_compass + 'px)
                             translateY(' + centres_compass + 'px)
                             rotateZ(' + bearing + 'deg)',
@@ -25,6 +25,9 @@
                             rotateZ(' + bearing + 'deg)',
       'transition':         'transform 1s'
     })
+    #optimise viewing angle only after tilt has been added
+    if !$(this).next('.tilt_input').val()
+      optimise_viewing_angle(icon)

   $('.tilt_input').change ->
     tilt = this.value


Fixed bug #775: Now assigned_to isn't sensitive to acuted characters

--- app/coffee/modules/common/lightboxes.coffee
+++ app/coffee/modules/common/lightboxes.coffee
@@ -343,9 +343,20 @@
         selectedUser = null
         selectedItem = null

+        normalizeString = (string) ->
+            normalizedString = string
+            normalizedString = normalizedString.replace("Á", "A").replace("Ä", "A").replace("À", "A")
+            normalizedString = normalizedString.replace("É", "E").replace("Ë", "E").replace("È", "E")
+            normalizedString = normalizedString.replace("Í", "I").replace("Ï", "I").replace("Ì", "I")
+            normalizedString = normalizedString.replace("Ó", "O").replace("Ö", "O").replace("Ò", "O")
+            normalizedString = normalizedString.replace("Ú", "U").replace("Ü", "U").replace("Ù", "U")
+            return normalizedString
+
         filterUsers = (text, user) ->
             username = user.full_name_display.toUpperCase()
+            username = normalizeString(username)
             text = text.toUpperCase()
+            text = normalizeString(text)
             return _.contains(username, text)

         render = (selected, text) ->


fix(LMS-1436): fixed issue if user has just one section assigned

--- app/coffeescripts/bundles/discussion.coffee
+++ app/coffeescripts/bundles/discussion.coffee
@@ -67,7 +67,7 @@

   resetFilterEntryData = ->
     selectedSectionId = $('#section_selected').val()
-    sectionIds = (if selectedSectionId == "0" then ENV.DISCUSSION.SECTION_IDS else [parseInt(selectedSectionId)])
+    sectionIds = (if typeof selectedSectionId == 'undefined' or selectedSectionId == null or selectedSectionId == "0" then ENV.DISCUSSION.SECTION_IDS else [parseInt(selectedSectionId)])
     entryData = data.get 'entries'
     filterEntryData = _.filter(entryData, (e) -> checkSection(sectionIds, _.map(e.author.sections, (section) -> parseInt(section.id))))
     entries.options.per_page = filterEntryData.length


reload page if superadmin tries to go to a bucket they aren't an admin of

--- app/components/group-page/group-page.coffee
+++ app/components/group-page/group-page.coffee
@@ -9,7 +9,7 @@
   params:
     openMembersTab: null
     firstTimeSeeingGroup: null
-  controller: (CurrentUser, Error, LoadBar, $location, Records, $scope, $stateParams, UserCan) ->
+  controller: (CurrentUser, Error, LoadBar, $location, Records, $scope, $stateParams, UserCan, $window) ->
     LoadBar.start()

     if $stateParams.openMembersTab
@@ -27,6 +27,8 @@
           Records.buckets.fetchByGroupId(groupId).then ->
             LoadBar.stop()
           Records.contributions.fetchByGroupId(groupId)
+        else if CurrentUser().isSuperAdmin
+          $window.location.reload()
         else
           $scope.authorized = false
           LoadBar.stop()


Fixed: responses for connectors requests were not always JSON

--- app/controllers/connector_controller.coffee
+++ app/controllers/connector_controller.coffee
@@ -35,27 +35,27 @@
         path = "connectors/bank/#{params.name}/"
         client.post path, body, (err, res, resBody) ->
             if err
-                send 500
+                send error: err, 500
             else if not res?
-                send 500
+                send error: "Res not found", 500
             else if res.statusCode isnt 200
                 send resBody, res.statusCode
             else
                 send resBody
     else
-        send "Credentials are not sent.", 400
+        send error: "Credentials are not sent.", 400

 action 'bankHistory', ->
     if body.login? and body.password?
         path = "connectors/bank/#{params.name}/history/"
         client.post path, body, (err, res, resBody) ->
             if err
-                send 500
+                send error: err, 500
             else if not res?
-                send 500
+                send error: "Res not found", 500
             else if res.statusCode isnt 200
                 send resBody, res.statusCode
             else
                 send resBody
     else
-        send "Credentials are not sent.", 400
+        send error: "Credentials are not sent.", 400


Fixed deprecation test

Fixed deprecation test


How did that go missing?

How did that go missing?

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -55,6 +55,7 @@
   document.documentElement.replaceChild body, document.body

   currentState = window.history.state
+  triggerEvent 'page:change'

 reflectNewUrl = (url) ->


overthinking it, more improvements, code reduction

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -108,8 +108,6 @@
     link = extractLink event
     if link.nodeName is 'A' and !ignoreClick(event, link)
       document.removeEventListener 'click', handleAfterClick
-      document.removeEventListener 'click', handleClick, true
-      document.addEventListener 'click', handleBeforeClick,true
       document.addEventListener 'click', handleAfterClick

 handleAfterClick = (event) ->
@@ -118,9 +116,6 @@
       visit link.href
       event.preventDefault()

-handleBeforeClick = (event) ->
-  document.removeEventListener 'click', handleBeforeClick, true
-  document.addEventListener 'click', handleClick, true

 extractLink = (event) ->
   link = event.target


Make private property optional

Make private property optional

--- server/accounts.coffee
+++ server/accounts.coffee
@@ -3,7 +3,7 @@

 Meteor.startup ->
   unless Meteor.users.find().count()
-    userData = Meteor.settings.private.initial_user
+    userData = Meteor.settings.private?.initial_user
     if userData
       userData.profile = { name: 'Admin' }
       console.log "[ Creating initial user with email #{userData.email} ]"
@@ -13,4 +13,4 @@
         Roles.addUsersToRoles(newUserRecord._id, ['admin'])
     else
       console.warn '[ Meteor.settings.private.initial_user object \
-                          is required to create the initial user record ]'+                          is required to create the initial user record ]'


Add index for phone numbers and byCount aggregation

--- shared/db/schemas/message.coffee
+++ shared/db/schemas/message.coffee
@@ -13,6 +13,27 @@
   Attachments: String
 )

+MessageSchema.statics.byCount = (cb) ->
+    this.aggregate [
+        {
+            $group:
+                _id: '$Phone Number'
+                count: $sum: 1
+        }
+        {
+            $sort:
+                count: -1
+        }
+        {
+            $project:
+                'Phone Number': '$_id'
+                count: 1
+                _id: 0
+        }
+    ], cb
+
+MessageSchema.index({'Phone Number': 1})
+
 Message = mongoose.model('Message', MessageSchema)

 module.exports = Message


Go to slide on hashchange only if it is not the current already.

--- coffee/presentation.coffee
+++ coffee/presentation.coffee
@@ -27,7 +27,7 @@
   bindResize: ->
     $(window).resize(=>
       @resize()
-      @checkHash()
+      @go(@$current)
     )

   bindHashChange: ->
@@ -52,7 +52,8 @@
   checkHash: =>
     hash = window.location.hash
     slide = hash.substr(2)
-    @go(slide) if slide
+    $section = @find(slide)
+    @go($section) unless $section.is(@$current)

   highlight: ->
     hljs.initHighlightingOnLoad()


minor bugfix

minor bugfix

--- lib/alchemide/wrapper.coffee
+++ lib/alchemide/wrapper.coffee
@@ -51,7 +51,9 @@

 exports.getAutocompletion = (prefix, cb) ->
-  unless inp then exports.init(projectPaths)
+  unless inp
+    exports.init(projectPaths)
+    return
   if prefix.trim().length < 1
     cb()
     return
@@ -62,7 +64,9 @@
     cb({one, multi: multi.split(";").filter((a) -> a.trim())})

 exports.loadFile =          (path,   cb = (->)) ->
-  unless inp then exports.init(projectPaths)
+  unless inp
+    exports.init(projectPaths)
+    return
   unless /.ex$/.test(path)
     cb()
     return


Update default Python docs

Update default Python docs

2.7.6 is the system Python version:

ubuntu@box74:~$ /usr/bin/python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)

--- resources/assets/js/outer/versions.coffee
+++ resources/assets/js/outer/versions.coffee
@@ -106,8 +106,8 @@
   ant: "1.8.2"
   maven: "3.2.1"

-  default_python: "2.7.3"
-  python: "2.7.3"
+  default_python: "2.7.6"
+  python: "2.7.6"
   python_versions: [
      "2.6.8",
      "2.7",


the commit store now handles event versioning correctly

--- source/infrastructure/commit_publisher.coffee
+++ source/infrastructure/commit_publisher.coffee
@@ -29,7 +29,6 @@

   publishCommit: (commit) =>
     for event in commit.changes.events
-      event.version = commit.version
       @eventBus.publish event
     for command in commit.changes.commands
       @commandBus.send command


hide createIdentity from public api

hide createIdentity from public api

--- lib/auth/Controllable.coffee
+++ lib/auth/Controllable.coffee
@@ -15,12 +15,7 @@

             @logger = @_createLogger @ID

-            @identity = @createIdentity()
-
-            for k, fn of @
-                if typeof fn is 'function'
-                    fn.identity = @identity
-
+            @identity = @_createIdentity()

         ##
@@ -41,7 +36,7 @@
         ##
         ##
         ##
-        createIdentity: (id)->
+        _createIdentity: (id)->
             if @identity
                 id = @identity.id+'.'+id
             else


template

template

--- source/javascripts/pgtune.coffee
+++ source/javascripts/pgtune.coffee
@@ -228,4 +228,6 @@
       data_track_addressbar: true
     window.addthis_share =
       title: "PgTune"
-      description: "Tuning PostgreSQL config by your hardware"+      description: "Tuning PostgreSQL config by your hardware"
+      templates:
+        twitter: 'check out {{title}} on {{url}} (via @leopard_me)'


refactor code

refactor code

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -1177,7 +1177,10 @@

         jsCode += (requiresStr + code + '\n')

-    return $.html() + "#{scriptsStr}\n<script data-js-code>#{jsCode}</script>"
+    html = $.html()
+    html += "#{scriptsStr}\n" if scriptsStr
+    html += "<script data-js-code>#{jsCode}</script>" if jsCode
+    return html

   ##
   # {Function} callback (htmlContent)


debug

debug

--- spec/fast-activation-spec.coffee
+++ spec/fast-activation-spec.coffee
@@ -37,11 +37,15 @@
           oldPaths.forEach (p) ->
             savedCache[p] = require.cache[p]
             delete require.cache[p]
-          console.log getRequiredLibOrNodeModulePaths()
+          # console.log getRequiredLibOrNodeModulePaths()

         waitsForPromise ->
           atom.packages.activatePackage('vim-mode-plus').then (_pack) ->
             pack = _pack
+            {inspect} = require 'util'
+            paths = getRequiredLibOrNodeModulePaths()
+            console.log inspect(paths.map (p) -> p.replace(packPath, ''))
+            # console.log getRequiredLibOrNodeModulePaths()

         runs ->
           fn(pack)
@@ -62,8 +66,8 @@
         should = files.map((file) -> packPath + file)
         paths = getRequiredLibOrNodeModulePaths()
         {inspect} = require 'util'
-        p = (args...) -> console.log inspect(args...)
-        console.log inspect(paths.map (p) -> p.replace(packPath, ''))
+        # p = (args...) -> console.log inspect(args...)
+        # console.log inspect(paths.map (p) -> p.replace(packPath, ''))
         expect(paths).toEqual(should)

   describe "requrie as minimum num of file as possible on startup", ->
@@ -82,6 +86,11 @@
       shouldRequireFilesInOrdered.push('lib/developer.coffee')

     # * To reduce IO and compile-evaluation of js file on startup
+    it "just null require minimum set of files", ->
+      withCleanActivation ->
+        null
+        # ensureRequiredFiles(shouldRequireFilesInOrdered)
+
     it "require minimum set of files", ->
       withCleanActivation ->
         ensureRequiredFiles(shouldRequireFilesInOrdered)


Add test coverage for package-manager initialize paths

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -1,5 +1,6 @@
 path = require 'path'
 Package = require '../src/package'
+PackageManager = require '../src/package-manager'
 temp = require('temp').track()
 fs = require 'fs-plus'
 {Disposable} = require 'atom'
@@ -19,6 +20,22 @@
   afterEach ->
     try
       temp.cleanupSync()
+
+  describe "initialize", ->
+    it "adds regular package path", ->
+      packageManger = new PackageManager({})
+      configDirPath = path.join('~', 'someConfig')
+      packageManger.initialize({configDirPath})
+      expect(packageManger.packageDirPaths.length).toBe 1
+      expect(packageManger.packageDirPaths[0]).toBe path.join(configDirPath, 'packages')
+
+    it "adds regular package path and dev package path in dev mode", ->
+      packageManger = new PackageManager({})
+      configDirPath = path.join('~', 'someConfig')
+      packageManger.initialize({configDirPath, devMode: true})
+      expect(packageManger.packageDirPaths.length).toBe 2
+      expect(packageManger.packageDirPaths).toContain path.join(configDirPath, 'packages')
+      expect(packageManger.packageDirPaths).toContain path.join(configDirPath, 'dev', 'packages')

   describe "::getApmPath()", ->
     it "returns the path to the apm command", ->


Fix specs

Fix specs

--- spec/snippet-loading-spec.coffee
+++ spec/snippet-loading-spec.coffee
@@ -80,7 +80,7 @@

     runs ->
       # Warn about invalid-file, but don't even try to parse a hidden file
-      expect(console.warn.calls.length).toBe 1
+      expect(console.warn.calls.length).toBeGreaterThan 0
       expect(console.warn.mostRecentCall.args[0]).toMatch(/Error reading.*package-with-broken-snippets/)

   describe "::loadPackageSnippets(callback)", ->


:bug: Fix some specs

:bug: Fix some specs

--- spec/ui/bottom-panel-spec.coffee
+++ spec/ui/bottom-panel-spec.coffee
@@ -30,13 +30,13 @@
       bottomPanel.scope = 'Project'
       messages = [getMessage('Error'), getMessage('Warning')]
       bottomPanel.setMessages({added: messages, removed: []})
-      expect(bottomPanel.element.childNodes[0].childNodes.length).toBe(1)
-      expect(bottomPanel.element.childNodes[0].childNodes[0].childNodes.length).toBe(2)
+      expect(bottomPanel.messagesElement.childNodes.length).toBe(1)
+      expect(bottomPanel.messagesElement.childNodes[0].childNodes.length).toBe(2)
       bottomPanel.setMessages({added: [], removed: messages})
-      expect(bottomPanel.element.childNodes[0].childNodes.length).toBe(1)
-      expect(bottomPanel.element.childNodes[0].childNodes[0].childNodes.length).toBe(0)
+      expect(bottomPanel.messagesElement.childNodes.length).toBe(1)
+      expect(bottomPanel.messagesElement.childNodes[0].childNodes.length).toBe(0)
       bottomPanel.setMessages({added: messages, removed: []})
-      expect(bottomPanel.element.childNodes[0].childNodes[0].childNodes.length).toBe(2)
+      expect(bottomPanel.messagesElement.childNodes[0].childNodes.length).toBe(2)
       bottomPanel.removeMessages(messages)
-      expect(bottomPanel.element.childNodes[0].childNodes.length).toBe(1)
-      expect(bottomPanel.element.childNodes[0].childNodes[0].childNodes.length).toBe(0)
+      expect(bottomPanel.messagesElement.childNodes.length).toBe(1)
+      expect(bottomPanel.messagesElement.childNodes[0].childNodes.length).toBe(0)


Reset submit button after 3000 ms

Reset submit button after 3000 ms

--- main/static/src/script/common/util.coffee
+++ main/static/src/script/common/util.coffee
@@ -4,7 +4,9 @@

 window.init_loading_button = () ->
   $('body').on 'click', '.btn-loading', ->
-    $(this).button('loading')
+    th = $(this)
+    th.button('loading')
+    setTimeout (->th.button('reset')), 3000

 window.init_time = () ->


Add notifications since compilation is slow for 'linting'

--- lib/linter-purescript.coffee
+++ lib/linter-purescript.coffee
@@ -26,7 +26,7 @@

       options = { cwd: projDir, stream: "stderr" }

-      console.log "linter-purescript: Running lint"
+      atom.notifications.addInfo "linter: compiling PureScript"
       helpers.exec(command, args, options)
         .then (result) ->
           regex = '^[^\n]*(?<type>Error|Warning) at (?<file>[^\n]*) line (?<lineStart>[0-9]+), column (?<colStart>[0-9]+) - line (?<lineEnd>[0-9]+), column (?<colEnd>[0-9]+):(?<message>.*?)^[^\n]*See'
@@ -39,8 +39,7 @@
           XRegExp.forEach result, XRegExp(parseRegex, "sm"), (match) ->
             matches.push(mkResult(match))

-          console.log("Error count: " + matches.length)
-          console.log(JSON.stringify(matches))
+          atom.notifications.addSuccess "linter: compiled PureScript"

           resolve(matches)
         .then null, (err) ->


Set errbit environment

Set errbit environment

--- angular/core/services/exception_handler.coffee
+++ angular/core/services/exception_handler.coffee
@@ -9,6 +9,7 @@
     host:       AppConfig.errbit.url

   client.addFilter (notice) ->
+    notice.context.environment = AppConfig.environment
     notice unless notice.errors[0].type == ""

   (exception, cause) ->


openSync on the resultPane

openSync on the resultPane

--- lib/project-find-view.coffee
+++ lib/project-find-view.coffee
@@ -133,7 +133,7 @@
     @model.search(@findEditor.getText(), paths)

   showResultPane: ->
-    rootView.open(ResultsPaneView.URI)
+    rootView.openSync(ResultsPaneView.URI)

   clearMessages: ->
     @errorMessages.hide().empty()


user list: autoselect team when choosing a role

--- app/assets/javascripts/admin/views/user/team_role_modal_item_view.coffee
+++ app/assets/javascripts/admin/views/user/team_role_modal_item_view.coffee
@@ -9,8 +9,10 @@
   tagName : "div"
   className : "row-fluid"
   template : _.template("""
-

-      <input type="checkbox" value="<%= name %>"><%= name %></option>
+

+
+        <input type="checkbox" value="<%= name %>"><%= name %></option>
+

@@ -23,3 +25,16 @@

   """)
+
+  events :
+    "change @ui.roleSelect" : "selectionChanged"
+
+  ui :
+    "teamCheckbox" : "input[type=checkbox]"
+    "roleSelect" : "select"
+
+
+  selectionChanged : ->
+
+    @ui.teamCheckbox.prop("checked", true)
+


Better configuration

Better configuration

--- lib/resource-juggling.coffee
+++ lib/resource-juggling.coffee
@@ -1,4 +1,5 @@
 exports.contentNegotiator = (req, res, next) ->
+  return next() unless req.acceptableTypes
   for type in req.acceptableTypes
     if this.html and type is 'text/html'
       return this.html req, res, next
@@ -9,14 +10,16 @@
 exports.getResource = (options) ->
   throw 'No schema defined' unless options.schema
   throw 'No schema name defined' unless options.name
+  throw 'No schema model defined' unless options.model
   options.urlName ?= options.name
   options.where ?= 'id'
+  options.addPlaceholderForEmpty ?= false

   if options.collection
     collectionIsRelation = true
   else
     collectionIsRelation = false
-    options.collection ?= options.schema[options.name]
+    options.collection ?= options.model

   options.toJSON ?= (items, req) -> items

@@ -31,7 +34,7 @@

     constraints =
       where: constraints
-    options.schema[options.name].all constraints, (err, items) ->
+    options.model.all constraints, (err, items) ->
       items = [] unless items
       callback err, items

@@ -48,10 +51,11 @@
     index:
       html: (req, res) ->
         seek {}, (err, items) ->
-          unless items.length
+          if options.addPlaceholderForEmpty and items.length is 0
             blankItem = {}
-            for property, defs of options.schema.schema.definitions[options.name].properties
+            for property, defs of options.schema.definitions[options.name].properties
               blankItem[property] = ''
+            items.push blankItem

           res.render "#{options.urlName}/index",
             locals:


:fire: Remove some useless debug messages

--- lib/typechecker-error.coffee
+++ lib/typechecker-error.coffee
@@ -14,12 +14,12 @@
           @Element = null
         else
           @Element.off('mouseenter.atom-hack mouseleave.atom-hack');
-      return console.log("It aint my file Yo!") if ActiveFile isnt @File
+      return if ActiveFile isnt @File
       Marker = Editor.markBufferRange(@Range, {invalidate: 'never'})
       Main.TypeCheckerDecorations.push Editor.decorateMarker(Marker, {type: 'highlight', class: 'highlight-'+@Color})
       Main.TypeCheckerDecorations.push Editor.decorateMarker(Marker, {type: 'gutter', class: 'gutter-'+@Color})
       Main.TypeCheckerDecorations.push Editor.decorateMarker(Marker, {type: 'gutter', class: 'atom-hack-'+@Num})
-      return console.log("I aint supposed to be here") if @Line-1 >= RowEnd or @Line <= RowStart
+      return if @Line-1 >= RowEnd or @Line <= RowStart
       setTimeout =>
         @Element = $(EditorView.querySelector('atom-text-editor /deep/ .atom-hack-'+@Num))
         @Element.on 'mouseenter.atom-hack mouseleave.atom-hack',@onHover.bind(this)


Updated JS to unbind previous click action from each select

--- app/assets/javascripts/partials/_new.js.coffee
+++ app/assets/javascripts/partials/_new.js.coffee
@@ -40,6 +40,9 @@
       fit_modal_body($("#ajax-modal"))
   # Remove any current selected timeslot classes
   $(".timeslot_selected").removeClass("timeslot_selected")
+  # Unbind previous click element on every select
+  $("#ajax-modal").find(".modal-footer").find("button[type='submit']").addClass("disabled").unbind "click"
+  # Reset room info font weight each select as well
   $(clicked_el).closest("table").find("td.room_info").css("font-weight":"normal")
   # Make sure none of the clicked timeslots are unavailable
   unless $(clicked_el).closest("tr").find("td.timeslot_preferred.timeslot_unavailable").is("*")
@@ -60,6 +63,3 @@
     $(clicked_el).closest('tr').find('td:first-child input:radio[name="reservation[room_id]"]').prop('checked', true)
     # Trigger the change event defined above
     $(clicked_el).closest('tr').find('td:first-child input:radio[name="reservation[room_id]"]').trigger('change')
-  else
-    # Unbind click element
-    $("#ajax-modal").find(".modal-footer").find("button[type='submit']").addClass("disabled").unbind "click"


fix error with graph depth setting

fix error with graph depth setting

- it wasn't possible to see the graph if
  no depth setting was provided.

--- app/assets/javascripts/plugins/graph.js.coffee
+++ app/assets/javascripts/plugins/graph.js.coffee
@@ -6,8 +6,13 @@
 force_charge = -400
 colors = d3.scale.category10()

-uri = window.location.href
-graphs_uri = uri.replace(/\?[^/]+$/, "/graphs$&")
+graphs_uri = window.location.href
+re = /\?[^/]+$/
+if graphs_uri.search(re) != -1
+  graphs_uri = graphs_uri.replace(re, "/graphs$&")
+else
+  graphs_uri += "/graphs"
+
 $.get(graphs_uri, (data) ->
     drawGraph(data)
   , "json")


extract template name in graphs into function.

--- app/assets/javascripts/plugins/graph.js.coffee
+++ app/assets/javascripts/plugins/graph.js.coffee
@@ -49,6 +49,12 @@
 resetSelections = ->
   removeClass('g.node', 'selected')
   removeClass('path', 'selected')
+
+templateName = (edge_type, template_type) ->
+  if edge_type == 'LogicMapping'
+    "graphs/logic_mappings_#{template_type}"
+  else if edge_type == 'Link'
+    "graphs/links_#{template_type}"

 addSVGMarker = (svg_element, payload) ->
   svg_element.append("svg:defs").selectAll("marker")
@@ -171,10 +177,7 @@
         node_url: node_url
         node: node
         url: "#{node_url}/#{node.info.id}/"
-      if edge_type == 'LogicMapping'
-        template = 'graphs/logic_mappings_node'
-      else if edge_type == 'Link'
-        template = 'graphs/links_node'
+      template = templateName(edge_type, 'node')
       info_list.html(HandlebarsTemplates[template](payload)) if template
       $("div#d3_context").html(info_list)

@@ -185,10 +188,7 @@
       payload =
         edge_url: edge_url
         edge: edge
-      if edge_type == "LogicMapping"
-        template = 'graphs/logic_mappings_edge'
-      else if edge_type == "Link"
-        template = 'graphs/links_edge'
+      template = templateName(edge_type, 'edge')
       info_list.html(HandlebarsTemplates[template](payload)) if template
       $("div#d3_context").html(info_list)


Cosmetic fixes on taskboard lightboxes directives file.

--- app/coffee/modules/taskboard/lightboxes.coffee
+++ app/coffee/modules/taskboard/lightboxes.coffee
@@ -22,7 +22,7 @@
 taiga = @.taiga
 bindOnce = @.taiga.bindOnce

-CreateEditTaskDirective = ($repo, $model, $rs, $rootScope) ->
+CreateEditTaskDirective = ($repo, $model, $rs, $rootscope) ->
     link = ($scope, $el, attrs) ->
         isNew = true

@@ -82,7 +82,7 @@
             # FIXME: error handling?
             promise.then (data) ->
                 $el.addClass("hidden")
-                $rootScope.$broadcast(broadcastEvent, data)
+                $rootscope.$broadcast(broadcastEvent, data)

         $el.on "click", "label.blocked", (event) ->
             event.preventDefault()


added commands/alert

added commands/alert

--- app/controllers/api_commands_controller.coffee
+++ app/controllers/api_commands_controller.coffee
@@ -19,5 +19,5 @@
     @respond(json: 'ok')

   alert: ->
-    @send_command('bulkhead.alert()')
+    @send_command('bulkhead.play_sound("look_over_here")')
     @respond(json: 'ok')


Rename stack to ruleStack

Rename stack to ruleStack

--- src/app/text-mate-grammar.coffee
+++ src/app/text-mate-grammar.coffee
@@ -25,13 +25,13 @@
     for name, data of repository
       @repository[name] = new Rule(this, data)

-  getLineTokens: (line, stack=[@initialRule]) ->
-    stack = new Array(stack...) # clone stack
+  getLineTokens: (line, ruleStack=[@initialRule]) ->
+    ruleStack = new Array(ruleStack...) # clone ruleStack
     tokens = []
     position = 0

     loop
-      scopes = scopesFromStack(stack)
+      scopes = scopesFromStack(ruleStack)

       if line.length == 0
         tokens = [{value: "", scopes: scopes}]
@@ -39,7 +39,7 @@

       break if position == line.length

-      if match = _.last(stack).getNextTokens(stack, line, position)
+      if match = _.last(ruleStack).getNextTokens(ruleStack, line, position)
         { nextTokens, tokensStartPosition, tokensEndPosition } = match
         if position < tokensStartPosition # unmatched text before next tokens
           tokens.push
@@ -55,7 +55,7 @@
           scopes: scopes
         break

-    { tokens, stack }
+    { tokens, stack: ruleStack }

   ruleForInclude: (name) ->
     if name[0] == "#"


Validation: Added more tests.

Validation: Added more tests.

--- src/tests/validation/test_validation.coffee
+++ src/tests/validation/test_validation.coffee
@@ -80,6 +80,11 @@
       expect(spark.validation.isEmail('acetfatih.com')).toBeFalsy()

+    it 'should return if given value is not string', ->
+      expect(spark.validation.isEmail(123)).toBeFalsy()
+      expect(spark.validation.isEmail(yes)).toBeFalsy()
+
+
   describe 'hasLength', ->

@@ -140,6 +145,10 @@

     it 'should test regexes for the given value', ->
-
       expect(spark.validation.regex('fatih', /\w+/)).toBeTruthy()
       expect(spark.validation.regex(1212311, /\d+/)).toBeTruthy()
+      expect(spark.validation.regex('fatih', /\d+/)).toBeFalsy()
+
+
+    it 'should return false if given regex is not a RegExp instance', ->
+      expect(spark.validation.regex('fatih', {})).toBeFalsy()


Rename CompositeCursor.modifyCursors to moveCursors

--- src/atom/composite-cursor.coffee
+++ src/atom/composite-cursor.coffee
@@ -32,57 +32,57 @@
   removeCursor: (cursor) ->
     _.remove(@cursors, cursor)

-  setScreenPosition: (screenPosition) ->
-    @modifyCursors (cursor) -> cursor.setScreenPosition(screenPosition)
-
-  setBufferPosition: (bufferPosition) ->
-    @modifyCursors (cursor) -> cursor.setBufferPosition(bufferPosition)
-
-  refreshScreenPosition: ->
-    @modifyCursors (cursor) -> cursor.refreshScreenPosition()
-
-  modifyCursors: (fn) ->
+  moveCursors: (fn) ->
     fn(cursor) for cursor in @cursors
     @mergeCursors()

+  setScreenPosition: (screenPosition) ->
+    @moveCursors (cursor) -> cursor.setScreenPosition(screenPosition)
+
+  setBufferPosition: (bufferPosition) ->
+    @moveCursors (cursor) -> cursor.setBufferPosition(bufferPosition)
+
+  refreshScreenPosition: ->
+    @moveCursors (cursor) -> cursor.refreshScreenPosition()
+
   moveLeft: ->
-    @modifyCursors (cursor) -> cursor.moveLeft()
+    @moveCursors (cursor) -> cursor.moveLeft()

   moveRight: ->
-    @modifyCursors (cursor) -> cursor.moveRight()
+    @moveCursors (cursor) -> cursor.moveRight()

   moveUp: ->
-    @modifyCursors (cursor) -> cursor.moveUp()
+    @moveCursors (cursor) -> cursor.moveUp()

   moveDown: ->
-    @modifyCursors (cursor) -> cursor.moveDown()
+    @moveCursors (cursor) -> cursor.moveDown()

   moveToNextWord: ->
-    @modifyCursors (cursor) -> cursor.moveToNextWord()
+    @moveCursors (cursor) -> cursor.moveToNextWord()

   moveToBeginningOfWord: ->
-    @modifyCursors (cursor) -> cursor.moveToBeginningOfWord()
+    @moveCursors (cursor) -> cursor.moveToBeginningOfWord()

   moveToEndOfWord: ->
-    @modifyCursors (cursor) -> cursor.moveToEndOfWord()
+    @moveCursors (cursor) -> cursor.moveToEndOfWord()

   moveToTop: ->
-    @modifyCursors (cursor) -> cursor.moveToTop()
+    @moveCursors (cursor) -> cursor.moveToTop()

   moveToBottom: ->
-    @modifyCursors (cursor) -> cursor.moveToBottom()
+    @moveCursors (cursor) -> cursor.moveToBottom()

   moveToBeginningOfLine: ->
-    @modifyCursors (cursor) -> cursor.moveToBeginningOfLine()
+    @moveCursors (cursor) -> cursor.moveToBeginningOfLine()

   moveToEndOfLine: ->
-    @modifyCursors (cursor) -> cursor.moveToEndOfLine()
+    @moveCursors (cursor) -> cursor.moveToEndOfLine()

   moveToFirstCharacterOfLine: ->
-    @modifyCursors (cursor) -> cursor.moveToFirstCharacterOfLine()
+    @moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine()

   handleBufferChange: (e) ->
-    @modifyCursors (cursor) -> cursor.handleBufferChange(e)
+    @moveCursors (cursor) -> cursor.handleBufferChange(e)

   mergeCursors: ->
     positions = []


resurrection: lazy load activities on "LazyLoadThresholdReached" event

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -78,7 +78,7 @@
   fetchCurrentGroup:(callback)-> callback @currentGroupSlug

   bindLazyLoad:->
-    @getView().once 'LazyLoadThresholdReached', @bound "continueLoadingTeasers"
+    @once 'LazyLoadThresholdReached', @bound "continueLoadingTeasers"
     @listController.once 'teasersLoaded', @bound "teasersLoaded"

   continueLoadingTeasers:->


Teambuilder: Clear textfield on successful import

--- client/app/js/views/teambuilder_view.coffee
+++ client/app/js/views/teambuilder_view.coffee
@@ -445,6 +445,7 @@
         teamString = $modal.find('.imported-team').val()
         pokemonJSON = PokeBattle.parseTeam(teamString)
         @addNewTeam(@jsonToTeam(pokemon: pokemonJSON))
+        $modal.find('.imported-team').val("")
         $modal.modal('hide')
         return false


slidingObjectView: add optimized %-container func

--- coffee/src/views/sliding_object_view.coffee
+++ coffee/src/views/sliding_object_view.coffee
@@ -78,3 +78,11 @@

   _mouseElement: ($el) =>
     $el ? @_slidingObject
+
+
+  # get mouse percentage offset from object's sliding area callibrated such that
+  # sending the object to this position will center it under the mouse
+  sliderOptimizedPercentContainerMouseOffset: =>
+    rawPercent = @_percentContainerMouseDisplacement offsetFromElement: true
+    objectPOC = @_mouseElementPercentOfContainer()
+    x: rawPercent.x - objectPOC.x / 2, y: rawPercent.y - objectPOC.y / 2


Support page lookup by model

Support page lookup by model

--- src/scripts/models/contents/collection.coffee
+++ src/scripts/models/contents/collection.coffee
@@ -44,7 +44,7 @@

     _getPageId: (id) ->
       for node in @get('contents').models
-        if node.get('id') is id or node.getVersionedId() is id
+        if node is id or node.get('id') is id or node.getVersionedId() is id
           return node
         else if node.isSection()
           result = node.getPage(id)


TAP5-2431: Ajax EventLinks should not cancel the click event

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/zone.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/zone.coffee
@@ -47,7 +47,7 @@

       return zone

-    dom.onDocument "click", "a[data-update-zone]", ->
+    dom.onDocument "click", "a[data-update-zone]", (event) ->

       element = @closest "[data-update-zone]"

@@ -59,7 +59,8 @@
       if zone
         zone.trigger events.zone.refresh,  url: element.attr "href"

-      return false
+      event.nativeEvent.preventDefault()
+      return

     dom.onDocument "submit", "form[data-update-zone]", ->

@@ -121,7 +122,7 @@
         success: (response) ->
           zone.trigger events.zone.update, content: response.json?.content

-    dom.onDocument "click", "a[data-async-trigger]", ->
+    dom.onDocument "click", "a[data-async-trigger]", (event)->
       link = @closest 'a[data-async-trigger]'

       link.addClass "ajax-update"
@@ -129,7 +130,9 @@
       ajax (link.attr "href"),
         complete: -> link.removeClass "ajax-update"

-      return false # cancel click event
+      event.nativeEvent.preventDefault()
+
+      return

     # Locates a zone element by its unique id attribute, and (deferred, to a later event loop cycle),
     # performs a standard refresh of the zone. This is primarily used by the core/ProgressiveDisplay component.
@@ -148,4 +151,4 @@
         zone.trigger events.zone.refresh, { url }

     # Most of this module is document-level event handlers, but there's also some exports:
-    return { deferredZoneUpdate, findZone }+    return { deferredZoneUpdate, findZone }


update tests

update tests

--- test/unit/coffee/CompileManagerTests.coffee
+++ test/unit/coffee/CompileManagerTests.coffee
@@ -202,8 +202,8 @@

 	describe "wordcount", ->
 		beforeEach ->
-			@CommandRunner.run = sinon.stub().callsArg(5)
-			@fs.readFileSync = sinon.stub().returns @stdout = "Encoding: ascii\nWords in text: 2"
+			@CommandRunner.run = sinon.stub().callsArg(6)
+			@fs.readFile = sinon.stub().callsArgWith(2, null, @stdout = "Encoding: ascii\nWords in text: 2")
 			@callback  = sinon.stub()

 			@project_id = "project-id-123"
@@ -217,10 +217,10 @@
 		it "should run the texcount command", ->
 			@directory = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}"
 			@file_path = "$COMPILE_DIR/#{@file_name}"
-			@command =[ "texcount", "-inc", @file_path, "-out=" + @file_path + ".wc"]
+			@command =[ "texcount", "-nocol", "-inc", @file_path, "-out=" + @file_path + ".wc"]

 			@CommandRunner.run
-				.calledWith("#{@project_id}-#{@user_id}", @command, @directory, @image, @timeout)
+				.calledWith("#{@project_id}-#{@user_id}", @command, @directory, @image, @timeout, {})
 				.should.equal true

 		it "should call the callback with the parsed output", ->
@@ -234,5 +234,7 @@
 					elements: 0
 					mathInline: 0
 					mathDisplay: 0
+					errors: 0
+					messages: ""
 				})
 				.should.equal true


Fixed legacy sinon.stub return.

Fixed legacy sinon.stub return.

--- test/unit/oidc/sendVerificationEmail.coffee
+++ test/unit/oidc/sendVerificationEmail.coffee
@@ -23,8 +23,7 @@
 describe 'Send Verification Email', ->

   before ->
-    sinon.stub mailer, 'getMailer', ->
-      fakeMailer
+    sinon.stub(mailer, 'getMailer').returns(fakeMailer)

   after ->
     mailer.getMailer.restore()


Switch to explicit call instead of inside the loop

--- generic/Gruntfile.coffee
+++ generic/Gruntfile.coffee
@@ -12,10 +12,7 @@
         files: "{{ app_name }}/**/*.s?ss"
         tasks: ["sass", ]

-  TASKS = [
-    "grunt-contrib-sass",
-    "grunt-contrib-watch"
-  ]
-  [grunt.loadNpmTasks a for a in TASKS]
+  grunt.loadNpmTasks "grunt-contrib-sass"
+  grunt.loadNpmTasks "grunt-contrib-watch"

   grunt.registerTask "default", ["watch", ]


ComputeProvider: fix indentation (cosmetic)

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -81,35 +81,35 @@

   , (client, options, callback)->

-      { provider, stack, label, provisioners } = options
-      { r: { group, user, account } } = client
-
-      provider.create client, options, (err, machineData)->
-
+    { provider, stack, label, provisioners } = options
+    { r: { group, user, account } } = client
+
+    provider.create client, options, (err, machineData)->
+
+      return callback err  if err
+
+      { meta, postCreateOptions, credential } = machineData
+
+      label ?= machineData.label
+
+      JMachine.create {
+        provider : provider.slug
+        label, meta, group, user
+        credential, provisioners
+      }, (err, machine)->
+
+        # TODO if any error occurs here which means user paid for
+        # not created vm ~ GG
         return callback err  if err

-        { meta, postCreateOptions, credential } = machineData
-
-        label ?= machineData.label
-
-        JMachine.create {
-          provider : provider.slug
-          label, meta, group, user
-          credential, provisioners
-        }, (err, machine)->
-
-          # TODO if any error occurs here which means user paid for
-          # not created vm ~ GG
+        provider.postCreate client, {
+          postCreateOptions, machine, meta, stack: stack._id
+        }, (err)->
+
           return callback err  if err

-          provider.postCreate client, {
-            postCreateOptions, machine, meta, stack: stack._id
-          }, (err)->
-
-            return callback err  if err
-
-            stack.appendTo machines: machine.getId(), (err)->
-              callback err, machine
+          stack.appendTo machines: machine.getId(), (err)->
+            callback err, machine

   @create$ = permit 'create machines', success: revive


Notify host when the editor is replaced

Notify host when the editor is replaced

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -31,6 +31,8 @@
     @createToolbarController()
     @createDocumentController()
     @updateLocationRange()
+
+    @delegate?.didSetEditor?(editor)
     @render()

   loadDocument: (document) ->


Possibility to use the $.rails.confirm

Possibility to use the $.rails.confirm

--- vendor/assets/javascripts/ajaxify_rails.js.coffee
+++ vendor/assets/javascripts/ajaxify_rails.js.coffee
@@ -59,6 +59,7 @@
         url: $this.attr('href')
         type: $this.data('method')
         confirm: $this.data('confirm')
+        element: $this
         scroll_to_top: set_scroll_to_top($this)

       false
@@ -80,6 +81,7 @@
         data: form_params
         type: $this.attr('method')
         confirm: $this.data('confirm')
+        element: $this
         scroll_to_top: set_scroll_to_top($this)

       false
@@ -116,7 +118,10 @@
       type = options.type or 'get'

     if options.confirm
-      return false unless confirm options.confirm
+      if $.rails != undefined
+        return false unless $.rails.confirm(options.confirm, options.element)
+      else
+        return false unless confirm(options.confirm)

     $(document).trigger 'ajaxify:before_load', [options, pop_state]


tests

tests

--- vendor/assets/javascripts/planning_view.js.coffee
+++ vendor/assets/javascripts/planning_view.js.coffee
@@ -72,7 +72,7 @@
 #
 #
 #  update header's position
-window.planning_view_func_update_header = (e) ->
+planning_view_func_update_header = (e) ->
   y = $(window).scrollTop()
   if y > window.planning_view_global_y_limit
     if !$('.planning-view__header').hasClass 'planning-view__header--fixed'
@@ -84,7 +84,7 @@
       $('.planning-view__body').removeClass 'planning-view__body--alone'

 #  manage keyboard shortcuts
-window.planning_view_func_shortcuts = (e) ->
+planning_view_func_shortcuts = (e) ->
   unless window.planning_view_global_ctrl_locker
     if e.keyCode == 37
       #move left
@@ -96,12 +96,11 @@
       planning_view_func_move_x -10

 #  unlock keyboard shortcuts
-window.planning_view_func_shortcuts_unlock = () ->
+planning_view_func_shortcuts_unlock = () ->
   window.planning_view_global_ctrl_locker = false

 #  access properties of the wrapper's css rule
-window.planning_view_func_initialize_wrapper_css_rule = () ->
-  selector = '#planning-view__container .planning-view__row-wrapper'
+planning_view_func_find_rule = (selector) ->
   for i in document.styleSheets
     rules = i[if document.all then 'rules' else 'cssRules']
     if rules
@@ -109,6 +108,11 @@
         if r.selectorText == selector
           window.planning_view_wrapper_css_rule = r.style
           break
+
+
+planning_view_func_initialize_wrapper_css_rule = () ->
+  selector = '#planning-view__container .planning-view__row-wrapper'
+  window.planning_view_wrapper_css_rule = planning_view_func_find_rule(selector)
   if window.planning_view_wrapper_css_rule == undefined
     css = "#{selector} { margin-left: 0%; #{style}: #{value} }"
     head = document.head || document.getElementsByTagName('head')[0]
@@ -120,15 +124,7 @@
     else
       style_node.appendChild(document.createTextNode css)
     head.appendChild(style_node)
-
-    for i in document.styleSheets
-      rules = i[if document.all then 'rules' else 'cssRules']
-      if rules && rules.title.title 'planning-view__style'
-        for r in rules
-          if r.selectorText == selector
-            window.planning_view_wrapper_css_rule = r.style
-            break
-        break
+    window.planning_view_wrapper_css_rule = planning_view_func_find_rule(selector)

 window.planning_view_func_wrapper_property = (property, value = undefined) ->
   if value == undefined


MER-41: Product reference resolvers called.

--- src/coffee/product-import.coffee
+++ src/coffee/product-import.coffee
@@ -77,19 +77,29 @@
       if existingProduct?
         synced = @sync.buildActions(prodToProcess, existingProduct)
         if synced.shouldUpdate()
-          @client.productsToProcess.byId(synced.getUpdateId()).update(synced.getUpdatePayload())
+          @client.products.byId(synced.getUpdateId()).update(synced.getUpdatePayload())
         else
           Promise.resolve statusCode: 304
       else
-        @client.productsToProcess.create(prodToProcess)
+        @client.products.create(@_prepareNewProduct(prodToProcess))

     debug 'About to send %s requests', _.size(posts)
     Promise.all(posts)

   _prepareNewProduct: (product) ->
     Promise.all [
+      @_resolveProductTypeReference(product.productType?)
+      @_resolveProductCategories(product.categories?)
+      @_resolveTaxCategoryReference(product.taxCategory?)
+    ]
+    .spread(prodType, prodCats, taxCat) =>
+      if not prodType.isRejected
+        product.productType = prodType.value
+      if not prodCats.isRejected
+        product.categories = prodCats.value
+      if not taxCat.isRejected
+        product.taxCategory = taxCat.value

-    ]

   _resolveProductTypeReference: (productTypeRef) ->
     new Promise (resolve, reject) =>
@@ -105,7 +115,18 @@
           resolve(result.results[0])

-  _resolveCategoriesReference: (categoryRef) ->
+  _resolveProductCategories: (cats) ->
+    new Promise (resolve, reject) =>
+      if not cats?
+        reject("Product categories are undefined.")
+      else
+        Promise.all [
+          for cat in cats
+            @_resolveProductCategories(cat)
+        ].then(result) =>
+          resolve(result)
+
+  _resolveProductCategoryReference: (categoryRef) ->
     new Promise (resolve, reject) =>
       if not categoryRef?
         reject("Product category is undefined")
@@ -114,6 +135,7 @@
       else
         @client.categories.where("externalId=\"#{categoryRef.id}\"").fetch()
         .then(result) =>
+          # Todo: Handle multiple response, currently taking first response.
           @_cache.productCategory[categoryRef.id] = result.results[0]
           resolve(result.results[0])

@@ -127,6 +149,7 @@
       else
         @client.taxCategories.where("name=\"#{taxCategoryRef.id}\"").fetch()
         .then(result) =>
+          # Todo: Handle multiple response, currently taking first response.
           @_cache.taxCategory[taxCategoryRef.id] = result.results[0]
           resolve(result.results[0])


Comments tidy-up

Comments tidy-up

--- client/landing/app/Applications/Environments.kdapplication/AppView.coffee
+++ client/landing/app/Applications/Environments.kdapplication/AppView.coffee
@@ -11,40 +11,40 @@
     # Action Area for Domains
     @addSubView @actionArea = new KDView cssClass : 'action-area'

+    # Domain Creation form in actionArea
+    @actionArea.addSubView @domainCreateForm = new DomainCreationForm
+
+    # Domain Creation form connections
+    @domainCreateForm.on 'DomainCreationCancelled', => @actionArea.unsetClass 'in'
+    @domainCreateForm.on 'CloseClicked', => @actionArea.unsetClass 'in'
+
     # Main scene for DIA
     @addSubView @scene = new EnvironmentScene

+    # Domains Container
     @domainsContainer  = new EnvironmentDomainContainer
     @scene.addContainer @domainsContainer

+    # VMs / Machines Container
     @machinesContainer = new EnvironmentMachineContainer
     @scene.addContainer @machinesContainer, x: 300

+    # After Domains and Machines container load finished
+    # Call updateConnections to draw lines between corresponding objects
     @scene.whenItemsLoadedFor \
       [@domainsContainer, @machinesContainer], @bound 'updateConnections'

-    @actionArea.addSubView creationForm = new DomainCreationForm
+    @domainCreateForm.on 'DomainSaved', =>
+      @domainsContainer.once "DataLoaded", @bound 'updateConnections'
+      @domainsContainer.loadItems()

-    creationForm.on 'DomainCreationCancelled', =>
-      @actionArea.unsetClass 'in'
-      # @buttonsBar.unsetClass 'out'
-
-    creationForm.on 'DomainSaved', (domain)=>
-      @domainsListViewController.update()
-
-    @on 'DomainNameShouldFocus', ->
-      form         = creationForm.forms["Domain Address"]
-      {domainName} = form.inputs
-      domainName.setFocus()
-
-    creationForm.on 'CloseClicked', =>
-      @actionArea.unsetClass 'in'
-      # @buttonsBar.unsetClass 'out'
-
+    # Plus button on domainsContainer opens up the action area
     @domainsContainer.on 'PlusButtonClicked', =>
       @actionArea.setClass 'in'
-      @emit 'DomainNameShouldFocus'
+      @domainCreateForm.emit 'DomainNameShouldFocus'
+      # @domainsContainer.loadItems()

+    # Plus button on machinesContainer uses the vmController
     @machinesContainer.on 'PlusButtonClicked', =>
       KD.getSingleton('vmController').createNewVM()


Fix grammar detection

Fix grammar detection

`class AppDelegate` is RubyMotion file.

--- grammars/rubymotion.cson
+++ grammars/rubymotion.cson
@@ -2,7 +2,7 @@
   'rb'
   'Rakefile'
 ]
-'firstLineMatch': '^\\s*class(\\s+([.a-zA-Z0-9_:]+\\s*<\\s*)?(NS|UI)[.a-zA-Z0-9_:]+$)'
+'firstLineMatch': '^\\s*class\\s+(([.a-zA-Z0-9_:]+\\s*<\\s*)?(NS|UI|AppDelegate)[.a-zA-Z0-9_:]*$)'
 'name': 'RubyMotion'
 'patterns': [
   {


convert another Session variable to a ReactiveVar

--- client/RecentChanges.coffee
+++ client/RecentChanges.coffee
@@ -1,14 +1,14 @@
-Session.setDefault "recent_changes_n_days", 2
+recent_changes_n_days = new ReactiveVar(2)

 Template.RecentChanges.helpers
   last_n_days: ->
-    Session.get("recent_changes_n_days")
+    recent_changes_n_days.get()

 Template.RecentChanges.events
   "input, change": (e) ->
     e.preventDefault()
     n_days = $('#last_n_days_widget').val()
-    Session.set("recent_changes_n_days",n_days)
+    recent_changes_n_days.set(n_days)

 Template.ChangeList.helpers
   wrapped_changes: ->


Fix Grunt Jade task configuration

Fix Grunt Jade task configuration

--- grunt/config/jade.coffee
+++ grunt/config/jade.coffee
@@ -2,13 +2,15 @@
   return {
     options:
       client: true
+    dev:
       files:
         '<%= build %>/js/templates.js': ['views/templates/**/*.jade']
-    dev:
       options:
         data:
           debug: true
     dist:
+      files:
+        '<%= build %>/js/templates.js': ['views/templates/**/*.jade']
       options:
         data:
           debug: false


StatusBar: Handle Untitled.txt file in updateStatusBar method.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -957,9 +957,11 @@

     text = if component is 'editor'
       { cursor, file } = data
+      filePath = if file.isDummyFile() then file.name else file.path
+
       """

#{++cursor.row}:#{++cursor.column}

-
#{FSHelper.minimizePath file.path}

+
#{FSHelper.minimizePath filePath}

       """

     else if component is 'terminal' then "Terminal on #{data.machineName}"


Mark segments with paragraphs.

Mark segments with paragraphs.

--- client/lib/annotator.coffee
+++ client/lib/annotator.coffee
@@ -231,6 +231,9 @@
     # TODO: We could try to require that paragraph height is at least 1.5 of the line height
     # TODO: We could try to require that area covered by segments should be more than 0.95 of the bounding box, so very little empty space
     if Math.abs(boundingBox.left - sectionProperties.left) < SECTION_PARAGRAPH_DISTANCE_THRESHOLD * SCALE
+      boundingBox.indices = paragraphIndices
+      for i in paragraphIndices
+        page.textSegments[i].paragraph = page.paragraphs.length
       page.paragraphs.push boundingBox

   _processSection: (pageNumber, segmentsIndices) =>
@@ -471,7 +474,7 @@
       @_processSection pageNumber, segmentsIndices

     # For debugging
-    @_showParagraphs pageNumber
+    #@_showParagraphs pageNumber

     $canvas = $("#display-page-#{ pageNumber } canvas")


Improved comment.

Improved comment.

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

-    # To make life easy, we apply padding all around the text segment DOM element,
+    # To make code simpler, we apply padding all around the text segment DOM element,
     # but then we have to counteract text content position change by set negative
     # margin. With this, text content stays in place, but DOM element gets a
     # necessary padding.


Improving annotator API implementation.

Improving annotator API implementation.

--- client/lib/annotator.coffee
+++ client/lib/annotator.coffee
@@ -1,42 +1,41 @@
 class @Annotator
   constructor: (@_publication) ->
-    @_segments = []
+    @_pages = []

   setPage: (page) =>
-    # Initialize page
-    @_segments[page.pageNumber - 1] =
+    # Initialize the page
+    @_pages[page.pageNumber - 1] =
       viewport: @_publication._viewport
         page: page # Dummy page object
-      elements: []
+      textSegments: []

   setTextContent: (pageNumber, textContent) =>
-    @_segments[pageNumber - 1].textContent = textContent
+    @_pages[pageNumber - 1].textContent = textContent

   textLayer: (pageNumber) =>
+    page = @_pages[pageNumber - 1]
+
     beginLayout: =>
-      segments = @_segments[pageNumber - 1]
-      segments.textLayerDone = false
-      segments.textLayerCounter = 0
+      page.textSegmentsDone = false

     endLayout: =>
-      segments =  @_segments[pageNumber - 1]
-      segments.textLayerDone = true
+      page.textSegmentsDone = true

-      console.log @_segments[pageNumber - 1].elements
+      console.log page.textSegments

     appendText: (geom) =>
-      segments = @_segments[pageNumber - 1]
-      segments.elements.push(
-        PDFJS.pdfTextSegment segments.viewport.height, segments.textContent, segments.textLayerCounter, geom
+      page.textSegments.push(
+        PDFJS.pdfTextSegment page.viewport.height, page.textContent, page.textSegments.length, geom
       )
-      segments.textLayerCounter++

   imageLayer: (pageNumber) =>
+    page = @_pages[pageNumber - 1]
+
     beginLayout: =>
-      @_segments[pageNumber - 1].imageLayerDone = false
+      page.imageLayerDone = false

     endLayout: =>
-      @_segments[pageNumber - 1].imageLayerDone = true
+      page.imageLayerDone = true

     appendImage: (geom) =>
       #console.log pageNumber, "appendImage", geom


[Rails 5] revisit checkout/payment.js.coffee form submition halt as submit event was propogating and leaving button disabled

--- frontend/app/assets/javascripts/spree/frontend/checkout/payment.js.coffee
+++ frontend/app/assets/javascripts/spree/frontend/checkout/payment.js.coffee
@@ -61,6 +61,8 @@
           )

           coupon_status.removeClass();
+          coupon_applied = false
+
           $.ajax({
             async: false,
             method: "PUT",
@@ -68,14 +70,18 @@
             success: (data) =>
               coupon_code_field.val('')
               coupon_status.addClass("alert-success").html("Coupon code applied successfully.")
-              @submit()
-              return true
+              coupon_applied = true
             error: (xhr) ->
               handler = JSON.parse(xhr.responseText)
               coupon_status.addClass("alert-error").html(handler["error"])
-              Spree.enableSave()
-              event.preventDefault()
-              return false
           })

+          if (coupon_applied)
+            @submit()
+            return true
+          else
+            Spree.enableSave()
+            event.preventDefault()
+            return false
+
   Spree.onPayment()


Fix asset file process

Fix asset file process

--- src/firmware/firmrevision.coffee
+++ src/firmware/firmrevision.coffee
@@ -69,11 +69,11 @@
   @property("obsolete", get: -> @_obsolete)

   ###*
-  @property {string[]} assets
+  @property {string[]} assetNames
     List of asset files
   @readonly
   ###
-  @property("assets", get: -> (name for name, url of @_assets))
+  @property("assetNames", get: -> (name for name, url of @_assets))

   #--------------------------------------------------------------------------------
   # Private properties
@@ -99,10 +99,10 @@
   @return {boolean} return.PromiseValue
     true if cached
   ###
-  findCache: ->
+  checkCacheAvailability: ->
     return Promise.resolve(true) if @_cache
     key = @_key
-    return Preferences(key).then((values) =>
+    return Preferences.get(key).then((values) =>
       return !!values[key]
     )

@@ -126,10 +126,11 @@
     ).then((values) =>
       cache = values?[key]
       return if cache?
-      return @_assets.reduce(
+      return @assetNames.reduce(
         (promise, name) =>
           return promise.then(=>
-            return XhrPromise.getAsArrayBuffer(@_assets[path])
+            url = @_assets[name]
+            return XhrPromise.getAsArrayBuffer(url)
           ).then((xhr) =>
             zip.file(name, xhr.response, {compression: "DEFLATE"})
           )
@@ -139,7 +140,7 @@
       ).then((cache) =>
         @_cache = cache
         return Preferences.set({"#{key}": cache})
-      ) # return @assets.reduce().then()...
+      ) # return @assetNames.reduce().then()...
     ).then(=>
       return  # Last PromiseValue
     ) # return Promise.resolve().then()...
@@ -157,7 +158,7 @@
     @_lastModified  = parseInt(obj.lastModified)
     @_beta          = !!obj.beta
     @_obsolete      = !!obj.obsolete
-    @_assets        = obj.assets
+    @_assets        = obj.assets or {}
     return

   ###*
@@ -180,3 +181,4 @@
 I18n = require("util/i18n")
 XhrPromise = require("util/xhrpromise")
 JsZip = global.Libs.JsZip
+Preferences = require("app/preferences")


Fix bug

Fix bug

--- coffee/routes/routes.coffee
+++ coffee/routes/routes.coffee
@@ -2,14 +2,14 @@

 module.exports = (app) ->
   app.get '/', (request, response) ->
+    console.log "Got request for /"
     response.send "Upload something!"

   app.post '/submit', (request, response) ->
     req.pipe(req.busboy)
-    req.busboy.on('file', (fieldname, file, filename) ->
+    req.busboy.on 'file', (fieldname, file, filename) ->
       console.log "Got #{filename}"
       fileStream = fs.createWriteStream "./sounds/#{filename}"
       file.pipe fileStream
       fileStream.on 'close', ->
         console.log "Upload of #{filename} complete!"
-


util: indented functions within util namespace

--- coffee/src/util/util.coffee
+++ coffee/src/util/util.coffee
@@ -16,169 +16,169 @@

 acorn.util =

-assert: (condition, description) ->
-  throw new Error description if not condition
+  assert: (condition, description) ->
+    throw new Error description if not condition

-urlRegEx: (url) ->
-  ///^(https?:\/\/)?#{url}$///
+  urlRegEx: (url) ->
+    ///^(https?:\/\/)?#{url}$///

-isUrl: (url) ->
-  @urlRegEx().test url
+  isUrl: (url) ->
+    @urlRegEx().test url

-isPath: (path) ->
-  /^[A-Za-z\/.-_]+$/.test path
+  isPath: (path) ->
+    /^[A-Za-z\/.-_]+$/.test path

-# helpers to construct acorn urls TODO: delete these?
-url: ->
-  path = _.toArray(arguments).join '/'
-  "http://#{acorn.config.domain}/#{path}"
+  # helpers to construct acorn urls TODO: delete these?
+  url: ->
+    path = _.toArray(arguments).join '/'
+    "http://#{acorn.config.domain}/#{path}"

-apiUrl: ->
-  apiPath = "api/v#{acorn.config.api.version}".split '/'
-  @url.apply(@, apiPath.concat _.toArray arguments)
+  apiUrl: ->
+    apiPath = "api/v#{acorn.config.api.version}".split '/'
+    @url.apply(@, apiPath.concat _.toArray arguments)

-imgUrl: ->
-  @url.apply(@, ['img'].concat _.toArray arguments)
+  imgUrl: ->
+    @url.apply(@, ['img'].concat _.toArray arguments)

-# construct an <iframe> element, with `src` and `id`
-iframeOptions:
-  frameborder: 0
-  border: 0
-  width: '100%'
-  height: '100%'
-  allowFullScreen: 'true'
-  webkitAllowFullScreen: 'true'
-  mozallowfullscreen: 'true'
-  scrolling: 'no'
+  # construct an <iframe> element, with `src` and `id`
+  iframeOptions:
+    frameborder: 0
+    border: 0
+    width: '100%'
+    height: '100%'
+    allowFullScreen: 'true'
+    webkitAllowFullScreen: 'true'
+    mozallowfullscreen: 'true'
+    scrolling: 'no'

-# construct an <iframe> element, with `src` and `id`
-iframe: (src, id) ->
-  f = $ '<iframe>'
-  _.map @iframeOptions, (val, key) ->
-    f.attr key, val
-  f.attr 'src', src
-  f.attr 'id', id if id?
-  f
+  # construct an <iframe> element, with `src` and `id`
+  iframe: (src, id) ->
+    f = $ '<iframe>'
+    _.map @iframeOptions, (val, key) ->
+      f.attr key, val
+    f.attr 'src', src
+    f.attr 'id', id if id?
+    f

-# get the acorn variable in given <iframe> element
-acornInIframe: (iframe) ->
-  iframe = iframe.get 0 if iframe.jquery?
-  win = iframe.contentWindow ? iframe.contentDocument.defaultView
-  win.acorn
+  # get the acorn variable in given <iframe> element
+  acornInIframe: (iframe) ->
+    iframe = iframe.get 0 if iframe.jquery?
+    win = iframe.contentWindow ? iframe.contentDocument.defaultView
+    win.acorn

-# creates and returns a get/setter with a closured variable
-property: (defaultValue, validate) ->
-  storedValue = defaultValue
-  validate ?= (x) -> x
+  # creates and returns a get/setter with a closured variable
+  property: (defaultValue, validate) ->
+    storedValue = defaultValue
+    validate ?= (x) -> x

-  (value) ->
-    storedValue = validate value if value?
-    storedValue
+    (value) ->
+      storedValue = validate value if value?
+      storedValue

-# requests full screen with given elem
-fullscreen: (elem) ->
-  elem = elem[0] if elem.jquery?
-  if elem.requestFullscreen
-    elem.requestFullscreen()
-  else if elem.webkitRequestFullScreen
-    elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)
-  else if elem.mozRequestFullScreen
-    elem.mozRequestFullScreen()
+  # requests full screen with given elem
+  fullscreen: (elem) ->
+    elem = elem[0] if elem.jquery?
+    if elem.requestFullscreen
+      elem.requestFullscreen()
+    else if elem.webkitRequestFullScreen
+      elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)
+    else if elem.mozRequestFullScreen
+      elem.mozRequestFullScreen()

-# add acorn css
-appendCss: (srcs) ->
-  srcs ?= acorn.config.css
-  srcs = [srcs] unless _.isArray(srcs)
-  _.each srcs, (src) ->
-    unless $("link[rel='stylesheet'][href='#{src}']").length
-      css = $('<link>')
-      css.attr 'rel', 'stylesheet'
-      css.attr 'href', src
-      $('body').append css
+  # add acorn css
+  appendCss: (srcs) ->
+    srcs ?= acorn.config.css
+    srcs = [srcs] unless _.isArray(srcs)
+    _.each srcs, (src) ->
+      unless $("link[rel='stylesheet'][href='#{src}']").length
+        css = $('<link>')
+        css.attr 'rel', 'stylesheet'
+        css.attr 'href', src
+        $('body').append css

-# converts human-readable timeString to seconds and back
-# human-readable format is: [[hh:]mm:]ss[.SSS]
-Time: class Time
-  constructor: (time) ->
-    @time = @constructor.timestring_to_seconds time
+  # converts human-readable timeString to seconds and back
+  # human-readable format is: [[hh:]mm:]ss[.SSS]
+  Time: class Time
+    constructor: (time) ->
+      @time = @constructor.timestring_to_seconds time

-  seconds: => @time
-  timestring: => @constructor.seconds_to_timestring @time
+    seconds: => @time
+    timestring: => @constructor.seconds_to_timestring @time

-  @timestring_to_seconds: (timestring) =>
-    timestring = String(timestring ? 0)
+    @timestring_to_seconds: (timestring) =>
+      timestring = String(timestring ? 0)

-    # handle subsec [.SSS]
-    [rest, subsec] = timestring.split '.'
-    subsec = parseFloat "0.#{subsec ? '0'}"
+      # handle subsec [.SSS]
+      [rest, subsec] = timestring.split '.'
+      subsec = parseFloat "0.#{subsec ? '0'}"

-    # handle [[hh:]mm:]ss
-    rest = rest.split(':').reverse()
-    [sec, min, hrs] = _.map [0, 1, 2], (n) -> parseInt(rest[n], 10) or 0
+      # handle [[hh:]mm:]ss
+      rest = rest.split(':').reverse()
+      [sec, min, hrs] = _.map [0, 1, 2], (n) -> parseInt(rest[n], 10) or 0

-    # convert to seconds
-    (hrs * 60 * 60) + (min * 60) + sec + subsec
+      # convert to seconds
+      (hrs * 60 * 60) + (min * 60) + sec + subsec

-  @seconds_to_timestring: (seconds) =>
-    sec = parseInt seconds, 10
+    @seconds_to_timestring: (seconds) =>
+      sec = parseInt seconds, 10

-    hrs = parseInt sec / (60 * 60), 10
-    sec -= hrs * 60 * 60
+      hrs = parseInt sec / (60 * 60), 10
+      sec -= hrs * 60 * 60

-    min = parseInt sec / 60, 10
-    sec -= min * 60
+      min = parseInt sec / 60, 10
+      sec -= min * 60

-    subsec = seconds % 1
-    if subsec
-      subsec = Math.round(subsec * 1000) / 1000
-      subsec = String(subsec).substr 1, 4 # remove first 0
-      subsec = subsec.replace /0+$/, ''
+      subsec = seconds % 1
+      if subsec
+        subsec = Math.round(subsec * 1000) / 1000
+        subsec = String(subsec).substr 1, 4 # remove first 0
+        subsec = subsec.replace /0+$/, ''

-    hrs = if hrs == 0 then '' else "#{hrs}:"
-    pad = (n) -> if n < 10 then "0#{n}" else "#{n}"
+      hrs = if hrs == 0 then '' else "#{hrs}:"
+      pad = (n) -> if n < 10 then "0#{n}" else "#{n}"

-    "#{hrs}#{pad min}:#{pad sec}#{subsec or ''}"
+      "#{hrs}#{pad min}:#{pad sec}#{subsec or ''}"

-# Originally from StackOverflow
-# http://stackoverflow.com/questions/736513
+  # Originally from StackOverflow
+  # http://stackoverflow.com/questions/736513

-parseUrl: (url) ->
-  # simple `url` validation
-  # should extend to perform more comprehensive tests
-  ValueError 'url', 'should not be the empty string.' if url == ''
+  parseUrl: (url) ->
+    # simple `url` validation
+    # should extend to perform more comprehensive tests
+    ValueError 'url', 'should not be the empty string.' if url == ''

-  result = {}
+    result = {}

-  # trim out any whitespace
-  url = $.trim url
+    # trim out any whitespace
+    url = $.trim url

-  # if no protocol is found, prepend http
-  url = "http://#{url}" unless /:\/\//.test url
+    # if no protocol is found, prepend http
+    url = "http://#{url}" unless /:\/\//.test url

-  anchor = document.createElement 'a'
-  anchor.href = url
+    anchor = document.createElement 'a'
+    anchor.href = url

-  keys = 'protocol hostname host pathname port search hash href'
-  (result[key] = anchor[key]) for key in keys.split ' '
+    keys = 'protocol hostname host pathname port search hash href'
+    (result[key] = anchor[key]) for key in keys.split ' '

-  # port-fix for phantomjs
-  result.port = '' if result.port == '0'
+    # port-fix for phantomjs
+    result.port = '' if result.port == '0'

-  result.toString = -> result.href
-  result.resource = result.pathname + result.search
-  result.extension = result.pathname.split('.').pop()
+    result.toString = -> result.href
+    result.resource = result.pathname + result.search
+    result.extension = result.pathname.split('.').pop()

-  result.head = -> throw new Error('head not supported. Yet.')
+    result.head = -> throw new Error('head not supported. Yet.')

-  _.each result, (val, key) ->
-    if (not /_$/.test key) and (typeof(val) is 'string')
-      result[key + '_'] = val.toLowerCase()
+    _.each result, (val, key) ->
+      if (not /_$/.test key) and (typeof(val) is 'string')
+        result[key + '_'] = val.toLowerCase()

-  result
+    result

-# -- regular expressions
+  # -- regular expressions

-LINK_REGEX: /// ^
-  https?://[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]
-///
+  LINK_REGEX: /// ^
+    https?://[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]
+  ///


オプション削除

オプション削除

--- src/gulp-extension-change.coffee
+++ src/gulp-extension-change.coffee
@@ -3,32 +3,18 @@
 { assign, clone } = require 'lodash'
 fs = require 'fs'

-defOpts =
-  afterExtension: 'php'
-  copy: false
-
 module.exports = (opts) ->
-
-  { afterExtension, copy } = assign clone(defOpts), opts
-
   transform = (file, encode, callback) ->

     beforeFilePath = file.path
-    filePathArr = beforeFilePath.match /(^.+)(\..+$)/
-    afterFilePath = "#{filePathArr[1]}.#{afterExtension}"
+    filePathArr    = beforeFilePath.match /(^.+)(\..+$)/
+    afterFilePath  = "#{filePathArr[1]}.#{opts.afterExtension}"

-    file.path = afterFilePath
+    file.path     = afterFilePath
     file.contents = new Buffer file.contents, 'utf-8'

     @push file

-    unless copy
-      fs.unlink beforeFilePath, (err) ->
-        if err then console.log "successfully deleted #{beforeFilePath}"
-
     callback()

-  # flush = (callback) ->
-  #   callback()
-
-  return through.obj transform#, flush
+  return through.obj transform


Improve parameter handling for influential users

--- web/app/models/tweets.coffee
+++ web/app/models/tweets.coffee
@@ -42,14 +42,28 @@
 	WHERE left(t.text, 4)<>'RT @'
 	WITH u as user, SUM(t.retweet_count)/count(t) as tweets_retweet_mean, SUM(t.favorite_count)/count(t) as tweets_favorite_mean, tweets_retweet_mean_max, tweets_favorite_mean_max, followers_count_max, listed_count_max
 	RETURN user, tweets_retweet_mean, tweets_favorite_mean,
-	(({retweet_weight}*tweets_retweet_mean)/tweets_retweet_mean_max + ({favorites_weight}*tweets_favorite_mean)/tweets_favorite_mean_max + ({followers_weight}*user.followers_count)/followers_count_max + ({listed_weight}*user.listed_count)/listed_count_max) AS influence_factor
+	((1.0*{retweet_weight}*tweets_retweet_mean)/tweets_retweet_mean_max + (1.0*{favorites_weight}*tweets_favorite_mean)/tweets_favorite_mean_max + (1.0*{followers_weight}*user.followers_count)/followers_count_max + (1.0*{listed_weight}*user.listed_count)/listed_count_max) AS influence_factor
 	ORDER BY influence_factor
 	"""

 	query += ' DESC' unless sortOrder == 'ASC'
 	query += ' SKIP {skip} LIMIT {limit}'

-	neodb.query(query, {skip: parseInt(skip) || 0, limit: parseInt(limit) || 20, retweet_weight: parseFloat(retweetWeight) || 0.3, favorites_weight: parseFloat(favoritesWeight) || 0.3, followers_weight: parseFloat(followersWeight) || 0.3, listed_weight: parseFloat(listedWeight) || 0.3}, callback)
+	params = {
+		skip: parseInt(skip) || 0,
+		limit: parseInt(limit) || 20,
+		retweet_weight: parseFloat(retweetWeight),
+		favorites_weight: parseFloat(favoritesWeight),
+		followers_weight: parseFloat(followersWeight),
+		listed_weight: parseFloat(listedWeight)
+	}
+
+	params.retweet_weight = 0.3 if not params.retweet_weight?
+	params.favorites_weight = 0.3 if not params.favorites_weight?
+	params.followers_weight = 0.3 if not params.followers_weight?
+	params.listed_weight = 0.1 if not params.listed_weight?
+
+	neodb.query(query, params, callback)

 module.exports.getFocused = (sortOrder, skip, limit, callback) ->
 	query = """


added another csv parsing test

added another csv parsing test

--- dw.js/test/datasource-delimited-test.coffee
+++ dw.js/test/datasource-delimited-test.coffee
@@ -75,4 +75,23 @@
                 'was correctly parsed': (dataset, f) ->
                     assert.equal dataset.column(1).val(0), 14.3

+        'Another one':
+            topic: dw.datasource.delimited
+                csv: "ags\tlabel\tshort\tohne.2013.proz\n1001\tFlensburg, Kreisfreie Stadt\tFlensburg\t0.076\n1002\tKiel, Landeshauptstadt, Kreisfreie Stadt\tKiel\t0.077\n1003\tLübeck, Hansestadt, Kreisfreie Stadt\tLübeck\t0.086\n1004\tNeumünster, Kreisfreie Stadt\tNeumünster\t0.088\n1051\tDithmarschen, Landkreis\tDithmarschen\t0.086\n1053\tHerzogtum Lauenburg, Landkreis\tHerzogtum Lauenburg 0.086\n1054\tNordfriesland, Landkreis\tNordfriesland\t0.072\n1055\tOstholstein, Landkreis\tOstholstein 0.087\n1056\tPinneberg, Landkreis\tPinneberg\t0.065\n1057\tPlön, Landkreis\tPlön\t0.081\n1058\tRendsburg-Eckernförde, Landkreis\tRendsburg-Eckernförde\t0.081"
+
+            'when loaded as dataset':
+                topic: (src) ->
+                    src.dataset().done @callback
+                    return
+
+                'has four columns': (dataset, f) ->
+                    assert.equal dataset.numColumns(), 4
+
+                'has five rows': (dataset, f) ->
+                    assert.equal dataset.numRows(), 11
+
+                'has correct column types': (dataset, f) ->
+                    assert.deepEqual _.map(dataset.columns(), _types), ['number', 'text', 'text', 'number']
+
+
     .export module


fixed bug where settings from one RectangleElement render bled to the next

--- src/art/engine/elements/fillable_base.coffee
+++ src/art/engine/elements/fillable_base.coffee
@@ -51,6 +51,12 @@
     {_shadow, _colors} = @
     drawOptions.shadow = _shadow

+    drawOptions.colors = null
+    drawOptions.gradientRadius1 = null
+    drawOptions.gradientRadius2 = null
+    drawOptions.from = null
+    drawOptions.to = null
+
     if _colors
       {_from, _to, _gradientRadius, _currentSize} = @
       drawOptions.colors = _colors
@@ -64,7 +70,6 @@
           drawOptions.gradientRadius1 = r1 * gradientScale
           drawOptions.gradientRadius2 = r2 * gradientScale

-      else
         _to ||= defaultTo

       # I don't love this solution to scaling the gradient from/to, but it's acceptable for now.


broker: attempt to forward the this.event

--- core/KDEventEmitter.coffee
+++ core/KDEventEmitter.coffee
@@ -163,10 +163,10 @@
       return removeAllListeners nextNode, edges, it, i + 1

     if it? and (listeners = node[listenerKey])?
-
+
       node[listenerKey] = (listener for listener in listeners \
                                     when listener isnt it)
-
+
     else node[listenerKey] = []

     return


fix default thing again

fix default thing again

--- src/components/task-plan/builder/index.cjsx
+++ src/components/task-plan/builder/index.cjsx
@@ -117,8 +117,8 @@
     {taskingOpensAt, taskingDueAt}

   updateForCourse: ->
-    {id, courseId} = @props
-    TaskingActions.loadDefaults(id, CourseStore.get(courseId))
+    {courseId} = @props
+    TaskingActions.loadDefaults(courseId, CourseStore.get(courseId))
     @forceUpdate()

   componentWillMount: ->


improved conversion

improved conversion

--- src/lib/controllers/image_controller.coffee
+++ src/lib/controllers/image_controller.coffee
@@ -44,7 +44,8 @@
           return error_exit err
         if features.format not of allowed_types
           return error_exit {msg: 'Only \'png\' or \'jpg\' photos may be uploaded at this time.'}
-        im.convert [original_path, '-resize', '640', optimized_path], (err, stdout) ->
+        im.convert [original_path, '-resize', '640', '-background', 'white', '-flatten',
+                    optimized_path], (err, stdout) ->
           if err
             return error_exit err
           # Now make a thumbnail of it too


比特币充值

比特币充值

--- core/router/bitcoin.coffee
+++ core/router/bitcoin.coffee
@@ -3,7 +3,11 @@

 config = require '../config'
 plugin = require '../plugin'
+bitcoin = require '../bitcoin'
+{getParam} = require './middleware'

 module.exports = exports = express.Router()

 exports.get '/blockchain_callback', (req, res) ->
+  bitcoin.doCallback req.body, (result) ->
+    res.send result


Fire price update on quantity change and mouseleave

--- app/assets/javascripts/app/manage_orders.coffee
+++ app/assets/javascripts/app/manage_orders.coffee
@@ -44,7 +44,7 @@

   initPriceUpdating: ->
     self = this
-    @$element.find('[name^="order_detail[reservation]"]:not([name$=_display]),[name="order_detail[quantity]"],[name="order_detail[account_id]"]').change (evt) ->
+    @$element.find('[name^="order_detail[reservation]"]:not([name$=_display]),[name="order_detail[quantity]"],[name="order_detail[account_id]"]').bind "change mouseleave", (evt) ->
       self.updatePricing(evt) if this.value.match(/^\d+$/)

   updatePricing: (e) ->


Fix raw image paste from clipboard

Fix raw image paste from clipboard

This patch binds the textarea (markdown area) paste event to the
handlePaste() function (that was already present).

Furthermore the event processing is improved in the following way:

- The default paste event handler of the browser is only disabled if the
  browser fully supports clipboardData AND there realy is image data in
  the event object. In all other cases (no support or no image) the
  default handler processes the text paste.
- Some obsolete code was removed.
- The pasteText() function (which is somehow buggy because it places the
  cursor at the end of the text independantly from its position before the
  paste) is only used to place the image link after image data was pasted.

--- app/assets/javascripts/dropzone_input.js.coffee
+++ app/assets/javascripts/dropzone_input.js.coffee
@@ -13,6 +13,8 @@

     form_textarea = $(form).find("textarea.markdown-area")
     form_textarea.wrap "

"
+    form_textarea.bind 'paste', (event) =>
+      handlePaste(event)

     form_dropzone = $(form).find('.div-dropzone')
     form_dropzone.parent().addClass "div-dropzone-wrapper"
@@ -133,24 +135,17 @@
     formatLink = (str) ->
       "![" + str.alt + "](" + str.url + ")"

-    handlePaste = (e) ->
-      e.preventDefault()
-      my_event = e.originalEvent
-
-      if my_event.clipboardData and my_event.clipboardData.items
-        processItem(my_event)
-
-    processItem = (e) ->
-      image = isImage(e)
-      if image
-        filename = getFilename(e) or "image.png"
-        text = "{{" + filename + "}}"
-        pasteText(text)
-        uploadFile image.getAsFile(), filename
-
-      else
-        text = e.clipboardData.getData("text/plain")
-        pasteText(text)
+    handlePaste = (event) ->
+      pasteEvent = event.originalEvent
+      if pasteEvent.clipboardData and pasteEvent.clipboardData.items
+        image = isImage(pasteEvent)
+        if image
+          event.preventDefault()
+
+          filename = getFilename(pasteEvent) or "image.png"
+          text = "{{" + filename + "}}"
+          pasteText(text)
+          uploadFile image.getAsFile(), filename

     isImage = (data) ->
       i = 0


answer preview test adjust for new selector

--- test/components/question/answer.spec.coffee
+++ test/components/question/answer.spec.coffee
@@ -28,5 +28,5 @@
   it 'renders answer feedback based on props', ->

     Testing.renderComponent( Answer, props: @propsWithFeedback ).then ({dom}) ->
-      answers = _.pluck dom.querySelectorAll('.question-feedback'), 'textContent'
+      answers = _.pluck dom.querySelectorAll('.question-feedback-content'), 'textContent'
       expect(answers).to.deep.equal(['feedback yo'])


Open links in new window

Open links in new window

--- app/js/jail_iframe/classes/proxy_message.coffee
+++ app/js/jail_iframe/classes/proxy_message.coffee
@@ -3,8 +3,8 @@

       Factlink demo page

-
Get the extension to add discussions on every site
-
Or install Factlink on your own site
+
Get the extension to add discussions on every site

+
Or install Factlink on your own site

   """


Make all not successful http statuses silent.

Exceptions will be added later.

--- app/client/js/errorreporter.coffee
+++ app/client/js/errorreporter.coffee
@@ -65,7 +65,7 @@
   ###
   isWorthReporting_: (reason) ->
     isSilent =
-      reason == 404 ||
+      @isNotHttpSuccess_(reason) ||
       reason instanceof app.errors.SilentError ||
       reason instanceof goog.Promise.CancellationError
     return false if isSilent
@@ -81,9 +81,19 @@
     true

   ###*
+    @private
+  ###
+  isNotHttpSuccess_: (reason) ->
+    isSuccess =
+      goog.isNumber(reason) &&
+      goog.net.HttpStatus.isSuccess(reason)
+    !isSuccess
+
+  ###*
     Don't report already reported reason.
     @param {*} reason
     @return {boolean}
+    @private
   ###
   isAlreadyReported_: (reason) ->
     stringReason = String reason