2
0

HeadScript.php 14 KB

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