summaryrefslogtreecommitdiff
path: root/framework/Web/UI/WebControls/TClientScriptLoader.php
blob: 1c5b9d29f8546fc955e7eab44dd6ade33b96ea5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
<?php
/**
 * TClientScriptLoader class file.
 *
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2007 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id: TClientScriptLoader.php 1827 2007-04-02 06:19:55Z wei $
 * @package System.Web.UI.WebControls
 */

/**
 * The TClientScriptLoader publish a collection of javascript files as assets. 
 * The {@link PackagePath setPackagePath} property can be an existing asset directory
 * or a namespace path to the directory containing javascript files. E.g.
 * <code>
 *   <com:TClientScriptLoader PackagePath=<%~ mylib/js %> />
 *   <com:TClientScriptLoader PackagePath="Application.myscripts" />
 * </code>
 *
 * When the files in the {@link PackagePath setPackagePath} are published as assets, a script loader
 * php file corresponding to TClientScriptLoader::SCRIPT_LOADER is also copied to that asset directory.
 *
 * The script loader, combines multiple javascript files and serve up as gzip if possible.
 * Allowable scripts and script dependencies can be specified in a "packages.php" file
 * with the following format. This "packages.php" is optional, if absent the filenames
 * without ".js" extension are used. The "packages.php" must be in the directory given by
 * {@link PackagePath setPackagePath}.
 *
 * <code>
 * <?php
 *  $packages = array(
 *     'package1' => array('file1.js', 'file2.js'),
 *     'package2' => array('file3.js', 'file4.js'));
 *
 *  $deps = array(
 *     'package1' => array('package1'),
 *     'package2' => array('package1', 'package2')); //package2 requires package1 first.
 *
 *  return array($packages,$deps); //must return $packages and $deps in an array
 * </code>
 *
 * Set the {@link PackageScripts setPackageScripts} property with value 'package1' to serve
 * up the 'package1' scripts. A maxium of 25 packages separated by commas is allowed.
 *
 * Dependencies of the packages are automatically resolved by the script loader php file.
 * E.g.
 * <code>
 * <com:TClientScriptLoader PackagePath=<%~ mylib/js %> PackageScripts="package2" />
 * </code>
 *
 * The {@link setDebugMode DebugMode} property when false
 * removes comments and whitespaces from the published javascript files. If
 * the DebugMode property is not set, the debug mode is determined from the application mode.
 *
 * The {@link setEnableGzip EnableGzip} property (default is true) enables the 
 * published javascripts to be served as zipped if the browser and php server allows it. 
  
 * If the DebugMode is false either explicitly or when the application mode is non-debug,
 * then cache headers are also sent to inform the browser and proxies to cache the file.
 * Moreover, the post-processed (comments removed and zipped) are saved in the assets 
 * directory for the next requests. That is, in non-debug mode the scripts are cached
 * in the assets directory until they are deleted.
 *
 * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
 * @version $Id$
 * @package System.Web.WebControls
 * @since 3.1
 */
class TClientScriptLoader extends TWebControl
{
	const SCRIPT_LOADER = 'Web/Javascripts/clientscripts.php';

	/**
	 * @return string tag name of the script element
	 */
	protected function getTagName()
	{
		return 'script';
	}
	
	/**
	 * Adds attribute name-value pairs to renderer.
	 * This overrides the parent implementation with additional button specific attributes.
	 * @param THtmlWriter the writer used for the rendering purpose
	 */
	protected function addAttributesToRender($writer)
	{
		$writer->addAttribute('type','text/javascript');
		$writer->addAttribute('src',$this->getClientScriptUrl());
		parent::addAttributesToRender($writer);
	}

	/**
	 * @return string clientscript.php url.
	 */
	protected function getClientScriptUrl()
	{
		$baseUrl = $this->publishScriptLoader();
		$scripts = split('/\s*[, ]+\s*/', $this->getPackageScripts());
		$url = $baseUrl . '?js=' . implode(',', $scripts);
		if($this->getDebugMode()!==false && $this->getApplication()->getMode()===TApplicationMode::Debug)
			$url.='&amp;mode=debug';
		if($this->getEnableGzip()===false)
			$url.='&amp;gzip=false';
		return $url;
	}

	/**
	 * Copies the client script loader php file to the asset directory if appropriate.
	 * @return string clientscript.php url
	 * @throws TConfigurationException if error in publishing the package path.
	 */
	protected function publishScriptLoader()
	{
		list($path, $url) = $this->getPublishedPackagePath();
		if(is_dir($path))
		{
			$scriptLoader = Prado::getFrameworkPath().'/'.self::SCRIPT_LOADER;
			$scriptLoaderFile = basename($scriptLoader);
			$dest = $path.'/'.$scriptLoaderFile;
			if(!is_file($dest))
				copy($scriptLoader,$dest);
			return $url.'/'.$scriptLoaderFile;
		}
		else
			throw new TConfigurationException('clientscript_invalid_package_path',
				$this->getPackagePath(), $this->getUniqueID());
	}

	/**
	 * Publishes the package path assets, tries path as namespace first.
	 * @return array tuple ($path, $url).
	 */
	protected function getPublishedPackagePath()
	{
		$assets = $this->getApplication()->getAssetManager();
		//assumes dot path first
		$dir = Prado::getPathOfNameSpace($this->getPackagePath());
		if(!is_null($dir))
		{
			$url = $assets->publishFilePath($dir); //show throw an excemption if invalid
			return array($dir, $url);
		}
		$url = $this->getPackagePath();
		$packageDir = str_replace($assets->getBaseUrl(), '', $url);
		return array($assets->getBasePath().$packageDir,$url);
	}
	
	/**
	 * @param string custom javascript library directory.
	 */
	public function setPackagePath($value)
	{
		$this->setViewState('PackagePath', $value);
	}

	/**
	 * @return string custom javascript library directory.
	 */
	public function getPackagePath()
	{
		return $this->getViewState('PackagePath');
	}

	/**
	 * @param string load specific packages from the javascript library in the PackagePath, 
	 * comma delimited package names. A maximum of 25 packages is allowed.
	 */
	public function setPackageScripts($value)
	{
		$this->setViewState('PackageScripts', $value,'');
	}

	/**
	 * @return string comma delimited list of javascript library packages to load.
	 */
	public function getPackageScripts()
	{
		return $this->getViewState('PackageScripts','');
	}

	/**
	 * @param boolean enables gzip compression of the javascript.
	 */
	public function setEnableGzip($value)
	{
		$this->setViewState('EnableGzip', TPropertyValue::ensureBoolean($value), true);
	}

	/**
	 * @return boolean enables gzip compression of the javascript if possible, default is true.
	 */
	public function getEnableGzip()
	{
		return $this->getViewState('EnableGzip', true);
	}

	/**
	 * @return boolean javascript comments stripped in non-debug mode. 
	 * Debug mode will depend on the application mode if null.
	 */
	public function getDebugMode()
	{
		return $this->getViewState('DebugMode');
	}

	/**
	 * @param boolean true to enable debug mode, default is null thus dependent on the application mode.
	 */
	public function setDebugMode($value)
	{
		$this->setViewState('DebugMode', TPropertyValue::ensureBoolean($value), null);
	}
}

?>