CodeMirror insists on Shift-Ctrl- instead of Ctrl-Shift-

--- client/prefs-keys.coffee
+++ client/prefs-keys.coffee
@@ -35,9 +35,9 @@
   ['ZMO', 'Decrease font size',       ['Ctrl--']]
   ['ZMR', 'Reset font size',          ['Ctrl-0']]
   ['DMR', 'Load demo file',           []]
-  ['DMN', 'Next line in demo',        ['Ctrl-Shift-N']]
-  ['DMP', 'Previous line in demo',    ['Ctrl-Shift-P']]
-  ['DMK', 'Toggle key display mode',  ['Ctrl-Shift-K']]
+  ['DMN', 'Next line in demo',        ['Shift-Ctrl-N']]
+  ['DMP', 'Previous line in demo',    ['Shift-Ctrl-P']]
+  ['DMK', 'Toggle key display mode',  ['Shift-Ctrl-K']]
 ]

 keyHTML = (k) -> "#{k}× "


Perform the averaging correctly.

Perform the averaging correctly.

--- bokehjs/test/models/transforms/jitter_transform.coffee
+++ bokehjs/test/models/transforms/jitter_transform.coffee
@@ -18,7 +18,8 @@
     transform.set('distribution', 'uniform')

     it "should average the fixed values", ->
-      vals =  Array.apply(null, Array(10)).map ->
+      N = 1000
+      vals =  Array.apply(null, Array(N)).map ->
                 5
       rets = transform.v_compute(vals)

@@ -26,7 +27,7 @@
         return a+b

       thesum = rets.reduce(add, 0)
-      thediff = (thesum/1000) - 5
+      thediff = (thesum/N) - 5
       expect(thediff).to.be.below 0.01

@@ -35,7 +36,8 @@
     transform.set('distribution', 'normal')

     it "should average the fixed values", ->
-      vals =  Array.apply(null, Array(10)).map ->
+      N = 1000
+      vals =  Array.apply(null, Array(N)).map ->
                 5
       rets = transform.v_compute(vals)

@@ -43,5 +45,5 @@
         return a+b

       thesum = rets.reduce(add, 0)
-      thediff = (thesum/1000) - 5
+      thediff = (thesum/N) - 5
       expect(thediff).to.be.below 0.01


:bug: forgot to add arg to foreach

:bug: forgot to add arg to foreach

--- test-integration/task-plan/cleanup.coffee
+++ test-integration/task-plan/cleanup.coffee
@@ -35,7 +35,7 @@
     )

     # Delete published but not opened plans
-    forEach('.plan.is-published:not(.is-open)',
+    forEach(@, '.plan.is-published:not(.is-open)',
       (plan, index, total) =>
         @addTimeout(10) # Published plans take a while to delete
         plan.click()


Remove underscores from some function names, re-order source code.

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -86,27 +86,33 @@
 #
 # mocked_method
 #
-_build_mocked_method = (method_name) ->
+build_mocked_method = (method_name) ->
   (args...) ->
-    expectation = _find_expectation(@, method_name, args...)
-    _throw_unknown_expectation("#{method_name}(#{args})") unless expectation?
-    _check_for_duplicate_expectations()
+    expectation = find_expectation(@, method_name, args...)
+    throw_unknown_expectation("#{method_name}(#{args})") unless expectation?
+    check_for_duplicate_expectations()
     expectation.called = true
     throw expectation._throws if expectation._throws?
     expectation._returns

-_find_expectation = (object, method_name, args...) ->
+find_expectation = (object, method_name, args...) ->
   for expectation in all_expectations when expectation.matches(object, method_name, args...)
     return expectation
   undefined

-_check_for_duplicate_expectations = ->
+check_for_duplicate_expectations = ->
   # TODO: use each with index and slice to avoid last element
   return if all_expectations.length < 2
   for outer in [0..all_expectations.length-2]
     for inner in [outer+1..all_expectations.length-1]
       if all_expectations[outer].equals( all_expectations[inner] )
-        _throw_duplicate_expectation("#{all_expectations[outer].method_name}(#{all_expectations[outer]._args})")
+        throw_duplicate_expectation("#{all_expectations[outer].method_name}(#{all_expectations[outer]._args})")
+
+throw_unknown_expectation = (expectation) ->
+  throw "#{expectation} does not match any expectations"
+
+throw_duplicate_expectation = (expectation) ->
+  throw "#{expectation} is a duplicate expectation"

 #
@@ -201,10 +207,10 @@
 _install_mock_method = (expectation, object, method_name) ->
   if typeof object == 'function'
     expectation._original_method = object.prototype[ method_name ]
-    object.prototype[ method_name ] = _build_mocked_method(method_name)
+    object.prototype[ method_name ] = build_mocked_method(method_name)
   else
     expectation._original_method = object[ method_name ]
-    object[ method_name ] = _build_mocked_method(method_name)
+    object[ method_name ] = build_mocked_method(method_name)

 _throw_args_usage = ->
   throw "you need to supply at least one argument to args(), e.g. my_mock.expects('my_method').args(42)"
@@ -226,9 +232,3 @@

 _throw_returns_and_throws_both_called = ->
   throw new Error("you called returns() and throws() on the same expectation; use one or the other but not both")
-
-_throw_duplicate_expectation = (expectation) ->
-  throw "#{expectation} is a duplicate expectation"
-
-_throw_unknown_expectation = (expectation) ->
-  throw "#{expectation} does not match any expectations"


Environments: Changed ugly notification of domain deletion and made the message optional.

--- client/Environments/views/domains/domaindeletionmodal.coffee
+++ client/Environments/views/domains/domaindeletionmodal.coffee
@@ -7,6 +7,7 @@
     options.overlayClick  ?= no
     options.cssClass       = KD.utils.curry 'env-deletion-modal', options.cssClass
     options.content      or= "
This will remove the domain #{data.domain} permanently, there is no way back!
"
+    options.deleteMesage or= "#{domain.domain} has been removed."
     options.buttons      or=
       "Remove"   :
         cssClass : "modal-clean-red"
@@ -14,9 +15,13 @@
           domain = @getData()
           domain.remove (err)=>
             return KD.showError err  if err
-            new KDNotificationView {title:"#{data.domain} has been removed."}
             @emit "domainRemoved"
             @destroy()
+            new KDNotificationView
+              title    : options.deleteMesage
+              type     : "mini"
+              cssClass : "success"
+              duration : 5000

       "Keep it"  :
         cssClass : "modal-clean-green"


minor: codestyling [ci skip]

minor: codestyling [ci skip]

--- client/account/lib/views/referral/referralcustomviews.coffee
+++ client/account/lib/views/referral/referralcustomviews.coffee
@@ -85,13 +85,13 @@
         text_invite   : 'Your personal invite link:'
         view          :
           partial     : getReferralUrl nick()
-          cssClass    : "text link"
+          cssClass    : 'text link'
           click       : ->
             referralUrl = @getElement()
             @utils.selectText referralUrl
             @setTooltip
               title     : "#{superKey} + C to copy"
-              placement : "above"
+              placement : 'above'
               sticky    : yes
             @tooltip.show()
             @tooltip.once 'ReceivedClickElsewhere', @tooltip.bound 'destroy'


Remove MemberFollowToggleButton from Members.AppView

--- client/app/Applications/Members.kdapplication/AppView.coffee
+++ client/app/Applications/Members.kdapplication/AppView.coffee
@@ -161,66 +161,3 @@
         KD.getSingleton('contentDisplayController').emit "ContentDisplayWantsToBeHidden", @

     @listenWindowResize()
-
-
-class MemberFollowToggleButton extends KDToggleButton
-
-  constructor:(options = {}, data)->
-    options = $.extend
-      title           : "Follow"
-      dataPath        : "followee"
-      defaultState    : "Follow"
-      loader          :
-        color         : "#333333"
-        diameter      : 18
-      states          : [
-        title         : "Follow"
-        callback      : (callback)->
-          KD.requireMembership
-            callback  : =>
-              @getData().follow (err, response)=>
-                @hideLoader()
-                unless err
-                  @getData().followee = yes
-                  @setClass 'following-btn'
-                  KD.track "Members", "Follow", @getData().profile.nickname
-                  callback? null
-            tryAgain  : yes
-            onFailMsg : 'Login required to follow members'
-            onFail    : => @hideLoader()
-      ,
-        title         : "Unfollow"
-        callback      : (callback)->
-          @getData().unfollow (err, response)=>
-            @hideLoader()
-            unless err
-              @getData().followee = no
-              @unsetClass 'following-btn'
-              KD.track "Members", "Unfollow", @getData().profile.nickname
-              callback? null
-      ]
-    , options
-
-    super options, data
-
-    decorator = (following)=>
-      if @getData().followee
-        @setClass 'following-btn'
-        @setState "Unfollow"
-      else
-        @setState "Follow"
-        @unsetClass 'following-btn'
-
-    if @getData().followee?
-      decorator @getData().followee
-    else
-      KD.whoami().isFollowing? @getData().getId(), "JAccount", \
-        (err, following) =>
-          @getData().followee = following
-          warn err  if err and KD.isLoggedIn()
-          decorator following
-
-  decorateState:(name)->
-
-    @setClass 'following-btn' if name is 'Unfollow'
-    super


Trailing whitespace

Trailing whitespace

--- test/components/common/notifications.cjsx
+++ test/components/common/notifications.cjsx
@@ -43,9 +43,3 @@
       expect(rows.length).to.eq 0
       done()
     )
-
-
-
-
-
-


work on group readmes/splash pages

work on group readmes/splash pages

--- core/KDObject.coffee
+++ core/KDObject.coffee
@@ -1,6 +1,6 @@
 class KDObject extends KDEventEmitter

-  READY = 1
+  [NOTREADY, READY] = [0,1]

   utils: __utils

@@ -30,7 +30,7 @@
     target.on event, @lazyBound 'emit', prefix + event

   ready:(listener)->
-    if @readyState > 0 then listener()
+    if @readyState is READY then listener()
     else @once 'ready', listener

   inheritanceChain:(options)->


sidebar: fix route handling of section headers

--- client/Social/Activity/sidebar/activitysideview.coffee
+++ client/Social/Activity/sidebar/activitysideview.coffee
@@ -38,7 +38,12 @@
       partial  : @getOption 'title'
       click    : (event) =>
         KD.utils.stopDOMEvent event
-        KD.singletons.router.handleRoute searchLink
+
+        route = if 'add-icon' in event.target.classList
+        then event.target.getAttribute 'href'
+        else searchLink
+
+        KD.singletons.router.handleRoute route

     @listController.on 'ListIsEmptied', @lazyBound 'setClass', 'empty'


StacksV2: providerParser: update regex based on the review

--- client/admin/lib/views/stacksv2/providersparser.coffee
+++ client/admin/lib/views/stacksv2/providersparser.coffee
@@ -1,11 +1,12 @@
 module.exports = providersParser = (content) ->

+    regex     = /\$\{var\.(.*?)\_/g
     providers = {}
+    match     = regex.exec content

-    # Find all matches for ${var.PROVIDER_}
-    (content.match /\$\{var\.(.*?)\_/g).forEach (match) ->
-      # Trim prefix/suffix and remove duplicates
-      providers[match[6...-1]] = null
+    while match
+      providers[match[1]] = null
+      match = regex.exec content

     # Return list of providers
     return Object.keys providers


better

better

--- client/apps/edit/components/section_video/index.coffee
+++ client/apps/edit/components/section_video/index.coffee
@@ -15,7 +15,6 @@
     progress: null, coverSrc: @props.section.get('cover_image_url')

   componentDidMount: ->
-    @props.section.on 'change:url', => @forceUpdate()
     $(@refs.input.getDOMNode()).focus() unless @props.section.get('url')

   onClickOff: ->
@@ -25,8 +24,8 @@
       @props.section.set cover_image_url: @state.coverSrc

   onChangeUrl: (e) ->
-    console.log 'chnage'
     @props.section.set url: $(@refs.input.getDOMNode()).val()
+    @forceUpdate()

   uploadCoverImage: (e) ->
     gemup e.target.files[0],


Fix fetchCompiledApp and runApp in kodingAppsController for 3rd party Apps

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -6,7 +6,6 @@

   @manifests = {}

-
   @getManifestFromPath = getManifestFromPath = (path)->

     folderName = (p for p in path.split("/") when /\.kdapp/.test p)[0]
@@ -31,7 +30,7 @@

     {profile} = KD.whoami()
     path = if 'string' is typeof manifest then manifest else manifest.path
-    path = if /^~/.test path then "/Users/#{profile.nickname}#{path.substr(1)}" else path
+    path = if /^~/.test path then "/home/#{profile.nickname}#{path.substr(1)}" else path
     return path.replace /(\/+)$/, ""

   # #
@@ -50,8 +49,7 @@
         if err
           @fetchAppsFromFs (err, apps)=>
             if err then callback()
-            else
-              callback null, apps
+            else callback null, apps
         else
           callback? err, apps

@@ -79,7 +77,7 @@
               # to not to break the result of the stack
               cb null, response

-        {manifests} = @constructor
+        manifests = @constructor.manifests
         async.parallel stack, (err, result)=>
           result.forEach (rawManifest)->
             if rawManifest
@@ -117,11 +115,8 @@

   fetchCompiledApp:(manifest, callback)->

-    {name} = manifest
-    appPath = @getAppPath manifest
-    indexJsPath = "#{appPath}/index.js"
-    @kiteController.run "cat #{escapeFilePath indexJsPath}", (err, response)=>
-      callback err, response
+    indexJs = FSHelper.createFileFromPath "#{@getAppPath manifest}/index.js"
+    indexJs.fetchContents (err, response)=> callback null, response

   # #
   # MISC
@@ -191,7 +186,6 @@
       if err then warn err
       else
         if options and options.type is "tab"
-
           KD.getSingleton("appManager").open manifest.name,
             requestedFromAppsController : yes
           , (appInstance)->
@@ -218,7 +212,6 @@
           callback?()
           return null

-        log "App to run:", name
         callback?()

   publishApp:(path, callback)->
@@ -554,39 +547,6 @@
           callback? err
         else
           callback? null
-
-
-  # cloneApp:(path, callback)->
-
-  #   @fetchApps (err, manifests = {})=>
-  #     if err
-  #       warn err
-  #       new KDNotificationView type : "mini", title : "There was an error, please try again later!"
-  #       callback? err
-  #     else
-  #       manifest = getManifestFromPath path
-
-  #       {repo} = manifest
-
-  #       if /^git/.test repo      then repoType = "git"
-  #       else if /^svn/.test repo then repoType = "svn"
-  #       else if /^hg/.test repo  then repoType = "hg"
-  #       else
-  #         err = "Unsupported repository specified, quitting!"
-  #         new KDNotificationView type : "mini", title : err
-  #         callback? err
-  #         return no
-
-  #       appPath = "/home/#{KD.whoami().profile.nickname}/Applications/#{manifest.name}.kdapp"
-  #       appBackupPath = "#{appPath}.old#{@utils.getRandomNumber 9999}"
-
-  #       @kiteController.run "mv #{escapeFilePath appPath} #{escapeFilePath appBackupPath}" , (err, response)->
-  #         if err then warn err
-  #         @kiteController.run "#{forkRepoCommandMap()[repoType]} #{repo} #{escapeFilePath getAppPath manifest}", (err, response)->
-  #           if err then warn err
-  #           else
-  #             log response, "App cloned!"
-  #           callback? err, response

   # #
   # HELPERS


ListView

ListView

- Trigger "content-resize" unconditionally for non maximized
  Layouts after each Layout run. This avoids timing & sizing problem
  we have seen with opened Layers which contained ListViews.


Gauge charts now support retina displays.

--- coffee/time/gauge.coffee
+++ coffee/time/gauge.coffee
@@ -35,7 +35,7 @@
       'z-index': '1'

     @svg.append('g')
-      .attr('transform', "translate(#{@centerX()}, #{@textY()})")
+      .attr('transform', "translate(#{@textX()}, #{@textY()})")
       .append('text')
         .attr('class', 'value')
         .text(@options.format(@value))
@@ -73,13 +73,16 @@
     @update value

   # @return [Number] The radius for the gauge.
-  radius: -> @height / 1.58
+  radius: -> @getHeight() / 1.58

   # @return [Number] The center position x-coordinate for the gauge.
-  centerX: -> @width / 2
+  centerX: -> @getWidth() / 2

   # @return [Number] The center position y-coordinate for the gauge.
-  centerY: -> 0.68 * @height
+  centerY: -> 0.68 * @getHeight()
+
+  # @return [Number] The x-coordinate for the gauge text display.
+  textX: -> @width / 2

   # @return [Number] The y-coordinate for the gauge text display.
   textY: -> 0.48 * @height
@@ -103,7 +106,7 @@
     [cx, cy, r] = [@centerX(), @centerY(), @radius()]
     [tickOffset, tickSize] = [@options.tickOffset, @options.tickSize]

-    @ctx.clearRect(0, 0, @width, @height)
+    @clear()

     # Draw Ticks
     t = d3.scale.linear()
@@ -151,15 +154,15 @@
     @ctx.translate cx, cy
     @ctx.rotate @getAngle(@value)

-    @ctx.moveTo 4, 0
-    @ctx.lineTo -4, 0
-    @ctx.lineTo -1, 19-r
+    @ctx.moveTo 4 * @pixelRatio, 0
+    @ctx.lineTo -4 * @pixelRatio, 0
+    @ctx.lineTo -1 * @pixelRatio, 19-r
     @ctx.lineTo 1, 19-r
     @ctx.fill()

     @setStyles '.epoch .gauge .needle-base'
     @ctx.beginPath()
-    @ctx.arc 0, 0, (@width / 25), 0, 2*Math.PI
+    @ctx.arc 0, 0, (@getWidth() / 25), 0, 2*Math.PI
     @ctx.fill()

     @ctx.restore()


refactored statds api class

refactored statds api class

--- src/api/statsd.coffee
+++ src/api/statsd.coffee
@@ -81,14 +81,14 @@
       path = render_url + ".statuses." + status_array[i] + ".count,'1week'))&format=json";
       results[status_array[i]] = yield exports.fetchData(path);
       console.log(JSON.stringify(results))
+      var failed = 'data' in results.Failed ? results.Failed.data[0][0] : 0,
+        processing = 'data'  in results.Processing ? results.Processing.data[0][0]  : 0,
+        completed = 'data' in results.Completed ? results.Completed.data[0][0]  : 0,
+        successful = 'data' in  results.Successful ? results.Successful.data[0][0] : 0,
+        completedWErrors = 'data' in results['Completed with error(s)'] ? results['Completed with error(s)'].data[0][0] : 0;
     }
-    ;

-    var failed = 'data' in results.Failed ? results.Failed.data[0][0] : 0,
-      processing = 'data'  in results.Processing ? results.Processing.data[0][0]  : 0,
-      completed = 'data' in results.Completed ? results.Completed.data[0][0]  : 0,
-      successful = 'data' in  results.Successful ? results.Successful.data[0][0] : 0,
-      completedWErrors = 'data' in results['Completed with error(s)'] ? results['Completed with error(s)'].data[0][0] : 0;
+

     data.push({


mongo-imports limit removed

mongo-imports limit removed

--- data/mongo-imports2.coffee
+++ data/mongo-imports2.coffee
@@ -30,7 +30,7 @@
       #pgclient.on('drain', pgclient.end.bind(pgclient))
       pgclient.connect()

-      limit = 10
+      #limit = 10

       cntQuery = pgclient.query(
         if limit?


Docs-App Ui-Router updates + tidy

Docs-App Ui-Router updates + tidy

--- docs/app/src/application.coffee
+++ docs/app/src/application.coffee
@@ -1,8 +1,7 @@
-EventricDocs = angular.module("EventricDocs", [
-
-  'EventricDocs.Controller.DocsPageCtrl'
+
+EventricDocs = angular.module('EventricDocs', [
+  require 'eventric/docs/app/src/api/api.ng'
   'EventricDocs.Controller.ApplicationCtrl'
-
   'pagesData'
   'ui.router'
 ])
@@ -10,20 +9,23 @@

 .config ($stateProvider, $urlRouterProvider) ->
   # Now set up the states
-  $stateProvider.state "docs",
-    url: "/docs/:moduleName/:functionName"
-    templateUrl: 'src/docs_page/docs_page.ng.html'
-    controller: 'DocsPageCtrl'
+  $stateProvider.state 'home',
+    url: '/home'
+    templateUrl: 'src/home/home.ng.html'
+    controller: 'HomeCtrl'

-  $urlRouterProvider.otherwise("/")
+  $stateProvider.state 'api',
+    url: '/api/:moduleName/:functionName'
+    templateUrl: 'src/api/api.ng.html'
+    controller: 'ApiCtrl'
+
   return

+angular.module('EventricDocs.Controller.ApplicationCtrl', [])

-angular.module("EventricDocs.Controller.ApplicationCtrl", [])
-
-.controller "ApplicationCtrl", [
-  "$scope", "DOC_PAGES"
-  ($scope, DOC_PAGES) ->
-    $scope.DOC_PAGES = DOC_PAGES
+.controller 'ApplicationCtrl', [
+  '$scope', 'API_OVERVIEW'
+  ($scope, API_OVERVIEW) ->
+    $scope.API_OVERVIEW = API_OVERVIEW
 ]


Minor fix

Minor fix

--- app/controllers/classify.coffee
+++ app/controllers/classify.coffee
@@ -7,7 +7,7 @@
     '.tree .question': 'question'

   events:
-    'click .tree .answer': 'answer'
+    'click .tree .answer a': 'answer'

   constructor: ->
     super


Fix sessionStorage initialization code

Fix sessionStorage initialization code

With previous version of this code sessionStorage was never initialized
properly. This breaks travis become.

--- app/initializers/storage.coffee
+++ app/initializers/storage.coffee
@@ -19,8 +19,8 @@
   try
     # firefox will not throw error on access for sessionStorage var,
     # you need to actually get something from session
-    sessionStorage.getItem('foo')
-    storage = sessionStorage
+    window.sessionStorage.getItem('foo')
+    storage = window.sessionStorage
   catch err
     storage = Storage.create()


Add placeholder subject dimensions

Add placeholder subject dimensions

--- app/pages/project/classify.cjsx
+++ app/pages/project/classify.cjsx
@@ -121,6 +121,7 @@
           user_agent: navigator.userAgent
           user_language: counterpart.getLocale()
           utc_offset: ((new Date).getTimezoneOffset() * 60).toString() # In seconds
+          subject_dimensions: (null for location in subject.locations)
         links:
           project: project.id
           workflow: workflow.id


Disable preemptive registration checks, display raw error

--- app/partials/register-form.cjsx
+++ app/partials/register-form.cjsx
@@ -46,6 +46,7 @@
     passwordTooShort: null
     passwordsDontMatch: null
     emailConflict: null
+    error: null

   componentDidMount: ->
     auth.listen @handleAuthChange
@@ -169,6 +170,8 @@
             <Translate content="registerForm.alreadySignedIn" name={@state.user.display_name} />{' '}
             <button type="button" className="minor-button" onClick={@handleSignOut}><Translate content="registerForm.signOut" /></button>

+        else if @state.error?
+          {@state.error.toString()}
         else
            }

@@ -190,9 +193,9 @@
       badNameChars: badChars
       nameConflict: null

-    if exists and badChars.length is 0
-      @debouncedCheckFornameConflict ?= debounce @checkFornameConflict, REMOTE_CHECK_DELAY
-      @debouncedCheckFornameConflict name
+    # if exists and badChars.length is 0
+    #   @debouncedCheckFornameConflict ?= debounce @checkFornameConflict, REMOTE_CHECK_DELAY
+    #   @debouncedCheckFornameConflict name

   debouncedCheckFornameConflict: null
   checkFornameConflict: (display_name) ->
@@ -216,9 +219,9 @@
     @promiseToSetState emailConflict: Promise.resolve null # Cancel any existing request.

     email = @refs.email.getDOMNode().value
-    if email.match /.+@.+\..+/
-      @debouncedCheckForEmailConflicts ?= debounce @checkForEmailConflicts, REMOTE_CHECK_DELAY
-      @debouncedCheckForEmailConflicts email
+    # if email.match /.+@.+\..+/
+    #   @debouncedCheckForEmailConflicts ?= debounce @checkForEmailConflicts, REMOTE_CHECK_DELAY
+    #   @debouncedCheckForEmailConflicts email

   debouncedCheckForEmailConflicts: null
   checkForEmailConflicts: (email) ->
@@ -251,10 +254,14 @@
     credited_name = @refs.realName.getDOMNode().value
     global_email_communication = @refs.okayToEmail.getDOMNode().checked

+    @setState error: null
     @props.onSubmit?()
     auth.register {display_name, password, email, credited_name, global_email_communication}
-      .then @props.onSuccess
-      .catch @props.onFailure
+      .then =>
+        @props.onSuccess? arguments...
+      .catch (error) =>
+        @setState {error}
+        @props.onFailure? arguments...

   handleSignOut: ->
     auth.signOut()


Rename default ogres-die goal

Rename default ogres-die goal

--- app/schemas/models/level.coffee
+++ app/schemas/models/level.coffee
@@ -267,7 +267,7 @@
     victory: {}
     type: 'hero'
     goals: [
-      {id: 'ogres-die', name: 'Ogres must die.', killThangs: ['ogres'], worldEndsAfter: 3}
+      {id: 'ogres-die', name: 'Defeat the ogres.', killThangs: ['ogres'], worldEndsAfter: 3}
       {id: 'humans-survive', name: 'Your hero must survive.', saveThangs: ['Hero Placeholder'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true}
     ]
     concepts: ['basic_syntax']


Log messages to the console as well

Log messages to the console as well

--- server/common/log.coffee
+++ server/common/log.coffee
@@ -37,6 +37,7 @@
     console.log 'loggly not available'

 log = (msg) ->
+  console.log msg
   if logglyClient?
     if (typeof msg) is 'string'
       logglyClient.log(prefix + msg, tags)


Actually call the onNoMoreSubjects method

--- src/classifier.coffee
+++ src/classifier.coffee
@@ -39,7 +39,7 @@
       @onSubjectSelect subject

     @listenTo @Subject, 'no-more', (e) =>
-      @onNoMoreSubjects
+      @onNoMoreSubjects()

     if IS_DEV
       window.classifier = this


fixes #66 Sends hull.currentUser everytime

--- src/client/api.coffee
+++ src/client/api.coffee
@@ -9,6 +9,7 @@

     rpc = false
     rawFetch = null
+    emitUserEvent = null
     module =
       require:
         paths:
@@ -91,6 +92,8 @@
         #
         #

+        emitUserEvent = ->
+          app.core.mediator.emit('hull.currentUser', app.core.currentUser)

         app.core.setCurrentUser = setCurrentUser = (headers={})->
           return unless app.config.appId
@@ -104,11 +107,11 @@
                 id:   headers['Hull-User-Id'],
                 sig:  headers['Hull-User-Sig']
               }
-              app.core.mediator.emit('hull.currentUser', app.core.currentUser)
+              emitUserEvent()
           else
             $.removeCookie(cookieName, path: "/")
             app.core.currentUser = false
-            app.core.mediator.emit('hull.currentUser', app.core.currentUser) if currentUserId
+            emitUserEvent() if currentUserId

           app.sandbox.config ?= {}
           app.sandbox.config.curentUser = app.core.currentUser
@@ -277,10 +280,7 @@
           data = remoteConfig.data

           if data.headers && data.headers['Hull-User-Id']
-            app.core.currentUser = {
-              id:   data.headers['Hull-User-Id'],
-              sig:  data.headers['Hull-User-Sig']
-            }
+            app.core.setCurrentUser data.headers

           window.clearTimeout(timeout)
           app.config.assetsUrl            = remoteConfig.assetsUrl
@@ -320,7 +320,7 @@
         base.app    = rawFetch('app', true);
         base.org    = rawFetch('org', true);

-        app.core.mediator.emit  'hull.currentUser', app.core.currentUser
+        emitUserEvent()
         app.core.mediator.on    'hull.currentUser', clearModelsCache

     module


save crashreport function

save crashreport function

--- server/import-api.coffee
+++ server/import-api.coffee
@@ -13,10 +13,22 @@
 OPTIONAL_FIELDS  = ["profile","imei","mac"]
 MANDATORY_FILES  = ["core","rich-core","stack-trace"]

+# module globals
+DB_CRASHREPORTS  = "" #crashreport collection in db, set by init function
+
 parse_files = (files, cb) ->
     stackparser.parse_stack_trace_file files["stack-trace"].path, (err, trace_arr) ->
         return cb err if err?
         cb null, trace_arr #debug (early callback, only stack trace read)
+
+
+save_crashreport = (crashreport, cb) ->
+    q = DB_CRASHREPORTS.find({'id':crashreport.id}).upsert().update(crashreport)
+    q.run (err) ->
+        if err?
+            cb err
+        else
+            cb null

 validate_crashreport_fields = (fields) ->
     err = []
@@ -131,7 +143,7 @@
                 console.log err

 init_import_api = (settings, app, db) ->
-    crashreports = db.collection('crashreports')
+    DB_CRASHREPORTS = db.collection('crashreports')
     app.post "/api/import", (req, res) ->

         # verify content type (multipart)
@@ -175,13 +187,11 @@
                     #console.log "Crashreport: " + util.inspect(crashreport) #debug

                     # store to mongodb
-                    q = crashreports.find({'id':crashreport.id}).upsert().update(crashreport)
-                    q.run (err) ->
+                    save_crashreport crashreport, (err) ->
                         if err?
-                            res.send {"ok":"0","errors":err}
-                            # TODO: cleanup?
-                            return
+                            return res.send {"ok":"0","errors":err} #TODO: cleanup?
                         res.send {"ok":"1","url":"#{settings.server.url}/crashreports/" + crashreport.id}
+

 exports.init_import_api = init_import_api


Fix up the non-sandbox case.

Fix up the non-sandbox case.

--- app/templates/_Gruntfile.coffee
+++ app/templates/_Gruntfile.coffee
@@ -68,13 +68,14 @@
         options:
           open: 'http://<%%= connect.options.hostname %>:<%%= connect.options.port %>'
           base: ['<%%= project.app.static %>']
-      sandbox:
+      <% if (sandbox) { %>sandbox:
         options:
           open: 'http://<%%= connect.options.hostname %>:<%%= connect.options.port %>/sandbox.html'
           base: [
             '<%%= project.app.static %>'
             '../'
           ]
+      <% } %>
       test:
         options:
           port: 9001
@@ -156,9 +157,9 @@
     <% } %>

     # Automatically inject Bower components into an HTML file
-    'bower-install':
+    <% if (sandbox) { %>'bower-install':
       sandbox:
-        html: '<%%= project.dev.static %>/sandbox.html'
+        html: '<%%= project.dev.static %>/sandbox.html'<% } %>

     # Renames files for browser caching purposes
     rev:
@@ -233,11 +234,7 @@
   grunt.registerTask 'develop',
   'everything you need to start writing code', [
     'build:development'
-    <% if (sandbox) { %>
-    'connect:sandbox'
-    <% } else { %>
-    'connect:livereload'
-    <% } %>
+    <% if (sandbox) { %>'connect:sandbox'<% } else { %>'connect:livereload'<% } %>
     'watch'
   ]

@@ -254,7 +251,7 @@
   grunt.registerTask 'build:development',
   'build static files for a development environment', [
     'clean:static'
-    'bower-install'
+    <% if (sandbox) { %>'bower-install'<% } %>
     'sass:development'
     'copy:css'
     'copy:html'


changed some coffeelint rules

changed some coffeelint rules

--- app/templates/_gruntfile.coffee
+++ app/templates/_gruntfile.coffee
@@ -101,7 +101,13 @@
       options:
         max_line_length:
           level: 'ignore'
-
+        missing_fat_arrows:
+          level: 'warn'
+        line_endings:
+          level: 'error'
+        space_operators:
+          level: 'error'
+

     jshint:
       options:


Update menu.coffee

Update menu.coffee

--- ui/tests/models/menu.coffee
+++ ui/tests/models/menu.coffee
@@ -1,7 +1,11 @@
 MenuModel = require('../../scripts/models/menu')

-describe('MenuModel', ->
-    it.skip('should describe something', ->
-
-    )
+describe.skip('MenuModel', ->
+    it('should generate a list of menu items', ->)
+    it('should have the right menu items on logged out', ->)
+    it('should have the right menu items when logged in', ->)
+    it('should update the menu items when the state changes', ->)
+    it('should allow setting a custom icon', ->)
+    it('should capitalize the page title', ->)
+    it('should use underscores for URLs', ->)
 )


Excluding compute nodes with no usable images.

--- dit4c-highcommand/app/assets/javascripts/routes.coffee
+++ dit4c-highcommand/app/assets/javascripts/routes.coffee
@@ -55,7 +55,8 @@
                       node.images = response.data.filter (image) ->
                         # Only include downloaded images
                         image.metadata
-                      insertInOrder(node)
+                      if (node.images.length > 0)
+                        insertInOrder(node)
               usableNodes
     )


Fix JSON stringify for selectors

Fix JSON stringify for selectors

--- updaters/lib/updater.coffee
+++ updaters/lib/updater.coffee
@@ -61,7 +61,7 @@

       processed = []
       for key, value of obj
-        key = "\"#{key}\"" if key.match(/'|-|@/)
+        key = "\"#{key}\"" if key.match(/'|-|@|:/)
         value = @stringify(value, local)
         value = ' ' + value unless value[0] == "\n"
         processed.push(key + ':' + value)


allow automatic searching for projects through "search" url param

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -13,8 +13,11 @@
     enableVersionSorting()
     enableSelect2()
     enableProjectSearch()
+    autoProjectSearch()
     fixSelect2InDocCollectionModal()
     submitNewProjectFormViaAjax()
+
+$searchInput = $('form#project-search input#search')

 showCookieBar = ->
   unless $.cookie('cookie-consent')?
@@ -77,7 +80,7 @@

 enableProjectSearch = ->
   $projects = $('.projects .project')
-  $('form#project-search input#search')
+  $searchInput
     .keyup ->
       query = $(@).val()
       found = false
@@ -95,7 +98,17 @@
         found = true
         $projects.slideDown()
       $('.projects .not-found').toggle !found
-    .trigger 'keyup'
+    .trigger('keyup')
+
+autoProjectSearch = ->
+  queryMatch = window.location.search.match(/search=([^&]+)/)
+  if queryMatch?
+    $searchInput
+      .val(queryMatch[1])
+      .trigger('keyup')
+    $('html, body').animate
+      scrollTop: $searchInput.offset().top - 20
+    , 'slow'

 fixSelect2InDocCollectionModal = ->
   $('#combined-docs').on 'shown.bs.modal', (e) ->


添加默认selector,修复JS在未有对应selector页面，也会执行而导致的报错

--- app/assets/javascripts/bucketerize.js.coffee
+++ app/assets/javascripts/bucketerize.js.coffee
@@ -242,6 +242,7 @@
     bucket_type: "Bucket"
     resource_type: "Shot"
     mode: 'custom'
+    selector: '[data-rel=bucketerize]'

   _init: ->
     for key, val of @_default_configs
@@ -251,33 +252,36 @@
     @buckets_path = @path_fix + "/buckets"
     @bucketings_path = @path_fix + "/bucketings"

+    console.log @configs["selector"]
+    console.log @$el
     @$el = jQuery(@configs["selector"])
-
-    @resource_type = @configs['resource_type']
-    @bucket_type = @configs["bucket_type"]
-
-    if @configs['mode'] == 'modal'
-      @configs['hook_class'] = ModalBucketerizeHook if !@configs['hook_class']
-    else if @configs['mode'] == 'standard'
-      @configs['hook_class'] = StandardBucketerizeHook if !@configs['hook_class']
-    else
-      @configs['hook_class'] = BucketerizeHook
-
-    @hook = new @configs["hook_class"](@)
+    if @$el.length > 0
+      @resource_type = @configs['resource_type']
+      @bucket_type = @configs["bucket_type"]
+
+      if @configs['mode'] == 'modal'
+        @configs['hook_class'] = ModalBucketerizeHook if !@configs['hook_class']
+      else if @configs['mode'] == 'standard'
+        @configs['hook_class'] = StandardBucketerizeHook if !@configs['hook_class']
+      else
+        @configs['hook_class'] = BucketerizeHook
+
+      @hook = new @configs["hook_class"](@)

   get_all_buckets: () ->
-    console.log @
-    resource_ids = @hook.assigned_resource_ids()
-    if !@bucket_type or !@resource_type
-      return @hook.error {error: "params blank"}
+    if @$el.length > 0
+      resource_ids = @hook.assigned_resource_ids()
+      if !@bucket_type or !@resource_type
+        return @hook.error {error: "params blank"}

     @_get_buckets()

   get_resources_buckets: () ->
-    if !@bucket_type or !@resource_type
-      return @hook.error {error: "params blank"}
-
-    @_get_buckets(@hook.assigned_resource_ids())
+    if @$el.length > 0
+      if !@bucket_type or !@resource_type
+        return @hook.error {error: "params blank"}
+
+      @_get_buckets(@hook.assigned_resource_ids())

   _get_buckets: (resource_ids) ->
     console.log '_get_buckets'
@@ -303,20 +307,24 @@

   create_bucket: (bucket_name, bucket_desc) ->
     console.log 'create bucket'
-    @_create_bucket(@bucket_type, bucket_name, bucket_desc)
+    if @$el.length > 0
+      @_create_bucket(@bucket_type, bucket_name, bucket_desc)

   add_to: () ->
     console.log 'add to bucket'
-    console.log @hook.assigned_bucket_ids()
-    @_add_to_bucket(@resource_type, @hook.assigned_resource_ids(), @bucket_type, @hook.assigned_bucket_ids())
+    if @$el.length > 0
+      console.log @hook.assigned_bucket_ids()
+      @_add_to_bucket(@resource_type, @hook.assigned_resource_ids(), @bucket_type, @hook.assigned_bucket_ids())

   remove_from: () ->
     console.log 'remove from bucket'
-    @_remove_from_bucket(@resource_type, @hook.assigned_resource_ids(), @bucket_type, @hook.assigned_bucket_ids())
+    if @$el.length > 0
+      @_remove_from_bucket(@resource_type, @hook.assigned_resource_ids(), @bucket_type, @hook.assigned_bucket_ids())

   replace_buckets: () ->
     console.log 'replace_buckets'
-    @_replace_buckets(@resource_type, @hook.assigned_resource_ids(), @bucket_type, @hook.assigned_bucket_ids())
+    if @$el.length > 0
+      @_replace_buckets(@resource_type, @hook.assigned_resource_ids(), @bucket_type, @hook.assigned_bucket_ids())

   _create_bucket: (bucket_type, name, desc) ->
     jQuery.ajax


Fix water crash of helicopter

Fix water crash of helicopter

--- app/scripts/lib/script_modules/entity.coffee
+++ app/scripts/lib/script_modules/entity.coffee
@@ -300,7 +300,7 @@
         .attr(
           w: @entity.w + 10
           x: @entity.x - 5
-          y: @_getSeaLevel() - 10
+          y: @_getSeaLevel() - 10 - Crafty.viewport.y
           h: 20
           z: @entity.z - 1
         )
@@ -308,7 +308,7 @@
     if Game.explosionMode?
       @_waterSplash()
     @entity.addComponent('WaterSplashes')
-    @entity.setSealevel(@_getSeaLevel())
+    @entity.setSealevel(@_getSeaLevel() - Crafty.viewport.y)

     if @entity.has('ViewportFixed')
       waterSpot.addComponent('ViewportFixed')


improved react component keying,

improved react component keying,

--- react-and-coffeescript/client/code/app/entry.coffee
+++ react-and-coffeescript/client/code/app/entry.coffee
@@ -25,7 +25,7 @@
 monterrey = rr
     render: ->
         div
-            key: Math.random()
+            key: 'monterrey'
             className: 'monterrey',
             "ha"
             colorado
@@ -40,24 +40,26 @@
     componentDidMount: ->
         window.addEventListener 'con---sole', (a) =>
             cc 'we heard', a.detail
-            #@state.logs.push a.detail[1]
-            #@setState {logs: @state.logs.push(a.detail[1])}
+            # line_text = a.detail.reduce (acc, i) ->
+            #     acc + i.toString()
+            # , ''
+            line_text = [a.detail]
             @setState
-                logs: [@state.logs..., a.detail[1]]
+                logs: [@state.logs..., line_text]

     render: ->
         div
-            key: Math.random(),
-            for item in @state.logs
+            key: 'structured_console',
+            for item, idx in @state.logs
                 span
-                    key: Math.random(),
+                    key: 'sc_line' + idx,
                     item

 remote_control = rr

     render: ->
         div
-            key: Math.random(),
+            key: 'remote_control',
             "something here soon to control "
             input
                 placeholder: 'radio'
@@ -70,12 +72,13 @@
     __handle_change: (e) ->
         c e.currentTarget.value
         t.rpc 'gate.ping', (res)->
+            c res

     render: ->
         div
-            key: Math.random()
+            key: 'colorado'
             style:
                 'position': 'absolute'
                 'top': @state.position.y + 'px'


Post message and image separate

Post message and image separate

This lets clients turn the image link into real images

--- src/codinglove.coffee
+++ src/codinglove.coffee
@@ -56,7 +56,8 @@

     txt = txt.replace(/[\n\r]/g, '')

-    response_handler "#{txt} #{img_src}"
+    response_handler "#{txt}"
+    response_handler "#{img_src}"

 send_meme = (message, location, response_handler)->
   url = location
@@ -72,7 +73,8 @@

     txt = get_meme_txt(body, ".post h3 a")

-    response_handler "#{txt} #{img_src}"
+    response_handler "#{txt}"
+    response_handler "#{img_src}"

 get_meme_image = (body, selector)->
   $ = cheerio.load(body)


allow fetching configuration via JSON url

--- src/coffee/app.coffee
+++ src/coffee/app.coffee
@@ -26,11 +26,13 @@
     @getOptions()
     @checkForValidLanguage()
     @getPodcastData()
+
+    return @
+
+  init: () ->
     @checkIntegrity()
     @addCss()
     @renderButtonIframe()
-
-    return @

   update: () ->
     @getPodcastData()
@@ -79,10 +81,25 @@
     if dataSource = @scriptElem.dataset.jsonData
       @extractPodcastDataFromJson(window[dataSource])

-  fetchPodcastDataFromUrl: () ->
+  fetchPodcastDataFromUrl: (url) ->
+    request = new XMLHttpRequest()
+    request.open('GET', url, true)
+
+    request.onload = () =>
+      if request.status >= 200 && request.status < 400
+        resp = JSON.parse(request.responseText)
+        @extractPodcastDataFromJson(resp)
+      else
+        console.debug('Error fetching configuration', request)
+
+    request.onerror = () =>
+      console.debug('Error fetching configuration', request)
+
+    request.send()

   extractPodcastDataFromJson: (data) ->
     @podcast = data
+    @init()

   checkIntegrity: () ->
     if @podcast && @podcast.feeds.length == 0


Updating variable and method names; and simplified limit param

--- twitch.coffee
+++ twitch.coffee
@@ -23,11 +23,15 @@
 # Todo:
 # - Save favourites?

+#
+# Config
+#
+TWITCH_MAX_RESULTS = 5
+TWITCH_STORAGE_KEY = process.env.HUBOT_TWITCH_KEY or "_twitch"
+
 module.exports = (robot) ->
-    maxResults = 5
-
-    twitchData = ->
-        robot.brain.data.twitch or= {}
+    GetTTVData = ->
+        robot.brain.data[TWITCH_STORAGE_KEY] or= {}

     robot.respond /ttv fav/i, (msg) ->
         return
@@ -39,14 +43,14 @@
         return

     robot.respond /ttv featured/i, (msg) ->
-        twitchRequest msg, '/streams/featured', limit: maxResults, (object) ->
+        GetTwitchResult msg, '/streams/featured', null, (object) ->
             for feature in object.featured
                 channel = feature.stream.channel
                 msg.send "#{feature.stream.game}: #{channel.display_name} (#{channel.status}) - #{channel.url} [Viewers: #{feature.stream.viewers}]"

     robot.respond /ttv game (.+)/i, (msg) ->
         category = msg.match[1]
-        twitchRequest msg, '/streams', { game: category, limit: maxResults }, (object) ->
+        GetTwitchResult msg, '/streams', { game: category }, (object) ->
             if object._total is 0
                 msg.reply "No live streams were found in \"#{category}\". Try a different category or try again later."
                 return
@@ -55,12 +59,12 @@
                 channel = stream.channel
                 msg.send "#{channel.display_name} (\"#{channel.status}\"): #{channel.url} [Viewers: #{stream.viewers}]"

-            if object._total > maxResults
-                msg.reply "There are #{object._total - maxResults} other \"#{category}\" live streams."
+            if object._total > TWITCH_MAX_RESULTS
+                msg.reply "There are #{object._total - TWITCH_MAX_RESULTS} other \"#{category}\" live streams."

     robot.respond /ttv search (.+)/i, (msg) ->
         query = msg.match[1]
-        twitchRequest msg, "/search/streams", { q: query, limit: maxResults }, (object) ->
+        GetTwitchResult msg, "/search/streams", { q: query }, (object) ->
             if object._total is 0
                 msg.reply "No live streams were found using search query: \"#{query}\". Try a different query or try again later."
                 return
@@ -69,11 +73,11 @@
                 channel = stream.channel
                 msg.send "#{channel.display_name} (\"#{channel.status}\"): #{channel.url} [Viewers: #{stream.viewers}]"

-            if object._total > maxResults
-                msg.reply "There are #{object._total - maxResults} other live streams matching your search query: \"#{query}\"."
+            if object._total > TWITCH_MAX_RESULTS
+                msg.reply "There are #{object._total - TWITCH_MAX_RESULTS} other live streams matching your search query: \"#{query}\"."

     robot.respond /ttv stream (.+)/i, (msg) ->
-        twitchRequest msg, "/streams/#{msg.match[1]}", null, (object) ->
+        GetTwitchResult msg, "/streams/#{msg.match[1]}", null, (object) ->
             if object.status is 404
                 msg.reply "The stream you have entered (\"#{msg.match[1]}\") does not exist."
                 return
@@ -88,14 +92,15 @@
             msg.send "Viewers: #{object.stream.viewers}"

     robot.respond /ttv top/i, (msg) ->
-        createURL = (game) ->
-            "http://www.twitch.tv/directory/game/#{encodeURIComponent(game)}"
+        createGameURL = (game) ->
+            "https://www.twitch.tv/directory/game/#{encodeURIComponent(game)}"

-        twitchRequest msg, "/games/top", limit: maxResults, (object) ->
+        GetTwitchResult msg, "/games/top", null, (object) ->
             for gameObj, i in object.top
-                msg.send "#{i + 1}. #{gameObj.game.name} | Viewers: #{gameObj.viewers} | Channels: #{gameObj.channels} | #{createURL(gameObj.game.name)}"
+                msg.send "#{i + 1}. #{gameObj.game.name} | Viewers: #{gameObj.viewers} | Channels: #{gameObj.channels} | #{createGameURL(gameObj.game.name)}"

-twitchRequest = (msg, api, params = {}, handler) ->
+GetTwitchResult = (msg, api, params = {}, handler) ->
+    params.limit or= TWITCH_MAX_RESULTS
     msg.http("https://api.twitch.tv/kraken#{api}")
         .query(params)
         .get() (err, res, body) ->


Remove console.log.

Remove console.log.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -183,7 +183,6 @@
             default: 'no'

           prompt.get property, (err, result) ->
-            console.log 'ask', result
             if _.isString(result.ask) and result.ask.match(/y(es){0,1}/i)
               run()
             else


Fix #220: Modified check originally from #207 to more accurately target syntax errors

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -143,6 +143,14 @@
     require './rules/non_empty_constructor_needs_parens.coffee'
 )

+hasSyntaxError = (source) ->
+    try
+        # If there are syntax errors this will abort the lexical and line
+        # linters.
+        CoffeeScript.tokens(source)
+        return false
+    return true
+
 # Check the source against the given configuration and return an array
 # of any errors found. An error is an object with the following
 # properties:
@@ -177,7 +185,7 @@

     # only do further checks if the syntax is okay, otherwise they just fail
     # with syntax error exceptions
-    if astErrors.length == 0
+    unless hasSyntaxError(source)
         # Do lexical linting.
         lexicalLinter = new LexicalLinter(source, config, _rules, CoffeeScript)
         lexErrors = lexicalLinter.lint()


Fix default model for collection

Fix default model for collection

--- src/collection.coffee
+++ src/collection.coffee
@@ -46,6 +46,10 @@
   lastFetchOptions: null
   firstFetchOptions: null

+  model: (attrs, options) ->
+    Model = require('./model')
+    new Model(attrs, options)
+

   #
   # Init
@@ -53,7 +57,6 @@
   constructor: (models, options) ->
     super

-    @model ?= require('./model')
     @storageManager = require('./storage-manager').get()

     @firstFetchOptions = Collection.pickFetchOptions(options) if options


need to use id that is passed in to onStepCompleted as id on TaskStep  is different for multiparts

--- tutor/src/components/task-step/index.cjsx
+++ tutor/src/components/task-step/index.cjsx
@@ -38,7 +38,8 @@
   bindStore: TaskStepStore

   onStepCompleted: (id) ->
-    {id, taskId} = @props
+    {id} = @props unless id? # need to allow `id` from arguments pass through, especially for multi-part
+    {taskId} = @props

     if StepPanel.canWrite(id)
       TaskActions.completeStep(id, taskId)


LOGS off

LOGS off

--- source/Art/Engine/Events/PointerEventManager.coffee
+++ source/Art/Engine/Events/PointerEventManager.coffee
@@ -149,7 +149,7 @@
   restoreFocus: ->
     if @_savedFocusedElement && !simpleBrowserInfo.touch
       if @_savedFocusedElement.canvasElement == @canvasElement
-        log restoreFocus: document.activeElement
+        # log restoreFocus: document.activeElement
         @focus null, rootToElementPath @_savedFocusedElement
       @_savedFocusedElement = null

@@ -426,7 +426,7 @@
     pointer ?= @_activePointers[0]

     if focusPath[0] != @canvasElement
-      log.warn "_updateFocusedPath - path invalid - DOES THIS EVER HAPPEN?"
+      # log.warn "_updateFocusedPath - path invalid - DOES THIS EVER HAPPEN?"
       focusPath = @validatedFocusPath

     @_currentFocusPath = updatePath @_currentFocusPath, focusPath,


updated tests for PRettyError::getObject()

--- scripts/coffee/test/PrettyError.coffee
+++ scripts/coffee/test/PrettyError.coffee
@@ -26,13 +26,13 @@

 	p = new PrettyError

-describe "toMarkup"
+describe "getObject"

 it "should return a string", ->

 	p = new PrettyError

-	p.toMarkup(error "hello").should.be.a 'string'
+	p.getObject(error "hello").should.be.an 'object'

 describe "getStyle()"

@@ -66,7 +66,7 @@

 	e = error -> "a".should.equal "b"

-	# markup = p.toMarkup e
+	# markup = p.getObject e

 	# require('fs').writeFileSync 'f:/someFile.txt', markup, flag: 'w+'


add gulp watch task for haml and jade convertion

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -50,7 +50,16 @@
 testTarget = './jThree/test/build/test.js'

 # entries of ts files (same as tsconfig files)
-tsEntries = ["./jThree/**/*.ts", "./test/**/*.ts"]
+tsEntries = ['./jThree/**/*.ts', './test/**/*.ts']
+
+# templete convertion root (for entries of jade and haml)
+templeteRoot = 'jThree/wwwroot'
+
+# extention of jade
+jadeExtention = '.jdgoml'
+
+# extention of haml
+hamlExtention = '.hgoml'

 # path to tsconfig.json
 tsconfigPath = './tsconfig.json'
@@ -129,8 +138,8 @@
     if bundler == 'webpack'
       return ->
         if watching && c.dest.length >= 2
-          gulp.watch "#{c.dest[0]}/#{c.name}", ->
-            copyFiles("#{c.dest[0]}/#{c.name}", c.dest[1..])
+          gulp.watch path.join(c.dest[0], c.name), ->
+            copyFiles(path.join(c.dest[0], c.name), c.dest[1..])
         gulp
           .src path.resolve __dirname, c.entry
           .pipe webpack
@@ -167,7 +176,7 @@
           .pipe gulp.dest(c.dest[0])
           .on 'end', ->
             unless watching
-              copyFiles("#{c.dest[0]}/#{c.name}", c.dest[1..])
+              copyFiles(path.join(c.dest[0], c.name), c.dest[1..])
     else if bundler == 'browserify'
       # tsdBundlePath = JSON.parse(fs.readFileSync(tsdPath))?.bundle
       return watchify (watchify) ->
@@ -187,7 +196,7 @@
           .pipe rename(c.name)
           .pipe gulp.dest(c.dest[0])
           .on 'end', ->
-            copyFiles("#{c.dest[0]}/#{c.name}", c.dest[1..])
+            copyFiles(path.join(c.dest[0], c.name), c.dest[1..])
           .on 'error', ->
             gutil.log gutil.colors.black.bgYellow 'If tsconfig.json is not up-to-date, run command: "./   node_modules/.bin/gulp --require coffee-script/register update-tsconfig-files"'

@@ -212,12 +221,14 @@
 ###
 main watch task
 ###
-gulp.task 'watch:main', ['enable-watch-mode', 'build:main', 'server', 'watch-reload']
-
-gulp.task 'watch-reload', ->
+gulp.task 'watch:main', ['enable-watch-mode', 'build:main', 'server', 'watch:reload']
+
+gulp.task 'watch:reload', ->
   gulp.watch watchForReload, ['reload']

-gulp.task 'watch', ['watch:main']
+gulp.task 'watch:templete', ['watch:haml', 'watch:jade']
+
+gulp.task 'watch', ['watch:main', 'watch:templete']

 ###
@@ -253,9 +264,9 @@
     .pipe typedoc
       module: 'commonjs'
       target: 'es5'
-      out: "#{typedocDest}/#{branch}"
+      out: path.join(typedocDest, branch)
       name: 'jThree'
-      json: "#{typedocDest}/#{branch}.json"
+      json: path.join(typedocDest, "#{branch}.json")

 ###
@@ -306,22 +317,38 @@
 ###
 haml Task
 ###
-gulp.task 'haml', ->
-  gulp
-    .src 'jThree/wwwroot/**/*.hgoml'
+hamlCompile = (src, dest) ->
+  gutil.log("Haml Compiling " + gutil.colors.magenta(src.toString()))
+  gulp
+    .src src
     .pipe haml
       ext: '.goml'
-    .pipe gulp.dest 'jThree/wwwroot'
+    .pipe gulp.dest dest
+
+gulp.task 'haml', ->
+  hamlCompile path.join(templeteRoot, '**', "*#{hamlExtention}"), templeteRoot
+
+gulp.task 'watch:haml', ['haml'], ->
+  gulp.watch path.join(templeteRoot, '**', "*#{hamlExtention}"), (e) ->
+    hamlCompile e.path, path.dirname(e.path)

 ###
 jade Task
 ###
-gulp.task 'jade', ->
-  gulp
-    .src 'jThree/wwwroot/**/*.jdgoml'
+jadeCompile = (src, dest) ->
+  gutil.log("Jade Compiling " + gutil.colors.magenta(src.toString()))
+  gulp
+    .src src
     .pipe jade
       pretty: true
     .pipe rename
       extname: ".goml"
-    .pipe gulp.dest 'jThree/wwwroot'
+    .pipe gulp.dest dest
+
+gulp.task 'jade', ->
+  jadeCompile path.join(templeteRoot, '**', "*#{jadeExtention}"), templeteRoot
+
+gulp.task 'watch:jade', ['jade'], ->
+  gulp.watch path.join(templeteRoot, '**', "*#{jadeExtention}"), (e) ->
+    jadeCompile e.path, path.dirname(e.path)


Update gulpfile

Update gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -126,8 +126,8 @@
   arch: "x64"
   version: "0.34.0"
   prune: true
-  ignore: new RegExp("(^\/node_modules\/(?!#{INCLUDE_MODULES.join("|")}).*|gulpfile\.coffee|^src/.*|^sass/.*|.*\.js\.map$)")
-  asar: false
+  ignore: new RegExp("(^\/node_modules\/(?!#{INCLUDE_MODULES.join("|")}).*|gulpfile\.coffee|^dist/.*|^src/.*|^sass/.*|.*\.js\.map$)")
+  asar: true
   overwrite: true
   "app-version": VERSION
 }


fix #297

fix #297

--- bokehjs/src/coffee/range/data_range1d.coffee
+++ bokehjs/src/coffee/range/data_range1d.coffee
@@ -22,7 +22,7 @@
         span = (max - min) * (1 + @get('rangepadding'))
       else
         if max != 0
-          span = max * (1 + @get('rangepadding'))
+          span = Math.abs(max) * (1 + @get('rangepadding'))
         else
           span = 2
       center = (max + min) / 2.0


update .js preview build

update .js preview build

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -255,9 +255,8 @@
   # 'preview'
   browserSync.init({server: "build", port:3000})

-  gulp.watch "assets/coffeescript/**", ->
+  gulp.watch "assets/**/*.{js,coffee}", ->
     js()
-    gutil.log gutil.colors.white.bgBlue("Coffeescript file changed."), "Compiling and reloading..."

   gulp.watch "./assets/stylus/**/*.styl", ['css_preview']


Deparallelize closure compilation.

Deparallelize closure compilation.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -107,10 +107,8 @@
     'unittest'
     'dicontainer'
     'concat-deps'
-    [
-      'compile-clientapp'
-      'compile-serverapp'
-    ] if args.production
+    'compile-clientapp' if args.production
+    'compile-serverapp' if args.production
     'concat-all'
     'livereload-notify' if este.shouldNotify()
     done


remove ship

remove ship

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -50,11 +50,7 @@
 # Watch when run

 gulp.task 'default', ['main', 'watch']
-

 # Tag and upload new feature

-gulp.task 'ship', shell.task [
-  'gulp tag --minor'
-  'node aws/upload.js'
-]
+gulp.task 'upload', shell.task ['node aws/upload.js']


update webp task src to contents and move webp task before wintersmith

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -48,9 +48,8 @@

 gulp.task "preview", (cb)->
   return runSequence  ['build:clean', 'js:clean'],
-                      ['css:build','js:build'],
+                      ['css:build','js:build', 'webp:make'],
                       'build_wintersmith',
-                      'webp:make',
                       'css:clean',
                       cb

@@ -239,7 +238,7 @@
     .pipe(gulp.dest('./'))

 gulp.task 'webp:make', ->
-  return gulp.src('./build/media/images/**/*.{jpg,png}',{base: './'})
+  return gulp.src('./contents/media/images/**/*.{jpg,png}',{base: './'})
     .pipe plumber()
     .pipe size()
     .pipe webp


fixed keyboard tracking events after losing focus

--- platform/src/services.coffee
+++ platform/src/services.coffee
@@ -6,12 +6,13 @@
   keysDown = {}

   # Use the KeyboardService event namespace
-  $(options.elementSelector).on "keydown.GE.KeyboardService keyup.GE.KeyboardService", (event) ->
+  $(options.elementSelector).on "keydown.GE.KeyboardService keyup.GE.KeyboardService blur.GE.KeyboardService", (event) ->
     # jQuery standardizes the keycode into http://api.jquery.com/event.which/
-    if event.type == "keydown"
-      keysDown[event.which] = true
-    else
-      delete keysDown[event.which]
+    switch event.type
+      when "keydown" then keysDown[event.which] = true
+      when "keyup" then delete keysDown[event.which]
+      when "blur" then keysDown = {}
+      else throw new Error("Unexpected event type")

   return {
     provideData: -> return { "keysDown": keysDown }
@@ -47,6 +48,7 @@
         mouse.position =
           x: event.clientX - rect.left
           y: event.clientY - rect.top
+      else throw new Error("Unexpected event type")

   return {
     provideData: -> return mouse


charts not dissapearing

charts not dissapearing

--- platform/src/services.coffee
+++ platform/src/services.coffee
@@ -337,23 +337,10 @@

 # Define chart output service
 registerService 'Chart', (options = {}) ->
-  CANVAS_CSS = "position: absolute; left: 0px; top: 0px;"
-
   capitalizeFirstLetter = (str) -> str[0].toUpperCase() + str.slice(1)

-  createLayers = ->
-    # Convert layers to ordered
-    createdLayers = {}
-    zIndex = 0
-    for layerName in options.layers
-      layer = $("<canvas id='canvasLayer-#{layerName}' class='gameCanvas' width='#{options.size[0]}' height='#{options.size[1]}' tabIndex=0 style='z-index: #{zIndex}; #{CANVAS_CSS}' />")
-      $(options.elementSelector).append(layer)
-      createdLayers[layerName] = layer
-      zIndex++
-
-    return createdLayers
-
-  layers = {}
+  # Format { name: { canvas:, data: }}
+  charts = {}

   return {
     provideData: -> {}
@@ -362,13 +349,19 @@
       # Expecting serviceData like { chartA: { size:, position:, depth:, data:, options: }}

       # Remove old charts
-      # OPT: keep old charts around if not changed
-      for layerName, canvas of layers then canvas.remove()
-
-      layers = {}
-
-      # Create new ones
+      for chartName in _.difference(_.keys(charts), _.keys(serviceData))
+        charts[chartName].remove()
+        delete charts[chartName]
+
+      # Create new charts and update old ones
       for chartName, chartData of serviceData
+        # If the chart exists and has the same data, don't bother redrawing it
+        if _.isEqual(charts[chartName]?.data, chartData) then continue
+
+        # Check it's a valid chart type
+        if chartData.type not in ["line", "bar", "radar", "polar", "pie", "doughnut"]
+          throw new Error("Unknown chart type: '#{chartData.type}'")
+
         # Define options
         displayOptions =
           size: chartData.size ? options.size
@@ -382,19 +375,17 @@
         canvasCss = "z-index: #{displayOptions.depth}; position: absolute; left: #{displayOptions.position[0]}px; top: #{displayOptions.position[1]}px;"
         canvas = $("<canvas #{canvasProps} style='#{canvasCss}' />")
         $(options.elementSelector).append(canvas)
-        layers[layerName] = canvas
+        charts[chartName] =
+          canvas: canvas
+          data: chartData

         # Create a new Chart object
         chart = new Chart(canvas[0].getContext("2d"))

-        # Check it's a real chart type
-        if chartData.type not in ["line", "bar", "radar", "polar", "pie", "doughnut"]
-          throw new Error("Unknown chart type: '#{chartData.type}'")
-
         # Call the correct method on it
         chart[capitalizeFirstLetter(chartData.type)](chartData.data, chartOptions)

     destroy: ->
-      for layerName, canvas of layers then canvas.remove()
-  }
-
+      for chartName, canvas of charts then canvas.remove()
+  }
+


Fix persistence problems in the number control

Overall, the control was very fragile before and could easily get out of sync if you clicked between controls too fast or if you re-opened the control after browsing to a different control. These issues are now fixed by correctly handling the 3 situations where the bounds might need to be changes: stats reset, onRender(), setValue().

--- src/coffee/cilantro/ui/field/controls/number.coffee
+++ src/coffee/cilantro/ui/field/controls/number.coffee
@@ -16,29 +16,24 @@
             'change .btn-select': 'change'
             'click .range-help-button': 'toggleHelpText'

-        initialize: (options) ->
-            @model = options.model
-
-            @model.stats.on 'reset', =>
-                @statsMin = _.find(@model.stats.models, (stat) ->
-                    return stat.get('label') == 'Min'
-                )?.get('value')
-
-                if @statsMin? and not @ui.lowerBound.val()
-                    @ui.lowerBound.val(@statsMin)
-
-                @statsMax = _.find(@model.stats.models, (stat) ->
-                    return stat.get('label') == 'Max'
-                )?.get('value')
-
-                if @statsMax and not @ui.upperBound.val()
-                    @ui.upperBound.val(@statsMax)
-
         ui:
             inOutSelect: '.btn-select'
             lowerBound: '.range-from'
             upperBound: '.range-to'
             help: '.help-block'
+
+        initialize: (options) ->
+            @model = options.model
+
+            # Try to read the min and max directly from the model's stats
+            # collection but in the event that the stats have not already
+            # been retrieved, attach a listener to the reset event so we can
+            # read the stats when the are fetched.
+            if @model.stats.length > 0
+                @readMinMaxStats()
+            else
+                @model.stats.on 'reset', =>
+                    @readMinMaxStats()

         onRender: ->
             @inOutSelect = new button.ButtonSelect
@@ -53,8 +48,30 @@

             @inOutSelect.render().$el.prependTo(@$el)
             @ui.help.hide()
+            @updateBounds()

             @set(@context)
+
+        # Tries to find and read the values of the stats models with the keys
+        # 'min' and 'max' from this model's StatsCollection. After these values
+        # are read, the bounds will be updated if they need to be so that the
+        # min/max are reflected in the UI in the input boxes when needed.
+        readMinMaxStats: =>
+            statsMin = _.find(@model.stats.models, (stat) ->
+                return stat.get('label') == 'Min'
+            )?.get('value')
+
+            if statsMin?
+                @minLowerBound = statsMin
+
+            statsMax = _.find(@model.stats.models, (stat) ->
+                return stat.get('label') == 'Max'
+            )?.get('value')
+
+            if statsMax?
+                @maxUpperBound = statsMax
+
+            @updateBounds()

         toggleHelpText: (event) ->
             @ui.help.toggle()
@@ -63,9 +80,23 @@
         isBetweenSelected: ->
             return @inOutSelect.getSelection() == 'between'

+        # If this control is NOT closed then this method will set the upper
+        # and lower bounds to the min/max read from readMinMaxStats() iff the
+        # upper and lower bounds are currently empty.
+        updateBounds: ->
+            if @isClosed? and not @isClosed
+                if @ui.lowerBound.val() == ""
+                    @ui.lowerBound.val(@minLowerBound)
+                if @ui.upperBound.val() == ""
+                    @ui.upperBound.val(@maxUpperBound)
+
         getField: ->
             return @model.id

+        # Uses the logical combination of the (not) between dropdown and the
+        # upper/lower bounds text boxes to determine the operator for this
+        # control. The help information for this control has a plain English
+        # explanation of this logic.
         getOperator: ->
             from = @ui.lowerBound.val()
             to = @ui.upperBound.val()
@@ -82,6 +113,10 @@

             return operator

+        # Use the filled state of the upper/lower bound inputs to create the
+        # value range or explicit value for this control. If both the upper
+        # and lower bounds are left blank, null will be returned to invalidate
+        # this range.
         getValue: ->
             from_text = @ui.lowerBound.val()
             from_num = Number(from_text)
@@ -97,7 +132,6 @@
                 value = from_num
             else if to_text != ""
                 value = to_num
-            # If both fields are emtpy, return null to invalidate this range
             else
                 value = null

@@ -116,15 +150,14 @@
                 when 'range', '-range'
                     if value[0]?
                         @ui.lowerBound.val(value[0])
-                    else
-                        @ui.lowerBound.val('')
                     if value[1]?
                         @ui.upperBound.val(value[1])
-                    else
-                        @ui.upperBound.val('')
                 when 'gte'
-                    @ui.lowerBound.val(value)
+                    if value?
+                        @ui.lowerBound.val(value)
                 when 'lte'
-                    @ui.upperBound.val(value)
+                    if value?
+                        @ui.upperBound.val(value)
+

     { NumberControl }


Fix calc_bruteforce_cardinality

Fix calc_bruteforce_cardinality

The function was overestimating the cardinality
for upper-case characters and digits by mistakenly
identifying them as symbols. Thus a simple repeat
sequence like "AAAAAA" would have cardinality
26+33=59 rather than 26.

--- scoring.coffee
+++ scoring.coffee
@@ -234,9 +234,9 @@
     ord = chr.charCodeAt(0)
     if 0x30 <= ord <= 0x39
       digits = true
-    if 0x41 <= ord <= 0x5a
+    else if 0x41 <= ord <= 0x5a
       upper = true
-    if 0x61 <= ord <= 0x7a
+    else if 0x61 <= ord <= 0x7a
       lower = true
     else
       symbols = true


attempt at fixing android issue with axis animations

--- src/documents/lib/jquery-slidescrollpanel.js.coffee
+++ src/documents/lib/jquery-slidescrollpanel.js.coffee
@@ -460,14 +460,23 @@
 		$wrap.show()
 		@resize()

-		# Check
+		# Init
 		if @isInvisible()
-			$wrap.css(@getHidePositionStyles()).prop(@getShowAxisProperties()).addClass(@config.wrapVisibleClass)
+			$wrap.prop(@getHideAxisProperties())
+			$wrap.css(@getShowPositionStyles())
+			# $wrap.css(@getHidePositionStyles())
+			# $wrap.prop(@getShowAxisProperties())
+			$wrap.addClass(@config.wrapVisibleClass)
 		else
-			$wrap.css(@getDesiredPositionStyles()).prop(@getShowAxisProperties())
-
-		# Show
-		$wrap.stop(true,false).animate @getShowPositionStyles(), 400, =>
+			@enable(null, {alterClass:false})
+			# $wrap.css(@getDesiredPositionStyles())
+			# $wrap.prop(@getShowAxisProperties())
+
+		# Animate
+		animateOpts = @getShowAxisProperties()
+		# animateOpts = @getShowPositionStyles()
+		$wrap.stop(true,false).animate animateOpts, 400, =>
+			@enable()
 			$(window).trigger('resize')
 			@$getEl().trigger('slidescrollpanelin')
 			return next?()
@@ -487,13 +496,21 @@
 		# Prepare
 		$wrap = @$getWrapper()

-		# Hide
+		# Prepare
 		@resize()
-		$wrap.css(@getDesiredPositionStyles()).prop(@getShowAxisProperties())
-		$wrap.stop(true,false).animate @getHidePositionStyles(), 400, =>
-			# Disable
+
+		# Init
+		@enable(null, {alterClass:false})
+		# $wrap.css(@getDesiredPositionStyles())
+		# $wrap.prop(@getShowAxisProperties())
+
+		# Animate
+		animateOpts = @getHideAxisProperties()
+		# animateOpts = @getHidePositionStyles()
+		$wrap.stop(true,false).animate animateOpts, 400, =>
+			@disable()
+			$wrap.removeClass(@config.wrapVisibleClass)
 			$(window).trigger('resize')
-			$wrap.removeClass(@config.wrapVisibleClass)
 			@$getEl().trigger('slidescrollpanelout')
 			return next?()

@@ -542,10 +559,10 @@

 			# Auto Show
 			if @config.autoShowAbove and percentVisible >= @config.autoShowAbove
-				@showPanel()
+				@showPanel()  if percentVisible

 			# Auto Hide
-			else if @config.autoHideBelow and percentVisible <= @config.autoHideBelow
+			else if percentVisible and @config.autoHideBelow and percentVisible <= @config.autoHideBelow
 				@hidePanel()

 			# Keep visible


Allow size and point to be a single number as a shortcut

So you can do layer.size = 10 to get a 10 x 10 layer.

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -298,6 +298,7 @@
 		get: -> _.pick(@, ["x", "y"])
 		set: (point) ->
 			return if not point
+			point = {x: point, y: point} if _.isNumber(point)
 			for k in ["x", "y"]
 				@[k] = point[k] if point.hasOwnProperty(k)

@@ -305,6 +306,7 @@
 		get: -> _.pick(@, ["width", "height"])
 		set: (size) ->
 			return if not size
+			size = {width: size, height: size} if _.isNumber(size)
 			for k in ["width", "height"]
 				@[k] = size[k] if size.hasOwnProperty(k)


Revert model-cursor

Revert model-cursor

--- src/controller.coffee
+++ src/controller.coffee
@@ -253,7 +253,7 @@
       # ## Document initialization
       # We start of with an empty document
       @tree = new model.Segment()
-      @cursor = @tree.start
+      @tree.start.insert @cursor

       @resize()

@@ -2255,17 +2255,12 @@
   # ================================

   hook 'populate', 0, ->
-    @cursor = null
+    @cursor = new model.CursorToken()

   # A cursor cannot be inside only a block.
   # Convenience function for this validation:
-  Editor::isValidCursorPosition = (pos) ->
-    unless pos? then return false
-
-    parent = (pos.container ? pos.parent)
-
-    return parent.type in ['indent', 'segment'] and
-      not pos.container?.isLassoSegment
+  isValidCursorPosition = (pos) ->
+    return pos.parent.type in ['indent', 'segment']

   # A cursor is only allowed to be on a line.
   Editor::moveCursorTo = (destination, attemptReparse = false) ->
@@ -2276,14 +2271,30 @@

     @highlightFlashShow()

-    @cursor = destination
-
-    until @cursor.type in ['newline', 'indentEnd'] or
-        @cursor.container is @tree
-      @cursor = @cursor.next
+    # Otherwise, splice the cursor out.
+    @cursor.remove()
+
+    # If the destination is in the tree,
+    # figure out where we want to place the cursor.
+
+    # We can place the cursor
+    # at the start of the tree
+    if destination is @tree.start
+      destination.insert @cursor
+
+    # If we got some other token,
+    # scan forward until we find
+    # a viable spot for the cursor.
+    else
+      head = destination
+      until head.type in ['newline', 'indentEnd'] or head is @tree.end
+        head = head.next
+
+      if head.type is 'newline' then head.insert @cursor
+      else head.insertBefore @cursor

     # Keep scanning forward if this is an improper location.
-    unless @isValidCursorPosition @cursor then @moveCursorTo @cursor.next
+    unless isValidCursorPosition @cursor then @moveCursorTo @cursor.next

     if attemptReparse
       @reparseHandwrittenBlocks()
@@ -2296,29 +2307,39 @@

     @highlightFlashShow()

-    until @cursor.type in ['newline', 'indentEnd'] or
-        @cursor.container is @tree
-      @cursor = @cursor.prev
+    # If we're at the beginning, abort.
+    unless head? then return
+
+    until head.type in ['newline', 'indentEnd'] or head is @tree.start
+      head = head.prev
+
+    # Splice out
+    @cursor.remove()
+
+    # Splice in
+    if head.type is 'newline' or head is @tree.start then head.insert @cursor
+    else head.insertBefore @cursor

     # Keep sacnning backwards if this is an improper location.
-    unless @isValidCursorPosition @cursor then @moveCursorUp()
+    unless isValidCursorPosition @cursor then @moveCursorUp()

     @reparseHandwrittenBlocks()
     @redrawHighlights()
     @scrollCursorIntoPosition()

   Editor::determineCursorPosition = ->
-    if @cursor?
-      parent = @cursor.container ? @cursor.parent
+    if @cursor? and @cursor.parent?
       @view.getViewNodeFor(@tree).layout 0, @nubbyHeight

       head = @cursor; line = 0
-      until head is parent.start
+      until head is @cursor.parent.start
+        head = head.prev
         line++ if head.type is 'newline'
-        head = head.prev
-
-      bound = @view.getViewNodeFor(parent).bounds[line]
-      if @cursor instanceof model.EndToken
+
+      bound = @view.getViewNodeFor(@cursor.parent).bounds[line]
+      if @cursor.nextVisibleToken()?.type is 'indentEnd' and
+         @cursor.prev?.prev.type isnt 'indentStart' or
+         (@cursor.next is @tree.end and @cursor.prev isnt @tree.start)
         return new @draw.Point bound.x, bound.bottom()
       else
         return new @draw.Point bound.x, bound.y
@@ -2592,6 +2613,110 @@

     return null

+  # INDENT CREATE/DESTROY SUPPORT
+  # ================================
+
+  ###
+  # CreateIndent undo operation
+  class CreateIndentOperation extends UndoOperation
+    constructor: (pos, @depth) ->
+      @location = pos.getSerializedLocation()
+
+    undo: (editor) ->
+      indent = editor.tree.getTokenAtLocation(@location).indent
+      indent.start.prev.append indent.end.next; indent.notifyChange()
+
+    redo: (editor) ->
+      head = editor.tree.getTokenAtLocation(@location)
+
+      newIndent = new model.Indent DEFAULT_INDENT_DEPTH
+      head.prev.append(newIndent.start)
+               .append(new model.NewlineToken())
+               .append(newIndent.end)
+               .append(head)
+
+  # DestroyIndent undo operation
+  class DestroyIndentOperation extends UndoOperation
+    constructor: (indent) ->
+      @location = indent.start.getSerializedLocation()
+      @indent = indent.clone()
+
+    undo: (editor) ->
+      head = editor.tree.getTokenAtLocation(@location)
+
+      newIndent = @indent.clone()
+      head.prev.append newIndent.start
+      newIndent.end.append head
+
+      newIndent.notifyChange()
+
+    redo: (editor) ->
+      indent = editor.tree.getTokenAtLocation(@location).indent
+      indent.start.prev.append indent.end.next; indent.notifyChange()
+
+  # If we press tab while we are editing
+  # a handwritten block, we create and indent.
+  hook 'key.tab', 0, ->
+    if @textFocus? and @textFocus.handwritten
+      @addMicroUndoOperation 'CAPTURE_POINT'
+
+      # Seek the block directly before this
+      head = @textFocus.start
+      until head.type is 'blockEnd'
+        head = head.prev
+
+      # If it ends in an indent,
+      # move this block to that indent.
+      if head.prev.type is 'indentEnd'
+        until head.type in ['blockEnd', 'indentStart']
+          head = head.prev
+
+      # Otherwise, create an indent right before this.
+      else
+        @addMicroUndoOperation new CreateIndentOperation head, DEFAULT_INDENT_DEPTH
+
+        newIndent = new model.Indent DEFAULT_INDENT_DEPTH
+        newIndent.start.append(new model.NewlineToken()).append newIndent.end
+        newIndent.spliceIn head.prev
+        newIndent.notifyChange()
+
+        head = newIndent.start
+
+      # Go through the motions of moving this block into
+      # the indent we have just found.
+      @addMicroUndoOperation new PickUpOperation @textFocus.start.prev.container
+      @textFocus.start.prev.container.spliceOut() #MUTATION
+
+      @addMicroUndoOperation new DropOperation @textFocus.start.prev.container, head
+      @textFocus.start.prev.container.spliceIn head #MUTATION
+
+      # Move the cursor up to where the block now is.
+      @moveCursorTo @textFocus.start.prev.container.end
+
+      @redrawMain()
+
+  # If we press backspace at the start of an empty
+  # indent (an indent containing only whitespace),
+  # delete that indent.
+  hook 'key.backspace', 0, (state) ->
+    if state.capturedBackspace then return
+
+    if  not @textFocus? and
+        @cursor.prev?.prev?.type is 'indentStart' and
+        (indent = @cursor.prev.prev.indent).stringify().trim().length is 0
+
+      @addMicroUndoOperation new DestroyIndentOperation indent
+      indent.notifyChange()
+
+      indent.start.prev.append indent.end.next #MUTATION
+
+      @moveCursorTo indent.end.next
+
+      state.capturedBackspace = true
+
+      @redrawMain()
+  ###
+
   # ANIMATION AND ACE EDITOR SUPPORT
   # ================================

@@ -3269,8 +3394,7 @@
     @addMicroUndoOperation new SetValueOperation @tree, newParse

     @tree = newParse; @gutterVersion = -1
-    @cursor = @tree.start
-
+    @tree.start.insert @cursor
     @removeBlankLines()
     @redrawMain()


alwaysShowPalette should use actual palette width

Instead of positioning the ace editor in alwaysShowPalette mode based
on the requested style.width of the palette, position it based
on the actual offsetWidth of the palette.  This makes things work
well in the case where a host has overridden the CSS using max-width.

--- src/controller.coffee
+++ src/controller.coffee
@@ -3143,7 +3143,7 @@
         # Translate the ACE editor div into frame.
         @aceElement.style.top = '0px'
         if @alwaysShowPalette
-          @aceElement.style.left = @paletteWrapper.style.width
+          @aceElement.style.left = "#{@paletteWrapper.offsetWidth}px"
         else
           @aceElement.style.left = '0px'


Refs #68; Cleaned up the option method and did some QA.

--- src/core/chart.coffee
+++ src/core/chart.coffee
@@ -50,8 +50,11 @@
     for key, value of scope
       if Epoch.isObject(value)
         trigger_options(instance, value, prefix + '.' + key)
+      else if prefix.length > 0
+        instance.trigger "option:#{prefix.substr(1)}.#{key}"
       else
-        instance.trigger "option:#{prefix.substr(1)}.#{key}"
+        instance.trigger "option:#{key}"
+

   # Creates a new base chart.
   # @param [Object] options Options to set for this chart.
@@ -83,33 +86,39 @@
   #
chart.option(key, value) - Sets an option and triggers the associated event.

   #
chart.option(object) - Allows for the setting of multiple options at once.

   #

-  option: (key, value) ->
-    return Epoch.Util.defaults({}, @options) if arguments.length == 0
-
-    if Epoch.isObject(key)
-      @options = Epoch.Util.defaults(key, @options)
-      trigger_options @, key, ''
-
-    if key? and value?
-      parts = key.split('.')
+  option: ->
+    # No Arguments: Return a copy of this chart's options
+    if arguments.length == 0
+      return Epoch.Util.defaults({}, @options)
+
+    # Get the option with the given key
+    if arguments.length == 1 and Epoch.isString(arguments[0])
+      parts = arguments[0].split('.')
+      console.log parts
+      scope = @options
+      while parts.length and scope?
+        subkey = parts.shift()
+        scope = scope[subkey]
+        console.log subkey, scope
+      return scope
+
+    # Set an option with the specified key to a specified value
+    if arguments.length == 2 and Epoch.isString(arguments[0])
+      parts = arguments[0].split('.')
       scope = @options
       while parts.length and scope?
         subkey = parts.shift()
         if parts.length == 0
-          scope[subkey] = value
-          @trigger "option:#{key}"
+          scope[subkey] = arguments[1]
+          @trigger "option:#{arguments[0]}"
           return
         scope = scope[subkey]

-    if key?
-      parts = key.split('.')
-      scope = @options
-      while parts.length and scope?
-        subkey = parts.shift()
-        scope = scope[subkey]
-      return scope
-
-
+    # Set new options given an object
+    if arguments.length == 1 and Epoch.isObject(arguments[0])
+      @options = Epoch.Util.defaults(arguments[0], @options)
+      trigger_options @, arguments[0], ''
+      return

   # Determines if the chart is currently visible in a document.
   # @return [Boolean] True if the chart is visible, false otherwise.


Move comment-matching rule to global repository


activity: fix of sad face emoji issue

activity: fix of sad face emoji issue

--- client/activity/lib/util/renderEmojis.coffee
+++ client/activity/lib/util/renderEmojis.coffee
@@ -4,7 +4,7 @@
   'broken_heart'
   'confounded'
   'flushed'
-  'flowning'
+  'frowning'
   'grinning'
   'heart'
   'kissing_heart'


Finalize the database modal.

Finalize the database modal.

--- client/app/CommonViews/databasesmodal.coffee
+++ client/app/CommonViews/databasesmodal.coffee
@@ -4,7 +4,7 @@

     options =
       title                   : "Manage Databases"
-      content                 : ''#"You can create and manage your databases here."
+      content                 : ''
       helpContent             :
         """
           You can create and modify your databases from this modal.
@@ -99,17 +99,28 @@
         @newDBCreatedWidget = new NewDBCreatedWidget {}, data

       dbCreateForm.buttons.Create.setTitle "Ok, got it"
+
+      dbCreateForm.buttons.Create.setClass "modal-clean-gray"
+      dbCreateForm.buttons.Create.unsetClass "modal-clean-green"
+
       dbCreateForm.buttons.Create.setCallback =>
         @newDBCreatedWidget.unsetClass 'ready'
+
         dbCreateForm.inputs.dbType.makeEnabled()
         dbCreateForm.inputs.dbKind.makeEnabled()
+
         dbCreateForm.buttons.Create.setTitle "Create"
+        dbCreateForm.buttons.Create.setClass "modal-clean-green"
+        dbCreateForm.buttons.Create.unsetClass "modal-clean-gray"
         dbCreateForm.buttons.Create.hideLoader()
+
         dbCreateForm.buttons.Create.setCallback =>
           form = @modalTabs.forms["Create New Database"]
           @dbController.addDatabase form.getFormData(), =>
             form.buttons.Create.hideLoader()
+
         KD.utils.wait 300, => @setPositions()
+
       @dbController.loadItems()

 class AccountDatabaseListController extends KDListViewController
@@ -185,16 +196,17 @@
           responses = responses.concat dbs
         if responseToWait == 0
           @hideLazyLoader()
-
           if responses.length > 0
             @instantiateListItems responses
           else
-            @addCustomItem "You don't have any database to show."
-
+            @addCustomItem """You don't have any databases.
+                              Why don't you create a new one?"""
           callback?()
       , =>
+        @hideLazyLoader()
         @addCustomItem """It seems there is something wrong with
                           database provider. Please try again later."""
+        callback?()
       , 10000

   deleteDatabase:(listItem)->
@@ -214,7 +226,6 @@
   updateDatabase:(listItem, password)->

     data = listItem.getData()
-    log "Requested DB Type", data

     @talkToKite
       method        : @commands[data.dbType].update


denemee

denemee

--- scripts/trial.coffee
+++ scripts/trial.coffee
@@ -1,4 +1,4 @@
 module.exports = (robot) ->

-  robot.hear /badger/i, (res) ->
+  robot.hear /deneme/i, (res) ->
     res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERSSSSSSSSSSSSSSS"


added line colors

added line colors

--- scripts/wmata.coffee
+++ scripts/wmata.coffee
@@ -55,5 +55,5 @@
       trains = body.Trains
       str = ''
       for train in trains
-        str += train.DestinationName + ': ' + train.Min + '\r\n'
+        str += '<' + train.Line + '>' + train.DestinationName + ': ' + train.Min + '\r\n'
       msg.send str


Optimise checking for serving up stale images.

--- server/router.coffee
+++ server/router.coffee
@@ -82,9 +82,8 @@
         redirect()

       # Check if requested image is stale
-      for image of room.images
-        if room.images[image].timestamp > reqImage.timestamp
-          redirect()
+      if room.imageTimestamp > reqImage.timestamp
+        redirect()

       unless @response.finished
         imagePath = path.join Meteor.settings.imageDir, roomId, reqImage.filename


Clarify comment.

Clarify comment.

--- server/server.coffee
+++ server/server.coffee
@@ -23,14 +23,15 @@
 MONGODB_PORT = 27017
 QIREST_PORT = 5000

-# The grunt build tasks place all compiled and copied files within
-# the public directory. However, the Angular, jspm and test
-# frameworks all make it hard to run in a root other than the
-# top-level project directory. Therefore, we make the parent
-# directory the root and qualify the HTML with the public
-# subdirectory where necessary. This scheme is not ideal, since
-# the web client potentially has access to source. However, since
-# the source is public anyway, we'll let that slide.
+# The grunt build tasks place all compiled and copied files
+# within the public directory. However, the Angular, jspm and
+# test frameworks all conspire to force the demo-mode assumption
+# that the root is the top-level project directory. Therefore,
+# we make the parent directory the root and qualify the HTML
+# with the public subdirectory where necessary. This scheme is
+# not ideal, since the web client potentially has access to
+# source. However, since the source is public anyway, we'll
+# let that slide.
 root = path.join(__dirname, '..')

 # The REST request handler.


move highlight line to a function

move highlight line to a function

--- lib/python-debugger-view.coffee
+++ lib/python-debugger-view.coffee
@@ -124,16 +124,18 @@
       fileName = fileName.trim() if fileName
       fileName = null if fileName == "<string>"

-    if lineNumber && fileName
-      lineNumber = parseInt(lineNumber)
-      atom.workspace.open(fileName).then (editor) ->
-        position = Point(lineNumber - 1, 0)
-        editor.setCursorBufferPosition(position)
-        editor.unfoldBufferRow(lineNumber)
-        editor.scrollToBufferPosition(position)
-        # TODO: add decoration to current line?
-
+    @highlightLineInEditor fileName, lineNumber
     @addOutput(data_str.trim())
+
+  highlightLineInEditor: (fileName, lineNumber) ->
+    return unless fileName && lineNumber
+    lineNumber = parseInt(lineNumber)
+    atom.workspace.open(fileName).then (editor) ->
+      position = Point(lineNumber - 1, 0)
+      editor.setCursorBufferPosition(position)
+      editor.unfoldBufferRow(lineNumber)
+      editor.scrollToBufferPosition(position)
+      # TODO: add decoration to current line?

   runBackendDebugger: ->
     args = [path.join(@backendDebuggerPath, @backendDebuggerName)]


Using Rubocop's JSON format output to show notifications

--- lib/rubocop-auto-correct.coffee
+++ lib/rubocop-auto-correct.coffee
@@ -105,11 +105,11 @@
           @rubocopNotFoundError()
         else
           rubocop = spawnSync(command, args, { encoding: 'utf-8', timeout: 5000 })
-          if (rubocop.status != 0)
-            @rubocopOutput(rubocop.stderr)
+          if (rubocop.stderr)
+            @rubocopOutput({"stderr": "#{rubocop.stderr}"})
           else
             buffer.setTextViaDiff(fs.readFileSync(tempFilePath, 'utf-8'))
-            @rubocopOutput(rubocop.stdout)
+            @rubocopOutput(JSON.parse(rubocop.stdout))

   autoCorrectFile: (filePath)  ->
     rubocopCommand = @rubocopCommand()
@@ -120,9 +120,9 @@
         .concat(@projectRootRubocopConfig(filePath))

       stdout = (output) =>
-        @rubocopOutput(output)
+        @rubocopOutput(JSON.parse(output))
       stderr = (output) =>
-        @rubocopOutput(output)
+        @rubocopOutput({"stderr": "#{output}"})

       which command, (err) =>
         if (err)
@@ -139,30 +139,30 @@
       ''' }
     )

-  rubocopOutput: (output) ->
+  rubocopOutput: (data) =>
     debug = atom.config.get('rubocop-auto-correct.debugMode')
     notification = atom.config.get('rubocop-auto-correct.notification')
-    console.log(output) if debug

-    data = JSON.parse(output)
     console.log(data) if debug

-    if (data.summary.offense_count == 0)
+    if (data.stderr)
+      atom.notifications.addError(data.stderr) if notification
+    else if (data.summary.offense_count == 0)
       atom.notifications.addSuccess("No offenses found") if notification
     else
-      atom.notifications.addWarning("#{data.summary.offense_count} offenses found!")
+      atom.notifications.addWarning("#{data.summary.offense_count} offenses found!") if notification
       for file in data.files
         for offense in file.offenses
           if offense.corrected
             atom.notifications.addSuccess(
               "Line: #{offense.location.line}, Col:#{offense.location.column} (FIXED)",
               { detail: "#{offense.message}" }
-            )
+            ) if notification
           else
             atom.notifications.addWarning(
               "Line: #{offense.location.line}, Col:#{offense.location.column}",
               { detail: "#{offense.message}" }
-            )
+            ) if notification

   makeTempFile: (filename) ->
     directory = temp.mkdirSync()


Added Enumerable#reverse method.

Added Enumerable#reverse method.

--- lib/ST/Enumerable.coffee
+++ lib/ST/Enumerable.coffee
@@ -121,6 +121,14 @@
       list.add item unless fn(item)
     list

+  # Returns a new list with callback items in reverse order
+  @method 'reverse', () ->
+    list = ST.List.create()
+    list.retains false
+    @each (item) ->
+      list.insertAt 0, item
+    list
+
   # Returns lowest value
   @method 'min', (fn) ->
     fn = fn && ST.toProc fn


upgrade marshal definition with cc/ec

upgrade marshal definition with cc/ec

--- library/membrane/marshal.coffee
+++ library/membrane/marshal.coffee
@@ -35,14 +35,14 @@
 http = require "http"
 util = require "util"

-{remote, external} = require "./remote"
+{cc, ec, external} = require "./remote"
 {Archetype} = require "../nucleus/archetype"

 # This class is not meant to be instantianted, but rather to be used
 # as static class, a container. It exposes the functionality for the
 # data marshalling to be applied on the data that is being transfered
 # between the server site and the client site. Used on both sites.
-module.exports.Marshal = remote -> class Marshal extends Archetype
+module.exports.Marshal = cc -> class Marshal extends Archetype

     # Recover the sequence of values transferred from the another
     # environment. Typically you would use this on the parameter


Remove PUBLISHED notification on creation

--- lib/notifications.coffee
+++ lib/notifications.coffee
@@ -336,6 +336,7 @@
               changed[key] = true
           ## Ignore some initial values during creation of message.
           if notification.created
+            delete changed.published if msg.published
             delete changed.deleted unless msg.deleted
             delete changed.body unless 0 < msg.body.trim().length
             delete changed.tags unless 0 < _.size msg.tags


Increment priority by group

Increment priority by group

--- lib/tool-bar-view.coffee
+++ lib/tool-bar-view.coffee
@@ -7,7 +7,10 @@
     @div class: 'tool-bar'

   addItem: (newItem) ->
-    newItem.priority ?= @items[@items.length - 1]?.priority ? 50
+    lastItem = @items.filter((item) -> item.group isnt newItem.group)?.pop()
+    newItem.priority ?= lastItem?.priority + 1 ? 50
+    newItem.get(0).dataset.group = newItem.group if atom.devMode
+    newItem.get(0).dataset.priority = newItem.priority if atom.devMode
     nextItem = null
     for existingItem, index in @items
       if existingItem.priority > newItem.priority


Much better

Much better

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -245,12 +245,12 @@
         teasers = []
         collectTeasers = bongo.race (i, root, fin)->
           root.fetchSource (err, teaser)->
-            if not teaser
+            if err then callback err
+            else if not teaser
               console.warn "Source does not exists:", root.sourceName, root.sourceId
-            if err then callback err
             else
-              teasers.push(teaser) if teaser
-              fin()
+              teasers.push(teaser)
+            fin()
         , -> callback null, teasers
         collectTeasers node for node in contents


Fix relation issue

Fix relation issue

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -14,7 +14,6 @@

   JAppStorage = require './appstorage'
   JTag = require './tag'
-  JPrivateMessage = require './messages/privatemessage'

   @getFlagRole = 'content'

@@ -106,7 +105,8 @@
         lastStatusUpdate    : String
       globalFlags           : [String]
       meta                  : require 'bongo/bundles/meta'
-    relationships           :
+    relationships           : ->
+      JPrivateMessage = require './messages/privatemessage'

       mount         :
         as          : 'owner'


Callback should be called once

Callback should be called once

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -95,7 +95,11 @@

                 if cursor._endFlag && cursor._outstandingRequests is 0
                     @_delQuery(token)
-
+            else if feed?
+                feed._addResponse(response)
+
+                if feed._endFlag && feed._outstandingRequests is 0
+                    @_delQuery(token)
             else if cb?
                 # Behavior varies considerably based on response type
                 switch response.t


switching database to like/dislike rating

--- database.coffee
+++ database.coffee
@@ -38,16 +38,17 @@

 BoulderSchema = new Schema
-    setters : { type : [ ObjectID ], required : true                     },
-    grade   : { type : String,       required : true                     },
-    gradenr : { type : Number,       required : true                     },
-    sector  : { type : Number,       required : true                     },
-    date    : { type : Date,         required : false, default: Date.now },
-    removed : { type : Date,         required : false, default: null     },
-    stars   : { type : [ Number ],   required : false, default: []       },
-    name    : { type : String,       required : false, default: ""       },
-    comments: { type : [ String ],   requried : false, default: []       },
-    addedBy : { type : ObjectID,     required : false                    }
+    setters  : { type : [ ObjectID ], required : true                     },
+    grade    : { type : String,       required : true                     },
+    gradenr  : { type : Number,       required : true                     },
+    sector   : { type : Number,       required : true                     },
+    date     : { type : Date,         required : false, default: Date.now },
+    removed  : { type : Date,         required : false, default: null     },
+    likes    : { type : Number,       required : false, default: 0        },
+    dislikes : { type : Number,       required : false, default: 0        },
+    name     : { type : String,       required : false, default: ""       },
+    comments : { type : [ String ],   requried : false, default: []       },
+    addedBy  : { type : ObjectID,     required : false                    }

 BoulderSchema.statics =
@@ -73,8 +74,12 @@
                     setter.gradingLevel = setter.gradingLevel - 1
                     setter.save()

-    vote: (id, nr_stars) ->
-        Boulder.update {_id: id}, {$push: {stars : nr_stars}}, (err, boulder) ->
+    like: (id) ->
+        Boulder.update {_id: id}, {$inc: {likes : 1}}, (err, boulder) ->
+            console.log err if err
+
+    dislike: (id) ->
+        Boulder.update {_id: id}, {$inc: {dislikes : 1}}, (err, boulder) ->
             console.log err if err

     unscrew: (id) ->
@@ -85,9 +90,6 @@

 BoulderSchema.methods =
-    rating: ->
-        @stars.reduce(((a, x) -> a + x), 0) / @stars.length || 0
-
     colorName: ->
         switch @grade
             when '0' then 'yellow'


Clarify variable names.

Clarify variable names.

--- server/api.coffee
+++ server/api.coffee
@@ -6,28 +6,30 @@
   # middleware is not executed to further process the
   # request.
   #
-  # @param request the HTTP request object
-  # @param response the HTTP response object that Express sends
+  # @param httpRequest the HTTP request object
+  # @param httpResponse the HTTP response object that Express
+  #   sends
   # @param next the Express middleware to execute when the
   #   request is received (unused since this function is a
   #   request end point)
-  (request, response, next) ->
+  (httpRequest, httpResponse, next) ->
     # The REST server URL.
-    path = "#{ hostname }:#{ port }#{ request.path }"
+    path = "#{ hostname }:#{ port }#{ httpRequest.path }"
     # Send the request to the REST server.
     # When a successful REST response is received, then parse
     # the REST response body as a JSON object.
     # If the REST request was unsuccessful, then pass on the
     # REST response error status and a message.
-    request[request.method.toLowerCase()](path)
-      .query(request.query)
+    method = httpRequest.method.toLowerCase()
+    request[method](path)
+      .query(httpRequest.query)
       .accept('json')
-      .send(request.body)
+      .send(httpRequest.body)
       .end (error, restResponse) ->
         if restResponse.ok
-          restResponse.json restResponse.body
+          httpResponse.json restResponse.body
         else
-          response.status(restResponse.status)
+          httpResponse.status(restResponse.status)
             .send("The requested resource #{ hostname }:#{ port }" +
-                  "#{ request.path } request was unsuccessful:" +
+                  "#{ httpRequest.path } request was unsuccessful:" +
                   " #{ restResponse.reason } (#{ restResponse.status })")


Revise text in coffee

Revise text in coffee

--- app/_coffee/main.coffee
+++ app/_coffee/main.coffee
@@ -1 +1 @@
-console.log 'Welcome to Rff Fixtures!'
+console.log 'Welcome to rff-fixtures!'


Use sharify data instead of config

Use sharify data instead of config

--- components/block_v2/view.coffee
+++ components/block_v2/view.coffee
@@ -1,5 +1,5 @@
 Backbone = require 'backbone'
-{ API_URL } = require '../../config.coffee'
+{ API_URL } = require('sharify').data
 Channel = require '../../models/channel.coffee'
 Block = require '../../models/block.coffee'
 ConnectView = require '../connect/client/connect_view.coffee'


Remove other console, sigh

Remove other console, sigh

--- components/layout/client.coffee
+++ components/layout/client.coffee
@@ -166,7 +166,6 @@

 showPremiumMessage = ->
   if (!sd.CURRENT_USER.is_pro and sd.CURRENT_USER.channel_count >= 2 )
-    console.log('should show premium')
     model = new Backbone.Model
       id: 'premium_message'
       title: "Help support Are.na"


chore: add commend for a task

chore: add commend for a task

--- build/config/copy.coffee
+++ build/config/copy.coffee
@@ -15,6 +15,7 @@
       ]
     ]

+  # copy fonts to the dist directory
   dist:
     files: [
       expand: true


Adds core asset to chain_config

Adds core asset to chain_config

--- dl/src/chain/config.coffee
+++ dl/src/chain/config.coffee
@@ -1,11 +1,14 @@
 module.exports = _this =
+    core_asset: "CORE"
     address_prefix: "GPH"
     expire_in_secs: 15
     networks:
         BitShares:
+            core_asset: "BTS"
             address_prefix: "BTS"
             chain_id: "4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8"
         Muse:
+            core_asset: "MUSE"
             address_prefix: "MUSE"
             chain_id: "45ad2d3f9ef92a49b55c2227eb06123f613bb35dd08bd876f2aea21925a67a67"