Pell   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 28
eloc 70
c 3
b 0
f 0
dl 0
loc 240
ccs 77
cts 77
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A hasDefaultValue() 0 3 1
A renderWidget() 0 13 2
A getTextAreaId() 0 3 1
A getWrapperId() 0 4 1
A run() 0 5 1
A getDefaultValue() 0 9 2
C init() 0 37 12
A getWrapperClass() 0 3 1
A registerClientScript() 0 5 1
A getWrapperTag() 0 3 1
A getTextArea() 0 11 2
A buildClientScript() 0 39 3
1
<?php
2
/**
3
 * @copyright Copyright (c) 2013-2019 Sergio Coderius!
4
 * @link https://coderius.biz.ua
5
 * @license This program is free software: the MIT License (MIT)
6
 * @author Sergio Coderius <[email protected]>
7
 */
8
9
namespace coderius\pell;
10
11
use yii\helpers\Html;
12
use yii\helpers\Json;
13
use coderius\pell\PellInputWidget;
14
use yii\web\View;
15
use yii\web\JsExpression;
16
use yii\helpers\ArrayHelper;
17
use yii\base\InvalidArgumentException;
18
19
/**
20
 * Pell widget renders a pell js plugin for WYSIWYG editing.
21
 * 
22
 * For example to use the Pell widget with a [[\yii\base\Model|model]]:
23
 *
24
 * ```php
25
 * echo Pell::widget([
26
 *     'model' => $model,
27
 *     'attribute' => 'text',
28
 * ]);
29
 * ```
30
 *
31
 * The following example will used not as an element of form:
32
 *
33
 * ```php
34
 * echo Pell::widget([
35
 *     'asFormPart'  => false,
36
 *     'value'  => $value,
37
 * ]);
38
 * ```
39
 * 
40
 * You can also use this widget in an [[\yii\widgets\ActiveForm|ActiveForm]] using the [[\yii\widgets\ActiveField::widget()|widget()]]
41
 * method, for example like this:
42
 * 
43
 * ```php
44
 * use coderius\pell\Pell;
45
 * 
46
 * <?= $form->field($model, 'text')->widget(Pell::className(), []);?>
47
 * ```
48
 * 
49
 * Note that if Pell widget using inside form - [Pell::asFormPart] must be set to true (by default)
50
 * 
51
 * @author Sergio Coderius <[email protected]>
52
 */ 
53
class Pell extends PellInputWidget
54
{
55
    /**
56
     * If widget used inside form, then param `$asFormPart` must be set as true and tag textarea (hidden) must be created
57
     *
58
     * @var boolean
59
     */
60
    public $asFormPart = true;
61
62
    /**
63
     * Container tag for widget
64
     *
65
     * @var array
66
     */
67
    public $wrapperOptions = [];
68
    
69
    /**
70
     * @var array the options for the Pell JS plugin.
71
     * Please refer to the Pell JS plugin Web page for possible options.
72
     * @see https://github.com/jaredreich/pell/blob/master/README.md
73
     */
74
    public $clientOptions = [];
75
76
    protected $clientPluginInstanceName = 'pell';
77
78
    /**
79
     * Initializes the Pell widget.
80
     * This method will initialize required property values and create wrapper html tag.
81
     * @return void
82
     */
83 27
    public function init()
84
    {
85 27
        parent::init();
86
87
        //Attribute id in textarea must be set
88 27
        if(!isset($this->inputOptions['id']) && true === $this->asFormPart){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
89 15
            $pell = $this->clientPluginInstanceName;
90 15
            $this->inputOptions['id'] = "$pell-textarea-" . $this->getId();
91
        }
92
93
        //In Html::textarea attribute name must be set
94 27
        if(!$this->name && true === $this->asFormPart && false === $this->hasModel()){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
95 3
            throw new InvalidArgumentException("Param 'name' must be specified for textarea attribute name.");
96
        }
97
98 24
        if(!isset($this->wrapperOptions['tag'])){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
99 24
            $this->wrapperOptions['tag'] = 'div';
100
        }
101
102 24
        if(!isset($this->wrapperOptions['id'])){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
103 24
            $this->wrapperOptions['id'] = $this->getWrapperId();
104
        }
105
106 24
        if(!isset($this->wrapperOptions['class'])){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
107
            //From yii\widgets\ActiveField [$inputOptions = ['class' => 'form-control']]
108 24
            $this->wrapperOptions['class'] = 'form-control';
109
110 24
            Html::addCssStyle($this->wrapperOptions, 'height: auto', false);
111
        }
112
113
        //If widget is used as part of the form, setting [clientOptions['onChange']] from widget options not allowed
114 24
        if(isset($this->clientOptions['onChange']) && $this->asFormPart){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
115 3
            throw new InvalidArgumentException("Param 'onChange' cannot be specified if the widget is used as part of the form.");
116
        }
117
118 21
        if(isset($this->clientOptions['element'])){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
119 3
            throw new InvalidArgumentException("Param 'element' cannot be specified. This param set by widget.");
120
        }
121 18
    }
122
123
    /**
124
     * Runs the widget.
125
     */
126 9
    public function run()
127
    {
128 9
        echo $this->renderWidget() . "\n";
129 9
        $js = $this->buildClientScript();
130 9
        $this->registerClientScript($js);
131 9
    }
132
133
    /**
134
     * Renders the Pell widget.
135
     * @return string the rendering result.
136
     */
137 9
    protected function renderWidget()
138
    {
139 9
        $content = null;
140
141
        //If [$this->asFormPart === true] then create hidden textarea inside wrapper to seva passed data
142 9
        if($this->asFormPart){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
143 9
            $content = $this->getTextArea();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $content is correct as $this->getTextArea() targeting coderius\pell\Pell::getTextArea() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
144
        }
145
        
146 9
        $tag = $this->getWrapperTag();
147 9
        ArrayHelper::remove($this->wrapperOptions, 'tag');
148
        
149 9
        return Html::tag($tag, $content, $this->wrapperOptions);
150
    }    
151
152
    /**
153
     * Build js script
154
     *
155
     * @return string
156
     */
157 15
    protected function buildClientScript()
158
    {
159 15
        $js = [];
160 15
        $wrapperId = $this->getWrapperId();
161 15
        $element = new JsExpression("document.getElementById('$wrapperId')");
162
163 15
        $this->clientOptions['element'] = $element;
164
        
165
        //If widget used inside form and needed generete [Html::textarea] or [Html::activeTextarea]
166 15
        if($this->asFormPart){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
167 15
            $textAreaId = $this->getTextAreaId();
168
            //Write content from editor to hidden texarea when that was changed
169 15
            $this->clientOptions['onChange'] = new JsExpression(
170
            "html => {
171 15
    document.getElementById('$textAreaId').innerHTML = html;
172
},"
173
            );
174
        }
175
        
176 15
        $clientOptions = Json::encode($this->clientOptions);
177 15
        $pell = $this->clientPluginInstanceName;
178
179
        //Editor js instance constant name
180 15
        $editorJsVar = "{$pell}Editor_" . $this->getId();
181
182
        //Init plugin javascript
183 15
        $js[] = "";//set \n
184 15
        $js[] = "const $editorJsVar = $pell.init($clientOptions);";
185
        
186
        //If isset default value like value from db, or if set `$this->value`
187 15
        if($this->hasDefaultValue()){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
188 3
            $defVal = $this->getDefaultValue();
189
190
            //Pass value to editor
191 3
            $js[] = new JsExpression("$editorJsVar.content.innerHTML = `$defVal`");
192
        }
193 15
        $js[] = "";//set \n
194
195 15
        return implode("\n", $js);
196
    }    
197
198
    /**
199
     * Registers Pell js plugin
200
     * @param string $js the JS code block to be registered
201
     * @param string $key the key that identifies the JS code block. If null, it will use
202
     * $js as the key. If two JS code blocks are registered with the same key, the latter
203
     * will overwrite the former.
204
     * 
205
     * @return void
206
     */
207 12
    protected function registerClientScript($js, $key = null)
208
    {
209 12
        $view = $this->getView();
210 12
        PellAsset::register($view);
211 12
        $view->registerJs($js, View::POS_END, $key);
212 12
    }
213
214
    /**
215
     * Generates a textarea tag for the given model attribute.
216
     * The model attribute value will be used as the content in the textarea.
217
     *
218
     * @return void
219
     */
220 9
    protected function getTextArea()
221
    {
222 9
        Html::addCssStyle($this->inputOptions, 'display:none;');
223
224 9
        if ($this->hasModel()) {
225 6
            $textarea = Html::activeTextarea($this->model, $this->attribute, $this->inputOptions);
226
        } else {
227 3
            $textarea = Html::textarea($this->name, $this->value, $this->inputOptions);
228
        }
229
230 9
        return $textarea;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $textarea returns the type string which is incompatible with the documented return type void.
Loading history...
231
    }
232
233
    /**
234
     * Return wrapper id for wrapper tag id attribute
235
     *
236
     * @return string
237
     */
238 24
    public function getWrapperId()
239
    {
240 24
        $pell = $this->clientPluginInstanceName;
241 24
        return "$pell-wrap-". $this->getId();
242
    }
243
244 3
    public function getWrapperClass()
245
    {
246 3
        return $this->wrapperOptions['class'];
247
    }
248
249
    /**
250
     * Return a textarea attribute id
251
     *
252
     * @return string
253
     */
254 18
    public function getTextAreaId()
255
    {
256 18
        return $this->inputOptions['id'];
257
    }
258
259
    /**
260
     * Return wrapper tag name
261
     *
262
     * @return string
263
     */
264 12
    public function getWrapperTag()
265
    {
266 12
        return $this->wrapperOptions['tag'];
267
    }
268
    
269
    /**
270
     * Return default value returned of model attribute if exists or by passed to `inputOptions['value']`
271
     *
272
     * @return string
273
     */
274 15
    protected function getDefaultValue()
275
    {
276 15
        if ($this->hasModel()) {
277 12
            $value = Html::getAttributeValue($this->model, $this->attribute);
278
        } else {
279 3
            $value = $this->value;
280
        }
281
282 15
        return $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value also could return the type array which is incompatible with the documented return type string.
Loading history...
283
    }
284
285
    /**
286
     * Check is default value is not empty
287
     *
288
     * @return boolean
289
     */
290 15
    public function hasDefaultValue()
291
    {
292 15
        return !empty($this->getDefaultValue());
293
    }
294
}
295