| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-10 04:30:47 +03:00
										 |  |  | 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){ | 
					
						
							| 
									
										
										
										
											2016-10-26 02:18:04 +03:00
										 |  |  | 				// check if WikiWord is escaped...
 | 
					
						
							| 
									
										
										
										
											2016-10-10 04:30:47 +03:00
										 |  |  | 				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 | 
					
						
							|  |  |  | 			})} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-10 04:30:47 +03:00
										 |  |  | 	// XXX should this be here???
 | 
					
						
							|  |  |  | 	__wiki_link__: RegExp('('+[ | 
					
						
							| 
									
										
										
										
											2016-10-26 02:18:04 +03:00
										 |  |  | 		//'\\\\?(\\/|\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*',
 | 
					
						
							|  |  |  | 		'\\\\?\\/?(\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*', | 
					
						
							| 
									
										
										
										
											2016-10-10 04:30:47 +03:00
										 |  |  | 		'\\\\?\\[[^\\]]+\\]', | 
					
						
							|  |  |  | 	].join('|') +')', 'g'), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 	// 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) | 
					
						
							| 
									
										
										
										
											2016-09-27 06:01:21 +03:00
										 |  |  | 					.map(function(page){ return page.raw() }) | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 					.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() | 
					
						
							| 
									
										
										
										
											2016-09-27 06:01:21 +03:00
										 |  |  | 							.attr('src', page.path()) | 
					
						
							|  |  |  | 							.text(page.raw())[0] | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 					})) | 
					
						
							|  |  |  | 			}), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* | 
					
						
							|  |  |  | 		// 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() | 
					
						
							| 
									
										
										
										
											2016-10-06 01:40:54 +03:00
										 |  |  | 								.attr('src', page.path()) | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 							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
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2016-10-10 04:30:47 +03:00
										 |  |  | 		// NOTE: we do not set the title tag here because this will be 
 | 
					
						
							|  |  |  | 		// 		done for every included page... 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 		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\/)+/
 | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 	// XXX slow when lots of pages need to be included...
 | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 	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...
 | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 					var text = e.nodeValue | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					// conditional comment...
 | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 					if(e.nodeType == e.COMMENT_NODE){ | 
					
						
							|  |  |  | 						text =  /^<!--\s*\[pWiki\[(.|\n)*\]\]\s*-->$/.test(text) ? | 
					
						
							|  |  |  | 							text | 
					
						
							|  |  |  | 								.replace(/^<!--\s*\[pWiki\[/, '') | 
					
						
							|  |  |  | 								.replace(/\]\]\s*-->$/, '') | 
					
						
							|  |  |  | 							: ('<!--'+ text +'-->') | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 					/* | 
					
						
							|  |  |  | 					var t = _parseText(context, text, macro) | 
					
						
							|  |  |  | 					text != t  | 
					
						
							|  |  |  | 						&& $(e).replaceWith(t) | 
					
						
							|  |  |  | 					//*/
 | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 					$(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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 		// XXX DEBUG...
 | 
					
						
							|  |  |  | 		var t = Date.now() | 
					
						
							|  |  |  | 		console.log('>>>', context.path()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 		// merge includes...
 | 
					
						
							|  |  |  | 		parsed | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 			.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()) | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 23:28:50 +03:00
										 |  |  | 					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) | 
					
						
							| 
									
										
										
										
											2016-09-27 04:44:15 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// 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 }) |