wrapped requires for proper bootstrapping

--- src/mist.coffee
+++ src/mist.coffee
@@ -15,24 +15,30 @@
 config = require 'commander'
 packageJson = require '../package'

+# action wrapper
+# we wrap action functions due to the fact the bootstrap process
+# only builds the bare-necessities. this function requires on demand,
+# not on every run.
+wrapAction = (name)->-> (require name).apply null, arguments
+
 # basic configuration
 config.cwd = process.cwd()

 config.version packageJson.version
 config.command 'build'
   .description 'build the project'
-  .action require './mist-build'
+  .action wrapAction './mist-build'
 config.command 'clean'
   .description 'clean the project of all outputs'
-  .action require './mist-clean'
+  .action wrapAction './mist-clean'
 config.command 'glob [globs...]'
   .description 'test file globbing patterns'
-  .action require './mist-glob'
+  .action wrapAction './mist-glob'
 config.command 'render'
   .description 'render Ninja configuration to a file'
   .option '--out <file>', 'the filename of the rendered configuration',
     'build.ninja'
-  .action require './mist-render'
+  .action wrapAction './mist-render'

 # default
 argv = process.argv


Make bound classes more robust

Make bound classes more robust

--- src/node.coffee
+++ src/node.coffee
@@ -5,14 +5,16 @@
   constructor: (@ast, @element) ->

   addBoundClass: (className) ->
-    @boundClasses or= []
-    if @boundClasses?.indexOf(className) is -1
+    @boundClasses or= new Collection()
+    unless @boundClasses?.includes(className)
       @boundClasses.push(className)
-    @updateClass()
+      @updateClass()

   removeBoundClass: (className) ->
-    @boundClasses.splice(@boundClasses.indexOf(className), 1) if @boundClasses
-    @updateClass()
+    if @boundClasses
+      index = @boundClasses.indexOf(className)
+      @boundClasses.delete(className)
+      @updateClass()

   setAttributeClasses: (name="__self__", value) ->
     @attributeClasses or= {}
@@ -39,15 +41,15 @@
     @children or []

   bindEvent: (event, fun) ->
-    @boundEvents or= []
     if event
+      @boundEvents or= new Collection()
       @boundEvents.push({ event, fun })
       event.bind(fun)

   unbindEvent: (event, fun) ->
-    @boundEvents or= []
     if event
-      @boundEvents.splice(@boundEvents.indexOf(fun), 1)
+      @boundEvents or= new Collection()
+      @boundEvents.delete(fun)
       event.unbind(fun)

   detach: ->
@@ -62,7 +64,7 @@
     classes = @ast.classes
     if @attributeClasses
       classes = classes.concat(value) for own _, value of @attributeClasses
-    classes = classes.concat(@boundClasses) if @boundClasses?.length
+    classes = classes.concat(@boundClasses.toArray()) if @boundClasses?.length
     classes.sort()
     if classes.length
       assignUnlessEqual(@element, "className", classes.join(' '))


hard delete a doc on health check

hard delete a doc on health check

--- services/docstore/app/coffee/HealthChecker.coffee
+++ services/docstore/app/coffee/HealthChecker.coffee
@@ -1,4 +1,4 @@
-ObjectId = require("mongojs").ObjectId
+{db, ObjectId} = require "./mongojs"
 request = require("request")
 async = require("async")
 _ = require("underscore")
@@ -40,4 +40,4 @@
 		async.series jobs, (err)->
 			if err?
 				callback(err)
-			request.del getOpts(), callback
+			db.docs.remove {_id: doc_id, project_id: project_id}, callback


removed useless transform attribute

removed useless transform attribute

--- src/coffee/choropleth.coffee
+++ src/coffee/choropleth.coffee
@@ -131,7 +131,6 @@
             .attr('class', 'legend-bg')
             .attr('width', box_w)
             .attr('height', box_h)
-            .attr('transform', 'translate(0, 0)')

         l_tr = 'translate(' + offset_t + ',' + offset_t * 3 + ')'


displayUrl can't possibly be null...?

displayUrl can't possibly be null...?

--- resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
+++ resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
@@ -44,9 +44,7 @@
             # different beatmapset, format: 1234#567
             linkText = "#{targetBeatmapsetDiscussions.beatmapsetId}##{targetBeatmapsetDiscussions.discussionId}"

-      linkText ?= displayUrl
-
-      osu.link(url, linkText ? url, options)
+      osu.link(url, linkText ? displayUrl, options)

   @discussionMode: (discussion) ->


AvatarAreaIconMenu: fix conflict issue

AvatarAreaIconMenu: fix conflict issue

--- client/Main/avatararea/avatarareaiconmenu.coffee
+++ client/Main/avatararea/avatarareaiconmenu.coffee
@@ -100,17 +100,6 @@

   accountChanged:(account)->

-<<<<<<< HEAD
-    {notificationsPopup} = this
-
-    notificationsPopup.listController.removeAllItems()
-
-    if KD.isLoggedIn()
-      # Fetch Notifications
-      notificationsPopup.listController.fetchNotificationTeasers (err, notifications)=>
-        return warn "Notifications cannot be received", err  if err
-        notificationsPopup.listController.instantiateListItems notifications
-=======
     {listController} = @notificationsPopup
     listController.removeAllItems()

@@ -118,7 +107,8 @@

     # Fetch Notifications
     KD.utils.defer ->
-      listController.fetchNotificationTeasers (teasers)->
+      listController.fetchNotificationTeasers (err, teasers)->
+        return warn "Notifications cannot be received", err  if err?
         listController.instantiateListItems filterNotifications teasers

@@ -136,4 +126,3 @@
       try
         snapshot = JSON.parse Encoder.htmlDecode notification.snapshot
         snapshot.anchor.constructorName in activityNameMap
->>>>>>> master


GroupStackSettings: move fetchers under view operations

--- client/admin/lib/views/groupstacksettings.coffee
+++ client/admin/lib/views/groupstacksettings.coffee
@@ -33,37 +33,6 @@
     super options, data

-  fetchData: (callback)->
-
-    { groupsController }            = kd.singletons
-    { JCredential, JStackTemplate } = remote.api
-
-    JCredential.some {}, { limit: 30 }, (err, credentials) ->
-
-      return callback {message: 'Failed to fetch credentials:', err}  if err
-
-      currentGroup = groupsController.getCurrentGroup()
-
-      if not currentGroup.stackTemplates?.length > 0
-        callback null, {credentials}
-        return
-
-      {stackTemplates} = currentGroup
-      stackTemplateId  = stackTemplates.first # TODO support multiple templates
-
-      JStackTemplate.some
-        _id   : stackTemplateId
-      , limit : 1
-      , (err, stackTemplates)->
-
-          if err
-            console.warn 'Failed to fetch stack template:', err
-            callback null, {credentials}
-          else
-            stackTemplate = stackTemplates.first
-            callback null, {credentials, stackTemplate}
-
-
   createEditorPane: (content) ->

     content = Encoder.htmlDecode content
@@ -129,6 +98,36 @@
             @setStack stackTemplate

         @createOutputView()
+
+  fetchData: (callback)->
+
+    { groupsController }            = kd.singletons
+    { JCredential, JStackTemplate } = remote.api
+
+    JCredential.some {}, { limit: 30 }, (err, credentials) ->
+
+      return callback {message: 'Failed to fetch credentials:', err}  if err
+
+      currentGroup = groupsController.getCurrentGroup()
+
+      if not currentGroup.stackTemplates?.length > 0
+        callback null, {credentials}
+        return
+
+      {stackTemplates} = currentGroup
+      stackTemplateId  = stackTemplates.first # TODO support multiple templates
+
+      JStackTemplate.some
+        _id   : stackTemplateId
+      , limit : 1
+      , (err, stackTemplates)->
+
+          if err
+            console.warn 'Failed to fetch stack template:', err
+            callback null, {credentials}
+          else
+            stackTemplate = stackTemplates.first
+            callback null, {credentials, stackTemplate}

   setStack: (stackTemplate) ->


video: enable video feature for everyone

--- client/app/lib/util/isVideoFeatureEnabled.coffee
+++ client/app/lib/util/isVideoFeatureEnabled.coffee
@@ -2,7 +2,9 @@

 module.exports = isVideoFeatureEnabled = ->

+  return yes
+
   # for now we are only allowing super-admins to start video chat.
   # After we release it publicly, we only need to change the logic here. ~Umut
-  checkFlag 'super-admin'
+  # checkFlag 'super-admin'


Tests: Call done function for async methods

--- client/app/test/kodinglistcontroller.test.coffee
+++ client/app/test/kodinglistcontroller.test.coffee
@@ -96,7 +96,7 @@

   describe '::followLazyLoad', ->

-    it 'should call hideLazyLoader if filterState.busy is yes', ->
+    it 'should call hideLazyLoader if filterState.busy is yes', (done) ->

       listController  = new KodingListController { fetcherMethod : kd.noop }
       listController.filterStates.busy = yes
@@ -105,9 +105,11 @@

       listController.emit 'LazyLoadThresholdReached'

-      kd.utils.wait 333, -> expect(spy).toHaveBeenCalled()
-
-    it 'should increase filterStates.skip value', ->
+      kd.utils.wait 333, ->
+        expect(spy).toHaveBeenCalled()
+        done()
+
+    it 'should increase filterStates.skip value', (done) ->

       listController  = new KodingListController { fetcherMethod : kd.noop, limit : 20 }

@@ -117,8 +119,9 @@

       kd.utils.wait 333, ->
         expect(listController.filterStates.skip).toBe 20
-
-    it 'should call fetch with correct options', ->
+        done()
+
+    it 'should call fetch with correct options', (done) ->

       fetcherMethod   = (query, options, callback) -> callback null, []
       listController  = new KodingListController { fetcherMethod, limit : 20 }
@@ -129,8 +132,9 @@
       kd.utils.wait 333, ->
         expect(fetchSpy.calls.first.arguments[0]).toEqual listController.filterStates.query
         expect(fetchSpy.calls.first.arguments[2]).toEqual { skip : listController.filterStates.skip }
-
-    it 'should call addListItems', ->
+        done()
+
+    it 'should call addListItems', (done) ->

       fetcherMethod   = (query, options, callback) -> callback null, []
       listController  = new KodingListController { fetcherMethod, limit : 20 }
@@ -141,8 +145,9 @@

       kd.utils.wait 333, ->
         expect(addListItemsSpy).toHaveBeenCalled()
-
-    it 'should increase filterStates.page value', ->
+        done()
+
+    it 'should increase filterStates.page value', (done) ->

       fetcherMethod   = (query, options, callback) -> callback null, []
       listController  = new KodingListController { fetcherMethod, limit : 20 }
@@ -153,6 +158,7 @@

       kd.utils.wait 333, ->
         expect(listController.filterStates.page).toBe 1
+        done()

   describe '::loadItems', ->


Fixes saving issues -- silent: true

Fixes saving issues -- silent: true

--- client/apps/edit/components/display/index.coffee
+++ client/apps/edit/components/display/index.coffee
@@ -19,6 +19,7 @@
     @renderThumbnailForms()
     @setCharCounts()
     $('.edit-display__inputs').first().slideDown().prev().addClass('active')
+    # @renderArticle = @article.clone()

   renderThumbnailForms: =>
     $magImage = $('.edit-display--magazine .edit-display__image-upload')
@@ -92,15 +93,15 @@
       $(e).parent().find('.edit-char-count').removeClass('edit-char-count-limit')
     $(e).parent().find('.edit-char-count').text(textLength + ' Characters')

-  renderPreviews: (e) ->
+  renderPreviews: (e, img) ->
     # Pluck out the value that needs to be set
     dataValue = $(e.target).data('value')
     if dataValue in ['headline','author']
-      emailMetadata = @renderArticle.get('email_metadata') or {}
+      emailMetadata = @article.get('email_metadata') or {}
       emailMetadata[dataValue] = $(e.target).val()
-      @article.set 'email_metadata', emailMetadata
+      @article.set { email_metadata: emailMetadata }, silent: true
     else if dataValue
-      @article.set dataValue, $(e.target).val()
+      @article.set { "#{dataValue}": $(e.target).val() }, silent: true

     # Rerenders all the previews
     $('.edit-display--magazine .edit-display__preview').html magazinePreview


Build: constants minor refac

Build: constants minor refac

--- client/landing/gulptasks/helper.constants.coffee
+++ client/landing/gulptasks/helper.constants.coffee
@@ -1,10 +1,11 @@
 argv = require('minimist') process.argv

+site = argv.site or 'landing'
+base = "#{__dirname}/.."
+
 module.exports =
-  STYLES_PATH     : ["#{__dirname}/../landing/styl/*.styl"]
-  COFFEE_PATH     : ["#{__dirname}/../landing/coffee/**/*.coffee"]
-  BROWSERFIY_PATH : ["#{__dirname}/../landing/coffee/main.coffee"]
-  INDEX_PATH      : ["#{__dirname}/../landing/index.html"]
-  SERVER_FILE     : "#{__dirname}/../server/server.coffee"
-  SERVER_PATH     : ["#{__dirname}/../server/**/*.coffee"]
-  BUILD_PATH      : argv.outputDir ? "#{__dirname}/../static/a/out"
+  STYLES_PATH     : ["#{base}/#{site}/styl/*.styl"]
+  COFFEE_PATH     : ["#{base}/#{site}/coffee/**/*.coffee"]
+  BROWSERFIY_PATH : ["#{base}/#{site}/coffee/main.coffee"]
+  INDEX_PATH      : ["#{base}/#{site}/index.html"]
+  BUILD_PATH      : argv.outputDir ? "#{base}/static/site.#{site}/a/out"


- [grunt] better browsers option for grunt-autoprefixer

--- tasks/grunt-autoprefixer.coffee
+++ tasks/grunt-autoprefixer.coffee
@@ -7,6 +7,6 @@
   @config 'autoprefixer',
     build:
       options:
-        browsers: ['last 2 versions', '> 1%']
+        browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1']
       files:
         '<%= file.build.style.prefixed %>': '<%= file.build.style.compiled %>'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/function_extensions.coffee
+++ test/function_extensions.coffee
@@ -25,7 +25,7 @@
   score = 0
   onceScore = ->
     ( ->
-
+      score
     ).once()

 test "#returning", ->


Remove dupe artsyPassport stub

Remove dupe artsyPassport stub

--- desktop/test/lib/setup.coffee
+++ desktop/test/lib/setup.coffee
@@ -22,7 +22,6 @@
           urlencoded: @urlencoded = sinon.stub().returns((req, res, next) -> next())
         bucketAssets: @bucketAssets = sinon.stub().returns((req, res, next) -> next())
         cors: @cors = sinon.stub().returns((req, res, next) -> next())
-        artsyPassport: @artsyPassport = sinon.stub().returns((req, res, next) -> next())
         flash: @flash = sinon.stub().returns((req, res, next) -> next())
         logger: @logger = sinon.stub().returns((req, res, next) -> next())
         http: @http = globalAgent: {}


default CSS animation fill mode to both

default CSS animation fill mode to both

--- scripts/jquery/css_animation.coffee
+++ scripts/jquery/css_animation.coffee
@@ -37,6 +37,11 @@
 createAnimation = (name, speed, easing, callback) ->
   options = jQuery.speed speed, easing, callback
   animation = { name }
+
+  # Animation's first keyframe is applied immediately, before the delay, and
+  # the final keyframe sticks after the animation ends. Do we really want it
+  # any other way?
+  options.fillMode ?= 'both'

   for own propertyName of jQuery.cssAnimationProperties when options[propertyName]?
     animation[propertyName] = options[propertyName]


Should be checking if any of the entries has more than 0 user

--- resources/assets/coffee/_classes/changelog-chart.coffee
+++ resources/assets/coffee/_classes/changelog-chart.coffee
@@ -85,7 +85,7 @@

     @hasData = @config.buildHistory? &&
       @config.buildHistory.length > 0 &&
-      _.every(@config.buildHistory, (b) -> b.user_count > 0)
+      _.some(@config.buildHistory, (b) -> b.user_count > 0)

     @resize()


remove bootable because xo-cli can't send boolean

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -815,7 +815,7 @@
     mode: mode
     type: 'Disk'
     userdevice: position
-    bootable : bootable
+    bootable : true
     empty : false
     other_config: {}
     qos_algorithm_type: ''
@@ -832,7 +832,6 @@
   vdi: { type: 'string' }
   position: { type: 'string' }
   mode: { type: 'string' }
-  bootable: { type: 'string' }
 }

 exports.diskAdd = ({id, name, size, SR, position}) ->


adding in api listeners for vocab create, save, and load

--- src/api.coffee
+++ src/api.coffee
@@ -1,6 +1,7 @@
 $ = require 'jquery'
 _ = require 'underscore'
-{ExerciseActions} = require './stores/exercise'
+{ExerciseActions, ExerciseStore} = require './stores/exercise'
+{VocabularyActions, VocabularyStore} = require './stores/vocabulary'
 {ErrorsActions} = require './stores/errors'

 # Do some special things when running without a tutor-server backend.
@@ -104,7 +105,24 @@
     url: "/api/exercises/#{exerciseUid}/attachments/#{attachmentId}"
     httpMethod: 'DELETE'

+  apiHelper VocabularyActions, VocabularyActions.load, VocabularyActions.loaded, 'GET', (id) ->
+    url: "/api/vocab_terms/#{id}@draft"

+  apiHelper VocabularyActions, VocabularyActions.create, VocabularyActions.created, 'POST', (id, obj) ->
+    # backend expects the changed props and the entire exercise for some reason
+    obj = VocabularyStore.get(id)
+
+    url:"/api/vocab_terms"
+    httpMethod: 'POST'
+    payload: obj
+
+  apiHelper VocabularyActions, VocabularyActions.save, VocabularyActions.saved , 'PUT', (id, obj) ->
+    # backend expects the changed props and the entire exercise for some reason
+    obj = VocabularyStore.get(id)
+
+    url:"/api/vocab_terms"
+    httpMethod: 'PUT'
+    payload: obj

 uploadExerciseImage = (exerciseUid, image, cb) ->
   url = "/api/exercises/#{exerciseUid}/attachments"


Formatting.

Formatting.

--- src/app.coffee
+++ src/app.coffee
@@ -229,10 +229,8 @@
                  @selectedUnits.trigger 'reset', @selectedUnits
             else
                 unit.fetch
-                    data:
-                        include: 'department,municipality,services'
-                    success: =>
-                        @selectedUnits.trigger 'reset', @selectedUnits
+                    data: include: 'department,municipality,services'
+                    success: => @selectedUnits.trigger 'reset', @selectedUnits
         selectUnitById: (id) ->
             deferred = $.Deferred()
             unit = @getUnit id


print error

print error

--- src/app.coffee
+++ src/app.coffee
@@ -59,4 +59,6 @@

 ], (err) ->

-    Database.db.close() if err+    if err
+        logger.error err
+        Database.db.close()


Only lists running instances when doing a query

--- src/aws.coffee
+++ src/aws.coffee
@@ -150,7 +150,7 @@
           if duration?
             diff = moment().diff instance.LaunchTime, 'seconds'
             inRange = diff > duration.asSeconds()
-          inRange and name?.toLowerCase().indexOf(query.toLowerCase()) > -1
+          inRange and instance.State.Name is 'running' and name?.toLowerCase().indexOf(query.toLowerCase()) > -1
         instances.length > 0

       if matches.length > 0


Be a little less specific about height measurements since it is so different between browsers.

--- spec/javascripts/mercury/snippet_toolbar_spec.js.coffee
+++ spec/javascripts/mercury/snippet_toolbar_spec.js.coffee
@@ -113,10 +113,10 @@
     it "positions itself based on the snippet", ->
       @snippetToolbar.element.show()
       @snippetToolbar.position()
-      if $.browser.webkit
-        expect(@snippetToolbar.element.offset()).toEqual({top: 18, left : 200})
-      else
-        expect(@snippetToolbar.element.offset()).toEqual({top: 14, left : 200})
+      # use a tolerance since there are measurement differences between browsers we want it between 14-18
+      expect(@snippetToolbar.element.offset().top).toBeLessThan(19)
+      expect(@snippetToolbar.element.offset().top).toBeGreaterThan(13)
+      expect(@snippetToolbar.element.offset().left).toEqual(200)

   describe "#appear", ->


adding debugging

adding debugging

--- src/poll.coffee
+++ src/poll.coffee
@@ -59,11 +59,15 @@
 		@robot.logger.info "Adding a vote for #{vote} for #{user}"
 		#check if the user already voted
 		for option in @robot.brain.data.poll.options
+			@robot.logger.info "Checking #{option}"
 			if option.users != undefined
+				@robot.logger.info "Some have voted for this one"
 				option.users = option.users.filter (currentUser) -> currentUser isnt user
 		if @robot.brain.data.poll.options[vote].users != undefined
+			@robot.logger.info "add user to this option since it's defined"
 			@robot.brain.data.poll.options[vote].users.push(user)
 		else
+			@robot.logger.info "First vote, create new array"
 			@robot.brain.data.poll.options[vote].users = []
 			@robot.brain.data.poll.options[vote].users.push(user)
 		@robot.brain.save()
@@ -72,8 +76,10 @@

 	robot.respond /view results/i, (msg) ->
 		@robot.logger.info "View results"
-		topic = @robot.brain.data.poll.topic
+		topic = @robot.brain.data.poll.topic
+		@robot.logger.info "#{@robot.brain.data.poll.options}"
 		options = @robot.brain.data.poll.options
+		@robot.logger.info "#{options}"
 		optionString = ""
 		for option in options
 			index = options.indexOf(option)


Update jquery.age.coffee

Update jquery.age.coffee

Changed the adjust method to use Math.floor rather than Math.round

--- javascripts/jquery.age.coffee
+++ javascripts/jquery.age.coffee
@@ -62,7 +62,7 @@
     return @settings.prefixes.future if interval > 0

   adjust: (interval, scale) =>
-    Math.round(Math.abs(interval / scale))
+    Math.floor(Math.abs(interval / scale))

   formatting: (interval) =>
     seconds: @adjust(interval, 1000)


Bugfixes to committees import.

Bugfixes to committees import.

--- jobs/import-committees.coffee
+++ jobs/import-committees.coffee
@@ -25,7 +25,7 @@
     members = [members]

   members.forEach? (member) ->
-    db.collection("members").findOne {assemblyId: member.Member.Id}, (err, ourMember) -> ifSuccessful err, callback, ->
+    db.collection("members").findOne {assemblyId: Number(member.Member.Id)}, (err, ourMember) -> ifSuccessful err, callback, ->
       unless ourMember?
         console.error("Member " + member.Member.Id + " is missing.")
         return
@@ -40,7 +40,8 @@
       ,
         "$set": memberSet
       , (err) ->
-        console.error "Something went wrong updating committees: " + err
+        if err
+          console.error "Something went wrong updating committees: " + err

 persistCommittee = (assemblyCommittee, session, db, callback) ->
   members = assemblyCommittee.Members.CommitteeMember


Cleaned render.relationship

Cleaned render.relationship

--- js/binding/src/binding.coffee
+++ js/binding/src/binding.coffee
@@ -132,7 +132,7 @@
 			entity_view = find("..#{entity._type()}.?.#{view}")
 			definition = entity._definition()
 			entity_view.children(".properties").append(render.property(entity._type(), property, entity[property](), view)) for own property, property_def of definition.properties
-			entity_view.children(".relationships").append(render.relationship(entity, relationship, view)) for relationship in definition.relationships
+			entity_view.children(".relationships").append(render.relationship(entity, rel_name, relationship, view)) for own rel_name, relationship of definition.relationships
 			entity_view

 		property: (type, name, property, view = "view") ->
@@ -144,15 +144,14 @@
 			    removeAttr('id').
 				html(_.template(property_view.html(), data))

-		relationship: (entity, relationship, view = "view") ->
-			if (relationship.type is "has_a" or relationship.type is "is_a")
-				return render.entity(entity["get_" + relationship.name]())
-			else
+		relationship: (entity, rel_name, relationship, view = "view") ->
+			if relationship.type is "has_many"
 				rel = $()
-				rels = entity["get_" + relationship.name]()
+				rels = entity[rel_name]()
 				rel.append(render.entity(ent)) for ent in rels
 				return rel
-
+			else
+				return render.entity(entity[rel_name]())
 	})

 	init = (options) ->


Fix issues in removing entities with unmatching related data.

--- js/core/src/LocalStore.coffee
+++ js/core/src/LocalStore.coffee
@@ -88,18 +88,28 @@
 					results = _(results).filter(_sieve(name, property, spec[name]))

 			# Include relationships
-			relations = []
 			for name, relationship of def.relationships
 				if name of spec
 					# For all the entities found so far, include their relationships as well
-					for entity in results
-						relations.push ( =>
+					give = []
+					take = []
+					for entity, i in results
+						related = ( =>
 							relspec = _.extend({}, spec[name], {_type: relationship.to.type})
 							relspec[relationship.to.property] = entity[relationship.property]
 							# Just going to use
 							@_lookup(relspec)
-						)()
-			results.push(relations)
+						)()
+						if related.length
+							give.push(related)
+						else
+							take.push(i)
+					take.reverse()
+					for i in take
+						end = results[i+1...]
+						results = results[...i]
+						[].push.apply(results, end)
+					[].push.apply(results, give)

 			# Return the filtered results.
 			results


hubot can execute a state

hubot can execute a state

--- src/salt.coffee
+++ src/salt.coffee
@@ -8,9 +8,11 @@
 # Commands:
 #   hubot salt <ping> <all> - Execute test.ping on all minions
 #   hubot salt <ping> <minion> - Execute test.ping on a specific minion
+#   hubot salt <apply> <state_name> to <minion> - Executing a state or formula on a specific minion
 #
 # Notes:
-#   <optional notes required for the script>
+#   Salt REST API needs to be up and running
+#   <https://docs.saltstack.com/en/latest/ref/netapi/all/salt.netapi.rest_cherrypy.html>
 #
 # Author:
 #   Mostafa Hussein[@<mostafa.hussein91@gmail.com>]
@@ -18,41 +20,77 @@
 salt_api_url = process.env.SALT_API_URL
 salt_token = process.env.SALT_X_TOKEN

+salt =
+  commands:
+    ping: "test.ping"
+    apply: "state.apply"
+    exec: "cmd.run"
+    whois: "grains.items"
+
 missingEnvironment = (msg) ->
-    missingData = false
-    unless salt_api_url?
-      msg.send "You need to set SALT_API_URL"
-      missingData |= true
-    unless salt_token?
-      msg.send "You need to set SALT_X_TOKEN"
-      missingData |= true
-    missingData
+  missingData = false
+  unless salt_api_url?
+    msg.send "You need to set SALT_API_URL"
+    missingData |= true
+  unless salt_token?
+    msg.send "You need to set SALT_X_TOKEN"
+    missingData |= true
+  missingData
+
+wrongCommand = (msg) ->
+  undefinedCommand = false
+  unless salt.commands[msg.match[1].trim()]?
+    msg.send "Sorry, I don't understand this command: " + msg.match[1].trim()
+    undefinedCommand |= true
+  undefinedCommand

 module.exports = (robot) ->
-  robot.respond /salt\s+([\w-.]+)\s+(.+)/i, (msg) ->
+
+  saltApiReq = (data, callback) ->
+    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
+    robot.http(salt_api_url)
+      .headers('Content-Type': 'application/json', 'X-Auth-Token': salt_token)
+      .post(data) (err, res, body) ->
+        if res.statusCode == 401
+          callback("Unauthorized Request, This might be a permission issue or the Token is expired.")
+        else
+          try
+            jsonBody = JSON.parse(body)
+            textBody = JSON.stringify(jsonBody, null, 2)
+            callback(textBody)
+
+  robot.respond /salt\s+([\w-.]+)\s+([\w-.]+)$/i, (msg) ->
     unless (missingEnvironment msg)
-      salt_function = msg.match[1].trim()
-      salt_target = msg.match[2].trim()
+      unless (wrongCommand msg)
+        salt_function = salt.commands[msg.match[1].trim()]
+        salt_target = msg.match[2].trim()
+        if salt_target == 'all'
+          salt_target = '*'

-      if salt_function == 'ping'
-        salt_function = 'test.ping'
+        data = JSON.stringify({
+          client: 'local',
+          tgt: salt_target,
+          fun: salt_function,
+        })

-      if salt_target == 'all'
-        salt_target = '*'
+        saltApiReq data, (result) ->
+          msg.send result

-      data = JSON.stringify({
-        client: 'local',
-        tgt: salt_target,
-        fun: salt_function,
-      })
-      process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
-      robot.http(salt_api_url)
-        .headers('Content-Type': 'application/json', 'X-Auth-Token': salt_token)
-        .post(data) (err, res, body) ->
-          if res.statusCode == 401
-            msg.reply "Unauthorized Request, This might be a permission issue or the Token is expired."
-          else
-            try
-              jsonBody = JSON.parse(body)
-              textBody = JSON.stringify(jsonBody, null, 2)
-              msg.reply textBody
+  robot.respond /salt\s+([\w-.]+)\s+([\w-.]+)\s+to\s+(.+)/i, (msg) ->
+    unless (missingEnvironment msg)
+      unless (wrongCommand msg)
+        salt_function = salt.commands[msg.match[1].trim()]
+        salt_args = msg.match[2].trim()
+        salt_target = msg.match[3].trim()
+        if salt_target == 'all'
+          salt_target = '*'
+
+        data = JSON.stringify({
+          client: 'local',
+          tgt: salt_target,
+          fun: salt_function,
+          arg: salt_args
+        })
+
+        saltApiReq data, (result) ->
+          msg.send result


Fixed test

Fixed test

--- karma/test-controllers.coffee
+++ karma/test-controllers.coffee
@@ -633,7 +633,7 @@
       labelsPieChart = spyOnPromise($q, $scope, StatisticsService, 'labelsPieChart')
       fetchPartners = spyOnPromise($q, $scope, PartnerService, 'fetchAll')
       fetchLabels = spyOnPromise($q, $scope, LabelService, 'fetchAll')
-      fetchUsers = spyOnPromise($q, $scope, UserService, 'fetchAll')
+      fetchUsers = spyOnPromise($q, $scope, UserService, 'fetchNonPartner')

       $scope.onTabSelect(0)


201 statuscode added

201 statuscode added

--- server/kitchen-coffee/src/Controller/Chefs.coffee
+++ server/kitchen-coffee/src/Controller/Chefs.coffee
@@ -38,6 +38,7 @@
                     @statusCode = 500
                     callback err
                 else
+                    @statusCode = 201
                     callback result

     delete : (callback) ->


Working transform pipe implementation!

Working transform pipe implementation!

--- src/stream.iced
+++ src/stream.iced
@@ -1,26 +1,18 @@
 stream = require('stream');
 enc = require('./encoding');

-exports.Streamer = class Streamer extends stream.Duplex
+exports.StreamEncoder = class StreamEncoder extends stream.Transform

-	constructor : (base) ->
-		encoding = enc.b58 if base is 58
-		encoding = enc.b62 if base is 62
-		encoding = enc.b64 if base is 64
-		console.log('ERRA!') unless encoding?
-		@encoder = encoding.encoding
-		super({highWaterMark: encoding.in_block_len})
+	constructor : (@encoder) ->
+		super({highWaterMark : encoder.in_block_len})

-	encode : (src) ->
-		src.on('readable', () ->
-			chunk = null
-			while (chunk = src.read(@highWaterMark)) != null
-				@write(@encoder.encode(chunk))
-			)
+	_transform : (chunk, encoding, callback) ->
+			@push(@encoder.encode(chunk))

-	decode : (src) ->
-		src.on('readable', () ->
-			chunk = null
-			while (chunk = src.read(@highWaterMark)) != null
-				@write(@encoder.decode(chunk))
-			)
+exports.StreamDecoder = class Streamer extends stream.Transform
+
+	constructor : (@encoder) ->
+		super({highWaterMark : encoder.out_block_len})
+
+	_transform : (chunk, encoding, callback) ->
+			@push(@encoder.decode(chunk))


add help command

add help command

--- src/tell.coffee
+++ src/tell.coffee
@@ -26,6 +26,22 @@
   else
     virustotal.setKey(process.env.HUBOT_VIRUSTOTAL_API)

+  robot.respond /virustotal help$/, (msg) ->
+    cmds = []
+    arr = [
+      "auth url <url> - get url report"
+    ]
+
+    for str in arr
+      cmd = str.split " - "
+      cmds.push "`#{cmd[0]}` - #{cmd[1]}"
+
+    if replyInPrivate and msg.message?.user?.name?
+      msg.reply 'replied to you in private!'
+      robot.send {room: msg.message?.user?.name}, cmds.join "\n"
+    else
+      msg.reply cmds.join "\n"
+
   robot.respond /virustotal url (.+)$/i, (msg) ->
     url = msg.match[1]


Remove jQuery from keymap-extensions

Remove jQuery from keymap-extensions

--- src/keymap-extensions.coffee
+++ src/keymap-extensions.coffee
@@ -2,7 +2,6 @@
 path = require 'path'
 KeymapManager = require 'atom-keymap'
 CSON = require 'season'
-{jQuery} = require 'space-pen'
 Grim = require 'grim'

 bundledKeymaps = require('../package.json')?._atomKeymaps
@@ -61,9 +60,4 @@

     atom.notifications.addError(message, {detail, dismissable: true})

-# This enables command handlers registered via jQuery to call
-# `.abortKeyBinding()` on the `jQuery.Event` object passed to the handler.
-jQuery.Event::abortKeyBinding = ->
-  @originalEvent?.abortKeyBinding?()
-
 module.exports = KeymapManager


show response in form target if present

show response in form target if present

--- exifexodus.coffee
+++ exifexodus.coffee
@@ -141,6 +141,10 @@
                 response from the server (code #{ xhr.status }).", cleaned

             if xhr.responseType is 'document' or /<[\w\W]*>/.test xhr.responseText
+              if target = form.getAttribute 'target'
+                unless target in ['_blank', '_self', '_parent', '_top']
+                  return document.getElementById(target)?.innerHTML = xhr.response
+
               document.write xhr.response
             else
               reportErr 'Uploaded image but received ambiguous response from server.', cleaned


Removes the largerepo endpoint

Removes the largerepo endpoint

--- src/ghe.coffee
+++ src/ghe.coffee
@@ -10,24 +10,23 @@
 #
 # Commands:
 #   hubot ghe (status|info) license - returns license information
-#   hubot ghe (status|info) largerepo - returns information about large repositories
 #
 # Authors:
 #   pnsk, mgriffin

 module.exports = (robot) ->
-  robot.respond /ghe (status|info)? ?(.*)$/i, (msg) ->
-    url = process.env.HUBOT_GHE_URL + '/api/v3'
-    token = process.env.HUBOT_GHE_TOKEN
+  url = process.env.HUBOT_GHE_URL + '/api/v3'
+  token = process.env.HUBOT_GHE_TOKEN
+  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"

-    unless token
-      msg.send "token isn't set."
+  unless token
+    msg.send "token isn't set."

-    info_term = msg.match[2]
-    switch info_term
-      when "license" then ghe_license msg,token,url
-      when "largerepo" then ghe_largerepo msg,token,url
-      else msg.send "I don't know about #{info_term}"
+  robot.respond /ghe info$/i, (msg) ->
+    ghe_license msg,token,url
+
+  robot.respond /ghe stats (.*)$/i, (msg) ->
+    ghe_stats msg,token,url

 ghe_license = (msg, token, url) ->
     msg.http("#{url}/enterprise/settings/license")
@@ -38,31 +37,22 @@
         else
            if res.statusCode is 200
                results = JSON.parse body
-               msg.send "GHE has #{results.seats} seats, and #{results.seats_used} are used. License expires at #{results.expire_at}"
+               msg.send "GHE has #{results.seats} seats, and #{results.seats_used} are used. License expires at #{results.expire_at}."
            else
                msg.send "statusCode: #{res.statusCode}"

-ghe_largerepo = (msg, token, url) ->
-    repo_min = 2000000
-    msg.http("#{url}/search/repositories?q=size:>=#{repo_min}")
-    .header("Authorization", "token #{token}")
-    .get() (err, res, body) ->
-        if err
-             msg.send "error"
-        else
-           if res.statusCode is 200
-               results = JSON.parse body
-               msg.send "more than #{repo_min}KB repos. (aww)"
-               if results.total_count is 0
-                 msg.send "no repos."
-               else
-                 results.items.sort compareSize
-                 reporank = ""
-                 for item, index in results.items
-                   reporank = reporank + "#{index+1}:  #{item.full_name} #{item.size} \n"
-                 msg.send "#{reporank}"
-           else
-               msg.send "statusCode: #{res.statusCode}"
-
-compareSize = (a, b) ->
-  b.size - a.size
+ghe_stats = (msg, token, url) ->
+  type = msg.match[1]
+  msg.http("#{url}/enterprise/stats/#{type}")
+  .header("Authorization", "token #{token}")
+  .get() (err, res, body) ->
+    if err
+      msg.send "error"
+    else
+      if res.statusCode is 200
+        results = JSON.parse body
+        switch type
+          when "issues" then msg.send "#{results.total_issues} issues in total; #{results.open_issues} open and #{results.closed_issues} closed."
+          when "users" then msg.send "#{results.total_users} users, #{results.admin_users} admins and #{results.suspended_users} suspended."
+      else
+        msg.send "statusCode: #{res.statusCode} -- #{body} -- type: #{type} -- #{msg.match[1]}"


Guard against deleted submodule

Guard against deleted submodule

--- src/git.coffee
+++ src/git.coffee
@@ -357,6 +357,6 @@
       @submodules = submodules

       for submodulePath, submoduleRepo of @getRepo().submodules
-        submoduleRepo.upstream = submodules[submodulePath].upstream
+        submoduleRepo.upstream = submodules[submodulePath]?.upstream ? {ahead: 0, behind: 0}

       @emit 'statuses-changed' unless statusesUnchanged


Fix extra-indent issue

Fix extra-indent issue

--- src/view.coffee
+++ src/view.coffee
@@ -145,7 +145,7 @@
             top: padding
             bottom: @view.opts.indentTongueHeight
             left: @view.opts.indentWidth + @view.opts.padding
-            right: 0
+            right: padding
         else if parenttype is 'block' or (
             parenttype is 'socket' and @model.type is 'text')
           @margins =
@@ -591,9 +591,14 @@
               else 0

             # Unless we are in the middle of an indent,
-            # add padding on the right of the child
-            @dimensions[desiredLine].width += size.width +
-              childNode.margins.left + childNode.margins.right
+            # add padding on the right of the child.
+            #
+            # Exception: Children with invisible bounding boxes
+            # should remain invisible. This matters
+            # mainly for indents starting at the end of a line.
+            unless size.width is 0
+              @dimensions[desiredLine].width += size.width +
+                childNode.margins.left + childNode.margins.right

             # Compute max distance above and below text
             @distanceToBase[desiredLine].above = Math.max(
@@ -659,11 +664,14 @@
         for lineChild, i in @lineChildren[line]
           childView = @view.getViewNodeFor lineChild.child
           childLine = line - lineChild.startLine
-
-          childLeft += childView.margins.left
-          childView.computeBoundingBoxX childLeft, childLine
-          childLeft +=
-            childView.dimensions[childLine].width + childView.margins.right
+
+          if childView.dimensions[childLine].width is 0
+            childView.computeBoundingBoxX childLeft, childLine
+          else
+            childLeft += childView.margins.left
+            childView.computeBoundingBoxX childLeft, childLine
+            childLeft +=
+              childView.dimensions[childLine].width + childView.margins.right

         # Return the bounds we just
         # computed.


Use new GK25 grid definition

Use new GK25 grid definition

--- src/map.coffee
+++ src/map.coffee
@@ -2,21 +2,21 @@
     crs_name = 'EPSG:3879'
     proj_def = '+proj=tmerc +lat_0=0 +lon_0=25 +k=1 +x_0=25500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'

-    bounds = [2.547210017612655e7, 6654072.819370746, 2.553763617612655e7, 6719608.819370746]
+    bounds = [25440000, 6630000, 25571072, 6761072]
     crs = new L.Proj.CRS.TMS crs_name, proj_def, bounds,
-        resolutions: [1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]
+        resolutions: [256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625]
     map = new L.Map 'map',
         crs: crs
         continuusWorld: true
         worldCopyJump: false
         zoomControl: false
-    map.setView [60.171944, 24.941389], 8
+    map.setView [60.171944, 24.941389], 5

     L.control.scale(imperial: false, maxWidth: 200).addTo map

-    layer = new L.Proj.TileLayer.TMS 'http://geoserver.hel.fi/geoserver/gwc/service/tms/1.0.0/hel:Palvelukartta@ETRS-GK25@gif/{z}/{x}/{y}.gif', crs,
+    layer = new L.Proj.TileLayer.TMS 'http://geoserver.hel.fi/geoserver/gwc/service/tms/1.0.0/hel:Opaskartta@ETRS-GK25@gif/{z}/{x}/{y}.gif', crs,
         maxZoom: 10
-        minZoom: 5
+        minZoom: 2
         continuousWorld: true
         tms: false
     map.addLayer layer


Implement alert functionality

Implement alert functionality

--- sources/tuktuk.modal.coffee
+++ sources/tuktuk.modal.coffee
@@ -1,6 +1,6 @@
 window.TukTuk.Modal = do (tk = TukTuk) ->

-  lock = undefined
+  lock  = undefined
   modal = undefined

   ###
@@ -23,6 +23,15 @@
     , 250
     @

+  alert = (message = "") ->
+    lock.addClass("active").show()
+    @_hideAnyModal()
+    modal = tk.dom("[data-tuktuk=modal][data-modal=alert")
+    text  = modal.find "#text"
+    text.html message
+    modal.addClass "active"
+    @
+
   ###
       @loading: Describe method
   ###
@@ -42,11 +51,34 @@
     tk.dom("[data-tuktuk-modal]").on "click", ->
       TukTuk.Modal.show(tk.dom(this).attr('data-tuktuk-modal'))

-    tk.dom(document.body).append """
+    loading = """

       """
+    alert_template = """
+

+
+
Alert

+
+

+
+          <button data-modal="close" class="button large theme on-right margin-bottom">Accept</button>
+
+

+      """
+    prompt_template = """
+      """
+    confirm_template = """
+      """
+
+    tk.dom(document.body).append """
+      #{alert_template}
+      #{prompt_template}
+      #{confirm_template}
+      #{loading}
+      """
+
     tk.dom("[data-tuktuk=lock]").on "click", (event) ->
       loading = lock.attr("data-loading")
       TukTuk.Modal.hide() unless event.target is modal or loading is "true"
@@ -54,6 +86,7 @@
     lock = tk.dom("[data-tuktuk=lock]").first()
   )()

-  show: show
-  hide: hide
-  loading: loading
+  show    : show
+  hide    : hide
+  loading : loading
+  alert   : alert


fix conditional syntax

fix conditional syntax

--- app/chromepaper.coffee
+++ app/chromepaper.coffee
@@ -59,7 +59,7 @@
     archivedArticle.addClass("archived")

     if archivedArticle.hasClass("selected")
-      selectedArticle = nextArticle.length ? nextArticle : previousArticle
+      selectedArticle = if nextArticle.length then nextArticle else previousArticle
       showSelectedArticle()

   articleClicked = () ->


removed test event type

removed test event type

--- app/coffee/main.coffee
+++ app/coffee/main.coffee
@@ -31,7 +31,6 @@
         name: "First Monday"
         day: ["Monday"]
         dayNum : 1
-        type: "test"
         recurrance: "monthly"
         notes: "This is the first Monday of the month"
     drmCalendar.createEvent
@@ -39,25 +38,21 @@
         month: "April"
         eventDate: 24
         day: ["Thursday"]
-        type: "test"
         recurrance: "biweekly"
         notes: "Every other Thursday"
     drmCalendar.createEvent
         name: "Wake Up"
         day: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
-        type: "test"
         time: "6:00am"
         recurrance: "weekly"
         note: "Wake Up Every Day"
     drmCalendar.createEvent
         name: "Sleep In!"
         day: ["Saturday", "Sunday"]
-        type: "test"
         time: "9:00am"
         recurrance: "weekly"
     drmCalendar.createEvent
         name: "Eat Lunch"
-        type: "test"
         time: "12:00pm"
         notes: "eat a healthy lunch"
         recurrance: "daily"
@@ -67,7 +62,6 @@
         year: 2014
         time: '1:00pm'
         eventDate: 4
-        type: "test"
         recurrance: "none"
         note: "do this once"
     drmCalendar.createEvent
@@ -76,7 +70,6 @@
         year: 2014
         time: '1:30pm'
         eventDate: 4
-        type: "test"
         recurrance: "none"
         note: "do this once"
     drmCalendar.createEvent
@@ -85,7 +78,6 @@
         year: 2014
         time: '2:30pm'
         eventDate: 4
-        type: "test"
         recurrance: "none"
         note: "do this once"


Coffeelint

Coffeelint

--- src/scripts/modules/media/tabs/contents/contents.coffee
+++ src/scripts/modules/media/tabs/contents/contents.coffee
@@ -9,9 +9,9 @@
   basicNumbering = (nodes, parentNumber) ->
     for node, index in nodes
       chapterNumber = if parentNumber?
-         "#{parentNumber}.#{index+1}"
+        "#{parentNumber}.#{index+1}"
        else
-         index+1
+        index+1
       node.set('chapter', chapterNumber)
       if node.isSection()
         childNodes = node.get('contents')?.models


Fix byte constant tests

Fix byte constant tests

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -304,8 +304,8 @@
       it "tokenizes constant value in bytes", ->
         for constant in constants
           {tokens} = grammar.tokenizeLine constant
-          for token in tokens
-            expect(token).toHaveScopes ["source.powershell", "constant.numeric.scientific.powershell"]
+          expect(tokens[0]).toHaveScopes ["source.powershell", "constant.numeric.integer.powershell"]
+          expect(tokens[1]).toHaveScopes ["source.powershell", "keyword.other.powershell"]

     describe "Constant float values", ->
       constants = [


make sure empty text nodes are parseable

--- test/all.coffee
+++ test/all.coffee
@@ -364,9 +364,10 @@
 			throw err
 		if result isnt output
 			throw Error result+" !== "+output
-
+
 	describe ".innerHTML can parse", ->
 		cases = [
+			["", ""],
 			["text", "text"],
 			["<p>","<p/>"],
 			["<div/>"],


Add a comment for campaign access permissions (#420)

--- app/models/User.coffee
+++ app/models/User.coffee
@@ -642,6 +642,7 @@
   #   - Free campaigns
   #   - Student with full license
   #   - Teacher
+  # Update in server/models/User also, if updated here.
   hasCampaignAccess: (campaignData) ->
     return true if utils.freeCampaignIds.includes(campaignData._id)
     return true if @isAdmin() or @isInternal()


feat : add user model

feat : add user model

--- app/models/user.coffee
+++ app/models/user.coffee
@@ -1,7 +1,60 @@
 `import DS from 'ember-data'`
+`import {moment, ago} from 'ember-moment/computed'`
+`import ENUMS from 'irene/enums'`

-User = DS.Model.extend {
-
-}
+User = DS.Model.extend
+  username: DS.attr 'string'
+  email: DS.attr 'string'
+  emailmd5: DS.attr 'string'
+  firstName: DS.attr 'string'
+  lastName: DS.attr 'string'
+  ownedProject: DS.hasMany 'project', inverse:'owner', async:false
+  submissions: DS.hasMany 'submissions', inverse:'user', async:false
+  namespaces: DS.attr 'string'
+  colloborations: DS.hasMany 'colloboration', inverse:'user', async:false
+  expiryDate: DS.attr 'date'
+  hasGithubToken: DS.attr 'boolean'
+  hasJiraToken: DS.attr 'boolean'
+  socketId: DS.attr 'string'
+  limitedScans: DS.attr()
+  scansLeft: DS.attr()
+
+  humanizedExpiryDate : ago 'expiryDate', true
+
+  urls:null
+
+  gravatarUrl:( ->
+    "//s.gravatar.com/avatar/#{@get "emailmd5"}?s=50"
+  ).property "emailmd5"
+
+  isCurrentUser:( ->
+    applicationController = @container.lookup "controller:application"
+    currentUser = applicationController.get "currentUser"
+    currentUser.get("id") is @get "id"
+  ).property()
+
+  statText: ( ->
+    limitedScans = @get "limitedScans"
+    if limitedScans
+      "Scans Left"
+    else
+      "Expiry Date"
+  ).property "limitedScans"
+
+  statValue:( ->
+    limitedScans = @get "limitedScans"
+    if limitedScans
+      @get "scansLeft"
+    else
+      @get "humanizedExpiryDate"
+  ).property "limitedScans"
+
+  statIcon:( ->
+    limitedScans = @get "limitedScans"
+    if limitedScans
+      "search"
+    else
+      "calender"
+  ).property "limitedScans"

 `export default User`


Fixes for offline sync

Fixes for offline sync

--- app/sync.coffee
+++ app/sync.coffee
@@ -216,13 +216,14 @@
     [className, model] = data
     return unless className in ["List", "Task"]
     # Generate new id
-    id = "s-" + @user.index(className)
-    @user.incrIndex className
-    model.id = id
+    if model.id is "inbox" then id is model.id
+    else
+      id = "s-" + @user.index(className)
+      @user.incrIndex className
+      model.id = id
+    console.log model
     # Add item to server
-    group = @user.data(className)
-    if not group
-      group = @user.data(className, {})
+    group = @user.data(className) or @user.data(className, {})
     group[id] = model
     # Save to server
     @user.save(className)
@@ -240,6 +241,12 @@
     [className, changes] = data
     return unless className in ["List", "Task"]
     id = changes.id
+
+    # Check model exists on server
+    if not @user.data(className).hasOwnProperty id
+      console.log "#{className} doesn't exist on server"
+      return
+
     # Set timestamp
     timestamps = data[2]
     if timestamps
@@ -278,22 +285,27 @@
     @emit 'destroy', [className, id]

-  # -----------
-  # Sync Events
-  # -----------
+  # --------------------
+  # Offline Sync Merging
+  # --------------------

   # Sync
   sync: (queue, fn) =>
     Log "Running sync"
     for event in queue
       [type, [className, model], timestamp] = event
+
       switch type
         when "create"
           @create [className, model, timestamp]
+
         when "update"
           @update [className, model, timestamp]
+
         when "destroy"
-          @destroy [className, model, timestamp]
+          if @checkTime className, id, timestamp
+            @destroy [className, model, timestamp]
+
     fn [@getArray("Task"), @getArray("List")] if fn

 module?.exports = Sync


Use fs.resolveOnLoadPath() instead of require.resolve()

--- spec/status-bar-spec.coffee
+++ spec/status-bar-spec.coffee
@@ -112,7 +112,7 @@

     it "displays the current branch for files in repositories", ->
       path = require.resolve('fixtures/git/master.git/HEAD')
-      project.setPath(require.resolve('fixtures/git/master.git'))
+      project.setPath(fs.resolveOnLoadPath('fixtures/git/master.git'))
       rootView.open(path)
       expect(statusBar.branchArea).toBeVisible()
       expect(statusBar.branchLabel.text()).toBe 'master'
@@ -128,7 +128,7 @@

     beforeEach ->
       path = require.resolve('fixtures/git/working-dir/file.txt')
-      newPath = fs.join(require.resolve('fixtures/git/working-dir'), 'new.txt')
+      newPath = fs.join(fs.resolveOnLoadPath('fixtures/git/working-dir'), 'new.txt')
       fs.write(newPath, "I'm new here")
       git.getPathStatus(path)
       git.getPathStatus(newPath)


fix finder with dirs

fix finder with dirs

--- asterisk.coffee
+++ asterisk.coffee
@@ -91,6 +91,9 @@
     socket.on 'suggest', (req) ->
         s = req.query
         dir = req.directory
+        if s and s[0] == "/"
+            dir = s[0..s.lastIndexOf("/")-1]
+            s = s[s.lastIndexOf("/")+1..]
         print "s", s, "dir", dir
         finder = findem(dir, s)
         finder.on 'end', (files) ->


Numerical sort function because [100, 300, 5] is sorted else wise.

--- picoCTF-web/web/coffee/dependencies.coffee
+++ picoCTF-web/web/coffee/dependencies.coffee
@@ -32,6 +32,10 @@
   style = getStyle data
   $.notify data.message, style

+@numericalSort = (data) ->
+  data.sort (a, b) ->
+    return (b - a)
+
 $.fn.apiNotify = (data, configuration) ->
   configuration["className"] = getStyle data
   return $(this).notify(data.message, configuration)


:lipstick:

:lipstick:

--- src/extensions/command-panel/preview-list.coffee
+++ src/extensions/command-panel/preview-list.coffee
@@ -28,7 +28,6 @@

   setSelectedOperationIndex: (index) ->
     @children(".selected").removeClass('selected')
-    console.log @children("li:eq(#{index})")
     @children("li:eq(#{index})").addClass('selected')

   #getSelectedOperation: ->


Simplify Asset component

Simplify Asset component

--- base/Asset.cjsx
+++ base/Asset.cjsx
@@ -1,71 +1,18 @@
 React = require 'react'
-
-path = require 'path'
-fs = require 'fs'
-
-CoffeeScript    = require 'coffee-script'
-Sass            = require 'node-sass'
-UglifyJS        = require 'uglify-js'
-sqwish          = require 'sqwish'
-autoprefixer    = require 'autoprefixer-core'
-
-compileSass = (project_root, source_path) ->
-    compiled = Sass.renderSync
-        file: source_path
-        includePaths: [project_root]
-    return compiled
-
-compileCoffee = (project_root, source_path) ->
-    source = fs.readFileSync(source_path).toString()
-    compiled = CoffeeScript.compile(source)
-    # TODO: browserify
-    return compiled
-
-minifyScript = (script_str) -> UglifyJS.minify(script_str, fromString: true).code
-prefixAndMinifyCSS = (css_str) ->
-    css_str = autoprefixer.process(css_str).css
-    return sqwish.minify(css_str)
-
-_inline_asset_cache = {}

 module.exports = React.createClass
     displayName: 'Asset'
     render: ->
-        # Reset the asset cache
-        if Object.keys(_inline_asset_cache).length > 5
-            _inline_asset_cache = {}
-        _inline_asset_cache[global.ASSET_URL] ?= {}
-
         _render = =>
             full_path = "#{ global.ASSET_URL }#{ @props.path }"
             _ext = @props.path.split('.').pop()
             switch _ext
                 when 'js', 'coffee'
-                    if @props.inline
-                        source_path = path.join(global.PROJECT_ROOT, 'assets', @props.path)
-                        if _ext is 'coffee'
-                            asset_content = compileCoffee(global.PROJECT_ROOT, source_path)
-                        else
-                            asset_content = fs.readFileSync(source_path).toString()
-                        asset_content = minifyScript(asset_content)
-                        return <script dangerouslySetInnerHTML={
-                                __html: asset_content
-                            }></script>
                     if _ext is 'coffee'
                         full_path = full_path.replace('.coffee', '.js')
                     return <script src=full_path async=@props.async></script>

                 when 'css', 'sass'
-                    if @props.inline
-                        source_path = path.join(global.PROJECT_ROOT, 'assets', @props.path)
-                        if _ext is 'sass'
-                            asset_content = compileSass(global.PROJECT_ROOT, source_path)
-                        else
-                            asset_content = fs.readFileSync(source_path).toString()
-                        asset_content = prefixAndMinifyCSS(asset_content)
-                        return <style dangerouslySetInnerHTML={
-                                __html: asset_content
-                            }></style>
                     if _ext is 'sass'
                         full_path = full_path.replace('.sass', '.css')
                     return <link
@@ -77,10 +24,4 @@
                     console.warn("Asset got unknown asset type (#{ _ext })")
                     return null

-        if @props.inline and _inline_asset_cache[global.ASSET_URL][@props.path]?
-            return _inline_asset_cache[global.ASSET_URL][@props.path]
-
-        output = _render()
-        if @props.inline
-            _inline_asset_cache[global.ASSET_URL][@props.path] = output
-        return output+        return _render()


add new codedeploy resources

add new codedeploy resources

--- src/coffin.coffee
+++ src/coffin.coffee
@@ -33,6 +33,10 @@
         Distribution: null
       CloudWatch:
         Alarm: null
+      CodeDeploy
+        Application: null
+        DeploymentConfig: null
+        DeploymentGroup: null
       DynamoDB:
         Table: null
       EC2:


Document/input coffeelint fixes

Document/input coffeelint fixes

--- tests/src/document/input.coffee
+++ tests/src/document/input.coffee
@@ -7,37 +7,39 @@
 describe 'src/document string interpolation', ->
     describe '`attrs`', ->
         it 'lookup neft:fragment', ->
-            source = createView """
+            source = createView '''
                 <neft:fragment neft:name="a" x="2">${attrs.x}</neft:fragment>
                 <neft:use neft:fragment="a" />
-            """
+            '''
             view = source.clone()

             renderParse view
             assert.is view.node.stringify(), '2'

         it 'is accessible by context', ->
-            source = createView """
-                <neft:fragment neft:name="a" x="2">${this.attrs.x}</neft:fragment>
+            source = createView '''
+                <neft:fragment neft:name="a" x="2">
+                    ${this.attrs.x}
+                </neft:fragment>
                 <neft:use neft:fragment="a" />
-            """
+            '''
             view = source.clone()

             renderParse view
             assert.is view.node.stringify(), '2'

         it 'lookup neft:use', ->
-            source = createView """
+            source = createView '''
                 <neft:fragment neft:name="a" x="1">${attrs.x}</neft:fragment>
                 <neft:use neft:fragment="a" x="2" />
-            """
+            '''
             view = source.clone()

             renderParse view
             assert.is view.node.stringify(), '2'

         it 'always keeps proper sources order', ->
-            source = createView """
+            source = createView '''
                 <neft:fragment neft:name="a" x="1">
                     <neft:fragment neft:name="b" x="1">
                         ${attrs.x}
@@ -45,7 +47,7 @@
                     <neft:use neft:fragment="b" x="4" />
                 </neft:fragment>
                 <neft:use neft:fragment="a" x="3" />
-            """
+            '''
             view = source.clone()

             renderParse view
@@ -82,10 +84,10 @@

     describe '`ids`', ->
         it 'refers to nodes', ->
-            source = createView """
+            source = createView '''

                 ${ids.first.attrs.label}
-            """
+            '''
             view = source.clone()

             renderParse view
@@ -95,10 +97,10 @@
             assert.is view.node.stringify(), '23'

         it 'is accessible by context', ->
-            source = createView """
+            source = createView '''

                 ${this.ids.first.attrs.label}
-            """
+            '''
             view = source.clone()

             renderParse view
@@ -108,13 +110,13 @@
             assert.is view.node.stringify(), '23'

     it 'file `ids` are accessed in fragments', ->
-        source = createView """
+        source = createView '''

             <neft:fragment neft:name="a">
                 ${ids.first.attrs.label}
             </neft:fragment>
             <neft:use neft:fragment="a" />
-        """
+        '''
         view = source.clone()

         renderParse view
@@ -125,9 +127,9 @@

     describe '`scope`', ->
         it 'is accessed in rendered file', ->
-            source = createView """
+            source = createView '''
                 ${scope.a}
-            """
+            '''
             view = source.clone()

             renderParse view,
@@ -140,9 +142,9 @@
             assert.is view.node.stringify(), '2'

         it 'is accesible by context', ->
-            source = createView """
+            source = createView '''
                 ${this.scope.a}
-            """
+            '''
             view = source.clone()

             renderParse view,
@@ -155,10 +157,10 @@
             assert.is view.node.stringify(), '2'

         it 'lookup neft:use', ->
-            source = createView """
+            source = createView '''
                 <neft:fragment neft:name="a">${scope.a}</neft:fragment>
                 <neft:use neft:fragment="a" />
-            """
+            '''
             view = source.clone()

             renderParse view,
@@ -171,11 +173,11 @@
             assert.is view.node.stringify(), '2'

         it 'lookup neft:each', ->
-            source = createView """
+            source = createView '''
                 <neft:blank neft:each="[1]">
                     ${scope.a}
                 </neft:blank>
-            """
+            '''
             view = source.clone()

             renderParse view,
@@ -188,9 +190,9 @@
             assert.is view.node.stringify(), '2'

     it 'handler is called on signal', ->
-        source = createView """
+        source = createView '''

-        """
+        '''
         view = source.clone()

         calls = 0
@@ -204,9 +206,9 @@
         assert.is calls, 1

     it 'returned handler is called on signal with context and parameters', ->
-        source = createView """
+        source = createView '''

-        """
+        '''
         view = source.clone()

         calls = 0
@@ -222,10 +224,10 @@
         assert.is calls, 1

     it 'attribute handler is called with proper context and parameters', ->
-        source = createView """
+        source = createView '''
             <neft:attr name="y" value="3" />

-        """
+        '''
         view = source.clone()

         calls = 0
@@ -240,10 +242,10 @@
         assert.is calls, 1

     it 'attribute handler is not called if the document is not rendered', ->
-        source = createView """
+        source = createView '''
             <neft:attr name="y" value="3" />

-        """
+        '''
         view = source.clone()

         calls = 0
@@ -261,10 +263,10 @@

     describe 'support realtime changes', ->
         it 'on `attrs`', ->
-            source = createView """
+            source = createView '''
                 <neft:fragment neft:name="a">${attrs.x}</neft:fragment>
                 <neft:use neft:fragment="a" x="2" y="1" />
-            """
+            '''
             view = source.clone()
             elem = view.node.children[0]

@@ -299,10 +301,10 @@
             assert.is view.node.stringify(), '2'

         it 'on `scope` neft:use', ->
-            source = createView """
+            source = createView '''
                 <neft:fragment neft:name="a">${scope.a}</neft:fragment>
                 <neft:use neft:fragment="a" />
-            """
+            '''
             view = source.clone()

             renderParse view,
@@ -312,11 +314,11 @@
             assert.is view.node.stringify(), '2'

         it 'on `scope` neft:each', ->
-            source = createView """
+            source = createView '''
                 <neft:blank neft:each="[1]">
                     ${scope.a}
                 </neft:blank>
-            """
+            '''
             view = source.clone()

             renderParse view,


Fixed removing tags.

Fixed removing tags.

--- app/assets/javascripts/app/controllers/tag.coffee
+++ app/assets/javascripts/app/controllers/tag.coffee
@@ -87,6 +87,7 @@

   destroy: (e) ->
     e.preventDefault()
+    e.stopPropagation()
     row = $(e.currentTarget).closest('tr')
     id = row.data('id')
     new DestroyConfirm(


Fast gulp build, again.

Fast gulp build, again.

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -45,18 +45,20 @@
 	,
 		gulp
 		.src './bower_components/jquery-pjax/jquery.pjax.js'
+		.pipe uglify()
 	,
 		gulp
 		.src './browser.coffee'
 		.pipe coffee()
+		.pipe uglify()
 	,
 		browserify()
 		.transform coffeeify
 		.require './lib/validation.coffee', expose: 'validation'
 		.bundle().pipe(source('validation.js')).pipe(buffer())  # epic wrapper, don't ask how does it work
+		.pipe uglify()
 	)
 	.pipe concat 'scripts.js'
-	.pipe uglify()
 	.pipe cleanDest './assets'
 	.pipe gulp.dest './assets'


nearly happy with browser tests

nearly happy with browser tests

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -37,9 +37,7 @@
   }
 }

-# TRAVIS_BUILD = process.env.TRAVIS_JOB_NUMBER
 ZUUL_OPTS = [ '--concurrency', 2 ]
-# if TRAVIS_BUILD? then ZUUL_OPTS.push '--sauce-connect', TRAVIS_BUILD

 gulp.task 'default', ->
   gulp.src SRC


Better nothing output

Better nothing output

--- src/index.cjsx
+++ src/index.cjsx
@@ -14,4 +14,4 @@

{messages}

       )
     else
-      return
+      return


Adding ln as base_unit

Adding ln as base_unit

Fixes: #1933


Add comments to filter_form coffee script module

--- app/assets/javascripts/modules/filter_form.coffee
+++ app/assets/javascripts/modules/filter_form.coffee
@@ -1,4 +1,10 @@
+# Wraps initMobileFilterSwitch, initFilterExpand, initFieldsetHeadlineClickHandler
+
 initFilterForm = ->
+
+  # General handling of shortening/teasing complete form for advanced search. Starts collapsed, unless it detects
+  # otherwise in sessionStorage
+
   initFilterExpand = ->

     $filterForm = $('.filter-form')
@@ -7,6 +13,7 @@
     collapseLabel = I18n.t 'js.less_filter_options'
     offer_filter_open = sessionStorage.getItem("offer_filter_open")

+    # Detect or set state: offer_filter_open
     unless offer_filter_open == "true"
       $filterForm
               .addClass 'filter-form--isCollapsed'
@@ -15,6 +22,7 @@

       sessionStorage.setItem("offer_filter_open", "false")

+    # Prepend expander button dynamically
     if !$('.filter-form__expander').length
       $filterForm.prepend '
' + expandLabel + '
'

@@ -23,8 +31,10 @@
           .html collapseLabel
           .attr 'aria-expanded', true

+    # var $expander here, after creation
     $expander = $('.filter-form__expander')

+    # Click handling for expander button click
     $expander.on 'click', ->

       if $filterForm.hasClass('filter-form--isCollapsed')
@@ -51,8 +61,12 @@

         sessionStorage.setItem("offer_filter_open", "false")

+  # In mediaquery 's', the inner fieldsets of the advanced search form collapse and the fieldset headlines get click
+  # handlers

   initFieldsetHeadlineClickHandler = ->
+
+    # Detect if in mediaquery 's'

     if ($(window).width() <= 440)

@@ -63,10 +77,13 @@

       $('.filter-form').find('.filter-form__fieldset__headline').attr 'aria-collapsed', true

+      # Click event
+
       $('.filter-form__fieldset__headline').on 'click', ->

         that = $(this)

+        # Toggle fieldset contents visibility
         that.next(".input").toggleClass 'is--hidden'

         if that.attr('aria-collapsed') == 'true'
@@ -74,11 +91,16 @@
         else
           that.attr('aria-collapsed', 'true')

+    # If not in 's', but resizing to 'm' or bigger, revert form in case of need
+
     else
       $('.filter-form')
         .find('.col-form-inner .input').removeClass 'is--hidden'
       .find('filter-form__fieldset__headline').attr 'aria-collapsed', 'true'

+
+  # In mediaquery 's', the whole advanced search form collapses (not just only teaser look, like collapsed state on desktop)
+  # Dynamically adding the according switch button here:

   initMobileFilterSwitch = ->

@@ -90,11 +112,14 @@
     mobileFilterSwitch = $('.filter-form__switch')
     filterForm = $('.filter-form')

+    # Detect if in mediaquery 's'

     if ($(window).width() <= 440)

       mobileFilterSwitch.show()
       filterForm.addClass 'is--hidden'
+
+      # Click event

       mobileFilterSwitch.on 'click', ->
         filterForm.toggleClass 'is--hidden'
@@ -108,10 +133,10 @@
           that.attr('aria-collapsed', 'true')

     else
-      mobileFilterSwitch.hide()
+      mobileFilterSwitch.hide
       filterForm.removeClass 'is--hidden'

-
+  # Calling subfunctions
   initMobileFilterSwitch()
   initFilterExpand()
   initFieldsetHeadlineClickHandler()


Added some simple validation for drop reflection before form submit

--- app/assets/javascripts/modules/raindrop.js.coffee
+++ app/assets/javascripts/modules/raindrop.js.coffee
@@ -85,10 +85,16 @@
       @reflectionInput.val('')
     .bind 'ajax:complete', (request,response) =>
       @reflectionsList.append(response.responseText)
+    .submit =>
+      @validateInput()

     $('html').bind 'click', (e) =>
-      @reflectionForm.removeClass('active')
+      @reflectionForm.removeClass('active')
+
+  validateInput: ->
+      # Validates the input client side to ease load on chat servers.
+      (@reflectionInput.attr('value').match(/^\s*$/) == null) and (@reflectionInput.val.length > 0)

 $.fn.rainDrop = ->
   new RainDrop(@,arguments[0])


Pulling the whole dataset.

Pulling the whole dataset.

--- admin/app/scripts/main.coffee
+++ admin/app/scripts/main.coffee
@@ -7,33 +7,45 @@
       recordWordPlural: 'datasets'
       sortDir:          'desc'
       sortField:        'title'
-      perPage:          10
+      perPage:          @size
       unsortedClass:    'glyphicon glyphicon-sort'
       ascSortClass:     'glyphicon glyphicon-sort-by-attributes'
       descSortClass:    'glyphicon glyphicon-sort-by-attributes-alt'

     @table = new DataTable [], tableOptions
     @table.loading true
+    req_size = new XMLHttpRequest()
+    req_size.open 'GET', 'http://ec2-52-10-17-100.us-west-2.compute.amazonaws.com:8080/datasets_index/_count', true
+    req_size.onload = =>
+      if req_size.status >= 200 and req_size.status < 400
+        count = JSON.parse(req_size.responseText).count
+        req = new XMLHttpRequest()
+        req.open 'GET', 'http://ec2-52-10-17-100.us-west-2.compute.amazonaws.com:8080/datasets_index/_search?&size=68', true

-    req = new XMLHttpRequest()
-    req.open 'GET', 'http://ec2-52-10-17-100.us-west-2.compute.amazonaws.com:8080/datasets_index/_search?', true
+        req.onload = =>
+          if req.status >= 200 and req.status < 400
+            response = JSON.parse req.responseText
+            hits = response.hits.hits.map (hit) => new Datasets @, hit
+            @table.rows hits
+            @table.loading false
+          else
+            alert "Error communicating with server"
+            @table.loading false

-    req.onload = =>
-      if req.status >= 200 and req.status < 400
-        response = JSON.parse req.responseText
-        hits = response.hits.hits.map (hit) => new Datasets @, hit
-        @table.rows hits
-        @table.loading false
+        req.onerror = =>
+          alert "Error communicating with server"
+          @table.loading false
+
+        req.send()
       else
         alert "Error communicating with server"
         @table.loading false

-    req.onerror = =>
+    req_size.onerror = =>
       alert "Error communicating with server"
       @table.loading false

-    req.send()
-
+    req_size.send()
     ko.applyBindings @

 class Datasets


allow .user_content to have elementTogglers

test plan:

make a wiki page.
go to the html view.
type:

<button class="element_toggler" aria-controls="thing">Show Dialog</button>
<form id="thing" data-turn-into-dialog style="display:none">
  if you see this it worked
</form>

save the page.
click the 'Show Dialog' button.
if it shows the dialog it works.

Change-Id: I818546b7e7cf396b9e8993e5d0067d79240c21fa
Reviewed-on: https://gerrit.instructure.com/11645
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Ryan Florence <2d8900489f91ac272fd9ce9d5789db6522668ca2@instructure.com>

--- app/coffeescripts/behaviors/elementToggler.coffee
+++ app/coffeescripts/behaviors/elementToggler.coffee
@@ -17,7 +17,7 @@
 #
 # <button class="element_toggler" aria-controls="thing">Show Thing Dialog</button>
 # <form id="thing" data-turn-into-dialog='{"width":450,"modal":true}' style="display:none">
-#   This will pop up as a dilog when you click the button and pass along the data-turn-into-dialog
+#   This will pop up as a dialog when you click the button and pass along the data-turn-into-dialog
 #   options.  then it will pass it through fixDialogButtons to turn the buttons in your markup
 #   into proper dialog buttons (look at fixDialogButtons to see what it does)
 #   <div class="button-container">
@@ -76,6 +76,13 @@

     $allElementsControllingRegion.each updateTextToState( if showRegion then 'Shown' else 'Hidden' )

-  $(document).delegate '.element_toggler[aria-controls]:not(.user_content *)', 'click', preventDefault ->
-    $region = $("##{$(this).attr('aria-controls')}")
+  $(document).delegate '.element_toggler[aria-controls]', 'click', preventDefault ->
+    $this = $(this)
+
+    # allow .links inside .user_content to be elementTogglers, but only for other elements inside of
+    # that .user_content area
+    $parent = $this.closest('.user_content')
+    $parent = $(document.body) unless $parent.length
+
+    $region = $parent.find("##{$this.attr('aria-controls')}")
     toggleRegion($region) if $region.length


:space_invader: fixed minimap tileset change when jumping to another layer

--- app/assets/javascripts/lib/nypl_inspector.coffee
+++ app/assets/javascripts/lib/nypl_inspector.coffee
@@ -95,6 +95,8 @@

     @overlay = Utils.addOverlay(@map, @tileset, @tiletype, 3)

+    @addMinimap() if @miniMapControl
+
   clearScreen: () ->
     # rest should be implemented in the inspector instance

@@ -115,11 +117,13 @@
     # rest should be implemented in the inspector instance

   addMinimap: () ->
+    @miniMapControl.removeFrom(@map) if @miniMapControl
+
     if @options.hasMiniMap
       zoomOffset = 5
       mini = Utils.mapOverlay(@tileset, @tiletype, 3, @map.minZoom-zoomOffset, @map.maxZoom-zoomOffset)
       @miniMap = new L.LayerGroup([mini])
-      miniMapControl = new L.Control.MiniMap(@miniMap,
+      @miniMapControl = new L.Control.MiniMap(@miniMap,
         toggleDisplay: true
         position: 'topleft'
         autoToggleDisplay: true


additional logs

additional logs

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -118,7 +118,6 @@
       @applyAnimate()
       console.log 'autohide-tree-view: applied setting animate'
       @hide true
-      console.log ''
       console.log 'autohide-tree-view: enabled'
     catch e
       console.error e
@@ -182,6 +181,7 @@

   applyHiddenWidth: (width) ->
     return unless @enabled
+    console.log 'autohide-tree-view: treeViewElement and parentNode found: ', treeViewEl?, 'and', treeViewEl.parentNode?
     width = @getHiddenWidth() if isNaN parseInt width
     treeViewEl.style.width = "#{width}px"
     treeViewEl.parentNode.style.width = "#{width}px"


Add index ddb record count.

Add index ddb record count.

--- app/assets/javascripts/pods/pods_store.js.coffee
+++ app/assets/javascripts/pods/pods_store.js.coffee
@@ -4,8 +4,23 @@
     @records
   update: (new_records) ->
     @records = @records.concat(new_records)
+    @countAll (c) ->
+      console.log c
     @writeObjects(new_records)
   writeObjects: (pods) ->
+    @database (db) ->
+      t = db.transaction 'pods', 'readwrite'
+      s = t.objectStore('pods')
+      for pod in pods
+        r = s.put(pod)
+  countAll: (callback) ->
+    @database (db) ->
+      t = db.transaction 'pods', 'readwrite'
+      s = t.objectStore('pods')
+      r = s.count()
+      r.onsuccess = ->
+        callback(r.result)
+  database: (callback) ->
     r = indexedDB.open('pods', 1)
     r.onupgradeneeded = (e) ->
       db = event.target.result
@@ -16,8 +31,4 @@
           unique: false
     r.onsuccess = (e) ->
       db = r.result
-      t = db.transaction 'pods', 'readwrite'
-      s = t.objectStore('pods')
-      for pod in pods
-        r = s.put(pod)
-  +      callback(db)


added move_to for in page navigation

added move_to for in page navigation

--- app/assets/javascripts/views/user_view.js.coffee
+++ app/assets/javascripts/views/user_view.js.coffee
@@ -1,8 +1,14 @@
 (($) ->
  class window.CommonView
    update_title: (title) ->
-    $("#page-title").html title
-    document.title = "Blabbr - #{title}"
+     $("#page-title").html title
+     document.title = "Blabbr - #{title}"
+
+   move_to: (hash) ->
+     hash = window.location.hash || hash
+     selector = $(hash)
+     selector.addClass('anchor')
+     $('html,body').animate({scrollTop: selector.offset().top},'slow')

  class window.UserView extends CommonView
     constructor: (@user) ->
@@ -42,6 +48,7 @@
       new PostView(topic) for topic in @topic.posts
       @paginate @selector.find('.pagination')
       new PostNewView @topic.topic
+      @move_to @selector.get(0)

     insertQuote: (e)->
       if $(e.target).is('p, ul')


fix(vertxEventBusProvider): avoid default options object mutations

--- src/angular-vertxbus-adapter.coffee
+++ src/angular-vertxbus-adapter.coffee
@@ -28,7 +28,7 @@
   * sockjsOptions (default {}): optional SockJS options (new SockJS(url, undefined, options))
 ###
 module = angular.module('knalli.angular-vertxbus', ['ng'])
-.constant('angularVertxbusOptions', DEFAULT_OPTIONS)
+.constant('angularVertxbusOptions', angular.extend({}, DEFAULT_OPTIONS))

 .provider('vertxEventBus', (angularVertxbusOptions) ->


Only include emissary Emitter in deprecation mode

--- src/config.coffee
+++ src/config.coffee
@@ -1,6 +1,5 @@
 _ = require 'underscore-plus'
 fs = require 'fs-plus'
-EmitterMixin = require('emissary').Emitter
 {CompositeDisposable, Disposable, Emitter} = require 'event-kit'
 CSON = require 'season'
 path = require 'path'
@@ -290,7 +289,6 @@
 #
 module.exports =
 class Config
-  EmitterMixin.includeInto(this)
   @schemaEnforcers = {}

   @addSchemaEnforcer: (typeName, enforcerFunction) ->
@@ -1147,6 +1145,9 @@
   resultObject

 if Grim.includeDeprecatedAPIs
+  EmitterMixin = require('emissary').Emitter
+  EmitterMixin.includeInto(Config)
+
   Config::restoreDefault = (scopeSelector, keyPath) ->
     Grim.deprecate("Use ::unset instead.")
     @unset(scopeSelector, keyPath)


META-72: Improve regex so its asserts start of line in more cases

--- src/config.coffee
+++ src/config.coffee
@@ -44,7 +44,7 @@

   @transitions:
     if Config.maps.transitions
-      regex: new RegExp "(?:^|\\s)((?:#{Config.projects.prefixes}-)(?:\\d+))\\s+(?:to\\s+|>\\s?)(#{(Config.maps.transitions.map (t) -> t.name).join "|"})", "i"
+      regex: new RegExp "^((?:#{Config.projects.prefixes}-)(?:\\d+))\\s+(?:to\\s+|>\\s?)(#{(Config.maps.transitions.map (t) -> t.name).join "|"})", "i"
       shouldRegex: new RegExp "\\s+>\\s?(#{(Config.maps.transitions.map (t) -> t.name).join "|"})", "i"

   @priority:
@@ -63,22 +63,22 @@

   @watch:
     notificationsRegex: /jira (allow|start|enable|disallow|disable|stop)( notifications)?/i
-    regex: new RegExp "(?:^|\\s)((?:#{Config.projects.prefixes}-)(?:\\d+)) (un)?watch(?: @?([\\w._]*))?", "i"
+    regex: new RegExp "^((?:#{Config.projects.prefixes}-)(?:\\d+)) (un)?watch(?: @?([\\w._]*))?", "i"

   @subtask:
     regex: new RegExp "subtask\\s+((?:#{Config.projects.prefixes}-)(?:\\d+)) ([^]+)", "i"

   @assign:
-    regex: new RegExp "(?:^|\\s)((?:#{Config.projects.prefixes}-)(?:\\d+))(?: (un)?assign)? @?([\\w._]*)\\s*$", "i"
+    regex: new RegExp "^((?:#{Config.projects.prefixes}-)(?:\\d+))(?: (un)?assign)? @?([\\w._]*)\\s*$", "i"

   @clone:
-    regex: new RegExp "(?:^|\\s)((?:#{Config.projects.prefixes}-)(?:\\d+))\\s*(?:(?:>|clone(?:s)?(?:\\s+to)?)\\s*)(?:#)?(#{Config.projects.channels})", "i"
+    regex: new RegExp "^((?:#{Config.projects.prefixes}-)(?:\\d+))\\s*(?:(?:>|clone(?:s)?(?:\\s+to)?)\\s*)(?:#)?(#{Config.projects.channels})", "i"

   @comment:
-    regex: new RegExp "(?:^|\\s)((?:#{Config.projects.prefixes}-)(?:\\d+))\\s?(?:<\\s?)([^]+)", "i"
+    regex: new RegExp "^((?:#{Config.projects.prefixes}-)(?:\\d+))\\s?(?:<\\s?)([^]+)", "i"

   @labels:
-    addRegex: new RegExp "(?:^|\\s)((?:#{Config.projects.prefixes}-)(?:\\d+))\\s?<(\\s*#\\S+)+$", "i"
+    addRegex: new RegExp "^((?:#{Config.projects.prefixes}-)(?:\\d+))\\s?<(\\s*#\\S+)+$", "i"
     regex: /(?:\s+|^)#\S+/g

   @search:


:bug: Select Bezier curve corners

:bug: Select Bezier curve corners

--- lib/cubic-bezier-curve.coffee
+++ lib/cubic-bezier-curve.coffee
@@ -318,8 +318,11 @@
 				for match in matches
 					idx = line.indexOf match
 					len = match.length
-					if idx isnt -1 and idx <= col and idx + len >= col
+					someMatch = match
+					somePattern = pattern
+					if idx isnt -1 and idx <= col + 1 and idx + len >= col - 1
 						return { start: idx, end: idx+len, pattern: pattern.source, select: match, row: row}
+
 		return {start: col, end: col, row: row}


fix: test case

fix: test case

--- tests/integration/components/api-filter-test.coffee
+++ tests/integration/components/api-filter-test.coffee
@@ -13,4 +13,4 @@

   @render hbs """{{api-filter}}"""

-  assert.equal @$().text().trim(), 'API Scanner URL FilterEnter the API endpoint to scan: eg. api.appknox.com. Do not specify the scheme (http://...), port (:443/...) & path (.../users)Save Filter+ ADD NEW URL  Are you sure you want to remove from url filters?CancelOk'
+  assert.equal @$().text().trim(), 'API Scanner URL FilterEnter the API endpoint to scan: eg. api.appknox.com. Do not specify the scheme (http://...), port (:443/...) & path (.../users)Save Filter+ ADD NEW URLAre you sure you want to remove from url filters?CancelOk'


clean up

clean up

--- examples/robocode2.coffee
+++ examples/robocode2.coffee
@@ -1,3 +1,10 @@
+###
+#
+# Node.js & CoffeeScript
+#
+####
+
+

 dgram = require('dgram')

@@ -9,11 +16,6 @@
 randInt = (min, max) ->
   Math.floor(Math.random() * (max - min + 1)) + min

-RED = [255, 0, 0]
-GREEN = [0, 255, 0]
-BLUE = [0, 0, 255]
-BLACK = [0, 0, 0]
-WHITE = [255, 255, 255]

 class EffectClient

@@ -100,122 +102,163 @@
     cb set, all, next, program
     return program

-client = new EffectClient
-  min: 0
-  max: 38
-  nick: process.env.TAG or "epe"
-  ip: process.argv[2] or "localhost"
-  port: 9909
-
-
-player = new Player client
-
-keyboardKeys =
-
-  o: player.createProgram (set, all, next) ->
-    all BLACK...
-    next 1000
-
-  p: player.createProgram (set, all, next) ->
-    all WHITE...
-    next 1000
-
-  r: player.createProgram (set, all, next) ->
-
-    all BLACK
-    len = 10
-
-    for i in [client.min..client.max]
-      for j in [0..len]
-        set i-j, BLACK...
-
-      for j in [0..len]
-        set i+j, RED...
-
-      next 20
-
-
-  b: player.createProgram (set, all, next) ->
-
-    all 10, 10, 10
-    next 0
-
-    for i in [10..255] by 5
-      all 0, 0, i
-      next 30
-
-    for i in [10..255] by 5
-      all 0, 0, 255-i
-      next 30
-
-    next 30
-
-
-
-  h: player.createProgram (set, all, next) ->
-    all GREEN...
-    next 100
-    all BLUE...
-    next 100
-
-  j: player.createProgram (set, all, next) ->
-    all BLACK...
-    next 100
-    all RED...
-    next 100
-
-  l: player.createProgram (set, all, next) ->
-
-    for i in [0..38/2]
-      set i, BLACK...
-
-    for i in [38/2..38]
-      set i, RED...
-
-    next 300
-
-    for i in [0..38/2]
-      set i, RED...
-
-    for i in [38/2..38]
-      set i, BLACK...
-
-    next 300
-
-
-  n: player.createProgram (set, all, next) ->
-
-    all BLACK
-
-    for i in [client.min..client.max]
-      set i-1, BLACK...
-      set i-2, BLACK...
-      set i, BLUE...
-      set i+1, BLUE...
-      set i+2, BLUE...
-      next 100
-
-
-
-
-
-process.stdin.on 'keypress', (char, key) ->
-  if char.charCodeAt(0) is 27 # esc
-    process.exit 0
-
-  return if not key # Ignore bad keys
-
-  console.log ("#{ k }: #{ p.length }" for k, p of keyboardKeys).join ", "
-
-  if key.name is "s" # stop
-    console.log "Stopping"
-    player.stop()
-    return
-
-  if program = keyboardKeys[key.name]
-    console.log "Playing", key.name
-    player.loop program
-  else
-    console.log "Unkown program #{ char }"
-
-
+
+
+
+
+
+main = ->
+
+  client = new EffectClient
+    min: 0
+    max: 38
+    nick: process.env.TAG or "epe"
+    ip: process.argv[2] or "localhost"
+    port: 9909
+
+
+  player = new Player client
+
+  RED = [255, 0, 0]
+  GREEN = [0, 255, 0]
+  BLUE = [0, 0, 255]
+  BLACK = [0, 0, 0]
+  WHITE = [255, 255, 255]
+
+  keyboardKeys =
+
+    o:
+      name: "OFF"
+      program: player.createProgram (set, all, next) ->
+        all BLACK...
+        next 1000
+
+    p:
+      name: "ON"
+      program: player.createProgram (set, all, next) ->
+        all WHITE...
+        next 1000
+
+    r:
+      name: "round red"
+      program: player.createProgram (set, all, next) ->
+
+        all BLACK
+        len = 10
+
+        for i in [client.min..client.max]
+          for j in [0..len]
+            set i-j, BLACK...
+
+          for j in [0..len]
+            set i+j, RED...
+
+          next 20
+
+
+    b:
+      name: "blue fade"
+      program: player.createProgram (set, all, next) ->
+
+        all 10, 10, 10
+        next 0
+
+        for i in [10..255] by 5
+          all 0, 0, i
+          next 30
+
+        for i in [10..255] by 5
+          all 0, 0, 255-i
+          next 30
+
+        next 30
+
+
+    h:
+      name: "fast green blue"
+      program: player.createProgram (set, all, next) ->
+        all GREEN...
+        next 100
+        all BLUE...
+        next 100
+
+    j:
+      name: "flash red"
+      program: player.createProgram (set, all, next) ->
+        all BLACK...
+        next 100
+        all RED...
+        next 100
+
+    l:
+      name: "red sides"
+      program: player.createProgram (set, all, next) ->
+
+        for i in [0..38/2]
+          set i, BLACK...
+
+        for i in [38/2..38]
+          set i, RED...
+
+        next 300
+
+        for i in [0..38/2]
+          set i, RED...
+
+        for i in [38/2..38]
+          set i, BLACK...
+
+        next 300
+
+
+    n:
+      name: "roud blue"
+      program: player.createProgram (set, all, next) ->
+
+        all BLACK
+
+        for i in [client.min..client.max]
+          set i-1, BLACK...
+          set i-2, BLACK...
+          set i, BLUE...
+          set i+1, BLUE...
+          set i+2, BLUE...
+          next 100
+
+
+
+
+  printHelp = ->
+
+    console.log ""
+    for k, ob of keyboardKeys
+      console.log "#{ k }: #{ ob.name }"
+    console.log ""
+
+
+  printHelp()
+  process.stdin.on 'keypress', (char, key) ->
+
+
+    if char.charCodeAt(0) is 27 # esc
+      process.exit 0
+    if key?.name is "c" and key.ctrl
+      process.exit 0
+
+
+    if char is "s" # stop
+      console.log "Stopping"
+      player.stop()
+      return
+
+    printHelp()
+
+    if program = keyboardKeys[char]?.program
+      console.log "Playing", program.name
+      player.loop program
+    else
+      console.log "Unkown program #{ char }"
+
+
+if require.main is module
+  main()


Improve CLI Beautify class to handle more error catching

--- lib/langs/cli-beautify.coffee
+++ lib/langs/cli-beautify.coffee
@@ -29,7 +29,8 @@

               # Process the command
               processCmd = (cmd) ->
-                if cmd
+                if typeof cmd is "string"
+
                   config = env: process.env
                   isWin = /^win/.test(process.platform)
                   unless isWin
@@ -68,20 +69,32 @@
                           return

                     else
-                      console.log "Beautifcation Error: ", err
+                      console.error "Beautifcation Error: ", err
                       callback err
                       deleteOutputFile()
                     return

+                # Check if there's an error
+                else if cmd instanceof Error
+                  return callback(cmd)
                 else
-                  console.log "Beautify CLI command not valid."
-                  callback(new Error("Beautify CLI command not valid."))
-                return
+                  console.error "CLI Beautifier command not valid."
+                  return callback(new Error("CLI Beautifier command not valid."+
+                    " Invalid command '#{cmd}'."))

               # Get the command
-              cmd = getCmd(info.path, outputPath, options, processCmd) # jshint ignore: line
-              processCmd cmd  if typeof cmd is "string"
+              try
+                cmd = getCmd(info.path, outputPath, options, processCmd) # jshint ignore: line
+              catch e
+                return callback(e)
+
+              if typeof cmd is "string"
+                processCmd cmd
+              # Check if there's an error
+              else if cmd instanceof Error
+                return callback(cmd)
+
             return

           return


On second thought, these fields shouldn't be private.

--- lib/poutine/collection.coffee
+++ lib/poutine/collection.coffee
@@ -1,7 +1,7 @@
 # Represents a collection and all the operation you can do on one.  Database
 # methods like find and insert operate through a collection.
 class Collection
-  constructor: (@name, @_database, @model)->
+  constructor: (@name, @database, @model)->

   # -- Finders --

@@ -35,16 +35,16 @@
       [callback, options] = [options, null]
     if !callback && !options && selector instanceof Function
       [callback, selector] = [selector, null]
-    if selector instanceof @_database.ObjectID || selector instanceof String
+    if selector instanceof @database.ObjectID || selector instanceof String
       if callback
-        this.one selector, options, callback
-      else
-        return this.where(_id: selector).extend(options)
+        @one selector, options, callback
+      else
+        return @where(_id: selector).extend(options)
     else
       if callback
-        this.all selector, options, callback
-      else
-        return this.where(selector).extend(options)
+        @all selector, options, callback
+      else
+        return @where(selector).extend(options)

   # Passes matching object from this query to callback.
   #
@@ -114,12 +114,12 @@
         [callback, selector] = [selector, null]
     throw new Error("Callback required") unless callback instanceof Function
     objects = []
-    this.each selector, options, (error, object)=>
+    @each selector, options, (error, object)=>
       return callback error if error
       if object
         objects.push object
       else
-        callback null, objects, @_database
+        callback null, objects, @database

   # Passes number of records in this query to callback.
   #
@@ -166,14 +166,14 @@

   # Passes error, collection and database to callback.
   _connect: (callback)->
-    @_database.driver (error, connection, end)=>
+    @database.driver (error, connection, end)=>
       return callback error if error
       connection.collection @name, (error, collection)=>
         if error
           end()
           callback error
         else
-          callback null, collection, @_database
+          callback null, collection, @database

   # Used internally to open a cursor for queries.
   _query: (selector, options, callback)->
@@ -211,7 +211,7 @@
 #     titles = (post.title for post in posts)
 #     console.log "Found these posts:", titles
 class Scope
-  constructor: (@_collection, @_selector, @_options)->
+  constructor: (@collection, @selector, @options)->

   # -- Refine selector/options --

@@ -222,11 +222,11 @@
   #   connect().find("posts").where(author_id: author._id)
   where: (selector)->
     combined = {}
-    if @_selector
-      combined[k] = v for k, v of @_selector
+    if @selector
+      combined[k] = v for k, v of @selector
     if selector
       combined[k] = v for k, v of selector
-    return new Scope(@_collection, combined, @_options)
+    return new Scope(@collection, combined, @options)

   # Instructs query to return only named fields.  You can call with multiple
   # arguments, an array argument or no arguments if you're only interested in
@@ -279,16 +279,16 @@
   # Returns a scope with combined options.
   extend: (options)->
     combined = {}
-    if @_options
-      combined[k] = v for k,v of @_options
+    if @options
+      combined[k] = v for k,v of @options
     if options
       combined[k] = v for k,v of options
-    return new Scope(@_collection, @_selector, combined)
+    return new Scope(@collection, @selector, combined)

   # Changes sorting order.
   sort: (fields, dir)->
     throw "Direction must be 1 (asc) or -1 (desc)" unless dir == 1 || dir == -1
-    sort = @_options.sort || []
+    sort = @options.sort || []
     for field in fields
       if Array.isArray(field)
         sort = sort.concat([f, dir] for f in field)
@@ -305,7 +305,7 @@
   #   connect().find("posts", author_id: id).one (err, post, db)->
   one: (callback)->
     throw new Error("Callback required") unless callback instanceof Function
-    @_collection.one @_selector, @_options, callback
+    @collection.one @selector, @options, callback

   # Passes each object to callback.  Passes null as last object.
   #
@@ -313,7 +313,7 @@
   #   connect().find("posts").each (err, post, db)->
   each: (callback)->
     throw new Error("Callback required") unless callback instanceof Function
-    @_collection.each @_selector, @_options, callback
+    @collection.each @selector, @options, callback

   # Passes all objects to callback.
   #
@@ -321,21 +321,21 @@
   #   connect().find("posts").all (err, posts)->
   all: (callback)->
     throw new Error("Callback required") unless callback instanceof Function
-    @_collection.all @_selector, @_options, callback
+    @collection.all @selector, @options, callback

   # Passes number of records in this query to callback.
   #
   # Example:
   #   connect().find("posts", author_id: id).count (err, count)->
   count: (callback)->
-    @_collection.count @_selector, callback
+    @collection.count @selector, callback

   # Passes distinct values callback.
   #
   # Example:
   #   connect().find("posts").distinct "author_id", (err, author_ids)->
   distinct: (key, callback)->
-    @_collection.distinct key, @_selector, callback
+    @collection.distinct key, @selector, callback

   # -- Transformation --
@@ -353,7 +353,7 @@
       fn = (object)-> object[name]
     throw new Error("Callback required") unless callback instanceof Function
     results = []
-    @_collection.each @_selector, @_options, (error, object)->
+    @collection.each @selector, @options, (error, object)->
       return callback error if error
       if object
         try
@@ -377,7 +377,7 @@
       fn = (object)-> object[name]
     throw new Error("Callback required") unless callback instanceof Function
     results = []
-    @_collection.each @_selector, @_options, (error, object)->
+    @collection.each @selector, @options, (error, object)->
       return callback error if error
       if object
         try
@@ -403,7 +403,7 @@
     if arguments.length < 3
       [value, fn, callback] = [null, value, fn]
     throw new Error("Callback required") unless callback instanceof Function
-    @_collection.each @_selector, @_options, (error, object)->
+    @collection.each @selector, @options, (error, object)->
       return callback error if error
       if object
         try
@@ -423,7 +423,7 @@
       @_cursor.nextObject (error, object)->
         callback error, object
     else
-      @_collection._query @_selector, @_options, (error, @_cursor)=>
+      @collection._query @selector, @options, (error, @_cursor)=>
         return callback error if error
         @_cursor.nextObject callback
     return
@@ -440,7 +440,4 @@
     return

-
-
-
 exports.Collection = Collection


cancel atom-scan's previous workspace.scan promise

--- lib/provider/atom-scan.coffee
+++ lib/provider/atom-scan.coffee
@@ -30,7 +30,7 @@
       items

   scanWorkspace: ->
-    scanPromise = atom.workspace.scan @searchRegex, (result) =>
+    @scanPromise = atom.workspace.scan @searchRegex, (result) =>
       if result?.matches?.length
         {filePath, matches} = result
         @updateItems matches.map (match) ->
@@ -41,10 +41,20 @@
             range: Range.fromObject(match.range)
           }

-    scanPromise.then =>
-      @finishUpdateItems()
+    @scanPromise.then (message) =>
+      # Relying on Atom's workspace.scan's specific implementation
+      # `workspace.scan` return cancellable promise.
+      # When cancelled, promise is NOT rejected, instead it's resolved with 'cancelled' message
+      if message isnt 'cancelled'
+        @scanPromise = null
+        @finishUpdateItems()

   search: (filePath) ->
+    if @scanPromise?
+      console.log 'cancel'
+      @scanPromise.cancel()
+      @scanPromise = null
+
     if filePath?
       # When non project file was saved. We have nothing todo, so just return old @items.
       return @items unless atom.project.contains(filePath)


Some more refactoring of RandomAccessList

--- lib/random_access_list.coffee
+++ lib/random_access_list.coffee
@@ -27,13 +27,13 @@
   half = (w) -> Math.floor w/2

   cons: (x) ->
-    new RandomAccessList(
+    [w, t, r] =
       if @trees?.rest() and @trees.get(0)[0] == @trees.get(1)[0]
         [[w1, t1], [w2, t2]] = @trees.take(2).toArray()
-        new List([1+w1+w2, [x,t1,t2]], @trees.drop(2))
+        [1+w1+w2, [x,t1,t2], @trees.drop(2)]
       else
-        new List([1, [x]], @trees)
-    )
+        [1, [x], @trees]
+    new RandomAccessList(new List([w, t], r))

   first: -> @trees.first()[1][0] if @trees


Use `in` instad of `some` where possible

--- lib/suggestion-builder.coffee
+++ lib/suggestion-builder.coffee
@@ -28,8 +28,7 @@

   isIn: (scope) ->
     scope.every (s1) =>
-      @options.scopeDescriptor.scopes.some (s) ->
-        s==s1
+      s1 in @options.scopeDescriptor.scopes

   getPrefix: (rx=/[\w.']+/) =>
     @lineSearch rx


add Tag::replace

add Tag::replace

--- src/xml.coffee
+++ src/xml.coffee
@@ -1,7 +1,7 @@
 { EventEmitter } = require 'events'
 { deep_merge, prettify, new_attrs, safe } = require './util'
 EVENTS = ['add', 'attr', 'attr:remove', 'text', 'raw', 'show', 'hide', 'remove',
-          'close']
+          'replace', 'close']

 parse_args = (name, attrs, children, opts) ->
@@ -220,6 +220,12 @@
             else if @closed
                 ">#{@content}</#{@name}>" # FIXME children ?

+    replace: (tag) =>
+        # FIXME should happen smthing smart?
+        # should both instances become one? when yes, how?
+        @emit 'replace', this, tag
+        this
+
     remove: () =>
         @closed = 'removed' unless @closed
         @emit 'remove', this


Fixed headers at response

Fixed headers at response

--- lib/zenproxy.fileserve.coffee
+++ lib/zenproxy.fileserve.coffee
@@ -9,18 +9,20 @@
     is_valid = true if stat?.isFile()

   if is_valid
-    extension = path.extname(url)?.slice(1) or "html"
     last_modified = fs.statSync(url).mtime
     cache_modified = request.headers["if-modified-since"]
+    headers =
+      "Content-Type"  : MIME[path.extname(url)?.slice(1) or "html"]
+      "Content-Length": stat.size
+      "Cache-Control" : "max-age=#{maxage.toString()}"
+      "Last-Modified" : last_modified
+
     if cache_modified? and __time(last_modified) is __time(cache_modified)
-      response.end undefined, 304, MIME[extension]
+      response.writeHead 304, headers
+      response.end()
     else
       fs.readFile url, (error, value) ->
-        response.writeHead 200,
-          "Content-Type"  : MIME[extension]
-          "Content-Length": stat.size
-          "Cache-Control" : "max-age=#{maxage.toString()}"
-          "Last-Modified" : last_modified
+        response.writeHead 200, headers
         response.end value
   else
     response.writeHead 200, "Content-Type": "text/html"


ListView: Fix removeItem index parameter

--- src/components/list/listview.coffee
+++ src/components/list/listview.coffee
@@ -58,7 +58,7 @@

   removeItem: (itemInstance, itemData, index) ->
-    destroy = (item, index) =>
+    destroy = (item, i) =>
       @emit 'ItemWasRemoved', item, i
       item.destroy()
       return yes


Shortened timeout.

Shortened timeout.

--- Jakefile.coffee
+++ Jakefile.coffee
@@ -129,7 +129,7 @@
       jake.Task['make'].reenable true
       jake.Task['make'].invoke()
       exports.watchTimeoutWaiting = false
-    , 500
+    , 100

 # DIRECTORIES