moved element map out to it's own constant

--- dom.coffee
+++ dom.coffee
@@ -288,275 +288,6 @@
 		@_private.classes = value.split(' ')

 class Element extends Node
-	@Map = { # map tag names to classes, for use in .createElement
-		_: class HTMLElement extends Element
-			constructor: (a...) ->
-				super a...
-		a: class HTMLAnchorElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "A"
-				super a...
-		area: class HTMLAreaElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "AREA"
-				super a...
-		audio: class HTMLAudioElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "AUDIO"
-				super a...
-		base: class HTMLBaseElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "BASE"
-				super a...
-		blockquote: class HTMLBlockquoteElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "BLOCKQUOTE"
-				super a...
-		body: class HTMLBodyElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "BODY"
-				super a...
-		br: class HTMLBRElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "BR"
-				super a...
-		button: class HTMLButtonElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "BUTTON"
-				super a...
-		canvas: class HTMLCanvasElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "CANVAS"
-				super a...
-		caption: class HTMLTableCaptionElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "CAPTION"
-				super a...
-		col: class HTMLTableColElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "COL"
-				super a...
-		colgroup: class HTMLTableColElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "COLGROUP"
-				super a...
-		del: class HTMLDelElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "DEL"
-				super a...
-		details: class HTMLDetailsElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "DETAILS"
-				super a...
-		div: class HTMLDivElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "DIV"
-				super a...
-		dl: class HTMLDListElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "DL"
-				super a...
-		embed: class HTMLEmbedElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "EMBED"
-				super a...
-		fieldSet: class HTMLFieldSetElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "FIELDSET"
-				super a...
-		form: class HTMLFormElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "FORM"
-				super a...
-		h1: class HTMLHeadingElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "H1"
-				super a...
-		h2: class HTMLHeading2Element extends HTMLHeadingElement
-			constructor: (a...) ->
-				a[0] = "H1"
-				super a...
-		h3: class HTMLHeading3Element extends HTMLHeadingElement
-			constructor: (a...) ->
-				a[0] = "H1"
-				super a...
-		h4: class HTMLHeading4Element extends HTMLHeadingElement
-			constructor: (a...) ->
-				a[0] = "H1"
-				super a...
-		h5: class HTMLHeading5Element extends HTMLHeadingElement
-			constructor: (a...) ->
-				a[0] = "H1"
-				super a...
-		h6: class HTMLHeading6Element extends HTMLHeadingElement
-			constructor: (a...) ->
-				a[0] = "H6"
-				super a...
-		head: class HTMLHeadElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "HEAD"
-				super a...
-		hr: class HTMLHRElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "HR"
-				super a...
-		html: class HTMLHtmlElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "HTML"
-				super a...
-		iframe: class HTMLIFrameElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "IFRAME"
-				super a...
-		image: class HTMLImageElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "IMAGE"
-				super a...
-		input: class HTMLInputElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "INPUT"
-				super a...
-		ins: class HTMLInsElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "INS"
-				super a...
-		keygen: class HTMLKeygenElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "KEYGEN"
-				super a...
-		label: class HTMLLabelElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "LABEL"
-				super a...
-		legend: class HTMLLegendElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "LEGEND"
-				super a...
-		li: class HTMLLIElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "LI"
-				super a...
-		link: class HTMLLinkElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "LINK"
-				super a...
-		map: class HTMLMapElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "MAP"
-				super a...
-		menu: class HTMLMenuElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "MENU"
-				super a...
-		meta: class HTMLMetaElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "META"
-				super a...
-		meter: class HTMLMeterElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "METER"
-				super a...
-		object: class HTMLObjectElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "OBJECT"
-				super a...
-		ol: class HTMLOListElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "OL"
-				super a...
-		optgroup: class HTMLOptGroupElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "OPTGROUP"
-				super a...
-		option: class HTMLOptionElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "OPTION"
-				super a...
-		output: class HTMLOutputElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "OUTPUT"
-				super a...
-		p: class HTMLParagraphElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "P"
-				super a...
-		param: class HTMLParamElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "PARAM"
-				super a...
-		pre: class HTMLPreElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "PRE"
-				super a...
-		progress: class HTMLProgressElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "PROGRESS"
-				super a...
-		quote: class HTMLQuoteElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "QUOTE"
-				super a...
-		script: class HTMLScriptElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "SCRIPT"
-				super a...
-		select: class HTMLSelectElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "SELECT"
-				super a...
-		source: class HTMLSourceElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "SOURCE"
-				super a...
-		style: class HTMLStyleElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "STYLE"
-				super a...
-		table: class HTMLTableElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TABLE"
-				super a...
-		thead: class HTMLTableHeadElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "THEAD"
-				super a...
-		tbody: class HTMLTableBodyElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TBODY"
-				super a...
-		tfoot: class HTMLTableFootElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TFOOT"
-				super a...
-		td: class HTMLTableCellElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TD"
-				super a...
-		th: class HTMLTableHeadElement extends HTMLTableCellElement
-			constructor: (a...) ->
-				a[0] = "TH"
-				super a...
-		tr: class HTMLTableRowElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TR"
-				super a...
-		textarea: class HTMLTextAreaElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TEXTAREA"
-				super a...
-		title: class HTMLTitleElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "TITLE"
-				super a...
-		ul: class HTMLUListElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "UL"
-				super a...
-		video: class HTMLVideoElement extends HTMLElement
-			constructor: (a...) ->
-				a[0] = "VIDEO"
-				super a...
-	}
 	constructor: (a...) ->
 		a[2] ?= Node.ELEMENT_NODE
 		super a...
@@ -627,11 +358,7 @@
 	# blur: NotSupported
 	# render
 	toString: (pretty=false, deep=true, indentLevel = 0) ->
-		try
-			name = @nodeName?.toLowerCase()
-		catch err
-			console.log @
-			throw err
+		name = @nodeName?.toLowerCase()
 		if pretty and deep
 			indent = repeat("  ", indentLevel)
 			newline = "\n"
@@ -654,6 +381,276 @@
 			ret[r++] = indent + "</#{name}>" + newline
 		ret.join('')

+ELEMENT_MAP = { # map tag names to classes, for use in .createElement
+	_: class HTMLElement extends Element
+		constructor: (a...) ->
+			super a...
+	a: class HTMLAnchorElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "A"
+			super a...
+	area: class HTMLAreaElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "AREA"
+			super a...
+	audio: class HTMLAudioElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "AUDIO"
+			super a...
+	base: class HTMLBaseElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "BASE"
+			super a...
+	blockquote: class HTMLBlockquoteElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "BLOCKQUOTE"
+			super a...
+	body: class HTMLBodyElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "BODY"
+			super a...
+	br: class HTMLBRElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "BR"
+			super a...
+	button: class HTMLButtonElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "BUTTON"
+			super a...
+	canvas: class HTMLCanvasElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "CANVAS"
+			super a...
+	caption: class HTMLTableCaptionElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "CAPTION"
+			super a...
+	col: class HTMLTableColElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "COL"
+			super a...
+	colgroup: class HTMLTableColElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "COLGROUP"
+			super a...
+	del: class HTMLDelElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "DEL"
+			super a...
+	details: class HTMLDetailsElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "DETAILS"
+			super a...
+	div: class HTMLDivElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "DIV"
+			super a...
+	dl: class HTMLDListElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "DL"
+			super a...
+	embed: class HTMLEmbedElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "EMBED"
+			super a...
+	fieldSet: class HTMLFieldSetElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "FIELDSET"
+			super a...
+	form: class HTMLFormElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "FORM"
+			super a...
+	h1: class HTMLHeadingElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "H1"
+			super a...
+	h2: class HTMLHeading2Element extends HTMLHeadingElement
+		constructor: (a...) ->
+			a[0] = "H1"
+			super a...
+	h3: class HTMLHeading3Element extends HTMLHeadingElement
+		constructor: (a...) ->
+			a[0] = "H1"
+			super a...
+	h4: class HTMLHeading4Element extends HTMLHeadingElement
+		constructor: (a...) ->
+			a[0] = "H1"
+			super a...
+	h5: class HTMLHeading5Element extends HTMLHeadingElement
+		constructor: (a...) ->
+			a[0] = "H1"
+			super a...
+	h6: class HTMLHeading6Element extends HTMLHeadingElement
+		constructor: (a...) ->
+			a[0] = "H6"
+			super a...
+	head: class HTMLHeadElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "HEAD"
+			super a...
+	hr: class HTMLHRElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "HR"
+			super a...
+	html: class HTMLHtmlElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "HTML"
+			super a...
+	iframe: class HTMLIFrameElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "IFRAME"
+			super a...
+	image: class HTMLImageElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "IMAGE"
+			super a...
+	input: class HTMLInputElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "INPUT"
+			super a...
+	ins: class HTMLInsElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "INS"
+			super a...
+	keygen: class HTMLKeygenElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "KEYGEN"
+			super a...
+	label: class HTMLLabelElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "LABEL"
+			super a...
+	legend: class HTMLLegendElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "LEGEND"
+			super a...
+	li: class HTMLLIElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "LI"
+			super a...
+	link: class HTMLLinkElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "LINK"
+			super a...
+	map: class HTMLMapElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "MAP"
+			super a...
+	menu: class HTMLMenuElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "MENU"
+			super a...
+	meta: class HTMLMetaElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "META"
+			super a...
+	meter: class HTMLMeterElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "METER"
+			super a...
+	object: class HTMLObjectElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "OBJECT"
+			super a...
+	ol: class HTMLOListElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "OL"
+			super a...
+	optgroup: class HTMLOptGroupElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "OPTGROUP"
+			super a...
+	option: class HTMLOptionElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "OPTION"
+			super a...
+	output: class HTMLOutputElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "OUTPUT"
+			super a...
+	p: class HTMLParagraphElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "P"
+			super a...
+	param: class HTMLParamElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "PARAM"
+			super a...
+	pre: class HTMLPreElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "PRE"
+			super a...
+	progress: class HTMLProgressElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "PROGRESS"
+			super a...
+	quote: class HTMLQuoteElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "QUOTE"
+			super a...
+	script: class HTMLScriptElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "SCRIPT"
+			super a...
+	select: class HTMLSelectElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "SELECT"
+			super a...
+	source: class HTMLSourceElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "SOURCE"
+			super a...
+	style: class HTMLStyleElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "STYLE"
+			super a...
+	table: class HTMLTableElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TABLE"
+			super a...
+	thead: class HTMLTableHeadElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "THEAD"
+			super a...
+	tbody: class HTMLTableBodyElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TBODY"
+			super a...
+	tfoot: class HTMLTableFootElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TFOOT"
+			super a...
+	td: class HTMLTableCellElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TD"
+			super a...
+	th: class HTMLTableHeadElement extends HTMLTableCellElement
+		constructor: (a...) ->
+			a[0] = "TH"
+			super a...
+	tr: class HTMLTableRowElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TR"
+			super a...
+	textarea: class HTMLTextAreaElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TEXTAREA"
+			super a...
+	title: class HTMLTitleElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "TITLE"
+			super a...
+	ul: class HTMLUListElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "UL"
+			super a...
+	video: class HTMLVideoElement extends HTMLElement
+		constructor: (a...) ->
+			a[0] = "VIDEO"
+			super a...
+}
+
 Element::__defineGetter__ 'tagName', () -> @nodeName
 Element::__defineGetter__ 'innerHTML', () ->
 	h = []
@@ -721,29 +718,22 @@
 		@_private = extend @_private, {
 			idMap: {}
 		}
-	createCDATASection: (value) ->
-		new CData(value, @)
-	createComment: (value) ->
-		new Comment(value, @)
-	createDocumentFragment: () ->
-		new DocumentFragment(@)
+	createCDATASection: (value) -> new CData(value, @)
+	createComment: (value) -> new Comment(value, @)
+	createDocumentFragment: () -> new DocumentFragment(@)
 	createElement: (name) ->
-		nodeClass = Element.Map[name?.toLowerCase()]
+		nodeClass = ELEMENT_MAP[name?.toLowerCase()]
 		if not nodeClass?
-			new Element.Map['_'](name.toUpperCase())
+			new ELEMENT_MAP['_'](name.toUpperCase())
 		else
 			new nodeClass(null,null,null,@)
 	# createEntityReference: NotSupported
 	createEvent: (type) ->
 		switch type
-			when "MutationEvents"
-				new MutationEvent()
-			else
-				new Event()
-	createTextNode: (text) ->
-		new Text(text, @)
-	getElementById: (id) ->
-		@_private.idMap[id]
+			when "MutationEvents" then new MutationEvent()
+			else new Event()
+	createTextNode: (text) -> new Text(text, @)
+	getElementById: (id) -> @_private.idMap[id]

 class HTMLDocument extends Document
 	constructor: () ->


Safety check on tooltip target timeout

Safety check on tooltip target timeout

--- app/assets/site/tooltip.coffee
+++ app/assets/site/tooltip.coffee
@@ -13,6 +13,7 @@
       $scope.style = {}

       $timeout ->
+        return unless tooltip.target
         doc = window.document.documentElement
         box = tooltip.target.getBoundingClientRect()


[backport] Fix analytics data send on register through invitation

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -533,6 +533,9 @@

         onSuccessSubmitRegister = (response) ->
             $analytics.trackEvent("auth", "invitationAccept", "invitation accept with new user", 1)
+            $analytics.trackEvent("auth", "register", "user registration", 1)
+            $analytics.addEcStep("register", null, null)
+
             $location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
             $confirm.notify("success", "You've successfully joined this project",
                                        "Welcome to #{_.escape($scope.invitation.project_name)}")


Display more details when picking 1 vendor show description and any specials

--- src/streetfood.coffee
+++ src/streetfood.coffee
@@ -48,7 +48,7 @@

 calculateOpenStateAndScore = (vendor, now) ->
   now or= new Date().getTime()
-  if vendor.open.length > 0
+  if vendor.open?.length > 0
     nextOpen = vendor.open[0].start * 1000
     nextClose = vendor.open[0].end * 1000
     if nextOpen < now and nextClose > now + 30*60*1000 # open for next 30 minutes
@@ -114,7 +114,11 @@
   vendorInfo += " - #{scoredVendor.vendor.rating} fans - #{formatUrl(scoredVendor.vendor, city)}"
   msg.send vendorInfo

-msgVendorPicture = (msg, vendor) ->
+msgVendorDetails = (msg, vendor) ->
+  if vendor.description
+    msg.send vendor.description
+  if vendor.open?.length and vendor.open[0].special
+    msg.send "*** #{vendor.open[0].special} ***"
   if vendor.images?.header?.length
     msg.send msg.random vendor.images.header

@@ -159,7 +163,7 @@
         return msg.send "Sorry, I couldn't find any food carts" unless choice?

         msgVendorInfo msg, choice, location.city
-        msgVendorPicture msg, choice.vendor
+        msgVendorDetails msg, choice.vendor

   robot.respond /top (\d+) (street( )?food|food( )?cart(s)?)( (in|near) (.+))?/i, (msg) ->
     n = +msg.match[1]


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -141,7 +141,7 @@

         return (t) ->
             b = i(t);
-            return $(@node).arc(b)
+            return arc(b)

   renderX3: (data) ->


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -261,7 +261,7 @@

             # calculate the default position for the label,
             #   so that the middle of the label is centered in the arc
-            bbox = d3.getBBox()
+            bbox = getBBox()
             #bbox.width and bbox.height will
             #describe the size of the label text
             labelRadius = radius - 20


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -317,40 +317,40 @@
         maxLabelWidth = 0
         maxLabelHeight = 0

-        #labels.text((d) ->
+        labels.text((d) ->
             # Set the text *first*, so we can query the size
             # of the label with .getBBox()
-        #    return d.value
-        #)
-        #.each((d, i) ->
+            return d.value
+        )
+        .each((d, i) ->
             # Move all calculations into the each function.
             # Position values are stored in the data object
             # so can be accessed later when drawing the line

             # calculate the position of the center marker
-        #    a = (d.startAngle + d.endAngle) / 2
+            a = (d.startAngle + d.endAngle) / 2

             #trig functions adjusted to use the angle relative
             #to the "12 o'clock" vector:
-        #    d.cx = Math.sin(a) * (radius - 75)
-        #    d.cy = -Math.cos(a) * (radius - 75)
+            d.cx = Math.sin(a) * (radius - 75)
+            d.cy = -Math.cos(a) * (radius - 75)

             # calculate the default position for the label,
             #   so that the middle of the label is centered in the arc
-        #    bbox = this.getBBox()
+            bbox = this.getBBox()
             #bbox.width and bbox.height will
             #describe the size of the label text
-        #    labelRadius = radius - 20
-        #    d.x =  Math.sin(a) * (labelRadius)
-        #    d.l = d.x - bbox.width / 2 - 2
-        #    d.r = d.x + bbox.width / 2 + 2
-        #    d.y = -Math.cos(a) * (radius - 20)
-        #    d.b = d.oy = d.y + 5
-        #    d.t = d.y - bbox.height - 5
+            labelRadius = radius - 20
+            d.x =  Math.sin(a) * (labelRadius)
+            d.l = d.x - bbox.width / 2 - 2
+            d.r = d.x + bbox.width / 2 + 2
+            d.y = -Math.cos(a) * (radius - 20)
+            d.b = d.oy = d.y + 5
+            d.t = d.y - bbox.height - 5

             # check whether the default position
             #   overlaps any other labels
-        #    conflicts = []
+            conflicts = []
             #labelLayout.visit( (node, x1, y1, x2, y2) ->
                 #recurse down the tree, adding any overlapping
                 #node is the node in the quadtree,
@@ -362,63 +362,63 @@
                 #2. right edge of node is to the left of left edge of label
                 #3. top (minY) edge of node is greater than the bottom of label
                 #4. bottom (maxY) edge of node is less than the top of label
-            #    if  (x1 > d.r + maxLabelWidth/2) or (x2 < d.l - maxLabelWidth/2) or (y1 > d.b + maxLabelHeight/2) or (y2 < d.t - maxLabelHeight/2 )
-            #        return true #don't bother visiting children or checking this node
+                if  (x1 > d.r + maxLabelWidth/2) or (x2 < d.l - maxLabelWidth/2) or (y1 > d.b + maxLabelHeight/2) or (y2 < d.t - maxLabelHeight/2 )
+                    return true #don't bother visiting children or checking this node

-            #    p = node.point
-            #    v = false
-            #    h = false
-            #    if p #p is defined, i.e., there is a value stored in this node
-            #        h =  ( ((p.l > d.l) and (p.l <= d.r)) or ((p.r > d.l) and (p.r <= d.r)) or ((p.l < d.l) and (p.r >=d.r) ) ) #horizontal conflict
-            #        v =  ( ((p.t > d.t) and (p.t <= d.b)) or ((p.b > d.t) and (p.b <= d.b)) or ((p.t < d.t) and (p.b >=d.b) ) ) #vertical conflict
-            #        if h and v
-            #            conflicts.push(p) #add to conflict list
-            #)
-
-        #    if conflicts.length
-        #        console.log(d, " conflicts with ", conflicts);
-        #        rightEdge = d3.max(conflicts, (d2) ->
-        #            return d2.r
-        #        )
-
-        #        d.l = rightEdge
-        #        d.x = d.l + bbox.width / 2 + 5
-        #        d.r = d.l + bbox.width + 10
-
-        #    else console.log("no conflicts for ", d)
+                p = node.point
+                v = false
+                h = false
+                if p #p is defined, i.e., there is a value stored in this node
+                    h =  ( ((p.l > d.l) and (p.l <= d.r)) or ((p.r > d.l) and (p.r <= d.r)) or ((p.l < d.l) and (p.r >=d.r) ) ) #horizontal conflict
+                    v =  ( ((p.t > d.t) and (p.t <= d.b)) or ((p.b > d.t) and (p.b <= d.b)) or ((p.t < d.t) and (p.b >=d.b) ) ) #vertical conflict
+                    if h and v
+                        conflicts.push(p) #add to conflict list
+            )
+
+            if conflicts.length
+                console.log(d, " conflicts with ", conflicts);
+                rightEdge = d3.max(conflicts, (d2) ->
+                    return d2.r
+                )
+
+                d.l = rightEdge
+                d.x = d.l + bbox.width / 2 + 5
+                d.r = d.l + bbox.width + 10
+
+            else console.log("no conflicts for ", d)

             # add this label to the quadtree, so it will show up as a conflict
             #   for future labels.
             #labelLayout.add( d )
-        #    maxLabelWidth = Math.max(maxLabelWidth, bbox.width+10)
-        #    maxLabelHeight = Math.max(maxLabelHeight, bbox.height+10)
-        #)
-        #.transition() #we can use transitions now!
-        #.attr("x", (d) ->
-        #            return d.x
-        #        )
-        #        .attr("y", (d) ->
-        #            return d.y
-        #        )
-
-
-        #pointers = pointerGroup.selectAll("path.pointer")
-        #    .data(piedata)
-        #pointers.enter()
-        #    .append("path")
-        #    .attr("class", "pointer")
-        #    .style("fill", "none")
-        #    .style("stroke", "black")
-        #    .attr("marker-end", "url(#circ)")
-        #pointers.exit().remove()
-
-        #pointers.transition().attr("d", (d) ->
-        #    if d.cx > d.l
-        #        return "M" + (d.l+2) + "," + d.b + "L" + (d.r-2) + "," + d.b + " " + d.cx + "," + d.cy
-        #    else
-        #        return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy
-        #
-        #)
+            maxLabelWidth = Math.max(maxLabelWidth, bbox.width+10)
+            maxLabelHeight = Math.max(maxLabelHeight, bbox.height+10)
+        )
+        .transition() #we can use transitions now!
+        .attr("x", (d) ->
+                    return d.x
+                )
+                .attr("y", (d) ->
+                    return d.y
+                )
+
+
+        pointers = pointerGroup.selectAll("path.pointer")
+            .data(piedata)
+        pointers.enter()
+            .append("path")
+            .attr("class", "pointer")
+            .style("fill", "none")
+            .style("stroke", "black")
+            .attr("marker-end", "url(#circ)")
+        pointers.exit().remove()
+
+        pointers.transition().attr("d", (d) ->
+            if d.cx > d.l
+                return "M" + (d.l+2) + "," + d.b + "L" + (d.r-2) + "," + d.b + " " + d.cx + "," + d.cy
+            else
+                return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy
+
+        )

   renderX5: (data) ->
         #console.log("update pie", data);


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -191,7 +191,7 @@
         arcs.append("svg:path").attr("fill", (d, i) -> color i)
           .attr("fill-opacity", 0.4).attr("d", arc)

-  render: (data) ->
+  renderX5: (data) ->
         #console.log("update pie", data);

         if !data
@@ -426,7 +426,7 @@
                 return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy

         )
-  renderX5: (data) ->
+  render: (data) ->
         #console.log("update pie", data);

         if !data
@@ -452,7 +452,6 @@
         $(@node).children("svg").remove();

         svg = d3.select(@node).append("svg:svg")
-            .data([data])
             .attr("width", width)
             .attr("height", height)
             .append("svg:g")


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -1,51 +1,53 @@
 class Dashing.FullpieAgent extends Dashing.Widget

     @accessor 'data'
+
+    constructor: ->
+        @_current = ''
+
+        #$(@node).fadeOut().fadeIn()
+        @container = $(@node).parent()
+
+        #
+        # CONFIG ZONE
+        #
+
+        # Width of the SVG area
+        @width = (Dashing.widget_base_dimensions[0] * @container.data('sizex')) - Dashing.widget_margins[0] * 4 * (@container.data('sizex') - 1)
+
+        # Height of the SVG area allowing for header and footer text
+        @height = (Dashing.widget_base_dimensions[1] * @container.data('sizey')) - 120
+
+        # Calculated min dimension of the SVG area
+        @radius = Math.min(@width, @height) / 2
+
+        # Outer radius of the pie
+        @radiuso = Math.min(@width, @height) / 3
+
+        # Inner radius of the pie (zero = pie, non-zero = donut)
+        @radiusi = @radiuso * 2 / 3
+
+        # Color scale for pie slices
+        @color = d3.scale.category20()
+
+        # X-offset for drop shadow filter
+        @dropshadowx = 2
+
+        # Y-offset for drop shadow filter
+        @dropshadowy = 2
+
+        # [STRING] Blur value for drop shadow filter
+        @dropshadowblur = '1.2'
+
+        #
+        # END CONFIG ZONE
+        #
+
+        @pie = d3.layout.pie().value((d) -> d.value).sort(null)
+        @arc = d3.svg.arc().innerRadius(@radiusi).outerRadius(@radiuso)

     ready: ->
         #@oldPieData = ''
-        @_current = ''
-
-        #$(@node).fadeOut().fadeIn()
-        @container = $(@node).parent()
-
-        #
-        # CONFIG ZONE
-        #
-
-        # Width of the SVG area
-        @width = (Dashing.widget_base_dimensions[0] * @container.data('sizex')) - Dashing.widget_margins[0] * 4 * (@container.data('sizex') - 1)
-
-        # Height of the SVG area allowing for header and footer text
-        @height = (Dashing.widget_base_dimensions[1] * @container.data('sizey')) - 120
-
-        # Calculated min dimension of the SVG area
-        @radius = Math.min(@width, @height) / 2
-
-        # Outer radius of the pie
-        @radiuso = Math.min(@width, @height) / 3
-
-        # Inner radius of the pie (zero = pie, non-zero = donut)
-        @radiusi = @radiuso * 2 / 3
-
-        # Color scale for pie slices
-        @color = d3.scale.category20()
-
-        # X-offset for drop shadow filter
-        @dropshadowx = 2
-
-        # Y-offset for drop shadow filter
-        @dropshadowy = 2
-
-        # [STRING] Blur value for drop shadow filter
-        @dropshadowblur = '1.2'
-
-        #
-        # END CONFIG ZONE
-        #
-
-        @pie = d3.layout.pie().value((d) -> d.value).sort(null)
-        @arc = d3.svg.arc().innerRadius(@radiusi).outerRadius(@radiuso)

     onData: (data) ->


some fixes to gamepad api

some fixes to gamepad api

--- app/assets/javascripts/input.coffee
+++ app/assets/javascripts/input.coffee
@@ -66,7 +66,7 @@
 			# http://www.gamepadjs.com/

 			gamepad : null
-			delay : 1000 / 30
+			delay :  200
 			buttonCallbackMap : {}
 			buttonNameMap :
 				"ButtonA" : "faceButton0"
@@ -76,6 +76,18 @@
 				"ButtonStart"  : "start"
 				"ButtonSelect" : "select"

+				"ButtonLeftTrigger"  : " leftShoulder0"
+				"ButtonRightTrigger" : "rightShoulder0"
+				"ButtonLeftShoulder" : "leftShoulder1"
+				"ButtonRightShoulder": "rightShoulder1"
+
+				"ButtonUp"    : "dpadUp"
+				"ButtonDown"  : "dpadDown"
+				"ButtonLeft"  : "dpadLeft"
+				"ButtonRight" : "dpadRight"
+
+				"ButtonLeftStick"  : "leftStickButton"
+				"ButtonRightStick" : "rightStickButton"
 				"LeftStickX" : "leftStickX"
 				"LeftStickY" : "leftStickY"
 				"RightStickX": "rightStickX"


Clean up tests names

Clean up tests names

--- test/blocks.coffee
+++ test/blocks.coffee
@@ -4,7 +4,7 @@
 describe 'evil.block', ->
   afterEach -> $('#fixtures').html('')

-  it 'should add role alias', ->
+  it 'adds role alias', ->
     body '' +
          '' +
          ''
@@ -13,7 +13,7 @@

   describe 'function API', ->

-    it 'should execute callback only if find selector', ->
+    it 'executes callback only if find selector', ->
       called = false
       evil.block '.page', -> called = true

@@ -23,7 +23,7 @@
       body ''
       called.should.be.true

-    it 'should execute objects of functions', ->
+    it 'executes objects of functions', ->
       called = ''
       evil.block '.page', ->
         initA: ->
@@ -34,7 +34,7 @@
       body ''
       called.should.eql('ab')

-    it 'should send jQuery and page', ->
+    it 'sends jQuery and page', ->
       args = []
       evil.block '.page', ->
         args = arguments
@@ -44,7 +44,7 @@
       args[0].should.eql(jQuery)
       args[2].should.eql($ $('.page').get(0))

-    it 'should send b function', ->
+    it 'sends b function', ->
       b = null
       evil.block '.page', ($, _b) -> b = _b

@@ -55,21 +55,21 @@
       b('a').length.should.eql(2)
       b.link.length.should.eql(2)

-    it 'should came case role name', ->
+    it 'camel-cases role name', ->
       b = null
       evil.block '.page', ($, _b) -> b = _b

       body ''
       b.camelCaseRole.length.should.eql(1)

-    it 'should call on every finded block', ->
+    it 'calls on every finded block', ->
       blocks = []
       evil.block '.page', ($, b, block)-> blocks.push(block)

       body ''
       blocks.should.eql ($ el for el in $ '.page')

-    it 'should properly find elems inside', ->
+    it 'properly finds elems inside', ->
       bs     = []
       blocks = []
       evil.block '.page', ($, b, block) ->
@@ -86,7 +86,7 @@

   describe 'class API', ->

-    it 'should execute callback only if find selector', ->
+    it 'executes callback only if find selector', ->
       called = false
       evil.block '.page',
         init: ->
@@ -98,7 +98,7 @@
       body ''
       called.should.be.true

-    it 'should create properties for each role', ->
+    it 'creates properties for each role', ->
       prop = false
       evil.block '.page',
         init: ->
@@ -107,7 +107,7 @@
       body '

'
       prop[0].should.eql $('@roleName')[0]

-    it 'should listen block events', ->
+    it 'listens block events', ->
       burning = ''
       evil.block '.page',
         'on fire burn': (e, param) ->
@@ -117,7 +117,7 @@
       $('.page').trigger('fire', '1').trigger('burn', '2')
       burning.should.eql ' fire 1 burn 2'

-    it 'should listen elements events', ->
+    it 'listens elements events', ->
       burning = ''
       evil.block '.page',
         'fire burn on @a, @b': (el, e, param) ->
@@ -132,7 +132,7 @@
       $('@b').trigger('burn', '2')
       burning.should.eql ' a 1 b 2'

-    it 'should find inside', ->
+    it 'finds inside', ->
       finded = false
       evil.block '.page',
         init: ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/camera.coffee
+++ test/camera.coffee
@@ -3,6 +3,8 @@
 test "create", ->
   ok Camera()

-test "", -
+test "overlay", ->
+  object
+
 # Clear out the module
 module()


Groups: Vm always on not set bug is fixed

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -141,27 +141,34 @@
   @setAlwaysOn = secure (client, options, callback)->
     {connection: delegate: account} = client
     {vmName, status} = options
-
-    selector =
-      hostnameAlias : vmName
-      webHome       : account.profile.nickname
-
-    @one selector, (err, vm) ->
+    JDomain = require './domain'
+    JDomain.isDomainEligible {domain: vmName}, (err, webHome) =>
       return callback err if err
-      return callback new KodingError "VM Not FOUND" unless vm
-      subscriptionOptions = targetOptions: selector: tags: $in: ["vm"]
-      account.fetchSubscriptions null, subscriptionOptions, (err, subscriptions) ->
-        return callback err  if err
-        return callback new KodingError "Subscription not found"  unless subscriptions.length
-        [subscription] = subscriptions
-        JPaymentPack.one tags: "alwayson", (err, pack) ->
+
+      selector =
+        hostnameAlias : vmName
+        webHome       : webHome
+      @one selector, (err, vm) ->
+        return callback err if err
+        return callback new KodingError "VM Not FOUND" unless vm
+
+        [{ id: groupId }] = vm.groups
+        JGroup = require './group'
+        JGroup.one { _id: groupId }, (err, group)->
           return callback err  if err
-          return callback new KodingError "Always On pack not found"  unless pack
-          {debit, credit} = subscription
-          fn = if status then debit else credit
-          fn.call subscription, {pack}, (err) ->
+          return callback new KodingError "Group not found"  unless group
+          group.fetchSubscription (err, subscription) ->
             return callback err  if err
-            vm.update $set: alwaysOn: status, callback
+            return callback new KodingError "Group subscription not found"  unless subscription
+
+            JPaymentPack.one tags: "alwayson", (err, pack) ->
+              return callback err  if err
+              return callback new KodingError "Always On pack not found"  unless pack
+              {debit, credit} = subscription
+              fn = if status then debit else credit
+              fn.call subscription, {pack}, (err) ->
+                return callback err  if err
+                vm.update $set: alwaysOn: status, callback

   @fetchDefaultVm_ = (client, callback)->
     {delegate} = client.connection


Missed one platform check, refs #94

Missed one platform check, refs #94

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -331,7 +331,7 @@
   exports.loadJSON definition, success

 exports.loadFile = (file, success) ->
-  unless typeof process is 'object' and process.title is 'node'
+  unless process and process.execPath and process.execPath.indexOf('node') isnt -1
     try
       definition = require file
       exports.loadJSON definition, success


added initial version of popup.coffee

added initial version of popup.coffee

--- src/lib/popup.coffee
+++ src/lib/popup.coffee
@@ -0,0 +1,42 @@
+# [Template](http://neocotic.com/template)
+# (c) 2012 Alasdair Mercer
+# Freely distributable under the MIT license.
+# For all details and documentation:
+# <http://neocotic.com/template>
+
+#### Private variables
+
+# Easily accessible reference to the extension controller.
+{ext} = chrome.extension.getBackgroundPage()
+
+#### Private functions
+
+ # Calculate the widest text used by the `div` elements in the popup and assign
+ # it to all the others.
+resizePopupText = ->
+  textItems   = document.getElementsByClassName 'text'
+  width       = 0
+
+  for textItem in textItems
+    width = textItem.scrollWidth if textItem.scrollWidth > width
+  for textItem in textItems
+    textItem.style.width = "#{width}px"
+
+#### Popup page setup
+
+popup = window.popup =
+
+  #### Public functions
+
+  # Initialize the popup page.
+  init: ->
+    # Insert the prepared HTML in to the popup's body.
+    document.body.innerHTML = ext.popupHTML
+    # Fix dimensions of feature text
+    resizePopupText()
+
+  # Send a request to the background page using the information provided.
+  sendRequest: (item) ->
+    chrome.extension.sendRequest
+      data: name: item.getAttribute 'name'
+      type: 'popup'


use robot.logger.info instead of console.log

--- src/scripts/jira.coffee
+++ src/scripts/jira.coffee
@@ -251,7 +251,7 @@
       return

     if (ignoredusers.some (user) -> user == msg.message.user.name)
-      console.log 'ignoring user due to blacklist:', msg.message.user.name
+      robot.logger.info 'ignoring user due to blacklist:', msg.message.user.name
       return

     for matched in msg.match


Fix default value for reqBody

Fix default value for reqBody

--- src/web.coffee
+++ src/web.coffee
@@ -26,7 +26,7 @@

     if !callback
       callback = reqBody
-      body = null
+      reqBody = null

     # Optional headers


Fix level editor surface pixelation when window resized on subtab

Noticed that the canvas was getting width of 100 set when window
  resized while the canvas was not visible, so we check for greater
  than 100 width as a simple fix.


Document resin/helpers

Document resin/helpers

--- lib/resin/helpers/helpers.coffee
+++ lib/resin/helpers/helpers.coffee
@@ -1,9 +1,36 @@
 _ = require('lodash')
 path = require('path')

+# Check is path is absolute
+#
+# @private
+#
+# @param {String} path path
+# @return {Boolean} is absolute
+#
+# @example Is path absolute?
+#		console.log isAbsolutePath('/usr') # True
+#		console.log isAbsolutePath('../Music') # False
+#
 exports.isAbsolutePath = (p) ->
 	return path.resolve(p) is p

+# Prefix relative value paths with another path
+#
+# @private
+#
+# @param {String} prefix path prefix
+# @param {Object} object object containing relative paths as values
+#
+# @note Absolute values will be omitted
+#
+# @example Prefix object with path
+#		object =
+#			dataPrefix: 'resin'
+#
+#		object = prefixObjectValuesWithPath('/opt', object)
+#		console.log(object.dataPrefix) # /opt/resin
+#
 exports.prefixObjectValuesWithPath = (prefix, object) ->
 	return _.object _.map object, (value, key) ->
 		result = [ key ]


Add test case showing one mock object passed by mock() using another.

--- test/TinyMockJS.test.coffee
+++ test/TinyMockJS.test.coffee
@@ -109,6 +109,11 @@
         m2.expects("my_method2")
         m1.my_method1()
         m2.my_method2()
+
+  it "allows one mock object to use another", ->
+    mock (m1, m2) ->
+      m1.expects("my_method").returns(m2)
+      m1.my_method().should.equal(m2)

 describe "expects(method_name)", ->


Get rid of debug logging

Get rid of debug logging

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -3,5 +3,3 @@
 angular.module('chessApp')
 	.controller 'MainCtrl', ($scope) ->
 		$scope.game = new Game
-		$scope.game.test()
-		$scope.game.log()


Adjust time-out delay

Adjust time-out delay

--- test/api/loader.spec.coffee
+++ test/api/loader.spec.coffee
@@ -31,7 +31,7 @@
     _.delay =>
       expect(@api.isPending()).to.be.false
       done()
-    , 21 # longer than loader's isLocal delay
+    , 50 # longer than loader's isLocal delay

   it 'debounces calls to the same URL', (done) ->
     for i in [1..10]
@@ -39,4 +39,4 @@
     _.delay =>
       expect(@jquery.ajax.callCount).equal(1)
       done()
-    , 21
+    , 50


replace all and next buttons work

replace all and next buttons work

--- lib/search-in-buffer-view.coffee
+++ lib/search-in-buffer-view.coffee
@@ -53,6 +53,9 @@

     @regexOptionButton.on 'click', @toggleRegexOption
     @caseSensitiveOptionButton.on 'click', @toggleCaseSensitiveOption
+
+    @replaceNextButton.on 'click', @replaceNext
+    @replaceAllButton.on 'click', @replaceAll

     @findEditor.on 'core:confirm', @confirmFind
     @replaceEditor.on 'core:confirm', @confirmReplace
@@ -144,6 +147,10 @@
     bufferRange = @searchModel.getActiveResultsModel().replaceCurrentResultAndFindNext(replaceText, currentBufferRange).range
     @highlightSearchResult(bufferRange)

+  replaceAll: =>
+    replaceText = @replaceEditor.getText()
+    @searchModel.getActiveResultsModel().replaceAll(replaceText)
+
   findPrevious: =>
     @jumpToSearchResult('findPrevious')


Set first graph as main if no other suitable is found

--- src/projects.coffee
+++ src/projects.coffee
@@ -8,7 +8,8 @@
   for graph in project.graphs
     return graph.properties.id if graph.name is 'main'
     return graph.properties.id if graph.properties.main
-  return null
+  # No suitable graph found, use first
+  return project.graphs[0].properties.id

 exports.getProjectHash = (project, callback) ->
   unless project.graphs.length


fucking uglify eats unicode

fucking uglify eats unicode

--- config/application.coffee
+++ config/application.coffee
@@ -47,6 +47,11 @@
       navBarUrl: process.env['NAVBAR_URL'] || "/api/v1/navbar"
       oauthKey: process.env['OAUTH_KEY'] || "de92924d00c19567c3566d17fe2e783666fd05bcecea385b508b4a3e393dd204"

+  uglify:
+    options:
+      beautify:
+        ascii_only: true
+
   watch:
     handlebars:
       tasks: ["ember_handlebars:compile", "concat:js"]


Remove rest from youtrack

Remove rest from youtrack

--- plugins/youtrack-issues/index.coffee
+++ plugins/youtrack-issues/index.coffee
@@ -48,7 +48,7 @@
       .end (res) ->
         return callback res.error if res.error
         callback null,
-          url: res.header.location
+          url: res.header.location.replace("/rest/", "/")

   @receiveEvent = (config, event, callback) ->
     @fetchToken config, (err, token) =>


jasmineEmulator updated

jasmineEmulator updated

--- test/jasmineEmulator.coffee
+++ test/jasmineEmulator.coffee
@@ -10,19 +10,19 @@

   {
     beforeEach: (fn)->
-      befores[currentDescribe] ||= []
       befores[currentDescribe].push fn

     afterEach: (fn)->
-      afters[currentDescribe] ||= []
       afters[currentDescribe].push fn

     it: (name, fn)->
-      tests[currentDescribe] ||= []
       tests[currentDescribe].push fn

     describe: (name, fn)->
       currentDescribe = name
+      befores[name]   = []
+      tests[name]     = []
+      afters[name]    = []
       describes[name] = fn

     run: (context = this)->


Only avoid scrolling if comment is first one

--- app/js/views/comment.coffee
+++ app/js/views/comment.coffee
@@ -53,7 +53,7 @@
     if previous
       # Scroll into view if a previous comment was already selected
       @$el.scrollIntoView(20)
-    else if @$el.isOutOfView()
+    else if @model != @model.collection.first()
       # Scroll to top if no previous comment was selected
       @$el.closest('.subject').prop 'scrollTop', @$el.position().top


rename toDataXCoord() to toDays()

rename toDataXCoord() to toDays()

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -166,7 +166,7 @@
     $('#graph-container').addClass('showing-prev-data')
     for i in [0...@dataLength]
       if @data.x[i] >= xLeft and @data.x[i] <= xRight
-        x = ((+@data.x[i]+@toDataXCoord(@leftPadding)-@xMin)/(@xMax-@xMin)) * (@canvas.width-@leftPadding)
+        x = ((+@data.x[i]+@toDays(@leftPadding)-@xMin)/(@xMax-@xMin)) * (@canvas.width-@leftPadding)
         y = ((+@data.y[i]-@yMin)/(@yMax-@yMin)) * @canvas.height
         y = -y + @canvas.height # flip y-values
         @ctx.beginPath()
@@ -223,8 +223,8 @@
     # draw marks
     if @marks
       for mark in @marks.all
-        scaledMin = ((parseFloat(mark.dataXMinRel) + parseFloat(@toDataXCoord(@leftPadding)) - parseFloat(xMin) - parseFloat(val) ) / (parseFloat(xMax) - parseFloat(xMin)) ) * parseFloat(@canvas.width-@leftPadding)
-        scaledMax = ((parseFloat(mark.dataXMaxRel) + parseFloat(@toDataXCoord(@leftPadding)) - parseFloat(xMin) - parseFloat(val) ) / (parseFloat(xMax) - parseFloat(xMin)) ) * parseFloat(@canvas.width-@leftPadding)
+        scaledMin = ((parseFloat(mark.dataXMinRel) + parseFloat(@toDays(@leftPadding)) - parseFloat(xMin) - parseFloat(val) ) / (parseFloat(xMax) - parseFloat(xMin)) ) * parseFloat(@canvas.width-@leftPadding)
+        scaledMax = ((parseFloat(mark.dataXMaxRel) + parseFloat(@toDays(@leftPadding)) - parseFloat(xMin) - parseFloat(val) ) / (parseFloat(xMax) - parseFloat(xMin)) ) * parseFloat(@canvas.width-@leftPadding)
         #                                ^ prevents from moving towards left                          ^ prevents marks from moving towards right
         mark.element.style.width = (parseFloat(scaledMax)-parseFloat(scaledMin)) + "px"
         mark.element.style.left = parseFloat(scaledMin) + "px"
@@ -454,7 +454,7 @@

   toCanvasXCoord: (dataPoint) -> ((parseFloat(dataPoint) - parseFloat(@xMin)) / (parseFloat(@xMax) - parseFloat(@xMin))) * (parseFloat(@canvas.width)-parseFloat(@leftPadding))
   toCanvasYCoord: (dataPoint) -> ((parseFloat(dataPoint) - parseFloat(@yMin)) / (parseFloat(@yMax) - parseFloat(@yMin))) * (parseFloat(@canvas.height))
-  toDataXCoord: (canvasPoint) -> ((parseFloat(canvasPoint) / (parseFloat(@canvas.width)-parseFloat(@leftPadding))) * (parseFloat(@xMax) - parseFloat(@xMin))) + parseFloat(@xMin)
+  toDays: (canvasPoint) -> ((parseFloat(canvasPoint) / (parseFloat(@canvas.width)-parseFloat(@leftPadding))) * (parseFloat(@xMax) - parseFloat(@xMin))) + parseFloat(@xMin)
   toDataYCoord: (canvasPoint) -> ((parseFloat(canvasPoint) / parseFloat(@canvas.height)) * (parseFloat(@yMax) - parseFloat(@yMin))) + parseFloat(@yMin)

   addMarkToGraph: (e) =>
@@ -605,8 +605,8 @@
     @canvasXMax = markRightX

     #data coords
-    @dataXMinRel = @canvasGraph.toDataXCoord(@canvasXMin-@canvasGraph.leftPadding)
-    @dataXMaxRel = @canvasGraph.toDataXCoord(@canvasXMax-@canvasGraph.leftPadding)
+    @dataXMinRel = @canvasGraph.toDays(@canvasXMin-@canvasGraph.leftPadding)
+    @dataXMaxRel = @canvasGraph.toDays(@canvasXMax-@canvasGraph.leftPadding)
     @dataXMinGlobal = @dataXMinRel + @originalMin
     @dataXMaxGlobal = @dataXMaxRel + @originalMin


Fix number formatter

Fix number formatter

--- app/lib/formatNumber.coffee
+++ app/lib/formatNumber.coffee
@@ -9,7 +9,7 @@

   for n, i in numbers.split('').reverse()
     result.push n
-    result.push ',' if (i + 1) % 3 is 0
+    result.push ',' if (i + 1) % 3 and numbers.length > 3

   result = result.reverse()


last fix for dimming the editor

last fix for dimming the editor

--- app/lib/image_filter.coffee
+++ app/lib/image_filter.coffee
@@ -49,5 +49,5 @@
 module.exports.revertImage = revertImage = (img, borderImageSelector) ->
   jqimg = $(img)
   return unless jqimg.data('original')
-  $(borderImageSelector).css 'border-image-source', 'url(' + img.src + ')'
+  $(borderImageSelector).css 'border-image-source', 'url(' + jqimg.data('original') + ')'
   img.src = jqimg.data('original')


Fix #736 Invisible thangs still have shadows

--- app/lib/surface/Mark.coffee
+++ app/lib/surface/Mark.coffee
@@ -93,6 +93,7 @@
     @lastHeight = @sprite.thang.height

   buildShadow: ->
+    alpha = @sprite.thang?.alpha ? 1
     width = (@sprite.thang?.width ? 0) + 0.5
     height = (@sprite.thang?.height ? 0) + 0.5
     longest = Math.max width, height
@@ -103,7 +104,7 @@
     height *= Camera.PPM * @camera.y2x  # TODO: doesn't work with rotation
     @mark = new createjs.Shape()
     @mark.mouseEnabled = false
-    @mark.graphics.beginFill "black"
+    @mark.graphics.beginFill "rgba(0, 0, 0, #{alpha})"
     if @sprite.thang.shape in ['ellipsoid', 'disc']
       @mark.graphics.drawEllipse 0, 0, width, height
     else


Remove unnecessary done messages

Remove unnecessary done messages

--- test/sample.coffee
+++ test/sample.coffee
@@ -16,15 +16,13 @@
       done()

   describe 'testing HTML', ->
-    it 'retrieves the page title', (done) ->
+    it 'retrieves the page title', ->
       page.evaluate (-> document.title), (result) ->
         expect(result).to.equal('MyWay!')
-        done()

   describe 'testing CSS', ->
-    it 'retrieves computed styles', (done) ->
+    it 'retrieves computed styles', ->
       page.evaluate (->
         getComputedStyle(document.body).getPropertyValue('font-family')
       ), (result) ->
         expect(result).to.have.string('Helvetica Neue')
-        done()


Disable high zoom level bbox markers in IE.

Caused considerable sluggishness, made
map scrolling unusable in center areas
in Internet Explorer.

--- src/map-view.coffee
+++ src/map-view.coffee
@@ -459,6 +459,8 @@
                 paddingBottomRight: [20,20]

         showAllUnitsAtHighZoom: ->
+            if getIeVersion()
+                return
             if $(window).innerWidth() <= appSettings.mobile_ui_breakpoint
                 return
             if @map.getZoom() >= map.MapUtils.getZoomlevelToShowAllMarkers()


Fix progress bar with push

Fix progress bar with push

--- app/static/coffee/index.coffee
+++ app/static/coffee/index.coffee
@@ -12,7 +12,7 @@
     renderFinished = =>
         NProgress.inc()
         waitRendering -= 1
-        if waitRendering == 0
+        if waitRendering <= 0
             NProgress.done()

     renderProjects = =>


Fix live reload error.

Fix live reload error.

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -30,13 +30,20 @@
           ]
         options:
           debug: true
-          transform: ['coffeeify','hbsfy']
-          extensions: ['.coffee','.hbs']
+          transform: ['coffeeify', 'hbsfy']
+          extensions: ['.coffee', '.hbs']
           insertGlobals: true
           aliasMappings: [
-            cwd: 'app/controllers'
-            src: ['**/*.coffee']
-            dest: 'controllers'
+            {
+              cwd: 'app/controllers'
+              src: ['**/*.coffee']
+              dest: 'controllers'
+            },
+            {
+              cwd: 'app/templates'
+              src: ['**/*.hbs']
+              dest: '../templates'
+            }
           ]
           shim:
             jquery:
@@ -140,12 +147,12 @@
           nospawn: true
       hbs:
         files: ['app/templates/**/*.hbs']
-        tasks: ['scripts', 'concat:devJs']
+        tasks: ['browserify:app']
         options:
           debounceDelay: 250
       js:
         files: ['app/**/*.coffee'],
-        tasks: ['scripts', 'concat:devJs']
+        tasks: ['browserify:app']
         options:
           debounceDelay: 250
       livereload:


Inserting HTML div before _elementBorder, fixing DOM test

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -715,7 +715,7 @@

 			if not @_elementHTML
 				@_elementHTML = document.createElement "div"
-				@_element.appendChild @_elementHTML
+				@_element.insertBefore @_elementHTML, @_elementBorder

 			@_elementHTML.style.zoom = @context.scale
 			@_elementHTML.innerHTML = value


return max_value for version if local or beta or future build

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -334,6 +334,12 @@
 Utils.framerStudioVersion = ->

 	if Utils.isFramerStudio()
+
+		isBeta = navigator.userAgent.indexOf("FramerStudio/beta") >= 0
+		isLocal = navigator.userAgent.indexOf("FramerStudio/local") >= 0
+		isFuture = navigator.userAgent.indexOf("FramerStudio/future") >= 0
+		return Number.MAX_VALUE if isBeta or isLocal or isFuture
+
 		matches = navigator.userAgent.match(/\d+$/)
 		version = parseInt(matches[0]) if matches and matches.length > 0
 		return version if _.isNumber(version)


set fictional 100x100 value

set fictional 100x100 value

--- app/utils/connector-rfb.coffee
+++ app/utils/connector-rfb.coffee
@@ -27,8 +27,8 @@
     @rfb.connect ENV.deviceFarmHost, ENV.deviceFarmPort, '1234', "#{ENV.deviceFarmPath}?token=#{@deviceToken}"
     if @rfb.get_display
       display = @rfb.get_display
-      scaleRatio = display.autoscale screen.w, screen.h, true
-      @rfb.get_mouse().set_scale scaleRatio
+      scaleRatio = display.autoscale 100, 100, true
+      @rfb.get_mouse().set_scale scaleRatio

   disconnect: ->
     @rfb.disconnect()


路径输入框改div

路径输入框改div

--- lib/project/sync-project-info.coffee
+++ lib/project/sync-project-info.coffee
@@ -24,26 +24,25 @@
         @div class: 'form-group', =>
           @label '应用创建位置', class: 'col-sm-3 control-label'
           @div class: 'col-sm-9', =>
-            @subview 'appPath', new TextEditorView(mini: true)
+            @div class:'textEditStyle', outlet: 'appPath'
             @span class: 'inline-block status-added icon icon-file-directory openFolder', click: 'openFolder'
         @div class: 'col-sm-9 col-sm-offset-3', =>
           @div '该目录已存在', class: 'text-warning hide', outlet: 'errorMsg'

   initialize: ->
-    @appId.getModel().onDidChange => @setPath()
+    @appId.getModel().onDidChange => @checkProjectName()
     @appName.getModel().onDidChange => @checkInput()
-    @appPath.getModel().onDidChange => @checkPath()
+    # @appPath.getModel().onDidChange => @checkPath()

   attached: ->
     @type = @parentView.options.newType
     @getProjectDetail(@parentView.options.projectId, @parentView.options.account_id)
     @parentView.setNextBtn('finish')
     @parentView.disableNext()
-    @appPath.basePath = desc.newProjectDefaultPath
-    @appPath.setText @appPath.basePath
+    @appPath.setText desc.newProjectDefaultPath

   getProjectDetail: (projectId, accountId) ->
-    params =
+    params =
       sendCookie: true
       qs:
         account: accountId
@@ -60,27 +59,29 @@
     atom.pickFolder (paths) =>
       if paths?
         console.log paths[0]
-        path = pathM.join paths[0],@appId.getText()
-        console.log  path
-        @appPath.basePath = path
-        @appPath.setText @appPath.basePath
+        @appPath.html paths[0]

   getElement: ->
     @element

   getProjectInfo: ->
+    appId = @appId.getText().trim()
+    appPath = @appPath.html().trim()
+    path = pathM.join appPath,appId
+    dir = new Directory(path)
+    path = pathM.join desc.newProjectDefaultPath,dir.getBaseName() if dir.getParent().isRoot() is yes
     projectInfo =
-      appId : @appId.getText();
-      appName : @appName.getText();
-      appPath : @appPath.getText();
+      appId : @appId.getText()
+      appName : @appName.getText()
+      appPath : path

-    # console.log projectInfo
+    console.log projectInfo
     projectInfo

   checkInput: ->
     flag1 = @appId.getText().trim() isnt ""
     flag2 = @appName.getText().trim() isnt ""
-    flag3 = @appPath.getText().trim() isnt ""
+    flag3 = @appPath.html().trim() isnt ""
     flag4 = @errorMsg.hasClass('hide')

     if flag1 and flag2 and flag3 and flag4
@@ -89,22 +90,27 @@
       @parentView.disableNext()

-  setPath: ->
-    currPath = @appPath.basePath
-    # console.log currPath
-    @appPath.setText pathM.join currPath,@appId.getText().trim() if currPath isnt ""
-    @checkInput()
+  checkProjectName: ->
+    str = @appId.getText().trim()
+    console.log Util.checkProjectName str
+    if Util.checkProjectName str
+      @errorMsg2.addClass('hide')
+    else
+      @errorMsg2.removeClass('hide')
+    @checkPath()

   checkPath: ->
-    path = @appPath.getText().trim()
-    @appPath.basePath = path if @appPath.hasFocus()
+    appId = @appId.getText().trim()
+    appPath = @appPath.html().trim()
+    path = pathM.join appPath,appId
     if path isnt ""
       dir = new Directory(path);
       dir.exists()
         .then (isExists) =>
-          # console.log isExists,@,dir.getRealPathSync()
+          console.log isExists,dir.getRealPathSync()
           unless isExists
             @errorMsg.addClass('hide')
+
           else
             @errorMsg.removeClass('hide')
           @checkInput()


trigger a 'appstate' event when sending heartbeat

Signed-off-by: Manson Li <f2a598d7717cc6757bef28b84a9ab02e5efc6048@matchstick.tv>

--- lib/sender/FlintSenderManager.coffee
+++ lib/sender/FlintSenderManager.coffee
@@ -213,7 +213,8 @@
             headers =
                 'Accept': 'application/xml; charset=utf8'
                 'Authorization': @token
-            @_request 'GET', @serviceUrl, headers, null, null
+            @_getState @serviceUrl, headers, (result, state) =>
+                @emit 'appstate', result, state, @additionalData
         ), @heartbeatInterval

     _stopHeartbeat: ->


Fix errors with parsing indented code-blocks

Resolves gh-22.

--- grammars/restructuredtext.cson
+++ grammars/restructuredtext.cson
@@ -26,6 +26,24 @@
         'include': '#raw-blocks'
       }
       {
+        'include': '#inlines'
+      }
+      {
+        'include': '#tags'
+      }
+      {
+        'include': '#tables'
+      }
+      {
+        'include': '#headings'
+      }
+      {
+        'include': '#comments'
+      }
+    ]
+  'inlines':
+    'patterns': [
+      {
         'include': '#emphasis'
       }
       {
@@ -114,18 +132,6 @@
       {
         'include': '#citations'
       }
-      {
-        'include': '#tags'
-      }
-      {
-        'include': '#tables'
-      }
-      {
-        'include': '#headings'
-      }
-      {
-        'include': '#comments'
-      }
     ]
   'citations':
     'patterns': [
@@ -197,7 +203,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.coffee'
         'patterns': [
           {
@@ -210,7 +216,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.js'
         'patterns': [
           {
@@ -223,7 +229,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.json'
         'patterns': [
           {
@@ -236,7 +242,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.css'
         'patterns': [
           {
@@ -249,7 +255,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.xml'
         'patterns': [
           {
@@ -262,7 +268,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.ruby'
         'patterns': [
           {
@@ -275,7 +281,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.java'
         'patterns': [
           {
@@ -288,7 +294,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.erlang'
         'patterns': [
           {
@@ -301,7 +307,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.cs'
         'patterns': [
           {
@@ -314,7 +320,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.php'
         'patterns': [
           {
@@ -327,7 +333,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.shell'
         'patterns': [
           {
@@ -340,7 +346,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.python'
         'patterns': [
           {
@@ -353,7 +359,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.objc'
         'patterns': [
           {
@@ -366,7 +372,7 @@
         'captures':
           '2':
             'name': 'entity.name.directive.restructuredtext'
-        'end': '^(?!\\1[ \\t])(?=\\S+)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'contentName': 'source.embedded.yaml'
         'patterns': [
           {
@@ -562,13 +568,19 @@
   'raw-blocks':
     'patterns': [
       {
-        'begin': '(::)$'
+        'begin': '^(?!\\s*\\.\\.\\s\\w+)(\\s*)(.*)(::)$'
         'beginCaptures':
-          '1':
+          '2':
+            'patterns': [
+              {
+                'include': '#inlines'
+              }
+            ]
+          '3':
             'name': 'punctuation.section.raw.restructuredtext'
         'comment': 'Literal Blocks'
         'contentName': 'meta.raw.block.restructuredtext'
-        'end': '^(?=\\S)'
+        'end': '^(?!\\s*$|\\1[ \\t]+\\S)'
         'patterns': [
           {
             'include': '#raw-blocks-inner'
@@ -609,7 +621,7 @@
           '2':
             'name': 'punctuation.definition.field.restructuredtext'
         'comment': 'tags (and field lists'
-        'match': '(:)[A-z][A-z0-9  =\\s\\t_.]*(:)'
+        'match': '(:)[A-Za-z][\\w\\s=.]*(:)'
         'name': 'entity.name.tag.restructuredtext'
       }
     ]


Scope unmarked interpreted text as strings

--- grammars/restructuredtext.cson
+++ grammars/restructuredtext.cson
@@ -753,7 +753,7 @@
 			(?=$|[-.,:;!?\\\\/'\"\x29\\\x5D}>\\s«»༻⟭⟯⸡⸠⸣⸥⸧⸩‚„‟‛]|(?![\\x00-\\x9F])[\\p{Pe}\\p{Pi}\\p{Pf}\\p{Pd}\\p{Po}])"
 		captures:
 			1: name: "punctuation.definition.interpreted-text.begin.restructuredtext"
-			2: patterns: [include: "#escapes"]
+			2: name: "string.interpolated.restructuredtext", patterns: [include: "#escapes"]
 			3: name: "punctuation.definition.interpreted-text.end.restructuredtext"


set window.FULLY_REACT

set window.FULLY_REACT

--- src/webapp-smc.coffee
+++ src/webapp-smc.coffee
@@ -4,6 +4,7 @@
 require('./smc-webapp/client_browser.coffee')

 FULLY_REACT = false  # set to true to enable a full react version of SMC
+window.FULLY_REACT = FULLY_REACT

 if FULLY_REACT
     require('./smc-webapp/landing-react')


remove verbose output for debug --help

remove verbose output for debug --help

--- src/steroids.coffee
+++ src/steroids.coffee
@@ -78,7 +78,7 @@
     if argv.version
       firstOption = "version"

-    if firstOption not in ["emulate"] and argv.help
+    if firstOption not in ["emulate", "debug"] and argv.help
       firstOption = "usage"

     @ensureProjectIfNeededFor(firstOption, otherOptions)


enable random limits

enable random limits

--- src/stripler.coffee
+++ src/stripler.coffee
@@ -83,14 +83,14 @@
         msg.reply "#{msg.match[1]}: #{resp.account_data.Balance}"
   robot.hear /^([^ ]+)\+\+\+/, (msg) ->
     time_diff = (new Date() - last_tipped)
-    if false #time_diff < rate_limit
+    if time_diff < rate_limit
       time_left = (rate_limit - time_diff) / 1000
       msg.reply "Slow down! You can only tip every #{rate_limit / 1000} seconds. Try again in #{time_left}s."
       return
     last_tipped = new Date()
     targetUser = msg.match[1]
     sendingUser = msg.message.user.name
-    if false#targetUser == sendingUser
+    if targetUser == sendingUser
       msg.reply "You can't tip yourself, silly."
       return


order for test syncs

order for test syncs

--- apps/fair/client/booths.coffee
+++ apps/fair/client/booths.coffee
@@ -17,8 +17,8 @@
     new BorderedPulldown el: $('#fair-booths-sort .bordered-pulldown')
     @$el.show() unless sd.NODE_ENV == 'test'
     @renderHeader()
+    @fetchFeedItems()
     @renderExhibitorCount()
-    @fetchFeedItems()

   renderHeader: ->
     @$('h1').text if @filter.section


Don't hash because Henry is hashist!

Don't hash because Henry is hashist!

--- libraries/redis-wrapper/index.coffee
+++ libraries/redis-wrapper/index.coffee
@@ -27,7 +27,7 @@
 		heartbeatInterval = heartbeatOpts.heartbeat_interval or 1000 #ms
 		reconnectAfter = heartbeatOpts.reconnect_after or 5000 #ms

-		id = require("crypto").createHash("md5").update(Math.random().toString()).digest("hex")
+		id = require("crypto").randomBytes(16).toString("hex")
 		heartbeatChannel = "heartbeat-#{id}"
 		lastHeartbeat = Date.now()


Fix and refactor viewBox/overflowBox parsing

* "0,0,1,1" previously did not parse
* Ignore overflowBox with unparsable entries
* Share code between viewBox and overflowBox parsing

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -33,12 +33,15 @@
   toString: ->
     "svgtiler: #{@message}"

+parseBox = (box) ->
+  return null unless box
+  box = box.split /\s*[\s,]\s*/
+  .map parseNum
+  return null if null in box
+  box
+
 overflowBox = (xml) ->
-  if xml.documentElement.hasAttribute 'overflowBox'
-    xml.documentElement.getAttribute('overflowBox').split /\s*,?\s+/
-    .map parseFloat
-  else
-    null
+  parseBox xml.documentElement.getAttribute 'overflowBox'

 parseNum = (x) ->
   parsed = parseFloat x
@@ -55,12 +58,7 @@
   ##   - text
   ##   - line widths which extend bounding box
   if xml.documentElement.hasAttribute 'viewBox'
-    viewBox = xml.documentElement.getAttribute('viewBox').split /\s*,?\s+/
-    .map parseNum
-    if null in viewBox
-      null
-    else
-      viewBox
+    parseBox xml.documentElement.getAttribute 'viewBox'
   else
     recurse = (node) ->
       if node.nodeType != node.ELEMENT_NODE or


Use CoffeeScript's built-in __filename, __dirname, etc. setting

coffeescript.eval has built-in support for making a fake module, so use it.

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -325,14 +325,8 @@
   @title: "CoffeeScript mapping file"
   @help: "Object mapping symbol names to SYMBOL e.g. dot: 'dot.svg'"
   parse: (data) ->
-    ## Mimick NodeJS module's __filename and __dirname variables
-    __filename = path.resolve @filename
-    data = """
-      __filename = #{JSON.stringify __filename}
-      __dirname = #{JSON.stringify path.dirname __filename}
-      #{data}
-    """
-    @load require('coffeescript').eval data
+    @load require('coffeescript').eval data,
+      filename: path.resolve @filename

 class Mappings
   constructor: (@maps = []) ->


Fixing up pluralizer

Fixing up pluralizer

--- admin/static/coffee/util.coffee
+++ admin/static/coffee/util.coffee
@@ -136,7 +136,7 @@
 # Helpers for pluralization of nouns and verbs
 Handlebars.registerHelper 'pluralize_noun', (noun, num, capitalize) ->
     ends_with_y = noun.substr(-1) is 'y'
-    if num is 1 or (num is 0 and not ends_with_y)
+    if num is 1
         result = noun
     else
         if ends_with_y and (noun isnt 'key')


Improve tooltip specs

Improve tooltip specs

--- spec_app/spec/javascripts/up/tooltip_spec.js.coffee
+++ spec_app/spec/javascripts/up/tooltip_spec.js.coffee
@@ -4,35 +4,54 @@

     describe 'up.tooltip.attach', ->

-      it 'opens a tooltop above the center of the given element', ->
-        $link = affix('span').text('button label')
-        $link.css(
-          position: 'absolute'
-          left: '200px'
-          top: '200px'
-          width: '50px'
-          height: '50px'
-        )
+      it 'opens a tooltip with the given text', ->
+        $link = affix('span')
         up.tooltip.attach($link, html: 'tooltip text')
         $tooltip = $('.up-tooltip')
-        expect($tooltip).toBeInDOM()
         expect($tooltip).toHaveText('tooltip text')
-        tooltipBox = up.util.measure($tooltip, relative: true)
-        linkBox = up.util.measure($link, relative: true)
-        expect(tooltipBox.top).toBeAround(linkBox.top - tooltipBox.height, 15)
-        expect(tooltipBox.left).toBeAround(linkBox.left + 0.5 * (linkBox.width - tooltipBox.width), 15)
-
-      it 'allows HTML for the tooltip text when contents are given as .html option', ->
+
+      it 'allows HTML for the tooltip text when contents are given as { html } option', ->
         $link = affix('span')
         up.tooltip.attach($link, html: 'text')
         $tooltip = $('.up-tooltip')
         expect($tooltip.html()).toEqual('text')

-      it 'escapes HTML for the tooltip text when contents given as .html option', ->
+      it 'escapes HTML for the tooltip text when contents given as { text } option', ->
         $link = affix('span')
         up.tooltip.attach($link, text: 'text')
         $tooltip = $('.up-tooltip')
         expect($tooltip.html()).toEqual('<b>text</b>')
+
+      describe 'positioning', ->
+
+        beforeEach ->
+          @$link = affix('span').text('button label')
+          @$link.css(
+            position: 'absolute'
+            left: '200px'
+            top: '200px'
+            width: '50px'
+            height: '50px'
+          )
+          @linkBox = up.util.measure(@$link, relative: true)
+
+        describe 'with { position: "top" }', ->
+
+          it 'centers the tooltip above the given element', ->
+            up.tooltip.attach(@$link, html: 'tooltip text', position: 'top')
+            $tooltip = $('.up-tooltip')
+            tooltipBox = up.util.measure($tooltip, relative: true)
+            expect(tooltipBox.top).toBeAround(@linkBox.top - tooltipBox.height, 15)
+            expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width), 15)
+
+        describe 'with { position: "bottom" }', ->
+
+          it 'centers the tooltip below the given element', ->
+            up.tooltip.attach(@$link, html: 'tooltip text', position: 'bottom')
+            $tooltip = $('.up-tooltip')
+            tooltipBox = up.util.measure($tooltip, relative: true)
+            expect(tooltipBox.top).toBeAround(@linkBox.top + @linkBox.height, 15)
+            expect(tooltipBox.left).toBeAround(@linkBox.left + 0.5 * (@linkBox.width - tooltipBox.width), 15)

       it 'closes an existing tooltip'


Fix 156.2. Focus back to input field. (#219)

* Fix 156.2. Focus back to input field.

--- spirit/core/static/spirit/scripts/src/editor.coffee
+++ spirit/core/static/spirit/scripts/src/editor.coffee
@@ -56,22 +56,27 @@

     addBold: =>
         @wrapSelection("**", "**", @options.boldedText)
+        $('#id_comment').focus()
         return false

     addItalic: =>
         @wrapSelection("*", "*", @options.italicisedText)
+        $('#id_comment').focus()
         return false

     addList: =>
         @wrapSelection("\n* ", "", @options.listItemText)
+        $('#id_comment').focus()
         return false

     addUrl: =>
         @wrapSelection("[", "](#{ @options.linkUrlText })", @options.linkText)
+        $('#id_comment').focus()
         return false

     addImage: =>
         @wrapSelection("![", "](#{ @options.imageUrlText })", @options.imageText)
+        $('#id_comment').focus()
         return false

     addPoll: =>
@@ -82,6 +87,7 @@
             "[/poll]\n"
         @wrapSelection("", poll, "")  # todo: append to current pointer position
         @pollCounter++
+        $('#id_comment').focus()
         return false

     togglePreview: =>


handle markdown files with md

handle markdown files with md

--- src/notebook.coffee
+++ src/notebook.coffee
@@ -327,6 +327,9 @@
           cell = add_input_cell run_context.notebook, code: file.content, after: run_context.output_cell
           if options.run
             run cell
+        else if extension is 'md'
+          run_without_input_cell run_context.notebook, ->
+            @md file.content
         else
           try
             imported = JSON.parse file.content


move cursor to end when setting value

move cursor to end when setting value

--- src/notebook.coffee
+++ src/notebook.coffee
@@ -201,6 +201,7 @@

   set_cell_value = (cell, value) ->
     cell.editor.setValue value
+    cell.editor.setCursor(line: cell.editor.lineCount() - 1)

   focus_cell = (cell) ->
     cell.editor.focus()


coverphoto : decorateHeader if group isnt koding

--- client/Social/Activity/views/feedcoverphotoview.coffee
+++ client/Social/Activity/views/feedcoverphotoview.coffee
@@ -13,7 +13,9 @@

       @decorateHeader()  if @group.slug isnt "koding"

-      @group.on "update", @bound "decorateHeader"
+      @group.on "update", =>
+        if @group.slug isnt "koding"
+          @decorateHeader()

   decorateHeader:->


Sidebar: Renamed method.

Sidebar: Renamed method.

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -113,7 +113,7 @@

     if isChannelCollaborative channel
       if channelMessage.payload['system-message'] in ['start', 'stop']
-        @fetchMachines =>
+        @fetchEnvironmentData =>
           @setWorkspaceUnreadCount channel, unreadCount

     switch update.channel.typeConstant
@@ -263,7 +263,7 @@
       index = 0  if isPrivateMessage

       if isChannelCollaborative channel
-        @fetchMachines (data) =>
+        @fetchEnvironmentData (data) =>
           @sharedMachinesList.updateList data.shared.concat data.collaboration
           @setWorkspaceUnreadCount channel, unreadCount
       else
@@ -287,7 +287,7 @@

     # acet - refactor required.
     # if @workspaceItemChannelMap[id]
-    #   @fetchMachines => @fetchWorkspaces()
+    #   @fetchEnvironmentData => @fetchWorkspaces()

     # TODO update participants in sidebar

@@ -510,8 +510,7 @@
       machineList.selectMachineAndWorkspace machine.uid, workspace.slug

-  # TODO: Rename this method to fetchEnvironment - acet!
-  fetchMachines: (callback) ->
+  fetchEnvironmentData: (callback) ->

     environmentDataProvider.fetch (data) => callback data

@@ -657,7 +656,7 @@

   updateMachines: (callback = kd.noop) ->

-    @fetchMachines (data) =>
+    @fetchEnvironmentData (data) =>
       @ownMachinesList.updateList data.own
       @sharedMachinesList.updateList data.shared.concat data.collaboration


Templates: DigitalOcean use optional values as userInputs

--- client/app/lib/providers/templates/digitalocean.coffee
+++ client/app/lib/providers/templates/digitalocean.coffee
@@ -13,9 +13,9 @@
     "digitalocean_droplet": {
       "do-instance": {
         "name": "koding-${var.koding_group_slug}-${var.koding_stack_id}-${count.index+1}",
-        "size": "512mb",
-        "region": "nyc2",
-        "image": "ubuntu-14-04-x64",
+        "size": "${var.userInput_size}",
+        "region": "${var.userInput_region}",
+        "image": "${var.userInput_image}",
         "user_data": "\\necho \\\"hello world!\\\" >> /helloworld.txt\\n"
       }
     }
@@ -40,11 +40,11 @@
       # and this is its identifier (required)
       name: 'koding-${var.koding_group_slug}-${var.koding_stack_id}-${count.index+1}'
       # select your instance_type here: eg. 512mb
-      size: 512mb
+      size: '${var.userInput_size}'
       # select your instance zone which must be in provided region: eg. nyc2
-      region: nyc2
+      region: '${var.userInput_region}'
       # base image for your droplet
-      image: ubuntu-14-04-x64
+      image: '${var.userInput_image}'
       # on user_data section we will write bash and configure our VM
       user_data: |-
         # let's create a file on your root folder:
@@ -57,3 +57,9 @@
         # for more information please use the search box above

     '''
+
+  # Defaults for userInputs
+  user_inputs:
+    size: '512mb'
+    image: 'ubuntu-14-04-x64'
+    region: 'nyc2'


Improved Handlebars logging

Improved Handlebars logging

--- SingularityUI/app/thirdPartyConfigurations.coffee
+++ SingularityUI/app/thirdPartyConfigurations.coffee
@@ -49,4 +49,6 @@
     debug: false

 # Overwrite Handlebars logging
-Handlebars.log = (stuff) => console.log stuff
+Handlebars.logger.log = (stuff...) =>
+    for thing in stuff[1..]
+        console.log thing


Refresh apps after a success install

Refresh apps after a success install

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -421,11 +421,12 @@
                 log "kite response", err, res
                 if err then warn err
                 else
-                  app.install (err)->
+                  app.install (err)=>
                     log err if err
                     log callback
                     # This doesnt work :#
                     appManager.openApplication "StartTab"
+                    @refreshApps()
                     # callback?()

   # #


Put apps to appstorages when its really needed.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -54,7 +54,8 @@

     @watcher.on "ManifestHasChanged", (app, change)=>
       @fetchAppFromFs app, =>
-        @emit "UpdateAppData", app
+        @putAppsToAppStorage null, =>
+          @emit "UpdateAppData", app

   getAppPath:(manifest, escaped=no)->

@@ -107,7 +108,7 @@
         warn "Manifest file is broken:", e
         return cb null

-      @putAppsToAppStorage manifests
+      @constructor.manifests = manifests
       cb null

   fetchAppsFromFsHelper:(apps, callback)->
@@ -131,6 +132,7 @@
       else
         apps = @filterAppsFromFileList files
         @fetchAppsFromFsHelper apps, (result)=>
+          @putAppsToAppStorage()
           for callback in @_fetchQueue
             callback null, @getManifests()
           @_fetchQueue = []


cleaning and fixing

cleaning and fixing

--- src/opensong.coffee
+++ src/opensong.coffee
@@ -1,9 +1,9 @@
-#
-#  Project: opensong.js
-#  Description: displays OpenSong files nicely on a web page
-#  Author: Andreas Boehrnsen
-#  License: LGPL 2.1
-#
+###
+ Project: opensong.js
+ Description: displays OpenSong files nicely on a web page
+ Author: Andreas Boehrnsen
+ License: LGPL 2.1
+###

 # Reference jQuery
 $ = jQuery
@@ -38,6 +38,9 @@

   while lyricsLines.length > 0
     line = lyricsLines.shift()
+
+    continue unless line?
+
     switch line[0]
       when "["
         header = line.match(/\[(.*)\]/)[1]
@@ -88,7 +91,7 @@
           htmlTableRows = htmlTableRows + "
" + textLineNr + "	" + textArr.join("	") + "
\n"

         # attach the line again in front (we cut it off in the while loop)
-        lyricsLines.unshift textLine if textLine isnt `undefined`
+        lyricsLines.unshift textLine if textLine isnt 'undefined'
         $(domElem).append "
" + htmlTableRows + "
"
       when " "
         $(domElem).append "
" + line.substr(1) + "
"


Node Modules Koding: replace dash with async.parallel

--- node_modules_koding/bongo-client/src/cacheable.coffee
+++ node_modules_koding/bongo-client/src/cacheable.coffee
@@ -1,7 +1,7 @@
 'use strict'

 ModelLoader = require './modelloader'
-{dash} = require 'sinkrow'
+async       = require 'async'

 module.exports = ->
   switch arguments.length
@@ -49,12 +49,12 @@
 handleBatch =(batch, callback)->
   return handleByName.call @, batch, callback  if 'string' is typeof batch
   models = []
-  queue = batch.map (single, i)=>=>
+  queue = batch.map (single, i) => (fin) =>
     {name, type, constructorName, id} = single
     handleSingle.call @, type or name or constructorName, id, (err, model)->
       if err then callback err
       else
         models[i] = model
-        queue.fin()
-  dash queue, -> callback null, models
+        fin()
+  async.parallel queue, -> callback null, models
   return


Fixing modal positioning on modal service

--- app/coffee/plugins/modal.coffee
+++ app/coffee/plugins/modal.coffee
@@ -22,7 +22,7 @@

     service.open = (name, ctx) ->
         dom = angular.element("##{modals[name]}")
-        $('body').scrollTop(0)
+        $(dom.find('.modal')).css('top': $('body').scrollTop() + 15)

         defered = $q.defer()


fix branches after api cleanup

fix branches after api cleanup

--- app/controllers/branches.coffee
+++ app/controllers/branches.coffee
@@ -7,15 +7,16 @@
   defaultBranch: (->
     repos = @get('model')
     output = repos.filter (item, index) ->
-      item if item.repository.default_branch['@href'] == item['@href']
-    output[0]
+      item if item.default_branch == true
+    if output.length
+      output[0]
   ).property('model')

   activeBranches: (->
     repos = @get('model')
     repos = repos.filter (item, index) ->
-      item if item.exists_on_github == true && item.repository.default_branch['@href'] != item['@href']
-    .sortBy('last_build.finished_at')
+      item if item.exists_on_github == true && item.default_branch == false
+    .sortBy('default_branch.last_build.finished_at')
     .reverse()
   ).property('model')


Add testing code to disable dialoges

Add testing code to disable dialoges

--- app/models/DialogManager.coffee
+++ app/models/DialogManager.coffee
@@ -45,6 +45,10 @@

   showDialog: (data, callback) =>
+    # if debug
+    #   callback?()
+    #   return
+
     if @isDialog()
       console.error "I'm already showing a dialog, so you must be doing something wrong!"
       @hideDialog()


Manual clone

Manual clone

--- src/prisoner.coffee
+++ src/prisoner.coffee
@@ -106,6 +106,25 @@

         true

+      clone: ->
+        cloned_game = new Game
+          width: @width
+          height: @height
+          prisoner: new Piece({
+            x: @pieces[0].begin.x, y: @pieces[0].begin.y
+          }, {
+            x: @pieces[0].end.x, y: @pieces[0].end.y
+          })
+
+        for i in [1...@pieces.length]
+          cloned_game.addPiece new Piece({
+            x: @pieces[i].begin.x, y: @pieces[i].begin.y
+          }, {
+            x: @pieces[i].end.x, y: @pieces[i].end.y
+          })
+
+        cloned_game
+
   arrayToGame = (array) ->
     height = array.length
     width  = array[0].length
@@ -153,11 +172,8 @@
         checked.push json_game

         for i in [0...game.pieces.length]
-          pos_game = JSON.parse json_game
-          pos_game.__proto__ = game.__proto__
-
-          neg_game = JSON.parse json_game
-          neg_game.__proto__ = game.__proto__
+          pos_game = game.clone()
+          neg_game = game.clone()

           if pos_game.move(i, true)
             queue.push pos_game


Add thresholds to scoreTypes property in levels

scoreTypes items may be objects which include a threshold
property

--- app/schemas/models/level.coffee
+++ app/schemas/models/level.coffee
@@ -323,7 +323,28 @@
   }}
   campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden'  # Automatically set by campaign editor.
   campaignIndex: c.int title: 'Campaign Index', description: 'The 0-based index of this level in its campaign.', format: 'hidden'  # Automatically set by campaign editor.
-  scoreTypes: c.array {title: 'Score Types', description: 'What metric to show leaderboards for. Most important one first, not too many (2 is good).', uniqueItems: true}, c.scoreType
+  scoreTypes: c.array {title: 'Score Types', description: 'What metric to show leaderboards for. Most important one first, not too many (2 is good).'}, {
+      anyOf: [
+        c.scoreType,
+        {
+          type: 'object'
+          title: 'Score Type Object'
+          required: ['type']
+          additionalProperties: false
+          properties: {
+            type: c.scoreType
+            thresholds: {
+              type: 'object'
+              properties: {
+                bronze: { type: 'number' }
+                silver: { type: 'number' }
+                gold: { type: 'number' }
+              }
+            }
+          }
+        }
+      ]
+    }
   concepts: c.array {title: 'Programming Concepts', description: 'Which programming concepts this level covers.', uniqueItems: true, format: 'concepts-list'}, c.concept
   primaryConcepts: c.array {title: 'Primary Concepts', description: 'The main 1-3 concepts this level focuses on.', uniqueItems: true}, c.concept
   picoCTFProblem: { type: 'string', description: 'Associated picoCTF problem ID, if this is a picoCTF level' }


Clean up WindowExtensions.before a bit

Clean up WindowExtensions.before a bit

--- lib/Spec/WindowExtensions.coffee
+++ lib/Spec/WindowExtensions.coffee
@@ -52,8 +52,7 @@

   # Adds a setup step to the current test case
   before: (action) ->
-    test = Spec.testStack[Spec.testStack.length - 1]
-    test.before.push action
+    Spec.currentTest().before.push action

   # Tests if matched value is boolean true
   beTrue: (value) ->


tests minor fixes

tests minor fixes

--- tests/test_loader_wd.coffee
+++ tests/test_loader_wd.coffee
@@ -31,7 +31,7 @@
         return @_items.slice()

 describe "WD.js", ->
-    @timeout(10000)
+    @timeout(15000)

     DELAY = 200

@@ -110,6 +110,7 @@
                 mock(system)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(DELAY)
@@ -165,6 +166,7 @@
                 mock(system)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .setLocalStorageKey("test_item", "should_not be removed")
             .get('http://127.0.0.1:3332/app/')
@@ -227,6 +229,7 @@
                 mock(system)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .then => @urls_log.clear()
             .get('http://127.0.0.1:3332/app/')
@@ -342,6 +345,7 @@
                 spa.Builder.from_config("/app/spa.yaml").build()
                 fs.unlinkSync("/app/manifest.json")
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(3*DELAY)
@@ -379,6 +383,7 @@
                 mock(system)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(3*DELAY)
@@ -416,6 +421,7 @@
                 mock(system)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(3*DELAY)
@@ -473,6 +479,7 @@
                 fs.writeFileSync("/app/a.js", content)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(5*DELAY)
@@ -513,6 +520,7 @@
                 expect(@manifest).to.have.property("version")
                 expect(@manifest.version).to.be.a("String")
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(3*DELAY)
@@ -568,6 +576,7 @@
                         """)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
+            .sleep(DELAY)
             .clearLocalStorage()
             .get('http://127.0.0.1:3332/app/')
             .sleep(20*DELAY)
@@ -607,7 +616,6 @@
                 for i in [1..13]
                     q = "(#{q} + #{q})"

-                console.log(q.length)
                 NUM = 10

                 for i in [1..10]
@@ -631,9 +639,10 @@
                         """)
                 spa.Builder.from_config("/app/spa.yaml").build()
             .get('http://127.0.0.1:3332/')
-            .clearLocalStorage()
-            .get('http://127.0.0.1:3332/app/')
-            .sleep(20*DELAY)
+            .sleep(DELAY)
+            .clearLocalStorage()
+            .get('http://127.0.0.1:3332/app/')
+            .sleep(30*DELAY)
             .title()
             .then (title) =>
                 expect(title).to.equal("15730830")


:racehorse: Don’t emit marker events for folds inside other folds

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -168,8 +168,7 @@
         oldExtent: Point(oldRowExtent, 0),
         newExtent: Point(newRowExtent, 0)
       }])
-
-    @notifyObserversIfMarkerScreenPositionsChanged()
+      @notifyObserversIfMarkerScreenPositionsChanged()

     foldId


Add DisplayLayer.prototype.foldsIntersectingBufferRange

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -45,6 +45,9 @@
       @emitter.emit 'did-change-text-sync', {start, replacedExtent, replacementExtent}

     foldId
+
+  foldsIntersectingBufferRange: (bufferRange) ->
+    @foldsMarkerLayer.findMarkers(intersectsRange: bufferRange).map ({id}) -> id

   destroyFold: (foldId) ->
     if foldMarker = @foldsMarkerLayer.getMarker(foldId)


Documentation! :book:

Documentation! :book:

--- lib/formats/jsonschema-v4.coffee
+++ lib/formats/jsonschema-v4.coffee
@@ -3,6 +3,7 @@
 {resolveType} = require '../typeresolution'

+# Takes object type node and lists its property nodes.
 listProperties = (objectType, cb) ->
   props = []
   for member in (objectType.sections or []) when member.type is 'member'
@@ -11,6 +12,9 @@
   cb null, props

+# Turns property node into a 'resolved property' object with both
+# representation in JSON Schema and also additional info, such as property
+# name, attributes, etc.
 resolveProperty = (prop, options, cb) ->
   async.waterfall [
     (next) -> handleType prop.content, options, next
@@ -22,12 +26,17 @@
   ], cb

+# Turns multiple property nodes into 'resolved properties', i.e. objects
+# carrying both representations of those properties in JSON Schema
+# and also additional info, such as property names, attributes, etc.
 resolveProperties = (props, options, cb) ->
   async.map props, (prop, next) ->
     resolveProperty prop, options, next
   , cb

+# Takes 'resolved properties' and generates JSON Schema
+# for their wrapper object type node.
 buildObjectSchema = (resolvedProps, options, cb) ->
   schemaProps = {}
   schemaRequired = []
@@ -44,6 +53,7 @@
   cb null, schema

+# Generates JSON Schema representation for given object type node.
 handleObject = (objectType, simpleTypeSpec, options, cb) ->
   async.waterfall [
     (next) -> listProperties objectType, next
@@ -52,14 +62,18 @@
   ], cb

+# Generates JSON Schema representation for given array type node.
 handleArray = (arrayType, simpleTypeSpec, options, cb) ->
   cb null, type: 'array'

+# Generates JSON Schema representation for given primitive
+# type node (string, number, etc.).
 handlePrimitiveType = (primitiveType, simpleTypeSpec, options, cb) ->
   cb null, type: simpleTypeSpec.name

+# Generates JSON Schema representation for given type node.
 handleType = (type, options, cb) ->
   resolveType type, (err, simpleTypeSpec) ->
     return cb err if err
@@ -72,11 +86,13 @@
         handlePrimitiveType type, simpleTypeSpec, options, cb

+# Adds JSON Schema declaration to given schema object.
 addSchemaDeclaration = (schema, cb) ->
   schema["$schema"] = "http://json-schema.org/draft-04/schema#"
   cb null, schema

+# Transforms given MSON AST into JSON Schema.
 transform = (ast, options, cb) ->
   async.waterfall [
     (next) -> handleType ast, options, next


Add `markdown-changed` emit event

Add `markdown-changed` emit event

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -101,6 +101,7 @@
         @showError(error)
       else
         @html(@tokenizeCodeBlocks(@resolveImagePaths(html)))
+        @emit('markdown-changed')

   getTitle: ->
     if @file?


Add resizing to GridView.

Add resizing to GridView.

--- src/grid/GridView.coffee
+++ src/grid/GridView.coffee
@@ -114,10 +114,12 @@
       shadow.style.left = @root.style.left
       @parentEl.insertBefore shadow, @root

-    positionShadow = (id, row, col) ->
+    positionShadow = (id, row, col, height, width) ->
       [row, col] = @parent._packer.constrain id, row, col
       shadow.style.top = "#{row * SIZE}px"
       shadow.style.left = "#{col * SIZE}px"
+      if height then shadow.style.height = "#{height * SIZE}px"
+      if width then shadow.style.width = "#{width * SIZE}px"

     # (Called with GridItemView as "this")
     deleteShadow = ->
@@ -155,7 +157,43 @@
           item.root.style.top = "#{pos.row * SIZE}px"
           item.root.style.left = "#{pos.col * SIZE}px"

-
+    onResize = (e) ->
+      {x, y, width, height} = e.data
+
+      width = Math.round(width / SIZE)
+      height = Math.round(height / SIZE)
+
+      left = parseInt(@root.style.left)
+      top = parseInt(@root.style.top)
+      leftGridPos = Math.round(left / SIZE)
+      topGridPos = Math.round(top / SIZE)
+
+      shifted = @parent._packer.moveTo @id, topGridPos, leftGridPos, height, width
+
+      if shadow
+        positionShadow.call(@, @id, topGridPos, leftGridPos, height, width)
+
+      setGridSize.call @parent
+
+      @parent.context.items.forEach (item) =>
+        if item.id is @id then return
+        if shifted.hasOwnProperty item.id
+          pos = shifted[item.id]
+          item.root.style.top = "#{pos.row * SIZE}px"
+          item.root.style.left = "#{pos.col * SIZE}px"
+
+      shifted
+
+    itemView.on 'resize', throttle(onResize, 100, trailing: false)
+    itemView.on 'resizeend', (e) ->
+      shifted = onResize.call @, e
+      @parent.context.items.forEach (item) ->
+        if shifted.hasOwnProperty item.id
+          pos = shifted[item.id]
+          item.root.style.top = "#{pos.row * SIZE}px"
+          item.root.style.left = "#{pos.col * SIZE}px"
+          item.root.style.width = "#{pos.width * SIZE}px"
+          item.root.style.height = "#{pos.height * SIZE}px"


[Experiment 3] Bind context emitter

[Experiment 3] Bind context emitter

--- src/desktop/components/split_test/middleware.coffee
+++ src/desktop/components/split_test/middleware.coffee
@@ -1,6 +1,7 @@
 SplitTest = require './server_split_test.coffee'
 runningTests = require './running_tests'
 qs = require 'qs'
+httpContext = require 'express-http-context'
 { setSplitTest } = require './splitTestContext'

 module.exports = (req, res, next) ->
@@ -20,7 +21,12 @@
   if runningTests['client_navigation_v3']
     res.locals.sd['EXPERIMENTAL_APP_SHELL'] = Boolean(res.locals.sd['CLIENT_NAVIGATION_V3'] is 'experiment')

+
+  # See https://github.com/skonves/express-http-context/issues/13
+  httpContext.ns.bindEmitter(req)
+  httpContext.ns.bindEmitter(res)
+
   # Store value in globally available location.
-  setSplitTest('EXPERIMENTAL_APP_SHELL', res.locals.sd['EXPERIMENTAL_APP_SHELL'])
+  httpContext.set('EXPERIMENTAL_APP_SHELL', res.locals.sd['EXPERIMENTAL_APP_SHELL'])

   next()


fix a typo

fix a typo

--- library/exposure/navigate.coffee
+++ library/exposure/navigate.coffee
@@ -105,4 +105,4 @@
             return unless value?.meta?.endpoint or 0
             assert endpoint = try value.meta.endpoint
             logger.debug "mount endpoint #{endpoint}"
-            crossroads.addRoute "#{endpoint}", value
+            crossroads.addRoute endpoint, value