Moving script, style and link tags to document head

I would like to know if there is a better way to approach the following the problem.

A have several possible layers of modules that may each contain style, link or script tags. The reason for this is to so that styles, link and script specific to the module that they belong to may be defined directly in the template which results in invalid HTML.

A simple example of this could be the module Index defining a specific style criteria for its contents (shown below).


<style type="text/css">
#mod-index p {
 color: red;
}
</style>

<div id="mod-index">
	<p>Index</p>
</div>

Now left untouched embedding this template into the master template results in the below HTML.


...
<body>

<div id="container">

	<style type="text/css">
#mod-index p {
 color: red;
}
</style>

<div id="mod-index">
	<p>Index</p>
</div>
</div>

</body>
</html>

So my current solution to this problem is upon getting the HTML string using the below code to extract and move all script, style and link tags defined in the body to the head.


...
/*
* move all style, link and script tags w/ contents to head
*/
GLOBAL $s;
$s = array();
$strHTML = preg_replace_callback('/(<style.*?>.*?<\\/style>|<link.*?>|<script.*?>.*?<\\/script>)/si',create_function('$m','GLOBAL $s;$s[] = $m[0];return "";'),$strHTML);		
if(!empty($s)) $strHTML = preg_replace('/<\\/head>/',implode("\
",$s)."\
</head>",$strHTML,1);
		
echo $strHTML;

The below code included would then yield the below from the previous example.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8">
	<title>Home</title>
<style type="text/css">
#mod-index p {
 color: red;
}
</style>
</head>
<body>

<div id="container">

	

<div id="mod-index">

	<p>Index</p>
</div>
</div>

</body>
</html>

Notice how the style tag and its contents have been correctly moved to head of the document - perfect.

However, the primary problem I have with the solution is that it isn’t 100% concrete. Given user content that “may” match patterns the entire page would break. So I’m wondering if perhaps there is a more concrete way to achieve the above without breaking page when user content may match the patterns?

The below represents a concrete solution to the problem but its not very pretty or easy to understand for those lacking PHP knowledge whereas, the previous cleanly and automatically modifies everything.


<?php
$this->addCss(
'<style type="text/css">
#mod-index p {
 color: red;
}
</style>';
);
?>

<div id="mod-index">
	<p>Index</p>
</div>

Why not separate the HTML and CSS for each template file into two separate files.

template1.html
template1.css

Include each part into the correct part of the document. Depending on how your templating system is setup you could even automatically attempt to find a matching css document and include it where appropriate for the HTML requests.

Agreed, your implementation is clearly indicating that you have a natural divide between mark-up and styling.

Once you separate these objects, you’ll find it quite trivial to render them as required, but I’m guessing you already know this Oddz… :wink:

The system here is built to support module reuse throughout multiple sites. One example of the purpose behind this is so that a single default module called Login can be created and reused in every site after without writing anymore code. So the problem becomes that that styles and scripts that pertain to the default module which doesn’t belong to any particular site need to be defined directly in the module so that they are automatically included for any site that uses the Login module. Then the programmer has the option of further modifying the default CSS for the Login module through CSS defined in the master template which belongs to the site which they are defining.

Now here is an example of this. The code is currently held in the file Default/Template/Login.php.


<link rel="stylesheet" type="text/css" href="<?php echo $base_url; ?>/css/login.css"> 

<?php $objModule->paintHeaderMessages(); ?>
<form id="login-<?php echo $instance_num; ?>" name="<?php echo $frm_name; ?>" action="<?php echo $frm_action; ?>" method="<?php echo $frm_method; ?>">
	<fieldset>
		<legend>Login</legend>
		<ul>
			<li class="username">
				<label for="login-<?php echo $instance_num; ?>-username"><?php echo $frm_config['username']['label']; ?><?php if($frm_config['username']['required'] === 'Y') { ?><span class="required">*</span><?php } ?></label>
				<input type="text" name="<?php echo $frm_name; ?>[username]" value="<?php echo $frm_values['username']; ?>" maxlength="<?php echo $frm_config['username']['max']; ?>" id="login-<?php echo $instance_num; ?>-username" class="">
				<?php if(isset($frm_errors['username'])) { ?><p><?php echo $frm_errors['username']; ?></p><?php } ?>
			</li>
			<li class="password">
				<label for="login-<?php echo $instance_num; ?>-password"><?php echo $frm_config['password']['label']; ?><?php if($frm_config['password']['required'] === 'Y') { ?><span class="required">*</span><?php } ?></label>
				<input type="password" name="<?php echo $frm_name; ?>[password]" value="<?php echo $frm_values['password']; ?>" maxlength="<?php echo $frm_config['password']['max']; ?>" id="login-<?php echo $instance_num; ?>-password" class="">
				<?php if(isset($frm_errors['password'])) { ?><p><?php echo $frm_errors['password']; ?></p><?php } ?>
			</li>
			<li class="login">
				<input type="submit" name="<?php echo $frm_name; ?>[login]" value="Login">
			</li>
		</ul>
	</fieldset>
</form>

Now nothing there is really important to the situation at hand besides the link. The link defines a single style sheet that pertains directly to this module regardless of the site that it is included in.

When it is all said and done that module is executed and then the HTML is held in a variable called $LOGIN that be echoed inside any template that belongs to the site. In this example I’m dumping the $LOGIN out inside the master template.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8">
	<title><?php echo $TITLE; ?></title>
</head>
<body>

<div id="container">
	
	<?php echo $LOGIN; ?>
	<?php echo $REQUEST_CONTENT; ?>

</div>

</body>
</html>

Now without any special handling of the links, css or script tags I end up with those items in the body rather then the head. This may be fine for the script tags but style tags inside the body yield invalid code. So there needs to be some way to move them to the head regardless of the number of modules that executed outside of the main yield/view held in the $REQUEST_CONTENT variable.

I could define the CSS at the application level to avoid this problem but that means that anyone working on template layer would need to be aware and able to change the module (PHP) layer. This is not something I want at all. I would like to keep all presentational attributes within the template itself rather then introducing its direct management into the application to avoid a designers need modify the module(PHP) code. So there lies the problem…

So I believe the issue here that a divide ceases to exist between mark-up and styling because they are all treated as one?

I would say so, you’re already having to create hacks on your own application, a clear sign you’re doing something wrong. No? :wink:

If you have control over the main page output (which it appears you do), just have this call upon all registered modules for their CSS and add this to your HEAD section.

Personally, I’d have a separate file for the CSS, this would maintain user familiarity and enable easier maintenance.

How your application uses this data is a different matter of course. but if you’re after ease of use and maintenance it would be how I would approach it.

Obligatory code nonsense.

<?php
class Login_Module extends Application_Widget implements IWidgetable
{
    public function getCss(){
        return 'css';
    }
    
    public function getHtml(){
        return 'html';
    }
}
?>

<html>
    <head>
        $application->widgetCollection->renderCss();
    </head>
</html>

What you provided as an example is similar to how it currently handles problem but there still needs to be a way to extract the data from the template unless the data is explicitly defined in the module. However, if the CSS is defined within the module that means that anyone working on the template layer would need to be aware of the Module layer and able to manipulate it. This is not something I want. I would like all presentational attributes to be defined inside the template so that they can easy to managed from within hat layer so that designers need not have any knowledge of any of the Module level code.

How your application aggregates, differentiates and then displays the CSS is upto you.

At runtime, your application would iterate through all the registered/requested modules, aggregate the CSS required (default or not) and assign it to $__CSS, same goes for the module’s html template, in this case, $__LOGIN.

Given this, how would the designer need to know anything about the module system’s behaviour?

#master template

<html>
    <head>
        <title>
            $__TITLE
        </title>
        $__CSS
    </head>
    <div class="widget">
        $__LOGIN
    </div>
</html>

#login html template = /application/modules/login/template.html

<form id="login">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <input type="submit" name="login" value="login" />
</form>

#login css template = /application/modules/login/template.css

input{
    padding: 5px;
}

Yeah, then I need to use some type of naming/directory schema which I also don’t want. The CSS for the given module could reside anywhere not just in the directory that it pertains. However, I see the obvious advantage. Still though I would like to keep everything as flexible as possible. Having the CSS be defined directly in the template it pertains is the most flexible solution while your previous solution is very rigid.

Sorry, but I may of misled you. The point I was trying to make was that it’s infinitely more flexible having these elements as separate entities within the system (because they are, they have different roles and responsibilities).

Your module/widget template is merely a collection of these elements in a format your user is familiar with.

How you store/retrieve them is again, at your discretion.

xml, file, database …

<module>
    <html><![CDATA[ ]]></html>
    <css><![CDATA[ ]]></css>
    <javascript><![CDATA[ ]]></javascript>
<module>

The problem with that solution is that it doesn’t support dynamic JavaScript and CSS unless the CSS and JavaScript is defined within the module class or some other application level class rather then the template file.

The best solution I can come up with at this point is to extract style and link tags upon execution of each template. Then add them to the master template when it is being executed. The script tags will be left alone but the intention may very well be that the script should reside exactly where it has been placed and moving it to the head may break something.


class Template extends Resource {
...

	/*
	* styles and links extracted from modules
	* to be placed in head of master template file.
	*/
	,$_arrStyles
	,$_arrLinks;

 ...

	/*
	* Get contents of a Template
	*
	* @param str template file path
	* @return str template contents
	*/
	public function fetch($strTemplateFile,Module $objModule=null) {
	
        ...
		
		ob_start();
		eval('?>'.$strFileContents);
		$strTemplateContents = ob_get_contents();
		
		/*
		* Remove all style and link tags from document
		* and store in array to be moved to head of master template.
		*/
		if($objModule !== null) {
			$strTemplateContents = preg_replace_callback('/(<style.*?>.*?<\\/style>|<link.*?>)/si',array($this,'parseTags'),$strTemplateContents);
		}
		
		ob_end_clean();
		
		/*
		* When executing master template embed links and styles in head of document
		*/
		if($objModule === null) {
			return preg_replace('/<\\/head>/',"\
".implode("\
",$this->_arrLinks)."\
".implode("\
",$this->_arrStyles)."\
</head>",$strTemplateContents,1);
		}
		
		return $strTemplateContents;
	
	}
	
	/*
	* Parse out and isolate style and link tags
	* from executed template contents.
	*
	* @param arr pattern matches
	* @return str empty string
	*/
	public function parseTags($arrMatches) {
		
		/*
		* Determine appropriate array to place data in
		*/
		if(strpos($arrMatches[0],'<style') === 0) {
			$this->_arrStyle[] = $arrMatches[0];
		} else {
			$this->_arrLinks[] = $arrMatches[0];
		}
		
		return '';
	}
}

Would you mind explaining how this is so? Maybe define ‘dynamic’ and how you would be unable to add this to your template yet be able to add the module specific entity.

Well, it seems we’ve not progressed anywhere.:confused:

Although I must admit, I do find it a little worrying that you’re happy to settle for ‘hacking’ your own application to add a feature. :frowning:

None of the options I provided may indeed be suitable, or quite possibly relevant, but you should never have to hack your own application.

IMO*.

*which is probably wrong/misguided/plain ol’ silly. :lol:

Here is a simple example using the instance number for identification so that several of these modules can be executed but be independent of each other.


<style type="text/css">
#login-<?php echo $instance_num;  ?> {
	background-color: red;
}
</style>

<?php $objModule->paintHeaderMessages(); ?>
<form id="login-<?php echo $instance_num; ?>" name="<?php echo $frm_name; ?>" action="<?php echo $frm_action; ?>" method="<?php echo $frm_method; ?>">
	<fieldset>
		<legend>Login</legend>
		<ul>
			<li class="username">
				<label for="login-<?php echo $instance_num; ?>-username"><?php echo $frm_config['username']['label']; ?><?php if($frm_config['username']['required'] === 'Y') { ?><span class="required">*</span><?php } ?></label>
				<input type="text" name="<?php echo $frm_name; ?>[username]" value="<?php echo $frm_values['username']; ?>" maxlength="<?php echo $frm_config['username']['max']; ?>" id="login-<?php echo $instance_num; ?>-username" class="">
				<?php if(isset($frm_errors['username'])) { ?><p><?php echo $frm_errors['username']; ?></p><?php } ?>
			</li>
			<li class="password">
				<label for="login-<?php echo $instance_num; ?>-password"><?php echo $frm_config['password']['label']; ?><?php if($frm_config['password']['required'] === 'Y') { ?><span class="required">*</span><?php } ?></label>
				<input type="password" name="<?php echo $frm_name; ?>[password]" value="<?php echo $frm_values['password']; ?>" maxlength="<?php echo $frm_config['password']['max']; ?>" id="login-<?php echo $instance_num; ?>-password" class="">
				<?php if(isset($frm_errors['password'])) { ?><p><?php echo $frm_errors['password']; ?></p><?php } ?>
			</li>
			<li class="login">
				<input type="submit" name="<?php echo $frm_name; ?>[login]" value="Login">
			</li>
		</ul>
	</fieldset>
</form>

Yeah, I don’t like it either but it doesn’t seem as if there aren’t any better ways to support the feature without changing the under lying structure drastically. That is why I asked here…

Perhaps adding some look behinds to the regex for the <pre> tag would make the expression more concrete.