Fix template

Fix template

--- src/test/test_helpers/fixtures/editor_with_image.coffee
+++ src/test/test_helpers/fixtures/editor_with_image.coffee
@@ -2,5 +2,5 @@

 window.JST["test/test_helpers/fixtures/editor_with_image"] = () => """
 <trix-editor input="my_input" autofocus placeholder="Say hello..."></trix-editor>
-<input id="my_input" type="hidden" value="ab<img src="<%= TEST_IMAGE_URL %>" width="10" height="10">">
+<input id="my_input" type="hidden" value="ab">
 """


Revert "rollbar log first & last entry in feed to debug duplicate entires in feed"

This reverts commit 664986d188edc2dedd70ba9af57194f9efd2e40a.

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -169,47 +169,21 @@
         cache.overview.reverse()  if cache?.overview
         callback null, cache

-  getTimeFromItem: (item) ->
-    # memberbucket data has no serverside model it comes from cache
-    # so it has no meta, that's why we check its date by its overview
-    lastDate = if item.createdAtTimestamps
-      new Date item.createdAtTimestamps.first
-    else
-      new Date item.meta.createdAt
-
-    return lastDate
-
   continueLoadingTeasers:->

     unless isLoading
       if @listController.itemsOrdered.last
-
-        # DEBUG: duplicate entries in activity feed
         lastItemData = @listController.itemsOrdered.last.getData()
-        lastDate = @getTimeFromItem(lastItemData)
-        lastId = lastItemData._id
-
-        firstItemData = @listController.itemsOrdered.first.getData()
-        firstDate = @getTimeFromItem(firstItemData)
-        firstId = firstItemData._id
-
-        timeStamps =
-          top:
-            date:firstDate
-            id:lastId
-          bottom:
-            date:lastDate
-            id:lastId
-          correct:firstDate>lastDate
-
-        _rollbar.push(msg:"duplicate entries debug", timeStamps:timeStamps, level:"debug")
-        # DEBUG: duplicate entries in activity feed
+        # memberbucket data has no serverside model it comes from cache
+        # so it has no meta, that's why we check its date by its overview
+        lastDate = if lastItemData.createdAtTimestamps
+          new Date lastItemData.createdAtTimestamps.first
+        else
+          new Date lastItemData.meta.createdAt
       else
         lastDate = new Date
-        _rollbar.push(msg:"could not find date", date:lastDate)

       lastTimeStamp = lastDate.getTime()
-
       @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->


Fixed placeholder text in auto complete

Fixed placeholder text in auto complete

--- core/app/backbone/views/channels/auto_complete_channels_view.coffee
+++ core/app/backbone/views/channels/auto_complete_channels_view.coffee
@@ -13,11 +13,11 @@
       filter_on: 'slug_title'
       search_list_view: (options) -> new AutoCompleteSearchChannelsView(options)
       search_collection: -> new TopicSearchResults [], user: currentUser
-
-      if Factlink.Global.can_haz.topic_facts
-        placeholder: -> Factlink.Global.t.topic_name.capitalize()
-      else
-        placeholder: -> Factlink.Global.t.channel_name.capitalize()
+      placeholder: ->
+        if Factlink.Global.can_haz.topic_facts
+          Factlink.Global.t.topic_name.capitalize()
+        else
+          Factlink.Global.t.channel_name.capitalize()

     @_results_view = new AutoCompleteResultsChannelsView(collection: @collection)


switching a couple of the half_assignment tests to ensure that : and = are still interchangeable

--- test/test_half_assignments.coffee
+++ test/test_half_assignments.coffee
@@ -11,7 +11,7 @@

 ok num is 3

-num: * 10
+num = * 10

 ok num is 30

@@ -25,7 +25,7 @@

 ok val is 'value'

-val: and 'other'
+val = and 'other'

 ok val is 'other'


Better handling of workflows on project edit page

--- app/pages/edit-project.cjsx
+++ app/pages/edit-project.cjsx
@@ -1,4 +1,5 @@
 React = require 'react'
+PromiseToSetState = require '../lib/promise-to-set-state'
 ChangeListener = require '../components/change-listener'
 PromiseRenderer = require '../components/promise-renderer'
 auth = require '../api/auth'
@@ -8,11 +9,31 @@
 InPlaceForm = require '../components/in-place-form'
 MarkdownEditor = require '../components/markdown-editor'

+HandlePropChanges =
+  componentDidMount: ->
+    for prop, handler of @propChangeHandlers ? {}
+      handler.call this, @props[prop]
+
+  componentWillReceiveProps: (nextProps) ->
+    for prop, handler of @propChangeHandlers ? {}
+      unless nextProps[prop] is @props[prop]
+        handler.call this, nextProps[prop]
+
 ProjectEditPage = React.createClass
   displayName: 'EditProjectPage'

+  mixins: [HandlePropChanges, PromiseToSetState]
+
+  getDefaultProps: ->
+    project: null
+
   getInitialState: ->
+    workflows: []
     busy: false
+
+  propChangeHandlers:
+    project: (project) ->
+      @promiseToSetState workflows: project.link 'workflows'

   render: ->
     handleProjectChange = handleInputChange.bind @props.project
@@ -41,13 +62,12 @@
                 <MarkdownEditor name="introduction" value={@props.project.introduction} onChange={handleProjectChange} />

                 Workflows

-                <PromiseRenderer promise={@props.project.link 'workflows'}>{(error, workflows) =>
-                  if workflows?
-

-                      {for workflow in workflows
-
<Link to="edit-workflow" params={id: workflow.id}>{workflow.display_name}</Link>
}
-

-                }</PromiseRenderer>
+

+                  {for workflow in @state.workflows
+                    <ChangeListener key={workflow.id} target={workflow}>{=>
+
<Link to="edit-workflow" params={id: workflow.id}>{workflow.display_name}</Link>

+                    }</ChangeListener>}
+

                 <button type="submit" disabled={@state.busy or not @props.project.hasUnsavedChanges()}>Save</button>

                 <button type="button" onClick={@handleDelete}>Delete this project</button>

@@ -68,7 +88,6 @@
         @setState busy: false

   handleDelete: ->
-    # TODO: Make this nicer.
     if prompt('Enter REALLY to delete this project forever.')?.toUpperCase() is 'REALLY'
       @setState busy: true, =>
         @props.project.delete().then =>


fixed to subjectSet.id

fixed to subjectSet.id

fixed to subjectSet.id

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -318,7 +318,7 @@
               toggle = @handleSubjectSetToggle.bind this, subjectSet

                 <input type="checkbox" checked={assigned} onChange={toggle} />
-                {subjectSet.display_name} (#{@props.subjectSet.id})
+                {subjectSet.display_name} (#{subjectSet.id})

}


+ ExpressionStack: tested evaluation after adding operand

--- test/unit/expression-stack.coffee
+++ test/unit/expression-stack.coffee
@@ -15,7 +15,7 @@
   describe ".evaluate()", ->
     expectedMessage = ".evaluate() called on empty syntax tree"
     it "throws Error('#{expectedMessage}')", ->
-      shouldThrow expectedMessage, - testedStack.evaluate()
+      shouldThrow expectedMessage, -> testedStack.evaluate()

   describe ".pop()", ->
     expectedMessage = ".pop() called at the bottom of the stack"
@@ -43,3 +43,10 @@
       it ".stackName is 'bottom'", ->
         testedStack.stackName.should.equal "bottom"

+  describe "after .addOperand(() => true)", ->
+    beforeEach ->
+      testedStack.addOperand () -> true
+
+    it ".evaluate() returns true", ->
+      testedStack.evaluate().should.equal true
+


OX-197 performance testing

OX-197 performance testing

--- app/assets/javascripts/model/binary/pullqueue.coffee
+++ app/assets/javascripts/model/binary/pullqueue.coffee
@@ -171,7 +171,7 @@

     new ArrayBufferSocket(
       senders : [
-        #new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
+        new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
         new ArrayBufferSocket.XmlHttpRequest("/binary/ajax?dataSetId=#{@dataSetId}&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
       ]
       requestBufferType : Float32Array


Bail out as early as possible when attempting to wrap selections

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- lib/bracket-matcher.coffee
+++ lib/bracket-matcher.coffee
@@ -127,8 +127,6 @@
     bracketsRemoved

   wrapSelectionInBrackets: (bracket) ->
-    return false unless @getScopedSetting('bracket-matcher.wrapSelectionsInBrackets')
-
     if bracket is '#'
       return false unless @isCursorOnInterpolatedString()
       bracket = '#{'
@@ -136,6 +134,9 @@
     else
       return false unless @isOpeningBracket(bracket)
       pair = @matchManager.pairedCharacters[bracket]
+
+    return false unless @editor.selections.some((s) -> not s.isEmpty())
+    return false unless @getScopedSetting('bracket-matcher.wrapSelectionsInBrackets')

     selectionWrapped = false
     @editor.mutateSelectedText (selection) ->


level up function

level up function

--- app/services/pokemon.coffee
+++ app/services/pokemon.coffee
@@ -1,9 +1,13 @@
 `import breeds from '../helpers/breeds'`

-breedDescription = (name) ->
-    if not breeds.hasOwnProperty name
-        throw error: "Unknown Breed: #{name}"
-    breeds[name]
+breedDescription = (pokemon) ->
+    if typeof pokemon is 'object' and pokemon.get?
+        pokemon = pokemon.get 'breed'
+
+    if not breeds.hasOwnProperty pokemon
+        throw error: "Unknown Breed: #{pokemon}"
+
+    breeds[pokemon]

 statNames = [
     'hp'
@@ -45,11 +49,20 @@
         genes = randomGenes()

         pokemon = @store.createRecord 'pokemon', { level, breed, stats, genes }
-
         @levelUp pokemon for [1..level]

     levelUp: (pokemon) ->
+        changes = { }
+        breed = breedDescription pokemon

+        Ember.changeProperties =>
+            for stat in statNames
+                bonus = breed.stats[stat] / 50
+                bonus += 1 if stat is 'hp'
+                pokemon.stats[stat] += bonus
+                changes[stat] = bonus
+
+        changes

 `export default PokemonService`


Lazy-load the sdk template props

Lazy-load the sdk template props

Change-type: patch

--- lib/balena.coffee
+++ lib/balena.coffee
@@ -65,10 +65,10 @@
 	# @memberof balena
 	###
 	sdkTemplate =
-		auth: require('./auth')
-		models: require('./models')
-		logs: require('./logs')
-		settings: require('./settings')
+		auth: -> require('./auth')
+		models: -> require('./models')
+		logs: -> require('./logs')
+		settings: -> require('./settings')

 	defaults opts,
 		apiUrl: 'https://api.balena-cloud.com/'
@@ -104,8 +104,17 @@
 		sdkInstance: sdk
 	}

-	for moduleName, moduleFactory of sdkTemplate
-		sdk[moduleName] = moduleFactory(deps, opts)
+	Object.keys(sdkTemplate).forEach (moduleName) ->
+		Object.defineProperty(sdk, moduleName, {
+			enumerable: true,
+			configurable: true,
+			get: ->
+				moduleFactory = sdkTemplate[moduleName]()
+				# We need the delete first as the current property is read-only
+				# and the delete removes that restriction
+				delete this[moduleName]
+				return (this[moduleName] = moduleFactory(deps, opts))
+		})

 	###*
 	# @typedef Interceptor


Remove require directive from component JS

--- app/cells/markdown_readonly/markdown_readonly.coffee
+++ app/cells/markdown_readonly/markdown_readonly.coffee
@@ -1,5 +1,3 @@
-#= require jquery
-
 $(document).on 'uic:domchange', (e) ->
   $(e.target).find('[data-toggle="markdown-readonly"]').each (_, el) ->
     $el = $(el)


Remove debug logs

Remove debug logs

--- app/src/fup/versatile_firmware_update_request.coffee
+++ app/src/fup/versatile_firmware_update_request.coffee
@@ -17,8 +17,6 @@
     super

   _setCurrentState: (state) ->
-    l "State ", state,  ledger.fup.FirmwareUpdateRequest.States.LoadingBootloader, ledger.fup.FirmwareUpdateRequest.States.LoadingBootloaderReloader
     if state is ledger.fup.FirmwareUpdateRequest.States.LoadingBootloader or ledger.fup.FirmwareUpdateRequest.States.LoadingBootloaderReloader
-      l "USE SETUP"
       @_osLoader = 'SETUP_OS_LOADER'
     super


Include protocol in urls

Include protocol in urls

--- src/scripts/modules/header/header.coffee
+++ src/scripts/modules/header/header.coffee
@@ -12,11 +12,13 @@
       url: @url
     }

-    initialize: (options) ->
+    initialize: (options = {}) ->
       super()
-      @page = options?.page
-      @url = options?.url or 'content'
+      @page = options.page
+      @url = @createLink(options.url or 'content')

     setLegacyLink: (url) ->
-      @url = "#{settings.legacy}/#{url}"
+      @url = @createLink(url)
       @render()
+
+    createLink: (url) -> "#{location.protocol}//#{settings.legacy}/#{url}"


Replace 'getValue' with underscore's _.result method

--- src/lib/sync.coffee
+++ src/lib/sync.coffee
@@ -6,12 +6,6 @@
   update: 'POST'
   delete: 'POST'
   read: 'GET'
-
-getValue = (object, prop) ->
-  if val = object?[prop]
-    if _.isFunction(val) then val() else val
-  else
-    null

 module.exports = class Sync

@@ -34,7 +28,7 @@

   sync: (method, model, options) =>

-    url = @buildUrl options.url ? getValue model, 'url'
+    url = @buildUrl options.url ? _.result model, 'url'

     if not options.data and model and method in ['create', 'update']


Change file to soft tabs.

Change file to soft tabs.

--- lib/completions-jsx.coffee
+++ lib/completions-jsx.coffee
@@ -1,693 +1,693 @@
 module.exports =

 events: [
-	{ name: 'onCopy' }
-	{ name: 'onCut' }
-	{ name: 'onPaste' }
-	{ name: 'onCompositionEnd' }
-	{ name: 'onCompositionStart' }
-	{ name: 'onCompositionUpdate' }
-	{ name: 'onKeyDown' }
-	{ name: 'onKeyPress' }
-	{ name: 'onKeyUp' }
-	{ name: 'onFocus' }
-	{ name: 'onBlur' }
-	{ name: 'onChange' }
-	{ name: 'onInput' }
-	{ name: 'onSubmit' }
-	{ name: 'onClick' }
-	{ name: 'onContextMenu' }
-	{ name: 'onDoubleClick' }
-	{ name: 'onDrag' }
-	{ name: 'onDragEnd' }
-	{ name: 'onDragEnter' }
-	{ name: 'onDragExit' }
-	{ name: 'onDragLeave' }
-	{ name: 'onDragOver' }
-	{ name: 'onDragStart' }
-	{ name: 'onDrop' }
-	{ name: 'onMouseDown' }
-	{ name: 'onMouseEnter' }
-	{ name: 'onMouseLeave' }
-	{ name: 'onMouseMove' }
-	{ name: 'onMouseOut' }
-	{ name: 'onMouseOver' }
-	{ name: 'onMouseUp' }
-	{ name: 'onSelect' }
-	{ name: 'onTouchCancel' }
-	{ name: 'onTouchEnd' }
-	{ name: 'onTouchMove' }
-	{ name: 'onTouchStart' }
-	{ name: 'onScroll' }
-	{ name: 'onWheel' }
-	{ name: 'onAbort' }
-	{ name: 'onCanPlay' }
-	{ name: 'onCanPlayThrough' }
-	{ name: 'onDurationChange' }
-	{ name: 'onEmptied' }
-	{ name: 'onEncrypted' }
-	{ name: 'onEnded' }
-	{ name: 'onError' }
-	{ name: 'onLoadedData' }
-	{ name: 'onLoadedMetadata' }
-	{ name: 'onLoadStart' }
-	{ name: 'onPause' }
-	{ name: 'onPlay' }
-	{ name: 'onPlaying' }
-	{ name: 'onProgress' }
-	{ name: 'onRateChange' }
-	{ name: 'onSeeked' }
-	{ name: 'onSeeking' }
-	{ name: 'onStalled' }
-	{ name: 'onSuspend' }
-	{ name: 'onTimeUpdate' }
-	{ name: 'onVolumeChange' }
-	{ name: 'onWaiting' }
+  { name: 'onCopy' }
+  { name: 'onCut' }
+  { name: 'onPaste' }
+  { name: 'onCompositionEnd' }
+  { name: 'onCompositionStart' }
+  { name: 'onCompositionUpdate' }
+  { name: 'onKeyDown' }
+  { name: 'onKeyPress' }
+  { name: 'onKeyUp' }
+  { name: 'onFocus' }
+  { name: 'onBlur' }
+  { name: 'onChange' }
+  { name: 'onInput' }
+  { name: 'onSubmit' }
+  { name: 'onClick' }
+  { name: 'onContextMenu' }
+  { name: 'onDoubleClick' }
+  { name: 'onDrag' }
+  { name: 'onDragEnd' }
+  { name: 'onDragEnter' }
+  { name: 'onDragExit' }
+  { name: 'onDragLeave' }
+  { name: 'onDragOver' }
+  { name: 'onDragStart' }
+  { name: 'onDrop' }
+  { name: 'onMouseDown' }
+  { name: 'onMouseEnter' }
+  { name: 'onMouseLeave' }
+  { name: 'onMouseMove' }
+  { name: 'onMouseOut' }
+  { name: 'onMouseOver' }
+  { name: 'onMouseUp' }
+  { name: 'onSelect' }
+  { name: 'onTouchCancel' }
+  { name: 'onTouchEnd' }
+  { name: 'onTouchMove' }
+  { name: 'onTouchStart' }
+  { name: 'onScroll' }
+  { name: 'onWheel' }
+  { name: 'onAbort' }
+  { name: 'onCanPlay' }
+  { name: 'onCanPlayThrough' }
+  { name: 'onDurationChange' }
+  { name: 'onEmptied' }
+  { name: 'onEncrypted' }
+  { name: 'onEnded' }
+  { name: 'onError' }
+  { name: 'onLoadedData' }
+  { name: 'onLoadedMetadata' }
+  { name: 'onLoadStart' }
+  { name: 'onPause' }
+  { name: 'onPlay' }
+  { name: 'onPlaying' }
+  { name: 'onProgress' }
+  { name: 'onRateChange' }
+  { name: 'onSeeked' }
+  { name: 'onSeeking' }
+  { name: 'onStalled' }
+  { name: 'onSuspend' }
+  { name: 'onTimeUpdate' }
+  { name: 'onVolumeChange' }
+  { name: 'onWaiting' }
 ]

 globalAttributes: [
-	{ name: 'className'}
-	{ name: 'class'}
-	{ name: 'id'}
-	{ name: 'itemProp'}
-	{ name: 'itemScope'}
-	{ name: 'itemType'}
-	{ name: 'itemID'}
-	{ name: 'dangerouslySetInnerHTML'}
-	{ name: 'data-'}
-	{ name: 'aria-'}
-	{ name: 'style'}
-        { name: 'accessKey'}
-        { name: 'contentEditable'}
-        { name: 'contextMenu'}
-        { name: 'dir'}
-        { name: 'draggable'}
-        { name: 'hidden'}
-        { name: 'lang'}
-        { name: 'spellCheck'}
-        { name: 'tabIndex'}
-        { name: 'title'}
+  { name: 'className'}
+  { name: 'class'}
+  { name: 'id'}
+  { name: 'itemProp'}
+  { name: 'itemScope'}
+  { name: 'itemType'}
+  { name: 'itemID'}
+  { name: 'dangerouslySetInnerHTML'}
+  { name: 'data-'}
+  { name: 'aria-'}
+  { name: 'style'}
+  { name: 'accessKey'}
+  { name: 'contentEditable'}
+  { name: 'contextMenu'}
+  { name: 'dir'}
+  { name: 'draggable'}
+  { name: 'hidden'}
+  { name: 'lang'}
+  { name: 'spellCheck'}
+  { name: 'tabIndex'}
+  { name: 'title'}
 ]

 htmlElements: [
-	{ name:	'a'
-	attributes: [
-		{ name:	'href'}
-		{ name:	'target'}
-		{ name:	'download'}
-		{ name:	'rel'}
-		{ name:	'hrefLang'}
-		{ name:	'type'}
-	]}
-	{ name:	'abbr'
-	attributes: []}
-	{ name:	'address'
-	attributes: []}
-	{ name:	'area'
-	attributes: [
-		{ name:	'alt'}
-		{ name:	'coords'}
-		{ name:	'href'}
-		{ name:	'hrefLang'}
-		{ name:	'media'}
-		{ name:	'rel'}
-		{ name:	'shape'}
-		{ name:	'target'}
-		{ name:	'type'}
-	]}
-	{ name:	'article'
-	attributes: []}
-	{ name:	'aside'
-	attributes: []}
-	{ name:	'audio'
-	attributes: [
-		{ name:	'autoPlay'}
-		{ name:	'controls'}
-		{ name:	'crossOrigin'}
-		{ name:	'loop'}
-		{ name:	'mediaGroup'}
-		{ name:	'muted'}
-		{ name:	'preload'}
-		{ name:	'src'}
-	]}
-	{ name:	'b'
-	attributes: []}
-	{ name:	'base'
-	attributes: [
-		{ name:	'href'}
-		{ name:	'target'}
-	]}
-	{ name:	'bdi'
-	attributes: []}
-	{ name:	'bdo'
-	attributes: []}
-	{ name:	'big'
-	attributes: []}
-	{ name:	'blockquote'
-	attributes: []}
-	{ name:	'body'
-	attributes: []}
-	{ name:	'br'
-	attributes: []}
-	{ name:	'button'
-	attributes: [
-		{ name:	'autoFocus'}
-		{ name:	'disabled'}
-		{ name:	'form'}
-		{ name:	'formAction'}
-		{ name:	'formEncType'}
-		{ name:	'formMethod'}
-		{ name:	'formNoValidate'}
-		{ name:	'formTarget'}
-		{ name:	'name'}
-		{ name:	'type'}
-		{ name:	'value'}
-	]}
-	{ name:	'canvas'
-	attributes: [
-		{ name:	'height'}
-		{ name:	'width'}
-	]}
-	{ name:	'caption'
-	attributes: []}
-	{ name:	'cite'
-	attributes: []}
-	{ name:	'code'
-	attributes: []}
-	{ name:	'col'
-	attributes: [
-		{ name:	'span'}
-	]}
-	{ name:	'colgroup'
-	attributes: [
-		{ name:	'span'}
-	]}
-	{ name:	'data'
-	attributes: []}
-	{ name:	'datalist'
-	attributes: []}
-	{ name:	'dd'
-	attributes: []}
-	{ name:	'del'
-	attributes: [
-		{ name:	'dateTime'}
-	]}
-	{ name:	'details'
-	attributes: [
-		{ name:	'open'}
-	]}
-	{ name:	'dfn'
-	attributes: [
-		{ name:	'open'}
-	]}
-	{ name:	'dialog'
-	attributes: []}
-	{ name:	'div'
-	attributes: []}
-	{ name:	'dl'
-	attributes: []}
-	{ name:	'dt'
-	attributes: []}
-	{ name:	'em'
-	attributes: []}
-	{ name:	'embed'
-	attributes: [
-		{ name:	'height'}
-		{ name:	'src'}
-		{ name:	'type'}
-		{ name:	'width'}
-	]}
-	{ name:	'fieldset'
-	attributes: [
-		{ name:	'disabled'}
-		{ name:	'form'}
-		{ name:	'name'}
-	]}
-	{ name:	'figcaption'
-	attributes: []}
-	{ name:	'figure'
-	attributes: []}
-	{ name:	'footer'
-	attributes: []}
-	{ name:	'form'
-	attributes: [
-		{ name:	'acceptCharset'}
-		{ name:	'action'}
-		{ name:	'autoComplete'}
-		{ name:	'encType'}
-		{ name:	'method'}
-		{ name:	'name'}
-		{ name:	'noValidate'}
-		{ name:	'target'}
-	]}
-	{ name:	'h1'
-	attributes: []}
-	{ name:	'h2'
-	attributes: []}
-	{ name:	'h3'
-	attributes: []}
-	{ name:	'h4'
-	attributes: []}
-	{ name:	'h5'
-	attributes: []}
-	{ name:	'h6'
-	attributes: []}
-	{ name:	'head'
-	attributes: []}
-	{ name:	'header'
-	attributes: []}
-	{ name:	'hr'
-	attributes: []}
-	{ name:	'html'
-	attributes: [
-		{ name:	'manifest'}
-		{ name:	'lang'}
-	]}
-	{ name:	'i'
-	attributes: []}
-	{ name:	'iframe'
-	attributes: [
-		{ name:	'height'}
-		{ name:	'name'}
-		{ name:	'sandbox'}
-		{ name:	'seamless'}
-		{ name:	'src'}
-		{ name:	'srcDoc'}
-		{ name:	'width'}
-	]}
-	{ name:	'img'
-	attributes: [
-		{ name:	'alt'}
-		{ name:	'height'}
-		{ name:	'src'}
-		{ name:	'usemap'}
-		{ name:	'width'}
-	]}
-	{ name:	'input'
-	attributes: [
-		{ name:	'accept'}
-		{ name:	'alt'}
-		{ name:	'autoComplete'}
-		{ name:	'autoFocus'}
-		{ name:	'checked'}
-		{ name:	'disabled'}
-		{ name:	'form'}
-		{ name:	'formAction'}
-		{ name:	'formEncType'}
-		{ name:	'formMethod'}
-		{ name:	'formNoValidate'}
-		{ name:	'formTarget'}
-		{ name:	'height'}
-		{ name:	'list'}
-		{ name:	'max'}
-		{ name:	'maxLength'}
-		{ name:	'min'}
-		{ name:	'multiple'}
-		{ name:	'name'}
-		{ name:	'pattern'}
-		{ name:	'placeholder'}
-		{ name:	'readOnly'}
-		{ name:	'required'}
-		{ name:	'size'}
-		{ name:	'src'}
-		{ name:	'step'}
-		{ name:	'type'}
-		{ name:	'value'}
-		{ name:	'width'}
-	]}
-	{ name:	'ins'
-	attributes: [
-		{ name:	'dateTime'}
-	]}
-	{ name:	'kbd'
-	attributes: []}
-	{ name:	'keygen'
-	attributes: [
-		{ name:	'autoFocus'}
-		{ name:	'challenge'}
-		{ name:	'disabled'}
-		{ name:	'form'}
-		{ name:	'keyType'}
-		{ name:	'name'}
-	]}
-	{ name:	'label'
-	attributes: [
-		{ name:	'htmlFor'}
-		{ name:	'form'}
-	]}
-	{ name:	'legend'
-	attributes: [
-		{ name:	'value'}
-	]}
-	{ name:	'li'
-	attributes: []}
-	{ name:	'link'
-	attributes: [
-		{ name:	'disabled'}
-		{ name:	'href'}
-		{ name:	'hrefLang'}
-		{ name:	'media'}
-		{ name:	'rel'}
-		{ name:	'sizes'}
-		{ name:	'type'}
-	]}
-	{ name:	'main'
-	attributes: []}
-	{ name:	'map'
-	attributes: [
-		{ name:	'name'}
-	]}
-	{ name:	'mark'
-	attributes: []}
-	{ name:	'menu'
-	attributes: [
-		{ name:	'label'}
-		{ name:	'type'}
-	]}
-	{ name:	'menuitem'
-	attributes: []}
-	{ name:	'meta'
-	attributes: [
-		{ name:	'charset'}
-		{ name:	'content'}
-		{ name:	'httpEquiv'}
-		{ name:	'name'}
-	]}
-	{ name:	'meter'
-	attributes: []}
-	{ name:	'nav'
-	attributes: []}
-	{ name:	'noscript'
-	attributes: []}
-	{ name:	'object'
-	attributes: [
-		{ name:	'data'}
-		{ name:	'form'}
-		{ name:	'height'}
-		{ name:	'name'}
-		{ name:	'type'}
-		{ name:	'usemap'}
-		{ name:	'width'}
-	]}
-	{ name:	'ol'
-	attributes: [
-		{ name:	'start'}
-		{ name:	'type'}
-	]}
-	{ name:	'optgroup'
-	attributes: [
-		{ name:	'disabled'}
-		{ name:	'label'}
-	]}
-	{ name:	'option'
-	attributes: [
-		{ name:	'disabled'}
-		{ name:	'label'}
-		{ name:	'selected'}
-		{ name:	'value'}
-	]}
-	{ name:	'output'
-	attributes: [
-		{ name:	'htmlFor'}
-		{ name:	'form'}
-		{ name:	'name'}
-	]}
-	{ name:	'p'
-	attributes: []}
-	{ name:	'param'
-	attributes: [
-		{ name:	'name'}
-		{ name:	'value'}
-	]}
-	{ name:	'picture'
-	attributes: []}
-	{ name:	'pre'
-	attributes: []}
-	{ name:	'progress'
-	attributes: [
-		{ name:	'form'}
-		{ name:	'max'}
-		{ name:	'value'}
-	]}
-	{ name:	'q'
-	attributes: []}
-	{ name:	'rp'
-	attributes: []}
-	{ name:	'rt'
-	attributes: []}
-	{ name:	'ruby'
-	attributes: []}
-	{ name:	's'
-	attributes: []}
-	{ name:	'samp'
-	attributes: []}
-	{ name:	'script'
-	attributes: [
-		{ name:	'async'}
-		{ name:	'charSet'}
-		{ name:	'defer'}
-		{ name:	'src'}
-		{ name:	'type'}
-	]}
-	{ name:	'section'
-	attributes: []}
-	{ name:	'select'
-	attributes: [
-		{ name:	'autoFocus'}
-		{ name:	'disabled'}
-		{ name:	'form'}
-		{ name:	'multiple'}
-		{ name:	'name'}
-		{ name:	'required'}
-		{ name:	'size'}
-	]}
-	{ name:	'small'
-	attributes: []}
-	{ name:	'source'
-	attributes: [
-		{ name:	'media'}
-		{ name:	'src'}
-		{ name:	'type'}
-	]}
-	{ name:	'span'
-	attributes: []}
-	{ name:	'strong'
-	attributes: []}
-	{ name:	'style'
-	attributes: [
-		{ name:	'disabled'}
-		{ name:	'media'}
-		{ name:	'scoped'}
-		{ name:	'type'}
-	]}
-	{ name:	'sub'
-	attributes: []}
-	{ name:	'summary'
-	attributes: []}
-	{ name:	'sup'
-	attributes: []}
-	{ name:	'table'
-	attributes: []}
-	{ name:	'tbody'
-	attributes: []}
-	{ name:	'td'
-	attributes: [
-		{ name:	'colSpan'}
-		{ name:	'headers'}
-		{ name:	'rowSpan'}
-	]}
-	{ name:	'textarea'
-	attributes: [
-		{ name:	'autoFocus'}
-		{ name:	'cols'}
-		{ name:	'disabled'}
-		{ name:	'form'}
-		{ name:	'label'}
-		{ name:	'maxLength'}
-		{ name:	'name'}
-		{ name:	'placeholder'}
-		{ name:	'readonly'}
-		{ name:	'required'}
-		{ name:	'rows'}
-		{ name:	'wrap'}
-	]}
-	{ name:	'tfoot'
-	attributes: []}
-	{ name:	'th'
-	attributes: [
-		{ name:	'colSpan'}
-		{ name:	'headers'}
-		{ name:	'rowSpan'}
-		{ name:	'scope'}
-	]}
-	{ name:	'thead'
-	attributes: []}
-	{ name:	'time'
-	attributes: [
-		{ name:	'dateTime'}
-	]}
-	{ name:	'title'
-	attributes: []}
-	{ name:	'tr'
-	attributes: []}
-	{ name:	'track'
-	attributes: [
-		{ name:	'default'}
-		{ name:	'label'}
-		{ name:	'src'}
-	]}
-	{ name:	'u'
-	attributes: []}
-	{ name:	'ul'
-	attributes: []}
-	{ name:	'var'
-	attributes: []}
-	{ name:	'video'
-	attributes: [
-		{ name:	'autoPlay'}
-		{ name:	'controls'}
-		{ name:	'height'}
-		{ name:	'loop'}
-		{ name:	'mediaGroup'}
-		{ name:	'muted'}
-		{ name:	'poster'}
-		{ name:	'preload'}
-		{ name:	'src'}
-		{ name:	'width'}
-	]}
-	{ name:	'wbr'
-	attributes: []}
-	# svg elements
-	{ name:	'circle'
-	attributes: [
-		{ name:	'cx'}
-		{ name:	'cy'}
-		{ name:	'r'}
-		{ name:	'stroke'}
-		{ name:	'strokeWidth'}
-		{ name:	'fill'}
-	]}
-	{ name:	'clipPath'
-	attributes: [
-		{ name:	'clipPath'}
-	]}
-	{ name:	'defs'
-	attributes: []}
-	{ name:	'ellipse'
-	attributes: [
-		{ name:	'cx'}
-		{ name:	'cy'}
-		{ name:	'rx'}
-		{ name:	'ry'}
-	]}
-	{ name:	'g'
-	attributes: [
-		{ name:	'fill'}
-		{ name:	'opacity'}
-	]}
-	{ name:	'line'
-	attributes: [
-		{ name:	'x1'}
-		{ name:	'x2'}
-		{ name:	'y1'}
-		{ name:	'y2'}
-	]}
-	{ name:	'linearGradient'
-	attributes: [
-		{ name:	'x1'}
-		{ name:	'x2'}
-		{ name:	'y1'}
-		{ name:	'y2'}
-		{ name:	'spreadMethod'}
-		{ name:	'xlinkHref'}
-	]}
-	{ name:	'mask'
-	attributes: [
-		{ name:	'mashUnits'}
-		{ name:	'x'}
-		{ name:	'y'}
-		{ name:	'width'}
-		{ name:	'height'}
-	]}
-	{ name:	'path'
-	attributes: [
-		{ name:	'd'}
-		{ name:	'transform'}
-	]}
-	{ name:	'pattern'
-	attributes: [
-		{ name:	'patternContentUnits'}
-		{ name:	'x'}
-		{ name:	'y'}
-		{ name:	'width'}
-		{ name:	'height'}
-		{ name:	'viewBox'}
-		{ name:	'xlinkHref'}
-	]}
-	{ name:	'polygon'
-	attributes: [
-		{ name:	'points'}
-	]}
-	{ name:	'polyline'
-	attributes: [
-		{ name:	'points'}
-	]}
-	{ name:	'radialGradient'
-	attributes: [
-		{ name:	'gradientUnits'}
-		{ name:	'gradientTransform'}
-		{ name:	'cx'}
-		{ name:	'cy'}
-		{ name:	'r'}
-		{ name:	'fx'}
-		{ name:	'fy'}
-		{ name:	'spreadMethod'}
-		{ name:	'xlinkHref'}
-	]}
-	{ name:	'rect'
-	attributes: [
-		{ name:	'x'}
-		{ name:	'y'}
-		{ name:	'rx'}
-		{ name:	'ry'}
-		{ name:	'width'}
-		{ name:	'height'}
-	]}
-	{ name:	'stop'
-	attributes: [
-		{ name:	'offset'}
-		{ name:	'stopColor'}
-		{ name:	'stopOpacity'}
-	]}
-	{ name:	'svg'
-	attributes: [
-		{ name:	'x'}
-		{ name:	'y'}
-		{ name:	'width'}
-		{ name:	'height'}
-		{ name:	'viewBox'}
-		{ name:	'preserveAspectRatio'}
-		{ name:	'zoomAndPan'}
-	]}
-	{ name:	'text'
-	attributes: [
-		{ name:	'x'}
-		{ name:	'y'}
-		{ name:	'dx'}
-		{ name:	'dy'}
-		{ name:	'rotate'}
-		{ name:	'textLength'}
-		{ name:	'lengthAdjust'}
-	]}
-	{ name:	'tspan'
-	attributes: [
-		{ name:	'x'}
-		{ name:	'y'}
-		{ name:	'dx'}
-		{ name:	'dy'}
-		{ name:	'rotate'}
-		{ name:	'textLength'}
-		{ name:	'lengthAdjust'}
-		{ name:	'xlinkHref'}
-	]}
+  { name:	'a'
+  attributes: [
+    { name:	'href'}
+    { name:	'target'}
+    { name:	'download'}
+    { name:	'rel'}
+    { name:	'hrefLang'}
+    { name:	'type'}
+  ]}
+  { name:	'abbr'
+  attributes: []}
+  { name:	'address'
+  attributes: []}
+  { name:	'area'
+  attributes: [
+    { name:	'alt'}
+    { name:	'coords'}
+    { name:	'href'}
+    { name:	'hrefLang'}
+    { name:	'media'}
+    { name:	'rel'}
+    { name:	'shape'}
+    { name:	'target'}
+    { name:	'type'}
+  ]}
+  { name:	'article'
+  attributes: []}
+  { name:	'aside'
+  attributes: []}
+  { name:	'audio'
+  attributes: [
+    { name:	'autoPlay'}
+    { name:	'controls'}
+    { name:	'crossOrigin'}
+    { name:	'loop'}
+    { name:	'mediaGroup'}
+    { name:	'muted'}
+    { name:	'preload'}
+    { name:	'src'}
+  ]}
+  { name:	'b'
+  attributes: []}
+  { name:	'base'
+  attributes: [
+    { name:	'href'}
+    { name:	'target'}
+  ]}
+  { name:	'bdi'
+  attributes: []}
+  { name:	'bdo'
+  attributes: []}
+  { name:	'big'
+  attributes: []}
+  { name:	'blockquote'
+  attributes: []}
+  { name:	'body'
+  attributes: []}
+  { name:	'br'
+  attributes: []}
+  { name:	'button'
+  attributes: [
+    { name:	'autoFocus'}
+    { name:	'disabled'}
+    { name:	'form'}
+    { name:	'formAction'}
+    { name:	'formEncType'}
+    { name:	'formMethod'}
+    { name:	'formNoValidate'}
+    { name:	'formTarget'}
+    { name:	'name'}
+    { name:	'type'}
+    { name:	'value'}
+  ]}
+  { name:	'canvas'
+  attributes: [
+    { name:	'height'}
+    { name:	'width'}
+  ]}
+  { name:	'caption'
+  attributes: []}
+  { name:	'cite'
+  attributes: []}
+  { name:	'code'
+  attributes: []}
+  { name:	'col'
+  attributes: [
+    { name:	'span'}
+  ]}
+  { name:	'colgroup'
+  attributes: [
+    { name:	'span'}
+  ]}
+  { name:	'data'
+  attributes: []}
+  { name:	'datalist'
+  attributes: []}
+  { name:	'dd'
+  attributes: []}
+  { name:	'del'
+  attributes: [
+    { name:	'dateTime'}
+  ]}
+  { name:	'details'
+  attributes: [
+    { name:	'open'}
+  ]}
+  { name:	'dfn'
+  attributes: [
+    { name:	'open'}
+  ]}
+  { name:	'dialog'
+  attributes: []}
+  { name:	'div'
+  attributes: []}
+  { name:	'dl'
+  attributes: []}
+  { name:	'dt'
+  attributes: []}
+  { name:	'em'
+  attributes: []}
+  { name:	'embed'
+  attributes: [
+    { name:	'height'}
+    { name:	'src'}
+    { name:	'type'}
+    { name:	'width'}
+  ]}
+  { name:	'fieldset'
+  attributes: [
+    { name:	'disabled'}
+    { name:	'form'}
+    { name:	'name'}
+  ]}
+  { name:	'figcaption'
+  attributes: []}
+  { name:	'figure'
+  attributes: []}
+  { name:	'footer'
+  attributes: []}
+  { name:	'form'
+  attributes: [
+    { name:	'acceptCharset'}
+    { name:	'action'}
+    { name:	'autoComplete'}
+    { name:	'encType'}
+    { name:	'method'}
+    { name:	'name'}
+    { name:	'noValidate'}
+    { name:	'target'}
+  ]}
+  { name:	'h1'
+  attributes: []}
+  { name:	'h2'
+  attributes: []}
+  { name:	'h3'
+  attributes: []}
+  { name:	'h4'
+  attributes: []}
+  { name:	'h5'
+  attributes: []}
+  { name:	'h6'
+  attributes: []}
+  { name:	'head'
+  attributes: []}
+  { name:	'header'
+  attributes: []}
+  { name:	'hr'
+  attributes: []}
+  { name:	'html'
+  attributes: [
+    { name:	'manifest'}
+    { name:	'lang'}
+  ]}
+  { name:	'i'
+  attributes: []}
+  { name:	'iframe'
+  attributes: [
+    { name:	'height'}
+    { name:	'name'}
+    { name:	'sandbox'}
+    { name:	'seamless'}
+    { name:	'src'}
+    { name:	'srcDoc'}
+    { name:	'width'}
+  ]}
+  { name:	'img'
+  attributes: [
+    { name:	'alt'}
+    { name:	'height'}
+    { name:	'src'}
+    { name:	'usemap'}
+    { name:	'width'}
+  ]}
+  { name:	'input'
+  attributes: [
+    { name:	'accept'}
+    { name:	'alt'}
+    { name:	'autoComplete'}
+    { name:	'autoFocus'}
+    { name:	'checked'}
+    { name:	'disabled'}
+    { name:	'form'}
+    { name:	'formAction'}
+    { name:	'formEncType'}
+    { name:	'formMethod'}
+    { name:	'formNoValidate'}
+    { name:	'formTarget'}
+    { name:	'height'}
+    { name:	'list'}
+    { name:	'max'}
+    { name:	'maxLength'}
+    { name:	'min'}
+    { name:	'multiple'}
+    { name:	'name'}
+    { name:	'pattern'}
+    { name:	'placeholder'}
+    { name:	'readOnly'}
+    { name:	'required'}
+    { name:	'size'}
+    { name:	'src'}
+    { name:	'step'}
+    { name:	'type'}
+    { name:	'value'}
+    { name:	'width'}
+  ]}
+  { name:	'ins'
+  attributes: [
+    { name:	'dateTime'}
+  ]}
+  { name:	'kbd'
+  attributes: []}
+  { name:	'keygen'
+  attributes: [
+    { name:	'autoFocus'}
+    { name:	'challenge'}
+    { name:	'disabled'}
+    { name:	'form'}
+    { name:	'keyType'}
+    { name:	'name'}
+  ]}
+  { name:	'label'
+  attributes: [
+    { name:	'htmlFor'}
+    { name:	'form'}
+  ]}
+  { name:	'legend'
+  attributes: [
+    { name:	'value'}
+  ]}
+  { name:	'li'
+  attributes: []}
+  { name:	'link'
+  attributes: [
+    { name:	'disabled'}
+    { name:	'href'}
+    { name:	'hrefLang'}
+    { name:	'media'}
+    { name:	'rel'}
+    { name:	'sizes'}
+    { name:	'type'}
+  ]}
+  { name:	'main'
+  attributes: []}
+  { name:	'map'
+  attributes: [
+    { name:	'name'}
+  ]}
+  { name:	'mark'
+  attributes: []}
+  { name:	'menu'
+  attributes: [
+    { name:	'label'}
+    { name:	'type'}
+  ]}
+  { name:	'menuitem'
+  attributes: []}
+  { name:	'meta'
+  attributes: [
+    { name:	'charset'}
+    { name:	'content'}
+    { name:	'httpEquiv'}
+    { name:	'name'}
+  ]}
+  { name:	'meter'
+  attributes: []}
+  { name:	'nav'
+  attributes: []}
+  { name:	'noscript'
+  attributes: []}
+  { name:	'object'
+  attributes: [
+    { name:	'data'}
+    { name:	'form'}
+    { name:	'height'}
+    { name:	'name'}
+    { name:	'type'}
+    { name:	'usemap'}
+    { name:	'width'}
+  ]}
+  { name:	'ol'
+  attributes: [
+    { name:	'start'}
+    { name:	'type'}
+  ]}
+  { name:	'optgroup'
+  attributes: [
+    { name:	'disabled'}
+    { name:	'label'}
+  ]}
+  { name:	'option'
+  attributes: [
+    { name:	'disabled'}
+    { name:	'label'}
+    { name:	'selected'}
+    { name:	'value'}
+  ]}
+  { name:	'output'
+  attributes: [
+    { name:	'htmlFor'}
+    { name:	'form'}
+    { name:	'name'}
+  ]}
+  { name:	'p'
+  attributes: []}
+  { name:	'param'
+  attributes: [
+    { name:	'name'}
+    { name:	'value'}
+  ]}
+  { name:	'picture'
+  attributes: []}
+  { name:	'pre'
+  attributes: []}
+  { name:	'progress'
+  attributes: [
+    { name:	'form'}
+    { name:	'max'}
+    { name:	'value'}
+  ]}
+  { name:	'q'
+  attributes: []}
+  { name:	'rp'
+  attributes: []}
+  { name:	'rt'
+  attributes: []}
+  { name:	'ruby'
+  attributes: []}
+  { name:	's'
+  attributes: []}
+  { name:	'samp'
+  attributes: []}
+  { name:	'script'
+  attributes: [
+    { name:	'async'}
+    { name:	'charSet'}
+    { name:	'defer'}
+    { name:	'src'}
+    { name:	'type'}
+  ]}
+  { name:	'section'
+  attributes: []}
+  { name:	'select'
+  attributes: [
+    { name:	'autoFocus'}
+    { name:	'disabled'}
+    { name:	'form'}
+    { name:	'multiple'}
+    { name:	'name'}
+    { name:	'required'}
+    { name:	'size'}
+  ]}
+  { name:	'small'
+  attributes: []}
+  { name:	'source'
+  attributes: [
+    { name:	'media'}
+    { name:	'src'}
+    { name:	'type'}
+  ]}
+  { name:	'span'
+  attributes: []}
+  { name:	'strong'
+  attributes: []}
+  { name:	'style'
+  attributes: [
+    { name:	'disabled'}
+    { name:	'media'}
+    { name:	'scoped'}
+    { name:	'type'}
+  ]}
+  { name:	'sub'
+  attributes: []}
+  { name:	'summary'
+  attributes: []}
+  { name:	'sup'
+  attributes: []}
+  { name:	'table'
+  attributes: []}
+  { name:	'tbody'
+  attributes: []}
+  { name:	'td'
+  attributes: [
+    { name:	'colSpan'}
+    { name:	'headers'}
+    { name:	'rowSpan'}
+  ]}
+  { name:	'textarea'
+  attributes: [
+    { name:	'autoFocus'}
+    { name:	'cols'}
+    { name:	'disabled'}
+    { name:	'form'}
+    { name:	'label'}
+    { name:	'maxLength'}
+    { name:	'name'}
+    { name:	'placeholder'}
+    { name:	'readonly'}
+    { name:	'required'}
+    { name:	'rows'}
+    { name:	'wrap'}
+  ]}
+  { name:	'tfoot'
+  attributes: []}
+  { name:	'th'
+  attributes: [
+    { name:	'colSpan'}
+    { name:	'headers'}
+    { name:	'rowSpan'}
+    { name:	'scope'}
+  ]}
+  { name:	'thead'
+  attributes: []}
+  { name:	'time'
+  attributes: [
+    { name:	'dateTime'}
+  ]}
+  { name:	'title'
+  attributes: []}
+  { name:	'tr'
+  attributes: []}
+  { name:	'track'
+  attributes: [
+    { name:	'default'}
+    { name:	'label'}
+    { name:	'src'}
+  ]}
+  { name:	'u'
+  attributes: []}
+  { name:	'ul'
+  attributes: []}
+  { name:	'var'
+  attributes: []}
+  { name:	'video'
+  attributes: [
+    { name:	'autoPlay'}
+    { name:	'controls'}
+    { name:	'height'}
+    { name:	'loop'}
+    { name:	'mediaGroup'}
+    { name:	'muted'}
+    { name:	'poster'}
+    { name:	'preload'}
+    { name:	'src'}
+    { name:	'width'}
+  ]}
+  { name:	'wbr'
+  attributes: []}
+  # svg elements
+  { name:	'circle'
+  attributes: [
+    { name:	'cx'}
+    { name:	'cy'}
+    { name:	'r'}
+    { name:	'stroke'}
+    { name:	'strokeWidth'}
+    { name:	'fill'}
+  ]}
+  { name:	'clipPath'
+  attributes: [
+    { name:	'clipPath'}
+  ]}
+  { name:	'defs'
+  attributes: []}
+  { name:	'ellipse'
+  attributes: [
+    { name:	'cx'}
+    { name:	'cy'}
+    { name:	'rx'}
+    { name:	'ry'}
+  ]}
+  { name:	'g'
+  attributes: [
+    { name:	'fill'}
+    { name:	'opacity'}
+  ]}
+  { name:	'line'
+  attributes: [
+    { name:	'x1'}
+    { name:	'x2'}
+    { name:	'y1'}
+    { name:	'y2'}
+  ]}
+  { name:	'linearGradient'
+  attributes: [
+    { name:	'x1'}
+    { name:	'x2'}
+    { name:	'y1'}
+    { name:	'y2'}
+    { name:	'spreadMethod'}
+    { name:	'xlinkHref'}
+  ]}
+  { name:	'mask'
+  attributes: [
+    { name:	'mashUnits'}
+    { name:	'x'}
+    { name:	'y'}
+    { name:	'width'}
+    { name:	'height'}
+  ]}
+  { name:	'path'
+  attributes: [
+    { name:	'd'}
+    { name:	'transform'}
+  ]}
+  { name:	'pattern'
+  attributes: [
+    { name:	'patternContentUnits'}
+    { name:	'x'}
+    { name:	'y'}
+    { name:	'width'}
+    { name:	'height'}
+    { name:	'viewBox'}
+    { name:	'xlinkHref'}
+  ]}
+  { name:	'polygon'
+  attributes: [
+    { name:	'points'}
+  ]}
+  { name:	'polyline'
+  attributes: [
+    { name:	'points'}
+  ]}
+  { name:	'radialGradient'
+  attributes: [
+    { name:	'gradientUnits'}
+    { name:	'gradientTransform'}
+    { name:	'cx'}
+    { name:	'cy'}
+    { name:	'r'}
+    { name:	'fx'}
+    { name:	'fy'}
+    { name:	'spreadMethod'}
+    { name:	'xlinkHref'}
+  ]}
+  { name:	'rect'
+  attributes: [
+    { name:	'x'}
+    { name:	'y'}
+    { name:	'rx'}
+    { name:	'ry'}
+    { name:	'width'}
+    { name:	'height'}
+  ]}
+  { name:	'stop'
+  attributes: [
+    { name:	'offset'}
+    { name:	'stopColor'}
+    { name:	'stopOpacity'}
+  ]}
+  { name:	'svg'
+  attributes: [
+    { name:	'x'}
+    { name:	'y'}
+    { name:	'width'}
+    { name:	'height'}
+    { name:	'viewBox'}
+    { name:	'preserveAspectRatio'}
+    { name:	'zoomAndPan'}
+  ]}
+  { name:	'text'
+  attributes: [
+    { name:	'x'}
+    { name:	'y'}
+    { name:	'dx'}
+    { name:	'dy'}
+    { name:	'rotate'}
+    { name:	'textLength'}
+    { name:	'lengthAdjust'}
+  ]}
+  { name:	'tspan'
+  attributes: [
+    { name:	'x'}
+    { name:	'y'}
+    { name:	'dx'}
+    { name:	'dy'}
+    { name:	'rotate'}
+    { name:	'textLength'}
+    { name:	'lengthAdjust'}
+    { name:	'xlinkHref'}
+  ]}
 ]


autocomplete doesn't run when ensime stopped

--- lib/ensime.coffee
+++ lib/ensime.coffee
@@ -247,7 +247,7 @@
     @subscriptions.dispose()
     @controlSubscription.dispose()

-
+    @autocompletePlusProvider = null

@@ -276,7 +276,6 @@
     log('provideAutocomplete called')

     getProvider = =>
-      log('getProvider called')
       @autocompletePlusProvider

     {
@@ -290,7 +289,6 @@
             log('ensime.getSuggestions')
             provider.getCompletions(editor.getBuffer(), bufferPosition, resolve)
         else
-          log('@autocompletePlusProvider undefined')
           []
     }


 Merge branch 'master.lightweightVersion.relnotfoundError' into 'master'

LightweightKoding: Fixed rel not found error, which causes empty pages in lightweight version.

--- server/lib/crawler/helpers.coffee
+++ server/lib/crawler/helpers.coffee
@@ -30,7 +30,7 @@
   return fullName

 getNickname = (account) ->
-  nickname = "/"
+  nickname = "A koding user"
   if account?.data?.profile?.nickname?
     nickname = account.data.profile.nickname
   return nickname
@@ -86,56 +86,49 @@
     tags = []
     tags = teaser.tags  if teaser?.tags?

-    Relationship.one selector, (err, rel) =>
+    sel =
+      "_id" : model.originId
+
+    JAccount.one sel, (err, acc) =>
       if err
         console.error err
         return callback err, null
-      unless rel
-        console.error "Rel not found"
-        return callback new Error "Rel not found", null
-      sel =
-        "_id" : rel.targetId

-      JAccount.one sel, (err, acc) =>
-        if err
-          console.error err
-          callback err, null
+      # No need to return if acc is not found.
+      # Write default values instead.
+      fullName = getFullName acc
+      nickname = getNickname acc
+      hash     = getUserHash acc
+      slug     = teaser?.slug or "#"

-        fullName = getFullName acc
-        nickname = getNickname acc
-        slug = "#"
-        slug = teaser.slug  if teaser?.slug?
+      if model?.body? and putBody
+        body = marked model.body,
+          gfm       : true
+          pedantic  : false
+          sanitize  : true
+        body = normalizeActivityBody model, body
+      else
+        body = ""

-        hash = getUserHash acc
+      activityContent =
+        slug             : teaser.slug
+        fullName         : fullName
+        nickname         : nickname
+        hash             : hash
+        title            :  if model?.title? then model.title else model.body or ""
+        body             : body
+        createdAt        : if model?.meta?.createdAt? then formatDate model.meta.createdAt else ""
+        numberOfComments : teaser.repliesCount or 0
+        numberOfLikes    : model?.meta?.likes or 0
+        comments         : comments
+        tags             : tags
+        type             : model?.bongo_?.constructorName

-        if model?.body? and putBody
-          body = marked model.body,
-            gfm       : true
-            pedantic  : false
-            sanitize  : true
-          body = normalizeActivityBody model, body
-        else
-          body = ""
-
-        activityContent =
-          slug             : teaser.slug
-          fullName         : fullName
-          nickname         : nickname
-          hash             : hash
-          title            :  if model?.title? then model.title else model.body or ""
-          body             : body
-          createdAt        : if model?.meta?.createdAt? then formatDate model.meta.createdAt else ""
-          numberOfComments : teaser.repliesCount or 0
-          numberOfLikes    : model?.meta?.likes or 0
-          comments         : comments
-          tags             : tags
-          type             : model?.bongo_?.constructorName
-
-        if createFullHTML
-          content = getSingleActivityPage {activityContent, model}
-        else
-          content = getSingleActivityContent activityContent, model
-        return callback null, content
+      if createFullHTML
+        content = getSingleActivityPage {activityContent, model}
+      else
+        content = getSingleActivityContent activityContent, model
+      return callback null, content

 decorateComment = (JAccount, comment, callback) ->
   { Relationship } = require 'jraphical'


Fix news header

Fix news header

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,7 +3,7 @@

 export default allNews = () ->
     return news([
-        newsItem("Про районную олимпиаду", String.raw"""
+        newsItem("Про Открытую олимпиаду (заочку)", String.raw"""

Началась Открытая олимпиада школьников (так называемая Заочка). Очень известная олимпиада, поступательная, но задачи не самые простые. Рекомендую участвовать всем, у кого уровень 3+.

         """),
         newsItem("Про районную олимпиаду", String.raw"""


:lipstick:

:lipstick:

--- spec/app/display-buffer-spec.coffee
+++ spec/app/display-buffer-spec.coffee
@@ -565,4 +565,3 @@
   describe ".maxLineLength()", ->
     it "returns the length of the longest screen line", ->
       expect(displayBuffer.maxLineLength()).toBe 65
-


Change dash variable from instance var to local

--- test-integration/student/dashboard.coffee
+++ test-integration/student/dashboard.coffee
@@ -6,29 +6,28 @@
 describe 'Student Dashboard', ->

   @it 'Shows Performance Forecast', ->
-    @dash = new StudentDashboard(@, 'student01', 'Biology I')
-    guide = @dash.progressGuide()
+    dash = new StudentDashboard(@, 'student01', 'Biology I')
+    guide = dash.progressGuide()
     expect(guide.valid).to.equal(true, 'Forecast is missing')

   @it 'can read forecast topic', ->
-    @dash = new StudentDashboard(@, 'student01', 'Biology I')
-    forecast = @dash.progressGuide().getForecast(index: 0)
+    dash = new StudentDashboard(@, 'student01', 'Biology I')
+    forecast = dash.progressGuide().getForecast(index: 0)
     forecast.getTopic().then (topic) ->
       expect(topic.chapter_section).to
         .match(/\d+\.\d+/, "First topic had incorrect chapter_section")

-  @it 'Can practice Forecast', (done) ->
-    @dash = new StudentDashboard(@, 'student01', 'Biology I')
-    forecast = @dash.progressGuide().getForecast(index: 1)
+  @it 'Can practice Forecast', ->
+    dash = new StudentDashboard(@, 'student01', 'Biology I')
+    forecast = dash.progressGuide().getForecast(index: 1)
     expect(forecast.valid).to.equal(true, 'Forecast was not found')
     forecast.practice().then (taskstep) ->
       taskstep.getExerciseId().then (num) ->
         expect(num).to.match(/\d+\@\d+/)
-        done()

-  it 'displays tasks that are assigned', ->
+  @it 'displays tasks that are assigned', ->
     title = @freshId()
     @login('teacher01')
     CourseSelect.goToCourseByName(@, 'Biology I')
@@ -41,8 +40,8 @@
       action: 'PUBLISH'
     @logout()

-    @dash = new StudentDashboard(@, 'student01', 'Biology I')
-    @dash.findVisibleTask(title: title).then (task) ->
+    dash = new StudentDashboard(@, 'student01', 'Biology I')
+    dash.findVisibleTask(title: title).then (task) ->
       expect(task).not.to.be.null
       expect(task).to.be.an.instanceof(DashboardTask)
       task.getTitle().then (taskTitle) ->


fixed broken references

fixed broken references

--- src/lib/background.coffee
+++ src/lib/background.coffee
@@ -99,7 +99,7 @@
   )
   # Add the column headers to the orders table.
   $('<thead/>').append(
-    $('<tr/>').append.apply this, [
+    $.prototype.append.apply $('<tr/>'), [
       $ '<th/>', text: utils.i18n 'order_header'
       $ '<th/>', text: utils.i18n 'status_header'
       $ '<th/>', text: utils.i18n 'actions_header'
@@ -119,8 +119,8 @@
   # Otherwise; let's create a row for each order.
   for order, i in ext.orders
     tbody.append(
-      $('<tr/>').append.apply this, [
-        $('<td/>').append.apply this, [
+      $.prototype.append.apply $('<tr/>'), [
+        $.prototype.append.apply $('<td/>'), [
           $ '<strong/>', text: order.label
           '<br />'
           $ '<a/>',


removed unnecessary variable

removed unnecessary variable

--- servers/testhelper/verifyslughandlerhelper.coffee
+++ servers/testhelper/verifyslughandlerhelper.coffee
@@ -20,8 +20,6 @@

   @generateVerifySlugRequestParams = (opts = {}) ->

-    name = opts?.body?.name or generateRandomString()
-
     url  = generateUrl
       route : '-/teams/verify-domain'


Update existing RecurlyWrapper tests.

Update existing RecurlyWrapper tests.

--- services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
+++ services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
@@ -2,8 +2,8 @@
 sinon = require 'sinon'
 crypto = require 'crypto'
 querystring = require 'querystring'
-RecurlyWrapper = require "../../../../app/js/Features/Subscription/RecurlyWrapper"
-Settings = require "settings-sharelatex"
+modulePath = "../../../../app/js/Features/Subscription/RecurlyWrapper"
+SandboxedModule = require('sandboxed-module')
 tk = require("timekeeper")

 fixtures =
@@ -97,22 +97,37 @@

 describe "RecurlyWrapper", ->
-	beforeEach ->
-		Settings.plans = [{
-			planCode: "collaborator"
-			name: "Collaborator"
-			features:
-				collaborators: -1
-				versioning: true
-		}]
-		Settings.defaultPlanCode =
-			collaborators: 0
-			versioning: false
+
+	before ->
+		@settings =
+			plans: [{
+				planCode: "collaborator"
+				name: "Collaborator"
+				features:
+					collaborators: -1
+					versioning: true
+			}]
+			defaultPlanCode:
+				collaborators: 0
+				versioning: false
+			apis:
+				recurly:
+					apiKey: 'nonsense'
+					privateKey: 'private_nonsense'
+
+		@RecurlyWrapper = RecurlyWrapper = SandboxedModule.require modulePath, requires:
+			"settings-sharelatex": @settings
+			"logger-sharelatex":
+				err:   sinon.stub()
+				error: sinon.stub()
+				log:   sinon.stub()
+			"request": sinon.stub()

 	describe "sign", ->
+
 		before (done) ->
 			tk.freeze Date.now() # freeze the time for these tests
-			RecurlyWrapper.sign({
+			@RecurlyWrapper.sign({
 				subscription :
 					plan_code : "gold"
 					name      : "$$$"
@@ -127,7 +142,7 @@
 		it "should be signed correctly", ->
 			signed = @signature.split("|")[0]
 			query = @signature.split("|")[1]
-			crypto.createHmac("sha1", Settings.apis.recurly.privateKey).update(query).digest("hex").should.equal signed
+			crypto.createHmac("sha1", @settings.apis.recurly.privateKey).update(query).digest("hex").should.equal signed

 		it "should be url escaped", ->
 			query = @signature.split("|")[1]
@@ -180,7 +195,7 @@
 				"  " +
 				"  " +
 				"</subscription>"
-			RecurlyWrapper._parseXml xml, (error, data) ->
+			@RecurlyWrapper._parseXml xml, (error, data) ->
 				data.subscription.plan.plan_code.should.equal "gold"
 				data.subscription.plan.name.should.equal "Gold plan"
 				data.subscription.uuid.should.equal "44f83d7cba354d5b84812419f923ea96"
@@ -188,32 +203,36 @@
 				data.subscription.unit_amount_in_cents.should.equal 800
 				data.subscription.currency.should.equal "EUR"
 				data.subscription.quantity.should.equal 1
+
 				data.subscription.activated_at.should.deep.equal new Date("2011-05-27T07:00:00Z")
 				should.equal data.subscription.canceled_at, null
 				should.equal data.subscription.expires_at, null
+
 				data.subscription.current_period_started_at.should.deep.equal new Date("2011-06-27T07:00:00Z")
+
 				data.subscription.current_period_ends_at.should.deep.equal new Date("2011-07-27T07:00:00Z")
 				should.equal data.subscription.trial_started_at, null
 				should.equal data.subscription.trial_ends_at, null
-				data.subscription.subscription_add_ons.should.deep.equal [{
+
+				data.subscription.subscription_add_ons[0].should.deep.equal {
 					add_on_code: "ipaddresses"
 					quantity: "10"
 					unit_amount_in_cents: "150"
-				}]
+				}
 				data.subscription.account.url.should.equal "https://api.recurly.com/v2/accounts/1"
 				data.subscription.url.should.equal "https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96"
 				data.subscription.plan.url.should.equal "https://api.recurly.com/v2/plans/gold"
 				done()
-
+
 	describe "getSubscription", ->
 		describe "with proper subscription id", ->
 			before ->
-				@apiRequest = sinon.stub(RecurlyWrapper, "apiRequest", mockApiRequest)
-				RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", (error, recurlySubscription) =>
+				@apiRequest = sinon.stub(@RecurlyWrapper, "apiRequest", mockApiRequest)
+				@RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", (error, recurlySubscription) =>
 					@recurlySubscription = recurlySubscription
 			after ->
-				RecurlyWrapper.apiRequest.restore()
-
+				@RecurlyWrapper.apiRequest.restore()
+
 			it "should look up the subscription at the normal API end point", ->
 				@apiRequest.args[0][0].url.should.equal "subscriptions/44f83d7cba354d5b84812419f923ea96"

@@ -222,12 +241,12 @@

 		describe "with ReculyJS token", ->
 			before ->
-				@apiRequest = sinon.stub(RecurlyWrapper, "apiRequest", mockApiRequest)
-				RecurlyWrapper.getSubscription "70db44b10f5f4b238669480c9903f6f5", {recurlyJsResult: true}, (error, recurlySubscription) =>
+				@apiRequest = sinon.stub(@RecurlyWrapper, "apiRequest", mockApiRequest)
+				@RecurlyWrapper.getSubscription "70db44b10f5f4b238669480c9903f6f5", {recurlyJsResult: true}, (error, recurlySubscription) =>
 					@recurlySubscription = recurlySubscription
 			after ->
-				RecurlyWrapper.apiRequest.restore()
-
+				@RecurlyWrapper.apiRequest.restore()
+
 			it "should return the subscription", ->
 				@recurlySubscription.uuid.should.equal "44f83d7cba354d5b84812419f923ea96"

@@ -236,30 +255,30 @@

 		describe "with includeAccount", ->
 			beforeEach ->
-				@apiRequest = sinon.stub(RecurlyWrapper, "apiRequest", mockApiRequest)
-				RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", {includeAccount: true}, (error, recurlySubscription) =>
+				@apiRequest = sinon.stub(@RecurlyWrapper, "apiRequest", mockApiRequest)
+				@RecurlyWrapper.getSubscription "44f83d7cba354d5b84812419f923ea96", {includeAccount: true}, (error, recurlySubscription) =>
 					@recurlySubscription = recurlySubscription
 			afterEach ->
-				RecurlyWrapper.apiRequest.restore()
+				@RecurlyWrapper.apiRequest.restore()

 			it "should request the account from the API", ->
 				@apiRequest.args[1][0].url.should.equal "accounts/104"
-
+
 			it "should populate the account attribute", ->
 				@recurlySubscription.account.account_code.should.equal "104"
-
+

 	describe "updateSubscription", ->
 		beforeEach (done) ->
 			@recurlySubscriptionId = "subscription-id-123"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				@requestOptions = options
 				callback null, {}, fixtures["subscriptions/44f83d7cba354d5b84812419f923ea96"]
-			RecurlyWrapper.updateSubscription @recurlySubscriptionId, { plan_code : "silver", timeframe: "now" }, (error, recurlySubscription) =>
+			@RecurlyWrapper.updateSubscription @recurlySubscriptionId, { plan_code : "silver", timeframe: "now" }, (error, recurlySubscription) =>
 				@recurlySubscription = recurlySubscription
 				done()
 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()

 		it "should send an update request to the API", ->
 			@apiRequest.called.should.equal true
@@ -275,59 +294,58 @@
 		it "should return the updated subscription", ->
 			should.exist @recurlySubscription
 			@recurlySubscription.plan.plan_code.should.equal "gold"
-
+

 	describe "cancelSubscription", ->
 		beforeEach (done) ->
 			@recurlySubscriptionId = "subscription-id-123"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				options.url.should.equal "subscriptions/#{@recurlySubscriptionId}/cancel"
 				options.method.should.equal "put"
 				callback()
-			RecurlyWrapper.cancelSubscription(@recurlySubscriptionId, done)
+			@RecurlyWrapper.cancelSubscription(@recurlySubscriptionId, done)

 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()

 		it "should send a cancel request to the API", ->
 			@apiRequest.called.should.equal true
-
+
 	describe "reactivateSubscription", ->
 		beforeEach (done) ->
 			@recurlySubscriptionId = "subscription-id-123"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				options.url.should.equal "subscriptions/#{@recurlySubscriptionId}/reactivate"
 				options.method.should.equal "put"
 				callback()
-			RecurlyWrapper.reactivateSubscription(@recurlySubscriptionId, done)
+			@RecurlyWrapper.reactivateSubscription(@recurlySubscriptionId, done)

 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()

 		it "should send a cancel request to the API", ->
 			@apiRequest.called.should.equal true
-
-
+
+

 	describe "redeemCoupon", ->

 		beforeEach (done) ->
 			@recurlyAccountId = "account-id-123"
 			@coupon_code = "312321312"
-			@apiRequest = sinon.stub RecurlyWrapper, "apiRequest", (options, callback) =>
+			@apiRequest = sinon.stub @RecurlyWrapper, "apiRequest", (options, callback) =>
 				options.url.should.equal "coupons/#{@coupon_code}/redeem"
 				options.body.indexOf("<account_code>#{@recurlyAccountId}</account_code>").should.not.equal -1
 				options.body.indexOf("<currency>USD</currency>").should.not.equal -1
 				options.method.should.equal "post"
 				callback()
-			RecurlyWrapper.redeemCoupon(@recurlyAccountId, @coupon_code, done)
+			@RecurlyWrapper.redeemCoupon(@recurlyAccountId, @coupon_code, done)

 		afterEach ->
-			RecurlyWrapper.apiRequest.restore()
+			@RecurlyWrapper.apiRequest.restore()

 		it "should send the request to redem the coupon", ->
 			@apiRequest.called.should.equal true
-

 	describe "_addressToXml", ->

@@ -341,7 +359,7 @@
 				nonsenseKey: "rubbish"

 		it 'should generate the correct xml', () ->
-			result = RecurlyWrapper._addressToXml @address
+			result = @RecurlyWrapper._addressToXml @address
 			should.equal(
 				result,
 				"""
@@ -355,5 +373,52 @@
 				"""
 			)

-
-
+	describe 'createSubscription', ->
+
+		beforeEach ->
+			@user =
+				_id: 'some_id'
+				email: 'user@example.com'
+			@subscriptionDetails =
+				currencyCode: "EUR"
+				plan_code:    "some_plan_code"
+				coupon_code:  ""
+				isPaypal: true
+				address:
+					address1: "addr_one"
+					address2: "addr_two"
+					country:  "some_country"
+					state:    "some_state"
+					zip:      "some_zip"
+			@recurly_token_id = "a-token-id"
+
+
+		describe 'when all goes well', ->
+
+			beforeEach ->
+
+			describe 'when paypal', ->
+				beforeEach ->
+
+			describe 'when not paypal', ->
+				beforeEach ->
+
+	describe '_createCreditCardSubscription', ->
+
+		beforeEach ->
+
+		describe 'when all goes well', ->
+
+			beforeEach ->
+
+		describe 'when api request produces an error', ->
+
+			beforeEach ->
+
+	describe '_createPaypalSubscription', ->
+
+		beforeEach ->
+
+		describe 'when all goes well', ->
+
+			beforeEach ->


Disabled cache tests

Disabled cache tests

--- test/generators/relational/to_json.coffee
+++ test/generators/relational/to_json.coffee
@@ -308,4 +308,4 @@
 # beforeEach should return the models_json for the current run
 module.exports = (options) ->
   runTests(options, false)
-  runTests(options, true)
+  # runTests(options, true)


fix templates & clsi set dynamically. and more robust bool vals from env vars

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -476,22 +476,22 @@
 		fieldName: process.env["SHARELATEX_LDAP_FIELD_NAME"] or 'LDAP User'
 		placeholder: process.env["SHARELATEX_LDAP_PLACEHOLDER"] or 'LDAP User ID'
 		emailAtt: process.env["SHARELATEX_LDAP_EMAIL_ATT"] or 'mail'
-		anonymous: process.env["SHARELATEX_LDAP_ANONYMOUS"] or false
+		anonymous: process.env["SHARELATEX_LDAP_ANONYMOUS"] == "true"
 		adminDN: process.env["SHARELATEX_LDAP_ADMIN_DN"]
 		adminPW: process.env["SHARELATEX_LDAP_ADMIN_PW"]
-		starttls:  process.env["SHARELATEX_LDAP_TLS"] or false
+		starttls:  process.env["SHARELATEX_LDAP_TLS"] == "true"
 		nameAtt: process.env["SHARELATEX_LDAP_NAME_ATT"]
 		lastNameAtt: process.env["SHARELATEX_LDAP_LAST_NAME_ATT"]

 	if process.env["SHARELATEX_LDAP_TLS_OPTS_CA_PATH"]
 		settings.ldap.tlsOptions =
-			rejectUnauthorized: process.env["SHARELATEX_LDAP_TLS_OPTS_REJECT_UNAUTH"] or false
+			rejectUnauthorized: process.env["SHARELATEX_LDAP_TLS_OPTS_REJECT_UNAUTH"] == "true"
 			ca: process.env["SHARELATEX_LDAP_TLS_OPTS_CA_PATH"] # e.g.'/etc/ldap/ca_certs.pem'

 # Compiler
 # --------
-if process.env["DOCKER_IN_DOCKER"]
-	clsi:
+if process.env["DOCKER_IN_DOCKER"] == "true"
+	settings.clsi =
 		commandRunner: "docker-runner-sharelatex"
 		docker:
 			image: "sharelatex-texlive"
@@ -503,7 +503,7 @@
 # Templates
 # ---------
 if process.env["SHARELATEX_TEMPLATES_USER_ID"]
-	templates:
+	settings.templates =
 		mountPointUrl: "/templates"
 		user_id: process.env["SHARELATEX_TEMPLATES_USER_ID"]


test answers are prefixed with letter choice

--- shared/test/components/exercise-preview/index.spec.coffee
+++ shared/test/components/exercise-preview/index.spec.coffee
@@ -72,3 +72,12 @@
     _.extend(@props.exercise, context: '')
     Testing.renderComponent( ExercisePreview, props: @props ).then ({dom}) ->
       expect(dom.querySelector('div.context')).not.to.exist
+
+  it 'renders answer choices', ->
+    Testing.renderComponent( ExercisePreview, props: @props ).then ({dom}) ->
+      answers = _.pluck dom.querySelectorAll('.openstax-answer .answer-label'), 'textContent'
+      expect(answers).to.deep.equal([
+        "aa sequence of DNA", "ba sequence of rRNA",
+        "ca sequence of mRNA.", "da sequence of tRNA.",
+        "aYES", "bNO"
+      ])


- add functions to SensorList to handle sensors added from context menu

--- src/main/webapp/lib/collections/SensorListCollection.coffee
+++ src/main/webapp/lib/collections/SensorListCollection.coffee
@@ -5,7 +5,7 @@

   # when initialized go through the models and set selected to false
   initialize:(@models) ->
-    @models.forEach((sensor) -> sensor.set('selected', false))
+    @models.forEach((sensor) -> @_setUpEvents(sensor))
     $a.broker.on("map:clear_map", @clear, @)
     $a.broker.on('sensors:add', @addSensor, @)

@@ -28,6 +28,34 @@
       sensor.set('selected', true) if !sensor.get('selected')
     )

+  # addSensor creates a sensor of type and the at the position passed in and adds
+  # it to the collection as well as to the models schema.
+  # It is called from the context menu's add sensor event
+  addNode: (position, type) ->
+    s = new $a.Sensor()
+    p = new $a.Position()
+    pt = new $a.Point()
+    pt.set(
+            {
+              'lat':position.lat(),
+              'lng':position.lng(),
+              'elevation':NaN
+            }
+          )
+    p.set('point', [])
+    p.get('point').push(pt)
+    s.set('display_position', p)
+    s.set('type', type || 'static_point')
+    @add(s)
+    $a.models.sensors().push(s)
+    @_setUpEvents(s)
+    s
+
+  # This method sets up the events each sensor should listen too
+  _setUpEvents: (sensor) ->
+    sensor.bind('remove', => @destroy)
+    sensor.set('selected', false)
+
   #this method clears the collection upon a clear map
   clear: ->
     $a.sensorList = {}


fix "location is not from this program" warnings

--- lib/assets/javascripts/jax/shader/program.js.coffee
+++ lib/assets/javascripts/jax/shader/program.js.coffee
@@ -49,15 +49,16 @@
         @setAttribute context, attribute, value
       else
         # console.log 'not using', name
-        if @isAttributeEnabled context, attribute.location
-          if attribute.location is 0 and @_dislikedAttributes.indexOf(name) is -1
+        id = context.id
+        if @isAttributeEnabled context, attribute.location[id]
+          if attribute.location[id] is 0 and @_dislikedAttributes.indexOf(name) is -1
             # console.log 'disliking', name
             # make a note that this attribute was disabled; finish iterating
             # in case other attributes follow suit; then rebind and retry
             @_dislikedAttributes.push name
             mustRebind = true
           # console.log 'disabling', name
-          @disableAttribute context, attribute.location, attribute.name
+          @disableAttribute context, attribute.location[id], attribute.name
       attribute.value = value
     if mustRebind
       descriptor = @getDescriptor context
@@ -75,31 +76,33 @@
     true

   setAttribute: (context, variable, value) ->
-    unless @isAttributeEnabled context, variable.location
-      @enableAttribute context, variable.location, variable.name
+    id = context.id
+    unless @isAttributeEnabled context, variable.location[id]
+      @enableAttribute context, variable.location[id], variable.name
     value.bind context
-    context.gl.vertexAttribPointer variable.location, value.itemSize, value.dataType || GL_FLOAT, false, 0, value.offset || 0
+    context.gl.vertexAttribPointer variable.location[id], value.itemSize, value.dataType || GL_FLOAT, false, 0, value.offset || 0

   setUniform: (context, variable, value) ->
     {gl} = context
+    id = context.id
     switch variable.type
-      when 'float'          then gl.uniform1f  variable.location, value
-      when 'bool', 'int'    then gl.uniform1i  variable.location, value
-      when 'vec2'           then gl.uniform2fv variable.location, value
-      when 'vec3'           then gl.uniform3fv variable.location, value
-      when 'vec4'           then gl.uniform4fv variable.location, value
-      when 'bvec2', 'ivec2' then gl.uniform2iv variable.location, value
-      when 'bvec3', 'ivec3' then gl.uniform3iv variable.location, value
-      when 'bvec4', 'ivec4' then gl.uniform4iv variable.location, value
-      when 'mat2' then gl.uniformMatrix2fv variable.location, false, value
-      when 'mat3' then gl.uniformMatrix3fv variable.location, false, value
-      when 'mat4' then gl.uniformMatrix4fv variable.location, false, value
+      when 'float'          then gl.uniform1f  variable.location[id], value
+      when 'bool', 'int'    then gl.uniform1i  variable.location[id], value
+      when 'vec2'           then gl.uniform2fv variable.location[id], value
+      when 'vec3'           then gl.uniform3fv variable.location[id], value
+      when 'vec4'           then gl.uniform4fv variable.location[id], value
+      when 'bvec2', 'ivec2' then gl.uniform2iv variable.location[id], value
+      when 'bvec3', 'ivec3' then gl.uniform3iv variable.location[id], value
+      when 'bvec4', 'ivec4' then gl.uniform4iv variable.location[id], value
+      when 'mat2' then gl.uniformMatrix2fv variable.location[id], false, value
+      when 'mat3' then gl.uniformMatrix3fv variable.location[id], false, value
+      when 'mat4' then gl.uniformMatrix4fv variable.location[id], false, value
       when 'sampler2D', 'samplerCube'
         if !(value instanceof Jax.Texture) or value.ready()
           gl.activeTexture GL_TEXTURE0 + @__textureIndex
           value.refresh context unless value.isValid context
           gl.bindTexture value.options.target, value.getHandle context
-          gl.uniform1i variable.location, value = @__textureIndex++
+          gl.uniform1i variable.location[id], value = @__textureIndex++
       else throw new Error "Unexpected variable type: #{variable.type}"

   insert: (vsrc, fsrc, index) ->
@@ -131,8 +134,10 @@
           return s
       a.name.toLowerCase().localeCompare b.name.toLowerCase()
     nextAvailableLocation = 0
+    id = descriptor.context.id
     for variable in variables
-      variable.location = nextAvailableLocation
+      variable.location or= {}
+      variable.location[id] = nextAvailableLocation
       gl.bindAttribLocation descriptor.glProgram, nextAvailableLocation, variable.name
       nextAvailableLocation += switch variable.type
         when 'mat2' then 2
@@ -142,8 +147,9 @@
     true

   enableAllAttributes: (descriptor) ->
+    id = descriptor.context.id
     for name, variable of @variables.attributes
-      @enableAttribute descriptor.context, variable.location, name
+      @enableAttribute descriptor.context, variable.location[id], name
     true

   enableAttribute: (context, location, name) ->
@@ -160,9 +166,11 @@
   getUniformLocations: (descriptor) ->
     {gl} = descriptor.context
     program = @getGLProgram descriptor.context
+    id = descriptor.context.id
     for name, variable of @variables.uniforms
-      variable.location = gl.getUniformLocation program, variable.name
-      if variable.location is null
+      variable.location or= {}
+      variable.location[id] = gl.getUniformLocation program, variable.name
+      if variable.location[id] is null
         # remove variables that were not used in the shader, to prevent
         # uselessly iterating through them later
         delete @variables.uniforms[name]
@@ -259,6 +267,7 @@
       descriptor = @_contexts[context.id] =
         valid: false
         context: context
+      # console.log @variables
       maxAttrs = @getShaderContext(descriptor, 'vertex').maxVertexAttribs
       context._enabledAttributes or= new Uint8Array maxAttrs
     descriptor


Support slim file

Support slim file

--- lib/emojis-provider.coffee
+++ lib/emojis-provider.coffee
@@ -4,7 +4,7 @@
 emoji = require('emoji-images')

 module.exports =
-  selector: '.source.gfm, .text.html, .text.plain, .text.git-commit, .comment, .string, .source.emojicode'
+  selector: '.source.gfm, .text.html, .text.slim, .text.plain, .text.git-commit, .comment, .string, .source.emojicode'

   wordRegex: /::?[\w\d_\+-]+$/
   emojiFolder: 'atom://autocomplete-emojis/node_modules/emoji-images/pngs'


OX-203 increased clipping distance for 3dview

--- app/assets/javascripts/controller/cameracontroller.coffee
+++ app/assets/javascripts/controller/cameracontroller.coffee
@@ -29,8 +29,8 @@

     @updateCamViewport()
     for cam in @cameras
-      cam.near = -100000
-      cam.far  =  100000
+      cam.near = -1000000
+      cam.far  =  1000000

   update : =>
     gPos = @flycam.getGlobalPos()


#533 Plane2D: redraw tile also if smaller zoom step bucket is loaded

--- app/assets/javascripts/oxalis/model/binary/plane2d.coffee
+++ app/assets/javascripts/oxalis/model/binary/plane2d.coffee
@@ -55,6 +55,15 @@
     @dataTexture = { renderTile: @renderDataTile }

     @cube.on "bucketLoaded", (bucket) =>
+
+      zoomStepDiff = @dataTexture.zoomStep - bucket[3]
+      if zoomStepDiff > 0
+        bucket = [
+          bucket[0] >> zoomStepDiff
+          bucket[1] >> zoomStepDiff
+          bucket[2] >> zoomStepDiff
+          @dataTexture.zoomStep
+        ]

       # Checking, whether the new bucket intersects with the current layer
       if @dataTexture.layer >> (@cube.BUCKET_SIZE_P + bucket[3]) == bucket[@W] and @dataTexture.topLeftBucket?


Test that crashing event listeners do not cause other event listeners to not run

--- spec_app/spec/javascripts/up/event_spec.js.coffee
+++ spec_app/spec/javascripts/up/event_spec.js.coffee
@@ -209,6 +209,44 @@
         unregister()
         expect(register).not.toThrowError()

+      describe 'when a listener throws an error', ->
+        beforeEach ->
+          # Even though a crashing listener will not actually crash the event dispatching process,
+          # it will still dispatch an ErrorEvent to window.onerror (which is good behavior).
+          # However, Jasmine also observes window.onerror and fails the spec, so we need to disable
+          # this behavior for this purpose.
+          @oldGlobalErrorHandler = window.onerror
+          window.onerror = u.noop
+
+        afterEach ->
+          window.onerror = @oldGlobalErrorHandler
+
+        it 'does not prevent other event listeners from being called', ->
+          nativeCallbackBefore = jasmine.createSpy('native callback before')
+          upCallbackBefore = jasmine.createSpy('Unpoly callback before')
+          crashingUpCallback = -> throw new Error('error from crashing Unpoly callback')
+          upCallbackAfter = jasmine.createSpy('Unpoly callback after')
+          nativeCallbackAfter = jasmine.createSpy('native callback after')
+
+          document.addEventListener('foo', nativeCallbackBefore)
+          up.on('foo', upCallbackBefore)
+          up.on('foo', crashingUpCallback)
+          up.on('foo', upCallbackAfter)
+          document.addEventListener('foo', nativeCallbackAfter)
+
+          emit = -> up.emit('foo')
+
+          expect(emit).not.toThrowError()
+
+          expect(nativeCallbackBefore).toHaveBeenCalled()
+          expect(upCallbackBefore).toHaveBeenCalled()
+          expect(upCallbackAfter).toHaveBeenCalled()
+          expect(nativeCallbackAfter).toHaveBeenCalled()
+
+          # Since we're not using up.on we need to clean up manually
+          document.removeEventListener('foo', nativeCallbackBefore)
+          document.removeEventListener('foo', nativeCallbackAfter)
+
       describe 'passing of [up-data]', ->

         it 'parses an [up-data] attribute as JSON and passes the parsed object as a third argument to the listener', asyncSpec (next) ->


Added marker and address to leaflet map.

--- source/javascripts/leaflet.coffee
+++ source/javascripts/leaflet.coffee
@@ -7,6 +7,8 @@

 baseLayers = [ "OpenStreetMap.DE" ]
-overlays = [ "OpenStreetMap.Mapnik" ]
-layerControl = L.control.layers.provided(baseLayers, overlays).addTo(map)
+#overlays = [ "OpenStreetMap.Mapnik" ]
+layerControl = L.control.layers.provided(baseLayers).addTo(map)
 marker = L.marker([ 52.0420556802915, 8.8904132197084979 ]).addTo(map)
+    .bindPopup('Steinmühle Lemgo
 Entruper Weg 88
32657 Lemgo')
+    .openPopup();


Add method to update pagination

Add method to update pagination

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>

--- vendor/assets/javascripts/fustrate/generic_table.coffee
+++ vendor/assets/javascripts/fustrate/generic_table.coffee
@@ -44,3 +44,9 @@
   updated: =>
     $('tbody tr.no-records', @table)
       .toggle $('tbody tr:not(.no-records):not(.loading)', @table).length < 1
+
+  # This should be fed a response from a JSON request for a paginated
+  # collection.
+  updatePagination: (response) =>
+    $('.pagination', @root)
+      .replaceWith (new Fustrate.Components.Pagination response).generate()


Changes on gruntfile

Changes on gruntfile

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -31,19 +31,19 @@

     stylus:
       engine:
-        options: compress: true, import: [ '__constants']
-        files: '<%= meta.endpoint %>/<%=meta.file%>.css': '<%= source.stylus %>'
+        options: compress: true, import: [ "__constants"]
+        files: "<%= meta.endpoint %>/<%=meta.file%>.css": "<%= source.stylus %>"
       theme_default:
-        options: compress: true, import: [ '__constants']
-        files: '<%= meta.endpoint %>/<%=meta.file%>.theme.default.css': '<%= source.theme_default %>'
+        options: compress: true, import: [ "__constants"]
+        files: "<%= meta.endpoint %>/<%=meta.file%>.theme.default.css": "<%= source.theme_default %>"
       theme_mock:
-        options: compress: true, import: [ '__constants']
-        files: '<%= meta.endpoint %>/<%=meta.file%>.theme.mock.css': '<%= source.theme_mock %>'
+        options: compress: true, import: [ "__constants"]
+        files: "<%= meta.endpoint %>/<%=meta.file%>.theme.mock.css": "<%= source.theme_mock %>"

     copy:
       main:
         files:
-          src: '<%= source.icons %>',
+          src: "<%= source.icons %>",
           dest: "<%= meta.endpoint %>/<%=meta.file%>.icons.css"

     watch:


Updated source map generation + mocha test

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -6,9 +6,9 @@
     date: grunt.template.today('dd-mm-yyyy')
     pkg: grunt.file.readJSON 'package.json'
     coffee:
-      options:
-        sourceMap: true
       app:
+        options:
+          sourceMap: true
         files:
           'dist/<%= pkg.name %>.js': ['src/*.coffee']
       test:
@@ -30,6 +30,11 @@
       app: ['src/**/*.coffee'],
       test: ['src/**/*.coffee'],
       grunt: ['Gruntfile.coffee']
+    mocha:
+      options:
+        reporter: 'Spec'
+        run: true
+      all: ['test/*.html']

   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-uglify'


add clsi2_priority to settings.defaults

add clsi2_priority to settings.defaults

--- services/web/config/settings.defaults.coffee
+++ services/web/config/settings.defaults.coffee
@@ -88,6 +88,8 @@
 			url: "http://localhost:3009"
 		clsi:
 			url: "http://localhost:3013"
+		clsi_priority:
+			url: "http://localhost:3013"
 		templates:
 			url: "http://localhost:3007"
 		githubSync:


:lipstick:

:lipstick:

--- src/app/config.coffee
+++ src/app/config.coffee
@@ -58,10 +58,9 @@

   requireUserInitScript: ->
     try
-      console.log @userInitScriptPath
       require userInitScriptPath if fs.exists(userInitScriptPath)
     catch error
-      console.error "Failed to load `#{@userInitScriptPath}`", error.stack, error
+      console.error "Failed to load `#{userInitScriptPath}`", error.stack, error

   valueAtKeyPath: (keyPath) ->
     value = this


Allow html attributes to be applied to Editors

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -31,7 +31,9 @@
   ###

   @content: (params) ->
-    @div class: @classes(params), tabindex: -1, =>
+    attributes = { class: @classes(params), tabindex: -1 }
+    _.extend(attributes, params.attributes) if params.attributes
+    @div attributes, =>
       @subview 'gutter', new Gutter
       @input class: 'hidden-input', outlet: 'hiddenInput'
       @div class: 'scroll-view', outlet: 'scrollView', =>


Add Editor.getRelativePath() that delegates to EditSession

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -558,6 +558,9 @@
   # {Delegates to: EditSession.getPath}
   getPath: -> @activeEditSession?.getPath()

+  # {Delegates to: EditSession.getRelativePath}
+  getRelativePath: -> @activeEditSession?.getRelativePath()
+
   #  {Delegates to: Buffer.getLineCount}
   getLineCount: -> @getBuffer().getLineCount()


avoid multiple lookup of $('.ide')

avoid multiple lookup of $('.ide')

--- client/ide.coffee
+++ client/ide.coffee
@@ -8,7 +8,7 @@
 require '../jquery.layout'

 module.exports = ->
-  $('body').html """
+  $('body').html $ide = $ """

@@ -93,7 +93,7 @@
     $('title').text s; return

   die = -> # don't really, just pretend
-    if !isDead then isDead = 1; $('.ide').addClass 'disconnected'; for _, {widget} of wins then widget.die()
+    if !isDead then isDead = 1; $ide.addClass 'disconnected'; for _, {widget} of wins then widget.die()
     return

   D.socket
@@ -183,7 +183,7 @@
       )
       return

-  layout = $('.ide').layout
+  layout = $ide.layout
     defaults: enableCursorHotkey: 0
     north: spacing_closed: 0, spacing_open: 0, resizable: 0, togglerLength_open: 0
     east:  spacing_closed: 0, size: '0%', resizable: 1, togglerLength_open: 0


Add window.profile, which is like measure but also records a profile

--- src/app/window.coffee
+++ src/app/window.coffee
@@ -168,3 +168,10 @@
   result = new Date().getTime() - start
   console.log description, result
   value
+
+window.profile = (description, fn) ->
+  measure description, ->
+    console.profile(description)
+    value = fn()
+    console.profileEnd(description)
+    value


Removed debug commented code

Removed debug commented code

--- app/assets/javascripts/javascript_renderer/helpers/form_options.coffee
+++ app/assets/javascripts/javascript_renderer/helpers/form_options.coffee
@@ -135,12 +135,6 @@
         [ options_in_group[1], optgroup_options ]
       else
         [ options_in_group, { label: index } ]
-    # if grouped_options instanceof Array
-    #   optgroup_options          = options_in_group[2] || {}
-    #   optgroup_options['label'] = options_in_group[0]
-    #   options_for_select += H.contentTag('optgroup', H.optionsForSelect(options_in_group[1], selectors), optgroup_options )
-    # else
-    #   options_for_select += H.contentTag('optgroup', H.optionsForSelect(options_in_group, selectors), { label: index })

     options_for_select += H.contentTag('optgroup', H.optionsForSelect(options_in_group, selectors), optgroup_options )


correct typo bug

correct typo bug

--- src/coffee/validators/maxsize.coffee
+++ src/coffee/validators/maxsize.coffee
@@ -9,7 +9,7 @@
 angular.module('CoolFormValidators').
   factory('maxSizeValidator', ->

-    validator = (name, fields, rule) ->
+    validator = (name, fields, options) ->
       if !fields[name].value? or typeof fields[name].value != "string"
         return false
       if fields[name].value.length <= options.size then true else false


update tests

update tests

--- apps/editorial_features/test/components/eoy/client.coffee
+++ apps/editorial_features/test/components/eoy/client.coffee
@@ -119,7 +119,7 @@
   describe '#initialize', ->

     it 'renders content from curation and superarticle', ->
-      $('.scroller__items section').should.have.lengthOf 11
+      $('.scroller__items section').should.have.lengthOf 12
       $('.article-sa-sticky-header').should.have.lengthOf 1

     it 'closes all scroller sections on load', ->
@@ -136,17 +136,17 @@

     it 'returns an array of heights that corresponds to each section', ->
       zones = @view.getScrollZones()
-      zones[0].should.equal 805
-      zones[1].should.equal 1333
-      zones[2].should.equal 1861
-      zones[3].should.equal 2389
-      zones[4].should.equal 2917
-      zones[5].should.equal 3445
-      zones[6].should.equal 3973
-      zones[7].should.equal 4501
-      zones[8].should.equal 5029
-      zones[9].should.equal 5557
-      zones[10].should.equal 6085
+      zones[0].should.equal 825
+      zones[1].should.equal 1373
+      zones[2].should.equal 1901
+      zones[3].should.equal 2429
+      zones[4].should.equal 2957
+      zones[5].should.equal 3485
+      zones[6].should.equal 4013
+      zones[7].should.equal 4541
+      zones[8].should.equal 5069
+      zones[9].should.equal 5597
+      zones[10].should.equal 6125

   describe '#closestSection', ->

@@ -159,7 +159,7 @@
     it 'opens containers on scroll', ->
       @view.doSlider($(window).scrollTop())
       $('.scroller__items section[data-section=0]').height().should.equal 0
-      $('.scroller__items section[data-state=open]').should.have.lengthOf 2
+      $('.scroller__items section[data-state=open]').should.have.lengthOf 3

   describe '#animateBody', ->

@@ -206,16 +206,16 @@
     it 'sets height based on position', ->
       @view.windowHeight = 900
       @view.setupSliderHeight()
-      @view.containerHeight.should.equal 805
+      @view.containerHeight.should.equal 825
       @view.activeHeight.should.equal 528
-      @view.openHeight.should.equal 6160
+      @view.openHeight.should.equal 6728

     it 'sets up heights for the scroller', ->
       @view.setupSliderHeight()
-      $('.scroller__items section[data-section=0]').height().should.equal 805
-      @view.containerHeight.should.equal 805
+      $('.scroller__items section[data-section=0]').height().should.equal 825
+      @view.containerHeight.should.equal 825
       @view.activeHeight.should.equal 528
-      @view.openHeight.should.equal 6160
+      @view.openHeight.should.equal 6728

   describe '#deferredLoadBody', ->


More reasonable max_screen_time.

More reasonable max_screen_time.

--- app/client.coffee
+++ app/client.coffee
@@ -225,7 +225,7 @@
     .attr("y", (f) -> -f.field_width / 2 + sp)

 # in miliseconds
-max_screen_time = 30
+max_screen_time = 100

 drawRobots = (robots, color, timestamp) ->
   timestampify(robots, timestamp)


Fix fullUsers subscription when not searching for multiple users

--- server/publications/fullUsers.coffee
+++ server/publications/fullUsers.coffee
@@ -8,8 +8,11 @@

 	filter = _.trim filter
 	if filter
-		filterReg = new RegExp filter, "i"
-		query = { $or: [ { username: filterReg }, { name: filterReg }, { "emails.address": filterReg } ] }
+		if limit is 1
+			query = { username: filter }
+		else
+			filterReg = new RegExp filter, "i"
+			query = { $or: [ { username: filterReg }, { name: filterReg }, { "emails.address": filterReg } ] }
 	else
 		query = {}


social: fix require issue

social: fix require issue

--- workers/social/lib/social/models/referrableemail.coffee
+++ workers/social/lib/social/models/referrableemail.coffee
@@ -1,6 +1,5 @@
 jraphical = require "jraphical"
 module.exports = class JReferrableEmail extends jraphical.Module
-  JAccount           = require "./account"
   {ObjectId, secure} = require "bongo"

   @share()
@@ -31,6 +30,8 @@
       return callback err  if err

       {username} = session.data
+
+      JAccount = require "./account"
       JAccount.one {"profile.nickname": username}, (err, account)=>
         return callback err  if err
         r = new JReferrableEmail {email, title, username}


referrableemail: add 'save' to shared events

--- workers/social/lib/social/models/referrableemail.coffee
+++ workers/social/lib/social/models/referrableemail.coffee
@@ -7,9 +7,11 @@
   @share()

   @set
-
+    sharedEvents  :
+      instance    : [
+        { name    : "save" }
+      ]
     sharedMethods :
-
       static      :
         invite    :
           (signature String, Function)
@@ -17,11 +19,9 @@
           (signature Function)
         deleteEmailsForAccount:
           (signature Function)
-
       instance    :
         invite    :
           (signature Function)
-
     schema        :
       title       : String
       email       :


Update plunker includes

Update plunker includes

--- servers/www/assets/js/plunker.coffee
+++ servers/www/assets/js/plunker.coffee
@@ -2,7 +2,6 @@
 #= require vendor/jquery.cookie
 #= require vendor/underscore
 #= require vendor/backbone
-#= require vendor/page

 #= require lib/router


Text Edit #6

Text Edit #6

--- src/components/LandingExplain.coffee
+++ src/components/LandingExplain.coffee
@@ -19,9 +19,7 @@
   render: ->
     { onClick } = @props
     div null,
-      h2 null, "Die Welt steckt voller Entdecker."
-       br null
-      "Und voller Orte, die darauf warten entdeckt zu werden."
+      h2 null, "Die Welt steckt voller Entdecker. Und voller Orte, die darauf warten entdeckt zu werden."
       div
       img style: {float: "left"}, src: require "../img/lp_illu1.jpg"
       div style: {marginTop: "200px"},
@@ -39,7 +37,7 @@
       img style: {float: "left"}, src: require "../img/lp_illu3.jpg"
       div style: {marginTop: "200px"},
         h3 null, "Gestalte die Welt von morgen."
-        p null, "Gemeinsam mti dir möchten wir den positiven Wandel" +
+        p null, "Gemeinsam mit dir möchten wir den positiven Wandel" +
             "unserer Gesellschaft sichtbar und erfahrbar machen."
       div style: clear: "both"
       hr id: "tutorial"
@@ -163,6 +161,7 @@
         div null, "PR & Marketing"
       div className: "teamMember",
         div className: "circle blue"
+          img className: "teamImage", src: require "../img/team/placeholder_4.png"
         div null, "Wir freuen uns über neue"
         div null, "Teammitglieder!"


started rendering messageS

started rendering messageS

--- src/components/app/pages/home.coffee
+++ src/components/app/pages/home.coffee
@@ -1,5 +1,6 @@
 React = require 'react'
 cursorStore = require '../stores/cursor_store'
+messageStore = require '../stores/message_store'
 notificationStore = require '../stores/notification_store'
 threadStore = require '../stores/thread_store'
 userSessionStore = require '../stores/user_session_store'
@@ -38,9 +39,26 @@
     @unsubscribeFromThreadStore()
     @unsubscribeFromUserSessionStore()

-  _renderThreadNames: ->
+  _renderMessage: (message) ->
+    {metadata, data} = message
+
{metadata.AuthorId}: {data.value}

+
+  _renderThreadNamesAndMessages: ->
     names = []
+    messages = []
     for thread, object of @state.threadNames
+      messageTree = threadStore.getTree thread
+      # Perform a traversal on the tree and print out all the messages
+      queue = [messageTree]
+      while queue.length and queue[0]
+        debugger
+        messageNode = queue.shift()
+        message = messageStore.getMessage(messageNode.id)
+        messages.push @_renderMessage message
+        for node in messageNode.children
+          queue.push node
+
+
       names.push(
{object.displayName}
)
     return names

@@ -51,7 +69,7 @@

Welcome

         Hello {if @state.user then @state.user.username else "World"}!
-        {@_renderThreadNames()}
+        {@_renderThreadNamesAndMessages()}

     )


Add descriptive text to zoom links.

Add descriptive text to zoom links.

--- src/map-view.coffee
+++ src/map-view.coffee
@@ -392,8 +392,8 @@

             L.control.zoom(
                 position: 'bottomright'
-                zoomInText: ''
-                zoomOutText: '').addTo @map
+                zoomInText: "#{i18n.t('assistive.zoom_in')}"
+                zoomOutText: "#{i18n.t('assistive.zoom_out')}").addTo @map
             @popups.addTo @map
             @infoPopups.addTo @map


Autocomplete: removed js animation

Autocomplete: removed js animation

--- src/components/autocomplete/autocompletecontroller.coffee
+++ src/components/autocomplete/autocompletecontroller.coffee
@@ -132,7 +132,7 @@

   hideDropdown:->
     dropdownWrapper = @dropdown.getView()
-    dropdownWrapper.$().fadeOut 75
+    dropdownWrapper.hide()

   showDropdown:->

@@ -147,7 +147,7 @@
     offset.top += input.getHeight()
     dropdownWrapper.$().css offset

-    dropdownWrapper.$().fadeIn 75
+    dropdownWrapper.show()
     windowController.addLayer dropdownWrapper

     # parent = @getView()


Remove test motion

Remove test motion

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -1277,19 +1277,3 @@
         enclosingRange.containsRange(range)

     forwardingRanges[0]?.end.translate([0, -1]) or enclosingRange?.start
-
-class MoveThreeColumnRight extends Motion
-  @extend()
-  inclusive: true
-  moveCursor: (cursor) ->
-    point = cursor.getBufferPosition()
-    newPoint = point.translate([0, 3])
-    cursor.setBufferPosition(newPoint)
-
-class MoveThreeColumnLeft extends Motion
-  @extend()
-  inclusive: true
-  moveCursor: (cursor) ->
-    point = cursor.getBufferPosition()
-    newPoint = point.translate([0, -3])
-    cursor.setBufferPosition(newPoint)


Replace tempfile path with actual path in getInfo

--- lib/editor-controller.coffee
+++ lib/editor-controller.coffee
@@ -1,5 +1,6 @@
 {Range,CompositeDisposable} = require('atom')
 {HaskellGhcModMessage} = require('./haskell-ghc-mod-message')
+replaceAll = require './replace-all'

 module.exports =
 class EditorController
@@ -115,8 +116,8 @@

   getInfo: ->
     range=@getSymbolRange()
-    @process.getInfo @getText(), @getSymbol(range), (data) =>
-      @showMessage range,data
+    @process.getInfo @getText(), @getSymbol(range), (data,path) =>
+      @showMessage range,replaceAll(data,path,@getPath())

   doCheck: ->
     @clearError()
@@ -137,3 +138,6 @@
   getSymbol: (range) ->
     range=@getSymbolRange() unless range
     @editor.getTextInBufferRange range
+
+  getPath: ->
+    @editor.getPath()


initialize application/all view

initialize application/all view

--- server/lib/request.coffee
+++ server/lib/request.coffee
@@ -140,6 +140,16 @@
                 function (doc) {
                     if(doc.docType && doc.docType.toLowerCase() === "device") {
                         return emit(doc.login, doc)
+                    }
+                }
+                """
+        # Usefull to manage device access
+        application:
+            all:
+                map: """
+                function(doc) {
+                    if(doc.docType && doc.docType.toLowerCase() === "application") {
+                        return emit(doc._id, doc);
                     }
                 }
                 """


fix aliases feature

fix aliases feature

--- lib/fuzzy-finder-view.coffee
+++ lib/fuzzy-finder-view.coffee
@@ -92,7 +92,7 @@
     else
       # the path is an npm package name or an npm package subpath
       name = filePath
-      aliases = @getAliases
+      aliases = @getAliases()
       relativePath = @prettifyPath(filePath)
       if aliases[name]
         name = aliases[name]


sugar for OpenURL#open

sugar for OpenURL#open

--- src/models/OpenURL.coffee
+++ src/models/OpenURL.coffee
@@ -1,9 +1,14 @@
 class OpenURL

   @open: (options={}, callbacks={}) ->
+    url = if options.constructor.name == "String"
+      options
+    else
+      options.url
+
     steroids.nativeBridge.nativeCall
       method: "openURL"
       parameters:
-        url: options.url
+        url: url
       successCallbacks: [callbacks.onSuccess]
       failureCallbacks: [callbacks.onFailure]


Update user in updateCf only if rating changed (fix #181)

--- server/models/user.coffee
+++ server/models/user.coffee
@@ -78,13 +78,16 @@
     @update({$set: {dormant: @dormant}})

 usersSchema.methods.updateCfRating = ->
+    oldRating = @cf?.rating
     logger.debug "Updating cf rating ", @name
     res = await calculateCfRating this
     logger.debug "Updated cf rating ", @name, res
     if not res
         return
     res.login = @cf.login
-    @update({$set: {cf: res}})
+    await @update({$set: {cf: res}})
+    @cf = res
+    return @cf.rating != oldRating

 usersSchema.methods.updateAchieves = (achieves) ->
     logger.info "updating achieves login ", @_id, achieves
@@ -245,8 +248,8 @@
     logger.info "Updating cf ratings"
     for u in await User.findAll()
         if u.cf.login
-            await u.updateCfRating()
-            await User.updateUser(u._id, {})
+            if await u.updateCfRating()
+                await User.updateUser(u._id, {})
             await sleep(500)  # don't hit CF request limit
     logger.info "Updated cf ratings"


Disable immediate new products

Disable immediate new products

--- server/routes/post.coffee
+++ server/routes/post.coffee
@@ -20,7 +20,7 @@
         tagline: tagline
         link: link
         posterUser: userId
-        displayDay: new Date()
+        # displayDay: new Date()

       newProduct.save (err, product) ->
         res.redirect '/'


add data-attribute to grammar list

add data-attribute to grammar list

--- lib/grammar-list-view.coffee
+++ lib/grammar-list-view.coffee
@@ -25,7 +25,9 @@
   viewForItem: (grammar) ->
     element = document.createElement('li')
     element.classList.add('active') if grammar is @currentGrammar
-    element.textContent = grammar.name ? grammar.scopeName
+    grammarName = grammar.name ? grammar.scopeName
+    element.textContent = grammarName
+    element.setAttribute('data-grammar', grammarName)
     element

   confirmed: (grammar) ->


Fixed line device length

Fixed line device length

--- coffee/index.iced
+++ coffee/index.iced
@@ -20,9 +20,11 @@
 updater.on "download-failed", (err) ->
   console.log err
 updater.on "download-finished", ->
-
+
 updater.on "update-installed", ->
-  alert "A new Update was installed! Please finish your work and restart this application! (Also we shouldn't be that intrusive.)"
+  alert "A new Update was installed!
+    Please finish your work and restart this application!
+     (Also we shouldn't be that intrusive.)"

 await updater.isUpdateAvailable defer err, av


Updated summary links/buttons

Updated summary links/buttons

--- app/annotation/summary.cjsx
+++ app/annotation/summary.cjsx
@@ -77,12 +77,12 @@

Have you seen this chimp before?

Help us identify and name these chimps!

-            <button className="learn-more-btn" onClick={@props.openTutorial.bind(null, "chimps")}>Learn More</button>
-            <button className="all-chimps-btn">See all known Chimps</button>
+            <button className="learn-more-btn" onClick={@props.openTutorial.bind(null, "chimps")}>How does this work?</button>
+            <button className="all-chimps-btn">Known chimps at this site</button>

-            <button className="seen-chimp-btn">{"Yes, I've seen this Chimp before!"}</button>
-            <button className="new-chimp-btn">No, this is a new Chimp.</button>
+            <button className="seen-chimp-btn">This is a known chimp!</button>
+            <button className="new-chimp-btn">This is a new chimp!</button>

       }


:lipstick: Move animate as an instance method

--- lib/minimap-element.coffee
+++ lib/minimap-element.coffee
@@ -4,25 +4,6 @@
 CanvasDrawer = require './mixins/canvas-drawer'

 MinimapQuickSettingsView = null
-
-animate = ({from, to, duration, step}) ->
-  start = new Date()
-
-  swing = (progress) ->
-    return 0.5 - Math.cos( progress * Math.PI ) / 2
-
-  update = ->
-    passed = new Date() - start
-    if duration == 0
-      progress = 1
-    else
-      progress = passed / duration
-    progress = 1 if progress > 1
-    delta = swing(progress)
-    step(from + (to-from)*delta)
-    requestAnimationFrame(update) if progress < 1
-
-  update()

 # Public:
 class MinimapElement extends HTMLElement
@@ -380,7 +361,8 @@
       duration = 300
     else
       duration = 0
-    animate(from: from, to: to, duration: duration, step: step)
+
+    @animate(from: from, to: to, duration: duration, step: step)

   relayMousewheelEvent: (e) =>
     editorElement = atom.views.getView(@minimap.textEditor)
@@ -455,6 +437,25 @@
     else
       "scale(#{x}, #{y})"

+  animate: ({from, to, duration, step}) ->
+    start = new Date()
+
+    swing = (progress) ->
+      return 0.5 - Math.cos( progress * Math.PI ) / 2
+
+    update = ->
+      passed = new Date() - start
+      if duration == 0
+        progress = 1
+      else
+        progress = passed / duration
+      progress = 1 if progress > 1
+      delta = swing(progress)
+      step(from + (to-from)*delta)
+      requestAnimationFrame(update) if progress < 1
+
+    update()
+
 #    ######## ##       ######## ##     ## ######## ##    ## ########
 #    ##       ##       ##       ###   ### ##       ###   ##    ##
 #    ##       ##       ##       #### #### ##       ####  ##    ##