Completed
Push — master ( 4500a3...cfb9f9 )
by Thierry
01:39
created

Manager::getReadyScript()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 42
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 29
nc 2
nop 0
dl 0
loc 42
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Manager.php - Jaxon plugin manager
5
 *
6
 * Register Jaxon plugins, generate corresponding code, handle request
7
 * and redirect them to the right plugin.
8
 *
9
 * @package jaxon-core
10
 * @author Jared White
11
 * @author J. Max Wilson
12
 * @author Joseph Woolley
13
 * @author Steffen Konerow
14
 * @author Thierry Feuzeu <[email protected]>
15
 * @copyright Copyright (c) 2005-2007 by Jared White & J. Max Wilson
16
 * @copyright Copyright (c) 2008-2010 by Joseph Woolley, Steffen Konerow, Jared White  & J. Max Wilson
17
 * @copyright 2016 Thierry Feuzeu <[email protected]>
18
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
19
 * @link https://github.com/jaxon-php/jaxon-core
20
 */
21
22
namespace Jaxon\Plugin;
23
24
use Jaxon\Jaxon;
25
use RecursiveDirectoryIterator;
26
use RecursiveIteratorIterator;
27
use RegexIterator;
28
use RecursiveRegexIterator;
29
30
class Manager
31
{
32
    use \Jaxon\Utils\Traits\Manager;
33
    use \Jaxon\Utils\Traits\Config;
34
    use \Jaxon\Utils\Traits\Cache;
35
    use \Jaxon\Utils\Traits\Minifier;
36
    use \Jaxon\Utils\Traits\Template;
37
    use \Jaxon\Utils\Traits\Translator;
38
39
    /**
40
     * The response type.
41
     *
42
     * @var string
43
     */
44
    const RESPONSE_TYPE = 'JSON';
45
46
    /**
47
     * All plugins, indexed by priority
48
     *
49
     * @var array
50
     */
51
    private $aPlugins;
52
53
    /**
54
     * Request plugins, indexed by name
55
     *
56
     * @var array
57
     */
58
    private $aRequestPlugins;
59
60
    /**
61
     * Response plugins, indexed by name
62
     *
63
     * @var array
64
     */
65
    private $aResponsePlugins;
66
67
    /**
68
     * Directories where Jaxon classes to be registered are found
69
     *
70
     * @var array
71
     */
72
    private $aClassDirs;
73
74
    /**
75
     * True if the Composer autoload is enabled
76
     *
77
     * @var boolean
78
     */
79
    private $bAutoloadEnabled;
80
81
    /**
82
     * The Composer autoloader
83
     *
84
     * @var Autoloader
85
     */
86
    private $xAutoloader;
87
88
    /**
89
     * Javascript confirm function
90
     *
91
     * @var \Jaxon\Request\Interfaces\Confirm
92
     */
93
    private $xConfirm;
94
95
    /**
96
     * Default javascript confirm function
97
     *
98
     * @var \Jaxon\Request\Support\Confirm
99
     */
100
    private $xDefaultConfirm;
101
102
    /**
103
     * Javascript alert function
104
     *
105
     * @var \Jaxon\Request\Interfaces\Alert
106
     */
107
    private $xAlert;
108
109
    /**
110
     * Default javascript alert function
111
     *
112
     * @var \Jaxon\Request\Support\Alert
113
     */
114
    private $xDefaultAlert;
115
116
    /**
117
     * Initialize the Jaxon Plugin Manager
118
     */
119
    public function __construct()
120
    {
121
        $this->aRequestPlugins = array();
122
        $this->aResponsePlugins = array();
123
        $this->aPlugins = array();
124
        $this->aClassDirs = array();
125
126
        $this->bAutoloadEnabled = true;
127
        $this->xAutoloader = null;
128
129
        // Javascript confirm function
130
        $this->xConfirm = null;
131
        $this->xDefaultConfirm = new \Jaxon\Request\Support\Confirm();
132
133
        // Javascript alert function
134
        $this->xAlert = null;
135
        $this->xDefaultAlert = new \Jaxon\Request\Support\Alert();
136
    }
137
138
    /**
139
     * Use the Composer autoloader
140
     *
141
     * @return void
142
     */
143
    public function useComposerAutoloader()
144
    {
145
        $this->bAutoloadEnabled = true;
146
        $this->xAutoloader = require(__DIR__ . '/../../../../autoload.php');
147
    }
148
149
    /**
150
     * Disable the autoloader in the library
151
     *
152
     * The user shall provide an alternative autoload system.
153
     *
154
     * @return void
155
     */
156
    public function disableAutoload()
157
    {
158
        $this->bAutoloadEnabled = false;
159
        $this->xAutoloader = null;
160
    }
161
162
    /**
163
     * Set the javascript confirm function
164
     *
165
     * @param \Jaxon\Request\Interfaces\Confirm         $xConfirm     The javascript confirm function
166
     *
167
     * @return void
168
     */
169
    public function setConfirm(\Jaxon\Request\Interfaces\Confirm $xConfirm)
170
    {
171
        $this->xConfirm = $xConfirm;
172
    }
173
174
    /**
175
     * Get the javascript confirm function
176
     *
177
     * @return \Jaxon\Request\Interfaces\Confirm
178
     */
179
    public function getConfirm()
180
    {
181
        return (($this->xConfirm) ? $this->xConfirm : $this->xDefaultConfirm);
182
    }
183
184
    /**
185
     * Get the default javascript confirm function
186
     *
187
     * @return \Jaxon\Request\Support\Confirm
188
     */
189
    public function getDefaultConfirm()
190
    {
191
        return $this->xDefaultConfirm;
192
    }
193
194
    /**
195
     * Set the javascript alert function
196
     *
197
     * @param \Jaxon\Request\Interfaces\Alert           $xAlert       The javascript alert function
198
     *
199
     * @return void
200
     */
201
    public function setAlert(\Jaxon\Request\Interfaces\Alert $xAlert)
202
    {
203
        $this->xAlert = $xAlert;
204
    }
205
206
    /**
207
     * Get the javascript alert function
208
     *
209
     * @return \Jaxon\Request\Interfaces\Alert
210
     */
211
    public function getAlert()
212
    {
213
        return (($this->xAlert) ? $this->xAlert : $this->xDefaultAlert);
214
    }
215
216
    /**
217
     * Get the default javascript alert function
218
     *
219
     * @return \Jaxon\Request\Support\Alert
220
     */
221
    public function getDefaultAlert()
222
    {
223
        return $this->xDefaultAlert;
224
    }
225
226
    /**
227
     * Inserts an entry into an array given the specified priority number
228
     *
229
     * If a plugin already exists with the given priority, the priority is automatically incremented until a free spot is found.
230
     * The plugin is then inserted into the empty spot in the array.
231
     *
232
     * @param Plugin         $xPlugin               An instance of a plugin
233
     * @param integer        $nPriority             The desired priority, used to order the plugins
234
     *
235
     * @return void
236
     */
237
    private function setPluginPriority(Plugin $xPlugin, $nPriority)
238
    {
239
        while (isset($this->aPlugins[$nPriority]))
240
        {
241
            $nPriority++;
242
        }
243
        $this->aPlugins[$nPriority] = $xPlugin;
244
        // Sort the array by ascending keys
245
        ksort($this->aPlugins);
246
    }
247
248
    /**
249
     * Register a plugin
250
     *
251
     * Below is a table for priorities and their description:
252
     * - 0 thru 999: Plugins that are part of or extensions to the jaxon core
253
     * - 1000 thru 8999: User created plugins, typically, these plugins don't care about order
254
     * - 9000 thru 9999: Plugins that generally need to be last or near the end of the plugin list
255
     *
256
     * @param Plugin         $xPlugin               An instance of a plugin
257
     * @param integer        $nPriority             The plugin priority, used to order the plugins
258
     *
259
     * @return void
260
     */
261
    public function registerPlugin(Plugin $xPlugin, $nPriority = 1000)
262
    {
263
        $bIsAlert = ($xPlugin instanceof \Jaxon\Request\Interfaces\Alert);
264
        $bIsConfirm = ($xPlugin instanceof \Jaxon\Request\Interfaces\Confirm);
265
        if($xPlugin instanceof Request)
266
        {
267
            // The name of a request plugin is used as key in the plugin table
268
            $this->aRequestPlugins[$xPlugin->getName()] = $xPlugin;
269
        }
270
        elseif($xPlugin instanceof Response)
271
        {
272
            // The name of a response plugin is used as key in the plugin table
273
            $this->aResponsePlugins[$xPlugin->getName()] = $xPlugin;
274
        }
275
        elseif(!$bIsConfirm && !$bIsAlert)
276
        {
277
            throw new \Jaxon\Exception\Error($this->trans('errors.register.invalid', array('name' => get_class($xPlugin))));
278
        }
279
        // This plugin implements the Alert interface
280
        if($bIsAlert)
281
        {
282
            $this->setAlert($xPlugin);
283
        }
284
        // This plugin implements the Confirm interface
285
        if($bIsConfirm)
286
        {
287
            $this->setConfirm($xPlugin);
288
        }
289
        // Register the plugin as an event listener
290
        if($xPlugin instanceof \Jaxon\Utils\Interfaces\EventListener)
291
        {
292
            $this->addEventListener($xPlugin);
293
        }
294
295
        $this->setPluginPriority($xPlugin, $nPriority);
296
    }
297
298
    /**
299
     * Generate a hash for all the javascript code generated by the library
300
     *
301
     * @return string
302
     */
303
    private function generateHash()
304
    {
305
        $sHash = $this->getVersion();
306
        foreach($this->aPlugins as $xPlugin)
307
        {
308
            $sHash .= $xPlugin->generateHash();
309
        }
310
        return md5($sHash);
311
    }
312
313
    /**
314
     * Check if the current request can be processed
315
     *
316
     * Calls each of the request plugins and determines if the current request can be processed by one of them.
317
     * If no processor identifies the current request, then the request must be for the initial page load.
318
     *
319
     * @return boolean
320
     */
321
    public function canProcessRequest()
322
    {
323
        foreach($this->aRequestPlugins as $xPlugin)
324
        {
325
            if($xPlugin->canProcessRequest())
326
            {
327
                return true;
328
            }
329
        }
330
        return false;
331
    }
332
333
    /**
334
     * Process the current request
335
     *
336
     * Calls each of the request plugins to request that they process the current request.
337
     * If any plugin processes the request, it will return true.
338
     *
339
     * @return boolean
340
     */
341
    public function processRequest()
342
    {
343
        foreach($this->aRequestPlugins as $xPlugin)
344
        {
345
            if($xPlugin->canProcessRequest())
346
            {
347
                return $xPlugin->processRequest();
348
            }
349
        }
350
        // Todo: throw an exception
351
        return false;
352
    }
353
    
354
    /**
355
     * Register a function, event or callable object
356
     *
357
     * Call each of the request plugins and give them the opportunity to handle the
358
     * registration of the specified function, event or callable object.
359
     *
360
     * @param array         $aArgs                The registration data
361
     *
362
     * @return mixed
363
     */
364
    public function register($aArgs)
365
    {
366
        foreach($this->aRequestPlugins as $xPlugin)
367
        {
368
            $mResult = $xPlugin->register($aArgs);
369
            if($mResult instanceof \Jaxon\Request\Request || is_array($mResult) || $mResult === true)
370
            {
371
                return $mResult;
372
            }
373
        }
374
        throw new \Jaxon\Exception\Error($this->trans('errors.register.method', array('args' => print_r($aArgs, true))));
375
    }
376
377
    /**
378
     * Add a path to the class directories
379
     *
380
     * @param string            $sDirectory             The path to the directory
381
     * @param string|null       $sNamespace             The associated namespace
382
     * @param string            $sSeparator             The character to use as separator in javascript class names
383
     * @param array             $aProtected             The functions that are not to be exported
384
     *
385
     * @return boolean
386
     */
387
    public function addClassDir($sDirectory, $sNamespace = null, $sSeparator = '.', array $aProtected = array())
388
    {
389
        if(!is_dir(($sDirectory = trim($sDirectory))))
390
        {
391
            return false;
392
        }
393
        // Only '.' and '_' are allowed to be used as separator. Any other value is ignored and '.' is used instead.
394
        if(($sSeparator = trim($sSeparator)) != '_')
395
        {
396
            $sSeparator = '.';
397
        }
398
        if(!($sNamespace = trim($sNamespace, ' \\')))
399
        {
400
            $sNamespace = null;
401
        }
402
        if(($sNamespace))
0 ignored issues
show
Bug Best Practice introduced by
The expression $sNamespace of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
403
        {
404
            // If there is an autoloader, register the dir with PSR4 autoloading
405
            if(($this->xAutoloader))
406
            {
407
                $this->xAutoloader->setPsr4($sNamespace . '\\', $sDirectory);
408
            }
409
        }
410
        elseif(($this->xAutoloader))
411
        {
412
            // If there is an autoloader, register the dir with classmap autoloading
413
            $itDir = new RecursiveDirectoryIterator($sDirectory);
414
            $itFile = new RecursiveIteratorIterator($itDir);
415
            // Iterate on dir content
416
            foreach($itFile as $xFile)
417
            {
418
                // skip everything except PHP files
419
                if(!$xFile->isFile() || $xFile->getExtension() != 'php')
420
                {
421
                    continue;
422
                }
423
                $this->xAutoloader->addClassMap(array($xFile->getBasename('.php') => $xFile->getPathname()));
424
            }
425
        }
426
        $this->aClassDirs[] = array(
427
            'directory' => rtrim($sDirectory, DIRECTORY_SEPARATOR),
428
            'namespace' => $sNamespace,
429
            'separator' => $sSeparator,
430
            'protected' => $aProtected
431
        );
432
        return true;
433
    }
434
435
    /**
436
     * Register an instance of a given class from a file
437
     *
438
     * @param object            $xFile                  The PHP file containing the class
439
     * @param string            $sDirectory             The path to the directory
440
     * @param string|null       $sNamespace             The associated namespace
441
     * @param string            $sSeparator             The character to use as separator in javascript class names
442
     * @param array             $aProtected             The functions that are not to be exported
443
     * @param array             $aOptions               The options to register the class with
444
     *
445
     * @return void
446
     */
447
    protected function registerClassFromFile($xFile, $sDirectory, $sNamespace = null, $sSeparator = '.',
448
        array $aProtected = array(), array $aOptions = array())
449
    {
450
        $sDS = DIRECTORY_SEPARATOR;
451
        // Get the corresponding class path and name
452
        $sClassPath = trim(substr($xFile->getPath(), strlen($sDirectory)), $sDS);
453
        $sClassName = $xFile->getBasename('.php');
454
        if(($sNamespace))
455
        {
456
            $sClassPath = $sNamespace . '\\' . $sClassPath;
457
            $sClassName = '\\' . $sClassPath . '\\' . $sClassName;
458
        }
459
        // Require the file only if autoload is enabled but there is no autoloader
460
        if(($this->bAutoloadEnabled) && !($this->xAutoloader))
461
        {
462
            require_once($xFile->getPathname());
463
        }
464
        // Create and register an instance of the class
465
        if(!array_key_exists('*', $aOptions) || !is_array($aOptions['*']))
466
        {
467
            $aOptions['*'] = array();
468
        }
469
        $aOptions['*']['separator'] = $sSeparator;
470
        if(($sNamespace))
471
        {
472
            $aOptions['*']['namespace'] = $sNamespace;
473
        }
474
        if(($sClassPath))
475
        {
476
            $aOptions['*']['classpath'] = $sClassPath;
477
        }
478
        // Filter excluded methods
479
        $aProtected = array_filter($aProtected, function ($sName) {return is_string($sName);});
480
        if(count($aProtected) > 0)
481
        {
482
            $aOptions['*']['protected'] = $aProtected;
483
        }
484
        $this->register(array(Jaxon::CALLABLE_OBJECT, $sClassName, $aOptions));
485
    }
486
487
    /**
488
     * Register callable objects from all class directories
489
     *
490
     * @param array             $aOptions               The options to register the classes with
491
     *
492
     * @return void
493
     */
494
    public function registerClasses(array $aOptions = array())
495
    {
496
        $sDS = DIRECTORY_SEPARATOR;
497
        // Change the keys in $aOptions to have "\" as separator
498
        $aNewOptions = array();
499
        foreach($aOptions as $key => $aOption)
500
        {
501
            $key = trim(str_replace(['.', '_'], ['\\', '\\'], $key), ' \\');
502
            $aNewOptions[$key] = $aOption;
503
        }
504
505
        foreach($this->aClassDirs as $aClassDir)
506
        {
507
            // Get the directory
508
            $sDirectory = $aClassDir['directory'];
509
            // Get the namespace
510
            $sNamespace = $aClassDir['namespace'];
511
            $nLen = strlen($sNamespace);
0 ignored issues
show
Unused Code introduced by
$nLen 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...
512
513
            $itDir = new RecursiveDirectoryIterator($sDirectory);
514
            $itFile = new RecursiveIteratorIterator($itDir);
515
            // Iterate on dir content
516
            foreach($itFile as $xFile)
517
            {
518
                // skip everything except PHP files
519
                if(!$xFile->isFile() || $xFile->getExtension() != 'php')
520
                {
521
                    continue;
522
                }
523
524
                // Get the class name
525
                $sClassPath = substr($xFile->getPath(), strlen($sDirectory));
526
                $sClassPath = trim(str_replace($sDS, '\\', $sClassPath), '\\');
527
                $sClassName = $sClassPath . '\\' . $xFile->getBasename('.php');
528
                if(($sNamespace))
529
                {
530
                    $sClassName = $sNamespace . '\\' . $sClassName;
531
                }
532
                // Get the class options
533
                $aClassOptions = [];
534
                if(array_key_exists($sClassName, $aNewOptions))
535
                {
536
                    $aClassOptions = $aNewOptions[$sClassName];
537
                }
538
539
                $this->registerClassFromFile($xFile, $sDirectory, $sNamespace,
540
                    $aClassDir['separator'], $aClassDir['protected'], $aClassOptions);
541
            }
542
        }
543
    }
544
545
    /**
546
     * Register an instance of a given class
547
     *
548
     * @param string            $sClassName             The name of the class to be registered
549
     * @param array             $aOptions               The options to register the class with
550
     *
551
     * @return bool
552
     */
553
    public function registerClass($sClassName, array $aOptions = array())
554
    {
555
        if(!($sClassName = trim($sClassName, ' \\._')))
556
        {
557
            return false;
558
        }
559
        $sDS = DIRECTORY_SEPARATOR;
560
561
        // Replace "." and "_" with antislashes, and set the class path.
562
        $sClassName = str_replace(['.', '_'], ['\\', '\\'], $sClassName);
563
        $sClassPath = '';
564
        if(($nLastSlashPosition = strrpos($sClassName, '\\')) !== false)
565
        {
566
            $sClassPath = substr($sClassName, 0, $nLastSlashPosition);
567
            $sClassName = substr($sClassName, $nLastSlashPosition + 1);
568
        }
569
        // Path to the file, relative to a registered directory.
570
        $sPartPath = str_replace('\\', $sDS, $sClassPath) . $sDS . $sClassName . '.php';
571
572
        // Search for the class file in all directories.
573
        foreach($this->aClassDirs as $aClassDir)
574
        {
575
            // Get the separator
576
            $sSeparator = $aClassDir['separator'];
577
            // Get the namespace
578
            $sNamespace = $aClassDir['namespace'];
579
            $nLen = strlen($sNamespace);
580
            $sFullPath = '';
581
            // Check if the class belongs to the namespace
582
            if(($sNamespace) && substr($sClassPath, 0, $nLen) == $sNamespace)
583
            {
584
                $sFullPath = $aClassDir['directory'] . $sDS . substr($sPartPath, $nLen + 1);
585
            }
586
            elseif(!($sNamespace))
587
            {
588
                $sFullPath = $aClassDir['directory'] . $sDS . $sPartPath;
589
            }
590
            if(($sFullPath) && is_file($sFullPath))
591
            {
592
                // Found the file in this directory
593
                $xFileInfo = new \SplFileInfo($sFullPath);
594
                $sDirectory = $aClassDir['directory'];
595
                $aProtected = $aClassDir['protected'];
596
                $this->registerClassFromFile($xFileInfo, $sDirectory, $sNamespace, $sSeparator, $aProtected, $aOptions);
597
                return true;
598
            }
599
        }
600
        return false;
601
    }
602
603
    /**
604
     * Find a user registered callable object by class name
605
     *
606
     * @param string        $sClassName            The class name of the callable object
607
     *
608
     * @return object
609
     */
610
    public function getRegisteredObject($sClassName)
611
    {
612
        $xObject = null; // The user registered object
613
        $xPlugin = $this->getRequestPlugin('CallableObject'); // The CallableObject plugin
614
        if(($xPlugin))
615
        {
616
            $xObject = $xPlugin->getRegisteredObject($sClassName);
617
        }
618
        return $xObject;
619
    }
620
621
    /**
622
     * Get the base URI of the Jaxon library javascript files
623
     *
624
     * @return string
625
     */
626
    private function getJsLibUri()
627
    {
628
        if(!$this->hasOption('js.lib.uri'))
629
        {
630
            return 'https://cdn.jsdelivr.net/jaxon/1.2.0/';
631
        }
632
        // Todo: check the validity of the URI
633
        return rtrim($this->getOption('js.lib.uri'), '/') . '/';
634
    }
635
    
636
    /**
637
     * Get the extension of the Jaxon library javascript files
638
     *
639
     * The returned string is '.min.js' if the files are minified.
640
     *
641
     * @return string
642
     */
643
    private function getJsLibExt()
644
    {
645
        $jsDelivrUri = 'https://cdn.jsdelivr.net';
646
        $nLen = strlen($jsDelivrUri);
647
        // The jsDelivr CDN only hosts minified files
648
        if(($this->getOption('js.app.minify')) || substr($this->getJsLibUri(), 0, $nLen) == $jsDelivrUri)
649
        {
650
            return '.min.js';
651
        }
652
        return '.js';
653
    }
654
655
    /**
656
     * Check if the javascript code generated by Jaxon can be exported to an external file
657
     *
658
     * @return boolean
659
     */
660
    public function canExportJavascript()
661
    {
662
        // Check config options
663
        // - The js.app.extern option must be set to true
664
        // - The js.app.uri and js.app.dir options must be set to non null values
665
        if(!$this->getOption('js.app.extern') ||
666
            !$this->getOption('js.app.uri') ||
667
            !$this->getOption('js.app.dir'))
668
        {
669
            return false;
670
        }
671
        // Check dir access
672
        // - The js.app.dir must be writable
673
        $sJsAppDir = $this->getOption('js.app.dir');
674
        if(!is_dir($sJsAppDir) || !is_writable($sJsAppDir))
675
        {
676
            return false;
677
        }
678
        return true;
679
    }
680
681
    /**
682
     * Set the cache directory for the template engine
683
     *
684
     * @return void
685
     */
686
    private function setTemplateCacheDir()
687
    {
688
        if($this->hasOption('core.template.cache_dir'))
689
        {
690
            $this->setCacheDir($this->getOption('core.template.cache_dir'));
691
        }
692
    }
693
694
    /**
695
     * Get the HTML tags to include Jaxon javascript files into the page
696
     *
697
     * @return string
698
     */
699
    public function getJs()
700
    {
701
        $sJsLibUri = $this->getJsLibUri();
702
        $sJsLibExt = $this->getJsLibExt();
703
        $sJsCoreUrl = $sJsLibUri . 'jaxon.core' . $sJsLibExt;
704
        $sJsDebugUrl = $sJsLibUri . 'jaxon.debug' . $sJsLibExt;
705
        $sJsVerboseUrl = $sJsLibUri . 'jaxon.verbose' . $sJsLibExt;
706
        $sJsLanguageUrl = $sJsLibUri . 'lang/jaxon.' . $this->getOption('core.language') . $sJsLibExt;
707
708
        // Add component files to the javascript file array;
709
        $aJsFiles = array($sJsCoreUrl);
710
        if($this->getOption('core.debug.on'))
711
        {
712
            $aJsFiles[] = $sJsDebugUrl;
713
            $aJsFiles[] = $sJsLanguageUrl;
714
            if($this->getOption('core.debug.verbose'))
715
            {
716
                $aJsFiles[] = $sJsVerboseUrl;
717
            }
718
        }
719
720
        // Set the template engine cache dir
721
        $this->setTemplateCacheDir();
722
        $sCode = $this->render('jaxon::plugins/includes.js', array(
723
            'sJsOptions' => $this->getOption('js.app.options'),
724
            'aUrls' => $aJsFiles,
725
        ));
726
        foreach($this->aResponsePlugins as $xPlugin)
727
        {
728
            $sCode .= rtrim($xPlugin->getJs(), " \n") . "\n";
729
        }
730
        return $sCode;
731
    }
732
733
    /**
734
     * Get the HTML tags to include Jaxon CSS code and files into the page
735
     *
736
     * @return string
737
     */
738
    public function getCss()
739
    {
740
        // Set the template engine cache dir
741
        $this->setTemplateCacheDir();
742
743
        $sCode = '';
744
        foreach($this->aResponsePlugins as $xPlugin)
745
        {
746
            $sCode .= rtrim($xPlugin->getCss(), " \n") . "\n";
747
        }
748
        return $sCode;
749
    }
750
751
    /**
752
     * Get the correspondances between previous and current config options
753
     *
754
     * They are used to keep the deprecated config options working.
755
     * They will be removed when the deprecated options will lot be supported anymore.
756
     *
757
     * @return array
758
     */
759
    private function getOptionVars()
760
    {
761
        return array(
762
            'sResponseType'             => self::RESPONSE_TYPE,
763
            'sVersion'                  => $this->getOption('core.version'),
764
            'sLanguage'                 => $this->getOption('core.language'),
765
            'bLanguage'                 => $this->hasOption('core.language') ? true : false,
766
            'sRequestURI'               => $this->getOption('core.request.uri'),
767
            'sDefaultMode'              => $this->getOption('core.request.mode'),
768
            'sDefaultMethod'            => $this->getOption('core.request.method'),
769
            'sCsrfMetaName'             => $this->getOption('core.request.csrf_meta'),
770
            'bDebug'                    => $this->getOption('core.debug.on'),
771
            'bVerboseDebug'             => $this->getOption('core.debug.verbose'),
772
            'sDebugOutputID'            => $this->getOption('js.lib.output_id'),
773
            'nResponseQueueSize'        => $this->getOption('js.lib.queue_size'),
774
            'sStatusMessages'           => $this->getOption('js.lib.show_status') ? 'true' : 'false',
775
            'sWaitCursor'               => $this->getOption('js.lib.show_cursor') ? 'true' : 'false',
776
            'sDefer'                    => $this->getOption('js.app.options'),
777
        );
778
    }
779
780
    /**
781
     * Get the javascript code for Jaxon client side configuration
782
     *
783
     * @return string
784
     */
785
    private function getConfigScript()
786
    {
787
        $aVars = $this->getOptionVars();
788
        $sYesScript = 'jaxon.confirm.skip(command);jaxon.tools.queue.process(command.response)';
789
        $sNoScript = 'jaxon.tools.queue.process(command.response)';
790
        $sConfirmScript = $this->getConfirm()->confirm('msg', $sYesScript, $sNoScript);
791
        $aVars['sConfirmScript'] = $this->render('jaxon::plugins/confirm.js', array('sConfirmScript' => $sConfirmScript));
792
793
        return $this->render('jaxon::plugins/config.js', $aVars);
794
    }
795
796
    /**
797
     * Get the javascript code to be run after page load
798
     *
799
     * Also call each of the response plugins giving them the opportunity
800
     * to output some javascript to the page being generated.
801
     *
802
     * @return string
803
     */
804
    private function getReadyScript()
805
    {
806
        // Print Jaxon config vars
807
        $sJsLibUri = $this->getJsLibUri();
808
        $sJsLibExt = $this->getJsLibExt();
809
        $sJsCoreUrl = $sJsLibUri . 'jaxon.core' . $sJsLibExt;
810
        $sJsDebugUrl = $sJsLibUri . 'jaxon.debug' . $sJsLibExt;
811
        $sJsVerboseUrl = $sJsLibUri . 'jaxon.verbose' . $sJsLibExt;
812
        $sJsLanguageUrl = $sJsLibUri . 'lang/jaxon.' . $this->getOption('core.language') . $sJsLibExt;
813
814
        $sJsCoreError = $this->trans('errors.component.load', array(
815
            'name' => 'jaxon',
816
            'url' => $sJsCoreUrl,
817
        ));
818
        $sJsDebugError = $this->trans('errors.component.load', array(
819
            'name' => 'jaxon.debug',
820
            'url' => $sJsDebugUrl,
821
        ));
822
        $sJsVerboseError = $this->trans('errors.component.load', array(
823
            'name' => 'jaxon.debug.verbose',
824
            'url' => $sJsVerboseUrl,
825
        ));
826
        $sJsLanguageError = $this->trans('errors.component.load', array(
827
            'name' => 'jaxon.debug.lang',
828
            'url' => $sJsLanguageUrl,
829
        ));
830
831
        $sPluginScript = '';
832
        foreach($this->aResponsePlugins as $xPlugin)
833
        {
834
            $sPluginScript .= "\n" . trim($xPlugin->getScript(), " \n");
835
        }
836
837
        $aVars = $this->getOptionVars();
838
        $aVars['sPluginScript'] = $sPluginScript;
839
        $aVars['sJsCoreError'] = $sJsCoreError;
840
        $aVars['sJsDebugError'] = $sJsDebugError;
841
        $aVars['sJsVerboseError'] = $sJsVerboseError;
842
        $aVars['sJsLanguageError'] = $sJsLanguageError;
843
844
        return $this->render('jaxon::plugins/ready.js', $aVars);
845
    }
846
847
    /**
848
     * Get the javascript code to be sent to the browser
849
     *
850
     * Also call each of the request plugins giving them the opportunity
851
     * to output some javascript to the page being generated.
852
     * This is called only when the page is being loaded initially.
853
     * This is not called when processing a request.
854
     *
855
     * @return string
856
     */
857
    public function getScript()
858
    {
859
        // Set the template engine cache dir
860
        $this->setTemplateCacheDir();
861
862
        // Get the config and plugins scripts
863
        $sScript = $this->getConfigScript() . "\n" . $this->getReadyScript() . "\n";
864
        foreach($this->aRequestPlugins as $xPlugin)
865
        {
866
            $sScript .= "\n" . trim($xPlugin->getScript(), " \n");
867
        }
868
        if($this->canExportJavascript())
869
        {
870
            $sJsAppURI = rtrim($this->getOption('js.app.uri'), '/') . '/';
871
            $sJsAppDir = rtrim($this->getOption('js.app.dir'), '/') . '/';
872
873
            // The plugins scripts are written into the javascript app dir
874
            $sHash = $this->generateHash();
875
            $sOutFile = $sHash . '.js';
876
            $sMinFile = $sHash . '.min.js';
877
            if(!is_file($sJsAppDir . $sOutFile))
878
            {
879
                file_put_contents($sJsAppDir . $sOutFile, $sScript);
880
            }
881
            if(($this->getOption('js.app.minify')))
882
            {
883
                if(($this->minify($sJsAppDir . $sOutFile, $sJsAppDir . $sMinFile)))
884
                {
885
                    $sOutFile = $sMinFile;
886
                }
887
            }
888
889
            // The returned code loads the generated javascript file
890
            $sScript = $this->render('jaxon::plugins/include.js', array(
891
                'sJsOptions' => $this->getOption('js.app.options'),
892
                'sUrl' => $sJsAppURI . $sOutFile,
893
            ));
894
        }
895
        else
896
        {
897
            // The plugins scripts are wrapped with javascript tags
898
            $sScript = $this->render('jaxon::plugins/wrapper.js', array(
899
                'sJsOptions' => $this->getOption('js.app.options'),
900
                'sScript' => $sScript,
901
            ));
902
        }
903
        
904
        return $sScript;
905
    }
906
907
    /**
908
     * Find the specified response plugin by name and return a reference to it if one exists
909
     *
910
     * @param string        $sName                The name of the plugin
911
     *
912
     * @return \Jaxon\Plugin\Response
913
     */
914
    public function getResponsePlugin($sName)
915
    {
916
        if(array_key_exists($sName, $this->aResponsePlugins))
917
        {
918
            return $this->aResponsePlugins[$sName];
919
        }
920
        return null;
921
    }
922
923
    /**
924
     * Find the specified request plugin by name and return a reference to it if one exists
925
     *
926
     * @param string        $sName                The name of the plugin
927
     *
928
     * @return \Jaxon\Plugin\Request
929
     */
930
    public function getRequestPlugin($sName)
931
    {
932
        if(array_key_exists($sName, $this->aRequestPlugins))
933
        {
934
            return $this->aRequestPlugins[$sName];
935
        }
936
        return null;
937
    }
938
}
939