Flash of text appears when instructor goes to staff grading fixed. ORA-249

--- lms/static/coffee/src/staff_grading/staff_grading.coffee
+++ lms/static/coffee/src/staff_grading/staff_grading.coffee
@@ -286,6 +286,7 @@

   get_problem_list: () ->
     @list_view = true
+    @render_view(true)
     @backend.post('get_problem_list', {}, @ajax_callback)

   submit_and_get_next: () ->
@@ -340,7 +341,7 @@
     @max_score = 0
     @state = state_no_data

-  render_view: () ->
+  render_view: (before_ajax) ->
     # clear the problem list and breadcrumbs
     @problem_list.html('''
         <tr>
@@ -372,10 +373,13 @@
     @meta_info_wrapper.toggle(show_grading_elements)
     @action_button.hide()

-    if @list_view
-      @render_list()
-    else
-      @render_problem()
+    if before_ajax
+      @scroll_to_top()
+    else
+      if @list_view
+        @render_list()
+      else
+        @render_problem()

   problem_link:(problem) ->
     link = $('<a>').attr('href', "javascript:void(0)").append(


Only guard version mismatches when given a valid semver specifier

Fixes #136

--- lib/cli/ensure-appropriate-version.coffee
+++ lib/cli/ensure-appropriate-version.coffee
@@ -2,7 +2,7 @@

 module.exports = ->
   return unless specifiedVersion = specifiedLinemanVersion()
-  return if /^git(\+(.*))?:\/\//.test(specifiedVersion)
+  return unless semver.validRange(specifiedVersion)

   actualVersion = actualLinemanVersion()
   unless semver.satisfies(actualVersion, specifiedVersion)


increase board page size past 10 (#5005)

allow more than 10 discussion radio buttons. I don't think this is great
UX but it's a quick fix for a reported issue.

--- app/talk/discussion-new-form.cjsx
+++ app/talk/discussion-new-form.cjsx
@@ -39,6 +39,7 @@
           .get
             section: projectSection(project)
             subject_default: false
+            page_size: 50
           .then (boards) =>
             @setState {boards}


if numProjects is 0, don't fetch Projects. and not, fetch Projects.

--- coffee/controllers/mainCtrl.coffee
+++ coffee/controllers/mainCtrl.coffee
@@ -60,7 +60,11 @@
   _loadProjects = (a) ->
     redmine = Redmine.get(a)
     promises = []
-    a.numProjects > 0 and promises.push(redmine.loadProjectsRange({}, 0, a.numProjects))
+    if a.numProjects is 0
+    else if not a.numProjects
+      promises.push(redmine.loadProjectsRange({}, 0))
+    else
+      promises.push(redmine.loadProjectsRange({}, 0, a.numProjects))
     if a.projectList
       promises.add a.projectList.map (id) ->
         if Object.isNumber(id)
@@ -104,7 +108,7 @@

     data.reduce (a, b) ->
       if b.projects
-        a.projects.push b.projects
+        a.projects.add b.projects
       else if b.project
         a.projects.push b.project
       return a


Copy favicon.ico to dist on build.

Copy favicon.ico to dist on build.

--- community/browser/Gruntfile.coffee
+++ community/browser/Gruntfile.coffee
@@ -227,7 +227,7 @@
         files: [
           expand: true
           cwd: "<%= yeoman.app %>/images"
-          src: "**/*.{png,jpg,jpeg}"
+          src: "**/*.{png,jpg,jpeg,ico}"
           dest: "<%= yeoman.dist %>/images"
         ]


Revert to original up arrow icon

Revert to original up arrow icon

@shd101wyy reports the new one doesn't work on MacOS.

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -629,7 +629,7 @@
     backToTopBtn = document.createElement('div')
     backToTopBtn.classList.add('back-to-top-btn')
     backToTopBtn.classList.add('btn')
-    backToTopBtn.innerHTML = '⭱'
+    backToTopBtn.innerHTML = '⬆'
     @toolbar.appendChild(backToTopBtn)

     backToTopBtn.onclick = ()=>


Remove unused require.

Remove unused require.

--- app/assets/javascripts/components/activity_stop.js.jsx.coffee
+++ app/assets/javascripts/components/activity_stop.js.jsx.coffee
@@ -1,5 +1,4 @@
 #= require ../models
-#= require react-transition-group/src/index

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


Fixes bug where file browser would not always show files

--- app/views/filebrowser_view.coffee
+++ app/views/filebrowser_view.coffee
@@ -35,8 +35,8 @@
   setModel: (model) ->
     @model?.off 'change', @render
     @model = model
+    @model.on 'change', @render, this
     @model.fetchRootFolder() # This loads the files on the root of the project
-    @model.on 'change', @render, this

   render: ->
     app.logger.log "FilebrowserView#render"


restore stub

restore stub

--- components/clock/test/view.coffee
+++ components/clock/test/view.coffee
@@ -18,7 +18,7 @@
       sd.CURRENT_PATH = ""
       benv.expose { $: benv.require 'jquery' }
       sinon.stub Backbone, 'sync'
-      sinon.stub location, 'reload'
+      @locationStub = sinon.stub location, 'reload'
       Backbone.$ = $
       @view = new ClockView
         model: new Sale(fabricate('sale'), clockState: 'open')
@@ -28,7 +28,7 @@
   after ->
     benv.teardown()
     Backbone.sync.restore()
-    location.reload.restore()
+    @locationStub.restore()

   beforeEach ->
     @triggerSpy = sinon.stub()


Config: change services endpoint for production config

--- config/main.sjc-production.coffee
+++ config/main.sjc-production.coffee
@@ -175,7 +175,7 @@
         servicesEndpoint: "/-/services/broker"
         sockJS   : "https://broker-#{version}.koding.com/subscribe"
       brokerKite:
-        servicesEndpoint: "/-/services/broker"
+        servicesEndpoint: "/-/services/brokerKite"
         brokerExchange: 'brokerKite'
         sockJS   : "https://brokerkite-#{version}.koding.com/subscribe"
       apiUri    : 'https://koding.com'


Use Spree.url() to add tokens to API calls to stock_locations

On 2.0, stock_locations AJAX class are getting a 401 on http://localhost:3000/admin/stock_transfers/new since securing the stock locations API end points.

Fixes #3828

--- backend/app/assets/javascripts/admin/stock_transfer.js.coffee
+++ backend/app/assets/javascripts/admin/stock_transfer.js.coffee
@@ -30,7 +30,7 @@

       $('#transfer_receive_stock').change (event) => @receive_stock_change(event)

-      $.getJSON Spree.routes.stock_locations_api, (data) =>
+      $.getJSON Spree.url(Spree.routes.stock_locations_api), (data) =>
         @locations = (location for location in data.stock_locations)
         @force_receive_stock() if @locations.length < 2

@@ -92,13 +92,13 @@
       if @cached_variants?
         @populate_select @cached_variants
       else
-        $.getJSON Spree.routes.variants_api, (data) =>
+        $.getJSON Spree.url(Spree.routes.variants_api), (data) =>
           @cached_variants = _.map(data.variants, (variant) -> new TransferVariant(variant))
           @populate_select @cached_variants

     _refresh_transfer_stock_items: ->
       stock_location_id = $('#transfer_source_location_id').val()
-      $.getJSON Spree.routes.stock_locations_api + "/#{stock_location_id}/stock_items", (data) =>
+      $.getJSON Spree.url(Spree.routes.stock_locations_api + "/#{stock_location_id}/stock_items"), (data) =>
         @populate_select _.map(data.stock_items, (stock_item) -> new TransferStockItem(stock_item))

     populate_select: (variants) ->
@@ -177,5 +177,3 @@
     transfer_locations = new TransferLocations
     transfer_variants = new TransferVariants
     transfer_add_variants = new TransferAddVariants
-
-


JS: Remove now unused main.coffee

JS: Remove now unused main.coffee

--- core/htdocs_source/app/main.coffee
+++ core/htdocs_source/app/main.coffee
@@ -1,3 +0,0 @@
-import App from './app'
-
-App.create()


remove more renderable

remove more renderable

--- app/context.coffee
+++ app/context.coffee
@@ -33,11 +33,6 @@
     @help object
     true

-handle_renderable = (object) ->
-  if is_renderable object
-    @add_renderable object
-    true
-
 handle_using_extension = (object) ->
   handlers = collect_extension_points @, 'context_result_handler'
   context = @
@@ -52,7 +47,6 @@
   ignored
   handle_cmd
   handle_module
-  handle_renderable
   handle_using_extension
   handle_any_object
 ]
@@ -60,7 +54,6 @@
 display_object = (ctx, object) ->
   for handler in result_handlers
     return if handler.call ctx, object
-

 collect_extension_points = (context, extension_point) ->
   modules.collect_extension_points context.modules, extension_point
@@ -145,9 +138,6 @@

 # FIXME figure out a real check for a react component
 is_component = (o) -> o?.__realComponentInstance?
-
-is_renderable = (o) ->
-  o? and is_component(o)

 component_for_renderable = (renderable) ->
   if is_component renderable


remove cruft

remove cruft

--- bokehjs/src/coffee/models/tools/gestures/box_zoom_tool.coffee
+++ bokehjs/src/coffee/models/tools/gestures/box_zoom_tool.coffee
@@ -16,16 +16,6 @@
     w = hend - hstart
     h = vend - vstart
     a = w/h
-
-    # if curpoint[0] > hend
-    #   curpoint[0] = hend
-    # if curpoint[0] < hstart
-    #   curpoint[0] = hstart
-    #
-    # if curpoint[1] > vend
-    #   curpoint[1] = vend
-    # if curpoint[1] < vstart
-    #   curpoint[1] = vstart

     # current aspect of cursor-defined box
     vw = Math.abs(basepoint[0]-curpoint[0])


Tweaks.

Tweaks.

--- config/social/jkn2-twitter.coffee
+++ config/social/jkn2-twitter.coffee
@@ -1,29 +1,31 @@

 configs = []

-## configs.push
-##   name: "Twitter"
-##   comment:
-##     """
-##     In addition to selecting tweets, this also selects the "New Tweets" button at the top of the page.  Scroll
-##     to the top and hit "Enter" to load new tweets.
-##     """
-##   regexps: "^https?://([a-z]+\\.)?twitter.com/?"
-##   selectors: do ->
-##     selectors = [ "div.new-tweets-bar" ]
-##     # for context in [ "div.home-stream", "div.profile-stream", "div.list-stream", "div.search-stream", "li.stream-item" ]
-##     for context in [ "li.stream-item" ]
-##       for content in [ "div.original-tweet", "li.conversation-root", "li.js-simple-tweet", "div.QuoteTweet" ]
-##         selectors.push "#{context} #{content}"
-##     selectors
-##   activators: [
-##     "div.original-tweet a.twitter-timeline-link"
-##     # "div.multi-photos"
-##     # View/Hide conversation (also view photo).
-##     "div.stream-item-footer span.expand-stream-item"
-##     "div.stream-item-footer span.collapse-stream-item"
-##   ]
-##   offset: 140
+configs.push
+  name: "Twitter"
+  comment:
+    """
+    In addition to selecting tweets, this also selects the "New Tweets" button at the top of the page.  Scroll
+    to the top and hit "Enter" to load new tweets.
+    """
+  regexps: "^https?://([a-z]+\\.)?twitter.com/?"
+  selectors: do ->
+    selectors = [ "div.new-tweets-bar" ]
+    # for context in [ "div.home-stream", "div.profile-stream", "div.list-stream", "div.search-stream", "li.stream-item" ]
+    for context in [ "li.stream-item" ]
+      for content in [ "div.original-tweet", "li.conversation-root", "li.js-simple-tweet", "div.QuoteTweet" ]
+        selectors.push "#{context} #{content}"
+    selectors.push "div.self-thread-head"
+    selectors.push "li.missing-tweets-bar"
+    selectors
+  activators: [
+    "div.original-tweet a.twitter-timeline-link"
+    # "div.multi-photos"
+    # View/Hide conversation (also view photo).
+    "div.stream-item-footer span.expand-stream-item"
+    "div.stream-item-footer span.collapse-stream-item"
+  ]
+  offset: 70

 require("../../common.js").Common.mkConfigs configs, name: "Twitter"


Debug mutations

Debug mutations

--- src/reactive.coffee
+++ src/reactive.coffee
@@ -113,6 +113,7 @@
     # dependency
     record: (dep, f) ->
       _(@stack).last().addNestedBind(dep) if @stack.length > 0 and not @isMutating
+      console.log "No dependency #{dep} to record", @stack, f unless dep
       @stack.push(dep)
       # reset isMutating
       wasMutating = @isMutating


fix comment in main-nav-coffee

fix comment in main-nav-coffee

--- pages/main-nav.coffee
+++ pages/main-nav.coffee
@@ -4,7 +4,7 @@

 MainNav =
   translateReadymadeLinks: (pages...) ->
-    # translate existing readymade links matching #{page}Link in translations file
+    # translate existing readymade links matching site.#{page}Link in translations file

     links = $(".readymade-site-links")


fix build path

fix build path

--- build.coffee
+++ build.coffee
@@ -5,7 +5,7 @@
 _ = require 'lodash'

 target.build = ->
-  exec 'docker build -t mooxe/base ./src'
+  exec 'docker build -t mooxe/base .'

 target.in = ->
   exec 'docker run -t -i mooxe/base /bin/bash'


Avoid race comditions in background completer, change id handling.

--- pages/vomnibar.coffee
+++ pages/vomnibar.coffee
@@ -219,10 +219,9 @@
     chrome.runtime.sendMessage({ handler: "refreshCompleter", name: @name })

   filter: (query, callback) ->
-    id = BackgroundCompleter.messageId += 1
     @filterPort.onMessage.addListener (msg) =>
       @filterPort.onMessage.removeListener(arguments.callee)
-      if id == BackgroundCompleter.messageId
+      if msg.id == BackgroundCompleter.messageId
         # The result objects coming from the background page will be of the form:
         #   { html: "", type: "", url: "" }
         # type will be one of [tab, bookmark, history, domain].
@@ -235,7 +234,7 @@
           result
         callback(results)

-    @filterPort.postMessage({ id: id, name: @name, query: query })
+    @filterPort.postMessage({ id: ++BackgroundCompleter.messageId, name: @name, query: query })

 extend BackgroundCompleter,
   #


beaglebone.coffee: Change version
to yocto-morty instead of yocto-jethro

We now switch to yocto morty

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@resin.io>

--- beaglebone.coffee
+++ beaglebone.coffee
@@ -39,7 +39,7 @@
 		machine: 'beaglebone'
 		image: 'resin-image-flasher'
 		fstype: 'resinos-img'
-		version: 'yocto-jethro'
+		version: 'yocto-morty'
 		deployArtifact: 'resin-image-flasher-beaglebone.resinos-img'
 		compressed: true


comment-out support for ancient firefox.  revisit this with proper feature detection asap.

--- client/Framework/components/upload/kdmultipartuploader.coffee
+++ client/Framework/components/upload/kdmultipartuploader.coffee
@@ -15,7 +15,7 @@
 "

   serializedToMultipart: (list) ->
-    (@makeMultipartItem i.name, i.value for i in list).join("")
+    (@makeMultipartItem i.name, i.value for i in list).join ""

   fileToMultipart: (callback) ->
     # {files, id} = @input
@@ -54,16 +54,19 @@
     body += @serializedToMultipart [name: "#{@id}-size", value: @file.size]
     @fileToMultipart (fileData) ->
       body += fileData
-      if xhr.sendAsBinary?  # Firefox 4 - 5
-        xhr.sendAsBinary body
-      else if Uint8Array?  # File API, Chrome / Firefox 6.
-        len = i = body.length
+      # TODO: review: better feature detection, commenting-out "the immediately below":
+      # if xhr.sendAsBinary?  # Firefox 4 - 5
+      #   xhr.sendAsBinary body
+      # else if Uint8Array?  # File API, Chrome / Firefox 6.
+      # / "the immediately below"

-        arrb = new ArrayBuffer len
-        ui8a = new Uint8Array arrb
-
-        ui8a[i] = body.charCodeAt(i) & 0xff  while i--
+      len = i = body.length

-        blob = new Blob [ui8a]
-        xhr.send blob
+      arrb = new ArrayBuffer len
+      ui8a = new Uint8Array arrb
+
+      ui8a[i] = body.charCodeAt(i) & 0xff  while i--
+
+      blob = new Blob [ui8a]
+      xhr.send blob
     return this


Don't globalRefresh when the window is blurred, and do refresh immediately when the window is later focused

--- SingularityUI/app/lib/router.coffee
+++ SingularityUI/app/lib/router.coffee
@@ -27,18 +27,30 @@
     globalRefresh()
     htmlClasses()

+windowBlurred = false
+
+$(window).on 'blur', ->
+    windowBlurred = true
+
+$(window).on 'focus', ->
+    windowBlurred = false
+    refresh()
+
 nav = ->
     if not app.views.navigationView?
         app.views.navigationView = new NavigationView
     app.views.navigationView.render()

 window.globalRefreshTimeout = undefined
-globalRefresh = =>
+globalRefresh = ->
     clearTimeout(window.globalRefreshTimeout) if window.globalRefreshTimeout
     window.globalRefreshTimeout = setInterval ->
-        if not $('body > .vex').length
-            app.views.current?.refresh?()
+        refresh()
     , 20 * 1000
+
+refresh = ->
+    if not $('body > .vex').length and not windowBlurred
+        app.views.current?.refresh?()

 class Router extends Backbone.Router


Update getDoc signature to match reality

--- services/real-time/app/coffee/WebsocketController.coffee
+++ services/real-time/app/coffee/WebsocketController.coffee
@@ -70,7 +70,7 @@
 				callback()
 			, WebsocketController.FLUSH_IF_EMPTY_DELAY

-	joinDoc: (client, doc_id, fromVersion = -1, callback = (error, doclines, version, ops) ->) ->
+	joinDoc: (client, doc_id, fromVersion = -1, callback = (error, doclines, version, ops, ranges) ->) ->
 		metrics.inc "editor.join-doc"
 		Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
 			return callback(error) if error?


Extract icon_class into reusable bit

Extract icon_class into reusable bit

--- core/app/backbone/views/react_install_extension_button.coffee
+++ core/app/backbone/views/react_install_extension_button.coffee
@@ -8,11 +8,17 @@
   safari: 'https://static.factlink.com/extension/firefox/factlink.safariextz'
   chrome: 'javascript:chrome.webstore.install()'

+icon_class_by_browser =
+  firefox: 'install-firefox'
+  safari: 'install-safari'
+  chrome: 'install-chrome'
+
 window.ReactInstallExtensionButton = React.createClass
   displayName: 'ReactInstallExtensionButton'
   render: ->
     browserName = determineBrowser()
     extension_link = extension_link_by_browser[browserName]
+    icon_class = icon_class_by_browser[browserName]
     extra_class = if @props.huge_button then 'button-huge' else null
     if document.documentElement.getAttribute('data-factlink-extension-loaded') != undefined
       _div [],
@@ -23,14 +29,14 @@
       _div [],
         _div ['visible-when-chrome'],
           _a ['button-success', extra_class, href: extension_link],
-            _span ['install-chrome']
+            _span [icon_class]
             'Install Factlink for ' + browserName.capitalize()
         _div ['visible-when-firefox'],
           _a ['button-success', extra_class, href: extension_link],
-            _span ['install-firefox']
+            _span [icon_class]
             'Install Factlink for ' + browserName.capitalize()
         _div ['visible-when-safari'],
           _a ['button-success', extra_class, href: extension_link],
-            _span ['install-safari']
+            _span [icon_class]
             'Install Factlink for ' + browserName.capitalize()


Properly call error callback in ajax utility

--- app/utils/ajax.coffee
+++ app/utils/ajax.coffee
@@ -143,7 +143,7 @@
           options.success.call(options.context, data, xhr.status, xhr)
         else
           reject(xhr)
-          options.error.call(data, xhr.status, xhr)
+          options.error.call(options.context, data, xhr.status, xhr)

     data = options.data
     if typeof(options.data) == "object" && (Ember.isNone(options.contentType) || options.contentType.match /application\/json/)


Hide header on subsequent search

Hide header on subsequent search

--- src/packages/command-panel/lib/command-panel-view.coffee
+++ src/packages/command-panel/lib/command-panel-view.coffee
@@ -127,6 +127,7 @@
   execute: (command=@escapedCommand()) ->
     @loadingMessage.show()
     @previewList.hide()
+    @previewHeader.hide()
     @errorMessages.empty()

     try


remove smartadmin caal to datepicker

remove smartadmin caal to datepicker

--- BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
+++ BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
@@ -160,15 +160,28 @@
 activateHidden = (hidden) ->
   hidden.addClass('focusable').attr('type', 'text')

+#ACTIVATE DATEPICKER
+activateDatepicker = (elements) ->
+  if $.fn.datepicker
+    elements.each ->
+      $this = $(this)
+      dataDateFormat = $this.data('dateformat') or 'dd.mm.yy'
+      $this.datepicker
+        dateFormat: dataDateFormat
+        prevText: ''
+        nextText: ''
+      $this = null
+      return
+
 #ACTIVATE FORM JS
 activateForm = (view, form) ->
-  runAllForms()
   activateSelect2(elements) if (elements = $(".select2", form)) && elements.length > 0
   activateOrchestraNodeChoice(elements) if (elements = $(".orchestra-node-choice", form)) && elements.length > 0
   activateColorPicker(elements) if (elements = $(".colorpicker", view.el)) && elements.length > 0
   activateHelper(elements) if (elements = $(".helper-block", form)) && elements.length > 0
   activateTinyMce(view, elements) if (elements = $("textarea.tinymce", form)) && elements.length > 0
   activateHidden(elements) if (elements = $("input[type='hidden'][required='required']", form)) && elements.length > 0
+  activateDatepicker(elements) if (elements = $(".datepicker", form)) && elements.length > 0
   $("[data-prototype]", form).each ->
     PO.formPrototypes.addPrototype $(@)
   loadExtendView(view, 'contentTypeSelector') if (elements = $(".contentTypeSelector", form)) && elements.length > 0


make sure due date is used as reference for making open date.  the lack of a route query on the mini editor was making plans being copied to tomorrow be confused about the open auto date

--- tutor/src/components/task-plan/initialize-editing.coffee
+++ tutor/src/components/task-plan/initialize-editing.coffee
@@ -14,17 +14,15 @@
   else
     moment(now).add(1, 'day').format(TimeHelper.ISO_DATE_FORMAT)

-getQueriedOpensAt = (taskPlanId, termStart) ->
+getQueriedOpensAt = (planId, dueAt, termStart) ->
   {opens_at} = Router.currentQuery() # attempt to read the open date from query params
-  isNewPlan = TaskPlanStore.isNew(taskPlanId)
+  isNewPlan = TaskPlanStore.isNew(planId)
   opensAt = if opens_at and isNewPlan then TimeHelper.getMomentPreserveDate(opens_at)
   if not opensAt
     # default open date is tomorrow
     opensAt = getOpensAtDefault(termStart)

-  # if there is a queried due date, make sure it's not the same as the open date
-  dueAt = getQueriedDueAt()
-
+  # if there is a current due date, make sure it's not the same as the open date
   if dueAt?
     dueAtMoment = TimeHelper.getMomentPreserveDate(dueAt)
     # there's a corner case is certain timezones where isAfter doesn't quite cut it
@@ -35,9 +33,12 @@

   opensAt

-getQueriedDueAt = ->
+getCurrentDueAt = (planId) ->
   {due_at} = Router.currentQuery() # attempt to read the due date from query params
-  TimeHelper.getMomentPreserveDate(due_at).format(TimeHelper.ISO_DATE_FORMAT) if due_at
+  if due_at?
+    TimeHelper.getMomentPreserveDate(due_at).format(TimeHelper.ISO_DATE_FORMAT)
+  else if TaskingStore.hasTasking(planId)
+    TaskingStore.getFirstDueDate(planId)

 getTaskPlanOpensAt = (planId) ->
   firstDueAt = _.first(TaskPlanStore.get(planId)?.tasking_plans)?.due_at
@@ -46,9 +47,9 @@

 setPeriodDefaults = (courseId, planId, term) ->
   unless TaskingStore.hasTasking(planId)
-    if TaskPlanStore.isNew(planId) and not TaskingStore.hasTasking(planId)
-      due_date = getQueriedDueAt() or getTaskPlanOpensAt(planId)
-      TaskingActions.create(planId, {open_date: getQueriedOpensAt(planId, term.start), due_date})
+    if TaskPlanStore.isNew(planId)
+      due_date = getCurrentDueAt(planId) or getTaskPlanOpensAt(planId)
+      TaskingActions.create(planId, {open_date: getQueriedOpensAt(planId, due_date, term.start), due_date})
     else
       {tasking_plans} = TaskPlanStore.get(planId)
       TaskingActions.loadTaskings(planId, tasking_plans)
@@ -68,9 +69,10 @@
   TaskingActions.loadDefaults(courseId, courseDefaults, periods)

-module.exports = (taskPlanId, courseId, term) ->
-  TaskingActions.loadTaskToCourse(taskPlanId, courseId)
+module.exports = (planId, courseId, term) ->
+  courseTimezone = CourseStore.getTimezone(courseId)
+  TaskingActions.loadTaskToCourse(planId, courseId)
   loadCourseDefaults(courseId)

   #set the periods defaults only after the timezone has been synced
-  return setPeriodDefaults(courseId, taskPlanId, term)
+  return setPeriodDefaults(courseId, planId, term)


Use should(foo).be.null to test for null

--- Src/coffeescript/cql-execution/test/elm/interval/test.coffee
+++ Src/coffeescript/cql-execution/test/elm/interval/test.coffee
@@ -761,7 +761,7 @@
     y.equals(x).should.be.true

   it 'should properly calculate before/after unions', ->
-    (@intBeforeUnion.exec(@ctx) == null).should.be.true
+    should(@intBeforeUnion.exec(@ctx)).be.null

   it 'should properly calculate meets unions', ->
     x = @intFullInterval.exec(@ctx)
@@ -821,7 +821,7 @@
     y.equals(x).should.be.true

   it 'should properly calculate before/after unions', ->
-    (@dateTimeBeforeUnion.exec(@ctx) == null).should.be.true
+    should(@dateTimeBeforeUnion.exec(@ctx)).be.null

   it 'should properly calculate meets unions', ->
     x = @dateTimeFullInterval.exec(@ctx)
@@ -856,7 +856,7 @@
     setup @, data

    it 'should properly calculate sameAs except', ->
-    (@intSameAsExcept.exec(@ctx) == null).should.be.true
+    should(@intSameAsExcept.exec(@ctx)).be.null

   it 'should properly calculate before/after except', ->
     @intBeforeExcept.exec(@ctx).should.eql new Interval(0,4)
@@ -872,13 +872,13 @@
     y.equals(x).should.be.true

   it 'should properly calculate begins/begun by except', ->
-    (@intBeginsExcept.exec(@ctx) == null).should.be.true
+    should(@intBeginsExcept.exec(@ctx)).be.null

   it 'should properly calculate includes/included by except', ->
-    (@intDuringExcept.exec(@ctx) == null).should.be.true
+    should(@intDuringExcept.exec(@ctx)).be.null

   it 'should properly calculate ends/ended by except', ->
-    (@intEndsExcept.exec(@ctx) == null).should.be.true
+    should(@intEndsExcept.exec(@ctx)).be.null

 # TODO
 # it 'should properly handle imprecision', ->
@@ -887,8 +887,8 @@
   @beforeEach ->
     setup @, data

-   it 'should properly calculate sameAs except', ->
-    (@dateTimeSameAsExcept.exec(@ctx) == null).should.be.true
+  it 'should properly calculate sameAs except', ->
+    should(@dateTimeSameAsExcept.exec(@ctx)).be.null

   it 'should properly calculate before/after except', ->
     @dateTimeBeforeExcept.exec(@ctx).should.eql new Interval(new DateTime(2012, 1, 1, 0, 0, 0, 0), new DateTime(2012, 4, 1, 0, 0, 0, 0))
@@ -904,13 +904,13 @@
     y.equals(x).should.be.true

   it 'should properly calculate begins/begun by except', ->
-    (@dateTimeBeginsExcept.exec(@ctx) == null).should.be.true
+    should(@dateTimeBeginsExcept.exec(@ctx)).be.null

   it 'should properly calculate includes/included by except', ->
-    (@dateTimeDuringExcept.exec(@ctx) == null).should.be.true
+    should(@dateTimeDuringExcept.exec(@ctx)).be.null

   it 'should properly calculate ends/ended by except', ->
-    (@dateTimeEndsExcept.exec(@ctx) == null).should.be.true
+    should(@dateTimeEndsExcept.exec(@ctx)).be.null

 # TODO
 # it 'should properly handle imprecision', ->
@@ -925,7 +925,7 @@
     x.equals(y).should.be.true

   it 'should properly calculate before/after intersect', ->
-    (@intBeforeIntersect.exec(@ctx) == null).should.be.true
+    should(@intBeforeIntersect.exec(@ctx)).be.null

   it 'should properly calculate meets intersect', ->
     x = @intMeetsInterval.exec(@ctx)
@@ -962,7 +962,7 @@
     x.equals(y).should.be.true

   it 'should properly calculate before/after intersect', ->
-    (@dateTimeBeforeIntersect.exec(@ctx) == null).should.be.true
+    should(@dateTimeBeforeIntersect.exec(@ctx)).be.null

   it 'should properly calculate meets intersect', ->
     x = @dateTimeMeetsInterval.exec(@ctx)


Usage of eval() is forbidden by FirefoxOS CSP

In privileged and certified FirefoxOS apps, the Content Security Policy forbids the usage of potentially hazardous functions such as eval(). This is a quick fix for the only occurrence of eval() in QuoJS.

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -139,8 +139,8 @@
         diff = parseInt(GESTURE.initial_angle - angle, 10)
         if Math.abs(diff) > 20 or GESTURE.angle_difference isnt 0
             i = 0
-            symbol = if GESTURE.angle_difference < 0 then "-" else "+"
-            eval "diff " + symbol + "= 180;" while Math.abs(diff - GESTURE.angle_difference) > 90 and i++ < 10
+            symbol = if GESTURE.angle_difference < 0 then -1 else 1
+            diff += 180 * symbol while Math.abs(diff - GESTURE.angle_difference) > 90 and i++ < 10
             GESTURE.angle_difference = parseInt(diff, 10)
             _trigger "rotating", angle: GESTURE.angle_difference


SocialWorker: Tests: JGroup: replace daisy with async.series

--- workers/social/lib/social/models/group/index.test.coffee
+++ workers/social/lib/social/models/group/index.test.coffee
@@ -5,7 +5,6 @@
 JApiToken                     = require '../apitoken'
 JInvitation                   = require '../invitation'
 { async
-  daisy
   expect
   expectRelation
   withDummyClient
@@ -80,14 +79,14 @@

           queue = [

-            ->
+            (next) ->
               # making sure client is a member of the group before trying to kick
               group.searchMembers adminClient, userFormData.username, {}, (err, members) ->
                 expect(err).to.not.exist
                 expect(members.length).to.be.equal 1
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting session to exist before kicking member
               params =
                 username  : account.profile.nickname
@@ -97,22 +96,22 @@
                 expect(err).to.not.exist
                 expect(data).to.exist
                 expect(data).to.be.an 'object'
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting no error from kick member request
               group.kickMember adminClient, account._id, (err) ->
                 expect(err).to.not.exist
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # admin client search for kicked member within group member
               group.searchMembers adminClient, userFormData.username, {}, (err, members) ->
                 expect(err).to.not.exist
                 expect(members.length).to.be.equal 0
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting session to be deleted after kicking member
               params =
                 username  : account.profile.nickname
@@ -121,33 +120,30 @@
               JSession.one params, (err, data) ->
                 expect(err).to.not.exist
                 expect(data).to.not.exist
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # we cannot add blocked user to group
               group.approveMember account, (err) ->
                 expect(err).to.exist
                 expect(err.message).to.be.equal 'This account is blocked'
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # we should be able to unblock the member
               group.unblockMember adminClient, account.getId(), (err) ->
                 expect(err).to.not.exist
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # unblocked member can be added again
               group.approveMember account, (err) ->
                 expect(err).to.not.exist
-                queue.next()
-
-            ->
-              done()
+                next()

           ]

-          daisy queue
+          async.series queue, done

   describe '#searchMembers()', ->
@@ -163,25 +159,23 @@

           queue = [

-            ->
+            (next) ->
               # fetching koding group
               JGroup.one { slug : 'koding' }, (err, group_) ->
                 expect(err).to.not.exist
                 group = group_
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting to not be able to fetch members data
               group.searchMembers client, username, {}, (err, members) ->
                 expect(err?.message).to.be.equal 'Access denied'
                 expect(members).to.not.exist
-                queue.next()
-
-            -> done()
+                next()

           ]

-          daisy queue
+          async.series queue, done

     describe 'if username is registered', ->
@@ -193,26 +187,24 @@

           queue = [

-            ->
+            (next) ->
               # fetching group
               JGroup.one { slug : 'koding' }, (err, group_) ->
                 expect(err).to.not.exist
                 group = group_
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting to find newly registered member
               group.searchMembers client, userFormData.username, {}, (err, members) ->
                 expect(err).to.not.exist
                 expect(members.length).to.be.equal 1
                 expect(members[0].profile.nickname).to.be.equal userFormData.username
-                queue.next()
-
-            -> done()
+                next()

           ]

-          daisy queue
+          async.series queue, done

       describe 'when group is not koding', (done) ->
@@ -246,25 +238,23 @@

             queue = [

-              ->
+              (next) ->
                 # expecting new member client to be able fetch self data
                 group.searchMembers client, userFormData.username, {}, (err, members) ->
                   expect(err).to.not.exist
                   expect(members[0].profile.nickname).to.be.equal userFormData.username
-                  queue.next()
-
-              ->
+                  next()
+
+              (next) ->
                 # expecting admin to be able to fetch new member's data
                 group.searchMembers adminClient, userFormData.username, {}, (err, members) ->
                   expect(err).to.not.exist
                   expect(members[0].profile.nickname).to.be.equal userFormData.username
-                  queue.next()
-
-              -> done()
+                  next()

             ]

-            daisy queue
+            async.series queue, done

   describe '#isInAllowedDomain()', ->
@@ -332,7 +322,7 @@

           queue = [

-            ->
+            (next) ->
               # expecting group to be created successfully
               JGroup.create client, groupData, account, (err, group_) ->
                 expect(err)                  .to.not.exist
@@ -341,9 +331,9 @@
                 expect(group.title)          .to.be.equal groupTitle
                 expect(group.visibility)     .to.be.equal groupData.visibility
                 expect(group.allowedDomains) .to.include groupData.allowedDomains[0]
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting owner account and group relationship to be created
               params =
                 $and : [
@@ -354,9 +344,9 @@

               # expecting relationship to be created
               expectRelation.toExist params, ->
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting account to be saved as a member
               params =
                 $and : [
@@ -366,13 +356,11 @@
                 ]

               expectRelation.toExist params, ->
-                queue.next()
-
-            -> done()
+                next()

           ]

-          daisy queue
+          async.series queue, done

     describe 'when group data is not valid', ->
@@ -388,28 +376,26 @@

           queue = [

-            ->
+            (next) ->
               # expecting error for 'koding' slug
               groupData.slug = 'koding'

               expectedError = 'The slug koding is not available.'
               JGroup.create client, groupData, account, (err, data) ->
                 expect(err?.message).to.be.equal expectedError
-                queue.next()
-
-            ->
+                next()
+
+            (next) ->
               # expecting validaton error when slug is empty
               groupData.slug = ''

               JGroup.create client, groupData, account, (err, data) ->
                 expect(err).to.exist
-                queue.next()
-
-            -> done()
+                next()

           ]

-          daisy queue
+          async.series queue, done

       it 'should pass error if slug is in use', (done) ->


Remove unnecessary util class

Remove unnecessary util class

--- annotran/static/scripts/annotator/plugin/substitution.coffee
+++ annotran/static/scripts/annotator/plugin/substitution.coffee
@@ -1,7 +1,6 @@
 Annotator = require('annotator')
 $ = Annotator.$
 xpathRange = Annotator.Range
-Util = Annotator.Util

 # This plugin implements the UI code for selecting sentences by clicking
 module.exports = class Substitution extends Annotator.Plugin


locator filter on keyup

locator filter on keyup

--- app/assets/javascripts/ab_admin/components/locator.js.coffee
+++ app/assets/javascripts/ab_admin/components/locator.js.coffee
@@ -31,7 +31,7 @@
       $input_from = $cont.find('textarea:disabled')
       window.google_t($input_from.val(), $input_to, self.from_locale, self.to_locale)

-    $('.filter_field').change ->
+    $('.filter_field').keyup ->
       $el = $(this)
       ind = $el.closest('th').index() + 1
       text = $el.val().replace(/'/, '\'')


記録したあとflashが表示されるように修正

記録したあとflashが表示されるように修正

--- app/assets/javascripts/v3/base/components/AnnFlash.js.coffee
+++ app/assets/javascripts/v3/base/components/AnnFlash.js.coffee
@@ -10,7 +10,7 @@

   data: ->
     type: gon.flash.type || "notice"
-    message: gon.flash.message || ""
+    message: gon.flash.body || ""

   computed:
     show: ->


init properly config file

init properly config file

with devces array set in the config file

--- config.coffee
+++ config.coffee
@@ -10,12 +10,12 @@
 fs.ensureFileSync configPath

 if fs.readFileSync(configPath).toString() is ''
-    fs.writeFileSync configPath, '{ "devices": {} }'
+    fs.writeFileSync configPath, JSON.stringify devices: [], null, 2

 module.exports =
     dir: defaultDir
     dbPath: path.join defaultDir, 'db'
-    config: require configPath or {}
+    config: require configPath or devices: []

     getConfig: (deviceName) ->
         deviceName = @getDeviceName() unless deviceName?


Dont need to check if the timeout is cleared

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -206,8 +206,8 @@

   # Private: Gets called when the content has been modified
   contentsModified: =>
-    clearTimeout(@delayTimeout) if @delayTimeout
     delay = atom.config.get('autocomplete-plus.autoActivationDelay')
+    clearTimeout(@delayTimeout)
     @delayTimeout = setTimeout(@runAutocompletion, delay)

   # Private: Gets called when the cursor has moved. Cancels the autocompletion if


Deleted some plugins and stuff.

Deleted some plugins and stuff.

--- atom/atom/config.cson
+++ atom/atom/config.cson
@@ -60,7 +60,6 @@
       "autocomplete-bibtex"
       "wrap-guide"
       "whitespace"
-      "tabs"
     ]
     ignoredNames: [
       ".git"
@@ -77,7 +76,7 @@
   editor:
     autoIndentOnPaste: false
     fontFamily: "Hack"
-    fontSize: 12
+    fontSize: 13
     invisibles:
       space: "•"
       tab: "›"
@@ -120,13 +119,13 @@
     executablePath: "/usr/local/bin/jscs"
     onlyConfig: true
   "linter-jshint":
+    disableWhenNoJshintrcFileInPath: true
     jshintExecutablePath: "/usr/local/bin/jshint"
   "linter-scss-lint":
     executablePath: "/usr/local/bin/scss-lint"
   "linter-tidy":
     tidyExecutablePath: "/usr/local/bin/tidy5"
-  "linter-ui-default":
-    showPanel: true
+  "linter-ui-default": {}
   "markdown-preview":
     useGitHubStyle: true
   "markdown-preview-plus":
@@ -145,8 +144,7 @@
     locked: true
     silent: true
   "one-dark-ui":
-    fontSize: 11
-    layoutMode: "Compact"
+    fontSize: 14
   "one-light-ui":
     fontSize: 10
     layoutMode: "Compact"


Kernel: Make linter happier

Kernel: Make linter happier

--- lib/kernel.coffee
+++ lib/kernel.coffee
@@ -12,9 +12,9 @@
 module.exports =
 class Kernel
     constructor: (@kernelSpec, @grammar, @config, @configPath, @onlyConnect = false) ->
-        console.log "Kernel spec:", @kernelSpec
-        console.log "Kernel configuration:", @config
-        console.log "Kernel configuration file path:", @configPath
+        console.log 'Kernel spec:', @kernelSpec
+        console.log 'Kernel configuration:', @config
+        console.log 'Kernel configuration file path:', @configPath
         @language = @kernelSpec.grammarLanguage
         @kernelName = @kernelSpec.display_name
         @executionCallbacks = {}
@@ -32,24 +32,24 @@
             atom.notifications.addInfo 'Using custom kernel connection:',
                 detail: @configPath
         else
-            if @language == 'python' and not @kernelSpec.argv?
-                commandString = "ipython"
+            if @language is 'python' and not @kernelSpec.argv?
+                commandString = 'ipython'
                 args = [
-                    "kernel",
-                    "--no-secure",
+                    'kernel',
+                    '--no-secure',
                     "--hb=#{@config.hb_port}",
                     "--control=#{@config.control_port}",
                     "--shell=#{@config.shell_port}",
                     "--stdin=#{@config.stdin_port}",
                     "--iopub=#{@config.iopub_port}",
-                    "--colors=NoColor"
+                    '--colors=NoColor'
                     ]

             else
                 commandString = _.head(@kernelSpec.argv)
                 args = _.tail(@kernelSpec.argv)
                 args = _.map args, (arg) =>
-                    if arg == '{connection_file}'
+                    if arg is '{connection_file}'
                         return @configPath
                     else
                         return arg
@@ -104,36 +104,36 @@
         @ioSocket.connect(address + @config.iopub_port)
         @ioSocket.subscribe('')

-        @shellSocket.on 'message', @onShellMessage.bind(this)
-        @ioSocket.on 'message', @onIOMessage.bind(this)
-
-        @shellSocket.on 'connect', () -> console.log "shellSocket connected"
-        @controlSocket.on 'connect', () -> console.log "controlSocket connected"
-        @ioSocket.on 'connect', () -> console.log "ioSocket connected"
+        @shellSocket.on 'message', @onShellMessage.bind @
+        @ioSocket.on 'message', @onIOMessage.bind @
+
+        @shellSocket.on 'connect', () -> console.log 'shellSocket connected'
+        @controlSocket.on 'connect', () -> console.log 'controlSocket connected'
+        @ioSocket.on 'connect', () -> console.log 'ioSocket connected'

         try
             @shellSocket.monitor()
             @controlSocket.monitor()
             @ioSocket.monitor()
         catch err
-            console.log "Kernel:", err
+            console.error 'Kernel:', err

     interrupt: ->
-        console.log "sending SIGINT"
+        console.log 'sending SIGINT'
         unless @onlyConnect
             @kernelProcess.kill('SIGINT')

     # onResults is a callback that may be called multiple times
     # as results come in from the kernel
     _execute: (code, requestId, onResults) ->
-        console.log "sending execute"
+        console.log 'sending execute'

         header =
                 msg_id: requestId,
-                username: "",
-                session: "00000000-0000-0000-0000-000000000000",
-                msg_type: "execute_request",
-                version: "5.0"
+                username: '',
+                session: '00000000-0000-0000-0000-000000000000',
+                msg_type: 'execute_request',
+                version: '5.0'

         content =
                 code: code
@@ -151,26 +151,26 @@
         @shellSocket.send new jmp.Message message

     execute: (code, onResults) ->
-        requestId = "execute_" + uuid.v4()
+        requestId = 'execute_' + uuid.v4()
         @_execute(code, requestId, onResults)

     executeWatch: (code, onResults) ->
-        requestId = "watch_" + uuid.v4()
+        requestId = 'watch_' + uuid.v4()
         @_execute(code, requestId, onResults)

     complete: (code, onResults) ->
-        console.log "sending completion"
-
-        requestId = "complete_" + uuid.v4()
+        console.log 'sending completion'
+
+        requestId = 'complete_' + uuid.v4()

         column = code.length

         header =
                 msg_id: requestId
-                username: ""
-                session: "00000000-0000-0000-0000-000000000000"
-                msg_type: "complete_request"
-                version: "5.0"
+                username: ''
+                session: '00000000-0000-0000-0000-000000000000'
+                msg_type: 'complete_request'
+                version: '5.0'

         content =
                 code: code
@@ -188,16 +188,16 @@

     inspect: (code, cursor_pos, onResults) ->
-        console.log "sending inspect"
-
-        requestId = "inspect_" + uuid.v4()
+        console.log 'sending inspect'
+
+        requestId = 'inspect_' + uuid.v4()

         header =
                 msg_id: requestId
-                username: ""
-                session: "00000000-0000-0000-0000-000000000000"
-                msg_type: "inspect_request"
-                version: "5.0"
+                username: ''
+                session: '00000000-0000-0000-0000-000000000000'
+                msg_type: 'inspect_request'
+                version: '5.0'

         content =
                 code: code
@@ -218,7 +218,7 @@

     onShellMessage: (message) ->
-        console.log "shell message:", message
+        console.log 'shell message:', message

         unless @_isValidMessage message
             return
@@ -260,7 +260,7 @@

     onIOMessage: (message) ->
-        console.log "IO message:", message
+        console.log 'IO message:', message

         unless @_isValidMessage message
             return
@@ -283,6 +283,54 @@
         unless callback?
             return

+        result = @_parseIOMessage message
+
+        if result?
+            callback result
+
+
+    _isValidMessage: (message) ->
+        unless message?
+            console.log 'Invalid message: null'
+            return false
+
+        unless message.content?
+            console.log 'Invalid message: Missing content'
+            return false
+
+        if message.content.execution_state is 'starting'
+            # Kernels send a starting status message with an empty parent_header
+            console.log 'Dropped starting status IO message'
+            return false
+
+        unless message.parent_header?
+            console.log 'Invalid message: Missing parent_header'
+            return false
+
+        unless message.parent_header.msg_id?
+            console.log 'Invalid message: Missing parent_header.msg_id'
+            return false
+
+        unless message.parent_header.msg_type?
+            console.log 'Invalid message: Missing parent_header.msg_type'
+            return false
+
+        unless message.header?
+            console.log 'Invalid message: Missing header'
+            return false
+
+        unless message.header.msg_id?
+            console.log 'Invalid message: Missing header.msg_id'
+            return false
+
+        unless message.header.msg_type?
+            console.log 'Invalid message: Missing header.msg_type'
+            return false
+
+        return true
+
+
+    _parseIOMessage: (message) ->
         result = @_parseDisplayIOMessage message

         unless result?
@@ -294,49 +342,7 @@
         unless result?
             result = @_parseStreamIOMessage message

-        if result?
-            callback result
-
-
-    _isValidMessage: (message) ->
-        unless message?
-            console.log "Invalid message: null"
-            return false
-
-        unless message.content?
-            console.log "Invalid message: Missing content"
-            return false
-
-        if message.content.execution_state is 'starting'
-            # Kernels send a starting status message with an empty parent_header
-            console.log "Dropped starting status IO message"
-            return false
-
-        unless message.parent_header?
-            console.log "Invalid message: Missing parent_header"
-            return false
-
-        unless message.parent_header.msg_id?
-            console.log "Invalid message: Missing parent_header.msg_id"
-            return false
-
-        unless message.parent_header.msg_type?
-            console.log "Invalid message: Missing parent_header.msg_type"
-            return false
-
-        unless message.header?
-            console.log "Invalid message: Missing header"
-            return false
-
-        unless message.header.msg_id?
-            console.log "Invalid message: Missing header.msg_id"
-            return false
-
-        unless message.header.msg_type?
-            console.log "Invalid message: Missing header.msg_type"
-            return false
-
-        return true
+        return result

     _parseDisplayIOMessage: (message) ->
@@ -356,29 +362,13 @@

     _parseDataMime: (data) ->
-        if data?
-            imageMimes = Object.getOwnPropertyNames(data).filter (mime) ->
-                return mime.startsWith 'image/'
-
-            if data.hasOwnProperty 'text/html'
-                mime = 'text/html'
-
-            else if data.hasOwnProperty 'image/svg+xml'
-                mime = 'image/svg+xml'
-
-            else if not (imageMimes.length is 0)
-                mime = imageMimes[0]
-
-            else if data.hasOwnProperty 'text/markdown'
-                mime = 'text/markdown'
-            else if data.hasOwnProperty 'application/pdf'
-                mime = 'application/pdf'
-
-            else if data.hasOwnProperty 'text/latex'
-                mime = 'text/latex'
-
-            else if data.hasOwnProperty 'text/plain'
-                mime = 'text/plain'
+        unless data?
+            return null
+
+        mime = @_getMimeType data
+
+        unless mime?
+            return null

         if mime is 'text/plain'
             result =
@@ -388,7 +378,7 @@
                 stream: 'pyout'
             result.data['text/plain'] = result.data['text/plain'].trim()

-        else if mime?
+        else
             result =
                 data:   {}
                 type:   mime
@@ -398,10 +388,38 @@
         return result

+    _getMimeType: (data) ->
+        imageMimes = Object.getOwnPropertyNames(data).filter (mime) ->
+            return mime.startsWith 'image/'
+
+        if data.hasOwnProperty 'text/html'
+            mime = 'text/html'
+
+        else if data.hasOwnProperty 'image/svg+xml'
+            mime = 'image/svg+xml'
+
+        else if not (imageMimes.length is 0)
+            mime = imageMimes[0]
+
+        else if data.hasOwnProperty 'text/markdown'
+            mime = 'text/markdown'
+
+        else if data.hasOwnProperty 'application/pdf'
+            mime = 'application/pdf'
+
+        else if data.hasOwnProperty 'text/latex'
+            mime = 'text/latex'
+
+        else if data.hasOwnProperty 'text/plain'
+            mime = 'text/plain'
+
+        return mime
+
+
     _parseErrorIOMessage: (message) ->
         msg_type = message.header.msg_type

-        if msg_type is 'error' or msg_type == 'pyerr'
+        if msg_type is 'error' or msg_type is 'pyerr'
             result = @_parseErrorMessage message

         return result
@@ -459,16 +477,16 @@

     destroy: ->
-        console.log "sending shutdown"
+        console.log 'sending shutdown'

         requestId = uuid.v4()

         header =
                 msg_id: requestId,
-                username: "",
+                username: '',
                 session: 0,
-                msg_type: "shutdown_request",
-                version: "5.0"
+                msg_type: 'shutdown_request',
+                version: '5.0'

         content =
                 restart: false
@@ -483,8 +501,10 @@
         @ioSocket.close()

         if @onlyConnect
+            detail = 'Shutdown request sent to custom kernel connection in ' +
+                @configPath
             atom.notifications.addInfo 'Custom kernel connection:',
-                detail: "Shutdown request sent to custom kernel connection in #{@configPath}"
+                detail: detail

         unless @onlyConnect
-            @kernelProcess.kill('SIGKILL')
+            @kernelProcess.kill 'SIGKILL'


terminal: make outputbox content editable element

Browser handles content editable elements as input elements which brings
selection and copy-paste behavior.

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -55,7 +55,10 @@

     @updateSize()

-    @outputbox = $(document.createElement("div"))
+    outputboxElement = document.createElement "div"
+    @outputbox = $ outputboxElement
+    @outputbox.attr "contenteditable", yes
+    @outputbox.attr "spellcheck", off
     @outputbox.css "cursor", "text"
     @outputbox.append @measurebox.getDomElement()


subnav-path breadcrumb works on all pages

--- client/code/view/subnav-path.coffee
+++ client/code/view/subnav-path.coffee
@@ -1,4 +1,12 @@
 class Cu.View.Subnav extends Backbone.View
+  render: ->
+    @$el.html("""
+

+

+          #{@options.text}
+

+
""")
+    @

 class Cu.View.DataHubNav extends Backbone.View
   events:
@@ -109,6 +117,7 @@

 class Cu.View.DatasetNav extends Backbone.View
+  # This view should be passed a dataset model!
   render: ->
     @$el.html("""

@@ -121,12 +130,13 @@
           /

-          [dataset.displayName]
+          #{@model.get 'displayName'}

""")
     @

 class Cu.View.DatasetSettingsNav extends Backbone.View
+  # This view should be passed a dataset model!
   render: ->
     @$el.html("""

@@ -139,7 +149,7 @@
           /

-          [dataset.displayName]
+          #{@model.get 'displayName'}

           /
@@ -151,6 +161,7 @@
     @

 class Cu.View.ViewNav extends Backbone.View
+  # This view should be passed a view model!
   render: ->
     @$el.html("""

@@ -163,13 +174,13 @@
           /

-          [dataset.displayName]
+          #{@model.get('plugsInTo').get 'displayName'}

           /

-          [view.displayName]
+          #{@model.get 'displayName'}

""")
     @


login: factor login logic to mainController

Also, temporarily disable the hack that is causing the page to sometimes reload

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -134,7 +134,6 @@
       cssClass : "login-form"
       testPath : "login-form"
       callback : (formData)=>
-        formData.clientId = $.cookie('clientId')
         @doLogin formData

     @registerForm = new RegisterInlineForm
@@ -164,7 +163,6 @@
     @resetForm = new ResetInlineForm
       cssClass : "login-form"
       callback : (formData)=>
-        formData.clientId = $.cookie('clientId')
         @doReset formData

     @headBanner = new KDCustomHTMLView
@@ -250,12 +248,12 @@
     </footer>
     """

-  doReset:({recoveryToken, password, clientId})->
+  doReset:({recoveryToken, password})->
     KD.remote.api.JPasswordRecovery.resetPassword recoveryToken, password, (err, username)=>
       @resetForm.button.hideLoader()
       @resetForm.reset()
       @headBanner.hide()
-      @doLogin {username, password, clientId}
+      @doLogin {username, password}

   doRecover:(formData)->
     KD.remote.api.JPasswordRecovery.recoverPassword formData['username-or-email'], (err)=>
@@ -332,10 +330,8 @@
         , 1000

   doLogin:(credentials)->
-    (KD.getSingleton 'mainController').isLoggingIn on
-    credentials.username = credentials.username.toLowerCase().trim()
-    KD.remote.api.JUser.login credentials, @bound 'afterLoginCallback'
-
+    (KD.getSingleton 'mainController').handleLogin credentials, @bound 'afterLoginCallback'
+
   runExternal = (token)->
     KD.getSingleton("kiteController").run
       kiteName        : "externals"
@@ -355,11 +351,9 @@
       showError err
       @loginForm.resetDecoration()
     else
-      {account, replacementToken} = params
-      $.cookie 'clientId', replacementToken  if replacementToken
+      {account} = params
       mainController = KD.getSingleton('mainController')
       mainView       = mainController.mainViewController.getView()
-      mainController.accountChanged account
       mainView.show()
       mainView.$().css "opacity", 1


Sort entries by date

Sort entries by date

--- client/source/models/entries.coffee
+++ client/source/models/entries.coffee
@@ -8,3 +8,6 @@
   fetchByBucket: (bucketId) ->
     @url += "?#{$.param(bucketId: bucketId)}"
     @fetch()
+
+  comparator: (entry) ->
+    - new Date(entry.get('publishDate')).getTime()


Fixed dots not adjusting their bracketing color on goal screen

--- client/src/Screen/GoalScreen.coffee
+++ client/src/Screen/GoalScreen.coffee
@@ -83,7 +83,7 @@

   getNextBracketColor: (bracketsCounter) ->
-    brightnessPercent = 70-((bracketsCounter*2)/@numberDots)*20
+    brightnessPercent = 80-((bracketsCounter*2)/@numberDots)*50
     return "hsl(120, 50%, #{brightnessPercent}%)"

@@ -187,7 +187,6 @@
           if($(dot).attr('data-bracket') is 'right') then counter--
           if(counter == 0)
             matchingRightBracket = dot
-            console.log "Malfoy"
             break
         # Remove the left bracket
         $(element).attr('data-bracket','none')
@@ -263,15 +262,12 @@
       thisReference = this
       $('#added-goal li.dice[data-index='+index+']').remove()
       @cleanUpBrackets()
+      @pairUpBrackets()
       $(html).appendTo("#notadded-goal").bind 'click', (event) ->
         thisReference.addDiceToGoal($(this).data('index'));

   cleanUpBrackets: () ->
-    @cleanUpRedundantDots()
-    #@pairUpBrackets()
-
-  cleanUpRedundantDots: () ->
     thisReference = this
     for d in $('#added-goal li')
       twoDotsCase = ($(d).prev().attr('data-bracket') is 'none') and ($(d).attr('data-bracket') is 'none')
@@ -279,6 +275,8 @@
       dotRightBrkCase = (($(d).prev().attr('data-bracket') is 'none') and ($(d).attr('data-bracket') is 'right'))
       leftBkrRightBrkCase1 = (($(d).prev().attr('data-bracket') is 'left') and ($(d).attr('data-bracket') is 'right'))
       leftBkrRightBrkCase2 = (($(d).prev().attr('data-bracket') is 'right') and ($(d).attr('data-bracket') is 'left'))
+
+      if $(d).attr('data-bracket') is 'none' then $(d).css('color', '')

       if (twoDotsCase || leftBrkDotCase || dotRightBrkCase || leftBkrRightBrkCase1 || leftBkrRightBrkCase2)
         # Adjust the left dot accordingly (since we always delete the right ... see below)
@@ -303,23 +301,4 @@
         # Now try and clean up again
         @cleanUpBrackets()

-
-
-
-
-
-    ### previousDot = $('li.dice[data-index='+index+']').prev()
-    nextDot     = $('li.dice[data-index='+index+']').next()
-    $(nextDot).remove()###
-    # Remove the dice from the goal
-    ###if($(previousDot).data('bracket') is 'left' and $(nextDot).data('bracket') is 'right')
-      $(previousDot).attr('data-bracket', 'none')
-      $(previousDot).remove()
-      @numberDots--
-    else
-      $(previousDot).attr('data-bracket', nextDot.data('bracket'))###
-    ###if(@numberInGoal == 0)
-      $('#added-goal li.dot.first').remove()
-      @numberDots--###
-
-
+


Allow the command configure for rubocop to have arguments.

With this change, the rubocop command can be set to something like:

```
/Users/amuino/.rvm/bin/rvm-exec ruby-2.2.1@rubocop rubocop
```

The code was inspired by linter-rubocop, which allows the same config.
https://github.com/AtomLinter/linter-rubocop/blob/master/index.coffee#L22-L23

--- lib/rubocop-auto-correct.coffee
+++ lib/rubocop-auto-correct.coffee
@@ -117,8 +117,9 @@
             atom.notifications.addSuccess(message) if notification

   autoCorrectFile: (filePath)  ->
-    command = atom.config.get('rubocop-auto-correct.rubocopCommandPath')
-    args = ['-a', filePath]
+    command = atom.config.get('rubocop-auto-correct.rubocopCommandPath').split(/\s+/).filter((i) -> i).concat(['-a', filePath])
+    args = command[1..]
+    command = command[0]
     debug = atom.config.get('rubocop-auto-correct.debugMode')
     notification = atom.config.get('rubocop-auto-correct.notification')
     stdout = (output) ->


cache variable

cache variable

--- lib/parser.coffee
+++ lib/parser.coffee
@@ -419,13 +419,14 @@
     baseNodes = @parse()

     recurse = (nodeArray, keys, level = 0) ->
-      return (_.sortBy nodeArray, (node) -> node[keys[level]].basicInfo) if level is keys.length - 1
-
-      uniqueKeys = _.sortBy _.uniq _.pluck (_.pluck nodeArray, keys[level]), 'basicInfo'
+      key = keys[level]
+      return (_.sortBy nodeArray, (node) -> node[key].basicInfo) if level is keys.length - 1
+
+      uniqueKeys = _.sortBy _.uniq _.pluck (_.pluck nodeArray, key), 'basicInfo'
       children = _.map uniqueKeys, (key) -> _name: key

       _.each children, (child) ->
-        nodesMatchingKey = _.filter nodeArray, (node) -> node[keys[level]].basicInfo is child._name
+        nodesMatchingKey = _.filter nodeArray, (node) -> node[key].basicInfo is child._name
         child._children = recurse nodesMatchingKey, keys, level+1

     recurse baseNodes, @options.keySort


Fix help link to be real

Fix help link to be real

--- lib/prompt.coffee
+++ lib/prompt.coffee
@@ -16,7 +16,7 @@

 module.exports = (name, required) ->
   console.log "Please enter the following config details for #{name.bold}".green
-  console.log "Need help? see http://carrot.github.io/ship/#{name}".grey
+  console.log "Need help? see https://github.com/carrot/ship".grey

   deferred = W.defer()


Harmonize with Atom's default setup and language

--- lib/sunset.coffee
+++ lib/sunset.coffee
@@ -7,35 +7,35 @@
 module.exports = Sunset =
   config:
     daytime_syntax_theme:
-      title: 'Daytime Theme (Syntax)'
+      title: 'Daytime Syntax Theme'
       description: 'What syntax theme should I use during daylight?'
       type: 'string'
-      default: 'atom-light-syntax'
+      default: 'one-light-syntax'
     daytime_ui_theme:
-      title: 'Daytime Theme (UI)'
+      title: 'Daytime UI Theme'
       description: 'What UI theme should I use during daylight?'
       type: 'string'
-      default: 'atom-light-ui'
+      default: 'one-light-ui'
     nighttime_syntax_theme:
-      title: 'Nighttime Theme (Syntax)'
+      title: 'Night-time Syntax Theme'
       description: 'What syntax theme should I use during nighttime?'
       type: 'string'
-      default: 'atom-dark-syntax'
+      default: 'one-dark-syntax'
     nighttime_ui_theme:
-      title: 'Nighttime Theme (UI)'
+      title: 'Night-time UI Theme'
       description: 'What UI theme should I use during nighttime?'
       type: 'string'
-      default: 'atom-dark-ui'
+      default: 'one-dark-ui'
     when_does_it_get_dark:
-      title: 'When does nighttime start?'
-      description: 'When (in 24hr format) does the sun set?'
+      title: 'When does the sun set?'
+      description: 'Use a 24hr format: 8:00PM equals 20:00, so type 2000'
       default: 1800
       minimum: 0
       maximum: 2400
       type: 'number'
     when_does_it_get_light:
-      title: 'When does daytime start?'
-      description: 'When (in 24hr format) does the sun rise?'
+      title: 'When does the sun rise?'
+      description: 'Use a 24hr format: 8:00PM equals 20:00, so type 2000'
       default: 500
       minimum: 0
       maximum: 2400
@@ -63,11 +63,11 @@

     @config.daytime_syntax_theme.description =
       @config.nighttime_syntax_theme.description =
-        'Values can be `' + cached_themes.syntax.join(', ') + '`'
+        'Choose from: `' + cached_themes.syntax.join(', ') + '`'

     @config.daytime_ui_theme.description =
       @config.nighttime_ui_theme.description =
-        'Values can be `' + cached_themes.ui.join(', ') + '`'
+        'Choose from: `' + cached_themes.ui.join(', ') + '`'

     # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
     @subscriptions = new CompositeDisposable


Improve flip script with new features

Improve flip script with new features

Demo:
hubot flip
hubot> (╯°□°）╯︵ ┻━┻
hubot unflip
hubot> ┬──┬ ノ( º _ ºノ)
hubot rage flip
hubot> (ノಠ益ಠ)ノ彡 ┻━┻
hubot flip java
hubot> (╯°□°）╯︵ ɐʌɐɾ
hubot rage flip .NET
hubot> (ノಠ益ಠ)ノ彡 ⊥Ǝᴎ˙
hubot unflip ɐʌɐɾ
hubot> java ノ( º _ ºノ)
hubot flip me
hubot> (╯°□°）╯︵ ʃʃǝɥS
hubot unflip me
hubot> Shell ノ( º _ ºノ)

--- src/scripts/flip.coffee
+++ src/scripts/flip.coffee
@@ -6,14 +6,45 @@
 #
 # Commands:
 #   hubot flip <text> - express your anger
+#   hubot rage flip <text> - express your anger with rage
+#   hubot unflip <text>
 #
-# Author:
+# Authors:
 #   jergason
+#   spajus

 flip = require 'flip'

 module.exports = (robot) ->
-  robot.respond /(flip) (.*)$/i, (msg) ->
-    toFlip = msg.match[2]
-    flipped = flip(toFlip)
-    msg.send "(╯°□°）╯︵ #{flipped}"
+
+  robot.respond /(rage )?flip( .*)?$/i, (msg) ->
+    if msg.match[1] == 'rage '
+      guy = '(ノಠ益ಠ)ノ彡'
+    else
+      guy = '(╯°□°）╯︵'
+
+    toFlip = (msg.match[2] || '').trim()
+
+    if toFlip == 'me'
+      toFlip = msg.message.user.name
+
+    if toFlip == ''
+      flipped = '┻━┻'
+    else
+      flipped = flip(toFlip)
+
+    msg.send "#{guy} #{flipped}"
+
+
+  robot.respond /unflip( .*)?$/i, (msg) ->
+    toUnflip = (msg.match[1] || '').trim()
+
+    if toUnflip == 'me'
+      unflipped = msg.message.user.name
+    else if toUnflip == ''
+      unflipped = '┬──┬'
+    else
+      unflipped = flip(toUnflip)
+
+    msg.send "#{unflipped} ノ( º _ ºノ)"
+


Moved output of alt text (tooltip) below image.

--- src/scripts/xkcd.coffee
+++ src/scripts/xkcd.coffee
@@ -27,6 +27,6 @@
           msg.send 'Comic not found.'
         else
           object = JSON.parse(body)
-          msg.send object.alt
           msg.send object.title
           msg.send object.img
+          msg.send object.alt


Too much callbacks

Too much callbacks

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -348,9 +348,8 @@
       cached
     else if (value = @manager.check_helpers(key)) != undefined
       if typeof(value) == 'function'
-        value (callback) =>
-          callback (value) =>
-            @fiber.resume value
+        value (result) =>
+          @fiber.resume result
         Fiber.yield()
       else
         value


Removed git plus from Atom package list

Removed git plus from Atom package list

--- atom/packages.cson
+++ atom/packages.cson
@@ -4,7 +4,6 @@
   "file-icons"
   "git-blame"
   "git-log"
-  "git-plus"
   "git-time-machine"
   "hex"
   "highlight-line"


Fix typo in awk `for` snippet

Fix typo in awk `for` snippet


Use describe instead of context

Use describe instead of context

Jest no longer supports `context`, so switch the `rrit` snippet to use `describe` instead.

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -72,7 +72,7 @@
   'Redux reducer action spec':
     'prefix': 'rrit'
     'body': """
-            context('$1', () => {
+            describe('$1', () => {
               const state = initialState
               const action = $2
               const newState = reducer(state, action)


Add CSS and Sass snippets

Add CSS and Sass snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -607,6 +607,42 @@
     'prefix': 'endwhile'
     'body': '<?php endwhile; ?>$1'

+# CSS, Sass, and Less
+
+'.source.css':
+
+  '@media':
+    'prefix': 'med'
+    'body': '@media $1 {\n\t$2\n}'
+
+# Sass
+
+'.source.css.scss':
+
+  '@if':
+    'prefix': 'if'
+    'body': '@if ${1:condition} {\n\t$2\n} $3'
+
+  '@elseif':
+    'prefix': 'elseif'
+    'body': '@else if ${1:condition} {\n\t$2\n} $3'
+
+  '@else':
+    'prefix': 'else'
+    'body': '@else {\n\t$1\n}'
+
+  '@for':
+    'prefix': 'for'
+    'body': '@for ${1:\$i} from ${2:1} ${3:through} ${4:\$n} {\n\t$5\n}'
+
+  '@each':
+    'prefix': 'each'
+    'body': '@each ${1:\$item} in ${2:\$obj} {\n\t$3\n}'
+
+  '@include':
+    'prefix': 'inc'
+    'body': '@include '
+
 # Shell scripts (particularly Bash)

 '.source.shell':


slightly better loosing behavior. The alert statement should be replaced by some sort of animation or other medium that conveys loosing. The rest of the function that stops and starts the clock, changes protagonist, and such is spot on.

--- web/scripts/gameManager.coffee
+++ web/scripts/gameManager.coffee
@@ -314,9 +314,13 @@
         return

     gameLost: =>
-        alert "You Have Lost!"
         if clockHandle?
             clearInterval clockHandle
+        @visual.changeState @protagonist.index, 4
+        @protagonist.moves = null
+        @startedGame = false
+        alert "You Have Lost!"
+        clockHandle = setInterval @clock, 17
         return

     checkInBounds: (playerX, playerY) ->


don't convert synopsis text to boolean

don't convert synopsis text to boolean

--- server/express/lib/server.coffee
+++ server/express/lib/server.coffee
@@ -389,8 +389,8 @@
           p2 = page.story[1]
           synopsis ||= p1.text if p1 && p1.type == 'paragraph'
           synopsis ||= p2.text if p2 && p2.type == 'paragraph'
-          synopsis ||= p1.text? if p1
-          synopsis ||= p2.text? if p2
+          synopsis ||= p1.text if p1
+          synopsis ||= p2.text if p2
           synopsis ||= page.story? && "A page with #{page.story.length} items." || "A page with no story."

           sitemap.push({


Remove Lenovo DTM pixel from CodePlay tags

--- server/lib/code-play-tags.coffee
+++ server/lib/code-play-tags.coffee
@@ -50,14 +50,5 @@
     })(window,document,'script','dataLayer','GTM-NBV9T9');</script>
     <!-- End Google Tag Manager -->

-    <!-- Lenovo DTM JS -->
-    <script src="//assets.adobedtm.com/9f0b01d01b963414024fff848ca9d3750437e1fa/satelliteLib-72ed6aba7cec9558bd09a076c144b1856387c6d5.js"></script>
-    <script> window.lmd={ cc:"ww", bu:"public", language:"en" }; </script>
-    <!-- End Lenovo DTM JS -->
-
 '''
-    footer: '''
-<!-- Lenovo DTM tag -->
-<script type="text/javascript">if(typeof _satellite !== "undefined")_satellite.pageBottom(); </script>
-<!-- End Lenovo DTM tag -->
-'''
+    footer: ''


Translated up to 470

Translated up to 470

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -307,7 +307,7 @@
     tip_premature_optimization: "La optimización prematura es la raíz de la maldad. - Donald Knuth"
     tip_brute_force: "Cuando tengas duda, usa la fuerza bruta. - Ken Thompson"
     tip_extrapolation: "Solo hay dos tipos de personas: Esas que pueden extrapolar desde información incompleta..."
-    tip_superpower: "Programar es lo más cercanos que tenemos a superpoderes."
+    tip_superpower: "Programar es lo más cercano que tenemos a superpoderes."

   game_menu:
     inventory_tab: "Inventario"
@@ -327,7 +327,7 @@
     auth_caption: "Guarda tu progreso."

   inventory:
-    choose_inventory: "Elegir artículos"
+    choose_inventory: "Equipar objetos"
     equipped_item: "Equipado"
 #    required_purchase_title: "Required"
     available_item: "Disponible"
@@ -360,9 +360,9 @@
     items: "Más de 250 ítems de bonus!"
     parents: "Para padres"
     parents_title: "Su hijo aprenderá a programar."
-    parents_blurb1: "Con CodeCombat, su hijo aprenderá a escribiendo código real. Empezaran aprendiendo comandos simples avanzando a tópicos más complejos."
+    parents_blurb1: "Con CodeCombat, su hijo aprenderá a escribiendo código real. Empezaran aprendiendo comandos simples avanzando a temas más complejos."
     parents_blurb2: "Por $9.99 USD/mes, recibirán nuevos desafíos todas las semanas y soporte personal por email de programadores profesionales."
-    parents_blurb3: "Sin Riesgo: Garantía de 100% de devolución, fácil 1-click y desuscribirse."
+    parents_blurb3: "Sin Riesgo: Garantía de 100% de devolución, fácil 1-click y des- suscribirse."
     subscribe_button: "Suscríbete  Ahora"
     stripe_description: "Suscripción Mensual"
     subscription_required_to_play: "Necesitas una suscripción para jugar este nivel."
@@ -374,10 +374,10 @@
     default: "por Defecto"
     experimental: "Experimental"
     python_blurb: "Simple pero poderoso."
-    javascript_blurb: "El lenguaje de la web."
-    coffeescript_blurb: "Mejor JavaScript."
+    javascript_blurb: "El lenguaje de la web (no es Java)."
+    coffeescript_blurb: "JavaScript pero más bonito."
     clojure_blurb: "Un Lisp moderno."
-    lua_blurb: "Para Juegos."
+    lua_blurb: "Lenguaje ara Juegos."
     io_blurb: "Simple pero oscuro."
     status: "Estado"
     weapons: "Armas"
@@ -415,7 +415,7 @@
     granted_by: "Concedido por"

   save_load:
-    granularity_saved_games: "Almacenado"
+    granularity_saved_games: "Guardado"
     granularity_change_history: "Historia"

   options:
@@ -428,9 +428,9 @@
     editor_config: "Config. de Editor"
     editor_config_title: "Configuración del Editor"
     editor_config_level_language_label: "Lenguaje para este Nivel"
-    editor_config_level_language_description: "Definir el lenguaje de programación para Este nivel."
+    editor_config_level_language_description: "Definir el lenguaje de programación para este nivel."
     editor_config_default_language_label: "Lenguaje de Programación Predeterminado"
-    editor_config_default_language_description: "Definir el lenguaje de programación que deseas para codificar cuando inicias nuevos niveles."
+    editor_config_default_language_description: "Definir el lenguaje de programación que deseas para programar cuando inicias nuevos niveles."
     editor_config_keybindings_label: "Atajos de Teclado"
     editor_config_keybindings_default: "Default (As)"
     editor_config_keybindings_description: "Añade atajos adicionales conocidos de los editores comunes."
@@ -449,9 +449,9 @@
     why_paragraph_2_prefix: "De eso se trata la programación. Será divertido. No casi divertido"
     why_paragraph_2_italic: "bien un premio"
     why_paragraph_2_center: "pero algo divertido"
-    why_paragraph_2_italic_caps: "¡NO MAMÁ, TENGO QUE FINALIZAR EL NIVEL!"
+    why_paragraph_2_italic_caps: "¡NO MAMÁ, TENGO QUE TERMINAR EL NIVEL!"
     why_paragraph_2_suffix: "Por tal motivo CodeCombat es un juego multiusuario, no un curso con gamificación. No finalizaremos hasta que terminemos--pero en esta ocasión, es una buena cosa."
-    why_paragraph_3: "si te vas a volver adicto a un juego, hazlo a este y conviértete en uno de los magos de la era tecnológica."
+    why_paragraph_3: "si te vas a volver adicto a un juego, que sea este y conviértete en uno de los magos de la era tecnológica."
     press_title: "Blogeros/Prensa"
     press_paragraph_1_prefix: "¿Quieres escribirnos? Descarga y usa con confianza todos los recursos incluídos en nuestro"
     press_paragraph_1_link: "paquete de prensa"


empty sensor list

empty sensor list

--- src/main/webapp/app/coffeescripts/views/MapNetworkView.coffee
+++ src/main/webapp/app/coffeescripts/views/MapNetworkView.coffee
@@ -33,7 +33,7 @@
     list = $a.models.get('networklist').get('network')[0].get('linklist')
     $a.linkList = new $a.LinkCollection(list.get('link'))
     list = $a.models.get('sensorlist')
-    $a.sensorList = new $a.SensorCollection(list.get('sensor'))
+    $a.sensorList = new $a.SensorCollection(list?.get('sensor') || [])

   _drawScenarioItems: () ->
     if @scenario.get('sensorlist')?


Edited isNaN check in fullpie.coffee

Edited isNaN check in fullpie.coffee

--- widgets/fullpie/fullpie.coffee
+++ widgets/fullpie/fullpie.coffee
@@ -48,22 +48,9 @@
     for val in data
       sum += val.value

-#    arcs.append("svg:text").attr("transform", (d, i) ->
-#      procent_val = Math.round(data[i].value/sum * 100)
-#      d.innerRadius = (radius * (100-procent_val)/100) - 45  #45=max text size/2
-#      d.outerRadius = radius
-#      "translate(" + arc.centroid(d) + ")")
-#      .attr('fill', "#fff")
-#      .attr("text-anchor", "middle").text((d, i) -> data[i].label).attr('font-size', '28px')
-#      .append('svg:tspan')
-#      .attr('x', 0)
-#      .attr('dy', 25)
-#      .attr('font-size', '90%')
-#      .text((d,i) -> data[i].value + ' (' + Math.round(data[i].value/sum * 100) + '%)')
-
     arcs.append("svg:text").attr("transform", (d, i) ->
-      percent_val = getpercentval(data[i])
-      d.innerRadius = (radius * (100-percent_val)/100) - 45  #45=max text size/2
+      procent_val = Math.round(data[i].value/sum * 100)
+      d.innerRadius = (radius * (100-procent_val)/100) - 45  #45=max text size/2
       d.outerRadius = radius
       "translate(" + arc.centroid(d) + ")")
       .attr('fill', "#fff")
@@ -72,10 +59,4 @@
       .attr('x', 0)
       .attr('dy', 25)
       .attr('font-size', '90%')
-      .text((d,i) -> data[i].value + ' (' + percent_val + '%)')
-
-    getpercentval: (x) ->
-      if isNan(Math.round(x.value/sum * 100))
-        0
-      else
-        Math.round(x.value/sum * 100)
+      .text((d,i) -> data[i].value + ' (' + Math.round(data[i].value/sum * 100) + '%)')


Add a comment explaining the choice of XMLHttpRequest for UIComponent

--- content_scripts/ui_component.coffee
+++ content_scripts/ui_component.coffee
@@ -12,6 +12,8 @@
     # Default to everything hidden while the stylesheet loads.
     styleSheet.innerHTML = "iframe {display: none;}"

+    # Use an XMLHttpRequest, possibly via the background page, to fetch the stylesheet. This allows us to
+    # catch and recover from failures that we could not have caught when using CSS @include (eg. #1817).
     UIComponent::styleSheetGetter ?= new AsyncDataFetcher @fetchFileContents "content_scripts/vimium.css"
     @styleSheetGetter.use (styles) -> styleSheet.innerHTML = styles


trouble url should be more forgiving and ignore missing params

--- shared/src/model/exercise.coffee
+++ shared/src/model/exercise.coffee
@@ -1,4 +1,6 @@
 qs = require 'qs'
+omitBy      = require 'lodash/omitBy'
+isUndefined = require 'lodash/isUndefined'

 # This list maps Tutor book UUID's to the their official OpenStax title.
 # It's used to pre-fill the errata submission form on webview with
@@ -19,14 +21,17 @@
 Exercises =

   troubleUrl: ({bookUUID, project, exerciseId, chapter_section, title}) ->
-    [chapter, section] = chapter_section if chapter_section
+    if chapter_section and title
+      [chapter, section] = chapter_section
+      location = "#{chapter}.#{section} #{title}"

-    Exercises.ERRATA_FORM_URL + '?' + qs.stringify(
+    urlParams =
       source: project # either tutor or CC
-      location: "#{chapter}.#{section} #{title}"
+      location: location
       book: BOOK_UID_XREF[bookUUID]
       exerciseId: exerciseId
-    )
+
+    Exercises.ERRATA_FORM_URL + '?' + qs.stringify(omitBy(urlParams, isUndefined))

   getParts: (exercise) ->
     exercise.content?.questions or []


small changes to the router

small changes to the router

--- app/routes/router.coffee
+++ app/routes/router.coffee
@@ -117,8 +117,8 @@
               body:
                 images:[
                   {
-                    type:'md5'
-                    value:'55799f62663c7dd0eb3924b38366e879'
+                    type:'url'
+                    value:'https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150'
                   }
                 ]
                 highlighter: {}
@@ -135,7 +135,7 @@
               statusCode: 200
               identifier: identifier
               message: 'Algorithm created'
-            sendResponse(res, response)
+            sendResponse(res, null, response)
         ))
       )


Fix handling of null selection in sidebar.coffee

--- annotran/static/scripts/annotator/sidebar.coffee
+++ annotran/static/scripts/annotator/sidebar.coffee
@@ -217,6 +217,11 @@

     body = document.body

+    if range is null
+      super
+      this.show() unless annotation.$highlight
+      return
+
     # convert this to a range in the current document and extract the start and end points
     #range = new xpathRange.sniff(packager).normalize(document.body)
     selection_box = range.toRange().getBoundingClientRect()


[polling-service]: added js functionality

--- app/assets/javascripts/polling_service.js.coffee
+++ app/assets/javascripts/polling_service.js.coffee
@@ -9,10 +9,10 @@
     $element.data( 'polling_is_updating', true );
     url = $element.data('updatePath')
     return unless url
-
+    dataType = if url.search(/^[^\?]+\.js/) >= 0 then 'script' else 'html'
     $.ajax
       url: url,
-      dataType: 'html',
+      dataType: dataType,
       data: {'polling_service': true},
       success: ( data, textStatus, jqXHR ) ->
         # try to get loacation from response header
@@ -29,7 +29,8 @@
         else
           # no redirect -> replace content with html from response
           #exists = $.contains(document.documentElement, $element)
-          $element.replaceWith(data)
+          if dataType == 'html'
+            $element.replaceWith(data)
       error: () ->
         $element.data( 'polling_is_updating', false );
       complete: () ->


fix checkbox state when "Split Size" line is initially disabled

--- app/assets/javascripts/utils/RickshawUtil.coffee
+++ app/assets/javascripts/utils/RickshawUtil.coffee
@@ -165,6 +165,8 @@
             .attr("width", graph.width)
             .attr("height", 1)
           nodes[0][0].setAttribute("fill", "#ff0000")
+        else
+          $(".line").addClass("disabled")

   class @AllSeriesToggle extends Backbone.View
     events:


fix rounding to whole number

fix rounding to whole number

In Gradebook, the Assignment Details modal should show a decimal
average computed for the assignment. Previously, it rounded to a
whole number. Now it rounds to 2 decimal places.

fixes CNVS-15945

test plan:
1. Create an assignment (publish it)
2. Go to the gradebook and enter grades with one and
   two decimal places (e.g 5.5, 5.35)
3. hover over the assignment title
4. click on dropdown and click on assignment details
5. notice that the average score is rounded up/down
6. it should now display proper decimal rounding

Change-Id: I56d8600aab3b5184c494b1f32889cd4d231d0148
Reviewed-on: https://gerrit.instructure.com/78252
Reviewed-by: Shahbaz Javeed <6692d7be3fa215e5483a121d6fb314def6049877@instructure.com>
Reviewed-by: Jeremy Neander <20252aad5efdc666f083d82ab63ed0f9f72a043e@instructure.com>
Tested-by: Jenkins
QA-Review: KC Naegle <9fbecd016338f9910821a8a5d5d0ae4fc9abfaa5@instructure.com>
Product-Review: Christi Wruck

--- app/coffeescripts/AssignmentDetailsDialog.coffee
+++ app/coffeescripts/AssignmentDetailsDialog.coffee
@@ -2,9 +2,10 @@
   'i18n!assignment_details'
   'jquery'
   'jst/AssignmentDetailsDialog'
+  'compiled/util/round'
   'jqueryui/dialog'
   'compiled/jquery/fixDialogButtons'
-], (I18n, $, assignmentDetailsDialogTemplate) ->
+], (I18n, $, assignmentDetailsDialogTemplate, round) ->

   class AssignmentDetailsDialog
     constructor: ({@assignment, @students}) ->
@@ -41,7 +42,7 @@
         average: do (scores) =>
           total = 0
           total += score for score in scores
-          @nonNumericGuard Math.round(total / scores.length)
+          @nonNumericGuard round((total / scores.length), 2)

       scores: scores
       locals: locals


hide "add reply to topic" for closed discussions

Change-Id: Ibef1c208d02a91750301b86cd9a1ad691948ce16
Reviewed-on: https://gerrit.instructure.com/9750
Tested-by: Hudson <dfb64870173313a9a7b56f814c8e3b33e268497a@instructure.com>
Reviewed-by: Zach Wily <9de8c4480303b5335cd2a33eefe814615ba3612a@instructure.com>

--- app/coffeescripts/discussions/EntriesView.coffee
+++ app/coffeescripts/discussions/EntriesView.coffee
@@ -64,7 +64,7 @@
       @collectionView = new EntryCollectionView
         $el: @$el
         collection: @collection
-        showReplyButton: true
+        showReplyButton: ENV.DISCUSSION.PERMISSIONS.CAN_REPLY
       @collection.reset entries
       @updateFromNewEntries()
       @setUnreadEntries()


Looser linkstrength on clustering

Looser linkstrength on clustering

--- app/scripts/alchemy/clustering/clustering.coffee
+++ app/scripts/alchemy/clustering/clustering.coffee
@@ -26,7 +26,7 @@
             sourceCluster = nodes[edge.source.id]._properties[@clusterKey]
             targetCluster = nodes[edge.target.id]._properties[@clusterKey]
             if sourceCluster is targetCluster
-                0.3
+                0.15
             else
                 0
         _friction = () ->


Fix char limit counter after creating post

--- assets/javascripts/views/new_post_form.js.coffee
+++ assets/javascripts/views/new_post_form.js.coffee
@@ -10,6 +10,7 @@
     @render()

   init: =>
+    @frozen = false
     @$errors = ($ '.alert-error', @$el).first().hide()

     @$post_btn = ($ 'input[type=submit]', @$el)


Fix states in PlayerController

Fix states in PlayerController

--- app/assets/javascripts/controllers/player_controller.js.coffee
+++ app/assets/javascripts/controllers/player_controller.js.coffee
@@ -10,15 +10,20 @@
   ).property('artists.@each.recordings.@each')

   playing: (->
-    @get('currentState') == @get('states.stopped')
+    @get('currentState') == @get('states.playing')
   ).property('currentState')

-  artistsChanged: (->
+  artistsDidChange: (->
     @set('currentRecording', null)
   ).observes('artists.@each')

+  playerDidStop: (->
+    @set('currentRecording', null) if @get('currentState') == @get('states.stopped')
+  ).property('currentState')
+
   play: ->
-    @set('currentRecording', @get('lastRecording') || @get('recordings')[0])
+    @set('currentRecording', @get('recordings')[0])
+    @send('play')

   actions: {
     play: -> @set('currentState', @get('states.playing'))


:dog: For hound

:dog: For hound

--- app/assets/javascripts/farmbot_app/services/calendar.js.coffee
+++ app/assets/javascripts/farmbot_app/services/calendar.js.coffee
@@ -8,12 +8,12 @@
     Data
       .findAll('sequence', {})
       .catch(fail)
-      .then (sequences) =>
+      .then (sequences) ->
         Data.loadRelations('sequence', s._id, ['step']) for s in sequences
         Data
           .findAll('schedule', {})
           .catch(fail)
-          .then (schedules) =>
+          .then (schedules) ->
             deferred.resolve(sequences: sequences, schedules: schedules)
     promise
   # Welcome to the hairest method in all of Farmbot!
@@ -39,12 +39,14 @@
       accumulator

     _.chain(source)
-     .map('calendar')            # Grab 2d array of all possible execution times.
+     .map('calendar')            # Grab 2d array of all possible
+                                 # execution times.
      .flatten()                  # Flatten down into 1d array
-     .uniq()                     # Grab uniq values to give all possible calendar
-                                 # points
+     .uniq()                     # Grab uniq values to give all possible
+                                 # calendar points
      .reduce(groupByDueDate, {}) # Return:
-                                 # [{date1: [schedules...]},{date2: [schedules]}]
+                                 # [ {date1: [schedules...]},
+                                 #   {date2: [schedules]}]
      .pairs()                    # Return: [ [date1, [schedules]],
                                  #           [date2, [schedules]], . . .]
      .reduce(insertFieldThatIsNamedNextTime, []) # Splice in 'next_time' field


Allow clicks to pass through shape objects

--- app/assets/javascripts/modules/map/spatial_selection.js.coffee
+++ app/assets/javascripts/modules/map/spatial_selection.js.coffee
@@ -24,9 +24,11 @@
       @_colorOptions = colorOptions =
         color: normalColor
         dashArray: null
+        clickable: false
       @_errorOptions = errorOptions =
         color: errorColor
         dashArray: null
+        clickable: false
       selectedOptions =
         dashArray: '10, 10'


Hide the no entries found notice on first asset upload

--- app/assets/javascripts/tenon/features/asset_uploader.js.coffee
+++ app/assets/javascripts/tenon/features/asset_uploader.js.coffee
@@ -19,6 +19,7 @@
     @$list.prepend(JST["tenon/templates/assets/asset_row"](asset : data.result))
     setTimeout () =>
       @$list.find('.hidden').removeClass('hidden')
+      @$list.find('.info').hide()
       data.context.addClass('hidden')
     , 1000


clear element value only for real new objects

--- app/assets/javascripts/uploadcare/widget/base-widget.js.coffee
+++ app/assets/javascripts/uploadcare/widget/base-widget.js.coffee
@@ -76,14 +76,15 @@
       @template.reset()

     __setObject: (newFile) =>
-      if newFile != @currentObject
-        @__reset()
-        if newFile
-          @currentObject = newFile
-          @__watchCurrentObject()
-        @__onChange.fire(@currentObject)
-      if not newFile
+      if newFile == @currentObject
+        return
+      @__reset()
+      if newFile
+        @currentObject = newFile
+        @__watchCurrentObject()
+      else
         @element.val('')
+      @__onChange.fire(@currentObject)

     __watchCurrentObject: ->
       object = @__currentFile()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -23,7 +23,6 @@

   @name every
   @methodOf TimedEvents#
-  @see Point.distance
   @returns {Number} Distance between the two objects
   ###
   every: (n, fn) ->


sending all events to particles

sending all events to particles

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -12,7 +12,7 @@
     everyEvents: []

   self.bind "update", (elapsedTime) ->
-
+    for event

   ###*
   Execute fn every n frames.


Don’t trigger minimize event

Don’t trigger minimize event

It’s difficult to prevent this command from actually minimizing the
window on both the released version of Atom and the custom elements
branch at the same time. The easiest approach is just to trigger a
different command.

--- spec/command-palette-spec.coffee
+++ spec/command-palette-spec.coffee
@@ -88,11 +88,11 @@
     it "detaches the palette, then focuses the previously focused element and emits the selected command on it", ->
       eventHandler = jasmine.createSpy('eventHandler').andReturn(false)
       activeEditor = atom.workspaceView.getActiveView()
-      eventName = palette.items[5].eventName ? palette.items[5].name
+      eventName = palette.items[3].name

       activeEditor.preempt eventName, eventHandler

-      palette.confirmed(palette.items[5])
+      palette.confirmed(palette.items[3])

       expect(activeEditor.isFocused).toBeTruthy()
       expect(eventHandler).toHaveBeenCalled()


Bug fix in ELEM.getVisibleSize (would cause an infinite loop)

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -175,7 +175,9 @@
         [ _parentClientWidth, _parentClientHeight ] = [ _parent.clientWidth, _parent.clientHeight ]
         w = _parentClientWidth - _elem.offsetLeft if w > _parentClientWidth
         h = _parentClientWidth - _elem.offsetTop  if h > _parentClientHeight
-        [ _elem, _parent ] = [ _elem.parentNode, _elem.parentNode ]
+      _elem = _elem.parentNode
+      break unless _parent.parentNode
+      _parent = _parent.parentNode
     [ w, h ]

   ###


:green_heart: Fix broken travis tests

:green_heart: Fix broken travis tests

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -433,7 +433,7 @@
           nextAnimationFrame()

         it 'adjusts the width of the minimap', ->
-          expect(minimapElement.offsetWidth).toBeCloseTo(editorElement.offsetWidth / 11, 0)
+          expect(minimapElement.offsetWidth).toBeCloseTo(editorElement.offsetWidth / 11, -1)
           expect(minimapElement.style.width).toEqual('')

     describe 'when minimap.minimapScrollIndicator setting is true', ->


:bug: Fire the keyboard events on the quick setting element

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -874,42 +874,42 @@

         describe 'core:move-down', ->
           beforeEach ->
-            atom.commands.dispatch workspaceElement, 'core:move-down'
+            atom.commands.dispatch quickSettingsElement, 'core:move-down'

           it 'selects the second item', ->
             expect(quickSettingsElement.querySelector('li.active:nth-child(2)')).toExist()

           describe 'reaching a separator', ->
             beforeEach ->
-              atom.commands.dispatch workspaceElement, 'core:move-down'
+              atom.commands.dispatch quickSettingsElement, 'core:move-down'

             it 'moves past the separator', ->
               expect(quickSettingsElement.querySelector('li.active:last-child')).toExist()

           describe 'then core:move-up', ->
             beforeEach ->
-              atom.commands.dispatch workspaceElement, 'core:move-up'
+              atom.commands.dispatch quickSettingsElement, 'core:move-up'

             it 'selects again the first item of the list', ->
               expect(quickSettingsElement.querySelector('li.active:first-child')).toExist()

         describe 'core:move-up', ->
           beforeEach ->
-            atom.commands.dispatch workspaceElement, 'core:move-up'
+            atom.commands.dispatch quickSettingsElement, 'core:move-up'

           it 'selects the last item', ->
             expect(quickSettingsElement.querySelector('li.active:last-child')).toExist()

           describe 'reaching a separator', ->
             beforeEach ->
-              atom.commands.dispatch workspaceElement, 'core:move-up'
+              atom.commands.dispatch quickSettingsElement, 'core:move-up'

             it 'moves past the separator', ->
               expect(quickSettingsElement.querySelector('li.active:nth-child(2)')).toExist()

           describe 'then core:move-down', ->
             beforeEach ->
-              atom.commands.dispatch workspaceElement, 'core:move-down'
+              atom.commands.dispatch quickSettingsElement, 'core:move-down'

             it 'selects again the first item of the list', ->
               expect(quickSettingsElement.querySelector('li.active:first-child')).toExist()


tests added for bad inputs

tests added for bad inputs

--- specs/KataPokerHands.spec.coffee
+++ specs/KataPokerHands.spec.coffee
@@ -2,14 +2,28 @@

 describe('Result', () ->

-    it('should be null', () ->
-        # bad input
+    it('should be null for bad input', () ->
         expect(Poker.compare_hands('bad hands input')).toEqual(null)
-        # bad cards
+    )
+
+    it('should be null for bad card suits', () ->
         expect(Poker.compare_hands('Black: 2X 3D 5S 9C KD '+
                                    ' White: 2A 3H 5C 9S KH')).toEqual(null)
+    )
+
+    it('should be null for bad card values', () ->
         expect(Poker.compare_hands('Black: 1H 3D 5S 9C KD '+
                                    ' White: 2D 3H 5C 9S KH')).toEqual(null)
+    )
+
+    it('should be null if too many cards for black player', () ->
+        expect(Poker.compare_hands('Black: 1H 3D 5S 9C KS KC KD '+
+                                   ' White: 2D 3H 5C 9S KH')).toEqual(null)
+    )
+
+    it('should be null if too many cards for white player', () ->
+        expect(Poker.compare_hands('Black: 1H 3D 5S 9C KD '+
+                                   ' White: 2D 3H 2H 5C 9S KH')).toEqual(null)
     )

     it('should be tie', () ->


fix scrolling in frame mode

fix scrolling in frame mode

--- src/client/embeds/iframe.coffee
+++ src/client/embeds/iframe.coffee
@@ -46,7 +46,7 @@
     if doc and doc.readyState=='complete'
       getIframe.window(iframe).location.href='/'
       doc.open();
-      doc.write "<!DOCTYPE html>"
+      doc.write '<!DOCTYPE html>'
       doc.close();
       @polyfill(doc).then -> callback(iframe)
     else


Adding keys to search file for linux & windows machines

--- keymaps/greyatom-tree-view.cson
+++ keymaps/greyatom-tree-view.cson
@@ -1,5 +1,8 @@
 '.platform-darwin':
  'cmd-p': 'commit-live-file-search:toggle'
+
+'.platform-win32, .platform-linux':
+  'ctrl-p': 'commit-live-file-search:toggle'

 'atom-workspace':
   'ctrl-alt-o': 'commit-live-tree-view:toggle'


Fix facebook share param

Fix facebook share param

--- src/collections/teamMedia.coffee
+++ src/collections/teamMedia.coffee
@@ -158,7 +158,7 @@
       teamMediumId: teamMediumId,
       facebookPageId: facebookPageId,
       caption: caption,
-      postToWall: postToWall
+      isSuppressedFromFeed: isSuppressedFromFeed
     }

     @collections.teamMedia.exec('facebookShareMedium', params)


Enable debug console output

Enable debug console output

--- app/api/article.coffee
+++ app/api/article.coffee
@@ -77,6 +77,8 @@
       description: param req, 'description'
       canAdd: param req, 'canAdd'
       author: req.user.id
+    template.allowed = 0 if template.type == 2
+    template.solved = 0 if template.type == 3
     template.canAdd = false if template.type == 1
     template.photo = photo.path if photo? && photo.path?
     db.collections.article.create template
@@ -129,6 +131,7 @@
     obj.author = req.user.toJSON()
     res.json obj
   .catch (e) ->
+    console.log e.stack
     res.status 400
     res.send e.message


add width

add width

--- app/application.coffee
+++ app/application.coffee
@@ -35,9 +35,13 @@

   frequency: (options) =>
     @synth.frequency.value = options.frequency
-    Tone.Transport.setTimeout =>
-      @synth.stop()
-    , 1
+    unless options.skipTimeout
+      Tone.Transport.setTimeout =>
+        @synth.stop()
+      , 1
+
+  width: (options) =>
+    @synth.width.value = options.width

   start: =>
     @synth.start()