add index.coffee entry

add index.coffee entry

--- index.coffee
+++ index.coffee
@@ -0,0 +1,12 @@
+fs = require 'fs'
+path = require 'path'
+
+module.exports = (robot, scripts) ->
+  scriptsPath = path.resolve(__dirname, 'src')
+  fs.exists scriptsPath, (exists) ->
+    if exists
+      for script in fs.readdirSync(scriptsPath)
+        if scripts? and '*' not in scripts
+          robot.loadFile(scriptsPath, script) if script in scripts
+        else
+          robot.loadFile(scriptsPath, script)


refactor out dupe code for dragging

refactor out dupe code for dragging

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


Add some dark side of force

Add some dark side of force

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


Hubot should say "hello" when I say "herro" to it

Why bot not friendly???

--- scripts/morning.coffee
+++ scripts/morning.coffee
@@ -24,10 +24,10 @@
     "Good 'aye!, %"
 ]
 module.exports = (robot) ->
-    robot.hear /(hello|good( [d'])?ay(e)?)/i, (msg) ->
+    robot.hear /(he(ll|rr)o|good( [d'])?ay(e)?)/i, (msg) ->
         hello = msg.random hellos
         msg.send hello.replace "%", msg.message.user.name

     robot.hear /(^(good )?m(a|o)rnin(g)?)/i, (msg) ->
         hello = msg.random mornings
-        msg.send hello.replace "%", msg.message.user.name+        msg.send hello.replace "%", msg.message.user.name


Remove cl, 2

Remove cl, 2

--- helper/settings.coffee
+++ helper/settings.coffee
@@ -18,5 +18,4 @@
   for file in files
     if fs.existsSync(file)
       settings = _.deepExtend settings, JSON.parse fs.readFileSync file, {encoding: "UTF-8"}
-  console.log settings
   settings


- Don't explode on an unbound listening socket.

--- debugger.coffee
+++ debugger.coffee
@@ -150,7 +150,7 @@

         else if handle instanceof net.Server
           addr = handle.address()
-          server.outputStream.write "#{c.yellow('Listening Socket')} #{c.bold(addr.family)} #{addr.address}:#{addr.port}\n"
+          server.outputStream.write "#{c.yellow('Listening Socket')} #{c.bold(addr?.family)} #{addr?.address}:#{addr?.port}\n"
         else if handle.constructor.name is "ChildProcess"
           server.outputStream.write "#{c.cyan('Child Process')} [pid: #{handle.pid}] #{handle.spawnargs.join(' ')}\n"
         else if handle.constructor.name is "Pipe"


Add a request to get all recurring events

--- server/models/requests.coffee
+++ server/models/requests.coffee
@@ -21,6 +21,8 @@
     event:
         all       : cozydb.defaultRequests.all
         byDate    : (doc) -> emit new Date(doc.start), doc
+        reccuring : (doc) ->
+            emit doc.id, doc if doc.rrule? and doc.rrule.length > 0
         tags      : tagsView
         byCalendar: cozydb.defaultRequests.by 'tags[0]'


Disallow new accounts

Disallow new accounts

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

--- server/disallow.coffee
+++ server/disallow.coffee
@@ -0,0 +1,3 @@
+#Don't allow new accounts
+Accounts.validateNewUser ->
+  false


update dev

update dev

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


live bind input events in index.coffee

live bind input events in index.coffee

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


Added school changes to schema

Added school changes to schema

--- app/schemas/models/user.coffee
+++ app/schemas/models/user.coffee
@@ -134,7 +134,7 @@
       schoolFilter:
         title: 'School'
         type: 'string'
-        enum: ['Top 20 Eng.', 'Other US', 'Other Intl.']
+        enum: ['Top 20 Eng.', 'Other US', 'Other Intl.','Top School', 'Other']
       locationFilter:
         title: 'Location'
         type: 'string'


Condense / cleanup TextBuffer serialization specs

Typically it's fine to test serialization behaviorally. If we can
deserialize the serialized state correctly, then we're generally
happy. We don't need explicit tests on the serialized state… but I
added a couple assertions to ensure we don't write text when we don't
need to. It would have been more correct to just modify the saved
file and verify we load the new state, but it's not worth the hassle.

--- spec/app/text-buffer-spec.coffee
+++ spec/app/text-buffer-spec.coffee
@@ -1231,51 +1231,37 @@
         expect(buffer.clipPosition([0,10])).toEqual [0,9]
         expect(buffer.clipPosition([10,Infinity])).toEqual [0,9]

-  describe "when the buffer is serialized", ->
-    describe "when the contents of the buffer are saved on disk", ->
-      it "stores the file path", ->
-        data = buffer.serialize()
-        expect(data.path).toBe(buffer.getPath())
-        expect(data.text).toBeFalsy()
-
-    describe "when the buffer has unsaved changes", ->
-      it "stores the file path and the changed buffer text", ->
+  describe "serialization", ->
+    serializedState = null
+
+    reloadBuffer = () ->
+      serializedState = buffer.serialize()
+      buffer.release()
+      buffer = Buffer.deserialize(serializedState)
+
+    describe "when the serialized buffer had no unsaved changes", ->
+      it "loads the current contents of the file at the serialized path", ->
+        path = buffer.getPath()
+        text = buffer.getText()
+        reloadBuffer()
+        expect(serializedState.text).toBeUndefined()
+        expect(buffer.getPath()).toBe(path)
+        expect(buffer.getText()).toBe(text)
+
+    describe "when the serialized buffer had unsaved changes", ->
+      it "restores the previous unsaved state of the buffer", ->
+        path = buffer.getPath()
         buffer.setText("abc")
-        data = buffer.serialize()
-        expect(data.path).toBe(buffer.getPath())
-        expect(data.text).toBe("abc")
-
-    describe "when the buffer has never been saved", ->
-      it "stores the changed buffer text", ->
-        buffer.release()
-        buffer = project.bufferForPath(null)
+        reloadBuffer()
+        expect(serializedState.text).toBe "abc"
+        expect(buffer.getPath()).toBe(path)
+        expect(buffer.getText()).toBe("abc")
+
+    describe "when the serialized buffer was unsaved and had no path", ->
+      it "restores the previous unsaved state of the buffer", ->
+        buffer.setPath(undefined)
         buffer.setText("abc")
-        data = buffer.serialize()
-        expect(data.path).toBeFalsy()
-        expect(data.text).toBe("abc")
-
-  describe "when a buffer is deserialized", ->
-    reloadBuffer = () ->
-      serialized = buffer.serialize()
-      buffer.release()
-      buffer = Buffer.deserialize(serialized)
-
-    it "loads the contents of the file saved on disk when there are no unsaved changes", ->
-      path = buffer.getPath()
-      reloadBuffer()
-      expect(buffer.getPath()).toBe(path)
-
-    it "loads the stored changes if the file was modified", ->
-      path = buffer.getPath()
-      buffer.setText("abc")
-      reloadBuffer()
-      expect(buffer.getPath()).toBe(path)
-      expect(buffer.getText()).toBe("abc")
-
-    it "loads the stored changes if the file was never saved", ->
-      buffer.release()
-      buffer = project.bufferForPath(null)
-      buffer.setText("abc")
-      reloadBuffer()
-      expect(buffer.getPath()).toBeFalsy()
-      expect(buffer.getText()).toBe("abc")
+        reloadBuffer()
+        expect(serializedState.path).toBeUndefined()
+        expect(buffer.getPath()).toBeUndefined()
+        expect(buffer.getText()).toBe("abc")


I did something here.

I did something here.

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


Cleanup

Cleanup

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/urbanetic/bismuth-schema.git/'


Terminal: restore tab feature is updated

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -30,11 +30,64 @@

     @on 'VMItemClicked',     @bound 'prepareAndRunTerminal'
     @on 'PlusHandleClicked', @bound 'handlePlusClick'
+    @on 'WebTermConnected',  @bound 'updateSessions'

     {vmController} = KD.singletons
     vmController.on 'vm.progress.error', => notify cssClass : 'error'

-    @startTab.on "sessionSelected", ({vm, session}) => @createNewTab {vm, session, mode: 'resume'}
+    @on "sessionSelected", ({vm, session}) => @createNewTab {vm, session, mode: 'resume'}
+    @restoreTabs()
+
+  restoreTabs: ->
+    @fetchStorage (storage) =>
+      sessions = storage.getValue 'savedSessions'
+      return  unless sessions?.length
+
+      notify
+        title     : "Checking for previous sessions"
+        cssClass  : "success"
+
+      # group sessions by alias
+      aliases = []
+      sessions.forEach (session) ->
+        [alias, sessionId] = session.split ':'
+        aliases.push alias  unless alias in aliases
+
+      # fetch vms and store in an object with the key of alias
+      KD.singletons.vmController.fetchVMs (err, vms) =>
+        return warn err  if err
+        vmList = {}
+        vms.map (vm) ->
+          vmList[vm.hostnameAlias] = vm
+
+        {dash} = Bongo
+
+        # fetch all active vm sessions via terminal kites
+        activeSessions = []
+        {vmController:{terminalKites : kites}} = KD.singletons
+        queue = aliases.map (alias)->->
+          kites[alias].webtermGetSessions().then (sessions) =>
+            activeSessions = activeSessions.concat sessions
+            queue.fin()
+          .catch (err) ->
+            warn err
+            queue.fin()
+
+        # after all active sessions are fetched, compare them with last open sessions
+        sessionRestored = no
+        dash queue, =>
+          sessions.forEach (session) =>
+            [alias, sessionId] = session.split ':'
+            if sessionId in activeSessions
+              sessionRestored = yes
+              @createNewTab {vm: vmList[alias], session: sessionId, mode: 'resume'}
+
+          activeIndex = storage.getValue 'activeIndex'
+          activePane = @tabView.getPaneByIndex activeIndex ? 1
+          if sessionRestored and activeIndex isnt 0
+            @tabView.showPane activePane
+            { terminalView } = activePane.getOptions()
+            terminalView.setKeyView()

   initPane: (pane) ->

@@ -51,7 +104,6 @@

   handlePaneShown:(pane, index)->
-
     @_windowDidResize()
     {terminalView} = pane.getOptions()

@@ -66,26 +118,6 @@
   fetchStorage: (callback) ->
     storage = KD.getSingleton('appStorageController').storage 'Terminal', '1.0.1'
     storage.fetchStorage -> callback storage
-
-  restoreTabs: (vm) ->
-
-    notify
-      title     : "Checking for previous sessions"
-      cssClass  : "success"
-
-    @fetchStorage (storage) =>
-      sessions = storage.getValue 'savedSessions'
-      activeIndex = storage.getValue 'activeIndex'
-      if sessions?.length
-        for session in sessions
-          [vmName, sessionId] = session.split ':'
-          @createNewTab { vm, session: sessionId, mode: 'resume' }
-        activePane = @tabView.getPaneByIndex activeIndex ? 0
-        @tabView.showPane activePane
-        { terminalView } = activePane.getOptions()
-        terminalView.setKeyView()
-      else
-        @addNewTab vm

   showApprovalModal: (remote, command)->
@@ -159,7 +191,7 @@
     {terminalView} = pane.getOptions()
     terminalView.terminal?.scrollToBottom()
     terminalView.once 'WebTermConnected', (remote)=>
-
+      @emit "WebTermConnected"
       if query.command
         command = decodeURIComponent query.command
         @showApprovalModal remote, command
@@ -217,6 +249,7 @@

     @appendTerminalTab terminalView
     terminalView.connectToTerminal()
+    @forwardEvent terminalView, "WebTermConnected"

   addStartTab:->

@@ -265,8 +298,8 @@
         { terminalView } = pane.getOptions()
         return unless terminalView
         sessionId = terminalView.sessionId ? terminalView.getOption 'session'
-        vmName = terminalView.getOption 'vmName'
-        sessions.push "#{ vmName }:#{ sessionId }"
+        {hostnameAlias} = terminalView.getOption 'vm'
+        sessions.push "#{ hostnameAlias }:#{ sessionId }"
       storage.setValue 'savedSessions', sessions
       storage.setValue 'activeIndex', activeIndex


clio-32363 thInput spec file did not end with a single new line.

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/seanhealy/lib-themisui.git/'


add tests for right parser

add tests for right parser

error: pathspec 'test/access-control.coffee' did not match any file(s) known to git

--- test/access-control.coffee
+++ test/access-control.coffee
@@ -0,0 +1,36 @@
+app = require '../app'
+
+module.exports =
+
+  'parseRight': (test) ->
+    app (
+      parseRight
+    ) ->
+      test.equal parseRight(''), null
+      test.equal parseRight('# canGetUsers'), null
+      test.equal parseRight(' canGetUsers'), null
+      test.deepEqual parseRight('canGetUsers'),
+        name: 'canGetUsers'
+        args: []
+      test.equal parseRight('canGetUsers('), null
+      test.equal parseRight('canGetUsers(('), null
+      test.equal parseRight('canGetUsers(())'), null
+      test.deepEqual parseRight('canGetUsers()'),
+        name: 'canGetUsers'
+        args: []
+      test.equal parseRight('canGetUsers)'), null
+      test.deepEqual parseRight('canGetUsers(1)'),
+        name: 'canGetUsers'
+        args: [1]
+      test.equal parseRight('canGetUsers(one)'), null
+      test.equal parseRight('canGetUsers(\'one)'), null
+      test.equal parseRight('canGetUsers(one\')'), null
+      test.deepEqual parseRight('canGetUsers(\'one\')'),
+        name: 'canGetUsers'
+        args: ['one']
+      test.deepEqual parseRight('canGetUsers( \'one\', 2, 3, \'four  \' )'),
+        name: 'canGetUsers'
+        args: ['one', 2, 3, 'four  ']
+      test.equal parseRight('canGetUsers( \'one\', 2, 3, \'four  \',  )'), null
+
+      test.done()


worker/social: remove unnecessay lines

worker/social: remove unnecessay lines

--- workers/social/lib/social/models/group/groupdata.coffee
+++ workers/social/lib/social/models/group/groupdata.coffee
@@ -1,5 +1,3 @@
-# coffeelint: disable=duplicate_key
-
 { secure, signature, Base, JsPath:{ getAt } } = Bongo
 module.exports = class JGroupData extends Base
   JPermissionSet = require './permissionset'
@@ -83,5 +81,3 @@

       data.update operation, (err) ->
         callback err
-
-


Enable multi language filter in projects_path

To filter more than two language projects_path, use Cmd or Ctrl key
when selecting language.
This behavior is well known in many OS, so people will be want to use
this when filtering multi languages.

--- app/assets/javascripts/projects.js.coffee
+++ app/assets/javascripts/projects.js.coffee
@@ -1,28 +1,34 @@
 $ ->
   projects = $('#projects').clone()

+  languages = []
   $('#languages a').click (e) ->
-    language = $(this).data().language
+    clicked_language = $(this).data().language

-    $('#noprojects').hide()
-
-    if language?
-      language = [language] unless $.isArray(language)
-
-      $('#languages li')
-        .addClass('disabled')
-        .find($.map(language, (l) -> "a[data-language=#{l}]").join(','))
-        .add(this)
-        .parent('li')
-        .removeClass('disabled')
-
-      $('#projects').quicksand $(projects).find(".#{language.join(', .')}"), duration: 0, ->
-        if $('#projects .project:visible').length == 0
-          $('#noprojects').show()
-
-    else
+    unless clicked_language?
       $('#languages li').removeClass('disabled')
       $('#projects').html(projects.find('.project'))
       projects = $('#projects').clone()
       $('#projects').css('height', 'auto')
-    return false
+      return
+
+    unless e.ctrlKey or e.metaKey
+      languages = [].concat(clicked_language)
+    else
+      unless clicked_language in languages
+        languages.push clicked_language
+      else
+        languages = (language for language in languages when language != clicked_language)
+
+    $('#noprojects').hide()
+
+    if languages.length > 0
+      $('#languages li')
+        .addClass('disabled')
+        .find($.map(languages, (l) -> "a[data-language=#{l}]").join(','))
+        .parent('li')
+        .removeClass('disabled')
+
+      $('#projects').quicksand $(projects).find(".#{languages.join(', .')}"), duration: 0, ->
+        if $('#projects .project:visible').length == 0
+          $('#noprojects').show()


bug fix

bug fix

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


s3 config

s3 config


More for #133.

More for #133.

--- app/lib/world/script_event_prereqs.coffee
+++ app/lib/world/script_event_prereqs.coffee
@@ -11,7 +11,7 @@
     return false if ap.lessThan? and not (v < ap.lessThan)
     return false if ap.lessThanOrEqualTo? and not (v <= ap.lessThanOrEqualTo)
     return false if ap.containingString? and (not v or v.search(ap.containingString) is -1)
-    return false if ap.notContainingString? and v?.search(ap.containingString) isnt -1
+    return false if ap.notContainingString? and v?.search(ap.notContainingString) isnt -1
     return false if ap.containingRegexp? and (not v or v.search(new RegExp(ap.containingRegexp)) is -1)
     return false if ap.notContainingRegexp? and v?.search(new RegExp(ap.notContainingRegexp)) isnt -1


Add confirm exit

Add confirm exit

--- views/app.cjsx
+++ views/app.cjsx
@@ -172,6 +172,21 @@
   ]
   window.toggleModal title, content, footer

+confirmExit = false
+exitPoi = ->
+  confirmExit = true
+  window.close()
+window.onbeforeunload = (e) ->
+  if confirmExit
+    return true
+  else
+    toggleModal '关闭 poi', '确认退出？', [
+      name: '确定退出'
+      func: exitPoi
+      style: 'warning'
+    ]
+    return false
+
 window.addEventListener 'game.request', (e) ->
   {method, path} = e.detail
   log "正在请求 #{method} #{path}"


Handles () parens

Handles () parens

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


Changed message for camera delete and share remove.

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


Defer state building in TextEditorPresenter

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -34,6 +34,7 @@
     @observeModel()
     @observeConfig()
     @buildState()
+    @invalidate()
     @startBlinkingCursors() if @focused
     @updating = false

@@ -111,6 +112,23 @@
     @shouldUpdateLineNumbersState = false
     @shouldUpdateGutterOrderState = false
     @shouldUpdateCustomGutterDecorationState = false
+
+  invalidate: ->
+    @shouldUpdateFocusedState = true
+    @shouldUpdateHeightState = true
+    @shouldUpdateVerticalScrollState = true
+    @shouldUpdateHorizontalScrollState = true
+    @shouldUpdateScrollbarsState = true
+    @shouldUpdateHiddenInputState = true
+    @shouldUpdateContentState = true
+    @shouldUpdateDecorations = true
+    @shouldUpdateLinesState = true
+    @shouldUpdateCursorsState = true
+    @shouldUpdateOverlaysState = true
+    @shouldUpdateLineNumberGutterState = true
+    @shouldUpdateLineNumbersState = true
+    @shouldUpdateGutterOrderState = true
+    @shouldUpdateCustomGutterDecorationState = true

   observeModel: ->
     @disposables.add @model.onDidChange =>
@@ -226,35 +244,6 @@
     @customGutterDecorations = {}
     @lineNumberGutter =
       tiles: {}
-
-    @updateState()
-
-  updateState: ->
-    @shouldUpdateLinesState = true
-    @shouldUpdateLineNumbersState = true
-
-    @updateContentDimensions()
-    @updateScrollbarDimensions()
-    @updateStartRow()
-    @updateEndRow()
-
-    @updateFocusedState()
-    @updateHeightState()
-    @updateVerticalScrollState()
-    @updateHorizontalScrollState()
-    @updateScrollbarsState()
-    @updateHiddenInputState()
-    @updateContentState()
-    @updateDecorations()
-    @updateTilesState()
-    @updateCursorsState()
-    @updateOverlaysState()
-    @updateLineNumberGutterState()
-    @updateCommonGutterState()
-    @updateGutterOrderState()
-    @updateCustomGutterDecorationState()
-
-    @resetTrackedUpdates()

   updateFocusedState: ->
     @state.focused = @focused


Temporary for Windows #93

Temporary for Windows #93

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/Adirelle/atom-autocomplete-php.git/'


trigger slideshow on image click

trigger slideshow on image click

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


Remove console.log

Remove console.log

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/erikaheidi/24pullrequests.git/'


Touching up.

Touching up.

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -1,7 +1,6 @@
 Utils =
   getCurrentVersion: ->
-    # Chromium #15242 will make this XHR request to access the manifest
-    # unnecessary
+    # Chromium #15242 will make this XHR request to access the manifest unnecessary
     manifestRequest = new XMLHttpRequest()
     manifestRequest.open("GET", chrome.extension.getURL("manifest.json"), false)
     manifestRequest.send(null)
@@ -54,8 +53,8 @@
     # More or less RFC compliant URL host part parsing. This should be sufficient for our needs
     urlRegex = new RegExp(
       '^(?:([^:]+)(?::([^:]+))?@)?' + # user:password (optional) => \1, \2
-      '([^:]+|\\[[^\\]]+\\])' + # host name (IPv6 addresses in square brackets allowed) => \3
-      '(?::(\\d+))?$' # port number (optional) => \4
+      '([^:]+|\\[[^\\]]+\\])'       + # host name (IPv6 addresses in square brackets allowed) => \3
+      '(?::(\\d+))?$'                 # port number (optional) => \4
       )

     # Official ASCII TLDs that are longer than 3 characters + inofficial .onion TLD used by TOR
@@ -92,7 +91,7 @@

   # Creates a search URL from the given :query.
   createSearchUrl: (query) ->
-    # Escape explicitely to encode characters like "+" correctly
+    # Escape explicitly to encode characters like "+" correctly
     "http://www.google.com/search?q=" + encodeURIComponent(query)

   # Converts :string into a Google search if it's not already a URL. We don't bother with escaping characters


Fix SimpleCache bugs.

Fix SimpleCache bugs.

This fixes two bugs in SimpleCache.

1. Rotate the cache on the next tick.  There is a marginally small
   chance that the cache will rotate between calls to .has() and .get().
   So, we do the rotation ansynchronously.  This guarantees that these
   two functions will always see the same cache state.

2. The implementation of .clear() (which is unused, I think) has at some
   point become out of date (and incorrect) w.r.t. the implementation of
   .rotate().

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -323,14 +323,15 @@
       null

   rotate: (force = false) ->
-    if force or @entries < Object.keys(@cache).length or @expiry < new Date() - @lastRotation
-      @lastRotation = new Date()
-      @previous = @cache
-      @cache = {}
+    Utils.nextTick =>
+      if force or @entries < Object.keys(@cache).length or @expiry < new Date() - @lastRotation
+        @lastRotation = new Date()
+        @previous = @cache
+        @cache = {}

   clear: ->
-    @rotate true
-    @rotate true
+    @cache = {}
+    @previous = {}

 # This is a simple class for the common case where we want to use some data value which may be immediately
 # available, or for which we may have to wait.  It implements a use-immediately-or-wait queue, and calls the


Prevent user to focus on other's profile editing areas

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


ResourceListItem: add new button to request destroy over kloud

--- client/admin/lib/views/resources/resourcelistitem.coffee
+++ client/admin/lib/views/resources/resourcelistitem.coffee
@@ -43,6 +43,11 @@
     @details.addSubView controller.getView()

     @details.addSubView new kd.ButtonView
+      title    : 'Request Destroy'
+      cssClass : 'solid small red fr'
+      callback : @bound 'handleDestroy'
+
+    @details.addSubView new kd.ButtonView
       title    : 'Delete'
       cssClass : 'solid small red fr'
       callback : @bound 'handleDelete'
@@ -50,6 +55,28 @@
     @ownerView = new AvatarView {
       size: { width: 25, height: 25 }
     }, resource.owner
+
+
+  handleDestroy: ->
+
+    resource              = @getData()
+    delegate              = @getDelegate()
+    { computeController } = kd.singletons
+
+    computeController.ui.askFor 'deleteStack', {}, (status) ->
+      return  unless status.confirmed
+
+      resource.maintenance prepareForDestroy: yes, (err) ->
+        return  if showError err
+
+        computeController.destroyStack resource, (err) ->
+          return  if showError err
+
+          # FIXME ~GG this reload mechanism needs to be
+          #           replaced with auto instance update instead
+          delegate.emit 'ReloadItems'
+          computeController.once "stateChanged-#{resource._id}", ->
+            delegate.emit 'ReloadItems'

   handleDelete: ->


remove an unused spec helper

remove an unused spec helper

No test uses this "toString" function and when other processes
call toString implicitly (like in a regex comparison or similar) this
can override the expected behavior, producing errors for
elements that don't have the expected configuration.  These errors
fail js tests intermittantly, contributing to the fragility of the
aux build

removing it cuts down on spurious javascript failures.

Change-Id: I5b1eb12ecc7d1b849282f5011dbad89142208bf4
Reviewed-on: https://gerrit.instructure.com/52320
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
Reviewed-by: Zach Wily <9de8c4480303b5335cd2a33eefe814615ba3612a@instructure.com>
Tested-by: Jenkins
Product-Review: Ethan Vizitei <73eeb0b2f65d05a906adf3b21ee1f9f5a2aa3c1c@instructure.com>
QA-Review: Ethan Vizitei <73eeb0b2f65d05a906adf3b21ee1f9f5a2aa3c1c@instructure.com>

--- spec/coffeescripts/helpers/assertions.coffee
+++ spec/coffeescripts/helpers/assertions.coffee
@@ -1,14 +1,4 @@
 define ['jquery'], ($) ->
-
-  $.fn.toString = ->
-    return '' unless this.length
-    id = this.attr 'id'
-    className = this.attr('class').replace(/\s/g, '.')
-    tag = this[0].tagName.toLowerCase()
-    str = tag
-    str += "##{id}" if id
-    str += ".#{className}" if className
-    "<#{str}>"

   isVisible: ($el, message = '') ->
     ok $el.length, "elements found"


fix for "bug that causes moveLineUp to move the wrong selections"

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -831,6 +831,11 @@
         selection = selections.shift()
         selectionsToMove = [selection]

+        while selection.end.row is selections[0]?.start.row
+          selectionsToMove.push(selections[0])
+          selection.end.row = selections[0].end.row
+          selections.shift()
+
         # Compute the range spanned by all these selections...
         linesRangeStart = [selection.start.row, 0]
         if selection.end.row > selection.start.row and selection.end.column is 0


add mergedset

add mergedset

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

--- src/gg/scale/mergedset.coffee
+++ src/gg/scale/mergedset.coffee
@@ -0,0 +1,49 @@
+#<< gg/scale/factory
+#
+
+#
+#
+# Manage a graphic/pane/layer's set of scales
+# a Wrapper around {aes -> {type -> scale} } + utility functions
+#
+# lazily instantiates scale objects as they are requests.
+# uses internal scalesFactory for creation
+#
+class gg.scale.MergedSet
+  @ggpackage = 'gg.scale.MergedSet'
+
+  constructor: (@sets=[]) ->
+    @mapping = {}
+    @setup()
+
+  key: (col, type, klassName) ->
+    JSON.stringify([col, String(type), klassName])
+
+  setup: ->
+    mapping = {} # (scaleclass, type, aes) -> [ scales to merge ]
+    for set in @sets
+      for s in set.scalesList()
+        key = @key s.aes, s.type, s.constructor.name
+        mapping[key] = [] unless key of mapping
+        mapping[key].push s
+
+    @mapping = _.o2map mapping, (ss, key) ->
+      s = ss[0].clone()
+      for other in _.rest(ss)
+        s.mergeDomain other.domain()
+      [key, s]
+
+  contains: (aes, type, klassName) ->
+    @key(aes, type, klassName) of @mapping
+
+  get: (aes, type, klassName) ->
+    @mapping[@key aes, type, klassName]
+
+  toString: ->
+    ss = _.map @mapping, (s, k) ->
+      k + "\t->\t" + s.toString()
+    ss.join('\n')
+
+
+
+


Remove old cropping css manipulation -- this is done with template binding now.

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  multiplexed_consumer.js.coffee

add  multiplexed_consumer.js.coffee

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

--- lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee
+++ lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee
@@ -0,0 +1,65 @@
+Firehose.multiplexChannel = "channels@firehose"
+
+class Firehose.MultiplexedConsumer extends Firehose.Consumer
+  @subscriptionQuery: (config) ->
+    {
+      subscribe: [
+        "#{channel}!#{opts.last_sequence || 0}" for channel, opts of config.channels
+      ].join(",")
+    }
+
+  @normalizeChannels: (config) ->
+    for chan, opts of config.channels
+      if chan[0] != "/"
+        delete config.channels[chan]
+        config.channels["/" + chan] = opts
+
+  constructor: (@config = {}) ->
+    @messageHandlers = {}
+    @config.message ||= @message
+    @config.uri += Firehose.multiplexChannel
+
+    @config.params = Firehose.MultiplexedConsumer.subscriptionQuery(@config)
+
+    Firehose.MultiplexedConsumer.normalizeChannels(@config)
+    for channel, opts of @config.channels
+      @_addSubscriptionHandler(channel, opts)
+
+    super(@config)
+
+  websocketTransport: (config) =>
+    new Firehose.MultiplexedWebSocket(config)
+
+  longpollTransport: (config) =>
+    new Firehose.MultiplexedLongPoll(config)
+
+  message: (msg) =>
+    if handler = @messageHandlers[msg.channel]
+      handler(@config.parse msg.message)
+
+  _addSubscriptionHandler: (channel, opts) =>
+    if opts.message
+      @messageHandlers[channel] = opts.message
+
+  _removeSubscriptionHandler: (channelNames...) =>
+    for chan in channelNames
+      delete @messageHandlers[chan]
+
+  _updateParams: =>
+    Firehose.MultiplexedConsumer.normalizeChannels(@config)
+    @config.params = Firehose.MultiplexedConsumer.subscriptionQuery(@config)
+
+  subscribe: (channel, opts = {}) =>
+    @config.channels[channel] = opts
+
+    @_updateParams()
+    @_addSubscriptionHandler(channel, opts)
+    @transport.subscribe(channel, opts)
+
+  unsubscribe: (channelNames...) =>
+    for channel in channelNames
+      delete @config.channels[channel]
+
+    @_updateParams()
+    @_removeSubscriptionHandler(channelNames...)
+    @transport.unsubscribe(channelNames...)


test field extraction for #256.1

test field extraction for #256.1

error: pathspec 'resource/translators/Pandoc' did not match any file(s) known to git
error: pathspec 'JSON.coffee' did not match any file(s) known to git


adding in homework plan tests

adding in homework plan tests

error: pathspec 'test/components/homework-plan.spec.coffee' did not match any file(s) known to git

--- test/components/homework-plan.spec.coffee
+++ test/components/homework-plan.spec.coffee
@@ -0,0 +1,110 @@
+{expect} = require 'chai'
+_ = require 'underscore'
+{routerStub, commonActions} = require './helpers/utilities'
+
+VALID_MODEL = require '../../api/plans/2.json'
+READINGS = require '../../api/courses/1/readings.json'
+EXERCISES = require '../../api/courses/1/exercises.json'
+
+{TaskPlanActions, TaskPlanStore} = require '../../src/flux/task-plan'
+{ExerciseActions, ExerciseStore} = require '../../src/flux/exercise'
+{TocActions, TocStore} = require '../../src/flux/toc'
+{HomeworkPlan} = require '../../src/components/task-plan/homework'
+
+courseId = 1
+
+window.confirm = () -> return true
+window.alert = () ->
+
+describe 'Homework Builder', ->
+  beforeEach ->
+    #load homeworks and load exercises
+    TaskPlanActions.loaded(VALID_MODEL, VALID_MODEL.id)
+    ExerciseActions.loaded(EXERCISES, courseId, VALID_MODEL.settings.page_ids)
+    TocActions.loaded(READINGS)
+  afterEach ->
+    routerStub.unmount()
+    TaskPlanActions.reset()
+    ExerciseActions.reset()
+
+  it 'should load expected homework at the homework url', (done) ->
+    #loads homework title, and has correct number of exercises
+    tests = ({div}) ->
+      expect(div.querySelector('#homework-title').value).to.equal(VALID_MODEL.title)
+      expect(div.querySelector('#homework-due-date')).to.be.not.null
+      expect(div.querySelector('.-select-problems')).to.be.not.null
+      expect(div.querySelectorAll('.card.exercise').length).to.equal(2)
+      done()
+
+    routerStub.goTo("/courses/#{courseId}/homework/#{VALID_MODEL.id}").then(tests).catch(done)
+
+  it 'should not allow editable due date or add problems button if homework is published', (done) ->
+    PUBLISHED_MODEL = _.mapObject(VALID_MODEL)
+    PUBLISHED_MODEL = _.extend(PUBLISHED_MODEL , {published_at: new Date()})
+
+    TaskPlanActions.loaded(PUBLISHED_MODEL, PUBLISHED_MODEL.id)
+    #set homework to not published
+    #check if due date input exists
+    tests = ({div}) ->
+      expect(div.querySelector('#homework-due-date')).to.be.null
+      expect(div.querySelector('.-select-problems')).to.be.null
+      done()
+
+    routerStub.goTo("/courses/#{courseId}/homework/#{PUBLISHED_MODEL.id}").then(tests).catch(done)
+
+  it 'should remove a problem when remove button is clicked', (done) ->
+    removeExercise = (obj) ->
+      expect(obj.div.querySelectorAll('.exercise.card').length).to.be.equal(VALID_MODEL.settings.exercise_ids.length)
+      commonActions.clickButton(obj.div, '.-remove-exercise')
+      obj
+
+    checkExerciseWasRemoved = (obj) ->
+      expect(obj.div.querySelectorAll('.exercise.card').length).to.be.equal(VALID_MODEL.settings.exercise_ids.length - 1)
+      done()
+
+    routerStub.goTo("/courses/#{courseId}/homework/#{VALID_MODEL.id}")
+      .then(removeExercise)
+      .then(checkExerciseWasRemoved)
+
+  it 'should allow for add exercises if homework is not published', (done) ->
+    ExerciseActions.loaded(EXERCISES, courseId, READINGS[0].children[0].id)
+
+    NO_EXERCISES_MODEL = _.mapObject(VALID_MODEL)
+    NO_EXERCISES_MODEL = _.extend(NO_EXERCISES_MODEL, {settings: {}})
+
+    TaskPlanActions.loaded(NO_EXERCISES_MODEL, NO_EXERCISES_MODEL.id)
+
+    checkAddButtonExists = (obj) ->
+      div = obj.div
+      expect(div.querySelector('.-select-problems')).to.not.be.null
+      commonActions.clickButton(div, '.-select-problems')
+      obj
+
+    #should show select readings once select problems button is clicked
+    selectSections = (obj) ->
+      div = obj.div
+      expect(div.querySelector('.edit-homework').className.indexOf('hide')).to.not.be.equal(-1)
+      expect(div.querySelector('.select-reading-modal')).to.not.be.null
+      expect(div.querySelector('.-section-checkbox')).to.not.be.null
+      commonActions.clickButton(div, 'div.section')
+      obj
+
+    clickShowProblems = (obj) ->
+      commonActions.clickButton(obj.div, '.-show-problems')
+      obj
+
+    #should show select problems once sections are selected and show problems button is clicked
+    checkRenderedExercises = (obj) ->
+      div = obj.div
+      expect(div.querySelector('.exercise.card')).to.not.be.null
+      obj
+      done()
+
+    routerStub.goTo("/courses/#{courseId}/homework/#{VALID_MODEL.id}")
+      .then(checkAddButtonExists)
+      .then(selectSections)
+      .then(clickShowProblems)
+      .then(checkRenderedExercises)
+      .catch(done)
+
+


added socket.io functionality

added socket.io functionality

* callback on req.io.emit
* get, set functions on socket

--- lib/request.coffee
+++ lib/request.coffee
@@ -8,10 +8,16 @@

     broadcast: (event, message) ->
         @socket.broadcast.emit(event, message)
-
-    emit: (event, message) ->
-        @socket.emit(event, message)
-
+
+    emit: (event, message, cb) ->
+        @socket.emit(event, message, cb)
+
+    get: (key, cb) ->
+      @socket.get(key, cb)
+
+    set: (key, val, cb) ->
+      @socket.set(key, val, cb)
+
     room: (room) ->
         new RoomIO(room, @socket)


文字数の二倍を越える場合はメソッド呼び出し前で戻るように変更。 少しだけ速くなるはず。ほんの少しだけ。

--- lib/japanese-wrap-manager.coffee
+++ lib/japanese-wrap-manager.coffee
@@ -52,6 +52,8 @@

     displayBuffer.findWrapColumn = (line, softWrapColumn=@getSoftWrapColumn()) ->
       return unless @isSoftWrapped()
+      # If all characters are full width, the width is twice the length.
+      return unless (line.length * 2) > sotfWrapColumn
       return @japaneseWrapManager.findJapaneseWrapColumn(line, softWrapColumn)

   # restore Display#findWrapColumn()
@@ -65,8 +67,6 @@

   # Japanese Wrap Column
   findJapaneseWrapColumn: (line, sotfWrapColumn) ->
-    # If all characters are full width, the width is twice the length.
-    return unless (line.length * 2) > sotfWrapColumn
     size = 0
     for wrapColumn in [0...line.length]
       if @zeroWidthCharRegexp.test(line[wrapColumn])


Allow different options.rest.bases for the same instance of rest.coffee

--- src/server/rest.coffee
+++ src/server/rest.coffee
@@ -4,7 +4,7 @@

 http = require 'http'
 url  = require 'url'
-nameregex = null
+nameregexes = {}

 send403 = (res, message = 'Forbidden\n') ->
   res.writeHead 403, {'Content-Type': 'text/plain'}
@@ -73,13 +73,13 @@
 #   > matchDocName '/hello_world'
 #   undefined
 matchDocName = (urlString, base) ->
-  if !nameregex?
+  if !nameregexes[base]?
     base ?= ""
     base = base[...-1] if base[base.length - 1] == "/"
-    nameregex = new RegExp("^#{base}\/doc\/(?:([^\/]+?))\/?$", "i")
+    nameregexes[base] = new RegExp("^#{base}\/doc\/(?:([^\/]+?))\/?$", "i")

   urlParts = url.parse urlString
-  parts = urlParts.pathname.match nameregex
+  parts = urlParts.pathname.match nameregexes[base]
   return parts[1] if parts

 # prepare data for createClient. If createClient success, then we pass client


Common js support

Common js support

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


add experiment / tests for `$wye()` (to be counterpart to `$tee()`)

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


add index.coffee

add index.coffee

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

--- index.coffee
+++ index.coffee
@@ -0,0 +1,5 @@
+KDObject = require './src/object'
+
+module.exports = {
+  KDObject
+}


A bootable web-app

A bootable web-app

--- index.coffee
+++ index.coffee
@@ -0,0 +1,27 @@
+path = require 'path'
+
+Express = require 'express'
+Redis = require 'redis'
+
+root = path.resolve(__dirname)
+
+redis = do (->
+  info = Url.parse process.env.REDISTOGO_URL or
+    process.env.REDISCLOUD_URL or
+    process.env.BOXEN_REDIS_URL or
+    'redis://localhost:6379'
+  storage = Redis.createClient(info.port, info.hostname)
+  storage.auth info.auth.split(":")[1] if info.auth
+  return storage
+)
+
+app = Express()
+
+app.set 'port', (process.env.PORT or 5000)
+app.use express.static(path.resolve(root, 'public'))
+
+app.get '/', (request, response) ->
+  response.send "All who's pants?"
+
+app.listen app.get('port'), ->
+  console.log "Your pants running at http://localhost:#{app.get('port')}/"


Implement localStorage-ish interface to embedder

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/fulcrumapp/fulcrum-expressions.git/'


Yellow arrow shouldn't point to disabled levels

--- app/views/play/CampaignView.coffee
+++ app/views/play/CampaignView.coffee
@@ -303,7 +303,7 @@
         for nextLevelOriginal in level.nextLevels
           nextLevel = _.find levels, original: nextLevelOriginal
           dontPointTo = ['lost-viking','kithgard-mastery']
-          if nextLevel and not nextLevel.locked and @levelStatusMap[nextLevel.slug] isnt 'complete' and nextLevel.slug not in dontPointTo and not nextLevel.replayable and (
+          if nextLevel and not nextLevel.locked and not nextLevel.disabled and @levelStatusMap[nextLevel.slug] isnt 'complete' and nextLevel.slug not in dontPointTo and not nextLevel.replayable and (
             me.isPremium() or
             not nextLevel.requiresSubscription or
             nextLevel.slug is 'apocalypse' or


deprecate 3.1.9 migration

deprecate 3.1.9 migration

--- src/steroids/ApplicationConfigUpdater.coffee
+++ src/steroids/ApplicationConfigUpdater.coffee
@@ -23,83 +23,6 @@
       JSON.parse packageJsonContents

     packageJson?.engines?.steroids
-
-  updateTo3_1_9: ->
-    deferred = Q.defer()
-
-    if @validateSteroidsEngineVersion(">=3.1.9")
-      deferred.resolve()
-    else
-      steroidsEngineVersion = @getSteroidsEngineVersion() || "undefined"
-      Help.attention()
-      console.log(
-        """
-        #{chalk.bold("engine.steroids")} was #{chalk.bold(steroidsEngineVersion)} in #{chalk.bold("package.json")}, expected #{chalk.bold(">=3.1.9")}
-
-        This is likely because your project was created with an older version of Steroids CLI. We will
-        now run through a few migration tasks to ensure that your project functions correctly.
-
-        """
-      )
-
-      promptConfirm().then( =>
-        @updateTo3_1_0()
-      ).then( =>
-        @updateTo3_1_4()
-      ).then( =>
-        @ensurePackageJsonExists()
-      ).then( =>
-        @ensureNoBadGruntDeps()
-      ).then( =>
-        @ensureSteroidsEngineIsDefinedWithVersion("3.1.9")
-      ).then( =>
-        Help.SUCCESS()
-        console.log chalk.green("Migration successful, moving on!")
-        deferred.resolve()
-      ).fail (msg)->
-        msg = msg ||
-          """
-          \n#{chalk.bold.red("Migration aborted")}
-          #{chalk.bold.red("=================")}
-
-          Please read through the instructions again!
-
-          """
-        deferred.reject(msg)
-
-    return deferred.promise
-
-  ensurePackageJsonExists: ->
-    deferred = Q.defer()
-
-    console.log("Checking to see if #{chalk.bold("package.json")} exists in project root...")
-
-    if fs.existsSync paths.application.configs.packageJson
-      console.log chalk.green("OK!")
-      deferred.resolve()
-    else
-      console.log(
-        """
-          \n#{chalk.red.bold("Could not find package.json in project root")}
-          #{chalk.red.bold("===========================================")}
-
-          We could not find a #{chalk.bold("package.json")} file in project root. This is required
-          for project npm dependencies and the #{chalk.bold("engines.steroids")} field.
-
-          We will create the file now.
-
-        """
-      )
-
-      promptConfirm().then( ->
-        console.log("\nCreating #{chalk.bold("package.json")} in project root...")
-        fs.writeFileSync paths.application.configs.packageJson, fs.readFileSync(paths.templates.packageJson)
-        console.log("#{chalk.green("OK!")}")
-        deferred.resolve()
-      ).fail ->
-        deferred.reject()
-
-    return deferred.promise

   ensureSteroidsEngineIsDefinedWithVersion: (version)->
     deferred = Q.defer()
@@ -173,64 +96,6 @@

     return deferred.promise

-  ensureNoBadGruntDeps: ->
-    deferred = Q.defer()
-
-    console.log "Checking for erroneous npm dependencies..."
-
-    packageJsonData = fs.readFileSync paths.application.configs.packageJson, 'utf-8'
-    packageJson = JSON.parse(packageJsonData)
-
-    if (packageJson?.devDependencies?["grunt-extend-config"]? ||
-       packageJson?.devDependencies?["grunt-contrib-clean"]? ||
-       packageJson?.devDependencies?["grunt-contrib-concat"]? ||
-       packageJson?.devDependencies?["grunt-contrib-copy"]? ||
-       packageJson?.devDependencies?["grunt-contrib-sass"]? ||
-       packageJson?.devDependencies?["grunt-contrib-coffee"]? ||
-       packageJson?.devDependencies?["grunt"]?)
-
-      Help.error()
-      console.log(
-        """
-        #{chalk.red.bold("Erroneous npm dependencies found")}
-        #{chalk.red.bold("================================")}
-
-        Due to an oversight in our previous migration script, migrated projects' #{chalk.bold("package.json")}
-        files ended up having several #{chalk.bold("devDependencies")} that should not be there.
-
-        The misplaced dependencies cause errors if the user removes his #{chalk.bold("node_modules")} directory
-        and then runs #{chalk.bold("npm install")} afterwards (this is due to #{chalk.bold("grunt-steroids")} using absolute
-        version numbers for its peerDependencies, and npm wanting to use the latest patch
-        version).
-
-        Unless you know what you're doing, ensure that your #{chalk.bold("package.json")} has none of the
-        following #{chalk.bold("devDependencies")} by deleting them from the file:
-
-          "grunt-extend-config"
-          "grunt-contrib-clean"
-          "grunt-contrib-concat"
-          "grunt-contrib-copy"
-          "grunt-contrib-sass"
-          "grunt-contrib-coffee"
-          "grunt"
-
-        Note that you shouldn't remove the #{chalk.bold("grunt-steroids")} dependency, as that's required by
-        Steroids CLI to work!
-
-        """
-      )
-
-      promptUnderstood().then( ->
-        deferred.resolve()
-      ).fail ->
-        deferred.reject()
-
-    else
-      console.log chalk.green("OK!")
-      deferred.resolve()
-
-    return deferred.promise
-
   # Inquirer utils

   promptConfirm = ->


add slugifier as helper

add slugifier as helper

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


Add pattern

Add pattern

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/nunulk/robo-blanca.git/'


Client: Activity: Test: Created CommentListItemContainer component

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

--- client/activity/lib/components/commentlistitem/container.coffee
+++ client/activity/lib/components/commentlistitem/container.coffee
@@ -0,0 +1,77 @@
+kd           = require 'kd'
+View         = require './view'
+React        = require 'kd-react'
+immutable    = require 'immutable'
+ActivityFlux = require 'activity/flux'
+
+module.exports = class CommentListItemContainer extends React.Component
+
+  @propTypes =
+    channelId      : React.PropTypes.string
+    onMentionClick : React.PropTypes.func.isRequired
+    comment        : React.PropTypes.instanceOf immutable.Map
+
+
+  @defaultProps =
+    channelId      : ''
+    comment        : immutable.Map()
+
+
+  constructor: (props) ->
+
+    super
+
+    @state =
+      hasValue     : yes
+      commentValue : @props.comment.get('body') or ''
+      focusOnInput : no
+
+
+  onClick: (event) ->
+
+    kd.utils.stopDOMEvent event
+
+    { onMentionClick, comment } = @props
+
+    onMentionClick comment
+
+
+  updateComment: ->
+
+    commentId = @props.comment.get '_id'
+    { message } = ActivityFlux.actions
+
+    message.unsetMessageEditMode commentId, @props.channelId
+    message.editMessage commentId, @state.commentValue.trim()
+
+
+  cancelEdit: ->
+
+    { comment, channelId } = @props
+    { message } = ActivityFlux.actions
+
+    @setState { commentValue: comment.get 'body' }
+
+    message.unsetMessageEditMode comment.get('_id'), channelId
+
+
+  onChange: (event) ->
+
+    hasValue = no
+    value    = event.target.value
+    hasValue = yes  if value.trim()
+    @setState { hasValue, commentValue: value }
+
+
+  render: ->
+
+    <View
+      ref           = 'view'
+      comment       = { @props.comment }
+      hasValue      = { @state.hasValue }
+      onClick       = { @bound 'onClick' }
+      onChange      = { @bound 'onChange' }
+      cancelEdit    = { @bound 'cancelEdit' }
+      commentValue  = { @state.commentValue }
+      updateComment = { @bound 'updateComment' } />
+


Add refresh timer to periodically update guild info

remote: Repository unavailable due to DMCA takedown.
remote: See the takedown notice for more details:
remote:   https://github.com/github/dmca/blob/master/2018/2018-09-28-TeraPC.md.
fatal: unable to access 'https://github.com/meishuu/tera-discord-relay.git/': The requested URL returned error: 403


Refactoring

Refactoring

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


ADDED config for angular

ADDED config for angular

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

--- app/assets/javascripts/angular/http.js.coffee
+++ app/assets/javascripts/angular/http.js.coffee
@@ -0,0 +1,3 @@
+Grasshopper.config ["$httpProvider", ($httpProvider) ->
+  $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
+  $httpProvider.defaults.header.common.Accept = "application/json"


Fixed coffeescript for work with Turbolinks

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/vas-and-tor/gibdd_rails_app.git/'


Debug getArchives function

Debug getArchives function

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/GregDesplaces/classe-1914.git/'


Fixed typos in translations.

Fixed typos in translations.

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


Terminal: refactored reconnection

Terminal: refactored reconnection

--- client/Terminal/webtermview.coffee
+++ client/Terminal/webtermview.coffee
@@ -190,22 +190,27 @@

   attemptToReconnect: ->
     return  if @reconnected
-    @reconnectingNotification ?= new KDNotificationView
-      type      : "mini"
+    @getDelegate().notify
+      cssClass  : "error"
       title     : "Trying to reconnect your Terminal"
       duration  : 2 * 60 * 1000 # 2 mins
-      container : @container
-
-    vmController = KD.getSingleton "vmController"
+
     hasResponse  = no

-    vmController.info (@getOption 'vmName') or @getDelegate().getOption("vmName"), (err, res) =>
+    {vm} = @getOptions()
+    {hostnameAlias, region} = vm
+
+    kite = KD.getSingleton("vmController").terminalKites[hostnameAlias]
+    kite?.webtermGetSessions().then (sessions) =>
       hasResponse = yes
       return if @reconnected
       @handleReconnect()
-      @clearConnectionAttempts()
-
-    @utils.wait 500, => @reconnectAttemptFailed() unless hasResponse
+
+    @utils.wait 500, =>
+      unless hasResponse
+        # TODO resolve this workaround
+        serviceGenericName = "terminal-kite-terminal-#{region}"
+        @reconnectAttemptFailed serviceGenericName, hostnameAlias

   clearConnectionAttempts: ->
     @clearBackoffTimeout()
@@ -215,20 +220,14 @@

     @clearConnectionAttempts()
     options =
-      session  : @sessionId
-      joinUser : KD.nick()
-
-    @reinitializeWebTerm options
-    @reconnectingNotification?.destroy()
+      session : @sessionId
+      vm      : @getOption('vm')
+
+    @emit "WebTermNeedsToBeRecovered", options
     @reconnected = yes
-
-  reinitializeWebTerm: (options = {}) ->
-    return  if @reconnected
-    @emit "WebTermNeedsToBeRecovered", options

   handleConnectionFailure: ->
     return if @failedToReconnect
-    @reconnectingNotification?.destroy()
     @reconnected       = no
     @failedToReconnect = yes
     @clearConnectionAttempts()


[Prefetch] Fixed MR comments

[Prefetch] Fixed MR comments

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


Add test placeholder.

Add test placeholder.

error: pathspec 'test/mocha-http-server.coffee' did not match any file(s) known to git

--- test/mocha-http-server.coffee
+++ test/mocha-http-server.coffee
@@ -0,0 +1,2 @@
+describe 'mocha-http-server', ->
+  it 'should run mocha tests', ->


Current version of anno.coffee

Current version of anno.coffee

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

--- anno.coffee
+++ anno.coffee
@@ -0,0 +1,587 @@
+
+# Anno.js
+# =======
+# Powerful step-by-step guides, interactive tutorials or just plain ol' annotations.
+#
+# Anno.js is built to be absurdly extensible, but still works great out of the box (and looks damn fine doing it).
+
+
+# The MIT License (MIT)
+# ---------------------
+#
+# Copyright (c) 2013 Dan Fox
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+# the Software, and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+class Anno
+  @version: '1.4.6'
+# @version: BreakingAPIChange.NewCompatibleFeatures.Bugfix
+
+
+# Constructing an Anno object
+# ---------------------------
+#
+# An Anno object represents a single annotation on your page.  Each Anno has a `target` jQuery selector
+# and a `content` string. For example:
+#
+#     pizzaAnno = new Anno({
+#       target: '.pizza-list',
+#       content: 'Choose your pizza from the list below.'
+#     })
+#     pizzaAnno.show()
+#
+# This creates a plain Anno object and overrides the `target` and `content` properties.  It uses defaults
+# for everything else.
+
+  constructor: (options) ->
+    if options instanceof Anno
+      console.warn 'Anno constructor parameter is already an Anno object.'
+    if not options?
+      console.warn "new Anno() created with no options.  It's recommended to supply at least target and content."
+    for key,val of options when key in ['chainTo', 'start', 'show', 'hide', 'hideAnno', 'chainSize', 'chainIndex', 'version']
+      console.warn "Anno: Overriding '#{key}' is not recommended.  Can you override a delegated function instead?"
+
+    for key,val of options
+      this[key]=val
+
+# Note, you can customize *anything* in your Anno object by overriding the method or property when you construct it.
+# In addition to `target` and `content`, I usually like to specify `buttons` and `position`.  You might
+# also like to try specifying `onShow` or `onHide` callbacks, `className` or maybe `overlayElem()`.
+#
+# Read on to find out more.
+
+# Making step-by-step tours using chaining
+# ----------------------------------------
+#
+# Naturally, you can chain Anno objects together to make a sequential tour. For example:
+#
+#     deliveryAnno = new Anno({
+#       target: '#address-form',
+#       content: "Enter your address and we'll deliver your pizza"
+#       position: 'left'
+#     })
+#     pizzaAnno.chainTo(deliveryAnno)
+#
+# The `pizzaAnno` object now has a `_chainNext` property and will switch to the
+# `deliveryAnno` when you click Next.
+
+  chainTo: (obj) ->
+    if obj?
+      if not @_chainNext? # this is the end of the chain, add obj to the end.
+        @_chainNext = if obj instanceof Anno then obj else new Anno(obj)
+        @_chainNext._chainPrev = this
+      else # pass the obj further along
+        @_chainNext.chainTo(obj)
+    else
+      console.error "Can't chainTo a null object."
+    return this
+
+# Anything that starts with an underscore is probably a bad idea to change. By all means read
+# these, but don't come crying to me if you overwrote something and your entire website blew up.
+
+  _chainNext: null
+  _chainPrev: null
+
+# You can also make long Anno chains without having to write `chainTo` every time using
+# `Anno.chain()`. We can rewrite the two step example above like this:
+#
+#    annoTour = Anno.chain([
+#      {
+#        target: '.pizza-list',
+#        content: 'Choose your pizza from the list below.'
+#      },
+#      {
+#        target: '#address-form',
+#        content: "Enter your address and we'll deliver your pizza"
+#        position: 'left'
+#      }
+#    ])
+#
+# Note, the `annoTour` variable still only points to the single Anno object (for `.pizza-list`),
+# we've just chained another one onto it anonymously.
+
+  @chain: (array) ->
+    head = new Anno( array.shift() )
+    if array.length >= 1 # array is now the tail.
+      head.chainTo( Anno.chain(array) )
+    return head
+
+  chainSize: () ->
+    if @_chainNext? then @_chainNext.chainSize() else 1+@chainIndex()
+
+  chainIndex: (index) ->
+    # anno.chainIndex(x) gets the xth object in the chain
+    if index?
+      (find = (curr, i, u) ->
+        if curr?
+          ci = curr.chainIndex()
+          if      0 <= ci < i  then find(curr._chainNext, i, u)
+          else if i <  ci <= u then find(curr._chainPrev, i, u)
+          else if   ci is i    then curr
+        else console.error "Couldn't switch to index '#{i}'. Chain size is '#{u}'"
+      )(this, index, @chainSize())
+    # anno.chainIndex() gets the current index;
+    else
+      if @_chainPrev? then 1+@_chainPrev.chainIndex() else 0
+
+
+# If you find yourself setting the same property on every Anno object you create, you can
+# set default values at the top of your script that will apply to every Anno object from then onwards.
+
+  @setDefaults: (options) ->
+    for key,val of options
+      Anno::[key] = val
+
+
+
+# When you call `anno.show()`, your annotation is displayed on top of a nice overlay and a callback is executed.
+# All of the methods used here can be overridden in the same way we changed the `content` property.  This makes
+# Anno.js really powerful.  You can configure it and replace all kinds of behaviour without resorting to nasty hacks.
+#
+# Animations are all done with 300ms CSS transitions, so you can change your UI without touching any javascript.
+
+  start: () -> @show()
+
+  show: () ->
+    # TODO warn if this Anno has already been shown.
+    $target = @targetFn()
+    if @_annoElem? then console.warn "Anno elem for '#{@target}' has already been generated.  Did you call show() twice?"
+    @_annoElem = @annoElem()
+    lastButton = @_annoElem.find('button').last()
+
+    @showOverlay()
+    @emphasiseTarget()
+
+    $target.after(@_annoElem) # TODO: be more intelligent.
+    setTimeout (() => @_annoElem.removeClass('anno-hidden')), 10 # hack to make Chrome render the opacity:0 state.
+
+    @positionAnnoElem()
+    @positionArrow(@_annoElem.find('.anno-arrow').first())
+
+    $target.scrollintoview()
+    setTimeout (() => @_annoElem.scrollintoview()) , 300
+
+    if @rightArrowClicksLastButton
+      lastButton.keydown( (evt) -> if evt.keyCode is 39 then $(this).click()  ) # right arrow
+    if @autoFocusLastButton
+      lastButton.focus() if $target.find(':focus').length is 0 # don't steal focus from inside target element
+
+    @_returnFromOnShow = @onShow(this, $target, @_annoElem)
+    return this
+
+  rightArrowClicksLastButton: true
+  autoFocusLastButton: true
+
+# The onShow callback is incredibly useful.  By default, it does nothing, but you can override it to set
+# up click listeners, adjust your page html and generally provide lots of interactivity.
+#
+# The most common use of this method is to register a click listener on the target element.  Whatever
+# value you return from the `onShow` function will get passed to the `onHide` callback.  You can use this
+# to unbind event listeners.
+#
+# Both `target` and `annoElem` are already jQuery objects
+
+  onShow: (anno, target, annoElem) -> # TODO: replace target with $target.
+
+  _returnFromOnShow = null
+
+# Hiding is done in two functions so that you can re-use one overlay for a long chain of Anno's.
+
+  hide: () ->
+    @hideAnno()
+    @hideOverlay()
+    return this
+
+# `hideAnno()` hides the Anno element and restores the `target` element, leaving the overlay behind.
+# It also calls the `onHide` listener and passes in whatever value `onShow` returned as the fourth argument.
+
+  hideAnno: () ->
+    @deemphasiseTarget()
+
+    if @_annoElem?
+      @_annoElem.addClass('anno-hidden')
+      setTimeout () =>
+        @_annoElem.remove() # this method causes hideAnno to get called twice sometimes -> bad.
+        @_annoElem = null
+      , 300
+
+      @onHide(this, @targetFn(), @_annoElem, @_returnFromOnShow)
+    else
+      console.warn "Can't hideAnno() for '#{@target}' when @_annoElem is null.  Did you call hideAnno() twice?"
+
+    return this
+
+  onHide: (anno, target, annoElem, returnFromOnShow) ->
+
+# `switchTo` hides the current Anno and displays the next one in the chain without animating out the old overlay.
+# Note. `otherAnno.show()` will probably replace the overlay, but it won't do a weird fade flicker.
+
+  switchTo: (otherAnno) ->
+    if otherAnno?
+      @hideAnno() # This may call hideAnno when it isnt shown
+      otherAnno.show()
+    else
+      console.warn "Can't switchTo a null object. Hiding completely instead. "
+      @hide() # end of the line, need to remove the overlay too
+
+  switchToChainNext: () -> @switchTo @_chainNext
+
+  switchToChainPrev: () -> @switchTo @_chainPrev
+
+
+# `targetFn()` is used to access the DOM element that you want to annotate (as a jQuery object).
+# It will try to make sense of whatever you put in the `target` property, so you'll probably never
+# need to override this function.
+
+  targetFn: () ->
+    if typeof @target is 'string'
+      r = $(@target).filter(':not(.anno-placeholder)')
+      if r.length is 0 then console.error "Couldn't find Anno.target '#{@target}'."
+      if r.length > 1 then console.warn "Anno target '#{@target}' matched #{r.length} elements. Targeting the first one."
+      r.first()
+    else if @target instanceof jQuery
+      if @target.length > 1 then console.warn "Anno jQuery target matched #{@target.length} elements. Targeting the first one."
+      return @target.first()
+    else if @target instanceof HTMLElement
+      $(@target)
+    else if typeof @target is 'function'
+      @target()
+    else
+      console.error "Unrecognised Anno.target. Please supply a jQuery selector string, a jQuery "+
+          "object, a raw DOM element or a function returning a jQuery element. target:"
+      console.error @target
+
+  target: 'h1'
+
+
+# `annoElem()` generates the jQuery object that will be inserted into the DOM.  It relies on
+# `@contentElem()` and `buttonsElem()` to generate the interesting bits.
+#
+# This method is ripe for overriding. Just copy the code below and tinker with the HTML. For example
+# you could add some extra `div`s to display the current step number (by calling `chainIndex() + 1`).
+
+  annoElem: () ->
+    @_annoElem = $("""

+

+
""")
+    @_annoElem.find('.anno-inner').
+      append( @contentElem() ).
+      append( @buttonsElem() ) # these a jquery elements, not HTML strings.
+    return @_annoElem # NB: returning the original pointer each time breaks button click events...
+
+# TODO: evaluate how easy it would be to change Anno content while its displayed.
+
+  _annoElem: null
+
+  className: '' # TODO useful classes .anno-width-150, 175, 200, 250 (default 300)
+
+# `contentElem()` is called by `annoElem()` to produce a jQuery object.
+
+  contentElem: () -> $("
"+@contentFn()+"
")
+
+# Most of the time it will suffice to override the `content` property when you construct your Anno.
+# However, if you want to generate the content when `showAnno()` is called (perhaps the text you display
+# depends on an earlier step) you can override `contentFn()` instead.
+
+  contentFn: () -> @content
+  content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
+
+
+# `buttonsElem()` produces the HTML for all those buttons (as a jQuery object).  Just like `contentElem()`
+# above, it delegates to `buttonFn()`.
+
+  buttonsElem: () ->
+    return $("

").
+      append (b.buttonElem(this) for b in @buttonsFn())
+
+# `buttonsFn()` returns a list of `AnnoButton` objects based on whatever you put in the `buttons` parameter.
+
+  buttonsFn: () ->
+    if @buttons instanceof Array
+      @buttons.map (b) -> new AnnoButton(b)
+    else
+      [new AnnoButton(@buttons)]
+
+# By default, Anno will construct a single button for you, using defaults provided by the `AnnoButton` class.
+# You can supply a single object, a list of objects or even a list of `AnnoButtons`.
+
+  buttons:  [ {} ]
+
+
+
+
+
+# TODO: write about pointer-events: none
+
+  showOverlay: () ->
+    if $('.anno-overlay').length is 0
+      $('body').append(e = @overlayElem().addClass 'anno-hidden')
+      setTimeout (() -> e.removeClass 'anno-hidden'), 10
+    else
+      # TODO try to mutate classNames & listeners rather than replacing the DOM node -> smooth animation
+      $('.anno-overlay').replaceWith @overlayElem()
+
+  overlayElem: () ->
+    $("

").
+      click( (evt) => @overlayClick.call(this, this, evt) )
+
+  overlayClassName: '' # TODO talk about .anno-hidden
+  overlayClick: (anno, evt) -> anno.hide()
+
+  hideOverlay: () ->
+    $('.anno-overlay').addClass 'anno-hidden'
+    setTimeout (() -> $('.anno-overlay').remove()), 300
+
+
+
+
+
+  emphasiseTarget: ($target = @targetFn()) ->
+    if $target.attr('style')? then _oldTargetCSS = $target.attr('style')
+
+    # TODO: register & remove a specific listener ... would this ruin existing jQuery scroll functions?
+    $target.closest(':scrollable').on 'mousewheel', (evt) ->
+      evt.preventDefault()
+      evt.stopPropagation()
+
+    if $target.css('position')  is 'static'
+      $target.after(@_placeholder = $target.clone().addClass('anno-placeholder')) # ensures that the jquery :first selector in targetFn works.
+      $target.css( position:'absolute' )
+
+      # if switching to position absolute has caused a dimension collapse, manually set H/W.
+      if $target.outerWidth() < @_placeholder.outerWidth()
+        $target.css('width', @_placeholder.outerWidth())
+      if $target.outerHeight() < @_placeholder.outerHeight()
+        $target.css('height', @_placeholder.outerHeight())
+
+    if $target.css('background') is 'rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box'
+      $target.css( background: 'white')
+
+    $target.css( zIndex:'1001' )
+
+    return $target
+
+  _oldTargetCSS: ''
+  _placeholder: null
+
+  deemphasiseTarget: () ->
+    @_placeholder?.remove()
+    $target = @targetFn()
+    $target.closest(':scrollable').off('mousewheel')
+    return $target.attr('style',@_oldTargetCSS)
+
+
+
+# `positionAnnoElem()` sets the CSS of the Anno element so that it appears next to your target in a sensible way.
+# It positions the Anno element based on a string from `positionFn()`. It can also use a hash containing any
+# of the properties `top`, `left`, `right` or `bottom`.
+
+  positionAnnoElem: (annoEl = @_annoElem) ->
+    pos = @positionFn()
+
+    $targetEl = @targetFn()
+    offset = $targetEl.position()
+    switch pos
+      when 'top', 'bottom'
+        annoEl.css(left: offset.left+'px')
+      when 'center-top', 'center-bottom'
+        annoEl.css(left: offset.left+($targetEl.outerWidth()/2 - annoEl.outerWidth()/2)+'px')
+      when 'left', 'right'
+        annoEl.css(top: offset.top+'px')
+      when 'center-left', 'center-right'
+        annoEl.css(top: offset.top+($targetEl.outerHeight()/2 - annoEl.outerHeight()/2)+'px')
+
+    switch pos
+      when 'top', 'center-top'
+        annoEl.css( top: offset.top-annoEl.outerHeight()+'px')
+      when 'bottom', 'center-bottom'
+        annoEl.css( top: offset.top+$targetEl.outerHeight()+'px')
+      when 'left', 'center-left'
+        annoEl.css(left: offset.left-annoEl.outerWidth()+'px')
+      when 'right', 'center-right'
+        annoEl.css(left: offset.left+$targetEl.outerWidth()+'px')
+      else
+        if pos.left? or pos.right? or pos.top? or pos.bottom?
+         annoEl.css(pos)
+        else
+          console.error "Unrecognised position: '#{pos}'"
+
+    return annoEl
+
+# `positionFn()` simply returns whatever you put in the `position` property (see below for options).
+# If you left it blank, `positionFn()` will try to guess which side of your target you would like your
+# annotation to be displayed on (based on `Anno.preferredPositions`).
+
+  positionFn: () ->
+    if @position?
+      @position
+    else if @_annoElem?
+      # time to guess!!
+      $target = @targetFn()
+
+      $container = $target.closest(':scrollable')
+      $container = $('body') if $container.length is 0
+
+      targetOffset = $target.offset()         # both relative to document
+      containerOffset = $container.offset()
+      targetBounds =
+        left: targetOffset.left - containerOffset.left
+        top:  targetOffset.top - containerOffset.top
+      targetBounds.right = targetBounds.left + $target.outerWidth() # dist from left edge of $container to right of elem
+      targetBounds.bottom = targetBounds.top + $target.outerHeight()
+
+      viewBounds =
+        w: $container.width() or $container.width()
+        h: $container.height() or $container.height()
+
+      annoBounds =
+        w: @_annoElem.outerWidth()
+        h: @_annoElem.outerHeight()
+
+      bad = []
+
+      if annoBounds.w > targetBounds.left then bad = bad.concat ['left', 'center-left']
+      if annoBounds.h > targetBounds.top  then bad = bad.concat ['top', 'center-top']
+      if annoBounds.w + targetBounds.right  > viewBounds.w  then bad = bad.concat ['right', 'center-right']
+      if annoBounds.h + targetBounds.bottom > viewBounds.h then bad = bad.concat ['bottom', 'center-bottom']
+
+      allowed = Anno.preferredPositions.filter (p) -> p not in bad
+      if allowed.length is 0
+        console.error "Anno couldn't guess a position for '#{@target}'. Please supply one in the constructor."
+      else
+        console.warn "Anno: guessing position:'#{allowed[0]}' for '#{@target}'. "+
+          "Possible Anno.preferredPositions: [#{allowed}]."
+      @position = allowed[0] # store this value for later - saves recomputing.
+
+# When there are several different positions that the Anno element could by displayed, `positionFn()` chooses
+# the first one available in `Anno.preferredPositions`.  Feel free to override this if you like your annotations
+# to appear on top by default.
+
+  @preferredPositions = ['bottom', 'right', 'left', 'top',
+          'center-bottom', 'center-right', 'center-left', 'center-top'] # TODO order these based on research.
+
+# The `position` property decides where your annotation will be displayed. You should supply
+# any of `top`, `left`, `bottom`, `right`, `center-top`, `center-left`, `center-bottom` or `center-right`.
+#
+# Alternatively, you can supply a hash of CSS attributes to set. (e.g. `{ top: '10px', left: '57px' }`). This
+# is useful if you have a large `target` element and you want to point the arrow at something specific.
+#
+# You may omit the `position` attribute entirely and Anno will use its best guess, however, this is not recommended
+# (and you'll get a warning on the console as punishment).
+
+  position: null
+
+
+
+# `positionArrow()` ensures that the little pointy arrow is actually aiming at your `target` element. It does this
+# based on a string it gets from `arrowPositionFn()`.
+
+  positionArrow: ($arrowElem = @_annoElem.find('.anno-arrow').first()) ->
+    $arrowElem.addClass('anno-arrow-'+@arrowPositionFn()) # enables some different CSS
+
+# 90% of the time, `arrowPositionFn()` will just make sure that your arrow is appears closest to your `target`
+# element by returning the opposite string to whatever `positionFn()` sent.  (If your annotation appears on the
+# left hand side of your target, it'll tell the arrow to point to the right.)
+#
+# If you manually positioned your annoElem (by supplying CSS `left` and `top` attributes), `arrowPositionFn()`
+# will attempt to guess which way you want to the arrow to point.  If you'd rather not leave it to chance,
+# simply override the `arrowPosition` when you construct your Anno object.
+
+  arrowPositionFn: () ->
+    if @arrowPosition?
+      return @arrowPosition
+    else if typeof @positionFn() is 'string'
+      return {
+        'top': 'bottom'
+        'center-top': 'center-bottom'
+        'left': 'right'
+        'center-left' : 'center-right'
+        'right' : 'left'
+        'center-right' : 'center-left'
+        'bottom': 'top'
+        'center-bottom' : 'center-top'
+      }[@positionFn()]
+    else
+      # guess! # TODO: base this of distance to target.
+      pos = l : parseInt(@positionFn().left, 10) ,  t : parseInt(@positionFn().top, 10)
+      if Math.abs(pos.l) > Math.abs(pos.t)
+        r = if pos.l < 0 then 'center-right' else 'center-left'
+      else
+        r = if pos.t < 0 then 'center-bottom' else 'center-top'
+      console.warn "Guessing arrowPosition='#{r}' for #{@target}. Include this in your constructor for consistency."
+      return r
+
+# `arrowPosition` definitively decides which direction you want the arrow to point.  The only reason you should
+# ever need to override this is if you've supplied a CSS hash as the `position` property.
+
+  arrowPosition: null # TODO replace 'arrowPosition' with 'arrowDirection'
+
+
+
+
+
+
+
+
+
+class AnnoButton
+  @version: '1.1.0'
+
+  constructor: (options) ->
+    for key,val of options
+      this[key]=val
+
+
+  buttonElem: (anno) ->
+    return $("<button class='anno-btn'></button>").
+      html( @textFn(anno) ).
+      addClass( @className ).
+      click( (evt) => @click.call(anno, anno, evt) )
+
+  textFn: (anno) ->
+    if @text? then @text
+    else if anno._chainNext? then 'Next' else 'Done'
+
+  text: null
+
+  className: ''
+
+# `click` is called when your button is clicked.  Note, the `this` keyword is bound to the parent
+# Anno object.  If you really want to access `AnnoButton` properties, you could always use CoffeeScript's
+# fat arrow.
+
+  click: (anno, evt) ->
+    if anno._chainNext?
+      anno.switchToChainNext()
+    else
+      anno.hide()
+
+# These are some handy presets that you can use by adding `AnnoButton.NextButton` to your Anno object's
+# `buttons` list.
+
+  @NextButton: new AnnoButton({ text: 'Next' , click: () -> @switchToChainNext()  })
+
+  @DoneButton: new AnnoButton({ text: 'Done' , click: () -> @hide()  })
+
+  @BackButton: new AnnoButton(
+      text: 'Back'
+      className: 'anno-btn-low-importance'
+      click: () -> @switchToChainPrev()
+    )


Added periodical check

Added periodical check

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/goofy-bz/kresus.git/'


keep versions in sync fixes #18

keep versions in sync fixes #18

--- server.coffee
+++ server.coffee
@@ -5,7 +5,7 @@
 QueryString = require 'querystring'

 port            = parseInt process.env.PORT        || 8081
-version         = "0.5.0"
+version         = "1.0.2"
 excluded        = process.env.CAMO_HOST_EXCLUSIONS || '*.example.org'
 shared_key      = process.env.CAMO_KEY             || '0x24FEEDFACEDEADBEEFCAFE'
 max_redirects   = process.env.CAMO_MAX_REDIRECTS   || 4


Copy-pasta a test

Copy-pasta a test

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

--- tests/poo-tracker_test.coffee
+++ tests/poo-tracker_test.coffee
@@ -0,0 +1,14 @@
+assert = require 'assert'
+
+Helper = require('hubot-test-helper')
+helper = new Helper('../scripts/poo-tracker.coffee')
+
+describe 'poo-tracker', ->
+  room = null
+
+  beforeEach ->
+    room = helper.createRoom()
+    room.user.say 'shithead', '@hubot hello, this is dog'
+
+  it 'compiled if it got this fucking far', ->
+    assert room.messages.length > 0


starting to work on simulating the effects of cards

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

--- simulate/cards.coffee
+++ simulate/cards.coffee
@@ -0,0 +1,334 @@
+# Cards here are built in an *almost* object-oriented way. Almost, because each
+# card is a singleton value, not a class. There are no instances of cards,
+# there are just cards.
+#
+# Therefore, they "inherit" from each other by copying their properties, using
+# the cardFrom function. There isn't even a need for Javascript's prototype
+# system here.
+
+makeCard = (name, origCard, props) ->
+  # Derive a card from an existing one.
+  newCard = {}
+  for key, value of origCard
+    newCard[key] = value
+  newCard.name = name
+  for key, value of props
+    newCard[key] = value
+  newCard.parent = origCard.name   # for debugging
+  newCard
+
+anycard = {
+  isAction: false
+  isTreasure: false
+  isVictory: false
+  isAttack: false
+  isReaction: false
+  isDuration: false
+  isPrize: false
+
+  # The _base cost_ of a card is defined here. To find out what a card
+  # *actually* costs, use the getCost() method. In most cases, the cost can
+  # be set directly through the "cost" attribute, but it should never be
+  # accessed that way.
+  cost: 0
+  costPotion: 0
+  costInCoins: (state) -> this.cost
+  costInPotions: (state) -> this.costPotion
+
+  getCost: (state) ->
+    coins = this.costInCoins()
+    coins -= state.bridges
+    if this.isAction
+      coins -= state.quarries * 2
+    if coins < 0
+      coins = 0
+    return [coins, this.costInPotions()]
+
+  # There are two kinds of methods of a card. Those that can be statically
+  # evaluated based on a state take a single argument, the state.
+  #
+  # Anything that might require making a decision -- such as the effect of
+  # a card -- takes two arguments: the state, and "ret", a continuation
+  # function that will be passed the return value.
+  #
+  # When evaluating different hypothetical decisions to make, remember:
+  #   - The state should have unknown information (no cheating!)
+  #   - Use a fresh copy of the state each time, because actions are allowed
+  #     to mutate states for efficiency.
+
+  actions: 0
+  cards: 0
+  coins: 0
+  buys: 0
+  vp: 0
+
+  getActions: (state) -> this.actions
+  getCards: (state) -> this.cards
+  getCoins: (state) -> this.coins
+  getBuys: (state) -> this.buys
+  getVP: (state) -> this.vp
+  mayBeBought: (state) -> true
+  gainEffects: []
+  playEffects: []
+  gainInPlayEffects: []
+  cleanupEffects: []
+  durationEffects: []
+  shuffleEffects: []
+  attackReactions: []
+  gainReactions: []
+
+  onPlay: (state, ret) ->
+    state.current.actions += this.getActions()
+    state.current.coins += this.getCoins()
+    state.current.buys += this.getBuys()
+    cardsToDraw = this.getCards()
+    if cardsToDraw > 0
+      state.current.drawCards
+        cardsToDraw,
+        (newState) -> this.playEffectLoop(newState, ret)
+    else
+      this.playEffectLoop(state, ret)
+
+  playEffectLoop: (state, ret) ->
+    this.playEffectLoopInner(state, this.playEffects, ret)
+
+  playEffectLoopInner: (state, effects, ret) ->
+    if effects.length == 0
+      ret(state)
+    else
+      nextEffect = effects[0]
+      remainingEffects = effects[1...effects.length]
+      nextEffect(
+        state,
+        (newState) -> playEffectLoopInner(newState, remainingEffects, ret)
+      )
+
+  toString: () -> this.name
+}
+
+###
+BASE CARDS
+
+Estate and Silver are the prototypes that other Victory and Treasure cards
+derive from, respectively. (Copper is actually more complex than Silver!)
+###
+
+Curse = makeCard 'Curse', basicCard, {
+  cost: 0
+  vp: -1
+}
+
+Estate = makeCard 'Estate', basicCard, {
+  cost: 2
+  isVictory: true
+  vp: 1
+}
+
+Duchy = makeCard 'Duchy', Estate, {cost: 5, vp: 3}
+Province = makeCard 'Province', Estate, {cost: 8, vp: 6}
+Colony = makeCard 'Colony', Estate, {cost: 11, vp: 10}
+
+Silver = makeCard 'Silver', basicCard, {
+  cost: 3
+  isTreasure: true
+  coins: 2
+}
+
+Copper = makeCard 'Copper', Silver, {
+  cost: 0
+  coins: 1
+  getCoins: (state) -> state.copperValue ? 1
+}
+
+Gold = makeCard 'Gold', Silver, {cost: 6, coins: 3}
+Platinum = makeCard 'Platinum', Silver, {cost: 9, coins: 5}
+Potion = makeCard 'Potion', Silver, {
+  cost: 4
+  coins: 0
+  getPotion: (state) -> 1
+}
+
+# And one that isn't a base card, but is easily described in terms of them:
+Harem = makeCard 'Harem', Silver, {
+  cost: 6
+  isVictory: true
+  getVP: (state) -> 2
+}
+
+###
+VANILLA CARDS
+
+These cards have effects that involve no decisions, and are expressed entirely
+in +actions, +cards, +coins, +buys, and VP.
+###
+
+Village = makeCard 'Village', basicCard, {actions: 2, cards: 1}
+WorkersVillage = makeCard "Worker's Village", basicCard, {
+  cost: 4
+  actions: 2
+  cards: 1
+  buys: 1
+}
+Laboratory = makeCard 'Laboratory', basicCard, {cost: 5, actions: 1, cards: 2}
+Smithy = makeCard 'Smithy', basicCard, {cost: 4, cards: 3}
+Festival = makeCard 'Festival', basicCard, {cost: 5, actions: 2, coins: 2}
+Woodcutter = makeCard 'Woodcutter', basicCard, {cost: 3, coins: 2, buys: 1}
+GreatHall = makeCard 'Great Hall', basicCard, {
+  cost: 3, actions: 1, cards: 1, vp: 1, isVictory: true
+}
+Market = makeCard 'Market', basicCard, {
+  cost: 5, actions: 1, cards: 1, coins: 1, buys: 1
+}
+Bazaar = makeCard 'Bazaar', basicCard, {
+  cost: 5, actions: 2, cards: 1, coins: 1
+}
+
+###
+Not-quite-vanilla cards that still involve no mid-card decisions.
+###
+Bank = makeCard 'Bank', Silver, {
+  cost: 7
+  getCoins: (state) ->
+    coins = 0
+    for card in state.current.inPlay
+      if card.isTreasure
+        coins += 1
+    coins
+}
+
+Bridge = makeCard 'Bridge', basicCard, {
+  cost: 4
+  coins: 1
+  buys: 1
+  playEffects: [
+    (state, ret) ->
+      state.bridges += 1
+      ret(state)
+  ]
+}
+
+Coppersmith = makeCard 'Coppersmith', basicCard, {
+  cost: 4
+  playEffects: [
+    (state, ret) ->
+      state.copperValue += 1
+      ret(state)
+  ]
+}
+
+Diadem = makeCard 'Diadem', Silver, {
+  cost: 0
+  isPrize: True
+  getCoins: (state) -> 2 + state.current.actions
+}
+
+Duke = makeCard "Duke", Estate, {
+  cost: 5
+  getVP: (state) ->
+    vp = 0
+    for card in state.current.getDeck()
+      if card is Duchy
+        vp += 1
+    vp
+}
+
+Gardens = makeCard "Gardens", Estate, {
+  cost: 4
+  getVP: (state) -> Math.floor(state.current.getDeck().length / 10)
+}
+
+GrandMarket = makeCard "Grand Market", Market, {
+  cost: 6
+  coins: 2
+  mayBeBought: (state) ->
+    for card in state.current.inPlay
+      if card is Copper
+        return false
+    true
+}
+
+Menagerie = makeCard "Menagerie", basicCard, {
+  cost: 3
+  actions: 1
+  playEffects: [
+    (state, ret) -> state.current.revealHand(ret),
+    (state, ret) ->
+      seen = {}
+      cardsToDraw = 3
+      for card in state.current.hand
+        if seen[card.name]?
+          cardsToDraw = 1
+          break
+        seen[card.name] = True
+      state.current.drawCards(1, ret)
+}
+
+Monument = makeCard "Monument", basicCard, {
+  cost: 4
+  coins: 2
+  playEffects: [
+    (state, ret) ->
+      state.current.chips += 1
+      ret(state)
+  ]
+}
+
+Peddler = makeCard 'Peddler', basicCard, {
+  cost: 8
+  actions: 1
+  cards: 1
+  coins: 1
+  costInCoins: (state) ->
+    cost = 8
+    if state.phase is 'buy'
+      for card in state.current.inPlay
+        cost -= 2
+        break if cost <= 0
+    cost
+}
+
+PhilosophersStone = makeCard "Philosopher's Stone", Silver, {
+  cost: 3
+  costPotion: 1
+  getCoins: (state) ->
+    Math.floor((state.current.draw.length + state.current.discard.length) / 5)
+}
+
+Princess = makeCard 'Princess', basicCard, {
+  cost: 0
+  buys: 1
+  isPrize: true
+  playEffects: [
+    (state, ret) ->
+      state.bridges += 2
+      ret(state)
+  ]
+}
+
+Quarry = makeCard 'Quarry', Silver, {
+  cost: 4
+  coins: 1
+  playEffects: [
+    (state, ret) ->
+      state.quarries += 1
+      ret(state)
+  ]
+
+# Shanty Town should also be easy
+
+###
+So far, a State needs to support these methods:
+  bridges
+  copperValue
+  current
+  current.chips
+  current.hand
+  current.draw
+  current.inPlay
+  current.drawCards()
+  current.getDeck()
+  current.revealHand()
+  phase
+  quarries
+###
+


Got rid of lame code.

Got rid of lame code.

--- lib/index.coffee
+++ lib/index.coffee
@@ -80,9 +80,6 @@

     return this

-setupRequest = (io) ->
-
-
 listen = express.application.listen
 express.application.listen = ->
     args = Array.prototype.slice.call arguments, 0


Test example

Test example

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

--- test/examples.coffee
+++ test/examples.coffee
@@ -0,0 +1,31 @@
+childProcess = require "child_process"
+supertest = require "supertest"
+assert = require "assert"
+
+
+describe "Example", ->
+    coffee = "#{__dirname}/../node_modules/.bin/coffee"
+    test = supertest "http://127.0.0.1:3000"
+
+    describe "hello_world", ->
+        example = "#{__dirname}/../examples/hello_world"
+
+        it "should run", (callback) ->
+            example = childProcess.spawn coffee, [example],
+                env: NODE_ENV: "production", PATH: process.env.PATH
+
+            output = ""
+            example.stdout.on "data", (data) ->
+                output += data.toString()
+                example.emit "started" if output.indexOf("3000") >= 0
+
+            example.on "started", ->
+                req = test.get "/"
+                req.end (err, res) ->
+                    assert.equal 200, res.status
+                    assert.equal "Hello World!", res.body[0].message
+
+                    example.on "exit", ->
+                        callback()
+
+                    example.kill()


make sure timeouts don't build up on the tooltip

fix tooltip so that it doesn't stick when rapidly hovering over items
with tooltips in quick succession

fixes CNVS-9640

test plan
=========
- enable draft state
- go to the assignments homepage
- move your mouse rapidly across the publish buttons
- verify that none of the publish buttons stay visible after the mouse
  moves off of them

Change-Id: Ia4d5759cd0f3071eb265754efb4533e8fbed4bc5
Reviewed-on: https://gerrit.instructure.com/26716
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Caleb Guanzon <54fa1f30d451e4f3c341a8f3c47c9215fd0be9c6@instructure.com>
Reviewed-by: Cameron Sutter <853bcc0cb8f5834b7805e8eb9a762054be43c731@instructure.com>
Product-Review: Matthew Irish <01acd3247a4780711d0930f64c5667e7367603a2@instructure.com>

--- app/coffeescripts/behaviors/tooltip.coffee
+++ app/coffeescripts/behaviors/tooltip.coffee
@@ -27,7 +27,37 @@
   do ($) ->
     $.widget "custom.timeoutTooltip", $.ui.tooltip,
       _open: ( event, target, content ) ->
-        setTimeout @_superApply.bind(this, arguments), 20
+        # if you move very fast, it's possible that
+        # @timeout will be defined
+        return if @timeout
+        apply = @_superApply.bind(@, arguments)
+        @timeout = setTimeout (=>
+          # make sure close will be called
+          delete @timeout
+          # remove extra handlers we added, super will add them back
+          @_off(target, "mouseleave focusout keyup")
+          apply.call(@)
+        ), 20
+        # this is from the jquery ui tooltip _open
+        # we need to bind events to trigger close so that the
+        # timeout is cleared when we mouseout / or leave focus
+        @_on( target, {
+          mouseleave: "close"
+          focusout: "close"
+          keyup: ( event ) ->
+            if ( event.keyCode == $.ui.keyCode.ESCAPE )
+              fakeEvent = $.Event(event)
+              fakeEvent.currentTarget = target[0]
+              this.close( fakeEvent, true )
+          }
+        )
+
+      close: (event) ->
+        if @timeout
+          clearTimeout(@timeout)
+          delete @timeout
+          return
+        @_superApply(arguments)

   # you can provide a 'using' option to jqueryUI position (which gets called by jqueryui Tooltip to
   # position it on the screen), it will be passed the position cordinates and a feedback object which,


Update img files

Update img files


test array time

test array time

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

--- test_arr_time.coffee
+++ test_arr_time.coffee
@@ -0,0 +1,57 @@
+TDS = require './tds'
+
+iter = parseInt process.argv[2]
+if isNaN iter
+ iter = null
+iter ?= 20
+n = 10000000
+
+console.time 'time'
+
+Point = TDS.Struct "Point",
+ {key: "x", type: TDS.Types.Int32}
+ {key: "y", type: TDS.Types.Int32}
+
+get = (arr, p, i) ->
+ arr.views[i][p]
+
+set = (arr, p, i, v) ->
+ arr.views[i][p] = v
+
+points = null
+last = new Point()
+for j in [0...iter]
+ if points?
+  last.copyFrom points.end()
+ else
+  last.set x: 1, y: 3
+
+ points = TDS.Array Point, n
+
+ points.set 0, last
+ for i in [1...n]
+  #if j is 1
+  # console.log (points.get_i i-1, 0)
+  # break
+
+
+  #points.setX i, (points.getX i-1) + i
+  #points.setY i, (points.getY i-1) + 2
+
+  #points.set_i i, 0, (points.get_i i-1, 0) + i
+  #points.set_i i, 1, (points.get_i i-1, 1) + 2
+
+  #set points, i, 0, (get points, i-1, 0) + i
+  #set points, i, 1, (get points, i-1, 1) + 2
+
+  points.views[0][i] = points.views[0][i-1] + i
+  points.views[1][i] = points.views[1][i-1] + 2
+
+  #points.views[0][i] = (points.get_i i-1, 0) + i
+  #points.views[1][i] = (points.get_i i-1, 1) + 2
+
+
+console.log points.getObject 1
+console.log points.getObject n-1
+console.timeEnd 'time'
+#


more cleanup

more cleanup

--- app/assets/javascripts/angular/controllers/GradeSchemeElementsCtrl.js.coffee
+++ app/assets/javascripts/angular/controllers/GradeSchemeElementsCtrl.js.coffee
@@ -1,22 +1,43 @@
 @gradecraft.controller 'GradeSchemeElementsCtrl', ['$scope', '$http', 'GradeSchemeElementsService', ($scope, $http, GradeSchemeElementsService) ->
   GradeSchemeElementsService.getGradeSchemeElements().success (response)->
-    #$scope.generateInputs(response.grade_scheme_elements)
+    generateFormModels(response.grade_scheme_elements)
+
+  generateFormModels = (grade_scheme_elements) ->
+    gse.model.grade_scheme_elements = grade_scheme_elements
+    init()
+
+    # gse.model = {
+    #   gradeSchemes: [
+    #     {
+    #       letter:'abc',
+    #       level:'',
+    #       lowRange:'',
+    #       highRange:''
+    #     },
+    #     {
+    #       letter:'abc',
+    #       level:'',
+    #       lowRange:'',
+    #       highRange:''
+    #     }
+    #   ]
+    # }
+
+    # 0: Object
+    # course_id: 1
+    # created_at: "2015-06-08T16:29:05.500-04:00"
+    # description: null
+    # grade_scheme_id: null
+    # high_range: 600000
+    # id: 1
+    # letter: "F"
+    # level: "Rat"
+    # low_range: 0
+    # updated_at: "2015-06-08T16:29:05.500-

   gse = @
   gse.onSubmit = () ->
     alert(JSON.stringify(gse.model), null, 2)
-  #$scope.gse.model = {}
-  # $scope.gse.fields = []
-
-  gse.author = {
-    # name: 'Joe Zhou',
-    # url: 'https://plus.google.com/u/0/111062476999618400219/posts'
-  }
-  gse.exampleTitle = 'Repeating Section';
-  # gse.env = {
-  #   angularVersion: angular.version.full,
-  #   formlyVersion: formlyVersion
-  # }

   fieldOne = {
     className: 'row',
@@ -62,7 +83,7 @@
     gse.fields = [
       {
         type: 'gradeScheme',
-        key: 'investments',
+        key: 'gradeSchemes',
         templateOptions: {
           btnText:'Add another investment',
           fields: [
@@ -71,45 +92,8 @@
         }
       }
     ]
-    gse.model = {
-      investments: [
-        {
-          investmentName:'abc',
-          investmentDate:(new Date()).toDateString(),
-          stockIdentifier:'',
-          investmentValue:'',
-          relationshipName:'',
-          complianceApprover:'',
-          requestorComment:''
-        },
-        {
-          investmentName:'haf',
-          investmentDate:(new Date()).toDateString(),
-          stockIdentifier:'',
-          investmentValue:'',
-          relationshipName:'',
-          complianceApprover:'',
-          requestorComment:''
-        }
-      ]
-    }
     return
-
-  init()
   gse.originalFields = angular.copy(gse.fields);

-  # $scope.generateInput = (element) ->
-  #   tmp = {}
-  #   tmp.type = 'input'
-  #   tmp.templateOptions = {}
-  #   tmp.key = 'parentType'
-  #   tmp.templateOptions.label = element.letter
-  #   tmp
-
-  # $scope.generateInputs = (elements) ->
-  #   fields = []
-  #   angular.forEach elements, (element, index)->
-  #     fields.push $scope.generateInput(element)
-  #   $scope.gse.fields = fields
   return
 ]


Social: create a new class for account operations in social api