TClientScriptManager::getRenderedScriptFiles()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
/**
4
 * TClientScriptManager and TClientSideOptions class file.
5
 *
6
 * @author Qiang Xue <[email protected]>
7
 * @author Gabor Berczi <[email protected]> (lazyload additions & progressive rendering)
8
 * @link https://github.com/pradosoft/prado
9
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
10
 */
11
12
namespace Prado\Web\UI;
13
14
use Prado\Prado;
15
use Prado\TApplicationMode;
16
use Prado\Exceptions\TInvalidOperationException;
17
use Prado\Web\Javascripts\TJavaScript;
18
use Prado\Web\Javascripts\TJavaScriptAsset;
19
use Prado\Web\UI\ActiveControls\ICallbackEventHandler;
20
use Prado\Web\THttpUtility;
21
22
/**
23
 * TClientScriptManager class.
24
 *
25
 * TClientScriptManager manages javascript and CSS stylesheets for a page.
26
 *
27
 * @author Qiang Xue <[email protected]>
28
 * @author Gabor Berczi <[email protected]> (lazyload additions & progressive rendering)
29
 * @since 3.0
30
 */
31
class TClientScriptManager extends \Prado\TApplicationComponent
32
{
33
	/**
34
	 * file containing javascript packages and their cross dependencies
35
	 */
36
	public const PACKAGES_FILE = 'Web/Javascripts/packages.php';
37
	/**
38
	 * file containing css packages and their cross dependencies
39
	 */
40
	public const CSS_PACKAGES_FILE = 'Web/Javascripts/css-packages.php';
41
	/**
42
	 * @var TPage page who owns this manager
43
	 */
44
	private $_page;
45
	/**
46
	 * @var array registered hidden fields, indexed by hidden field names
47
	 */
48
	private $_hiddenFields = [];
49
	/**
50
	 * @var array javascript blocks to be rendered at the beginning of the form
51
	 */
52
	private $_beginScripts = [];
53
	/**
54
	 * @var array javascript blocks to be rendered at the end of the form
55
	 */
56
	private $_endScripts = [];
57
	/**
58
	 * @var array javascript files to be rendered in the form
59
	 */
60
	private $_scriptFiles = [];
61
	/**
62
	 * @var array javascript files to be rendered in page head section
63
	 */
64
	private $_headScriptFiles = [];
65
	/**
66
	 * @var array javascript blocks to be rendered in page head section
67
	 */
68
	private $_headScripts = [];
69
	/**
70
	 * @var array CSS files
71
	 */
72
	private $_styleSheetFiles = [];
73
	/**
74
	 * @var array CSS declarations
75
	 */
76
	private $_styleSheets = [];
77
	/**
78
	 * @var array registered PRADO script libraries
79
	 */
80
	private $_registeredScripts = [];
81
	/**
82
	 * Client-side javascript library dependencies, loads from PACKAGES_FILE;
83
	 * @var array
84
	 */
85
	private static $_scripts;
86
	/**
87
	 * Client-side javascript library packages, loads from PACKAGES_FILE;
88
	 * @var array
89
	 */
90
	private static $_scriptsPackages;
91
	/**
92
	 * Client-side javascript library source folders, loads from PACKAGES_FILE;
93
	 * @var array
94
	 */
95
	private static $_scriptsFolders;
96
	/**
97
	 * @var array registered PRADO style libraries
98
	 */
99
	private $_registeredStyles = [];
100
	/**
101
	 * Client-side style library dependencies, loads from CSS_PACKAGES_FILE;
102
	 * @var array
103
	 */
104
	private static $_styles;
105
	/**
106
	 * Client-side style library packages, loads from CSS_PACKAGES_FILE;
107
	 * @var array
108
	 */
109
	private static $_stylesPackages;
110
	/**
111
	 * Client-side style library folders, loads from CSS_PACKAGES_FILE;
112
	 * @var array
113
	 */
114
	private static $_stylesFolders;
115
116
	private $_renderedHiddenFields;
117
118
	private $_renderedScriptFiles = [];
119
120
	private $_expandedScripts;
121
	private $_expandedStyles;
122
123
	/**
124
	 * Constructor.
125
	 * @param TPage $owner page that owns this client script manager
126
	 */
127
	public function __construct(TPage $owner)
128
	{
129
		$this->_page = $owner;
130
		parent::__construct();
131
	}
132
133
	/**
134
	 * @return bool whether THead is required in order to render CSS and js within head
135
	 * @since 3.1.1
136
	 */
137
	public function getRequiresHead()
138
	{
139
		return count($this->_styleSheetFiles) || count($this->_styleSheets)
140
			|| count($this->_headScriptFiles) || count($this->_headScripts);
141
	}
142
143
	public static function getPradoPackages()
144
	{
145
		return self::$_scriptsPackages;
146
	}
147
148
	public static function getPradoScripts()
149
	{
150
		return self::$_scripts;
151
	}
152
153
	/**
154
	 * Registers Prado javascript by library name. See "Web/Javascripts/packages.php"
155
	 * for library names.
156
	 * @param string $name script library name.
157
	 */
158
	public function registerPradoScript($name)
159
	{
160
		$this->registerPradoScriptInternal($name);
161
		$params = func_get_args();
162
		$this->_page->registerCachingAction('Page.ClientScript', 'registerPradoScript', $params);
163
	}
164
165
	/**
166
	 * Registers a Prado javascript library to be loaded.
167
	 * @param mixed $name
168
	 */
169
	protected function registerPradoScriptInternal($name)
170
	{
171
		// $this->checkIfNotInRender();
172
		if (!isset($this->_registeredScripts[$name])) {
173
			if (self::$_scripts === null) {
0 ignored issues
show
introduced by
The condition self::_scripts === null is always false.
Loading history...
174
				$packageFile = Prado::getFrameworkPath() . DIRECTORY_SEPARATOR . self::PACKAGES_FILE;
175
				[$folders, $packages, $deps] = include($packageFile);
176
				self::$_scriptsFolders = $folders;
177
				self::$_scripts = $deps;
178
				self::$_scriptsPackages = $packages;
179
			}
180
181
			if (isset(self::$_scripts[$name])) {
182
				$this->_registeredScripts[$name] = true;
183
			} else {
184
				throw new TInvalidOperationException('csmanager_pradoscript_invalid', $name);
185
			}
186
187
			if (($packages = array_keys($this->_registeredScripts)) !== []) {
188
				$packagesUrl = [];
189
				$isDebug = $this->getApplication()->getMode() === TApplicationMode::Debug;
190
				foreach ($packages as $p) {
191
					foreach (self::$_scripts[$p] as $dep) {
192
						foreach (self::$_scriptsPackages[$dep] as $script) {
193
							if (!isset($this->_expandedScripts[$script])) {
194
								[$base, $subPath] = $this->getScriptPackageFolder($script);
195
								[$path, $baseUrl] = $this->getPackagePathUrl($base);
196
197
								$this->_expandedScripts[$script] = true;
198
								if ($isDebug) {
199
									if (!in_array($url = $baseUrl . '/' . $subPath, $packagesUrl)) {
200
										$packagesUrl[] = $url;
201
									}
202
								} else {
203
									$minPath = preg_replace('/^(.*)(?<!\.min)\.js$/', "\\1.min.js", $subPath);
204
									if (!in_array($url = $baseUrl . '/' . $minPath, $packagesUrl)) {
205
										if (!is_file($filePath = $path . DIRECTORY_SEPARATOR . $minPath)) {
206
											file_put_contents($filePath, TJavaScript::JSMin(file_get_contents($base . '/' . $subPath)));
207
											chmod($filePath, Prado::getDefaultFilePermissions());
208
										}
209
										$packagesUrl[] = $url;
210
									}
211
								}
212
							}
213
						}
214
					}
215
				}
216
				foreach ($packagesUrl as $url) {
217
					$this->registerScriptFile($url, $url);
218
				}
219
			}
220
		}
221
	}
222
223
	/**
224
	 * @param mixed $script
225
	 * @return string Prado javascript library base asset url.
226
	 */
227
	public function getPradoScriptAssetUrl($script = 'prado')
228
	{
229
		if (!isset(self::$_scriptsFolders[$script])) {
230
			$this->registerPradoScriptInternal($script);
231
		}
232
233
		$base = Prado::getPathOfNameSpace(self::$_scriptsFolders[$script]);
234
		$assets = Prado::getApplication()->getAssetManager();
235
		return $assets->getPublishedUrl($base);
236
	}
237
238
	/**
239
	 * @param mixed $script
240
	 * @return string Prado javascript library base asset path in local filesystem.
241
	 */
242
	public function getPradoScriptAssetPath($script = 'prado')
243
	{
244
		if (!isset(self::$_scriptsFolders[$script])) {
245
			$this->registerPradoScriptInternal($script);
246
		}
247
248
		$base = Prado::getPathOfNameSpace(self::$_scriptsFolders[$script]);
249
		$assets = Prado::getApplication()->getAssetManager();
250
		return $assets->getPublishedPath($base);
251
	}
252
253
	/**
254
	 * Returns the URLs of all script files referenced on the page
255
	 * @return array Combined list of all script urls used in the page
256
	 */
257
	public function getScriptUrls()
258
	{
259
		$scripts = array_values(array_map(function ($v) {
260
			return $v->getUrl();
261
		}, $this->_headScriptFiles));
262
		$scripts = array_merge($scripts, array_values($this->_scriptFiles));
263
		$scripts = array_unique($scripts);
264
265
		return $scripts;
266
	}
267
268
	/**
269
	 * @param string $base javascript or css package path.
270
	 * @return array tuple($path,$url).
271
	 */
272
	protected function getPackagePathUrl($base)
273
	{
274
		$assets = Prado::getApplication()->getAssetManager();
275
		if (strpos($base, $assets->getBaseUrl()) === false) {
276
			return [$assets->getPublishedPath($base), $assets->publishFilePath($base)];
277
		} else {
278
			return [$assets->getBasePath() . str_replace($assets->getBaseUrl(), '', $base), $base];
279
		}
280
	}
281
282
	/**
283
	 * @param string $script javascript package source folder path.
284
	 * @return array tuple($basepath,$subpath).
285
	 */
286
	protected function getScriptPackageFolder($script)
287
	{
288
		[$base, $subPath] = explode("/", $script, 2);
289
290
		if (!array_key_exists($base, self::$_scriptsFolders)) {
291
			throw new TInvalidOperationException('csmanager_pradostyle_invalid', $base);
292
		}
293
294
		$namespace = self::$_scriptsFolders[$base];
295
		if (($dir = Prado::getPathOfNameSpace($namespace)) !== null) {
296
			$namespace = $dir;
297
		}
298
299
		return [$namespace, $subPath];
300
	}
301
302
	/**
303
	 * @param string $script css package source folder path.
304
	 * @return array tuple($basepath,$subpath).
305
	 */
306
	protected function getStylePackageFolder($script)
307
	{
308
		[$base, $subPath] = explode("/", $script, 2);
309
310
		if (!array_key_exists($base, self::$_stylesFolders)) {
311
			throw new TInvalidOperationException('csmanager_pradostyle_invalid', $base);
312
		}
313
314
		$namespace = self::$_stylesFolders[$base];
315
		if (($dir = Prado::getPathOfNameSpace($namespace)) !== null) {
316
			$namespace = $dir;
317
		}
318
319
		return [$namespace, $subPath];
320
	}
321
322
	/**
323
	 * Returns javascript statement that create a new callback request object.
324
	 * @param ICallbackEventHandler&\Prado\Web\UI\TControl $callbackHandler callback response handler
325
	 * @param null|array $options additional callback options
326
	 * @return string javascript statement that creates a new callback request.
327
	 */
328
	public function getCallbackReference(ICallbackEventHandler $callbackHandler, $options = null)
329
	{
330
		$options = !is_array($options) ? [] : $options;
331
		$class = new \ReflectionClass($callbackHandler);
0 ignored issues
show
Unused Code introduced by
The assignment to $class is dead and can be removed.
Loading history...
332
		$clientSide = $callbackHandler->getActiveControl()->getClientSide();
333
		$options = array_merge($options, $clientSide->getOptions()->toArray());
334
		$optionString = TJavaScript::encode($options);
335
		$this->registerPradoScriptInternal('ajax');
336
		$id = $callbackHandler->getUniqueID();
0 ignored issues
show
Bug introduced by
The method getUniqueID() does not exist on Prado\Web\UI\ActiveControls\ICallbackEventHandler. Since it exists in all sub-types, consider adding an abstract or default implementation to Prado\Web\UI\ActiveControls\ICallbackEventHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

336
		/** @scrutinizer ignore-call */ 
337
  $id = $callbackHandler->getUniqueID();
Loading history...
337
		return "new Prado.CallbackRequest('{$id}',{$optionString})";
338
	}
339
340
	/**
341
	 * Registers callback javascript for a control.
342
	 * @param string $class javascript class responsible for the control being registered for callback
343
	 * @param array $options callback options
344
	 */
345
	public function registerCallbackControl($class, $options)
346
	{
347
		$optionString = TJavaScript::encode($options);
348
		$code = "new {$class}({$optionString});";
349
		$this->_endScripts[sprintf('%08X', crc32($code))] = $code;
350
		$this->registerPradoScriptInternal('ajax');
351
352
		$params = func_get_args();
353
		$this->_page->registerCachingAction('Page.ClientScript', 'registerCallbackControl', $params);
354
	}
355
356
	/**
357
	 * Registers postback javascript for a control. A null class parameter will prevent
358
	 * the javascript code registration.
359
	 * @param string $class javascript class responsible for the control being registered for postback
360
	 * @param array $options postback options
361
	 */
362
	public function registerPostBackControl($class, $options)
363
	{
364
		if ($class === null) {
365
			return;
366
		}
367
		if (!isset($options['FormID']) && ($form = $this->_page->getForm()) !== null) {
368
			$options['FormID'] = $form->getClientID();
369
		}
370
		$optionString = TJavaScript::encode($options);
371
		$code = "new {$class}({$optionString});";
372
373
		$this->_endScripts[sprintf('%08X', crc32($code))] = $code;
374
		$this->registerPradoScriptInternal('prado');
375
376
		$params = func_get_args();
377
		$this->_page->registerCachingAction('Page.ClientScript', 'registerPostBackControl', $params);
378
	}
379
380
	/**
381
	 * Register a default button to panel. When the $panel is in focus and
382
	 * the 'enter' key is pressed, the $button will be clicked.
383
	 * @param string|TControl $panel panel (or its unique ID) to register the default button action
384
	 * @param string|TControl $button button (or its unique ID) to trigger a postback
385
	 */
386
	public function registerDefaultButton($panel, $button)
387
	{
388
		$panelID = is_string($panel) ? $panel : $panel->getUniqueID();
389
390
		if (is_string($button)) {
391
			$buttonID = $button;
392
		} elseif ($button instanceof \Prado\Web\UI\IButtonControl) {
393
			$button->setIsDefaultButton(true);
0 ignored issues
show
Bug introduced by
The method setIsDefaultButton() does not exist on Prado\Web\UI\TControl. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

393
			$button->/** @scrutinizer ignore-call */ 
394
            setIsDefaultButton(true);
Loading history...
394
			$buttonID = $button->getUniqueID();
395
		} else {
396
			return;
397
		}
398
		$options = TJavaScript::encode($this->getDefaultButtonOptions($panelID, $buttonID));
399
		$code = "new Prado.WebUI.DefaultButton($options);";
400
401
		$this->_endScripts['prado:' . $panelID] = $code;
402
		$this->registerPradoScriptInternal('prado');
403
404
		$params = [$panelID, $buttonID];
405
		$this->_page->registerCachingAction('Page.ClientScript', 'registerDefaultButton', $params);
406
	}
407
408
	/**
409
	 * @param string $panelID the unique ID of the container control
410
	 * @param string $buttonID the unique ID of the button control
411
	 * @return array default button options.
412
	 */
413
	protected function getDefaultButtonOptions($panelID, $buttonID)
414
	{
415
		$options['ID'] = TControl::convertUniqueIdToClientId($panelID);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
416
		$options['Panel'] = TControl::convertUniqueIdToClientId($panelID);
417
		$options['Target'] = TControl::convertUniqueIdToClientId($buttonID);
418
		$options['EventTarget'] = $buttonID;
419
		$options['Event'] = 'click';
420
		return $options;
421
	}
422
423
	/**
424
	 * Registers the control to receive default focus.
425
	 * @param string $target the client ID of the control to receive default focus
426
	 */
427
	public function registerFocusControl($target)
428
	{
429
		$this->registerPradoScriptInternal('jquery');
430
		if ($target instanceof TControl) {
431
			$target = $target->getClientID();
432
		}
433
		$this->_endScripts['prado:focus'] = 'jQuery(\'#' . $target . '\').focus();';
434
435
		$params = func_get_args();
436
		$this->_page->registerCachingAction('Page.ClientScript', 'registerFocusControl', $params);
437
	}
438
439
	/**
440
	 * Registers Prado style by library name. See "Web/Javascripts/packages.php"
441
	 * for library names.
442
	 * @param string $name style library name.
443
	 */
444
	public function registerPradoStyle($name)
445
	{
446
		$this->registerPradoStyleInternal($name);
447
		$params = func_get_args();
448
		$this->_page->registerCachingAction('Page.ClientScript', 'registerPradoStyle', $params);
449
	}
450
451
	/**
452
	 * Registers a Prado style library to be loaded.
453
	 * @param mixed $name
454
	 */
455
	protected function registerPradoStyleInternal($name)
456
	{
457
		// $this->checkIfNotInRender();
458
		if (!isset($this->_registeredStyles[$name])) {
459
			if (self::$_styles === null) {
0 ignored issues
show
introduced by
The condition self::_styles === null is always false.
Loading history...
460
				$packageFile = Prado::getFrameworkPath() . DIRECTORY_SEPARATOR . self::CSS_PACKAGES_FILE;
461
				[$folders, $packages, $deps] = include($packageFile);
462
				self::$_stylesFolders = $folders;
463
				self::$_styles = $deps;
464
				self::$_stylesPackages = $packages;
465
			}
466
467
			if (isset(self::$_styles[$name])) {
468
				$this->_registeredStyles[$name] = true;
469
			} else {
470
				throw new TInvalidOperationException('csmanager_pradostyle_invalid', $name);
471
			}
472
473
			if (($packages = array_keys($this->_registeredStyles)) !== []) {
474
				$packagesUrl = [];
475
				$isDebug = $this->getApplication()->getMode() === TApplicationMode::Debug;
0 ignored issues
show
Unused Code introduced by
The assignment to $isDebug is dead and can be removed.
Loading history...
476
				foreach ($packages as $p) {
477
					foreach (self::$_styles[$p] as $dep) {
478
						foreach (self::$_stylesPackages[$dep] as $style) {
479
							if (!isset($this->_expandedStyles[$style])) {
480
								[$base, $subPath] = $this->getStylePackageFolder($style);
481
								[$path, $baseUrl] = $this->getPackagePathUrl($base);
482
483
								$this->_expandedStyles[$style] = true;
484
								// TODO minify css?
485
								if (!in_array($url = $baseUrl . '/' . $subPath, $packagesUrl)) {
486
									$packagesUrl[] = $url;
487
								}
488
							}
489
						}
490
					}
491
				}
492
				foreach ($packagesUrl as $url) {
493
					$this->registerStyleSheetFile($url, $url);
494
				}
495
			}
496
		}
497
	}
498
499
	/**
500
	 * Registers a CSS file to be rendered in the page head
501
	 *
502
	 * The CSS files in themes are registered in {@see OnPreRenderComplete onPreRenderComplete} if you want to override
503
	 * CSS styles in themes you need to register it after this event is completed.
504
	 *
505
	 * Example:
506
	 * ```php
507
	 * <?php
508
	 * class BasePage extends TPage {
509
	 *   public function onPreRenderComplete($param) {
510
	 *     parent::onPreRenderComplete($param);
511
	 *     $url = 'path/to/your/stylesheet.css';
512
	 *     $this->getPage()->getClientScript()->registerStyleSheetFile($url, $url);
513
	 *   }
514
	 * }
515
	 * ```
516
	 *
517
	 * @param string $key a unique key identifying the file
518
	 * @param string $url URL to the CSS file
519
	 * @param string $media media type of the CSS (such as 'print', 'screen', etc.). Defaults to empty, meaning the CSS applies to all media types.
520
	 */
521
	public function registerStyleSheetFile($key, $url, $media = '')
522
	{
523
		if ($media === '') {
524
			$this->_styleSheetFiles[$key] = $url;
525
		} else {
526
			$this->_styleSheetFiles[$key] = [$url, $media];
527
		}
528
529
		$params = func_get_args();
530
		$this->_page->registerCachingAction('Page.ClientScript', 'registerStyleSheetFile', $params);
531
	}
532
533
	/**
534
	 * Registers a CSS block to be rendered in the page head
535
	 * @param string $key a unique key identifying the CSS block
536
	 * @param string $css CSS block
537
	 * @param string $media media type of the CSS (such as 'print', 'screen', etc.). Defaults to empty, meaning the CSS applies to all media types.
538
	 */
539
	public function registerStyleSheet($key, $css, $media = '')
540
	{
541
		$this->_styleSheets[$key] = $css;
542
543
		$params = func_get_args();
544
		$this->_page->registerCachingAction('Page.ClientScript', 'registerStyleSheet', $params);
545
	}
546
547
	/**
548
	 * Returns the URLs of all stylesheet files referenced on the page
549
	 * @return array List of all stylesheet urls used in the page
550
	 */
551
	public function getStyleSheetUrls()
552
	{
553
		$stylesheets = array_values(
554
			array_map(function ($e) {
555
				return is_array($e) ? $e[0] : $e;
556
			}, $this->_styleSheetFiles)
557
		);
558
559
		foreach (Prado::getApplication()->getAssetManager()->getPublished() as $path => $url) {
560
			if (substr($url, strlen($url) - 4) == '.css') {
561
				$stylesheets[] = $url;
562
			}
563
		}
564
565
		$stylesheets = array_unique($stylesheets);
566
567
		return $stylesheets;
568
	}
569
570
	/**
571
	 * Returns all the stylesheet code snippets referenced on the page
572
	 * @return array List of all stylesheet snippets used in the page
573
	 */
574
	public function getStyleSheetCodes()
575
	{
576
		return array_unique(array_values($this->_styleSheets));
577
	}
578
579
	/**
580
	 * Registers a javascript file in the page head
581
	 * @param string $key a unique key identifying the file
582
	 * @param string $url URL to the javascript file
583
	 * @param bool $async load the javascript file asynchronously, default false
584
	 */
585
	public function registerHeadScriptFile($key, $url, $async = false)
586
	{
587
		$this->checkIfNotInRender();
588
		$this->_headScriptFiles[$key] = new TJavaScriptAsset($url, $async);
589
590
		$params = func_get_args();
591
		$this->_page->registerCachingAction('Page.ClientScript', 'registerHeadScriptFile', $params);
592
	}
593
594
	/**
595
	 * Registers a javascript block in the page head.
596
	 * @param string $key a unique key identifying the script block
597
	 * @param string $script javascript block
598
	 */
599
	public function registerHeadScript($key, $script)
600
	{
601
		$this->checkIfNotInRender();
602
		$this->_headScripts[$key] = $script;
603
604
		$params = func_get_args();
605
		$this->_page->registerCachingAction('Page.ClientScript', 'registerHeadScript', $params);
606
	}
607
608
	/**
609
	 * Registers a javascript file to be rendered within the form
610
	 * @param string $key a unique key identifying the file
611
	 * @param string $url URL to the javascript file to be rendered
612
	 */
613
	public function registerScriptFile($key, $url)
614
	{
615
		$this->_scriptFiles[$key] = $url;
616
617
		$params = func_get_args();
618
		$this->_page->registerCachingAction('Page.ClientScript', 'registerScriptFile', $params);
619
	}
620
621
	/**
622
	 * Registers a javascript script block at the beginning of the form
623
	 * @param string $key a unique key identifying the script block
624
	 * @param string $script javascript block
625
	 */
626
	public function registerBeginScript($key, $script)
627
	{
628
		$this->checkIfNotInRender();
629
		$this->_beginScripts[$key] = $script;
630
631
		$params = func_get_args();
632
		$this->_page->registerCachingAction('Page.ClientScript', 'registerBeginScript', $params);
633
	}
634
635
	/**
636
	 * Registers a javascript script block at the end of the form
637
	 * @param string $key a unique key identifying the script block
638
	 * @param string $script javascript block
639
	 */
640
	public function registerEndScript($key, $script)
641
	{
642
		$this->_endScripts[$key] = $script;
643
644
		$params = func_get_args();
645
		$this->_page->registerCachingAction('Page.ClientScript', 'registerEndScript', $params);
646
	}
647
648
	/**
649
	 * Registers a hidden field to be rendered in the form.
650
	 * @param string $name a unique key identifying the hidden field
651
	 * @param array|string $value hidden field value, if the value is an array, every element
652
	 * in the array will be rendered as a hidden field value.
653
	 */
654
	public function registerHiddenField($name, $value)
655
	{
656
		$this->_hiddenFields[$name] = $value;
657
658
		$params = func_get_args();
659
		$this->_page->registerCachingAction('Page.ClientScript', 'registerHiddenField', $params);
660
	}
661
662
	/**
663
	 * @param string $key a unique key
664
	 * @return bool whether there is a CSS file registered with the specified key
665
	 */
666
	public function isStyleSheetFileRegistered($key)
667
	{
668
		return isset($this->_styleSheetFiles[$key]);
669
	}
670
671
	/**
672
	 * @param string $key a unique key
673
	 * @return bool whether there is a CSS block registered with the specified key
674
	 */
675
	public function isStyleSheetRegistered($key)
676
	{
677
		return isset($this->_styleSheets[$key]);
678
	}
679
680
	/**
681
	 * @param string $key a unique key
682
	 * @return bool whether there is a head javascript file registered with the specified key
683
	 */
684
	public function isHeadScriptFileRegistered($key)
685
	{
686
		return isset($this->_headScriptFiles[$key]);
687
	}
688
689
	/**
690
	 * @param string $key a unique key
691
	 * @return bool whether there is a head javascript block registered with the specified key
692
	 */
693
	public function isHeadScriptRegistered($key)
694
	{
695
		return isset($this->_headScripts[$key]);
696
	}
697
698
	/**
699
	 * @param string $key a unique key
700
	 * @return bool whether there is a javascript file registered with the specified key
701
	 */
702
	public function isScriptFileRegistered($key)
703
	{
704
		return isset($this->_scriptFiles[$key]);
705
	}
706
707
	/**
708
	 * @param string $key a unique key
709
	 * @return bool whether there is a beginning javascript block registered with the specified key
710
	 */
711
	public function isBeginScriptRegistered($key)
712
	{
713
		return isset($this->_beginScripts[$key]);
714
	}
715
716
	/**
717
	 * @param string $key a unique key
718
	 * @return bool whether there is an ending javascript block registered with the specified key
719
	 */
720
	public function isEndScriptRegistered($key)
721
	{
722
		return isset($this->_endScripts[$key]);
723
	}
724
725
	/**
726
	 * @return bool true if any end scripts are registered.
727
	 */
728
	public function hasEndScripts()
729
	{
730
		return count($this->_endScripts) > 0;
731
	}
732
733
	/**
734
	 * @return bool true if any begin scripts are registered.
735
	 */
736
	public function hasBeginScripts()
737
	{
738
		return count($this->_beginScripts) > 0;
739
	}
740
741
	/**
742
	 * @param string $key a unique key
743
	 * @return bool whether there is a hidden field registered with the specified key
744
	 */
745
	public function isHiddenFieldRegistered($key)
746
	{
747
		return isset($this->_hiddenFields[$key]);
748
	}
749
750
	/**
751
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
752
	 */
753
	public function renderStyleSheetFiles($writer)
754
	{
755
		$str = '';
756
		foreach ($this->_styleSheetFiles as $url) {
757
			if (is_array($url)) {
758
				$str .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"{$url[1]}\" href=\"" . THttpUtility::htmlEncode($url[0]) . "\" />\n";
759
			} else {
760
				$str .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"" . THttpUtility::htmlEncode($url) . "\" />\n";
761
			}
762
		}
763
		$writer->write($str);
764
	}
765
766
	/**
767
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
768
	 */
769
	public function renderStyleSheets($writer)
770
	{
771
		if (count($this->_styleSheets)) {
772
			$writer->write("<style type=\"text/css\">\n/*<![CDATA[*/\n" . implode("\n", $this->_styleSheets) . "\n/*]]>*/\n</style>\n");
773
		}
774
	}
775
776
	/**
777
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
778
	 */
779
	public function renderHeadScriptFiles($writer)
780
	{
781
		$this->renderScriptFiles($writer, $this->_headScriptFiles);
782
	}
783
784
	/**
785
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
786
	 */
787
	public function renderHeadScripts($writer)
788
	{
789
		$writer->write(TJavaScript::renderScriptBlocks($this->_headScripts));
790
	}
791
792
	public function renderScriptFilesBegin($writer)
793
	{
794
		$this->renderAllPendingScriptFiles($writer);
795
	}
796
797
	public function renderScriptFilesEnd($writer)
798
	{
799
		$this->renderAllPendingScriptFiles($writer);
800
	}
801
802
	public function markScriptFileAsRendered($url)
803
	{
804
		$url = (is_object($url) && ($url instanceof TJavaScriptAsset)) ? $url->getUrl() : $url;
805
		$this->_renderedScriptFiles[$url] = $url;
806
		$params = func_get_args();
807
		$this->_page->registerCachingAction('Page.ClientScript', 'markScriptFileAsRendered', $params);
808
	}
809
810
	protected function renderScriptFiles($writer, array $scripts)
811
	{
812
		foreach ($scripts as $script) {
813
			$writer->write(TJavaScript::renderScriptFile($script));
814
			$this->markScriptFileAsRendered($script);
815
		}
816
	}
817
818
	protected function getRenderedScriptFiles()
819
	{
820
		return $this->_renderedScriptFiles;
821
	}
822
823
	/**
824
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
825
	 */
826
	public function renderAllPendingScriptFiles($writer)
827
	{
828
		if (!empty($this->_scriptFiles)) {
829
			$addedScripts = array_diff($this->_scriptFiles, $this->getRenderedScriptFiles());
830
			$this->renderScriptFiles($writer, $addedScripts);
831
		}
832
	}
833
834
	/**
835
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
836
	 */
837
	public function renderBeginScripts($writer)
838
	{
839
		$writer->write(TJavaScript::renderScriptBlocks($this->_beginScripts));
840
	}
841
842
	/**
843
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
844
	 */
845
	public function renderEndScripts($writer)
846
	{
847
		$writer->write(TJavaScript::renderScriptBlocks($this->_endScripts));
848
	}
849
850
	/**
851
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
852
	 */
853
	public function renderBeginScriptsCallback($writer)
854
	{
855
		$writer->write(TJavaScript::renderScriptBlocksCallback($this->_beginScripts));
856
	}
857
858
	/**
859
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
860
	 */
861
	public function renderEndScriptsCallback($writer)
862
	{
863
		$writer->write(TJavaScript::renderScriptBlocksCallback($this->_endScripts));
864
	}
865
866
	public function renderHiddenFieldsBegin($writer)
867
	{
868
		$this->renderHiddenFieldsInt($writer, true);
869
	}
870
871
	public function renderHiddenFieldsEnd($writer)
872
	{
873
		$this->renderHiddenFieldsInt($writer, false);
874
	}
875
876
	/**
877
	 * Flushes all pending script registrations
878
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
879
	 * @param null|TControl $control the control forcing the flush (used only in error messages)
880
	 */
881
	public function flushScriptFiles($writer, $control = null)
882
	{
883
		if (!$this->_page->getIsCallback()) {
884
			$this->_page->ensureRenderInForm($control);
885
			$this->renderAllPendingScriptFiles($writer);
886
		}
887
	}
888
889
	/**
890
	 * Renders hidden fields. To avoid browsers from trying to restore the previous
891
	 * state of the fields after a page reload, the autocomplete="off" attribute is used.
892
	 * Unfortunately this attribute is invalid for hidden fields, so text fields are
893
	 * rendered instead (#642).
894
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for the rendering purpose
895
	 * @param mixed $initial
896
	 */
897
	protected function renderHiddenFieldsInt($writer, $initial)
898
	{
899
		if ($initial) {
900
			$this->_renderedHiddenFields = [];
901
		}
902
		$str = '';
903
		foreach ($this->_hiddenFields as $name => $value) {
904
			if (in_array($name, $this->_renderedHiddenFields)) {
905
				continue;
906
			}
907
			$id = strtr($name, ':', '_');
908
			if (is_array($value)) {
909
				foreach ($value as $v) {
910
					$str .= '<input type="text" style="display:none" autocomplete="off" name="' . $name . '[]" id="' . $id . '" value="' . THttpUtility::htmlEncode($value) . "\" />\n";
0 ignored issues
show
Bug introduced by
$value of type array is incompatible with the type string expected by parameter $s of Prado\Web\THttpUtility::htmlEncode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

910
					$str .= '<input type="text" style="display:none" autocomplete="off" name="' . $name . '[]" id="' . $id . '" value="' . THttpUtility::htmlEncode(/** @scrutinizer ignore-type */ $value) . "\" />\n";
Loading history...
911
				}
912
			} else {
913
				$str .= '<input type="text" style="display:none" autocomplete="off" name="' . $name . '" id="' . $id . '" value="' . THttpUtility::htmlEncode($value) . "\" />\n";
914
			}
915
			$this->_renderedHiddenFields[] = $name;
916
		}
917
		if ($str !== '') {
918
			$writer->write("<div style=\"visibility:hidden;\">\n" . $str . "</div>\n");
919
		}
920
	}
921
922
	public function getHiddenFields()
923
	{
924
		return $this->_hiddenFields;
925
	}
926
927
	/**
928
	 * Checks whether page rendering has not begun yet
929
	 * @throws \Exception
930
	 */
931
	protected function checkIfNotInRender()
932
	{
933
		if ($form = $this->_page->getInFormRender()) {
0 ignored issues
show
Unused Code introduced by
The assignment to $form is dead and can be removed.
Loading history...
934
			throw new \Exception('Operation invalid when page is already rendering');
935
		}
936
	}
937
}
938