Edit Hippo text

Edit Hippo text

Removed parentheses from scientific name.

--- app/lib/field-guide-content/hippopotamus.coffee
+++ app/lib/field-guide-content/hippopotamus.coffee
@@ -4,7 +4,7 @@
     The common hippopotamus has a grayish-brown, round, bulky body that is set on short, stubby legs. Each foot has four toes and although they are webbed, the toes splay enough to distribute the weight of the animal when it is on land. Their eyes and nostrils are located high on their heads, allowing them to see and breathe while primarily submerged in the water. They produce an oily red substance, which moistens the skin and protects them against the sun.
   '''

-  scientificName: '(Hippopotamus amphibius) '
+  scientificName: 'Hippopotamus amphibius'
   mainImage: 'assets/fieldguide-content/mammals/hippopotamus/hippo-feature.jpg'

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


Flips "Find Friends" cookie if user authenticates

--- apps/onboarding/scenes/find_friends/view.coffee
+++ apps/onboarding/scenes/find_friends/view.coffee
@@ -1,5 +1,6 @@
 { invoke } = require 'underscore'
 Backbone = require 'backbone'
+Cookies = require 'cookies-js'
 { API_URL } = require('sharify').data
 Authentication = require '../../../../models/authentication.coffee'
 FoundFriendsView = require '../../../../components/found_friends/view.coffee'
@@ -38,6 +39,10 @@

     @$('.js-found-friends').html view.render().$el

+    if @authentication.id?
+      # Hide "Find Friends" tip on profile
+      Cookies.set 'find_friends_message', true
+
   render: ->
     @$el.html template
       state: @state


Remove unused parents.coffee test fixture

--- bokehjs/test/test_common/fixtures/parent.coffee
+++ bokehjs/test/test_common/fixtures/parent.coffee
@@ -1,20 +0,0 @@
-_ = require "underscore"
-utils = require "../../utils"
-Collection = utils.require "common/collection"
-HasParent = utils.require "common/has_parent"
-
-class TestParent extends HasParent
-  type: 'TestParent'
-  parent_properties: ['testprop']
-
-  display_defaults: ->
-    _.extend {}, super(), {
-      testprop: 'defaulttestprop'
-    }
-
-class TestParentCollection extends Collection
-  model: TestParent
-
-module.exports =
-  Model: TestParent
-  Collection: new TestParentCollection()


Fix test using Math.PI.

Fix test using Math.PI.

--- bokehjs/test/test_common/test_properties.coffee
+++ bokehjs/test/test_common/test_properties.coffee
@@ -138,7 +138,7 @@

       it "should convert degrees to radians and flip sign", ->
         prop = new Properties.Angle({obj: generate_obj({a: 1, a_units: "deg"}), attr: 'a'})
-        expect(prop.transform [1, 2, 3]).to.be.deep.equal new Float64Array [-Math.pi/180.0, -2*Math.pi/180.0, -3*Math.pi/180.0]
+        expect(prop.transform [1, 2, 3]).to.be.deep.equal new Float64Array [-Math.PI/180.0, -2*Math.PI/180.0, -3*Math.PI/180.0]

       it "should pass radians with sign flipped", ->
         prop = new Properties.Angle({obj: generate_obj({a: 1, a_units: "rad"}), attr: 'a'})


added DrmCalendar class and methods

added DrmCalendar class and methods

--- drm-calendar.coffee
+++ drm-calendar.coffee
@@ -5,7 +5,291 @@

 ( ($) ->
     class window.DrmCalendar
-        constructor: (@calendar = $('.drm-calendar')) ->
+        constructor: (@calendarClass = 'drm-calendar', @daysPerWeek = 7) ->
+            self = @
+            self.today = new Date()
+            self.currentMonth = self.today.getMonth()
+            self.currentYear = self.today.getFullYear()
+            self.currentDay = @today.getDate()
+            self.calendarInnerClass = 'drm-calendar-inner'
+            self.calendar = $ ".#{self.calendarClass}"
+            self.calendarNav = $ '.drm-calendar-nav'
+            self.calendarSelect = $ '.drm-calendar-select'
+            self.calendarSelectButton = self.calendarSelect.find 'button[type=submit]'
+            self.classes =
+                weekend: 'drm-cal-weekend'
+                muted: 'drm-cal-muted'
+                holiday: 'drm-cal-holiday'
+                today: 'drm-cal-today'
+
+            self.months = [
+                'January'
+                'February'
+                'March'
+                'April'
+                'May'
+                'June'
+                'July'
+                'August'
+                'September'
+                'October'
+                'November'
+                'December']
+
+            self.days = [
+                'Sunday'
+                'Monday'
+                'Tuesday'
+                'Wednesday'
+                'Thursday'
+                'Friday'
+                'Saturday']
+
+            self.weekend = [
+                $.inArray('Sunday', self.days)
+                $.inArray('Saturday', self.days)
+            ]
+
+            self.staticHolidays =
+                january_1: "New Year's Day"
+                february_2: "Groudhog Day"
+                february_14: "Valentine's Day"
+                march_17: "St. Patrick's Day"
+                april_01: "April Fool's Day"
+                april_22: "Earth Day"
+                may_1: "May Day"
+                may_5: "Cinco De Mayo"
+                june_14: "Flag Day"
+                july_4: "Independence Day"
+                september_11: "Patroit Day"
+                october_31: "Halloween"
+                november_11: "Veteran's Day"
+                december_7: "Pearl Harbor Day"
+                december_23: "Festivus"
+                december_24: "Christmas Eve"
+                december_25: "Christmas"
+                december_31: "New Year's Eve"
+
+            self.variableHolidays =
+                january_monday_3: "Martin Luther King's Birthday"
+                february_monday_3: "President's Day"
+                april_friday_last: "Arbor Day"
+                may_sunday_2: "Mother's Day"
+                may_monday_last: "Memorial Day"
+                june_sunday_3: "Father's Day"
+                september_monday_1: "Labor Day"
+                october_monday_2: "Columbus Day"
+                november_thursday_4: "Thanksgiving"
+
+            self.createCalendar self.currentMonth, self.currentYear
+
+            self.calendarNav.on 'click', '.drm-calendar-prev, .drm-calendar-next', ->
+                direction = $(@).data 'dir'
+                self.advanceMonth.call @, direction
+
+            self.calendarNav.on 'click', '.drm-calendar-current', ->
+                self.changeCalendar.call @, self.currentMonth, self.currentYear
+
+            self.calendarSelectButton.on 'click', (e) ->
+                that = $ @
+                month = that.parent().find('#month').val()
+                year = that.parent().find('#year').val()
+
+                month = parseInt month, 10
+                year = parseInt year, 10
+
+                e.preventDefault()
+
+                self.changeCalendar.call self, month, year
+
+        getDaysInMonth: (month, year) ->
+            days = new Date(year, month, 0).getDate()
+            days
+
+        getDayOfWeek: (month, year, day) ->
+            day = new Date year, month, day
+            day.getDay()
+
+        getWeeksInMonth: (numberDays, dayShift) =>
+            Math.ceil (numberDays + dayShift) / @daysPerWeek
+
+        toTitleCase: (str) ->
+            str.replace /\w\S*/g, (txt) ->
+                txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
+
+        createDaysInMonth: =>
+            self = @
+            numberDays = []
+            $.each @months, (key, value) ->
+                numberDays.push self.getDaysInMonth (key + 1), self.currentYear
+            numberDays
+
+        highlightCurrentDay: =>
+            calendarInner = @calendar.find "div.#{@calendarInnerClass}"
+            month = calendarInner.data 'month'
+            year = calendarInner.data 'year'
+
+            if month is @currentMonth and year is @currentYear
+                calendarInner.find("[data-date=#{@currentDay}]").addClass @classes.today
+
+        highlightHolidays: =>
+            self = @
+            calendarInner = self.calendar.find "div.#{@calendarInnerClass}"
+            currentMonth = calendarInner.data 'month'
+
+            $.each self.staticHolidays, (key, value) ->
+                dateArr = key.split '_'
+                month = $.inArray self.toTitleCase(dateArr[0]), self.months
+                date = parseInt dateArr[1], 10
+
+                if currentMonth is month
+                    holiday = calendarInner.find("[data-date=#{date}]").addClass self.classes.holiday
+                    eventList = $ '

',
+                        class: 'events'
+                        html: "
#{value}
"
+
+                    eventList.appendTo holiday
+
+        getVariableHolidays: (numberDays, dayShift) =>
+            self = @
+            calendarInner = self.calendar.find "div.#{@calendarInnerClass}"
+            currentMonth = calendarInner.data 'month'
+            weeks = calendarInner.find 'tr'
+
+            $.each self.variableHolidays, (key, value) ->
+                dateArr = key.split '_'
+                month = $.inArray self.toTitleCase(dateArr[0]), self.months
+                day = $.inArray self.toTitleCase(dateArr[1]), self.days
+                numberWeeks = self.getWeeksInMonth(numberDays, dayShift)
+                lastWeekLength = weeks.eq(numberWeeks).length
+
+                if dateArr[2] is 'last' and dayShift <= day
+                    dayNum = if lastWeekLength < day then (numberWeeks - 1) else numberWeeks
+                else if dateArr[2] is 'last' and dayShift > day
+                    dayNum = (numberWeeks - 1)
+                else
+                    dayNum = parseInt dateArr[2], 10
+
+                if currentMonth is month
+                    holidayWeek = if dayShift <= day then holidayWeek = weeks.eq dayNum else holidayWeek = weeks.eq dayNum + 1
+                    holidayDate = holidayWeek.find('td').eq(day).data 'date'
+
+                    holiday = calendarInner.find("[data-date=#{holidayDate}]").addClass self.classes.holiday
+                    eventList = $ '

',
+                        class: 'events'
+                        html: "
#{value}
"
+
+                    eventList.appendTo holiday
+
+        highlightWeekends: =>
+            self = @
+            weeks = @calendar.find("div.#{@calendarInnerClass}").find 'tr'
+
+            $.each weeks, ->
+                dates = $(@).find "td"
+                $.each self.weekend, (key, value) ->
+                    dates.eq(value).not(".#{self.classes.muted}, .#{self.classes.today}, .#{self.classes.holiday}").addClass self.classes.weekend
+
+        advanceMonth: (direction) =>
+            calendarInner = @calendar.find "div.#{@calendarInnerClass}"
+
+            if direction is 'prev'
+                month = if calendarInner.data('month') - 1 >= 0 then calendarInner.data('month') - 1 else 11
+                year = if calendarInner.data('month') - 1 >= 0 then calendarInner.data('year') else calendarInner.data('year') - 1
+            else if direction is 'next'
+                month = if calendarInner.data('month') + 1 < 12 then calendarInner.data('month') + 1 else 0
+                year = if calendarInner.data('month') + 1 < 12 then calendarInner.data('year') else calendarInner.data('year') + 1
+
+            @changeCalendar month, year
+
+        changeCalendar: (month, year) =>
+            self = @
+            calendarInner = @calendar.find "div.#{@calendarInnerClass}"
+            calendarInner.fadeOut 300, ->
+                @remove()
+                self.createCalendar month, year
+
+        createCalendar: (month, year) =>
+            self = @
+            numberDays = self.getDaysInMonth (month + 1), year
+            prevMonthNumberDays = self.getDaysInMonth month, year
+            firstDay = self.getDayOfWeek month, year, 1
+            dayShift = if firstDay is self.daysPerWeek then 0 else firstDay
+            numberWeeks = self.getWeeksInMonth numberDays, dayShift
+
+            # console.log "Days Per Week: #{daysPerWeek}"
+            # console.log "Number of Days: #{numberDays}"
+            # console.log "Prev Month Number Days: #{prevMonthNumberDays}"
+            # console.log "First Day: #{firstDay}"
+            # console.log "Day Shift: #{dayShift}"
+            # console.log "Number Weeks: #{numberWeeks}"
+
+            weekdays = '

'
+            $.each @days, (key, value) ->
+                weekdays += "#{value}	"
+            weekdays += '

'
+
+            weeks = '
'
+            i = 1
+            date = 1
+            l = 1
+            prevDays = (prevMonthNumberDays - dayShift) + 1
+            nextDates = 1
+
+            while i <= numberWeeks
+                j = 1
+                weeks += '
'
+                # if we are in week 1 we need to shift to the correct day of the week
+                if i is 1 and firstDay isnt 0
+                    # add cells for the previous month until we get to the first day
+                    while l <= dayShift
+                        weeks += "#{prevDays}	"
+                        prevDays += 1
+                        l += 1
+                        j += 1
+                    # start adding cells for the current month
+                    while j <= self.daysPerWeek
+                        weeks += "#{date}	"
+                        j += 1
+                        date += 1
+                # if we are in the last week of the month we need to add blank cells for next month
+                else if i is numberWeeks
+                    while j <= self.daysPerWeek
+                        # finish adding cells for the current month
+                        if date <= numberDays
+                            weeks += "#{date}	"
+                        # start adding cells for next month
+                        else
+                            weeks += "#{nextDates}	"
+                            nextDates += 1
+                        j += 1
+                        date += 1
+                else
+                    # if we are in the middle of the month add cells for the current month
+                    while j <= self.daysPerWeek
+                        weeks += "#{date}	"
+                        j += 1
+                        date += 1
+                weeks += '
'
+                i += 1
+            weeks += '

'
+
+            calendar = $ '

',
+                class: self.calendarInnerClass
+                html: weekdays + weeks
+                'data-month': month
+                'data-year': year
+
+            heading = $ '

',
+                text: "#{@months[month]} #{year}"
+
+            calendar.appendTo ".#{self.calendarClass}"
+            heading.prependTo ".#{self.calendarInnerClass}"
+
+            self.highlightCurrentDay()
+            self.highlightWeekends()
+            self.highlightHolidays()
+            self.getVariableHolidays numberDays, dayShift

     new DrmCalendar()


cleanup

cleanup

--- apps/page/index.coffee
+++ apps/page/index.coffee
@@ -15,17 +15,16 @@
 app.get '/privacy', routes.vanityUrl('privacy')
 app.get '/past-privacy', routes.vanityUrl('past-privacy')
 app.get '/security', routes.vanityUrl('security')
-app.get '/press', (req, res) -> res.redirect '/about/page/press'
 app.get '/conditions-of-sale', routes.vanityUrl('conditions-of-sale')
 app.get '/auction-info', routes.vanityUrl('auction-info')
 app.get '/embed-terms', routes.vanityUrl('embed-terms')
-app.get '/embeddable-artists', routes.vanityUrl('embeddable-artists')
 app.get '/past-terms-8-5-13', routes.vanityUrl('past-terms-8-5-13')
 app.get '/past-privacy-10-29-12', routes.vanityUrl('past-privacy-10-29-12')
 app.get '/rrf-emerging-curator-competition-official-rules', routes.vanityUrl('rrf-emerging-curator-competition-official-rules')
 app.get '/past-privacy-8-5-13', routes.vanityUrl('past-privacy-8-5-13')
 app.get '/past-terms-9-26-13', routes.vanityUrl('past-terms-9-26-13')

+app.get '/page/:id', routes.index
 app.get '/job/:id', routes.index
 # Handle urls like /job/devops-engineer/about
 app.get '/job/:id/*', routes.index


Client: Teams: formatting

Client: Teams: formatting

--- client/admin/lib/views/members/teammemberscommonview.coffee
+++ client/admin/lib/views/members/teammemberscommonview.coffee
@@ -21,7 +21,7 @@
     options.listViewItemClass      or= MemberItemView
     options.listViewItemOptions    or= {}
     options.searchInputPlaceholder or= 'Find by name/username'
-    options.showSearchFieldAtFirst  or= no
+    options.showSearchFieldAtFirst or= no
     options.sortOptions            or= [
       { title: 'Screen name',   value: 'fullname' }
       { title: 'Nickname',      value: 'nickname' }


Take care shared vms for groups

Take care shared vms for groups

--- client/app/MainApp/filetree/itemsubviews/copyurlview.coffee
+++ client/app/MainApp/filetree/itemsubviews/copyurlview.coffee
@@ -18,9 +18,19 @@

     KD.getSingleton('vmController').fetchVMDomains hostname, (err, domains)=>
       if domains?.length > 0 and not err
-        @publicPath = domains.first
-        @inputUrl.setValue (FSHelper.plainPath path).replace \
-          ////home/(.*)/Web/(.*)///, "http://#{@publicPath}/$2"
+
+        path  = FSHelper.plainPath path
+        match = path.match ///home/(\w+)/Web/(.*)///
+        return  unless match
+        [rest..., user, pathrest] = match
+
+        if /^shared-/.test hostname
+          subdomain = unless user is KD.nick() then "" else "#{user}."
+        else
+          subdomain = ''
+
+        @publicPath = "#{subdomain}#{domains.first}/#{pathrest}"
+        @inputUrl.setValue "http://#{@publicPath}"

   focusAndSelectAll:->
     @inputUrl.setFocus()


add encryption and decryption

add encryption and decryption

--- src/sekritweb.coffee
+++ src/sekritweb.coffee
@@ -43,10 +43,10 @@
         ['.span3.hidden-phone']
         ['%form.span6'
           ['%fieldset'
-            ['%legend', 'Create new Sekrit Cirkle ...']
+            ['%legend', 'Create new Cirkle ...']
             ['%input#friendly', {
               type:'text'
-              placeholder:'Enter name here (optional)'
+              placeholder:'Enter name here (optional) ...'
               }]
             ['%button#create', {type:'submit', class:'btn'}, 'Create']]] ]]

@@ -73,8 +73,24 @@
       cirkle = new Cipher cirkleString
       [
         ['.row'
-          ['%h2.span12', friendly]
-          ]]
+          ['%h2.span12', "#{friendly} Cirkle"]]
+        ['.row'
+          ['%form.span6'
+            ['%fieldset'
+              ['%legend', 'Create a Sekrit']
+              ['%textarea#msg-in.span6', {
+                placeholder:'Type your message here ...' }]
+              ['.well#sekrit-out']]]
+          ['%form.span6'
+            ['%fieldset'
+              ['%legend', 'Read a Sekrit']
+              ['%textarea#sekrit-in.span6', {
+                placeholder:'Paste a sekrit here ...' }]
+              ['.well#msg-out']]]]]
+    $('#msg-in').keypress ->
+      $('#sekrit-out').text cirkle.encrypt $(@).val()
+    $('#sekrit-in').keypress ->
+      $('#msg-out').text cirkle.decrypt $(@).val()

   cirkleString = fromHash()


Fix post button on reply form

Fix post button on reply form

--- assets/javascripts/views/new_post_form.js.coffee
+++ assets/javascripts/views/new_post_form.js.coffee
@@ -15,7 +15,8 @@
     @$post_btn = ($ 'input[type=submit]', @$el)

     @$form = ($ 'form', @$el)
-    @$form.off().on 'submit', @submit
+    @$form.off('submit.post').on 'submit.post', @submit
+    @$post_btn.off('click.post').on 'click.post', @submit

     ## form validation and character limit counter
     @$textarea = ($ 'textarea', @$el)


groups/profile customise: fixed color picker

--- client/landing/app/Applications/Groups.kdapplication/Views/groupscustomizeviews.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupscustomizeviews.coffee
@@ -410,6 +410,7 @@
     else if @type is 'customColor'
       @color = new StaticGroupBackgroundColorPickerView
         cssClass : 'custom-color-picker'
+        delegate : @getDelegate()
       ,data

   click: ->


@include with ember

@include with ember

--- test/cases/support/emberTest.coffee
+++ test/cases/support/emberTest.coffee
@@ -71,4 +71,25 @@
     _c1 = new _C1

     assert.equal _c1.aVariable, "aVariable!!!"
-    assert.equal _c1.aMethod(), "aMethod!!!"+    assert.equal _c1.aMethod(), "aMethod!!!"
+
+  test 'class A extends Tower.Class (should be ember object)', ->
+    class A extends Tower.Class
+
+    assert.equal A.superclass.name, Tower.Class.name
+
+  test '@include', ->
+    viaInclude =
+      includedMethod: "includedMethod!"
+
+    viaReopen =
+      reopenedMethod: "reopenedMethod!"
+
+    class A extends Tower.Class
+      @include viaInclude
+      @reopen viaReopen
+
+    a = new A
+
+    assert.equal a.includedMethod, "includedMethod!"
+    assert.equal a.reopenedMethod, "reopenedMethod!"


Spec update

Spec update

--- spec/language-haskell-spec.coffee
+++ spec/language-haskell-spec.coffee
@@ -70,12 +70,12 @@
       """
       expect(lines).toEqual  [
           [
-            { value : '1', scopes : [ 'source.haskell', 'constant.numeric.haskell' ] }
+            { value : '1', scopes : [ 'source.haskell', 'constant.numeric.decimal.haskell' ] }
           ],
           [
             { value : '/', scopes : [ 'source.haskell', 'keyword.operator.haskell' ] }
             { value : ' ', scopes : [ 'source.haskell' ] }
-            { value : '2', scopes : [ 'source.haskell', 'constant.numeric.haskell' ] }
+            { value : '2', scopes : [ 'source.haskell', 'constant.numeric.decimal.haskell' ] }
           ]
         ]

@@ -122,7 +122,7 @@
       data = 'main = (putStrLn :: String -> IO ()) ("Hello World" :: String)'
       {tokens} = grammar.tokenizeLine(data)
       expect(tokens).toEqual [
-        { value : 'main', scopes : [ 'source.haskell' ] }
+        { value : 'main', scopes : [ 'source.haskell', 'identifier.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
         { value : '=', scopes : [ 'source.haskell', 'keyword.operator.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
@@ -156,7 +156,7 @@
       data = 'main = putStrLn "Hello World" :: IO ()'
       {tokens} = grammar.tokenizeLine(data)
       expect(tokens).toEqual [
-        { value : 'main', scopes : [ 'source.haskell' ] }
+        { value : 'main', scopes : [ 'source.haskell', 'identifier.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }
         { value : '=', scopes : [ 'source.haskell', 'keyword.operator.haskell' ] }
         { value : ' ', scopes : [ 'source.haskell' ] }


Fix spec

Fix spec

--- spec/markdown-heading-spec.coffee
+++ spec/markdown-heading-spec.coffee
@@ -23,7 +23,9 @@

     # http://spec.commonmark.org/0.22/#example-29
     {tokens} = grammar.tokenizeLine('#5 bolt')
-    expect(tokens[0]).toEqual value: '#5 bolt', scopes: ['text.md']
+    expect(tokens[0]).toEqual value: '#', scopes: ['text.md', 'issue.reference.gfm.md', 'punctuation.md']
+    expect(tokens[1]).toEqual value: '5', scopes: ['text.md', 'issue.reference.gfm.md']
+    expect(tokens[2]).toEqual value: ' bolt', scopes: ['text.md']

     # Variant of #29
     {tokens} = grammar.tokenizeLine('#foobar')


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -46,7 +46,7 @@
     ###*
     @name clamp
     @methodOf Point#
-    @param {Number} n V
+    @param {Number} n Maxim
     @returns {Point} A new point whose magnitude has been clamped to the given value.
     ###
     clamp: (n) ->


Underlines and newlines bro

Underlines and newlines bro

--- src/quest.coffee
+++ src/quest.coffee
@@ -21,7 +21,7 @@
           @client.end()
           if err
             console.error "An error occurred!".red.bold
-            console.error err.message.red
+            console.error err.message.red.underline

   transaction: (cb) ->
     console.log "Beginning transaction".blue.underline
@@ -54,5 +54,7 @@
       else
         console.time('Execution time') if @time
         result = @client.query.sync(@client, query)
-        console.timeEnd('Execution time') if @time
+        if @time
+          console.timeEnd('Execution time')
+          console.log()
     result


move more private functions

move more private functions

--- src/quill.coffee
+++ src/quill.coffee
@@ -22,22 +22,6 @@

 DEFAULT_API_OPTIONS = { silent: false, source: 'api' }
-
-# fn(Number index, Number length, String name, String value, Object options = {})
-# fn(Number index, Number length, Object formats, Object options = {})
-# fn(Object range, String name, String value, Object options = {})
-# fn(Object range, Object formats, Object options = {})
-buildParams = (params...) ->
-  if _.isObject(params[0])
-    index = params[0].start
-    length = params[0].end - index
-    params.splice(0, 1, index, length)
-  if _.isString(params[2])
-    formats = {}
-    formats[params[2]] = params[3]
-    params.splice(2, 2, formats)
-  params[3] = _.defaults(params[3] or {}, DEFAULT_API_OPTIONS)
-  return params

 class Quill extends EventEmitter2
@@ -104,7 +88,7 @@
     @editor.renderer.addStyles(styles)

   deleteText: (index, length, options = {}) ->
-    [index, length, formats, options] = buildParams(index, length, {}, options)
+    [index, length, formats, options] = this._buildParams(index, length, {}, options)
     return unless length > 0
     delta = Tandem.Delta.makeDeleteDelta(this.getLength(), index, length)
     @editor.applyDelta(delta, options)
@@ -118,7 +102,7 @@
     @editor.root.focus()

   formatLines: (index, length, name, value, options) ->
-    [index, length, formats, options] = buildParams(index, length, name, value, options)
+    [index, length, formats, options] = this._buildParams(index, length, name, value, options)
     [line, offset] = @editor.doc.findLineAt(index)
     # TODO potential util function with editor._formatAt
     delta = Tandem.Delta.getIdentity(this.getLength())
@@ -135,7 +119,7 @@
     @editor.applyDelta(delta, options)

   formatText: (index, length, name, value, options) ->
-    [index, length, formats, options] = buildParams(index, length, name, value, options)
+    [index, length, formats, options] = this._buildParams(index, length, name, value, options)
     return unless length > 0
     delta = Tandem.Delta.makeRetainDelta(this.getLength(), index, length, formats)
     @editor.applyDelta(delta, options)
@@ -165,7 +149,7 @@
     this.insertText(index, Format.EMBED_TEXT, type, source)

   insertText: (index, text, name, value, options = {}) ->
-    [index, length, formats, options] = buildParams(index, 0, name, value, options)
+    [index, length, formats, options] = this._buildParams(index, 0, name, value, options)
     return unless text.length > 0
     delta = Tandem.Delta.makeInsertDelta(this.getLength(), index, text, formats)
     @editor.applyDelta(delta, options)
@@ -207,5 +191,21 @@
     options = _.defaults(options, DEFAULT_API_OPTIONS)
     @editor.selection.update(options.silent)

+  # fn(Number index, Number length, String name, String value, Object options = {})
+  # fn(Number index, Number length, Object formats, Object options = {})
+  # fn(Object range, String name, String value, Object options = {})
+  # fn(Object range, Object formats, Object options = {})
+  _buildParams: (params...) ->
+    if _.isObject(params[0])
+      index = params[0].start
+      length = params[0].end - index
+      params.splice(0, 1, index, length)
+    if _.isString(params[2])
+      formats = {}
+      formats[params[2]] = params[3]
+      params.splice(2, 2, formats)
+    params[3] = _.defaults(params[3] or {}, DEFAULT_API_OPTIONS)
+    return params
+

 module.exports = Quill


Factor out splitText

Factor out splitText

--- src/range.coffee
+++ src/range.coffee
@@ -9,6 +9,24 @@
 # https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
 ELEMENT_NODE = 1
 TEXT_NODE = 3
+
+
+exports.splitBoundaries = splitBoundaries = (range) ->
+  {startContainer, startOffset, endContainer, endOffset} = range
+
+  if isTextNode(endContainer)
+    if 0 < endOffset < endContainer.length
+      endContainer = splitText(endContainer, endOffset)
+      range.setEnd(endContainer, 0)
+
+  if isTextNode(startContainer)
+    if 0 < startOffset < startContainer.length
+      if startContainer is endContainer
+        startContainer = splitText(startContainer, startOffset)
+        range.setEnd(startContainer, endOffset - startOffset)
+      else
+        startContainer = splitText(startContainer, startOffset)
+      range.setStart(startContainer, 0)

 # Public: Creates a wrapper around a range object obtained from a DOMSelection.
@@ -31,45 +49,18 @@
     @endContainer            = obj.endContainer
     @endOffset               = obj.endOffset

-  splitBoundaries: ->
-    if isTextNode(@endContainer)
-      if 0 < @endOffset < @endContainer.length
-        if @startContainer is @endContainer
-          @commonAncestorContainer = @endContainer.parentNode
-          @endContainer = splitText(@endContainer, @endOffset)
-
-          if @endOffset <= @startOffset
-            @startContainer = @endContainer
-            @startOffset -= @endOffset
-
-          @endOffset = 0
-        else
-          @endContainer = splitText(@endContainer, @endOffset)
-          @endOffset = 0
-
-    if isTextNode(@startContainer)
-      if 0 < @startOffset < @startContainer.length
-        if @commonAncestorContainer is @startContainer
-          @commonAncestorContainer = @commonAncestorContainer.parentNode
-
-        if @startContainer is @endContainer
-          @commonAncestorContainer = @startContainer.parentNode
-          @startContainer = splitText(@startContainer, @startOffset)
-
-          if @startOffset <= @endOffset
-            @endContainer = @startContainer
-            @endOffset -= @startOffset
-
-          @startOffset = 0
-        else
-          @startContainer = splitText(@startContainer, @startOffset)
-          @startOffset = 0
+
+  setStart: (@startContainer, @startOffset) ->
+    @commonAncestorContainer = commonAncestor(@startContainer, @endContainer)
+
+  setEnd: (@endContainer, @endOffset) ->
+    @commonAncestorContainer = commonAncestor(@startContainer, @endContainer)

   # Public: Normalize the start and end to TextNode boundaries.
   #
   # Returns an instance of NormalizedRange
   normalize: (root) ->
-    this.splitBoundaries()
+    splitBoundaries(this)

     # Initialize the result.
     commonAncestor = @commonAncestorContainer


a few of the better bits (IMO) from pull request #346

--- src/route.coffee
+++ src/route.coffee
@@ -28,7 +28,7 @@
     @options = $.extend({}, @options, options)

     if (@options.history)
-      @history = @historySupport && @options.history
+      @history = @historySupport and @options.history

     return if @options.shim

@@ -68,11 +68,7 @@
     return if options.shim

     if @history
-      history.pushState(
-        {},
-        document.title,
-        @path
-      )
+      history.pushState({}, document.title, @path)
     else
       window.location.hash = @path

@@ -98,10 +94,9 @@
     @matchRoute(@path)

   @matchRoute: (path, options) ->
-    for route in @routes
-      if route.match(path, options)
-        @trigger('change', route, path)
-        return route
+    for route in @routes when route.match(path, options)
+      @trigger('change', route, path)
+      return route

   constructor: (@path, @callback) ->
     @names = []
@@ -119,7 +114,7 @@
                  .replace(namedParam, '([^\/]*)')
                  .replace(splatParam, '(.*?)')

-      @route = new RegExp('^' + path + '$')
+      @route = new RegExp("^#{path}$")
     else
       @route = path

@@ -148,4 +143,4 @@
   navigate: ->
     Spine.Route.navigate.apply(Spine.Route, arguments)

-module?.exports = Spine.Route+module?.exports = Spine.Route


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Place entire hard tab value inside span

Place entire hard tab value inside span

Previously only the first character was included in the span.

--- src/token.coffee
+++ src/token.coffee
@@ -135,9 +135,8 @@
       classes = 'hard-tab'
       classes += ' indent-guide' if hasIndentGuide
       classes += ' invisible-character' if invisibles.tab
-      html = @value.replace StartCharacterRegex, (match) =>
-        match = invisibles.tab ? match
-        "#{@escapeString(match)}"
+      value = @value.replace(StartCharacterRegex, invisible.tab) if invisible.tab
+      html = "#{@escapeString(value)}"
     else
       startIndex = 0
       endIndex = @value.length


payment: improve/unify billing method view

--- client/app/Applications/Dashboard.kdapplication/views/billingmethodview.coffee
+++ client/app/Applications/Dashboard.kdapplication/views/billingmethodview.coffee
@@ -20,10 +20,12 @@

   getCardInfoPartial: (billingInfo) ->
     if billingInfo
-      { cardFirstName, cardLastName, cardNumber, cardMonth, cardYear
-        cardType, address1, address2, city, state, zip } = billingInfo
+      { description, cardFirstName, cardLastName, cardNumber, cardMonth
+        cardYear, cardType, address1, address2, city, state, zip } = billingInfo
       address = [address1, address2].filter(Boolean).join ' '
+      description ?= "#{cardFirstName}'s #{cardType}"
       """
+
#{description}

#{cardFirstName} #{cardLastName}

#{cardNumber} - #{cardMonth}/#{cardYear} (#{cardType})

#{address}


Validate Gists when response is 200

Validate Gists when response is 200

--- viewer/app.coffee
+++ viewer/app.coffee
@@ -22,6 +22,12 @@
     else
         next()

+validGist = (files) ->
+    for f in ['script.coffee', 'markup.jade', 'style.styl', 'settings.json', 'notes.md']
+        if not files?[f]?
+            return false
+    return true
+
 handleRequests = (request, response, next) ->
     unless request.url is '/favicon.ico'
         # The GitHub API doesn't handle trailing slashes, so trim them.
@@ -30,7 +36,7 @@
             url = url.substring(0, url.length - 1)

         getGist url, (data, github_response) ->
-            if github_response.statusCode is 200
+            if github_response.statusCode is 200 and validGist(data.files)
                 content = renderer
                     style       : data.files['style.styl'].content
                     script      : data.files['script.coffee'].content
@@ -39,11 +45,13 @@
                 htmlResponse(response, content)
             else
                 content = """
-                    Proto Gist not found at
+                    Valid Proto Gist not found at
                     api.github.com/gists#{ request.url }:
                     #{ github_response.statusCode }

+

                     #{ github_response.raw.toString() }
+

                 """
                 htmlResponse(response, content, 404)


Added close distance raycasting for block selections

--- src/world.coffee
+++ src/world.coffee
@@ -243,20 +243,33 @@
     this.getChunk x, y, z, true
     @generator.generateChunk x, y, z

-  performRayCast: (ray) ->
+  iterChunksAroundCamera: (range, callback) ->
+    if range == null
+      for key, chunk of @chunks
+        callback(chunk, parseKey(key)...)
+      return
+
+    [cx, cy, cz] = this.chunkAtCameraPosition()
+    for x in [cx - range..cx + range]
+      for y in [cy - range..cy + range]
+        for z in [cz - range..cz + range]
+          chunk = @chunks["#{x}|#{y}|#{z}"]
+          if chunk
+            callback(chunk, x, y, z)
+
+  performRayCast: (ray, range = null) ->
     aabb = new webglmc.AABB()
     chunkSize = CUBE_SIZE * @chunkSize
     hits = []

-    for key, chunk of @chunks
-      [cx, cy, cz] = parseKey key
+    this.iterChunksAroundCamera range, (chunk, cx, cy, cz) =>

       aabb.vec1[0] = chunkSize * cx
       aabb.vec1[1] = chunkSize * cy
       aabb.vec1[2] = chunkSize * cz
       vec3.add aabb.vec1, [chunkSize, chunkSize, chunkSize], aabb.vec2
       if !ray.intersectsAABB aabb
-        continue
+        return

       offX = @chunkSize * cx
       offY = @chunkSize * cy
@@ -289,13 +302,17 @@

     new WorldRaycastResult ray, hits[0]...

-  pickBlockAtScreenPosition: (x, y) ->
+  pickBlockAtScreenPosition: (x, y, range = null) ->
     ray = webglmc.Ray.fromScreenSpaceNearToFar x, y
-    this.performRayCast ray
-
-  pickBlockAtScreenCenter: ->
+    this.performRayCast ray, range
+
+  pickBlockAtScreenCenter: (range = null) ->
     {width, height} = webglmc.engine
-    this.pickBlockAtScreenPosition width / 2, height / 2
+    this.pickBlockAtScreenPosition width / 2, height / 2, range
+
+  pickCloseBlockAtScreenCenter: ->
+    rv = this.pickBlockAtScreenCenter 1
+    if rv && rv.hit.distance < 10 then rv else null

   draw: ->
     {gl} = webglmc.engine


remove all client stuff

remove all client stuff

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -20,28 +20,6 @@
   uglify.uglify.gen_code(ast)

-# Soft dependencies:
-jsdom = null
-
-# CoffeeScript-generated JavaScript may contain anyone of these; when we "rewrite"
-# a function (see below) though, it loses access to its parent scope, and consequently to
-# any helpers it might need. So we need to reintroduce these helpers manually inside any
-# "rewritten" function.
-coffeescript_helpers = """
-  var __slice = Array.prototype.slice;
-  var __hasProp = Object.prototype.hasOwnProperty;
-  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-  var __extends = function(child, parent) {
-    for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
-    function ctor() { this.constructor = child; }
-    ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype;
-    return child; };
-  var __indexOf = Array.prototype.indexOf || function(item) {
-    for (var i = 0, l = this.length; i < l; i++) {
-      if (this[i] === item) return i;
-    } return -1; };
-""".replace /\n/g, ''
-
 # Shallow copy attributes from `sources` (array of objects) to `recipient`.
 # Does NOT overwrite attributes already present in `recipient`.
 copy_data_to = (recipient, sources) ->
@@ -105,9 +83,6 @@
   app = context.app = express.createServer()
   io = context.io = socketio.listen(app)

-  # Reference to the zappa client, the value will be set later.
-  client = null
-
   # Tracks if the zappa middleware is already mounted (`@use 'zappa'`).
   zappa_used = no

@@ -137,19 +112,6 @@
           for k, v of arguments[0]
             route verb: verb, path: k, handler: v

-  context.client = (obj) ->
-    context.use 'zappa' unless zappa_used
-    for k, v of obj
-      js = ";zappa.run(#{v});"
-      js = minify(js) if app.settings['minify']
-      route verb: 'get', path: k, handler: js, contentType: 'js'
-
-  context.coffee = (obj) ->
-    for k, v of obj
-      js = ";#{coffeescript_helpers}(#{v})();"
-      js = minify(js) if app.settings['minify']
-      route verb: 'get', path: k, handler: js, contentType: 'js'
-
   context.js = (obj) ->
     for k, v of obj
       js = String(v)
@@ -160,12 +122,6 @@
     for k, v of obj
       css = String(v)
       route verb: 'get', path: k, handler: css, contentType: 'css'
-
-  context.stylus = (obj) ->
-    for k, v of obj
-      css = require('stylus').render v, filename: k, (err, css) ->
-        throw err if err
-        route verb: 'get', path: k, handler: css, contentType: 'css'

   context.helper = (obj) ->
     for k, v of obj
@@ -202,11 +158,7 @@
           send = (code) ->
             res.contentType 'js'
             res.send code
-          if req.method.toUpperCase() isnt 'GET' then next()
-          else
-            switch req.url
-              when '/zappa/zappa.js' then send client
-              else next()
+          next()

     use = (name, arg = null) ->
       zappa_used = yes if name is 'zappa'
@@ -357,28 +309,6 @@
   # Go!
   func.apply(context, [context])

-  # The stringified zappa client.
-  client = require('./client').build(zappa.version, app.settings)
-  client = ";#{coffeescript_helpers}(#{client})();"
-  client = minify(client) if app.settings['minify']
-
-  if app.settings['default layout']
-    context.view layout: ->
-      doctype 5
-      html ->
-        head ->
-          title @title if @title
-          if @scripts
-            for s in @scripts
-              script src: s + '.js'
-          script(src: @script + '.js') if @script
-          if @stylesheets
-            for s in @stylesheets
-              link rel: 'stylesheet', href: s + '.css'
-          link(rel: 'stylesheet', href: @stylesheet + '.css') if @stylesheet
-          style @style if @style
-        body @body
-
   context

 # Takes a function and runs it as a zappa app. Optionally accepts a port number, and/or


DAMNED SEMICOLON

DAMNED SEMICOLON

--- src/translator.coffee
+++ src/translator.coffee
@@ -38,7 +38,7 @@
     if trans.trans_skip[v.mx_hash.hash_key]?
       # LATER
       if node.mx_hash.eol_pass and v.mx_hash.hash_key == 'eol'
-        list.push "\n"
+        list.push ";\n"
     else if fn = trans.trans_token[v.mx_hash.hash_key]
       list.push fn v.value
     else if trans.trans_value[v.mx_hash.hash_key]?


Reduced EventEmitter/Bacon duality shenanigans

--- src/coffee/JuliaProcess.coffee
+++ src/coffee/JuliaProcess.coffee
@@ -25,6 +25,7 @@
     @juliaPath = opt?.juliaPath ? '/usr/bin/julia'
     @emitter = new EventEmitter()
     _.keys(EventEmitter::).forEach (method) =>
+      return unless @emitter[method]
       @[method] = @emitter[method].bind @emitter
     ['ready','killed'].forEach (method) =>
       @[method] = Bacon.fromEventTarget @emitter, method
@@ -81,21 +82,21 @@
     @stop()
     @start()
   stream: (eventName) ->
-    Bacon.fromEventTarget @, eventName, getArgs
+    Bacon.fromEventTarget @emitter, eventName, getArgs
   detachProcess: ->
     @_detach()
   evaluate: (code) ->
     if code?.data?.length
-      @emit.apply @, _.flatten [code.event, code.data]
+      @emitter.emit.apply @emitter, _.flatten [code.event, code.data]
     else
-      @emit code?.event ? 'noop'
+      @emitter.emit code?.event ? 'noop'
   compute: (code, callback) ->
     callbackId = "computed-value-#{@counter++}"
     code = ("\n#{code}").replace /\n/g, "\n  "
     code = "@emit \"#{callbackId}\" eval(quote\n#{code}\nend)"
     @send code
     if _.isFunction callback
-      @once callbackId, =>
+      @emitter.once callbackId, =>
         callback.apply @, arguments
     else
       @stream callbackId


開場前通知のメッセージが正常に表示されない問題を修正

開場前通知のメッセージが正常に表示されない問題を修正

--- src/coffee/notification.coffee
+++ src/coffee/notification.coffee
@@ -1,6 +1,8 @@
 LOGGER = new Logger

 class Notification
+  @BEFORE_TIME_SEC: 300
+
   constructor: ->
     @liveChecker = chrome.extension.getBackgroundPage().liveChecker
     $ =>
@@ -76,7 +78,7 @@
       else if openTime and now > openTime
         # open gate
         text = 'まもなく放送開始'
-      else if openTime and now > openTime - LiveInfoHtml.BEFORE_TIME_SEC
+      else if openTime and now > openTime - Notification.BEFORE_TIME_SEC * 1000
         # before open gate
         text = 'まもなく開場'
     # Set status.


Remove debugger

Remove debugger

--- app/src/app.coffee
+++ app/src/app.coffee
@@ -98,7 +98,6 @@
           if error?
             # TODO: Handle wallet opening fatal error
             e "Raise", error
-          debugger
           @_listenPreferencesEvents()
           @_listenCountervalueEvents(true)
           ledger.utils.Logger.updateGlobalLoggersLevel()


reactivity: methods renaming

reactivity: methods renaming

--- client/activity/lib/flux/actions/channel.coffee
+++ client/activity/lib/flux/actions/channel.coffee
@@ -29,7 +29,7 @@
  * @param {object} params - func parameters
  * @return {Promise}
 ###
-loadChannelWithFunc = (func, params) ->
+loadChannelWithFn = (func, params) ->

   { LOAD_CHANNEL_BEGIN
     LOAD_CHANNEL_FAIL
@@ -114,7 +114,7 @@
   func   = kd.singletons.socialapi.channel.byId
   params = { id }

-  loadChannelWithFunc func, params
+  loadChannelWithFn func, params

 ###*
@@ -130,7 +130,7 @@
   params = { name, type }
   func   = kd.singletons.socialapi.channel.byName

-  loadChannelWithFunc func, params
+  loadChannelWithFn func, params

 ###*
@@ -277,7 +277,7 @@

   options.name = query
   func = kd.singletons.socialapi.channel.searchTopics
-  loadChannelsWithFunc func, options
+  loadChannelsWithFn func, options

 ###*
@@ -288,10 +288,10 @@
 loadChannels = (options = {}) ->

   func = kd.singletons.socialapi.channel.list
-  loadChannelsWithFunc func, options
-
-
-loadChannelsWithFunc = (func, options) ->
+  loadChannelsWithFn func, options
+
+
+loadChannelsWithFn = (func, options) ->

   { LOAD_CHANNELS_BEGIN
     LOAD_CHANNELS_SUCCESS


reactivity: fix for merge issue

reactivity: fix for merge issue

--- client/activity/lib/flux/actions/channel.coffee
+++ client/activity/lib/flux/actions/channel.coffee
@@ -394,11 +394,12 @@

 addParticipantsByNames = (channelId, names) ->

-  users = kd.singletons.reactor.evaluateToJS getters.allUsers
+  users        = kd.singletons.reactor.evaluateToJS getters.allUsers
   participants = (user for userId, user of users when names.indexOf(user.profile.nickname) > -1)
   accountIds   = (user.socialApiId for user in participants)
-
-  addParticipants { channelId, accountIds }
+  userIds      = (user._id for user in participants)
+
+  addParticipants channelId, accountIds, userIds

 ###*


managed: Better method of getting klient, from the jMachine

--- client/app/lib/providers/managed/helpers.coffee
+++ client/app/lib/providers/managed/helpers.coffee
@@ -1,5 +1,6 @@
-nick = require 'app/util/nick'
-kd   = require 'kd'
+kd      = require 'kd'
+nick    = require 'app/util/nick'
+Machine = require '../machine'

 # The maximum number of heartbeat attempts when looking for
@@ -76,7 +77,7 @@
 ###
 heartbeatKites = (interval = HEARTBEAT_INTERVAL, callback = kd.noop) ->

-  { router, computeController } = kd.singletons
+  { router } = kd.singletons

   beatCount  = 0
   oldKites   = {}
@@ -116,16 +117,17 @@
       unless newKite
         return

-      createMachine newKite, (err, machine) ->
+      createMachine newKite, (err, jMachine) ->
         return kallback err  if err

         # Route to the IDE
-        kd.utils.defer -> router.handleRoute "/IDE/#{machine.slug}"
+        kd.utils.defer -> router.handleRoute "/IDE/#{jMachine.slug}"
+
+        machine = Machine { machine: jMachine }

         # Get the klient kite, and the info from that klient so we
         # can popup a Provider specific modal
-        # TODO: Is there a better way to get the klient kite?
-        klient = computeController.machinesById[machine._id].getBaseKite()
+        klient = machine.getBaseKite()
         klient.klientInfo().nodeify kallback

     queryPromise.catch (err) ->


video: helper add docs to gravatar helper

--- client/app/lib/videocollaboration/helper.coffee
+++ client/app/lib/videocollaboration/helper.coffee
@@ -117,6 +117,12 @@
     callback null, publisher

+###*
+ * get user's gravatar, return the default avatar if user doesn't have an avatar.
+ *
+ * @param {JAccount} account
+ * @param {number} size
+###
 _getGravatarUri = (account, size = 355) ->

   {hash} = account.profile


ok, got the token, onwards!

ok, got the token, onwards!

--- test/lib/twitter.iced
+++ test/lib/twitter.iced
@@ -1,12 +1,16 @@

 request = require 'request'
 cheerio = require 'cheerio'
+{katch} = require('iced-utils').util
+{make_esc} = require('iced-error')

 #=====================================================================

 exports.TwitterBot = class TwitterBot

   constructor : ({@username, @password}) ->
+
+  #---------------------------------

   load_page : (path, status = [200], cb) ->
     uri = [ "https://twitter.com" , path ].join "/"
@@ -15,12 +19,27 @@
       err = new Error "HTTP status code #{sc}"
     cb err, body, res

+  #---------------------------------
+
+  grab_auth_token : ($, cb) ->
+    err = null
+    if not (raw = $("#init-data").val())? or raw.length is 0
+      err = new Error "No #init-data form data found"
+    else
+      [err, json] = katch () -> JSON.parse(raw)
+    if not err? and not (ret = json.formAuthenticityToken)?
+      err = new Error "init-data didn't have a 'formAuthenticityToken'"
+    cb err, ret
+
+  #---------------------------------
+
   load_login_page : (cb) ->
-    await @load_page "/login", null, defer err, body
+    esc = make_esc cb, "load_log_page"
+    await @load_page "/login", null, esc defer body
     $ = cheerio.load body
-    tok = (JSON.parse $("#init-data").val()).formAuthenticityToken
+    await @grab_auth_token $, esc defer tok
     console.log tok
-    cb err
+    cb null

 #=====================================================================


course nav uses api/courses/courseId route again

--- src/components/course-periods-nav.cjsx
+++ src/components/course-periods-nav.cjsx
@@ -5,7 +5,6 @@
 camelCase = require 'camelcase'

 {CourseActions, CourseStore} = require '../flux/course'
-{CourseListingActions, CourseListingStore} = require '../flux/course-listing'

 CoursePeriodsNav = React.createClass
   displayName: 'CoursePeriodsNav'
@@ -77,13 +76,10 @@
   render: ->
     courseId = @getCourseId()

-    # CourseListingActions.load instead to use courses and roles loaded from /api/user/courses
-
     <LoadableItem
       id={courseId}
       store={CourseStore}
       actions={CourseActions}
-      load={CourseListingActions.load}
       renderItem={=> <CoursePeriodsNav {...@props} courseId={courseId} />}
     />


Modal: modalview to take overlay options in its arguments

--- src/components/modals/modalview.coffee
+++ src/components/modals/modalview.coffee
@@ -162,10 +162,17 @@
     @$('.kdmodal-content').css maxHeight : innerHeight - 120
     @setY Math.round((innerHeight - @getHeight()) / 2)  unless @getOptions().position.top

-  putOverlay:->
-    isRemovable = @getOptions().overlayClick
-    @overlay    = new KDOverlayView { isRemovable }
-    @overlay.once "click", @bound "destroy"  if isRemovable
+
+  putOverlay: ->
+
+    {overlayOptions, overlayClick} = @getOptions()
+    overlayOptions                ?= {}
+    overlayOptions.isRemovable    ?= overlayClick
+
+    @overlay = new KDOverlayView overlayOptions
+
+    @overlay.once 'click', @bound 'destroy'  if overlayClick
+

   createButton:(title, buttonOptions)->
     buttonOptions.title    = title


Update user event is processSubmit failed

--- server/cron/downloadSubmits.coffee
+++ server/cron/downloadSubmits.coffee
@@ -109,31 +109,32 @@

     run: ->
         logger.info "SubmitDownloader.run ", @minPages, '-', @limitPages
-
-        page = 0
-        while true
-            logger.info("Dowloading submits, page #{page}")
-            submits = await @baseDownloader.getSubmitsFromPage(page)
-            if not submits.length
-                break
-            results = await Promise.all(@processSubmit(submit) for submit in submits)
-            needContinue = true
-            for r in results
-                needContinue = needContinue and r
-            if (page < @minPages - 1) # always load at least minPages pages
+
+        try
+            page = 0
+            while true
+                logger.info("Dowloading submits, page #{page}")
+                submits = await @baseDownloader.getSubmitsFromPage(page)
+                if not submits.length
+                    break
+                results = await Promise.all(@processSubmit(submit) for submit in submits)
                 needContinue = true
-            if not needContinue
-                break
-            page = page + 1
-            if page > @limitPages
-                break
-
-        tables = await Table.find({})
-        addedPromises = []
-        for uid, tmp of @dirtyUsers
-            logger.debug "Will process dirty user ", uid
-            addedPromises.push(@processDirty(uid))
-        await Promise.all(addedPromises)
+                for r in results
+                    needContinue = needContinue and r
+                if (page < @minPages - 1)  # always load at least minPages pages
+                    needContinue = true
+                if not needContinue
+                    break
+                page = page + 1
+                if page > @limitPages
+                    break
+        finally
+            tables = await Table.find({})
+            addedPromises = []
+            for uid, tmp of @dirtyUsers
+                logger.debug "Will process dirty user ", uid
+                addedPromises.push(@processDirty(uid))
+            await Promise.all(addedPromises)
         logger.info "Finish SubmitDownloader.run ", @minPages, '-', @limitPages

 class LastSubmitDownloader extends SubmitDownloader


Unblock method channelsList

Unblock method channelsList

--- server/methods/channelsList.coffee
+++ server/methods/channelsList.coffee
@@ -1,5 +1,7 @@
 Meteor.methods
 	channelsList: (filter, channelType, limit, sort) ->
+		this.unblock()
+
 		if not Meteor.userId()
 			throw new Meteor.Error 'error-invalid-user', 'Invalid user', { method: 'channelsList' }


fix initial slide

fix initial slide

--- client/main.coffee
+++ client/main.coffee
@@ -109,6 +109,9 @@
     @iframe = @$("iframe")
     @model.bind "change:id", => @reload()
     @model.bind "saved", => @reload()
+    @model.bind "initialfetch", (e) =>
+      if e.source is "db"
+        @reload()

   reload: ->


add a comment

add a comment

--- server/controllers/binaries.coffee
+++ server/controllers/binaries.coffee
@@ -45,7 +45,7 @@

             # Build file data
             fileData =
-                name: name
+                name: 'file'
                 "content-type": part.headers['content-type']

             # Update binary list set on given doc then save file to CouchDB
@@ -88,12 +88,12 @@

     form.on 'progress', (bytesReceived, bytesExpected) ->
-        # TODO handle progress

     form.on 'error', (err) ->
         next err

     form.on 'close', ->
+        # If no file was found, returns a client error.
         res.send 400, error: 'No file sent' if nofile
         next()


Fixed: wrong account identifier field

Fixed: wrong account identifier field

--- server/models/bankoperation.coffee
+++ server/models/bankoperation.coffee
@@ -21,8 +21,8 @@

 BankOperation.allFromBankAccountDate = (account, callback) ->
     params =
-        startkey: [account.uuid + "0"]
-        endkey: [account.uuid]
+        startkey: [account.accountNumber + "0"]
+        endkey: [account.accountNumber]
         descending: true
     BankOperation.request "allByBankAccountAndDate", params, callback


Add test case to test/test.coffee

Add test case to test/test.coffee

--- test/test.coffee
+++ test/test.coffee
@@ -55,3 +55,132 @@
         assert et.annotated.textContent is 'test 02'
       done()

+    it 'eval array', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 03
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        assert.deepEqual et.result, [0, 1, 2, 3]
+        assert et.annotation.textContent is 'test 03'
+        assert et.annotated.textContent is 'test 03'
+      done()
+
+    it 'eval object', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 04
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        assert.deepEqual et.result,
+          a: 'child'
+          b: 'child'
+          c:
+            d: 'nested child'
+        assert et.annotation.textContent is 'test 04'
+        assert et.annotated.textContent is 'test 04'
+      done()
+
+    it 'eval regexp', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 05
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        assert et.result.test 'regex search test     finish'
+        assert et.result.test 'regexppppppppp search   testfinish'
+        assert et.annotation.textContent is 'test 05'
+        assert et.annotated.textContent is 'test 05'
+      done()
+
+    it 'eval boolean', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 06
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        assert et.result is true
+        assert et.annotation.textContent is 'test 06'
+        assert et.annotated.textContent is 'test 06'
+      done()
+
+    it 'eval function object', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 07
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        a = Math.random()
+        b = Math.random()
+        assert et.result(a, b) is a + b
+        assert et.result.name is 'testFunc'
+        assert et.annotation.textContent is 'test 07'
+        assert et.annotated.textContent is 'test 07'
+      done()
+
+    it 'eval function call', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 08
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        assert et.result is true
+        assert et.annotation.textContent is 'test 08'
+        assert et.annotated.textContent is 'test 08'
+      done()
+
+    it 'eval immediately invoked function call', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 09
+      '''
+      evaluated = ET.evalBy '.et'
+      for et in evaluated
+        assert et.result is 'immediately invoked function test'
+        assert et.annotation.textContent is 'test 09'
+        assert et.annotated.textContent is 'test 09'
+      done()
+
+
+  describe 'eval annotation', ->
+
+    it 'with sibling element', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 10: annotation
+        test 10: annotated
+      '''
+      evaluated = ET.evalAnnotationsBy '.et-a'
+      for et in evaluated
+        assert et.result is 'annotation test'
+        assert et.annotation.textContent is 'test 10: annotation'
+        assert et.annotated.textContent is 'test 10: annotated'
+      done()
+
+    it 'without sibling element', (done) ->
+      document.body.appendChild div
+      div.innerHTML = '''
+        test 11: annotation
+      '''
+      evaluated = ET.evalAnnotationsBy '.et-a'
+      for et in evaluated
+        assert et.result is 'annotation test'
+        assert et.annotation.textContent is 'test 11: annotation'
+        assert et.annotated is null
+      done()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -3,7 +3,7 @@
 test "text moves up vertically", ->
   ;

-
+test "te"

 module()


Add family households

Add family households

--- source/javascripts/mappings.coffee
+++ source/javascripts/mappings.coffee
@@ -12,7 +12,6 @@
   'White population':         (d) -> d.b02001e2
   'Black population':         (d) -> d.b02001e3
   'Hispanic population':      (d) -> d.b03002e12
-  'Asian population':         (d) -> d.b02001e5
   'Percent white':            (d) -> d.b02001e2 / d.b01001e1 * 100
   'Percent black':            (d) -> d.b02001e3 / d.b01001e1 * 100
   'Percent hispanic':         (d) -> d.b03002e12 / d.b01001e1 * 100
@@ -26,7 +25,8 @@
   'Living in poverty':          (d) -> d.b17017e1
   'Median household Income':    (d) -> d.b19013e1
   'Receive public assistance':  (d) -> d.b19057e2
-  'Households > $200k income':  (d) -> d.b19001e17
+  'Households >= $200k income':  (d) -> d.b19001e17

   # Housing
-  'Percentage of vacant housing units': (d) -> d.b25004e1 / d.b25001e1 * 100
+  'Percentage of vacant housing units': (d) -> d.b25002e3 / d.b25001e1 * 100
+  'Family Households': (d) -> d.b11001e2


add total population mapping

add total population mapping


Deprecate requiring `Git`

Deprecate requiring `Git`

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -1,11 +1,16 @@
 {Point, Range} = require 'text-buffer'
+{deprecate} = require 'grim'

 module.exports =
   BufferedNodeProcess: require '../src/buffered-node-process'
   BufferedProcess: require '../src/buffered-process'
-  Git: require '../src/git'
+  GitRepository: require '../src/git'
   Point: Point
   Range: Range
+
+Object.defineProperty module.exports, 'Git', get: ->
+  deprecate "Please require `GitRepository` instead of `Git`: `{Git} = require 'atom'`"
+  module.exports.GitRepository

 # The following classes can't be used from a Task handler and should therefore
 # only be exported when not running as a child node process


Add help text to check a file

Add help text to check a file

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -867,12 +867,17 @@
     render_currently_selected: ->
         checked = @props.checked_files?.size ? 0
         total = @props.listing.length
+        style =
+            color      : '#999'
+            height     : '22px'
+            fontWeight : '200'
+
         if checked is 0
-

-                {"#{total} #{misc.plural(total, 'item')}"}
+

+                {"#{total} #{misc.plural(total, 'item')}"} -- Check an entry below to see options.

         else
-

+

                 {"#{checked} of #{total} #{misc.plural(total, 'item')} selected"}
                 <Space/>
                 {@render_select_entire_directory()}
@@ -946,7 +951,6 @@
                     <ButtonGroup>
                         {@render_check_all_button()}
                     </ButtonGroup>
-
                     {@render_action_buttons()}
                 </ButtonToolbar>
             </Col>


Commit of standard search before group/faceting search is implemented.

--- web/site/search.coffee
+++ web/site/search.coffee
@@ -16,6 +16,12 @@
     $scope.search = ->
       $location.search('query', $scope.query, 'offset', offset)

+#      We want the query to look something like this:
+#    facet=true&facet.field=content_type&facet.mincount=1&group=true&group.field=content_type&group.limit=10
+
+    # group.offset is offset
+    # group.limit is limit
+
 #    $scope.containersForVoume = (volume_id) ->
 #      $location.search('query', $scope.query, 'offset', offset)


adjusted font size, fixed the label click event (query version seems to be too slow)

--- components/inputs/KDInputViewWithPreview.coffee
+++ components/inputs/KDInputViewWithPreview.coffee
@@ -56,7 +56,7 @@
       @$("input.preview_checkbox").prop("checked",yes)

     @$("label").on "click",=>
-      @$("input.preview_checkbox").click()
+      @$("input.preview_checkbox").get(0).click()

   generatePreview:=>
     if @showPreview


Removing useless transition listing on lostFocus This transition is supposed to detect that a control was just added, and consequently end the edit for the controls in the "edit" state. But this never happens, because when a control is added, an HTML replacement is done, and all the controls loose their state.

--- src/resources/forms/orbeon/builder/resources/cell/placeholders.coffee
+++ src/resources/forms/orbeon/builder/resources/cell/placeholders.coffee
@@ -106,7 +106,6 @@
             { events: [ 'mouseExistsGridTd' ],      elements: 'elementsInContainer',  from: [ 'mock' ],                                                                                to: 'initial',                  actions: [ 'removeMock' ]                                   }
             { events: [ 'mouseExistsGridTd' ],      elements: 'elementsInContainer',  from: [ 'placeholder' ],                                                                          to: 'initial',                  actions: [ 'hidePlaceholder', 'removeMock'  ]               }
             { events: [ 'click' ],                  elements: 'elementClosest',       from: [ 'placeholder', 'mock' ],                                                                  to: 'wait-xhr-to-edit',         actions: [ 'storeSeqNo' ]                                   }
-            { events: [ 'controlAdded' ],           elements: 'elementsAll',          from: [ 'edit' ],                                                                                to: 'edit-done',                actions: [ 'endEdit', 'removeMock', 'fireEditDone' ]        }
             { events: [ 'controlAdded' ],           elements: 'labelsInContainer',    from: [ 'initial' ],                                                                              to: 'edit',                     actions: [ 'removeFor', 'createMock', 'startEdit' ]         }
             { events: [ 'ajaxResponse' ],           elements: 'elementsAll',          from: [ 'wait-xhr-to-edit' ],                 conditions: [ 'isNextSeqNo' ],                      to: 'edit',                     actions: [ 'startEdit' ]                                    }
             { events: [ 'enterKey', 'lostFocus' ],  elements: 'elementClosest',       from: [ 'edit' ],                                                                                to: 'edit-done',                actions: [ 'endEdit', 'removeMock', 'fireEditDone' ]        }


Fix loading of Rich Text page in Test Controls

--- services/web/public/coffee/ide/test-controls/controllers/TestControlsController.coffee
+++ services/web/public/coffee/ide/test-controls/controllers/TestControlsController.coffee
@@ -11,4 +11,6 @@
 			window.openLinkedFileModal()

 		$scope.richText = () ->
-			window.location.href = window.location.toString() + '&rt=true'
+			current = window.location.toString()
+			target = "#{current}#{if window.location.search then '&' else '?'}rt=true"
+			window.location.href = target


Support single number for rect

Support single number for rect

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -737,6 +737,8 @@
 		return args[0]
 	if _.isObject(args)
 		return args
+	if _.isNumber(args)
+		return {top:args, right:args, bottom:args, left:args}

 	return {}


If we can zoom, highlight

If we can zoom, highlight

--- static/coffee/v2_locations.coffee
+++ static/coffee/v2_locations.coffee
@@ -111,6 +111,9 @@
       info.update(layer.feature.properties)

     clickFeature = (e) ->
+      if (not districtZoom and not wardZoom)
+        highlight(e)
+        return
       if (districtZoom and e.target.feature.properties.level == STATE_LEVEL)
         map.removeLayer(states)
         loadBoundary(url, e.target.feature.properties, e.target)


Support options to tikzpicture env

Support options to tikzpicture env

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -990,7 +990,7 @@
     comment: 'The manual shows it can take an optional argument that may be several lines. This will be ignored for now.'
     name: 'meta.environment.embedded.$5.latex'
     contentName: 'source.embedded.tikz'
-    begin: '((\\\\)begin)(\\{)((tikzpicture)\\*?)(\\})'
+    begin: '((\\\\)begin)(\\{)((tikzpicture)\\*?)(\\})(?:(\\[)(.*?)(\\]))?'
     beginCaptures:
       1: name: 'support.function.environment.begin.latex'
       2: name: 'punctuation.definition.function.begin.latex'
@@ -998,6 +998,14 @@
       4: name: 'variable.parameter.environment.$5.latex'
       # 5: Used for meta scope name (basically just non starred version)
       6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8:
+        name: 'variable.parameter.optional.function.environment.tikz.latex'
+        patterns: [
+          { include: '#tikzOptions' }
+          { include: '$self' }
+        ]
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
     end: '((\\\\)end)(\\{)(\\4)(\\})'
     endCaptures:
       1: name: 'support.function.environment.end.latex'
@@ -1137,6 +1145,10 @@
     match: '(\\d*\\.\\d+|(\\d+\\.)?\\d*)'
     name: 'constant.numeric.math.latex'
   }
+  tikzOptions: {
+    comment: 'Specifically for the options that can be given to tikz. Placeholder for now'
+    match: '$impossible'
+  }

 # Unfortunate necessity (mainly making a unique grammar for each coding language)
   mintGeneric: {


Look only for "agda" in data-grammar

Look only for "agda" in data-grammar

--- keymaps/agda-mode.cson
+++ keymaps/agda-mode.cson
@@ -7,7 +7,7 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/behind-atom-keymaps-in-depth
-'atom-text-editor[data-grammar="source agda"]':
+'atom-text-editor[data-grammar~="agda"]':
     'ctrl-c        ctrl-l': 'agda-mode:load'
     # invoke input method
     '\\': 'agda-mode:input-symbol'
@@ -21,12 +21,6 @@
     '\"' : 'agda-mode:input-symbol-double-quote'
     '\'' : 'agda-mode:input-symbol-single-quote'
     '\`' : 'agda-mode:input-symbol-back-quote'
-
-'atom-text-editor[data-grammar="source lagda"]':
-    'ctrl-c        ctrl-l': 'agda-mode:load'
-    # invoke input method
-    '\\': 'agda-mode:input-symbol'
-    'alt-/': 'agda-mode:input-symbol'

 'atom-text-editor.agda':
     # Global commands


bump version to 1.2.1 in breadcrumbsEatr.coffee

--- src/coffee/breadcrumbsEatr.coffee
+++ src/coffee/breadcrumbsEatr.coffee
@@ -2,7 +2,7 @@
  * BreadcrumbsEatr - jQuery plugin that transforms a breadcrumbs to a responsive one. Useful when making responsive websites.
  * Copyright(c) Exequiel Ceasar Navarrete <exequiel.navarrete@ninthdesign.com>
  * Licensed under MIT
- * Version 1.2.0
+ * Version 1.2.1
 ###
 (($, window, document, undefined_) ->
     "use strict"


teardown log

teardown log

--- src/coffee/compose/actions.coffee
+++ src/coffee/compose/actions.coffee
@@ -35,15 +35,15 @@
     eventEmitter

   stop: (instance, data) ->
+    eventEmitter = new events.EventEmitter()
     [scriptDir, scriptPath] = buildScriptPaths instance

-    cb = (data) ->
-      console.log 'dc down', data.toString()
+    env = buildEnv config, data
+    emitLogCb = (data) -> eventEmitter.emit 'teardown-log', data.toString()

-    env = buildEnv config, data
-
-    runCmd 'docker-compose', ['-f', scriptPath, '-p', instance, 'down', '--remove-orphans'], env, {stderr: cb}, ->
+    runCmd 'docker-compose', ['-f', scriptPath, '-p', instance, 'down', '--remove-orphans'], env, {stderr: emitLogCb}, ->
       console.log 'Done, stopped', instance
+    eventEmitter

 #
 # Helper functions to write files and run processes


Bump the version to 1.0.2

Bump the version to 1.0.2

--- src/coffeescripts/toast.js.coffee
+++ src/coffeescripts/toast.js.coffee
@@ -8,7 +8,7 @@
 OPTIONS = ['duration', 'content', 'id', 'className', 'animate', 'allowHTML']

 class window.Toast
-  @VERSION: '1.0.1'
+  @VERSION: '1.0.2'

   constructor: (options = {}) ->
     @tc = new TransitionCallbacks(clearAll: true, timeout: 500)


allow customizing the label

allow customizing the label

--- src/components/navbar/logout.cjsx
+++ src/components/navbar/logout.cjsx
@@ -7,6 +7,12 @@
 CSRF_Token = CurrentUserStore.getCSRFToken()

 LogoutLink = React.createClass
+
+  propTypes:
+    label: React.PropTypes.string
+
+  getDefaultProps: ->
+    label: 'Log Out'

   onLinkClick: (ev) ->
     ev.currentTarget.querySelector('form').submit()
@@ -24,7 +30,7 @@
           method='post'>
           <input type='hidden' name='_method' value='delete'/>
           <input type='hidden' name='authenticity_token' value={CSRF_Token}/>
-          <input type='submit' aria-label="Log Out" value='Log Out' />
+          <input type='submit' aria-label={@props.label} value={@props.label} />
         </form>
       </a>
     </li>


"the double tap"

"the double tap"

--- src/loaders/AssetLoader.coffee
+++ src/loaders/AssetLoader.coffee
@@ -32,11 +32,12 @@
 	# Starts to load all the assets
 	load: () ->
 		for asset in @assets
-			filetype = (asset.url.split ".").pop().toLowerCase()
-			loader = @assetsTypes[filetype] or throw new Error "[Rotten.AssetLoader] Filetype '#{filetype}' is not supported"
-			loader = new loader asset
-			loader.listen "loaded", do(asset) => @assetLoaded asset
-			loader.load()
+			do (asset) =>
+				filetype = (asset.url.split ".").pop().toLowerCase()
+				loader = @assetsTypes[filetype] or throw new Error "[Rotten.AssetLoader] Filetype '#{filetype}' is not supported"
+				loader = new loader asset
+				loader.listen "loaded", => @assetLoaded asset
+				loader.load()

 	# Called when an asset has just been loaded


Closes #40 process next queue item after connection timeout

--- server/common/dal/queryQueue.coffee
+++ server/common/dal/queryQueue.coffee
@@ -27,7 +27,7 @@

     request.on 'row', (columns) ->
       obj = {}
-
+
       columns.forEach (column) ->
         obj[column.metadata.colName] = column.value

@@ -37,6 +37,8 @@
       if err
         console.log 'error with sql connection ' + err
         nextQuery.cb err
+        #recursive call to process anything else on the queue
+        processQueryQueue()
       else
         conn.execSql request

@@ -63,4 +65,4 @@

 module.exports =
   runQuery: runQuery
-  _processQueryQueue: processQueryQueue+  _processQueryQueue: processQueryQueue


Refactor file upload directive

Refactor file upload directive

Fixes #27 and #84

Added
`mac-upload-previews` to allow upload previews
`mac-upload-progress` callback on upload progress

Deprecated
`mac-upload-selector`
`mac-upload-enable-on`
`mac-upload-disable-on`
`mac-upload-disabled` - should use `ng-disable` instead
`mac-upload-param-name`

Breaking changes
Besides of deprecated attributes, file upload directive should work the same.

--- src/directives/file_upload.coffee
+++ src/directives/file_upload.coffee
@@ -7,107 +7,169 @@
 @dependencies
 - jQuery file upload

-@param {String}   mac-upload-route      The route we're uploading our files
-@param {Function} mac-upload-submit     Function to call on submit
-@param {Function} mac-upload-success    Upload success callback
-@param {Function} mac-upload-error      Upload error callback
-@param {String}   mac-upload-selector   Selector to proxy clicking on file upload
-@param {String}   mac-upload-drop-zone  The selector that we can drop files onto
-@param {String}   mac-upload-enable-on  The broadcast message to catch to enable file upload
-@param {String}   mac-upload-disable-on The broadcast message to catch to disable file upload
-@param {Boolean}  mac-upload-disabled   Boolean value to disable or enable file upload
-@param {String}   mac-upload-form-data  Additional form data
-@param {String}   mac-upload-param-name Parameter name for the file
+@param {String}     mac-upload-route      File upload route
+@param {Function}   mac-upload-submit     Function to call on submit
+@param {Function}   mac-upload-success    Upload success callback
+@param {Function}   mac-upload-error      Upload error callback
+@param {Expression} mac-upload-previews   List of uploaded files {Array}
+@param {Function}   mac-upload-progress   Upload progress callback
+@param {String}     mac-upload-drop-zone  The selector that we can drop files onto
+@param {Expression} mac-upload-form-data  Additional form data {Array|Object|Function|FormData}
 ###

-angular.module("Mac").directive "macUpload", [ ->
-  scope:
-    macUploadSubmit:    "&macUploadSubmit"
-    macUploadError:     "&macUploadError"
-    macUploadSuccess:   "&macUploadSuccess"
-    macUploadRoute:     "=macUploadRoute"
-    macUploadEnableOn:  "=macUploadEnableOn"
-    macUploadDisableOn: "=macUploadDisableOn"
-    macUploadDisabled:  "=macUploadDisabled"
-    macUploadFormData:  "=macUploadFormData"
-    macUploadParamName: "=macUploadParamName"
+angular.module("Mac").
+directive("macUpload", ["$rootScope", "$parse", ($rootScope, $parse) ->
+  require:    ["macUpload", "?macUploadPreviews"]
+  controller: ["$scope", ->]
+  link:       ($scope, element, attrs, ctrls) ->
+    uploadCtrl  = ctrls[0]
+    previewCtrl = ctrls[1]

-  link: (scope, element, attributes) ->
-    isInitialized = false
-    disableOn     = attributes.macUploadDisableOn if attributes.macUploadDisableOn?
-    enableOn      = attributes.macUploadEnableOn  if attributes.macUploadEnableOn?
-    parent        = element.parent()
-    input         = parent.find "input"
+    setOptions = (option, value) ->
+      element.fileupload("option", option, value) if value?

-    initialize = ->
-      return if (attributes.macUploadDisabled? and scope.macUploadDisabled) or not scope.route
+    applyCallback = (action, $event, $data) ->
+      callbackFn = $parse attrs["macUpload#{action}"]
+      if callbackFn?
+        $scope.$apply ->
+          $status        = $data.jqXHR?.status
+          args           = {$event, $data, $status}
+          args.$response = $data.result.data if action is "Success"
+          callbackFn $scope, args

-      input.fileupload "destroy" if isInitialized
-      options =
-        url:              scope.route
-        replaceFileInput: true
+    options =
+      url:    $parse(attrs.macUploadRoute)($scope) or ""
+      submit: ($event, $data) ->
+        submitEvent = -> applyCallback "Submit", $event, $data

-        add: (event, data) ->
-          data.submit()
+        # Handle previews.
+        if previewCtrl?
+          previewCtrl.add $data.files, submitEvent
+        else
+          submitEvent()

-        submit: (event, response) ->
-          if attributes.macUploadSubmit?
-            scope.$apply scope.macUploadSubmit $event: event, $response: response
+      fail:     ($event, $data) -> applyCallback "Error", $event, $data
+      done:     ($event, $data) -> applyCallback "Success", $event, $data
+      progress: ($event, $data) ->
+        previewCtrl?.updateProgress? $data
+        applyCallback "Progress", $event, $data

-        error: (response, status) ->
-          if attributes.macUploadError?
-            responseObject = {}
-            for own key, value of response
-              unless typeof value is "function"
-                responseObject[key] = value
+    if attrs.macUploadDropZone?
+      $(document).on "drop dragover", (event) -> event.preventDefault()

-            args =
-              $response: responseObject
-              $data:     response.data
-              $status:   status
+      # Add and remove droppable class
+      dragoverTimeout = null
+      dropZone        = element.parents attrs.macUploadDropZone

-            scope.$apply scope.macUploadError args
+      $(document).bind "dragover", (event) ->
+        clearTimeout(dragoverTimeout) if dragoverTimeout?

-        success: (response, status) ->
-          if attributes.macUploadSuccess?
-            scope.$apply scope.macUploadSuccess $response: response, $data: response.data, $status: status
+        node   = $(event.target).parents attrs.macUploadDropZone
+        method = if node.length then "addClass" else "removeClass"
+        dropZone[method] "droppable"

-      options.dropZone  = if attributes.macUploadDropZone? then $(attributes.macUploadDropZone) else null
-      options.formData  = scope.macUploadFormData if attributes.macUploadFormData?
-      options.paramName = scope.macUploadParamName if attributes.macUploadParamName?
+        dragoverTimeout = setTimeout ->
+          clearTimeout(dragoverTimeout) if dragoverTimeout?
+          dropZone.removeClass "droppable"
+        , 250

-      input.fileupload options
-      isInitialized = true
+    options.dropZone = dropZone

-    if attributes.macUploadSelector?
-      # Clicks on the usually hidden upload file input field via another selector
-      element.parents(attributes.macUploadSelector).click (event) -> element.click()
+    element.fileupload(options).
+    on([
+      'fileuploadadd',
+      'fileuploadsubmit',
+      'fileuploadsend',
+      'fileuploaddone',
+      'fileuploadfail',
+      'fileuploadalways',
+      'fileuploadprogress',
+      'fileuploadprogressall',
+      'fileuploadstart',
+      'fileuploadstop',
+      'fileuploadchange',
+      'fileuploadpaste',
+      'fileuploaddrop',
+      'fileuploaddragover',
+      'fileuploadchunksend',
+      'fileuploadchunkdone',
+      'fileuploadchunkfail',
+      'fileuploadchunkalways',
+      'fileuploadprocessstart',
+      'fileuploadprocess',
+      'fileuploadprocessdone',
+      'fileuploadprocessfail',
+      'fileuploadprocessalways',
+      'fileuploadprocessstop'
+    ].join(' '), (event, data) ->
+      $scope.$emit event.type, data
+    )

-      # Stops the child element from going into an infinite click loop
-      element.click (event) ->
-        $rootScope.$broadcast "clickedOnAttachment"
-        event.stopPropagation()
+    $scope.$watch attrs.macUploadRoute,     (route) -> setOptions "url", route
+    $scope.$watch attrs.macUploadFormData,  (value) -> setOptions "formData", value
+]).

-    # Disable the browser's default drag and drop behavior so we can have our own.
-    $(document).on "drop dragover", (event) -> event.preventDefault()
+directive("macUploadPreviews", ["$rootScope", ($rootScope) ->
+  restrict:   "A"
+  require:    ["macUploadPreviews", "macUpload"]
+  controller: ["$scope", "$attrs", "$parse", ($scope, $attrs, $parse) ->
+    @previews = (value) ->
+      previewsGet = $parse $attrs.macUploadPreviews
+      previewsSet = previewsGet.assign

-    scope.$watch "macUploadDisabled", (isDisabled) ->
-      return unless isDisabled?
-      if isDisabled
-        input.prop("disabled", "disabled")
+      if value?
+        previewsSet $scope, value
       else
-        input.removeProp "disabled"
-      initialize()
+        previewsGet $scope

-    scope.$watch "macUploadFormData", (value) ->
-      if value? and isInitialized
-        input.fileupload "options", formData: value
+    @getByFilename = (filename) ->
+      previews = @previews() or []
+      for i in [previews.length - 1..0] by -1
+        preview = previews[i]
+        return preview if preview.fileName is filename

-    scope.$on(disableOn, -> input.fileupload "disable") if disableOn?
-    scope.$on(enableOn,  -> input.fileupload "enable")  if enableOn?
+    @add = (files = [], callback) ->
+      for file in files
+        reader = new FileReader

-    scope.$watch "macUploadRoute", (route) ->
-      return unless route
-      scope.route = route
-      initialize()
+        pushToPreviews = (event, state) ->
+          previews = @previews()
+          if previews?
+            newFile =
+              fileName: file.name
+              type:     file.type
+              data:     event.target.result
+            previews.push newFile
+            @previews previews
+
+          callback? newFile
+
+        reader.onload  = (event) => pushToPreviews.apply this, [event, "load"]
+        reader.onerror = (event) => pushToPreviews.apply this, [event, "error"]
+
+        reader.readAsDataURL file
+
+    return
   ]
+  link: ($scope, element, attrs, ctrls) ->
+    previewCtrl = ctrls[0]
+]).
+
+directive("macUploadProgress", [->
+  restrict:   "A"
+  require:    ["macUploadProgress", "macUploadPreviews"]
+  controller: ["$scope", ($scope) ->
+    updateProgress = (data) ->
+      preview           = @getByFilename data.files[0].name
+      preview?.progress = parseInt(data.loaded / data.total * 100, 10)
+
+    # Extending preview controller with progress
+    @updatePreviewCtrl = (ctrl) ->
+      ctrl.updateProgress = updateProgress
+  ]
+  link: ($scope, element, attrs, ctrls) ->
+    progressCtrl = ctrls[0]
+    previewsCtrl = ctrls[1]
+
+    progressCtrl?.updatePreviewCtrl previewsCtrl
+])


FileUploadButton: Fixed enable / disabled click state.

--- src/elements/Button/Button.coffee
+++ src/elements/Button/Button.coffee
@@ -278,7 +278,6 @@
 					return

 				ev.stopPropagation()
-				ev.preventDefault()
 				@onClickAction(ev)
 				return

@@ -382,6 +381,7 @@

 	onClickAction: (ev) ->
 		if @__disabled # or ev.button != 0
+			ev.preventDefault()
 			return

 		@getTooltip()?.hide(ev)


Typo.

Typo.

--- src/routing/hash_bang_navigator.coffee
+++ src/routing/hash_bang_navigator.coffee
@@ -26,6 +26,7 @@
     window.location.hash = link

   replaceState: (stateObject, title, path) ->
+    loc = window.location
     link = @linkTo(path)
     return if link == loc.hash


Hack to make textarea work

Hack to make textarea work

--- src/server/browser_server/index.coffee
+++ src/server/browser_server/index.coffee
@@ -393,8 +393,7 @@
             # Hack for textarea, as it doesn't have a value attribute
             # in the DOM.
             if target.tagName.toLowerCase() is "textarea" and
-            attribute is "value"
-                target.value = value
+            attribute is "value" then target.value = value
             else target.setAttribute(attribute, value)
             @setByClient = null


more incr timeout for text file api tests

--- src/smc-hub/test/api/text_files.coffee
+++ src/smc-hub/test/api/text_files.coffee
@@ -26,7 +26,7 @@
                 done(err)

     it "creates a text file in a project", (done) ->
-        @timeout(15000)
+        @timeout(20000)
         api.call
             event : 'write_text_file_to_project'
             body  :
@@ -39,7 +39,7 @@
                 done(err)

     it "reads a text file in a project", (done) ->
-        @timeout(15000)
+        @timeout(20000)
         api.call
             event : 'read_text_file_from_project'
             body  :


handle an edge race condition noticed in prod (that was harmless-ish)

--- src/smc-project/jupyter/jupyter.coffee
+++ src/smc-project/jupyter/jupyter.coffee
@@ -493,6 +493,9 @@
                 @_call(opts)

     _call: (opts) =>
+        if not @_channels?
+            opts.cb('not running (no channels defined)')
+            return
         message =
             header:
                 msg_id   : misc.uuid()


sendEvent() should not set values, do that in setAttribute() instead. Use sendEvent instead of trigger() internally

--- layout.coffee
+++ layout.coffee
@@ -245,7 +245,7 @@

       @_bindConstraints() if @constraints
       # console.log 'new node', @, attributes
-      @trigger('init', @) if @events?['init']
+      @sendEvent('init', @)

     _applyConstraint: (name, expression) ->
       @constraints ?= {}
@@ -268,9 +268,11 @@
       return

     sendEvent: (name, value) ->
-      @[name] = value
       # send event
-      @trigger(name, value, @) if @events?[name]
+      if @events?[name]
+        @trigger(name, value, @)
+      # else
+      #   console.log 'no event named', name, @events, @

     setAttribute: (name, value) ->
       # TODO: add support for dynamic constraints
@@ -291,6 +293,8 @@
         if setter of @
           # console.log 'calling setter', setter, value, @[setter]
           @[setter](value)
+
+        @[name] = value

       @sendEvent(name, value)
       @
@@ -325,7 +329,7 @@
         # store references to parent and children
         parent[@name] = @ if @name
         parent.subnodes.push(@)
-        parent.trigger('subnodes', @) if parent.events?['subnodes']
+        parent.sendEvent('subnodes', @)

     set_name: (name) ->
       # console.log 'set_name', name, this
@@ -341,7 +345,7 @@

     destroy: (skipevents) ->
       # console.log 'destroy node', @
-      @trigger('destroy', @) if events?['destroy']
+      @sendEvent('destroy', @)

       if @listeningTo
         @stopListening()
@@ -389,6 +393,7 @@
       # console.log('setStyle', name, value, @el)
       @el.style[name] = value
       # @jqel.css(name, value)
+
     set_parent: (parent) ->
       if parent instanceof Sprite
         parent = parent.el
@@ -458,7 +463,7 @@
       # store references subviews
       if parent instanceof View
         parent.subviews.push(@)
-        parent.trigger('subviews', @) if parent.events?['subviews']
+        parent.sendEvent('subviews', @)
         parent = parent.sprite

       @sprite.set_parent parent
@@ -674,7 +679,7 @@
     added: (child) =>
       # console.log 'added layout', child, @
       if child
-        @trigger('subview', child) if @events?['subview']
+        @sendEvent('subview', child)
       @update(null, child)

     # override to update the position of the parent view's children


fix: prevent several session pages

fix: prevent several session pages

Now we check if a session page is already up (in the frame of the
automatic session update, not the manual updates). If yes then we don't
display the page, if not then we display it. Like that even after hours
of idle time, there should be at most one session tab.

--- src.coffee/userStudyToolbox.coffee
+++ src.coffee/userStudyToolbox.coffee
@@ -3,6 +3,7 @@
 class window.mem0r1es.UserStudyToolbox

   constructor : (@storageManager)->
+    @sessionPageDisplayed = 0
     @currentCount = 0
     @initLastDump()
     @dumpServerURL = "http://127.0.0.1:8080/"
@@ -46,6 +47,7 @@
     return

   saveSession : (messageContent, sendResponse) =>
+    @sessionPageDisplayed = false
     @storageManager.store "userStudySessions", messageContent, () =>
       @updateLastActivityTime()
       localStorage.setItem 'lastUserStudySessionId', messageContent.userStudySessionId
@@ -65,8 +67,9 @@
     return lastActivityTime

   checkIfNeedNewContext : () ->
-    if (new Date().getTime() - @getLastActivityTime())>10*1000*60
+    if (new Date().getTime() - @getLastActivityTime())>10*1000*60 and not @sessionPageDisplayed
       chrome.tabs.create {'url': chrome.extension.getURL('html/sessionInfo.html'), pinned:true}
+      @sessionPageDisplayed = true
     @updateLastActivityTime()

   countDumpedData : (sendResponse) ->


Flush redis after configuration changes that could affect cache integrity.

--- lib/db.coffee
+++ lib/db.coffee
@@ -27,6 +27,29 @@
 objectsCollection = null

+# Only flush redis DB if aliases or custom commands config has changed
+flushRedis = (callback) ->
+  redisClient.get('config_hash', (err, res) ->
+    return callback(err) if err
+
+    hash = require('crypto')
+      .createHash('md5')
+      .update(JSON.stringify(config.PARAMETERS))
+      .update(JSON.stringify(config.CUSTOM_COMMANDS))
+      .digest('hex')
+
+    if res == hash
+      return callback()
+
+    redisClient.flushdb((err) ->
+      return callback(err) if err
+      redisClient.set('config_hash', hash, (err, res) ->
+        return callback(err)
+      )
+    )
+  )
+
+
 connect = (callback) ->
   callbackCounter = 6
   mongodb.MongoClient.connect(config.MONGODB_CONNECTION_URL, {db:{w:1},server:{autoReconnect:true}}, (err, db) ->
@@ -72,9 +95,15 @@

     exports.redisClient = redisClient = redis.createClient(config.REDIS_PORT, config.REDIS_HOST)
     redisClient.select(config.REDIS_DB, (err) ->
-      if --callbackCounter == 0 or err
-        callbackCounter = 0
-        return callback(err)
+      if err
+        callbackCounter = 0
+        return callback(err)
+
+      flushRedis((err) ->
+        if --callbackCounter == 0 or err
+          callbackCounter = 0
+          return callback(err)
+      )
     )
   )


fix sorting function for all cases

fix sorting function for all cases

--- csi.coffee
+++ csi.coffee
@@ -358,7 +358,12 @@

         # get the css text from the css object compiled during the build
         compileCss = (allCss) ->
-          ordered = (css for n,css of allCss).sort (a, b) -> a.order > b.order
+          ordered = (css for n,css of allCss).sort (a, b) ->
+            if a.order > b.order
+                return 1
+            if a.order < b.order
+                return -1
+            return 0
           (css.contents for css in ordered).join("\n")

         # return the contents of `filename` w/ the css url(...) paths


small correction to file_changed expose

small correction to file_changed expose

--- lib/api/compile.coffee
+++ lib/api/compile.coffee
@@ -49,7 +49,7 @@
     __track('api', { name: 'compile' })

     if opts
-      @file_changed = opts.fileChanged
+      @roots.file_changed = opts.fileChanged

     @roots.emit('start')


check for currentProviderIdx; unregister provider correctly; added coffeescript settings

--- lib/atom-ternjs.coffee
+++ lib/atom-ternjs.coffee
@@ -7,10 +7,10 @@
 class AtomTernInitializer

   disposables: []
-  grammars: ['JavaScript', 'CoffeeScript']
+  grammars: ['JavaScript']
   client: null
   documentationView: null
-  currentProviderIdx: 0
+  currentProviderIdx: false
   activeTextEditor: null

   # autocomplete
@@ -20,6 +20,11 @@

   # config
   config:
+    coffeeScript:
+      title: 'CoffeeScript'
+      description: 'Completions for CoffeeScript.'
+      type: 'boolean'
+      default: false
     docs:
       title: 'Documentation'
       description: 'Whether to include documentation strings, urls, and origin files (if found) in the result data.'
@@ -95,6 +100,15 @@
       console.error 'error', err

   registerEvents: ->
+    @disposables.push atom.config.observe 'atom-ternjs.coffeeScript', =>
+      if atom.config.get('atom-ternjs.coffeeScript')
+        @grammars.push 'CoffeeScript'
+        # TODO: register editors
+      else
+        idx = @grammars.indexOf('CoffeeScript')
+        return unless idx > -1
+        @grammars.splice(idx, 1)
+        # TODO: unregister editors
     @disposables.push atom.workspace.onDidOpen (e) =>
       return unless e.item and @isValidEditor(e.item)
       @startServer()
@@ -108,6 +122,7 @@
     return true

   setCurrentProvider: ->
+    @currentProviderIdx = false
     @activeTextEditor = atom.workspace.getActiveTextEditor()
     return unless @activeTextEditor
     for provider, idx in @providers
@@ -140,6 +155,7 @@
   unregisterEditor: (editor) ->
     provider = @getProviderForEditor(editor)
     return unless provider
+    @autocompletePlus.unregisterProvider provider
     provider.dispose()
     idx = @providers.indexOf(provider)
     @providers.splice(idx, 1)
@@ -164,6 +180,7 @@
     atom.commands.add 'atom-text-editor', 'tern:definition': (event) =>
         @findDefinition()
     atom.commands.add 'atom-text-editor', 'tern:startCompletion': (event) =>
+      return if @currentProviderIdx is false
       @providers[@currentProviderIdx].callPreBuildSuggestions(true)
     atom.commands.add 'atom-text-editor', 'tern:stop': (event) =>
       @stopServer()


fix Em.Handlebars

fix Em.Handlebars

--- app/assets/javascripts/controllers/transmogrification_edit_controller.js.coffee
+++ app/assets/javascripts/controllers/transmogrification_edit_controller.js.coffee
@@ -38,6 +38,7 @@
       when 'Ember Handlebars'
         try
           template = Em.Handlebars.compile source
+          data = {buffer:[]}
           data.view = {registerObserver:Em.K,appendChild:Em.K}
           output = template(context,{data:data})
           data.buffer.join('')


rollback transaction only when closing record

--- app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
@@ -61,6 +61,8 @@

   actions:
     closeError: ->
+      @get('store').get('defaultTransaction').rollback()
+      @clearModifiedFlags()
       @transitionToParentController()

     toggleUpdatesVisible: ->
@@ -155,7 +157,7 @@
       shipment.setProperties({'_destroyed': true, '_modified': true})

     cancelShipmentEdit: (shipment) ->
-      @get('store').get('defaultTransaction').rollback()
       shipment.setProperties(shipment.get('data'))
+      shipment.set('_modified', false)
       @set('currentShipment', null)
       $('.shipment-form-modal').modal('hide')


contextmenu: require necessary SDK files

--- lib/contextmenu.coffee
+++ lib/contextmenu.coffee
@@ -1,3 +1,6 @@
+{ browserWindows } = require('sdk/windows')
+{ viewFor } = require('sdk/view/core')
+
 # Base class for everything concerning context menus
 # @abstract
 class MenuObjectBase


DE16995 - Iteration summary app doesn't display test results from within Test Sets

--- iterationsummary/IterationSummaryAppSpec.coffee
+++ iterationsummary/IterationSummaryAppSpec.coffee
@@ -669,6 +669,19 @@
                 Pass: 1
                 Fail: 1
                 "Hello Kitty": 1
+        }
+      ])
+
+    _prepareTestSetData: (testSetSummaryVerdicts) ->
+      @ajax.whenQuerying('userstory').respondWith()
+      @ajax.whenQuerying('defectsuite').respondWith()
+      @ajax.whenQuerying('defect').respondWith()
+
+      @ajax.whenQuerying('testset').respondWith([
+        {
+          Summary:
+            TestCases:
+              LastVerdict: testSetSummaryVerdicts
         }
       ])

@@ -755,6 +768,15 @@
       expect(configDefects.message).toBe app.self.PAST_WITH_DEFECTS

       expect(testsSpy).not.toHaveBeenCalled()
+
+  it "does aggregate testset data for UE or EE subscriptions", ->
+    testPassingSelector = '.x4-component.header.testsPassing'
+
+    @_prepareTestSetData(Pass: 2, Fail: 1, Inconclusive: 1)
+    @_createApp({}).then (app) =>
+      @waitForVisible(css: testPassingSelector).then =>
+        expect(Ext.DomQuery.selectNode(testPassingSelector).innerHTML).toContain "50% Tests Passing"
+

   it "refreshes app on objectUpdate of artifacts", ->
     @_createApp({}).then (app) =>


shortened error message

shortened error message

--- src/rules/camel_case_classes.coffee
+++ src/rules/camel_case_classes.coffee
@@ -7,7 +7,7 @@
     rule:
         name: 'camel_case_classes'
         level : 'error'
-        message : 'Class names should be UpperCamelCased (aka PascalCased)'
+        message : 'Class names should be UpperCamelCased'
         description: """
             This rule mandates that all class names are UpperCamelCased. Camel
             casing class names is a generally accepted way of distinguishing


De-lint.

De-lint.

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -110,7 +110,7 @@
         return @remoteName().then (remoteName) ->
           ["No URL defined for remote: #{remoteName}"]

-      @gitHubRepoUrl().then (gitHubRepoUrl) =>
+      @gitHubRepoUrl().then (gitHubRepoUrl) ->
         unless gitHubRepoUrl
           return ["Remote URL is not hosted on GitHub: #{gitUrl}"]