=== MITS No Lonely Words ===
Contributors: wpaluchmits
Tags: typography, nbsp, polish, widows, orphans
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 2.2.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Author: MITS
Author URI: https://mits.pl

Polish typography fix. Prevents lonely conjunctions at line ends. Processes content once on save, zero overhead on frontend.

== Description ==

**No Lonely Words** solves a common Polish typography problem: single-letter conjunctions and prepositions (a, i, w, z, o, u…) left alone at the end of a line. The plugin replaces the trailing space after these words with a non-breaking space (`&nbsp;`) so they always stay attached to the following word.

= How it works =

Content is processed **once when a post is saved** and the result is stored in post meta. On every subsequent frontend request the plugin reads directly from WordPress's in-memory meta cache (no regex, no extra database queries). For posts created before activation, inline processing runs on the first view and is cached for the rest of that request.

= Supported words =

Polish conjunctions and prepositions: a, i, w, z, o, u, do, ku, na, od, po, we, za, ze, ale, czy, bo, nie, już, też, się, jak, co, to, oraz, lecz, więc, przez, przed, przy, nad, pod, bez, dla, m.in., np., tj. and more.

= Supported filters =

* `the_title`
* `the_content`
* `the_excerpt`
* `woocommerce_short_description`
* `term_description`
* `widget_text_content`
* `single_cat_title`

= Requirements =

* PHP 8.0+, WordPress 6.0+

Developed and maintained by **[MITS](https://mits.pl)**, a WordPress agency. We build fast, reliable WordPress sites for businesses in Poland and beyond.

= Privacy =

This plugin does not collect any personal data and does not make any external HTTP requests.

== Installation ==

1. Upload the `no-lonely-words` folder to `/wp-content/plugins/`.
2. Activate the plugin via **Plugins → Installed Plugins**.
3. Re-save any existing posts to pre-process their content, or let the plugin handle them inline on the first view.

== Frequently Asked Questions ==

= Does it affect the editor? =

No. The plugin only modifies the **output** of content filters. The raw post content in the database and in the editor stays unchanged.

= Does it work with WooCommerce? =

Yes. Product titles, short descriptions, and full descriptions are all supported via dedicated WooCommerce filters.

= Does it work with caching plugins? =

Yes. Processed content is stored in post meta (a standard WordPress database table), so it survives full-page cache flushes and is always correct regardless of caching setup.

= How do I process existing posts? =

Go to **Posts → All Posts** (or Products), select all, and use the **Bulk Edit → Update** action. This triggers `save_post` for each post, which processes and stores the content. Alternatively, any post re-saved individually is processed immediately.

= Does it work with WPML or Polylang? =

The plugin uses standard WordPress filters and post meta, so it is compatible with WPML and Polylang. Each language version of a post is stored and processed independently.

== Screenshots ==

1. No admin interface needed. The plugin works automatically in the background.

== Changelog ==

= 2.2.0 =
* `the_content` filter now skips pages built with Elementor, Beaver Builder, or Divi. These builders replace `the_content` at priority 12 with their own fully-rendered HTML; running `mitsnolo_process` on that output corrupted widget markup (carousel, nested sections). The fix has no effect on standard posts/pages.
* Each save now stores the previous version of the cache in `_no_lonely_words[prev]`.
* Added admin metabox (post sidebar) with "Wyczyść cache" and "Przywróć poprzednią" buttons for manual revert.

= 2.1.0 =
* Renamed all functions and constants to use the `mitsnolo_` prefix (4+ characters).
* Added `wp_kses_post()` escaping to content/excerpt filter callbacks.
* Added `wp_kses()` escaping with inline-tag allowlist to title/heading filter callbacks.

= 2.0.0 =
* Rewrite: process on `save_post`, store result in postmeta. Zero regex on frontend requests.
* Static in-request cache layer for repeated filter calls within a single page load.
* Fallback inline processing for posts without cached meta (e.g. created before activation).
* `the_title` filter now correctly receives the post ID as the second argument.

= 1.0.0 =
* Initial release.

== Upgrade Notice ==

= 2.0.0 =
Major performance improvement. Content is now processed once on save instead of on every frontend request. Re-save existing posts to pre-populate the cache.
