refactored base filter view, handles rendering artworks and routing events

error: pathspec 'components/filter2/view.coffee' did not match any file(s) known to git

--- components/filter2/view.coffee
+++ components/filter2/view.coffee
@@ -0,0 +1,103 @@
+_ = require 'underscore'
+_s = require 'underscore.string'
+Backbone = require 'backbone'
+FilterArtworks = require '../../collections/filter_artworks.coffee'
+ArtworkColumnsView = require '../artwork_columns/view.coffee'
+DropdownGroupView = require './dropdown_group/view.coffee'
+FilterFixedHeader = require './fixed_header/view.coffee'
+HeadlineView = require './headline/view.coffee'
+CountView = require './count/view.coffee'
+SortView = require './sort/view.coffee'
+
+module.exports = class FilterView extends Backbone.View
+  giveUpCount: 0
+  columnWidth: 300
+  facets: ['price_range', 'dimension_range', 'medium']
+
+  initialize: ({@collection, @params}) ->
+    @initSubViews()
+
+    @listenTo @collection, 'sync', @render
+
+    @listenTo @params, 'change:page', =>
+      @collection.fetch
+        remove: false
+        data: @params.toJSON()
+
+    @listenTo @params, "change:sort", @reset
+    for facet in @facets
+      @listenTo @params, "change:#{facet}", @reset
+
+    $.onInfiniteScroll @nextPage
+
+  initSubViews: ->
+    new DropdownGroupView
+      el: @$('.filter-artworks-nav')
+      collection: @collection
+      params: @params
+      facets: @facets
+
+    new HeadlineView
+      el: @$('.filter-heading')
+      collection: @collection
+      params: @params
+      facets: @facets
+
+    new CountView
+      el: @$('.filter-sort-count-count')
+      collection: @collection
+      params: @params
+      facets: @facets
+
+    new SortView
+      el: @$('.filter-sort-count-pulldown')
+      collection: @collection
+      params: @params
+      facets: @facets
+
+    new FilterFixedHeader
+      el: @$('.filter-fixed-header-nav')
+      params: @params
+      scrollToEl: @$('.filter-artworks-sort-count')
+
+  render: (collection, response) =>
+    @giveUpCount++ if response.hits?.length is 0
+
+    @$('.filter-artworks').attr 'data-state',
+      if @collection.length is 0 then 'finished-paging'
+      else if @giveUpCount > 2 then 'finished-paging'
+      else if @params.get('page') > 500 then 'finished-paging'
+      else ''
+
+    @collection.prepareCounts @collection.counts
+    @newColumnsView() unless @columnsView?
+    @columnsView.appendArtworks(new FilterArtworks(response.hits).models)
+
+  newColumnsView: =>
+    @columnsView?.remove()
+    @$('.filter-artworks-list').html $el = $("

")
+    @columnsView = new ArtworkColumnsView
+      el: $el
+      collection: new FilterArtworks
+      artworkSize: 'tall'
+      numberOfColumns: Math.round $el.width() / @columnWidth
+      gutterWidth: 40
+
+  nextPage: =>
+    return if @$('.filter-artworks').is(':hidden') or
+              @$('.filter-artworks').attr('data-state') is 'finished-paging'
+    @params.set page: (@params.get('page') + 1) or 1
+
+  reset: =>
+    @params.
+      set({ page: 1, size: @pageSize }, { silent: true }).
+      trigger('change change:page')
+
+    @giveUpCount = 0
+
+    @$('.filter-artworks-list').html ''
+
+    @collection.trigger 'reset'
+
+    _.defer @newColumnsView
+


Changed channel variable name

Changed channel variable name

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/'


Client: Activity: Test: Created ChannelParticipantsDropdownItem container component to separate ChannelParticipantsDropdownItem component as smart-dumb components

error: pathspec 'client/activity/lib/components/channelparticipantsdropdownitem/container.coffee' did not match any file(s) known to git

--- client/activity/lib/components/channelparticipantsdropdownitem/container.coffee
+++ client/activity/lib/components/channelparticipantsdropdownitem/container.coffee
@@ -0,0 +1,22 @@
+kd        = require 'kd'
+View      = require './view'
+React     = require 'kd-react'
+immutable = require 'immutable'
+
+module.exports = class ChannelParticipantsDropdownItemContainer extends React.Component
+
+  @propTypes =
+    isSelected : React.PropTypes.bool
+    index      : React.PropTypes.number
+    item       : React.PropTypes.instanceOf(immutable.Map).isRequired
+
+
+  @defaultProps =
+    index      : 0
+    isSelected : no
+
+
+  render: ->
+
+    <View {...@props}/>
+


groups summary: when click is received elsewhere, it will now hide

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/'


Create the categorizable behaviour

Create the categorizable behaviour

error: pathspec 'lib/behaviours/Categorizable.coffee' did not match any file(s) known to git

--- lib/behaviours/Categorizable.coffee
+++ lib/behaviours/Categorizable.coffee
@@ -0,0 +1,86 @@
+_          = require 'underscore'
+Backbone   = require 'backbone'
+Marionette = require 'backbone.marionette'
+
+
+###
+Taggable behaviour
+========================================
+###
+module.exports = class Categorizable extends Marionette.Behavior
+
+  ###
+  @property {Object} Default options
+  ###
+  defaults: ->
+    categoryField:          '[name="category"]'
+    categoryModelAttribute: 'category'
+
+
+
+  ###
+  @property {Object} DOM elements
+  ###
+  ui: ->
+    categoryField: @options.categoryField
+
+
+
+  ###
+  Handler executed when the view is rendered
+  ###
+  onRender: ->
+    # wait a little so syphon can do their stuff
+    setTimeout(=>
+      if _.isString @ui.categoryField then return
+
+      # by default, using syphon to populate the form, it will set a JSON as a value
+      # change the value to something that selectize can interpret correctly
+      category = @view.model.get @options.categoryModelAttribute
+
+      if category and category instanceof Backbone.Model
+        defaultLocale = category.get 'defaultLocale'
+        @ui.categoryField.val defaultLocale.name
+
+      # setup the widget
+      @setUpCategoriesWidget @view.availableCategories
+    , 200)
+
+
+
+  ###
+  Converts the categories input field (just a text input) into a categories widget
+
+  @param {Collection} availableCategories  Tags Collection used to autocomplete
+                                           while the user types. The user can still
+                                           create new categories (not on the autocomplete)
+  ###
+  setUpCategoriesWidget: (availableCategories) ->
+    @ui.categoryField.selectize
+      persist: true
+      maxItems: 1
+      options: @buildOptions availableCategories
+      createOnBlur: true,
+      create: true
+      allowEmptyOption: true
+      closeAfterSelect: true
+
+
+
+  ###
+  Converts the tags collection into an array usable by the widget
+
+  @param {Collection} categories  Tags Collection used to autocomplete
+  ###
+  buildOptions: (categories) ->
+    unless categories
+      return []
+
+    categories.reduce((memo, category) ->
+      locale  = category.get 'defaultLocale'
+
+      if locale
+        categoryName = locale.name
+        memo.push { text: categoryName, value: categoryName }
+      memo
+    , [])


Client: Activity: Test: Created ChannelParticipantsInputWidget component.


Use push with a splat rather than concat

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -150,7 +150,7 @@
           if map and imgClientRects.length > 0
             areas = map.getElementsByTagName "area"
             areasAndRects = DomUtils.getClientRectsForAreas imgClientRects[0], areas
-            visibleElements = visibleElements.concat areasAndRects
+            visibleElements.push areasAndRects...

       # Check aria properties to see if the element should be ignored.
       if (element.getAttribute("aria-hidden")?.toLowerCase() in ["", "true"] or


opt: unhandled rejction

opt: unhandled rejction

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -214,7 +214,7 @@
 		if isLongStackTrace and p[$promiseStack]
 			while p
 				hStack += p[$promiseStack]
-				p = p[$prePromise]
+				p = p._pre

 		format = (str) ->
 			if typeof __filename == 'string'
@@ -411,7 +411,6 @@
 	$resolved = 1
 	$pending = 2

-	$prePromise = '_pre'
 	$promiseStack = '_pStack'

 	# These are some symbols. They won't be used to store data.
@@ -427,6 +426,10 @@
 	 * @private
 	###
 	_pCount: 0
+
+	_pre: null
+
+	_hasUnhandled: false

 	# *************************** Promise Hepers ****************************

@@ -463,7 +466,7 @@
 		if isFunction onRejected
 			p2._onRejected = onRejected

-		p2[$prePromise] = p1
+		p2._pre = p1

 		# 2.2.6
 		if p1._state == $pending
@@ -504,6 +507,16 @@
 		Yaku.onUnhandledRejection p._value, p
 		return

+	checkRejection = (p) ->
+		pre = p._pre
+		while pre
+			return if pre._hasUnhandled
+
+			pre = pre._pre
+
+		p._hasUnhandled = true
+		scheduleUnhandledRejection p
+
 	callHanler = (handler, value) ->
 		# 2.2.5
 		handler value
@@ -525,7 +538,7 @@
 		p._value = value

 		if state == $rejected
-			scheduleUnhandledRejection p
+			checkRejection p

 		i = 0
 		len = p._pCount


Add initial ModuleCache spec

Add initial ModuleCache spec

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

--- spec/module-cache-spec.coffee
+++ spec/module-cache-spec.coffee
@@ -0,0 +1,10 @@
+path = require 'path'
+Module = require 'module'
+
+describe 'ModuleCache', ->
+  beforeEach ->
+    spyOn(Module, '_findPath').andCallThrough()
+
+  it 'resolves atom shell module paths without hitting the filesystem', ->
+    require.resolve('shell')
+    expect(Module._findPath.callCount).toBe 0


incomplete: finally, I have a clue how IndexedDB works

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

--- coffee/indexeddb.coffee
+++ coffee/indexeddb.coffee
@@ -0,0 +1,179 @@
+#Storing and loading configuration in/from IndexedDB
+#
+# DB structure:
+#  Table: architectures
+#{
+#    name: str
+#    gridN: int
+#    gridM: int
+#    fucntionType: str (binary / Day-Night binary / Custom)
+#    functionId:str (code for binary, hash for custom)
+#    field: ste (stringified)
+#
+
+{E} = require "./htmlutil.coffee"
+{DomBuilder} = require "./dom_builder.coffee"
+
+#Using info from https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
+#
+window.indexedDB = window.indexedDB ? window.mozIndexedDB ? window.webkitIndexedDB ? window.msIndexedDB
+# This line should only be needed if it is needed to support the object's constants for older browsers
+window.IDBTransaction = window.IDBTransaction ? window.webkitIDBTransaction ? window.msIDBTransaction ? {READ_WRITE: "readwrite"}
+window.IDBKeyRange = window.IDBKeyRange ? window.webkitIDBKeyRange ? window.msIDBKeyRange
+
+exports.hasDbSupport = -> window.indexedDB?
+
+exports.GenerateFileList = class GenerateFileList
+  constructor: ->
+    unless (@container = E 'file-dialog-files')?
+      throw new Error "Container not found"
+    self.db = null
+
+    @status = "working"
+
+    request = window.indexedDB.open "SavedFields", Date.now()
+    request.onupgradeneeded = (e) => @upgradeNeeded e
+    request.onerror = (e) =>
+      console.log "DB error: #{e.target.errorCode}"
+      @status="error"
+    request.onsuccess = (e)=>
+      @db = e.target.result
+      console.log "Success, first run: #{@firstrun}"
+      @addSampleFiles =>
+        @loadDataFor 3, 4
+
+  upgradeNeeded: (e)->
+    console.log "Upgrade !"
+
+    db = e.target.result
+    if db.objectStoreNames.contains "files"
+      console.log "Dropping files..."
+      db.deleteObjectStore "files"
+    @fileStore = db.createObjectStore "files"
+
+  recordKey: (rcd) -> @key rcd.gridN, rcd.gridM, rcd.funcId, rcd.name
+
+  key: (n, m, func, name) ->
+    paddedNum = (x) -> ("0000" + x.toString(16)).substr(4)
+    "#{paddedNum n}$#{paddedNum m}$#{func}$#{name}"
+
+  loadFromCursor: (cursor) ->
+    dom = new DomBuilder()
+
+    startGridGroup = (gridName) ->
+      dom.tag("div").CLASS("files-grid-group")
+         .tag("h1").text("Grid: #{gridName}").end()
+    closeGridGroup = ->
+      dom.end()
+
+    startFuncGroup = (funcType, funcId) ->
+      funcName = "#{funcType}: #{funcId}"
+      dom.tag("div").CLASS("files-func-group")
+         .tag("h2").text("Rule: #{funcName}").end()
+         .tag("table").tag("thead").tag("tr")
+         .tag("th").text("Name").end().tag("th").text("Size").end()
+         .end().end()
+         .tag("tbody")
+
+    closeFuncGroup = ->
+      dom.end().end().end() #tbody table div
+
+    lastGrid = null
+    lastFunc = null
+
+    cursor.onsuccess = (e)=>
+      res = e.target.result
+      console.log "Load file: #{res.key}" if res?
+      if res
+        record = res.value
+
+        grid = "{#{record.gridN};#{record.gridM}}"
+        if grid isnt lastGrid
+          #loading next group
+          #close the previous group
+          closeFuncGroup() if lastFunc isnt null
+          closeGridGroup() if lastGrid isnt null
+          startGridGroup grid
+          lastGrid = grid
+          lastFunc = null
+
+        if record.funcId isnt lastFunc
+          closeFuncGroup() if lastFunc isnt null
+          startFuncGroup record.funcType, record.funcId
+          lastFunc = record.funcId
+
+        dom.tag('tr')
+           .tag('td').rtag('alink','a').a('href',"#load#{record.name}").text(res.value.name).end().end()
+           .tag('td').text(""+res.value.field.length).end()
+           .end()
+        #dom.tag('div').CLASS("file-list-file").text(res.value.name).end()
+        dom.vars.alink.addEventListener "click", ((key)=> (e) =>
+          e.preventDefault()
+          @clickedFile key
+          )(res.key)
+
+        res.continue()
+      else
+        closeFuncGroup() if lastFunc isnt null
+        closeGridGroup() if lastGrid isnt null
+        @container.innerHTML = ""
+        @container.appendChild dom.finalize()
+
+  clickedFile: (key) -> console.log "Load key #{key}"
+
+  loadData:  ->
+    console.log "Loaddata"
+    transaction = @db.transaction ["files"], "readonly"
+    filesStore = transaction.objectStore "files"
+    cursor = filesStore.openCursor()
+    @loadFromCursor cursor
+
+  loadDataFor: (gridN, gridM, funcId) ->
+    transaction = @db.transaction ["files"], "readonly"
+    filesStore = transaction.objectStore "files"
+    #create range
+
+    # key is N, M, func, name
+    if funcId?
+      range = IDBKeyRange.bound @key(gridN, gridM, funcId, ""), @key(gridN, gridM, funcId+" ", ""), false, true
+    else
+      range = IDBKeyRange.bound @key(gridN, gridM, "",""), @key(gridN, gridM+1, "",""), false, true
+
+    cursor = filesStore.openCursor range
+    @loadFromCursor cursor
+
+
+  addSampleFiles:  (onFinish) ->
+    # Add few random riles.
+    # Transaction commits, when the last onsuccess does not schedules any more requests.
+    #
+    transaction = @db.transaction(["files"],"readwrite");
+    filesStore = transaction.objectStore "files"
+    i = 0
+    doAdd = =>
+      sampleFile =
+        gridN: (i/2)|0+3
+        gridM: (i/3)|0+4
+        name: "File #{i+1}"
+        funcId: "B 3 S 2 3"
+        funcType: "binary"
+        field: "|1"
+      key = @recordKey sampleFile
+      #console.log "Key: #{key}"
+      #
+      request = filesStore.add sampleFile, key
+      request.onerror = (e)=>
+        console.log "error adding file"
+        console.dir e.target.error
+      request.onsuccess = =>
+        #console.log "file added successfully"
+        if i < 3000
+          #console.log "Adding next file"
+          i += 1
+          doAdd()
+        else
+          console.log "End"
+          onFinish()
+
+    doAdd()
+


Tests: credentialHelper: provide some verbose logs for debugging

--- workers/social/testhelper/models/computeproviders/credentialhelper.coffee
+++ workers/social/testhelper/models/computeproviders/credentialhelper.coffee
@@ -48,11 +48,8 @@
   options.title    ?= 'koding'

   JCredential.create client, options, (err, credential) ->
-
     addToRemoveList client, credential.identifier  if credential
-
-    console.log '>>> ERROR ON CREATE CREDENTIAL', err  if err
-
+    console.error 'createCredential:', err  if err
     callback err, { credential }

@@ -65,6 +62,7 @@
     { client } = data

     createCredential client, options, (err, { credential }) ->
+      console.error 'withConvertedUserAndCredential:', err  if err
       expect(err).to.not.exist
       data.credential = credential
       callback data


Fix footer showing when not intended

Fix footer showing when not intended

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/C-Bouthoorn/Epsilon.git/'


Fixes #12: add indents support for Atom.

error: pathspec 'tools/syntax_highlighting/atom/language-do/settings/language-do.cson' did not match any file(s) known to git

--- tools/syntax_highlighting/atom/language-do/settings/language-do.cson
+++ tools/syntax_highlighting/atom/language-do/settings/language-do.cson
@@ -0,0 +1,17 @@
+'.source.do':
+	'editor':
+		'increaseIndentPattern': '(?x)
+				\\bthen\\s*$ |
+				\\belse\\s*$ |
+				\\bdo\\s*$ |
+				\\bstructure\\b.*$ |
+				\\bfunction\\b.*$ |
+				\\(\\s*$ |
+				\\[\\s*$
+			'
+		'decreaseIndentPattern': '(?x)
+				^\\s*end\\b |
+				^\\s*else\\b |
+				^\\s*\\) |
+				^\\s*\\]
+			'


add currency to invoice list tooltip

add currency to invoice list tooltip

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/xaun/impac-angular.git/'


Start adding some handy Polymer-flavored JavaScript snippets.

error: pathspec 'snippets/js-polymer-declaration.cson' did not match any file(s) known to git

--- snippets/js-polymer-declaration.cson
+++ snippets/js-polymer-declaration.cson
@@ -0,0 +1,17 @@
+".source.js":
+  "Polymer: properties":
+    "prefix": "properties"
+    "body": """
+    properties: {
+      ${1:name}: {
+        type: ${2:Boolean}
+      }
+    }
+    """
+  "Polymer: ready()":
+    "prefix": "ready"
+    "body": """
+    ready: function() {
+      $1
+    }
+    """


+ meta/abstract-type

+ meta/abstract-type

error: pathspec 'src/meta/abstract-type.coffee' did not match any file(s) known to git

--- src/meta/abstract-type.coffee
+++ src/meta/abstract-type.coffee
@@ -0,0 +1,43 @@
+isString        = require 'util-ex/lib/is/type/string'
+isArray         = require 'util-ex/lib/is/type/array'
+isFunction      = require 'util-ex/lib/is/type/function'
+defineProperty  = require 'util-ex/lib/defineProperty'
+getObjectKeys   = Object.keys
+
+module.exports = class AbstractTypeAttributes
+  concat: (attrs)->
+    for k,v of attrs
+      @[k] = v
+    return
+  _initialize: (aOptions)->
+  initialize: (aOptions)->
+    @_initialize(aOptions)
+    @names = @getNames()
+    return
+  constructor: (aOptions)->
+    defineProperty @, 'names', {}
+    @initialize(aOptions)
+
+  assignTo: (src, dest, aExclude, aSerialized)->
+    vNames = @names
+    if isString aExclude
+      aExclude = [aExclude]
+    else if not isArray aExclude
+      aExclude = []
+    for k, v of vNames
+      continue if v in aExclude
+      continue if aSerialized and (k[0] is '$')
+      vAttr = @[k]
+      if k is 'name'
+        vName = src[v] || src.name
+        dest.name = vName if vName and vName isnt dest.name
+      else if !aSerialized or (src[v]? and src[v] isnt vAttr.value)
+        if aSerialized or !isFunction(vAttr.assign) or !vAttr.assign(dest, src, v)
+          dest[v] = src[v]
+    return dest
+  getNames: ->
+    result = {}
+    for k in getObjectKeys @
+      v = @[k]
+      result[k] = v.name || k
+    result


For firefox lower 35 we need to set a class to hide own dropdown images whole file can be removed after dropping firefox 34 and lower support.

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

--- app/assets/javascripts/app/controllers/widget/ff_lt_35.js.coffee
+++ app/assets/javascripts/app/controllers/widget/ff_lt_35.js.coffee
@@ -0,0 +1,10 @@
+class FFlt35
+  constructor: ->
+    data = App.Browser.detection()
+    if data.browser is 'Firefox' && data.version && data.version < 35
+
+      # for firefox lower 35 we need to set a class to hide own dropdown images
+      # whole file can be removed after dropping firefox 34 and lower support
+      $('html').addClass('ff-lt-35')
+
+App.Config.set( 'aaa_ff-lt-35', FFlt35, 'Widgets' )


add katakana PUNSUKO

add katakana PUNSUKO

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/askdkc/aw_hubot.git/'


fix tests

fix tests

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/prognostikos/uploadcare-widget.git/'


fix typo in conf database dummy

fix typo in conf database dummy

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/namineundorf/auraya.git/'


Add Gruntfile

Add Gruntfile

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

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,80 @@
+module.exports = (grunt) ->
+  'use strict'
+
+  grunt.initConfig
+    pkg: grunt.file.readJSON('package.json')
+
+    # Task configuration
+    clean:
+      dist: 'dist'
+
+    coffee:
+      dist:
+        expand: true
+        cwd: 'public/assets/coffee'
+        src: ['*.coffee']
+        dest: 'public/assets/dist/js'
+        ext: '.js'
+
+    uglify:
+      options:
+        mangle: true
+        preserveComments: 'some'
+      dist:
+        expand: true
+        cwd: 'public/assets/dist/js'
+        src: ['**/*.js', '!**/*.min.js']
+        dest: 'public/assets/dist/js'
+        ext: '.min.js'
+
+    csscomb:
+      options:
+        config: 'public/assets/less/.csscomb.json'
+      dist:
+        expand: true
+        cwd: 'public/assets/dist/css'
+        src: ['*.css', '!*.min.css']
+        dest: 'public/assets/dist/css'
+
+    cssmin:
+      options:
+        compatibility: 'ie8'
+        keepSpecialComments: '*'
+        advanced: false
+      dist:
+        expand: true
+        cwd: 'public/assets/dist/css'
+        src: ['*.css', '!*.min.css']
+        dest: 'public/assets/dist/css'
+        ext: '.min.css'
+
+    less:
+      dist:
+        options:
+          strictMath: true
+          sourceMap: true
+          outputSourceFiles: true
+        expand: true
+        cwd: 'public/assets/less'
+        src: ['*.less']
+        dest: 'public/assets/dist/css'
+        ext: '.css'
+
+    watch:
+      coffee:
+        files: 'public/assets/coffee/*.coffee'
+        tasks: ['dist-js']
+      less:
+        files: 'public/assets/less/*.less'
+        tasks: ['dist-css']
+
+  require('load-grunt-tasks')(grunt, { scope: 'devDependencies' })
+  require('time-grunt')(grunt)
+
+  # Docs task
+  grunt.registerTask('dist-js', ['coffee:dist', 'uglify:dist'])
+  grunt.registerTask('dist-css', ['less:dist', 'csscomb:dist', 'cssmin:dist'])
+  grunt.registerTask('dist', ['clean', 'dist-js', 'dist-css'])
+
+  # Default task
+  grunt.registerTask('default', ['dist'])


Update the coffescript compiler for the project to join all files before compilation. Compile to the same dist/rivets.js target.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,8 +11,19 @@

     coffee:
       all:
+        options:
+          join: true
         files:
-          'dist/rivets.js': 'src/rivets.coffee'
+          'dist/rivets.js': [
+            'src/rivets.coffee'
+            'src/util.coffee'
+            'src/view.coffee'
+            'src/bindings.coffee'
+            'src/parsers.coffee'
+            'src/binders.coffee'
+            'src/adapters.coffee'
+            'src/export.coffee'
+          ]

     concat:
       all:


use grunt

use grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,23 @@
+module.exports = (grunt) ->
+
+	grunt.initConfig
+
+		pkg: grunt.file.readJSON 'package.json'
+
+		requirejs:
+			js:
+				options:
+					findNestedDependencies: true
+					baseUrl: 'js'
+					wrap: true
+					preserveLicenseComments: false
+					optimize: 'none'
+					mainConfigFile: 'js/main.js'
+					include: ['js/main.js']
+					out: 'resume.js'
+					onBuildWrite: (name, path, contents) ->
+						(require 'amdclean').clean contents
+
+	grunt.loadNpmTasks 'grunt-contrib-requirejs'
+	grunt.registerTask 'build', ['requirejs:js']
+	grunt.registerTask 'default', ['build']


Create Gruntfile.coffee

Create Gruntfile.coffee

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,15 @@
+'use strict'
+module.exports = (grunt) ->
+
+  grunt.initConfig
+    coffee:
+      src:
+        expand: yes
+        cwd: 'src'
+        src: ['**/*.coffee']
+        dest: 'src'
+        ext: '.js'
+
+  grunt.loadNpmTasks 'grunt-contrib-coffee'
+
+  grunt.registerTask 'default', ['coffee']


Added support for "im"

Added support for "im"

--- src/scripts/working-on.coffee
+++ src/scripts/working-on.coffee
@@ -29,7 +29,7 @@
     msg.send messageText

-  robot.respond /(i\'m|i am) working on (.*)/i, (msg) ->
+  robot.respond /(i\'m|i am|im) working on (.*)/i, (msg) ->
     name = msg.message.user.name
     user = robot.brain.userForName name


nope, no domain info to keep there

nope, no domain info to keep there


converted PortfolioHierarchyAppTest to spec

error: pathspec 'iterationplanningboard/portfoliohierarchy/PortfolioHierarchyAppSpec.coffee' did not match any file(s) known to git

--- iterationplanningboard/portfoliohierarchy/PortfolioHierarchyAppSpec.coffee
+++ iterationplanningboard/portfoliohierarchy/PortfolioHierarchyAppSpec.coffee
@@ -0,0 +1,119 @@
+Ext = window.Ext4 || window.Ext
+
+describe 'Rally.apps.portfoliohierarchy.PortfolioHierarchyApp', ->
+
+  helpers
+
+    _createApp: (settings) ->
+      globalContext = Rally.environment.getContext()
+
+      context = Ext.create 'Rally.app.Context',
+        initialValues:
+          project: globalContext.getProject()
+          workspace: globalContext.getWorkspace()
+          user: globalContext.getUser()
+          subscription: globalContext.getSubscription()
+
+      options =
+        context: context
+        renderTo: 'testDiv'
+
+      if settings
+        options.settings = settings
+
+      @app = Ext.create 'Rally.apps.portfoliohierarchy.PortfolioHierarchyApp', options
+
+      @waitForComponentReady(@app)
+
+  beforeEach ->
+    @userStoryModel = Rally.mock.data.ModelFactory.getUserStoryModel()
+
+    @ajax.whenQuerying('typedefinition').respondWith [
+      Rally.mock.data.types.PortfolioItemStrategy.getModelDefinition()
+    ]
+
+  afterEach ->
+    @app?.destroy()
+
+
+  it 'test draws portfolio tree', ->
+    interceptor = @ajax.whenQuerying('PortfolioItem/Strategy').respondWith [
+      {FormattedID: 'S1', ObjectID: '1', Name: 'Strategy 1'}
+    ]
+
+    @_createApp().then =>
+
+      sinon.assert.calledOnce(interceptor)
+      expect(@app.query('rallyportfolioitemtreeitem').length).toBe 1
+
+
+  it 'test filter info displayed', ->
+    @ajax.whenQuerying('PortfolioItem/Strategy').respondWith()
+
+    @_createApp().then =>
+
+      expect(@app.getEl().down('.filterInfo')).toBeInstanceOf Ext.Element
+
+
+
+  it 'test project setting label is shown if following a specific project scope', ->
+    @ajax.whenQuerying('PortfolioItem/Strategy').respondWith()
+
+    @_createApp(
+      project: '/project/431439'
+    ).then =>
+
+      @app.down('rallyfilterinfo').tooltip.show()
+
+      tooltipContent = Ext.get(Ext.query('.filterInfoTooltip')[0])
+
+      expect(tooltipContent.dom.innerHTML).toContain 'Project'
+      expect(tooltipContent.dom.innerHTML).toContain 'Project 1'
+
+      @app.down('rallyfilterinfo').tooltip.destroy()
+
+
+  it 'test project setting label shows "Following Global Project Setting" if following global project scope', ->
+    @ajax.whenQuerying('project').respondWith [{
+      Name: 'test Project'
+      _ref: '/project/2'
+    }]
+
+    @_createApp().then =>
+
+      @app.down('rallyfilterinfo').tooltip.show()
+
+      tooltipContent = Ext.get(Ext.query('.filterInfoTooltip')[0])
+
+      expect(tooltipContent.dom.innerHTML).toContain 'Following Global Project Setting'
+
+      @app.down('rallyfilterinfo').tooltip.destroy()
+
+
+  it 'test help component is shown', ->
+    @ajax.whenQuerying('PortfolioItem/Strategy').respondWith()
+
+    @_createApp().then =>
+
+      expect(@app.down('#header').getEl().down('.rally-help-icon').dom.innerHTML).toContain 'Help & Training'
+
+
+
+  it 'test empty query string does not create a filter', ->
+    @_createApp(
+      query: ''
+    ).then =>
+
+      tree = @app.down('rallytree')
+
+      expect(tree.topLevelStoreConfig.filters.length).toBe 0
+
+  it 'test non-empty query string creates a filter', ->
+    @_createApp(
+      query: '(Name = "blah")'
+    ).then =>
+
+      tree = @app.down('rallytree')
+
+      expect(tree.topLevelStoreConfig.filters.length).toBe 1
+      expect(tree.topLevelStoreConfig.filters[0].toString()).toEqual '(Name = "blah")'


everyone needs a little LMFAO in their lives

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

--- src/scripts/sigh.coffee
+++ src/scripts/sigh.coffee
@@ -0,0 +1,12 @@
+# http://xkcd.com/1009/
+
+module.exports = (robot) ->
+  sigh_counter = 0
+
+  robot.hear /(^|\W)[s]+[i]+[g]+[h]+(\z|\W|$)/i, (msg) ->
+    if sigh_counter == 3
+      sigh_counter = 0
+      msg.send "I work out"
+    else
+      sigh_counter += 1
+      msg.send "Girl look at that body"


Add grunt info.

Add grunt info.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,114 @@
+'use strict'
+
+module.exports = (grunt) ->
+  # project configuration
+  grunt.initConfig
+    # load package information
+    pkg: grunt.file.readJSON 'package.json'
+
+    meta:
+      banner: "/* ===========================================================\n" +
+        "# <%= pkg.name %> - v<%= pkg.version %>\n" +
+        "# ==============================================================\n" +
+        "# Copyright (c) 2014 <%= pkg.author.name %>\n" +
+        "# Licensed under the MIT license.\n" +
+        "*/\n"
+
+    coffeelint:
+      options: grunt.file.readJSON('node_modules/sphere-coffeelint/coffeelint.json')
+      default: ["Gruntfile.coffee", "src/**/*.coffee"]
+
+    clean:
+      default: "lib"
+      test: "test"
+
+    coffee:
+      options:
+        bare: true
+      default:
+        files: grunt.file.expandMapping(['**/*.coffee'], 'lib/',
+          flatten: false
+          cwd: 'src/coffee'
+          ext: '.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )
+      test:
+        files: grunt.file.expandMapping(['**/*.spec.coffee'], 'test/',
+          flatten: false
+          cwd: 'src/spec'
+          ext: '.spec.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )
+      testhelper:
+        files: grunt.file.expandMapping(['**/testhelpers.coffee'], 'test/',
+          flatten: false
+          cwd: 'src/spec'
+          ext: '.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )
+
+    concat:
+      options:
+        banner: "<%= meta.banner %>"
+      default:
+        expand: true
+        flatten: true
+        cwd: "lib"
+        src: ["*.js"]
+        dest: "lib"
+        ext: ".js"
+
+    # watching for changes
+    watch:
+      default:
+        files: ["src/coffee/*.coffee"]
+        tasks: ["build"]
+      test:
+        files: ["src/**/*.coffee"]
+        tasks: ["test"]
+
+    shell:
+      options:
+        stdout: true
+        stderr: true
+        failOnError: true
+      jasmine:
+        command: "jasmine-node --verbose --captureExceptions test"
+      coverage:
+        command: "istanbul cover jasmine-node --captureExceptions test && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
+      publish:
+        command: 'npm publish'
+
+    bump:
+      options:
+        files: ['package.json']
+        updateConfigs: ['pkg']
+        commit: true
+        commitMessage: 'Bump version to %VERSION%'
+        commitFiles: ['-a']
+        createTag: true
+        tagName: 'v%VERSION%'
+        tagMessage: 'Version %VERSION%'
+        push: true
+        pushTo: 'origin'
+        gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d'
+
+  # load plugins that provide the tasks defined in the config
+  grunt.loadNpmTasks "grunt-coffeelint"
+  grunt.loadNpmTasks "grunt-contrib-clean"
+  grunt.loadNpmTasks "grunt-contrib-coffee"
+  grunt.loadNpmTasks "grunt-contrib-concat"
+  grunt.loadNpmTasks "grunt-contrib-watch"
+  grunt.loadNpmTasks "grunt-shell"
+  grunt.loadNpmTasks 'grunt-bump'
+
+  # register tasks
+  grunt.registerTask "build", ["clean", "coffeelint", "coffee", "concat"]
+  grunt.registerTask "coverage", ["build", "shell:coverage"]
+  grunt.registerTask "test", ["build", "shell:jasmine"]
+  grunt.registerTask 'release', 'Release a new version and push it', (target) ->
+    target = 'patch' unless target
+    grunt.task.run "bump-only:#{target}", 'test', 'bump-commit'


Add Grunt configuration for building/watching package

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,25 @@
+module.exports = (grunt) ->
+
+  # Grunt configuration
+  grunt.initConfig
+    coffee:
+      source:
+        expand: true
+        cwd: './'
+        src: ['index.coffee', 'test/test.coffee']
+        dest: './'
+        ext: '.js'
+    watch:
+      backend:
+        files: ['index.coffee', 'test/test.coffee']
+        tasks: ['coffee']
+
+  # Build project
+  grunt.registerTask 'build', [
+    'coffee'
+  ]
+
+  # Load grunt modules
+  grunt.loadNpmTasks('grunt-contrib-coffee')
+  grunt.loadNpmTasks('grunt-contrib-watch')
+


Removed mux.js version lock now that issue has been fixed.

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/'


overwrite for fixtures store query

overwrite for fixtures store query

[#95231438]

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 Bookmarklet that works on Github only

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

--- app/assets/javascripts/bookmarklet.js.coffee
+++ app/assets/javascripts/bookmarklet.js.coffee
@@ -0,0 +1,18 @@
+if document.URL.indexOf("https://github.com/") == 0
+  open("http://knight.dev/repos/" + document.URL.split("/").slice(3).join("/"))
+else
+  alert("Please use this bookmarklet on a URL like https://github.com/rails/rails")
+
+
+###
+
+javascript:(function() {
+
+  if (document.URL.indexOf("https://github.com/") === 0) {
+    open("http://knight.dev/repos/" + document.URL.split("/").slice(3).join("/"));
+  } else {
+    alert("Please use this bookmarklet on a URL like https://github.com/rails/rails");
+  }
+
+}).call(this);
+###


Remove unused use cases for the RubricService.getAssignment

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/UM-USElab/gradecraft-development.git/'


Writer create restored

Writer create restored

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-gooddata-writer-add.git/'


Client: Activity: Updated PublicChatPane to pass onScrollThresholdReached and thread props for infinite scroll event handling

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/'


finder settings button is moved to another branch


fix spelling mistake

fix spelling mistake

--- client/landing/site.landing/coffee/features/AppView.coffee
+++ client/landing/site.landing/coffee/features/AppView.coffee
@@ -109,7 +109,7 @@

     'IDE'                  :
       'Workspaces'         :
-        text               : "Use workspaces to organize your project. Workpsaces keep everything neat and tidy."
+        text               : "Use workspaces to organize your project. Workspaces keep everything neat and tidy."
         iconClass          : "ide-workspaces"
       'Previews'           :
         text               : "Easily preview your work using the built-in browser. No more tab switching."


Put my name on the emails.

Put my name on the emails.

--- server/spendflow.coffee
+++ server/spendflow.coffee
@@ -1 +1,3 @@
 @spendflowAutomaticNotes = "Managed automatically"
+
+Accounts.emailTemplates.from = "Kevin at Spendflow <kevin@spendflow.co>"


Changing resolutions no longer triggers ads.


period as optional parameter for dates-picker


added cluster capability

added cluster capability

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

--- cluster.coffee
+++ cluster.coffee
@@ -0,0 +1,68 @@
+cluster = require 'cluster'
+
+if not cluster.isMaster then return require './app'
+
+util = require 'util'
+express = require 'express'
+gitpull = require 'git-pull'
+gitsha = require 'gitsha'
+require 'colors'
+pkg = require __dirname + '/package.json'
+util.log "#{pkg.name} (#{pkg.version}) - #{pkg.description}".green.bold
+
+numCPUs = require('os').cpus().length
+forkCount = 0
+forkTokenCount = numCPUs
+setInterval (-> forkTokenCount++ unless forkTokenCount >= numCPUs * 2), 1200000
+
+cluster.fork() for i in [0...numCPUs]
+
+cluster.on 'fork', -> forkCount++
+
+cluster.on 'exit', (worker, code, signal) ->
+	forkCount--
+	if worker.suicide
+		util.log "Worker killed: pid: #{worker.process.pid}, code #{code}, signal #{signal}"
+	else
+		util.log "Worker died: pid: #{worker.process.pid}, code #{code}, signal #{signal}".red
+		if forkTokenCount > 0
+			forkTokenCount--
+			util.log "Forking again. Tokens left: #{forkTokenCount}".yellow
+			setTimeout (-> cluster.fork()), 1000
+		else
+			util.log "Too many crashes. Giving up.".red
+			util.log "Forks left: #{forkCount}".yellow
+			if not forkCount then process.exit(2)
+
+controller = express.createServer()
+
+controller.post '/update', (req, res) ->
+	gitsha '.', (error, output) ->
+		if error then return util.error output
+		initChecksum = output
+		util.log "gitpull'ing...".cyan
+		util.log "initial checksum: #{output}"
+		gitpull '.', (error, output) ->
+			if error then return util.error output
+			util.log "gitpull success"
+			gitsha '.', (error, output) ->
+				if error then return util.error output
+				util.log "final checksum: #{output}"
+				if output is initChecksum
+					return util.log "No updates found!".red
+				util.log "Update found, restarting workers!".green
+				worker.disconnect() for id, worker of cluster.workers
+				cluster.fork() for i in [0...numCPUs]
+	res.send 'roger'
+
+controller.get '*', (req, res) ->
+	res.send '404 Not Found', 404
+
+controller.post '*', (req, res) ->
+	res.send '404 Not Found', 404
+
+controller.listen process.env.CONTROLLER_PORT or 0, ->
+	addr = controller.address().address
+	port = controller.address().port
+	util.log "[#{process.pid}] Controller: http://#{addr}:#{port}/"
+


Added filtration by users to project [#84276770]

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/'


BaseView#getTemplateData(): also return collection.meta and collection.params

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/SNASW/rendr.git/'


added edit view

added edit view

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

--- app/assets/javascripts/ember/views/projects/edit.js.coffee
+++ app/assets/javascripts/ember/views/projects/edit.js.coffee
@@ -0,0 +1,26 @@
+DashIt.EditProjectView = Ember.View.extend
+  tagName: "form"
+  templateName: "ember/templates/projects/edit"
+  init: ->
+    editableProject = DashIt.Project.create(@get("parentView").get("project"))
+    @set "project", editableProject
+    @_super()
+
+  didInsertElement: ->
+    @_super()
+    @$("input:first").focus()
+
+  cancelForm: ->
+    @get("parentView").hideEdit()
+
+  submit: (event) ->
+    self = this
+    project = @get("project")
+    event.preventDefault()
+    project.saveResource().fail((e) ->
+      DashIt.displayError e
+    ).done ->
+      parentView = self.get("parentView")
+      parentView.get("project").duplicatePropertiesprojectt
+      parentView.hideEdit()
+


Remove comment

Remove comment

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/Hiroto-K/HkApps.git/'


paymentcontroller  : show dock after payment complete


Updated grades/submit_buttons.coffee directive to not disable the Submit Grade buttons if the user returns to add rubric criteria levels


console.js.blendedin

console.js.blendedin

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

--- console.coffee
+++ console.coffee
@@ -0,0 +1,1054 @@
+((window) ->
+  baseURL = undefined
+  ccCache = undefined
+  ccPosition = undefined
+  exec = undefined
+  form = undefined
+  output = undefined
+  cursor = undefined
+  injected = undefined
+  sandboxframe = undefined
+  sandbox = undefined
+  fakeConsole = undefined
+  history = undefined
+  liveHistory = undefined
+  pos = undefined
+  libraries = undefined
+  body = undefined
+  logAfter = undefined
+  historySupported = undefined
+  sse = undefined
+  lastCmd = undefined
+  remoteId = undefined
+  codeCompleteTimer = undefined
+  keypressTimer = undefined
+  commands = undefined
+  fakeInput = undefined
+  iOSMobile = undefined
+  enableCC = undefined
+  modifierkeys = undefined
+  fakeInputFocused = undefined
+  dblTapTimer = undefined
+  taps = undefined
+  e = undefined
+  sortci = (a, b) ->
+    (if a.toLowerCase()
+    do([e] = [ ])=>
+      json = undefined
+      i = undefined
+      vi = undefined
+      type = undefined
+      parts = undefined
+      names = undefined
+      circular = undefined
+      json = ""
+      i = undefined
+      vi = undefined
+      type = ""
+      parts = []
+      names = []
+      circular = false
+      visited = visited or []
+      try
+        type = ({}).toString.call(o)
+      catch e # only happens when typeof is protected (...randomly)
+        type = "[object Object]"
+
+      # check for circular references
+      vi = 0
+      while vi < visited.length
+        if o is visited[vi]
+          circular = true
+          break
+        vi++
+      if circular
+        json = "[circular]"
+      else if type is "[object String]"
+        json = "\"" + o.replace(/\"/g, "\\\"") + "\""
+      else if type is "[object Array]"
+        visited.push o
+        json = "["
+        i = 0
+        while i < o.length
+          parts.push stringify(o[i], simple, visited)
+          i++
+        json += parts.join(", ") + "]"
+        json
+      else if type is "[object Object]"
+        visited.push o
+        json = "{"
+        for i of o
+          names.push i
+        names.sort sortci
+        i = 0
+        while i < names.length
+          parts.push stringify(names[i], `undefined`, visited) + ": " + stringify(o[names[i]], simple, visited)
+          i++
+        json += parts.join(", ") + "}"
+      else if type is "[object Number]"
+        json = o + ""
+      else if type is "[object Boolean]"
+        json = (if o then "true" else "false")
+      else if type is "[object Function]"
+        json = o.toString()
+      else if o is null
+        json = "null"
+      else if o is `undefined`
+        json = "undefined"
+      else unless simple?
+        visited.push o
+        json = type + "{\n"
+        for i of o
+          names.push i
+        names.sort sortci
+        i = 0
+        while i < names.length
+          try
+            parts.push names[i] + ": " + stringify(o[names[i]], true, visited) # safety from max stack
+          catch e
+            if e.name is "NS_ERROR_NOT_IMPLEMENTED" then
+          i++
+
+        # do nothing - not sure it's useful to show this error when the variable is protected
+        # parts.push(names[i] + ': NS_ERROR_NOT_IMPLEMENTED');
+        json += parts.join(",\n") + "\n}"
+      else
+        try
+          json = o + "" # should look like an object
+        catch e
+      json
+  cleanse = (s) ->
+    (s or "").replace /[<&]/g, (m) ->
+      {
+        "&": "&"
+        "<": "<"
+      }[m]
+
+  run = (cmd) ->
+    rawoutput = undefined
+    className = undefined
+    internalCmd = undefined
+    rawoutput = null
+    className = "response"
+    internalCmd = internalCommand(cmd)
+    if internalCmd
+      [
+        "info"
+        internalCmd
+      ]
+    else if remoteId isnt null
+
+      # send the remote event
+      xhr = new XMLHttpRequest()
+      params = "data=" + encodeURIComponent(cmd)
+      xhr.open "POST", "/remote/" + remoteId + "/run", true
+      xhr.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
+      xhr.send params
+      setCursorTo ""
+      [
+        "info"
+        "sent remote command"
+      ]
+    else
+      try
+        if "CoffeeScript" of window
+          cmd = window.CoffeeScript.compile(cmd,
+            bare: true
+          )
+        else if "CoffeeScript" of sandboxframe.contentWindow
+
+          # Maybe this should be removed or made optional
+          cmd = sandboxframe.contentWindow.CoffeeScript.compile(cmd,
+            bare: true
+          )
+        rawoutput = sandboxframe.contentWindow.eval(cmd)
+      catch e
+        rawoutput = e.message
+        className = "error"
+      [
+        className
+        cleanse(stringify(rawoutput))
+      ]
+  post = (cmd, blind, response) -> # passed in when echoing from remote console
+    do([e] = [ ])=>
+      el = undefined
+      li = undefined
+      span = undefined
+      parent = undefined
+      cmd = trim(cmd)
+      if blind is `undefined`
+        history.push cmd
+        setHistory history
+        window.history.pushState cmd, cmd, "?" + encodeURIComponent(cmd)  if historySupported
+      echo cmd  if not remoteId or response
+
+      # order so it appears at the top
+      el = document.createElement("div")
+      li = document.createElement("li")
+      span = document.createElement("span")
+      parent = output.parentNode
+      response = response or run(cmd)
+      if response isnt `undefined`
+        el.className = "response"
+        span.innerHTML = response[1]
+        prettyPrint [span]  unless response[0] is "info"
+        el.appendChild span
+        li.className = response[0]
+        li.innerHTML = ""
+        li.appendChild el
+        appendLog li
+        output.parentNode.scrollTop = 0
+        unless body.className
+          exec.value = ""
+          if enableCC
+            try
+              document.getElementsByTagName("a")[0].focus()
+              cursor.focus()
+              document.execCommand "selectAll", false, null
+              document.execCommand "delete", false, null
+            catch e
+      pos = history.length
+      return
+  log = (msg, className) ->
+    li = undefined
+    div = undefined
+    li = document.createElement("li")
+    div = document.createElement("div")
+    div.innerHTML = msg
+    prettyPrint [div]
+    li.className = className or "log"
+    li.innerHTML = ""
+    li.appendChild div
+    appendLog li
+    return
+  echo = (cmd) ->
+    li = undefined
+    lis = undefined
+    len = undefined
+    i = undefined
+    li = document.createElement("li")
+    li.className = "echo"
+    li.innerHTML = "
" + cleanse(cmd) + "
"
+    logAfter = null
+    if output.querySelector
+      logAfter = output.querySelector("li.echo") or null
+    else
+      lis = document.getElementsByTagName("li")
+      len = lis.length
+      i = undefined
+      i = 0
+      while i < len
+        if lis[i].className.indexOf("echo") isnt -1
+          logAfter = lis[i]
+          break
+        i++
+
+    # logAfter = output.querySelector('li.echo') || null;
+    appendLog li, true
+    return
+  info = (cmd) ->
+    li = undefined
+    li = document.createElement("li")
+    li.className = "info"
+    li.innerHTML = "
" + cleanse(cmd) + "
"
+
+    # logAfter = output.querySelector('li.echo') || null;
+    # appendLog(li, true);
+    appendLog li
+    return
+  appendLog = (el, echo) ->
+    if echo
+      unless output.firstChild
+        output.appendChild el
+      else
+        output.insertBefore el, output.firstChild
+    else
+
+      # if (!output.lastChild) {
+      #   output.appendChild(el);
+      #   // console.log('ok');
+      # } else {
+      # console.log(output.lastChild.nextSibling);
+      output.insertBefore el, (if logAfter then logAfter else output.lastChild.nextSibling) #  ? output.lastChild.nextSibling : output.firstChild
+    return
+  # }
+  changeView = (event) ->
+    do([e] = [ ])=>
+      which = undefined
+      return  if false and enableCC
+      which = event.which or event.keyCode
+      if which is 38 and event.shiftKey is true
+        body.className = ""
+        cursor.focus()
+        try
+          localStorage.large = 0
+        catch e
+
+        false
+      else if which is 40 and event.shiftKey is true
+        body.className = "large"
+        try
+          localStorage.large = 1
+        catch e
+
+        cursor.focus()
+        false
+  internalCommand = (cmd) ->
+    parts = undefined
+    c = undefined
+    parts = []
+    c = undefined
+    if cmd.substr(0, 1) is ":"
+      parts = cmd.substr(1).split(" ")
+      c = parts.shift()
+      (commands[c] or noop).apply this, parts
+  noop = ->
+  showhelp = ->
+    do([commands] = [ ])=>
+      commands = [
+        ":load <url> - to inject new DOM"
+        ":load <script_url> - to inject external library"
+        "      load also supports following shortcuts:
      jquery, underscore, prototype, mootools, dojo, rightjs, coffeescript, yui.
      eg. :load jquery"
+        ":listen [id] - to start remote debugging session"
+        ":clear - to clear the history (accessed using cursor keys)"
+        ":history - list current session history"
+        ":about"
+        ""
+        "Directions to inject CoffeeConsole in to any page (useful for mobile debugging)"
+      ]
+      commands.push ":close - to hide the console"  if injected
+
+      # commands = commands.concat([
+      #   'up/down - cycle history',
+      #   'shift+up - single line command',
+      #   'shift+down - multiline command',
+      #   'shift+enter - to run command in multiline mode'
+      # ]);
+      commands.join "\n"
+  load = (url) ->
+    if navigator.onLine
+      if arguments.length > 1 or libraries[url] or url.indexOf(".js") isnt -1
+        loadScript.apply this, arguments
+      else
+        loadDOM url
+    else
+      "You need to be online to use :load"
+  loadScript = ->
+    doc = undefined
+    doc = sandboxframe.contentDocument or sandboxframe.contentWindow.document
+    i = 0
+
+    while i < arguments.length
+      ((url) ->
+        script = document.createElement("script")
+        script.src = url
+        script.onload = ->
+          info "Loaded " + url, "http://" + window.location.hostname
+          info "Now you can type CoffeeScript instead of plain old JS!"  if url is libraries.coffeescript
+          return
+
+        script.onerror = ->
+          log "Failed to load " + url, "error"
+          return
+
+        doc.body.appendChild script
+        return
+      ) libraries[arguments[i]] or arguments[i]
+      i++
+    "Loading script..."
+  loadDOM = (url) ->
+    doc = undefined
+    script = undefined
+    cb = undefined
+    doc = sandboxframe.contentWindow.document
+    script = document.createElement("script")
+    cb = "loadDOM" + +new Date
+    script.src = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + encodeURIComponent(url) + "%22&format=xml&callback=" + cb
+    window[cb] = (yql) ->
+      do([e] = [ ])=>
+        html = undefined
+        if yql.results.length
+          html = yql.results[0].replace(/type=\"text\/javascript\"/gi, "type=\"x\"").replace(//, "").replace(/<\/body>/, "")
+          doc.body.innerHTML = html
+          info "DOM load complete"
+        else
+          log "Failed to load DOM", "error"
+        try
+          window[cb] = null
+          delete window[cb]
+        catch e
+
+        return
+
+    document.body.appendChild script
+    "Loading url into DOM..."
+  checkTab = (evt) ->
+    t = undefined
+    ss = undefined
+    se = undefined
+    tab = undefined
+    pre = undefined
+    sel = undefined
+    post = undefined
+    t = evt.target
+    ss = t.selectionStart
+    se = t.selectionEnd
+    tab = "  "
+
+    # Tab key - insert tab expansion
+    if evt.keyCode is 9
+      evt.preventDefault()
+
+      # Special case of multi line selection
+      if ss isnt se and t.value.slice(ss, se).indexOf("\n") isnt -1
+
+        # In case selection was not of entire lines (e.g. selection begins in the middle of a line)
+        # we ought to tab at the beginning as well as at the start of every following line.
+        pre = t.value.slice(0, ss)
+        sel = t.value.slice(ss, se).replace(/\n/g, "\n" + tab)
+        post = t.value.slice(se, t.value.length)
+        t.value = pre.concat(tab).concat(sel).concat(post)
+        t.selectionStart = ss + tab.length
+        t.selectionEnd = se + tab.length
+
+      # "Normal" case (no selection or selection on one line only)
+      else
+        t.value = t.value.slice(0, ss).concat(tab).concat(t.value.slice(ss, t.value.length))
+        if ss is se
+          t.selectionStart = t.selectionEnd = ss + tab.length
+        else
+          t.selectionStart = ss + tab.length
+          t.selectionEnd = se + tab.length
+
+    # Backspace key - delete preceding tab expansion, if exists
+    else if evt.keyCode is 8 and t.value.slice(ss - 4, ss) is tab
+      evt.preventDefault()
+      t.value = t.value.slice(0, ss - 4).concat(t.value.slice(ss, t.value.length))
+      t.selectionStart = t.selectionEnd = ss - tab.length
+
+    # Delete key - delete following tab expansion, if exists
+    else if evt.keyCode is 46 and t.value.slice(se, se + 4) is tab
+      evt.preventDefault()
+      t.value = t.value.slice(0, ss).concat(t.value.slice(ss + 4, t.value.length))
+      t.selectionStart = t.selectionEnd = ss
+
+    # Left/right arrow keys - move across the tab in one go
+    else if evt.keyCode is 37 and t.value.slice(ss - 4, ss) is tab
+      evt.preventDefault()
+      t.selectionStart = t.selectionEnd = ss - 4
+    else if evt.keyCode is 39 and t.value.slice(ss, ss + 4) is tab
+      evt.preventDefault()
+      t.selectionStart = t.selectionEnd = ss + 4
+    return
+  trim = (s) ->
+    (s or "").replace /^\s+|\s+$/g, ""
+  getProps = (cmd, filter) ->
+    do([e] = [ ])=>
+      surpress = undefined
+      props = undefined
+      surpress = {}
+      props = []
+      unless ccCache[cmd]
+        try
+
+          # surpress alert boxes because they'll actually do something when we're looking
+          # up properties inside of the command we're running
+          surpress.alert = sandboxframe.contentWindow.alert
+          sandboxframe.contentWindow.alert = ->
+
+
+          # loop through all of the properties available on the command (that's evaled)
+          ccCache[cmd] = sandboxframe.contentWindow.eval("console.props(" + cmd + ")").sort()
+
+          # return alert back to it's former self
+          delete sandboxframe.contentWindow.alert
+        catch e
+          ccCache[cmd] = []
+
+        # if the return value is undefined, then it means there's no props, so we'll
+        # empty the code completion
+        ccOptions[cmd] = []  if ccCache[cmd][0] is "undefined"
+        ccPosition = 0
+        props = ccCache[cmd]
+      else if filter
+
+        # console.log('>>' + filter, cmd);
+        i = 0
+        p = undefined
+
+        while (i < ccCache[cmd].length; p = ccCache[cmd][i])
+          props.push p.substr(filter.length, p.length)  unless p is filter  if p.indexOf(filter) is 0
+          i++
+      else
+        props = ccCache[cmd]
+      props
+  codeComplete = (event) ->
+    cmd = undefined
+    parts = undefined
+    which = undefined
+    cc = undefined
+    props = undefined
+    cmd = cursor.textContent.split(/[;\s]+/g).pop()
+    parts = cmd.split(".")
+    which = whichKey(event)
+    cc = undefined
+    props = []
+    if cmd
+
+      # get the command without the dot to allow us to introspect
+      if cmd.substr(-1) is "."
+
+        # get the command without the '.' so we can eval it and lookup the properties
+        cmd = cmd.substr(0, cmd.length - 1)
+
+        # returns an array of all the properties from the command
+        props = getProps(cmd)
+      else
+        props = getProps(parts.slice(0, parts.length - 1).join(".") or "window", parts[parts.length - 1])
+      if props.length
+        if which is 9 # tabbing cycles through the code completion
+          # however if there's only one selection, it'll auto complete
+          if props.length is 1
+            ccPosition = false
+          else
+            if event.shiftKey
+
+              # backwards
+              ccPosition = (if ccPosition is 0 then props.length - 1 else ccPosition - 1)
+            else
+              ccPosition = (if ccPosition is props.length - 1 then 0 else ccPosition + 1)
+        else
+          ccPosition = 0
+        if ccPosition is false
+          completeCode()
+        else
+
+          # position the code completion next to the cursor
+          unless cursor.nextSibling
+            cc = document.createElement("span")
+            cc.className = "suggest"
+            exec.appendChild cc
+          cursor.nextSibling.innerHTML = props[ccPosition]
+          exec.value = exec.textContent
+        return false  if which is 9
+      else
+        ccPosition = false
+    else
+      ccPosition = false
+    removeSuggestion()  if ccPosition is false and cursor.nextSibling
+    exec.value = exec.textContent
+    return
+  removeSuggestion = ->
+    exec.setAttribute "rows", 1  unless enableCC
+    cursor.parentNode.removeChild cursor.nextSibling  if enableCC and cursor.nextSibling
+    return
+  showHistory = ->
+    h = undefined
+    h = getHistory()
+    h.shift()
+    h.join "\n"
+  getHistory = ->
+    do([history,e] = [ ])=>
+      history = [""]
+      return history  if typeof JSON is "undefined"
+      try
+
+        # because FF with cookies disabled goes nuts, and because sometimes WebKit goes nuts too...
+        history = JSON.parse(sessionStorage.getItem("history") or "[\"\"]")
+      catch e
+
+      history
+
+  # I should do this onunload...but I'm being lazy and hacky right now
+  setHistory = (history) ->
+    do([e] = [ ])=>
+      return  if typeof JSON is "undefined"
+      try
+
+        # because FF with cookies disabled goes nuts, and because sometimes WebKit goes nuts too...
+        sessionStorage.setItem "history", JSON.stringify(history)
+      catch e
+
+      return
+  about = ->
+    "Built by @rem"
+
+  # loadjs: loadScript,
+
+  # place script request for new listen ID and start SSE
+  # fiddle to remove the [] around the repsonse
+
+  # I hate that I'm browser sniffing, but there's issues with Firefox and execCommand so code completion won't work
+
+  # FIXME Remy, seriously, don't sniff the agent like this, it'll bite you in the arse.
+
+  # stupid jumping through hoops if Firebug is open, since overwriting console throws error
+
+  # tweaks to interface to allow focus
+  # if (!('autofocus' in document.createElement('input'))) exec.focus();
+  whichKey = (event) ->
+    keys = undefined
+    keys =
+      38: 1
+      40: 1
+      Up: 38
+      Down: 40
+      Enter: 10
+      "U+0009": 9
+      "U+0008": 8
+      "U+0190": 190
+      Right: 39
+
+      # these two are ignored
+      "U+0028": 57
+      "U+0026": 55
+
+    keys[event.keyIdentifier] or event.which or event.keyCode
+  setCursorTo = (str) ->
+    rows = undefined
+    str = (if enableCC then cleanse(str) else str)
+    exec.value = str
+    if enableCC
+      document.execCommand "selectAll", false, null
+      document.execCommand "delete", false, null
+      document.execCommand "insertHTML", false, str
+    else
+      rows = str.match(/\n/g)
+      exec.setAttribute "rows", (if rows isnt null then rows.length + 1 else 1)
+    cursor.focus()
+    window.scrollTo 0, 0
+    return
+
+  # disabled for now
+
+  # this causes the field to lose focus - I'll leave it here for a while, see how we get on.
+  # what I need to do is rip out the contenteditable and replace it with something entirely different
+
+  # setCursorTo(cursor.innerText);
+  findNode = (list, node) ->
+    do([pos] = [ ])=>
+      pos = 0
+      i = 0
+
+      while i < list.length
+        return pos  if list[i] is node
+        pos += list[i].nodeValue.length
+        i++
+      -1
+  # history cycle
+  # cycle up
+  #history.length - 1;
+  # down
+  #0;
+  # enter (what about the other one)
+
+  # manually expand the textarea when we don't have code completion turned on
+  # complete code
+  # try code completion
+  # cycles available completions
+
+  # window.scrollTo(0,0);
+  completeCode = (focus) ->
+    tmp = undefined
+    l = undefined
+    range = undefined
+    selection = undefined
+    tmp = exec.textContent
+    l = tmp.length
+    removeSuggestion()
+    cursor.innerHTML = tmp
+    ccPosition = false
+
+    # daft hack to move the focus elsewhere, then back on to the cursor to
+    # move the cursor to the end of the text.
+    document.getElementsByTagName("a")[0].focus()
+    cursor.focus()
+    range = undefined
+    selection = undefined
+    if document.createRange #Firefox, Chrome, Opera, Safari, IE 9+
+      range = document.createRange() #Create a range (a range is a like the selection but invisible)
+      range.selectNodeContents cursor #Select the entire contents of the element with the range
+      range.collapse false #collapse the range to the end point. false means collapse to end rather than the start
+      selection = window.getSelection() #get the selection object (allows you to change selection)
+      selection.removeAllRanges() #remove any selections already made
+      selection.addRange range #make the range you have just created the visible selection
+    else if document.selection #IE 8 and lower
+      range = document.body.createTextRange() #Create a range (a range is a like the selection but invisible)
+      range.moveToElementText cursor #Select the entire contents of the element with the range
+      range.collapse false #collapse the range to the end point. false means collapse to end rather than the start
+      range.select() #Select the range (make it the visible selection
+    return
+  baseURL = (if window.top.COFFEECONSOLE then window.top.COFFEECONSOLE.baseURL else window.location.href.split(/[#?]/)[0].replace(/\/[^\/]*$/, ""))
+  window.info = info
+  ccCache = {}
+  ccPosition = false
+  window._console =
+    log: ->
+      l = undefined
+      i = undefined
+      l = arguments.length
+      i = 0
+      while i < l
+        log stringify(arguments[i], true)
+        i++
+      return
+
+    dir: ->
+      l = undefined
+      i = undefined
+      l = arguments.length
+      i = 0
+      while i < l
+        log stringify(arguments[i])
+        i++
+      return
+
+    props: (obj) ->
+      do([e] = [ ])=>
+        props = undefined
+        realObj = undefined
+        props = []
+        realObj = undefined
+        try
+          for p of obj
+            props.push p
+        catch e
+
+        props
+
+  (if document.addEventListener then window.addEventListener("message", (event) ->
+    post event.data
+    return
+  , false) else window.attachEvent("onmessage", ->
+    post window.event.data
+    return
+  ))
+  exec = document.getElementById("exec")
+  form = exec.form or {}
+  output = document.getElementById("output")
+  cursor = document.getElementById("exec")
+  injected = typeof window.top["COFFEECONSOLE"] isnt "undefined"
+  sandboxframe = (if injected then window.top["COFFEECONSOLE"] else document.createElement("iframe"))
+  sandbox = null
+  fakeConsole = "window.top._console"
+  history = getHistory()
+  liveHistory = (window.history.pushState isnt `undefined`)
+  pos = 0
+  libraries =
+    jquery: "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"
+    prototype: "http://ajax.googleapis.com/ajax/libs/prototype/1/prototype.js"
+    dojo: "http://ajax.googleapis.com/ajax/libs/dojo/1/dojo/dojo.xd.js"
+    mootools: "http://ajax.googleapis.com/ajax/libs/mootools/1/mootools-yui-compressed.js"
+    underscore: "http://documentcloud.github.com/underscore/underscore-min.js"
+    rightjs: "http://rightjs.org/hotlink/right.js"
+    yui: "http://yui.yahooapis.com/3.2.0/build/yui/yui-min.js"
+
+  body = document.getElementsByTagName("body")[0]
+  logAfter = null
+  historySupported = !!(window.history and window.history.pushState)
+  sse = null
+  lastCmd = null
+  remoteId = null
+  codeCompleteTimer = null
+  keypressTimer = null
+  commands =
+    help: showhelp
+    about: about
+    load: load
+    history: showHistory
+    clear: ->
+      setTimeout (->
+        output.innerHTML = ""
+        return
+      ), 10
+      "clearing..."
+
+    close: ->
+      if injected
+        sandboxframe.console.style.display = "none"
+        "hidden"
+      else
+        "noop"
+
+    listen: (id) ->
+      script = document.createElement("script")
+      callback = "_cb" + +new Date
+      script.src = "/remote/" + (id or "") + "?callback=" + callback
+      window[callback] = (id) ->
+        remoteId = id
+        sse.close()  if sse isnt null
+        sse = new EventSource("/remote/" + id + "/log")
+        sse.onopen = ->
+          remoteId = id
+          info "Connected to \"" + id + "\"\n\n"
+          return
+
+        sse.onmessage = (event) ->
+          data = JSON.parse(event.data)
+          if data.type and data.type is "error"
+            post data.cmd, true, [
+              "error"
+              data.response
+            ]
+          else if data.type and data.type is "info"
+            info data.response
+          else
+            data.response = data.response.substr(1, data.response.length - 2)  unless data.cmd is "remote console.log"
+            echo data.cmd
+            log data.response, "response"
+          return
+
+        sse.onclose = ->
+          info "Remote connection closed"
+          remoteId = null
+          return
+
+        try
+          body.removeChild script
+          delete window[callback]
+        catch e
+
+        return
+
+      body.appendChild script
+      "Creating connection..."
+
+  fakeInput = null
+  iOSMobile = navigator.userAgent.indexOf("AppleWebKit") isnt -1 and navigator.userAgent.indexOf("Mobile") isnt -1
+  enableCC = navigator.userAgent.indexOf("AppleWebKit") isnt -1 and navigator.userAgent.indexOf("Mobile") is -1 or navigator.userAgent.indexOf("OS 5_") isnt -1
+  if enableCC
+    exec.parentNode.innerHTML = "

"
+    exec = document.getElementById("exec")
+    cursor = document.getElementById("cursor")
+  if enableCC and iOSMobile
+    fakeInput = document.createElement("input")
+    fakeInput.className = "fakeInput"
+    fakeInput.setAttribute "spellcheck", "false"
+    fakeInput.setAttribute "autocorrect", "off"
+    fakeInput.setAttribute "autocapitalize", "off"
+    exec.parentNode.appendChild fakeInput
+  unless injected
+    body.appendChild sandboxframe
+    sandboxframe.setAttribute "id", "sandbox"
+  sandbox = sandboxframe.contentDocument or sandboxframe.contentWindow.document
+  unless injected
+    sandbox.open()
+    sandbox.write ""
+    sandbox.close()
+  cursor.focus()
+  output.parentNode.tabIndex = 0
+  output.ontouchstart = output.onclick = (event) ->
+    command = undefined
+    event = event or window.event
+    if event.target.nodeName is "A" and event.target.className is "permalink"
+      command = decodeURIComponent(event.target.search.substr(1))
+      setCursorTo command
+      window.history.pushState command, command, event.target.href  if liveHistory
+      false
+
+  exec.ontouchstart = ->
+    window.scrollTo 0, 0
+    return
+
+  exec.onkeyup = (event) ->
+    which = undefined
+    which = whichKey(event)
+    if enableCC and which isnt 9 and which isnt 16
+      clearTimeout codeCompleteTimer
+      codeCompleteTimer = setTimeout(->
+        codeComplete event
+        return
+      , 200)
+    return
+
+  if enableCC
+    cursor.__onpaste = (event) ->
+      setTimeout (->
+        cursor.innerHTML = cursor.innerText
+        return
+      ), 10
+      return
+  modifierkeys =
+    0: 1
+    16: 1
+
+  exec.onkeydown = (event) ->
+    keys = undefined
+    wide = undefined
+    which = undefined
+    event = event or window.event
+    keys =
+      38: 1
+      40: 1
+
+    wide = body.className is "large"
+    which = whichKey(event)
+    which = which.replace(/\/U\+/, "\\u")  if typeof which is "string"
+    if keys[which]
+      if event.shiftKey
+        changeView event
+      else unless wide
+        if enableCC and window.getSelection
+          window.selObj = window.getSelection()
+          selRange = selObj.getRangeAt(0)
+          cursorPos = findNode(selObj.anchorNode.parentNode.childNodes, selObj.anchorNode) + selObj.anchorOffset
+          value = exec.value
+          firstnl = value.indexOf("\n")
+          lastnl = value.lastIndexOf("\n")
+          if firstnl isnt -1
+            if which is 38 and cursorPos > firstnl
+              return
+            else return  if which is 40 and cursorPos < lastnl
+        if which is 38
+          pos--
+          pos = 0  if pos < 0
+        else if which is 40
+          pos++
+          pos = history.length  if pos >= history.length
+        if history[pos]? and history[pos] isnt ""
+          removeSuggestion()
+          setCursorTo history[pos]
+          false
+        else if pos is history.length
+          removeSuggestion()
+          setCursorTo ""
+          false
+    else if (which is 13 or which is 10) and event.shiftKey is false
+      removeSuggestion()
+      if event.shiftKey is true or event.metaKey or event.ctrlKey or not wide
+        command = exec.textContent or exec.value
+        post command  if command.length
+        false
+    else if (which is 13 or which is 10) and not enableCC and event.shiftKey is true
+      rows = exec.value.match(/\n/g)
+      rows = (if rows? then rows.length + 2 else 2)
+      exec.setAttribute "rows", rows
+    else if which is 9 and wide
+      checkTab event
+    else if event.shiftKey and event.metaKey and which is 8
+      output.innerHTML = ""
+    else if (which is 39 or which is 35) and ccPosition isnt false
+      completeCode()
+    else if event.ctrlKey and which is 76
+      output.innerHTML = ""
+    else if enableCC
+      if ccPosition isnt false and which is 9
+        codeComplete event
+        false
+      else removeSuggestion()  if ccPosition isnt false and cursor.nextSibling and not modifierkeys[which]
+    return
+
+  if enableCC and iOSMobile
+    fakeInput.onkeydown = (event) ->
+      which = undefined
+      removeSuggestion()
+      which = whichKey(event)
+      if which is 13 or which is 10
+        post @value
+        @value = ""
+        cursor.innerHTML = ""
+        false
+
+    fakeInput.onkeyup = (event) ->
+      which = undefined
+      cursor.innerHTML = cleanse(@value)
+      which = whichKey(event)
+      if enableCC and which isnt 9 and which isnt 16
+        clearTimeout codeCompleteTimer
+        codeCompleteTimer = setTimeout(->
+          codeComplete event
+          return
+        , 200)
+      return
+
+    fakeInputFocused = false
+    dblTapTimer = null
+    taps = 0
+    form.addEventListener "touchstart", (event) ->
+      if ccPosition isnt false
+        event.preventDefault()
+        clearTimeout dblTapTimer
+        taps++
+        if taps is 2
+          completeCode()
+          fakeInput.value = cursor.textContent
+          removeSuggestion()
+          fakeInput.focus()
+        else
+          dblTapTimer = setTimeout(->
+            taps = 0
+            codeComplete which: 9
+            return
+          , 200)
+      false
+
+  form.onsubmit = (event) ->
+    event = event or window.event
+    event.preventDefault and event.preventDefault()
+    removeSuggestion()
+    post exec.textContent or exec.value
+    false
+
+  document.onkeydown = (event) ->
+    which = undefined
+    event = event or window.event
+    which = event.which or event.keyCode
+    if event.shiftKey and event.metaKey and which is 8
+      output.innerHTML = ""
+      cursor.focus()
+    # space
+    else output.parentNode.scrollTop += 5 + output.parentNode.offsetHeight * ((if event.shiftKey then -1 else 1))  if event.target is output.parentNode and which is 32
+    changeView event
+
+  exec.onclick = ->
+    cursor.focus()
+    return
+
+  if window.location.search
+    post decodeURIComponent(window.location.search.substr(1))
+  else
+    post ":help", true
+  window.onpopstate = (event) ->
+    setCursorTo event.state or ""
+    return
+
+  setTimeout (->
+    window.scrollTo 0, 1
+    return
+  ), 500
+  setTimeout (->
+    document.getElementById("footer").className = "hidden"
+    return
+  ), 5000
+  getProps "window" # cache
+  try
+    document.body.className = "large"  unless not (localStorage.large * 1)
+  catch e
+
+  if document.addEventListener
+    document.addEventListener "deviceready", (->
+      cursor.focus()
+      return
+    ), false
+
+  # if (iOSMobile) {
+  #   document.getElementById('footer').style.display = 'none';
+  #   alert('hidden');
+  # }
+  if injected
+    (->
+      x = window._console
+      y = window.top.console
+      k = undefined
+      if y
+        for k of x
+          y[k] = x[k]
+      else
+        window.top.console = x
+      return
+    )()
+  return
+) this


Classroom: Minor updates on chapter view.


Tests: Skeleton for guv-heroku-workerstats

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

--- spec/workerstats.coffee
+++ spec/workerstats.coffee
@@ -0,0 +1,22 @@
+
+chai = require 'chai' if not chai
+path = require 'path'
+{ exec } = require 'child_process'
+
+guv_heroku_workerstats = (logfile, started, callback) ->
+  node = 'node'
+  prog = path.join __dirname, '..', 'bin', 'guv-heroku-workerstats'
+  cmd = "#{node} #{prog} --started #{started} #{logfile}"
+  return exec cmd, callback
+
+describe 'guv-heroku-workerstats', () ->
+
+  describe 'logfile without data', ->
+    it 'should error with no startups found'
+
+  # Sample < 10 (adjustable with --min-sample)?
+  describe 'logfile with too little data', ->
+    it 'should error with too few startups found'
+
+  describe 'logfile with plenty data', ->
+    it 'should return JSON with stats'


align all the things

align all the things

--- server.coffee
+++ server.coffee
@@ -8,9 +8,9 @@
 version         = "0.3.0"
 excluded        = process.env.CAMO_HOST_EXCLUSIONS || '*.example.org'
 shared_key      = process.env.CAMO_KEY             || '0x24FEEDFACEDEADBEEFCAFE'
+max_redirects   = process.env.CAMO_MAX_REDIRECTS   || 4
 camo_hostname   = process.env.CAMO_HOSTNAME        || "unknown"
 logging_enabled = process.env.CAMO_LOGGING_ENABLED || "disabled"
-max_redirects = process.env.CAMO_MAX_REDIRECTS     || 10;

 log = (msg) ->
   unless logging_enabled == "disabled"


Check for presence of destination directory

--- src/init.coffee
+++ src/init.coffee
@@ -29,10 +29,9 @@
   run: (options) ->
     {callback} = options
     options = @parseOptions(options.commandArgs)
-    if options.argv.convert?
+    if options.argv.convert
       sourcePath = path.resolve(options.argv.convert)
-      destinationPath = path.resolve(options.argv.theme)
-      @convertTheme(sourcePath, destinationPath, callback)
+      @convertTheme(sourcePath, options.argv.theme, callback)
     else if options.argv.package?
       packagePath = path.resolve(options.argv.package)
       templatePath = path.resolve(__dirname, '..', 'templates', 'package')
@@ -49,6 +48,12 @@
   convertTheme: (sourcePath, destinationPath, callback) ->
     unless fs.isFileSync(sourcePath)
       callback("TextMate theme file not found: #{sourcePath}")
+      return
+
+    if destinationPath
+      destinationPath = path.resolve(destinationPath)
+    else
+      callback("Specify directory to create theme in using --theme")
       return

     templatePath = path.resolve(__dirname, '..', 'templates', 'theme')


updates rasp

updates rasp

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/drsdrs/dooropener.git/'


Added auto zoom plugin

Added auto zoom plugin

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

--- src/plugins/auto_zoom.coffee
+++ src/plugins/auto_zoom.coffee
@@ -0,0 +1,22 @@
+class Scena.AutoZoom extends window.Scena.Plugin
+  initialize: =>
+    @initialBodyWidth = bodyWidth.call(@)
+    @initialBodyHeight = bodyHeight.call(@)
+    window.addEventListener('resize', @fitToWindow)
+    @fitToWindow()
+
+  fitToWindow: (e) =>
+    iw = window.innerWidth
+    ih = window.innerHeight
+    zoom = Math.min(iw / @initialBodyWidth, ih / @initialBodyHeight)
+    console.debug "#{iw} / #{@initialBodyWidth}"
+    window.document.body.style.zoom = zoom
+    console.debug "#{ih} / #{@initialBodyHeight}"
+
+  bodyWidth = ->
+    window.getComputedStyle(window.document.body).width.split('px')[0]
+
+  bodyHeight = ->
+    window.getComputedStyle(window.document.body).height.split('px')[0]
+
+new window.Scena.AutoZoom


if no timezone is set, it uses default one

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/clochix/cozy-ical.git/'


got rid of extraneous comment

got rid of extraneous comment

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/genericdave/speedr.js.git/'


deleting coffee script directory

deleting coffee script directory

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/erikakers/alphastone.git/'


make array copy function not enumerable

make array copy function not enumerable

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/aligerum/umbriel.git/'


Add test coverage

Add test coverage

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/CrowdTwist/widgets-js-sdk.git/'


Create atom/config.cson

Create atom/config.cson

error: pathspec 'atom/config.cson' did not match any file(s) known to git

--- atom/config.cson
+++ atom/config.cson
@@ -0,0 +1,45 @@
+'editor':
+  'fontSize': 13
+  'showIndentGuide': true
+  'fontFamily': 'Menlo'
+'core':
+  'themes': [
+    'atom-dark-ui'
+    'atom-dark-syntax'
+  ]
+  'ignoredNames': [
+    '.git'
+    '.svn'
+    '.DS_Store'
+    '.hg'
+  ]
+  'projectHome': '/Users/ecarey/repositories'
+  'audioBeep': false
+  'disabledPackages': [
+    'travis-ci-status'
+  ]
+'exception-reporting':
+  'userId': '42e5dfa4-c781-2acb-f743-bb312bfef836'
+'release-notes':
+  'viewedVersion': '0.85.0'
+'welcome':
+  'showOnStartup': false
+'metrics':
+  'userId': '764648fb67f080931e23b7adbf16bbdfa398b0d6'
+'autocomplete-plus':
+  'enableAutoActivation': false
+'autocomplete':
+  'includeCompletionsFromAllBuffers': true
+'spell-check':
+  'grammars': [
+    'source.gfm'
+    'text.git-commit'
+    'text.plain'
+    'text.plain.null-grammar'
+  ]
+'rspec':
+  'command': 'rspec'
+'achievements':
+  'NoticeDelay': 2500
+'script':
+  'path_prefix': '/Users/ecarey/.rbenv/shims/:/usr/local/bin'


Added: support for METHOD property

Added: support for METHOD property


Updated Gruntfile to monitor test correctly.

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/adrianlee44/galleryrs.git/'


Using gruntfile instead of cakefile

Using gruntfile instead of cakefile

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

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,35 @@
+module.exports = (grunt) ->
+
+  grunt.initConfig
+    pkg: grunt.file.readJSON 'package.json'
+    coffee:
+      compile:
+        files:
+          'dist/checkt.js': ['udefine/*.coffee', 'src/*.coffee']
+      tests:
+        files:
+          'test/tests.js': ['test/tests.coffee']
+    uglify:
+      options:
+        banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */\n'
+        report: 'gzip'
+      dist:
+        files:
+          'dist/<%= pkg.name %>.min.js': ['dist/<%= pkg.name %>.js']
+    mochaTest:
+      options:
+        reporter: 'spec'
+      test:
+        src: ['test/**/*.js']
+    coffeelint:
+      app: ['src/**/*.coffee'],
+      test: ['src/**/*.coffee'],
+      grunt: ['Gruntfile.coffee']
+
+  grunt.loadNpmTasks 'grunt-contrib-coffee'
+  grunt.loadNpmTasks 'grunt-contrib-uglify'
+  grunt.loadNpmTasks 'grunt-mocha-test'
+  grunt.loadNpmTasks 'grunt-coffeelint'
+
+  grunt.registerTask 'test', ['coffeelint', 'mochaTest']
+  grunt.registerTask 'default', ['test', 'coffee', 'uglify']


fix(grunt): avoid removing dev deps

fix(grunt): avoid removing dev deps

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -287,7 +287,7 @@
         command: "s3cmd sync -rP --guess-mime-type --delete-removed --no-preserve --cf-invalidate --add-header=Cache-Control:max-age=31536000 --exclude '.DS_Store' <%= config.cfg.static_files %> <%= config.cfg.s3_bucket %>"

       amsf__theme__to_dev_repo:
-        command: "rsync -avz --delete --progress --exclude=.git <%= amsf.base %>/themes/<%= amsf.theme.current %>/ /Users/sparanoid/Git/amsf-<%= amsf.theme.current %> > rsync-theme-dev.log"
+        command: "rsync -avz --delete --progress --exclude=.git --exclude=node_modules <%= amsf.base %>/themes/<%= amsf.theme.current %>/ /Users/sparanoid/Git/amsf-<%= amsf.theme.current %> > rsync-theme-dev.log"

     concurrent:
       options:


Grunt: update task order

Grunt: update task order

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/Aprila/laboratory.git/'


Add init test for SpellCheckManager

Add init test for SpellCheckManager

error: pathspec 'services/web/test/unit_frontend/coffee/ide/editor/aceEditor/spell-check/SpellCheckManagerTests.coffee' did not match any file(s) known to git

--- services/web/test/unit_frontend/coffee/ide/editor/aceEditor/spell-check/SpellCheckManagerTests.coffee
+++ services/web/test/unit_frontend/coffee/ide/editor/aceEditor/spell-check/SpellCheckManagerTests.coffee
@@ -0,0 +1,46 @@
+define [
+  'ide/editor/directives/aceEditor/spell-check/SpellCheckManager'
+], (SpellCheckManager) ->
+  describe 'SpellCheckManager', ->
+    beforeEach (done) ->
+      @timelord = sinon.useFakeTimers()
+
+      window.user = { id: 1 }
+      window.csrfToken = 'token'
+      @scope = {
+        $watch: sinon.stub()
+        spellCheck: true
+        spellCheckLanguage: 'en'
+      }
+      @wordManager = {
+        reset: sinon.stub()
+        clearRow: sinon.stub()
+        addHighlight: sinon.stub()
+      }
+      @adapter = {
+        getLines: sinon.stub()
+        wordManager: @wordManager
+      }
+      inject ($q, $http, $httpBackend, $cacheFactory) =>
+        @$http = $http
+        @$q = $q
+        @$httpBackend = $httpBackend
+        cache = $cacheFactory('spellCheckTest', {capacity: 1000})
+        @spellCheckManager = new SpellCheckManager(@scope, cache, $http, $q, @adapter)
+        done()
+
+    afterEach ->
+      @timelord.restore()
+
+    it 'runs a full check soon after init', () ->
+      @$httpBackend.when('POST', '/spelling/check').respond({
+        misspellings: [{
+          index: 0
+          suggestions: ['opposition']
+        }]
+      })
+      @adapter.getLines.returns(['oppozition'])
+      @spellCheckManager.init()
+      @timelord.tick(200)
+      @$httpBackend.flush()
+      expect(@wordManager.addHighlight).to.have.been.called


added photo picker module

added photo picker module

error: pathspec 'Modules/PhotoPicker/PhotoPickerTable.coffee' did not match any file(s) known to git

--- Modules/PhotoPicker/PhotoPickerTable.coffee
+++ Modules/PhotoPicker/PhotoPickerTable.coffee
@@ -0,0 +1,35 @@
+root.PhotoPicker.PhotoPickerTable = class PhotoPickerTable
+  constructor:(options = {}) ->
+    options = root._.extend({
+      backgroundColor: 'pink'
+    }, options)
+
+    @photos = []
+
+    @view = Ti.UI.createView options
+    @label = Ti.UI.createLabel { left: 10, top: 10 }
+    @view.add @label
+
+    @addFromGalleryButton = Ti.UI.createButton { title: 'Add From Gallery', bottom: 50 }
+    @addFromGalleryButton.addEventListener 'click', @addFromGallery
+    @view.add @addFromGalleryButton
+
+    @addFromCameraButton = Ti.UI.createButton { title: 'Add From Camera', bottom: 100 }
+    @addFromCameraButton.addEventListener 'click', @addFromCamera
+    @view.add @addFromCameraButton
+
+    @update()
+
+  update: =>
+    @label.text = "There are #{@photos.length} photos"
+
+  addFromGallery: =>
+
+  addFromCamera: =>
+    Ti.Media.showCamera { #TODO: GJ: can we set the orientation to force landscape?
+      success: (e) ->
+        #TODO: GJ: save to temp location
+        image = e.media
+      cancel: ->
+      error: ->
+    }


change lintOnFly to true

change lintOnFly to true

--- lib/main.coffee
+++ lib/main.coffee
@@ -44,7 +44,7 @@
     provider =
       grammarScopes: ['source.python', 'source.python.django']
       scope: 'file' # or 'project'
-      lintOnFly: false # must be false for scope: 'project'
+      lintOnFly: true # must be false for scope: 'project'
       lint: (textEditor) ->
         filePath = textEditor.getPath()
         fileText = textEditor.getText()


change file extension to correct GitHub's syntax highlighting

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/tom-szczarkowski/hn-stuff.git/'


[fix] Split by literal \n, not real \n

[fix] Split by literal \n, not real \n

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/Mikuia/Mikuia.git/'


add tests for SafeExec module

add tests for SafeExec module

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

--- test/unit/coffee/SafeExec.coffee
+++ test/unit/coffee/SafeExec.coffee
@@ -0,0 +1,42 @@
+assert = require("chai").assert
+sinon = require('sinon')
+chai = require('chai')
+should = chai.should()
+expect = chai.expect
+modulePath = "../../../app/js/SafeExec.js"
+SandboxedModule = require('sandboxed-module')
+
+describe "SafeExec", ->
+
+	beforeEach ->
+
+		@safe_exec = SandboxedModule.require modulePath, requires:
+			"logger-sharelatex":
+				log:->
+				err:->
+		@options = {timeout: 10*1000, killSignal: "SIGTERM" }
+
+	describe "safe_exec", ->
+
+		it "should execute a valid command", (done) ->
+			@safe_exec "/bin/echo hello", @options, (err, stdout, stderr) =>
+				stdout.should.equal "hello\n"
+				should.not.exist(err)
+				done()
+
+		it "should execute a command with non-zero exit status", (done) ->
+			@safe_exec "/bin/false", @options, (err, stdout, stderr) =>
+				stdout.should.equal ""
+				stderr.should.equal ""
+				err.message.should.equal "exit status 1"
+				done()
+
+		it "should handle an invalid command", (done) ->
+			@safe_exec "/bin/foobar", @options, (err, stdout, stderr) =>
+				err.code.should.equal "ENOENT"
+				done()
+
+		it "should handle a command that runs too long", (done) ->
+			@safe_exec "/bin/sleep 10", {timeout: 500, killSignal: "SIGTERM"}, (err, stdout, stderr) =>
+				err.should.equal "SIGTERM"
+				done()


Remove unnecessary render callback for op removal.

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/lmjohns3/thorax-media.git/'


Add menu items to move to next/previous diffs

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

--- menus/git-diff.cson
+++ menus/git-diff.cson
@@ -0,0 +1,12 @@
+'menu': [
+  {
+    'label': 'Packages'
+    'submenu': [
+      'label': 'Git Diff'
+      'submenu': [
+        { 'label': 'Move to Next Diff', 'command': 'git-diff:move-to-next-diff' }
+        { 'label': 'Move to Previous Diff', 'command': 'git-diff:move-to-previous-diff' }
+      ]
+    ]
+  }
+]


Don't wait for spec windows to save state

Since spec windows don't register handlers for the IPC messages requested window state to be saved, the promise never resolves and the close button needs to be clicked twice. To avoid this, we'll just resolve the promise immediately in a spec window so we can proceed to close it.

--- src/main-process/atom-window.coffee
+++ src/main-process/atom-window.coffee
@@ -178,6 +178,9 @@
     @unloading = false

   saveState: ->
+    if @isSpecWindow()
+      return Promise.resolve()
+
     @lastSaveStatePromise = new Promise (resolve) =>
       callback = (event) =>
         if BrowserWindow.fromWebContents(event.sender) is @browserWindow


fix images drag&drop

fix images drag&drop

--- app/assets/javascripts/uploadcare/widget/dragdrop.js.coffee
+++ app/assets/javascripts/uploadcare/widget/dragdrop.js.coffee
@@ -11,8 +11,12 @@
   ns.uploadDrop = (el, callback, settings) ->
     settings = s.build settings
     ns.receiveDrop el, (type, data) ->
-      method = if settings.multiple then 'filesFrom' else 'fileFrom'
-      callback uploadcare[method](type, data, settings)
+      callback(
+        if settings.multiple
+          uploadcare.filesFrom(type, data, settings)
+        else
+          uploadcare.fileFrom(type, data[0], settings)
+      )

   unless ns.support
     ns.receiveDrop = ->
@@ -29,13 +33,20 @@
           e.preventDefault() # Prevent opening files.

           dt = e.originalEvent.dataTransfer
-          if dt and dt.files.length
+          if not dt
+            return
+
+          if dt.files.length
             callback('object', dt.files)
+
           else
-            uris = dt.getData('text/uri-list')
+            uris = []
+            for uri in dt.getData('text/uri-list').split()
+              uri = $.trim(uri)
+              if uri && uri[0] != '#'
+                uris.push(uri)
+
             if uris
-              # opera likes to add \n at the end
-              uris = uris.replace /\n$/, ''
               callback('url', uris)

     ns.watchDragging = (el, receiver) ->


Add disclosures JS

Add disclosures JS

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

error: pathspec 'vendor/assets/javascripts/fustrate/components/disclosure.coffee' did not match any file(s) known to git

--- vendor/assets/javascripts/fustrate/components/disclosure.coffee
+++ vendor/assets/javascripts/fustrate/components/disclosure.coffee
@@ -0,0 +1,12 @@
+class Fustrate.Components.Disclosure extends Fustrate.Components.Base
+  @initialize: ->
+    $('body').on 'click', '.disclosure-title', (event) ->
+      disclosure = $(event.currentTarget).closest('.disclosure')
+
+      isOpen = disclosure.hasClass 'open'
+
+      disclosure
+        .toggleClass 'open'
+        .trigger "#{if isOpen then 'closed' else 'opened'}.disclosure"
+
+      false


:art: Remove unnecessary branching

:art: Remove unnecessary branching

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -888,13 +888,8 @@
         # could be multiple lines in the buffer. But at the same time, if the
         # next buffer row is wrapped, one line in the buffer can represent many
         # screen rows.
-        [nextBufferRowScreenStart, nextBufferRowScreenEnd] = @displayBuffer.rowMap.screenRowRangeForBufferRow(linesRange.end.row)
-        if nextBufferRowScreenEnd - nextBufferRowScreenStart > 1
-          followingScreenRow = nextBufferRowScreenEnd
-          followingBufferRow = @bufferRowForScreenRow(followingScreenRow)
-        else
-          followingScreenRow = @screenRowForBufferRow(linesRange.end.row) + 1
-          followingBufferRow = @bufferRowForScreenRow(followingScreenRow)
+        followingScreenRow = @displayBuffer.lastScreenRowForBufferRow(linesRange.end.row) + 1
+        followingBufferRow = @bufferRowForScreenRow(followingScreenRow)
         insertDelta = followingBufferRow - linesRange.end.row

         # Any folds in the text that is moved will need to be re-created.


skip timer

skip timer

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/LucasAdam/hubot-trivia-game-french.git/'