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) { |
|
|
|
|
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); |
|
|
|
|
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(); |
|
|
|
|
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); |
|
|
|
|
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); |
|
|
|
|
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) { |
|
|
|
|
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; |
|
|
|
|
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"; |
|
|
|
|
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()) { |
|
|
|
|
934
|
|
|
throw new \Exception('Operation invalid when page is already rendering'); |
935
|
|
|
} |
936
|
|
|
} |
937
|
|
|
} |
938
|
|
|
|