1<?php
2/**
3 * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
4 *
5 * This source code is licensed under the GPL license found in the
6 * COPYING  file in the root directory of this source tree.
7 *
8 * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
9 * @author   ComboStrap <support@combostrap.com>
10 *
11 */
12
13namespace ComboStrap;
14
15
16class Snippet
17{
18    /**
19     * The head in css format
20     * We need to add the style node
21     */
22    const TYPE_CSS = "css";
23
24    /**
25     * The snippet is attached to a bar (main, sidebar, ...) or to the page (request)
26     */
27    const SCOPE_BAR = "bar";
28    const SCOPE_PAGE = "page";
29    /**
30     * The head in javascript
31     * We need to wrap it in a script node
32     */
33    const TYPE_JS = "js";
34    /**
35     * A tag head in array format
36     * No need
37     */
38    const TAG_TYPE = "tag";
39
40    private $snippetId;
41    private $scope;
42    private $type;
43
44    /**
45     * @var bool
46     */
47    private $critical = false;
48
49    /**
50     * @var string the text script / style (may be null if it's an external resources)
51     */
52    private $content;
53    /**
54     * @var array
55     */
56    private $headsTags;
57
58    /**
59     * Snippet constructor.
60     */
61    public function __construct($snippetId, $snippetType)
62    {
63        $this->snippetId = $snippetId;
64        $this->type = $snippetType;
65        if ($this->type == self::TYPE_CSS) {
66            // All CSS should be loaded first
67            // The CSS animation / background can set this to false
68            $this->critical = true;
69        }
70    }
71
72    public static function createJavascriptSnippet($snippetId)
73    {
74         return new Snippet($snippetId,self::TYPE_JS);
75    }
76
77    public static function createCssSnippet($snippetId)
78    {
79        return new Snippet($snippetId,self::TYPE_CSS);
80    }
81
82    /**
83     * @deprecated You should create a snippet with a known type, this constructor was created for refactoring
84     * @param $snippetId
85     * @return Snippet
86     */
87    public static function createUnknownSnippet($snippetId)
88    {
89        return new Snippet($snippetId,"unknwon");
90    }
91
92
93    /**
94     * @param $bool - if the snippet is critical, it would not be deferred or preloaded
95     * @return Snippet for chaining
96     * All css that are for animation or background for instance
97     * should not be set as critical as they are not needed to paint
98     * exactly the page
99     */
100    public function setCritical($bool)
101    {
102        $this->critical = $bool;
103        return $this;
104    }
105
106    /**
107     * @param $content - Set an inline content for a script or stylesheet
108     * @return Snippet for chaining
109     */
110    public function setContent($content)
111    {
112        $this->content = $content;
113        return $this;
114    }
115
116    /**
117     * @return string
118     */
119    public function getContent()
120    {
121        if ($this->content == null) {
122            switch ($this->type) {
123                case self::TYPE_CSS:
124                    $this->content = $this->getCssRulesFromFile($this->snippetId);
125                    break;
126                case self::TYPE_JS:
127                    $this->content = $this->getJavascriptContentFromFile($this->snippetId);
128                    break;
129                default:
130                    LogUtility::msg("The snippet ($this) has no content", LogUtility::LVL_MSG_ERROR, "support");
131            }
132        }
133        return $this->content;
134    }
135
136    /**
137     * @param $tagName
138     * @return false|string - the css content of the css file
139     */
140    private function getCssRulesFromFile($tagName)
141    {
142
143        $path = Resources::getSnippetResourceDirectory() . "/style/" . strtolower($tagName) . ".css";
144        if (file_exists($path)) {
145            return file_get_contents($path);
146        } else {
147            LogUtility::msg("The css file ($path) was not found", LogUtility::LVL_MSG_WARNING, $tagName);
148            return "";
149        }
150
151    }
152
153    /**
154     * @param $tagName - the tag name
155     * @return false|string - the specific javascript content for the tag
156     */
157    private function getJavascriptContentFromFile($tagName)
158    {
159
160        $path = Resources::getSnippetResourceDirectory() . "/js/" . strtolower($tagName) . ".js";
161        if (file_exists($path)) {
162            return file_get_contents($path);
163        } else {
164            LogUtility::msg("The javascript file ($path) was not found", LogUtility::LVL_MSG_WARNING, $tagName);
165            return "";
166        }
167
168    }
169
170    public function __toString()
171    {
172        return $this->snippetId."-".$this->type;
173    }
174
175    /**
176     * Set all tags at once.
177     * @param array $tags
178     * @return Snippet
179     */
180    public function setTags(array $tags)
181    {
182        $this->headsTags = $tags;
183        return $this;
184    }
185
186    public function getTags()
187    {
188        return $this->headsTags;
189    }
190
191    public function getCritical()
192    {
193        return $this->critical;
194    }
195
196    public function getClass()
197    {
198        /**
199         * The class for the snippet is just to be able to identify them
200         *
201         * The `snippet` prefix was added to be sure that the class
202         * name will not conflict with a css class
203         * Example: if you set the class to `combo-list`
204         * and that you use it in a inline `style` tag with
205         * the same class name, the inline `style` tag is not applied
206         *
207         */
208        return "snippet-" . $this->snippetId . "-" . SnippetManager::COMBO_CLASS_SUFFIX;
209
210    }
211
212    /**
213     * @return string the HTML of the tag (works for now only with CSS content)
214     */
215    public function getHtmlStyleTag()
216    {
217        $content = $this->getContent();
218        $class = $this->getClass();
219        return <<<EOF
220<style class="$class">
221$content
222</style>
223EOF;
224
225    }
226
227    public function getId()
228    {
229        return $this->snippetId;
230    }
231
232
233}
234