Back to WebAssetManager class

Method calculateOrderOfActiveAssets

protected \Joomla\CMS\WebAsset\WebAssetItem[]
calculateOrderOfActiveAssets
(mixed $type)
Calculate weight of active Assets, by its Dependencies
Parameters
  • string $type The asset type, script or style
Returns
  • \Joomla\CMS\WebAsset\WebAssetItem[]
Since
  • 4.0.0

Method calculateOrderOfActiveAssets - Source code

/**
 * Calculate weight of active Assets, by its Dependencies
 *
 * @param   string  $type  The asset type, script or style
 *
 * @return  WebAssetItem[]
 *
 * @since  4.0.0
 */
protected function calculateOrderOfActiveAssets($type) : array
{
    // See https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
    $graphOrder = [];
    $activeAssets = $this->getAssets($type, false);
    // Get Graph of Outgoing and Incoming connections
    $connectionsGraph = $this->getConnectionsGraph($activeAssets);
    $graphOutgoing = $connectionsGraph['outgoing'];
    $graphIncoming = $connectionsGraph['incoming'];
    // Make a copy to be used during weight processing
    $graphIncomingCopy = $graphIncoming;
    // Find items without incoming connections
    $emptyIncoming = array_keys(array_filter($graphIncoming, function ($el) {
        return !$el;
    }));
    // Reverse, to start from a last enabled and move up to a first enabled, this helps to maintain an original sorting
    $emptyIncoming = array_reverse($emptyIncoming);
    // Loop through, and sort the graph
    while ($emptyIncoming) {
        // Add the node without incoming connection to the result
        $item = array_shift($emptyIncoming);
        $graphOrder[] = $item;
        // Check of each neighbor of the node
        foreach (array_reverse($graphOutgoing[$item]) as $neighbor) {
            // Remove incoming connection of already visited node
            unset($graphIncoming[$neighbor][$item]);
            // If there no more incoming connections add the node to queue
            if (empty($graphIncoming[$neighbor])) {
                $emptyIncoming[] = $neighbor;
            }
        }
    }
    // Sync Graph order with FIFO order
    $fifoWeights = [];
    $graphWeights = [];
    $requestedWeights = [];
    foreach (array_keys($this->activeAssets[$type]) as $index => $name) {
        $fifoWeights[$name] = $index * 10 + 10;
    }
    foreach (array_reverse($graphOrder) as $index => $name) {
        $graphWeights[$name] = $index * 10 + 10;
        $requestedWeights[$name] = $activeAssets[$name]->getOption('weight') ?: $fifoWeights[$name];
    }
    // Try to set a requested weight, or make it close as possible to requested, but keep the Graph order
    while ($requestedWeights) {
        $item = key($requestedWeights);
        $weight = array_shift($requestedWeights);
        // Skip empty items
        if ($weight === null) {
            continue;
        }
        // Check the predecessors (Outgoing vertexes), the weight cannot be lighter than the predecessor have
        $topBorder = $weight - 1;
        if (!empty($graphOutgoing[$item])) {
            $prevWeights = [];
            foreach ($graphOutgoing[$item] as $pItem) {
                $prevWeights[] = $graphWeights[$pItem];
            }
            $topBorder = max($prevWeights);
        }
        // Calculate a new weight
        $newWeight = $weight > $topBorder ? $weight : $topBorder + 1;
        // If a new weight heavier than existing, then we need to update all incoming connections (children)
        if ($newWeight > $graphWeights[$item] && !empty($graphIncomingCopy[$item])) {
            // Sort Graph of incoming by actual position
            foreach ($graphIncomingCopy[$item] as $incomingItem) {
                // Set a weight heavier than current, then this node to be processed in next iteration
                if (empty($requestedWeights[$incomingItem])) {
                    $requestedWeights[$incomingItem] = $graphWeights[$incomingItem] + $newWeight;
                }
            }
        }
        // Set a new weight
        $graphWeights[$item] = $newWeight;
    }
    asort($graphWeights);
    // Get Assets in calculated order
    $resultAssets = [];
    foreach (array_keys($graphWeights) as $name) {
        $resultAssets[$name] = $activeAssets[$name];
    }
    return $resultAssets;
}