diff --git a/wire/core/WireMarkupRegions.php b/wire/core/WireMarkupRegions.php
index a24774a4..9f6e0f16 100644
--- a/wire/core/WireMarkupRegions.php
+++ b/wire/core/WireMarkupRegions.php
@@ -37,7 +37,7 @@ class WireMarkupRegions extends Wire {
*
*/
public function find($selector, $markup, array $options = array()) {
-
+
if(strpos($selector, ',')) return $this->findMulti($selector, $markup, $options);
$defaults = array(
@@ -886,5 +886,251 @@ class WireMarkupRegions extends Wire {
$options['mode'] = 'after';
return $this->replace($selector, '', $markup, $options);
}
+
+ /**
+ * Identify and populate markup regions in given HTML
+ *
+ * To use this, you must set `$config->useMarkupRegions = true;` in your /site/config.php file.
+ * In the future it may be enabled by default for any templates with text/html content-type.
+ *
+ * This takes anything output before the opening `` that comes after it. For instance, if there's a `
` in the
+ * document, then a #content element output prior to the doctype will replace it during page render.
+ * This enables one to use delayed output as if it’s direct output. It also makes every HTML element
+ * in the output with an “id” attribute a region that can be populated from any template file. It’s
+ * a good pairing with a `$config->appendTemplateFile` that contains the main markup and region
+ * definitions, though can be used with or without it.
+ *
+ * Beyond replacement of elements, append, prepend, insert before, insert after, and remove are also
+ * supported via “pw-” prefix classes that you can add. The classes do not appear in the final output
+ * markup. When performing replacements or modifications to elements, PW will merge the attributes
+ * so that attributes present in the final output are present, plus any that were added by the markup
+ * regions. See the examples for more details.
+ *
+ * Below are some examples. Note that “main” is used as an example “id” attribute of an element that
+ * appears in the main document markup, and the examples below focus on manipulating it. The examples
+ * assume there is a `
` in the _main.php file (appendTemplateFile), and the lines in the
+ * examples would be output from a template file, which manipulates what would ultimately be output
+ * when the page is rendered.
+ * ~~~~~~
+ * Replacing and removing elements
+ *
This replaces the #main div and merges any attributes
+ *
This does the same as above
+ *
This removes #main completely
+ *
+ * Prepending and appending elements
+ *
This prepends #main with this p tag
+ *
This prepends #main and adds "bar" class to main
+ *
This appends #main with this p tag
+ *
This appends #main and adds a "foo" class to #main
+ *
Appends #main with this text + adds title attribute to #main
+ *
Appends #main with this text + removes class “baz” from #main
+ *
+ * Inserting new elements
+ *
This adds an h2 headline with this text before #main
" . $landmark;
+ $htmlDocument = str_replace($landmark, $debugNotes, $htmlDocument);
+ } else {
+ $htmlDocument .= "";
+ }
+ }
+
+ if(count($xregions) && $recursionLevel < 3) {
+ // see if they can be populated now
+ $numUpdates += $this->populate($htmlDocument, $xregions);
+ }
+
+ // if there is any leftover markup, place it above the HTML where it would usually go
+ if(strlen($leftoverMarkup)) {
+ $htmlDocument = $leftoverMarkup . $htmlDocument;
+ $numUpdates++;
+ }
+
+ return $numUpdates;
+ }
+
+ /**
+ * Determine the and populate 'mode' and 'new' properties for the given region
+ *
+ * Modifications are made directly to the given $region.
+ *
+ * The 'mode' property can be any of the following values:
+ * before, after, prepend, append, replace, remove.
+ *
+ * The 'new' property will be boolean true if the region is a new element that
+ * should be added to the document. It will be false if the region is to update
+ * an existing element in the document.
+ *
+ * The 'id' and 'class' attributes of the region may also be modified as well.
+ *
+ * @param array $region
+ * @return string
+ *
+ */
+ protected function populateRegionDetails(&$region) {
+
+ $modes = array('before', 'after', 'prepend', 'append', 'replace', 'remove');
+ $mode = '';
+ $isNewElement = false;
+ $id = isset($region['attrs']['id']) ? $region['attrs']['id'] : '';
+ $_id = $id;
+ $prefix = 'pw-';
+
+ if(strpos($id, $prefix) === 0) {
+ // i.e. pw-before-something
+ foreach($modes as $m) {
+ if(strpos($id, "$prefix$m-") === 0) {
+ list($pw, $mode, $id) = explode('-', $id, 3);
+ if($pw) {} // ignore
+ break;
+ }
+ }
+ }
+
+ if(!$mode) {
+ // attempt to get mode from class attribute
+ $replaceID = '';
+ foreach($region['classes'] as $key => $class) {
+ $replaceID = '';
+ if(strpos($class, $prefix) !== 0) continue;
+ list($pw, $m) = explode('-', $class, 2);
+ if($pw) {} // ignore
+ if(strpos($m, '-')) {
+ // i.e. pw-append-content
+ $replaceID = $id;
+ list($m, $id) = explode('-', $m, 2);
+ $isNewElement = true;
+ }
+ if(in_array($m, $modes)) {
+ $mode = $m;
+ // remove the class so it doesn't appear in original markup
+ unset($region['classes'][$key]);
+ $region['attrs']['class'] = implode(' ', $region['classes']);
+ break;
+ }
+ }
+ if($replaceID && ($isNewElement || $mode == 'before' || $mode == 'after')) {
+ $region['attrs']['data-id'] = $replaceID;
+ }
+ }
+
+ if(!$mode || !in_array($mode, $modes)) $mode = 'auto';
+
+ if(!$isNewElement) {
+ $isNewElement = empty($_id) || $mode == 'before' || $mode == 'after';
+ }
+
+ $region['new'] = $isNewElement;
+ $region['mode'] = $mode;
+ $region['note'] = "#$id ($mode" . ($isNewElement ? "+new" : "") . ")";
+ $region['attrs']['id'] = $id;
+
+ if(count($region['classes'])) $region['note'] .= "." . implode('.', $region['classes']);
+
+ return $mode;
+ }
+
+ /**
+ * Is the given HTML markup likely to have regions?
+ *
+ * @param string $html
+ * @return bool
+ *
+ */
+ public function hasRegions(&$html) {
+ if(strpos($html, ' id=') === false && strpos($html, 'pw-') === false) return false;
+ return true;
+ }
+
}
\ No newline at end of file
diff --git a/wire/modules/PageRender.module b/wire/modules/PageRender.module
index 84de0691..6231baa4 100644
--- a/wire/modules/PageRender.module
+++ b/wire/modules/PageRender.module
@@ -533,6 +533,28 @@ class PageRender extends WireData implements Module, ConfigurableModule {
$this->renderRecursionLevel--;
}
+ /**
+ * Populate markup regions directly to $html
+ *
+ * @param $html
+ *
+ */
+ protected function populateMarkupRegions(&$html) {
+ $pos = stripos($html, 'wire($markupRegions);
+ $markupRegions->populate($html, $htmlBefore);
+ }
+ }
+
/**
* Renders a field value
*
@@ -671,217 +693,6 @@ class PageRender extends WireData implements Module, ConfigurableModule {
return $tpl->render();
}
- /**
- * Identify and populate markup regions in given HTML
- *
- * To use this, you must set `$config->useMarkupRegions = true;` in your /site/config.php file.
- * In the future it may be enabled by default for any templates with text/html content-type.
- *
- * This takes anything output before the opening `` that comes after it. For instance, if there's a `
` in the
- * document, then a #content element output prior to the doctype will replace it during page render.
- * This enables one to use delayed output as if it’s direct output. It also makes every HTML element
- * in the output with an “id” attribute a region that can be populated from any template file. It’s
- * a good pairing with a `$config->appendTemplateFile` that contains the main markup and region
- * definitions, though can be used with or without it.
- *
- * Beyond replacement of elements, append, prepend, insert before, insert after, and remove are also
- * supported via “pw-” prefix classes that you can add. The classes do not appear in the final output
- * markup. When performing replacements or modifications to elements, PW will merge the attributes
- * so that attributes present in the final output are present, plus any that were added by the markup
- * regions. See the examples for more details.
- *
- * Below are some examples. Note that “main” is used as an example “id” attribute of an element that
- * appears in the main document markup, and the examples below focus on manipulating it. The examples
- * assume there is a `
` in the _main.php file (appendTemplateFile), and the lines in the
- * examples would be output from a template file, which manipulates what would ultimately be output
- * when the page is rendered.
- * ~~~~~~
- * Replacing and removing elements
- *
This replaces the #main div and merges any attributes
- *
This does the same as above
- *
This removes #main completely
- *
- * Prepending and appending elements
- *
This prepends #main with this p tag
- *
This prepends #main and adds "bar" class to main
- *
This appends #main with this p tag
- *
This appends #main and adds a "foo" class to #main
- *
Appends #main with this text + adds title attribute to #main
- *
Appends #main with this text + removes class “baz” from #main
- *
- * Inserting new elements
- *
This adds an h2 headline with this text before #main
- *
- *
This appends a div.foo to #main with this text
- *
This prepends a div.bar to #main with this text
- * ~~~~~~
- *
- * @param $out
- *
- */
- protected function populateMarkupRegions(&$out) {
-
- $pos = stripos($out, ' tag
- if(!$pos) return;
-
- $htmlBefore = substr($out, 0, $pos);
- if(!strlen(trim($htmlBefore)) || (strpos($htmlBefore, ' id=') === false && strpos($htmlBefore, 'pw-') === false)) return;
- $html = substr($out, $pos);
- if(strpos($html, ' id=') === false) return;
-
- $markupFinder = new WireMarkupRegions();
- $regions = $markupFinder->find(".pw-*, id=", $htmlBefore, array(
- 'verbose' => true,
- 'leftover' => true
- ));
-
- $leftoverMarkup = trim($regions["leftover"]);
- unset($regions["leftover"]);
- if(!count($regions)) return;
-
- $xregions = array(); // regions that weren't populated
- $populatedIDs = array();
- $numUpdates = 0;
- $debug = $this->wire('config')->debug && $this->wire('user')->isSuperuser();
-
- foreach($regions as $regionKey => $region) {
-
- $this->populateMarkupRegionMode($region);
- $mode = $region['mode'];
- $id = $region['attrs']['id'];
- $regionHTML = $region['region'];
- $mergeAttr = $region['attrs'];
- unset($mergeAttr['id']);
-
- if($region['new']) {
- // element is newly added element not already present
- $mergeAttr = array();
- $regionHTML = $region['html'];
- $attrs = $region['attrs'];
- unset($attrs['id']);
- if(isset($attrs['data-id'])) {
- $attrs['id'] = $attrs['data-id'];
- unset($attrs['data-id']);
- }
- $attrStr = count($attrs) ? ' ' . $markupFinder->renderAttributes($attrs, false) : '';
- if(!strlen(trim($attrStr))) $attrStr = '';
- $regionHTML = str_replace($region['open'], "<$region[name]$attrStr>", $regionHTML);
- }
-
- // if the id attribute doesn't appear in the html, skip it
- if(!$markupFinder->hasAttribute('id', $id, $html)) {
- $xregions[$regionKey] = $region;
- unset($regions[$regionKey]);
- continue;
- }
-
- // update the markup
- $html = $markupFinder->update("#$id", $regionHTML, $html, array(
- 'mode' => $mode,
- 'mergeAttr' => $mergeAttr,
- ));
-
- if($debug) {
- $populatedIDs[] = ltrim($regionKey, '#') . " => #$id ($mode)";
- }
-
- $numUpdates++;
- }
-
- if($debug) {
- $bull = "\n • ";
- $none = "None";
- $html .= "";
- }
-
- // if there is any leftover markup, output it above the HTML where it would usually go
- if(strlen($leftoverMarkup)) {
- $html = $leftoverMarkup . $html;
- $numUpdates++;
- }
-
- if($numUpdates) $out = $html;
- }
-
- /**
- * Determine the and populate 'mode' for the given region
- *
- * Note that this may modify the 'id' and/or 'class' attributes of $region
- * and it also adds a 'new' property to indicate whether the region is a new
- * element that should be added.
- *
- * @param array $region
- * @return string
- *
- */
- protected function populateMarkupRegionMode(&$region) {
-
- $modes = array('before', 'after', 'prepend', 'append', 'replace', 'remove');
- $mode = '';
- $isNewElement = false;
- $id = isset($region['attrs']['id']) ? $region['attrs']['id'] : '';
- $_id = $id;
- $prefix = 'pw-';
-
- if(strpos($id, $prefix) === 0) {
- // i.e. pw-before-something
- foreach($modes as $m) {
- if(strpos($id, "$prefix$m-") === 0) {
- list($pw, $mode, $id) = explode('-', $id, 3);
- if($pw) {} // ignore
- break;
- }
- }
- }
-
- if(!$mode) {
- // attempt to get mode from class attribute
- $replaceID = '';
- foreach($region['classes'] as $key => $class) {
- $replaceID = '';
- if(strpos($class, $prefix) !== 0) continue;
- list($pw, $m) = explode('-', $class, 2);
- if($pw) {} // ignore
- if(strpos($m, '-')) {
- // i.e. pw-append-content
- $replaceID = $id;
- list($m, $id) = explode('-', $m, 2);
- $isNewElement = true;
- }
- if(in_array($m, $modes)) {
- $mode = $m;
- // remove the class so it doesn't appear in original markup
- unset($region['classes'][$key]);
- $region['attrs']['class'] = implode(' ', $region['classes']);
- break;
- }
- }
- if($replaceID && ($isNewElement || $mode == 'before' || $mode == 'after')) {
- $region['attrs']['data-id'] = $replaceID;
- }
- }
-
- if(!$mode || !in_array($mode, $modes)) $mode = 'auto';
-
- if(!$isNewElement) {
- $isNewElement = empty($_id) || $mode == 'before' || $mode == 'after';
- }
-
- $region['new'] = $isNewElement;
- $region['mode'] = $mode;
- $region['attrs']['id'] = $id;
-
- return $mode;
- }
-
/**
* Provide a disk cache clearing capability within the module's configuration screen
*