Caffeinated!

Caffeinated!

--- js/core/src/Runtime.coffee
+++ js/core/src/Runtime.coffee
@@ -5,7 +5,7 @@
 #     http://jefri.org

 do (_=_) =>
-	root = this
+	root = @

 	# ## JEFRi Namespace
 	root.JEFRi =
@@ -29,53 +29,58 @@
 	# ### Runtime Constructor
 	root.JEFRi.Runtime = (contextUri, options, protos) ->
 		# Private variables we'll be using throughout the class.
-		self = this
-		ec = this
-		this.settings = _.extend({
-			# The location of our context
-			contextUri     : contextUri,
-			# If an entity already exists, does JEFRi update or replace?
-			updateOnIntern : true,
-			# The constructor for the default store.
-			store          : JEFRi.LocalStore
-		}, options)
+		self = ec = @

 		# Prepare a promise for completing context loading.
 		ready = _.Deferred()
-		this.ready = ready.promise()
-
-		# In-memory representation of the loaded context.
-		this._context = {
-			meta: {},
-			contexts: {},
-			entities: {}
-		}
-		# In-memory store of JEFRi entities.
-		this._instances = {}
-		# List of entities that have been built, but not persisted.
-		this._new = []
-		# Collection of entities that have modifications since their inception or last persistence. Partitioned by types.
-		this._modified = {}
-		# The default store
-		this._store = new this.settings.store({runtime: this})
-
-		# Some helper methods to manage modified entities.
-		# Add an entity to the modified set.
-		this._modified.set = (entity) ->
-			# Check if the type exists yet
-			if not self._modified[entity._type()]
-				# Add the type, since we didn't have it before.
-				self._modified[entity._type()] = {}
-
-			# Add the entity to the bucket.
-			self._modified[entity._type()][entity.id()] = entity
-			@_modified
-
-		# Remove an entity from the modified set.
-		this._modified.remove = (entity) ->
-			delete self._modified[entity._type()][entity.id()]
-			@_modified
-
+
+		# Fill in all the privileged properties
+		_.extend @,
+			settings: _.extend
+				# The location of our context
+				contextUri: contextUri,
+				# If an entity already exists, does JEFRi update or replace?
+				updateOnIntern: true,
+				# The constructor for the default store.
+				store: JEFRi.LocalStore
+			, options
+
+			ready: ready.promise()
+
+			# In-memory representation of the loaded context.
+			_context:
+				meta: {}
+				contexts: {}
+				entities: {}
+
+			# In-memory store of JEFRi entities.
+			_instances: {}
+
+			# List of entities that have been built, but not persisted.
+			_new: []
+
+			# Collection of entities that have modifications since their inception or last persistence. Partitioned by types.
+			_modified:
+
+				# Some helper methods to manage modified entities.
+				# Add an entity to the modified set.
+				set: (entity) ->
+					# Check if the type exists yet
+					if not self._modified[entity._type()]
+						# Add the type, since we didn't have it before.
+						self._modified[entity._type()] = {}
+
+					# Add the entity to the bucket.
+					self._modified[entity._type()][entity.id()] = entity
+					@_modified
+
+				# Remove an entity from the modified set.
+				remove: (entity) ->
+					delete self._modified[entity._type()][entity.id()]
+					@_modified
+
+		# Build the default store
+		@_store = new @settings.store({runtime: @})

 		# #### Private helper functions
 		# These handle most of the heavy lifting of building Entity classes.
@@ -90,12 +95,12 @@

 		# Takes a "raw" context object and orders it into the internal _context
 		# storage.  Also builds constructors and prototypes for the context.
-		_set_context = (context, protos) ->
+		_set_context = (context, protos) =>
 			# Save the attributes
-			_.extend(ec._context.attributes, context.attributes)
-
-			# Prepare each entity
-			_.each(context.entities, (definition, name) ->
+			_.extend ec._context.attributes, context.attributes
+
+			# Prepare each entity. Uses _.each to put (definition, name) in a closure.
+			_.each context.entities, (definition, name) ->
 				# Keep the definition locally, modifying it directly with the ctor and prototypes.
 				ec._context.entities[name] = definition
 				# Ready the instances bucket
@@ -103,13 +108,13 @@

 				# Build an entity's constructor.
 				definition.Constructor = (proto) ->
-					self = this
+					self = @

 					# Set the privileged accounting and property data.
-					this._new = true
-					this._modified = {_count: 0}
-					this._fields = {}
-					this._relationships = {}
+					@_new = true
+					@_modified = {_count: 0}
+					@_fields = {}
+					@_relationships = {}
 					# Check for runtime prototype override.
 					proto = proto || {}

@@ -117,40 +122,35 @@
 					proto[definition.key] = proto[definition.key] || _.UUID.v4()

 					# Set a bunch of default values, so they're all available.
-					_.each(definition.properties, (property, name) ->
+					for name, property of definition.properties
 						# Use the value provided to the constructor, or the default.
 						def = proto[name] || _default(property.type)
 						self[name](def)
-					)

 					# Attach a privileged copy of the full id, more for debugging than use.
-					this._id = this.id(true)
+					@_id = @id(true)

 					# Add runtime methods
-					_.extend(this.prototype, proto.prototype)
+					_.extend @prototype, proto.prototype

 					# Set a few event handlers
 					# Manage accounting after an entity has been persisted
-					_.on(this, 'persisted', () ->
-						this._new = false
-						this._modified = {
+					_.on @, 'persisted', () ->
+						@_new = false
+						@_modified = {
 							_count: 0
 						}
-						ec._modified.remove(this)
-					)
+						ec._modified.remove(@)
 					@

 				#Set up the prototype for any of this entity.
 				_build_prototype(name, definition, (protos && protos[name]))
-			)

 			ready.resolve()

 		# Set up all the required methods - id(), _type(), and the mutaccs.
-		_build_prototype = (name, definition, proto) ->
-			ec = self
-
-			_.extend(definition.Constructor.prototype,
+		_build_prototype = (name, definition, proto) =>
+			_.extend definition.Constructor.prototype,
 				# Get this entity's type. Use the closure'd reference.
 				_type: (full) ->
 					full = full || false
@@ -158,14 +158,14 @@

 				# Get this entity's ID.
 				id: (full) ->
-					return (full && "#{this._type()}/" || "") + this[definition.key]()
+					return (full && "#{@_type()}/" || "") + @[definition.key]()

 				# Find the status of an entity.
 				_status: () ->
 					state = "MODIFIED"
-					if (this._new)
+					if (@_new)
 						state = "NEW"
-					else if (_.isEmpty(this._modified))
+					else if (_.isEmpty(@_modified))
 						state = "PERSISTED"
 					return state

@@ -177,10 +177,10 @@
 					deferred = _.Deferred().then(callback)
 					top = !transaction
 					transaction = top && new JEFRi.Transaction() || transaction
-					transaction.add(this)
+					transaction.add(@)

 					#Call the on_persist handler
-					this.trigger('persisting', this, transaction)
+					@trigger('persisting', @, transaction)

 					#If we're on top, run the transaction...
 					if ( top ) then transaction.persist(callback)
@@ -189,189 +189,174 @@

 				# Encode returns the bare object.
 				_encode: () ->
-					min = {}
-
-					min._type = this._type()
+					min = {_type: @_type()}

 					#Add all the properties to the writer.
-					_.each(definition.properties, (prop, name) =>
-						min[name] = this[name]()
-					)
+					(min[prop] = @[prop]()) for prop of definition.properties

 					# Don't add relationships. Any walker will be responsible for adding only the entities they need.
-
 					return min
-			)

 			# Alias _encode as toJSON for ES5 JSON.stringify()
 			definition.Constructor.prototype.toJSON = definition.Constructor.prototype._encode

-			# Prepare property mutaccs.
-			_.each(definition.properties, (property, field) ->
-				_build_mutacc(definition, field, property)
-			)
+			# Prepare property mutators and accsessors.
+			_build_mutacc(definition, field, property) for field, property of definition.properties

 			# Prepare navigation mutaccs.
-			_.each(definition.relationships, (relationship, rel_name) ->
-				_build_relationship(definition, rel_name, relationship)
-			)
-
-
-			if (proto) then _.extend(definition.Constructor.prototype, proto.prototype)
+			_build_relationship(definition, rel_name, relationship) for rel_name, relationship of definition.relationships
+
+			# Add any additional prototypes functions
+			if (proto) then _.extend definition.Constructor.prototype, proto.prototype

 		# Prepare a mutacc for a specific property.
 		# The property mutacc must handle entity accounting details.
-		_build_mutacc = (definition, field, property) ->
+		_build_mutacc = (definition, field, property) =>
 			# Each field name is its own function combining getters and setters, depending on arguments.
 			definition.Constructor.prototype[field] = (value) ->
 				# Overloaded getter and setter.
-				if (undefined isnt value)
+				if (undefined isnt value)
 					# Value is defined, so this is a setter
-					return this[field].set.call(this, value)
-				else
+					return @[field].set.call(@, value)
+				else
 					# Just a getter.
-					return this[field].get.call(this)
+					return @[field].get.call(@)

 			# Add the actual getters and setters to the new field
-			_.extend(definition.Constructor.prototype[field],
+			_.extend definition.Constructor.prototype[field],
 				# The setter has some accounting details to handle
 				set: (value) ->
 					# Only actually update it if it is a new value.
-					if (value isnt this._fields[field])
+					if (value isnt @_fields[field])
 						# The actual set
-						this._fields[field] = value
+						@_fields[field] = value

 						# Update the modified list if not set...
-						if (!this._modified[field])
-							this._modified[field] = this._fields[field]
-							this._modified._count += 1
-							ec._modified.set(this)
+						if (!@_modified[field])
+							@_modified[field] = @_fields[field]
+							@_modified._count += 1
+							ec._modified.set(@)
 						else
 							# It might be getting set to the old value...
-							if (this._modified[field] is value)
-								delete this._modified[field]
-								this._modified._count -= 1
+							if (@_modified[field] is value)
+								delete @_modified[field]
+								@_modified._count -= 1
 							# If it was the last property, remove from the context's modified list.
-							if (this._modified._count is 0)
-								ec._modified.remove(this)
+							if (@_modified._count is 0)
+								ec._modified.remove(@)

 						# Notify observers
-						_.trigger(this, "modify", [field, value])
+						_.trigger(@, "modify", [field, value])
 				get: () ->
 					# Just a getter.
-					return this._fields[field]
-			)
+					return @_fields[field]

 		# Attach the mutators and accessors (mutaccs) to the prototype.
 		#/* TODO Thoroughly debug these functions... */
-		_build_relationship = (definition, field, relationship) ->
-			ec = self
-
+		_build_relationship = (definition, field, relationship) =>
 			# The relationship is the name of a function that acts as getter/setter
 			definition.Constructor.prototype[field] = (entity) ->
 				# Use arguments, since we might have a few things coming.
 				if arguments.length > 0
 					set = (relationship.type is "has_many") && "add" || "set"
-					return this[field][set].apply(this, arguments)
+					return @[field][set].apply(@, arguments)
 				else
-					return this[field].get.call(this)
+					return @[field].get.call(@)

 			# The multiple relations functions.
 			if "has_many" is relationship.type
-				_.extend(definition.Constructor.prototype[field],
+				_.extend definition.Constructor.prototype[field],
 					# Return the set of entities in the relationship.
 					get: (longGet) ->
 						# Lazy load
-						#if (longGet)
+						#if (longGet)
 							# This needs a bit of thought
 							#TODO
 						# Check if the field has ever been set
-						if !(field of this._relationships)
+						if !(field of @_relationships)
 							# The field hasn't been set, so we haven't ever gotten this relationship before.
 							# We'll need to go through and fix that.
 							# We'll need to grab everything who points to us...
-							self = this
-							this._relationships[field] = []
+							self = @
+							@_relationships[field] = []
 							# Loop over every entity this relationship could point to
-							_.each(ec._instances[relationship.to.type], (type) ->
+							for type in ec._instances[relationship.to.type]
 								# If these are related
-								if (type[relationship.to.property]() is self[relationship.property]())
+								if (type[relationship.to.property]() is self[relationship.property]())
 									# Add it
-									self._relationships[field].push(this)
-							)
-						return this._relationships[field]
+									self._relationships[field].push(@)
+						return @_relationships[field]

 					# Add an entity to the relationship.
 					add: (entity) ->
 						if (_.isArray(entity))
-							this[field].add.call(this, e) for e in entity
-							return this
-
-						if !(field of this._relationships)
+							@[field].add.call(@, e) for e in entity
+							return @
+
+						if !(field of @_relationships)
 							#Lazy load
-							this[field].get.call(this)
-
-						if (_.indexBy(this._relationships[field], _.bind(JEFRi.EntityComparator, null, entity)) < 0)
+							@[field].get.call(@)
+
+						if (_.indexBy(@_relationships[field], _.bind(JEFRi.EntityComparator, null, entity)) < 0)
 							#There is not a local reference to the found entity.
-							this._relationships[field].push(entity)
+							@_relationships[field].push(entity)

 							#Call the reverse setter
 							#	Need to find the back relationship...
-							back_rel = ec.back_rel(this._type(), field, relationship)
+							back_rel = ec.back_rel(@_type(), field, relationship)
 							#	Make sure it exists
-							if back_rel then entity[back_rel.name].set.call(entity, this)
+							if back_rel then entity[back_rel.name].set.call(entity, @)

 						# Notify observers
-						_.trigger(this, "modify", [field, entity])
-
-						return this
-				)
+						_.trigger(@, "modify", [field, entity])
+
+						return @
 			# Mutaccs for has_a and is_a
 			else
-				_.extend(definition.Constructor.prototype[field],
+				_.extend definition.Constructor.prototype[field],
 					get: (longGet) ->
 						#if(longGet) {
 						#	// Lazy load
 						#	// This needs a bit of thought
 						#	//TODO
 						#}
-						if(this._relationships[field] is undefined)
+						if(@_relationships[field] is undefined)
 							# Just need the one...
-							this._relationships[field] = ec._instances[relationship.to.type][this[relationship.property]()]
+							@_relationships[field] = ec._instances[relationship.to.type][@[relationship.property]()]
 							# Make sure we found one
-							if(this._relationships[field] is undefined)
+							if(@_relationships[field] is undefined)
 								# If not, create it.
 								key = {}
-								key[ec.definition(relationship.to.type).key] = this[relationship.to.property]()
-								this[field](ec.build(relationship.to.type, key))
-
-						return this._relationships[field]
+								key[ec.definition(relationship.to.type).key] = @[relationship.to.property]()
+								@[field](ec.build(relationship.to.type, key))
+
+						return @_relationships[field]

 					set: (entity) ->
-						id = this[relationship.property]()
-						this._relationships[field] = entity
+						id = @[relationship.property]()
+						@_relationships[field] = entity
 						if( id isnt entity[relationship.to.property]())
 							# Changing
 							entity[relationship.to.property](id)
 							if( "is_a" isnt relationship.type)
 								#Add or set this to the remote entity
 								#Need to find the back relationship...
-								back_rel = ec.back_rel(this._type(), field, relationship)
+								back_rel = ec.back_rel(@_type(), field, relationship)
 								back = ("has_many" is back_rel.type) && 'add' || 'set'
-								entity[back_rel.name][back].call(entity, this)
+								entity[back_rel.name][back].call(entity, @)

 						# Notify observers
-						_.trigger(this, "modify", [field, entity])
-
-						return this
-				)
-
-		if (options && options.debug)
+						_.trigger(@, "modify", [field, entity])
+
+						return @
+
+		# Prepare the runtime with the given contexts.
+		if (options && options.debug)
 			# The context object was provided by the caller
 			_set_context(options.debug.context, protos)
-		else if (!this.settings.contextUri)
+		else if (!@settings.contextUri)
 		else
-			_.get(this.settings.contextUri, {
+			_.get(@settings.contextUri, {
 				dataType: "application/json"
 			}).done(
 				(data) ->
@@ -379,78 +364,75 @@
 					data = _.isString(data) && JSON.parse(data) || data
 					_set_context(data, protos)
 			)
-		return @
+		@

 	# #### Entity Array helper
 	pushResult = (entity) ->
 		type = entity._type()
-		if (!this[type]) then this[type] = []
-		this[type].push(entity)
+		if (!@[type]) then @[type] = []
+		@[type].push(entity)

 	# #### Runtime Prototype
-	root.JEFRi.Runtime.prototype = _.extend({}, JEFRi.Runtime.prototype, {
+	root.JEFRi.Runtime.prototype = _.extend {}, JEFRi.Runtime.prototype,
 		# Reset the runtime's data, maintains context definitions.
 		clear: () ->
-			this._modified = {}
-			this._new = []
-			this._instances = {}
-			return this
+			@_modified = {}
+			@_new = []
+			@_instances = {}
+			return @

 		# Get the definition of an entity type.
 		definition: (name) ->
 			name = (typeof name is "string") && name || name._type()

-			return this._context.entities[name]
+			return @_context.entities[name]

 		# Find the relationship back to this entity, if it exists
 		back_rel: (type, field, relationship) ->
-			ec = this
+			ec = @
 			def = ec.definition(relationship.to.type)
 			back = null
-			_.each(def.relationships, (rel, srel_name) ->
-				if (rel.to.type is type && srel_name isnt field)
+			for srel_name, rel of def.relationships
+				if (rel.to.type is type && srel_name isnt field)
 					#Found it
 					back = rel
 					back.name = srel_name
-			)
 			return back

 		# Add the methods in the extend prototype to the prototype of type specified
 		# affecting _ALL_ instances, both current and future, of type.
 		extend: (type, extend) ->
-			if (this._context.entities[type])
-				_.extend(
-					this._context.entities[type].Constructor.prototype,
-					extend.prototype
-				)
+			if (@_context.entities[type])
+				_.extend @_context.entities[type].Constructor.prototype, extend.prototype
+

 		# Return the canonical memory reference of the entity.
 		intern: (entity, updateOnIntern) ->
-			updateOnIntern = !!updateOnIntern || this.settings.updateOnIntern
-
-			if (entity.length && ! entity._type)
+			updateOnIntern = !!updateOnIntern || @settings.updateOnIntern
+
+			if (entity.length && ! entity._type)
 				#Array-like
 				q = entity.length
 				for i in [0..q]
-					entity[i] = this.intern(entity[i], updateOnIntern)
+					entity[i] = @intern(entity[i], updateOnIntern)
 				return entity

 			ret
-			if (updateOnIntern)
+			if (updateOnIntern)
 				#Merge the given entity into the stored entity.
-				ret = this._instances[entity._type()][entity.id()] || entity
-				_.extend(ret._fields, entity._fields)
-			else
+				ret = @_instances[entity._type()][entity.id()] || entity
+				_.extend ret._fields, entity._fields
+			else
 				#Take the stored one if possible, otherwise use the given entity.
-				ret = this._instances[entity._type()][entity.id()] || entity
-
+				ret = @_instances[entity._type()][entity.id()] || entity
+
 			#Update the saved entity
-			this._instances[entity._type()][entity.id()] = ret
+			@_instances[entity._type()][entity.id()] = ret
 			return ret

 		# Return a new instance of an object described in the context.
 		build: (type, obj) ->
-			def = this.definition(type)
+			def = @definition(type)
 			obj = obj || {}
 			# We are going to build the new entity first, then, if there is a local
 			# instance, we will extend the local instance with the new instance.
@@ -459,32 +441,30 @@
 				# If the entity key is specified in obj, check the local storage.
 				demi = {_type : type}
 				demi[def.key] = obj[def.key]
-				instance = this.find(demi)
-				if (instance.length > 0)
+				instance = @find(demi)
+				if (instance.length > 0)
 					# Local instance, extend it with the new obj and return local.
 					instance = instance[0]
-					_.extend(instance._fields, r._fields)
+					_.extend instance._fields, r._fields
 					return instance
-			this._instances[type][r.id()] = r
-			this._new.push(r)
+			@_instances[type][r.id()] = r
+			@_new.push(r)
 			return r

 		# Expand and intern a transaction.
 		expand: (transaction, action) ->
-			self = this
+			self = @
 			entities = transaction.entities
 			action = action || "persisted"

 			ret = []

-			if (entities)
-				_.each(entities, (entity) ->
-					e = self.build(entity._type, entity)
-					e = self.intern(e, true)
-					#Make the entity not new...
-					_.trigger(e, action)
-					ret.push(e)
-				)
+			for entity in entities
+				e = self.build(entity._type, entity)
+				e = self.intern(e, true)
+				#Make the entity not new...
+				_.trigger(e, action)
+				ret.push(e)

 			transaction.entities = ret
 			return ret
@@ -493,27 +473,25 @@
 		transaction: (spec) ->
 			spec = spec || []

-			return new JEFRi.Transaction(spec, this._store)
+			return new JEFRi.Transaction(spec, @_store)

 		# Return an interned entity from the local instance matching spec.
 		#
 		# Spec requires an _type property and the entity key.
 		find: (spec) ->
-			if (typeof spec is "string")
+			if (typeof spec is "string")
 				spec = {_type : spec}
 			to_return = []
-			r = this.definition(spec._type)
-			results = this._instances[spec._type]
-
-			if (spec.hasOwnProperty(r.key))
+			r = @definition(spec._type)
+			results = @_instances[spec._type]
+
+			if (spec.hasOwnProperty(r.key))
 				# If a key is set, return only that result.
-				if (results[spec[r.key]])
+				if (results[spec[r.key]])
 					to_return.push(results[spec[r.key]])
-			else
+			else
 				# Add results to an array to clean up the return for the user.
-				_.each(results, (result) ->
-					to_return.push(result)
-				)
+				to_return.push(result) for key, result of results

 			return to_return

@@ -524,9 +502,9 @@
 		# result array will have one entity per spec.
 		get: (spec) ->
 			spec = _.isArray(spec) && spec || [spec]
-			self = this
+			self = @
 			results = {}
-			transaction = this.transaction()
+			transaction = @transaction()
 			deferred = _.Deferred()

 			results.push = pushResult
@@ -534,21 +512,17 @@
 			for _spec in spec
 				#Add the queries
 				_type = (_spec._type instanceof Function) && _spec._type() || _spec._type
-				def = this.definition(_type)
+				def = @definition(_type)
 				id = _spec[def.key]

 				#Check if the ID is set and exists locally
-				if ( id? && this._instances[_type][id])
+				if ( id? && @_instances[_type][id])
 					#It is local, so use that one
-					results.push(this._instances[_type][id])
-				else
+					results.push(@_instances[_type][id])
+				else
 					#Otherwise, add to transaction
 					transaction.add(_spec)

-#					if (this.hasOwnProperty("_page"))
-#						#Add the page to the meta
-#						transaction.attributes({page : this._page})
-#						delete this._page
 	#/*
 	#				TODO: If there are multiple specs, this will not work!
 	#				TODO: Need to figure out what a page means for multiple specs.
@@ -556,16 +530,14 @@
 	#*/

 			# If transaction is not empty
-			if (transaction.entities.length > 0)
+			if (transaction.entities.length > 0)
 				# Run the transaction
 				transaction.get().done((transaction) ->
 					# Merge the result sets, adding `gotten` things to `had` things.
-					_.each(transaction.entities, (entity) ->
-						results.push(entity)
-					)
+					results.push(entity) for entity in transaction.entities
 					deferred.resolve(results, transaction.attributes)
 				)
-			else
+			else
 				# just resolve...
 				deferred.resolve(results, {})
 			return deferred.promise()
@@ -575,7 +547,7 @@
 			spec = (spec instanceof Array) ? spec : [spec]
 			d = _.Deferred()

-			this.get(spec).then((data, meta) ->
+			@get(spec).then((data, meta) ->
 				_type = spec._type instanceof Function && spec._type() || spec._type
 				d.resolve(data[_type].pop(), meta)
 			)
@@ -584,52 +556,45 @@

 		# Save all the new entities.
 		save_new: (store) ->
-			transaction = this.transaction()
-			_.trigger(this, 'saving')
+			transaction = @transaction()
+			_.trigger(@, 'saving')

 			#Add all new entities to the transaction
-			transaction.add(this._new)
-
-			return this._save(transaction, store)
+			transaction.add(@_new)
+
+			return @_save(transaction, store)

 		# Save all entities with changes, including new entities.
 		save_all: (store) ->
-			transaction = this.transaction()
-			_.trigger(this, 'saving')
+			transaction = @transaction()
+			_.trigger(@, 'saving')

 			#Add all new entities to the transaction
 			# Modified is keyed by type...
-			_.each(this._modified, (modified) ->
+			for t, modified of @_modified
 				# ...and each key contains an object of entity instances
-				_.each(modified, (entity) ->
+				for k, entity of modified
 					entity._persist(transaction)
-				)
-			)
-
-			_.each(this._new, (neu) ->
-				this.persist(neu)
-			)
-
-			return this._save(transaction, store)
+
+			@persist(neu) for neu in @_new
+
+			return @_save(transaction, store)

 		_save: (transaction, store) ->
-			store = store || this._store
+			store = store || @_store
 			return store.execute('persist', transaction).then(
-				_.bind(this.expand, this)
+				_.bind(@expand, @)
 			)

 		# Returns transaction of all entities in local cache.
 		get_transaction_dump: () ->
-			transaction = this.transaction()
+			transaction = @transaction()

 			#Add all entities to the transaction
 			# _instances is keyed by type...
-			_.each(this._instances, (instance) ->
+			for t, instance in @_instances
 				# ...and each key contains an object of entity instances
-				_.each(instance, (entity) ->
+				for k, entity in instance
 					transaction.add(entity)
-				)
-			)
-
-			return transaction
-	})
+
+			return transaction


Dont set uid unless node needs it

Dont set uid unless node needs it

--- src/modules/Identity.coffee
+++ src/modules/Identity.coffee
@@ -17,7 +17,7 @@
       unless generate == false
         object._gss_id = id ||=
           "$" + (object.id || ++Identity.uid)
-      @[id] = object
+        @[id] = object
     return id

   get: (id) ->


:bug: Only move result file if it exists

--- lib/latex.coffee
+++ lib/latex.coffee
@@ -114,7 +114,8 @@
   moveResult: (result, filePath) ->
     originalFilePath = result.outputFilePath
     result.outputFilePath = @alterParentPath(filePath, result.outputFilePath)
-    fs.moveSync(originalFilePath, result.outputFilePath)
+    if fs.existsSync(originalFilePath)
+      fs.moveSync(originalFilePath, result.outputFilePath)

     syncFilePath = originalFilePath.replace(/\.pdf$/, '.synctex.gz')
     if fs.existsSync(syncFilePath)


:memo: Provide detailed docs on fsUtils

:memo: Provide detailed docs on fsUtils

--- src/fs-utils.coffee
+++ src/fs-utils.coffee
@@ -7,9 +7,22 @@
 path = require 'path'

 # Public: Useful extensions to node's built-in fs module
+#
+# Important, this extends Node's builtin in ['fs' module][fs], which means that you
+# can do anything that you can do with Node's 'fs' module plus a few extra
+# functions that we've found to be helpful.
+#
+# [fs]: http://nodejs.org/api/fs.html
 fsExtensions =
-  # Make the given path absolute by resolving it against the
-  # current working directory.
+  # Public: Make the given path absolute by resolving it against the current
+  # working directory.
+  #
+  # * relativePath:
+  #   The String containing the relative path. If the path is prefixed with
+  #   '~', it will be expanded to the current user's home directory.
+  #
+  # Returns the absolute path or the relative path if it's unable to determine
+  # it's realpath.
   absolute: (relativePath) ->
     return null unless relativePath?

@@ -25,11 +38,12 @@
     catch e
       relativePath

-  # Returns true if a file or folder at the specified path exists.
+  # Public: Returns true if a file or folder at the specified path exists.
   exists: (pathToCheck) ->
+    # TODO: rename to existsSync
     pathToCheck? and fs.statSyncNoException(pathToCheck) isnt false

-  # Returns true if the specified path is a directory that exists.
+  # Public: Returns true if the given path exists and is a directory.
   isDirectorySync: (directoryPath) ->
     return false unless directoryPath?.length > 0
     if stat = fs.statSyncNoException(directoryPath)
@@ -37,6 +51,7 @@
     else
       false

+  # Public: Asynchronously checks that the given path exists and is a directory.
   isDirectory: (directoryPath, done) ->
     return done(false) unless directoryPath?.length > 0
     fs.exists directoryPath, (exists) ->
@@ -49,7 +64,7 @@
       else
         done(false)

-  # Returns true if the specified path is a regular file that exists.
+  # Public: Returns true if the specified path exists and is a file.
   isFileSync: (filePath) ->
     return false unless filePath?.length > 0
     if stat = fs.statSyncNoException(filePath)
@@ -57,7 +72,7 @@
     else
       false

-  # Returns true if the specified path is executable.
+  # Public: Returns true if the specified path is executable.
   isExecutableSync: (pathToCheck) ->
     return false unless pathToCheck?.length > 0
     if stat = fs.statSyncNoException(pathToCheck)
@@ -65,8 +80,14 @@
     else
       false

-  # Returns an array with the paths of the files and folders
-  # contained in the directory path.
+  # Public: Returns an Array with the paths of the files and directories
+  # contained within the directory path. It is not recursive.
+  #
+  # * rootPath:
+  #   The absolute path to the directory to list.
+  # * extensions:
+  #   An array of extensions to filter the results by. If none are given, none
+  #   are filtered (optional).
   listSync: (rootPath, extensions) ->
     return [] unless @isDirectorySync(rootPath)
     paths = fs.readdirSync(rootPath)
@@ -74,6 +95,16 @@
     paths = paths.map (childPath) -> path.join(rootPath, childPath)
     paths

+  # Public: Asynchronously lists the files and directories in the given path.
+  # The listing is not recursive.
+  #
+  # * rootPath:
+  #   The absolute path to the directory to list.
+  # * extensions:
+  #   An array of extensions to filter the results by. If none are given, none
+  #   are filtered (optional)
+  # * callback:
+  #   The function to call
   list: (rootPath, rest...) ->
     extensions = rest.shift() if rest.length > 1
     done = rest.shift()
@@ -85,6 +116,7 @@
         paths = paths.map (childPath) -> path.join(rootPath, childPath)
         done(null, paths)

+  # Private: Returns only the paths which end with one of the given extensions.
   filterExtensions: (paths, extensions) ->
     extensions = extensions.map (ext) ->
       if ext is ''
@@ -93,6 +125,7 @@
         '.' + ext.replace(/^\./, '')
     paths.filter (pathToCheck) -> _.include(extensions, path.extname(pathToCheck))

+  # Deprecated: No one currently uses this.
   listTreeSync: (rootPath) ->
     paths = []
     onPath = (childPath) ->
@@ -101,22 +134,34 @@
     @traverseTreeSync(rootPath, onPath, onPath)
     paths

+  # Public: Moves the file or directory to the target synchronously.
   move: (source, target) ->
+    # TODO: This should be renamed to moveSync
     fs.renameSync(source, target)

-  # Remove the file or directory at the given path.
+  # Public: Removes the file or directory at the given path synchronously.
   remove: (pathToRemove) ->
+    # TODO: This should be renamed to removeSync
     rimraf.sync(pathToRemove)

-  # Open, read, and close a file, returning the file's contents.
+  # Public: Open, read, and close a file, returning the file's contents
+  # synchronously.
   read: (filePath) ->
+    # TODO: This should be renamed to readSync
     fs.readFileSync(filePath, 'utf8')

-  # Open, write, flush, and close a file, writing the given content.
+  # Public: Open, write, flush, and close a file, writing the given content
+  # synchronously.
+  #
+  # It also creates the necessary parent directories.
   writeSync: (filePath, content) ->
     mkdirp.sync(path.dirname(filePath))
     fs.writeFileSync(filePath, content)

+  # Public: Open, write, flush, and close a file, writing the given content
+  # asynchronously.
+  #
+  # It also creates the necessary parent directories.
   write: (filePath, content, callback) ->
     mkdirp path.dirname(filePath), (error) ->
       if error?
@@ -124,6 +169,7 @@
       else
         fs.writeFile(filePath, content, callback)

+  # Public: Copies the given path asynchronously.
   copy: (sourcePath, destinationPath, done) ->
     mkdirp path.dirname(destinationPath), (error) ->
       if error?
@@ -145,11 +191,23 @@

       sourceStream.pipe(destinationStream)

-  # Create a directory at the specified path including any missing parent
-  # directories.
+  # Public: Create a directory at the specified path including any missing
+  # parent directories synchronously.
   makeTree: (directoryPath) ->
+    # TODO: rename to makeTreeSync
     mkdirp.sync(directoryPath) if directoryPath and not @exists(directoryPath)

+  # Public: Recursively walk the given path and execute the given functions
+  # synchronously.
+  #
+  # * rootPath:
+  #   The String containing the directory to recurse into.
+  # * onFile:
+  #   The function to execute on each file, receives a single argument the
+  #   absolute path.
+  # * onDirectory:
+  #   The function to execute on each directory, receives a single argument the
+  #   absolute path (defaults to onFile)
   traverseTreeSync: (rootPath, onFile, onDirectory=onFile) ->
     return unless @isDirectorySync(rootPath)

@@ -167,6 +225,17 @@

     traverse(rootPath, onFile, onDirectory)

+  # Public: Recursively walk the given path and execute the given functions
+  # asynchronously.
+  #
+  # * rootPath:
+  #   The String containing the directory to recurse into.
+  # * onFile:
+  #   The function to execute on each file, receives a single argument the
+  #   absolute path.
+  # * onDirectory:
+  #   The function to execute on each directory, receives a single argument the
+  #   absolute path (defaults to onFile)
   traverseTree: (rootPath, onFile, onDirectory, onDone) ->
     fs.readdir rootPath, (error, files) ->
       if error
@@ -194,10 +263,28 @@
         queue.drain = onDone
         queue.push(path.join(rootPath, file)) for file in files

+  # Public: Hashes the contents of the given file.
+  #
+  # * pathToDigest:
+  #   The String containing the absolute path.
+  #
+  # Returns a String containing the MD5 hexadecimal hash.
   md5ForPath: (pathToDigest) ->
     contents = fs.readFileSync(pathToDigest)
     require('crypto').createHash('md5').update(contents).digest('hex')

+  # Public: Finds a relative path among the given array of paths.
+  #
+  # * loadPaths:
+  #   An Array of absolute and relative paths to search.
+  # * pathToResolve:
+  #   The string containing the path to resolve.
+  # * extensions:
+  #   An array of extensions to pass to {resolveExtensions} in which case
+  #   pathToResolve should not contain an extension (optional).
+  #
+  # Returns the absolute path of the file to be resolved if it's found and
+  # undefined otherwise.
   resolve: (args...) ->
     extensions = args.pop() if _.isArray(_.last(args))
     pathToResolve = args.pop()
@@ -218,10 +305,22 @@
         return @absolute(candidatePath) if @exists(candidatePath)
     undefined

+  # Deprecated:
   resolveOnLoadPath: (args...) ->
     loadPaths = Module.globalPaths.concat(module.paths)
     @resolve(loadPaths..., args...)

+  # Public: Finds the first file in the given path which matches the extension
+  # in the order given.
+  #
+  # * pathToResolve:
+  #   The String containing relative or absolute path of the file in question
+  #   without the extension or '.'.
+  # * extensions:
+  #   The ordered Array of extensions to try.
+  #
+  # Returns the absolute path of the file if it exists with any of the given
+  # extensions, otherwise it's undefined.
   resolveExtension: (pathToResolve, extensions) ->
     for extension in extensions
       if extension == ""
@@ -231,6 +330,7 @@
         return @absolute(pathWithExtension) if @exists(pathWithExtension)
     undefined

+  # Public: Returns true for extensions associated with compressed files.
   isCompressedExtension: (ext) ->
     _.indexOf([
       '.gz'
@@ -240,6 +340,7 @@
       '.zip'
     ], ext, true) >= 0

+  # Public: Returns true for extensions associated with image files.
   isImageExtension: (ext) ->
     _.indexOf([
       '.gif'
@@ -249,9 +350,27 @@
       '.tiff'
     ], ext, true) >= 0

+  # Public: Returns true for extensions associated with pdf files.
   isPdfExtension: (ext) ->
     ext is '.pdf'

+  # Public: Returns true for extensions associated with binary files.
+  isBinaryExtension: (ext) ->
+    _.indexOf([
+      '.DS_Store'
+      '.a'
+      '.o'
+      '.so'
+      '.woff'
+    ], ext, true) >= 0
+
+  # Public: Returns true for files named similarily to 'README'
+  isReadmePath: (readmePath) ->
+    extension = path.extname(readmePath)
+    base = path.basename(readmePath, extension).toLowerCase()
+    base is 'readme' and (extension is '' or @isMarkdownExtension(extension))
+
+  # Private: Used by isReadmePath.
   isMarkdownExtension: (ext) ->
     _.indexOf([
       '.markdown'
@@ -262,35 +381,8 @@
       '.ron'
     ], ext, true) >= 0

-  isBinaryExtension: (ext) ->
-    _.indexOf([
-      '.DS_Store'
-      '.a'
-      '.o'
-      '.so'
-      '.woff'
-    ], ext, true) >= 0
-
-  isReadmePath: (readmePath) ->
-    extension = path.extname(readmePath)
-    base = path.basename(readmePath, extension).toLowerCase()
-    base is 'readme' and (extension is '' or @isMarkdownExtension(extension))
-
-  readPlistSync: (plistPath) ->
-    plist = require 'plist'
-    plist.parseStringSync(@read(plistPath))
-
-  readPlist: (plistPath, done) ->
-    plist = require 'plist'
-    fs.readFile plistPath, 'utf8', (error, contents) ->
-      if error?
-        done(error)
-      else
-        try
-          done(null, plist.parseStringSync(contents))
-        catch parseError
-          done(parseError)
-
+  # Public: Reads and returns CSON, JSON or Plist files and returns the
+  # corresponding Object.
   readObjectSync: (objectPath) ->
     CSON = require 'season'
     if CSON.isObjectPath(objectPath)
@@ -298,6 +390,8 @@
     else
       @readPlistSync(objectPath)

+  # Public: Reads and returns CSON, JSON or Plist files and calls the specified
+  # callback with the corresponding Object.
   readObject: (objectPath, done) ->
     CSON = require 'season'
     if CSON.isObjectPath(objectPath)
@@ -305,4 +399,21 @@
     else
       @readPlist(objectPath, done)

+  # Private: Used by readObjectSync.
+  readPlistSync: (plistPath) ->
+    plist = require 'plist'
+    plist.parseStringSync(@read(plistPath))
+
+  # Private: Used by readObject.
+  readPlist: (plistPath, done) ->
+    plist = require 'plist'
+    fs.readFile plistPath, 'utf8', (error, contents) ->
+      if error?
+        done(error)
+      else
+        try
+          done(null, plist.parseStringSync(contents))
+        catch parseError
+          done(parseError)
+
 module.exports = _.extend({}, fs, fsExtensions)


More logging messages

More logging messages

--- src/generate.coffee
+++ src/generate.coffee
@@ -7,6 +7,7 @@
 time       = require("time")(Date) # Extend global object
 tinyliquid = require "tinyliquid"
 toposort   = require "toposort"
+util       = require "util"
 Q          = require "q"

 helpers    = require "./helpers"
@@ -168,7 +169,7 @@
   Q.all pages.map (page) -> writePage page, bundle

 writePage = (page, bundle) ->
-  log.verbose "generate", "writePage %s", page.url
+  log.silly "generate", "writePage(%s)", util.inspect page
   { site, config, liquidOptions, compiledLayouts, mergedPlugins, context } = bundle

   currentState.page = page
@@ -226,7 +227,7 @@
         Q.nfcall template, context
       .then ->
         # Write file
-        log.verbose "generate", "Writing file: %s", outpath
+        log.verbose "generate", "Writing page: %s", outpath
         Q.nfcall fs.outputFile, outpath, context.clearBuffer()

           # TODO: Re-enable the error checking that happens on tinyliquid
@@ -463,7 +464,7 @@
       posts

 loadPost = (config, file) ->
-  log.silly "generate", "Loading post: %s", file
+  log.silly "generate", "loadPost(%s)", file
   helpers.getMetadataAndContent(file)
     .then (val) ->
       { data, content } = val
@@ -501,6 +502,7 @@
       data.content = content

       log.verbose "generate", "Loaded post: %s", file
+      log.silly "generate", "loadPost(%s) -> %j", file, data
       data

 getPermalink = (slug, data, permalinkStyle) ->


Clean up script detection

Clean up script detection

--- src/htmldiff.coffee
+++ src/htmldiff.coffee
@@ -3,6 +3,10 @@
 is_whitespace = (char)-> /^\s+$/.test char
 is_tag = (token)-> /^\s*<[^>]+>\s*$/.test token
 isnt_tag = (token)-> not is_tag token
+is_script_tag = (token) -> token is '<script'
+ends_in_end_script_tag = (token) ->
+  token_end = token.substr token.length - 9
+  token_end is '</script>'

 class Match
   constructor: (@start_in_before, @start_in_after, @length)->
@@ -22,8 +26,7 @@
       when 'script'
         if is_end_of_tag char
           current_word += '>'
-          end = current_word.substr current_word.length - 9
-          if end is '</script>'
+          if ends_in_end_script_tag current_word
             words.push current_word
             current_word = ''
             if is_whitespace char
@@ -33,7 +36,7 @@
         else
           current_word += char
       when 'tag'
-        if current_word is '<script'
+        if is_script_tag current_word
           mode = 'script'
           current_word += char
         else if is_end_of_tag char


added Foxie.get()/set()

added Foxie.get()/set()

--- scripts/coffee/lib/Foxie.coffee
+++ scripts/coffee/lib/Foxie.coffee
@@ -115,9 +115,7 @@

 		@_children = []

-
-
-
+		@_customVars = {}

 	clone: (newself = Object.create @constructor::) ->

@@ -292,4 +290,20 @@

 				null

-		return _interface+		return _interface
+
+	get: (name) ->
+
+		@_customVars[name]
+
+	set: (name, val) ->
+
+		@_customVars[name] = val
+
+		@
+
+	unset: (name) ->
+
+		delete @_customVars[name]
+
+		@


connection wait: pass err

connection wait: pass err

--- test/connection/wait.coffee
+++ test/connection/wait.coffee
@@ -32,8 +32,8 @@
         host: 'localhost'
         port: port
       , (err, status) ->
-        status.should.be.true()
-      .call  (_, callback) ->
+        status.should.be.true() unless err
+      .call (_, callback) ->
         @options.server1.close callback
       .then next

@@ -49,7 +49,7 @@
       .connection.wait
         server: host: 'localhost', port: port1
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .connection.wait
         server: host: 'localhost', port: [port1, port2]
       , (err, status) ->
@@ -64,7 +64,7 @@
           [{host: 'localhost', port: port2}]
         ]
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .call  (_, callback) -> @options.server1.close callback
       .call  (_, callback) -> @options.server2.close callback
       .then next
@@ -79,7 +79,7 @@
       .connection.wait
         server: "localhost:#{port}"
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .call  (_, callback) ->
         @options.server1.close callback
       .then next
@@ -95,7 +95,7 @@
         servers: for i in [0...12]
           {host: 'localhost', port: port}
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .call  (_, callback) ->
         @options.server1.close callback
       .then next
@@ -114,9 +114,9 @@
         host: 'localhost'
         port: port
       , (err, status) ->
-        status.should.be.false()
-      .call  (_, callback) ->
-        @options.server1.close callback
+        status.should.be.false() unless err
+      .call  (_, callback) ->
+        @options.server1gle host and a single por.close callback
       # Status true
       .call ->
         setTimeout @options.server1.listen, 100
@@ -124,7 +124,7 @@
         host: 'localhost'
         port: port
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .call  (_, callback) ->
         @options.server1.close callback
       .then next
@@ -150,7 +150,7 @@
         quorum: true
         interval: 500
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .call (_, callback) ->
         @options.server1.close callback
       .call (_, callback) ->
@@ -178,7 +178,7 @@
         quorum: 2
         interval: 500
       , (err, status) ->
-        status.should.be.true()
+        status.should.be.true() unless err
       .call  (_, callback) ->
         @options.server1.close callback
       .call  (_, callback) ->


touch syncstring during save to ensure project is listening

--- src/smc-util/syncstring.coffee
+++ src/smc-util/syncstring.coffee
@@ -832,6 +832,15 @@
             #dbg("nothing changed so nothing to save")
             cb?()
             return value
+
+        # CRITICAL: First, we broadcast interest in the syncstring -- this will cause the relevant project
+        # (if it is running) to open the syncstring (if closed), and hence be aware that the client
+        # is requesting a save.  This is important if the client and database have changes not
+        # saved to disk, and the project stopped listening for activity on this syncstring due
+        # to it not being touched (due to active editing).  Not having this leads to a lot of "can't save"
+        # errors.
+        @touch()
+
         # compute transformation from _last to live -- exactly what we did
         patch = make_patch(@_last, value)
         @_last = value


Remove disused code

Remove disused code

--- test-main.coffee
+++ test-main.coffee
@@ -1,15 +0,0 @@
-allTestFiles = []
-
-Object.keys(window.__karma__.files).forEach (file) ->
-  # Normalize paths to RequireJS module names.
-  return
-
-require.config
-  # Karma serves files under /base, which is the basePath from your config file
-  baseUrl: "/base"
-
-  # dynamically load all test files
-  deps: allTestFiles
-
-  # we have to kickoff jasmine, as it is asynchronous
-  callback: window.__karma__.start


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -26,7 +26,7 @@
   indexSum = 0

   engine.each '.rad=Awesome', (obj, index) ->
-    ind
+    indexSum +=

     if index is 0
       equals obj, obj1


corrected test spec structure

corrected test spec structure

--- test/heterarchy.spec.coffee
+++ test/heterarchy.spec.coffee
@@ -129,17 +129,11 @@
             super
             @deriv = 'deriv'

+
     # Tests
     # -----

     describe 'mro', ->
-
-        it 'throws an error when trying to linearize non-linearizable class hierarchy', ->
-            expect (A, B, C) ->
-                class A
-                class B extends A
-                class C extends multi A, B
-            .to.throw(Error)

         it 'generates empty linearization for arbitrary object', ->
             (mro {}).should.eql []
@@ -162,6 +156,7 @@
                 EditableScrollablePane, ScrollablePane, EditablePane,
                 Pane, ScrollingMixin, EditingMixin, Object ]

+
     describe 'multi', ->

         describe 'instance methods', ->
@@ -175,6 +170,7 @@
                 obj = new G()
                 (mro G).should.eql [G, D, B, F, C, E, A, Object]
                 obj.method().should.equal 'G>D>B>F>C>E>A'
+

         describe 'class methods', ->

@@ -201,10 +197,10 @@
                     C.classMethod().should.equal 'Base2Base1'
                 )(0, 0, 0)

-        it 'overrides class methods properly in recursive multi case', ->
-            # exclude Object
-            for cls in mro(G)[0...-1]
-                cls.overrideNoSuper().should.equal cls.name.toLowerCase()
+            it 'overrides class methods properly in recursive multi case', ->
+                # exclude Object
+                for cls in mro(G)[0...-1]
+                    cls.overrideNoSuper().should.equal cls.name.toLowerCase()

         it 'gets constructed properly', ->
             obj = new D()
@@ -225,6 +221,12 @@
             (-> multi D, C, B)
                 .should.throw "Inconsistent multiple inheritance"

+            ((A, B, C) ->
+                class A
+                class B extends A
+                class C extends multi A, B)
+            .should.throw "Inconsistent multiple inheritance"
+
         it 'makes sure the next constructor after a root class', ->
             obj = new Deriv()
             obj.base1 .should.equal 'base1'
@@ -254,6 +256,7 @@
                 should.not.exist Map::__mro__
             if typeof Set isnt "undefined"
                 should.not.exist Set::__mro__
+

         describe 'freezes class properties', ->
             # This is just a limitation of the approach and these
@@ -285,6 +288,7 @@
             (isinstance new Pedalo(), D).should.be.false
             (isinstance new Pedalo(), A).should.be.false
             (isinstance new Pedalo(), SmallCatamaran).should.be.true
+

     describe 'issubclass', ->


Config: Prioritize userprofile for HOME search. In Windows, it turns out if the current user is not the default user, environment.HOME returns drive root instead of the user-profile directory. This PR changes the priority. This change will not break the current usage / implementations.

--- src/configfinder.coffee
+++ src/configfinder.coffee
@@ -47,7 +47,7 @@
         return npmConfig  if npmConfig
         projConfig = findFile("coffeelint.json", dir)
         return loadJSON(projConfig)  if projConfig
-    envs = process.env.HOME or process.env.HOMEPATH or process.env.USERPROFILE
+    envs = process.env.USERPROFILE or process.env.HOME or process.env.HOMEPATH
     home = path.normalize(path.join(envs, "coffeelint.json"))
     if fs.existsSync(home)
         console.log 'loaded', home


Use the same path for the cloned content so we do not have to rewrite image links in the HTML

--- scripts/gh-book/toc-node.coffee
+++ scripts/gh-book/toc-node.coffee
@@ -2,9 +2,10 @@
   'backbone'
   'cs!collections/content'
   'cs!gh-book/xhtml-file'
+  'cs!gh-book/uuid'
   'cs!models/content/inherits/saveable'
   'cs!mixins/tree'
-], (Backbone, allContent, XhtmlFile, SaveableModel, treeMixin) ->
+], (Backbone, allContent, XhtmlFile, uuid, SaveableModel, treeMixin) ->

   mediaType = 'application/vnd.org.cnx.section'

@@ -62,9 +63,16 @@
             newTitle = "Copy of #{title}"
             json = realModel.toJSON()
             json.title = newTitle
-            delete json.id
+
+            # The id of the new Content should contain the same path as the original content.
+            # This is so we do not have to rewrite all the links and images in the HTML
+            pathParts = realModel.id.split('/')
+            pathParts.pop()
+            pathParts.push(uuid())
+            json.id = pathParts.join('/')

             clone = allContent.model(json)
+            clone.setNew?()
             allContent.add(clone)

             pointerNode = root.newNode {title:newTitle, model:clone}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -1,6 +1,6 @@
 ###*
 The Controllable module adds simple movement
-when up, down, left, right are
+when up, down, left, right are helf

   # create a player and include ClampBounds


Return a string when inspect is called by node.js

Fixes #123

--- src/core/content.coffee
+++ src/core/content.coffee
@@ -231,6 +231,9 @@

 ContentTree.inspect = (tree, depth=0) ->
   ### Return a pretty formatted string representing the content *tree*. ###
+  if typeof tree is 'number'
+    # workaround for node.js calling inspect when converting objects to a string
+    return '[Function: ContentTree]'
   rv = []
   pad = ''
   for i in [0..depth]


Speaker deck: next, prev, get slide image url

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -5,11 +5,30 @@
   Connection = Backbone.Model.extend({})
   Slide = Backbone.Model.extend({})

+  ToggleView = Backbone.View.extend
+    tagName: 'div'
+    className: 'qslide'
+    id: 'qslideSwitch'
+    template: _.template 'Show'
+
+  WelcomeView = Backbone.View.extend
+    tagName: 'div'
+    className: 'qcommander'
+    id: 'qcommander'
+    template: _.template '
+
Close

+
+
More detail help

+
Slideshow Token: <%= token %>
<img src="<%= bc %>" alt="Waiting for token" />
+    '
+
   AppView = Backbone.View.extend
     tagName: 'div'
     className: 'qcommander'
     id: 'qcommander'
     template: _.template '
+
Close

+

More detail help

Slideshow Token: <%= token %>
<img src="<%= bc %>" alt="Waiting for token" />
     '
@@ -38,36 +57,32 @@
       slideInfo.set code

       remoteQueu = new Firebase "#{baseFirebaseUrl}qc/"
-
       console.log remoteQueu

+      this.showConnectionBoard()
       remoteQueu.limit(200).on 'child_added', (snapshot) ->
         console.log(snapshot)
         message = snapshot.val()
         console.log(message)
         switch message.cmd
           when 'handshake'
-            # decide if we scan allow this
-            if localStorage['allow']? and message.from == localStorage['allow']
-              return true
+            #if localStorage['allow']? and message.from == localStorage['allow']
+              #return true

-            if confirm("Allow connection from #{message.from}?")
-              @connection.set('connected_from', message.from)
+            if confirm("Allow connection from #{message.name}?")
+              connection.set('connected_from', message.from)
               localStorage['allow'] = message.from
-              return true
-            end
           when 'next'
-            remote.next
+            remote.next()
           when 'prev', 'previous'
-            remote.previous
+            remote.previous()
           when 'current'
-            remote.getCurrentSlide
+            remote.getCurrentSlide()
           else
             console.log "Not implement"
         #Okay, remove that command
         r = new Firebase("https://qcommander.firebaseio-demo.com//#{connection.get('token')}/qc/".concat(snapshot.name()))
         r.remove()
-      this.showConnectionBoard()

     showConnectionBoard: () ->
       return this.render()
@@ -92,10 +107,12 @@
       return this

     events:
-      "click #spin": "doPlay"
+      "click .js-close-welcome": 'closeWelcome'
       "hover #spin": "animatePlayButton"

-    doPlay: ()-> this.r.spin
+    closeWelcome: ()->
+      console? && console.log 'Close welcome form'
+      this.$el.hide()

     animatePlayButton: ()->
         this.playButton.transform
@@ -107,13 +124,13 @@
   class Remote
     constructor: (@url) ->
       @driver = {}
-      this.setupRemote
+      this.setupRemote()
     # Based on url, load corresponding remote driver
     setupRemote: () ->
       @driver = switch
-        when @url.indexOf('http://speakerdeck.com/') > 1 then new SpeakerdeskRemote
-        when @url.indexOf('http://slideshare.net/') > 1  then new SlideshareRemote
-        when @url.indexOf('http://www.scribd.com/') > 1  then new ScribdRemote
+        when @url.indexOf('://speakerdeck.com/') > 1 then new SpeakerdeskRemote
+        when @url.indexOf('://slideshare.net/') > 1  then new SlideshareRemote
+        when @url.indexOf('://www.scribd.com/') > 1  then new ScribdRemote
         else new RabbitRemote

     previous: () ->
@@ -148,12 +165,12 @@
       console? && console.log @container

     getCurrentSlideScreenshot: () ->
+      $('#player-content-wrapper > #slide_image', @container).prop('src')

     next: () ->
-      $('.overnav > .next', @container).click()
+      $('.overnav > .next', @container)[0].click()
     previous: () ->
-      $('.overnav > .prev', @container).click()
-
+      $('.overnav > .prev', @container)[0].click()

   class SlideshareRemote extends RemoteControlDriver
   class ScribdRemote extends RemoteControlDriver


语法测试用例不关注语句位置

语法测试用例不关注语句位置

--- test/jedi.coffee
+++ test/jedi.coffee
@@ -7,7 +7,7 @@
 		'comment single line':
 			input: '! comment line'
 			expect: [
-				['comment', [1, 1], ['comment line']]
+				['comment', [1, Number], ['comment line']]
 			]
 		'comment multiple lines':
 			input: '''
@@ -15,14 +15,14 @@
 					comment line 2
 			'''
 			expect: [
-				['comment', [1, 1], ['comment line 1', 'comment line 2']]
+				['comment', [1, Number], ['comment line 1', 'comment line 2']]
 			]
 		'doctype':
 			input: '!html5'
 			expect: [
 				[
 					'comment',
-					[1,1],
+					[1, Number],
 					['html5']
 				]
 			]
@@ -30,7 +30,7 @@
 		'suppress single line':
 			input: '-- suppress line'
 			expect: [
-				['suppress', [1, 1], ['suppress line']]
+				['suppress', [1, Number], ['suppress line']]
 			]
 		'suppress multiple lines':
 			input: '''
@@ -38,13 +38,13 @@
 					suppress line 2
 			'''
 			expect: [
-				['suppress', [1, 1], ['suppress line 1', 'suppress line 2']]
+				['suppress', [1, Number], ['suppress line 1', 'suppress line 2']]
 			]

 		'inject line':
 			input: '- inject line'
 			expect: [
-				['inject', [1, 1], 'inject line', null, []]
+				['inject', [1, Number], 'inject line', null, []]
 			]
 		'inject line with block':
 			input: '''
@@ -53,21 +53,21 @@
 					! line 2
 			'''
 			expect: [
-				['inject', [1, 1], 'inject line', null, [
-					['comment', [2, 2], ['line 1']]
-					['comment', [3, 2], ['line 2']]
+				['inject', [1, Number], 'inject line', null, [
+					['comment', [2, Number], ['line 1']]
+					['comment', [3, Number], ['line 2']]
 				]]
 			]

 		'binding of a simple symbol':
 			input: '= x'
 			expect: [
-				['binding', [1, 1], null, ['Symbol', 'x'], []]
+				['binding', [1, Number], null, ['Symbol', 'x'], []]
 			]
 		'binding of a list':
 			input: '= [1, 2, 3]'
 			expect: [
-				['binding', [1, 1], null,
+				['binding', [1, Number], null,
 					['List', [['Number', 1], ['Number', 2], ['Number', 3]]],
 					[]
 				]
@@ -75,7 +75,7 @@
 		'binding of a tuple':
 			input: "= ('hax', 18)"
 			expect: [
-				['binding', [ 1, 1 ], null,
+				['binding', [ 1, Number ], null,
 					['Tuple', [['String', 'hax'], ['Number', 18]]],
 					[]
 				]
@@ -88,7 +88,7 @@
 			expect: [
 				[
 					'binding',
-					[ 1, 1 ],
+					[ 1, Number ],
 					null,
 					[
 						'Tuple',
@@ -114,7 +114,7 @@
 				'Hello world!
 			"""
 			expect: [
-				['text', [1, 1], undefined, ['Hello world!']]
+				['text', [1, Number], undefined, ['Hello world!']]
 			]

 		'single quot text no escape/interpolation':
@@ -124,7 +124,7 @@
 			'''

 			expect: [
-				['text', [1, 1], undefined, ['Hello {user}!\\n']]
+				['text', [1, Number], undefined, ['Hello {user}!\\n']]
 			]

 		'single quot multiple lines text ':
@@ -135,7 +135,7 @@
 					super cool
 			"""
 			expect: [
-				['text', [1, 1], undefined, ['Hello world!', 'foo bar baz', 'rawr rawr', 'super cool']]
+				['text', [1, Number], undefined, ['Hello world!', 'foo bar baz', 'rawr rawr', 'super cool']]
 			]

 		'single quot text with single quot':
@@ -143,7 +143,7 @@
 				'	I'm ok!
 			"""
 			expect: [
-				['text', [1, 1], undefined, ["I'm ok!"]]
+				['text', [1, Number], undefined, ["I'm ok!"]]
 			]

 		'double quot text':
@@ -151,7 +151,7 @@
 				"Hello world!
 			'''
 			expect: [
-				['text', [1, 1], undefined, [
+				['text', [1, Number], undefined, [
 					[['String', 'Hello world!', 'Hello world!']]
 				]]
 			]
@@ -160,7 +160,7 @@
 				"Hello {user}!\\n
 			'''
 			expect: [
-				['text', [1, 1], undefined, [
+				['text', [1, Number], undefined, [
 					[
 						['String', 'Hello ', 'Hello ']
 						['Symbol', 'user'],
@@ -176,7 +176,7 @@
 					super cool
 			'''
 			expect: [
-				['text', [1, 1], ['Symbol', 'r'],
+				['text', [1, Number], ['Symbol', 'r'],
 					[
 						[['String', 'Hello world!', 'Hello world!']]
 						[['String', 'foo bar baz', 'foo bar baz']]
@@ -192,10 +192,10 @@
 					"{x} > {y}
 			'''
 			expect: [
-				['instruction', [1, 1], 'if',
+				['instruction', [1, Number], 'if',
 					['BinaryOp', '>', ['Symbol', 'x'], ['Symbol', 'y']]
 					[
-						['text', [2, 2], undefined, [
+						['text', [2, Number], undefined, [
 							[['Symbol', 'x'], ['String', ' > ', ' > '], ['Symbol', 'y']]
 						]]
 					]
@@ -208,9 +208,9 @@
 					"{v}
 			'''
 			expect: [
-				['instruction', [1, 1], 'for',
+				['instruction', [1, Number], 'for',
 					[['Symbol', 'v'], ['Symbol', 'x']]
-					[['text', [2, 2], undefined, [
+					[['text', [2, Number], undefined, [
 						[['Symbol', 'v']]
 					]]]
 				]
@@ -224,7 +224,7 @@
 			expect: [
 				[
 					'instruction',
-					[1,1],
+					[1, Number],
 					'for',
 					[
 						[
@@ -239,7 +239,7 @@
 					[
 						[
 							'text',
-							[2, 2],
+							[2, Number],
 							undefined,
 							[
 								[
@@ -281,22 +281,22 @@
 				div.test1
 			'''
 			expect: [
-				['element', [1, 1], ['div', ['test1'], undefined], undefined, []]
+				['element', [1, Number], ['div', ['test1'], undefined], undefined, []]
 			]
 		'element with binding':
 			input: '''
 				div.test1 = x
 			'''
 			expect: [
-				['element', [1, 1], ['div', ['test1'], undefined], ['Symbol', 'x'], []]
+				['element', [1, Number], ['div', ['test1'], undefined], ['Symbol', 'x'], []]
 			]
 		'nested elements with binding':
 			input: '''
 				div.test1 > div.test2 = x
 			'''
 			expect: [
-				['element', [1, 1], ['div', ['test1'], undefined], undefined, [
-					['element', [1, 23], ['div', ['test2'], undefined], ['Symbol', 'x'], []]
+				['element', [1, Number], ['div', ['test1'], undefined], undefined, [
+					['element', [1, Number], ['div', ['test2'], undefined], ['Symbol', 'x'], []]
 				]]
 			]
 			# should be [1, 13]
@@ -304,9 +304,9 @@
 		'element with attributes in one line':
 			input: "input @required @type='email'"
 			expect: [
-				['element', [1, 1], ['input', [], undefined], undefined, [
-					['attribute', [1, 19], 'required', undefined]
-					['attribute', [1, 19], 'type', '=', ['String', 'email']]
+				['element', [1, Number], ['input', [], undefined], undefined, [
+					['attribute', [1, Number], 'required', undefined]
+					['attribute', [1, Number], 'type', '=', ['String', 'email']]
 				]]
 			]
 			# should be [1, 7] and [1, 17]
@@ -314,11 +314,33 @@
 		'nested elements with attribute':
 			input: "li > a @href=url"
 			expect: [
-				['element', [1, 1], ['li', [], undefined], undefined, [
-					['element', [1, 9], ['a', [], undefined], undefined, [
-						['attribute', [1, 13], 'href', '=', ['Symbol', 'url']]
+				['element', [1, Number], ['li', [], undefined], undefined, [
+					['element', [1, Number], ['a', [], undefined], undefined, [
+						['attribute', [1, Number], 'href', '=', ['Symbol', 'url']]
 					]]
 				]]
 			]
 			# should be [1, 6], [1, 8]

+		'extend':
+			input: '''
+				!html5
+
+				html
+					head
+						-- start head
+
+						#headBlock
+							-- content of head
+
+						-- end head
+
+					body
+						-- start body
+
+						#bodyBlock
+							-- content of body
+
+						-- end body
+			'''
+


HBase env

HBase env

--- actions/hdp_hbase.coffee
+++ actions/hdp_hbase.coffee
@@ -8,7 +8,7 @@
   require('./hdp_core').configure ctx
   {realm} = ctx.config.krb5_client
   namenode = (ctx.config.servers.filter (s) -> s.hdp?.namenode)[0].host
-  zookeeper_hosts = (ctx.config.servers.filter (s) -> s.hdp?.zookeeper).map( (s) -> s.host ).join ','
+  zookeeper_hosts = ctx.config.servers.filter((s) -> s.hdp?.zookeeper).map((s) -> s.host).join ','
   ctx.config.hdp ?= {}
   ctx.config.hdp.hbase_user ?= 'hbase'
   ctx.config.hdp.hbase_log_dir ?= '/var/log/hbase'
@@ -87,9 +87,38 @@
   , (err, configured) ->
     next err, if configured then ctx.OK else ctx.PASS

+# node, hbase_log_dir is in hbase-env.sh not in log4j
+# module.exports.push (ctx, next) ->
+#   @name 'HDP HBase # Log'
+#   {hbase_log_dir} = ctx.config.hdp
+#   ctx.log 'Write log4j.properties'
+#   ctx.upload
+#     source: "#{__dirname}/hdp/hbase/log4j.properties"
+#     destination: '/etc/hbase/conf/log4j.properties'
+#     write: [
+#       match: /^(hbase.log.dir=).*$/mg
+#       replace: "$1#{hbase_log_dir}"
+#     ]
+#   , (err, configured) ->
+#     next err, if configured then ctx.OK else ctx.PASS
+
+module.exports.push (ctx, next) ->
+  @name 'HDP HBase # Env'
+  {hbase_log_dir} = ctx.config.hdp
+  ctx.log 'Write hbase-env.sh'
+  ctx.upload
+    source: "#{__dirname}/hdp/hbase/hbase-env.sh"
+    destination: '/etc/hbase/conf/hbase-env.sh'
+    write: [
+      match: /^(export HBASE_LOG_DIR=).*$/mg
+      replace: "$1#{hbase_log_dir}"
+    ]
+  , (err, uploaded) ->
+    next err, if uploaded then ctx.OK else ctx.PASS
+
 module.exports.push (ctx, next) ->
   @name 'HDP HBase # RegionServer'
-  regionservers = (ctx.config.servers.filter (s) -> s.hdp?.hbase_regionserver).join('\n')
+  regionservers = ctx.config.servers.filter((s) -> s.hdp?.hbase_regionserver).map((s) -> s.host).join('\n')
   {hbase_user} = ctx.config.hdp
   ctx.write
     content: regionservers


Add get method to event emitter

Add get method to event emitter

--- src/eventEmitter.coffee
+++ src/eventEmitter.coffee
@@ -23,7 +23,6 @@
   eventFactory('teamsnap-sdk-event', data)

 requestResponse = (method, xhr) =>
-  return if method is 'get'
   data = xhr.data
   eventData = null


remove some unnecessary parentheses

remove some unnecessary parentheses

--- src/relative-resolve.coffee
+++ src/relative-resolve.coffee
@@ -26,6 +26,6 @@
   aliases ?= {}
   resolved = resolvePath {extensions, aliases, root, cwd, path: givenPath}
   canonicalName = if isCore givenPath then givenPath else canonicalise root, resolved
-  if ({}.hasOwnProperty.call aliases, canonicalName)
+  if {}.hasOwnProperty.call aliases, canonicalName
     resolved = aliases[canonicalName] and resolvePath {extensions, aliases, root, path: aliases[canonicalName]}
   {filename: resolved, canonicalName}


Update sloth-me.coffee

Update sloth-me.coffee

Added sloth gifs

--- src/scripts/sloth-me.coffee
+++ src/scripts/sloth-me.coffee
@@ -48,6 +48,15 @@
   'http://i.imgur.com/C975XnF.jpg'
   'http://i.imgur.com/7rz0Bll.jpg'
   'http://i.imgur.com/Mtl0pTt.jpg'
+  'http://i.imgur.com/tBxfiOo.gif'
+  'http://i.imgur.com/aiXPItB.gif'
+  'http://i.imgur.com/WKIYCXY.gif'
+  'http://i.imgur.com/PwOFcmM.gif'
+  'http://i.imgur.com/gGgVGEn.gif'
+  'http://i.imgur.com/NAJYZRJ.gif'
+  'http://i.imgur.com/QppoTRe.gif'
+  'http://i.imgur.com/DWoZS2y.gif'
+  'http://i.imgur.com/eafrx63.gif'
 ]

 module.exports = (robot) ->


refactor remove/add to toggle

refactor remove/add to toggle

--- src/helpers/show.coffee
+++ src/helpers/show.coffee
@@ -6,7 +6,4 @@
     @$el.addClass 'ui-hide'

     @watch '_main', (value)->
-      if value
-        @$el.removeClass 'ui-hide'
-      else
-        @$el.addClass 'ui-hide'+      @$el.toggleClass 'ui-hide', !value


#24 for all IE quirks - use feature detection to check if it supports streaming (during tests)

--- tests/html/src/tests.coffee
+++ tests/html/src/tests.coffee
@@ -406,7 +406,7 @@
         a = new Array(counter)
         go = ->
             SockJS.chunkingTest client_opts.url + '/echo', (r) ->
-                if $.browser.msie and $.browser.version < 8
+                if $.browser.msie and not window.XDomainRequest
                     # on browsers with no streaming support...
                     equal(r, false)
                 else


Correction script test pour ajout instance du Bot

--- test/test.coffee
+++ test/test.coffee
@@ -1,18 +1,24 @@
 expect = require("chai").expect
 CodeFilter = require('../lib/filters/code')
+DiscordBot = require('discord.io')
+Helpers = require('../lib/helpers')
+
+Config = Helpers.getConfig(true)
+
+Bot = new DiscordBot(Config.getConstructorConfig())

 describe 'filters/code', ->

     describe '#isCode()', ->

         it 'should let standart text pass', ->
-            expect(CodeFilter.isCode('Salut les gens' )).to.not.be.ok
+            expect(CodeFilter.isCode('Salut les gens', Bot )).to.not.be.ok

         it 'should let code pass on one line', ->
-            expect(CodeFilter.isCode('vérifie les {} je pense que ça vient de là et j\'ai une <div> qui bloque !')).to.not.be.ok
+            expect(CodeFilter.isCode('vérifie les {} je pense que ça vient de là et j\'ai une <div> qui bloque !', Bot)).to.not.be.ok

         it 'should not catch pings', ->
-            expect(CodeFilter.isCode('Woops trop de code <@89677831675604992> ! http://pastie.org/private/co2hxfz4o905tz6nyygqmg')).to.not.be.ok
+            expect(CodeFilter.isCode('Woops trop de code <@89677831675604992> ! http://pastie.org/private/co2hxfz4o905tz6nyygqmg', Bot)).to.not.be.ok

         it 'should detect code based on }', ->
             expect(
@@ -26,7 +32,7 @@
                             });
                         });
                     });
-                """)
+                """, Bot)
             ).to.be.ok

         it 'should detect JSON', ->
@@ -36,7 +42,7 @@
      "likes": "LL16BeXfcf_aMbSBOoticKsQ",
      "favorites": "FL16BeXfcf_aMbSBOoticKsQ",
      "uploads": "UU16BeXfcf_aMbSBOoticKsQ"
-    },""")
+    },""", Bot)
             ).to.be.ok

@@ -71,5 +77,5 @@
                         </h1>
                     </div>
                 </div>
-            </div>""")
+            </div>""", Bot)
             ).to.be.ok


copy & paste issue

copy & paste issue

--- test/test.coffee
+++ test/test.coffee
@@ -5,12 +5,6 @@
 require '../backbone.typeahead.coffee'

 describe 'Backbone Typeahead', ->
-  testRoutes =
-    'todos/new': 'show'
-    'todos/:todo_id/comments/:id': 'showComment'
-    'todos/:id': 'show'
-    'todos': 'index'
-
   describe 'Readme Tests', ->
     class Albums extends Backbone.TypeaheadCollection
       typeaheadAttributes: ['band', 'name']


Remove SpecialForm

Remove SpecialForm

--- lisp.coffee
+++ lisp.coffee
@@ -15,13 +15,8 @@

 class CallFun
   constructor: (@funname, @args) ->
-  toString: -> "(#{@funname} #{@values.map((v) -> v.toString()).join(' ')})"
-
-# TODO: 下の実装と離れてしまっているのをどうにかしたい
-SF_NAMES = ['cond', 'quote', 'lambda', 'defun', 'setq', 'defmacro']
-class SpecialForm
-  constructor: (@name, @args) ->
-  toString: -> "(#{@name} #{@args.map((v) -> v.toString()).join(' ')})"
+    @values = [@funname, @args...]
+  toString: -> "(#{@values.map((v) -> v.toString()).join(' ')})"

 class Lambda
   constructor: (@params, @body) ->
@@ -128,24 +123,22 @@
     args = []
     funname = @expr()

-    isSF = SF_NAMES.indexOf(funname.name) != -1
     until @expects(')') or @isEOF()
       @skip()
-      args.push @expr(isSF)
+      args.push @expr()

     @forwards ')'

-    klass = if isSF then SpecialForm else CallFun
-    return new klass(funname, args)
-
-  expr: (isSF) ->
-    if @expects("'") or isSF #value
-      @forwards "'" unless isSF
+    return new CallFun(funname, args)
+
+  expr:  ->
+    if @expects("'") #value
+      @forwards "'"
       if @expects '(' #list
         return @list()
       else #atom
         return @atom()
-    else if @expects '(' #calling function or special form
+    else if @expects '(' #calling function
       return @call_fun()
     else #atom
       return @atom()


added error handling

added error handling

--- lisp.coffee
+++ lisp.coffee
@@ -185,6 +185,10 @@
                 if args[0].value == args[1].value then t else nil
               when 'atom'
                 if args[0] instanceof Atom then t else nil
+              else
+                throw "undefined function : #{funname.name}"
+          else
+            throw "#{JSON.stringify(funname)}(#{funname.constructor.name}) is not a function"
       when 'Symbol'
         envstack[envstack.length - 1].get(expr.name)
       else


*ahem* s/STRIPE/SLACK/

*ahem* s/STRIPE/SLACK/

--- src/microservice.coffee
+++ src/microservice.coffee
@@ -223,9 +223,9 @@
         res.statusCode = err.statusCode or 500
       if req.log
         req.log.error {err: err}, "Error"
-      if config.stripeHook
+      if config.slackHook
         options =
-          url: config.stripeHook
+          url: config.slackHook
           headers:
             "Content-Type": "application/json"
           json:
@@ -253,7 +253,7 @@
       cert: environment['CERT']
       logLevel: environment['LOG_LEVEL'] || 'info'
       logFile: environment['LOG_FILE'] || null
-      stripeHook: environment['STRIPE_HOOK']
+      slackHook: environment['SLACK_HOOK']
       driver: environment['DRIVER']
       params: if environment['PARAMS'] then JSON.parse(environment['PARAMS']) else {}
       appKeys: {}


fixed stats

fixed stats

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -40,8 +40,8 @@
 }`

 exports.retrieveChannelMetrics = `function *(type, channelId) {
-  var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId + ".count,'1day'))&from=-1weeks&format=json";
-  path += "&target=transformNull(summarize(stats.timers." + domain + ".Channels." + channelId + ".mean,'1day'))";
+  var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId + ".count,'1hour'))&from=-1days&format=json";
+  path += "&target=transformNull(summarize(stats.timers." + domain + ".Channels." + channelId + ".mean,'1hour'))";
   var data = [];
   var raw = yield fetchData(path);
   var i = 0;
@@ -95,3 +95,4 @@
   }
   return data;
 }`
+stats.timers.statsd-VirtualBox.Development.Channels.548efbf847212c61221d809d.mean


Let's not watch emacs temp files

Let's not watch emacs temp files

--- watch.coffee
+++ watch.coffee
@@ -21,6 +21,7 @@
   spawnApp()

 handleFileChange = (filename) ->
+  return if /^\.\#/.test(filename) # Fuck a color emacs temp
   if /coffee$/.test(filename) == true
     console.log chalk.yellow 'file changed: ', filename
     console.log chalk.yellow 'cycling app'


Set data array

Set data array

--- main.coffee
+++ main.coffee
@@ -6,10 +6,10 @@
 # (e.g., handontable).

 class Symbol
-
+
     constructor: (@id, @data) ->
-
-        @view = $("#"+@id).handsontable
+
+        @view = new Handsontable $("#"+@id)[0],
             data: @data
             startRows: @data.length
             startCols: @data[0].length
@@ -17,48 +17,40 @@
             colHeaders: true
             contextMenu: true
             columns: ({type: 'numeric'} for k in [1..@data[0].length])
-
-    render: ->
-        $("#"+@id).handsontable('getInstance').render()
+
+    render: -> @view.render()
+
+    setData: (data) ->
+        @view.loadData data
+        @render()

 # From GUI
-$blab.sym = []
-$blab.sym['A'] = new Symbol "A", [[1,2],[3,4]]
-$blab.sym['x'] = new Symbol "x", [[5],[6]]
-$blab.sym['b'] = new Symbol "b", [[]]
+symbol = (id, data) -> new Symbol id, data
+$blab.sym =
+    A: symbol "A", [[1,2],[3,4]]
+    x: symbol "x", [[5],[6]]
+    b: symbol "b", [[]]

 # In user coffeescript

 # For convenience
-A = $blab.sym['A'].data
-x = $blab.sym['x'].data
-b = $blab.sym['b'].data
+sym = $blab.sym
+A = sym['A'].data
+x = sym['x'].data
+b = sym['b'].data

 # User code
 fn = (A, x) ->
     A.dot x

-b = fn(A, x)
-
-# I'd hoped that sym['b'].data would update by reference, but it seems
-# like the last line has broken the link. So:
-$blab.sym['b'].data[0] = b[0]
-$blab.sym['b'].data[1] = b[1]
-
-# puzlet updates on compute
-$blab.sym['b'].render()
-
+compute = ->
+    b = fn(A, x)
+    sym['b'].setData b

 # compute from browser
-
-$("#comp").on "click", =>
-
-    b = fn(A, x)
-
-    $blab.sym['b'].data[0] = b[0]
-    $blab.sym['b'].data[1] = b[1]
-    $blab.sym['b'].render()
-
+$("#comp").on "click", => compute()
+
+compute()

 #---------------------SCRAP---------------------------------#


Fixed hours and min time format.

Fixed hours and min time format.

--- main.coffee
+++ main.coffee
@@ -1,7 +1,7 @@

 {MyoUI, Theme, mixins, css_utils, react_utils} = require 'myoui'
 # TODO: It is requiring local myoui from package.json but it is not commited.
-# current published myoui version (0.0.2) is not fully compatible with this app.
+# current published myoui version (0.0.2) is not fully compatible with this app.

 # adding default css code to the document
 require 'myoui/default_fonts'
@@ -244,7 +244,10 @@
             else if time < 3600000 # 1hour
                 ftime = '\n' + Math.floor(time/60000) + ' min'
             else
-                ftime = '\n' + Math.floor(time/3600000) + ' hours ' + (time % 3600000)*60 + ' min'
+                hours = time/3600000
+                only_hours = Math.floor(hours)
+                only_min = Math.floor((hours - only_hours) * 60)
+                ftime = '\n' + only_hours + ' hours ' + only_min + ' min'

             if activity_state.active
                 are_you_working_message = "


Require fs in context.coffee

Require fs in context.coffee

--- src/context.coffee
+++ src/context.coffee
@@ -2,6 +2,7 @@
 clone = require('clone')
 username = require('username')
 userid = require('userid')
+fs = require('fs')

 translatePath = (p, prefix) -> path.relative('/', path.join(prefix, p))


removed line as requested

removed line as requested

--- src/octonode/org.coffee
+++ src/octonode/org.coffee
@@ -103,7 +103,6 @@

   # Add a user to an organization
   # '/orgs/flatiron/members/pksunkara' PUT
-  # - role, required - params.role
   addMember: (user, options, cb) ->
     @client.put "/orgs/#{@name}/memberships/#{user}", options, (err, s, b, h) ->
       return cb(err) if err


console.log commented

console.log commented

--- test/test-cohtml-parser.coffee
+++ test/test-cohtml-parser.coffee
@@ -15,5 +15,5 @@

     scope = parser.extractScope()

-    console.log (require 'util').inspect scope, {depth:50, colors: true}
+    # console.log (require 'util').inspect scope, {depth:50, colors: true}


Change test to avoid race condition

Change test to avoid race condition

The old way of testing this had a race condition if the gearman srever
didn't reply at the right time. Now it's a true unit test so we can
avoid these issues.

--- test/test-worker-client.coffee
+++ test/test-worker-client.coffee
@@ -251,27 +251,22 @@

   it 'should process multiple packets in one tick', (done) ->
     @timeout 10000
-    output_data = "output_data"
-    worker = new Worker 'test', (payload, worker) ->
-      worker.data output_data
-      worker.data output_data
-      worker.data output_data
-      worker.complete output_data
-    , options
-
-    client = new Client options
+    gearman = new Gearman
     count = 0
-    job = client.submitJob 'test', ""
-    job.on 'data', (handle, data) ->
-      # Make sure that all three worker data packets get processed on the same tick.
-      # This fails if they aren't
-      process.nextTick -> assert.equal count, 3
+    gearman.on "WORK_DATA", (handle, data) ->
       count = count + 1
-      assert.equal output_data, data
-    job.on 'complete', ->
-      worker.disconnect()
-      client.disconnect()
-      done()
+      # Make sure we process both packets before the next tick
+      if count is 1
+        _.once process.nextTick ->
+          assert.equal count, 2
+          done()
+      assert.equal "data", data
+    buffer = new Buffer "\0RES"
+    buffer = Buffer.concat [buffer, new Buffer([0, 0, 0, 28])] # WORK_DATA
+    buffer = Buffer.concat [buffer, new Buffer([0, 0, 0, 11])] # Length
+    buffer = Buffer.concat [buffer, new Buffer("handle\0data")]
+    buffer = Buffer.concat [buffer, buffer]
+    gearman.receive buffer

   it 'client should handle partial packet writes', (done) ->
     @timeout 10000


crisp edges for crosshair

crisp edges for crosshair

--- app/graph_drawing.coffee
+++ app/graph_drawing.coffee
@@ -145,6 +145,7 @@

   verticalCrosshair = g.append('line')
     .attr('class', 'crosshair')
+    .style('shape-rendering': 'crispEdges')
     .attr('y1', 0)

   crosshairTime = g.append('text')


fixed loading state

fixed loading state

--- app/js/views/fact.coffee
+++ app/js/views/fact.coffee
@@ -44,9 +44,8 @@
       attention[action]()

   onClick: (options={}) =>
-    @show_button.startLoading()
-
     @attentions_do 'gain_attention'
+    @show_button.startLoading() # must be called after show
     @options.on_click success: =>
       @attentions_do 'loose_attention'


whoops, debug

whoops, debug

--- src/lib/engine.iced
+++ src/lib/engine.iced
@@ -147,7 +147,6 @@

     vo = @get_version_obj()
     uid = @unique_id vo
-    console.log "derive #{uid}"

     compute_hook = (i, tot) =>
       if (ret = (uid is @unique_id(vo))) and i % 10 is 0


#3 возврат статистики вместо объекта в колбеке on_complete

--- copy-files.coffee
+++ copy-files.coffee
@@ -52,7 +52,7 @@
     dst = normalize(dst)
     @_copy(src, dst, _.bind(@_complete, this))
   _complete: (status) ->
-    @options.on_complete(this) if _.isFunction(@options.on_complete)
+    @options.on_complete(@statistics) if _.isFunction(@options.on_complete)

   generateRollback: () ->
     rollback = []


Adding test log method

Adding test log method

--- tests/functional/logger.coffee
+++ tests/functional/logger.coffee
@@ -56,6 +56,22 @@
     info 'Hello world'
     info 'Hello world', 1
     info 'Hello world', 1, 2
+
+  it 'log', (done)->
+    msgs = [
+      'Hello world'
+      'Hello world 1'
+      'Hello world 1 2'
+    ]
+
+    global.__stdout = (data)->
+      data.should.equal msgs.shift()
+      done() unless msgs.length
+
+    {log} = require('../../lib/utils/logger')()
+    log 'Hello world'
+    log 'Hello world', 1
+    log 'Hello world', 1, 2

   it 'debug', (done)->
     msgs = [


Fixed initiative image stream sending.

Fixed initiative image stream sending.

--- src/wx.coffee
+++ src/wx.coffee
@@ -726,7 +726,7 @@
             image   : media_id: image
         , wrap callback

-      if image.match(regex_media_id)
+      if typeof image == 'string' && image.match(regex_media_id)
         send image
       else
         wx.upload 'image', image, (err, res) ->


host questions can contain html

host questions can contain html

--- @client/auth.coffee
+++ @client/auth.coffee
@@ -365,7 +365,8 @@
                 LABEL
                   htmlFor: field_id
                   style: subsection_heading_style
-                  label
+                  dangerouslySetInnerHTML: __html: label
+

                 render


Fix tests - API now requires push timestamps for all successful images

Signed-off-by: Tim Perry <5ee0edb9e2229c0838f1959779f1949031de0123@resin.io>

--- tests/integration/setup.coffee
+++ tests/integration/setup.coffee
@@ -184,6 +184,7 @@
 						content_hash: 'abc'
 						build_log: 'old web log'
 						start_timestamp: 1234
+						push_timestamp: 1234
 						status: 'success'
 			,
 				resin.pine.post
@@ -194,6 +195,7 @@
 						content_hash: 'def'
 						build_log: 'new web log'
 						start_timestamp: 54321
+						push_timestamp: 54321
 						status: 'success'
 			,
 				resin.pine.post
@@ -204,6 +206,7 @@
 						content_hash: 'jkl'
 						build_log: 'old db log'
 						start_timestamp: 123
+						push_timestamp: 123
 						status: 'success'
 			,
 				resin.pine.post
@@ -214,6 +217,7 @@
 						content_hash: 'ghi'
 						build_log: 'new db log'
 						start_timestamp: 54321
+						push_timestamp: 54321
 						status: 'success'
 			]
 			.spread (device, oldWebImage, newWebImage, oldDbImage, newDbImage) =>


Split properties into two separate classes

--- src/property.coffee
+++ src/property.coffee
@@ -11,43 +11,18 @@
           if target in object[name]
             object[dependency + "_property"]?.triggerChanges?(object)

-class SerenadeProperty
-  constructor: (@name, @options) ->
-    @valueName = "_s_#{@name}_val"
+class PropertyDefinition
+  constructor: (@name, options) ->
+    extend(this, options)

     @dependencies = []
     @localDependencies = []
     @globalDependencies = []

-    if @options.dependsOn
-      @addDependency(name) for name in [].concat(@options.dependsOn)
+    if @dependsOn
+      @addDependency(name) for name in [].concat(@dependsOn)

-    @serialize = @options.serialize
-    @format = @options.format
-
-  set: (object, value) ->
-    if typeof(value) is "function"
-      @options.get = value
-    else
-      if @options.set
-        @options.set.call(object, value)
-      else
-        def object, @valueName, value: value, configurable: true
-      @triggerChanges(object)
-
-  get: (object) ->
-    @registerGlobal(object)
-    if @options.get
-      # add a listener which adds any dependencies that haven't been specified
-      listener = (name) => @addDependency(name)
-      object._s_property_access.bind(listener) unless "dependsOn" of @options
-      value = @options.get.call(object)
-      object._s_property_access.unbind(listener) unless "dependsOn" of @options
-    else
-      value = object[@valueName]
-
-    object._s_property_access.trigger(@name)
-    value
+    @async = if "async" of options then options.async else settings.async

   addDependency: (name) ->
     if @dependencies.indexOf(name) is -1
@@ -63,9 +38,35 @@
       @localDependencies.push(name)
       @localDependencies.push(name) if @localDependencies.indexOf(name) is -1
       @globalDependencies.push({ subname, name, type }) if type
-      true
+
+class PropertyAccessor
+  constructor: (@definition, @object) ->
+    @name = @definition.name
+    @valueName = "_s_#{@name}_val"
+
+  set: (object, value) ->
+    if typeof(value) is "function"
+      @definition.get = value
     else
-      false
+      if @definition.set
+        @definition.set.call(object, value)
+      else
+        def object, @valueName, value: value, configurable: true
+      @triggerChanges(object)
+
+  get: (object) ->
+    @registerGlobal(object)
+    if @definition.get
+      # add a listener which adds any dependencies that haven't been specified
+      listener = (name) => @definition.addDependency(name)
+      object._s_property_access.bind(listener) unless "dependsOn" of @definition
+      value = @definition.get.call(object)
+      object._s_property_access.unbind(listener) unless "dependsOn" of @definition
+    else
+      value = object[@valueName]
+
+    object._s_property_access.trigger(@name)
+    value

   # Find all properties which are dependent upon this one
   dependents: (object) ->
@@ -89,37 +90,35 @@
   registerGlobal: (object) ->
     unless object["_glb_" + @name]
       object["_glb_" + @name] = true
-      for { name, type, subname } in @globalDependencies
+      for { name, type, subname } in @definition.globalDependencies
         globalDependencies[subname] or= []
         globalDependencies[subname].push({ object, subname, name, type, dependency: @name })

 defineProperty = (object, name, options={}) ->
-  property = new SerenadeProperty(name, options)
+  definition = new PropertyDefinition(name, options)

-  safePush(object, "_s_properties", property)
-
-  async = if "async" of options then options.async else settings.async
+  safePush(object, "_s_properties", definition)

   defineEvent object, "_s_property_access"
   defineEvent object, "change",
-    async: async
+    async: definition.async
     optimize: (queue) ->
       result = {}
       extend(result, item[0]) for item in queue
       [result]
   defineEvent object, "change_" + name,
-    async: async
+    async: definition.async
     bind: -> @[name] # make sure dependencies have been discovered and registered
     optimize: (queue) -> queue[queue.length - 1]

   def object, name,
-    get: -> property.get(this)
-    set: (value) -> property.set(this, value)
+    get: -> @[name + "_property"].get(this)
+    set: (value) -> @[name + "_property"].set(this, value)
     configurable: true
     enumerable: if "enumerable" of options then options.enumerable else true

   def object, name + "_property",
-    get: -> property
+    get: -> new PropertyAccessor(definition, this)
     configurable: true

   if typeof(options.serialize) is 'string'


Change copyright

Change copyright

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -27,7 +27,7 @@
     autoAddingHeaderEnabled: false
     autoUpdateEnabled: false
     email: "chirag.raman@gmail.com"
-    license: "Copyright (C) 2016 Multicomp Lab. All rights reserved."
+    license: "Copyright (C) 2016 Chirag Raman. All rights reserved."
     realname: "Chirag Raman"
     username: "chiragraman"
   "latex-plus":


remove some hacky editor view crap

remove some hacky editor view crap

--- atom.symlink/init.coffee
+++ atom.symlink/init.coffee
@@ -2,12 +2,6 @@

 # background update interval by seconds
 update_interval = 60 * 10
-
-atom.workspaceView.eachEditorView (editorView) ->
-  editor = editorView.getEditor()
-  filepath = editor.getPath()
-  if path.extname(editor.getPath()) is '.md'
-    editor.setSoftWrap(true)

 # function (image_url: String)
 # takes url of an image to be set as the background image


remove init.coffee to remove an error

remove init.coffee to remove an error

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


Handle SVGElement special attrs differently

--- src/reactive.coffee
+++ src/reactive.coffee
@@ -717,7 +717,11 @@

     for ev in events
       do (ev) ->
-        specialAttrs[ev] = (elt, fn) -> elt[ev]((e) -> fn.call(elt, e))
+        specialAttrs[ev] = (elt, fn) ->
+          if elt instanceof SVGElement
+            elt.addEventListener ev, fn
+          else
+            elt[ev]((e) -> fn.call(elt, e))

     # attr vs prop:
     # http://blog.jquery.com/2011/05/10/jquery-1-6-1-rc-1-released/


update opentsdb module for new data format

--- app/opentsdb.coffee
+++ app/opentsdb.coffee
@@ -37,11 +37,11 @@
         unless points
           points = []
           all_series[name] = points
-        points.push [parseInt(value, 10), parseInt(time, 10)]
+        points.push [parseInt(time, 10), parseInt(value, 10)]

     for name, points of all_series
       target: name
-      datapoints: _.sortBy points, ([v, t]) -> t
+      datapoints: _.sortBy points, ([ts, v]) -> ts

   parse_error_response: (response) ->
     doc = Html.parse_document response.responseText


Ignore hidden layers.

Ignore hidden layers.

--- AI2ASS.coffee
+++ AI2ASS.coffee
@@ -40,7 +40,7 @@
         @append "#{@suffix( )}\n#{@prefix( )}"

     appendPath: ( path ) ->
-      unless path.hidden or path.guides or path.clipping or not (path.stroked or path.filled)
+      unless path.hidden or path.guides or path.clipping or not (path.stroked or path.filled) or not path.layer.visible
         @split( path )
         @append ASS_createDrawingFromPoints path.pathPoints

@@ -239,6 +239,8 @@
       # LAYER, WHICH MEANS IT IS IMPOSSIBLE TO REPRODUCE THE WAY
       # SUBLAYERS ARE LAYERED. TL;DR IF YOU STICK A LAYER INSIDE ANOTHER
       # LAYER, FUCK YOU FOREVER.
+      unless currLayer.visible
+        return "Not doing anything to that invisible layer."

       for pageItem in currLayer.pageItems
         recursePageItem pageItem, output