|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_View |
|
17 * @subpackage Helper |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @version $Id: HeadScript.php 20363 2010-01-17 22:55:25Z mabe $ |
|
20 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
21 */ |
|
22 |
|
23 /** Zend_View_Helper_Placeholder_Container_Standalone */ |
|
24 require_once 'Zend/View/Helper/Placeholder/Container/Standalone.php'; |
|
25 |
|
26 /** |
|
27 * Helper for setting and retrieving script elements for HTML head section |
|
28 * |
|
29 * @uses Zend_View_Helper_Placeholder_Container_Standalone |
|
30 * @package Zend_View |
|
31 * @subpackage Helper |
|
32 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
33 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
34 */ |
|
35 class Zend_View_Helper_HeadScript extends Zend_View_Helper_Placeholder_Container_Standalone |
|
36 { |
|
37 /**#@+ |
|
38 * Script type contants |
|
39 * @const string |
|
40 */ |
|
41 const FILE = 'FILE'; |
|
42 const SCRIPT = 'SCRIPT'; |
|
43 /**#@-*/ |
|
44 |
|
45 /** |
|
46 * Registry key for placeholder |
|
47 * @var string |
|
48 */ |
|
49 protected $_regKey = 'Zend_View_Helper_HeadScript'; |
|
50 |
|
51 /** |
|
52 * Are arbitrary attributes allowed? |
|
53 * @var bool |
|
54 */ |
|
55 protected $_arbitraryAttributes = false; |
|
56 |
|
57 /**#@+ |
|
58 * Capture type and/or attributes (used for hinting during capture) |
|
59 * @var string |
|
60 */ |
|
61 protected $_captureLock; |
|
62 protected $_captureScriptType = null; |
|
63 protected $_captureScriptAttrs = null; |
|
64 protected $_captureType; |
|
65 /**#@-*/ |
|
66 |
|
67 /** |
|
68 * Optional allowed attributes for script tag |
|
69 * @var array |
|
70 */ |
|
71 protected $_optionalAttributes = array( |
|
72 'charset', 'defer', 'language', 'src' |
|
73 ); |
|
74 |
|
75 /** |
|
76 * Required attributes for script tag |
|
77 * @var string |
|
78 */ |
|
79 protected $_requiredAttributes = array('type'); |
|
80 |
|
81 /** |
|
82 * Whether or not to format scripts using CDATA; used only if doctype |
|
83 * helper is not accessible |
|
84 * @var bool |
|
85 */ |
|
86 public $useCdata = false; |
|
87 |
|
88 /** |
|
89 * Constructor |
|
90 * |
|
91 * Set separator to PHP_EOL. |
|
92 * |
|
93 * @return void |
|
94 */ |
|
95 public function __construct() |
|
96 { |
|
97 parent::__construct(); |
|
98 $this->setSeparator(PHP_EOL); |
|
99 } |
|
100 |
|
101 /** |
|
102 * Return headScript object |
|
103 * |
|
104 * Returns headScript helper object; optionally, allows specifying a script |
|
105 * or script file to include. |
|
106 * |
|
107 * @param string $mode Script or file |
|
108 * @param string $spec Script/url |
|
109 * @param string $placement Append, prepend, or set |
|
110 * @param array $attrs Array of script attributes |
|
111 * @param string $type Script type and/or array of script attributes |
|
112 * @return Zend_View_Helper_HeadScript |
|
113 */ |
|
114 public function headScript($mode = Zend_View_Helper_HeadScript::FILE, $spec = null, $placement = 'APPEND', array $attrs = array(), $type = 'text/javascript') |
|
115 { |
|
116 if ((null !== $spec) && is_string($spec)) { |
|
117 $action = ucfirst(strtolower($mode)); |
|
118 $placement = strtolower($placement); |
|
119 switch ($placement) { |
|
120 case 'set': |
|
121 case 'prepend': |
|
122 case 'append': |
|
123 $action = $placement . $action; |
|
124 break; |
|
125 default: |
|
126 $action = 'append' . $action; |
|
127 break; |
|
128 } |
|
129 $this->$action($spec, $type, $attrs); |
|
130 } |
|
131 |
|
132 return $this; |
|
133 } |
|
134 |
|
135 /** |
|
136 * Start capture action |
|
137 * |
|
138 * @param mixed $captureType |
|
139 * @param string $typeOrAttrs |
|
140 * @return void |
|
141 */ |
|
142 public function captureStart($captureType = Zend_View_Helper_Placeholder_Container_Abstract::APPEND, $type = 'text/javascript', $attrs = array()) |
|
143 { |
|
144 if ($this->_captureLock) { |
|
145 require_once 'Zend/View/Helper/Placeholder/Container/Exception.php'; |
|
146 $e = new Zend_View_Helper_Placeholder_Container_Exception('Cannot nest headScript captures'); |
|
147 $e->setView($this->view); |
|
148 throw $e; |
|
149 } |
|
150 |
|
151 $this->_captureLock = true; |
|
152 $this->_captureType = $captureType; |
|
153 $this->_captureScriptType = $type; |
|
154 $this->_captureScriptAttrs = $attrs; |
|
155 ob_start(); |
|
156 } |
|
157 |
|
158 /** |
|
159 * End capture action and store |
|
160 * |
|
161 * @return void |
|
162 */ |
|
163 public function captureEnd() |
|
164 { |
|
165 $content = ob_get_clean(); |
|
166 $type = $this->_captureScriptType; |
|
167 $attrs = $this->_captureScriptAttrs; |
|
168 $this->_captureScriptType = null; |
|
169 $this->_captureScriptAttrs = null; |
|
170 $this->_captureLock = false; |
|
171 |
|
172 switch ($this->_captureType) { |
|
173 case Zend_View_Helper_Placeholder_Container_Abstract::SET: |
|
174 case Zend_View_Helper_Placeholder_Container_Abstract::PREPEND: |
|
175 case Zend_View_Helper_Placeholder_Container_Abstract::APPEND: |
|
176 $action = strtolower($this->_captureType) . 'Script'; |
|
177 break; |
|
178 default: |
|
179 $action = 'appendScript'; |
|
180 break; |
|
181 } |
|
182 $this->$action($content, $type, $attrs); |
|
183 } |
|
184 |
|
185 /** |
|
186 * Overload method access |
|
187 * |
|
188 * Allows the following method calls: |
|
189 * - appendFile($src, $type = 'text/javascript', $attrs = array()) |
|
190 * - offsetSetFile($index, $src, $type = 'text/javascript', $attrs = array()) |
|
191 * - prependFile($src, $type = 'text/javascript', $attrs = array()) |
|
192 * - setFile($src, $type = 'text/javascript', $attrs = array()) |
|
193 * - appendScript($script, $type = 'text/javascript', $attrs = array()) |
|
194 * - offsetSetScript($index, $src, $type = 'text/javascript', $attrs = array()) |
|
195 * - prependScript($script, $type = 'text/javascript', $attrs = array()) |
|
196 * - setScript($script, $type = 'text/javascript', $attrs = array()) |
|
197 * |
|
198 * @param string $method |
|
199 * @param array $args |
|
200 * @return Zend_View_Helper_HeadScript |
|
201 * @throws Zend_View_Exception if too few arguments or invalid method |
|
202 */ |
|
203 public function __call($method, $args) |
|
204 { |
|
205 if (preg_match('/^(?P<action>set|(ap|pre)pend|offsetSet)(?P<mode>File|Script)$/', $method, $matches)) { |
|
206 if (1 > count($args)) { |
|
207 require_once 'Zend/View/Exception.php'; |
|
208 $e = new Zend_View_Exception(sprintf('Method "%s" requires at least one argument', $method)); |
|
209 $e->setView($this->view); |
|
210 throw $e; |
|
211 } |
|
212 |
|
213 $action = $matches['action']; |
|
214 $mode = strtolower($matches['mode']); |
|
215 $type = 'text/javascript'; |
|
216 $attrs = array(); |
|
217 |
|
218 if ('offsetSet' == $action) { |
|
219 $index = array_shift($args); |
|
220 if (1 > count($args)) { |
|
221 require_once 'Zend/View/Exception.php'; |
|
222 $e = new Zend_View_Exception(sprintf('Method "%s" requires at least two arguments, an index and source', $method)); |
|
223 $e->setView($this->view); |
|
224 throw $e; |
|
225 } |
|
226 } |
|
227 |
|
228 $content = $args[0]; |
|
229 |
|
230 if (isset($args[1])) { |
|
231 $type = (string) $args[1]; |
|
232 } |
|
233 if (isset($args[2])) { |
|
234 $attrs = (array) $args[2]; |
|
235 } |
|
236 |
|
237 switch ($mode) { |
|
238 case 'script': |
|
239 $item = $this->createData($type, $attrs, $content); |
|
240 if ('offsetSet' == $action) { |
|
241 $this->offsetSet($index, $item); |
|
242 } else { |
|
243 $this->$action($item); |
|
244 } |
|
245 break; |
|
246 case 'file': |
|
247 default: |
|
248 if (!$this->_isDuplicate($content)) { |
|
249 $attrs['src'] = $content; |
|
250 $item = $this->createData($type, $attrs); |
|
251 if ('offsetSet' == $action) { |
|
252 $this->offsetSet($index, $item); |
|
253 } else { |
|
254 $this->$action($item); |
|
255 } |
|
256 } |
|
257 break; |
|
258 } |
|
259 |
|
260 return $this; |
|
261 } |
|
262 |
|
263 return parent::__call($method, $args); |
|
264 } |
|
265 |
|
266 /** |
|
267 * Is the file specified a duplicate? |
|
268 * |
|
269 * @param string $file |
|
270 * @return bool |
|
271 */ |
|
272 protected function _isDuplicate($file) |
|
273 { |
|
274 foreach ($this->getContainer() as $item) { |
|
275 if (($item->source === null) |
|
276 && array_key_exists('src', $item->attributes) |
|
277 && ($file == $item->attributes['src'])) |
|
278 { |
|
279 return true; |
|
280 } |
|
281 } |
|
282 return false; |
|
283 } |
|
284 |
|
285 /** |
|
286 * Is the script provided valid? |
|
287 * |
|
288 * @param mixed $value |
|
289 * @param string $method |
|
290 * @return bool |
|
291 */ |
|
292 protected function _isValid($value) |
|
293 { |
|
294 if ((!$value instanceof stdClass) |
|
295 || !isset($value->type) |
|
296 || (!isset($value->source) && !isset($value->attributes))) |
|
297 { |
|
298 return false; |
|
299 } |
|
300 |
|
301 return true; |
|
302 } |
|
303 |
|
304 /** |
|
305 * Override append |
|
306 * |
|
307 * @param string $value |
|
308 * @return void |
|
309 */ |
|
310 public function append($value) |
|
311 { |
|
312 if (!$this->_isValid($value)) { |
|
313 require_once 'Zend/View/Exception.php'; |
|
314 $e = new Zend_View_Exception('Invalid argument passed to append(); please use one of the helper methods, appendScript() or appendFile()'); |
|
315 $e->setView($this->view); |
|
316 throw $e; |
|
317 } |
|
318 |
|
319 return $this->getContainer()->append($value); |
|
320 } |
|
321 |
|
322 /** |
|
323 * Override prepend |
|
324 * |
|
325 * @param string $value |
|
326 * @return void |
|
327 */ |
|
328 public function prepend($value) |
|
329 { |
|
330 if (!$this->_isValid($value)) { |
|
331 require_once 'Zend/View/Exception.php'; |
|
332 $e = new Zend_View_Exception('Invalid argument passed to prepend(); please use one of the helper methods, prependScript() or prependFile()'); |
|
333 $e->setView($this->view); |
|
334 throw $e; |
|
335 } |
|
336 |
|
337 return $this->getContainer()->prepend($value); |
|
338 } |
|
339 |
|
340 /** |
|
341 * Override set |
|
342 * |
|
343 * @param string $value |
|
344 * @return void |
|
345 */ |
|
346 public function set($value) |
|
347 { |
|
348 if (!$this->_isValid($value)) { |
|
349 require_once 'Zend/View/Exception.php'; |
|
350 $e = new Zend_View_Exception('Invalid argument passed to set(); please use one of the helper methods, setScript() or setFile()'); |
|
351 $e->setView($this->view); |
|
352 throw $e; |
|
353 } |
|
354 |
|
355 return $this->getContainer()->set($value); |
|
356 } |
|
357 |
|
358 /** |
|
359 * Override offsetSet |
|
360 * |
|
361 * @param string|int $index |
|
362 * @param mixed $value |
|
363 * @return void |
|
364 */ |
|
365 public function offsetSet($index, $value) |
|
366 { |
|
367 if (!$this->_isValid($value)) { |
|
368 require_once 'Zend/View/Exception.php'; |
|
369 $e = new Zend_View_Exception('Invalid argument passed to offsetSet(); please use one of the helper methods, offsetSetScript() or offsetSetFile()'); |
|
370 $e->setView($this->view); |
|
371 throw $e; |
|
372 } |
|
373 |
|
374 $this->_isValid($value); |
|
375 return $this->getContainer()->offsetSet($index, $value); |
|
376 } |
|
377 |
|
378 /** |
|
379 * Set flag indicating if arbitrary attributes are allowed |
|
380 * |
|
381 * @param bool $flag |
|
382 * @return Zend_View_Helper_HeadScript |
|
383 */ |
|
384 public function setAllowArbitraryAttributes($flag) |
|
385 { |
|
386 $this->_arbitraryAttributes = (bool) $flag; |
|
387 return $this; |
|
388 } |
|
389 |
|
390 /** |
|
391 * Are arbitrary attributes allowed? |
|
392 * |
|
393 * @return bool |
|
394 */ |
|
395 public function arbitraryAttributesAllowed() |
|
396 { |
|
397 return $this->_arbitraryAttributes; |
|
398 } |
|
399 |
|
400 /** |
|
401 * Create script HTML |
|
402 * |
|
403 * @param string $type |
|
404 * @param array $attributes |
|
405 * @param string $content |
|
406 * @param string|int $indent |
|
407 * @return string |
|
408 */ |
|
409 public function itemToString($item, $indent, $escapeStart, $escapeEnd) |
|
410 { |
|
411 $attrString = ''; |
|
412 if (!empty($item->attributes)) { |
|
413 foreach ($item->attributes as $key => $value) { |
|
414 if (!$this->arbitraryAttributesAllowed() |
|
415 && !in_array($key, $this->_optionalAttributes)) |
|
416 { |
|
417 continue; |
|
418 } |
|
419 if ('defer' == $key) { |
|
420 $value = 'defer'; |
|
421 } |
|
422 $attrString .= sprintf(' %s="%s"', $key, ($this->_autoEscape) ? $this->_escape($value) : $value); |
|
423 } |
|
424 } |
|
425 |
|
426 $type = ($this->_autoEscape) ? $this->_escape($item->type) : $item->type; |
|
427 $html = '<script type="' . $type . '"' . $attrString . '>'; |
|
428 if (!empty($item->source)) { |
|
429 $html .= PHP_EOL . $indent . ' ' . $escapeStart . PHP_EOL . $item->source . $indent . ' ' . $escapeEnd . PHP_EOL . $indent; |
|
430 } |
|
431 $html .= '</script>'; |
|
432 |
|
433 if (isset($item->attributes['conditional']) |
|
434 && !empty($item->attributes['conditional']) |
|
435 && is_string($item->attributes['conditional'])) |
|
436 { |
|
437 $html = $indent . '<!--[if ' . $item->attributes['conditional'] . ']> ' . $html . '<![endif]-->'; |
|
438 } else { |
|
439 $html = $indent . $html; |
|
440 } |
|
441 |
|
442 return $html; |
|
443 } |
|
444 |
|
445 /** |
|
446 * Retrieve string representation |
|
447 * |
|
448 * @param string|int $indent |
|
449 * @return string |
|
450 */ |
|
451 public function toString($indent = null) |
|
452 { |
|
453 $indent = (null !== $indent) |
|
454 ? $this->getWhitespace($indent) |
|
455 : $this->getIndent(); |
|
456 |
|
457 if ($this->view) { |
|
458 $useCdata = $this->view->doctype()->isXhtml() ? true : false; |
|
459 } else { |
|
460 $useCdata = $this->useCdata ? true : false; |
|
461 } |
|
462 $escapeStart = ($useCdata) ? '//<![CDATA[' : '//<!--'; |
|
463 $escapeEnd = ($useCdata) ? '//]]>' : '//-->'; |
|
464 |
|
465 $items = array(); |
|
466 $this->getContainer()->ksort(); |
|
467 foreach ($this as $item) { |
|
468 if (!$this->_isValid($item)) { |
|
469 continue; |
|
470 } |
|
471 |
|
472 $items[] = $this->itemToString($item, $indent, $escapeStart, $escapeEnd); |
|
473 } |
|
474 |
|
475 $return = implode($this->getSeparator(), $items); |
|
476 return $return; |
|
477 } |
|
478 |
|
479 /** |
|
480 * Create data item containing all necessary components of script |
|
481 * |
|
482 * @param string $type |
|
483 * @param array $attributes |
|
484 * @param string $content |
|
485 * @return stdClass |
|
486 */ |
|
487 public function createData($type, array $attributes, $content = null) |
|
488 { |
|
489 $data = new stdClass(); |
|
490 $data->type = $type; |
|
491 $data->attributes = $attributes; |
|
492 $data->source = $content; |
|
493 return $data; |
|
494 } |
|
495 } |