File indexing completed on 2024-12-22 05:36:37

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Dojo
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * Dojo module layer and custom build profile generation support
0024  *
0025  * @package    Zend_Dojo
0026  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0027  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0028  */
0029 class Zend_Dojo_BuildLayer
0030 {
0031     /**
0032      * Flag: whether or not to consume JS aggregated in the dojo() view
0033      * helper when generate the module layer contents
0034      * @var bool
0035      */
0036     protected $_consumeJavascript = false;
0037 
0038     /**
0039      * Flag: whether or not to consume dojo.addOnLoad events registered
0040      * with the dojo() view helper when generating the module layer file
0041      * contents
0042      * @var bool
0043      */
0044     protected $_consumeOnLoad = false;
0045 
0046     /**
0047      * Dojo view helper reference
0048      * @var Zend_Dojo_View_Helper_Dojo_Container
0049      */
0050     protected $_dojo;
0051 
0052     /**
0053      * Name of the custom layer to generate
0054      * @var string
0055      */
0056     protected $_layerName;
0057 
0058     /**
0059      * Path to the custom layer script relative to dojo.js (used when
0060      * creating the build profile)
0061      * @var string
0062      */
0063     protected $_layerScriptPath;
0064 
0065     /**
0066      * Build profile options
0067      * @var array
0068      */
0069     protected $_profileOptions = array(
0070         'action'        => 'release',
0071         'optimize'      => 'shrinksafe',
0072         'layerOptimize' => 'shrinksafe',
0073         'copyTests'     => false,
0074         'loader'        => 'default',
0075         'cssOptimize'   => 'comments',
0076     );
0077 
0078     /**
0079      * Associative array of module/path pairs for the build profile
0080      * @var array
0081      */
0082     protected $_profilePrefixes = array();
0083 
0084     /**
0085      * Zend_View reference
0086      * @var Zend_View_Interface
0087      */
0088     protected $_view;
0089 
0090     /**
0091      * Constructor
0092      *
0093      * @param  array|Zend_Config $options
0094      * @return void
0095      * @throws Zend_Dojo_Exception for invalid option argument
0096      */
0097     public function __construct($options = null)
0098     {
0099         if (null !== $options) {
0100             if ($options instanceof Zend_Config) {
0101                 $options = $options->toArray();
0102             } elseif (!is_array($options)) {
0103                 // require_once 'Zend/Dojo/Exception.php';
0104                 throw new Zend_Dojo_Exception('Invalid options provided to constructor');
0105             }
0106             $this->setOptions($options);
0107         }
0108     }
0109 
0110     /**
0111      * Set options
0112      *
0113      * Proxies to any setter that matches an option key.
0114      *
0115      * @param  array $options
0116      * @return Zend_Dojo_BuildLayer
0117      */
0118     public function setOptions(array $options)
0119     {
0120         $methods = get_class_methods($this);
0121         foreach ($options as $key => $value) {
0122             $method = 'set' . ucfirst($key);
0123             if (in_array($method, $methods)) {
0124                 $this->$method($value);
0125             }
0126         }
0127         return $this;
0128     }
0129 
0130     /**
0131      * Set View object
0132      *
0133      * @param  Zend_View_Interface $view
0134      * @return Zend_Dojo_BuildLayer
0135      */
0136     public function setView(Zend_View_Interface $view)
0137     {
0138         $this->_view = $view;
0139         return $this;
0140     }
0141 
0142     /**
0143      * Retrieve view object
0144      *
0145      * @return Zend_View_Interface|null
0146      */
0147     public function getView()
0148     {
0149         return $this->_view;
0150     }
0151 
0152     /**
0153      * Set dojo() view helper instance
0154      *
0155      * @param  Zend_Dojo_View_Helper_Dojo_Container $helper
0156      * @return Zend_Dojo_BuildLayer
0157      */
0158     public function setDojoHelper(Zend_Dojo_View_Helper_Dojo_Container $helper)
0159     {
0160         $this->_dojo = $helper;
0161         return $this;
0162     }
0163 
0164     /**
0165      * Retrieve dojo() view helper instance
0166      *
0167      * Will retrieve it from the view object if not registered.
0168      *
0169      * @return Zend_Dojo_View_Helper_Dojo_Container
0170      * @throws Zend_Dojo_Exception if not registered and no view object found
0171      */
0172     public function getDojoHelper()
0173     {
0174         if (null === $this->_dojo) {
0175             if (null === ($view = $this->getView())) {
0176                 // require_once 'Zend/Dojo/Exception.php';
0177                 throw new Zend_Dojo_Exception('View object not registered; cannot retrieve dojo helper');
0178             }
0179             $helper = $view->getHelper('dojo');
0180             $this->setDojoHelper($view->dojo());
0181         }
0182         return $this->_dojo;
0183     }
0184 
0185     /**
0186      * Set custom layer name; e.g. "custom.main"
0187      *
0188      * @param  string $name
0189      * @return Zend_Dojo_BuildLayer
0190      */
0191     public function setLayerName($name)
0192     {
0193         if (!preg_match('/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i', $name)) {
0194             // require_once 'Zend/Dojo/Exception.php';
0195             throw new Zend_Dojo_Exception('Invalid layer name provided; must be of form[a-z][a-z0-9_](\.[a-z][a-z0-9_])+');
0196         }
0197         $this->_layerName = $name;
0198         return $this;
0199     }
0200 
0201     /**
0202      * Retrieve custom layer name
0203      *
0204      * @return string|null
0205      */
0206     public function getLayerName()
0207     {
0208         return $this->_layerName;
0209     }
0210 
0211     /**
0212      * Set the path to the custom layer script
0213      *
0214      * Should be a path relative to dojo.js
0215      *
0216      * @param  string $path
0217      * @return Zend_Dojo_BuildLayer
0218      */
0219     public function setLayerScriptPath($path)
0220     {
0221         $this->_layerScriptPath = (string) $path;
0222         return $this;
0223     }
0224 
0225     /**
0226      * Get custom layer script path
0227      *
0228      * @return string|null
0229      */
0230     public function getLayerScriptPath()
0231     {
0232         return $this->_layerScriptPath;
0233     }
0234 
0235     /**
0236      * Set flag indicating whether or not to consume JS aggregated in dojo()
0237      * view helper
0238      *
0239      * @param  bool $flag
0240      * @return Zend_Dojo_BuildLayer
0241      */
0242     public function setConsumeJavascript($flag)
0243     {
0244         $this->_consumeJavascript = (bool) $flag;
0245         return $this;
0246     }
0247 
0248     /**
0249      * Get flag indicating whether or not to consume JS aggregated in dojo()
0250      * view helper
0251      *
0252      * @return bool
0253      */
0254     public function consumeJavascript()
0255     {
0256         return $this->_consumeJavascript;
0257     }
0258 
0259     /**
0260      * Set flag indicating whether or not to consume dojo.addOnLoad events
0261      * aggregated in dojo() view helper
0262      *
0263      * @param  bool $flag
0264      * @return Zend_Dojo_BuildLayer
0265      */
0266     public function setConsumeOnLoad($flag)
0267     {
0268         $this->_consumeOnLoad = (bool) $flag;
0269         return $this;
0270     }
0271 
0272     /**
0273      * Get flag indicating whether or not to consume dojo.addOnLoad events aggregated in dojo() view helper
0274      *
0275      * @return bool
0276      */
0277     public function consumeOnLoad()
0278     {
0279         return $this->_consumeOnLoad;
0280     }
0281 
0282     /**
0283      * Set many build profile options at once
0284      *
0285      * @param  array $options
0286      * @return Zend_Dojo_BuildLayer
0287      */
0288     public function setProfileOptions(array $options)
0289     {
0290         $this->_profileOptions += $options;
0291         return $this;
0292     }
0293 
0294     /**
0295      * Add many build profile options at once
0296      *
0297      * @param  array $options
0298      * @return Zend_Dojo_BuildLayer
0299      */
0300     public function addProfileOptions(array $options)
0301     {
0302         $this->_profileOptions = $this->_profileOptions + $options;
0303         return $this;
0304     }
0305 
0306     /**
0307      * Add a single build profile option
0308      *
0309      * @param  string $key
0310      * @param  value $value
0311      * @return Zend_Dojo_BuildLayer
0312      */
0313     public function addProfileOption($key, $value)
0314     {
0315         $this->_profileOptions[(string) $key] = $value;
0316         return $this;
0317     }
0318 
0319     /**
0320      * Is a given build profile option set?
0321      *
0322      * @param  string $key
0323      * @return bool
0324      */
0325     public function hasProfileOption($key)
0326     {
0327         return array_key_exists((string) $key, $this->_profileOptions);
0328     }
0329 
0330     /**
0331      * Retrieve a single build profile option
0332      *
0333      * Returns null if profile option does not exist.
0334      *
0335      * @param  string $key
0336      * @return mixed
0337      */
0338     public function getProfileOption($key)
0339     {
0340         if ($this->hasProfileOption($key)) {
0341             return $this->_profileOptions[(string) $key];
0342         }
0343         return null;
0344     }
0345 
0346     /**
0347      * Get all build profile options
0348      *
0349      * @return array
0350      */
0351     public function getProfileOptions()
0352     {
0353         return $this->_profileOptions;
0354     }
0355 
0356     /**
0357      * Remove a build profile option
0358      *
0359      * @param  string $name
0360      * @return Zend_Dojo_BuildLayer
0361      */
0362     public function removeProfileOption($name)
0363     {
0364         if ($this->hasProfileOption($name)) {
0365             unset($this->_profileOptions[(string) $name]);
0366         }
0367         return $this;
0368     }
0369 
0370     /**
0371      * Remove all build profile options
0372      *
0373      * @return Zend_Dojo_BuildLayer
0374      */
0375     public function clearProfileOptions()
0376     {
0377         $this->_profileOptions = array();
0378         return $this;
0379     }
0380 
0381     /**
0382      * Add a build profile dependency prefix
0383      *
0384      * If just the prefix is passed, sets path to "../$prefix".
0385      *
0386      * @param  string $prefix
0387      * @param  null|string $path
0388      * @return Zend_Dojo_BuildLayer
0389      */
0390     public function addProfilePrefix($prefix, $path = null)
0391     {
0392         if (null === $path) {
0393             $path = '../' . $prefix;
0394         }
0395         $this->_profilePrefixes[$prefix] = array($prefix, $path);
0396         return $this;
0397     }
0398 
0399     /**
0400      * Set multiple dependency prefixes for bulid profile
0401      *
0402      * @param  array $prefixes
0403      * @return Zend_Dojo_BuildLayer
0404      */
0405     public function setProfilePrefixes(array $prefixes)
0406     {
0407         foreach ($prefixes as $prefix => $path) {
0408             $this->addProfilePrefix($prefix, $path);
0409         }
0410         return $this;
0411     }
0412 
0413     /**
0414      * Get build profile dependency prefixes
0415      *
0416      * @return array
0417      */
0418     public function getProfilePrefixes()
0419     {
0420         $layerName = $this->getLayerName();
0421         if (null !== $layerName) {
0422             $prefix    = $this->_getPrefix($layerName);
0423             if (!array_key_exists($prefix, $this->_profilePrefixes)) {
0424                 $this->addProfilePrefix($prefix);
0425             }
0426         }
0427         $view = $this->getView();
0428         if (!empty($view)) {
0429             $helper = $this->getDojoHelper();
0430             if ($helper) {
0431                 $modules = $helper->getModules();
0432                 foreach ($modules as $module) {
0433                     $prefix = $this->_getPrefix($module);
0434                     if (!array_key_exists($prefix, $this->_profilePrefixes)) {
0435                         $this->addProfilePrefix($prefix);
0436                     }
0437                 }
0438             }
0439         }
0440         return $this->_profilePrefixes;
0441     }
0442 
0443     /**
0444      * Generate module layer script
0445      *
0446      * @return string
0447      */
0448     public function generateLayerScript()
0449     {
0450         $helper        = $this->getDojoHelper();
0451         $layerName     = $this->getLayerName();
0452         $modulePaths   = $helper->getModulePaths();
0453         $modules       = $helper->getModules();
0454         $onLoadActions = $helper->getOnLoadActions();
0455         $javascript    = $helper->getJavascript();
0456 
0457         $content = 'dojo.provide("' . $layerName . '");' . "\n\n(function(){\n";
0458 
0459         foreach ($modulePaths as $module => $path) {
0460             $content .= sprintf("dojo.registerModulePath(\"%s\", \"%s\");\n", $module, $path);
0461         }
0462         foreach ($modules as $module) {
0463             $content .= sprintf("dojo.require(\"%s\");\n", $module);
0464         }
0465 
0466         if ($this->consumeOnLoad()) {
0467             foreach ($helper->getOnLoadActions() as $callback) {
0468                 $content .= sprintf("dojo.addOnLoad(%s);\n", $callback);
0469             }
0470         }
0471         if ($this->consumeJavascript()) {
0472             $javascript = implode("\n", $helper->getJavascript());
0473             if (!empty($javascript)) {
0474                 $content .= "\n" . $javascript . "\n";
0475             }
0476         }
0477 
0478         $content .= "})();";
0479 
0480         return $content;
0481     }
0482 
0483     /**
0484      * Generate build profile
0485      *
0486      * @return string
0487      */
0488     public function generateBuildProfile()
0489     {
0490         $profileOptions  = $this->getProfileOptions();
0491         $layerName       = $this->getLayerName();
0492         $layerScriptPath = $this->getLayerScriptPath();
0493         $profilePrefixes = $this->getProfilePrefixes();
0494 
0495         if (!array_key_exists('releaseName', $profileOptions)) {
0496             $profileOptions['releaseName'] = substr($layerName, 0, strpos($layerName, '.'));
0497         }
0498 
0499         $profile = $profileOptions;
0500         $profile['layers'] = array(array(
0501             'name'              => $layerScriptPath,
0502             'layerDependencies' => array(),
0503             'dependencies'      => array($layerName),
0504         ));
0505         $profile['prefixes'] = array_values($profilePrefixes);
0506 
0507         return 'dependencies = ' . $this->_filterJsonProfileToJavascript($profile) . ';';
0508     }
0509 
0510     /**
0511      * Retrieve module prefix
0512      *
0513      * @param  string $module
0514      * @return void
0515      */
0516     protected function _getPrefix($module)
0517     {
0518         $segments  = explode('.', $module, 2);
0519         return $segments[0];
0520     }
0521 
0522     /**
0523      * Filter a JSON build profile to JavaScript
0524      *
0525      * @param  string $profile
0526      * @return string
0527      */
0528     protected function _filterJsonProfileToJavascript($profile)
0529     {
0530         // require_once 'Zend/Json.php';
0531         $profile = Zend_Json::encode($profile);
0532         $profile = trim($profile, '"');
0533         $profile = preg_replace('/' . preg_quote('\\') . '/', '', $profile);
0534         return $profile;
0535     }
0536 }