1 | <?php
|
---|
2 | /**
|
---|
3 | * DokuWiki JavaScript creator
|
---|
4 | *
|
---|
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
6 | * @author Andreas Gohr <[email protected]>
|
---|
7 | */
|
---|
8 |
|
---|
9 | if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../');
|
---|
10 | if(!defined('NOSESSION')) define('NOSESSION',true); // we do not use a session or authentication here (better caching)
|
---|
11 | if(!defined('NL')) define('NL',"\n");
|
---|
12 | if(!defined('DOKU_DISABLE_GZIP_OUTPUT')) define('DOKU_DISABLE_GZIP_OUTPUT',1); // we gzip ourself here
|
---|
13 | require_once(DOKU_INC.'inc/init.php');
|
---|
14 |
|
---|
15 | // Main (don't run when UNIT test)
|
---|
16 | if(!defined('SIMPLE_TEST')){
|
---|
17 | header('Content-Type: text/javascript; charset=utf-8');
|
---|
18 | js_out();
|
---|
19 | }
|
---|
20 |
|
---|
21 |
|
---|
22 | // ---------------------- functions ------------------------------
|
---|
23 |
|
---|
24 | /**
|
---|
25 | * Output all needed JavaScript
|
---|
26 | *
|
---|
27 | * @author Andreas Gohr <[email protected]>
|
---|
28 | */
|
---|
29 | function js_out(){
|
---|
30 | global $conf;
|
---|
31 | global $lang;
|
---|
32 | global $config_cascade;
|
---|
33 |
|
---|
34 | // The generated script depends on some dynamic options
|
---|
35 | $cache = getCacheName('scripts'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'],'.js');
|
---|
36 |
|
---|
37 | // array of core files
|
---|
38 | $files = array(
|
---|
39 | DOKU_INC.'lib/scripts/helpers.js',
|
---|
40 | DOKU_INC.'lib/scripts/events.js',
|
---|
41 | DOKU_INC.'lib/scripts/delay.js',
|
---|
42 | DOKU_INC.'lib/scripts/cookie.js',
|
---|
43 | DOKU_INC.'lib/scripts/script.js',
|
---|
44 | DOKU_INC.'lib/scripts/tw-sack.js',
|
---|
45 | DOKU_INC.'lib/scripts/ajax.js',
|
---|
46 | DOKU_INC.'lib/scripts/index.js',
|
---|
47 | DOKU_INC.'lib/scripts/drag.js',
|
---|
48 | DOKU_INC.'lib/scripts/textselection.js',
|
---|
49 | DOKU_INC.'lib/scripts/toolbar.js',
|
---|
50 | DOKU_INC.'lib/scripts/edit.js',
|
---|
51 | DOKU_INC.'lib/scripts/locktimer.js',
|
---|
52 | DOKU_INC.'lib/scripts/linkwiz.js',
|
---|
53 | DOKU_INC.'lib/scripts/media.js',
|
---|
54 | DOKU_INC.'lib/scripts/subscriptions.js',
|
---|
55 | # disabled for FS#1958 DOKU_INC.'lib/scripts/hotkeys.js',
|
---|
56 | DOKU_TPLINC.'script.js',
|
---|
57 | );
|
---|
58 |
|
---|
59 | // add possible plugin scripts and userscript
|
---|
60 | $files = array_merge($files,js_pluginscripts());
|
---|
61 | if(isset($config_cascade['userscript']['default'])){
|
---|
62 | $files[] = $config_cascade['userscript']['default'];
|
---|
63 | }
|
---|
64 |
|
---|
65 | // check cache age & handle conditional request
|
---|
66 | header('Cache-Control: public, max-age=3600');
|
---|
67 | header('Pragma: public');
|
---|
68 | if(js_cacheok($cache,$files)){
|
---|
69 | http_conditionalRequest(filemtime($cache));
|
---|
70 | if($conf['allowdebug']) header("X-CacheUsed: $cache");
|
---|
71 |
|
---|
72 | // finally send output
|
---|
73 | if ($conf['gzip_output'] && http_gzip_valid($cache)) {
|
---|
74 | header('Vary: Accept-Encoding');
|
---|
75 | header('Content-Encoding: gzip');
|
---|
76 | readfile($cache.".gz");
|
---|
77 | } else {
|
---|
78 | if (!http_sendfile($cache)) readfile($cache);
|
---|
79 | }
|
---|
80 | return;
|
---|
81 | } else {
|
---|
82 | http_conditionalRequest(time());
|
---|
83 | }
|
---|
84 |
|
---|
85 | // start output buffering and build the script
|
---|
86 | ob_start();
|
---|
87 |
|
---|
88 | // add some global variables
|
---|
89 | print "var DOKU_BASE = '".DOKU_BASE."';";
|
---|
90 | print "var DOKU_TPL = '".DOKU_TPL."';";
|
---|
91 | print "var DOKU_UHN = ".((int) useHeading('navigation')).";";
|
---|
92 | print "var DOKU_UHC = ".((int) useHeading('content')).";";
|
---|
93 |
|
---|
94 | // load JS specific translations
|
---|
95 | $json = new JSON();
|
---|
96 | $lang['js']['plugins'] = js_pluginstrings();
|
---|
97 | echo 'LANG = '.$json->encode($lang['js']).";\n";
|
---|
98 |
|
---|
99 | // load toolbar
|
---|
100 | toolbar_JSdefines('toolbar');
|
---|
101 |
|
---|
102 | // load files
|
---|
103 | foreach($files as $file){
|
---|
104 | echo "\n\n/* XXXXXXXXXX begin of ".str_replace(DOKU_INC, '', $file) ." XXXXXXXXXX */\n\n";
|
---|
105 | js_load($file);
|
---|
106 | echo "\n\n/* XXXXXXXXXX end of " . str_replace(DOKU_INC, '', $file) . " XXXXXXXXXX */\n\n";
|
---|
107 | }
|
---|
108 |
|
---|
109 |
|
---|
110 | // init stuff
|
---|
111 | js_runonstart("addEvent(document,'click',closePopups)");
|
---|
112 | js_runonstart('addTocToggle()');
|
---|
113 | js_runonstart("initSizeCtl('size__ctl','wiki__text')");
|
---|
114 | js_runonstart("initToolbar('tool__bar','wiki__text',toolbar)");
|
---|
115 | if($conf['locktime'] != 0){
|
---|
116 | js_runonstart("locktimer.init(".($conf['locktime'] - 60).",'".js_escape($lang['willexpire'])."',".$conf['usedraft'].", 'wiki__text')");
|
---|
117 | }
|
---|
118 | js_runonstart('scrollToMarker()');
|
---|
119 | js_runonstart('focusMarker()');
|
---|
120 | // init hotkeys - must have been done after init of toolbar
|
---|
121 | # disabled for FS#1958 js_runonstart('initializeHotkeys()');
|
---|
122 |
|
---|
123 | // end output buffering and get contents
|
---|
124 | $js = ob_get_contents();
|
---|
125 | ob_end_clean();
|
---|
126 |
|
---|
127 | // compress whitespace and comments
|
---|
128 | if($conf['compress']){
|
---|
129 | $js = js_compress($js);
|
---|
130 | }
|
---|
131 |
|
---|
132 | $js .= "\n"; // https://bugzilla.mozilla.org/show_bug.cgi?id=316033
|
---|
133 |
|
---|
134 | // save cache file
|
---|
135 | io_saveFile($cache,$js);
|
---|
136 | if(function_exists('gzopen')) io_saveFile("$cache.gz",$js);
|
---|
137 |
|
---|
138 | // finally send output
|
---|
139 | if ($conf['gzip_output']) {
|
---|
140 | header('Vary: Accept-Encoding');
|
---|
141 | header('Content-Encoding: gzip');
|
---|
142 | print gzencode($js,9,FORCE_GZIP);
|
---|
143 | } else {
|
---|
144 | print $js;
|
---|
145 | }
|
---|
146 | }
|
---|
147 |
|
---|
148 | /**
|
---|
149 | * Load the given file, handle include calls and print it
|
---|
150 | *
|
---|
151 | * @author Andreas Gohr <[email protected]>
|
---|
152 | */
|
---|
153 | function js_load($file){
|
---|
154 | if(!@file_exists($file)) return;
|
---|
155 | static $loaded = array();
|
---|
156 |
|
---|
157 | $data = io_readFile($file);
|
---|
158 | while(preg_match('#/\*\s*DOKUWIKI:include(_once)?\s+([\w\.\-_/]+)\s*\*/#',$data,$match)){
|
---|
159 | $ifile = $match[2];
|
---|
160 |
|
---|
161 | // is it a include_once?
|
---|
162 | if($match[1]){
|
---|
163 | $base = basename($ifile);
|
---|
164 | if($loaded[$base]) continue;
|
---|
165 | $loaded[$base] = true;
|
---|
166 | }
|
---|
167 |
|
---|
168 | if($ifile{0} != '/') $ifile = dirname($file).'/'.$ifile;
|
---|
169 |
|
---|
170 | if(@file_exists($ifile)){
|
---|
171 | $idata = io_readFile($ifile);
|
---|
172 | }else{
|
---|
173 | $idata = '';
|
---|
174 | }
|
---|
175 | $data = str_replace($match[0],$idata,$data);
|
---|
176 | }
|
---|
177 | echo $data;
|
---|
178 | }
|
---|
179 |
|
---|
180 | /**
|
---|
181 | * Checks if a JavaScript Cache file still is valid
|
---|
182 | *
|
---|
183 | * @author Andreas Gohr <[email protected]>
|
---|
184 | */
|
---|
185 | function js_cacheok($cache,$files){
|
---|
186 | if(isset($_REQUEST['purge'])) return false; //support purge request
|
---|
187 |
|
---|
188 | $ctime = @filemtime($cache);
|
---|
189 | if(!$ctime) return false; //There is no cache
|
---|
190 |
|
---|
191 | global $config_cascade;
|
---|
192 |
|
---|
193 | // some additional files to check
|
---|
194 | $files = array_merge($files, getConfigFiles('main'));
|
---|
195 | $files[] = $config_cascade['userscript']['default'];
|
---|
196 | $files[] = __FILE__;
|
---|
197 |
|
---|
198 | // now walk the files
|
---|
199 | foreach($files as $file){
|
---|
200 | if(@filemtime($file) > $ctime){
|
---|
201 | return false;
|
---|
202 | }
|
---|
203 | }
|
---|
204 | return true;
|
---|
205 | }
|
---|
206 |
|
---|
207 | /**
|
---|
208 | * Returns a list of possible Plugin Scripts (no existance check here)
|
---|
209 | *
|
---|
210 | * @author Andreas Gohr <[email protected]>
|
---|
211 | */
|
---|
212 | function js_pluginscripts(){
|
---|
213 | $list = array();
|
---|
214 | $plugins = plugin_list();
|
---|
215 | foreach ($plugins as $p){
|
---|
216 | $list[] = DOKU_PLUGIN."$p/script.js";
|
---|
217 | }
|
---|
218 | return $list;
|
---|
219 | }
|
---|
220 |
|
---|
221 | /**
|
---|
222 | * Return an two-dimensional array with strings from the language file of each plugin.
|
---|
223 | *
|
---|
224 | * - $lang['js'] must be an array.
|
---|
225 | * - Nothing is returned for plugins without an entry for $lang['js']
|
---|
226 | *
|
---|
227 | * @author Gabriel Birke <[email protected]>
|
---|
228 | */
|
---|
229 | function js_pluginstrings()
|
---|
230 | {
|
---|
231 | global $conf;
|
---|
232 | $pluginstrings = array();
|
---|
233 | $plugins = plugin_list();
|
---|
234 | foreach ($plugins as $p){
|
---|
235 | if (isset($lang)) unset($lang);
|
---|
236 | if (@file_exists(DOKU_PLUGIN."$p/lang/en/lang.php")) {
|
---|
237 | include DOKU_PLUGIN."$p/lang/en/lang.php";
|
---|
238 | }
|
---|
239 | if (isset($conf['lang']) && $conf['lang']!='en' && @file_exists(DOKU_PLUGIN."$p/lang/".$conf['lang']."/lang.php")) {
|
---|
240 | include DOKU_PLUGIN."$p/lang/".$conf['lang']."/lang.php";
|
---|
241 | }
|
---|
242 | if (isset($lang['js'])) {
|
---|
243 | $pluginstrings[$p] = $lang['js'];
|
---|
244 | }
|
---|
245 | }
|
---|
246 | return $pluginstrings;
|
---|
247 | }
|
---|
248 |
|
---|
249 | /**
|
---|
250 | * Escapes a String to be embedded in a JavaScript call, keeps \n
|
---|
251 | * as newline
|
---|
252 | *
|
---|
253 | * @author Andreas Gohr <[email protected]>
|
---|
254 | */
|
---|
255 | function js_escape($string){
|
---|
256 | return str_replace('\\\\n','\\n',addslashes($string));
|
---|
257 | }
|
---|
258 |
|
---|
259 | /**
|
---|
260 | * Adds the given JavaScript code to the window.onload() event
|
---|
261 | *
|
---|
262 | * @author Andreas Gohr <[email protected]>
|
---|
263 | */
|
---|
264 | function js_runonstart($func){
|
---|
265 | echo "addInitEvent(function(){ $func; });".NL;
|
---|
266 | }
|
---|
267 |
|
---|
268 | /**
|
---|
269 | * Strip comments and whitespaces from given JavaScript Code
|
---|
270 | *
|
---|
271 | * This is a port of Nick Galbreath's python tool jsstrip.py which is
|
---|
272 | * released under BSD license. See link for original code.
|
---|
273 | *
|
---|
274 | * @author Nick Galbreath <[email protected]>
|
---|
275 | * @author Andreas Gohr <[email protected]>
|
---|
276 | * @link http://code.google.com/p/jsstrip/
|
---|
277 | */
|
---|
278 | function js_compress($s){
|
---|
279 | $s = ltrim($s); // strip all initial whitespace
|
---|
280 | $s .= "\n";
|
---|
281 | $i = 0; // char index for input string
|
---|
282 | $j = 0; // char forward index for input string
|
---|
283 | $line = 0; // line number of file (close to it anyways)
|
---|
284 | $slen = strlen($s); // size of input string
|
---|
285 | $lch = ''; // last char added
|
---|
286 | $result = ''; // we store the final result here
|
---|
287 |
|
---|
288 | // items that don't need spaces next to them
|
---|
289 | $chars = "^&|!+\-*\/%=\?:;,{}()<>% \t\n\r'\"[]";
|
---|
290 |
|
---|
291 | $regex_starters = array("(", "=", "[", "," , ":");
|
---|
292 |
|
---|
293 | $whitespaces_chars = array(" ", "\t", "\n", "\r", "\0", "\x0B");
|
---|
294 |
|
---|
295 | while($i < $slen){
|
---|
296 | // skip all "boring" characters. This is either
|
---|
297 | // reserved word (e.g. "for", "else", "if") or a
|
---|
298 | // variable/object/method (e.g. "foo.color")
|
---|
299 | while ($i < $slen && (strpos($chars,$s[$i]) === false) ){
|
---|
300 | $result .= $s{$i};
|
---|
301 | $i = $i + 1;
|
---|
302 | }
|
---|
303 |
|
---|
304 | $ch = $s{$i};
|
---|
305 | // multiline comments (keeping IE conditionals)
|
---|
306 | if($ch == '/' && $s{$i+1} == '*' && $s{$i+2} != '@'){
|
---|
307 | $endC = strpos($s,'*/',$i+2);
|
---|
308 | if($endC === false) trigger_error('Found invalid /*..*/ comment', E_USER_ERROR);
|
---|
309 | $i = $endC + 2;
|
---|
310 | continue;
|
---|
311 | }
|
---|
312 |
|
---|
313 | // singleline
|
---|
314 | if($ch == '/' && $s{$i+1} == '/'){
|
---|
315 | $endC = strpos($s,"\n",$i+2);
|
---|
316 | if($endC === false) trigger_error('Invalid comment', E_USER_ERROR);
|
---|
317 | $i = $endC;
|
---|
318 | continue;
|
---|
319 | }
|
---|
320 |
|
---|
321 | // tricky. might be an RE
|
---|
322 | if($ch == '/'){
|
---|
323 | // rewind, skip white space
|
---|
324 | $j = 1;
|
---|
325 | while(in_array($s{$i-$j}, $whitespaces_chars)){
|
---|
326 | $j = $j + 1;
|
---|
327 | }
|
---|
328 | if( in_array($s{$i-$j}, $regex_starters) ){
|
---|
329 | // yes, this is an re
|
---|
330 | // now move forward and find the end of it
|
---|
331 | $j = 1;
|
---|
332 | while($s{$i+$j} != '/'){
|
---|
333 | while( ($s{$i+$j} != '\\') && ($s{$i+$j} != '/')){
|
---|
334 | $j = $j + 1;
|
---|
335 | }
|
---|
336 | if($s{$i+$j} == '\\') $j = $j + 2;
|
---|
337 | }
|
---|
338 | $result .= substr($s,$i,$j+1);
|
---|
339 | $i = $i + $j + 1;
|
---|
340 | continue;
|
---|
341 | }
|
---|
342 | }
|
---|
343 |
|
---|
344 | // double quote strings
|
---|
345 | if($ch == '"'){
|
---|
346 | $j = 1;
|
---|
347 | while( $s{$i+$j} != '"' && ($i+$j < $slen)){
|
---|
348 | if( $s{$i+$j} == '\\' && ($s{$i+$j+1} == '"' || $s{$i+$j+1} == '\\') ){
|
---|
349 | $j += 2;
|
---|
350 | }else{
|
---|
351 | $j += 1;
|
---|
352 | }
|
---|
353 | }
|
---|
354 | $result .= substr($s,$i,$j+1);
|
---|
355 | $i = $i + $j + 1;
|
---|
356 | continue;
|
---|
357 | }
|
---|
358 |
|
---|
359 | // single quote strings
|
---|
360 | if($ch == "'"){
|
---|
361 | $j = 1;
|
---|
362 | while( $s{$i+$j} != "'" && ($i+$j < $slen)){
|
---|
363 | if( $s{$i+$j} == '\\' && ($s{$i+$j+1} == "'" || $s{$i+$j+1} == '\\') ){
|
---|
364 | $j += 2;
|
---|
365 | }else{
|
---|
366 | $j += 1;
|
---|
367 | }
|
---|
368 | }
|
---|
369 | $result .= substr($s,$i,$j+1);
|
---|
370 | $i = $i + $j + 1;
|
---|
371 | continue;
|
---|
372 | }
|
---|
373 |
|
---|
374 | // whitespaces
|
---|
375 | if( $ch == ' ' || $ch == "\r" || $ch == "\n" || $ch == "\t" ){
|
---|
376 | // leading spaces
|
---|
377 | if($i+1 < $slen && (strpos($chars,$s[$i+1]) !== false)){
|
---|
378 | $i = $i + 1;
|
---|
379 | continue;
|
---|
380 | }
|
---|
381 | // trailing spaces
|
---|
382 | // if this ch is space AND the last char processed
|
---|
383 | // is special, then skip the space
|
---|
384 | $lch = substr($result,-1);
|
---|
385 | if($lch && (strpos($chars,$lch) !== false)){
|
---|
386 | $i = $i + 1;
|
---|
387 | continue;
|
---|
388 | }
|
---|
389 | // else after all of this convert the "whitespace" to
|
---|
390 | // a single space. It will get appended below
|
---|
391 | $ch = ' ';
|
---|
392 | }
|
---|
393 |
|
---|
394 | // other chars
|
---|
395 | $result .= $ch;
|
---|
396 | $i = $i + 1;
|
---|
397 | }
|
---|
398 |
|
---|
399 | return trim($result);
|
---|
400 | }
|
---|
401 |
|
---|
402 | //Setup VIM: ex: et ts=4 :
|
---|