mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-10-29 01:50:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			707 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			707 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| /**********************************************************************
 | |
| * 
 | |
| *
 | |
| *
 | |
| **********************************************************************/
 | |
| ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
 | |
| (function(require){ var module={} // make module AMD/node compatible...
 | |
| /*********************************************************************/
 | |
| 
 | |
| 
 | |
| var setWikiWords = function(text, show_brackets, skip){
 | |
| 	skip = skip || []
 | |
| 	skip = skip instanceof Array ? skip : [skip]
 | |
| 	return text 
 | |
| 		// set new...
 | |
| 		.replace(
 | |
| 			macro.__wiki_link__,
 | |
| 			function(l){
 | |
| 				// check if WikiWord is escaped...
 | |
| 				if(l[0] == '\\'){
 | |
| 					return l.slice(1) }
 | |
| 
 | |
| 				var path = l[0] == '[' ? 
 | |
| 					l.slice(1, -1) 
 | |
| 					: l
 | |
| 				var i = [].slice.call(arguments).slice(-2)[0]
 | |
| 
 | |
| 				// XXX HACK check if we are inside a tag...
 | |
| 				var rest = text.slice(i+1)
 | |
| 				if(rest.indexOf('>') < rest.indexOf('<')){
 | |
| 					return l }
 | |
| 
 | |
| 				return skip.indexOf(l) < 0 ? 
 | |
| 					('<a '
 | |
| 						+'class="wikiword" '
 | |
| 						+'href="#'+ path +'" '
 | |
| 						+'bracketed="'+ (show_brackets && l[0] == '[' ? 'yes' : 'no') +'" '
 | |
| 						//+'onclick="event.preventDefault(); go($(this).attr(\'href\').slice(1))" '
 | |
| 						+'>'
 | |
| 							+ (!!show_brackets ? path : l) 
 | |
| 						+'</a>')
 | |
| 					: l })}
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| 
 | |
| function Macro(doc, args, func){
 | |
| 	func.doc = doc
 | |
| 	func.macro_args = args
 | |
| 	return func }
 | |
| 
 | |
| 
 | |
| 
 | |
| // XXX should inline macros support named args???
 | |
| var macro =
 | |
| module = {
 | |
| 
 | |
| 	__include_marker__: '{{{INCLUDE-MARKER}}}',
 | |
| 
 | |
| 	// Abstract macro syntax:
 | |
| 	// 	Inline macro:
 | |
| 	// 		@macro(arg ..)
 | |
| 	//
 | |
| 	// 	HTML-like:
 | |
| 	// 		<macro arg=value ../>
 | |
| 	//
 | |
| 	// 	HTML-like with body:
 | |
| 	// 		<macro arg=value ..>
 | |
| 	// 			..text..
 | |
| 	// 		</macro>
 | |
| 	//
 | |
| 	// XXX should inline macros support named args???
 | |
| 	__macro__pattern__: 
 | |
| 		[[
 | |
| 			// @macro(arg ..)
 | |
| 			'\\\\?@([a-zA-Z-_]+)\\(([^)]*)\\)'
 | |
| 		].join('|'), 'mg'],
 | |
| 
 | |
| 	// default filters...
 | |
| 	//
 | |
| 	// NOTE: these are added AFTER the user defined filters...
 | |
| 	__filters__: [
 | |
| 		'wikiword',
 | |
| 		'noscript',
 | |
| 	],
 | |
| 	__post_filters__: [
 | |
| 		//'noscript',
 | |
| 		'title',
 | |
| 		'editor',
 | |
| 	],
 | |
| 
 | |
| 	// XXX should this be here???
 | |
| 	__wiki_link__: RegExp('('+[
 | |
| 		//'\\\\?(\\/|\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*',
 | |
| 		'\\\\?\\/?(\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*',
 | |
| 		'\\\\?\\[[^\\]]+\\]',
 | |
| 	].join('|') +')', 'g'),
 | |
| 
 | |
| 
 | |
| 
 | |
| 	// Macros...
 | |
| 	//
 | |
| 	// XXX add support for sort and reverse attrs in all relavant macros
 | |
| 	// 		(see: macro for details)
 | |
| 	macro: {
 | |
| 		"pwiki-comment": Macro('hide in pWiki',
 | |
| 			[],
 | |
| 			function(context, elem, state){ 
 | |
| 				return '' }),
 | |
| 		now: Macro('Create a now id',
 | |
| 			[],
 | |
| 			function(context, elem, state){ 
 | |
| 				return ''+Date.now() }),
 | |
| 		// select filter to post-process text...
 | |
| 		filter: Macro('Filter to post-process text',
 | |
| 			['name'],
 | |
| 			function(context, elem, state){
 | |
| 				var filter = $(elem).attr('name')
 | |
| 
 | |
| 				filter[0] == '-' ?
 | |
| 					// disabled -- keep at head of list...
 | |
| 					state.filters.unshift(filter)
 | |
| 					// normal -- tail...
 | |
| 					: state.filters.push(filter)
 | |
| 
 | |
| 				return '' }),
 | |
| 
 | |
| 		// include page/slot...
 | |
| 		//
 | |
| 		// NOTE: this will render the page in the caller's context.
 | |
| 		// NOTE: included pages are rendered completely independently 
 | |
| 		// 		from the including page.
 | |
| 		include: Macro('Include page',
 | |
| 			['src', 'isolated', 'text'],
 | |
| 			function(context, elem, state){
 | |
| 				var path = $(elem).attr('src')
 | |
| 
 | |
| 				// get and prepare the included page...
 | |
| 				state.include
 | |
| 					.push([elem, context.get(path)])
 | |
| 
 | |
| 				// return the marker...
 | |
| 				return this.__include_marker__ }),
 | |
| 
 | |
| 		// NOTE: this is similar to include, the difference is that this
 | |
| 		// 		includes the page source to the current context while 
 | |
| 		// 		include works in an isolated context
 | |
| 		source: Macro('Include page source (without parsing)',
 | |
| 			['src'], 
 | |
| 			function(context, elem, state){
 | |
| 				var path = $(elem).attr('src')
 | |
| 
 | |
| 				return context.get(path)
 | |
| 					.map(function(page){ return page.raw() })
 | |
| 					.join('\n') }),
 | |
| 
 | |
| 		quote: Macro('Include quoted page source (without parsing)',
 | |
| 			['src'], 
 | |
| 			function(context, elem, state){
 | |
| 				elem = $(elem)
 | |
| 				var path = elem.attr('src')
 | |
| 
 | |
| 				return $(context.get(path)
 | |
| 					.map(function(page){
 | |
| 						return elem
 | |
| 							.clone()
 | |
| 							.attr('src', page.path())
 | |
| 							.text(page.raw())[0] })) }),
 | |
| 
 | |
| 		/*
 | |
| 		// fill/define slot (stage 1)...
 | |
| 		//
 | |
| 		// XXX which should have priority the arg text or the content???
 | |
| 		_slot: Macro('Define/fill slot',
 | |
| 			['name', 'text'],
 | |
| 			function(context, elem, state, parse){
 | |
| 				var name = $(elem).attr('name')
 | |
| 
 | |
| 				// XXX
 | |
| 				text = $(elem).html()
 | |
| 				text = text == '' ? $(elem).attr('text') : text
 | |
| 				text = this.parse(context, text, state, true)
 | |
| 				//text = parse(elem)
 | |
| 
 | |
| 				if(state.slots[name] == null){
 | |
| 					state.slots[name] = text
 | |
| 					// return a slot macro parsable by stage 2...
 | |
| 					//return '<_slot name="'+name+'">'+ text +'</slot>'
 | |
| 					return elem
 | |
| 
 | |
| 				} else if(name in state.slots){
 | |
| 					state.slots[name] = text
 | |
| 					return '' } }),
 | |
| 		//*/
 | |
| 		// convert @ macro to html-like + parse content...
 | |
| 		slot: Macro('Define/fill slot',
 | |
| 			['name', 'text'],
 | |
| 			function(context, elem, state, parse){
 | |
| 				elem = $(elem)
 | |
| 				var name = elem.attr('name')
 | |
| 
 | |
| 				// XXX
 | |
| 				text = elem.html()
 | |
| 				text = text.trim() == '' ? 
 | |
| 					elem.html(elem.attr('text') || '').html() 
 | |
| 					: text
 | |
| 				text = parse(elem)
 | |
| 
 | |
| 				elem.attr('text', null)
 | |
| 				//elem.html(text)
 | |
| 
 | |
| 				return elem }),
 | |
| 
 | |
| 		// XXX revise macro definition rules -- see inside...
 | |
| 		// XXX do we need macro namespaces or context isolation (for inculdes)???
 | |
| 		macro: Macro('Define/fill macro',
 | |
| 			['name', 'src', 'sort'],
 | |
| 			function(context, elem, state, parse){
 | |
| 				elem = $(elem)
 | |
| 				var name = elem.attr('name')
 | |
| 				var path = elem.attr('src')
 | |
| 				var sort = elem.attr('sort')
 | |
| 
 | |
| 				state.templates = state.templates || {}
 | |
| 
 | |
| 				// get named macro...
 | |
| 				if(name){
 | |
| 					// XXX not sure which definition rules to use for macros...
 | |
| 					// 		- first define -- implemented now
 | |
| 					// 		- last define -- as in slots
 | |
| 					// 		- first contenr -- original
 | |
| 					//if(elem.html().trim() != ''){
 | |
| 					if(elem.html().trim() != '' 
 | |
| 							// do not redefine...
 | |
| 							&& state.templates[name] == null){
 | |
| 						state.templates[name] = elem.clone()
 | |
| 
 | |
| 					} else if(name in state.templates) {
 | |
| 						elem = state.templates[name] } }
 | |
| 
 | |
| 				// fill macro...
 | |
| 				if(path){
 | |
| 					var pages = context.get(path)
 | |
| 
 | |
| 					// no matching pages -- show the else block or nothing...
 | |
| 					if(pages.length == 0){
 | |
| 						var e = elem
 | |
| 							.find('else').first().clone()
 | |
| 								.attr('src', path)
 | |
| 						parse(e, context)
 | |
| 						return e } 
 | |
| 
 | |
| 					// see if we need to overload attrs...
 | |
| 					sort = sort == null ? 
 | |
| 						(elem.attr('sort') || '') 
 | |
| 						: sort
 | |
| 					sort = sort
 | |
| 							.split(/\s+/g)
 | |
| 							.filter(function(e){ return e && e != '' })
 | |
| 
 | |
| 					// do the sorting...
 | |
| 					pages = sort.length > 0 ? 
 | |
| 						pages.sort(sort) 
 | |
| 						: pages
 | |
| 
 | |
| 					// fill with pages...
 | |
| 					elem = elem.clone()
 | |
| 						.find('else')
 | |
| 							.remove()
 | |
| 						.end()
 | |
| 					return $(pages
 | |
| 						.map(function(page){
 | |
| 							var e = elem.clone()
 | |
| 								.attr('src', page.path())
 | |
| 							parse(e, page)
 | |
| 							return e[0] })) }
 | |
| 
 | |
| 				return '' })
 | |
| 	},
 | |
| 	
 | |
| 	// Post macros... 
 | |
| 	//
 | |
| 	// XXX this is disabled for now, see end of .parse(..)
 | |
| 	post_macro: {
 | |
| 		'*': Macro('cleanup...',
 | |
| 			[],
 | |
| 			function(context, elem, state, parse, match){
 | |
| 				if(match != null){
 | |
| 					return match[0] == '\\' ? 
 | |
| 						match.slice(1) 
 | |
| 						: match }
 | |
| 				return elem }),
 | |
| 		/*
 | |
| 		_slot: Macro('',
 | |
| 			['name'],
 | |
| 			function(context, elem, state){
 | |
| 				var name = $(elem).attr('name')
 | |
| 
 | |
| 				if(state.slots[name] == null){
 | |
| 					return $(elem).html()
 | |
| 
 | |
| 				} else if(name in state.slots){
 | |
| 					return state.slots[name] } }),
 | |
| 		//*/
 | |
| 
 | |
| 		/*
 | |
| 		// XXX rename to post-include and post-quote
 | |
| 		'page-text': Macro('',
 | |
| 			['src'],
 | |
| 			function(context, elem, state){
 | |
| 				elem = $(elem)
 | |
| 
 | |
| 				return elem.html(context.get(elem.attr('src')).text) }),
 | |
| 		'page-raw': Macro('',
 | |
| 			['src'],
 | |
| 			function(context, elem, state){
 | |
| 				elem = $(elem)
 | |
| 
 | |
| 				return elem.text(context.get(elem.attr('src')).text) }),
 | |
| 		//*/
 | |
| 	},
 | |
| 
 | |
| 	// Filters...
 | |
| 	//
 | |
| 	// Signature:
 | |
| 	// 	filter(text) -> html
 | |
| 	//
 | |
| 	filter: {
 | |
| 		default: 'html',
 | |
| 
 | |
| 		html: function(context, elem){ 
 | |
| 			return $(elem) },
 | |
| 		text: function(context, elem){ 
 | |
| 			return $('<span>')
 | |
| 				.append($('<pre>')
 | |
| 					.html($(elem).html())) },
 | |
| 		// XXX expperimental...
 | |
| 		json: function(context, elem){ return $('<span>')
 | |
| 			.html($(elem).text()
 | |
| 				// remove JS comments...
 | |
| 				.replace(/\s*\/\/.*$|\s*\/\*(.|[\n\r])*?\*\/\s*/mg, '')) },
 | |
| 
 | |
| 		// XXX
 | |
| 		nl2br: function(context, elem){ 
 | |
| 			return $('<div>')
 | |
| 				.html($(elem)
 | |
| 					.html()
 | |
| 					.replace(/\n/g, '<br>\n')) },
 | |
| 
 | |
| 		wikiword: function(context, elem){ 
 | |
| 			return $('<span>')
 | |
| 				.html(setWikiWords($(elem).html(), true, this.__include_marker__)) },
 | |
| 		// XXX need to remove all on* event handlers...
 | |
| 		noscript: function(context, elem){ 
 | |
| 			return $(elem)
 | |
| 				// remove script tags...
 | |
| 				.find('script')
 | |
| 					.remove()
 | |
| 					.end()
 | |
| 				// remove js links...
 | |
| 				.find('[href]')
 | |
| 					.filter(function(i, e){ return /javascript:/i.test($(e).attr('href')) })
 | |
| 						.attr('href', '#')
 | |
| 						.end()
 | |
| 					.end()
 | |
| 				// remove event handlers...
 | |
| 				// XXX .off() will not work here as we need to remove on* handlers...
 | |
| 		},
 | |
| 
 | |
| 		// XXX move this to a plugin...
 | |
| 		markdown: function(context, elem){
 | |
| 			var converter = new showdown.Converter({
 | |
| 				strikethrough: true,
 | |
| 				tables: true,
 | |
| 				tasklists: true,
 | |
| 			})
 | |
| 
 | |
| 			return $('<span>')
 | |
| 				.html(converter.makeHtml($(elem).html()))
 | |
| 				// XXX add click handling to checkboxes...
 | |
| 				.find('[checked]')
 | |
| 					.parent()
 | |
| 						.addClass('checked')
 | |
| 						.end()
 | |
| 					.end() },
 | |
| 	},
 | |
| 
 | |
| 
 | |
| 	// Post-filters...
 | |
| 	//
 | |
| 	// These are run on the final page.
 | |
| 	//
 | |
| 	// The main goal is to setup editors and other active stuff that the
 | |
| 	// user should not have direct access to, but that should be 
 | |
| 	// configurable per instance...
 | |
| 	//
 | |
| 	// for tech and other details see .filter
 | |
| 	//
 | |
| 	post_filter: {
 | |
| 		noscript: function(context, elem){
 | |
| 			// XXX
 | |
| 			return elem },
 | |
| 
 | |
| 		// Setup the page title and .title element...
 | |
| 		//
 | |
| 		// Use the text from:
 | |
| 		// 	1) set it H1 if it is the first tag in .text
 | |
| 		// 	2) set it to .location
 | |
| 		//
 | |
| 		// NOTE: we do not set the title tag here because this will be 
 | |
| 		// 		done for every included page... 
 | |
| 		title: function(context, elem){
 | |
| 			elem = $(elem)
 | |
| 			var title = elem.find('.text h1').first()
 | |
| 
 | |
| 			// show first H1 as title...
 | |
| 			if(elem.find('.text').text().trim().indexOf(title.text().trim()) == 0){
 | |
| 				title.detach()
 | |
| 				elem.find('.title').html(title.html()) }
 | |
| 
 | |
| 			return elem },
 | |
| 		// XXX this needs save/reload...
 | |
| 		editor: function(context, elem){
 | |
| 			// XXX title
 | |
| 			// 		- on focus set it to .title
 | |
| 			// XXX text
 | |
| 			// XXX raw
 | |
| 			// XXX checkbox
 | |
| 
 | |
| 			return elem },
 | |
| 	},
 | |
| 
 | |
| 
 | |
| 	// Parsing:
 | |
| 	//  1) expand macros
 | |
| 	//  2) apply filters
 | |
| 	//  3) merge and parse included pages:
 | |
| 	//  	1) expand macros
 | |
| 	//  	2) apply filters
 | |
| 	//  4) fill slots
 | |
| 	//  5) expand post-macros
 | |
| 	//
 | |
| 	// NOTE: stage 4 parsing is executed on the final merged page only 
 | |
| 	// 		once. i.e. it is not performed on the included pages.
 | |
| 	// NOTE: included pages are parsed in their own context.
 | |
| 	// NOTE: slots are parsed in the context of their containing page 
 | |
| 	// 		and not in the location they are being placed.
 | |
| 	//
 | |
| 	// XXX support quoted text...
 | |
| 	// XXX need to quote regexp chars of .__include_marker__...
 | |
| 	// XXX include recursion is detected but path recursion is not at 
 | |
| 	// 		this point...
 | |
| 	// 		e.g. the folowing paths resolve to the same page:
 | |
| 	// 			/SomePage
 | |
| 	// 			/SomePage/SomePage
 | |
| 	// 			or any path matching:
 | |
| 	// 				/\/(SomePage\/)+/
 | |
| 	// XXX slow when lots of pages need to be included...
 | |
| 	parse: function(context, text, state, skip_post, pattern){
 | |
| 		var that = this
 | |
| 
 | |
| 		state = state || {}
 | |
| 		state.filters = state.filters || []
 | |
| 		//state.slots = state.slots || {}
 | |
| 		state.include = state.include || []
 | |
| 		state.seen = state.seen || []
 | |
| 
 | |
| 		//pattern = pattern || RegExp('@([a-zA-Z-_]+)\\(([^)]*)\\)', 'mg')
 | |
| 		pattern = pattern
 | |
| 			|| RegExp.apply(null, this.__macro__pattern__)
 | |
| 
 | |
| 		// XXX need to quote regexp chars...
 | |
| 		var include_marker = RegExp(this.__include_marker__, 'g')
 | |
| 
 | |
| 		var parsed = typeof(text) == typeof('str') ? 
 | |
| 			$('<span>').html(text) 
 | |
| 			: text
 | |
| 
 | |
| 		var _parseText = function(context, text, macro){
 | |
| 			return text.replace(pattern, function(match){
 | |
| 				// quoted macro...
 | |
| 				if(match[0] == '\\' && macro['*'] == null){
 | |
| 					return match.slice(1) }
 | |
| 					//return match }
 | |
| 
 | |
| 				// XXX parse match...
 | |
| 				var d = match.match(/@([a-zA-Z-_:]*)\(([^)]*)\)/)
 | |
| 
 | |
| 				var name = d[1]
 | |
| 
 | |
| 				if(name in macro || '*' in macro){
 | |
| 					var elem = $('<'+name+'/>')
 | |
| 
 | |
| 					name = name in macro ? 
 | |
| 						name 
 | |
| 						: '*'
 | |
| 
 | |
| 					// format positional args....
 | |
| 					var a = d[2]
 | |
| 						.split(/((['"]).*?\2)|\s+/g)
 | |
| 						// cleanup...
 | |
| 						.filter(function(e){ 
 | |
| 							return e 
 | |
| 								&& e != '' 
 | |
| 								&& !/^['"]$/.test(e)})
 | |
| 						// remove quotes...
 | |
| 						.map(function(e){ 
 | |
| 							return /^(['"]).*\1$/.test(e) ? 
 | |
| 								e.slice(1, -1) 
 | |
| 								: e })
 | |
| 
 | |
| 					// add the attrs to the element...
 | |
| 					name != '*' 
 | |
| 						&& a.forEach(function(e, i){
 | |
| 							var k = ((macro[name] || {}).macro_args || [])[i]
 | |
| 							k 
 | |
| 								&& elem.attr(k, e) })
 | |
| 
 | |
| 					// call macro...
 | |
| 					var res = macro[name]
 | |
| 						.call(that, context, elem, state,
 | |
| 							function(elem, c){ 
 | |
| 								return _parse(c || context, elem, macro) },
 | |
| 							match)
 | |
| 
 | |
| 					return res instanceof jQuery ? 
 | |
| 							// merge html of the returned set of elements...
 | |
| 							res.map(function(i, e){ return e.outerHTML })
 | |
| 								.toArray()
 | |
| 								.join('\n')
 | |
| 						: typeof(res) != typeof('str') ? 
 | |
| 							res.outerHTML
 | |
| 						: res }
 | |
| 
 | |
| 				return match }) }
 | |
| 		// NOTE: this modifies parsed in-place...
 | |
| 		var _parse = function(context, parsed, macro){
 | |
| 			$(parsed).contents().each(function(_, e){
 | |
| 				// #text / comment node -> parse the @... macros...
 | |
| 				if(e.nodeType == e.TEXT_NODE 
 | |
| 						|| e.nodeType == e.COMMENT_NODE){
 | |
| 					// get actual element content...
 | |
| 					// NOTE: we need to do this like this to avoid 
 | |
| 					// 		unparsing special characters...
 | |
| 					var text = $('<div>').append($(e).clone()).html()
 | |
| 
 | |
| 					// conditional comment...
 | |
| 					if(e.nodeType == e.COMMENT_NODE 
 | |
| 							&& /^<!--\s*\[pWiki\[(.|\n)*\]\]\s*-->$/.test(text)){
 | |
| 						text = text
 | |
| 							.replace(/^<!--\s*\[pWiki\[/, '')
 | |
| 							.replace(/\]\]\s*-->$/, '') }
 | |
| 
 | |
| 					$(e).replaceWith(_parseText(context, text, macro))
 | |
| 
 | |
| 				// node -> html-style + attrs...
 | |
| 				} else {
 | |
| 					var name = e.nodeName.toLowerCase()
 | |
| 
 | |
| 					// parse attr values...
 | |
| 					for(var i=0; i < e.attributes.length; i++){
 | |
| 						var attr = e.attributes[i]
 | |
| 
 | |
| 						attr.value = _parseText(context, attr.value, macro) }
 | |
| 
 | |
| 					// macro match -> call macro...
 | |
| 					if(name in  macro){
 | |
| 						$(e).replaceWith(macro[name]
 | |
| 							.call(that, context, e, state, 
 | |
| 								function(elem, c){ 
 | |
| 									return _parse(c || context, elem, macro) }))
 | |
| 
 | |
| 					// normal tag -> sub-tree...
 | |
| 					} else {
 | |
| 						_parse(context, e, macro) } } })
 | |
| 
 | |
| 			return parsed }
 | |
| 		var _filter = function(lst, filters){
 | |
| 			lst
 | |
| 				// unique -- leave last occurance..
 | |
| 				.filter(function(k, i, lst){ 
 | |
| 					return k[0] != '-'
 | |
| 						// filter dupplicates... 
 | |
| 						&& lst.slice(i+1).indexOf(k) == -1 
 | |
| 							// filter disabled...
 | |
| 						&& lst.slice(0, i).indexOf('-' + k) == -1 })
 | |
| 				// unique -- leave first occurance..
 | |
| 				//.filter(function(k, i, lst){ return lst.slice(0, i).indexOf(k) == -1 })
 | |
| 				// apply the filters...
 | |
| 				.forEach(function(f){
 | |
| 					var k = f
 | |
| 					// get filter aliases...
 | |
| 					var seen = []
 | |
| 					while(typeof(k) == typeof('str') && seen.indexOf(k) == -1){
 | |
| 						seen.push(k)
 | |
| 						k = filters[k] }
 | |
| 					// could not find the filter...
 | |
| 					if(!k){
 | |
| 						//console.warn('Unknown filter:', f)
 | |
| 						return }
 | |
| 					// use the filter...
 | |
| 					parsed = k.call(that, context, parsed) }) }
 | |
| 
 | |
| 		// macro stage...
 | |
| 		_parse(context, parsed, this.macro)
 | |
| 
 | |
| 		// filter stage...
 | |
| 		_filter(state.filters.concat(this.__filters__), this.filter)
 | |
| 
 | |
| 		// XXX DEBUG...
 | |
| 		var t = Date.now()
 | |
| 		console.log('>>>', context.path())
 | |
| 
 | |
| 		// merge includes...
 | |
| 		parsed
 | |
| 			.html(parsed.html()
 | |
| 				.replace(include_marker, function(){
 | |
| 					var page = state.include.shift()
 | |
| 					var elem = $(page.shift())
 | |
| 					page = page.pop()
 | |
| 					var isolated = elem.attr('isolated') == 'true'
 | |
| 
 | |
| 					var seen = state.seen.slice()
 | |
| 					if(seen.indexOf(page.path()) >= 0){
 | |
| 						return elem.html() }
 | |
| 					seen.push(page.path())
 | |
| 
 | |
| 					return page.map(function(page){
 | |
| 						return $('<div>')
 | |
| 							.append(elem
 | |
| 								.clone()
 | |
| 								.attr('src', page.path())
 | |
| 								.append(that
 | |
| 									.parse(page,
 | |
| 										page.raw(), 
 | |
| 										{ 
 | |
| 											//slots: !isolated ? state.slots : {},
 | |
| 											templates: state.templates,
 | |
| 											seen: seen,
 | |
| 										}, 
 | |
| 										!isolated)))
 | |
| 										//true)))
 | |
| 							.html()
 | |
| 					}).join('\n') }))
 | |
| 
 | |
| 		// XXX DEBUG...
 | |
| 		//console.log('<<<', context.path(),'TIME:', Date.now() - t)
 | |
| 
 | |
| 		// post processing...
 | |
| 		if(!skip_post){
 | |
| 			// fill slots...
 | |
| 			// XXX need to prevent this from processing slots in editable
 | |
| 			// 		elements...
 | |
| 			slots = {}
 | |
| 			// get slots...
 | |
| 			parsed.find('slot')
 | |
| 				.each(function(i, e){
 | |
| 					e = $(e)
 | |
| 
 | |
| 					// XXX not sure about this...
 | |
| 					// 		...check if it prevents correct slot parsing
 | |
| 					// 		within an isolated include...
 | |
| 					if(e.parents('[isolated="true"]').length > 0){
 | |
| 						return }
 | |
| 
 | |
| 					var n = e.attr('name')
 | |
| 
 | |
| 					n in slots 
 | |
| 						&& e.detach()
 | |
| 
 | |
| 					slots[n] = e })
 | |
| 			// place slots...
 | |
| 			parsed.find('slot')
 | |
| 				.each(function(i, e){
 | |
| 					e = $(e)
 | |
| 
 | |
| 					// XXX not sure about this...
 | |
| 					// 		...check if it prevents correct slot parsing
 | |
| 					// 		within an isolated include...
 | |
| 					if(e.parents('[isolated="true"]').length > 0){
 | |
| 						return }
 | |
| 
 | |
| 					var n = e.attr('name')
 | |
| 
 | |
| 					e.replaceWith(slots[n]) })
 | |
| 
 | |
| 			// post-macro...
 | |
| 			// XXX for some odd reason this clears the backslash from 
 | |
| 			// 		quoted macros in raw fields...
 | |
| 			//this.post_macro 
 | |
| 			//	&& _parse(context, parsed, this.post_macro)
 | |
| 		}
 | |
| 
 | |
| 		// post-filter stage...
 | |
| 		// XXX get list from context.config...
 | |
| 		_filter(this.__post_filters__, this.post_filter)
 | |
| 
 | |
| 		// XXX shuld we get rid of the root span???
 | |
| 		return parsed.contents() },
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * vim:set ts=4 sw=4 :                               */ return module })
 |