Prototype for tab-through-input-elements feature. Refs #508.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -301,19 +301,34 @@
     HUD.showForDuration("Yanked URL", 1000)

   focusInput: (count) ->
-    results = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_ITERATOR_TYPE)
-
-    lastInputBox
-    i = 0
-
-    while (i < count)
-      currentInputBox = results.iterateNext()
-      break unless currentInputBox
-      continue if (DomUtils.getVisibleClientRect(currentInputBox) == null)
-      lastInputBox = currentInputBox
-      i += 1
-
-    lastInputBox.focus() if lastInputBox
+    xpathResult = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_ITERATOR_TYPE)
+    visibleInputs =
+      while (currentInputBox = xpathResult.iterateNext())
+        rect = DomUtils.getVisibleClientRect(currentInputBox)
+        continue if rect == null
+        { element: currentInputBox, rect: rect }
+
+    return if visibleInputs.length == 0
+
+    selectedInputIndex = Math.min(count - 1, visibleInputs.length - 1)
+
+    visibleInputs[selectedInputIndex].element.focus()
+
+    hintMarkers = (LinkHints.createMarkerFor(el) for el in visibleInputs)
+
+    for marker, idx in hintMarkers
+      marker.innerHTML = "<span>#{idx + 1}</span>"
+
+    hintMarkerContainingDiv = LinkHints.displayHints(hintMarkers)
+
+    handlerStack.push keydown: ->
+      if event.keyCode == KeyboardUtils.keyCodes.tab
+        if ++selectedInputIndex == hintMarkers.length
+          selectedInputIndex = 0
+        hintMarkers[selectedInputIndex].clickableItem.focus()
+      else
+        DomUtils.removeElement hintMarkerContainingDiv
+        handlerStack.pop()

 #
 # Sends everything except i & ESC to the handler in background_page. i & ESC are special because they control


Always soft-wrap Markdown files

Always soft-wrap Markdown files

error: pathspec 'atom.symlink/init.coffee' did not match any file(s) known to git

--- atom.symlink/init.coffee
+++ atom.symlink/init.coffee
@@ -0,0 +1,6 @@
+path = require 'path'
+
+atom.workspaceView.eachEditorView (editorView) ->
+  editor = editorView.getEditor()
+  if path.extname(editor.getPath()) is '.md'
+    editor.setSoftWrap(true)


:heavy_minus_sign: Remove useless packages

- file-icons: Slowdown startup time
- clip-merge: Use LaunchBar6 clip-merge instead
- atom-alignment,
  atom-ternjs,
  autocomplete-emojis,
  linter-eslint,
  linter-jsonlint,
  minimap-bookmarks: Don't need them ...

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ooJerryLeeoo/dotfiles.git/'


Add chain acceptance.

Add chain acceptance.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jamesotron/ember-enumerology.git/'


i18n tests

i18n tests

error: pathspec 'tests/i18n.coffee' did not match any file(s) known to git

--- tests/i18n.coffee
+++ tests/i18n.coffee
@@ -0,0 +1,37 @@
+should = require('chai').should()
+expect = require('chai').expect
+
+async = require 'async'
+glob = require 'glob'
+path = require 'path'
+_ = require 'lodash'
+
+_source = null
+keys_count = null
+locales = {}
+
+describe 'i18n', ->
+  before ->
+    _source = require '../i18n/_source.json'
+    keys_count = _.keys(_source).length
+
+    locales_files = glob.sync './i18n/*.json'
+    locales_files.shift()
+
+    _.each locales_files, (locale_path) ->
+      locale = path.basename(locale_path).replace('.json', '')
+      locales[locale] = require path.join('..', locale_path)
+
+
+  describe 'each locale', ->
+    it 'should have the same length of keys as _source', (done) ->
+      _.each _.keys(locales), (locale) ->
+        _.keys(locales[locale]).length.should.equal(keys_count)
+      done()
+
+    it 'should contain the same keys as _source', (done) ->
+      _.each _.keys(_source), (key) ->
+        _.each _.keys(locales), (locale) ->
+          expect(locales[locale][key]).to.exist
+
+      done()


Fixed the HeroVictoryModal handling of earned achievements which were earned a while ago. Before they were appearing as if achievements were always newly achieved.

--- app/views/play/level/modal/HeroVictoryModal.coffee
+++ app/views/play/level/modal/HeroVictoryModal.coffee
@@ -81,21 +81,25 @@
       @newEarnedAchievements.push ea
       @listenToOnce ea, 'sync', ->
         if _.all((ea.id for ea in @newEarnedAchievements))
+          @newEarnedAchievementsResource.markLoaded()
           @listenToOnce me, 'sync', ->
             @readyToContinue = true
             @updateSavingProgressStatus()
           me.fetch cache: false unless me.loading

     @readyToContinue = true if not @achievements.models.length
+
+    # have to use a something resource because addModelResource doesn't handle models being upserted/fetched via POST like we're doing here
+    @newEarnedAchievementsResource = @supermodel.addSomethingResource('earned achievements') if @newEarnedAchievements.length

   getRenderData: ->
     c = super()
     c.levelName = utils.i18n @level.attributes, 'name'
-    earnedAchievementMap = _.indexBy(@earnedAchievements?.models or [], (ea) -> ea.get('achievement'))
+    earnedAchievementMap = _.indexBy(@newEarnedAchievements or [], (ea) -> ea.get('achievement'))
     for achievement in @achievements.models
       earnedAchievement = earnedAchievementMap[achievement.id]
       if earnedAchievement
-        achievement.completedAWhileAgo = new Date() - Date.parse(earnedAchievement.get('created')) > 30 * 1000
+        achievement.completedAWhileAgo = new Date().getTime() - Date.parse(earnedAchievement.get('created')) > 30 * 1000
       achievement.worth = achievement.get 'worth', true
       achievement.gems = achievement.get('rewards')?.gems
     c.achievements = @achievements.models.slice()


Add initial rdfa-json.coffee draft implementation

error: pathspec 'js/rdfa-json.coffee' did not match any file(s) known to git

--- js/rdfa-json.coffee
+++ js/rdfa-json.coffee
@@ -0,0 +1,176 @@
+# namespace; works both in browser and node
+((ns) ->
+
+  ns.extract = (doc, base) ->
+    doc or= window.document
+    base or= window?.location.href
+    top = {}
+    if base
+      top["@id"] = base
+    root = {'@context': {}, '@graph': [top]}
+    idMap = {}
+    parseElement(doc.documentElement, root, top, null, {}, idMap)
+    return {data: root, map: idMap}
+
+  # TODO:
+  # - map first occurence of @id and fill in that
+  # - profile support (builtin html5 for now)
+  # IMP:
+  # - about + prop: intermediate current
+  # - fix hanging rev
+  # - if hanging and prop/rel/rev: inject bnode
+  # - interplay of about, property, rel/rev, typeof
+  # - xml:lang, xmlns:*
+  parseElement = (el, root, current, vocab, hanging, idMap) ->
+    attrs = el.attributes
+    graph = root['@graph']
+    ctxt = new Context(root['@context'], current)
+
+    if attrs?
+
+      if attrs.vocab?.value
+        vocab = attrs.vocab.value
+        ctxt.update('rdfa', "http://www.w3.org/ns/rdfa#")
+        top = graph[0]
+        top['rdfa:usesVocabulary'] = vocab
+      if attrs.prefix?.value
+        pfxs = attrs.prefix.value.split(/:?\s+/)
+        for i in [0..pfxs.length] by 2
+          pfx = pfxs[i]
+          ns = pfxs[i+1]
+          ctxt.update(pfx, ns) if attrs.lang?.value
+        # TODO: if we reach a non-xml literal here, add to just that?
+        ctxt.update('@language', attrs.lang.value)
+
+      # IMP: resolve against base?
+      if attrs.resource?
+        next = {'@id': attrs.resource.value}
+      else if attrs.href?
+        next = {'@id': attrs.href.value}
+      else if attrs.src?
+        next = {'@id': attrs.src.value}
+      else if attrs.about?
+        next = {'@id': attrs.about.value}
+
+      if not next and attrs.typeof
+        next = {}
+
+      predicate = attrs.property?.value or attrs.rel?.value or hanging.rel
+
+      if predicate
+        if next
+          value = next
+        else if attrs.property and attrs.content?
+          value = attrs.content.value
+        else unless attrs.rel or attrs.rev or hanging.rel or hanging.rev
+          # TODO: resolve datatype and match IRI
+          if attrs.datatype?.value == 'rdf:XMLLiteral'
+            value = el.innerHTML
+          else
+            value = el.textContent
+
+        if attrs.datatype
+          value = {'@value': value}
+          if attrs.datatype.value
+            value['@datatype'] = attrs.datatype.value
+
+      if attrs.typeof
+        types = attrs.typeof.value.split(/\s+/)
+        for type in types
+          if vocab and type.indexOf(':') == -1
+            ctxt.update(type, vocab + type)
+        (next or current)['@typeof'] = types
+
+      inlist = (attrs.inlist != undefined) or (hanging.rel and hanging.inlist)
+
+      if value
+        if predicate
+          for key, i in predicate.split(/\s+/)
+            key = storedKey(key, ctxt, vocab)
+            if key
+
+              if current[key]
+                items = current[key]
+                if inlist
+                  # IMP: always wrap list obj in array?
+                  if items instanceof Array
+                    if items[0]['@list']
+                      items = items[0]['@list']
+                    else
+                      l = []
+                      items.unshift({'@list': l})
+                      items = l
+                  else
+                    items = items['@list']
+              else
+                items = []
+                current[key] = if inlist then {'@list': items} else items
+
+              item = itemOrRef(value, i, idMap)
+
+              items.push(item)
+
+        rev = attrs.rev?.value or hanging.rev
+        if rev
+          for key, i in rev.split(/\s+/)
+            key = storedKey(key, ctxt, vocab)
+            item = itemOrRef(current, true, idMap)
+            items = value[key] or= []
+            items.push(item)
+            unless predicate
+              graph.push(value)
+
+        hanging = {}
+
+      else if attrs.rel or attrs.rev
+        hanging = {
+          rel: attrs.rel?.value,
+          rev: attrs.rev?.value,
+          inlist: attrs.inlist != undefined
+        }
+
+      else if next
+        # IMP: defer attach until a predicate for this is used?
+        if (sub for sub in el.childNodes when sub.nodeType is 1).length
+          graph.push(next)
+
+    for child in el.childNodes
+      if child.nodeType is 1
+        parseElement(child, root, next or current, vocab, hanging, idMap)
+    return
+
+  class Context
+    constructor: (@rootCtxt, @current) ->
+      @localCtxt = {}
+    update: (key, ref) ->
+      # IMP: recover from local conflict
+      ctxt = @rootCtxt
+      if @rootCtxt[key] and @rootCtxt[key] != ref
+        ctxt = @localCtxt
+        @current['@context'] = @localCtxt
+      ctxt[key] = ref
+
+  storedKey = (key, ctxt, vocab) ->
+    splitPos = key?.indexOf(':')
+    if splitPos > -1
+      # IMP: unless ns, find in profile
+    else
+      if vocab
+        iri = vocab + key
+        ctxt.update(key, iri)
+      else
+        # IMP: profile terms
+        return null
+    return key
+
+  itemOrRef = (value, asRef, idMap) ->
+    if asRef and typeof value == 'object' and not value['@value']
+      id = value['@id'] or= nextBNode()
+      return {'@id': id}
+    else
+      return value
+
+  bnode = 0
+  nextBNode = () -> '_:GEN'+ bnode++
+
+)(exports ? RDFaJSON = {})


Full unit test coverage with istanbul

Full unit test coverage with istanbul

Created coverage test for eventqueue.js

error: pathspec 'node/test/eventQueueTest.coffee' did not match any file(s) known to git

--- node/test/eventQueueTest.coffee
+++ node/test/eventQueueTest.coffee
@@ -0,0 +1,83 @@
+# eventQueueTest.coffee
+#----------------------------------------------------------------------------
+
+should = require "should"
+ROT = require "../lib/rot"
+
+describe "eventqueue", ->
+  it "should export ROT.EventQueue", ->
+    ROT.should.have.property "EventQueue"
+
+  it "should be possible to create an EventQueue", ->
+    eventQueue = new ROT.EventQueue()
+    eventQueue.should.be.ok
+
+  describe "EventQueue", ->
+    eventQueue = null
+
+    beforeEach ->
+      eventQueue = new ROT.EventQueue()
+      eventQueue.should.be.ok
+
+    describe "getTime", ->
+      it "should return 0, when no time has passed", ->
+        eventQueue.getTime().should.equal 0
+
+    describe "clear", ->
+      it "should clear the event queue of all events", ->
+        eventQueue.add "Event 1", 10
+        eventQueue.add "Event 2", 20
+        eventQueue.add "Event 3", 30
+        eventQueue.clear()
+        should(eventQueue.get()).equal null
+
+    describe "add", ->
+      it "should be able to handle events in time-order", ->
+        eventQueue.add "Event 1", 10
+        eventQueue.add "Event 2", 20
+        eventQueue.add "Event 3", 30
+        eventQueue.get().should.equal "Event 1"
+        eventQueue.get().should.equal "Event 2"
+        eventQueue.get().should.equal "Event 3"
+
+      it "should be able to handle events in reverse time-order", ->
+        eventQueue.add "Event 3", 30
+        eventQueue.add "Event 2", 20
+        eventQueue.add "Event 1", 10
+        eventQueue.get().should.equal "Event 1"
+        eventQueue.get().should.equal "Event 2"
+        eventQueue.get().should.equal "Event 3"
+
+    describe "get", ->
+      it "should return nothing, when no events are posted", ->
+        event = eventQueue.get()
+        should(event).equal null
+
+      it "should not advance time when it is not necessary", ->
+        eventQueue.add "Event 1", 10
+        eventQueue.add "Event 2", 10
+        eventQueue.add "Event 3", 10
+        eventQueue.get().should.equal "Event 1"
+        eventQueue.get().should.equal "Event 2"
+        eventQueue.get().should.equal "Event 3"
+
+    describe "remove", ->
+      it "should be able to remove events", ->
+        eventQueue.add "Event 1", 10
+        eventQueue.add "Event 2", 20
+        eventQueue.add "Event 3", 30
+        eventQueue.remove("Event 2").should.equal true
+        eventQueue.get().should.equal "Event 1"
+        eventQueue.get().should.equal "Event 3"
+
+      it "should return false if unable to remove the event", ->
+        eventQueue.add "Event 1", 10
+        eventQueue.add "Event 2", 20
+        eventQueue.add "Event 3", 30
+        eventQueue.remove("Event 7").should.equal false
+        eventQueue.get().should.equal "Event 1"
+        eventQueue.get().should.equal "Event 2"
+        eventQueue.get().should.equal "Event 3"
+
+#----------------------------------------------------------------------------
+# end of eventQueueTest.coffee


changed default drawer to be left as in docs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/supersonic.git/'


Add simple variable default and retrieve module.

error: pathspec 'src/vars/index.coffee' did not match any file(s) known to git

--- src/vars/index.coffee
+++ src/vars/index.coffee
@@ -0,0 +1,39 @@
+###*
+# @overview This module loads default variables in to the Stout UI variable
+# system which can shared compiled variables between SASS and JavaScript with
+# support for extension.
+# @module vars/default
+###
+
+err    = require 'stout-core/err'
+reduce = require 'lodash/reduce'
+
+variables = {}
+
+###*
+# Parses the passed variable path and attempts to read the value from the
+# internal store.
+#
+# @function read
+###
+read = (varPath) ->
+  parts = varPath.split '/'
+  reduce parts, (a, v) ->
+    if v isnt undefined then a[v]
+  , variables
+
+module.exports =
+
+  ###*
+  # Loads default UI variables.
+  #
+  # @param {Object} vars - The default values to load.
+  #
+  # @function default
+  ###
+  default: (path, vars) ->
+    if variables[path]
+      throw new err.Err "UI variable defaults \"#{path}\" already defined."
+    variables[path] = vars
+
+  get: read


Now passing AdParameters during flash initAd

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/OnCircle/gbox-video-player.git/'


[FIX] Clear ajax call timeout if reading choices from cache

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/saijim/Multipleter.git/'


(- fix noop) Properly scan environment variables

--- Paws/Source/additional.coffee
+++ Paws/Source/additional.coffee
@@ -87,7 +87,12 @@
                exports[name](process.env[name], environmental: yes)

          exports.colour = exports.color = (use = true)->
-            use_colour = use
+            if use == no or
+               use.charAt?(0) == 'n' or # no
+               use.charAt?(0) == 'f'    # false
+             use_colour = no
+            else
+             use_colour = yes

          if env_colour = process.env['COLOUR'] ? process.env['COLOR']
             exports.colour env_colour


remove selector

remove selector

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/OldGermanTrick/pretex.git/'


fix(recipes): fix saving recipes

fix(recipes): fix saving recipes

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/keboola/kbc-ui-doctool-admin.git/'


Missing migration for annotations' references.

In the past we stored link between a highlight and an annotation in the
document independently from the body. Now we require that each such
link has an <a/> tag in the body, so we add dummy links here if such is
missing.

error: pathspec 'server/migrations/0039-annotation-body.coffee' did not match any file(s) known to git

--- server/migrations/0039-annotation-body.coffee
+++ server/migrations/0039-annotation-body.coffee
@@ -0,0 +1,45 @@
+class Migration extends Document.PatchMigration
+  # In the past we stored link between a highlight and an annotation in the
+  # document independently from the body. Now we require that each such link
+  # has an  tag in the body, so we add dummy links here if such is missing.
+  name: "Adding missing reference links to body field"
+
+  forward: (db, collectionName, currentSchema, newSchema, callback) =>
+    db.collection collectionName, (error, collection) =>
+      return callback error if error
+      # At this stage only highlight referneces should exist, others were not yet possible through the interface
+      cursor = collection.find {_schema: currentSchema, body: {$exists: true}}, {body: 1, 'references.highlights': 1}
+      document = null
+      async.doWhilst (callback) =>
+        cursor.nextObject (error, doc) =>
+          return callback error if error
+          document = doc
+          return callback null unless document
+
+          async.eachSeries (document.references?.highlights or []), (highlight, callback) =>
+            assert highlight._id
+
+            # We do not want to depend on our parsing code as it might change through time and
+            # we want migrations to always work. So let's do a simple check here, checking if
+            # highlight's ID appears in the body as a string, assuming that is a link.
+            return callback null if new RegExp(highlight._id).test document.body
+
+            newBody = "#{ document.body }
(See h:#{ highlight._id })
"
+
+            collection.update {_schema: currentSchema, _id: document._id, body: document.body}, {$set: {body: newBody}}, (error, count) =>
+              return callback error if error
+              document.body = newBody
+              callback null
+          ,
+            (error) =>
+              return callback error if error
+              callback null
+      ,
+        =>
+          document
+      ,
+        (error) =>
+          return callback error if error
+          super db, collectionName, currentSchema, newSchema, callback
+
+Annotation.addMigration new Migration()


Breadcrumb specs

Breadcrumb specs

error: pathspec 'test/breadcrumbs/index.spec.coffee' did not match any file(s) known to git

--- test/breadcrumbs/index.spec.coffee
+++ test/breadcrumbs/index.spec.coffee
@@ -0,0 +1,23 @@
+{Testing, expect, sinon, _} = require 'openstax-react-components/test/helpers'
+
+{Breadcrumbs} = require 'breadcrumbs'
+TASK = require 'cc/tasks/C_UUID/m_uuid/GET'
+tasks = require 'task/collection'
+
+describe 'Breadcrumbs Component', ->
+  beforeEach ->
+    @props =
+      goToStep: sinon.spy()
+      moduleUUID: 'm_uuid'
+      collectionUUID: 'C_UUID'
+    tasks.load("#{@props.collectionUUID}/#{@props.moduleUUID}",  TASK)
+
+
+  it 'renders steps', ->
+    Testing.renderComponent( Breadcrumbs, props: @props ).then ({dom}) ->
+      expect(dom.querySelectorAll('.openstax-breadcrumbs-step').length).equal(4)
+
+  it 'calls goToStep callback', ->
+    Testing.renderComponent( Breadcrumbs, props: @props ).then ({dom}) =>
+      Testing.actions.click dom.querySelector('.openstax-breadcrumbs-step:first-child')
+      expect(@props.goToStep).to.have.been.called


Added debug info to line orientation task

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/UCSFMemoryAndAging/tabcat.git/'


Revert peer grading problem to master version

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bmedx/modulestore.git/'


Initial commit

Initial commit

app.coffee, CoffeeScript i.e., transcompiles to JavaScript -> app.js
Help: http://coffeescript.org/.

error: pathspec 'source/core/coffee/module/app.coffee' did not match any file(s) known to git

--- source/core/coffee/module/app.coffee
+++ source/core/coffee/module/app.coffee
@@ -0,0 +1,18 @@
+#
+# CBBL®
+# Computational Biology and Bioinformatics Laboratory.
+# _______________________________________________________________
+# CoffeeScript, http://coffeescript.org/.
+# The AMD JavaScript - for defining "Application" configurations.
+# _______________________________________________________________
+# Architecture and Code Handcrafted by Prabhat Kumar.
+# Architectuur en Code handgemaakt door Prabhat Kumar.
+# @author    : Prabhat Kumar [http://prabhatkumar.org/]
+# @copyright : Sequømics Corporation [http://sequomics.com/]
+# _______________________________________________________________
+# @date      : 10-May-2016
+# @license   : Apache, version 2.0
+# @require   : Node.js®
+# @require   : NPM
+# @require   : Grunt
+# _______________________________________________________________


Fixed issue with achievement modals

Fixed issue with achievement modals

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/alpire/picoCTF-web.git/'


Contextualizing javascript parameters

Contextualizing javascript parameters


:construction: Add stub for model tests

:construction: Add stub for model tests

error: pathspec 'spec/minimap-model-spec.coffee' did not match any file(s) known to git

--- spec/minimap-model-spec.coffee
+++ spec/minimap-model-spec.coffee
@@ -0,0 +1,18 @@
+
+describe 'MinimapModel', ->
+  [workspaceElement, editor, editorView] = []
+
+  beforeEach ->
+    waitsForPromise ->
+      atom.workspace.open('sample.js')
+
+    runs ->
+      workspaceElement = atom.views.getView(atom.workspace)
+      jasmine.attachToDOM(workspaceElement)
+      atom.config.set 'minimap.autoToggle', false
+
+    waitsFor ->
+      editor = atom.workspace.getActiveTextEditor()
+
+    runs ->
+      editorView = atom.views.getView(editor)


[preproc] note to self: this sucks

[preproc] note to self: this sucks

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/demhydraz/cafe.git/'


better verify token

better verify token

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/zzzev/insta.git/'


wait to subscribe

wait to subscribe


Added highcharts sparkline directive

Added highcharts sparkline directive

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/MicrosmartSA/ubiquity-ui.git/'


Add menu to toggle command palette

Add menu to toggle command palette

Closes atom/atom#1339

error: pathspec 'menus/command-palette.cson' did not match any file(s) known to git

--- menus/command-palette.cson
+++ menus/command-palette.cson
@@ -0,0 +1,25 @@
+'menu': [
+  {
+    'label': 'View'
+    'submenu': [
+      {
+        'label': 'Toggle Command Palette'
+        'command': 'command-palette:toggle'
+      }
+    ]
+  }
+  {
+    'label': 'Packages'
+    'submenu': [
+      {
+        'label': 'Command Palette',
+        'submenu': [
+          {
+            'label': 'Toggle'
+            'command': 'command-palette:toggle'
+          }
+        ]
+      }
+    ]
+  }
+]


add main-ember

add main-ember

error: pathspec 'app/scripts/main-ember.coffee' did not match any file(s) known to git

--- app/scripts/main-ember.coffee
+++ app/scripts/main-ember.coffee
@@ -0,0 +1,30 @@
+'use strict'
+@Clean = @Clean || {}
+
+@Clean = Ember.Application.create
+  LOG_TRANSITIONS: true
+  DEBUG: false
+  rootElement: '#application'
+
+@Clean.editor = new Editor
+  newPostCallback: (editor)->
+    editor.setConnecting true
+    $.ajax
+      url: 'http://localhost:3000'
+      success: (data)->
+        console.log 'ask id success'
+        editor.id = 'MOCKID123'
+        editor.setConnecting false
+      error: ->
+        console.log 'unable to ask id'
+        editor.setConnecting false
+  autosaveCallback: (editor)->
+    editor.setConnecting true
+    $.ajax
+      url: 'http://localhost:3000'
+      success: (data)->
+        console.log 'autosave success'
+        editor.setConnecting false
+      error: ->
+        console.log 'unable to autosave'
+        editor.setConnecting false


home/utilities: show different app download area via os

--- client/home/lib/utilities/index.coffee
+++ client/home/lib/utilities/index.coffee
@@ -1,4 +1,5 @@
 kd                            = require 'kd'
+os                            = require 'os'
 sectionize                    = require '../commons/sectionize'
 headerize                     = require '../commons/headerize'
 HomeUtilitiesKD               = require './homeutilitieskd'
@@ -24,8 +25,13 @@
     @wrapper.addSubView headerize  'KD CLI'
     @wrapper.addSubView sectionize 'KD CLI', HomeUtilitiesKD

-    @wrapper.addSubView headerize  'Koding OS X App'
-    @wrapper.addSubView sectionize 'Koding OS X App', HomeUtilitiesDesktopApp
+    switch os
+      when 'mac'
+        @wrapper.addSubView headerize  'Koding OS X App'
+        @wrapper.addSubView sectionize 'Koding App', HomeUtilitiesDesktopApp
+      when 'linux'
+        @wrapper.addSubView headerize  'Koding Linux App'
+        @wrapper.addSubView sectionize 'Koding App', HomeUtilitiesDesktopApp

     if '*' in allowedDomains or canEdit
         @wrapper.addSubView headerize  'Koding Button'


forgot to remove debug thing

forgot to remove debug thing

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/takeiteasy/doujin-db.git/'


sharedevents added to badge.coffee

sharedevents added to badge.coffee

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


Environments: Fix default stack meta.

Environments: Fix default stack meta.

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -65,6 +65,7 @@
       callback err, stack?.getId()

   @getStack = ({user, group, meta}, callback)->
+    meta  or= title: "Default", slug: "default"
     JStack.one {user, group, meta}, (err, stack)->
       return callback err  if err


Fix highlight.js to actually work on turbolinks:load

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/e-gob/interoperabilidad.git/'


Remove child content of the Directive DIV when the logged in state is reached

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/crdschurch/crds-ng-profile.git/'


Add servant script

Add servant script

Hubot is very attentive

error: pathspec 'src/scripts/servant.coffee' did not match any file(s) known to git

--- src/scripts/servant.coffee
+++ src/scripts/servant.coffee
@@ -0,0 +1,7 @@
+# Hubot is very attentive
+
+module.exports = (robot) ->
+  @name_regex = new RegExp("#{robot.name}\\?$", "i")
+
+  robot.hear @name_regex, (msg) ->
+    msg.reply "Yes, master?"


Add some Coffeescript to fetch our latest Tumblr blog post.

error: pathspec 'base/assets/javascripts/tumblr.coffee' did not match any file(s) known to git

--- base/assets/javascripts/tumblr.coffee
+++ base/assets/javascripts/tumblr.coffee
@@ -0,0 +1,35 @@
+Tumblr = Tumblr or {}
+Tumblr.RecentPosts = (el, postsCount) ->
+  apiUrl = "http://blog.hello-base.com/api/read/json?callback=?&filter=text&num=" + (postsCount or 10)
+  titleTypes =
+    regular: "regular-title"
+    link: "link-text"
+    quote: "quote-source"
+    photo: "photo-caption"
+    conversation: "conversation-title"
+    video: "video-caption"
+    audio: "audio-caption"
+    answer: "question"
+
+  renderPosts = (posts) ->
+    $.map $.map(posts, postInfo), renderPost
+
+  renderPost = (post) ->
+    ' The latest from our blog Standing Idol: ' + post.title + ' '
+
+  postInfo = (post) ->
+    titleType = titleTypes[post.type]
+    if titleType of post
+      title: post[titleType]
+      url: post["url-with-slug"]
+
+  render: ->
+    # loadingEl = $('
').text('Loading...').appendTo($(el))
+    $.getJSON apiUrl, (data) ->
+      # loadingEl.remove()
+      $('
').appendTo($(el)).hide().append(renderPosts(data.posts).join('\n')).slideDown 'slow'
+
+    this
+
+$ ->
+  new Tumblr.RecentPosts($('#standing-idol'), 1).render()


Added Pixie UI

Added Pixie UI

error: pathspec 'lib/assets/javascripts/pixie/ui.coffee' did not match any file(s) known to git

--- lib/assets/javascripts/pixie/ui.coffee
+++ lib/assets/javascripts/pixie/ui.coffee
@@ -0,0 +1,24 @@
+window.Pixie ||= {}
+
+###*
+Simple jQuery constructor wrappers for common elements.
+###
+
+elements = [
+  "Button"
+  "Canvas"
+  "Div"
+  "Img"
+  "Input"
+]
+
+Pixie.UI = {}
+
+elements.each (type) ->
+  tag = type.toLowerCase()
+  Pixie.UI[type] = (options) ->
+    options.class = 'btn' if type is 'Button'
+    jQuery "<#{tag}/>", options
+
+# Aliases
+Pixie.UI.Image = Pixie.UI.Img


close dropdown after select

close dropdown after select

[#90492230]

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/netguru/people.git/'


Not needed anymore

Not needed anymore

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/netguru/review.git/'


Add user credential test

Add user credential test

error: pathspec 'test/models/user_credential.coffee' did not match any file(s) known to git

--- test/models/user_credential.coffee
+++ test/models/user_credential.coffee
@@ -0,0 +1,20 @@
+UserCredential = require __dirname + "/../../models/user_credential"
+should = require 'should'
+
+describe 'User Credential', ->
+  it 'should be able to construct a user credential', ->
+    uc = new UserCredential
+      userId: 5
+    uc.userId.should.be.equal 5
+    uc.should.have.property 'id'
+    uc.should.have.property 'secret'
+
+  it 'should allow the generated properties to be passed in', ->
+    us = new UserCredential
+      userId: 5
+      id: 1324
+      secret: 'super secret key'
+
+    us.userId.should.be.equal 5
+    us.id.should.be.equal 1324
+    us.secret.should.be.equal 'super secret key'


Bringing back original base for gmaps4rails

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/rabid/nzbt.git/'


Op debugging

Op debugging

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/showell/CoffeeCoffee.git/'


make CAPA problems (MultipleChoice and Checkboxes) accessible

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bmedx/modulestore.git/'


fix(tags): fix default recipe id when creating a new tag

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/keboola/kbc-ui-doctool-admin.git/'


Add ReviewView top class

Add ReviewView top class

error: pathspec 'client/app/CommonViews/reviews/reviewview.coffee' did not match any file(s) known to git

--- client/app/CommonViews/reviews/reviewview.coffee
+++ client/app/CommonViews/reviews/reviewview.coffee
@@ -0,0 +1,88 @@
+class ReviewView extends KDView
+
+  constructor:(options, data)->
+
+    super
+
+    @setClass "comment-container"
+    @createSubViews data
+    @resetDecoration()
+    @attachListeners()
+
+  render:->
+    @resetDecoration()
+
+  createSubViews:(data)->
+
+    @reviewList = new KDListView
+      type          : "comments"
+      subItemClass  : CommentListItemView
+      delegate      : @
+    , data
+
+    @reviewController         = new ReviewListViewController view: @reviewList
+    @addSubView showMore      = new CommentViewHeader
+      delegate        : @reviewList
+      itemTypeString  : 'review'
+    , data
+    @addSubView @reviewController.getView()
+    @addSubView @commentForm  = new NewReviewForm delegate : @reviewList
+
+    @reviewList.on "OwnCommentHasArrived", -> showMore.ownCommentArrived()
+    @reviewList.on "ReviewIsDeleted", -> showMore.ownCommentDeleted()
+
+    data.fetchRelativeReviews limit:10, after:'meta.createdAt', (err, reviews)=>
+      for review in reviews
+        @reviewList.addItem review
+
+    @reviewList.emit "BackgroundActivityFinished"
+
+  attachListeners:->
+
+    @listenTo
+      KDEventTypes : "DecorateActiveCommentView"
+      listenedToInstance : @reviewList
+      callback : @decorateActiveCommentState
+
+    @listenTo
+      KDEventTypes : "CommentLinkReceivedClick"
+      listenedToInstance : @reviewList
+      callback : (pubInst, event) =>
+        @commentForm.commentInput.setFocus()
+
+    @reviewList.on "CommentCountClicked", =>
+      @reviewList.emit "AllCommentsLinkWasClicked"
+
+    @listenTo
+      KDEventTypes : "CommentViewShouldReset"
+      listenedToInstance : @reviewList
+      callback : @resetDecoration
+
+  resetDecoration:->
+    post = @getData()
+    if post.repliesCount is 0
+      @decorateNoCommentState()
+    else
+      @decorateCommentedState()
+
+  decorateNoCommentState:->
+    @unsetClass "active-comment"
+    @unsetClass "commented"
+    @setClass "no-comment"
+
+  decorateCommentedState:->
+    @unsetClass "active-comment"
+    @unsetClass "no-comment"
+    @setClass "commented"
+
+  decorateActiveCommentState:->
+    @unsetClass "commented"
+    @unsetClass "no-comment"
+    @setClass "active-comment"
+
+  decorateItemAsLiked:(likeObj)->
+    if likeObj?.results?.likeCount > 0
+      @setClass "liked"
+    else
+      @unsetClass "liked"
+    @ActivityActionsView.setLikedCount likeObj


Build, curse ye!

Build, curse ye!

--- config.coffee
+++ config.coffee
@@ -37,9 +37,7 @@
         # //# sourceMappingURL=/javascripts/app.js.map
         # So we remove the ones that have public in them.
         exec = require('child_process').exec
-        for file in files when /\.js/.test file.path
-          exec "perl -pi -e 's/\\/\\/# sourceMappingURL=public.*//g' #{file.path}"
-        exec c
+        exec "perl -pi -e 's/\\/\\/# sourceMappingURL=public.*//g' public/javascripts/*.js"

   files:
     javascripts:


add experiment

add experiment

error: pathspec 'src/experiments/jzr-composite-font-builder/collect-glyph-descriptions.coffee' did not match any file(s) known to git


add -trim

add -trim

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bradedelman/pdf2image.git/'


add robot's main

add robot's main

error: pathspec 'scripts/main.coffee' did not match any file(s) known to git

--- scripts/main.coffee
+++ scripts/main.coffee
@@ -0,0 +1,31 @@
+r = require('redis')
+util = require('./lib/util')
+git = require('./lib/git')
+
+module.exports = (robot) ->
+  if robot.adapterName == "slack"
+    robot.hear ///#{robot.name}-release///, (msg) ->
+      title = util.withDate("Release")
+      git.createPRwithCommitter(title).done((url) ->
+        msg.reply url
+      )
+
+#    robot.respond /.*/, (msg) ->
+#      msg.reply "test"
+
+#    robot.respond /redis/, (msg) ->
+#      k = "test"
+
+###
+client = r.createClient()
+client.set(k, "test-value")
+client.get(k, (err, reply) ->
+  if err
+    throw err
+  else if reply
+    # msg.reply JSON.parse(reply)
+    msg.reply reply
+  else
+    msg.reply "key: #{k}は見つかりません"
+)
+###


Notes for the tabs controller

Notes for the tabs controller

error: pathspec 'app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee' did not match any file(s) known to git

--- app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee
@@ -0,0 +1,45 @@
+Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location) ->
+  $scope.active = (tab)->
+    $location.path() == tab
+
+  $scope.tabs = ["contact", "about", "groups", "producers"]
+  for tab in $scope.tabs
+    $scope[tab] =
+      active: false #$scope.active("/" + tab)
+      path: "/" + tab
+
+  $scope.$watch ->
+    $location.path()
+  , (path)->
+    for tab in $scope.tabs
+      $scope[tab].active = $scope.active($scope[tab].path)
+
+  #$scope.select = (tab)->
+    #$location.path tab.path
+    #
+    #
+
+# directive -> ng-click -> scope method (not isolated) -> toggle active | change location
+# watch active expression -> change tab appearance
+
+#select = ->
+  #$location.path(tab.path)
+
+#active expression:
+  #"$location.path() == tab.path"
+
+
+# directive -> ng-click -> set active (on isolated scope)
+# ng-class -> change tab appearance
+# two-way binding active attr <-> tab.active
+
+# directive attr (select) -> scope.selectExpression
+# scope.select -> $parse ...
+#
+# in Directive
+# scope.select = $parse(attrs.select)
+# $scope.select($scope)
+#
+# 1: remove reverse binding on tab.active
+# 2: put $parse(attrs.select) onto tab
+# 3: override TabsetController/select to run tab.select() against original tab.$parent


Filter example files from built app

Filter example files from built app

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -69,16 +69,17 @@
     ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"

     testFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}tests?#{_.escapeRegExp(path.sep)}")
+    exampleFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}examples?#{_.escapeRegExp(path.sep)}")

     nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
     filterNodeModule = (pathToCopy) ->
       pathToCopy = path.resolve(pathToCopy)
-      nodeModulesFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy)
+      nodeModulesFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)

     packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)")
     filterPackage = (pathToCopy) ->
       pathToCopy = path.resolve(pathToCopy)
-      packageFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy)
+      packageFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)

     for directory in nonPackageDirectories
       cp directory, path.join(appDir, directory), filter: filterNodeModule
@@ -108,4 +109,4 @@
     dependencies = ['compile', "generate-license:save"]
     dependencies.push('copy-info-plist') if process.platform is 'darwin'
     dependencies.push('set-exe-icon') if process.platform is 'win32'
-    grunt.task.run(dependencies...)
+    # grunt.task.run(dependencies...)


Don't include *.pdb files

Don't include *.pdb files

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -56,6 +56,7 @@
       path.join('build', 'Release', 'obj.target')
       path.join('build', 'Release', 'obj')
       path.join('build', 'Release', '.deps')
+      path.join('build', 'Release', '.*\\.pdb')
       path.join('vendor', 'apm')
       path.join('resources', 'mac')
       path.join('resources', 'win')


Filter apm folter using standard node_modules filter

This reduces the apm folder size in the built app by 4MB

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -21,7 +21,6 @@

     cp 'atom.sh', path.join(appDir, 'atom.sh')
     cp 'package.json', path.join(appDir, 'package.json')
-    cp 'apm', path.join(appDir, 'apm')

     packageDirectories = []
     nonPackageDirectories = [
@@ -64,6 +63,7 @@
     cp 'spec', path.join(appDir, 'spec')
     cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/
     cp 'static', path.join(appDir, 'static')
+    cp 'apm', path.join(appDir, 'apm'), filter: nodeModulesFilter

     if process.platform is 'darwin'
       grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->


IDE: Select machine on sidebar when IDE became ready.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


Adds upgrade db framework tests.

Adds upgrade db framework tests.

error: pathspec 'test/unit/upgradeDBTest.coffee' did not match any file(s) known to git

--- test/unit/upgradeDBTest.coffee
+++ test/unit/upgradeDBTest.coffee
@@ -0,0 +1,97 @@
+upgradeDB = require '../../lib/upgradeDB'
+testUtils = require '../testUtils'
+Keystore = require('../../lib/model/keystore').Keystore
+Client = require('../../lib/model/clients').Client
+Q = require 'q'
+
+describe 'Upgrade DB Tests', ->
+
+  describe '.upgradeDB', ->
+
+    func1Complete = false
+    func2Complete = false
+
+    mockUpgradeFunc1 = () ->
+      defer = Q.defer()
+      setTimeout ->
+        if func2Complete
+          throw new Error 'Funtions ran non sequentially'
+        else
+          func1Complete = true
+          defer.resolve()
+      , 10
+      return defer.promise
+
+    mockUpgradeFunc2 = () ->
+      defer = Q.defer()
+      func2Complete = true
+      defer.resolve()
+      return defer.promise
+
+    it 'should run each upgrade function sequentially', (done) ->
+      upgradeDB.upgradeFuncs.length = 0
+      upgradeDB.upgradeFuncs.push
+        description: 'mock func 1'
+        func: mockUpgradeFunc1
+      upgradeDB.upgradeFuncs.push
+        description: 'mock func 2'
+        func: mockUpgradeFunc2
+
+      upgradeDB.upgradeDb ->
+        func1Complete.should.be.exactly true
+        func2Complete.should.be.exactly true
+        done()
+
+  describe 'updateFunction0 - Ensure cert fingerprint', ->
+
+    upgradeFunc = upgradeDB.upgradeFuncs[0].func
+
+    beforeEach (done) ->
+      testUtils.setupTestKeystore ->
+        Keystore.findOne (err, keystore) ->
+          keystore.cert.fingerprint = undefined
+          for cert in keystore.ca
+            cert.fingerprint = undefined
+          keystore.save (err) ->
+            if err then logger.error err
+            done()
+
+    it 'should add the fingerprint property to ca certificates', (done) ->
+      upgradeFunc().then ->
+        Keystore.findOne (err, keystore) ->
+          if err then logger.error err
+          for cert in keystore.ca
+            cert.fingerprint.should.exist
+          done()
+
+    it 'should add the fingerprint property to server certificate', (done) ->
+      upgradeFunc().then ->
+        Keystore.findOne (err, keystore) ->
+          if err then logger.error err
+          keystore.cert.fingerprint.should.exist
+          done()
+
+  describe 'updateFunction1 - Convert client.domain to client.fingerprint', ->
+
+    upgradeFunc = upgradeDB.upgradeFuncs[1].func
+
+    clientData =
+      clientID: "test"
+      clientDomain: "trust1.org" # in default test keystore
+      name: "Test client"
+      roles: [
+          "OpenMRS_PoC"
+          "PoC"
+        ]
+
+    before (done) ->
+      testUtils.setupTestKeystore ->
+        client = new Client clientData
+        client.save (err) ->
+          if err? then logger.error err
+          done()
+
+    it 'should convert client.domain match to client.certFingerprint match', ->
+      upgradeFunc().then ->
+        Client.findOne clientID: "test", (err, client) ->
+          client.certFingerprint.should.be.exactly "23:1D:0B:AA:70:06:A5:D4:DC:E9:B9:C3:BD:2C:56:7F:29:D2:3E:54"


Fix double-spawning of express.

Fix double-spawning of express.

--- test/watch_project_spec.coffee
+++ test/watch_project_spec.coffee
@@ -33,6 +33,8 @@
         'backbone-0.5.3.js'
       ]
       application.watch ->
+        return if expressProcess?
+        console.log 'Spawning process'
         expressProcess = spawn 'coffee', [
           path.join(__dirname, 'server', 'server.coffee'),
           '8080',
@@ -46,6 +48,7 @@
     application.stopWatching()
     application = null
     expressProcess?.kill? 'SIGHUP'
+    expressProcess = null
     removeDirectory 'brunch', -> removed = yes
     waitsFor (-> removed), 'Cannot remove', 200


add coffee-script version

add coffee-script version

error: pathspec 'src/cli.coffee' did not match any file(s) known to git

--- src/cli.coffee
+++ src/cli.coffee
@@ -0,0 +1,75 @@
+fs = require 'fs'
+path = require 'path'
+commander = require 'commander-b'
+moment = require 'moment'
+
+getCommand = ->
+  program = commander 'bbn'
+  program.version require('../package.json').version
+  program
+  .command 'new', 'create a new post'
+  .option '-d, --date <date>'
+  .option '-w, --weekend'
+  .action (options = {}) ->
+    config = getConfig()
+    options.directory = config.directory || '/home/bouzuya/blog.bouzuya.net'
+
+    ts = if options.date then options.date + 'T23:59:59+09:00' else null
+    date = moment.apply null, if ts? then [ts, 'YYYY-MM-DDThh:mm:ssZ'] else []
+    file = getPath options.directory, date.format('YYYY-MM-DD')
+
+    if fs.existsSync file
+      console.error "the post #{file} already exists"
+      return 1
+    else
+      data = getTemplate date, options
+      fs.writeFileSync file, data, encoding: 'utf8'
+      console.log "create a new post #{file}"
+      return 0
+  program
+
+getConfig = ->
+  configFile = path.join process.env.HOME, '.bbn.json'
+  if fs.existsSync configFile then require(configFile) else {}
+
+getPath = (dir, date) ->
+  [y, m, _] = date.split '-'
+  path.join dir, 'data', y, m, date + '-diary.md'
+
+getTemplate = (m, options) ->
+  """
+    ---
+    layout: post
+    pubdate: "#{m.format()}"
+    title: ''
+    tags: ['']
+    minutes:
+    pagetype: posts
+    ---
+
+    #{(if options.weekend then getTemplateForWeekend(m, options) else '')}
+  """
+
+getTemplateForWeekend = (m, options) ->
+  # [{ date: 'yyyy-mm-dd', title: 'abc' }, ...]
+  posts = [1..7]
+  .map (i) ->
+    moment(m).subtract(i, 'days').format('YYYY-MM-DD')
+  .map (date) ->
+    date: date
+    title: getTitle options.directory, date
+    url: "http://blog.bouzuya.net/#{date.replace(/-/g, '/')}/"
+  """
+  #{posts.map((i) -> "- [#{i.date} #{i.title}][#{i.date}]").join('\n')}
+
+  #{posts.map((i) -> "[#{i.date}]: #{i.url}").join('\n')}
+  """
+
+getTitle = (dir, date) ->
+  data = fs.readFileSync getPath(dir, date), encoding: 'utf8'
+  title = data.match(/^title: '?(.*)$/m)[1]
+  if title[title.length - 1] is '\'' then title.slice(0, -1) else title
+
+module.exports = ->
+  command = getCommand()
+  command.execute()


Changing the router.coffee to adapt to coffescript syntax

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Sgoettschkes/learning.git/'


<title>?

<title>?

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/guaka/wikifarmchanges.git/'


Dont' expire the session cookie.

Dont' expire the session cookie.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/muffin/tutorial-twitter-clone.git/'


Web library

Web library

error: pathspec 'src/web.coffee' did not match any file(s) known to git

--- src/web.coffee
+++ src/web.coffee
@@ -0,0 +1,93 @@
+# web.js
+#
+# Wrap http/https requests in a callback interface
+#
+# Copyright 2012, E14N https://e14n.com/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+urlparse = require('url').parse
+http = require('http')
+https = require('https')
+
+web = (verb, url, headers, reqBody, callback) ->
+
+    # Optional body
+
+    if !callback
+      callback = reqBody
+      body = null
+
+    # Optional headers
+
+    if !callback
+      callback = headers
+      headers = null
+
+    parts = urlparse url
+
+    if parts.protocol == 'http:'
+      mod = http
+    else if parts.protocol == 'https:'
+      mod = https
+    else
+      callback new Error("Unsupported protocol: #{parts.protocol}")
+
+    options =
+      host: parts.hostname
+      port: parts.port
+      path: parts.path
+      method: verb.toUpperCase()
+      headers: headers
+      agent: false
+
+    req = mod.request options, (res) ->
+      resBody = ''
+      res.setEncoding 'utf8'
+      res.on 'data', (chunk) ->
+        resBody = resBody + chunk
+      res.on 'error', (err) ->
+        callback err, null
+      res.on 'end', ->
+        callback null, res, resBody
+
+    req.on 'error', (err) ->
+      callback err, null
+
+    if reqBody
+      req.write reqBody
+
+    req.end()
+
+get = (url, headers, callback) ->
+  web "GET", url, headers, callback
+
+post = (url, headers, body, callback) ->
+  web "POST", url, headers, body, callback
+
+head = (url, headers, callback) ->
+  web "HEAD", url, headers, callback
+
+put = (url, headers, body, callback) ->
+  web "PUT", url, headers, body, callback
+
+del = (url, headers, callback) ->
+  web "DELETE", url, headers, callback
+
+module.exports =
+  web: web
+  get: get
+  post: post
+  head: head
+  put: put
+  del: del


Remove some commented out code

Remove some commented out code

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/nathan/lemma.git/'


Style update

Style update

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/evercam/evercam-dashboard.git/'


Fix balance graph date labels.

Fix balance graph date labels.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/pipeline/dotledger.git/'


initial pass at a doppio repl

initial pass at a doppio repl

based on http://coffeescript.org/documentation/docs/repl.html, still
needs cleanup

error: pathspec 'console/repl.coffee' did not match any file(s) known to git

--- console/repl.coffee
+++ console/repl.coffee
@@ -0,0 +1,68 @@
+#!/usr/bin/env coffee
+fs = require 'fs'
+path = require 'path'
+readline = require 'readline'
+jvm = require '../src/jvm'
+util = require '../src/util'
+ClassFile = require '../src/class_file'
+methods = require '../src/methods'
+
+classpath = [ ".", "#{__dirname}/../third_party/classes" ]
+
+exports.read_binary_file = (filename) ->
+  return null unless path.existsSync filename
+  util.bytestr_to_array fs.readFileSync(filename, 'binary')
+
+exports.read_classfile = (cls) ->
+  for p in classpath
+    data = exports.read_binary_file "#{p}/#{cls}.class"
+    return new ClassFile data if data?
+
+repl_run = (rs, cname, args) ->
+  cname = cname[0...-6] if cname[-6..] is '.class'
+  args ?= []
+  jvm.run_class rs, cname, args
+
+if require.main == module
+  optimist = require 'optimist'
+  {argv} = optimist
+
+  optimist.usage '''
+  Usage: $0
+  Optional flags:
+    --classpath=[path1:...:pathn]
+    --help
+  '''
+
+  return optimist.showHelp() if argv.help
+
+  if argv.classpath?
+    classpath = argv.classpath.split ':'
+    classpath.push "#{__dirname}/../third_party/classes"
+
+  stdout = process.stdout
+  stdin = process.openStdin()
+
+  write_stdout = stdout.write.bind process.stdout
+  read_stdin = (n_bytes, resume) ->
+    process.stdin.resume()
+    process.stdin.once 'data', (data) ->
+      process.stdin.pause()
+      resume data
+
+  rs = new runtime.RuntimeState(write_stdout, read_stdin, exports.read_classfile)
+
+  repl = readline.createInterface stdin, stdout
+
+  repl.on 'close', ->
+    repl.output.write '\n'
+    repl.input.destroy()
+  repl.on 'line', (line) ->
+    toks = line.split /\s+/
+    if toks.length > 0
+      repl_run rs, toks[0], toks[1..]
+    repl.output.write '\n'
+    repl.prompt()
+  repl.setPrompt 'doppio> '
+  repl.prompt()
+


Minor improvements on tgTagLine directive.

--- app/coffee/modules/common/tags.coffee
+++ app/coffee/modules/common/tags.coffee
@@ -124,7 +124,7 @@

     link = ($scope, $el, $attrs, $model) ->
         editable = if $attrs.editable == "true" then true else false
-        $el.addClass('tags-block')
+        $el.addClass("tags-block")

         addValue = (value) ->
             value = trim(value)
@@ -137,33 +137,34 @@
             $scope.$apply ->
                 $model.$setViewValue(normalizeTags(tags))

-
         $scope.$watch $attrs.ngModel, (val) ->
-            return if not val
-            renderTags($el, val, editable, $scope.project.tags_colors)
-
-        $scope.$watch "projectId", (val) ->
-            return if not val?
+            console.log "WATCH", val
+            renderTags($el, val, editable, $scope.project.tags_colors) if val?
+
+        bindOnce $scope, "projectId", (projectId) ->
+            # If not editable, no tags preloading is needed.
+            return if not editable
+
             positioningFunction = (position, elements) ->
                 menu = elements.element.element
-                menu.css 'width', elements.target.width
-                menu.css 'top', position.top
-                menu.css 'left', position.left
-
-            promise = $rs.projects.tags($scope.projectId)
-            promise.then (data) ->
-                if editable
-                    $el.find("input").autocomplete({
-                        source: data,
-                        position:
-                            my: "left top",
-                            using: positioningFunction
-                        select: (event, ui) ->
-                            addValue(ui.item.value)
-                            ui.item.value = ""
-                    })
-
-        $el.find("input").remove() if not editable
+                menu.css("width", elements.target.width)
+                menu.css("top", position.top)
+                menu.css("left", position.left)
+
+            $rs.projects.tags(projectId).then (data) ->
+                $el.find("input").autocomplete({
+                    source: data
+                    position: {
+                        my: "left top",
+                        using: positioningFunction
+                    }
+                    select: (event, ui) ->
+                        addValue(ui.item.value)
+                        ui.item.value = ""
+                })
+
+        if not editable
+            $el.find("input").remove()

         $el.on "keypress", "input", (event) ->
             return if event.keyCode != 13
@@ -177,7 +178,6 @@
             addValue(target.val())
             target.val("")
             $el.find("input").autocomplete("close")
-

         $el.on "click", ".icon-delete", (event) ->
             event.preventDefault()


Cleaner hastag

Cleaner hastag

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/craftedlabs/rubyconfuruguay-mobile.git/'


Fix specs when using realpathSync for files

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -1640,7 +1640,7 @@

   describe "Git status decorations", ->
     beforeEach ->
-      projectPath = temp.mkdirSync('tree-view-project')
+      projectPath = fs.realpathSync(temp.mkdirSync('tree-view-project'))
       workingDirFixture = path.join(__dirname, 'fixtures', 'git', 'working-dir')
       fs.copySync(workingDirFixture, projectPath)
       fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'))


Convert to coffeescript classes

Convert to coffeescript classes

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kenliau/Zooey.git/'


reduced our three views to a single "patient" view

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/UCSFMemoryAndAging/tabcat.git/'


bug fix

bug fix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bubblegroup/bubblebot.git/'


activity: activate reactivity unless group is koding


video: model emit talking state change events

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -181,7 +181,11 @@
     @subscribers[connectionId] = _subscriber

     _subscriber.on 'TalkingDidStart', =>
+      @emit 'ParticipantStartedTalking', nick
       @changeActiveParticipant nick  unless @state.selectedParticipant
+
+     _subscriber.on 'TalkingDidStop', =>
+      @emit 'ParticipantStoppedTalking', nick

     return _subscriber


Fix recovery and invitation work flows

Fix recovery and invitation work flows

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -286,14 +286,15 @@
         {JPasswordRecovery} = KD.remote.api
         JPasswordRecovery.validate recoveryToken, (err, isValid)=>
           if err or !isValid
-            new KDNotificationView
-              title   : 'Something went wrong.'
-              content : err?.message or """
-                That doesn't seem to be a valid recovery token!
-                """
+            unless KD.isLoggedIn()
+              new KDNotificationView
+                title   : 'Something went wrong.'
+                content : err?.message or """
+                  That doesn't seem to be a valid recovery token!
+                  """
           else
             mainController.loginScreen.headBannerShowRecovery recoveryToken
-          @clear()
+          @clear "/"

       '/:name?/Invitation/:inviteCode': ({params:{inviteCode}})=>
         inviteCode = decodeURIComponent inviteCode
@@ -302,12 +303,13 @@
           @handleRoute '/', entryPoint: KD.config.entryPoint
         else KD.remote.api.JInvitation.byCode inviteCode, (err, invite)=>
           if err or !invite? or invite.status not in ['active','sent']
-            if err then error err
-            new KDNotificationView
-              title: 'Invalid invitation code!'
+            unless KD.isLoggedIn()
+              if err then error err
+              new KDNotificationView
+                title: 'Invalid invitation code!'
           else
             mainController.loginScreen.headBannerShowInvitation invite
-          @clear()
+          @clear "/"

       '/:name?/Verify/:confirmationToken': ({params:{confirmationToken}})->
         confirmationToken = decodeURIComponent confirmationToken


VPA - Vertical Position Absolute

VPA - Vertical Position Absolute

--- app/assets/javascripts/ansi-interpreter.js.coffee
+++ app/assets/javascripts/ansi-interpreter.js.coffee
@@ -120,6 +120,8 @@
         @terminal.eraseInLine @n or 0
       when "L"
         @terminal.insertLines @cursorY, @n or 1
+      when "d" # VPA - Vertical Position Absolute
+        @terminal.setCursorLine(@n)
       when "l" # l, Reset mode
         console.log "(TODO) reset: " + @n
       when "m"


checklist display logic switched to jquery, next is that autoset others stuff

error: pathspec 'app/assets/javascripts/coffee/checklist.js.coffee' did not match any file(s) known to git

--- app/assets/javascripts/coffee/checklist.js.coffee
+++ app/assets/javascripts/coffee/checklist.js.coffee
@@ -0,0 +1,7 @@
+jQuery ->
+  $('#checklist a.questionLink').click (e) ->
+    question = $(@).parent('p').next('div.questionDiv')
+    if !question.is(':visible')
+      $('div.questionDiv:visible').hide "blind", {direction: "vertical"},500
+      question.show "blind", {direction: "vertical"}, 500
+    false


Fix occasional spec flakiness on travis

Fix occasional spec flakiness on travis

--- spec/text-editor-component-spec.coffee
+++ spec/text-editor-component-spec.coffee
@@ -827,7 +827,7 @@
           expect(lineNumberHasClass(1, 'folded')).toBe false

         it "does not fold when the line number componentNode is clicked", ->
-          nextAnimationFrame() # clear pending frame request
+          nextAnimationFrame() unless nextAnimationFrame is noAnimationFrame # clear pending frame request if needed

           lineNumber = component.lineNumberNodeForScreenRow(1)
           lineNumber.dispatchEvent(buildClickEvent(lineNumber))


:green_heart: Start fixing presenter specs

--- spec/text-editor-presenter-spec.coffee
+++ spec/text-editor-presenter-spec.coffee
@@ -658,53 +658,32 @@
           expect(presenter.getState().content.placeholderText).toBe "new-placeholder-text"

       describe ".tiles", ->
-        fffit "contains states for tiles that are visible on screen", ->
-          presenter = buildPresenter(explicitHeight: 3, scrollTop: 1, lineHeight: 1, tileCount: 3)
-
-          console.log JSON.stringify(presenter.getState().content.tiles)
+        it "contains states for tiles that are visible on screen", ->
+          presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileCount: 3)
+
           expectValues presenter.getState().content.tiles[0], {
             top: 0
           }
-          expectValues presenter.getState().content.tiles[1], {
-            top: 1
-          }
           expectValues presenter.getState().content.tiles[2], {
             top: 2
           }
-          expectValues presenter.getState().content.tiles[3], {
-            top: 3
-          }
           expectValues presenter.getState().content.tiles[4], {
             top: 4
           }
-          expect(presenter.getState().content.tiles[5]).toBeUndefined()
-
-        it "does not overdraw above the first row", ->
-          presenter = buildPresenter(explicitHeight: 3, scrollTop: 0, lineHeight: 1, tileCount: 3, tileOverdrawMargin: 1)
-          expect(presenter.getState().content.tiles[-1]).toBeUndefined()
-          expect(presenter.getState().content.tiles[0]).toBeDefined()
-          expect(presenter.getState().content.tiles[1]).toBeDefined()
-          expect(presenter.getState().content.tiles[2]).toBeDefined()
-          expect(presenter.getState().content.tiles[3]).toBeDefined()
-          expect(presenter.getState().content.tiles[4]).toBeDefined()
-          expect(presenter.getState().content.tiles[5]).toBeUndefined()
-
-        it "does not overdraw below the last row", ->
-          presenter = buildPresenter(explicitHeight: 3, scrollTop: 10, lineHeight: 1, tileCount: 3, tileOverdrawMargin: 1)
+          expectValues presenter.getState().content.tiles[6], {
+            top: 6
+          }
+
           expect(presenter.getState().content.tiles[8]).toBeUndefined()
-          expect(presenter.getState().content.tiles[9]).toBeDefined()
-          expect(presenter.getState().content.tiles[10]).toBeDefined()
-          expect(presenter.getState().content.tiles[11]).toBeDefined()
-          expect(presenter.getState().content.tiles[12]).toBeDefined()
-          expect(presenter.getState().content.tiles[13]).toBeUndefined()
-
-        xit "includes state for all tiles if no external ::explicitHeight is assigned", ->
+
+
+        it "includes state for all tiles if no external ::explicitHeight is assigned", ->
           presenter = buildPresenter(explicitHeight: null, tileCount: 12, tileOverdrawMargin: 1)
           expect(presenter.getState().content.tiles[0]).toBeDefined()
           expect(presenter.getState().content.tiles[12]).toBeDefined()

         it "is empty until all of the required measurements are assigned", ->
-          presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, tileCount: 3, tileOverdrawMargin: 1)
+          presenter = buildPresenter(explicitHeight: null, lineHeight: null, scrollTop: null, tileCount: 3)
           expect(presenter.getState().content.tiles).toEqual({})

           presenter.setExplicitHeight(25)
@@ -717,48 +696,57 @@
           expect(presenter.getState().content.tiles).not.toEqual({})

         it "updates when ::scrollTop changes", ->
-          presenter = buildPresenter(explicitHeight: 3, scrollTop: 0, lineHeight: 1, tileOverdrawMargin: 1, tileCount: 3)
+          presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileCount: 3)

           expect(presenter.getState().content.tiles[0]).toBeDefined()
+          expect(presenter.getState().content.tiles[2]).toBeDefined()
           expect(presenter.getState().content.tiles[4]).toBeDefined()
-          expect(presenter.getState().content.tiles[5]).toBeUndefined()
+          expect(presenter.getState().content.tiles[6]).toBeDefined()
+          expect(presenter.getState().content.tiles[8]).toBeUndefined()

           expectStateUpdate presenter, -> presenter.setScrollTop(2)

           expect(presenter.getState().content.tiles[0]).toBeUndefined()
-          expect(presenter.getState().content.tiles[1]).toBeDefined()
+          expect(presenter.getState().content.tiles[2]).toBeDefined()
+          expect(presenter.getState().content.tiles[4]).toBeDefined()
           expect(presenter.getState().content.tiles[6]).toBeDefined()
-          expect(presenter.getState().content.tiles[7]).toBeUndefined()
-
-        xit "updates when ::explicitHeight changes", ->
-          presenter = buildPresenter(explicitHeight: 3, scrollTop: 0, lineHeight: 1, tileOverdrawMargin: 1, tileCount: 3)
-
-          line5 = editor.tokenizedLineForScreenRow(5)
-
+          expect(presenter.getState().content.tiles[8]).toBeDefined()
+          expect(presenter.getState().content.tiles[10]).toBeUndefined()
+
+        it "updates when ::explicitHeight changes", ->
+          presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileCount: 3)
+
+          expect(presenter.getState().content.tiles[0]).toBeDefined()
+          expect(presenter.getState().content.tiles[2]).toBeDefined()
           expect(presenter.getState().content.tiles[4]).toBeDefined()
-          expect(presenter.getState().content.tiles[5]).toBeUndefined()
-
-          expectStateUpdate presenter, -> presenter.setExplicitHeight(35)
-
-          expect(presenter.getState().content.tiles[5]).toBeDefined()
           expect(presenter.getState().content.tiles[6]).toBeDefined()
-          expect(presenter.getState().content.tiles[7]).toBeUndefined()
-
-        xit "updates when ::lineHeight changes", ->
-          presenter = buildPresenter(explicitHeight: 15, scrollTop: 10, lineHeight: 10, lineOverdrawMargin: 0)
-
-          expect(lineStateForScreenRow(presenter, 0)).toBeUndefined()
-          expect(lineStateForScreenRow(presenter, 1)).toBeDefined()
-          expect(lineStateForScreenRow(presenter, 2)).toBeDefined()
-          expect(lineStateForScreenRow(presenter, 4)).toBeUndefined()
-
-          expectStateUpdate presenter, -> presenter.setLineHeight(5)
-
-          expect(lineStateForScreenRow(presenter, 0)).toBeUndefined()
-          expect(lineStateForScreenRow(presenter, 1)).toBeUndefined()
-          expect(lineStateForScreenRow(presenter, 2)).toBeDefined()
-          expect(lineStateForScreenRow(presenter, 5)).toBeDefined()
-          expect(lineStateForScreenRow(presenter, 6)).toBeUndefined()
+          expect(presenter.getState().content.tiles[8]).toBeUndefined()
+
+          expectStateUpdate presenter, -> presenter.setExplicitHeight(8)
+
+          expect(presenter.getState().content.tiles[0]).toBeDefined()
+          expect(presenter.getState().content.tiles[2]).toBeDefined()
+          expect(presenter.getState().content.tiles[4]).toBeDefined()
+          expect(presenter.getState().content.tiles[6]).toBeDefined()
+          expect(presenter.getState().content.tiles[8]).toBeDefined()
+          expect(presenter.getState().content.tiles[10]).toBeUndefined()
+
+
+        it "updates when ::lineHeight changes", ->
+          presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileCount: 3)
+
+          expect(presenter.getState().content.tiles[0]).toBeDefined()
+          expect(presenter.getState().content.tiles[2]).toBeDefined()
+          expect(presenter.getState().content.tiles[4]).toBeDefined()
+          expect(presenter.getState().content.tiles[6]).toBeDefined()
+          expect(presenter.getState().content.tiles[8]).toBeUndefined()
+
+          expectStateUpdate presenter, -> presenter.setLineHeight(2)
+
+          expect(presenter.getState().content.tiles[0]).toBeDefined()
+          expect(presenter.getState().content.tiles[2]).toBeDefined()
+          expect(presenter.getState().content.tiles[4]).toBeDefined()
+          expect(presenter.getState().content.tiles[6]).toBeUndefined()

         it "updates when the editor's content changes", ->
           lineStateForScreenRow = (presenter, tile, row) ->
@@ -788,7 +776,6 @@
           }

       xdescribe ".lines", ->
-
         it "does not remove out-of-view lines corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", ->
           presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10, lineOverdrawMargin: 1, stoppedScrollingDelay: 200)


Fixed spell palette changes for iPad

Fixed spell palette changes for iPad

--- app/views/play/level/tome/SpellPaletteView.coffee
+++ app/views/play/level/tome/SpellPaletteView.coffee
@@ -59,7 +59,7 @@
           itemGroup.append entry.el
           entry.render()  # Render after appending so that we can access parent container for popover
       @$el.addClass 'hero'
-      @updateMaxHeight()
+      @updateMaxHeight() unless application.isIPadApp

   afterInsert: ->
     super()