/** * Is this insertion spot right before or after a disallowed element? * * Loops through each of the "from_element" defined at speed bump * registration, and blocks insertion if either the previous or following * paragraph contains any of them. * * To add additional element constraints, you must define your own element * class which implements the method `paragraph_not_contains_element`. * The class name will be the uppercased version of the string passed in * from_element at speed bump registration. * * For example if you wanted to add an "hr" contraint, define a class at * `\Speed_Bumps\Constraints\Elements\Hr` with has a method called * "paragraph_not_contains_element" which checks that a string of text * doesn't contain an `<hr>`. */ public static function meets_minimum_distance_from_elements($can_insert, $context, $args, $already_inserted) { // Support passing an integer here, which will be treated as a unit of "paragraphs" if (is_int($args['from_element'])) { $args['from_element'] = array('paragraphs' => $args['from_element']); } if (!is_array($args['from_element'])) { return $can_insert; } $defaults = array_flip(array('paragraphs', 'words', 'characters')); $base_distance_constraints = array_intersect_key($args['from_element'], $defaults); $from_element = array_diff_key($args['from_element'], $defaults); if (!empty($from_element)) { foreach ($from_element as $key => $val) { $distance_constraints = $base_distance_constraints; if (is_int($key)) { $element_to_check = Factory::build(ucfirst($val)); } else { $element_to_check = Factory::build(ucfirst($key)); foreach (array('paragraphs', 'words', 'characters') as $unit) { if (isset($val[$unit])) { $distance_constraints[$unit] = $val[$unit]; } } } foreach (array_filter($distance_constraints) as $unit => $measurement) { $paragraphs_to_check = Text::content_within_distance_of($context['parts'], $context['index'], $unit, $measurement); foreach ($paragraphs_to_check as $paragraph) { if (!$element_to_check->paragraph_not_contains_element($paragraph)) { $can_insert = false; } } } } } return $can_insert; }
/** * Is this speed bump far enough away from others to insert here? * * Blocks a speed bump from being inserted if it doesn't mean the * distance defined in the speed bump's 'from_speedbump' registration * arguments. */ public static function meets_minimum_distance_from_other_inserts($can_insert, $context, $args, $already_inserted) { // Support passing an integer here, which will be treated as a unit of "paragraphs" if (is_int($args['from_speedbump'])) { $args['from_speedbump'] = array('paragraphs' => $args['from_speedbump']); } if (!is_array($args['from_speedbump'])) { return $can_insert; } if (is_int($args['from_speedbump'])) { $base_distance_constraints = array('paragraphs' => $args['from_speedbump']); $from_speed_bump = array(); } else { $defaults = array('paragraphs' => 1, 'words' => null, 'characters' => null); $base_distance_constraints = array_intersect_key((array) $args['from_speedbump'], $defaults); $from_speedbump = array_diff_key($args['from_speedbump'], $defaults); } $this_paragraph_index = $context['index']; if (count($already_inserted)) { foreach ($already_inserted as $speed_bump) { $distance_constraints = $base_distance_constraints; if (isset($from_speedbump[$speed_bump['speed_bump_id']]) && is_array($from_speedbump[$speed_bump['speed_bump_id']])) { foreach (array('paragraphs', 'words', 'characters') as $unit) { if (isset($from_speedbump[$speed_bump['speed_bump_id']][$unit])) { $distance_constraints[$unit] = $from_speedbump[$speed_bump['speed_bump_id']][$unit]; } } } $distance = Text::content_between_points($context['parts'], $speed_bump['index'], $context['index']); foreach ($distance_constraints as $unit => $measurement) { if (isset($args['from_speedbump'][$speed_bump['speed_bump_id']]) && isset($args['from_speedbump'][$speed_bump['speed_bump_id']][$unit])) { $measurement = $args['from_speedbump'][$speed_bump['speed_bump_id']][$unit]; } if ($measurement && Comparison::content_less_than($unit, $measurement, $distance)) { $can_insert = false; } } } } return $can_insert; }
/** * Inject speed bumps into a block of text, like post content. * * Can be called directly, like `Speed_Bumps()->insert_speed_bumps( $post->post_content );`. * * More common usage is by adding this function to a filter: * `add_filter( 'the_content', array( Speed_Bumps(), 'insert_speed_bumps' ), 1 );` * (Note the early priority, as it should be attached before `wpautop` runs.) * * @param string $the_content A block of text. Expected to be pre-texturized. * @return string The text with all registered speed bumps inserted at appropriate locations if possible. */ public function insert_speed_bumps($the_content) { $output = array(); $already_inserted = array(); $parts = Text::split_paragraphs($the_content); $total_paragraphs = count($parts); foreach ($parts as $index => $part) { $output[] = $part; $context = array('index' => $index, 'prev_paragraph' => $part, 'next_paragraph' => $index + 1 < $total_paragraphs ? $parts[$index + 1] : '', 'total_paragraphs' => $total_paragraphs, 'the_content' => $the_content, 'parts' => $parts); foreach ($this->get_speed_bumps() as $id => $args) { if (apply_filters('speed_bumps_' . $id . '_constraints', true, $context, $args, $already_inserted)) { $content_to_be_inserted = call_user_func($args['string_to_inject'], $context, $already_inserted); $output[] = $content_to_be_inserted; $already_inserted[] = array('index' => $index, 'speed_bump_id' => $id, 'inserted_content' => $content_to_be_inserted); } } } return implode(PHP_EOL . PHP_EOL, $output); }