root/trunk/framework/Web/UI/TClientScriptManager.php

Revision 2514, 20.5 kB (checked in by carl, 5 weeks ago)

#856 - Assets PRADO_CHMOD constant missing in several places

  • Property svn:keywords set to Id
Line 
1<?php
2/**
3 * TClientScriptManager and TClientSideOptions class file.
4 *
5 * @author Qiang Xue <qiang.xue@gmail.com>
6 * @link http://www.pradosoft.com/
7 * @copyright Copyright &copy; 2005-2008 PradoSoft
8 * @license http://www.pradosoft.com/license/
9 * @version $Id$
10 * @package System.Web.UI
11 */
12
13/**
14 * TClientScriptManager class.
15 *
16 * TClientScriptManager manages javascript and CSS stylesheets for a page.
17 *
18 * @author Qiang Xue <qiang.xue@gmail.com>
19 * @version $Id$
20 * @package System.Web.UI
21 * @since 3.0
22 */
23class TClientScriptManager extends TApplicationComponent
24{
25    /**
26     * directory containing Prado javascript files
27     */
28    const SCRIPT_PATH='Web/Javascripts/source';
29    /**
30     * the PHP script for loading Prado javascript files
31     */
32    const SCRIPT_LOADER='Web/Javascripts/clientscripts.php';
33
34    /**
35     * @var TPage page who owns this manager
36     */
37    private $_page;
38    /**
39     * @var array registered hidden fields, indexed by hidden field names
40     */
41    private $_hiddenFields=array();
42    /**
43     * @var array javascript blocks to be rendered at the beginning of the form
44     */
45    private $_beginScripts=array();
46    /**
47     * @var array javascript blocks to be rendered at the end of the form
48     */
49    private $_endScripts=array();
50    /**
51     * @var array javascript files to be rendered in the form
52     */
53    private $_scriptFiles=array();
54    /**
55     * @var array javascript files to be rendered in page head section
56     */
57    private $_headScriptFiles=array();
58    /**
59     * @var array javascript blocks to be rendered in page head section
60     */
61    private $_headScripts=array();
62    /**
63     * @var array CSS files
64     */
65    private $_styleSheetFiles=array();
66    /**
67     * @var array CSS declarations
68     */
69    private $_styleSheets=array();
70    /**
71     * @var array registered PRADO script libraries
72     */
73    private $_registeredPradoScripts=array();
74    /**
75     * Client-side javascript library dependencies, loads from SCRIPT_PATH.'/packages.php';
76     * @var array
77     */
78    private static $_pradoScripts;
79
80    /**
81     * Constructor.
82     * @param TPage page that owns this client script manager
83     */
84    public function __construct(TPage $owner)
85    {
86        $this->_page=$owner;
87    }
88
89    /**
90     * @return boolean whether THead is required in order to render CSS and js within head
91     * @since 3.1.1
92     */
93    public function getRequiresHead()
94    {
95        return count($this->_styleSheetFiles) || count($this->_styleSheets)
96            || count($this->_headScriptFiles) || count($this->_headScripts);
97    }
98
99    /**
100     * Registers Prado javascript by library name. See "Web/Javascripts/source/packages.php"
101     * for library names.
102     * @param string script library name.
103     */
104    public function registerPradoScript($name)
105    {
106        $this->registerPradoScriptInternal($name);
107        $params=func_get_args();
108        $this->_page->registerCachingAction('Page.ClientScript','registerPradoScript',$params);
109    }
110
111    /**
112     * Registers a Prado javascript library to be loaded.
113     */
114    private function registerPradoScriptInternal($name)
115    {
116        if(!isset($this->_registeredPradoScripts[$name]))
117        {
118            if(self::$_pradoScripts === null)
119            {
120                $packageFile = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH.'/packages.php';
121                list($packages,$deps)= include($packageFile);
122                self::$_pradoScripts = $deps;
123            }
124
125            if(isset(self::$_pradoScripts[$name]))
126                $this->_registeredPradoScripts[$name]=true;
127            else
128                throw new TInvalidOperationException('csmanager_pradoscript_invalid',$name);
129        }
130    }
131
132    /**
133     * @return string Prado javascript library base asset url.
134     */
135    public function getPradoScriptAssetUrl()
136    {
137        $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH;
138        $assets = Prado::getApplication()->getAssetManager();
139        return $assets->getPublishedUrl($base);
140    }
141
142    /**
143     * Renders the HTML tags for PRADO js files
144     * @param THtmlWriter writer
145     */
146    protected function renderPradoScripts($writer)
147    {
148        if(($packages=array_keys($this->_registeredPradoScripts))!==array())
149        {
150            $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH;
151            $url = $this->registerJavascriptPackages($base, $packages);
152            $writer->write(TJavaScript::renderScriptFile($url));
153        }
154    }
155
156    /**
157     * Publishes a javascript library path and register packages to be loaded.
158     * See TClientScriptLoader for component that enables users to register custom javascript libraries.
159     * @param string javascript library base path
160     * @param array list of packages or javascript files (without .js extension) to be loaded.
161     * @param boolean true to enable keep comments in javascript files loaded, null to use application configuration.
162     * @param boolean true to gzip the javascript code if browsers and php supports it.
163     * @return string javascript src url
164     * @since 3.1
165     */
166    public function registerJavascriptPackages($base, $packages, $debug=null, $gzip=true)
167    {
168        list($path,$url) = $this->getPackagePathUrl($base);
169        $scriptLoaderPath = $path.'/'.basename(self::SCRIPT_LOADER);
170        $scriptLoaderSrc = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_LOADER;
171        if(!is_file($scriptLoaderPath))
172        {
173            copy($scriptLoaderSrc, $scriptLoaderPath);
174            chmod($scriptLoaderPath, PRADO_CHMOD);
175        }
176        $url .= '/'.basename(self::SCRIPT_LOADER).'?js='.implode(',', $packages);
177        if($debug!==false && $this->getApplication()->getMode()===TApplicationMode::Debug)
178        {
179            $this->verifyJavascriptPackages($base,$path,$packages);
180            $url.='&amp;mode=debug';
181        }
182        if($gzip===false)
183            $url.='&amp;gzip=false';
184        return $url;
185    }
186
187    /**
188     * @throws TConfigurationException when javascript packages mismatch.
189     */
190    protected function verifyJavascriptPackages($base,$path,$scripts)
191    {
192        $file = $path.'/packages.php';
193        if(is_file($file))
194        {
195            list($packs,$deps) = include($file);
196            if(count($missing = array_diff($scripts, array_keys($deps))) > 0)
197            {
198                throw new TConfigurationException('csmanager_invalid_packages',
199                    $base.'/packages.php',implode(', ', $missing), implode(', ', array_keys($deps)));
200            }
201        }
202    }
203
204    /**
205     * @param string javascript package path.
206     * @return array tuple($path,$url).
207     */
208    protected function getPackagePathUrl($base)
209    {
210        $assets = Prado::getApplication()->getAssetManager();
211        if(strpos($base, $assets->getBaseUrl())===false)
212        {
213            if(($dir = Prado::getPathOfNameSpace($base)) !== null) {
214                $base = $dir;
215            }
216            return array($assets->getPublishedPath($base), $assets->publishFilePath($base));
217        }
218        else
219        {
220            return array($assets->getBasePath().str_replace($assets->getBaseUrl(),'',$base), $base);
221        }
222    }
223
224    /**
225     * Returns javascript statement that create a new callback request object.
226     * @param ICallbackEventHandler callback response handler
227     * @param array additional callback options
228     * @return string javascript statement that creates a new callback request.
229     */
230    public function getCallbackReference(ICallbackEventHandler $callbackHandler, $options=null)
231    {
232        $options = !is_array($options) ? array() : $options;
233        $class = new TReflectionClass($callbackHandler);
234        $clientSide = $callbackHandler->getActiveControl()->getClientSide();
235        $options = array_merge($options, $clientSide->getOptions()->toArray());
236        $optionString = TJavascript::encode($options);
237        $this->registerPradoScriptInternal('ajax');
238        $id = $callbackHandler->getUniqueID();
239        return "new Prado.CallbackRequest('{$id}',{$optionString})";
240    }
241
242    /**
243     * Registers callback javascript for a control.
244     * @param string javascript class responsible for the control being registered for callback
245     * @param array callback options
246     */
247    public function registerCallbackControl($class, $options)
248    {
249        $optionString=TJavaScript::encode($options);
250        $code="new {$class}({$optionString});";
251        $this->_endScripts[sprintf('%08X', crc32($code))]=$code;
252        $this->registerPradoScriptInternal('ajax');
253
254        $params=func_get_args();
255        $this->_page->registerCachingAction('Page.ClientScript','registerCallbackControl',$params);
256    }
257
258    /**
259     * Registers postback javascript for a control. A null class parameter will prevent
260     * the javascript code registration.
261     * @param string javascript class responsible for the control being registered for postback
262     * @param array postback options
263     */
264    public function registerPostBackControl($class,$options)
265    {
266        if($class === null) {
267            return;
268        }
269        if(!isset($options['FormID']) && ($form=$this->_page->getForm())!==null)
270            $options['FormID']=$form->getClientID();
271        $optionString=TJavaScript::encode($options);
272        $code="new {$class}({$optionString});";
273
274        $this->_endScripts[sprintf('%08X', crc32($code))]=$code;
275        $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]='';
276        $this->_hiddenFields[TPage::FIELD_POSTBACK_PARAMETER]='';
277        $this->registerPradoScriptInternal('prado');
278
279        $params=func_get_args();
280        $this->_page->registerCachingAction('Page.ClientScript','registerPostBackControl',$params);
281    }
282
283    /**
284     * Register a default button to panel. When the $panel is in focus and
285     * the 'enter' key is pressed, the $button will be clicked.
286     * @param TControl|string panel (or its unique ID) to register the default button action
287     * @param TControl|string button (or its unique ID) to trigger a postback
288     */
289    public function registerDefaultButton($panel, $button)
290    {
291        $panelID=is_string($panel)?$panel:$panel->getUniqueID();
292
293        if(is_string($button))
294            $buttonID=$button;
295        else
296        {
297            $button->setIsDefaultButton(true);
298            $buttonID=$button->getUniqueID();
299        }
300        $options = TJavaScript::encode($this->getDefaultButtonOptions($panelID, $buttonID));
301        $code = "new Prado.WebUI.DefaultButton($options);";
302
303        $this->_endScripts['prado:'.$panelID]=$code;
304        $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]='';
305        $this->registerPradoScriptInternal('prado');
306
307        $params=array($panelID,$buttonID);
308        $this->_page->registerCachingAction('Page.ClientScript','registerDefaultButton',$params);
309    }
310
311    /**
312     * @param string the unique ID of the container control
313     * @param string the unique ID of the button control
314     * @return array default button options.
315     */
316    protected function getDefaultButtonOptions($panelID, $buttonID)
317    {
318        $options['Panel'] = TControl::convertUniqueIdToClientId($panelID);
319        $options['Target'] = TControl::convertUniqueIdToClientId($buttonID);
320        $options['EventTarget'] = $buttonID;
321        $options['Event'] = 'click';
322        return $options;
323    }
324
325    /**
326     * Registers the control to receive default focus.
327     * @param string the client ID of the control to receive default focus
328     */
329    public function registerFocusControl($target)
330    {
331        $this->registerPradoScriptInternal('effects');
332        if($target instanceof TControl)
333            $target=$target->getClientID();
334        $id = TJavaScript::quoteString($target);
335        $this->_endScripts['prado:focus'] = 'new Effect.ScrollTo("'.$id.'"); Prado.Element.focus("'.$id.'");';
336
337        $params=func_get_args();
338        $this->_page->registerCachingAction('Page.ClientScript','registerFocusControl',$params);
339    }
340
341    /**
342     * Registers a CSS file to be rendered in the page head
343     *
344     * The CSS files in themes are registered in {@link OnPreRenderComplete onPreRenderComplete} if you want to override
345     * CSS styles in themes you need to register it after this event is completed.
346     *
347     * Example:
348     * <code>
349     * <?php
350     * class BasePage extends TPage {
351     *   public function onPreRenderComplete($param) {
352     *     parent::onPreRenderComplete($param);
353     *     $url = 'path/to/your/stylesheet.css';
354     *     $this->Page->ClientScript->registerStyleSheetFile($url, $url);
355     *   }
356     * }
357     * ?>
358     * </code>
359     *
360     * @param string a unique key identifying the file
361     * @param string URL to the CSS file
362     * @param string media type of the CSS (such as 'print', 'screen', etc.). Defaults to empty, meaning the CSS applies to all media types.
363     */
364    public function registerStyleSheetFile($key,$url,$media='')
365    {
366        if($media==='')
367            $this->_styleSheetFiles[$key]=$url;
368        else
369            $this->_styleSheetFiles[$key]=array($url,$media);
370
371        $params=func_get_args();
372        $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheetFile',$params);
373    }
374
375    /**
376     * Registers a CSS block to be rendered in the page head
377     * @param string a unique key identifying the CSS block
378     * @param string CSS block
379     */
380    public function registerStyleSheet($key,$css,$media='')
381    {
382        $this->_styleSheets[$key]=$css;
383
384        $params=func_get_args();
385        $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheet',$params);
386    }
387
388    /**
389     * Registers a javascript file in the page head
390     * @param string a unique key identifying the file
391     * @param string URL to the javascript file
392     */
393    public function registerHeadScriptFile($key,$url)
394    {
395        $this->_headScriptFiles[$key]=$url;
396
397        $params=func_get_args();
398        $this->_page->registerCachingAction('Page.ClientScript','registerHeadScriptFile',$params);
399    }
400
401    /**
402     * Registers a javascript block in the page head.
403     * @param string a unique key identifying the script block
404     * @param string javascript block
405     */
406    public function registerHeadScript($key,$script)
407    {
408        $this->_headScripts[$key]=$script;
409
410        $params=func_get_args();
411        $this->_page->registerCachingAction('Page.ClientScript','registerHeadScript',$params);
412    }
413
414    /**
415     * Registers a javascript file to be rendered within the form
416     * @param string a unique key identifying the file
417     * @param string URL to the javascript file to be rendered
418     */
419    public function registerScriptFile($key,$url)
420    {
421        $this->_scriptFiles[$key]=$url;
422
423        $params=func_get_args();
424        $this->_page->registerCachingAction('Page.ClientScript','registerScriptFile',$params);
425    }
426
427    /**
428     * Registers a javascript script block at the beginning of the form
429     * @param string a unique key identifying the script block
430     * @param string javascript block
431     */
432    public function registerBeginScript($key,$script)
433    {
434        $this->_beginScripts[$key]=$script;
435
436        $params=func_get_args();
437        $this->_page->registerCachingAction('Page.ClientScript','registerBeginScript',$params);
438    }
439
440    /**
441     * Registers a javascript script block at the end of the form
442     * @param string a unique key identifying the script block
443     * @param string javascript block
444     */
445    public function registerEndScript($key,$script)
446    {
447        $this->_endScripts[$key]=$script;
448
449        $params=func_get_args();
450        $this->_page->registerCachingAction('Page.ClientScript','registerEndScript',$params);
451    }
452
453    /**
454     * Registers a hidden field to be rendered in the form.
455     * @param string a unique key identifying the hidden field
456     * @param string|array hidden field value, if the value is an array, every element
457     * in the array will be rendered as a hidden field value.
458     */
459    public function registerHiddenField($name,$value)
460    {
461        $this->_hiddenFields[$name]=$value;
462
463        $params=func_get_args();
464        $this->_page->registerCachingAction('Page.ClientScript','registerHiddenField',$params);
465    }
466
467    /**
468     * @param string a unique key
469     * @return boolean whether there is a CSS file registered with the specified key
470     */
471    public function isStyleSheetFileRegistered($key)
472    {
473        return isset($this->_styleSheetFiles[$key]);
474    }
475
476    /**
477     * @param string a unique key
478     * @return boolean whether there is a CSS block registered with the specified key
479     */
480    public function isStyleSheetRegistered($key)
481    {
482        return isset($this->_styleSheets[$key]);
483    }
484
485    /**
486     * @param string a unique key
487     * @return boolean whether there is a head javascript file registered with the specified key
488     */
489    public function isHeadScriptFileRegistered($key)
490    {
491        return isset($this->_headScriptFiles[$key]);
492    }
493
494    /**
495     * @param string a unique key
496     * @return boolean whether there is a head javascript block registered with the specified key
497     */
498    public function isHeadScriptRegistered($key)
499    {
500        return isset($this->_headScripts[$key]);
501    }
502
503    /**
504     * @param string a unique key
505     * @return boolean whether there is a javascript file registered with the specified key
506     */
507    public function isScriptFileRegistered($key)
508    {
509        return isset($this->_scriptFiles[$key]);
510    }
511
512    /**
513     * @param string a unique key
514     * @return boolean whether there is a beginning javascript block registered with the specified key
515     */
516    public function isBeginScriptRegistered($key)
517    {
518        return isset($this->_beginScripts[$key]);
519    }
520
521    /**
522     * @param string a unique key
523     * @return boolean whether there is an ending javascript block registered with the specified key
524     */
525    public function isEndScriptRegistered($key)
526    {
527        return isset($this->_endScripts[$key]);
528    }
529
530    /**
531     * @return boolean true if any end scripts are registered.
532     */
533    public function hasEndScripts()
534    {
535        return count($this->_endScripts) > 0;
536    }
537
538    /**
539     * @return boolean true if any begin scripts are registered.
540     */
541    public function hasBeginScripts()
542    {
543        return count($this->_beginScripts) > 0;
544    }
545
546    /**
547     * @param string a unique key
548     * @return boolean whether there is a hidden field registered with the specified key
549     */
550    public function isHiddenFieldRegistered($key)
551    {
552        return isset($this->_hiddenFields[$key]);
553    }
554
555    /**
556     * @param THtmlWriter writer for the rendering purpose
557     */
558    public function renderStyleSheetFiles($writer)
559    {
560        $str='';
561        foreach($this->_styleSheetFiles as $url)
562        {
563            if(is_array($url))
564                $str.="<link rel=\"stylesheet\" type=\"text/css\" media=\"{$url[1]}\" href=\"".THttpUtility::htmlEncode($url[0])."\" />\n";
565            else
566                $str.="<link rel=\"stylesheet\" type=\"text/css\" href=\"".THttpUtility::htmlEncode($url)."\" />\n";
567        }
568        $writer->write($str);
569    }
570
571    /**
572     * @param THtmlWriter writer for the rendering purpose
573     */
574    public function renderStyleSheets($writer)
575    {
576        if(count($this->_styleSheets))
577            $writer->write("<style type=\"text/css\">\n/*<![CDATA[*/\n".implode("\n",$this->_styleSheets)."\n/*]]>*/\n</style>\n");
578    }
579
580    /**
581     * @param THtmlWriter writer for the rendering purpose
582     */
583