<commit_before>require('jquery-ancestors');

var selectron = require('./selectron');

var blockTags = [ 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI' ];      

var descendants = require('./descendants');

function align(element, alignment) {
	var containedChildren = selectron.contained(element, 1, 1, true);
	containedChildren.forEach(function(child) {
		if(!$(child).is('ul, ol')) $(child).css('text-align', alignment);
	});
}

function block(element, tag) {
	var contained = selectron.contained(element, blockTags.join(','), null, true).filter(function(node) {
			// this is to filter out LI with nested lists where only text in the nested
			// list is selected, not text in the actual LI tag siblings to the nested <ul>)
			//
			// this is to fix error that occurs if you have selected LI from nested list, but not any text
			// nodes in the LI containing the nested list. The LI containing 
			return node.nodeName !== 'LI' || $(node).children('UL,OL').length === 0 || selectron.containsSome(_.initial(node.childNodes), true);
		}),
		newBlocks = [],
		$startBlock = $(_.first(contained)),
		$endBlock = $(_.last(contained)),
		startOffset = selectron.offset($startBlock[0], 'start'),
		endOffset = selectron.offset($endBlock[0], 'end'),
		$ref;

	// $ref is the DOM element which we place our new
	// blocks before. if it is undefined, new blocks will
	// be appended to 'element'.
	
	if($endBlock.is('LI')) {
		// if endblock is in a list, we have to do some crazyness
		
		// begin by getting a reference to the ancestor lists
		// NOTE: $startList might not be a list. if $startBlock is not
		// a list, the $startList will be $startBlock (since all block
		// elements except LI are children of 'element'
		var $startList = $startBlock.closest('.spytext-field > *'),
			$endList = $endBlock.closest('.spytext-field > *');

		if(!$startList.is($endList)) {
			// if $startList and $endList are not the same
			// we place all new blocks before $endList
			$ref = $endList;
		} else if($endBlock[0].nextSibling || $endBlock.children('UL,OL').length > 0) {
			// if endBlock has following siblings or has a nested list,
			// create a new list and place it after startList.
			// place all new blocks before this new list
			$ref = $('<' + $endList[0].tagName + '>').insertAfter($startList).append($endBlock.children('UL,OL').children()).append($endBlock.nextAll());
		} else {
			// $startList is $endList and last selected LI is last child and has no 
			// nested list. simply place all new blocks after $startList/endList, ie
			// before the next element
			$ref = $endList.next();
		}
	} else {
		// $endBlock is not a list, simply place
		// new elements after $endBlocks next sibling
		$ref = $endBlock.next();
	}

	contained.forEach(function(child,i){
		var $newBlock = $('<' + tag + '>');

		// place the newBlock before the reference,
		// or append it to element
		if($ref.length > 0) 
			$ref.before($newBlock);
		else
			$(element).append($newBlock);


		newBlocks.push($newBlock.append(child.childNodes)[0]);

		// remove parent if child has no siblings,
		// otherwise simply remove the child
		if(!child.nextSibling && !child.previousSibling)
			$(child).parent().remove();
		else
			$(child).remove();
	});

	$(':empty:not("BR")', element).remove();
	
	// set the selection
	selectron.set({
		start: {
			ref: _.first(newBlocks),
			offset: startOffset
		},
		end: {
			ref: _.last(newBlocks),
			offset: endOffset
		},
	});
}

function clearTextNodes(element) {
	function isBlock(node) {
		return node && node.nodeType === 1 && !getComputedStyle(node).display.match(/inline/);
	}
	descendants(element, 3).forEach(function(textNode) {
		if(isBlock(textNode.previousSibling) || isBlock(textNode.nextSibling)) {
			textNode.textContent = textNode.textContent.trim();
			if(textNode.textContent.match(/^\s*$/)) {
				$(textNode).remove();
			} else if(textNode.parentNode.nodeName !== 'LI') {
				$(textNode).wrap('<p>');
			}
		}
	});

	if(!element.firstChild) {
		$(element).append('<p><br /></p>');
	}

	element.normalize();

	setBR(element);
}

function deleteRangeContents(element, rng) {
	rng = rng || selectron.range();


	var $startContainer = $(rng.startContainer),
		$startBlock = $startContainer.closest(blockTags.join(','), element),
		$endBlock = $(rng.endContainer).closest(blockTags.join(','), element),
		startPosition = {
			ref: $startBlock[0],
			offset: selectron.offset($startBlock[0], 'start')
		};

	rng.deleteContents();

	if(!$startBlock.is($endBlock)) {
		if($endBlock.is('LI')) {
			var $list;
			
			if($startBlock.is('LI')) {
				$list = $startBlock.parent();
			} else {
				$list = $endBlock.closest('.spytext-field > *');
			}

			var $nestedList = $endBlock.children('UL,OL');
			if($nestedList.length === 1) {
				if($startBlock.is('LI'))
					$startBlock.append($nestedList);
				else
					$list.append($nestedList.children());
			}

			if(!$list.is($endBlock.parent()) && $endBlock[0].nextSibling) {
				$list.append($endBlock.nextAll());
			}
		} 

		$startContainer.after($endBlock[0].childNodes);

		$endBlock.remove();
	}

	setBR($startBlock[0]);

	// deleteRangeContents will leave empty LI and UL. remove them.
	$(':empty:not("BR")', element).each(function() {
		var $el = $(this);
		var $parent;
		while($el.is(':empty')) {
			$parent = $el.parent();
			$el.remove();
			$el = $parent;
		}
	});

	selectron.set(startPosition);
}

function indent(element, outdent){
	var blocks = selectron.contained(element, blockTags.join(','), null, true),
		startBlock = _.first(blocks),
		endBlock = _.last(blocks),
		startOffset = selectron.offset(startBlock, 'start'),
		endOffset = selectron.offset(endBlock, 'end');<commit_msg>Better filter.<commit_after>require('jquery-ancestors');

var selectron = require('./selectron');

var blockTags = [ 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI' ];      

var descendants = require('./descendants');

function align(element, alignment) {
	var containedChildren = selectron.contained(element, 1, 1, true);
	containedChildren.forEach(function(child) {
		if(!$(child).is('ul, ol')) $(child).css('text-align', alignment);
	});
}

function block(element, tag) {
	var contained = selectron.contained(element, blockTags.join(','), null, true).filter(function(node) {
			// this is to filter out LI with nested lists where only text in the nested
			// list is selected, not text in the actual LI tag siblings to the nested <ul>)
			//
			// this is to fix error that occurs if you have selected LI from nested list, but not any text
			// nodes in the LI containing the nested list. The LI containing 
			return node.nodeName !== 'LI' || $(node).children('UL,OL').length === 0 || selectron.containsSome(_.initial(node.childNodes), true);
		}),
		newBlocks = [],
		$startBlock = $(_.first(contained)),
		$endBlock = $(_.last(contained)),
		startOffset = selectron.offset($startBlock[0], 'start'),
		endOffset = selectron.offset($endBlock[0], 'end'),
		$ref;

	// $ref is the DOM element which we place our new
	// blocks before. if it is undefined, new blocks will
	// be appended to 'element'.
	
	if($endBlock.is('LI')) {
		// if endblock is in a list, we have to do some crazyness
		
		// begin by getting a reference to the ancestor lists
		// NOTE: $startList might not be a list. if $startBlock is not
		// a list, the $startList will be $startBlock (since all block
		// elements except LI are children of 'element'
		var $startList = $startBlock.closest('.spytext-field > *'),
			$endList = $endBlock.closest('.spytext-field > *');

		if(!$startList.is($endList)) {
			// if $startList and $endList are not the same
			// we place all new blocks before $endList
			$ref = $endList;
		} else if($endBlock[0].nextSibling || $endBlock.children('UL,OL').length > 0) {
			// if endBlock has following siblings or has a nested list,
			// create a new list and place it after startList.
			// place all new blocks before this new list
			$ref = $('<' + $endList[0].tagName + '>').insertAfter($startList).append($endBlock.children('UL,OL').children()).append($endBlock.nextAll());
		} else {
			// $startList is $endList and last selected LI is last child and has no 
			// nested list. simply place all new blocks after $startList/endList, ie
			// before the next element
			$ref = $endList.next();
		}
	} else {
		// $endBlock is not a list, simply place
		// new elements after $endBlocks next sibling
		$ref = $endBlock.next();
	}

	contained.forEach(function(child,i){
		var $newBlock = $('<' + tag + '>');

		// place the newBlock before the reference,
		// or append it to element
		if($ref.length > 0) 
			$ref.before($newBlock);
		else
			$(element).append($newBlock);


		newBlocks.push($newBlock.append(child.childNodes)[0]);

		// remove parent if child has no siblings,
		// otherwise simply remove the child
		if(!child.nextSibling && !child.previousSibling)
			$(child).parent().remove();
		else
			$(child).remove();
	});

	$(':empty:not("BR")', element).remove();
	
	// set the selection
	selectron.set({
		start: {
			ref: _.first(newBlocks),
			offset: startOffset
		},
		end: {
			ref: _.last(newBlocks),
			offset: endOffset
		},
	});
}

function clearTextNodes(element) {
	function isBlock(node) {
		return node && node.nodeType === 1 && !getComputedStyle(node).display.match(/inline/);
	}
	descendants(element, 3).forEach(function(textNode) {
		if(isBlock(textNode.previousSibling) || isBlock(textNode.nextSibling)) {
			textNode.textContent = textNode.textContent.trim();
			if(textNode.textContent.match(/^\s*$/)) {
				$(textNode).remove();
			} else if(textNode.parentNode.nodeName !== 'LI') {
				$(textNode).wrap('<p>');
			}
		}
	});

	if(!element.firstChild) {
		$(element).append('<p><br /></p>');
	}

	element.normalize();

	setBR(element);
}

function deleteRangeContents(element, rng) {
	rng = rng || selectron.range();


	var $startContainer = $(rng.startContainer),
		$startBlock = $startContainer.closest(blockTags.join(','), element),
		$endBlock = $(rng.endContainer).closest(blockTags.join(','), element),
		startPosition = {
			ref: $startBlock[0],
			offset: selectron.offset($startBlock[0], 'start')
		};

	rng.deleteContents();

	if(!$startBlock.is($endBlock)) {
		if($endBlock.is('LI')) {
			var $list;
			
			if($startBlock.is('LI')) {
				$list = $startBlock.parent();
			} else {
				$list = $endBlock.closest('.spytext-field > *');
			}

			var $nestedList = $endBlock.children('UL,OL');
			if($nestedList.length === 1) {
				if($startBlock.is('LI'))
					$startBlock.append($nestedList);
				else
					$list.append($nestedList.children());
			}

			if(!$list.is($endBlock.parent()) && $endBlock[0].nextSibling) {
				$list.append($endBlock.nextAll());
			}
		} 

		$startContainer.after($endBlock[0].childNodes);

		$endBlock.remove();
	}

	setBR($startBlock[0]);

	// deleteRangeContents will leave empty LI and UL. remove them.
	$(':empty:not("BR")', element).each(function() {
		var $el = $(this);
		var $parent;
		while($el.is(':empty')) {
			$parent = $el.parent();
			$el.remove();
			$el = $parent;
		}
	});

	selectron.set(startPosition);
}

function indent(element, outdent){
	var blocks = selectron.contained(element, blockTags.join(','), null, true).filter(function(node) {
			return node.nodeName !== 'LI' || $(node).children('UL,OL').length === 0 || selectron.containsSome(_.initial(node.childNodes), true) || selectron.containsEvery(descendants(node, function(node) { return node.nodeType === 1 && !node.previousSibling; }, null, true), true);
		}),
		startBlock = _.first(blocks),
		endBlock = _.last(blocks),
		startOffset = selectron.offset(startBlock, 'start'),
		endOffset = selectron.offset(endBlock, 'end');
{'fraction_non_alphanumeric': 0.17627408993576016, 'fraction_numerical': 0.004282655246252677, 'mean_word_length': 3.8792310906811536, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '703045', 'n_tokens_mistral': 4251, 'n_tokens_neox': 3798, 'n_words': 944}