Completed
Push — syntaxtableclasses ( 0c4c02...2e0ebe )
by Andreas
05:40
created

js.php ➔ js_out()   F

Complexity

Conditions 17
Paths 2176

Size

Total Lines 128
Code Lines 81

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 81
nc 2176
nop 0
dl 0
loc 128
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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: application/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
    global $INPUT;
34
35
    // decide from where to get the template
36
    $tpl = trim(preg_replace('/[^\w-]+/','',$INPUT->str('t')));
37
    if(!$tpl) $tpl = $conf['template'];
38
39
    // The generated script depends on some dynamic options
40
    $cache = new cache('scripts'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'].DOKU_BASE.$tpl,'.js');
41
    $cache->_event = 'JS_CACHE_USE';
0 ignored issues
show
Bug introduced by
The property _event cannot be accessed from this context as it is declared private in class cache.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
42
43
    // load minified version for some files
44
    $min = $conf['compress'] ? '.min' : '';
45
46
    // array of core files
47
    $files = array(
48
                DOKU_INC."lib/scripts/jquery/jquery$min.js",
49
                DOKU_INC.'lib/scripts/jquery/jquery.cookie.js',
50
                DOKU_INC."lib/scripts/jquery/jquery-ui$min.js",
51
                DOKU_INC."lib/scripts/jquery/jquery-migrate$min.js",
52
                DOKU_INC.'inc/lang/'.$conf['lang'].'/jquery.ui.datepicker.js',
53
                DOKU_INC."lib/scripts/fileuploader.js",
54
                DOKU_INC."lib/scripts/fileuploaderextended.js",
55
                DOKU_INC.'lib/scripts/helpers.js',
56
                DOKU_INC.'lib/scripts/delay.js',
57
                DOKU_INC.'lib/scripts/cookie.js',
58
                DOKU_INC.'lib/scripts/script.js',
59
                DOKU_INC.'lib/scripts/qsearch.js',
60
                DOKU_INC.'lib/scripts/tree.js',
61
                DOKU_INC.'lib/scripts/index.js',
62
                DOKU_INC.'lib/scripts/textselection.js',
63
                DOKU_INC.'lib/scripts/toolbar.js',
64
                DOKU_INC.'lib/scripts/edit.js',
65
                DOKU_INC.'lib/scripts/editor.js',
66
                DOKU_INC.'lib/scripts/locktimer.js',
67
                DOKU_INC.'lib/scripts/linkwiz.js',
68
                DOKU_INC.'lib/scripts/media.js',
69
                DOKU_INC.'lib/scripts/compatibility.js',
70
# disabled for FS#1958                DOKU_INC.'lib/scripts/hotkeys.js',
71
                DOKU_INC.'lib/scripts/behaviour.js',
72
                DOKU_INC.'lib/scripts/page.js',
73
                tpl_incdir($tpl).'script.js',
74
            );
75
76
    // add possible plugin scripts and userscript
77
    $files   = array_merge($files,js_pluginscripts());
78
    if(!empty($config_cascade['userscript']['default'])) {
79
        foreach($config_cascade['userscript']['default'] as $userscript) {
80
            $files[] = $userscript;
81
        }
82
    }
83
84
    $cache_files = array_merge($files, getConfigFiles('main'));
85
    $cache_files[] = __FILE__;
86
87
    // check cache age & handle conditional request
88
    // This may exit if a cache can be used
89
    $cache_ok = $cache->useCache(array('files' => $cache_files));
90
    http_cached($cache->cache, $cache_ok);
91
92
    // start output buffering and build the script
93
    ob_start();
94
95
    $json = new JSON();
96
    // add some global variables
97
    print "var DOKU_BASE   = '".DOKU_BASE."';";
98
    print "var DOKU_TPL    = '".tpl_basedir($tpl)."';";
99
    print "var DOKU_COOKIE_PARAM = " . $json->encode(
100
            array(
101
                 'path' => empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'],
102
                 'secure' => $conf['securecookie'] && is_ssl()
103
            )).";";
104
    // FIXME: Move those to JSINFO
105
    print "var DOKU_UHN    = ".((int) useHeading('navigation')).";";
106
    print "var DOKU_UHC    = ".((int) useHeading('content')).";";
107
108
    // load JS specific translations
109
    $lang['js']['plugins'] = js_pluginstrings();
110
    $templatestrings = js_templatestrings($tpl);
111
    if(!empty($templatestrings)) {
112
        $lang['js']['template'] = $templatestrings;
113
    }
114
    echo 'LANG = '.$json->encode($lang['js']).";\n";
115
116
    // load toolbar
117
    toolbar_JSdefines('toolbar');
118
119
    // load files
120
    foreach($files as $file){
121
        if(!file_exists($file)) continue;
122
        $ismin = (substr($file,-7) == '.min.js');
123
        $debugjs = ($conf['allowdebug'] && strpos($file, DOKU_INC.'lib/scripts/') !== 0);
124
125
        echo "\n\n/* XXXXXXXXXX begin of ".str_replace(DOKU_INC, '', $file) ." XXXXXXXXXX */\n\n";
126
        if($ismin) echo "\n/* BEGIN NOCOMPRESS */\n";
127
        if ($debugjs) echo "\ntry {\n";
128
        js_load($file);
129
        if ($debugjs) echo "\n} catch (e) {\n   logError(e, '".str_replace(DOKU_INC, '', $file)."');\n}\n";
130
        if($ismin) echo "\n/* END NOCOMPRESS */\n";
131
        echo "\n\n/* XXXXXXXXXX end of " . str_replace(DOKU_INC, '', $file) . " XXXXXXXXXX */\n\n";
132
    }
133
134
    // init stuff
135
    if($conf['locktime'] != 0){
136
        js_runonstart("dw_locktimer.init(".($conf['locktime'] - 60).",".$conf['usedraft'].")");
137
    }
138
    // init hotkeys - must have been done after init of toolbar
139
# disabled for FS#1958    js_runonstart('initializeHotkeys()');
140
141
    // end output buffering and get contents
142
    $js = ob_get_contents();
143
    ob_end_clean();
144
145
    // strip any source maps
146
    stripsourcemaps($js);
147
148
    // compress whitespace and comments
149
    if($conf['compress']){
150
        $js = js_compress($js);
151
    }
152
153
    $js .= "\n"; // https://bugzilla.mozilla.org/show_bug.cgi?id=316033
154
155
    http_cached_finish($cache->cache, $js);
156
}
157
158
/**
159
 * Load the given file, handle include calls and print it
160
 *
161
 * @author Andreas Gohr <[email protected]>
162
 *
163
 * @param string $file filename path to file
164
 */
165
function js_load($file){
166
    if(!file_exists($file)) return;
167
    static $loaded = array();
168
169
    $data = io_readFile($file);
170
    while(preg_match('#/\*\s*DOKUWIKI:include(_once)?\s+([\w\.\-_/]+)\s*\*/#',$data,$match)){
171
        $ifile = $match[2];
172
173
        // is it a include_once?
174
        if($match[1]){
175
            $base = utf8_basename($ifile);
176
            if(array_key_exists($base, $loaded) && $loaded[$base] === true){
177
                $data  = str_replace($match[0], '' ,$data);
178
                continue;
179
            }
180
            $loaded[$base] = true;
181
        }
182
183
        if($ifile{0} != '/') $ifile = dirname($file).'/'.$ifile;
184
185
        if(file_exists($ifile)){
186
            $idata = io_readFile($ifile);
187
        }else{
188
            $idata = '';
189
        }
190
        $data  = str_replace($match[0],$idata,$data);
191
    }
192
    echo "$data\n";
193
}
194
195
/**
196
 * Returns a list of possible Plugin Scripts (no existance check here)
197
 *
198
 * @author Andreas Gohr <[email protected]>
199
 *
200
 * @return array
201
 */
202
function js_pluginscripts(){
203
    $list = array();
204
    $plugins = plugin_list();
205
    foreach ($plugins as $p){
206
        $list[] = DOKU_PLUGIN."$p/script.js";
207
    }
208
    return $list;
209
}
210
211
/**
212
 * Return an two-dimensional array with strings from the language file of each plugin.
213
 *
214
 * - $lang['js'] must be an array.
215
 * - Nothing is returned for plugins without an entry for $lang['js']
216
 *
217
 * @author Gabriel Birke <[email protected]>
218
 *
219
 * @return array
220
 */
221
function js_pluginstrings() {
222
    global $conf;
223
    $pluginstrings = array();
224
    $plugins = plugin_list();
225
    foreach ($plugins as $p){
226
        if (isset($lang)) unset($lang);
227
        if (file_exists(DOKU_PLUGIN."$p/lang/en/lang.php")) {
228
            include DOKU_PLUGIN."$p/lang/en/lang.php";
229
        }
230
        if (isset($conf['lang']) && $conf['lang']!='en' && file_exists(DOKU_PLUGIN."$p/lang/".$conf['lang']."/lang.php")) {
231
            include DOKU_PLUGIN."$p/lang/".$conf['lang']."/lang.php";
232
        }
233
        if (isset($lang['js'])) {
234
            $pluginstrings[$p] = $lang['js'];
235
        }
236
    }
237
    return $pluginstrings;
238
}
239
240
/**
241
 * Return an two-dimensional array with strings from the language file of current active template.
242
 *
243
 * - $lang['js'] must be an array.
244
 * - Nothing is returned for template without an entry for $lang['js']
245
 *
246
 * @param string $tpl
247
 * @return array
248
 */
249
function js_templatestrings($tpl) {
250
    global $conf;
251
    $templatestrings = array();
252
    if (file_exists(tpl_incdir($tpl)."lang/en/lang.php")) {
253
        include tpl_incdir($tpl)."lang/en/lang.php";
254
    }
255
    if (isset($conf['lang']) && $conf['lang']!='en' && file_exists(tpl_incdir($tpl)."lang/".$conf['lang']."/lang.php")) {
256
        include tpl_incdir($tpl)."lang/".$conf['lang']."/lang.php";
257
    }
258
    if (isset($lang['js'])) {
0 ignored issues
show
Bug introduced by
The variable $lang seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
259
        $templatestrings[$tpl] = $lang['js'];
260
    }
261
    return $templatestrings;
262
}
263
264
/**
265
 * Escapes a String to be embedded in a JavaScript call, keeps \n
266
 * as newline
267
 *
268
 * @author Andreas Gohr <[email protected]>
269
 *
270
 * @param string $string
271
 * @return string
272
 */
273
function js_escape($string){
274
    return str_replace('\\\\n','\\n',addslashes($string));
275
}
276
277
/**
278
 * Adds the given JavaScript code to the window.onload() event
279
 *
280
 * @author Andreas Gohr <[email protected]>
281
 *
282
 * @param string $func
283
 */
284
function js_runonstart($func){
285
    echo "jQuery(function(){ $func; });".NL;
286
}
287
288
/**
289
 * Strip comments and whitespaces from given JavaScript Code
290
 *
291
 * This is a port of Nick Galbreath's python tool jsstrip.py which is
292
 * released under BSD license. See link for original code.
293
 *
294
 * @author Nick Galbreath <[email protected]>
295
 * @author Andreas Gohr <[email protected]>
296
 * @link   http://code.google.com/p/jsstrip/
297
 *
298
 * @param string $s
299
 * @return string
300
 */
301
function js_compress($s){
302
    $s = ltrim($s);     // strip all initial whitespace
303
    $s .= "\n";
304
    $i = 0;             // char index for input string
305
    $j = 0;             // char forward index for input string
0 ignored issues
show
Unused Code introduced by
$j is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
306
    $line = 0;          // line number of file (close to it anyways)
0 ignored issues
show
Unused Code introduced by
$line is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
307
    $slen = strlen($s); // size of input string
308
    $lch  = '';         // last char added
0 ignored issues
show
Unused Code introduced by
$lch is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
309
    $result = '';       // we store the final result here
310
311
    // items that don't need spaces next to them
312
    $chars = "^&|!+\-*\/%=\?:;,{}()<>% \t\n\r'\"[]";
313
314
    // items which need a space if the sign before and after whitespace is equal.
315
    // E.g. '+ ++' may not be compressed to '+++' --> syntax error.
316
    $ops = "+-";
317
318
    $regex_starters = array("(", "=", "[", "," , ":", "!", "&", "|");
319
320
    $whitespaces_chars = array(" ", "\t", "\n", "\r", "\0", "\x0B");
321
322
    while($i < $slen){
323
        // skip all "boring" characters.  This is either
324
        // reserved word (e.g. "for", "else", "if") or a
325
        // variable/object/method (e.g. "foo.color")
326
        while ($i < $slen && (strpos($chars,$s[$i]) === false) ){
327
            $result .= $s{$i};
328
            $i = $i + 1;
329
        }
330
331
        $ch = $s{$i};
332
        // multiline comments (keeping IE conditionals)
333
        if($ch == '/' && $s{$i+1} == '*' && $s{$i+2} != '@'){
334
            $endC = strpos($s,'*/',$i+2);
335
            if($endC === false) trigger_error('Found invalid /*..*/ comment', E_USER_ERROR);
336
337
            // check if this is a NOCOMPRESS comment
338
            if(substr($s, $i, $endC+2-$i) == '/* BEGIN NOCOMPRESS */'){
339
                $endNC = strpos($s, '/* END NOCOMPRESS */', $endC+2);
340
                if($endNC === false) trigger_error('Found invalid NOCOMPRESS comment', E_USER_ERROR);
341
342
                // verbatim copy contents, trimming but putting it on its own line
343
                $result .= "\n".trim(substr($s, $i + 22, $endNC - ($i + 22)))."\n"; // BEGIN comment = 22 chars
344
                $i = $endNC + 20; // END comment = 20 chars
345
            }else{
346
                $i = $endC + 2;
347
            }
348
            continue;
349
        }
350
351
        // singleline
352
        if($ch == '/' && $s{$i+1} == '/'){
353
            $endC = strpos($s,"\n",$i+2);
354
            if($endC === false) trigger_error('Invalid comment', E_USER_ERROR);
355
            $i = $endC;
356
            continue;
357
        }
358
359
        // tricky.  might be an RE
360
        if($ch == '/'){
361
            // rewind, skip white space
362
            $j = 1;
363
            while(in_array($s{$i-$j}, $whitespaces_chars)){
364
                $j = $j + 1;
365
            }
366
            if( in_array($s{$i-$j}, $regex_starters) ){
367
                // yes, this is an re
368
                // now move forward and find the end of it
369
                $j = 1;
370
                while($s{$i+$j} != '/'){
371
                    if($s{$i+$j} == '\\') $j = $j + 2;
372
                    else $j++;
373
                }
374
                $result .= substr($s,$i,$j+1);
375
                $i = $i + $j + 1;
376
                continue;
377
            }
378
        }
379
380
        // double quote strings
381
        if($ch == '"'){
382
            $j = 1;
383
            while( $s{$i+$j} != '"' && ($i+$j < $slen)){
384
                if( $s{$i+$j} == '\\' && ($s{$i+$j+1} == '"' || $s{$i+$j+1} == '\\') ){
385
                    $j += 2;
386
                }else{
387
                    $j += 1;
388
                }
389
            }
390
            $string  = substr($s,$i,$j+1);
391
            // remove multiline markers:
392
            $string  = str_replace("\\\n",'',$string);
393
            $result .= $string;
394
            $i = $i + $j + 1;
395
            continue;
396
        }
397
398
        // single quote strings
399
        if($ch == "'"){
400
            $j = 1;
401
            while( $s{$i+$j} != "'" && ($i+$j < $slen)){
402
                if( $s{$i+$j} == '\\' && ($s{$i+$j+1} == "'" || $s{$i+$j+1} == '\\') ){
403
                    $j += 2;
404
                }else{
405
                    $j += 1;
406
                }
407
            }
408
            $string = substr($s,$i,$j+1);
409
            // remove multiline markers:
410
            $string  = str_replace("\\\n",'',$string);
411
            $result .= $string;
412
            $i = $i + $j + 1;
413
            continue;
414
        }
415
416
        // whitespaces
417
        if( $ch == ' ' || $ch == "\r" || $ch == "\n" || $ch == "\t" ){
418
            $lch = substr($result,-1);
419
420
            // Only consider deleting whitespace if the signs before and after
421
            // are not equal and are not an operator which may not follow itself.
422
            if ($i+1 < $slen && ((!$lch || $s[$i+1] == ' ')
423
                || $lch != $s[$i+1]
424
                || strpos($ops,$s[$i+1]) === false)) {
425
                // leading spaces
426
                if($i+1 < $slen && (strpos($chars,$s[$i+1]) !== false)){
427
                    $i = $i + 1;
428
                    continue;
429
                }
430
                // trailing spaces
431
                //  if this ch is space AND the last char processed
432
                //  is special, then skip the space
433
                if($lch && (strpos($chars,$lch) !== false)){
434
                    $i = $i + 1;
435
                    continue;
436
                }
437
            }
438
439
            // else after all of this convert the "whitespace" to
440
            // a single space.  It will get appended below
441
            $ch = ' ';
442
        }
443
444
        // other chars
445
        $result .= $ch;
446
        $i = $i + 1;
447
    }
448
449
    return trim($result);
450
}
451
452
//Setup VIM: ex: et ts=4 :
453