Use TextEditor::onDidChange

Use TextEditor::onDidChange

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

--- lib/dialog.coffee
+++ lib/dialog.coffee
@@ -15,7 +15,7 @@
       'core:confirm': => @onConfirm(@miniEditor.getText())
       'core:cancel': => @cancel()
     @miniEditor.on 'blur', => @remove()
-    @miniEditor.getModel().getBuffer().on 'changed', => @showError()
+    @miniEditor.getModel().onDidChange => @showError()
     @miniEditor.getModel().setText(initialPath)

     if select


Fix the goWorkspace method for bookshelve refactor

goWorkspace isn't used in gh-book, which is why this bug wasn't picked up until
now.

--- scripts/controllers/routing.coffee
+++ scripts/controllers/routing.coffee
@@ -5,10 +5,12 @@
 # There is a cyclic dependency between this and various views (`->` means "depends on"):
 # controller -> layout -> WorkspaceView -> WorkspaceItemView -> controller (because clicking an item will begin editing)
 define [
+  'underscore'
   'marionette'
   'cs!collections/content'
+  'cs!models/content/toc-node'
   'cs!views/layouts/workspace'
-], (Marionette, allContent, WorkspaceLayout) ->
+], (_, Marionette, allContent, TocNode, WorkspaceLayout) ->

   # Only reason to extend Backbone.Router is to get the @navigate method
@@ -44,9 +46,10 @@
       # To prevent cyclic dependencies, load the views once the app has loaded.
       require [
         'cs!views/layouts/workspace/menu'
-        'cs!views/layouts/workspace/sidebar'
+        'cs!views/layouts/workspace/bookshelf'
+        'cs!views/layouts/workspace/table-of-contents'
         'cs!views/workspace/content/search-results'
-        ], (menuLayout, SidebarView, SearchResultsView) =>
+        ], (menuLayout, BookshelfView, TocView, SearchResultsView) =>

         @_ensureLayout(menuLayout)

@@ -54,8 +57,17 @@
         allContent.load()
         .fail(() => alert 'Problem loading workspace. Please refresh and try again')
         .done () =>
-          @_showWorkspacePane(SidebarView)
-          @layout.sidebar.close()
+          #@_showWorkspacePane(SidebarView)
+          @_showWorkspacePane(BookshelfView)
+
+          # Find the first TocNode and show that in the Toc panel
+          firstBook = _.find allContent.without(this.model), (m) ->
+            m instanceof TocNode
+          tocView = new TocView
+            model: firstBook
+
+          @layout.sidebar.show(tocView)
+          #@layout.sidebar.close()
           @layout.content.show(new SearchResultsView {collection:allContent})

           # Update the URL


Revert "Check if we should redirect to some other content in a model."

This reverts commit 993bf1876a497a22487131f14f4b852baaddfba2.

--- scripts/controllers/routing.coffee
+++ scripts/controllers/routing.coffee
@@ -131,17 +131,7 @@
                 @layout.sidebar.show(modelView)
                 modelView.maximize()

-            # If the model has a contentView, open that. If the model is a
-            # container that requires an item within the container to be
-            # opened, it may implement a contentRedirect method that returns
-            # the model we should edit instead. In all cases, returning null
-            # is a valid indication that no view need be opened.
-            if model.contentView
-              model.contentView (view) =>
-                if view then @layout.content.show(view)
-            else if model.contentRedirect
-              model = model.contentRedirect() or model
-              model.contentView((view) => if view then @layout.content.show(view)) if model.contentView
+            model.contentView((view) => if view then @layout.content.show(view)) if model.contentView

             # Load the menu's toolbar
             if model.toolbarView


remove debug output

remove debug output

--- imports/ui/pages/meteor/StoragePage.coffee
+++ imports/ui/pages/meteor/StoragePage.coffee
@@ -20,8 +20,7 @@
   # appNameSelected: (name) -> App.emit 'app name selected', name
   # appsSearchEntered: (value) -> App.emit 'app search entered', value
   ds = DataStores.findOne()
-  console.table [ds]
-  console.table StorageBuckets.find(searchObj, sort: name: 1).fetch()
+
   buckets: StorageBuckets.find(searchObj, sort: name: 1).fetch()
   dataStore:
     total: dsTotal = parseInt (ds?.total or 0)


Permissions: add method to get permissions of role.

--- client/Dashboard/views/permissionsform.coffee
+++ client/Dashboard/views/permissionsform.coffee
@@ -199,3 +199,8 @@
       when 'tree'         then return createTree values
       else throw new Error "Unknown structure #{structure}"

+  getPermissionsOfRole: (role)->
+    allValues = @list()
+    selectedRoleValues = []
+    selectedRoleValues.push permission for permission in allValues when permission.role is role
+    createReducedList selectedRoleValues


Troubleshoot: since some services returns callback with no error even with fail situtations, status is down check is added to finish callback

--- client/Main/troubleshoot/healthchecker.coffee
+++ client/Main/troubleshoot/healthchecker.coffee
@@ -7,6 +7,7 @@
     @status = "not started"

   run: ->
+    @emit "healthCheckStarted"
     @status = "waiting"
     @startTime = Date.now()
     @setPingTimeout()
@@ -19,11 +20,14 @@
     , 5000

   finish: (data)->
-    @status = "success"
-    @finishTime = Date.now()
-    clearTimeout @pingTimeout
-    @pingTimeout = null
-    @emit "healthCheckCompleted"
+    # some services (e.g. kite controller) does return callback with error parameter
+    # hence we are having
+    unless @status is "down"
+      @status = "success"
+      @finishTime = Date.now()
+      clearTimeout @pingTimeout
+      @pingTimeout = null
+      @emit "healthCheckCompleted"

   reset: ->
     @status = "waiting"
@@ -31,4 +35,4 @@
     @startTime = null

   getResponseTime: ->
-    if @status is "success" then @finishTime - @startTime else ""+    if @status is "success" then @finishTime - @startTime else 0


collaboration: controller minor

collaboration: controller minor

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -263,6 +263,7 @@
       @myWatchMap        = null
       @mySnapshot        = null

+
   registerCollaborationSessionId: ->

     realtimeHelpers.registerCollaborationSessionId @rtm, @participants
@@ -693,26 +694,6 @@
         success: =>
           @modal?.destroy()
           @handleCollaborationEndedForParticipant()
-
-
-  handleCollaborationEndedForParticipant: ->
-
-    # TODO: fix explicit state checks.
-    return  unless @stateMachine.state in ['Active', 'Ending']
-
-    # TODO: fix implicit emit.
-    @rtm.once 'RealtimeManagerWillDispose', =>
-      @chat.emit 'CollaborationEnded'
-      @chat.destroy()
-      @chat = null
-      @statusBar.emit 'CollaborationEnded'
-      @removeParticipant nick()
-      @removeMachineNode()
-
-    @rtm.once 'RealtimeManagerDidDispose', =>
-      kd.utils.defer @bound 'quit'
-
-    @cleanupCollaboration()

   endCollaborationForHost: (callbacks) ->
@@ -741,6 +722,26 @@
         callbacks.success()

+  handleCollaborationEndedForParticipant: ->
+
+    # TODO: fix explicit state checks.
+    return  unless @stateMachine.state in ['Active', 'Ending']
+
+    # TODO: fix implicit emit.
+    @rtm.once 'RealtimeManagerWillDispose', =>
+      @chat.emit 'CollaborationEnded'
+      @chat.destroy()
+      @chat = null
+      @statusBar.emit 'CollaborationEnded'
+      @removeParticipant nick()
+      @removeMachineNode()
+
+    @rtm.once 'RealtimeManagerDidDispose', =>
+      kd.utils.defer @bound 'quit'
+
+    @cleanupCollaboration()
+
+
   showChat: ->

     switch @stateMachine.state


Bind event listeners when social channel is fetched

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -49,6 +49,7 @@
       return callback err  if err

       @setSocialChannel channel
+      @bindSocialChannelEvents()
       callback null, @socialChannel

@@ -672,7 +673,6 @@
     @transitionViewsToActive()
     @collectButtonShownMetric()
     @bindRealtimeEvents()
-    @bindSocialChannelEvents()

     # this method comes from VideoCollaborationController.
     # It's mixed into IDEAppController after CollaborationController.


Cursors now appear where we want them to! Fixes #4704961

--- math/expression_components_spec.coffee
+++ math/expression_components_spec.coffee
@@ -17,6 +17,9 @@
       @comp.isExpression()
       @comp.isFraction()
       @comp.isNumber()
+      @comp.isVariable()
+      @comp.isFraction()
+      @comp.isExponentiation()

 describe "Expression Components", ->
   beforeEach ->


made the unset actually unset

made the unset actually unset

--- core/app/backbone/collections/channels.coffee
+++ core/app/backbone/collections/channels.coffee
@@ -15,6 +15,7 @@
   unsetActiveChannel: ->
     activeChannel = @get(@activeChannelId)
     activeChannel.trigger "deactivate"  if activeChannel
+    delete @activeChannelId

   setActiveChannel: (channel) ->
     @unsetActiveChannel()  if @activeChannelId and @activeChannelId isnt channel.id


Fix battle state refresh

Fix battle state refresh

--- views/components/main/parts/task-panel.cjsx
+++ views/components/main/parts/task-panel.cjsx
@@ -97,7 +97,7 @@
   {
     for k, v of questRecord[id]
       if v.count? and v.required?
-
{v.description} - {v.count} / {v.required}

+
{v.description} - {v.count} / {v.required}

   }

@@ -205,12 +205,13 @@
       when '/kcsapi/api_req_kousyou/destroyitem2'
         flag = updateQuestRecord('destory_item', null, 1)
     return unless flag
-    for task in tasks
-      continue if task.id == 100000
-      if questGoals[task.id]?
-        task.tracking = true
-        task.percent = questRecord[task.id].count / questRecord[task.id].required
-        task.progress = questRecord[task.id].count + ' / ' + questRecord[task.id].required
+    if config.get('poi.quest.tracker.enable', true)
+      for task in tasks
+        continue if task.id == 100000
+        if questGoals[task.id]?
+          task.tracking = true
+          task.percent = questRecord[task.id].count / questRecord[task.id].required
+          task.progress = questRecord[task.id].count + ' / ' + questRecord[task.id].required
     tasks = _.sortBy tasks, (e) -> e.id
     @setState
       tasks: tasks
@@ -249,7 +250,23 @@
       shipType = window.$ships[shipId].api_stype
       if shipType in [7, 11, 13, 15]
         flag = updateQuestRecord('sinking', {shipType: shipType}, 1) || flag
-    @forceUpdate() if flag
+    if flag and config.get('poi.quest.tracker.enable', true)
+      {tasks} = @state
+      for task in tasks
+        continue if task.id == 100000
+        if questGoals[task.id]?
+          task.tracking = true
+          task.percent = questRecord[task.id].count / questRecord[task.id].required
+          task.progress = questRecord[task.id].count + ' / ' + questRecord[task.id].required
+      tasks = _.sortBy tasks, (e) -> e.id
+      @setState
+        tasks: tasks
+      event = new CustomEvent 'task.change',
+        bubbles: true
+        cancelable: true
+        detail:
+          tasks: tasks
+      window.dispatchEvent event
   refreshDay: ->
     curDay = getCurrentDay()
     return if prevDay == curDay


making some void functions explicit

making some void functions explicit

--- server/authentication/index.coffee
+++ server/authentication/index.coffee
@@ -160,6 +160,7 @@

   isRestricted = (user, callback) ->
     isInRole 'Restricted', user, callback
+    return

   isAdmin = (user, callback) ->
     isInRole 'Admin', user, (result) ->
@@ -167,9 +168,11 @@
         callback(result) if callback
       else
         isSysAdmin user, callback
+    return

   isSysAdmin = (user, callback) ->
     isInRole 'SysAdmin', user, callback
+    return

   logout = (req, res) ->
     req.logout()


code to periodically force update project state

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -224,6 +224,7 @@
     ###
     # get info about server and add to database

+        require('compute').compute_server(db_hosts:['localhost'],keyspace:'salvus',cb:(e,s)->console.log(e);s.add_server(host:'compute0-us', cb:(e)->console.log("done",e)))

         require('compute').compute_server(db_hosts:['smc0-us-central1-c'],keyspace:'salvus',cb:(e,s)->console.log(e);s.add_server(host:'compute0-us', cb:(e)->console.log("done",e)))

@@ -2677,10 +2678,61 @@
         cb(err)
     )

+update_states = (cb) ->
+    # TEMPORARY until I have time to fix a bug.
+    # Right now when a project times out starting, it gets stuck like that forever unless the client
+    # does a project.state(force:true,update:true,cb:...), which the hub clients at this moment
+    # evidently don't do.  So as a temporary workaround (I don't want to restart them until making status better!),
+    # we have this:
+    # 1. query database for all projects in starting state which started more than 60 seconds ago.
+    # 2. call .state(force:true,update:true,cb:...)
+    projects = undefined
+    dbg = (m) -> winston.debug("update_state: #{m}")
+    dbg()
+    async.series([
+        (cb) ->
+            dbg("querying db")
+            sqlite_db.select
+                table   : 'projects'
+                where   :
+                    state : 'starting'
+                columns : ['project_id', 'state_time']
+                cb      : (err, x) ->
+                    if err
+                        dbg("query err=#{misc.to_safe_str(err)}")
+                        cb(err)
+                    else
+                        projects = x
+                        dbg("got #{project.length} projects that are 'starting'")
+                        cb()
+        (cb) ->
+            if projects.length == 0
+                cb(); return
+            dbg("possibly updating each of #{projects.length} projects")
+            cutoff = new Date() - 1000*STATES.starting.timeout
+            f = (x, cb) ->
+                if x.state_time >= cutoff
+                    dbg("not updating #{x.project_id}")
+                    cb()
+                else
+                    dbg("updating #{x.project_id}")
+                    get_project
+                        project_id : x.project_id
+                        cb         : (err, project) ->
+                            if err
+                                cb(err)
+                            else
+                                project.state(force:true, update:true, cb:cb)
+            async.map(projects, f, cb)
+        ], (err) ->
+            setTimeout(update_states, 2*60*1000)
+            cb?(err)
+        )
+

 start_server = (cb) ->
     winston.debug("start_server")
-    async.series [init_stats, read_secret_token, init_sqlite_db, init_firewall, init_mintime, start_tcp_server], (err) ->
+    async.series [init_stats, read_secret_token, init_sqlite_db, init_firewall, init_mintime, start_tcp_server, update_states], (err) ->
         if err
             winston.debug("Error starting server -- #{err}")
         else


Fine grained query, remove old codes

Fine grained query, remove old codes

--- workers/social/lib/social/models/app.coffee
+++ workers/social/lib/social/models/app.coffee
@@ -388,11 +388,12 @@
     Relationship.all
       targetId  : client.connection.delegate.getId()
       as        : 'user'
+      sourceType: 'JApp'
     , (err, relationships)->
-      for app in apps
+      apps.forEach (app)->
         app.installed = no
         for relationship, index in relationships
-          if app.getId().equals relationship.sourceId
+          if app._id is relationship.sourceId
             app.installed = yes
             relationships.splice index,1
             break
@@ -471,58 +472,3 @@
     if limit > 0
       options.limit = limit
     @fetchReviews selector, options, callback
-
-  ####
-
-
-  # @create = secure (client, data, callback)->
-  #   {connection:{delegate}} = client
-  #   {thumbnails, screenshots, meta:{tags}} = data
-  #   Resource.storeImages client, thumbnails, (err, thumbnailsFilenames)->
-  #     if err
-  #       callback err
-  #     else
-  #       Resource.storeImages client, screenshots, (err, screenshotsFilenames)->
-  #         if err
-  #           callback err
-  #         else
-  #           app = new JApp {
-  #             title       : data.title
-  #             body        : data.body
-  #             thumbnails  : thumbnailsFilenames
-  #             screenshots : screenshotsFilenames
-  #             attachments : [
-  #               {
-  #                 as          : 'script'
-  #                 content     : data.scriptCode
-  #                 description : data.scriptDescription
-  #                 syntax      : data.scriptSyntax
-  #               },{
-  #                 as          : 'requirements'
-  #                 content     : data.requirementsCode
-  #                 syntax      : data.requirementsSyntax
-  #               }
-  #             ]
-  #           }
-  #           app.save (err)->
-  #             if err
-  #               callback err
-  #             else
-  #               if tags then app.addTags client, tags, (err)->
-  #                 if err
-  #                   callback err
-  #                 else
-  #                   callback null, app
-
-  # @findSuggestions = (client, seed, options, callback)->
-  #   {limit,blacklist}  = options
-
-  #   @some {
-  #     title   : seed
-  #     _id     :
-  #       $nin  : blacklist
-  #   },{
-  #     limit
-  #     sort    : 'title' : 1
-  #   }, callback
-


JNewApp: Make the admin check after all required checks so even-super admins can publish new apps

--- workers/social/lib/social/models/app.coffee
+++ workers/social/lib/social/models/app.coffee
@@ -184,10 +184,6 @@
       script : "#{url}/index.js"
       style  : "#{url}/resources/style.css"

-    # If user is admin
-    if account.can 'bypass-validations'
-      return callback null, urls
-
     # If url points to a vm url
     if (/^\[([^\]]+)\]/g.exec url)?[1]
       return callback null, urls
@@ -206,6 +202,10 @@

     unless githubUsername
       return callback new KodingError "URL is not allowed."
+
+    # If user is admin
+    if account.can 'bypass-validations'
+      return callback null, urls, {githubVerified: yes, app, githubUsername}

     account.fetchUser (err, user)->
       return callback err  if err?


Remove unnecessary assignment before when() call when opening a query

--- src/coffee/cilantro/ui/query/item.coffee
+++ src/coffee/cilantro/ui/query/item.coffee
@@ -36,10 +36,7 @@
             (context = c.data.contexts.getSession()).set('json', @model.get('context_json'), reset: true)
             (view = c.data.views.getSession()).set('json', @model.get('view_json'))

-            d1 = context.save(null, silent: true)
-            d2 = view.save(null, silent: true)
-
-            $.when(d1, d2).done ->
+            $.when(context.save(null, silent: true), view.save(null, silent: true)).done ->
                 c.publish(c.CONTEXT_SYNCED, context, 'success')
                 c.publish(c.VIEW_SYNCED, view, 'success')


Moderation: tag.slug_ assignment is removed

--- workers/social/lib/social/models/tag.coffee
+++ workers/social/lib/social/models/tag.coffee
@@ -277,7 +277,6 @@
       if err then callback err
       else
         tag.slug = slug.slug
-        tag.slug_ = slug.slug
         tag.save (err)->
           if err then callback err
           else


Default site root directory should be fully-qualified. It was relative to the lib directory.

--- lib/malifi.coffee
+++ lib/malifi.coffee
@@ -20,8 +20,7 @@
   unless root?
     throw new Error('malifi site root path required')
   options?= {}
-  debugger
-  baseSiteStack= new SiteStack(normalize(join(__dirname,root)))
+  baseSiteStack= new SiteStack(normalize(root))

   return malifiMainHandler= (req, res, next)->
     siteStack= null


[fix] Also check for contact emails in parse()

--- public/js/baobab.controller.compose.coffee
+++ public/js/baobab.controller.compose.coffee
@@ -34,7 +34,7 @@
           !_.isEmpty(contact.name) && contact.name.toLowerCase().indexOf(search.toLowerCase()) == 0)
       parse: (text) ->
         candidates = $contacts.list().filter (contact) ->
-          contact.email.toLowerCase() == text.toLowerCase()
+          !_.isEmpty(contact.email) && contact.email.toLowerCase() == text.toLowerCase()

         if (candidates.length == 1)
           candidates[0]


Fix undefined thing causing test to fail

--- src/desktop/components/modal/view.coffee
+++ src/desktop/components/modal/view.coffee
@@ -139,7 +139,7 @@

     @$el.
       attr('data-state', 'closed').
-      one($.support.transition.end, =>
+      one($.support.transition?.end, =>
         # Re-enable scrolling
         @scrollbar.reenable()


Fix a breaking error

Fix a breaking error

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -345,10 +345,10 @@
   # Parse the @get arguments into the prefix, key arguments,
   # and options.
   parse_args: (args) ->
-    prefix = args[0]
     @on_cycle = _.callback args
     opts = _.opts args
     key = args.join ':'
+    prefix = key.split(':')[0]
     { prefix, opts, key }

   # Look in both our local cache and in the LRU cache for the given
@@ -445,7 +445,7 @@
     for args, index in @gets
       opts = _.opts args
       key = args.join ':'
-      prefix = args[0]
+      prefix = key.split(':')[0]
       @key_opts[key] = { prefix, opts, index }
       @all_keys.push key


wrap yield element in domEngine obj

wrap yield element in domEngine obj

--- lib/client/javascripts/nali/view.js.coffee
+++ lib/client/javascripts/nali/view.js.coffee
@@ -261,7 +261,7 @@
     @

   addYieldAssistant: ( node ) ->
-    ( @yield = node.parentNode ).removeChild node
+    ( @yield = @_ node.parentNode )[0].removeChild node

   addRelationAssistant: ( node ) ->
     [ match, name, chain ] = node.textContent.match /{\s*(\w+) of @(\w*)\s*}/


Android bug

Android bug

Closes https://github.com/motif/company/issues/1614

--- framer/GestureInputRecognizer.coffee
+++ framer/GestureInputRecognizer.coffee
@@ -126,7 +126,7 @@
 	# ForceTap

 	_updateTouchForce: =>
-		return unless @session?.lastEvent?.touches.length
+		return unless @session?.lastEvent?.touches?.length
 		@session.force = @session.lastEvent.touches[0].force or 0
 		event = @_getGestureEvent(@session.lastEvent)
 		@forcetapchange(event)


Remove console.log

Remove console.log

--- framer/GestureInputRecognizer.coffee
+++ framer/GestureInputRecognizer.coffee
@@ -40,7 +40,6 @@
 		@touchstart(event)

 	touchstart: (event) =>
-		console.log "touchStart", event.target
 		# Only fire if we are not already in a session
 		return if @session


tweak help text formatting

tweak help text formatting

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -4,7 +4,7 @@
 DataHelper = require('./bungie-data-helper.coffee')

 dataHelper = new DataHelper
-helpText = "Use the \"help\" command to learn about using the bot, or check out the full readme here:\nhttps://github.com/phillipspc/showoff/blob/master/README.md"
+helpText = "Use the \"help\" command to learn about using the bot, or check out the full readme here: https://github.com/phillipspc/showoff/blob/master/README.md"

 module.exports = (robot) ->
   # executes when any text is directed at the bot


Debug bounties

Debug bounties

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -356,7 +356,7 @@
       !response.definitions.vendorDetails or
       !response.definitions.vendorDetails[vendorHash] or
       !response.definitions.vendorDetails[vendorHash].vendorName
-        console.log response.definitions.vendorDetails
+        console.log response.definitions.vendorDetails[vendorHash]
         console.log 'Error getting vendor details: ' + vendorHash
         return deferred.reject()


fix alignment in git-commit-spec.coffee

fix alignment in git-commit-spec.coffee

--- spec/models/git-commit-spec.coffee
+++ spec/models/git-commit-spec.coffee
@@ -133,24 +133,24 @@
         argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
         expect(argsTo_fsWriteFile[1].trim().charAt(0)).toBe commentchar_config

-    describe "when commit.template config is not set", ->
-      it "commit file starts with a blank line", ->
-        setupMocks()
-        waitsForPromise ->
-          GitCommit(repo).then ->
-            argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
-            expect(argsTo_fsWriteFile[1].charAt(0)).toEqual "\n"
+  describe "when commit.template config is not set", ->
+    it "commit file starts with a blank line", ->
+      setupMocks()
+      waitsForPromise ->
+        GitCommit(repo).then ->
+          argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
+          expect(argsTo_fsWriteFile[1].charAt(0)).toEqual "\n"

-    describe "when commit.template config is set", ->
-      it "commit file starts with content of that file", ->
-        templateFile = 'template'
-        setupMocks()
-        GitCommit(repo)
-        waitsFor ->
-          fs.writeFileSync.callCount > 0
-        runs ->
-          argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
-          expect(argsTo_fsWriteFile[1].indexOf(commitTemplate)).toBe 0
+  describe "when commit.template config is set", ->
+    it "commit file starts with content of that file", ->
+      templateFile = 'template'
+      setupMocks()
+      GitCommit(repo)
+      waitsFor ->
+        fs.writeFileSync.callCount > 0
+      runs ->
+        argsTo_fsWriteFile = fs.writeFileSync.mostRecentCall.args
+        expect(argsTo_fsWriteFile[1].indexOf(commitTemplate)).toBe 0

   describe "when 'stageChanges' option is true", ->
     it "calls git.add with update option set to true", ->


Update calls to Dock.isOpen to use .isVisible

--- spec/tree-view-package-spec.coffee
+++ spec/tree-view-package-spec.coffee
@@ -166,7 +166,7 @@
         atom.packages.deactivatePackage("tree-view")
         atom.packages.packageStates = {}
         atom.workspace.getLeftDock().hide()
-        expect(atom.workspace.getLeftDock().isOpen()).toBe(false)
+        expect(atom.workspace.getLeftDock().isVisible()).toBe(false)

         waitsForPromise ->
           atom.workspace.open('tree-view.js')
@@ -175,7 +175,7 @@

         runs ->
           # This assertion is failing because we auto-open docks when adding an item
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(false)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(false)

     describe "when the root view is opened to a directory", ->
       it "attaches to the workspace", ->
@@ -206,21 +206,21 @@

     describe "when the tree view is visible", ->
       beforeEach ->
-        expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+        expect(atom.workspace.getLeftDock().isVisible()).toBe(true)

       it "hides the tree view", ->
         workspaceElement.focus()
         waitsForPromise -> treeView.toggle()
         runs ->
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(false)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(false)

     describe "when the tree view is hidden", ->
       it "shows and focuses the tree view", ->
         atom.workspace.getLeftDock().hide()
-        expect(atom.workspace.getLeftDock().isOpen()).toBe(false)
+        expect(atom.workspace.getLeftDock().isVisible()).toBe(false)
         waitsForPromise -> treeView.toggle()
         runs ->
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
           expect(treeView.element).toHaveFocus()

   describe "when tree-view:toggle-focus is triggered on the root view", ->
@@ -232,7 +232,7 @@
         atom.workspace.getLeftDock().hide()
         waitsForPromise -> treeView.toggleFocus()
         runs ->
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
           expect(treeView.element).toHaveFocus()

     describe "when the tree view is shown", ->
@@ -242,11 +242,11 @@

         runs ->
           workspaceElement.focus()
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
           waitsForPromise -> treeView.toggleFocus()
           runs ->
-            expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+            expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
             expect(treeView.element).toHaveFocus()

       describe "when the tree view is focused", ->
@@ -256,9 +256,9 @@

           runs ->
             treeView.focus()
-            expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+            expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
             treeView.toggleFocus()
-            expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+            expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
             expect(treeView.element).not.toHaveFocus()

   describe "when the tree-view is destroyed", ->
@@ -339,25 +339,25 @@

         runs ->
           expect(atom.workspace.getActivePaneItem().getPath()).toBeUndefined()
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(false)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(false)

         waitsForPromise ->
           treeView.revealActiveFile()

         runs ->
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
           expect(treeView.focus).toHaveBeenCalled()

     describe "if there is no editor open", ->
       it "shows and focuses the tree view, but does not attempt to select a specific file", ->
         expect(atom.workspace.getActivePaneItem()).toBeUndefined()
-        expect(atom.workspace.getLeftDock().isOpen()).toBe(false)
+        expect(atom.workspace.getLeftDock().isVisible()).toBe(false)

         waitsForPromise ->
           treeView.revealActiveFile()

         runs ->
-          expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+          expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
           expect(treeView.focus).toHaveBeenCalled()

     describe 'if there are more items than can be visible in the viewport', ->
@@ -407,7 +407,7 @@
         treeView.focus()
         expect(treeView.element).toHaveFocus()
         atom.commands.dispatch(treeView.element, 'tool-panel:unfocus')
-        expect(atom.workspace.getLeftDock().isOpen()).toBe(true)
+        expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
         expect(treeView.element).not.toHaveFocus()
         expect(atom.workspace.getActivePane().isActive()).toBe(true)


added debugging code

added debugging code

--- src/backend/rfid/controller.coffee
+++ src/backend/rfid/controller.coffee
@@ -92,7 +92,9 @@
                 msg = "Reader: Error occured when listing serialports: #{error}"
                 logger.error msg
                 return deferred.reject msg
-
+
+            logger.debug "list of ports found: #{JSON.stringify(ports)}"
+
             for i in [0..ports.length]
                 port = ports[i]
                 logger.debug "port found: #{JSON.stringify(port)}"


:checkered_flag: Fix corrupting Windows Paths

containing non-Ascii characters
Remove workaround that just didn't update the path at all - now including
the Atom path update and preserving the non-Ascii characters

--- src/browser/squirrel-update.coffee
+++ src/browser/squirrel-update.coffee
@@ -56,6 +56,9 @@

 # Spawn powershell.exe and callback when it completes
 spawnPowershell = (args, callback) ->
+  # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding
+  # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell
+  args[0] = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8\r\n$output=#{args[0]}\r\n[Console]::WriteLine($output)"
   args.unshift('-command')
   args.unshift('RemoteSigned')
   args.unshift('-ExecutionPolicy')
@@ -90,13 +93,6 @@
     installMenu directoryKeyPath, '%1', ->
       installMenu(backgroundKeyPath, '%V', callback)

-isAscii = (text) ->
-  index = 0
-  while index < text.length
-    return false if text.charCodeAt(index) > 127
-    index++
-  true
-
 # Get the user's PATH environment variable registry value.
 getPath = (callback) ->
   spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) ->
@@ -104,12 +100,7 @@
       return callback(error)

     pathOutput = stdout.replace(/^\s+|\s+$/g, '')
-    if isAscii(pathOutput)
-      callback(null, pathOutput)
-    else
-      # FIXME Don't corrupt non-ASCII PATH values
-      # https://github.com/atom/atom/issues/5063
-      callback(new Error('PATH contains non-ASCII values'))
+    callback(null, pathOutput)

 # Uninstall the Open with Atom explorer context menu items via the registry.
 uninstallContextMenu = (callback) ->


allowing dot to branch name

allowing dot to branch name

--- scripts/deploy.coffee
+++ scripts/deploy.coffee
@@ -28,7 +28,7 @@

 module.exports = (robot) ->

-  robot.respond /deploy\s*([_a-zA-Z0-9-/']+\s)(to\s+)?(.+)/i, (msg) ->
+  robot.respond /deploy\s*([._a-zA-Z0-9-/']+\s)(to\s+)?(.+)/i, (msg) ->
     branch     = msg.match[1] or null
     target     = msg.match[3] or null
     branch     = branch.replace /^\s+|\s+$/g, ""


unfucking with the script

unfucking with the script

--- scripts/fierce.coffee
+++ scripts/fierce.coffee
@@ -115,7 +115,6 @@
   robot.hear /(^| )alan/i, (msg) ->
     roll = Math.floor(Math.random() * 20) + 1
       if roll is 1
-        msg send 'I hate this script'
         msg.send "http://www.youtube.com/watch?v=aXqkNF8fgs8"

   robot.hear /right about now/i, (msg) ->


added the where is jenkins command

added the where is jenkins command

--- scripts/fierce.coffee
+++ scripts/fierce.coffee
@@ -122,3 +122,6 @@

   robot.hear /secur(e|ity)/i, (msg) ->
     msg.send 'TODO: add security'
+
+  robot.respond /(where is )?jenkins(\?)?/i, (msg) ->
+    msg.send 'https://ci.fiercemarkets.com:8443/'


Revert ":bug: handle clicking next/previous"

This reverts commit d61eed67786e4a6876bbd5a3b328f62957342e28.

--- src/scripts/modules/media/nav/nav.coffee
+++ src/scripts/modules/media/nav/nav.coffee
@@ -114,7 +114,10 @@

       e.preventDefault()
       e.stopPropagation()
-      # Rely on the Content.sendPageViewAnalytics to fire
+      href = $(e.currentTarget).attr('href')
+      # this sends the page without a version number (which causes analytics to fire).
+      # It fires twice because @mediaParent.trackAnalytics() also sends an event.
+      router.navigate href, {trigger: false}, () => @mediaParent.trackAnalytics()

     closeContentsOnSmallScreen: ->
       if window.innerWidth < 640


improve handling of broken avatar images

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -176,7 +176,13 @@
     tabIndex: if props.focusable then 0 else -1

   if tag == IMG
-    attrs.alt = if props.hide_tooltip then '' else tooltip
+    # attrs.alt = if props.hide_tooltip then '' else tooltip
+    # the above fails too much on broken images, and
+    # screenreaders would probably be overwhelmed with saying all these stances.
+    # If in future it turns out we want alt text for accessibility, we can address
+    # the broken text ugliness by using img { text-indent: -10000px } to
+    # hide the alt text / broken image
+    attrs.alt = ""

   tag attrs,
     if add_initials
@@ -234,5 +240,17 @@
   .avatar.avatar_anonymous {
     cursor: default;
 }
+/* for styling icon of broken images */
+img.avatar:after {
+  position: absolute;
+  z-index: 2;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #f7954c;
+  border-radius: 50%;
+  content: "";
+}

 """


removed trailing whitespace

removed trailing whitespace

--- server/publish.coffee
+++ server/publish.coffee
@@ -89,7 +89,7 @@
 Meteor.publish("current_posts", (post_id) ->
   user_id = @userId()
   Session.set('user_id', user_id)
-  if user_id?
+  if user_id?
   #all the parents of the post
     ids = []
     ids.push( post_id )


remove console.info

remove console.info

--- src/main/coffeescript/view/MainView.coffee
+++ src/main/coffeescript/view/MainView.coffee
@@ -17,7 +17,6 @@
     resources = {}
     counter = 0
     for resource in @model.apisArray
-      console.info(resource);
       id = resource.name
       while typeof resources[id] isnt 'undefined'
         id = id + "_" + counter


verifying cleanup. test fails

verifying cleanup. test fails

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -101,7 +101,9 @@
       events.push(event.value)

   waitsFor streamEnded, 1000
-  runs -> expect(events).toEqual(expectedEvents)
+  runs ->
+    expect(events).toEqual(expectedEvents)
+    verifyCleanup()

 # get each event with new subscriber
 verifySwitching = (src, expectedEvents) ->
@@ -119,11 +121,20 @@
   runs ->
     src.subscribe(newSink())
   waitsFor streamEnded, 1000
-  runs -> expect(events).toEqual(expectedEvents)
+  runs ->
+    expect(events).toEqual(expectedEvents)
+    verifyCleanup()

-sources = []
+seqs = []
 soon = (f) -> setTimeout f, 100
 seq = (interval, values) ->
   source = Bacon.sequentially(interval, values)
-  sources.push(source)
+  seqs.push({ values : values, source : source })
   source
+
+verifyCleanup = ->
+  for seq in seqs
+    console.log("verify cleanup: #{seq.values}")
+    expect(seq.source.hasSubscribers()).toEqual(false)
+  seqs = []
+


Fixed test misspelling

Fixed test misspelling

--- spec/jira.spec.coffee
+++ spec/jira.spec.coffee
@@ -428,7 +428,7 @@
         expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function)

         @jira.request.mostRecentCall.args[1] null, statusCode:401
-        expect(@cb).toHaveBeenCalledWith '401: Error while retreiving issue types'
+        expect(@cb).toHaveBeenCalledWith '401: Error while retrieving issue types'

         # Successful Request
         @jira.request.mostRecentCall.args[1] null, statusCode:200, "body"


fallback renderer to fix linux CI?

fallback renderer to fix linux CI?

--- spec/juno-spec.coffee
+++ spec/juno-spec.coffee
@@ -10,14 +10,14 @@
   waitsForPromise -> atom.packages.activatePackage 'ink'
   waitsForPromise -> atom.packages.activatePackage 'julia-client'
   runs ->
-    client.onStdout (data) -> console.log data
-    client.onStderr (data) -> console.log data
     atom.config.set 'julia-client.juliaPath', 'julia'
     atom.config.set 'julia-client.juliaOptions',
       bootMode: 'Basic'
       optimisationLevel: 2
       deprecationWarnings: false
       precompiled: true
+      consoleOptions:
+        rendererType: true

 cyclerSetup = ->
   basicSetup()
@@ -26,7 +26,9 @@
 conn = null

 withClient = ->
-  beforeEach -> client.attach conn
+  beforeEach ->
+    if conn?
+      client.attach conn

 testClient = require './client'
 testEval = require './eval'


Add testcase for assembly

Add testcase for assembly

--- spec/languages.coffee
+++ spec/languages.coffee
@@ -746,4 +746,26 @@
       mixed: 2
       empty: 1
     }
+    {
+      names: ["asm"]
+      code:
+        """
+          global start
+
+          start:; entry point
+            push rbp
+            mov rsp, rbp
+            ;at&t syntax: ; weird
+            mov %rsp, %rax
+            pop rax
+            ret
+        """
+      total: 9
+      source: 7
+      comment: 2
+      single: 2
+      block: 0
+      mixed: 1
+      empty: 1
+    }
   ]


Adds some debug for...

Adds some debug for...

--- spec/main-spec.coffee
+++ spec/main-spec.coffee
@@ -34,7 +34,11 @@
             data = $(test.root, schema, refs, test.expand)

           if test.data
-            expect(test.data).toHaveSchema data, $.refs
+            try
+              expect(test.data).toHaveSchema data, $.refs
+            catch e
+              console.log JSON.stringify(data, null, 2)
+              throw e

           if test.hasRefs >= 0
             expect(data).toHaveRefs test.hasRefs


Default resource pero se puede meter uno custom

--- src/senders/async.producteca.sender.coffee
+++ src/senders/async.producteca.sender.coffee
@@ -9,6 +9,8 @@
   decoded = Buffer.from(token, "base64").toString();
   _.split(decoded, ":")[0]

+_resource = ({ Resource }) => _.reject(Resource, isNaN).join("")
+
 module.exports =
   user: ({ message: { HeadersForRequest } }) =>
     auth = _.find(HeadersForRequest, { Key: "Authorization" })
@@ -17,13 +19,6 @@
     _companyId(method, token)
     .tap (it) => console.log("USEER", it)
   ,
-  resource: ({ message: { Body } }) =>
-    parsedBody
-    try
-      parsedBody = JSON.parse(Body)
-    catch e
-      parsedBody = null
-    "test4"
-   # console.log("resource", parsedBody && parsedBody.channelProductId)
-   #parsedBody && parsedBody.channelProductId
+  resource: ({ message }, resourceGetter) =>
+    if _.isFunction(resourceGetter) then resourceGetter(message) else _resource(message)


Enable/Disable actions on selections

Enable/Disable actions on selections

--- app/assets/javascripts/georgia/prototypes/tables/pages.js.coffee
+++ app/assets/javascripts/georgia/prototypes/tables/pages.js.coffee
@@ -12,9 +12,9 @@

   update: () =>
     if @getChecked().length
-      @showActions()
+      @enableActions()
     else
-      @hideActions()
+      @disableActions()

   delete: (event) =>
     @stopEvent(event)
@@ -23,11 +23,11 @@
       type: 'DELETE'
     )

-  showActions: () =>
-    @deleteBtn.removeClass('hide')
+  enableActions: () =>
+    @deleteBtn.removeClass('disabled')

-  hideActions: () =>
-    @deleteBtn.addClass('hide')
+  disableActions: () =>
+    @deleteBtn.addClass('disabled')

   stopEvent: (event) ->
     event.stopPropagation()


Force login for start group page

Force login for start group page

--- angular/core/services/ability_service.coffee
+++ angular/core/services/ability_service.coffee
@@ -197,5 +197,6 @@
              'registeredAppsPage', \
              'registeredAppPage',  \
              'pollsPage',          \
-             'startPollPage' then true
+             'startPollPage',      \
+             'startGroupPage' then true
         else false


shows add button when removing link

shows add button when removing link

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -693,7 +693,7 @@
       li = $(this).closest 'li'
       list = li.closest(".js-uploaded-list")
       wrapper = list.closest(".js-upload-wrapper")
-      button = wrapper.find(".js-button-add")
+      button = wrapper.find(".button-add")
       max = wrapper.data('max-attachments')

       li.remove()


Add mapbox script

Add mapbox script

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -9,6 +9,7 @@
 #= require rails.validations
 #= require rails.validations.simple_form
 #= require rails.validations.simple_form.fix
+#= require mapbox
 #= require jquery.autosize
 #= require globals/_functions
 #= require tools/mapbox


Don't try to link to mp3 flavors from kaltura

refs: CNVS-12293
refs: CNVS-12998

This commit needs to go out with changeid:
I5b77f58133fb9081909771d50bb8f0a91db23d63
because it fixes audio playback in firefox.

it is it's own commit so that if we fix our mp3s
being served from kaltura (this ticket: CNVS-12998)
, we can just revert this one commit.

long story:
all our browsers except firefox will allow
<audio><source type="video/mp4" src="blah.mp4"></audio>
...and play back the audio inside the mp4 container

but firefox won't, so it was falling back to the
last of the options returned by /media_objects/<id>/info;
an 'audio/mp3' url. but the thing that url points to
right now in production
(e.g.: http://www.instructuremedia.com/p/100/sp/10000/download/entry_id/0_4b4pgy5p/flavor/0_092j8kek/ks/NWRiYzk5Njk2MTRjZTlhYWEwODBkYjVlOWQ2MTc2MzZmMGNhMDc0MHwxMDA7MTAwOzEzOTk2NzM4NjI7MDsxMzk5NTg3NDYyLjkwNzg7O2Rvd25sb2FkOjBfNGI0cGd5NXA7)
just returns text/html that says:
"This URL is expired"

Change-Id: Ie9bc2551b839247042fc7f3261f82845331e04fc
Reviewed-on: https://gerrit.instructure.com/34748
Reviewed-by: Sterling Cobb <7a57d889ea7f266299434459898a90c7f6a1ca4d@instructure.com>
QA-Review: Clare Strong <40d6bb3ed7fef516c73274131d5a837c16de74fd@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Paul Hinze <4750e031e77b9a66270d86f487e0d12ea4126f1c@instructure.com>

--- app/coffeescripts/jquery/mediaComment.coffee
+++ app/coffeescripts/jquery/mediaComment.coffee
@@ -45,7 +45,11 @@
   getSourcesAndTracks = (id) ->
     dfd = new $.Deferred
     $.getJSON "/media_objects/#{id}/info", (data) ->
-      sources = _.map data.media_sources, (source) -> "<source type='#{source.content_type}' src='#{source.url}' />"
+      # this 'when ...' is because right now in canvas, none of the mp3 urls actually work.
+      # see: CNVS-12998
+      sources = for source in data.media_sources when source.content_type isnt 'audio/mp3'
+        "<source type='#{source.content_type}' src='#{source.url}' />"
+
       tracks = _.map data.media_tracks, (track) ->
           languageName = mejs.language.codes[track.locale] || track.locale
           "<track kind='#{track.kind}' label='#{languageName}' src='#{track.url}' srclang='#{track.locale}' />"


ContentSearch: Fix invalid file path displayed on results view.

--- client/ide/views/contentsearch/idecontentsearchresultview.coffee
+++ client/ide/views/contentsearch/idecontentsearchresultview.coffee
@@ -11,7 +11,7 @@

     for fileName, lines of result
       @addSubView new KDCustomHTMLView
-        partial  : "~/#{fileName}"
+        partial  : "#{fileName}"
         cssClass : 'filename'

       for line in lines


added prepareHeaders call for @expected data

--- src/validators/headers-json-example.coffee
+++ src/validators/headers-json-example.coffee
@@ -20,7 +20,7 @@
       throw new errors.MalformedDataError "Expected is not an Object"

     try
-      @expected = JSON.parse(JSON.stringify(@expected))
+      @expected = @prepareHeaders JSON.parse(JSON.stringify(@expected))
     catch error
       outError = new errors.MalformedDataError "Headers validator - Expected malformed:" + error.message
       outError['data'] = @expected


Add `editor:toggle-soft-tabs` event

Add `editor:toggle-soft-tabs` event

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -140,6 +140,7 @@
         'core:close': @close
         'editor:save': @save
         'editor:newline-below': @insertNewlineBelow
+        'editor:toggle-soft-tabs': @toggleSoftTabs
         'editor:toggle-soft-wrap': @toggleSoftWrap
         'editor:fold-all': @foldAll
         'editor:unfold-all': @unfoldAll
@@ -532,6 +533,9 @@
     @activeEditSession.setScrollTop(@scrollTop())
     @activeEditSession.setScrollLeft(@scrollView.scrollLeft())

+  toggleSoftTabs: ->
+    @activeEditSession.setSoftTabs(not @activeEditSession.softTabs)
+
   toggleSoftWrap: ->
     @setSoftWrap(not @activeEditSession.getSoftWrap())


Missed a spot

Missed a spot

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -681,7 +681,10 @@
   # iterator - A {Function} that's called on each match
   backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...)

-  # Internal:
+  ###
+  # Internal #
+  ###
+
   configure: ->
     @observeConfig 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers)
     @observeConfig 'editor.showInvisibles', (showInvisibles) => @setShowInvisibles(showInvisibles)
@@ -690,7 +693,6 @@
     @observeConfig 'editor.fontSize', (fontSize) => @setFontSize(fontSize)
     @observeConfig 'editor.fontFamily', (fontFamily) => @setFontFamily(fontFamily)

-  # Internal: Responsible for handling events made to the editor.
   handleEvents: ->
     @on 'focus', =>
       @hiddenInput.focus()
@@ -762,7 +764,6 @@
       else
         @gutter.addClass('drop-shadow')

-  # Internal:
   selectOnMousemoveUntilMouseup: ->
     lastMoveEvent = null
     moveHandler = (event = lastMoveEvent) =>
@@ -781,7 +782,6 @@
       @activeEditSession.finalizeSelections()
       @syncCursorAnimations()

-  # Internal:
   afterAttach: (onDom) ->
     return unless onDom
     @redraw() if @redrawOnReattach
@@ -803,7 +803,6 @@

     @trigger 'editor:attached', [this]

-  # Internal:
   edit: (editSession) ->
     return if editSession is @activeEditSession

@@ -833,24 +832,12 @@
     if @attached and @activeEditSession.buffer.isInConflict()
       _.defer => @showBufferConflictAlert(@activeEditSession) # Display after editSession has a chance to display

-  # Internal: Retrieves the currently active session.
-  #
-  # Returns an {EditSession}.
   getModel: ->
     @activeEditSession

-  # Internal: Set the new active session.
-  #
-  # editSession - The new {EditSession} to use.
   setModel: (editSession) ->
     @edit(editSession)

-  # Public: Retrieves the {EditSession}'s buffer.
-  #
-  # Returns the current {Buffer}.
-  getBuffer: -> @activeEditSession.buffer
-
-  # Internal:
   showBufferConflictAlert: (editSession) ->
     atom.confirm(
       editSession.getPath(),
@@ -859,7 +846,6 @@
       "Cancel"
     )

-  # Internal:
   scrollTop: (scrollTop, options={}) ->
     return @cachedScrollTop or 0 unless scrollTop?
     maxScrollTop = @verticalScrollbar.prop('scrollHeight') - @verticalScrollbar.height()
@@ -876,13 +862,21 @@
     if options?.adjustVerticalScrollbar ? true
       @verticalScrollbar.scrollTop(scrollTop)

-  # Internal:
   scrollBottom: (scrollBottom) ->
     if scrollBottom?
       @scrollTop(scrollBottom - @scrollView.height())
     else
       @scrollTop() + @scrollView.height()

+  ###
+  # Public #
+  ###
+
+  # Public: Retrieves the {EditSession}'s buffer.
+  #
+  # Returns the current {Buffer}.
+  getBuffer: -> @activeEditSession.buffer
+
   # Public: Scrolls the editor to the bottom.
   scrollToBottom: ->
     @scrollBottom(@screenLineCount() * @lineHeight)


more changez ga

more changez
ga

--- src/background.coffee
+++ src/background.coffee
@@ -8,29 +8,30 @@
   else
     throw "Not resolved!"

-window.promise = (async, thisObj, args...) ->
+window.promisfy = (async, thisObj, args...) ->
   dfd = $.Deferred()
-
   args.push (callbackArgs...) ->
     dfd.resolve.apply dfd, callbackArgs
+  async.apply thisObj, args
+  dfd

-  async.apply thisObj, args

-  dfd
 window.BG =
+  updateData: ->
+    @getLastFocusedWindow().then( (chromeWindow)->

-      # promise chrome.windows.getLastFocused, @,

-  updateData: ->
-    chrome.windows.getLastFocused (window) ->
+    )
+    chrome.windows.getLastFocused (chromeWindow) ->
       return unless window?

+# returns a promise for the last focused window object
   getLastFocusedWindow: ->
-    promise chrome.windows.getLastFocused, @
+    promisify chrome.windows.getLastFocused, @

 # returns a promsie for current tab
   getCurrentTab: ->
-    promise chrome.windows.getLastFocused, @,
+    promisify chrome.windows.getLastFocused, @,

   getDomainFromTab: (tab)->
     tab.url


fix bug where output path would not be expanded

--- src/cli/common.coffee
+++ src/cli/common.coffee
@@ -58,15 +58,16 @@
         # ovveride conf and default options with any command line options
         if argv[key]?
           options[key] = argv[key]
-        # expand paths
-        if ['output', 'config', 'contents', 'templates'].indexOf(key) != -1
-          options[key] = path.join workDir, options[key]
       # pass along extra arguments from argv
       for key of argv
         # don't include optimist stuff
         if key[0] == '_' or key[0] == '$'
           continue
         options[key] ?= argv[key]
+      # expand paths
+      for key in ['output', 'config', 'contents', 'templates']
+        if options[key]
+          options[key] = path.join workDir, options[key]
       callback null, options
     (options, callback) ->
       # load locals json if neccessary


specs for rendering empty course for archived course list

--- test/components/course-listing.spec.coffee
+++ test/components/course-listing.spec.coffee
@@ -51,6 +51,11 @@
         expect(MASTER_COURSES_LIST[i].name).to.contain(renderDataset[i].title)
       # no refresh button when load succeeds
       expect(state.div.querySelector(".refresh-button")).to.be.null
+
+  it 'renders empty courses if course list only contains arhived course', ->
+    CourseListingActions.loaded([STUDENT_ARCHIVED_COURSE])
+    renderListing().then (state) ->
+      expect(state.div.querySelector('.-course-list-empty')).not.to.be.null

   it 'renders course appropriate help', ->
     CourseListingActions.loaded(MASTER_COURSES_LIST)


define click handler directly

define click handler directly

also removed update event, was unused

--- js-library/app/js/models/fact.coffee
+++ js-library/app/js/models/fact.coffee
@@ -3,9 +3,10 @@

 class Factlink.Fact
   # If you want to support more events add them to this variable:
-  _events: ["focus", "blur", "click", "update"]
+  _events: ["focus", "blur"]

   constructor: (id, elems) ->
+    @id = id
     # Internal object which will hold all bound event handlers
     @_bound_events = {}

@@ -15,19 +16,17 @@

     @highlight highlight_time_on_load

-    @balloon = new Factlink.Balloon id, @
+    @balloon = new Factlink.Balloon @id, @

     # Bind the own events
     $(@elements)
       .on('mouseenter', @focus)
       .on('mouseleave', @blur)
-      .on('click', @click)
+      .on('click', (=> @click()))
       .on 'inview', (event, isInView, visiblePart) =>
         @highlight(highlight_time_on_in_view) if ( isInView && visiblePart == 'both' )

     @bindFocus()
-
-    @bindClick id

   # This may look like some magic, but here we expose the Fact.blur/focus/click
   # methods
@@ -70,7 +69,7 @@

   bindFocus: ->
     @focus (e) =>
-      clearTimeout(@timeout)
+      clearTimeout(@balloon_timeout)

       @highlight()

@@ -82,7 +81,7 @@
         @balloon.show($(e.target).offset().top, e.pageX, e.show_fast)

     @blur (e) =>
-      clearTimeout(@timeout)
+      clearTimeout(@balloon_timeout)

       unless @balloon.loading()
         @stopHighlighting()
@@ -90,14 +89,13 @@
         hideBalloon = =>
           @balloon.hide()

-        @timeout = setTimeout hideBalloon, 300
+        @balloon_timeout = setTimeout hideBalloon, 300

-  bindClick: (id) ->
-    @click =>
-      @balloon.startLoading()
+  click: ->
+    @balloon.startLoading()

-      Factlink.showInfo id, =>
-        @balloon.stopLoading()
+    Factlink.showInfo @id, =>
+      @balloon.stopLoading()

   destroy: ->
     for el in @elements


Allow to select staging.

Allow to select staging.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -1,7 +1,7 @@
 Import = require '../lib/import'
 fs = require 'fs'
 argv = require('optimist')
-  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --csv file')
+  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --csv file --staging')
   .describe('projectKey', 'your SPHERE.IO project-key')
   .describe('clientId', 'your OAuth client id for the SPHERE.IO API')
   .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
@@ -18,9 +18,11 @@
     client_id: argv.clientId
     client_secret: argv.clientSecret
   timeout: timeout
-  oauth_host: 'auth.escemo.com'
-  host: 'api.escemo.com'
-  rejectUnauthorized: false
+
+if argv.staging
+  options.oauth_host = 'auth_sphere_cloud.commercetools.de'
+  options.host = 'api_sphere_cloud.commercetools.de'
+  options.rejectUnauthorized = false

 importer = new Import options


Fix runner.

Fix runner.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -15,7 +15,7 @@
     client_secret: argv.clientSecret

 impl = new OrderDistribution options
-impl.getUnexportedOrders(rest).then (orders) ->
+impl.getUnexportedOrders(impl.masterRest).then (orders) ->
   impl.run orders, (msg) ->
     console.log msg
 .fail (msg) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/collidable.coffee
+++ src/collidable.coffee
@@ -98,7 +98,7 @@
             b.I.touching |= DOWN

       unless overlap.equal(Point.ZERO)
-        if !a.immovable() and !a.immovable()
+        if !a.immovable() and !b.immovable()
           a.changePosition(overlap.scale(-0.5))
           b.changePosition(overlap.scale(+0.5))


bug fix

bug fix

--- src/plugins/request-portals.coffee
+++ src/plugins/request-portals.coffee
@@ -235,7 +235,7 @@

                     async.each t.tile.gameEntities, (entity, callback) ->

-                        if t.tile.deletedGameEntityGuids.indexOf entity[0] is -1
+                        if t.tile.deletedGameEntityGuids.indexOf(entity[0]) is -1
                             Entity.add entity[0], entity[1], entity[2], (type) ->
                                 Tile.data[id].portals++ if type is 'portal'
                                 callback()


Fix componentWillReceiveProps

Fix componentWillReceiveProps

--- public/src/queue.cjsx
+++ public/src/queue.cjsx
@@ -35,9 +35,9 @@
     email: ''
     isPrivate: @props.isPrivate

-  componentWillReceiveProps: ->
+  componentWillReceiveProps: (nextProps) ->
     @setState
-      isPrivate: @props.isPrivate
+      isPrivate: nextProps.isPrivate

   submitAddOwner: (ev) ->
     ev.preventDefault()


Move cursor on block drop

Move cursor on block drop

--- src/controller.coffee
+++ src/controller.coffee
@@ -687,7 +687,11 @@
           if @lastHighlight is @tree
             @addMicroUndoOperation new DropOperation @draggingBlock, @tree.start
             @draggingBlock.moveTo @tree.start #MUTATION
-
+
+      # Move the cursor to the position we just
+      # dropped the block
+      @moveCursorTo @draggingBlock.end, true
+
       # Now that we've done that, we can annul stuff.
       @draggingBlock = null
       @draggingOffset = null


Fix palette hit-test bug and tab/shift-tab bug

--- src/controller.coffee
+++ src/controller.coffee
@@ -1236,16 +1236,18 @@
     if state.consumedHitTest then return

     palettePoint = @trackerPointToPalette point
-    for block in @currentPaletteBlocks
-      hitTestResult = @hitTest palettePoint, block
-
-      if hitTestResult?
-        @clickedBlock = block
-        @clickedPoint = point
-        @clickedBlockIsPaletteBlock = true
-        state.consumedHitTest = true
-        return
-
+    if @scrollOffsets.palette.y < palettePoint.y < @scrollOffsets.palette.y + @paletteCanvas.height and
+       @scrollOffsets.palette.x < palettePoint.x < @scrollOffsets.palette.x + @paletteCanvas.width
+
+      for block in @currentPaletteBlocks
+        hitTestResult = @hitTest palettePoint, block
+
+        if hitTestResult?
+          @clickedBlock = block
+          @clickedPoint = point
+          @clickedBlockIsPaletteBlock = true
+          state.consumedHitTest = true
+          return

     @clickedBlockIsPaletteBlock = false

@@ -1951,35 +1953,26 @@
     @moveCursorTo @cursor.next.next
     @scrollCursorIntoPosition()

-  hook 'key.shift tab', 0, ->
-    if @socketFocus?
-      head = @socketFocus.start
-
+  hook 'key.tab', 0, ->
+    if @shiftKeyPressed
+      if @socketFocus? then head = @socketFocus.start
+      else head = @cursor
+
+      until (not head?) or head.type is 'socketEnd' and head.container.start.next.type is 'text'
+        head = head.prev
+      if head?
+        @setTextInputFocus head.container, -1, -1
+      return false
+
     else
-      head = @cursor
-
-    until (not head?) or head.type is 'socketEnd' and head.container.start.next.type is 'text'
-      head = head.prev
-
-    if head?
-      @setTextInputFocus head.container, -1, -1
-
-    return false
-
-  hook 'key.tab', 0, ->
-    if @socketFocus?
-      head = @socketFocus.end
-
-    else
-      head = @cursor
-
-    until (not head?) or head.type is 'socketStart' and head.container.start.next.type is 'text'
-      head = head.next
-
-    if head?
-      @setTextInputFocus head.container
-
-    return false
+      if @socketFocus? then head = @socketFocus.end
+      else head = @cursor
+
+      until (not head?) or head.type is 'socketStart' and head.container.start.next.type is 'text'
+        head = head.next
+      if head?
+        @setTextInputFocus head.container
+      return false

   Editor::deleteAtCursor = ->
     # Unfocus any inputs, which could get in the way.


removed extra pie tooltips

removed extra pie tooltips

--- app/assets/javascripts/pop-over.js.coffee
+++ app/assets/javascripts/pop-over.js.coffee
@@ -16,8 +16,9 @@
       placement: "right",
       trigger: "manual"
       })
-#adds bootstrap popovers to preview pies
+#adds bootstrap tooltip to preview pies
 $ ->
-  $(".pie").tooltip
-    placement: "top",
-    title: "Click to see more."+  if $("body.groups.show").length > 0 || $("body.dashboard.show").length > 0
+    $(".pie").tooltip
+      placement: "top",
+      title: "Click to see more."


Fixed calculating of table height

Fixed calculating of table height

--- app/assets/javascripts/students.js.coffee
+++ app/assets/javascripts/students.js.coffee
@@ -37,7 +37,7 @@
 #    ***************************************

     have_hscroll_bar = (parent, child) ->
-      child.innerWidth() >= parent.innerWidth()
+      child.innerWidth() > parent.innerWidth()

     marks_table.scroll( ->
       lessons_table.scrollLeft(marks_table.scrollLeft())


Remove useless console.log

Remove useless console.log

--- app/coffee/modules/kanban/sortable.coffee
+++ app/coffee/modules/kanban/sortable.coffee
@@ -43,7 +43,6 @@
             windowHeight = angular.element(window).height()
             columnHeight = windowHeight - elementOffset - mainPadding

-            console.log elementOffset, windowHeight, columnHeight
             $el.css("height", "#{columnHeight}px")

         oldParentScope = null


fix for tinymce 4.1.9 setBaseAndExtent() error on chrome 58

fixes CNVS-36555

test plan:
- load tinymce NOT through RCS (try quizzes)
- on the sidepanel, select an image to embed in the textarea
- click on the image in the text area
- make sure the image is selected and the footer shows `p >> img`
- rejoice!

Change-Id: I39a61d171065dca639ebd8dc262acd8247792fda
Reviewed-on: https://gerrit.instructure.com/110586
Tested-by: Jenkins
QA-Review: Jeremy Putnam <b030d6d6f199f844ec424882be473c2ab9adc93b@instructure.com>
Reviewed-by: Tyler Pickett <3f20fcf21b1970c94966160adcf831eedd808a06@instructure.com>
Product-Review: brian kirkby <9a8a656cc2d0f066b1a373934e1a6a0652844ecd@instructure.com>

--- app/coffeescripts/editor/stocktiny.coffee
+++ app/coffeescripts/editor/stocktiny.coffee
@@ -31,4 +31,17 @@
   # prevent tiny from loading any CSS assets
   tinymce.DOM.loadCSS = ->

+  # CNVS-36555 fix for setBaseAndExtent() bug in tinymce ~4.1.4 - 4.5.3 .
+  # replace when we upgrade tinymce to 4.5.4+. see CNVS-36555
+  _init = tinymce.Editor.prototype.init
+  tinymce.Editor.prototype.init = ->
+     ret = _init.apply(this,arguments)
+     editor = this
+     _tmceSel = editor.getWin().Selection.prototype
+     _tmceSel.setBaseAndExtent = (an, ao, focusNode, focusOffset) ->
+       if !focusNode.hasChildNodes() and focusOffset==1 and editor.dom.getContentEditableParent(focusNode) isnt "false"
+         editor.selection.select(focusNode)
+     ret
+  # end CNVS-36555
+
   tinymce


Better encryption cache management

Better encryption cache management

--- app/src/utils/storage/secure_store.coffee
+++ app/src/utils/storage/secure_store.coffee
@@ -7,28 +7,20 @@
     super(name)
     @_aes = new ledger.crypto.AES(key)
     @_hasCalledKeys = no
-    @_keysCache = []

   # @see Store.keys
   keys: (cb) ->
-    if @_hasCalledKeys
-      _.defer => cb?(@_keysCache)
-    else
-      super (encrypted_keys) =>
-        @_keysCache = _.uniq(@_keysCache.concat(_.compact(Try(=> @_aes.decrypt(encrypted_key)).orNull() for encrypted_key in encrypted_keys)))
-        @_hasCalledKeys = yes
-        cb?(@_keysCache)
-
-
+    super (encrypted_keys) =>
+      cb?(_.compact(@__decryptKey(encrypted_key) for encrypted_key in encrypted_keys))

   _preprocessKey: (key) ->
-    @_keysCache.push key unless _(@_keysCache).contains(key)
     super(@_aes.encrypt(key))

   _deprocessKey: (raw_key) ->
-    key = Try(=> @_aes.decrypt(super(raw_key))).orNull()
-    @_keysCache.push key unless _(@_keysCache).contains(key)
+    key = @__decryptKey(super raw_key)
     key

   _preprocessValue: (value) -> Try(=> @_aes.encrypt(super(value))).orNull()
   _deprocessValue: (raw_value) -> Try(=> super(@_aes.decrypt(raw_value))).orNull()
+
+  __decryptKey: (value) -> (@_decryptCache ||= {})[value] ||= (Try(=> @_aes.decrypt(value)).orNull())


Remove unnessecary local variable sampsize.

--- app/static/coffee/production-chart.coffee
+++ app/static/coffee/production-chart.coffee
@@ -2,16 +2,13 @@

   # Read CSV file: first row =>  labels
   maxval = 0
-  sampsize = 0
   reserve_scale = 0.01

   labels = new Array()
   values = new Array()

-  sampsize = data.length
-
   i = 0
-  while i <= sampsize - 1
+  while i <= data.length - 1
     labels[i] = parseInt(data[i]["Year"])
     values[i] =
       x: labels[i]
@@ -42,7 +39,7 @@
   p = 30
   x = d3.scale.linear().domain([
     labels[0]
-    labels[sampsize - 1]
+    labels[data.length - 1]
   ]).range([
     0
     w


might as well

might as well

--- apps/artwork/client/auction_detail.coffee
+++ apps/artwork/client/auction_detail.coffee
@@ -51,7 +51,8 @@
     ).addClass 'is-fade-in'
     @

-  openBuyersPremiumModal: ->
+  openBuyersPremiumModal: (e) ->
+    e.preventDefault()
     new ModalPageView
       width: '700px'
       pageId: 'buyers-premium'


Fix loading demo documents

Fix loading demo documents

--- assets/inspector/source_panel_view.coffee
+++ assets/inspector/source_panel_view.coffee
@@ -9,8 +9,8 @@
     event.preventDefault()
     {documentKey} = target.dataset
     document = Trix.Document.fromJSON(window.trixDocuments[documentKey])
-    @editorController.undoManager.recordUndoEntry("Load Document", context: documentKey)
-    @editorController.document.replaceDocument(document)
+    @editor.undoManager.recordUndoEntry("Load Document", context: documentKey)
+    @editor.document.replaceDocument(document)

   render: ->
     html = ""


ManageDomainsView::addDomain implementation is done [#78676742]

--- client/Main/domains/managedomainsview.coffee
+++ client/Main/domains/managedomainsview.coffee
@@ -17,7 +17,7 @@
       type              : 'text'
       attributes        :
         spellcheck      : no
-      callback          : => @emit 'AddDomain'
+      callback          : @bound 'addDomain'

     @inputView.addSubView new KDView
       partial           : @domainSuffix
@@ -36,18 +36,43 @@
     @addSubView @domainController.getView()
     @inputView.hide()

-    @on 'AddDomain', =>
-
-      @addDomain "#{@input.getValue()}#{domainSuffix}"
-      @input.setValue ""
-
-      @inputView.hide()
-      @emit "DomainInputCancelled"

-    domainList = @domainController.getListView()
-    domainList.on 'DeleteDomainRequested', (item)=>
-      @domainController.removeItem item
+
+
+
+  addDomain:->
+
+    {computeController} = KD.singletons
+
+    domain = "#{Encoder.XSSEncode @input.getValue().trim()}#{@domainSuffix}"
+    machineId = @machine._id
+
+    @loader.show()
+    @warning.hide()
+    @input.makeDisabled()
+
+    computeController.kloud
+
+      .addDomain {domainName: domain, machineId}
+
+      .then =>
+        @domainController.addItem { domain, machineId }
+        computeController.domains = []
+
+        @input.setValue ""
+        @inputView.hide()
+        @emit "DomainInputCancelled"
+
+      .catch (err)=>
+        warn "Failed to create domain:", err
+        @warning.setTooltip title: err.message
+        @warning.show()
+        @input.setFocus()
+
+      .finally =>
+        @input.makeEnabled()
+        @loader.hide()

   toggleInput:->


pricing: emit "Cancel" event through upgrade form

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -87,8 +87,8 @@
       cssClass     : "buy-packs"
       style        : "solid green medium"
       title        : "Buy Resource Packs"
-      callback     : =>
-        @emit 'Cancel'
+      callback     : ->
+        @parent.emit "Cancel"
         KD.singleton("router").handleRoute "/Pricing"

     return new JView


payment: prefer planApi provided in the options

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -182,9 +182,8 @@
   createSubscription: (options, callback) ->
     { plan, planOptions, promotionType, paymentMethod } = options
     { paymentMethodId } = paymentMethod
+    planOptions ?= {}
     { planApi } = planOptions
-
-    throw new Error "Must provide a plan API!"  unless planApi?

     options = {
       planOptions
@@ -193,7 +192,10 @@
       planCode: plan.planCode
     }

-    planApi.subscribe options, callback
+    if planApi
+      planApi.subscribe options, callback
+    else
+      plan.subscribe paymentMethodId, planOptions, callback

   transitionSubscription: (formData, callback) ->
     { productData, oldSubscription, promotionType, paymentMethod, createAccount, email } = formData


Ensure .env is set

Ensure .env is set

--- js-library/app/js/loader/loader_common.coffee
+++ js-library/app/js/loader/loader_common.coffee
@@ -126,6 +126,7 @@
   window.__internalFactlinkState = (args...) ->
     root.public_events.trigger(args...)
     return
+  window.__internalFactlinkState.env = this_env

   for args in queuedEvents
     window.__internalFactlinkState(args...)


don't hardcode help@sagemath.com...

don't hardcode help@sagemath.com...

--- src/smc-webapp/account_flux.coffee
+++ src/smc-webapp/account_flux.coffee
@@ -31,7 +31,7 @@
             cb            : (error, mesg) =>
                 @setTo(signing_in: false)
                 if error
-                    @setTo(sign_in_error : "There was an error signing you in (#{error}).  Please try again; if that doesn't work after a few minutes, email help@sagemath.com.")
+                    @setTo(sign_in_error : "There was an error signing you in (#{error}).  Please try again; if that doesn't work after a few minutes, email #{help()}.")
                     return
                 switch mesg.event
                     when 'sign_in_failed'


Fix broken stuff

Fix broken stuff

--- resources/assets/coffee/react/_components/countdown-timer.coffee
+++ resources/assets/coffee/react/_components/countdown-timer.coffee
@@ -49,10 +49,10 @@
     diff = @state.diff / 1000

     fields =
-      osu.trans('common.countdown.days'): Math.floor(diff / (60 * 60 * 24))
-      osu.trans('common.countdown.hours'): Math.floor((diff / (60 * 60)) % 24)
-      osu.trans('common.countdown.minutes'): Math.floor((diff / 60) % 60)
-      osu.trans('common.countdown.seconds'): Math.floor(diff % 60)
+      days: Math.floor(diff / (60 * 60 * 24))
+      hours: Math.floor((diff / (60 * 60)) % 24)
+      minutes: Math.floor((diff / 60) % 60)
+      seconds: Math.floor(diff % 60)

     div className: bn,
       div className: "#{bn}__header", "#{osu.trans('common.time.remaining')}:"
@@ -60,5 +60,5 @@
         div key: field, className: "#{bn}__field",
           div className: "#{bn}__digit",
             if value < 10 then "0#{value}" else value
-          div className: "#{bn}__label", field
+          div className: "#{bn}__label", osu.trans("common.countdown.#{field}")


Fix max count label

Fix max count label

--- src/unreadNotificationLabel.coffee
+++ src/unreadNotificationLabel.coffee
@@ -1,13 +1,10 @@
 TemplateClass = Template.unreadNotificationLabel
-
-# Limit value to avoid excessive width.
-MAX_COUNT = 1000

 TemplateClass.helpers
   value: ->
     value = getValue()
     return unless value?
-    if value > MAX_COUNT then (MAX_COUNT - 1) + '+' else value
+    if value >= UserEventStats.MAX_COUNT then (UserEventStats.MAX_COUNT - 1) + '+' else value
   hasValue: ->
     value = getValue()
     value? and value > 0


remove unused test

remove unused test

--- spec/javascripts/support/annotation_document_manager_spec.coffee
+++ spec/javascripts/support/annotation_document_manager_spec.coffee
@@ -123,32 +123,3 @@
       @manager.saveDocumentPayload(documentPayload, responseProcesor)
       expect(successStatus).toBe true
     )
-
-    xit('integration test - manipulates Dalphi\'s annotation document', ->
-      # to be discussed: shall we write integration tests from JS client's perspective?
-      expect(@manager.documentStore.length).toBe 1
-      documentPayload = @manager.next()
-      expect(documentPayload).toBe @manager.currentDocument.payload
-      documentId = @manager.currentDocument.id
-
-      expect(documentPayload.options[0]).toBeDefined()
-      targetOption = documentPayload.options[0]
-
-      expect(documentPayload.label).not.toBe targetOption
-      documentPayload.label = targetOption
-      @manager.saveDocumentPayload(documentPayload)
-
-      response = undefined
-      $.ajax
-        type: 'GET',
-        url: "#{@dalphiUrl}/api/v1/annotation_documents/#{documentId}",
-        dataType: 'json',
-        async: false,
-        success: (data) ->
-          response = data
-        error: ->
-          console.log 'error requesting annotation document\'s API (GET)'
-
-      expect(response).toBeDefined()
-      expect(response.payload.label).toBe targetOption
-    )


[grunt] rename `resolveLocaleDir` function into `getLocaleDir`, improve arguments' name

--- tasks/grunt-nunjucks-2-html.coffee
+++ tasks/grunt-nunjucks-2-html.coffee
@@ -34,9 +34,9 @@
   localesDir = grunt.config('path.source.locales')

-  # Output or not locale name based on whether it's base locale or not.
-  resolveLocaleDir = (loc) ->
-    urlify(if loc == baseLocale then '' else loc)
+  # Output or not locale's dir name based on whether it's base locale or not.
+  getLocaleDir = (locale) ->
+    urlify(if locale == baseLocale then '' else locale)

   # Load and invoke content of l10n files
@@ -54,7 +54,7 @@
   Task = ->
     # Define targets, with unique options and files, for each locale
     locales.forEach (currentLocale) =>
-      localeDir = resolveLocaleDir(currentLocale)
+      localeDir = getLocaleDir(currentLocale)

       @[currentLocale] = {}

@@ -136,7 +136,7 @@
            * @example blog link
           ###
           env.addGlobal 'localeDir', (locale = currentLocale) ->
-            localeDir = resolveLocaleDir(locale)
+            localeDir = getLocaleDir(locale)
             if localeDir then '/' + localeDir else ''

           ###*


Fixed a typo

Fixed a typo

--- client/app/CommonViews/paymentwidgets.coffee
+++ client/app/CommonViews/paymentwidgets.coffee
@@ -11,7 +11,7 @@
     @buttonSubscribe = new KDButtonView
       title    : "Subscribe"
       cssClass : "hidden"
-      callback : => @confirmSubscription ->
+      callback : => @confirmSubscription =>
                       @subscribe (err, sub)=>
                         @updateButtons()


Add action to set video state in chat view

--- client/ide/lib/views/chat/idechatview.coffee
+++ client/ide/lib/views/chat/idechatview.coffee
@@ -117,6 +117,15 @@
     @unsetClass 'loading'

+  setVideoActiveState: (state) ->
+
+    if state
+    then @setClass 'is-videoActive'
+    else @unsetClass 'is-videoActive'
+
+    kd.utils.defer @bound 'focus'
+
+
   handleVideoActive: ->

     @setClass 'is-videoActive'
@@ -126,9 +135,9 @@

   handleVideoEnded: ->

+    @setVideoActiveState off
     @chatPane.handleVideoEnded()
     @chatVideoView.hide()
-    @unsetClass 'is-videoActive'

   getVideoView: -> @chatVideoView


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -149,14 +149,14 @@

         #console.log('@oldPieData',@oldPieData)
         #@oldPieData = 1
-        console.log('@oldPieData',@oldPieData)
+        console.log('@oldPieData before the tween',@oldPieData)

         path = pathGroup.selectAll('path.pie').data(piedata)
         path.enter().append('path').attr('class', 'pie').attr('fill', (d, i) -> color i)

         path.transition().duration(750).attrTween('d', (d,i) ->
             theOldDataInPie = @oldPieData ? piedata
-            console.log('@oldPieData',@oldPieData)
+            console.log('@oldPieData in the tween',@oldPieData)
             # Interpolate the arcs in data space
             s0 = undefined
             e0 = undefined


Notify model when field blurs

Notify model when field blurs

--- client/lanes/components/shared/Input.cjsx
+++ client/lanes/components/shared/Input.cjsx
@@ -35,25 +35,36 @@
     selectOnFocus: (ev) ->
         ev.target.select()

+    onFieldBlur: ->
+        @onFieldInteraction()
+        @props.onBlur?()
+
     renderEdit: (label) ->
         value = @state.pendingValue or @props.value or @_getValue()
         label ||= @props.label or _.field2title(@props.name)
+
         props = _.extend({
             ref:       'input'
             className: _.classnames('value', changeset: @state.changeset)
             label:     if @props.unlabeled then false else label
-            value:     value
-            onKeyDown: @handleKeyDown if @props.onEnter
             onChange:  @validatedChangeHandler
         }, @props, {value: value})
-        if @props.selectOnFocus then props.onFocus = @selectOnFocus
-        if @props.inputOnly then @renderPlain(props) else @renderStyled(props, label)

-    renderPlain: (props) ->
-        <input {...props}/>
+        handlers = { onBlur: @onFieldBlur }

-    renderStyled: (props, label) ->
+        if @isFieldValueInvalid() then props.bsStyle   = 'error'
+        if @props.onEnter         then handlers.onKeyDown = @handleKeyDown
+        if @props.selectOnFocus   then handlers.onFocus   = @selectOnFocus
+
+        if @props.inputOnly then @renderPlain(props, handlers) else @renderStyled(props, handlers, label)
+
+    renderPlain: (props, handlers) ->
+        <input {...props} {...handlers} />
+
+    renderStyled: (props, handlers, label) ->
+
         colProps = _.omit(props, 'name')
+
         <BS.Col {...colProps} >
-            <BS.Input {...props} />
+            <BS.Input {...props} {...handlers} />
         </BS.Col>


Remove policy change banner

Remove policy change banner

--- components/main_layout/header/view.coffee
+++ components/main_layout/header/view.coffee
@@ -16,7 +16,6 @@
 Cookies = require '../../cookies/index.coffee'
 MobileHeaderView = require './mobile_header_view.coffee'
 bundleTemplate = -> require('./templates/bundles.jade') arguments...
-maybePopUpPolicyNotice = require './policy.coffee'

 module.exports = class HeaderView extends Backbone.View
   events:
@@ -26,7 +25,6 @@
     'click .mlh-logout': 'logout'

   initialize: ->
-    maybePopUpPolicyNotice() # TODO: REMOVE AFTER 01/25/15
     dealWithWelcomeBanner()
     @currentUser = CurrentUser.orNull()
     @checkForNotifications()


getBuffer with index support

getBuffer with index support

--- src/lib/InPort.coffee
+++ src/lib/InPort.coffee
@@ -148,12 +148,17 @@
     @buffer.filter((packet) -> return true if packet.event is 'data').length

   getBuffer: (scope, idx) ->
+    if @isAddressable()
+      if ip.scope?
+        return undefined unless scope of @scopedBuffer
+        return undefined unless idx of @scopedBuffer[scope]
+        return @scopedBuffer[scope][idx]
+      return undefined unless idx of @indexedBuffer
+      return @indexedBuffer[idx]
     if scope?
       return undefined unless scope of @scopedBuffer
-      buf = @scopedBuffer[scope]
-    else
-      buf = @buffer
-    return buf
+      return @scopedBuffer[scope]
+    return @buffer

   # Fetches a packet from the port
   get: (scope, idx) ->


Added flag for custom error pages

Added flag for custom error pages

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -328,6 +328,10 @@
 		# Extend Server
 		# Whether or not we should extend the server with extra middleware and routing
 		extendServer: true
+
+		# Enable Custom Error Pages
+		# A flag to provide an entry to handle custom error pages
+		customErrors: false

 		# Port
 		# The port that the server should use
@@ -2412,7 +2416,7 @@
 									contentRendered = document.get('contentRendered')
 									if err
 										docpad.error(err)
-										return res.send(err.message, 500)
+										return next(err)
 									else
 										return res.send(contentRendered)
 							else
@@ -2429,6 +2433,8 @@

 						# 404 Middleware
 						server.use (req,res,next) ->
+							if config.customErrors
+								return next()
 							return res.send(404)

 				# Start the Server


chore: add flag for LOG_RESOLVER

chore: add flag for LOG_RESOLVER

--- app/config/app.coffee
+++ app/config/app.coffee
@@ -8,6 +8,7 @@
     LOG_BINDINGS:                   true
     LOG_VIEW_LOOKUPS:               true
     LOG_ACTIVE_GENERATION:          true
+    LOG_RESOLVER:                   false

   Ember.RSVP.configure 'onerror', (error) ->
     if Ember.typeOf(error) is 'object'


Remove cors middleware

Remove cors middleware

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -2,8 +2,7 @@

 require 'coffee-cache'

-{ argv }      = require 'optimist'
-cors          = require 'cors'
+{ argv } = require 'optimist'

 Object.defineProperty global, \
   'KONFIG', { value : require 'koding-config-manager' }
@@ -85,7 +84,7 @@
 app.get  '/-/my/permissionsAndRoles'             , require './handlers/myPermissionsAndRoles'
 app.get  '/-/google-api/authorize/drive'         , require './handlers/authorizedrive'
 app.post '/-/support/new', bodyParser.json()     , require './handlers/supportnew'
-app.post '/-/wufoo/submit/:identifier?'          , cors(), require './handlers/wufooproxy'
+app.post '/-/wufoo/submit/:identifier?'          , require './handlers/wufooproxy'
 # should deprecate those /Validates, they don't look like api endpoints
 app.post '/:name?/Validate/Username/:username?'  , require './handlers/validateusername'
 app.post '/:name?/Validate/Email/:email?'        , require './handlers/validateemail'
@@ -121,8 +120,8 @@
 # app.get  '/-/oauth/github/callback'              , require './github_callback'
 app.get  '/-/oauth/gitlab/callback'              , require './gitlab_callback'

-app.get '/-/terraform/document-search/:query'    , cors(), require './handlers/stackscripttitle'
-app.get '/-/terraform/document-content/:query'   , cors(), require './handlers/stackscriptmarkdown'
+app.get '/-/terraform/document-search/:query'    , require './handlers/stackscripttitle'
+app.get '/-/terraform/document-content/:query'   , require './handlers/stackscriptmarkdown'

 # app.get  '/-/oauth/facebook/callback'            , require './facebook_callback'
 # app.get  '/-/oauth/google/callback'              , require './google_callback'


add/modify request lib for easy requesting from ui

--- newebe/client/app/lib/request.coffee
+++ newebe/client/app/lib/request.coffee
@@ -10,7 +10,7 @@
         success: (data) ->
             callback(null, data) if callback?
         error: ->
-            if data.msg? and callback?
+            if data? and data.msg? and callback?
                 callback new Error data.msg
             else if callback?
                 callback new Error "Server error occured"


DRY up extension checks

DRY up extension checks

--- src/ls-archive.coffee
+++ src/ls-archive.coffee
@@ -130,21 +130,38 @@
   entry.on 'data', (data) -> contents.push(data)
   entry.on 'end', -> callback(null, Buffer.concat(contents).toString())

+isTarExtension = (extension) ->
+  extension is '.tar'
+
+isZipExtension = (extension) ->
+  extension is '.zip' or extension is '.jar'
+
+isGzipExtension = (extension) ->
+  extension is '.gz' or extension is '.tgz'
+
 module.exports =
   list: (archivePath, callback) ->
-    switch path.extname(archivePath)
-      when '.tar' then listTar(archivePath, wrapCallback(callback))
-      when '.gz', '.tgz' then listGzip(archivePath, wrapCallback(callback))
-      when '.zip', '.jar' then listZip(archivePath, wrapCallback(callback))
-      else callback(new Error("'#{path.extname(archivePath)}' files are not supported"))
+    extension = path.extname(archivePath)
+    if isTarExtension(extension)
+      listTar(archivePath, wrapCallback(callback))
+    else if isGzipExtension(extension)
+      listGzip(archivePath, wrapCallback(callback))
+    else if isZipExtension(extension)
+      listZip(archivePath, wrapCallback(callback))
+    else
+      callback(new Error("'#{path.extname(archivePath)}' files are not supported"))
     undefined

   readFile: (archivePath, filePath, callback) ->
-    switch path.extname(archivePath)
-      when '.tar' then readFileFromTar(archivePath, filePath, wrapCallback(callback))
-      when '.gz', '.tgz' then readFileFromGzip(archivePath, filePath, wrapCallback(callback))
-      when '.zip', '.jar' then readFileFromZip(archivePath, filePath, wrapCallback(callback))
-      else callback(new Error("'#{path.extname(archivePath)}' files are not supported"))
+    extension = path.extname(archivePath)
+    if isTarExtension(extension)
+      readFileFromTar(archivePath, filePath, wrapCallback(callback))
+    else if isGzipExtension(extension)
+      readFileFromGzip(archivePath, filePath, wrapCallback(callback))
+    else if isZipExtension(extension)
+      readFileFromZip(archivePath, filePath, wrapCallback(callback))
+    else
+      callback(new Error("'#{path.extname(archivePath)}' files are not supported"))
     undefined

   readGzip: (gzipArchivePath, callback) ->


Tests: Change test names.

Tests: Change test names.

--- tests/src/activity/activity.coffee
+++ tests/src/activity/activity.coffee
@@ -282,7 +282,7 @@
     browser.end()

-  commentWithCode: (browser) ->
+  postCommentWithCode: (browser) ->

     helpers.beginTest(browser)

@@ -301,7 +301,7 @@
       .end()

-  postMessageWithImage: (browser) ->
+  postCommentWithImage: (browser) ->

     helpers.beginTest(browser)


Forwarding aws headers

Forwarding aws headers

--- routes/buckets.coffee
+++ routes/buckets.coffee
@@ -30,6 +30,7 @@
           message: "Cannot retrieve file: ", err.message
           error: {}
     else
+      res.set awsRes.headers
       awsRes.pipe res

 router.get '/content/*', ensureLoggedIn('/'), returnFile BUCKETS[0]


correct implementation of state update

correct implementation of state update

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -2661,24 +2661,21 @@
             dbg("querying db")
             sqlite_db.select
                 table   : 'projects'
-                where   :
-                    state : 'starting'
-                columns : ['project_id', 'state_time']
+                columns : ['project_id', 'state_time', 'state']
                 cb      : (err, x) ->
                     if err
                         dbg("query err=#{misc.to_safe_str(err)}")
                         cb(err)
                     else
-                        projects = x
-                        dbg("got #{projects.length} projects that are 'starting'")
+                        projects = (a for a in x when a.state == 'running' or a.state = 'starting' or a.state == 'stopping' or a.state == 'saving')
+                        dbg("got #{projects.length} projects that are '....ing'")
                         cb()
         (cb) ->
             if projects.length == 0
                 cb(); return
             dbg("possibly updating each of #{projects.length} projects")
-            cutoff = new Date() - 1000*STATES.starting.timeout
             f = (x, cb) ->
-                if x.state_time >= cutoff
+                if x.state_time >= new Date() - 1000*STATES[x.state].timeout
                     dbg("not updating #{x.project_id}")
                     cb()
                 else
@@ -2689,7 +2686,7 @@
                             if err
                                 cb(err)
                             else
-                                project.status(force:true, update:true, cb:cb)
+                                project.state(update:true, cb:cb)
             async.map(projects, f, cb)
         ], (err) ->
             setTimeout(update_states, 2*60*1000)


3d: make some changes to loading to reduce (to zero?) the possibility of a race condition.

--- salvus/page/3d.coffee
+++ salvus/page/3d.coffee
@@ -44,11 +44,13 @@
 $.ajaxSetup(cache: true) # when using getScript, cache result.

 load_threejs = (cb) ->
+    if Detector?
+        cb(); return
     _loading_threejs_callbacks.push(cb)
     #console.log("load_threejs")
     if _loading_threejs_callbacks.length > 1
         #console.log("load_threejs: already loading...")
-        return
+        return  # will get called later below

     load = (script, name, cb) ->
         if typeof(name) != 'string'
@@ -81,7 +83,7 @@
         (cb) -> load("/static/threejs/r#{VERSION}/Detector.min.js", cb)
         (cb) ->
             f = () ->
-                if THREE?
+                if Detector?
                     cb()
                 else
                     #console.log("load_threejs: waiting for THREEJS...")
@@ -93,8 +95,6 @@
             cb(err)
         _loading_threejs_callbacks = []
     )
-
-window.load_threejs = load_threejs

 _scene_using_renderer  = undefined
@@ -140,13 +140,15 @@
             foreground      : undefined
             spin            : false      # if true, image spins by itself when mouse is over it.
             camera_distance : 10
-            aspect_ratio    : undefined  # undefined does nothing or a triple [x,y,z] of length three, which scales the x,y,z coordinates of everything by the given values.
+            aspect_ratio    : undefined  # undefined does nothing or a triple [x,y,z] of length three,
+                                         # which scales the x,y,z coordinates of everything by the given values.
             stop_when_gone  : undefined  # if given, animation, etc., stops when this html element (not jquery!) is no longer in the DOM
             frame           : undefined  # if given call set_frame with opts.frame as input when init_done called
             cb              : undefined  # opts.cb(undefined, this object)

         @init_eval_note()
-        opts.cb?(undefined, @)
+        load_threejs () =>
+            opts.cb?(undefined, @)

     # client code should call this when start adding objects to the scene
     init: () =>


Corrected ajax request, parse response data

--- rasp-rails/app/assets/javascripts/init.coffee
+++ rasp-rails/app/assets/javascripts/init.coffee
@@ -1,16 +1,25 @@
 class Init
   constructor: ->
+
+    $.ajaxSetup headers:
+      "salt": "1235"
+      # "hash": "1e1a642f16892ef7ca87b2307adafc09"
+      "hash": "57b2e61b964e5ccdfd34d687db049885"
+
     @getTemperature()

   getTemperature: ->
-    $.ajax(
-      url: "http://uj-rasp.no-ip.org/api/temperature"
+
+    $.ajax
+      # url: "http://uj-rasp.no-ip.org/api/temperature"
+      url: "http://localhost:8080/api/temperature"
       type: "GET"
       dataType: "json"
-    ).complete (data) ->
-      # console.log data.responseText
-      $('#temperature-value').html(data.responseText)
-      return
+      crossDomain: true
+      complete: (data) ->
+        data = JSON.parse(data.responseText);
+        $('#temperature-value').html(data.value)
+        return

 jQuery ->
   window.init = new Init()


Playing with tests, not getting anywhere.

--- spec/widgets/toggle.spec.coffee
+++ spec/widgets/toggle.spec.coffee
@@ -1,7 +1,18 @@
 describe 'toggle widget', ->
+
+  beforeEach ->
+    # console.log("Hi")
+
+  afterEach ->
+    # console.log("Bye")
+
   it 'contains spec with an expectation', ->
     expect(true).toBe(true)

   it 'initializes an instance of the toggle widget', ->
-    toggleWidget = ($ "

").toggle()
+    element = ($ """<input type="checkbox">""").appendTo('body')
+    console.log('element', element)
+    toggleWidget = element.toggle()
     console.log('toggleWidget', toggleWidget)
+    element.trigger('click');
+    # console.log('body', document.body)


Enable server debug mode

Enable server debug mode

--- src/pow/server.coffee
+++ src/pow/server.coffee
@@ -28,7 +28,7 @@

   createApplicationPool: (config) ->
     root = path.dirname config
-    pool = createPool config, size: 3, idle: idle, cwd: root
+    pool = createPool config, size: 3, idle: idle, cwd: root, debug: true

     # TODO: Pump this to a file
     sys.pump pool.stdout, process.stdout


Add more bounties debugging logs

Add more bounties debugging logs

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -356,6 +356,7 @@
       !response.definitions.vendorDetails or
       !response.definitions.vendorDetails[vendorHash] or
       !response.definitions.vendorDetails[vendorHash].vendorName
+        console.log response.definitions.vendorDetails[vendorHash].vendorName
         console.log 'Error getting vendor details: ' + vendorHash
         return deferred.reject()


Update rocketchat.coffee

Update rocketchat.coffee

one unnecessary TAB on 95 line

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -92,7 +92,7 @@
 						@robot.logger.error "If joining GENERAL please make sure its using all caps"
 				)

-				@emit 'connected'
+			@emit 'connected'

 	send: (envelope, strings...) =>
 			@chatdriver.sendMessage(str, envelope.room) for str in strings


Switch global.coffee to DOMContentLoaded

--- scripts/global.coffee
+++ scripts/global.coffee
@@ -50,7 +50,7 @@
 window.switchTheme = switchTheme

 confirmCallback = null
-window.addEventListener('load', ->
+window.addEventListener('DOMContentLoaded', ->
     feedback = document.getElementById('feedback').querySelector('div')
     feedbackToggle = document.getElementById('toggle-feedback')
     feedbackToggle.addEventListener('click', (e) ->


Save points in brain.

Save points in brain.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -103,7 +103,7 @@

             for username in people
                 points[username] ?= 0
-                points[username] += 5
+                robot.brain.set points[username] += 5
                 save(robot)
     ), 60000

@@ -126,5 +126,5 @@
     robot.respond /allpoints$/i, (msg) ->
         if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])

-            allpoints = [robot.brain.get('points')]
+            allpoints = [robot.brain.get(points)]
             msg.send "Stored points: #{allpoints}"


Specify on both ends.

Specify on both ends.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -86,6 +86,6 @@
         robot.http("https://tmi.twitch.tv/group/user/masonest/chatters")
             .get() (err, res, body) ->
                 chat = JSON.parse(body)
-                chatters = if chat.chatters.moderators then "Current chatters: #{chat.chatters}." else "Whoops, try again."
+                chatters = if chat.moderators then "Current chatters: #{chat.moderators}." else "Whoops, try again."
                 msg.send "#{chatters}"


senpai aisatsu, and fix

senpai aisatsu, and fix

--- scripts/senpai.coffee
+++ scripts/senpai.coffee
@@ -13,12 +13,20 @@
 #   {アダ名 or nickname}--  - ヨクナイネ！
 #   hubot {アダ名 or nickname} 何点ですか?  - hubot に後輩の点数を教えてもらおう!

-
-AISATSU = [# {{{
+# {{{ AISATSU
+AISATSU_YOROSHIKU = [
   'よろしくな'
   'まいど'
   '元気か？'
-]# }}}
+]
+
+AISATSU_OHAYO = [
+  'おう、おはよう。今日はがんばれよ'
+  'おはよう、ちょっとネムいな'
+  '今日飲みに行くか'
+  'おはよう。今日金曜日だっけ？'
+]
+#}}}

 NANDE_TAMEGUCHI = [# {{{
   'つか、何でタメ口なん？'
@@ -95,7 +103,8 @@
   str.replace new RegExp('' + REGEXP_KEIGO + '$'), ''

 checkKeigo = (robot, msg) =>
-  msg.send msg.random NANDE_TAMEGUCHI unless isKeigo robot, msg
+  unless isKeigo robot, msg
+    msg.send msg.random NANDE_TAMEGUCHI if Math.random 0 < 0.1
 # }}}

 # {{{ Keisho
@@ -110,13 +119,21 @@
     robot.brain.data.usersInfo ||= {}
 # }}}

-  robot.respond /よろしく/, (msg) -># {{{
-    unless existsUser robot, msg, msg.message.user.name
+# {{{ あいさつ
+  robot.respond /よろしく/, (msg) ->
+    unless whoIsThis robot, msg, msg.message.user.name
       msg.send "#{msg.message.user.name} こいつ誰？ > all"
       checkKeigo robot, msg
       return
-    msg.send msg.random AISATSU
-    checkKeigo robot, msg
+    msg.send msg.random AISATSU_YOROSHIKU
+    checkKeigo robot, msg
+
+  robot.respond /おはよう/, (msg) ->
+    unless whoIsThis robot, msg, msg.message.user.name
+      msg.send "#{msg.message.user.name} こいつ誰？ > all"
+      checkKeigo robot, msg
+      return
+    msg.send msg.random AISATSU_OHAYO
 # }}}

   robot.respond /後輩の[ ]*([^ ]+)/, (msg) -># {{{
@@ -135,7 +152,7 @@
     return
 # }}}

-  robot.respond /([^ ]+)[ ]*のことは忘れてください/, (msg) -># {{{
+  robot.respond /([^ ]+)[ ]*のことは忘れて/, (msg) -># {{{
     name = msg.match[1]
     name = trimKeigo name
     name = trimKeisho name
@@ -198,7 +215,7 @@

 # }}}

-  robot.respond /([^ ]+)[ ]*は[ ]*([^ ]+)[ ]*って呼ばれ/, (msg) -># {{{
+  robot.respond /([^ ]+)[ ]*は[ ]*([^ ]+)[ ]*(って|と)呼ばれ/, (msg) -># {{{
     return if msg.message.match('ない|ません')
     realname = msg.match[1]
     nickname = msg.match[2]
@@ -233,7 +250,7 @@
     checkKeigo robot, msg
 # }}}

-  robot.respond /([^ ]+)[ ]*は[ ]*([^ ]+)[ ]*って呼ばれて(ない|ません)/, (msg) -># {{{
+  robot.respond /([^ ]+)[ ]*は[ ]*([^ ]+)[ ]*(って|と)呼ばれて(ない|ませ)/, (msg) -># {{{
     realname = msg.match[1]
     nickname = msg.match[2]
     nickname = trimKeisho nickname
@@ -270,11 +287,19 @@
 # {{{ plusplus
   robot.hear /([^ ]+)\+\+/i, (msg) ->
     name = msg.match[1]
-
+    name = trimKeisho name
     user = whoIsThis robot, msg, name
+    fromname = msg.message.user.name
+    fromuser = whoIsThis robot, msg, fromname
+
     unless user?
       msg.send "#{name} って誰？先に教えて"
       return
+
+    if user is fromuser
+      msg.send "#{name} 自分で点数いれんなよ。悲しいやつだな"
+      return
+

     count = getUserInfo robot, msg, user, 'COUNT'
     count ||= 0
@@ -290,8 +315,9 @@
 # {{{ minusminus
   robot.hear /([^ ]+)--/i, (msg) ->
     name = msg.match[1]
-
+    name = trimKeisho name
     user = whoIsThis robot, msg, name
+
     unless user?
       msg.send "#{name} って誰？先に教えて"
       return