Fixed bug #14277: Improve t3lib_compressor (thanks to Steffen Gebert)
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_pagerenderer.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Steffen Kamper (info@sk-typo3.de)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * TYPO3 pageRender class (new in TYPO3 4.3.0)
30 * This class render the HTML of a webpage, usable for BE and FE
31 *
32 * @author Steffen Kamper <info@sk-typo3.de>
33 * @package TYPO3
34 * @subpackage t3lib
35 * $Id$
36 */
37 class t3lib_PageRenderer implements t3lib_Singleton {
38
39 protected $compressJavascript = FALSE;
40 protected $compressCss = FALSE;
41 protected $removeLineBreaksFromTemplate = FALSE;
42
43 protected $concatenateFiles = FALSE;
44
45 protected $moveJsFromHeaderToFooter = FALSE;
46
47 protected $csConvObj;
48 protected $lang;
49
50 /* @var t3lib_compressor Instance of t3lib_compressor */
51 protected $compressor;
52
53 // static array containing associative array for the included files
54 protected static $jsFiles = array ();
55 protected static $jsFooterFiles = array ();
56 protected static $jsLibs = array ();
57 protected static $jsFooterLibs = array ();
58 protected static $cssFiles = array ();
59
60 protected $title;
61 protected $charSet;
62 protected $favIcon;
63 protected $baseUrl;
64
65 protected $renderXhtml = TRUE;
66
67 // static header blocks
68 protected $xmlPrologAndDocType = '';
69 protected $metaTags = array ();
70 protected $inlineComments = array ();
71 protected $headerData = array ();
72 protected $footerData = array ();
73 protected $titleTag = '<title>|</title>';
74 protected $metaCharsetTag = '<meta http-equiv="Content-Type" content="text/html; charset=|" />';
75 protected $htmlTag = '<html>';
76 protected $headTag = '<head>';
77 protected $baseUrlTag = '<base href="|" />';
78 protected $iconMimeType = '';
79 protected $shortcutTag = '<link rel="shortcut icon" href="%1$s"%2$s />
80 <link rel="icon" href="%1$s"%2$s />';
81
82 // static inline code blocks
83 protected $jsInline = array ();
84 protected $extOnReadyCode = array ();
85 protected $cssInline = array ();
86
87 protected $bodyContent;
88
89 protected $templateFile;
90
91 protected $jsLibraryNames = array ('prototype', 'scriptaculous', 'extjs');
92
93 const PART_COMPLETE = 0;
94 const PART_HEADER = 1;
95 const PART_FOOTER = 2;
96
97 // internal flags for JS-libraries
98 protected $addPrototype = FALSE;
99 protected $addScriptaculous = FALSE;
100 protected $addScriptaculousModules = array ('builder' => FALSE, 'effects' => FALSE, 'dragdrop' => FALSE, 'controls' => FALSE, 'slider' => FALSE);
101 protected $addExtJS = FALSE;
102 protected $addExtCore = FALSE;
103 protected $extJSadapter = 'ext/ext-base.js';
104
105 protected $enableExtJsDebug = FALSE;
106 protected $enableExtCoreDebug = FALSE;
107
108 // available adapters for extJs
109 const EXTJS_ADAPTER_JQUERY = 'jquery';
110 const EXTJS_ADAPTER_PROTOTYPE = 'prototype';
111 const EXTJS_ADAPTER_YUI = 'yui';
112
113 protected $extJStheme = TRUE;
114 protected $extJScss = TRUE;
115
116 protected $enableExtJSQuickTips = false;
117
118 protected $inlineLanguageLabels = array ();
119 protected $inlineSettings = array ();
120
121 protected $inlineJavascriptWrap = array ();
122
123 // used by BE modules
124 public $backPath;
125
126 /**
127 * Constructor
128 *
129 * @param string $templateFile declare the used template file. Omit this parameter will use default template
130 * @param string $backPath relative path to typo3-folder. It varies for BE modules, in FE it will be typo3/
131 * @return void
132 */
133 public function __construct($templateFile = '', $backPath = NULL) {
134
135 $this->reset();
136 $this->csConvObj = t3lib_div::makeInstance('t3lib_cs');
137
138 if (strlen($templateFile)) {
139 $this->templateFile = $templateFile;
140 }
141 $this->backPath = isset($backPath) ? $backPath : $GLOBALS['BACK_PATH'];
142
143 $this->inlineJavascriptWrap = array(
144 '<script type="text/javascript">' . LF . '/*<![CDATA[*/' . LF . '<!-- ' . LF,
145 '// -->' . LF . '/*]]>*/' . LF . '</script>' . LF
146 );
147 $this->inlineCssWrap = array(
148 '<style type="text/css">' . LF . '/*<![CDATA[*/' . LF . '<!-- ' . LF,
149 '-->' . LF . '/*]]>*/' . LF . '</style>' . LF
150 );
151
152 }
153
154 /**
155 * reset all vars to initial values
156 *
157 * @return void
158 */
159 protected function reset() {
160 $this->templateFile = TYPO3_mainDir . 'templates/template_page_backend.html';
161 $this->jsFiles = array ();
162 $this->jsFooterFiles = array ();
163 $this->jsInline = array ();
164 $this->jsFooterInline = array ();
165 $this->jsLibs = array ();
166 $this->cssFiles = array ();
167 $this->cssInline = array ();
168 $this->metaTags = array ();
169 $this->inlineComments = array ();
170 $this->headerData = array ();
171 $this->footerData = array ();
172 $this->extOnReadyCode = array ();
173 }
174 /*****************************************************/
175 /* */
176 /* Public Setters */
177 /* */
178 /* */
179 /*****************************************************/
180
181 /**
182 * Sets the title
183 *
184 * @param string $title title of webpage
185 * @return void
186 */
187 public function setTitle($title) {
188 $this->title = $title;
189 }
190
191
192 /**
193 * Enables/disables rendering of XHTML code
194 *
195 * @param boolean $enable Enable XHTML
196 * @return void
197 */
198 public function setRenderXhtml($enable) {
199 $this->renderXhtml = $enable;
200 }
201
202 /**
203 * Sets xml prolog and docType
204 *
205 * @param string $xmlPrologAndDocType complete tags for xml prolog and docType
206 * @return void
207 */
208 public function setXmlPrologAndDocType($xmlPrologAndDocType) {
209 $this->xmlPrologAndDocType = $xmlPrologAndDocType;
210 }
211
212 /**
213 * Sets meta charset
214 *
215 * @param string $charSet used charset
216 * @return void
217 */
218 public function setCharSet($charSet) {
219 $this->charSet = $charSet;
220 }
221
222 /**
223 * Sets language
224 *
225 * @param string $lang used language
226 * @return void
227 */
228 public function setLanguage($lang) {
229 $this->lang = $lang;
230 }
231
232 /**
233 * Sets html tag
234 *
235 * @param string $htmlTag html tag
236 * @return void
237 */
238 public function setHtmlTag($htmlTag) {
239 $this->htmlTag = $htmlTag;
240 }
241
242 /**
243 * Sets head tag
244 *
245 * @param string $tag head tag
246 * @return void
247 */
248 public function setHeadTag($headTag) {
249 $this->headTag = $headTag;
250 }
251
252 /**
253 * Sets favicon
254 *
255 * @param string $favIcon
256 * @return void
257 */
258 public function setFavIcon($favIcon) {
259 $this->favIcon = $favIcon;
260 }
261
262 /**
263 * Sets icon mime type
264 *
265 * @param string $iconMimeType
266 * @return void
267 */
268 public function setIconMimeType($iconMimeType) {
269 $this->iconMimeType = $iconMimeType;
270 }
271
272 /**
273 * Sets base url
274 *
275 * @param string $url
276 * @return void
277 */
278 public function setBaseUrl($baseUrl) {
279 $this->baseUrl = $baseUrl;
280 }
281
282 /**
283 * Sets template file
284 *
285 * @param string $file
286 * @return void
287 */
288 public function setTemplateFile($file) {
289 $this->templateFile = $file;
290 }
291
292 /**
293 * Sets back path
294 *
295 * @param string $backPath
296 * @return void
297 */
298 public function setBackPath($backPath) {
299 $this->backPath = $backPath;
300 }
301
302 /**
303 * Sets Content for Body
304 *
305 * @param string $content
306 * @return void
307 */
308 public function setBodyContent($content) {
309 $this->bodyContent = $content;
310 }
311
312 /*****************************************************/
313 /* */
314 /* Public Enablers */
315 /* */
316 /* */
317 /*****************************************************/
318 /**
319 * Enables MoveJsFromHeaderToFooter
320 *
321 * @param void
322 * @return void
323 */
324 public function enableMoveJsFromHeaderToFooter() {
325 $this->moveJsFromHeaderToFooter = TRUE;
326 }
327
328 /**
329 * Enables compression of javascript
330 *
331 * @param void
332 * @return void
333 */
334 public function enableCompressJavascript() {
335 $this->compressJavascript = TRUE;
336 }
337
338 /**
339 * Enables compression of css
340 *
341 * @param void
342 * @return void
343 */
344 public function enableCompressCss() {
345 $this->compressCss = TRUE;
346 }
347
348 /**
349 /**
350 * Enables concatenation of js/css files
351 *
352 * @param void
353 * @return void
354 */
355 public function enableConcatenateFiles() {
356 $this->concatenateFiles = TRUE;
357 }
358
359 /**
360 * Sets removal of all line breaks in template
361 *
362 * @param void
363 * @return void
364 */
365 public function enableRemoveLineBreaksFromTemplate() {
366 $this->removeLineBreaksFromTemplate = TRUE;
367 }
368
369 /*****************************************************/
370 /* */
371 /* Public Getters */
372 /* */
373 /* */
374 /*****************************************************/
375
376 /**
377 * Gets the title
378 *
379 * @return string $title title of webpage
380 */
381 public function getTitle() {
382 return $this->title;
383 }
384
385 /**
386 * Gets the charSet
387 *
388 * @return string $charSet
389 */
390 public function getCharSet() {
391 return $this->charSet;
392 }
393
394 /**
395 * Gets the language
396 *
397 * @return string $lang
398 */
399 public function getLanguage() {
400 return $this->lang;
401 }
402
403 /**
404 * Returns rendering mode XHTML or HTML
405 *
406 * @return boolean TRUE if XHTML, FALSE if HTML
407 */
408 public function getRenderXhtml() {
409 return $this->renderXhtml;
410 }
411
412 /**
413 * Gets html tag
414 *
415 * @return string $htmlTag html tag
416 */
417 public function getHtmlTag() {
418 return $this->htmlTag;
419 }
420
421 /**
422 * Gets head tag
423 *
424 * @return string $tag head tag
425 */
426 public function getHeadTag() {
427 return $this->headTag;
428 }
429
430 /**
431 * Gets favicon
432 *
433 * @return string $favIcon
434 */
435 public function getFavIcon() {
436 return $this->favIcon;
437 }
438
439 /**
440 * Gets icon mime type
441 *
442 * @return string $iconMimeType
443 */
444 public function getIconMimeType() {
445 return $this->iconMimeType;
446 }
447
448 /**
449 * Gets base url
450 *
451 * @return string $url
452 */
453 public function getBaseUrl() {
454 return $this->baseUrl;
455 }
456
457 /**
458 * Gets template file
459 *
460 * @return string $file
461 */
462 public function getTemplateFile($file) {
463 return $this->templateFile;
464 }
465
466 /**
467 * Gets MoveJsFromHeaderToFooter
468 *
469 * @return boolean
470 */
471 public function getMoveJsFromHeaderToFooter() {
472 return $this->moveJsFromHeaderToFooter;
473 }
474
475 /**
476 * Gets compress of javascript
477 *
478 * @return boolean
479 */
480 public function getCompressJavascript() {
481 return $this->compressJavascript;
482 }
483
484 /**
485 * Gets compress of css
486 *
487 * @return boolean
488 */
489 public function getCompressCss() {
490 return $this->compressCss;
491 }
492
493 /**
494 * Gets concatenate of files
495 *
496 * @return boolean
497 */
498 public function getConcatenateFiles() {
499 return $this->concatenateFiles;
500 }
501
502 /**
503 * Gets remove of empty lines from template
504 *
505 * @return boolean
506 */
507 public function getRemoveLineBreaksFromTemplate() {
508 return $this->removeLineBreaksFromTemplate;
509 }
510
511 /**
512 * Gets content for body
513 *
514 * @return string
515 */
516 public function getBodyContent() {
517 return $this->bodyContent;
518 }
519
520 /*****************************************************/
521 /* */
522 /* Public Function to add Data */
523 /* */
524 /* */
525 /*****************************************************/
526
527 /**
528 * Adds meta data
529 *
530 * @param string $meta meta data (complete metatag)
531 * @return void
532 */
533 public function addMetaTag($meta) {
534 if (!in_array($meta, $this->metaTags)) {
535 $this->metaTags[] = $meta;
536 }
537 }
538
539 /**
540 * Adds inline HTML comment
541 *
542 * @param string $comment
543 * @return void
544 */
545 public function addInlineComment($comment) {
546 if (!in_array($comment, $this->inlineComments)) {
547 $this->inlineComments[] = $comment;
548 }
549 }
550
551 /**
552 * Adds header data
553 *
554 * @param string $data free header data for HTML header
555 * @return void
556 */
557 public function addHeaderData($data) {
558 if (!in_array($data, $this->headerData)) {
559 $this->headerData[] = $data;
560 }
561 }
562
563 /**
564 * Adds footer data
565 *
566 * @param string $data free header data for HTML header
567 * @return void
568 */
569 public function addFooterData($data) {
570 if (!in_array($data, $this->footerData)) {
571 $this->footerData[] = $data;
572 }
573 }
574
575 /* Javascript Files */
576
577 /**
578 * Adds JS Library. JS Library block is rendered on top of the JS files.
579 *
580 * @param string $name
581 * @param string $file
582 * @param string $type
583 * @param boolean $compress flag if library should be compressed
584 * @param boolean $forceOnTop flag if added library should be inserted at begin of this block
585 * @param string $allWrap
586 * @return void
587 */
588 public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = FALSE, $forceOnTop = FALSE, $allWrap = '') {
589 if (!in_array(strtolower($name), $this->jsLibs)) {
590 $this->jsLibs[strtolower($name)] = array (
591 'file' => $file,
592 'type' => $type,
593 'section' => self::PART_HEADER,
594 'compress' => $compress,
595 'forceOnTop' => $forceOnTop,
596 'allWrap' => $allWrap
597 );
598 }
599
600 }
601
602 /**
603 * Adds JS Library to Footer. JS Library block is rendered on top of the Footer JS files.
604 *
605 * @param string $name
606 * @param string $file
607 * @param string $type
608 * @param boolean $compress flag if library should be compressed
609 * @param boolean $forceOnTop flag if added library should be inserted at begin of this block
610 * @param string $allWrap
611 * @return void
612 */
613 public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = FALSE, $forceOnTop = FALSE, $allWrap = '') {
614 if (!in_array(strtolower($name), $this->jsLibs)) {
615 $this->jsLibs[strtolower($name)] = array (
616 'file' => $file,
617 'type' => $type,
618 'section' => self::PART_FOOTER,
619 'compress' => $compress,
620 'forceOnTop' => $forceOnTop,
621 'allWrap' => $allWrap
622 );
623 }
624
625 }
626
627 /**
628 * Adds JS file
629 *
630 * @param string $file
631 * @param string $type
632 * @param boolean $compress
633 * @param boolean $forceOnTop
634 * @param string $allWrap
635 * @return void
636 */
637 public function addJsFile($file, $type = 'text/javascript', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '') {
638 if (!isset($this->jsFiles[$file])) {
639 $this->jsFiles[$file] = array (
640 'type' => $type,
641 'section' => self::PART_HEADER,
642 'compress' => $compress,
643 'forceOnTop' => $forceOnTop,
644 'allWrap' => $allWrap
645 );
646 }
647 }
648
649 /**
650 * Adds JS file to footer
651 *
652 * @param string $file
653 * @param string $type
654 * @param boolean $compress
655 * @param boolean $forceOnTop
656 * @return void
657 */
658 public function addJsFooterFile($file, $type = 'text/javascript', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '') {
659 if (!isset($this->jsFiles[$file])) {
660 $this->jsFiles[$file] = array (
661 'type' => $type,
662 'section' => self::PART_FOOTER,
663 'compress' => $compress,
664 'forceOnTop' => $forceOnTop,
665 'allWrap' => $allWrap
666 );
667 }
668 }
669
670 /*Javascript Inline Blocks */
671
672 /**
673 * Adds JS inline code
674 *
675 * @param string $name
676 * @param string $block
677 * @param boolean $compress
678 * @param boolean $forceOnTop
679 * @return void
680 */
681 public function addJsInlineCode($name, $block, $compress = TRUE, $forceOnTop = FALSE) {
682 if (!isset($this->jsInline[$name])) {
683 $this->jsInline[$name] = array (
684 'code' => $block . LF,
685 'section' => self::PART_HEADER,
686 'compress' => $compress,
687 'forceOnTop' => $forceOnTop
688 );
689 }
690 }
691
692 /**
693 * Adds JS inline code to footer
694 *
695 * @param string $name
696 * @param string $block
697 * @param boolean $compress
698 * @param boolean $forceOnTop
699 * @return void
700 */
701 public function addJsFooterInlineCode($name, $block, $compress = TRUE, $forceOnTop = FALSE) {
702 if (!isset($this->jsInline[$name])) {
703 $this->jsInline[$name] = array (
704 'code' => $block . LF,
705 'section' => self::PART_FOOTER,
706 'compress' => $compress,
707 'forceOnTop' => $forceOnTop
708 );
709 }
710 }
711
712 /**
713 * Adds Ext.onready code, which will be wrapped in Ext.onReady(function() {...});
714 *
715 * @param string $block javascript code
716 * @param boolean $forceOnTop position of the javascript code (TRUE for putting it on top, default is FALSE = bottom)
717 * @return void
718 */
719 public function addExtOnReadyCode($block, $forceOnTop = FALSE) {
720 if (!in_array($block, $this->extOnReadyCode)) {
721 if ($forceOnTop) {
722 array_unshift($this->extOnReadyCode, $block);
723 } else {
724 $this->extOnReadyCode[] = $block;
725 }
726 }
727 }
728
729 /* CSS Files */
730
731 /**
732 * Adds CSS file
733 *
734 * @param string $file
735 * @param string $rel
736 * @param string $media
737 * @param string $title
738 * @param boolean $compress
739 * @param boolean $forceOnTop
740 * @return void
741 */
742 public function addCssFile($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '') {
743 if (!isset($this->cssFiles[$file])) {
744 $this->cssFiles[$file] = array (
745 'rel' => $rel,
746 'media' => $media,
747 'title' => $title,
748 'compress' => $compress,
749 'forceOnTop' => $forceOnTop,
750 'allWrap' => $allWrap
751 );
752 }
753 }
754
755 /*CSS Inline Blocks */
756
757 /**
758 * Adds CSS inline code
759 *
760 * @param string $name
761 * @param string $block
762 * @param boolean $compress
763 * @param boolean $forceOnTop
764 * @return void
765 */
766 public function addCssInlineBlock($name, $block, $compressed = FALSE, $forceOnTop = FALSE) {
767 if (!isset($this->cssInline[$name])) {
768 $this->cssInline[$name] = array (
769 'code' => $block,
770 'compress' => $compress,
771 'forceOnTop' => $forceOnTop
772 );
773 }
774 }
775
776 /* JS Libraries */
777
778 /**
779 * call function if you need the prototype library
780 *
781 * @return void
782 */
783 public function loadPrototype() {
784 $this->addPrototype = TRUE;
785 }
786
787 /**
788 * call function if you need the Scriptaculous library
789 *
790 * @param string $modules add modules you need. use "all" if you need complete modules
791 * @return void
792 */
793 public function loadScriptaculous($modules = '') {
794 // Scriptaculous require prototype, so load prototype too.
795 $this->addPrototype = TRUE;
796 $this->addScriptaculous = TRUE;
797 if ($modules) {
798 if ($modules == 'all') {
799 foreach ($this->addScriptaculousModules as $key => $value) {
800 $this->addScriptaculousModules[$key] = TRUE;
801 }
802 } else {
803 $mods = t3lib_div::trimExplode(',', $modules);
804 foreach ($mods as $mod) {
805 if (isset($this->addScriptaculousModules[strtolower($mod)])) {
806 $this->addScriptaculousModules[strtolower($mod)] = TRUE;
807 }
808 }
809 }
810 }
811 }
812
813 /**
814 * call this function if you need the extJS library
815 *
816 * @param boolean $css flag, if set the ext-css will be loaded
817 * @param boolean $theme flag, if set the ext-theme "grey" will be loaded
818 * @param string $adapter choose alternative adapter, possible values: yui, prototype, jquery
819 * @return void
820 */
821 public function loadExtJS($css = TRUE, $theme = TRUE, $adapter = '') {
822 if ($adapter) {
823 // empty $adapter will always load the ext adapter
824 switch (t3lib_div::strtolower(trim($adapter))) {
825 case self::EXTJS_ADAPTER_YUI :
826 $this->extJSadapter = 'yui/ext-yui-adapter.js';
827 break;
828 case self::EXTJS_ADAPTER_PROTOTYPE :
829 $this->extJSadapter = 'prototype/ext-prototype-adapter.js';
830 break;
831 case self::EXTJS_ADAPTER_JQUERY :
832 $this->extJSadapter = 'jquery/ext-jquery-adapter.js';
833 break;
834 }
835 }
836 $this->addExtJS = TRUE;
837 $this->extJStheme = $theme;
838 $this->extJScss = $css;
839
840 }
841
842 /**
843 * Enables ExtJs QuickTips
844 * Need extJs loaded
845 *
846 * @return void
847 *
848 */
849 public function enableExtJSQuickTips() {
850 $this->enableExtJSQuickTips = TRUE;
851 }
852
853
854 /**
855 * call function if you need the ExtCore library
856 *
857 * @return void
858 */
859 public function loadExtCore() {
860 $this->addExtCore = TRUE;
861 }
862
863 /**
864 * call this function to load debug version of ExtJS. Use this for development only
865 *
866 */
867 public function enableExtJsDebug() {
868 $this->enableExtJsDebug = TRUE;
869 }
870
871 /**
872 * call this function to load debug version of ExtCore. Use this for development only
873 *
874 * @return void
875 */
876 public function enableExtCoreDebug() {
877 $this->enableExtCoreDebug = TRUE;
878 }
879
880 /**
881 * Adds Javascript Inline Label. This will occur in TYPO3.lang - object
882 * The label can be used in scripts with TYPO3.lang.<key>
883 * Need extJs loaded
884 *
885 * @param string $key
886 * @param string $value
887 * @return void
888 */
889 public function addInlineLanguageLabel($key, $value) {
890 $this->inlineLanguageLabels[$key] = $value;
891 }
892
893 /**
894 * Adds Javascript Inline Label Array. This will occur in TYPO3.lang - object
895 * The label can be used in scripts with TYPO3.lang.<key>
896 * Array will be merged with existing array.
897 * Need extJs loaded
898 *
899 * @param array $array
900 * @return void
901 */
902 public function addInlineLanguageLabelArray(array $array) {
903 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $array);
904 }
905
906 /**
907 * Adds Javascript Inline Setting. This will occur in TYPO3.settings - object
908 * The label can be used in scripts with TYPO3.setting.<key>
909 * Need extJs loaded
910 *
911 * @param string $namespace
912 * @param string $key
913 * @param string $value
914 * @return void
915 */
916 public function addInlineSetting($namespace, $key, $value) {
917 if ($namespace) {
918 if (strpos($namespace, '.')) {
919 $parts = explode('.', $namespace);
920 $a = &$this->inlineSettings;
921 foreach ($parts as $part) {
922 $a = &$a[$part];
923 }
924 $a[$key] = $value;
925 } else {
926 $this->inlineSettings[$namespace][$key] = $value;
927 }
928 } else {
929 $this->inlineSettings[$key] = $value;
930 }
931 }
932
933 /**
934 * Adds Javascript Inline Setting. This will occur in TYPO3.settings - object
935 * The label can be used in scripts with TYPO3.setting.<key>
936 * Array will be merged with existing array.
937 * Need extJs loaded
938 *
939 * @param string $namespace
940 * @param array $array
941 * @return void
942 */
943 public function addInlineSettingArray($namespace, array $array) {
944 if ($namespace) {
945 if (strpos($namespace, '.')) {
946 $parts = explode('.', $namespace);
947 $a = &$this->inlineSettings;
948 foreach ($parts as $part) {
949 $a = &$a[$part];
950 }
951 $a = array_merge((array) $a, $array);
952 } else {
953 $this->inlineSettings[$namespace] = array_merge((array) $this->inlineSettings[$namespace], $array);
954 }
955 } else {
956 $this->inlineSettings = array_merge($this->inlineSettings, $array);
957 }
958 }
959
960 /**
961 * Adds content to body content
962 *
963 * @param string $content
964 * @return void
965 */
966 public function addBodyContent($content) {
967 $this->bodyContent .= $content;
968 }
969
970 /*****************************************************/
971 /* */
972 /* Render Functions */
973 /* */
974 /* */
975 /*****************************************************/
976
977 /**
978 * render the section (Header or Footer)
979 *
980 * @param int $part section which should be rendered: self::PART_COMPLETE, self::PART_HEADER or self::PART_FOOTER
981 * @return string content of rendered section
982 */
983 public function render($part = self::PART_COMPLETE) {
984
985 $jsFiles = '';
986 $cssFiles = '';
987 $cssInline = '';
988 $jsInline = '';
989 $jsFooterInline = '';
990 $jsFooterLibs = '';
991 $jsFooterFiles = '';
992 $noJS = FALSE;
993
994
995
996 // preRenderHook for possible manuipulation
997 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'])) {
998 $params = array (
999 'jsLibs' => &$this->jsLibs,
1000 'jsFiles' => &$this->jsFiles,
1001 'jsFooterFiles' => &$this->jsFooterFiles,
1002 'cssFiles' => &$this->cssFiles,
1003 'headerData' => &$this->headerData,
1004 'footerData' => &$this->footerData,
1005 'jsInline' => &$this->jsInline,
1006 'cssInline' => &$this->cssInline,
1007 );
1008 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'] as $hook) {
1009 t3lib_div::callUserFunction($hook, $params, $this);
1010 }
1011 }
1012
1013 $jsLibs = $this->renderJsLibraries();
1014
1015 if ($this->concatenateFiles) {
1016 // do the file concatenation
1017 $this->doConcatenate();
1018 }
1019 if ($this->compressCss || $this->compressJavascript) {
1020 // do the file compression
1021 $this->doCompress();
1022 }
1023
1024 $metaTags = implode(LF, $this->metaTags);
1025
1026 // remove ending slashes from static header block
1027 // if the page is beeing rendered as html (not xhtml)
1028 // and define variable $endingSlash for further use
1029 if ($this->getRenderXhtml()) {
1030 $endingSlash = ' /';
1031 } else {
1032 $this->metaCharsetTag = str_replace(' />', '>', $this->metaCharsetTag);
1033 $this->baseUrlTag = str_replace(' />', '>', $this->baseUrlTag);
1034 $this->shortcutTag = str_replace(' />', '>', $this->shortcutTag);
1035 $endingSlash = '';
1036 }
1037
1038 if (count($this->cssFiles)) {
1039 foreach ($this->cssFiles as $file => $properties) {
1040 $file = t3lib_div::resolveBackPath($file);
1041 $file = t3lib_div::createVersionNumberedFilename($file);
1042 $tag = '<link rel="' . $properties['rel'] . '" type="text/css" href="' .
1043 htmlspecialchars($file) . '" media="' . $properties['media'] . '"' .
1044 ($properties['title'] ? ' title="' . $properties['title'] . '"' : '') .
1045 $endingSlash . '>';
1046 if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
1047 $tag = str_replace('|', $tag, $properties['allWrap']);
1048 }
1049 if ($properties['forceOnTop']) {
1050 $cssFiles = $tag . LF . $cssFiles;
1051 } else {
1052 $cssFiles .= LF . $tag;
1053 }
1054 }
1055 }
1056
1057 if (count($this->cssInline)) {
1058
1059 foreach ($this->cssInline as $name => $properties) {
1060 if ($properties['forceOnTop']) {
1061 $cssInline = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF . $cssInline;
1062 } else {
1063 $cssInline .= '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
1064 }
1065 }
1066 $cssInline = $this->inlineCssWrap[0] . $cssInline . $this->inlineCssWrap[1];
1067
1068 }
1069
1070 if (count($this->jsLibs)) {
1071 foreach ($this->jsLibs as $name => $properties) {
1072 $properties['file'] = t3lib_div::resolveBackPath($properties['file']);
1073 $properties['file'] = t3lib_div::createVersionNumberedFilename($properties['file']);
1074 $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . $properties['type'] . '"></script>';
1075 if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
1076 $tag = str_replace('|', $tag, $properties['allWrap']);
1077 }
1078 if ($properties['forceOnTop']) {
1079 if ($properties['section'] === self::PART_HEADER) {
1080 $jsLibs = $tag . LF . $jsLibs;
1081 } else {
1082 $jsFooterLibs = $tag . LF . $jsFooterLibs;
1083 }
1084 } else {
1085 if ($properties['section'] === self::PART_HEADER) {
1086 $jsLibs .= LF . $tag;
1087 } else {
1088 $jsFooterLibs .= LF . $tag;
1089 }
1090 }
1091
1092 }
1093 }
1094
1095 if (count($this->jsFiles)) {
1096 foreach ($this->jsFiles as $file => $properties) {
1097 $file = t3lib_div::resolveBackPath($file);
1098 $file = t3lib_div::createVersionNumberedFilename($file);
1099 $tag = '<script src="' . htmlspecialchars($file) . '" type="' . $properties['type'] . '"></script>';
1100 if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) {
1101 $tag = str_replace('|', $tag, $properties['allWrap']);
1102 }
1103 if ($properties['forceOnTop']) {
1104 if ($properties['section'] === self::PART_HEADER) {
1105 $jsFiles = $tag . LF . $jsFiles;
1106 } else {
1107 $jsFooterFiles = $tag . LF . $jsFooterFiles;
1108 }
1109 } else {
1110 if ($properties['section'] === self::PART_HEADER) {
1111 $jsFiles .= LF . $tag;
1112 } else {
1113 $jsFooterFiles .= LF . $tag;
1114 }
1115 }
1116 }
1117 }
1118
1119 if (count($this->jsInline)) {
1120 foreach ($this->jsInline as $name => $properties) {
1121 if ($properties['forceOnTop']) {
1122 if ($properties['section'] === self::PART_HEADER) {
1123 $jsInline = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF . $jsInline;
1124 } else {
1125 $jsFooterInline = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF . $jsFooterInline;
1126 }
1127 } else {
1128 if ($properties['section'] === self::PART_HEADER) {
1129 $jsInline .= '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
1130 } else {
1131 $jsFooterInline .= '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
1132 }
1133 }
1134 }
1135 }
1136
1137
1138 if ($jsInline) {
1139 $jsInline = $this->inlineJavascriptWrap[0] . $jsInline . $this->inlineJavascriptWrap[1];
1140 }
1141
1142 if ($jsFooterInline) {
1143 $jsFooterInline = $this->inlineJavascriptWrap[0] . $jsFooterInline . $this->inlineJavascriptWrap[1];
1144 }
1145
1146
1147 // get template
1148 $templateFile = t3lib_div::getFileAbsFileName($this->templateFile, TRUE);
1149 $template = t3lib_div::getURL($templateFile);
1150
1151 if ($this->removeEmptyLinesFromTemplate) {
1152 $template = strtr($template, array(LF => '', CR => ''));
1153 }
1154 if ($part != self::PART_COMPLETE) {
1155 $templatePart = explode('###BODY###', $template);
1156 $template = $templatePart[$part - 1];
1157 }
1158
1159 if ($this->moveJsFromHeaderToFooter) {
1160 $jsFooterLibs = $jsLibs . LF . $jsFooterLibs;
1161 $jsLibs = '';
1162 $jsFooterFiles = $jsFiles . LF . $jsFooterFiles;
1163 $jsFiles = '';
1164 $jsFooterInline = $jsInline . LF . $jsFooterInline;
1165 $jsInline = '';
1166 }
1167
1168 $markerArray = array(
1169 'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1170 'HTMLTAG' => $this->htmlTag,
1171 'HEADTAG' => $this->headTag,
1172 'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1173 'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1174 'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1175 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1176 'CSS_INCLUDE' => $cssFiles,
1177 'CSS_INLINE' => $cssInline,
1178 'JS_INLINE' => $jsInline,
1179 'JS_INCLUDE' => $jsFiles,
1180 'JS_LIBS' => $jsLibs,
1181 'TITLE' => $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '',
1182 'META' => $metaTags,
1183 'HEADERDATA' => $this->headerData ? implode(LF, $this->headerData) : '',
1184 'FOOTERDATA' => $this->footerData ? implode(LF, $this->footerData) : '',
1185 'JS_LIBS_FOOTER' => $jsFooterLibs,
1186 'JS_INCLUDE_FOOTER' => $jsFooterFiles,
1187 'JS_INLINE_FOOTER' => $jsFooterInline,
1188 'BODY' => $this->bodyContent,
1189 );
1190
1191 $markerArray = array_map('trim', $markerArray);
1192
1193 $this->reset();
1194 return trim(t3lib_parsehtml::substituteMarkerArray($template, $markerArray, '###|###'));
1195 }
1196
1197 /**
1198 * helper function for render the javascript libraries
1199 *
1200 * @return string content with javascript libraries
1201 */
1202 protected function renderJsLibraries() {
1203 $out = '';
1204
1205 if ($this->addPrototype) {
1206 $out .= '<script src="' . $this->processJsFile($this->backPath . 'contrib/prototype/prototype.js') .
1207 '" type="text/javascript"></script>' . LF;
1208 unset($this->jsFiles[$this->backPath . 'contrib/prototype/prototype.js']);
1209 }
1210
1211 if ($this->addScriptaculous) {
1212 $mods = array ();
1213 foreach ($this->addScriptaculousModules as $key => $value) {
1214 if ($this->addScriptaculousModules[$key]) {
1215 $mods[] = $key;
1216 }
1217 }
1218 // resolve dependencies
1219 if (in_array('dragdrop', $mods) || in_array('controls', $mods)) {
1220 $mods = array_merge(array('effects'), $mods);
1221 }
1222
1223 if (count($mods)) {
1224 foreach ($mods as $module) {
1225 $out .= '<script src="' . $this->processJsFile($this->backPath .
1226 'contrib/scriptaculous/' . $module . '.js') . '" type="text/javascript"></script>' . LF;
1227 unset($this->jsFiles[$this->backPath . 'contrib/scriptaculous/' . $module . '.js']);
1228 }
1229 }
1230 $out .= '<script src="' . $this->processJsFile($this->backPath .
1231 'contrib/scriptaculous/scriptaculous.js') . '" type="text/javascript"></script>' . LF;
1232 unset($this->jsFiles[$this->backPath . 'contrib/scriptaculous/scriptaculous.js']);
1233 }
1234
1235 // include extCore
1236 if ($this->addExtCore) {
1237 $out .= '<script src="' . $this->processJsFile($this->backPath .
1238 'contrib/extjs/ext-core' . ($this->enableExtCoreDebug ? '-debug' : '') . '.js') .
1239 '" type="text/javascript"></script>' . LF;
1240 unset($this->jsFiles[$this->backPath . 'contrib/extjs/ext-core' . ($this->enableExtCoreDebug ? '-debug' : '') . '.js']);
1241 }
1242
1243 // include extJS
1244 if ($this->addExtJS) {
1245 // use the base adapter all the time
1246 $out .= '<script src="' . $this->processJsFile($this->backPath .
1247 'contrib/extjs/adapter/' . ($this->enableExtJsDebug ?
1248 str_replace('.js', '-debug.js', $this->extJSadapter) : $this->extJSadapter)) .
1249 '" type="text/javascript"></script>' . LF;
1250 $out .= '<script src="' . $this->processJsFile($this->backPath .
1251 'contrib/extjs/ext-all' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') .
1252 '" type="text/javascript"></script>' . LF;
1253
1254 // add extJS localization
1255 $localeMap = $this->csConvObj->isoArray; // load standard ISO mapping and modify for use with ExtJS
1256 $localeMap[''] = 'en';
1257 $localeMap['default'] = 'en';
1258 $localeMap['gr'] = 'el_GR'; // Greek
1259 $localeMap['no'] = 'no_BO'; // Norwegian Bokmaal
1260 $localeMap['se'] = 'se_SV'; // Swedish
1261
1262
1263 $extJsLang = isset($localeMap[$this->lang]) ? $localeMap[$this->lang] : $this->lang;
1264 // TODO autoconvert file from UTF8 to current BE charset if necessary!!!!
1265 $extJsLocaleFile = 'contrib/extjs/locale/ext-lang-' . $extJsLang . '.js';
1266 if (file_exists(PATH_typo3 . $extJsLocaleFile)) {
1267 $out .= '<script src="' . $this->processJsFile($this->backPath .
1268 $extJsLocaleFile) . '" type="text/javascript" charset="utf-8"></script>' . LF;
1269 }
1270
1271
1272 // remove extjs from JScodeLibArray
1273 unset(
1274 $this->jsFiles[$this->backPath . 'contrib/extjs/ext-all.js'], $this->jsFiles[$this->backPath . 'contrib/extjs/ext-all-debug.js']
1275 );
1276 }
1277
1278 // Convert labels/settings back to UTF-8 since json_encode() only works with UTF-8:
1279 if ($this->getCharSet() !== 'utf-8') {
1280 if ($this->inlineLanguageLabels) {
1281 $this->csConvObj->convArray($this->inlineLanguageLabels, $this->getCharSet(), 'utf-8');
1282 }
1283 if ($this->inlineSettings) {
1284 $this->csConvObj->convArray($this->inlineSettings, $this->getCharSet(), 'utf-8');
1285 }
1286 }
1287
1288 $inlineSettings = $this->inlineLanguageLabels ? 'TYPO3.lang = ' . json_encode($this->inlineLanguageLabels) . ';' : '';
1289 $inlineSettings .= $this->inlineSettings ? 'TYPO3.settings = ' . json_encode($this->inlineSettings) . ';' : '';
1290
1291 if ($this->addExtCore || $this->addExtJS) {
1292 // set clear.gif, move it on top, add handler code
1293 $code = '';
1294 if (count($this->extOnReadyCode)) {
1295 foreach ($this->extOnReadyCode as $block) {
1296 $code .= $block;
1297 }
1298 }
1299
1300 $out .= $this->inlineJavascriptWrap[0] . '
1301 Ext.ns("TYPO3");
1302 Ext.BLANK_IMAGE_URL = "' . htmlspecialchars(t3lib_div::locationHeaderUrl($this->backPath . 'gfx/clear.gif')) . '";' . LF .
1303 $inlineSettings .
1304 'Ext.onReady(function() {' .
1305 ($this->enableExtJSQuickTips ? 'Ext.QuickTips.init();' . LF : '') . $code .
1306 ' });' . $this->inlineJavascriptWrap[1];
1307 unset ($this->extOnReadyCode);
1308
1309 if ($this->extJStheme) {
1310 if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
1311 $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', TRUE, TRUE);
1312 } else {
1313 $this->addCssFile($this->backPath . 'contrib/extjs/resources/css/xtheme-blue.css', 'stylesheet', 'all', '', TRUE, TRUE);
1314 }
1315 }
1316 if ($this->extJScss) {
1317 if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
1318 $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', TRUE, TRUE);
1319 } else {
1320 $this->addCssFile($this->backPath . 'contrib/extjs/resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', TRUE, TRUE);
1321 }
1322 }
1323 } else {
1324 if ($inlineSettings) {
1325 $out .= $this->inlineJavascriptWrap[0] . $inlineSettings . $this->inlineJavascriptWrap[1];
1326 }
1327 }
1328
1329 return $out;
1330 }
1331
1332 /*****************************************************/
1333 /* */
1334 /* Tools */
1335 /* */
1336 /* */
1337 /*****************************************************/
1338
1339 /**
1340 * concatenate files into one file
1341 * registered handler
1342 *
1343 * @return void
1344 */
1345 protected function doConcatenate() {
1346 // traverse the arrays, concatenate in one file
1347 // then remove concatenated files from array and add the concatenated file
1348
1349 if ($this->concatenateFiles) {
1350 $params = array (
1351 'jsLibs' => &$this->jsLibs,
1352 'jsFiles' => &$this->jsFiles,
1353 'jsFooterFiles' => &$this->jsFooterFiles,
1354 'cssFiles' => &$this->cssFiles,
1355 'headerData' => &$this->headerData,
1356 'footerData' => &$this->footerData,
1357 );
1358
1359 if ($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['concatenateHandler']) {
1360 // use extern concatenate routine
1361 t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['concatenateHandler'], $params, $this);
1362 } elseif (TYPO3_MODE === 'BE') {
1363 $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories());
1364 $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
1365 }
1366 }
1367 }
1368
1369 /**
1370 * compress inline code
1371 *
1372 */
1373 protected function doCompress() {
1374
1375 if ($this->compressJavascript && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler']) {
1376 // use extern compress routine
1377 $params = array (
1378 'jsInline' => &$this->jsInline,
1379 'jsFooterInline' => &$this->jsFooterInline,
1380 'jsLibs' => &$this->jsLibs,
1381 'jsFiles' => &$this->jsFiles,
1382 'jsFooterFiles' => &$this->jsFooterFiles,
1383 'headerData' => &$this->headerData,
1384 'footerData' => &$this->footerData,
1385 );
1386 t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'], $params, $this);
1387 } else {
1388 // traverse the arrays, compress files
1389 $this->compressError = '';
1390
1391 if ($this->compressJavascript) {
1392 if (count($this->jsInline)) {
1393 foreach ($this->jsInline as $name => $properties) {
1394 if ($properties['compress']) {
1395 $error = '';
1396 $this->jsInline[$name]['code'] = t3lib_div::minifyJavaScript($properties['code'], $error);
1397 if ($error) {
1398 $this->compressError .= 'Error with minify JS Inline Block "' . $name . '": ' . $error . LF;
1399 }
1400 }
1401 }
1402 }
1403 if (TYPO3_MODE === 'BE') {
1404 $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
1405 $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
1406 }
1407 }
1408 }
1409 if ($this->compressCss) {
1410 // use extern compress routine
1411 $params = array (
1412 'cssInline' => &$this->cssInline,
1413 'cssFiles' => &$this->cssFiles,
1414 'headerData' => &$this->headerData,
1415 'footerData' => &$this->footerData,
1416 );
1417
1418 if ($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler']) {
1419 // use extern concatenate routine
1420 t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
1421 } elseif (TYPO3_MODE === 'BE') {
1422 $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
1423 }
1424 }
1425 }
1426
1427 /**
1428 * Returns instance of t3lib_compressor
1429 *
1430 * @return t3lib_compressor Instance of t3lib_compressor
1431 */
1432 protected function getCompressor() {
1433 if ($this->compressor === NULL) {
1434 $this->compressor = t3lib_div::makeInstance('t3lib_compressor');
1435 }
1436 return $this->compressor;
1437 }
1438
1439 /**
1440 * Processes a Javascript file dependent on the current context
1441 *
1442 * Adds the version number for Frontend, compresses the file for Backend
1443 *
1444 * @param string $filename Filename
1445 * @return string new filename
1446 */
1447 protected function processJsFile($filename) {
1448 switch (TYPO3_MODE) {
1449 case 'FE':
1450 $filename = t3lib_div::createVersionNumberedFilename($filename);
1451 break;
1452 case 'BE':
1453 $filename = $this->getCompressor()->compressJsFile($filename);
1454 break;
1455 }
1456 return $filename;
1457 }
1458 }
1459
1460 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php']) {
1461 include_once ($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php']);
1462 }
1463 ?>