upgrade zend (#1559)
[openemr.git] / vendor / zendframework / zend-view / src / Helper / HeadScript.php
blob98664a9e922bc794aea5f67df4fcae7f79aeed38
1 <?php
2 /**
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
8 */
10 namespace Zend\View\Helper;
12 use stdClass;
13 use Zend\View;
14 use Zend\View\Exception;
16 /**
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
31 /**
32 * Script type constants
34 * @const string
36 const FILE = 'FILE';
37 const SCRIPT = 'SCRIPT';
39 /**
40 * Registry key for placeholder
42 * @var string
44 protected $regKey = 'Zend_View_Helper_HeadScript';
46 /**
47 * Are arbitrary attributes allowed?
49 * @var bool
51 protected $arbitraryAttributes = false;
53 /**
54 * Is capture lock?
56 * @var bool
58 protected $captureLock;
60 /**
61 * Capture type
63 * @var string
65 protected $captureScriptType;
67 /**
68 * Capture attributes
70 * @var null|array
72 protected $captureScriptAttrs = null;
74 /**
75 * Capture type (append, prepend, set)
77 * @var string
79 protected $captureType;
81 /**
82 * Optional allowed attributes for script tag
84 * @var array
86 protected $optionalAttributes = [
87 'charset',
88 'integrity',
89 'crossorigin',
90 'defer',
91 'async',
92 'language',
93 'src',
94 'id',
97 /**
98 * Required attributes for script tag
100 * @var string
102 protected $requiredAttributes = ['type'];
105 * Whether or not to format scripts using CDATA; used only if doctype
106 * helper is not accessible
108 * @var bool
110 public $useCdata = false;
113 * Constructor
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
135 * @return HeadScript
137 public function __invoke(
138 $mode = self::FILE,
139 $spec = null,
140 $placement = 'APPEND',
141 array $attrs = [],
142 $type = 'text/javascript'
144 if ((null !== $spec) && is_string($spec)) {
145 $action = ucfirst(strtolower($mode));
146 $placement = strtolower($placement);
147 switch ($placement) {
148 case 'set':
149 case 'prepend':
150 case 'append':
151 $action = $placement . $action;
152 break;
153 default:
154 $action = 'append' . $action;
155 break;
157 $this->$action($spec, $type, $attrs);
160 return $this;
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
169 * @return HeadScript
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',
177 $method
181 $action = $matches['action'];
182 $mode = strtolower($matches['mode']);
183 $type = 'text/javascript';
184 $attrs = [];
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',
191 $method
196 $content = $args[0];
198 if (isset($args[1])) {
199 $type = (string) $args[1];
201 if (isset($args[2])) {
202 $attrs = (array) $args[2];
205 switch ($mode) {
206 case 'script':
207 $item = $this->createData($type, $attrs, $content);
208 if ('offsetSet' == $action) {
209 $this->offsetSet($index, $item);
210 } else {
211 $this->$action($item);
213 break;
214 case 'file':
215 default:
216 if (! $this->isDuplicate($content)) {
217 $attrs['src'] = $content;
218 $item = $this->createData($type, $attrs);
219 if ('offsetSet' == $action) {
220 $this->offsetSet($index, $item);
221 } else {
222 $this->$action($item);
225 break;
228 return $this;
231 return parent::__call($method, $args);
235 * Retrieve string representation
237 * @param string|int $indent Amount of whitespaces or string to use for indention
238 * @return string
240 public function toString($indent = null)
242 $indent = (null !== $indent)
243 ? $this->getWhitespace($indent)
244 : $this->getIndent();
246 if ($this->view) {
247 $useCdata = $this->view->plugin('doctype')->isXhtml();
248 } else {
249 $useCdata = $this->useCdata;
252 $escapeStart = ($useCdata) ? '//<![CDATA[' : '//<!--';
253 $escapeEnd = ($useCdata) ? '//]]>' : '//-->';
255 $items = [];
256 $this->getContainer()->ksort();
257 foreach ($this as $item) {
258 if (! $this->isValid($item)) {
259 continue;
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
275 * @return void
277 public function captureStart(
278 $captureType = Placeholder\Container\AbstractContainer::APPEND,
279 $type = 'text/javascript',
280 $attrs = []
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;
290 ob_start();
294 * End capture action and store
296 * @return void
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';
312 break;
313 default:
314 $action = 'appendScript';
315 break;
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
327 * @return stdClass
329 public function createData($type, array $attributes, $content = null)
331 $data = new stdClass();
332 $data->type = $type;
333 $data->attributes = $attributes;
334 $data->source = $content;
336 return $data;
340 * Is the file specified a duplicate?
342 * @param string $file Name of file to check
343 * @return bool
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'])
352 return true;
356 return false;
360 * Is the script provided valid?
362 * @param mixed $value Is the given script valid?
363 * @return bool
365 protected function isValid($value)
367 if ((! $value instanceof stdClass)
368 || ! isset($value->type)
369 || (! isset($value->source)
370 && ! isset($value->attributes))
372 return false;
375 return true;
379 * Create script HTML
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
385 * @return string
387 public function itemToString($item, $indent, $escapeStart, $escapeEnd)
389 $attrString = '';
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'])) {
394 continue;
396 if ('defer' == $key) {
397 $value = 'defer';
399 if ('async' == $key) {
400 $value = 'async';
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 . '>';
411 } else {
412 $type = ($this->autoEscape) ? $this->escape($item->type) : $item->type;
413 $html = '<script type="' . $type . '"' . $attrString . '>';
415 if (! empty($item->source)) {
416 $html .= PHP_EOL;
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]-->';
441 } else {
442 $html = $indent . $html;
445 return $html;
449 * Override append
451 * @param string $value Append script or file
452 * @throws Exception\InvalidArgumentException
453 * @return void
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);
468 * Override prepend
470 * @param string $value Prepend script or file
471 * @throws Exception\InvalidArgumentException
472 * @return void
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);
487 * Override set
489 * @param string $value Set script or file
490 * @throws Exception\InvalidArgumentException
491 * @return void
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);
505 * Override offsetSet
507 * @param string|int $index Set script of file offset
508 * @param mixed $value
509 * @throws Exception\InvalidArgumentException
510 * @return void
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
528 * @return HeadScript
530 public function setAllowArbitraryAttributes($flag)
532 $this->arbitraryAttributes = (bool) $flag;
533 return $this;
537 * Are arbitrary attributes allowed?
539 * @return bool
541 public function arbitraryAttributesAllowed()
543 return $this->arbitraryAttributes;