Just as we saw for the simple modules at the start of the chapter, you need only instantiate the modules with the necessary arguments to configure them, call their create methods, and pl
Trang 1{
// The content for this module consists of a single image, along
// with text for the caption and the attribution
$attr = "";
if (!empty($this->picture["attr"]))
{
$attr = <<<EOD
<cite>
courtesy of {$this->picture["attr"]}
</cite>
EOD;
}
if (empty($this->picture["img_l"]))
$img = "";
else
{
$img = <<<EOD
<img src="{$this->picture["img_l"]}" alt="{$this->picture["text"]}"
width="600" />
EOD;
}
return <<<EOD
<div id="picvwr" class="{$this->type}">
<div class="vwrimg">
$img
</div>
$attr
<div class="vwrcap">
{$this->picture["text"]}
</div>
</div>
EOD;
}
}
?>
Example 7-7 presents the JavaScript that implements behaviors for the Picture Slider module In a manner consistent with what we discussed for large-scale JavaScript in Chapter 5 , the module has a JavaScript object that neatly encapsulates the functionality that the component requires, and the object is named PictureSlider to reflect the module’s name In Example 7-6 , the get_js_linked method of PictureSlider specifies
that this object is defined in the file identified by the key sitewide.js The JavaScript for
the module needs two YUI libraries (which you can download from http://developer yahoo.com/yui ) to support it Therefore, get_js_linked specifies keys for these files
before sitewide.js.
Trang 2Example 7-7 PictureSlider JavaScript object for the slideshow
PictureSlider = function()
{
// Set up references to the elements needed for the slider and viewer this.slider = document.getElementById("picsld");
if (this.slider)
{
this.tab = this.slider.getElementsByTagName("table");
this.tab = (this.tab && this.tab.length > 0) ? this.tab[0] : null; }
if (this.slider)
{
this.lb = YAHOO.util.Dom.getElementsByClassName
(
"btnl",
"img",
this.slider
);
this.lb = (this.lb && this.lb.length > 0) ? this.lb[0] : null; this.rb = YAHOO.util.Dom.getElementsByClassName
(
"btnr",
"img",
this.slider
);
this.rb = (this.rb && this.rb.length > 0) ? this.rb[0] : null; }
this.viewer = document.getElementById("picvwr");
// You pass values for the following parameters to the module's // constructor in PHP The module's get_js method in PHP dynamically // generates the JavaScript to set these members from those values this.stripWidth = 0;
this.totalCount = 0;
this.totalWidth = 0;
// This lock is needed to ensure that one left or right move of the // slider runs to completion; otherwise, misalignment could happen this.lock = false;
};
PictureSlider.prototype = new Object();
PictureSlider.prototype.slideL = function()
{
// Moving to the left adjusts the slider in a positive direction this.adjust(+(this.stripWidth));
};
Trang 3PictureSlider.prototype.slideR = function()
{
// Moving to the right adjusts the slider in a negative direction
this.adjust(-(this.stripWidth));
};
PictureSlider.prototype.adjust = function(amt)
{
// If already locked, do nothing; otherwise, get the lock and go
if (this.lock)
return;
else
this.lock = true;
var anim;
var ease = YAHOO.util.Easing.easeOut;
var pos = parseInt(YAHOO.util.Dom.getStyle(this.tab, "left"));
// Prevent moving past either end of the slider during an adjustment
if (amt > 0)
{
if (pos + amt > 0)
amt = 0;
}
if (amt < 0)
{
if (pos + amt <= -(this.totalWidth))
amt = 0;
}
// The following creates a closure that ensures access to members
// of the PictureSlider instance from inside the update method
var obj = this;
function handleComplete()
{
obj.update();
obj.lock = false;
}
// Do the sliding animation if there is any amount to move; otherwise,
// just call update directly to ensure the arrow buttons are updated
if (amt != 0)
{
anim = new YAHOO.util.Anim(this.tab, {left: {by: amt}}, 0.5, ease);
anim.onComplete.subscribe(handleComplete);
anim.animate();
}
else
{
this.update();
this.lock = false;
}
};
Trang 4PictureSlider.prototype.update = function()
{
var pos;
pos = parseInt(YAHOO.util.Dom.getStyle(this.tab, "left"));
// Switch images to indicate which buttons are enabled or disabled
if (pos >= 0)
this.lb.src = " /slide_arrow_off_l.gif";
else
this.lb.src = " /slide_arrow_l.gif";
if (pos <= -this.totalWidth + this.stripWidth)
this.rb.src = " /slide_arrow_off_r.gif";
else
this.rb.src = " /slide_arrow_r.gif";
};
PictureSlider.prototype.select = function(targ, n, img, text, attr) {
var sld;
var el;
// Switch the selection by changing the frame with the selected class
el = YAHOO.util.Dom.getElementsByClassName
(
"selected",
"div",
this.slider
);
if (el && el.length > 0)
YAHOO.util.Dom.removeClass(el[0], "selected");
if (targ)
YAHOO.util.Dom.addClass(targ, "selected");
// Reload the picture viewer with the current selection in the slider this.reload(img, text, attr);
// Update the text indicating the position of the selected picture
el = YAHOO.util.Dom.getElementsByClassName
(
"sldpos",
"div",
this.slider
);
if (el && el.length > 0)
{
el[0].innerHTML = "Showing picture <strong>" + n + "</strong> of " + this.totalCount;
}
};
Trang 5PictureSlider.prototype.reload = function(img, text, attr)
{
// Handle the case of no viewer associated with the picture slider
if (!this.viewer) return;
var el;
// Get the image viewer and change the image currently being shown
el = YAHOO.util.Dom.getElementsByClassName
(
"vwrimg",
"div",
this.viewer
);
if (el && el.length > 0)
{
el = el[0].getElementsByTagName("img");
if (el && el.length > 0)
{
el[0].src = img;
el[0].alt = text;
}
}
// Change the attribution in the picture viewer for the selection
el = this.viewer.getElementsByTagName("cite");
if (el && el.length > 0)
el[0].childNodes[0].nodeValue = "courtesy of" + attr;
// Change the caption in the picture viewer based on the selection
el = YAHOO.util.Dom.getElementsByClassName
(
"vwrcap",
"div",
this.viewer
);
if (el && el.length > 0)
el[0].childNodes[0].nodeValue = text;
};
PictureSlider.prototype.loaded = function()
{
// Fire this from your initialization method for the picture slider
var el;
el = YAHOO.util.Dom.getElementsByClassName
(
"sldtab",
"div",
this.slider
Trang 6);
YAHOO.util.Dom.setStyle
(
el[0],
"visibility",
"visible"
);
};
As you can see, a slideshow contains enough interrelated pieces that defining it using nicely encapsulated modules is critical to making it highly reusable, maintainable, and ultimately reliable in the long life cycle of a large web application Example 7-8 dem-onstrates how easy this module is to use despite all the interconnected pieces of its implementation Just as we saw for the simple modules at the start of the chapter, you need only instantiate the modules with the necessary arguments to configure them, call their create methods, and place them within the proper section of the layout for the
page Furthermore, you need only include a single include file, slideshow.inc, to
use the slideshow The necessary HTML, CSS, and JavaScript for its modules travel with the slideshow wherever you use it.
Example 7-8 Creating the modules for a slideshow
<?php
require_once(" /common/sitepage.inc");
require_once(" /common/slideshow.inc");
class NewCarDetailsPage extends SitePage
{
public function get_content()
{
// Create the picture slider and picture viewer for the slideshow
// The backend data will have been loaded earlier during a call to
// load_data
$mod = new PictureSlider
(
$this,
$this->load_data["new_car_info"]["gallery"]
);
$slider = $mod->create();
$mod = new PictureViewer
(
$this,
$this->load_data["new_car_info"]["gallery"][0]
);
Trang 7$viewer = $mod->create();
// Place the HTML markup for each module within the page layout
$mod = new DetailsLayout
(
$this,
array( ),
array($slider, $viewer),
array( ),
array( ),
array( ),
array( )
);
// Return the content, which the create method for the page uses
return $mod->create();
}
}
?>
Layouts and Containers
In Chapter 4 , we discussed layouts as highly reusable, generic templates that define the overarching structure of pages You saw that containers are even finer groupings of modules typically placed within layouts Together, layouts and containers play a vital role in fostering reusability, maintainability, and reliability in large web applications
by defining a number of standard sections in which to place modules on a page A section
is simply a region in which we can insert one or more modules.
As we explore implementations for layouts and containers, you’ll see that layouts and containers are just specialized types of modules That is, they require many of the same things that other modules do The main distinction is that aside from the additional structural elements that a layout or container defines, the content for a layout or con-tainer is actually just the content of other modules.
Because the operations you need to perform to generate the individual sections of a layout or container are generally the same for most layouts and containers, it’s useful
to define a base class, Layout, for this purpose Example 7-9 presents the Layout class, which is derived from Module This class defines get_section, which places a set of modules within a div and assigns the div a specified class so that styles for positioning and formatting the section can be applied.
Trang 8Example 7-9 Implementation of the Layout class
class Layout extends Module
{
public function construct($page)
{
parent:: construct($page);
}
public function get_section($class, $modules)
{
if (count($modules) == 0)
return "";
foreach ($modules as $content)
$section = empty($content) ? "" : $content;
if (empty($section))
return "";
return <<<EOD
<div class="$class">
$section
<! $class >
</div>
EOD;
}
}
Example 7-10 presents a layout class intended for laying out any page related to search results, be they search results for new cars, used cars, reviews, or articles in our web application This layout has sections for a header at the top, three content sections in the middle, and two footers across the bottom (this is the same layout from Fig-ure 4-3 in Chapter 4 ) Because any HTML for modules that you pass through the lay-out’s interface for each section simply gets inserted into the proper locations in the overall structure of the layout, it is highly reusable for any page whose design observes this same structure; you simply insert different modules As a result, layouts and con-tainers provide a good opportunity for engineers and designers to work together to establish standard guidelines under which pages can be designed and built Classes for containers are defined in much the same way as for layouts, but they typically organize smaller groups of modules for use within sections of a layout.
Example 7-10 Implementing a layout class
class ResultsLayout extends Layout
{
protected $layreshdr;
protected $layrespri;
protected $layressec;
protected $layrester;
protected $layresftr1;
protected $layresftr2;
Trang 9public function construct
(
$page,
$layreshdr,
$layrespri,
$layressec,
$layrester,
$layresftr1,
$layresftr2
)
{
parent:: construct($page);
$this->layreshdr = $layreshdr;
$this->layrespri = $layrespri;
$this->layressec = $layressec;
$this->layrester = $layrester;
$this->layresftr1 = $layresftr1;
$this->layresftr2 = $layresftr2;
}
public function get_css_linked()
{
return array("sidewide.css");
}
public function get_content()
{
$layreshdr = $this->get_section("layreshdr", $this->layreshdr);
$layrespri = $this->get_section("layrespri", $this->layrespri);
$layressec = $this->get_section("layressec", $this->layressec);
$layrester = $this->get_section("layrester", $this->layrester);
$layresftr1 = $this->get_section("layresftr1", $this->layresftr1);
$layresftr2 = $this->get_section("layresftr2", $this->layresftr2);
// The content for the layout is just the content of the modules
// passed into the layout and inserted into the layout sections
return <<<EOD
<div id="layres">
$layreshdr
<div class="layresmaj">
$layrespri
$layressec
$layrester
< layresmaj >
</div>
$layresftr1
$layresftr2
<! layres >
</div>
EOD;
}
}
Trang 10Special Considerations
The more you work with any large web application, the more you’ll turn up special considerations that you didn’t originally think about Fortunately, it’s relatively easy
to adapt the techniques with large-scale PHP introduced in this chapter for many dif-ferent situations Indeed, this is one of the most important tests of a good architecture: can it adapt to new situations without undue contortions? This section presents some examples of special situations in large web applications with solutions using the tech-niques from this chapter.
Handling Module Variations
In large web applications, modules frequently need to function or appear in a different manner on different pages For example, suppose you would like a module to support default, compact, and mid-size presentations, as illustrated in Chapter 4 Exam-ple 7-11 demonstrates a solution wherein the class of the module’s containing div be-comes parameterized To do this, you specify a data member for the module named
$class and define setter methods that change its value The module picks up the proper CSS when the class attribute of its containing div is set to the value stored in the
$class data member.
For more extensive variations requiring more significant changes to the HTML markup
or JavaScript for a module, you can manage those variations in a similar manner using data members to control how the HTML markup or JavaScript is generated It helps
to have a nicely encapsulated class for the module and a well-defined interface for selecting the variations.
Example 7-11 Handling variations of the Popular New Cars module
class PopularNewCars extends Module
{
protected $class;
public function construct($page, )
{
parent:: construct($page);
$this->class = "default";
// Set up the module using other arguments from the constructor
}
public function set_mode_default()
{
$this->class = "default";
}