3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\View\Helper
;
14 use Zend\View\Exception
;
17 * Helper for setting and retrieving script elements for HTML head section
19 * Allows the following method calls:
20 * @method HeadScript appendFile($src, $type = 'text/javascript', $attrs = [])
21 * @method HeadScript offsetSetFile($index, $src, $type = 'text/javascript', $attrs = [])
22 * @method HeadScript prependFile($src, $type = 'text/javascript', $attrs = [])
23 * @method HeadScript setFile($src, $type = 'text/javascript', $attrs = [])
24 * @method HeadScript appendScript($script, $type = 'text/javascript', $attrs = [])
25 * @method HeadScript offsetSetScript($index, $src, $type = 'text/javascript', $attrs = [])
26 * @method HeadScript prependScript($script, $type = 'text/javascript', $attrs = [])
27 * @method HeadScript setScript($script, $type = 'text/javascript', $attrs = [])
29 class HeadScript
extends Placeholder\Container\AbstractStandalone
32 * Script type constants
37 const SCRIPT
= 'SCRIPT';
40 * Registry key for placeholder
44 protected $regKey = 'Zend_View_Helper_HeadScript';
47 * Are arbitrary attributes allowed?
51 protected $arbitraryAttributes = false;
58 protected $captureLock;
65 protected $captureScriptType;
72 protected $captureScriptAttrs = null;
75 * Capture type (append, prepend, set)
79 protected $captureType;
82 * Optional allowed attributes for script tag
86 protected $optionalAttributes = [
98 * Required attributes for script tag
102 protected $requiredAttributes = ['type'];
105 * Whether or not to format scripts using CDATA; used only if doctype
106 * helper is not accessible
110 public $useCdata = false;
115 * Set separator to PHP_EOL.
117 public function __construct()
119 parent
::__construct();
121 $this->setSeparator(PHP_EOL
);
125 * Return headScript object
127 * Returns headScript helper object; optionally, allows specifying a script
128 * or script file to include.
130 * @param string $mode Script or file
131 * @param string $spec Script/url
132 * @param string $placement Append, prepend, or set
133 * @param array $attrs Array of script attributes
134 * @param string $type Script type and/or array of script attributes
137 public function __invoke(
140 $placement = 'APPEND',
142 $type = 'text/javascript'
144 if ((null !== $spec) && is_string($spec)) {
145 $action = ucfirst(strtolower($mode));
146 $placement = strtolower($placement);
147 switch ($placement) {
151 $action = $placement . $action;
154 $action = 'append' . $action;
157 $this->$action($spec, $type, $attrs);
164 * Overload method access
166 * @param string $method Method to call
167 * @param array $args Arguments of method
168 * @throws Exception\BadMethodCallException if too few arguments or invalid method
171 public function __call($method, $args)
173 if (preg_match('/^(?P<action>set|(ap|pre)pend|offsetSet)(?P<mode>File|Script)$/', $method, $matches)) {
174 if (1 > count($args)) {
175 throw new Exception\
BadMethodCallException(sprintf(
176 'Method "%s" requires at least one argument',
181 $action = $matches['action'];
182 $mode = strtolower($matches['mode']);
183 $type = 'text/javascript';
186 if ('offsetSet' == $action) {
187 $index = array_shift($args);
188 if (1 > count($args)) {
189 throw new Exception\
BadMethodCallException(sprintf(
190 'Method "%s" requires at least two arguments, an index and source',
198 if (isset($args[1])) {
199 $type = (string) $args[1];
201 if (isset($args[2])) {
202 $attrs = (array) $args[2];
207 $item = $this->createData($type, $attrs, $content);
208 if ('offsetSet' == $action) {
209 $this->offsetSet($index, $item);
211 $this->$action($item);
216 if (! $this->isDuplicate($content)) {
217 $attrs['src'] = $content;
218 $item = $this->createData($type, $attrs);
219 if ('offsetSet' == $action) {
220 $this->offsetSet($index, $item);
222 $this->$action($item);
231 return parent
::__call($method, $args);
235 * Retrieve string representation
237 * @param string|int $indent Amount of whitespaces or string to use for indention
240 public function toString($indent = null)
242 $indent = (null !== $indent)
243 ?
$this->getWhitespace($indent)
244 : $this->getIndent();
247 $useCdata = $this->view
->plugin('doctype')->isXhtml();
249 $useCdata = $this->useCdata
;
252 $escapeStart = ($useCdata) ?
'//<![CDATA[' : '//<!--';
253 $escapeEnd = ($useCdata) ?
'//]]>' : '//-->';
256 $this->getContainer()->ksort();
257 foreach ($this as $item) {
258 if (! $this->isValid($item)) {
262 $items[] = $this->itemToString($item, $indent, $escapeStart, $escapeEnd);
265 return implode($this->getSeparator(), $items);
269 * Start capture action
271 * @param mixed $captureType Type of capture
272 * @param string $type Type of script
273 * @param array $attrs Attributes of capture
274 * @throws Exception\RuntimeException
277 public function captureStart(
278 $captureType = Placeholder\Container\AbstractContainer
::APPEND
,
279 $type = 'text/javascript',
282 if ($this->captureLock
) {
283 throw new Exception\
RuntimeException('Cannot nest headScript captures');
286 $this->captureLock
= true;
287 $this->captureType
= $captureType;
288 $this->captureScriptType
= $type;
289 $this->captureScriptAttrs
= $attrs;
294 * End capture action and store
298 public function captureEnd()
300 $content = ob_get_clean();
301 $type = $this->captureScriptType
;
302 $attrs = $this->captureScriptAttrs
;
303 $this->captureScriptType
= null;
304 $this->captureScriptAttrs
= null;
305 $this->captureLock
= false;
307 switch ($this->captureType
) {
308 case Placeholder\Container\AbstractContainer
::SET
:
309 case Placeholder\Container\AbstractContainer
::PREPEND
:
310 case Placeholder\Container\AbstractContainer
::APPEND
:
311 $action = strtolower($this->captureType
) . 'Script';
314 $action = 'appendScript';
318 $this->$action($content, $type, $attrs);
322 * Create data item containing all necessary components of script
324 * @param string $type Type of data
325 * @param array $attributes Attributes of data
326 * @param string $content Content of data
329 public function createData($type, array $attributes, $content = null)
331 $data = new stdClass();
333 $data->attributes
= $attributes;
334 $data->source
= $content;
340 * Is the file specified a duplicate?
342 * @param string $file Name of file to check
345 protected function isDuplicate($file)
347 foreach ($this->getContainer() as $item) {
348 if (($item->source
=== null)
349 && array_key_exists('src', $item->attributes
)
350 && ($file == $item->attributes
['src'])
360 * Is the script provided valid?
362 * @param mixed $value Is the given script valid?
365 protected function isValid($value)
367 if ((! $value instanceof stdClass
)
368 ||
! isset($value->type
)
369 ||
(! isset($value->source
)
370 && ! isset($value->attributes
))
381 * @param mixed $item Item to convert
382 * @param string $indent String to add before the item
383 * @param string $escapeStart Starting sequence
384 * @param string $escapeEnd Ending sequence
387 public function itemToString($item, $indent, $escapeStart, $escapeEnd)
390 if (! empty($item->attributes
)) {
391 foreach ($item->attributes
as $key => $value) {
392 if ((! $this->arbitraryAttributesAllowed() && ! in_array($key, $this->optionalAttributes
))
393 ||
in_array($key, ['conditional', 'noescape'])) {
396 if ('defer' == $key) {
399 if ('async' == $key) {
402 $attrString .= sprintf(' %s="%s"', $key, ($this->autoEscape
) ?
$this->escape($value) : $value);
406 $addScriptEscape = ! (isset($item->attributes
['noescape'])
407 && filter_var($item->attributes
['noescape'], FILTER_VALIDATE_BOOLEAN
));
409 if (empty($item->type
) && $this->view
&& $this->view
->plugin('doctype')->isHtml5()) {
410 $html = '<script ' . $attrString . '>';
412 $type = ($this->autoEscape
) ?
$this->escape($item->type
) : $item->type
;
413 $html = '<script type="' . $type . '"' . $attrString . '>';
415 if (! empty($item->source
)) {
418 if ($addScriptEscape) {
419 $html .= $indent . ' ' . $escapeStart . PHP_EOL
;
422 $html .= $indent . ' ' . $item->source
;
424 if ($addScriptEscape) {
425 $html .= PHP_EOL
. $indent . ' ' . $escapeEnd;
428 $html .= PHP_EOL
. $indent;
430 $html .= '</script>';
432 if (isset($item->attributes
['conditional'])
433 && ! empty($item->attributes
['conditional'])
434 && is_string($item->attributes
['conditional'])
436 // inner wrap with comment end and start if !IE
437 if (str_replace(' ', '', $item->attributes
['conditional']) === '!IE') {
438 $html = '<!-->' . $html . '<!--';
440 $html = $indent . '<!--[if ' . $item->attributes
['conditional'] . ']>' . $html . '<![endif]-->';
442 $html = $indent . $html;
451 * @param string $value Append script or file
452 * @throws Exception\InvalidArgumentException
455 public function append($value)
457 if (! $this->isValid($value)) {
458 throw new Exception\
InvalidArgumentException(
459 'Invalid argument passed to append(); '
460 . 'please use one of the helper methods, appendScript() or appendFile()'
464 return $this->getContainer()->append($value);
470 * @param string $value Prepend script or file
471 * @throws Exception\InvalidArgumentException
474 public function prepend($value)
476 if (! $this->isValid($value)) {
477 throw new Exception\
InvalidArgumentException(
478 'Invalid argument passed to prepend(); '
479 . 'please use one of the helper methods, prependScript() or prependFile()'
483 return $this->getContainer()->prepend($value);
489 * @param string $value Set script or file
490 * @throws Exception\InvalidArgumentException
493 public function set($value)
495 if (! $this->isValid($value)) {
496 throw new Exception\
InvalidArgumentException(
497 'Invalid argument passed to set(); please use one of the helper methods, setScript() or setFile()'
501 return $this->getContainer()->set($value);
507 * @param string|int $index Set script of file offset
508 * @param mixed $value
509 * @throws Exception\InvalidArgumentException
512 public function offsetSet($index, $value)
514 if (! $this->isValid($value)) {
515 throw new Exception\
InvalidArgumentException(
516 'Invalid argument passed to offsetSet(); '
517 . 'please use one of the helper methods, offsetSetScript() or offsetSetFile()'
521 return $this->getContainer()->offsetSet($index, $value);
525 * Set flag indicating if arbitrary attributes are allowed
527 * @param bool $flag Set flag
530 public function setAllowArbitraryAttributes($flag)
532 $this->arbitraryAttributes
= (bool) $flag;
537 * Are arbitrary attributes allowed?
541 public function arbitraryAttributesAllowed()
543 return $this->arbitraryAttributes
;