//
// showdown.js -- A javascript port of Markdown.
//
// Copyright (c) 2007 John Fraser.
//
// Original Markdown Copyright (c) 2004-2005 John Gruber
//   <http://daringfireball.net/projects/markdown/>
//
// Redistributable under a BSD-style open source license.
// See license.txt for more information.
//
// The full source distribution is at:
//
//				A A L
//				T C A
//				T K B
//
//   <http://www.attacklab.net/>
//

//
// Wherever possible, Showdown is a straight, line-by-line port
// of the Perl version of Markdown.
//
// This is not a normal parser design; it's basically just a
// series of string substitutions.  It's hard to read and
// maintain this way,  but keeping Showdown close to the original
// design makes it easier to port new features.
//
// More importantly, Showdown behaves like markdown.pl in most
// edge cases.  So web applications can do client-side preview
// in Javascript, and then build identical HTML on the server.
//
// This port needs the new RegExp functionality of ECMA 262,
// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
// should do fine.  Even with the new regular expression features,
// We do a lot of work to emulate Perl's regex functionality.
// The tricky changes in this file mostly have the "attacklab:"
// label.  Major or self-explanatory changes don't.
//
// Smart diff tools like Araxis Merge will be able to match up
// this file with markdown.pl in a useful way.  A little tweaking
// helps: in a copy of markdown.pl, replace "#" with "//" and
// replace "$text" with "text".  Be sure to ignore whitespace
// and line endings.
//

//
// Showdown usage:
//
//   var text = "Markdown *rocks*.";
//
//   var converter = new Showdown.converter();
//   var html = converter.makeHtml(text);
//
//   alert(html);
//
// Note: move the sample code to the bottom of this
// file before uncommenting it.
//

//
// Showdown namespace
//
// METEOR CHANGE: remove "var" so that this isn't file-local.
Showdown = { extensions: {} };

//
// forEach
//
var forEach = Showdown.forEach = function(obj, callback) {
	if (typeof obj.forEach === 'function') {
		obj.forEach(callback);
	} else {
		var i, len = obj.length;
		for (i = 0; i < len; i++) {
			callback(obj[i], i, obj);
		}
	}
};

//
// Standard extension naming
//
var stdExtName = function(s) {
	return s.replace(/[_-]||\s/g, '').toLowerCase();
};

//
// converter
//
// Wraps all "globals" so that the only thing
// exposed is makeHtml().
//
Showdown.converter = function(converter_options) {

//
// Globals:
//

// Global hashes, used by various utility routines
var g_urls;
var g_titles;
var g_html_blocks;

// Used to track when we're inside an ordered or unordered list
// (see _ProcessListItems() for details):
var g_list_level = 0;

// Global extensions
var g_lang_extensions = [];
var g_output_modifiers = [];

//
// Automatic Extension Loading (node only):
//

if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof require !== 'undefind') {
	var fs = require('fs');

	if (fs) {
		// Search extensions folder
		var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){
			return ~file.indexOf('.js');
		}).map(function(file){
			return file.replace(/\.js$/, '');
		});
		// Load extensions into Showdown namespace
		Showdown.forEach(extensions, function(ext){
			var name = stdExtName(ext);
			Showdown.extensions[name] = require('./extensions/' + ext);
		});
	}
}

this.makeHtml = function(text) {
//
// Main function. The order in which other subs are called here is
// essential. Link and image substitutions need to happen before
// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the
// and  tags get encoded.
//

	// Clear the global hashes. If we don't clear these, you get conflicts
	// from other articles when generating a page which contains more than
	// one article (e.g. an index page that shows the N most recent
	// articles):
	g_urls = {};
	g_titles = {};
	g_html_blocks = [];

	// attacklab: Replace ~ with ~T
	// This lets us use tilde as an escape char to avoid md5 hashes
	// The choice of character is arbitray; anything that isn't
	// magic in Markdown will work.
	text = text.replace(/~/g,"~T");

	// attacklab: Replace $ with ~D
	// RegExp interprets $ as a special character
	// when it's in a replacement string
	text = text.replace(/\$/g,"~D");

	// Standardize line endings
	text = text.replace(/\r\n/g,"\n"); // DOS to Unix
	text = text.replace(/\r/g,"\n"); // Mac to Unix

	// Make sure text begins and ends with a couple of newlines:
	text = "\n\n" + text + "\n\n";

	// Convert all tabs to spaces.
	text = _Detab(text);

	// Strip any lines consisting only of spaces and tabs.
	// This makes subsequent regexen easier to write, because we can
	// match consecutive blank lines with /\n+/ instead of something
	// contorted like /[ \t]*\n+/ .
	text = text.replace(/^[ \t]+$/mg,"");

	// Run language extensions
	Showdown.forEach(g_lang_extensions, function(x){
		text = _ExecuteExtension(x, text);
	});

	// Handle github codeblocks prior to running HashHTML so that
	// HTML contained within the codeblock gets escaped propertly
	text = _DoGithubCodeBlocks(text);

	// Turn block-level HTML blocks into hash entries
	text = _HashHTMLBlocks(text);

	// Strip link definitions, store in hashes.
	text = _StripLinkDefinitions(text);

	text = _RunBlockGamut(text);

	text = _UnescapeSpecialChars(text);

	// attacklab: Restore dollar signs
	text = text.replace(/~D/g,"$$");

	// attacklab: Restore tildes
	text = text.replace(/~T/g,"~");

	// Run output modifiers
	Showdown.forEach(g_output_modifiers, function(x){
		text = _ExecuteExtension(x, text);
	});

	return text;
};
//
// Options:
//

// Parse extensions options into separate arrays
if (converter_options && converter_options.extensions) {

  var self = this;

	// Iterate over each plugin
	Showdown.forEach(converter_options.extensions, function(plugin){

		// Assume it's a bundled plugin if a string is given
		if (typeof plugin === 'string') {
			plugin = Showdown.extensions[stdExtName(plugin)];
		}

		if (typeof plugin === 'function') {
			// Iterate over each extension within that plugin
			Showdown.forEach(plugin(self), function(ext){
				// Sort extensions by type
				if (ext.type) {
					if (ext.type === 'language' || ext.type === 'lang') {
						g_lang_extensions.push(ext);
					} else if (ext.type === 'output' || ext.type === 'html') {
						g_output_modifiers.push(ext);
					}
				} else {
					// Assume language extension
					g_output_modifiers.push(ext);
				}
			});
		} else {
			throw "Extension '" + plugin + "' could not be loaded.  It was either not found or is not a valid extension.";
		}
	});
}

var _ExecuteExtension = function(ext, text) {
	if (ext.regex) {
		var re = new RegExp(ext.regex, 'g');
		return text.replace(re, ext.replace);
	} else if (ext.filter) {
		return ext.filter(text);
	}
};

var _StripLinkDefinitions = function(text) {
//
// Strips link definitions from text, stores the URLs and titles in
// hash references.
//

	// Link defs are in the form: ^[id]: url "optional title"

	/*
		var text = text.replace(/
				^[ ]{0,3}\[(.+)\]:  // id = $1  attacklab: g_tab_width - 1
				  [ \t]*
				  \n?				// maybe *one* newline
				  [ \t]*
				<?(\S+?)>?			// url = $2
				  [ \t]*
				  \n?				// maybe one newline
				  [ \t]*
				(?:
				  (\n*)				// any lines skipped = $3 attacklab: lookbehind removed
				  ["(]
				  (.+?)				// title = $4
				  [")]
				  [ \t]*
				)?					// title is optional
				(?:\n+|$)
			  /gm,
			  function(){...});
	*/

	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
	text += "~0";

	text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm,
		function (wholeMatch,m1,m2,m3,m4) {
			m1 = m1.toLowerCase();
			g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive
			if (m3) {
				// Oops, found blank lines, so it's not a title.
				// Put back the parenthetical statement we stole.
				return m3+m4;
			} else if (m4) {
				g_titles[m1] = m4.replace(/"/g,""");
			}

			// Completely remove the definition from the text
			return "";
		}
	);

	// attacklab: strip sentinel
	text = text.replace(/~0/,"");

	return text;
}

var _HashHTMLBlocks = function(text) {
	// attacklab: Double up blank lines to reduce lookaround
	text = text.replace(/\n/g,"\n\n");

	// Hashify HTML blocks:
	// We only want to do this for block-level HTML tags, such as headers,
	// lists, and tables. That's because we still want to wrap
s around
	// "paragraphs" that are wrapped in non-block-level tags, such as anchors,
	// phrase emphasis, and spans. The list of tags we're looking for is
	// hard-coded:
	var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside";
	var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside";

	// First, look for nested blocks, e.g.:
	//

	//

	//     tags for inner block must be indented.
	//

	//

	//
	// The outermost tags must start at the left margin for this to match, and
	// the inner nested divs must be indented.
	// We need to do this before the next, more liberal match, because the next
	// match will start at the first `<div>` and stop at the first `</div>`.

	// attacklab: This regex can be expensive when it fails.
	/*
		var text = text.replace(/
		(						// save in $1
			^					// start of line  (with /m)
			<($block_tags_a)	// start tag = $2
			\b					// word break
								// attacklab: hack around khtml/pcre bug...
			[^\r]*?\n			// any number of lines, minimally matching
			</\2>				// the matching end tag
			[ \t]*				// trailing spaces/tabs
			(?=\n+)				// followed by a newline
		)						// attacklab: there are sentinel newlines at end of document
		/gm,function(){...}};
	*/
	text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);

	//
	// Now match more liberally, simply from `\n<tag>` to `</tag>\n`
	//

	/*
		var text = text.replace(/
		(						// save in $1
			^					// start of line  (with /m)
			<($block_tags_b)	// start tag = $2
			\b					// word break
								// attacklab: hack around khtml/pcre bug...
			[^\r]*?				// any number of lines, minimally matching
			</\2>				// the matching end tag
			[ \t]*				// trailing spaces/tabs
			(?=\n+)				// followed by a newline
		)						// attacklab: there are sentinel newlines at end of document
		/gm,function(){...}};
	*/
	text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);

	// Special case just for
. It was easier to make a special case than
	// to make the other regex more complicated.

	/*
		text = text.replace(/
		(						// save in $1
			\n\n				// Starting after a blank line
			[ ]{0,3}
			(<(hr)				// start tag = $2
			\b					// word break
			([^<>])*?			//
			\/?>)				// the matching end tag
			[ \t]*
			(?=\n{2,})			// followed by a blank line
		)
		/g,hashElement);
	*/
	text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);

	// Special case for standalone HTML comments:

	/*
		text = text.replace(/
		(						// save in $1
			\n\n				// Starting after a blank line
			[ ]{0,3}			// attacklab: g_tab_width - 1
			<!
			(--[^\r]*?--\s*)+
			>
			[ \t]*
			(?=\n{2,})			// followed by a blank line
		)
		/g,hashElement);
	*/
	text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);

	// PHP and ASP-style processor instructions (<?...?> and <%...%>)

	/*
		text = text.replace(/
		(?:
			\n\n				// Starting after a blank line
		)
		(						// save in $1
			[ ]{0,3}			// attacklab: g_tab_width - 1
			(?:
				<([?%])			// $2
				[^\r]*?
				\2>
			)
			[ \t]*
			(?=\n{2,})			// followed by a blank line
		)
		/g,hashElement);
	*/
	text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);

	// attacklab: Undo double lines (see comment at top of this function)
	text = text.replace(/\n\n/g,"\n");
	return text;
}

var hashElement = function(wholeMatch,m1) {
	var blockText = m1;

	// Undo double lines
	blockText = blockText.replace(/\n\n/g,"\n");
	blockText = blockText.replace(/^\n/,"");

	// strip trailing blank lines
	blockText = blockText.replace(/\n+$/g,"");

	// Replace the element text with a marker ("~KxK" where x is its key)
	blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";

	return blockText;
};

var _RunBlockGamut = function(text) {
//
// These are all the transformations that form block-level
// tags like paragraphs, headers, and list items.
//
	text = _DoHeaders(text);

	// Do Horizontal Rules:
	var key = hashBlock("
");
	text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
	text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
	text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);

	text = _DoLists(text);
	text = _DoCodeBlocks(text);
	text = _DoBlockQuotes(text);

	// We already ran _HashHTMLBlocks() before, in Markdown(), but that
	// was to escape raw HTML in the original Markdown source. This time,
	// we're escaping the markup we've just created, so that we don't wrap
	//
 tags around block-level tags.
	text = _HashHTMLBlocks(text);
	text = _FormParagraphs(text);

	return text;
};

var _RunSpanGamut = function(text) {
//
// These are all the transformations that occur *within* block-level
// tags like paragraphs, headers, and list items.
//

	text = _DoCodeSpans(text);
	text = _EscapeSpecialCharsWithinTagAttributes(text);
	text = _EncodeBackslashEscapes(text);

	// Process anchor and image tags. Images must come first,
	// because ![foo][f] looks like an anchor.
	text = _DoImages(text);
	text = _DoAnchors(text);

	// Make links out of things like `<http://example.com/>`
	// Must come after _DoAnchors(), because you can use < and >
	// delimiters in inline links like [this](<url>).
	text = _DoAutoLinks(text);
	text = _EncodeAmpsAndAngles(text);
	text = _DoItalicsAndBold(text);

	// Do hard breaks:
	text = text.replace(/  +\n/g,"
\n");

	return text;
}

var _EscapeSpecialCharsWithinTagAttributes = function(text) {
//
// Within tags -- meaning between < and > -- encode [\ ` * _] so they
// don't conflict with their use in Markdown for code, italics and strong.
//

	// Build a regex to find HTML tags and comments.  See Friedl's
	// "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
	var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;

	text = text.replace(regex, function(wholeMatch) {
		var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
		tag = escapeCharacters(tag,"\\`*_");
		return tag;
	});

	return text;
}

var _DoAnchors = function(text) {
//
// Turn Markdown link shortcuts into XHTML  tags.
//
	//
	// First, handle reference-style links: [link text] [id]
	//

	/*
		text = text.replace(/
		(							// wrap whole match in $1
			\[
			(
				(?:
					\[[^\]]*\]		// allow brackets nested one level
					|
					[^\[]			// or anything else
				)*
			)
			\]

			[ ]?					// one optional space
			(?:\n[ ]*)?				// one optional newline followed by spaces

			\[
			(.*?)					// id = $3
			\]
		)()()()()					// pad remaining backreferences
		/g,_DoAnchors_callback);
	*/
	text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);

	//
	// Next, inline-style links: [link text](url "optional title")
	//

	/*
		text = text.replace(/
			(						// wrap whole match in $1
				\[
				(
					(?:
						\[[^\]]*\]	// allow brackets nested one level
					|
					[^\[\]]			// or anything else
				)
			)
			\]
			\(						// literal paren
			[ \t]*
			()						// no id, so leave $3 empty
			<?(.*?)>?				// href = $4
			[ \t]*
			(						// $5
				(['"])				// quote char = $6
				(.*?)				// Title = $7
				\6					// matching quote
				[ \t]*				// ignore any spaces/tabs between closing quote and )
			)?						// title is optional
			\)
		)
		/g,writeAnchorTag);
	*/
	text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);

	//
	// Last, handle reference-style shortcuts: [link text]
	// These must come last in case you've also got [link test][1]
	// or [link test](/foo)
	//

	/*
		text = text.replace(/
		(		 					// wrap whole match in $1
			\[
			([^\[\]]+)				// link text = $2; can't contain '[' or ']'
			\]
		)()()()()()					// pad rest of backreferences
		/g, writeAnchorTag);
	*/
	text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);

	return text;
}

var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
	if (m7 == undefined) m7 = "";
	var whole_match = m1;
	var link_text   = m2;
	var link_id	 = m3.toLowerCase();
	var url		= m4;
	var title	= m7;

	if (url == "") {
		if (link_id == "") {
			// lower-case and turn embedded newlines into spaces
			link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
		}
		url = "#"+link_id;

		if (g_urls[link_id] != undefined) {
			url = g_urls[link_id];
			if (g_titles[link_id] != undefined) {
				title = g_titles[link_id];
			}
		}
		else {
			if (whole_match.search(/\(\s*\)$/m)>-1) {
				// Special case for explicit empty url
				url = "";
			} else {
				return whole_match;
			}
		}
	}

	url = escapeCharacters(url,"*_");
	var result = "" + link_text + "";

	return result;
}

var _DoImages = function(text) {
//
// Turn Markdown image shortcuts into  tags.
//

	//
	// First, handle reference-style labeled images: ![alt text][id]
	//

	/*
		text = text.replace(/
		(						// wrap whole match in $1
			!\[
			(.*?)				// alt text = $2
			\]

			[ ]?				// one optional space
			(?:\n[ ]*)?			// one optional newline followed by spaces

			\[
			(.*?)				// id = $3
			\]
		)()()()()				// pad rest of backreferences
		/g,writeImageTag);
	*/
	text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);

	//
	// Next, handle inline images:  ![alt text](url "optional title")
	// Don't forget: encode * and _

	/*
		text = text.replace(/
		(						// wrap whole match in $1
			!\[
			(.*?)				// alt text = $2
			\]
			\s?					// One optional whitespace character
			\(					// literal paren
			[ \t]*
			()					// no id, so leave $3 empty
			<?(\S+?)>?			// src url = $4
			[ \t]*
			(					// $5
				(['"])			// quote char = $6
				(.*?)			// title = $7
				\6				// matching quote
				[ \t]*
			)?					// title is optional
		\)
		)
		/g,writeImageTag);
	*/
	text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);

	return text;
}

var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
	var whole_match = m1;
	var alt_text   = m2;
	var link_id	 = m3.toLowerCase();
	var url		= m4;
	var title	= m7;

	if (!title) title = "";

	if (url == "") {
		if (link_id == "") {
			// lower-case and turn embedded newlines into spaces
			link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
		}
		url = "#"+link_id;

		if (g_urls[link_id] != undefined) {
			url = g_urls[link_id];
			if (g_titles[link_id] != undefined) {
				title = g_titles[link_id];
			}
		}
		else {
			return whole_match;
		}
	}

	alt_text = alt_text.replace(/"/g,""");
	url = escapeCharacters(url,"*_");
	var result = "";

	return result;
}

var _DoHeaders = function(text) {

	// Setext-style headers:
	//	Header 1
	//	========
	//
	//	Header 2
	//	--------
	//
	text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
		function(wholeMatch,m1){return hashBlock('
' + _RunSpanGamut(m1) + "
");});

	text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
		function(matchFound,m1){return hashBlock('
' + _RunSpanGamut(m1) + "
");});

	// atx-style headers:
	//  # Header 1
	//  ## Header 2
	//  ## Header 2 with closing hashes ##
	//  ...
	//  ###### Header 6
	//

	/*
		text = text.replace(/
			^(\#{1,6})				// $1 = string of #'s
			[ \t]*
			(.+?)					// $2 = Header text
			[ \t]*
			\#*						// optional closing #'s (not counted)
			\n+
		/gm, function() {...});
	*/

	text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
		function(wholeMatch,m1,m2) {
			var h_level = m1.length;
			return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
		});

	function headerId(m) {
		return m.replace(/[^\w]/g, '').toLowerCase();
	}
	return text;
}

// This declaration keeps Dojo compressor from outputting garbage:
var _ProcessListItems;

var _DoLists = function(text) {
//
// Form HTML ordered (numbered) and unordered (bulleted) lists.
//

	// attacklab: add sentinel to hack around khtml/safari bug:
	// http://bugs.webkit.org/show_bug.cgi?id=11231
	text += "~0";

	// Re-usable pattern to match any entirel ul or ol list:

	/*
		var whole_list = /
		(									// $1 = whole list
			(								// $2
				[ ]{0,3}					// attacklab: g_tab_width - 1
				([*+-]|\d+[.])				// $3 = first list item marker
				[ \t]+
			)
			[^\r]+?
			(								// $4
				~0							// sentinel for workaround; should be $
			|
				\n{2,}
				(?=\S)
				(?!							// Negative lookahead for another list item marker
					[ \t]*
					(?:[*+-]|\d+[.])[ \t]+
				)
			)
		)/g
	*/
	var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;

	if (g_list_level) {
		text = text.replace(whole_list,function(wholeMatch,m1,m2) {
			var list = m1;
			var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";

			// Turn double returns into triple returns, so that we can make a
			// paragraph for the last item in a list, if necessary:
			list = list.replace(/\n{2,}/g,"\n\n\n");;
			var result = _ProcessListItems(list);

			// Trim any trailing whitespace, to put the closing `</$list_type>`
			// up on the preceding line, to get it past the current stupid
			// HTML block parser. This is a hack to work around the terrible
			// hack that is the HTML block parser.
			result = result.replace(/\s+$/,"");
			result = "<"+list_type+">" + result + "</"+list_type+">\n";
			return result;
		});
	} else {
		whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
		text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
			var runup = m1;
			var list = m2;

			var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
			// Turn double returns into triple returns, so that we can make a
			// paragraph for the last item in a list, if necessary:
			var list = list.replace(/\n{2,}/g,"\n\n\n");;
			var result = _ProcessListItems(list);
			result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
			return result;
		});
	}

	// attacklab: strip sentinel
	text = text.replace(/~0/,"");

	return text;
}

_ProcessListItems = function(list_str) {
//
//  Process the contents of a single ordered or unordered list, splitting it
//  into individual list items.
//
	// The $g_list_level global keeps track of when we're inside a list.
	// Each time we enter a list, we increment it; when we leave a list,
	// we decrement. If it's zero, we're not in a list anymore.
	//
	// We do this because when we're not inside a list, we want to treat
	// something like this:
	//
	//    I recommend upgrading to version
	//    8. Oops, now this line is treated
	//    as a sub-list.
	//
	// As a single paragraph, despite the fact that the second line starts
	// with a digit-period-space sequence.
	//
	// Whereas when we're inside a list (or sub-list), that line will be
	// treated as the start of a sub-list. What a kludge, huh? This is
	// an aspect of Markdown's syntax that's hard to parse perfectly
	// without resorting to mind-reading. Perhaps the solution is to
	// change the syntax rules such that sub-lists must start with a
	// starting cardinal number; e.g. "1." or "a.".

	g_list_level++;

	// trim trailing blank lines:
	list_str = list_str.replace(/\n{2,}$/,"\n");

	// attacklab: add sentinel to emulate \z
	list_str += "~0";

	/*
		list_str = list_str.replace(/
			(\n)?							// leading line = $1
			(^[ \t]*)						// leading whitespace = $2
			([*+-]|\d+[.]) [ \t]+			// list marker = $3
			([^\r]+?						// list item text   = $4
			(\n{1,2}))
			(?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
		/gm, function(){...});
	*/
	list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
		function(wholeMatch,m1,m2,m3,m4){
			var item = m4;
			var leading_line = m1;
			var leading_space = m2;

			if (leading_line || (item.search(/\n{2,}/)>-1)) {
				item = _RunBlockGamut(_Outdent(item));
			}
			else {
				// Recursion for sub-lists:
				item = _DoLists(_Outdent(item));
				item = item.replace(/\n$/,""); // chomp(item)
				item = _RunSpanGamut(item);
			}

			return  "
" + item + "
\n";
		}
	);

	// attacklab: strip sentinel
	list_str = list_str.replace(/~0/g,"");

	g_list_level--;
	return list_str;
}

var _DoCodeBlocks = function(text) {
//
//  Process Markdown `<pre><code>` blocks.
//

	/*
		text = text.replace(text,
			/(?:\n\n|^)
			(								// $1 = the code block -- one or more lines, starting with a space/tab
				(?:
					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
					.*\n+
				)+
			)
			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
		/g,function(){...});
	*/

	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
	text += "~0";

	text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
		function(wholeMatch,m1,m2) {
			var codeblock = m1;
			var nextChar = m2;

			codeblock = _EncodeCode( _Outdent(codeblock));
			codeblock = _Detab(codeblock);
			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace

			codeblock = "
" + codeblock + "\n
";

			return hashBlock(codeblock) + nextChar;
		}
	);

	// attacklab: strip sentinel
	text = text.replace(/~0/,"");

	return text;
};

var _DoGithubCodeBlocks = function(text) {
//
//  Process Github-style code blocks
//  Example:
//  ```ruby
//  def hello_world(x)
//    puts "Hello, #{x}"
//  end
//  ```
//

	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
	text += "~0";

	text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,
		function(wholeMatch,m1,m2) {
			var language = m1;
			var codeblock = m2;

			codeblock = _EncodeCode(codeblock);
			codeblock = _Detab(codeblock);
			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace

			codeblock = "
" + codeblock + "\n
";

			return hashBlock(codeblock);
		}
	);

	// attacklab: strip sentinel
	text = text.replace(/~0/,"");

	return text;
}

var hashBlock = function(text) {
	text = text.replace(/(^\n+|\n+$)/g,"");
	return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
}

var _DoCodeSpans = function(text) {
//
//   *  Backtick quotes are used for  spans.
//
//   *  You can use multiple backticks as the delimiters if you want to
//	 include literal backticks in the code span. So, this input:
//
//		 Just type ``foo `bar` baz`` at the prompt.
//
//	   Will translate to:
//
//
Just type foo `bar` baz at the prompt.

//
//	There's no arbitrary limit to the number of backticks you
//	can use as delimters. If you need three consecutive backticks
//	in your code, use four for delimiters, etc.
//
//  *  You can use spaces to get literal backticks at the edges:
//
//		 ... type `` `bar` `` ...
//
//	   Turns to:
//
//		 ... type `bar` ...
//

	/*
		text = text.replace(/
			(^|[^\\])					// Character before opening ` can't be a backslash
			(`+)						// $2 = Opening run of `
			(							// $3 = The code block
				[^\r]*?
				[^`]					// attacklab: work around lack of lookbehind
			)
			\2							// Matching closer
			(?!`)
		/gm, function(){...});
	*/

	text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
		function(wholeMatch,m1,m2,m3,m4) {
			var c = m3;
			c = c.replace(/^([ \t]*)/g,"");	// leading whitespace
			c = c.replace(/[ \t]*$/g,"");	// trailing whitespace
			c = _EncodeCode(c);
			return m1+""+c+"";
		});

	return text;
}

var _EncodeCode = function(text) {
//
// Encode/escape certain characters inside Markdown code runs.
// The point is that in code, these characters are literals,
// and lose their special Markdown meanings.
//
	// Encode all ampersands; HTML entities are not
	// entities within a Markdown code span.
	text = text.replace(/&/g,"&");

	// Do the angle bracket song and dance:
	text = text.replace(/</g,"<");
	text = text.replace(/>/g,">");

	// Now, escape characters that are magic in Markdown:
	text = escapeCharacters(text,"\*_{}[]\\",false);

// jj the line above breaks this:
//---

//* Item

//   1. Subitem

//            special char: *
//---

	return text;
}

var _DoItalicsAndBold = function(text) {

	//  must go first:
	text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
		"$2");

	text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
		"$2");

	return text;
}

var _DoBlockQuotes = function(text) {

	/*
		text = text.replace(/
		(								// Wrap whole match in $1
			(
				^[ \t]*>[ \t]?			// '>' at the start of a line
				.+\n					// rest of the first line
				(.+\n)*					// subsequent consecutive lines
				\n*						// blanks
			)+
		)
		/gm, function(){...});
	*/

	text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
		function(wholeMatch,m1) {
			var bq = m1;

			// attacklab: hack around Konqueror 3.5.4 bug:
			// "----------bug".replace(/^-/g,"") == "bug"

			bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0");	// trim one level of quoting

			// attacklab: clean up hack
			bq = bq.replace(/~0/g,"");

			bq = bq.replace(/^[ \t]+$/gm,"");		// trim whitespace-only lines
			bq = _RunBlockGamut(bq);				// recurse

			bq = bq.replace(/(^|\n)/g,"$1  ");
			// These leading spaces screw with
 content, so we need to fix that:
			bq = bq.replace(
					/(\s*
[^\r]+?<\/pre>)/gm,
				function(wholeMatch,m1) {
					var pre = m1;
					// attacklab: hack around Konqueror 3.5.4 bug:
					pre = pre.replace(/^  /mg,"~0");
					pre = pre.replace(/~0/g,"");
					return pre;
				});

			return hashBlock("
\n" + bq + "\n
");
		});
	return text;
}

var _FormParagraphs = function(text) {
//
//  Params:
//    $text - string to process with html
 tags
//

	// Strip leading and trailing lines:
	text = text.replace(/^\n+/g,"");
	text = text.replace(/\n+$/g,"");

	var grafs = text.split(/\n{2,}/g);
	var grafsOut = [];

	//
	// Wrap
 tags.
	//
	var end = grafs.length;
	for (var i=0; i<end; i++) {
		var str = grafs[i];

		// if this is an HTML marker, copy it
		if (str.search(/~K(\d+)K/g) >= 0) {
			grafsOut.push(str);
		}
		else if (str.search(/\S/) >= 0) {
			str = _RunSpanGamut(str);
			str = str.replace(/^([ \t]*)/g,"
");
			str += "
"
			grafsOut.push(str);
		}

	}

	//
	// Unhashify HTML blocks
	//
	end = grafsOut.length;
	for (var i=0; i<end; i++) {
		// if this is a marker for an html block...
		while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
			var blockText = g_html_blocks[RegExp.$1];
			blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
			grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
		}
	}

	return grafsOut.join("\n\n");
}

var _EncodeAmpsAndAngles = function(text) {
// Smart processing for ampersands and angle brackets that need to be encoded.

	// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
	//   http://bumppo.net/projects/amputator/
	text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");

	// Encode naked <'s
	text = text.replace(/<(?![a-z\/?\$!])/gi,"<");

	return text;
}

var _EncodeBackslashEscapes = function(text) {
//
//   Parameter:  String.
//   Returns:	The string, with after processing the following backslash
//			   escape sequences.
//

	// attacklab: The polite way to do this is with the new
	// escapeCharacters() function:
	//
	// 	text = escapeCharacters(text,"\\",true);
	// 	text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
	//
	// ...but we're sidestepping its use of the (slow) RegExp constructor
	// as an optimization for Firefox.  This function gets called a LOT.

	text = text.replace(/\\(\\)/g,escapeCharacters_callback);
	text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
	return text;
}

var _DoAutoLinks = function(text) {

	text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");

	// Email addresses: <address@domain.foo>

	/*
		text = text.replace(/
			<
			(?:mailto:)?
			(
				[-.\w]+
				\@
				[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
			)
			>
		/gi, _DoAutoLinks_callback());
	*/
	text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
		function(wholeMatch,m1) {
			return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
		}
	);

	return text;
}

var _EncodeEmailAddress = function(addr) {
//
//  Input: an email address, e.g. "foo@example.com"
//
//  Output: the email address as a mailto link, with each character
//	of the address encoded as either a decimal or hex entity, in
//	the hopes of foiling most address harvesting spam bots. E.g.:
//
//	foo
//	   @example.com
//
//  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
//  mailing list: <http://tinyurl.com/yu7ue>
//

	var encode = [
		function(ch){return "&#"+ch.charCodeAt(0)+";";},
		function(ch){return "&#x"+ch.charCodeAt(0).toString(16)+";";},
		function(ch){return ch;}
	];

	addr = "mailto:" + addr;

	addr = addr.replace(/./g, function(ch) {
		if (ch == "@") {
		   	// this *must* be encoded. I insist.
			ch = encode[Math.floor(Math.random()*2)](ch);
		} else if (ch !=":") {
			// leave ':' alone (to spot mailto: later)
			var r = Math.random();
			// roughly 10% raw, 45% hex, 45% dec
			ch =  (
					r > .9  ?	encode[2](ch)   :
					r > .45 ?	encode[1](ch)   :
								encode[0](ch)
				);
		}
		return ch;
	});

	addr = "" + addr + "";
	addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part

	return addr;
}

var _UnescapeSpecialChars = function(text) {
//
// Swap back in all the special characters we've hidden.
//
	text = text.replace(/~E(\d+)E/g,
		function(wholeMatch,m1) {
			var charCodeToReplace = parseInt(m1);
			return String.fromCharCode(charCodeToReplace);
		}
	);
	return text;
}

var _Outdent = function(text) {
//
// Remove one level of line-leading tabs or spaces
//

	// attacklab: hack around Konqueror 3.5.4 bug:
	// "----------bug".replace(/^-/g,"") == "bug"

	text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width

	// attacklab: clean up hack
	text = text.replace(/~0/g,"")

	return text;
}

var _Detab = function(text) {
// attacklab: Detab's completely rewritten for speed.
// In perl we could fix it by anchoring the regexp with \G.
// In javascript we're less fortunate.

	// expand first n-1 tabs
	text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width

	// replace the nth with two sentinels
	text = text.replace(/\t/g,"~A~B");

	// use the sentinel to anchor our regex so it doesn't explode
	text = text.replace(/~B(.+?)~A/g,
		function(wholeMatch,m1,m2) {
			var leadingText = m1;
			var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width

			// there *must* be a better way to do this:
			for (var i=0; i<numSpaces; i++) leadingText+=" ";

			return leadingText;
		}
	);

	// clean up sentinels
	text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width
	text = text.replace(/~B/g,"");

	return text;
}

//
//  attacklab: Utility functions
//

var escapeCharacters = function(text, charsToEscape, afterBackslash) {
	// First we have to escape the escape characters so that
	// we can build a character class out of them
	var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";

	if (afterBackslash) {
		regexString = "\\\\" + regexString;
	}

	var regex = new RegExp(regexString,"g");
	text = text.replace(regex,escapeCharacters_callback);

	return text;
}

var escapeCharacters_callback = function(wholeMatch,m1) {
	var charCodeToEscape = m1.charCodeAt(0);
	return "~E"+charCodeToEscape+"E";
}

} // end of Showdown.converter

// export
if (typeof module !== 'undefined') module.exports = Showdown;

// stolen from AMD branch of underscore
// AMD define happens at the end for compatibility with AMD loaders
// that don't enforce next-turn semantics on modules.
if (typeof define === 'function' && define.amd) {
    define('showdown', function() {
        return Showdown;
    });
}


// Fix issue #9866, as explained in this comment:


Package.describe({
summary: "JavaScript minifier",
version: "2.3.4"
});
Npm.depends({
"uglify-es": "3.3.9"

var uglify;

meteorJsMinify = function (source) {
  var result = {};
  var NODE_ENV = process.env.NODE_ENV || "development";

  uglify = uglify || Npm.require("uglify-es");

  try {
    var uglifyResult = uglify.minify(source, {
      compress: {
        drop_debugger: false,
        unused: false,
        dead_code: true,
        global_defs: {
          "process.env.NODE_ENV": NODE_ENV
        }
      }
    });

    if (typeof uglifyResult.code === "string") {
      result.code = uglifyResult.code;
      result.minifier = 'uglify-es';
    } else {
      throw uglifyResult.error ||
        new Error("unknown uglify.minify failure");
    }

  } catch (e) {
    // Although Babel.minify can handle a wider variety of ECMAScript
    // 2015+ syntax, it is substantially slower than UglifyJS, so we use
    // it only as a fallback.
    var options = Babel.getMinifierOptions({
      inlineNodeEnv: NODE_ENV
    });
    result.code = Babel.minify(source, options).code;
    result.minifier = 'babel-minify';
  }

  return result;
};


// autoupdate normally won't reload on server-only changes, but when
// running tests in the browser it's nice to have server changes cause
// the tests to reload.  Setting the auto update version to a
// different value when the server restarts accomplishes this.

if (Package.autoupdate)
  Package.autoupdate.Autoupdate.autoupdateVersion = Random.id();


var hasOwn = Object.prototype.hasOwnProperty;

function wrap(method) {
  var original = console[method];
  if (original && typeof original === "object") {
    // Turn callable console method objects into actual functions.
    console[method] = function () {
      return Function.prototype.apply.call(
        original, console, arguments
      );
    };
  }
}

if (typeof console === "object" &&
    // In older Internet Explorers, methods like console.log are actually
    // callable objects rather than functions.
    typeof console.log === "object") {
  for (var method in console) {
    // In most browsers, this hasOwn check will fail for all console
    // methods anyway, but fortunately in IE8 the method objects we care
    // about are own properties.
    if (hasOwn.call(console, method)) {
      wrap(method);
    }
  }
}


var fs = Npm.require('fs');
var child_process = Npm.require('child_process');
var querystring = Npm.require('querystring');
var urlParser = Npm.require('url');

// list of bot user agents that we want to serve statically, but do
// not obey the _escaped_fragment_ protocol. The page is served
// statically to any client whos user agent matches any of these
// regexps. Users may modify this array.
//
// An original goal with the spiderable package was to avoid doing
// user-agent based tests. But the reality is not enough bots support
// the _escaped_fragment_ protocol, so we need to hardcode a list
// here. I shed a silent tear.
Spiderable.userAgentRegExps = [
  /^facebookexternalhit/i,
  /^Facebot/,
  /^linkedinbot/i,
  /^twitterbot/i,
  /^slackbot-linkexpanding/i
];

// how long to let phantomjs run before we kill it (and send down the
// regular page instead). Users may modify this number.
Spiderable.requestTimeoutMs = 15*1000;
// maximum size of result HTML. node's default is 200k which is too
// small for our docs.
var MAX_BUFFER = 5*1024*1024; // 5MB

// Exported for tests.
Spiderable._urlForPhantom = function (siteAbsoluteUrl, requestUrl) {
  // reassembling url without escaped fragment if exists
  var parsedUrl = urlParser.parse(requestUrl);
  var parsedQuery = querystring.parse(parsedUrl.query);
  var escapedFragment = parsedQuery['_escaped_fragment_'];
  delete parsedQuery['_escaped_fragment_'];

  var parsedAbsoluteUrl = urlParser.parse(siteAbsoluteUrl);
  // If the ROOT_URL contains a path, Meteor strips that path off of the
  // request's URL before we see it. So we concatenate the pathname from
  // the request's URL with the root URL's pathname to get the full
  // pathname.
  if (parsedUrl.pathname.charAt(0) === "/") {
    parsedUrl.pathname = parsedUrl.pathname.substring(1);
  }
  parsedAbsoluteUrl.pathname = urlParser.resolve(parsedAbsoluteUrl.pathname,
                                                 parsedUrl.pathname);
  parsedAbsoluteUrl.query = parsedQuery;
  // `url.format` will only use `query` if `search` is absent
  parsedAbsoluteUrl.search = null;

  if (escapedFragment !== undefined && escapedFragment !== null && escapedFragment.length > 0) {
    parsedAbsoluteUrl.hash = '!' + decodeURIComponent(escapedFragment);
  }

  return urlParser.format(parsedAbsoluteUrl);
};

var PHANTOM_SCRIPT = Assets.getText("phantom_script.js");

WebApp.connectHandlers.use(function (req, res, next) {
  // _escaped_fragment_ comes from Google's AJAX crawling spec:
  // https://developers.google.com/webmasters/ajax-crawling/docs/specification
  if (/\?.*_escaped_fragment_=/.test(req.url) ||
      _.any(Spiderable.userAgentRegExps, function (re) {
        return re.test(req.headers['user-agent']); })) {

    var url = Spiderable._urlForPhantom(Meteor.absoluteUrl(), req.url);

    // This string is going to be put into a bash script, so it's important
    // that 'url' (which comes from the network) can neither exploit phantomjs
    // or the bash script. JSON stringification should prevent it from
    // exploiting phantomjs, and since the output of JSON.stringify shouldn't
    // be able to contain newlines, it should be unable to exploit bash as
    // well.
    var phantomScript = "var url = " + JSON.stringify(url) + ";" +
          PHANTOM_SCRIPT;

    // Allow override of phantomjs args via env var
    // We use one env var to try to keep env-var explosion under control.
    // We're not going to document this unless it is actually needed;
    // (if you find yourself needing this please let us know the use case!)
    var phantomJsArgs = process.env.METEOR_PKG_SPIDERABLE_PHANTOMJS_ARGS || '';

    // Default image loading to off (we don't need images)
    if (phantomJsArgs.indexOf("--load-images=") === -1) {
      phantomJsArgs += " --load-images=no";
    }

    // POODLE means SSLv3 is being turned off everywhere.
    // phantomjs currently defaults to SSLv3, and won't use TLS.
    // Use --ssl-protocol to set the default to TLSv1
    // (another option would be 'any', but really, we want to say >= TLSv1)
    // More info: https://groups.google.com/forum/#!topic/meteor-core/uZhT3AHwpsI
    if (phantomJsArgs.indexOf("--ssl-protocol=") === -1) {
      phantomJsArgs += " --ssl-protocol=TLSv1";
    }

    // Run phantomjs.
    //
    // Use '/dev/stdin' to avoid writing to a temporary file. We can't
    // just omit the file, as PhantomJS takes that to mean 'use a
    // REPL' and exits as soon as stdin closes.
    //
    // However, Node 0.8 broke the ability to open /dev/stdin in the
    // subprocess, so we can't just write our string to the process's stdin
    // directly; see https://gist.github.com/3751746 for the gory details. We
    // work around this with a bash heredoc. (We previous used a "cat |"
    // instead, but that meant we couldn't use exec and had to manage several
    // processes.)
    child_process.execFile(
      '/usr/bin/env',
      ['bash', '-c',
       ("exec phantomjs " + phantomJsArgs + " /dev/stdin <<'END'\n" +
        phantomScript + "END\n")],
      {timeout: Spiderable.requestTimeoutMs, maxBuffer: MAX_BUFFER},
      function (error, stdout, stderr) {
        if (!error && /<html/i.test(stdout)) {
          res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
          res.end(stdout);
        } else {
          // phantomjs failed. Don't send the error, instead send the
          // normal page.
          if (error && error.code === 127)
            Meteor._debug("spiderable: phantomjs not installed. Download and install from http://phantomjs.org/");
          else
            Meteor._debug("spiderable: phantomjs failed at " + url + ":", error, "\nstderr:", stderr, "\nstdout:", stdout);

          next();
        }
      });
  } else {
    next();
  }
});


summary: 'Standard javascript minifiers used with Meteor apps by default.',

keep keep replace keep keep keep keep keep

Package.describe({
  summary: "JavaScript minifier",
  version: "2.3.4"
});

Npm.depends({
  "uglify-es": "3.3.9"
});

Package.onUse(function (api) {
  api.use('babel-compiler');
  api.export(['meteorJsMinify']);
  api.addFiles(['minifier.js'], 'server');
});


@red: #842210;

h1 {
    color: @red;
}

img {
    display: block;
    position: absolute;
    top: 0px;
    right: 0px;
}


Package.describe({
  // XXX We currently hard-code the "launch-screen" package in the
  // build tool. If this package is in your app, we turn off the
  // default splash screen loading behavior (this packages hides it
  // explicitly). In the future, there should be a better interface
  // between such packages and the build tool.
  name: 'launch-screen',
  summary: 'Default and customizable launch screen on mobile.',
  version: '1.1.1'
});

Cordova.depends({
  'cordova-plugin-splashscreen': '4.1.0'
});

Package.onUse(function(api) {
  api.addFiles('mobile-launch-screen.js', 'web');
  api.addFiles('default-behavior.js', 'web');
  api.use(['blaze', 'templating'], 'web', { weak: true });

  api.export('LaunchScreen');
});


while (ret.outcome === 'changed-refreshable') {

// We stay in this loop as long as only refreshable assets have changed.

// When ret.refreshable becomes false, we restart the server.

var oldFuture = self.runFuture = new Future;

keep keep keep keep keep replace replace keep keep keep keep

var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;
var utils = require('../utils.js');
var Future = require('fibers/future');
var net = require('net');
var _ = require('underscore');
var files = require('../files.js');

selftest.define("css hot code push", function (options) {
  var s = new Sandbox({
    clients: options.clients
  });

  s.createApp("myapp", "css-injection-test");
  s.cd("myapp");
  s.testWithAllClients(function (run) {
    run.match("myapp");
    run.match("proxy");
    run.match("MongoDB");
    run.match("running at");
    run.match("localhost");

    run.connectClient();

    run.waitSecs(20);
    run.match("client connected");

    // 'numCssChanges' variable is set to 0 on a client refresh.
    // Since CSS changes should not trigger a client refresh, numCssChanges
    // should never reset.

    // The css file is initially empty.
    run.match("numCssChanges: 0");

    // Some browsers represent no background as 'transparent', others use
    // rgba(0, 0, 0, 0).
    run.match(/background-color: (transparent|rgba\(0, 0, 0, 0\))/);

    // The server does NOT restart if a new css file is added.
    s.write("test.css", "body { background-color: red; }");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 1");
    run.match(/background-color: (red|rgb\(255, 0, 0\))/);

    s.write("test.css", "body { background-color: blue; }");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 2");
    run.match(/background-color: (blue|rgb\(0, 0, 255\))/);

    // The server does NOT restart if a css file is removed.
    s.unlink("test.css");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 3");
    run.match(/background-color: (transparent|rgba\(0, 0, 0, 0\))/);

    s.write(".meteor/packages", "standard-app-packages \n my-package");
    run.match("added my-package");
    run.match("client connected");
    run.match("numCssChanges: 0");

    s.write("packages/my-package/foo.css", "body { background-color: blue; }");
    run.match("numCssChanges: 1");
    run.match(/background-color: (blue|rgb\(0, 0, 255\))/);

    // Add appcache and ensure that the browser still reloads.
    s.write(".meteor/packages", "standard-app-packages \n my-package \n appcache");
    run.match("added appcache");
    run.match("server restarted");
    run.match("numCssChanges: 0");
    run.match(/background-color: (blue|rgb\(0, 0, 255\))/);

    s.write("packages/my-package/foo.css", "body { background-color: red; }");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 1");
    run.match(/background-color: (red|rgb\(255, 0, 0\))/);

    // XXX: Remove me.  This shouldn't be needed, but sometimes
    // if we run too quickly on fast (or Linux?) machines, it looks
    // like there's a race and we see a weird state
    utils.sleepMs(10000);

    s.write(".meteor/packages", "standard-app-packages");
    run.match("removed my-package");
    run.match("numCssChanges: 0");
    run.match(/background-color: (transparent|rgba\(0, 0, 0, 0\))/);

    run.stop();
  });
});

selftest.define("versioning hot code push", function (options) {
  var s = new Sandbox();

  s.set("AUTOUPDATE_VERSION", "1.0");
  s.createApp("myapp", "hot-code-push-test");
  s.cd("myapp");

  s.testWithAllClients(function (run) {
    run.baseTimeout = 20;
    run.match("myapp");
    run.match("proxy");
    run.match("MongoDB");
    run.match("running at");
    run.match("localhost");
    run.connectClient();
    run.waitSecs(20);

    run.match("client connected: 0");

    run.forbidAll("Error listening");

    run.stop();
  });
});

selftest.define("javascript hot code push", function (options) {
  var s = new Sandbox({
    clients: options.clients
  });

  s.createApp("myapp", "hot-code-push-test");
  s.cd("myapp");
  s.testWithAllClients(function (run) {
    run.match("myapp");
    run.match("proxy");
    run.match("MongoDB");
    run.match("running at");
    run.match("localhost");

    run.connectClient();
    run.waitSecs(40);

    // There is initially no JavaScript file.
    run.match("client connected: 0");
    run.match("jsVar: undefined");
    run.match("sessionVar: null");

    // The server and client both restart if a shared js file is added
    // or removed.
    s.write("test.js", "jsVar = 'foo'");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: foo");
    run.match("sessionVar: true");

    s.unlink("test.js");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: undefined");

    // Only the client should refresh if a client js file is added. Thus,
    // "client connected" variable will be incremented.
    s.mkdir("client");

    s.write("client/test.js", "jsVar = 'bar'");
    run.match("client connected: 1");
    run.match("jsVar: bar");

    s.unlink("client/test.js");
    run.match("client connected: 2");
    run.match("jsVar: undefined");

    // When we change a server file the client should not refresh. We observe
    // this by changing a server file and then a client file and verifying
    // that the client has only connected once.
    s.mkdir("server");
    s.write("server/test.js", "jsVar = 'bar'");
    run.match("server restarted");

    s.write("client/empty.js", "");
    run.match("client connected: 0");
    // We should not be able to access a server variable from the client.
    run.match("jsVar: undefined");

    s.unlink("client/empty.js");
    run.waitSecs(5);
    run.match("client connected: 1");
    run.match("jsVar: undefined");

    // Break the HTML file. This should kill the server, and print errors.
    // (It would be reasonable behavior for this to NOT kill the server, since
    // it only affects the client. But this is a regression test for a bug where
    // fixing the HTML file wouldn't actually restart the server; that's the
    // important part of this test.)
    s.write("hot-code-push-test.html", ">");
    run.match("Errors prevented startup");
    run.match("bad formatting in HTML template");
    // Fix it. It should notice, and restart. The client will restart too.
    s.write("hot-code-push-test.html", "");
    run.match("server restarted");
    run.match("client connected: 0");
    // Write something else to it. The client should restart.
    s.write("hot-code-push-test.html", "<head><title>foo</title></head>");
    run.match("Client modified -- refreshing");
    run.match("client connected: 1");
    run.match("jsVar: undefined");

    s.write(".meteor/packages", "standard-app-packages \n my-package");
    run.match("added my-package");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: undefined");
    run.match("packageVar: foo");

    s.write("packages/my-package/foo.js", "packageVar = 'bar'");
    run.match("client connected: 1");
    run.match("jsVar: undefined");
    run.match("packageVar: bar");

    // Add appcache and ensure that the browser still reloads.
    s.write(".meteor/packages", "standard-app-packages \n appcache");
    run.match("added appcache");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: undefined");

    // XXX: Remove me.  This shouldn't be needed, but sometimes
    // if we run too quickly on fast (or Linux?) machines, it looks
    // like there's a race and we see a weird state
    utils.sleepMs(10000);

    s.write("client/test.js", "jsVar = 'bar'");
    run.match("client connected: 1");
    run.match("jsVar: bar");

    // Remove appcache and ensure that the browser still reloads.
    s.write(".meteor/packages", "standard-app-packages");
    run.match("removed appcache");
    run.match("server restarted");
    run.match("client connected: 0");

    s.write("client/test.js", "jsVar = 'baz'");
    run.match("client connected: 1");
    run.match("jsVar: baz");

    s.unlink("client/test.js");

    // Setting the autoupdateVersion to a different string should also
    // force the client to restart.
    s.write("server/test.js",
            "Package.autoupdate.Autoupdate.autoupdateVersion = 'random'");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: undefined");

    s.unlink("server/test.js");
    run.match("server restarted");

    run.stop();
  });
});


Package.describe({
  summary: "Deprecated package (now empty)",
  version: "1.0.2-ipc.0"
});

Package.on_use(function (api) {
  // Deprecated -- Meteor.startup has been folded into the main
  // 'meteor' package for now, because it seems reasonable to expect
  // that Meteor.startup would always be available without having to
  // include a special package.
});


run.match(/background-color: (transparent|rgba\(0, 0, 0, 0\))/);

s.write(".meteor/packages", "standard-app-packages \n my-package");

s.write("packages/my-package/foo.css", "body { background-color: blue; }");

keep keep keep replace keep keep keep keep keep

// run.match("added my-package");  // XXX #3006 re-add package changes


# browser-policy-content

This is one of a family of packages that can be used to easily
configure an app's browser-side security policies. The documentation
is in the
[browser-policy](https://atmospherejs.com/meteor/browser-policy)
package.


run.match(/background-color: (blue|rgb\(0, 0, 255\))/);

// Add appcache and ensure that the browser still reloads.

s.write(".meteor/packages", "standard-app-packages \n my-package \n appcache");

s.write("packages/my-package/foo.css", "body { background-color: red; }");

keep keep keep keep keep replace keep keep keep keep keep

// run.match("added appcache");  // XXX #3006 re-add package changes


Tinytest.add("absolute-url - basics", function(test) {

  _.each(['', 'http://'], function (prefix) {

    test.equal(Meteor.absoluteUrl({rootUrl: prefix + 'asdf.com'}),
               'http://asdf.com/');
    test.equal(Meteor.absoluteUrl(undefined, {rootUrl: prefix + 'asdf.com'}),
               'http://asdf.com/');
    test.equal(Meteor.absoluteUrl(undefined, {rootUrl: prefix + 'asdf.com/'}),
               'http://asdf.com/');

    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'asdf.com/'}),
               'http://asdf.com/foo');
    test.equal(Meteor.absoluteUrl('/foo', {rootUrl: prefix + 'asdf.com'}),
               'http://asdf.com//foo');
    test.equal(Meteor.absoluteUrl('#foo', {rootUrl: prefix + 'asdf.com'}),
               'http://asdf.com/#foo');

    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'asdf.com',
                                          secure: true}),
               'https://asdf.com/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: 'https://asdf.com',
                                          secure: true}),
               'https://asdf.com/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: 'https://asdf.com',
                                          secure: false}),
               'https://asdf.com/foo');

    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'localhost',
                                          secure: true}),
               'http://localhost/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'localhost:3000',
                                          secure: true}),
               'http://localhost:3000/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: 'https://localhost:3000',
                                          secure: true}),
               'https://localhost:3000/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + '127.0.0.1:3000',
                                          secure: true}),
               'http://127.0.0.1:3000/foo');

    // test replaceLocalhost
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'localhost:3000',
                                          replaceLocalhost: true}),
               'http://127.0.0.1:3000/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'localhost',
                                          replaceLocalhost: true}),
               'http://127.0.0.1/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + '127.0.0.1:3000',
                                          replaceLocalhost: true}),
               'http://127.0.0.1:3000/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + '127.0.0.1',
                                          replaceLocalhost: true}),
               'http://127.0.0.1/foo');
    // don't replace just any localhost
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'foo.com/localhost',
                                          replaceLocalhost: true}),
               'http://foo.com/localhost/foo');
    test.equal(Meteor.absoluteUrl('foo', {rootUrl: prefix + 'foo.localhost.com',
                                          replaceLocalhost: true}),
               'http://foo.localhost.com/foo');
  });
});

Tinytest.add("absolute-url - environment", function(test) {
  // make sure our test runner set the runtime configuration, and this
  // propagates to the client.
  test.isTrue(/^http/.test(__meteor_runtime_config__.ROOT_URL));
});


var fs = require('fs');
var path = require('path');
var Future = require('fibers/future');
var _ = require('underscore');
var auth = require('./auth.js');
var config = require('./config.js');
var httpHelpers = require('./http-helpers.js');
var release = require('./release.js');
var files = require('./files.js');
var ServiceConnection = require('./service-connection.js');
var utils = require('./utils.js');
var buildmessage = require('./buildmessage.js');
var compiler = require('./compiler.js');
var packageVersionParser = require('./package-version-parser.js');
var authClient = require('./auth-client.js');
var catalog = require('./catalog.js');
var projectContextModule = require('./project-context.js');

// Opens a DDP connection to a package server. Loads the packages needed for a
// DDP connection, then calls DDP connect to the package server URL in config,
// using a current user-agent header composed by http-helpers.js.
var openPackageServerConnection = function (packageServerUrl) {
  var serverUrl = packageServerUrl || config.getPackageServerUrl();
  return authClient.openServiceConnection(serverUrl);
};

// Given a connection, makes a call to the package server.  (Checks to see if
// the connection is connected, and reconnects if needed -- a workaround for
// the fact that connections in the tool do not reconnect)
exports.callPackageServer = function (conn) {
  // XXX This is broken since it doesn't actually replace the conn in the
  // caller, so it'll happen on every subsequent call
  if (!conn.connected) {
    conn.close();
    conn = exports.loggedInPackagesConnection();
  }
  var args = _.values(arguments)
        .slice(1, arguments.length);
  return conn.call.apply(conn, args);
};

var callPackageServerBM = exports.callPackageServerBM = function () {
  buildmessage.assertInJob();
  try {
    return exports.callPackageServer.apply(null, arguments);
  } catch (e) {
    buildmessage.error(e.reason || e.message);
    return null;
  }
};

// Requests and returns one page of new package data that we haven't cached on
// disk. We assume that data is cached chronologically, so essentially, we are
// asking for a diff from the last time that we did this.
// Takes in:
// - conn: the connection to use (does not have to be logged in)
// - syncToken: a syncToken object to be sent to the server that
//   represents the last time that we talked to the server.
// - options:
//    - useShortPages (Boolean). Ask the server for pages of ~3 records
//      instead of ~100, for testing pagination.
//
// Returns an object, containing the following fields:
//  - syncToken: a new syncToken object, that we can pass to the server in the future.
//  - collections: an object keyed by the name of server collections, with the
//    records as an array of javascript objects.
var loadRemotePackageData = function (conn, syncToken, options) {
  options = options || {};

  // Did we get disconnected between retries somehow? Then we should open a new
  // connection. We shouldn't use the callPackageServer method here though,
  // since we don't need to authenticate.
  if (!conn.connected) {
    conn.close();
    conn =  openPackageServerConnection();
  }

  var syncOpts = {};
  if (options && options.useShortPages) {
    syncOpts.shortPagesForTest = options.useShortPages;
  }
  if (options && options.compressCollections) {
    syncOpts.compressCollections = options.compressCollections;
  }
  return conn.call('syncNewPackageData', syncToken, syncOpts);
};

// Contacts the package server to get the latest diff and writes changes to
// disk.
//
// Takes in the dataStore, which is an example of the remote catalog. Contacts
// the package server and updates the sql database with the most recent
// information.
//
// Returns null if contacting the server times out, or an object with the
// following keys:
//     resetData : true if we should reset the database, otherwise false.
//     connectionFailed: true if we failed to connect to the server.
//
// options can include:
//  - packageStorageFile: String. The file to write the data to (overrides
//    `config.getPackageStorage()`)
//  - packageServerUrl: String. The package server (overrides
//    `config.getPackageServerUrl()`)
//  - useShortPages: Boolean. Request short pages of ~3 records from the
//    server, instead of ~100 that it would send otherwise
exports.updateServerPackageData = function (dataStore, options) {
  return buildmessage.enterJob({ title: 'Updating package catalog' }, function () {
    return _updateServerPackageData(dataStore, options);
  });
};

var _updateServerPackageData = function (dataStore, options) {
  var self = this;
  options = options || {};
  if (dataStore === null)
    throw Error("Data store expected");

  var done = false;
  var ret = {resetData: false};

  // For now, we don't have a great progress metric, so just use a spinner
  var useProgressbar = false;

  var start = undefined;
  // Guess that we're about an hour behind, as an opening guess
  var state = { current: 0, end: 60 * 60 * 1000, done: false};
  useProgressbar && buildmessage.reportProgress(state);

  var conn = openPackageServerConnection(options.packageServerUrl);

  // Provide some progress indication for connection
  // XXX though it is just a hack
  state.current = 1;
  useProgressbar && buildmessage.reportProgress(state);

  var getSomeData = function () {
    var syncToken = dataStore.getSyncToken() || {format: "1.1"};

    if (!start) {
      start = {};
      start.builds = syncToken.builds;
      start.versions = syncToken.versions;
      state.end = (Date.now() - start.builds) + (Date.now() - start.versions);
    }
    // XXX: This is a hack... syncToken should have a % done
    state.current =
      (syncToken.builds - start.builds) +
      (syncToken.versions - start.versions);
    useProgressbar && buildmessage.reportProgress(state);

    var compress = !!process.env.METEOR_CATALOG_COMPRESS_RPCS;

    // (loadRemotePackageData may throw)
    var remoteData = loadRemotePackageData(conn, syncToken, {
      useShortPages: options.useShortPages,
      compressCollections: compress
    });

    // Is the remote server telling us to ignore everything we've heard before?
    // OK, we can do that.
    if (remoteData.resetData) {
      dataStore.reset();
      // The caller may want to take this as a cue to delete packages from the
      // tropohouse.
      ret.resetData = true;
    }

    if (remoteData.collectionsCompressed) {
      var zlib = require('zlib');
      var colsGzippedBuffer = new Buffer(
        remoteData.collectionsCompressed, 'base64');
      var fut = new Future;
      zlib.gunzip(colsGzippedBuffer, fut.resolver());
      var colsJSON = fut.wait();
      remoteData.collections = JSON.parse(colsJSON);
      delete remoteData.collectionsCompressed;
    }

    // We always write to the data store; the fact there is no data is itself
    // data!  e.g. the last-refresh timestamp
    var syncComplete =
          _.isEqual(remoteData.collections, {}) || remoteData.upToDate;
    dataStore.insertData(remoteData, syncComplete);

    // If there is no new data from the server, don't bother writing things to
    // disk (unless we were just told to reset everything).
    if (!remoteData.resetData && _.isEqual(remoteData.collections, {})) {
      done = true;
      return;
    }

    if (remoteData.upToDate) {
      done = true;
    }
  };

  try {
    while (!done) {
      getSomeData();
    }
  } finally {
    conn.close();
  }

  return ret;
};

// Returns a logged-in DDP connection to the package server, or null if
// we cannot log in. If an error unrelated to login occurs
// (e.g. connection to package server times out), then it will be
// thrown.
exports.loggedInPackagesConnection = function () {
  return authClient.loggedInConnection(
    config.getPackageServerUrl(),
    config.getPackageServerDomain(),
    "package-server"
  );
};

// XXX this is missing a few things. In retrospect a better approach here might
//     be to actually make "save source somewhere else" or perhaps "add source
//     to tarball" be part of the package build itself...
var bundleSource = function (isopack, includeSources, packageDir,
                             pluginProviderPackageMap) {
  buildmessage.assertInJob();

  var name = isopack.name;

  var tempDir = files.mkdtemp('build-source-package-');
  var packageTarName = name + '-' + isopack.version + '-source';
  var dirToTar = path.join(tempDir, 'source', packageTarName);
  // XXX name probably needs to be escaped for windows?
  // XXX note that publish-for-arch thinks it knows how this tarball is laid
  //     out, which is a bit of a shame
  var sourcePackageDir = path.join(dirToTar, name);
  if (! files.mkdir_p(sourcePackageDir)) {
    buildmessage.error('Failed to create temporary source directory: ' +
                       sourcePackageDir);
    return null;
  }

  // We copy source files into a temp directory and then tar up the temp
  // directory. It would be great if we could avoid the copy, but as far
  // as we can tell, this is the only way to get a tarball with the
  // directory structure that we want (<package name>-<version-source/
  // at the top level).
  _.each(includeSources, function (f) {
    files.copyFile(path.join(packageDir, f),
                   path.join(sourcePackageDir, f));
  });

  // Write a package map to `.versions` inside the source tarball.  Note that
  // this differs in two ways from the `.versions` file that is maintained
  // inside standalone packages by 'meteor publish':
  //  (a) It only contains the direct, directly implied, and linked-into-plugin
  //      dependencies of the package, not all transitive dependencies.
  //  (b) It is ALWAYS put into the source tarball, even if the package came
  //      from inside an app, whereas the package-source-tree .versions file
  //      is only used for standalone packages
  var packageMapFilename = path.join(sourcePackageDir, '.versions');
  if (fs.existsSync(packageMapFilename))
    throw Error(".versions file already exists? " + packageMapFilename);
  var packageMapFile = new projectContextModule.PackageMapFile({
    filename: packageMapFilename
  });
  packageMapFile.write(pluginProviderPackageMap);

  // We put this inside the temp dir because mkdtemp makes sure that the
  // temp dir gets cleaned up on process exit, so we don't have to worry
  // about cleaning up our tarball (or our copied source files)
  // ourselves.
  var sourceTarball = path.join(tempDir, packageTarName + '.tgz');
  files.createTarball(dirToTar, sourceTarball);

  var tarballHash = files.fileHash(sourceTarball);
  var treeHash = files.treeHash(dirToTar);

  return {
    sourceTarball: sourceTarball,
    tarballHash: tarballHash,
    treeHash: treeHash
  };
};

var uploadTarball = function (putUrl, tarball) {
  var size = fs.statSync(tarball).size;
  var rs = fs.createReadStream(tarball);
  try {
    // Use getUrl instead of request, to throw on 4xx/5xx.
    httpHelpers.getUrl({
      method: 'PUT',
      url: putUrl,
      headers: {
        'content-length': size,
        'content-type': 'application/octet-stream',
        'x-amz-acl': 'public-read'
      },
      bodyStream: rs,
      bodyStreamLength: size
    });
  } finally {
    rs.close();
  }
};

exports.uploadTarball = uploadTarball;

var bundleBuild = function (isopack) {
  buildmessage.assertInJob();

  var tempDir = files.mkdtemp('build-package-');
  var packageTarName = isopack.tarballName();
  var tarInputDir = path.join(tempDir, packageTarName);

  // Note that we do need to do this even though we already have the isopack on
  // disk in an IsopackCache, because we don't want to include
  // isopack-buildinfo.json. (We don't include it because we're not passing
  // includeIsopackBuildInfo to saveToPath here.)
  isopack.saveToPath(tarInputDir);

  var buildTarball = path.join(tempDir, packageTarName + '.tgz');
  files.createTarball(tarInputDir, buildTarball);

  var tarballHash = files.fileHash(buildTarball);
  var treeHash = files.treeHash(tarInputDir, {
    // We don't include any package.json from an npm module in the tree hash,
    // because npm isn't super consistent about what it puts in there (eg, does
    // it include the "readme" field)? This ends up leading to spurious
    // differences. The tree hash will still notice any actual CODE changes in
    // the npm packages.
    ignore: function (relativePath) {
      var pieces = relativePath.split(path.sep);
      return pieces.length && _.last(pieces) === 'package.json'
        && _.contains(pieces, 'npm');
    }
  });

  return {
    buildTarball: buildTarball,
    tarballHash: tarballHash,
    treeHash: treeHash
  };
};

exports.bundleBuild = bundleBuild;

var createAndPublishBuiltPackage = function (conn, isopack) {
  buildmessage.assertInJob();

  // Note: we really want to do this before createPackageBuild, because the URL
  // we get from createPackageBuild will expire!
  var bundleResult;
  buildmessage.enterJob("bundling build", function () {
    bundleResult = bundleBuild(isopack);
  });
  if (buildmessage.jobHasMessages())
    return;

  var uploadInfo;
  buildmessage.enterJob('creating package build', function () {
    uploadInfo = callPackageServerBM(conn, 'createPackageBuild', {
      packageName: isopack.name,
      version: isopack.version,
      buildArchitectures: isopack.buildArchitectures()
    });
  });
  if (buildmessage.jobHasMessages())
    return;

  buildmessage.enterJob("uploading build", function () {
    uploadTarball(uploadInfo.uploadUrl,
                  bundleResult.buildTarball);
  });
  if (buildmessage.jobHasMessages())
    return;

  buildmessage.enterJob('publishing package build', function () {
    callPackageServerBM(conn, 'publishPackageBuild',
                        uploadInfo.uploadToken,
                        bundleResult.tarballHash,
                        bundleResult.treeHash);
  });
  if (buildmessage.jobHasMessages())
    return;
};

exports.createAndPublishBuiltPackage = createAndPublishBuiltPackage;

// Handle an error thrown on trying to connect to the package server.
exports.handlePackageServerConnectionError = function (error) {
  authClient.handleConnectionError(error, "package server");
};

// Publish the package information into the server catalog. Create new records
// for the package (if needed), the version and the build; upload source and
// isopack.
//
// packageSource: the packageSource for this package.
// compileResult: the compiled isopack and various source files.
// conn: the open, logged-in connection over which we should talk to the package
//       server. DO NOT CLOSE this connection here.
// options:
//      new: this package is new, we should call createPackage to create a new
//           package record.
//      existingVersion: we expect the version to exist already, and for us
//           to merely be providing a new build of the same source
//      doNotPublishBuild: do not publish the build of this package.
//
// Return true on success and an error code otherwise.
// XXX #3006 redo docs
exports.publishPackage = function (options) {
  buildmessage.assertInJob();
  var packageSource = options.packageSource;
  var conn = options.connection;
  var projectContext = options.projectContext;

  var name = packageSource.name;
  var version = packageSource.version;

  if (options.new && options.existingVersion)
    throw Error("is it new or does it exist?!?");

  // Check that the package name is valid.
  utils.validatePackageName(name, { useBuildmessage: true });
  if (buildmessage.jobHasMessages())
    return;

  // Check that we have a version.
  if (! version) {
    buildmessage.error(
      "Package cannot be published because it doesn't have a version");
    return;
  }

  // Check that the version description is under the character limit. (We check
  // all string limits on the server, but this is the one that is mostly likely
  // to be wrong)
  if (! packageSource.metadata.summary) {
    buildmessage.error(
      "Please describe what your package does. Set a summary " +
        "in Package.describe in package.js.");
    return;
  }

  if (packageSource.metadata.summary.length > 100) {
    buildmessage.error("Summary must be under 100 chars.");
    return;
  }

  // Check that we are an authorized maintainer of this package.
  if (!options['new']) {
    var packRecord = catalog.official.getPackage(name);
    if (! packRecord) {
      buildmessage.error(
        'There is no package named ' + name +
          '. If you are creating a new package, use the --create flag.');
      return;
    }

    if (!exports.amIAuthorized(name, conn, false)) {
      buildmessage.error(
        'You are not an authorized maintainer of ' + name + '.  Only ' +
          'authorized maintainers may publish new versions.');
    }
  }

  // Check that the package does not have any unconstrained references.
  var packageDeps = packageSource.getDependencyMetadata();
  _.each(packageDeps, function(refs, label) {
    if (refs.constraint == null) {
      if (packageSource.isCore && files.inCheckout() &&
          projectContext.localCatalog.getPackage(label)) {
        // Core package is using or implying another core package,
        // without a version number.  We fill in the version number.
        // (Well, we're assuming that the other package is core and
        // not some other sort of local package.)
        var versionString =
              projectContext.localCatalog.getLatestVersion(label).version;
        // modify the constraint on this dep that will be sent to troposphere
        refs.constraint = versionString;
      } else if (label === "meteor") {
        // HACK: We are willing to publish a package with a "null"
        // constraint on the "meteor" package to troposphere.  This
        // happens for non-core packages when not running from a
        // checkout, because all packages implicitly depend on the
        // "meteor" package, but do not necessarily specify an
        // explicit version for it, and we don't have a great way to
        // choose one here.
        // XXX come back to this, especially if we are incrementing the
        // major version of "meteor".  hopefully we will have more data
        // about the package system by then.
      } else {
        buildmessage.error(
          "You must specify a version constraint for package " + label);
      }
    }
  });
  if (buildmessage.jobHasMessages())
    return;

  var isopack = projectContext.isopackCache.getIsopack(name);
  if (! isopack)
    throw Error("no isopack " + name);

  var sourceFiles = isopack.getSourceFilesUnderSourceRoot(
    packageSource.sourceRoot);
  if (! sourceFiles)
    throw Error("isopack doesn't know what its source files are?");

  // We need to have built the test package to get all of its sources, even
  // though we're not publishing a BUILD for the test package.
  if (packageSource.testName) {
    var testIsopack = projectContext.isopackCache.getIsopack(
      packageSource.testName);
    if (! testIsopack)
      throw Error("no testIsopack " + packageSource.testName);
    var testSourceFiles = testIsopack.getSourceFilesUnderSourceRoot(
      packageSource.sourceRoot);
    if (! testSourceFiles)
      throw Error("test isopack doesn't know what its source files are?");
    sourceFiles = _.union(sourceFiles, testSourceFiles);
  }

  // Find the PackageMap of packages that are build time dependencies of this
  // package.
  var pluginProviderPackageMap =
        projectContext.isopackCache.getPluginProviderPackageMap(name);
  if (! pluginProviderPackageMap)
    throw Error("missing pluginProviderPackageMap for " + name);

  var sourceBundleResult;
  buildmessage.enterJob("bundling source", function () {
    sourceBundleResult = bundleSource(
      isopack, sourceFiles, packageSource.sourceRoot, pluginProviderPackageMap);
  });
  if (buildmessage.jobHasMessages())
    return;

  // Create the package. Check that the metadata exists.
  if (options.new) {
    buildmessage.enterJob("creating package", function () {
      callPackageServerBM(conn, 'createPackage', {
        name: packageSource.name
      });
    });
    if (buildmessage.jobHasMessages())
      return;
  }

  if (options.existingVersion) {
    var existingRecord = catalog.official.getVersion(name, version);
    if (! existingRecord) {
      buildmessage.error("Version does not exist.");
      return;
    }
    if (existingRecord.source.treeHash !== sourceBundleResult.treeHash) {
      buildmessage.error("Package source differs from the existing version.");
      return;
    }

    // XXX check that we're actually providing something new?
  } else {
    var uploadInfo;
    buildmessage.enterJob("creating package version", function () {
      var uploadRec = {
        packageName: packageSource.name,
        version: version,
        description: packageSource.metadata.summary,
        git: packageSource.metadata.git,
        compilerVersion: compiler.BUILT_BY,
        containsPlugins: packageSource.containsPlugins(),
        debugOnly: packageSource.debugOnly,
        releaseName: release.current.name,
        dependencies: packageDeps
      };
      uploadInfo = callPackageServerBM(conn, 'createPackageVersion', uploadRec);
    });
    if (buildmessage.jobHasMessages())
      return;

    // XXX If package version already exists, print a nice error message
    // telling them to try 'meteor publish-for-arch' if they want to
    // publish a new build.

    buildmessage.enterJob("uploading source", function () {
      uploadTarball(uploadInfo.uploadUrl, sourceBundleResult.sourceTarball);
    });
    if (buildmessage.jobHasMessages())
      return;

    buildmessage.enterJob("publishing package version", function () {
      callPackageServerBM(conn, 'publishPackageVersion',
                          uploadInfo.uploadToken,
                          { tarballHash: sourceBundleResult.tarballHash,
                            treeHash: sourceBundleResult.treeHash });
    });
    if (buildmessage.jobHasMessages())
      return;
  }

  if (! options.doNotPublishBuild) {
    createAndPublishBuiltPackage(conn, isopack);
    if (buildmessage.jobHasMessages())
      return;
  }

  return;
};

// Call the server to ask if we are authorized to update this release or
// package. This is a way to save time before sending data to the server. It
// will mostly ignore most errors (just in case we have a flaky network connection or
// something) and let the method deal with those.
//
// If this returns FALSE, then we are NOT authorized.
// Otherwise, return true.
exports.amIAuthorized = function (name, conn, isRelease) {
  var methodName = "amIAuthorized" +
    (isRelease ? "Release" : "Package");

  try {
    exports.callPackageServer(conn, methodName, name);
  } catch (err) {
    if (err.error === 401) {
      return false;
    }

    // We don't know what this error is. Probably we can't contact the server,
    // or the like. It would be a pity to fail all operations with the server
    // just because a preliminary check fails, so return true for now.
    return true;
  }
  return true;
};


// run.match("removed my-package");  // XXX #3006 re-add package changes


Package.describe({
  summary: "Session variable",
  version: '1.0.5-ipc.0'
});

Package.on_use(function (api) {
  api.use(['underscore', 'reactive-dict', 'ejson'], 'client');

  // Session can work with or without reload, but if reload is present
  // it should load first so we can detect it at startup and populate
  // the session.
  api.use('reload', 'client', {weak: true});

  api.export('Session', 'client');
  api.add_files('session.js', 'client');
});

Package.on_test(function (api) {
  api.use('tinytest');
  api.use('session', 'client');
  api.use('tracker');
  api.use('mongo');
  api.add_files('session_tests.js', 'client');
});


s.write(".meteor/packages", "standard-app-packages \n appcache");

// XXX: Remove me.  This shouldn't be needed, but sometimes

keep keep keep keep replace keep keep keep keep keep


/*
 * XXX Hack to make bootstrap work when bundled. This needs to be included
 * _after_ the standard bootstrap css files.
 *
 * After being bundled, all CSS is minified and served from the "/" dir.
 * Thus, we need to provide absolute paths to the icons here.
 */

[class^="icon-"],
[class*=" icon-"] {
  background-image: url("/packages/bootstrap/img/glyphicons-halflings.png");
}
/*
 * Selectors borrowed from bootstrap.css.  For all releases of bootstrap, when
 * we upgrade, update this file to borrow the selectors from where bootstrap.css
 * references any .png.  When we update to using .less instead, use the less
 * directives in a less file instead.
 */
.icon-white,
.nav-pills > .active > a > [class^="icon-"],
.nav-pills > .active > a > [class*=" icon-"],
.nav-list > .active > a > [class^="icon-"],
.nav-list > .active > a > [class*=" icon-"],
.navbar-inverse .nav > .active > a > [class^="icon-"],
.navbar-inverse .nav > .active > a > [class*=" icon-"],
.dropdown-menu > li > a:hover > [class^="icon-"],
.dropdown-menu > li > a:focus > [class^="icon-"],
.dropdown-menu > li > a:hover > [class*=" icon-"],
.dropdown-menu > li > a:focus > [class*=" icon-"],
.dropdown-menu > .active > a > [class^="icon-"],
.dropdown-menu > .active > a > [class*=" icon-"],
.dropdown-submenu:hover > a > [class^="icon-"],
.dropdown-submenu:focus > a > [class^="icon-"],
.dropdown-submenu:hover > a > [class*=" icon-"],
.dropdown-submenu:focus > a > [class*=" icon-"] {
  background-image: url("/packages/bootstrap/img/glyphicons-halflings-white.png");
}


// Given some functions, run them in every possible order.
//
// In simplest usage, takes one argument, an array of functions. Run
// those functions in every possible order. Or, if the first element
// of the array is an integer N, with the remaining elements being
// functions (N <= the number of functions), run every permutation of
// N functions from the array.
//
// Eg:
// try_all_permutations([A, B, C])
// => runs A, B, C; A, C, B; B, A, C; B, C, A; C, A, B; C, B, A
// (semicolons for clarity only)
//
// try_all_permutations([2, A, B, C])
// => runs A, B; A, C; B, A; B, C; C, A; C, B
//
// If more than one argument A_1, A_2 ... A_n is passed, each should
// be an array as described above. Compute the possible orderings O_1,
// O_2 ... O_n per above, and run the Cartesian product of the
// sets. (Except that unlike a proper Cartesian product, a set with
// zero elements will simply be ignored.)
//
// Eg:
// try_all_permutations([X], [A, B], [Y])
// => runs X, A, B, Y; X, B, A, Y
// try_all_permutations([X], [A, B], [], [Y])
// => same
//
// If a function is passed instead of an array, it will be treated as
// an array with one argument. In other words, these are the same:
// try_all_permutations([X], [A, B], [Y])
// try_all_permutations(X, [A, B], Y)

try_all_permutations = function () {
  var args = Array.prototype.slice.call(arguments);

  var current_set = 0;
  var chosen = [];

  var expand_next_set = function () {
    if (current_set === args.length) {
      _.each(chosen, function (f) { f(); });
    } else {
      var set = args[current_set];
      if (typeof set === "function")
        set = [set];

      current_set++;
      if (typeof set[0] === "number")
        pick(set[0], set.slice(1));
      else
        pick(set.length, set);
      current_set--;
    }
  };

  var pick = function (how_many, remaining) {
    if (how_many === 0)
      expand_next_set();
    else {
      for (var i = 0; i < remaining.length; i++) {
        chosen.push(remaining[i]);
        pick(how_many - 1,
             remaining.slice(0, i).concat(remaining.slice(i + 1)));
        chosen.pop();
      }
    }
  };

  expand_next_set();
};


// Remove appcache and ensure that the browser still reloads.

keep keep keep keep replace keep keep keep

// run.match("removed appcache");  // XXX #3006 re-add package changes


Tinytest.add(
  "spacebars - ui-dynamic-template - render template dynamically", function (test, expect) {
    var tmpl = Template.ui_dynamic_test;

    var nameVar = new ReactiveVar;
    var dataVar = new ReactiveVar;
    tmpl.helpers({
      templateName: function () {
        return nameVar.get();
      },
      templateData: function () {
        return dataVar.get();
      }
    });

    // No template chosen
    var div = renderToDiv(tmpl);
    test.equal(canonicalizeHtml(div.innerHTML), "");

    // Choose the "ui-dynamic-test-sub" template, with no data context
    // passed in.
    nameVar.set("ui_dynamic_test_sub");
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "test");

    // Set a data context.
    dataVar.set({ foo: "bar" });
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "testbar");
  });

// Same test as above, but the {{> Template.dynamic}} inclusion has no
// `dataContext` argument.
Tinytest.add(
  "spacebars - ui-dynamic-template - render template dynamically, no data context",
  function (test, expect) {
    var tmpl = Template.ui_dynamic_test_no_data;

    var nameVar = new ReactiveVar;
    tmpl.helpers({
      templateName: function () {
        return nameVar.get();
      }
    });

    var div = renderToDiv(tmpl);
    test.equal(canonicalizeHtml(div.innerHTML), "");

    nameVar.set("ui_dynamic_test_sub");
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "test");
  });

Tinytest.add(
  "spacebars - ui-dynamic-template - render template " +
    "dynamically, data context gets inherited",
  function (test, expect) {
    var tmpl = Template.ui_dynamic_test_inherited_data;

    var nameVar = new ReactiveVar();
    var dataVar = new ReactiveVar();
    tmpl.helpers({
      templateName: function () {
        return nameVar.get();
      },
      context: function () {
        return dataVar.get();
      }
    });

    var div = renderToDiv(tmpl);
    test.equal(canonicalizeHtml(div.innerHTML), "");

    nameVar.set("ui_dynamic_test_sub");
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "test");

    // Set the top-level template's data context; this should be
    // inherited by the dynamically-chosen template, since the {{>
    // Template.dynamic}} inclusion didn't include a data argument.
    dataVar.set({ foo: "bar" });
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "testbar");
  }
);

Tinytest.add(
  "spacebars - ui-dynamic-template - render template " +
    "dynamically, data context does not get inherited if " +
    "falsey context is passed in",
  function (test, expect) {
    var tmpl = Template.ui_dynamic_test_falsey_inner_context;

    var nameVar = new ReactiveVar();
    var dataVar = new ReactiveVar();
    tmpl.helpers({
      templateName: function () {
        return nameVar.get();
      },
      context: function () {
        return dataVar.get();
      }
    });

    var div = renderToDiv(tmpl);
    test.equal(canonicalizeHtml(div.innerHTML), "");

    nameVar.set("ui_dynamic_test_sub");
    Tracker.flush();
    // Even though the data context is falsey, we DON'T expect the
    // subtemplate to inherit the data context from the parent template.
    test.equal(canonicalizeHtml(div.innerHTML), "test");
  }
);

Tinytest.add(
  "spacebars - ui-dynamic-template - render template " +
    "dynamically, bad arguments",
  function (test, expect) {
    var tmplPrefix = "ui_dynamic_test_bad_args";
    var errors = [
      "Must specify 'template' as an argument",
      "Must specify 'template' as an argument",
      "Invalid argument to {{> Template.dynamic}}"
    ];

    for (var i = 0; i < 3; i++) {
      var tmpl = Template[tmplPrefix + i];
      test.throws(function () {
        Blaze._throwNextException = true;
        var div = renderToDiv(tmpl);
      });
    }
  }
);

Tinytest.add(
  "spacebars - ui-dynamic-template - render template " +
    "dynamically, falsey context",
  function (test, expect) {
    var tmpl = Template.ui_dynamic_test_falsey_context;
    var subtmpl = Template.ui_dynamic_test_falsey_context_sub;

    var subtmplContext;
    subtmpl.helpers({foo: function () {
      subtmplContext = this;
    }});
    var div = renderToDiv(tmpl);

    // Because `this` can only be an object, Blaze normalizes falsey
    // data contexts to {}.
    test.equal(subtmplContext, {});
  }
);

Tinytest.add(
  "spacebars - ui-dynamic-template - back-compat", function (test, expect) {
    var tmpl = Template.ui_dynamic_backcompat;

    var nameVar = new ReactiveVar;
    var dataVar = new ReactiveVar;
    tmpl.helpers({
      templateName: function () {
        return nameVar.get();
      },
      templateData: function () {
        return dataVar.get();
      }
    });

    // No template chosen
    var div = renderToDiv(tmpl);
    test.equal(canonicalizeHtml(div.innerHTML), "");

    // Choose the "ui-dynamic-test-sub" template, with no data context
    // passed in.
    nameVar.set("ui_dynamic_test_sub");
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "test");

    // Set a data context.
    dataVar.set({ foo: "bar" });
    Tracker.flush();
    test.equal(canonicalizeHtml(div.innerHTML), "testbar");
  });


#!/bin/bash

# import all the environment
source $(dirname $0)/common_env.sh

adb shell sqlite3 /data/data/com.android.providers.telephony/databases/telephony.db \
    "update carriers set proxy='10.0.2.2', port='3002' where current=1"
adb shell stop
adb shell start