Reorganize weather.coffee

Reorganize weather.coffee

--- homedisplay/info_weather/static/js/weather.coffee
+++ homedisplay/info_weather/static/js/weather.coffee
@@ -76,21 +76,41 @@
     else
       return ""

+  processWarnings = (data) ->
+    warnings = weather.find ".weather-warnings"
+
+    sea = data.sea.replace new RegExp("
", "g"), ""
+    warnings_html = ""
+    warnings_data =
+      "female": data.pedestrian
+      "ship": sea
+      "car": data.road
+      "pagelines": data.land
+    for own icon, warning of warnings_data
+        if warning == "Ei varoituksia."
+          continue
+        if warning == "Jalankulkukeli on tavanomainen koko maassa."
+          continue
+        warnings_html += """ #{warning}"""
+    warnings.html warnings_html
+
+  processSunInfo = (data) ->
+    for own key, value of data
+      sunrise = moment value.sunrise, "YYYYMMDDTHHmmss"
+      sunset = moment value.sunset, "YYYYMMDDTHHmmss"
+
+      weather.find(".sunrise").html """ #{sunrise.format("HH:mm")}        (#{sunrise.fromNowSynced()})"""
+      weather.find(".sunset").html """ #{sunset.format("HH:mm")} (#{sunset.fromNowSynced()})"""
+
+
   processData = (data) ->
     resetWeatherInfo()

     if data.main_forecasts.suninfo?
       suninfo = data.main_forecasts.suninfo
-      for own key, value of suninfo
-        sunrise = moment value.sunrise, "YYYYMMDDTHHmmss"
-        sunset = moment value.sunset, "YYYYMMDDTHHmmss"
-
-        weather.find(".sunrise").html """ #{sunrise.format("HH:mm")}        (#{sunrise.fromNowSynced()})"""
-        weather.find(".sunset").html """ #{sunset.format("HH:mm")} (#{sunset.fromNowSynced()})"""
-
-    current_index = 1
-    first_date = false
-    new_day = false
+
+    if data.main_warnings?
+      processWarnings data.main_warnings.warnings

     if data.main_forecasts? and data.main_forecasts.observations?
       for own key, value of data.main_forecasts.observations
@@ -132,17 +152,6 @@
     current_day = null
     days = 0

-    if data.main_warnings?
-      w = data.main_warnings.warnings
-      warnings = weather.find ".weather-warnings"
-
-      sea = w.sea.replace new RegExp("
", "g"), ""
-      console.log sea
-      warnings.html """ #{w.pedestrian}
-                        #{sea}
-                        #{w.road}
-                        #{w.land}
-                       """

     jq.each data.main_forecasts.forecasts[0].forecast, ->
       timestamp = moment @localtime, "YYYYMMDDTHHmmss"


Forward the crashed event to BrowserWindow, fix #2201

--- atom/browser/api/lib/browser-window.coffee
+++ atom/browser/api/lib/browser-window.coffee
@@ -25,6 +25,10 @@
   @webContents.on 'activate', =>
     if process.platform isnt 'darwin' and @isMenuBarAutoHide() and @isMenuBarVisible()
       @setMenuBarVisibility false
+
+  # Forward the crashed event.
+  @webContents.on 'crashed', =>
+    @emit 'crashed'

   # Redirect focus/blur event to app instance too.
   @on 'blur', (event) =>


read-only viewing of public html files and ipython notebooks was broken

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -773,7 +773,7 @@

         # This approach to public "editor"/viewer types is temporary.
         if extra_opts.public_access
-            if editor_name == 'codemirror' and filename_extension(filename) == 'html'
+            if filename_extension(filename) == 'html'
                 if opts.content.indexOf("#ipython_notebook") != -1
                     editor = new IPythonNBViewer(@, filename, opts.content, extra_opts)
                 else


just add xvd before disk letter to have the full disk name

--- app/modules/vm/index.coffee
+++ app/modules/vm/index.coffee
@@ -201,7 +201,7 @@
           result.xvdSeries = []
           result.xvds.forEach (v,k) ->
             # 97 is ascii code of 'a'
-            result.xvdSeries.push String.fromCharCode(Math.floor(k/2) + 97, ) + ' ' + if k % 2 then 'write' else 'read'
+            result.xvdSeries.push 'xvd' + String.fromCharCode(Math.floor(k/2) + 97, ) + ' ' + if k % 2 then 'write' else 'read'
             return
           result.date.forEach (v,k) ->
             result.date[k] = new Date(v*1000).toLocaleTimeString()


Fix missed character on plh snippet

Fix missed character on plh snippet

--- .atom/snippets.cson
+++ .atom/snippets.cson
@@ -29,7 +29,7 @@
     'body': '<?php echo $this->getChildHtml(\'${1:child}\') ?>'
   'Placeholder':
     'prefix': 'plh'
-    'body': '

$1
</p'
+    'body': '

$1

'
   'Paragraph':
   # Override atom default, which includes an awkward newline.
     'prefix': 'p'


don't remove multiple new lines

don't remove multiple new lines

--- static/index.coffee
+++ static/index.coffee
@@ -83,7 +83,7 @@
         break  if filename is promptFilename
     url = "tastes/#{filename}"

-    contentSrc = getDomContent().trim().replace(/\s+\n/g, '\n')
+    contentSrc = getDomContent().trim().replace(/[ \t\r]+\n/g, '\n')
     contentSrc += '\n'  if contentSrc.length
     setContent contentSrc  if contentSrc isnt getDomContent()
     return true  unless contentSrc.length


Further fix to course-ladder loading

Further fix to course-ladder loading

--- app/lib/LevelLoader.coffee
+++ app/lib/LevelLoader.coffee
@@ -180,18 +180,20 @@
       @opponentSession = @opponentSessionResource.model

     if @session.loaded
-      console.debug 'LevelLoader: session already loaded:', @session
+      console.debug 'LevelLoader: session already loaded:', @session if LOG
       @session.setURL '/db/level.session/' + @session.id
       @loadDependenciesForSession @session
     else
-      console.debug 'LevelLoader: loading session:', @session
+      console.debug 'LevelLoader: loading session:', @session if LOG
       @listenToOnce @session, 'sync', ->
         @session.setURL '/db/level.session/' + @session.id
         @loadDependenciesForSession @session
     if @opponentSession
       if @opponentSession.loaded
+        console.debug 'LevelLoader: opponent session already loaded:', @opponentSession if LOG
         @loadDependenciesForSession @opponentSession
       else
+        console.debug 'LevelLoader: loading opponent session:', @opponentSession if LOG
         @listenToOnce @opponentSession, 'sync', @loadDependenciesForSession

   loadDependenciesForSession: (session) ->
@@ -380,6 +382,7 @@
       @worldNecessities.push @maybeLoadURL(url, LevelComponent, 'component')

   onWorldNecessityLoaded: (resource) ->
+    # Note: this can also be called when session, opponentSession, or other resources with dedicated load handlers are loaded, before those handlers
     index = @worldNecessities.indexOf(resource)
     if resource.name is 'thang'
       @loadDefaultComponentsForThangType(resource.model)
@@ -396,7 +399,7 @@

   checkAllWorldNecessitiesRegisteredAndLoaded: ->
     reason = @getReasonForNotYetLoaded()
-    console.debug('LevelLoader: Reason not loaded:', reason)
+    console.debug('LevelLoader: Reason not loaded:', reason) if reason and LOG
     return !reason

   getReasonForNotYetLoaded: ->
@@ -405,7 +408,7 @@
     return 'not all session dependencies registered' if @sessionDependenciesRegistered and not @sessionDependenciesRegistered[@session.id] and not @sessionless
     return 'not all opponent session dependencies registered' if @sessionDependenciesRegistered and @opponentSession and not @sessionDependenciesRegistered[@opponentSession.id] and not @sessionless
     return 'session is not loaded' unless @session?.loaded or @sessionless
-    return 'opponent session is not loaded' if @opponentSession and not @opponentSession.loaded
+    return 'opponent session is not loaded' if @opponentSession and (not @opponentSession.loaded or @opponentSession.get('interpret'))
     return 'have not published level loaded' unless @publishedLevelLoaded or @sessionless
     return ''


Caso de que la respuesta no tenga scripts.

--- app/scripts/live_form.coffee
+++ app/scripts/live_form.coffee
@@ -12,11 +12,12 @@
           $('#sidebar').remove()
           $('#sidebar', data).prependTo('#extra')
           sources = data.match(/script src="([^"]+)"/g)
-          for source in sources
-            script = document.createElement('script')
-            script.setAttribute "type","text/javascript"
-            script.setAttribute "src", source.replace("script src=", "").replace(/"/g, "")
-            document.getElementsByTagName("head")[0].appendChild(script)
+          if sources
+            for source in sources
+              script = document.createElement('script')
+              script.setAttribute "type","text/javascript"
+              script.setAttribute "src", source.replace("script src=", "").replace(/"/g, "")
+              document.getElementsByTagName("head")[0].appendChild(script)

         data: { preview: true }
       )


refactor lightdurve outlier removal and normalization functions

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -2,13 +2,7 @@

 class CanvasGraph

-  events:
-    'click button[name="unfavorite"]': 'onClickUnfavorite'
-    'click button[name="turn-page"]': 'onTurnPage'
-
-  elements:
-    'nav': 'navigation'
-    'button[name="turn-page"]': 'pageTurners'
+  DEBUG = false

   constructor: (@canvas, @data) ->
     @leftPadding = 60
@@ -28,8 +22,8 @@
     for xValue, idx in [@data.x...]
       @data.x[idx] = xValue - @smallestX

-    # remove outliers and normalize
-    @removeOutliers(nsigma=8)
+    # normalize
+    @processLightcurve()

     # update min/max values
     @smallestX = Math.min @data.x...
@@ -107,36 +101,34 @@
       @ctx.textAlign = 'left'
       @ctx.fillText( @toDataYCoord((-yClick+@canvas.height)).toFixed(4), 15, yClick+5 ) # don't forget to flip y-axis values

-  removeOutliers: (nsigma) ->
-    mean = @mean(@data.y)
-    std = @std(@data.y)
-    x_new = []
+  processLightcurve: () ->
+    @data.y = @removeOutliers(@data.y, nsigma=8) # NOTE: nsigma < 8 removes tutorial subject transits
+    @data.y = @normalize(@data.y)
+
+  normalize: (data) ->
     y_new = []
-    for value, i in [@data.y...]
-      if Math.sqrt( Math.pow( value - mean, 2 ) ) > nsigma * std
-        # console.log 'removed outlier!'
-        continue
-      else
-        x_new.push @data.x[i]
-        y_new.push @data.y[i]
-
-    # normalize
-    yMax = Math.max y_new...
-    yMin = Math.min y_new...
-
-    mean = @mean(y_new)
-    std = @std(y_new)
-
-    # console.log 'MEAN: ', mean
-    # console.log 'STD : ', std
-
-    for y, i in [ y_new... ]
+    mean = @mean(data)
+    std = @std(data)
+
+    for y, i in [ data... ]
       y_new[i] = (y-1)/(std)
       y_new[i] = y/(mean)

-    @data.x = x_new
-    @data.y = y_new
-
+    return y_new
+
+  removeOutliers: (data, nsigma) ->
+    y_new = []
+    mean = @mean(data)
+    std = @std(data)
+
+    for y, i in [data...]
+      if Math.sqrt( Math.pow( y - mean, 2 ) ) > nsigma * std
+        console.log 'removed outlier' if DEBUG
+        continue # skip (outlier)
+      else
+        y_new.push data[i]
+
+    return y_new

   std: (data) ->
     mean = @mean(data)


Fix ledger.Amount conversions

Fix ledger.Amount conversions

--- app/src/utils/amount.coffee
+++ app/src/utils/amount.coffee
@@ -7,7 +7,12 @@
   # @param [String, Number] value A BTC amount.
   @fromBtc: (value) ->
     return value if _.isKindOf(value, Amount)
-    @fromSatoshi(+value * 10e7)
+    @fromSatoshi(+value * 1e8)
+
+  # @param [String, Number] value A bits (aka µBTC) amount.
+  @fromBits: (value) ->
+    return value if _.isKindOf(value, Amount)
+    @fromSatoshi(+value * 1e2)

   # @param [String, Number] value A satoshi amount.
   @fromSatoshi: (value) ->
@@ -56,12 +61,17 @@
   toByteString: () -> new ByteString(_.str.lpad(@toSatoshiString(16), 16, '0'), HEX)
   # @param [Number] base 10 or 16
   # @return [String]
-  toBtcString: (base=10) -> @_number.toString(base)
+  toBtcString: (base=10) -> @toBtcNumber().toString(base)
   # @return [Number]
-  toBtcNumber: () -> @_number.intValue()
+  toBtcNumber: () -> @toSatoshiNumber() / 1e8
   # @param [Number] base 10 or 16
   # @return [String]
-  toSatoshiString: (base=10) -> @_number.toString(base)
+  toBitsString: (base=10) -> @toBitsNumber().toString(base)
+  # @return [Number]
+  toBitsNumber: () -> @toSatoshiNumber() / 1e2
+  # @param [Number] base 10 or 16
+  # @return [String]
+  toSatoshiString: (base=10) -> @toSatoshiNumber().toString(base)
   # @return [Number]
   toSatoshiNumber: () -> @_number.intValue()
   # @return [Bitcoin.BigInteger]


Change hard coded magic number to reflect reality

--- test/unit/model/box.coffee
+++ test/unit/model/box.coffee
@@ -78,4 +78,4 @@
       @servers.should.include 'ds-ec2.scraperwiki.com'

     it 'gives me three servers (because that is how many there currently are)', ->
-      @servers.length.should.equal 3
+      @servers.length.should.equal 4


formatting

formatting

--- app/services/user-can.coffee
+++ app/services/user-can.coffee
@@ -1,7 +1,7 @@
 null

 ### @ngInject ###
-global.cobudgetApp.factory 'UserCan', (Toast, $location, $q, Records) ->
+global.cobudgetApp.factory 'UserCan', ($location, $q, Records, Toast) ->
   new class UserCan

     viewGroup: (group) ->
@@ -25,4 +25,5 @@
       validMemberships = Records.memberships.find({
         memberId: global.cobudgetApp.currentUserId
       })
-      validMemberships.length == 0+      validMemberships.length == 0
+


Allow changing first task

Allow changing first task

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -54,14 +54,24 @@
                     {' '}
                     {tasks[definition.type].getTaskText definition}
                     {if key is @props.workflow.first_task
-                       (first)}
+                       (first)}
                   </button>

}

-            Add task{' '}
-            <button type="button" className="pill-button" onClick={@addNewTask.bind this, 'single'}>Question</button>{' '}
-            <button type="button" className="pill-button" onClick={@addNewTask.bind this, 'drawing'}>Drawing</button>
+

+              Add task{' '}
+              <button type="button" className="pill-button" onClick={@addNewTask.bind this, 'single'}>Question</button>{' '}
+              <button type="button" className="pill-button" onClick={@addNewTask.bind this, 'drawing'}>Drawing</button>
+

+
+

+              First task{' '}
+              <select name="first_task" value={@props.workflow.first_task} onChange={@handleChange}>
+                {for taskKey, definition of @props.workflow.tasks
+                  <option key={taskKey} value={taskKey}>{tasks[definition.type].getTaskText definition}</option>}
+              </select>
+

           {unless @props.project.private


Update project builder imports

Update project builder imports

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -15,7 +15,7 @@
 workflowActions = require './actions/workflow'
 classnames = require 'classnames'

-`import ShortcutEditor from '../../classifier/tasks/shortcut-editor';`
+`import ShortcutEditor from '../../classifier/tasks/shortcut/shortcut-editor';`
 `import FeedbackEditor from './feedback/feedback-editor';`

 DEMO_SUBJECT_SET_ID = if process.env.NODE_ENV is 'production'


props.user is always undefined on first mount Remove the first user check since we should wait until the auth request has happened. Fixes #2767

--- app/pages/lab/project.cjsx
+++ app/pages/lab/project.cjsx
@@ -235,10 +235,6 @@
   displayName: 'EditProjectPageWrapper'
   mixins: [TitleMixin, History]
   title: 'Edit'
-
-  componentDidMount: ->
-    unless @props.user
-      @history.pushState(null, "/lab")

   componentWillReceiveProps: (nextProps) ->
     unless nextProps.user


add holiday notification.

add holiday notification.

--- scripts/meeting.coffee
+++ scripts/meeting.coffee
@@ -9,4 +9,8 @@
   new cron "0 10 10 * * 1-5", () ->
     if !(holiday.isHoliday(new Date()))
       robot.send {room: "general"}, "<!channel> そろそろ朝会の時間ですよ"
+    else
+      date = new Date()
+      d = holiday.between(date, date)[0]
+      robot.send {room: "general"}, "今日は #{d.name} でおやすみです"
   , null, true, "Asia/Tokyo"


Ensure logClick is defined in the pagination component Fixes #2715 and #2713

--- app/talk/lib/paginator.cjsx
+++ app/talk/lib/paginator.cjsx
@@ -31,6 +31,9 @@

   contextTypes:
     geordi: React.PropTypes.object
+
+  componentDidMount: ->
+    @logClick = @context.geordi?.makeHandler 'change-page'

   componentWillReceiveProps: (nextProps, nextContext)->
     @logClick = nextContext?.geordi?.makeHandler? 'change-page'


Update tonypai.coffee

Update tonypai.coffee

--- scripts/tonypai.coffee
+++ scripts/tonypai.coffee
@@ -33,6 +33,5 @@
               weather += yr + '-' + mt + '-' + dt + ' ' + val.weather[0].main + '(' + val.weather[0].description + ') ' + (val.temp.day / 10).toFixed(1) + '°C\n'

             result.send weather
-            result.send "/giphy weather"
           else
             result.send "GET "+url+"("+data.cod+")\nweather [taiwan city name]"


This stopped being good ages ago

This stopped being good ages ago

--- scripts/twitter.coffee
+++ scripts/twitter.coffee
@@ -14,7 +14,7 @@
       insult = msg.random ["idiot", "dumb ass", "moron", "asshole", "jackass", "butthole", "butt"]
       # For *SOME RAISIN* the error is in a list? Who the fuck does this? Why is this a thing?
       [ {message = "no fucking clue"}, ... ] = err if err
-      return msg.reply "No, because #{message}, #{insult}." if err
+      return msg.reply "No, because #{message.replace(/.$/, '')}, #{insult}." if err
       msg.reply "Fine, did it."


天気予報のデザインの修正

天気予報のデザインの修正

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -1,17 +1,34 @@
 # Description:
 #   天気を知らせるbot
+#　　参考:http://koheikimura.hatenablog.com/entry/2016/02/07/142501
 # Author:
 #   raoka0000

 module.exports = (robot) ->
-  robot.hear /((.*))の天気/i, (msg) ->
+  robot.hear /((.*))天気/i, (msg) ->
     day = -1
     day =  0 unless msg.match[1].indexOf("今日") == -1
     day =  1 unless msg.match[1].indexOf("明日") == -1
     day =  2 unless msg.match[1].indexOf("明後日") == -1
+    day_label = ["今日","明日","明後日"]
     request = msg.http('http://weather.livedoor.com/forecast/webservice/json/v1?city=260010')
     .get()
     request (err, res, body) ->
       json = JSON.parse body
-      if day == -1 then forecast = 'わからないクエ' else forecast = "#{msg.match[1]}の天気[京都]\n" + json['forecasts'][day]['telop']
-      msg.send forecast+      if day == -1
+        forecast = "#{json['description']['text']}"
+        day = 0
+      else
+        forecast = """
+          #{day_label[day]}の天気[京都]
+          #{json['forecasts'][day]['telop']}<http://weather.livedoor.com/area/forecast/2610000/|[詳細]>
+          """
+      data =
+        attachments: [
+          color: "81daf5"
+          text : forecast
+          thumb_url: json['forecasts'][day]['image']['url']
+          footer: "<#{json['copyright']['link']}|#{json['copyright']['title']}>"
+        ]
+      #console.log json
+      msg.send data


cleaning up atom

cleaning up atom

--- .atom/config.cson
+++ .atom/config.cson
@@ -1,28 +1,23 @@
-'global':
-  '*':
-    'exception-reporting':
-      'userId': '1c41ebe9-0a29-7a50-8b25-1e1f298b5311'
-    'welcome':
-      'showOnStartup': false
-    'editor':
-      'showInvisibles': true
-      'tabLength': 4
-    'core':
-      'disabledPackages': [
-        'linter-shellcheck'
-        'linter-phpcs'
-        'linter-phpmd'
-      ]
-  'exception-reporting':
-    'userId': 'f1311b16-a096-7a63-0149-ddc9c884d3da'
-  'welcome':
-    'showOnStartup': false
-  'editor':
-    'tabLength': 4
-    'softWrapAtPreferredLineLength': true
-    'invisibles': {}
-    'autoIndentOnPaste': false
-  'whitespace':
-    'removeTrailingWhitespace': false
-  'linter': {}
-  'core': {}
+"*":
+  "*":
+    "exception-reporting":
+      userId: "1c41ebe9-0a29-7a50-8b25-1e1f298b5311"
+    welcome:
+      showOnStartup: false
+    editor:
+      showInvisibles: true
+      tabLength: 4
+  "exception-reporting":
+    userId: "f1311b16-a096-7a63-0149-ddc9c884d3da"
+  welcome:
+    showOnStartup: false
+  editor:
+    showInvisibles: true
+    tabLength: 4
+    softWrap: true
+    softWrapAtPreferredLineLength: true
+    invisibles: {}
+  whitespace:
+    removeTrailingWhitespace: false
+  linter: {}
+  core: {}


Fix Dockerfile.windows, Makefile-xx

Fix Dockerfile.windows, Makefile-xx

--- .atom/config.cson
+++ .atom/config.cson
@@ -5,6 +5,7 @@
     disabledPackages: [
       "linter-eslint"
       "whitespace"
+      "linter-jenkins"
     ]
     ignoredNames: [
       ".git"
@@ -21,8 +22,9 @@
   "exception-reporting":
     userId: "05162874-e0c6-bfb2-ad47-5bead3da1b69"
   "file-types":
-    ".*\\.wxs$": "text.xml"
-    "^Dockerfile\\..*": "source.dockerfile"
+    "*.wxs$": "text.xml"
+    "Dockerfile.*": "source.dockerfile"
+    "Makefile*": "source.makefile"
   "go-plus":
     goPath: "/Users/stefan/go"
     showPanel: false


change atom config

change atom config

--- atom/config.cson
+++ atom/config.cson
@@ -1,16 +1,15 @@
 "*":
   "atom-beautify":
-    _analyticsUserId: "83429072-4046-4dd1-85f5-5a216dc56d2a"
     general:
-      _analyticsUserId: "a0fb5d50-2150-4a07-a43c-5161db14a586"
+      _analyticsUserId: "83429072-4046-4dd1-85f5-5a216dc56d2a"
   "atom-ternjs": {}
   core:
     audioBeep: false
-    telemetryConsent: "limited"
     customFileTypes:
       "text.html": [
         "html.swig"
       ]
+    telemetryConsent: "limited"
     themes: [
       "one-dark-ui"
       "oceanic-next"


synchornize atom configuration

synchornize atom configuration

--- atom/config.cson
+++ atom/config.cson
@@ -1,4 +1,6 @@
 "*":
+  "autocomplete-plus":
+    backspaceTriggersAutocomplete: true
   core:
     disabledPackages: [
       "language-c"
@@ -27,6 +29,8 @@
     userId: "0602831a-299e-5b96-11c6-326ec89bbb33"
   "find-and-replace":
     openProjectFindResultsInRightPane: true
+  "fuzzy-finder":
+    searchAllPanes: true
   "git-diff":
     showIconsInEditorGutter: true
   linter:
@@ -35,11 +39,17 @@
   "markdown-preview":
     useGitHubStyle: true
   minimap:
+    displayMinimapOnLeft: true
     plugins:
       linter: true
       linterDecorationsZIndex: 0
   pigments:
     extendAutocompleteToColorValue: true
     groupPaletteColors: "by file"
+  tabs:
+    enableVcsColoring: true
   "tree-view":
     hideIgnoredNames: true
+".html.php.text":
+  editor:
+    preferredLineLength: 120


Try out the Anka/Coder font

Try out the Anka/Coder font

--- atom/config.cson
+++ atom/config.cson
@@ -66,8 +66,8 @@
   docblockr:
     align_tags: "no"
   editor:
-    fontFamily: "Anonymous Pro"
-    fontSize: 16
+    fontFamily: "Anka/Coder"
+    fontSize: 15
     invisibles:
       cr: "↩"
       eol: " "
@@ -81,7 +81,7 @@
   "find-and-replace":
     scrollToResultOnLiveSearch: true
   fonts:
-    fontFamily: "Anonymous Pro"
+    fontFamily: "Anka/Coder"
   "fortune-background-tips":
     fortuneCommand: "fortune -s Computers"
   "fuzzy-finder":


Update config.cson for Atom

Update config.cson for Atom

--- atom/config.cson
+++ atom/config.cson
@@ -10,7 +10,7 @@
     'solarized-dark-syntax'
   ]
   'disabledPackages': []
-  'destroyEmptyPanes': true
+  'destroyEmptyPanes': false
 'editor':
   'fontSize': 13
   'hideGitIgnoredFiles': true
@@ -36,4 +36,4 @@
 'autocomplete':
   'includeCompletionsFromAllBuffers': true
 'release-notes':
-  'viewedVersion': '0.71.0'
+  'viewedVersion': '0.78.0'


atom config

atom config

--- atom/config.cson
+++ atom/config.cson
@@ -35,6 +35,7 @@
       "proto-repl-charts"
       "proto-repl-sayid"
       "rainbow-delimiters"
+      "linter-pycodestyle"
     ]
     ignoredNames: [
       ".git"


chore(atom/config.cson): backup atom/config.cson

--- atom/config.cson
+++ atom/config.cson
@@ -15,6 +15,9 @@
     html:
       indent_size: 2
       wrap_attributes_indent_size: 2
+  "atom-clock":
+    dateFormat: "HH:mm"
+    showClockIcon: true
   "atom-html-preview":
     triggerOnSave: true
   "atom-material-ui":
@@ -39,7 +42,12 @@
       "vim-mode-plus-ex-mode"
       "welcome"
       "markdown-format"
-      "terminal-plus"
+      "atom-pair"
+      "java-plus"
+      "java-import-wiz"
+      "java-generator"
+      "java-classpath-registry"
+      "autocomplete-java-minus"
     ]
     packagesWithKeymapsDisabled: []
     telemetryConsent: "limited"
@@ -50,6 +58,7 @@
   "cursor-history":
     flashOnLand: true
   editor:
+    fontFamily: "FiraCode-Regular"
     invisibles: {}
     scrollPastEnd: true
     showIndentGuide: true
@@ -62,7 +71,9 @@
   "exception-reporting":
     userId: "5f1fd872-de33-9d23-b5da-da0fbe791e6c"
   "file-icons": {}
-  "git-plus": {}
+  "git-plus":
+    general:
+      _analyticsUserId: "531f1de0-9539-4faa-9854-c3bf2d523b05"
   jumpy:
     fontSize: 1
   "markdown-preview":
@@ -87,6 +98,10 @@
   "one-dark-ui": {}
   pigments:
     markerType: "native-dot"
+  "platformio-ide-terminal":
+    style: {}
+    toggles:
+      autoClose: true
   "regex-railroad-diagram": {}
   script: {}
   "terminal-plus":
@@ -95,6 +110,7 @@
   "tree-view":
     alwaysOpenExisting: true
     hideVcsIgnoredFiles: true
+    showOnRightSide: true
   "vim-mode":
     useSmartcaseForSearch: true
     wrapLeftRightMotion: true


Ignore removable media hidden directories

--- atom/config.cson
+++ atom/config.cson
@@ -39,6 +39,9 @@
       "*.docx"
       "*.pages"
       "*.alfredworkflow"
+      ".Spotlight-V100"
+      ".fseventsd"
+      ".Trashes"
     ]
     packagesWithKeymapsDisabled: [
       "emmet"


Change EOL invisibles setting to a single space

See: atom/settings-view#237

--- atom/config.cson
+++ atom/config.cson
@@ -11,9 +11,9 @@
   'preferredLineLength': 100
   'invisibles':
     'cr': '↩'
-    'eol': ''
     'space': '·'
     'tab': '⇥'
+    'eol': ' '
   'normalizeIndentOnPaste': false
   'fontSize': 15
   'showInvisibles': true


Return scroll sensitivity to old default

--- atom/config.cson
+++ atom/config.cson
@@ -78,6 +78,7 @@
       tab: "⇥"
     preferredLineLength: 100
     scrollPastEnd: true
+    scrollSensitivity: 80
     showInvisibles: true
     tabType: "soft"
   "exception-reporting":


Use atom-text-editor tag instead of editor class

Resolves an Atom 1.0 deprecation.

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -1,4 +1,4 @@
-'atom-workspace .pane, atom-workspace .editor:not(.mini)':
+'atom-workspace .pane, atom-workspace atom-text-editor:not(.mini)':
   'ctrl-h': 'window:focus-pane-on-left'
   'ctrl-j': 'window:focus-pane-below'
   'ctrl-k': 'window:focus-pane-above'
@@ -7,7 +7,7 @@
   'ctrl-|': 'pane:split-right'
   'ctrl--': 'pane:split-down'

-'atom-workspace .editor:not(.mini)':
+'atom-workspace atom-text-editor:not(.mini)':
   'ctrl-;': 'editor:delete-to-end-of-line'

   'ctrl-g j': 'dot-atom:center-next-diff'
@@ -29,5 +29,5 @@
 'atom-workspace':
   'alt-cmd-ctrl-b': 'dot-atom:demo'

-'atom-workspace, .command-palette .editor':
+'atom-workspace, .command-palette atom-text-editor':
   'cmd-p': 'command-palette:toggle'


Fix ctrl-d key mapping

Fix ctrl-d key mapping

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -1,12 +1,10 @@
 '.platform-darwin':
   'ctrl-p': 'fuzzy-finder:toggle-file-finder'
   'ctrl-d': 'tree-view:toggle'
-
-'atom-text-editor.vim-mode:not(.insert-mode)':
-  'ctrl-d': 'unset!'

 'atom-text-editor[data-grammar="text html basic"]:not([mini]), atom-text-editor[data-grammar~="jade"]:not([mini]), atom-text-editor[data-grammar~="css"]:not([mini]), atom-text-editor[data-grammar~="sass"]:not([mini])':
   'tab': 'emmet:expand-abbreviation-with-tab'

 'atom-text-editor.vim-mode:not(.insert-mode)':
   'alt-4': 'vim-mode:move-to-last-character-of-line'
+  'ctrl-d': 'unset!'


Pass logger to handler

Pass logger to handler

--- src/urlhandler.coffee
+++ src/urlhandler.coffee
@@ -2,10 +2,10 @@
 flash = require './urlhandlers/flash.coffee'

 class URLHandler
-    @get: (url, headers, timeout, cb) ->
+    @get: (url, headers, timeout, logger, cb) ->
         if not window?
             # prevents browserify from including this file
-            return require('./urlhandlers/' + 'node.coffee').get(url, headers, timeout, cb)
+            return require('./urlhandlers/' + 'node.coffee').get(url, headers, timeout, logger, cb)
         else if xhr.supported()
             return xhr.get(url, headers, timeout, cb)
         else if flash.supported()


Added startswith

Added startswith

--- src/builtin/string.coffee
+++ src/builtin/string.coffee
@@ -142,6 +142,9 @@

   partition: (sep) -> return #if using regex in split, need to be careful to escape chars

+  startswith: (prefix, start = 0, end = @__len__()) ->
+    return @value.slice(start, end).indexOf(suffix) == 0
+
   swapcase: ->
     result = ''
     for char in @value


Fix getNumSamplesInChunk()

Fix getNumSamplesInChunk()

--- mp4.coffee
+++ mp4.coffee
@@ -917,7 +917,7 @@
       if not entry.numChunks?
         return null  # the last chunk
       if chunk < entry.firstChunk + entry.numChunks
-        return entry.samplesPerChunk * entry.numChunks
+        return entry.samplesPerChunk
     throw new Error "Chunk not found: #{chunk}"

   findChunk: (sampleNumber) ->


up number of gallery and shows appearing in city pages

--- apps/location/routes.coffee
+++ apps/location/routes.coffee
@@ -29,7 +29,7 @@

   requests.push partners.fetch
     cache: true
-    data: { near: context.coords.toString(), has_full_profile: true, size: 20, sort: 'relative_size' }
+    data: { near: context.coords.toString(), has_full_profile: true, size: 80, sort: 'relative_size' }
     success: ->
       return render() if partners.isEmpty()
       @partnerProfileIds = _.compact(partners.pluck('default_profile_id'))
@@ -53,6 +53,6 @@
       status: 'current'
       fair_id: null
       sort: '-start_at'
-      size: 40
+      size: 80

   Q.allSettled(requests).then(render).done()


Cleaned up view

Cleaned up view

--- src/views/view.coffee
+++ src/views/view.coffee
@@ -4,58 +4,47 @@
 {Frame} = require "../primitives/frame"
 {Matrix} = require "../primitives/matrix"

-{EventTypes} = require "../primitives/events"
-{EventClass} = require "../primitives/events"
-
 {EventEmitter} = require "../eventemitter"
 {Animation} = require "../animation"

 exports.ViewList = []

-# Base class for all views.
-#
-# @example How to subclass an animal
-# 	class Lion extends Animal
-# 		move: (direction, speed): ->
-#
+
 class View extends Frame

-
-
 	constructor: (args) ->

 		args ?= {}

+		# Keep a generic list of all current views
 		View.Views.push @
 		@id = View.Views.length

+		# Create the html element for this view
 		@_element = document.createElement "div"
 		@_element.id = @id

-		@addClass "uilayer"
-		# @addClass "textureBacked"
-		# @addClass "animated"
-
-		@clip = args.clip or View.Properties.clip
-
+		# Add the default css
+		@addClass "framer"
+
 		@_subViews = []
 		@_currentAnimations = []

+		# Set the view properties
+		@clip = args.clip or View.Properties.clip
 		@properties = args
+
+		# Overridable creation hook
+		@_postCreate?()

 		if not args.superView
 			@_insertElement()
-
-		# Override this prototype to change all behaviour
-		@_postCreate()
-
-	# @private
+
+
 	_postCreate: ->

 	# Helpers

-	# @property [String] The views name
-	#
 	@define "name",
 		get: ->
 			@_name or @id
@@ -98,10 +87,9 @@
 		# Get this views absolute frame on the screen
 		utils.convertPoint @frame, @, null

-	# Get the combined size of all subviews
-	#
+
 	contentFrame: ->
-
+		# Get the total size of all subviews
 		frame =
 			x: utils.min _.pluck(@subViews, "minX")
 			y: utils.min _.pluck(@subViews, "minY")
@@ -114,22 +102,6 @@
 	#############################################################################
 	## Geometry

-	###
-	Shortcut to animate this view.
-
-	@example Move the view to the right in half a second
-	  myView.animate({
-	    properties:{x:500},
-	    time: 500
-	  })
-
-	@param [Object] options the moving options
-	@option options [Object] properties properties to animate
-	@option options [Number] time the time in miliseconds
-	@option options [String] curve the animation curve
-	@param [Function] callback function to run after the animation is done
-	@return [Animation] the created animation object
-	###
 	@define "width",
 		get: ->
 			parseFloat @style.width
@@ -286,7 +258,6 @@
 			@__matrix = matrix
 			@style.webkitTransform = @__matrix.matrix().cssValues()

-	# @private
 	_computedMatrix: ->
 		new WebKitCSSMatrix @computedStyle.webkitTransform

@@ -331,7 +302,7 @@
 			return if value is @_superView

 			# Cancel previous pending insertions
-			document.removeEventListener "DOMContentLoaded", @__insertElement
+			# document.removeEventListener "DOMContentLoaded", @__insertElement

 			# Remove from previous superview subviews
 			if @_superView
@@ -386,36 +357,18 @@

 	#############################################################################
 	## Animation
-
-	###
-	Shortcut to animate this view.
-
-	@example Move the view to the right in half a second
-	  myView.animate({
-	    properties:{x:500},
-	    time: 500
-	  })
-
-	@param [Object] options the moving options
-	@option options [Object] properties properties to animate
-	@option options [Number] time the time in miliseconds
-	@option options [String] curve the animation curve
-	@param [Function] callback function to run after the animation is done
-	@return [Animation] the created animation object
-	###
+
 	animate: (args, callback) ->
+		# Shortcut to create and start animation on this view
 		args.view = @
 		animation = new Animation args
 		animation.start callback
 		return animation

-	###
-	Stop all current animations on this view
-	###
 	animateStop: ->
+		# Stop all animations on this view
 		@_currentAnimations.map (animation) ->
 			animation.stop()
-

 	#############################################################################
 	## HTML Helpers
@@ -463,18 +416,19 @@
 	removeClass: (className) ->
 		@classes = _.filter @classes, (item) -> item isnt className

-	# @private
 	_insertElement: ->

-		# If we are loaded we insert the node immediately, if not we wait
-		if document.readyState is "complete" or document.readySate is "loaded"
+		# Insert the element to the dom, wait for it if it's not loaded
+		# TODO: There might still be some bug here
+
+		if document.readyState in ["complete"]
 			@__insertElement()
 		else
-			document.addEventListener "DOMContentLoaded", @__insertElement
-
-	# @private
+			document.addEventListener "DOMContentLoaded",
+				@__insertElement
+
 	__insertElement: =>
-		document.body.appendChild @_element
+		@_rootElement.appendChild @_element

 	#############################################################################


fix goal format

fix goal format

--- assets/data/quest_goal.cson
+++ assets/data/quest_goal.cson
@@ -261,7 +261,7 @@
   type: 2
   "destory_item":
     description: "廃棄"
-    slotitemId: 15
+    slotitemId: [15]
     required: 6
     init: 0
 ###


Refactored LightCurve class.

Refactored LightCurve class.

--- assets/js/lightcurve.coffee
+++ assets/js/lightcurve.coffee
@@ -2,6 +2,10 @@
 $ ->
     class LightCurve
         constructor: (@selector) ->
+            @setGeometry()
+            @svg = @getSvg(@selector)
+
+        setGeometry: ->
             @margin =
                 top: 20
                 right: 20
@@ -12,8 +16,8 @@
             @width = @width - @margin.left - @margin.right
             @height = @height - @margin.top - @margin.bottom

-        getSvg: ->
-            d3.select(@selector).append('svg')
+        getSvg: (selector) ->
+            d3.select(selector).append('svg')
                 .attr
                     width: @width + @margin.left + @margin.right
                     height: @height + @margin.top + @margin.bottom
@@ -27,22 +31,22 @@
                 d3.scale.linear().range([0, @height]).nice()
             ]

-        drawData: (svg, data) ->
+        drawData: (data) ->
             [xScale, yScale] = @getScales()
             xScale.domain d3.extent data, (d) -> d.jd
             yScale.domain d3.extent data, (d) -> d.magnitude
             xAxis = d3.svg.axis().scale(xScale).orient('bottom').ticks(10)
             yAxis = d3.svg.axis().scale(yScale).orient('left').ticks(10)
-            svg.append('g')
+            @svg.append('g')
                 .attr
                     class: 'x axis'
                     transform: "translate(0,#{@height})"
                 .call(xAxis.tickSize(-@height, 0, 0))
-            svg.append('g')
+            @svg.append('g')
                 .attr
                     class: 'y axis'
                 .call(yAxis.tickSize(-@width, 0, 0))
-            svg.selectAll('circle')
+            @svg.selectAll('circle')
                 .data(data)
                 .enter()
                 .append('circle')
@@ -55,7 +59,6 @@

     selector = '.lightcurve'
     lc = new LightCurve(selector)
-    svg = lc.getSvg()
     csvUrl = $(selector).data 'csvSource'
     d3.csv(
         csvUrl,
@@ -64,6 +67,6 @@
             magnitude: +d.magnitude
         ),
         ((error, data) ->
-            lc.drawData svg, data
+            lc.drawData data
         )
     )


increase location timeout to 1000ms

increase location timeout to 1000ms

--- src/coffee/Weather.coffee
+++ src/coffee/Weather.coffee
@@ -6,7 +6,7 @@
     type: 'accurate'# like
     location:
       enableHighAccuracy: false
-      timeout: 500
+      timeout: 1000
       maximumAge: 1000 * 60 * 60 # 1 hour
     locationCacheAge: 30 * 60 * 1000
     weatherCacheAge: 30 * 60 * 1000


Simplify redirect logic

Simplify redirect logic

--- src/components/index.cjsx
+++ src/components/index.cjsx
@@ -26,21 +26,21 @@

   update: -> @setState({})

+  redirectToSingleCourse: (courseId, roleType) ->
+    destination = switch roleType
+      when 'student' then 'viewStudentDashboard'
+      when 'teacher' then 'taskplans'
+      else
+        throw new Error("BUG: Unrecognized role type #{roleType}")
+    _.defer => @context.router.replaceWith(destination, {courseId: courseId})
+
   render: ->
     if CurrentUserStore.isCoursesLoaded()
       courses = CurrentUserStore.getCourses()
-      if courses.length is 1
-        roles = courses[0].roles
-        if roles.length is 1
-          singleCourseId = courses[0].id
-          if roles[0].type is 'student'
-            _.defer  =>
-              @context.router.replaceWith('viewStudentDashboard', {courseId: singleCourseId})
-            null
-          if roles[0].type is 'teacher'
-            _.defer  =>
-              @context.router.replaceWith('taskplans', {courseId: singleCourseId})
-            null
+      if courses.length is 1 and courses[0].roles?.length is 1
+        @redirectToSingleCourse(courses[0].id, courses[0].roles[0].type)
+        return null # explicitly return null so React won't render
+
       if courses.length
         courses = _.map courses, (course) ->
           {id:courseId, name, roles} = course


fix replication cancelation

fix replication cancelation

--- backend/replication.coffee
+++ backend/replication.coffee
@@ -117,7 +117,7 @@
                 doc.docType is 'Folder' or doc.docType is 'File'
             live: false
             since: replication.startSeq
-        replication.replicator = pouch.db.replicate.from(url, opts)
+        replication.replicatorFrom = pouch.db.replicate.from(url, opts)
             .on 'change', replication.displayChange
             .on 'complete', replication.onRepComplete
             .on 'error', replication.onError
@@ -136,7 +136,7 @@
                 doc.docType is 'Folder' or doc.docType is 'File'
             live: true
             since: config.getSeq()
-        replication.replicator = pouch.db.replicate.from(url, opts)
+        replication.replicatorFrom = pouch.db.replicate.from(url, opts)
             .on 'change', replication.displayChange
             .on 'uptodate', replication.onSyncUpdate
             .on 'error', replication.onError
@@ -146,7 +146,7 @@
                 doc.docType is 'Folder' or doc.docType is 'File'
             live: true
             since: config.getChangeSeq()
-        replication.replicator = pouch.db.replicate.to(url, opts)
+        replication.replicatorTo = pouch.db.replicate.to(url, opts)
             .on 'change', replication.displayChange
             .on 'uptodate', replication.displayChange
             .on 'error', replication.onError
@@ -272,6 +272,8 @@
     # Stop running replications and stop
     cancelReplication: ->
         clearTimeout replication.timeout
-        replication.replicator.cancel() if replication.replicator?
+        replication.replicatorFrom.cancel() if replication.replicatorFrom?
+        replication.replicatorTo.cancel() if replication.replicatorTo?
         replication.timeout = null
-        replication.replicator = null
+        replication.replicatorFrom = null
+        replication.replicatorTo = null


Update modal.coffee

Update modal.coffee

Modify modal data method key

--- vendor/assets/javascripts/mice/modal.coffee
+++ vendor/assets/javascripts/mice/modal.coffee
@@ -187,11 +187,11 @@
   Plugin = (option, _relatedTarget) ->
     @each ->
       $element   = $(@)
-      data    = $element.data('bs.modal')
+      data    = $element.data('modal')
       options = $.extend({}, Modal.DEFAULTS, $element.data(), typeof option == 'object' && option)

       if (!data)
-        $element.data 'bs.modal', (data = new Modal(@, options))
+        $element.data 'modal', (data = new Modal(@, options))

       if typeof option == 'string'
         data[option](_relatedTarget)
@@ -222,7 +222,7 @@
       $element  = $(@)
       href      = $element.attr('href')
       $target   = $($element.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) # strip for ie7
-      option    = if $target.data('bs.modal') then 'toggle' else $.extend({ remote: !/#/.test(href) && href }, $target.data(), $element.data())
+      option    = if $target.data('modal') then 'toggle' else $.extend({ remote: !/#/.test(href) && href }, $target.data(), $element.data())

       e.preventDefault() if $element.is('a')


Rename the arguments in MS Translator

Rename the arguments in MS Translator

ref #2

--- lib/microsoft-translator-client.coffee
+++ lib/microsoft-translator-client.coffee
@@ -28,7 +28,7 @@
         grant_type: MicrosoftTranslatorClient.grantType
       json: true

-  translate: (text, from, to, onSucceeded, onFailed) ->
+  translate: (text, from, to, succeeded, failed) ->
     new Promise((resolve, reject) =>
       # Check whether the previous access token is valid
       currentTime = new Date().getTime()
@@ -100,10 +100,10 @@
       (error) => error
     ).then(
       (result) =>
-        onSucceeded(result)
+        succeeded(result)
         return
       ,
       (error) =>
-        onFailed(error)
+        failed(error)
         return
     )


Add semicolon to language-reason autocomplete.

--- editorSupport/language-reason/snippets/reason.cson
+++ editorSupport/language-reason/snippets/reason.cson
@@ -49,7 +49,7 @@
     body: """
       switch ${1:expression} {
       | ${2:firstCase} => ${3:result}
-      }
+      };
     """

   "Reason: Try-catch":


Remove mentions=subscribing filter for timeline feed

The subscribing/subscriber/mutual keywords have not been implemented on
any server yet.

--- lib/assets/javascripts/views/posts_feed.js.coffee
+++ lib/assets/javascripts/views/posts_feed.js.coffee
@@ -17,7 +17,7 @@

     default_queries = [
       { profiles: 'entity,refs', max_refs: 1, entities: options.entity || false },
-      { types: [TentStatus.config.POST_TYPES.STATUS_REPLY], mentions: 'subscribing', profiles: 'entity,refs', entities: options.entity || false },
+      { types: [TentStatus.config.POST_TYPES.STATUS_REPLY], profiles: 'entity,refs', entities: options.entity || false },
       { types: [TentStatus.config.POST_TYPES.STATUS_REPLY], entities: @entity, profiles: 'entity' }
     ]
     @feed_queries = _.map options.feed_queries || default_queries, (q) => q.types ?= @types; q


activityblock : get group slug from entrypoint

--- client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
+++ client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
@@ -30,6 +30,7 @@
     """

 class ActiveUsers extends ActivityRightBase
+
   constructor:(options={}, data)->
     {entryPoint} = KD.config
     if entryPoint?.type is "group" then group = entryPoint.slug else group = "koding"
@@ -69,6 +70,7 @@

     @showAllLink = new KDCustomHTMLView

+    {entryPoint} = KD.config
     if entryPoint?.type is "group" then group = entryPoint.slug else group = "koding"

     @showAllLink = new KDCustomHTMLView
@@ -127,6 +129,7 @@

     super options, data

+    {entryPoint} = KD.config
     if entryPoint?.type is "group" then groupSlug = entryPoint.slug else groupSlug = "koding"

     @showAllLink = new KDCustomHTMLView


DemosAppController: Added workspace sample code.

--- client/app/Applications/Demos.kdapplication/AppController.coffee
+++ client/app/Applications/Demos.kdapplication/AppController.coffee
@@ -14,8 +14,40 @@
     super options, data

   loadView:(mainView)->
-    mainView.addSubView new KDHeaderView
-      title : 'Demo App'
-
-    mainView.addSubView new KDCustomHTMLView
-      partial : 'Hello World!'
+    options =
+      title              : "Kodelicious"
+      version            : "0.1"
+      joinModalContent   : "
Here is the join modal
"
+      shareModalContent  : "
Here is the share modal
"
+      panels             : [
+        {
+          title          : "Collaborative IDE"
+          hint           : "
This is the Collaborative IDE that you ever dreamed. Use it wisely.
"
+          buttons        : [
+            {
+              title      : "Join"
+              cssClass   : "cupid-green join-button"
+              callback   : (panel, workspace) => workspace.showJoinModal()
+            }
+            {
+              title      : "Share"
+              callback   : (panel, workspace) => workspace.showShareModal()
+            }
+          ]
+          panes: [
+            {
+              type  : "finder"
+            }
+            {
+              type  : "tabbedEditor"
+            }
+            {
+              type  : "terminal"
+            }
+          ]
+        }
+      ]
+    kolab = new CollaborativeWorkspace options
+    kolab.on "PanelCreated", =>
+      kolab.activePanel.splitView.resizePanel "20%", 0
+    mainView.addSubView kolab


stacktemplateitem: member will edit his own stack from stacks page

--- client/home/lib/stacks/components/stacktemplateitem/index.coffee
+++ client/home/lib/stacks/components/stacktemplateitem/index.coffee
@@ -93,7 +93,8 @@

     editorUrl = "/Stack-Editor/#{template.get '_id'}"
     listItemClassName = 'HomeAppViewListItem-label'
-    listItemClassName = 'HomeAppViewListItem-label member'   unless isAdmin()
+    unless isAdmin() or stack.get('accessLevel') is 'private'
+      listItemClassName = 'HomeAppViewListItem-label member'
     <div className='HomeAppViewListItem StackTemplateItem'>
       <a
         ref='stackTemplateItem'


client: check template's originId with current user's id

--- client/home/lib/stacks/components/stacktemplateitem/index.coffee
+++ client/home/lib/stacks/components/stacktemplateitem/index.coffee
@@ -7,6 +7,7 @@
 getBoundingClientReact = require 'app/util/getBoundingClientReact'
 StackUpdatedWidget = require 'app/components/sidebarstacksection/stackupdatedwidget'
 isAdmin = require 'app/util/isAdmin'
+whoami = require 'app/util/whoami'

 module.exports = class StackTemplateItem extends React.Component

@@ -111,7 +112,7 @@

     editorUrl = "/Stack-Editor/#{template.get '_id'}"
     listItemClassName = 'HomeAppViewListItem-label'
-    unless isAdmin() or stack?.get('accessLevel') is 'private'
+    unless isAdmin() or template.get('originId') is whoami()._id
       listItemClassName = 'HomeAppViewListItem-label member'
     <div className='HomeAppViewListItem StackTemplateItem'>
       <a


allow admins to edit the html of proposals

Former-commit-id: ff8964e7d05c2fff4b1e5d40b52b3935472e0d77 [formerly 655bada7a88144756d0f9ee819cc7f420e1e9357] [formerly 112005b9eed7b0b962a7c1e7eac76291e6a91788 [formerly c1e2c9b8f7b088e24ac4da79cfd96c47e52b6863]] [formerly 4e040154e754fbaf30dcef0b5311a189e5c62497 [formerly e0bbbce2e1d196dac9bcffc501c020a2a827ea33] [formerly 7db1a1792f23eda6808bab4856bd62ac7628bb3e [formerly 7db1a1792f23eda6808bab4856bd62ac7628bb3e [formerly 0224b85ecf033174394f3694b5d778af94ecea02]]]]
Former-commit-id: e0fde6f880703993a7081de1c6cb6da2f99fdac8 [formerly 51be5b6451704dc66f62b66ad67c1f10a3810715] [formerly 04db2ea9fd664645c5856a96417943cbe7f3a1ce [formerly 2e34e7fc37b7cf8f86c84c9028bdc26fb0c44004]]
Former-commit-id: 5031bd0206bd9555aef88fea49fcf8d7e1027a22 [formerly 13d73a42feec77e70215507f95c1cbde11b2c25b]
Former-commit-id: 329fbdc37c51d95f496fbb2375f29f1e598d76f5
Former-commit-id: 38eb09dd234c432176aca846741f3e7e7321b4de

--- @client/form.coffee
+++ @client/form.coffee
@@ -130,7 +130,7 @@
       # },
     ]

-    if fetch('/current_user').is_super_admin
+    if fetch('/current_user').is_admin
       toolbar_items.push
         className: 'fa fa-code'
         title: 'Directly edit HTML'


fixing bug with creating new list

fixing bug with creating new list

--- @client/list.coffee
+++ @client/list.coffee
@@ -201,10 +201,13 @@
         delete customizations[list_key]

         # if tabs are enabled, add it to the current tab
-        if customizations['homepage_tabs']
+        if customizations['homepage_tabs']
           tabs = fetch('homepage_tabs')
           current_tab = customizations['homepage_tabs'][tabs.filter]
-          current_tab.push new_name
+          if current_tab
+            current_tab.push new_key
+          else
+            console.error "Cannot add the list to the current tab #{current_tab}"

       subdomain.customizations = JSON.stringify customizations, null, 2


Added import axios from axios.

Added import axios from axios.


Update gen to color just `g`

Update gen to color just `g`


Fix global macro errors within strings. Fixes #91.

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -1063,12 +1063,12 @@
   'macro-global-escaped':
     patterns: [
       {
-        begin: '(\\\\\\$)(\\\\\\{)?'
+        begin: '\\\\\\$\\{'
         beginCaptures:
           0: name: 'punctuation.definition.string.begin.stata'
-        end: '(\\\\\\})|(?=\\"|\\s|\\n|/|,)'
-        endCaptures:
-          1: name: 'punctuation.definition.string.end.stata'
+        end: '\\}'
+        endCaptures:
+          0: name: 'punctuation.definition.string.end.stata'
         patterns: [
           {include: '#macro-local'}
           {include: '#macro-global'}
@@ -1078,11 +1078,25 @@
           }
         ]
       }
+      {
+        begin: '(\\\\\\$)(?=[\\w&&[^0-9]])'
+        beginCaptures:
+          1: name: 'punctuation.definition.string.begin.stata'
+        end: '(?!\\w)'
+        patterns: [
+          {include: '#macro-local'}
+          {include: '#macro-global'}
+          {
+            match: '[\\w&&[^0-9_]]\\w{0,31}|_\\w{1,31}'
+            name: 'entity.name.type.class.stata'
+          }
+        ]
+      }
     ]
   'macro-global':
     patterns: [
       {
-        begin: '(\\$)(\\{)'
+        begin: '\\$\\{'
         beginCaptures:
           0: name: 'punctuation.definition.string.begin.stata'
         end: '\\}'
@@ -1104,12 +1118,10 @@
         ]
       }
       {
-        begin: '\\$'
+        begin: '\\$(?=[\\w&&[^0-9]])'
         beginCaptures:
           0: name: 'punctuation.definition.string.begin.stata'
         end: '(?!\\w)'
-        endCaptures:
-          1: name: 'punctuation.definition.string.end.stata'
         patterns: [
           {include: '#macro-local'}
           {include: '#macro-global'}


remove logs

remove logs

--- client/landing/app/CommonViews/comments/commentviewheader.coffee
+++ client/landing/app/CommonViews/comments/commentviewheader.coffee
@@ -67,14 +67,14 @@
     # if nothing changed it means user clicked like button
     # so we don't need to touch anything
     if @getData().repliesCount > @oldCount
-      log "ITEM ADDED"
+      # log "ITEM ADDED"
       @newCount++
     else if @getData().repliesCount < @oldCount
-      log "ITEM DELETED"
+      # log "ITEM DELETED"
       if @newCount > 0 then @newCount--
       if @onListCount > 0 then @onListCount--
     else
-      log "LIKE CLICKED"
+      # log "LIKE CLICKED"

     # If the count is changed then we need to update UI
     if @getData().repliesCount isnt @oldCount


apiNotify was only being called on correct submissions!

--- picoCTF-web/web/coffee/problems.coffee
+++ picoCTF-web/web/coffee/problems.coffee
@@ -15,10 +15,10 @@
   .done (data) ->
     if data['status'] is 1
       loadProblems()
-      apiNotify(data)
       setTimeout( ->
         $("div[data-target='#" + input.data("pid") + "']").click()
       , 100)
+    apiNotify data

 addProblemReview = (e) ->
   e.preventDefault()


On a ctrl keydown event, the ctrl boolean will be true

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -429,9 +429,9 @@

       # Simulate keydown events for the modifier key being pressed on its own
       # prior to the key it is modifying.
-      keymapManager.handleKeyboardEvent(buildKeydownEvent('ctrl', target: element))
+      keymapManager.handleKeyboardEvent(buildKeydownEvent('ctrl', ctrl: true, target: element))
       keymapManager.handleKeyboardEvent(buildKeydownEvent('a', ctrl: true, target: element))
-      keymapManager.handleKeyboardEvent(buildKeydownEvent('ctrl', target: element))
+      keymapManager.handleKeyboardEvent(buildKeydownEvent('ctrl', ctrl: true, target: element))
       keymapManager.handleKeyboardEvent(buildKeydownEvent('alt', ctrl: true, target: element))
       keymapManager.handleKeyboardEvent(buildKeydownEvent('b', ctrl: true, alt: true, target: element))


Remove stray console.log

Remove stray console.log

--- src/directory-view.coffee
+++ src/directory-view.coffee
@@ -26,7 +26,6 @@
     if repo?
       path = @directory.getPath()
       @directoryName.addClass('ignored') if repo.isPathIgnored(path)
-      console.log path, repo.getWorkingDirectory()
       if path is repo.getWorkingDirectory()
         @directoryName.addClass('repository-icon')
       else


Move MathJax config to base so it is always run

This prevents an issue where using MathJax would not be configured
correctly unless using the mathjax directive.


:art: Consolidate a bunch of the spec code


Extracted VoteLineView

Extracted VoteLineView

--- core/app/backbone/views/fact_relations/argument_vote_view.coffee
+++ core/app/backbone/views/fact_relations/argument_vote_view.coffee
@@ -1,8 +1,4 @@
-highlight = ($el) ->
-  _.defer (-> $el.addClass 'vote-up-down-highlight')
-  _.delay (-> $el.removeClass 'vote-up-down-highlight'), 1000
-
-class FactVoteLineView extends Backbone.Marionette.ItemView
+class VoteLineView extends Backbone.Marionette.ItemView

   className: 'vote-up-down-line'

@@ -15,32 +11,18 @@
     believes: @model.isBelieving()
     disbelieves: @model.isDisBelieving()

+  onChange: ->
+    @render()
+    _.defer (=> @$el.addClass 'vote-up-down-highlight')
+    _.delay (=> @$el.removeClass 'vote-up-down-highlight'), 1000
+
+class FactVoteLineView extends VoteLineView
   modelEvents:
     'sync': 'onChange'

-  onChange: ->
-    @render()
-    highlight @$el
-
-class FactRelationVoteLineView extends Backbone.Marionette.ItemView
-
-  className: 'vote-up-down-line'
-
-  events:
-    'click .js-believe':    -> @model.believe()
-    'click .js-disbelieve': -> @model.disbelieve()
-    'click .js-undo':       -> @model.undoOpinion()
-
-  templateHelpers: =>
-    believes: @model.isBelieving()
-    disbelieves: @model.isDisBelieving()
-
+class FactRelationVoteLineView extends VoteLineView
   modelEvents:
     'change:current_user_opinion': 'onChange'
-
-  onChange: ->
-    @render()
-    highlight @$el

 class ArgumentVoteView extends Backbone.Marionette.Layout
   className: 'vote-up-down'


Show statistics in topic popover.

Show statistics in topic popover.

--- core/app/backbone/views/topics/topic_popover_content_view.coffee
+++ core/app/backbone/views/topics/topic_popover_content_view.coffee
@@ -2,11 +2,14 @@
   template:
     text: """
       {{title}}
+

     """

   regions:
+    topicStatisticsRegion: '.js-topic-statistics-region'
     favouriteButtonRegion: '.js-favourite-button-region'

   onRender: ->
+    @topicStatisticsRegion.show new TopicStatisticsView(model: @model)
     @favouriteButtonRegion.show new FavouriteTopicButtonView(topic: @model, mini: true)


if not -> unless

if not -> unless

--- lib/notifications-log-item.coffee
+++ lib/notifications-log-item.coffee
@@ -27,7 +27,7 @@
     @element.appendChild(notificationElement)
     @element.appendChild(@timestamp)
     @element.addEventListener 'click', (e) =>
-      if not e.target.closest('.btn-toolbar a, .btn-toolbar button')?
+      unless e.target.closest('.btn-toolbar a, .btn-toolbar button')?
         @emitter.emit 'click'

     @element.getRenderPromise = -> notificationView.getRenderPromise()


if not -> unless

if not -> unless


chore: Opt out of new line length limit behavior.

--- grammars/repositories/asciidoc-grammar.cson
+++ grammars/repositories/asciidoc-grammar.cson
@@ -3,6 +3,7 @@
 scopeName: ''
 fileTypes: []

+limitLineLength: false
 maxTokensPerLine: 100

 patterns: [


StackView: update connections when data changed

--- client/Environments/views/stackview.coffee
+++ client/Environments/views/stackview.coffee
@@ -25,12 +25,14 @@
     # Domains Container
     @domains = new EnvironmentDomainContainer {}, @stack
     @scene.addContainer @domains
-    # @domains.on 'itemAdded', @lazyBound 'updateView', yes
+    @domains.on 'itemAdded',   @lazyBound 'updateView', yes
+    @domains.on 'itemRemoved', @lazyBound 'updateView', yes

     # VMs / Machines Container
     @machines = new EnvironmentMachineContainer {}, @stack
     @scene.addContainer @machines
-    # @machies.on 'itemAdded', @lazyBound 'updateView', yes
+    @machines.on 'itemAdded',   @lazyBound 'updateView', yes
+    @machines.on 'itemRemoved', @lazyBound 'updateView', yes

     # Extras Container
     # @extras = new EnvironmentExtraContainer {}, @stack
@@ -265,6 +267,7 @@

   updateView:(dataUpdated = no)->
+
     @scene.updateConnections()  if dataUpdated

     @setHeight @getProperHeight()  if @getHeight() > 50


Represent dates as Date objects rather than strings

--- src/eg/fieldmapper.coffee
+++ src/eg/fieldmapper.coffee
@@ -56,18 +56,22 @@
 	}

-	# It might be more "correct" to use Date objects to represent dates in
-	# the app. For the most part though, these dates are simply displayed
-	# to the user (as strings), possibly modified by the user (as strings),
-	# and then sent back to the server (again, as strings). So the lifetime
-	# for a Date object is incredibly short and I'm not sure it's worth it
-	# to convert them.
 	typemap = {
 		'':        identity
 		'fm':      guard_null (x) => if typeof x is 'object' then @fieldmap x else x
 		'number':  guard_null Number
 		'string':  guard_null String
-		'date':    guard_null String
+
+		# Use Date objects to represent dates in the app.
+		# For the most part though,
+		# dates are displayed to the user as strings,
+		# and possibly modified by the user as strings
+		# and then sent back to the server as strings.
+		'date': (x) ->
+			return x unless x?
+			[yr, mon, day, hh, mm, ss, tz] = ((String x).replace /\D/g, ' ').split ' '
+			new Date yr, --mon, day, hh, mm, ss
+
 		'boolean': (x) ->
 			switch x
 				when 't', '1' then true


first teamleader applicant should win, or first substituteTeamleader

--- both/methods/shift/shiftRequest.coffee
+++ both/methods/shift/shiftRequest.coffee
@@ -53,36 +53,13 @@
 							Meteor.call 'sendTeamUpdate', shiftId, teamId, 'participant'
 						else throw new Meteor.Error 500, TAPi18n.__('modal.shift.maximumReached')
 					else if team.pending.length >= team.min - 1
+
+						teamleader = team.pending.find(user => user.teamleader) || team.pending.find(user => user.substituteTeamleader)
+
 						approvedUsers = [ ]
 						declinedUsers = [ ]
-						hasTeamleader = false
-						chosenId = null
-						chosenIsTeamleader = false

-						if user.teamleader
-							hasTeamleader = true
-							chosenId = user._id
-							chosenIsTeamleader = true
-						else if user.substituteTeamleader
-							hasTeamleader = true
-							chosenId = user._id
-							chosenIsTeamleader = false
-
-						for pendingUser in team.pending when pendingUser.teamleader || pendingUser.substituteTeamleader
-							hasTeamleader = true
-
-							if !chosenId?
-								if pendingUser.teamleader
-									chosenId = pendingUser._id
-									chosenIsTeamleader = true
-								else if pendingUser.substituteTeamleader
-									chosenId = pendingUser._id
-									chosenIsTeamleader = false
-							else if !chosenIsTeamleader && pendingUser.substituteTeamleader
-								chosenId = pendingUser._id
-								chosenIsTeamleader = false
-
-						if hasTeamleader
+						if teamleader?
 							approvedUsers.push userId

 							Shifts.update _id: shiftId, 'teams._id': teamId,
@@ -121,7 +98,7 @@
 								for participant in otherTeam.participants when participant._id in approvedUsers
 									Meteor.call 'declineParticipant', shiftId, otherTeam._id, participant._id

-							Meteor.call 'setLeader', shiftId, team._id, chosenId
+							Meteor.call 'setLeader', shiftId, team._id, teamleader._id

 							for approvedUser in approvedUsers
 								Meteor.call 'sendConfirmation', shiftId, teamId, approvedUser


setup ga early

setup ga early

--- assets/scripts/travis.coffee
+++ assets/scripts/travis.coffee
@@ -107,8 +107,8 @@
       @store = app.store
       $ => app.initialize()

+setupGoogleAnalytics()
+
 require 'ext/i18n'
 require 'travis/ajax'
 require 'app'
-
-setupGoogleAnalytics()


:shirt:

:shirt:

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -428,7 +428,7 @@

       describe "on the middle of second line", ->
         it "deletes the last two lines", ->
-          editor.setCursorScreenPosition([1,2])
+          editor.setCursorScreenPosition([1, 2])
           keydown('d')
           keydown('j')
           expect(editor.getText()).toBe("12345\n")
@@ -441,7 +441,7 @@

       describe "on the end of the file", ->
         it "deletes the bottom two lines", ->
-          editor.setCursorScreenPosition([2,4])
+          editor.setCursorScreenPosition([2, 4])
           keydown('d')
           keydown('k')
           expect(editor.getText()).toBe("12345\n")
@@ -455,7 +455,7 @@

       describe "when on the middle of second line", ->
         it "deletes the first two lines", ->
-          editor.setCursorScreenPosition([1,2])
+          editor.setCursorScreenPosition([1, 2])
           keydown('d')
           keydown('k')
           expect(editor.getText()).toBe("ABCDE")


Fix test

Fix test

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -617,13 +617,13 @@
   describe "when a directory is double-clicked", ->
     it "toggles the directory expansion state and does not change the focus to the editor", ->
       jasmine.attachToDOM(workspaceElement)
-      treeView.focus()

       subdir = null
       waitsForFileToOpen ->
         sampleJs.trigger clickEvent(originalEvent: {detail: 1})

       runs ->
+        treeView.focus()
         subdir = root1.find('.directory:first')
         subdir.trigger clickEvent(originalEvent: {detail: 1})
         expect(subdir).toHaveClass 'selected'


provider: xss fix

provider: xss fix

--- workers/social/lib/social/error.coffee
+++ workers/social/lib/social/error.coffee
@@ -4,9 +4,11 @@

 module.exports = class KodingError extends Error

+  encoder   = require 'htmlencode'
+
   constructor: (message, name, errorObject) ->

-    return new KodingError message  unless this instanceof KodingError
+    return new KodingError encoder.XSSEncode message  unless this instanceof KodingError

     Error.call this


Updating Config documentation.

Updating Config documentation.

--- www/src/theoricus/config/config.coffee
+++ www/src/theoricus/config/config.coffee
@@ -34,8 +34,8 @@
   Config constructor, initializing the app's config settings.
   @class Config
   @constructor
-  @param @the {Theoricus} Shortcut for app's instance
-  @param @Settings {Object} App settings
+  @param the {Theoricus} Shortcut for app's instance
+  @param Settings {Object} App settings
   ###
   constructor:( @the, @Settings )->
     @disable_transitions = @Settings.disable_transitions ? false


do not put empty value for mail field when generated froom remote data

--- server/models/mail.coffee
+++ server/models/mail.coffee
@@ -53,9 +53,9 @@
         params =
             cid:         attachment.contentId or 'null'
             fileName:    attachment.fileName
-            contentType: attachment.contentType
-            length:      attachment.length
-            checksum:    attachment.checksum
+            contentType: attachment.contentType or ''
+            length:      attachment.length or 0
+            checksum:    attachment.checksum or ''
             mailbox:     @mailbox
             mailId:      @id


<model> Rule - improved increment/decrement updates.

--- server/models/rule.coffee
+++ server/models/rule.coffee
@@ -6,6 +6,7 @@
 ###

 cozydb = require 'cozydb'
+
 SensorRule = require './sensorRule'
 ActuatorRule = require './actuatorRule'

@@ -22,8 +23,7 @@
 	# @param callback (Function(Error):null):				Callback
 	###
 	@incrementNbSensorRules(callback)
-		@nbSensorRules++
-		RuleModel.update @, callback
+		@updateAttributes nbSensorRules: @nbSensorRules+1 callback

 	###
 	# decrementNbSensorRules
@@ -32,8 +32,7 @@
 	# @param callback (Function(Error):null):				Callback
 	###
 	@decrementNbSensorRules(callback)
-		@nbSensorRules--
-		RuleModel.update @, callback
+		@updateAttributes nbSensorRules: @nbSensorRules-1 callback

 	###
 	# incrementNbSensorRulesMet
@@ -42,8 +41,7 @@
 	# @param callback (Function(Error):null):				Callback
 	###
 	@incrementNbSensorRulesMet(callback)
-		@nbSensorRulesMet++
-		RuleModel.update @, callback
+		@updateAttributes nbSensorRulesMet: @nbSensorRulesMet+1 callback

 	###
 	# decrementNbSensorRulesMet
@@ -52,8 +50,7 @@
 	# @param callback (Function(Error):null):				Callback
 	###
 	@decrementNbSensorRulesMet(callback)
-		@nbSensorRulesMet--
-		RuleModel.update @, callback
+		@updateAttributes nbSensorRulesMet: @nbSensorRulesMet-1 callback

 	###
 	# createSensorRule
@@ -129,4 +126,3 @@
 				callback msgErr

 		super callback
-


changes to task plan store to get topics and creating working

--- src/flux/task-plan.coffee
+++ src/flux/task-plan.coffee
@@ -1,8 +1,12 @@
 _ = require 'underscore'
 {CrudConfig, makeSimpleStore, extendConfig} = require './helpers'

+CREATE_KEY = "CREATING"
+
 TaskPlanConfig =
-  _getPlan: (planId) -> @_local[planId]
+  _getPlan: (planId) ->
+    @_local[planId]?.topics = [] if (@_local[planId] and !@_local[planId].topics) #TODO take out once TaskPlan api is in place
+    @_local[planId]

   updateTitle: (id, title) ->
     plan = @_getPlan(id)
@@ -16,12 +20,33 @@

   addTopic: (id, topicId) ->
     plan = @_getPlan(id)
-    plan.topics.push(topicId) unless plan.topics.indexOf(topicId) >= 0
+    plan.topics.push(topicId) unless plan.topics?.indexOf(topicId) >= 0
     @emitChange()

   removeTopic: (id, topicId) ->
     plan = @_getPlan(id)
+
+    index = plan.topics?.indexOf(topicId)
+    plan.topics.splice(index, 1)
     @emitChange()
+
+  create: (due_at = null) ->
+    @_local[CREATE_KEY] = {
+      due_at: due_at
+    }
+
+  created: (result, id) ->
+    @_asyncStatus[id] = CREATED
+    @_local[id] = result
+
+  exports:
+    hasTopic: (id, topicId) ->
+      plan = @_getPlan(id)
+      plan?.topics?.indexOf(topicId) >= 0
+    getCreateKey: -> CREATE_KEY
+    getTopics: (id) ->
+      plan = @_getPlan(id)
+      plan?.topics

 extendConfig(TaskPlanConfig, CrudConfig)


Segragate Bower from Preflight

Segragate Bower from Preflight

--- library/membrane/preflight.coffee
+++ library/membrane/preflight.coffee
@@ -38,98 +38,17 @@
 http = require "http"
 util = require "util"

-{Screenplay} = require "./visual"
+{BowerSupport} = require "./bower"

-# This abstract base class service is an extension of the `Visual`
-# basis that does some further environment initialization and set
+# This abstract base class service is an extension of the Screenplay
+# family that does some further environment initialization and set
 # up. These preparations will be nececessary no matter what sort of
-# visual functionality you are going to implement. Currently the
-# primary purpose of preflight is implementation of Bower support.
-module.exports.Preflight = class Preflight extends Screenplay
+# Screebplay functionality you are going to implement. Currently the
+# purpose of preflight is drawing in the remoted and Bower packages.
+module.exports.Preflight = class Preflight extends BowerSupport

     # This is a marker that indicates to some internal subsystems
     # that this class has to be considered abstract and therefore
     # can not be treated as a complete class implementation. This
     # mainly is used to exclude or account for abstract classes.
     @abstract yes
-
-    # Install the specified package via Bower into the specific
-    # location within the system that is figured out automatically.
-    # All packages installed via Bower will be served as the static
-    # assets, by using the `pub` env dir. The package installation
-    # is per service and automatically will be included in `prelude`.
-    @bower: (target, options={}) ->
-        previous = @bowerings or []
-        noTarget = "target must be a string"
-        noOptions = "options must be an object"
-        assert _.isObject(options), noOptions
-        assert _.isString(target), noTarget
-        return @bowerings = previous.concat
-            options: options
-            target: target
-
-    # A hook that will be called prior to registering the service
-    # implementation. Please refer to this prototype signature for
-    # information on the parameters it accepts. Beware, this hook
-    # is asynchronously wired in, so consult with `async` package.
-    # Please be sure invoke the `next` arg to proceed, if relevant.
-    register: (kernel, router, next) ->
-        hash = crypto.createHash "md5"
-        hash.update @constructor.identify()
-        id = hash.digest("hex").toString()
-        bowerings = @constructor.bowerings ?= []
-        options = _.map(bowerings, (b) -> b.options)
-        options = _.merge Object.create({}), options...
-        directory = kernel?.scope?.envPath "pub", "bower", id
-        assert _.isString(directory), "failed to get dir"
-        options.directory = bowerings.directory = directory
-        targets = _.map bowerings, (b) -> b.target
-        running = "Running Bower install for %s service"
-        identify = @constructor?.identify().toString()
-        logger.info running.grey, identify.underline
-        @installation kernel, targets, options, next
-
-    # An internal routine that launches the actual Bower installer.
-    # It takes a series of pre calculated parameters to be able to
-    # perform the installation properly. Plese refer to the register
-    # hook implementation in this ABC service for more information.
-    installation: (kernel, targets, options, next) ->
-        install = bower.commands.install
-        bowerings = @constructor.bowerings ?= []
-        installer = install targets, {}, options
-        installer.on "error", (error) ->
-            reason = "failed Bower package installation"
-            logger.error error.message.toString().red, error
-            kernel.shutdownKernel reason.toString()
-        installer.on "end", (installed) =>
-            bowerings.installed = installed
-            message = "Get Bower lib %s@%s at %s"
-            for packet in _.values(installed or {})
-                name = packet.pkgMeta?.name.underline
-                version = packet.pkgMeta?.version.underline
-                where = @constructor.identify().underline
-                logger.debug message.cyan, name, version, where
-            return next()
-
-    # This server side method is called on the context prior to the
-    # context being compiled and flushed down to the client site. The
-    # method is wired in an asynchronous way for greater functionality.
-    # This is the place where you would be importing the dependencies.
-    prelude: (context, request, next) ->
-        list = bower.commands.list
-        bowerings = @constructor.bowerings ?= []
-        cached context if cached = bowerings.cached
-        return next() if _.isFunction cached
-        options = directory: bowerings.directory
-        esc = (p) -> new RegExp RegExp.escape "#{p}"
-        match = (k) -> (b) -> esc(k).test b.target
-        sorter = (v, k) -> _.findIndex bowerings, match(k)
-        list(paths: yes, options).on "end", (paths) ->
-            bowerings.cached = (context) ->
-                sorted = _.sortBy paths, sorter
-                files = _.flatten _.values sorted
-                for file in files then do (file) ->
-                    ext = (e) -> path.extname(file) is e
-                    context.scripts.push file if ext ".js"
-                    context.sheets.push file if ext ".css"
-            bowerings.cached context; next()


Update src/plugins/markdown-page.coffee

Update src/plugins/markdown-page.coffee

Modified regex for issue #80

--- src/plugins/markdown-page.coffee
+++ src/plugins/markdown-page.coffee
@@ -69,7 +69,7 @@

   if content[0...3] is '---'
     # "Front Matter"
-    result = content.match /-{3,}\s([\s\S]*?)-{3,}\s([\s\S]*)/
+    result = content.match /^-{3,}\s([\s\S]*?)-{3,}(\s[\s\S]*|\s?[^\s\S]*)$/
     if result?.length is 3
       metadata = result[1]
       markdown = result[2]


now finally fix?

now finally fix?

--- scripts/import_coppermine.coffee
+++ scripts/import_coppermine.coffee
@@ -32,8 +32,20 @@
     query("SET NAMES 'latin1';")
   ]).spread (root) ->
     convertSubcategories(0, root).then ->
-      setThumbnail root
-      saveAlbum root
+      finalizeAlbum root
+
+finalizeAlbum = (edegalAlbum, parent) ->
+  setThumbnail edegalAlbum
+
+  if parent? and not _.find(parent.subalbums, (subalbum) -> subalbum.path == edegalAlbum.path)
+    parent.subalbums.push _.pick edegalAlbum, 'path', 'title', 'thumbnail', '_pos'
+
+  edegalAlbum.subalbums = _.chain(edegalAlbum.subalbums)
+    .sortBy((subalbum) -> subalbum._pos)
+    .map((subalbum) -> _.omit(subalbum, '_pos'))
+    .value()
+
+  saveAlbum edegalAlbum

 decodeEntities = (obj, fields...) ->
   for field in fields
@@ -101,17 +113,7 @@

     Q.all(work)
   .then ->
-    setThumbnail edegalAlbum
-
-    if parent? and not _.find(parent.subalbums, (subalbum) -> subalbum.path == edegalAlbum.path)
-      parent.subalbums.push _.pick edegalAlbum, 'path', 'title', 'thumbnail', '_pos'
-
-    edegalAlbum.subalbums = _.chain(edegalAlbum.subalbums)
-      .sortBy((subalbum) -> subalbum._pos)
-      .map((subalbum) -> _.omit(subalbum, '_pos'))
-      .value()
-
-    saveAlbum edegalAlbum
+    finalizeAlbum edegalAlbum, parent
   .then ->
     edegalAlbum


Increase request timeout and max memory

Increase request timeout and max memory

--- h2o-web/src/main/steam/scripts/global.tests.coffee
+++ h2o-web/src/main/steam/scripts/global.tests.coffee
@@ -125,7 +125,7 @@
     opts =
       method: 'GET'
       url: "http://#{host}#{path}"
-      timeout: 15000
+      timeout: 900000
     makeRequest opts, go

   link$ _.h2oPost, (path, parameters, go) ->
@@ -133,7 +133,7 @@
       method: 'POST'
       url: "http://#{host}#{path}"
       form: parameters
-      timeout: 15000
+      timeout: 900000
     makeRequest opts, go

 _clouds = []
@@ -142,7 +142,7 @@
 _spawnCloud = ->
   diag "Starting new H2O cloud..."
   cloudId = uniqueCloudId()
-  cloud = spawn 'java', [ '-Xmx1g', '-jar', JAR_PATH, '-name', "steam_#{cloudId}", '-port', "#{cloudId + 60000}" ]
+  cloud = spawn 'java', [ '-Xmx4g', '-jar', JAR_PATH, '-name', "steam_#{cloudId}", '-port', "#{cloudId + 60000}" ]
   diag "PID #{cloud.pid}"
   _clouds.push cloud
   cloud


Disabled HTML5Mode & hashPrefix override as it is not needed as of now

--- client/redeem/javascripts/app/index.coffee
+++ client/redeem/javascripts/app/index.coffee
@@ -28,12 +28,14 @@

 .config ($locationProvider, $analyticsProvider) ->

+  $analyticsProvider.virtualPageviews false
+
   # https://github.com/angulartics/angulartics#full-path-tracking-for-pages-without-a-router
-  # $analyticsProvider.firstPageview true
-  # $analyticsProvider.withBase true
+  $analyticsProvider.firstPageview true
+  $analyticsProvider.withBase true

-  $locationProvider.html5Mode true
-  $locationProvider.hashPrefix '!'
+#   $locationProvider.html5Mode true
+#   $locationProvider.hashPrefix '!'

 .run ($rootScope, errorModal) ->


Removed useless  config in Redeem page

Removed useless  config in Redeem page

--- client/redeem/javascripts/app/index.coffee
+++ client/redeem/javascripts/app/index.coffee
@@ -26,7 +26,7 @@

 .controller 'RedeemCodeController', redeemCodeController

-.config ($locationProvider, $analyticsProvider) ->
+.config ($analyticsProvider) ->

   $analyticsProvider.virtualPageviews false

@@ -34,9 +34,6 @@
   $analyticsProvider.firstPageview true
   $analyticsProvider.withBase true

-#   $locationProvider.html5Mode true
-#   $locationProvider.hashPrefix '!'
-
 .run ($rootScope, errorModal) ->

   $rootScope.$on 'connection_refused', (msg, data) ->


added describe and query to adapter

added describe and query to adapter

--- lib/sails-migrations/adapter.coffee
+++ lib/sails-migrations/adapter.coffee
@@ -1,9 +1,25 @@
 class Adapter
   constructor: (@adapter)->

+  registerCollection: (tableName, cb)->
+    @adapter.registerCollection({ identity: tableName, config: @adapter.config }, cb)
+
   define: (tableName, definition, cb)->
-    @adapter.registerCollection({ identity: tableName, config: @adapter.config }, (err)=>
+    @registerCollection(tableName, (err)=>
       return cb(err) if err
       @adapter.define(tableName, definition, cb)
     )
+
+  query: (query, data, cb)->
+    tableName = 'query'
+    @registerCollection(tableName, (err)=>
+      return cb(err) if err
+      @adapter.query(tableName, query, data, cb)
+    )
+
+  describe: (tableName, cb)->
+    @registerCollection(tableName, (err)=>
+      return cb(err) if err
+      @adapter.describe(tableName, cb)
+    )
 module.exports = Adapter


Scripts: user-updater: better logging

Scripts: user-updater: better logging

--- scripts/user-updater/main.coffee
+++ scripts/user-updater/main.coffee
@@ -76,7 +76,7 @@

     fetchAccount account._id, (err, { account }) ->
       return callback err  if err
-      return callback new KodingError 'no account'  unless account
+      return callback "No account found for id: #{account?._id}, index: #{index}"  unless account

       account.fetchAppStorages (err, storages) ->
         return callback err  if err
@@ -103,7 +103,7 @@

   query = {}
   JAccount.count query, (err, accountCount) ->
-    console.log "#{accountCount} accounts found, starting..."
+    console.log "#{accountCount - skip} accounts found, starting..."

     fields = { _id : 1 }
     JAccount.someData query, fields, { skip }, (err, cursor) ->


Fix autocomplete for variables that start with a lowercase character

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -74,9 +74,6 @@

             if noCurrent
                 return null
-
-        else if className.charAt(0).toUpperCase() != className.charAt(0)
-            return null

         if className and className[0] == "\\"
             return className.substr(1) # FQCN, not subject to any further context.
@@ -556,7 +553,7 @@

         # Regex variable definition
         regexElement = new RegExp("\\#{element}[\\s]*=[\\s]*([^;]+);", "g")
-        regexNewInstance = new RegExp("\\#{element}[\\s]*=[\\s]*new[\\s]*\\\\?([A-Z][a-zA-Z_\\\\]*)+(?:(.+)?);", "g")
+        regexNewInstance = new RegExp("\\#{element}[\\s]*=[\\s]*new[\\s]*\\\\?([a-zA-Z][a-zA-Z_\\\\]*)+(?:(.+)?);", "g")
         regexCatch = new RegExp("catch[\\s]*\\([\\s]*([A-Za-z0-9_\\\\]+)[\\s]+\\#{element}[\\s]*\\)", "g")

         lineNumber = bufferPosition.row - 1


rm forgotten console.info()

rm forgotten console.info()

--- client/jquery-dyalog.coffee
+++ client/jquery-dyalog.coffee
@@ -23,9 +23,7 @@
           $(document).on 'keydown', '*', x.key, -> x.action(); false
         if x.checked?
           $a.addClass('toggle').toggleClass 'checked', x.checked
-            .on 'mousedown mouseup click', (e) ->
-              console.info e.type, $(e.target).text()
-              $(@).toggleClass 'checked'; mFocus null; x.action? $(@).hasClass '.checked'; false
+            .on 'mousedown mouseup click', (e) -> $(@).toggleClass 'checked'; mFocus null; x.action? $(@).hasClass '.checked'; false
         else
           if x.action then $a.on 'mousedown mouseup click', (e) -> mFocus null; x.action(); false
         if !x.items then return $a


kite: environment version number fix

kite: environment version number fix

--- node_modules_koding/koding-kite/environment.coffee
+++ node_modules_koding/koding-kite/environment.coffee
@@ -10,7 +10,7 @@
   constructor: (@options={})->
     @basePath = path.join env.HOME || env.HOMEPATH || env.USERPROFILE, ".kd"
     @options.username     or= env.USER
-    @options.version      or= "1"
+    @options.version      or= "0.0.1"
     @options.environment  or= "development"
     @options.region       or= "localhost"
     @options.kontrolAddress or= 'ws://localhost:4000/dnode'


Fixed error message

Fixed error message

--- client/app/main/main.controller.coffee
+++ client/app/main/main.controller.coffee
@@ -34,11 +34,13 @@
       state.go('main.matches', {player: playerName})
       location.path('/' + playerName + '/matches')
   errorSearch = do(scope=$scope)->
-    return ->
-      scope.error = 'No player found'
-  errorUpdate = do(scope=$scope)->
-    return ->
-      scope.error = 'Enable your plugin and refresh the game!'
+    return (error)->
+      console.log error
+      if error is 'Bad Request'
+        scope.error = 'Enable your plugin and refresh the game!'
+      else
+        scope.error = 'No player found'
+
   $scope.searchPlayer = (playerName)->
     if playerName
       console.log(playerName, 'searching')
@@ -55,17 +57,6 @@
         $scope.playerData = {}
         $http.get("/api/data/" + playerName).success(loadPlayerData).error(errorSearch)

-  $scope.refreshPlayer = ->
-    if $scope.profile
-      $http.put("/api/data/" + $scope.profile.playerName.split(' ').join('.')).success(
-        (playerData)->
-          $scope.error = ''
-          $scope.profile = playerData.profile
-          $scope.gameLog = playerData.gameLog
-          $scope.playerData = playerData
-          playerName = $scope.convertName(playerData.profile.playerName)
-          $scope.recentSearches = $scope.addRecent(playerName, playerData)
-        ).error(errorUpdate)
   $scope.clearHistory = ->
     $scope.recentSearches = Recent.clearHistory()
   $scope.checkLocation = (location)->


Added : Retrieve sensor data by last timestamp

--- express/routes/sensor.coffee
+++ express/routes/sensor.coffee
@@ -1,11 +1,13 @@
 mongo = require '../mongo'
 async = require 'async'
+
 exports.lastData = (req, res) ->
 #    if req.get('content-type').indexOf('application/json') == -1
 #        throw new Error("Body request is not JSON.")

     mongo.dataCollection.findOne(
         {'sensor_id' : req.params.sensor_id},
+        {timestamp: 1},
         (err, object) ->
             if err
                 res.send 500
@@ -31,16 +33,15 @@
                     (sensor, callback) ->
                         console.log("Fetching last data")
                         console.log(sensor)
-                        mongo.dataCollection.findOne(
-                            {'sensor_id' : sensor.sensor_id},
+                        mongo.dataCollection.find({'sensor_id' : sensor.sensor_id}).sort({'timestamp': -1}).toArray(
                             (err, data) ->
                                 if err
                                     res.send 500
                                 else
-                                    sensor.lastData = data
+                                    sensor.lastData = data[0]
                                     console.log sensor
                                     callback null, sensor
-                            )
+                        )
                     (err, result) ->
                         if(err)
                             res.send 500
@@ -57,13 +58,12 @@
             if err
                 res.send 500
             else
-                mongo.dataCollection.findOne(
-                    {'sensor_id' : req.params.sensor_id},
+                mongo.dataCollection.find({'sensor_id' : sensor.sensor_id}).sort({timestamp: -1}).toArray(
                     (err, data) ->
                         if err
                             res.send 500
                         else
-                            sensor.lastData = data
+                            sensor.lastData = data[0]
                             res.send(JSON.stringify(sensor), 200)
                     )
         )


linked_heatmaps: Add titles on plots to right indicating m,p values

--- linked_heatmaps/linked_heatmaps.coffee
+++ linked_heatmaps/linked_heatmaps.coffee
@@ -153,6 +153,15 @@
                .attr("dominant-baseline", "middle")
                .attr("fill", [darkBlue, darkRed][j])

+    for j in [2..3]
+      panels[i][j].append("text").attr("id", "mptitle#{i}#{j}")
+               .text("")
+               .attr("x", w/2)
+               .attr("y", -pad.top*0.3)
+               .attr("text-anchor", "middle")
+               .attr("dominant-baseline", "middle")
+               .attr("fill", labelcolor)
+
     # scales for loglik vs p/m curves
     xScalemp[i] = []
     xScalemp[i][0] = d3.scale.linear()
@@ -222,6 +231,8 @@
                        title1 = "loglik = #{onedig(d.kll)}"
                        d3.select("text#loglik#{d.chr}0").text(title0)
                        d3.select("text#loglik#{d.chr}1").text(title1)
+                       d3.select("text#mptitle#{d.chr}2").text("m = #{data.m[d.col]}")
+                       d3.select("text#mptitle#{d.chr}3").text("p = #{data.p[d.row]}")
                        panels[d.chr][0].append("rect").attr("id","mouseover0")
                                 .attr("x", xScaleImg[d.chr](data.m[d.col]))
                                 .attr("y", yScaleImg[d.chr](data.p[d.row]))
@@ -244,6 +255,8 @@
              .on "mouseout", (d) ->
                        d3.select("text#loglik#{d.chr}0").text("")
                        d3.select("text#loglik#{d.chr}1").text("")
+                       d3.select("text#mptitle#{d.chr}2").text("")
+                       d3.select("text#mptitle#{d.chr}3").text("")
                        d3.select("rect#mouseover0").remove()
                        d3.select("rect#mouseover1").remove()
                        d3.selectAll("g.mpcurve").remove()
@@ -264,6 +277,8 @@
                        title1 = "loglik = #{onedig(d.kll)}"
                        d3.select("text#loglik#{d.chr}0").text(title0)
                        d3.select("text#loglik#{d.chr}1").text(title1)
+                       d3.select("text#mptitle#{d.chr}2").text("m = #{data.m[d.col]}")
+                       d3.select("text#mptitle#{d.chr}3").text("p = #{data.p[d.row]}")
                        panels[d.chr][0].append("rect").attr("id","mouseover0")
                                 .attr("x", xScaleImg[d.chr](data.m[d.col]))
                                 .attr("y", yScaleImg[d.chr](data.p[d.row]))
@@ -287,6 +302,8 @@
              .on "mouseout", (d) ->
                        d3.select("text#loglik#{d.chr}0").text("")
                        d3.select("text#loglik#{d.chr}1").text("")
+                       d3.select("text#mptitle#{d.chr}2").text("")
+                       d3.select("text#mptitle#{d.chr}3").text("")
                        d3.select("rect#mouseover0").remove()
                        d3.select("rect#mouseover1").remove()
                        d3.selectAll("g.mpcurve").remove()


history viewer: set editor using a more sensible approach.

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -2991,17 +2991,22 @@
         return obj

     goto_revision: (num) ->
+        #t = misc.mswalltime()
         @element.find(".salvus-editor-history-revision-number").text("Revision " + num)
         if num == 0
             @element.find(".salvus-editor-history-revision-time").text("")
         else
             @element.find(".salvus-editor-history-revision-time").text(new Date(@parse_logstring(@log[@nlines-num+1]).time).toLocaleString())
         if @revision_num - num > 2
+            #console.log("goto_revision #{num} from #{@revision_num}"); t = misc.mswalltime()
             text = @history_editor.codemirror.getValue()
             text_old = text
+            #console.log("got value", misc.mswalltime(t)); t = misc.mswalltime()
             for patch in @log[(@nlines-@revision_num+1)..(@nlines-num)]
                 text = diffsync.dmp.patch_apply(@parse_logstring(patch).patch, text)[0]
-            @diffsync.patch_in_place(diffsync.dmp.patch_make(text_old, text))
+            #console.log("patched text", misc.mswalltime(t)); t = misc.mswalltime()
+            @history_editor.codemirror.setValue(text)
+            #console.log("changed editor", misc.mswalltime(t)); t = misc.mswalltime()
         else if @revision_num > num
             for patch in @log[(@nlines-@revision_num+1)..(@nlines-num)]
                 @diffsync.patch_in_place(@parse_logstring(patch).patch)
@@ -3010,7 +3015,7 @@
             text_old = text
             for patch in @log[(@nlines-num+1)..(@nlines-@revision_num)].reverse()
                 text = diffsync.dmp.patch_apply(@parse_logstring_inverse(patch).patch, text)[0]
-            @diffsync.patch_in_place(diffsync.dmp.patch_make(text_old, text))
+            @history_editor.codemirror.setValue(text)
         else
             for patch in @log[(@nlines-num+1)..(@nlines-@revision_num)].reverse()
                 @diffsync.patch_in_place(@parse_logstring_inverse(patch).patch)
@@ -3025,6 +3030,7 @@
             @forward_button.removeClass("disabled")
         if @ext == 'sagews'
             @worksheet.process_sage_updates()
+        #console.log("going to revision #{num} took #{misc.mswalltime(t)}ms")

     render_history: (first) =>
         @log = @file_history.live().split("\n")


dispatch stop instance request

dispatch stop instance request

--- imports/ui/pages/meteor/InstanceDetailPage.coffee
+++ imports/ui/pages/meteor/InstanceDetailPage.coffee
@@ -1,5 +1,6 @@
 _           = require 'lodash'
-{ connect }        = require 'react-redux'
+{ connect } = require 'react-redux'
+{ stopInstanceRequest } = require '/imports/redux/actions/instance.coffee'

 mapStateToProps = (state, { params }) ->
   instance = _.find state.collections.instances, {name: params.name}
@@ -8,6 +9,10 @@
   isLoading: not instance?

 mapDispatchToProps = (dispatch) ->
-    onStopInstance: -> console.log 'stopInstance'
+  onStopInstance: (instanceName) -> dispatch stopInstanceRequest instanceName

-module.exports = connect(mapStateToProps, mapDispatchToProps) require '../InstanceDetailPage.cjsx'
+mergeProps = (stateProps, dispatchProps, ownProps) ->
+  Object.assign {}, stateProps, dispatchProps, ownProps,
+    onStopInstance: -> dispatchProps.onStopInstance stateProps.instance.name
+
+module.exports = connect(mapStateToProps, mapDispatchToProps, mergeProps) require '../InstanceDetailPage.cjsx'


MainView: Removed WorkspaceSelected event listener.

--- client/Core/mainview.coffee
+++ client/Core/mainview.coffee
@@ -9,9 +9,6 @@
     super options, data

     @notifications = []
-
-    @on 'WorkspaceSelected', (options) =>
-      @activitySidebar.selectWorkspace options

   viewAppended: ->


Login: appending a style tag on interpretion for a random bg image

--- client/Login/AppView.coffee
+++ client/Login/AppView.coffee
@@ -78,6 +78,11 @@
     image.src  = bgImageUrl

     image.classList.add 'off-screen-login-image'
+
+    document.head.appendChild (new KDCustomHTMLView {
+      tagName    : 'style'
+      partial    : ".kdview.login-screen:after { background-image : url('#{bgImageUrl}')}"
+    }).getElement()

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


Bugfix: select default mailbox

Bugfix: select default mailbox

--- client/app/stores/account_store.coffee
+++ client/app/stores/account_store.coffee
@@ -109,8 +109,8 @@

         mailboxes = account.get('mailboxes')
         mailbox = mailboxes.filter (mailbox) ->
-            return mailbox.get('label') is 'INBOX'
-        if mailbox.count() is 1
+            return mailbox.get('label').toLowerCase() is 'inbox'
+        if mailbox.count() isnt 0
             return mailbox.first()
         else
             favorites = account.get('favorites')


Bugfix when toggling quick filters

Bugfix when toggling quick filters

--- client/app/stores/message_store.coffee
+++ client/app/stores/message_store.coffee
@@ -205,6 +205,7 @@
             _sortField   = sort.field
             if sort.order?
                 newOrder = sort.order
+                _sortOrder = if sort.order is '-' then 1 else -1
             else
                 currentField = _params.sort.substr(1)
                 currentOrder = _params.sort.substr(0, 1)


Fix rotate and pinch

Fix rotate and pinch

--- framer/GestureManager.coffee
+++ framer/GestureManager.coffee
@@ -9,7 +9,6 @@

 	constructor: (@layer) ->
 		@_manager = Hammer(@layer._element)
-

 	once: (eventName, listener) =>
 		super(eventName, listener)
@@ -39,10 +38,10 @@
 		# Add other recognizers if they existed already
 		existingRecognizers = @_getDependentRecognizersForEventFamily(eventFamily)

-		if existingRecognizers.length
-			@_manager.add(recognizer).recognizeWith(existingRecognizers)
-		else
-			@_manager.add(recognizer)
+		if existingRecognizers.length > 0
+			recognizer.recognizeWith(existingRecognizers)
+
+		@_manager.add(recognizer)

 		# Wrap this layer so we control the scope
 		listener._actual = (event) =>
@@ -87,12 +86,6 @@

 	_getRecognizer: (eventFamily) ->

-		existingRecognizer = @_manager.get(eventFamily)
-
-		if existingRecognizer
-			if eventFamily in [Gestures.Swipe, Gestures.Pan]
-			return existingRecognizer
-
 		switch eventFamily

 			when Gestures.Pan
@@ -115,8 +108,6 @@

 			when Gestures.DoubleTap
 				return new Hammer.Tap({event:Gestures.DoubleTap, taps:2})
-
-

 	_getDependentRecognizersForEventFamily: (eventFamily) ->

@@ -151,7 +142,7 @@
 			when Gestures.Pinch # Pinch depends on Pan and Rotate
 				if pan = @_manager.get(Gestures.Pan)
 					existingRecognizers.push(pan)
-				if rotate = @_manager.get(Gestures.Pinch)
+				if rotate = @_manager.get(Gestures.Rotate)
 					existingRecognizers.push(rotate)

 			when Gestures.DoubleTap # DoubleTap depends on Tap


Removing explicit scripts

Removing explicit scripts

--- source/compiler.coffee
+++ source/compiler.coffee
@@ -24,24 +24,14 @@
       compiler.buffer JSON.stringify(content)

     coffeescript: (content, compiler) ->
-      if compiler.explicitScripts
-        [
-          compiler.scriptTag(CoffeeScript.compile(content))
-        ]
-      else
-        [content]
+      [content]

     javascript: (content, compiler) ->
-      if compiler.explicitScripts
-        [
-          compiler.scriptTag(content)
-        ]
-      else
-        [
-          "`"
-          compiler.indent(content)
-          "`"
-        ]
+      [
+        "`"
+        compiler.indent(content)
+        "`"
+      ]

   styleTag: (content) ->
     @element "style", [], [
@@ -191,12 +181,10 @@

 exports.util = util

-exports.compile = (parseTree, {explicitScripts, name, compiler}={}) ->
+exports.compile = (parseTree, {name, compiler}={}) ->
   # HAX: Browserify can't put CoffeeScript into the web...
   if compiler
     CoffeeScript = compiler
-
-  util.explicitScripts = explicitScripts

   items = util.renderNodes(parseTree)


Add static methods.

Add static methods.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -1221,7 +1221,12 @@
       else if not @static
         o.scope.parent.assign '_this', 'this'
     idt   = o.indent
-    code  = 'public function'
+    code = ''
+    if @static
+      code = 'public static function'
+    else
+      code  = 'public function'
+
     code  += ' ' + @name
     code  += '(' + params.join(', ') + ') {'
     code  += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()


add link to other quotes

add link to other quotes

--- src/smc-webapp/landing_page.cjsx
+++ src/smc-webapp/landing_page.cjsx
@@ -651,6 +651,7 @@
                             They just open up a web browser and go to cloud.sagemath.com and that’s it. They just type code directly
                             in, hit shift+enter and it runs and they can see if it works. It provides immediate feedback. The course
                             management features work really well."

+
Read other quotes.

                         </Well>
                     </Col>
                     <Col sm=5>


remove little warning banner if course fee required

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -156,8 +156,9 @@
         projects :
             # get_total_project_quotas relys on this data
             # Will be removed by #1084
-            project_map              : rtypes.immutable.Map
-            get_total_project_quotas : rtypes.func
+            project_map                       : rtypes.immutable.Map
+            get_total_project_quotas          : rtypes.func
+            date_when_course_payment_required : rtypes.func
         "#{name}" :
             free_warning_extra_shown : rtypes.bool
             free_warning_closed      : rtypes.bool
@@ -190,6 +191,9 @@
         if not require('./customize').commercial
             return null
         if @props.free_warning_closed
+            return null
+        pay = @props.date_when_course_payment_required(@props.project_id)
+        if pay
             return null
         quotas = @props.get_total_project_quotas(@props.project_id)
         if not quotas?


Fixed: mail request was not authentified

--- server/lib/report-manager.coffee
+++ server/lib/report-manager.coffee
@@ -10,6 +10,8 @@

     constructor: ->
         @client = new Client "http://localhost:9101/"
+        unless process.env.NODE_ENV not in ["production", "test"]
+            clientDS.setBasicAuth process.env.NAME, process.env.TOKEN

     start: ->
         @prepareNextCheck()