Completed
Push — master ( 874c8c...541414 )
by Jean-Christophe
03:49
created

HtmlDropdown   C

Complexity

Total Complexity 68

Size/Duplication

Total Lines 328
Duplicated Lines 1.22 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 68
lcom 1
cbo 8
dl 4
loc 328
rs 5.6756
c 0
b 0
f 0

39 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 22 3
A getField() 0 3 1
A getDataField() 0 3 1
A addItem() 0 5 1
A addIcon() 0 5 1
A addIcons() 0 6 3
A insertItem() 0 8 1
A removeArrow() 0 6 2
A beforeAddItem() 0 15 4
A fromDatabaseObject() 0 3 1
A addInput() 0 6 2
A addSearchInputItem() 0 3 1
A addDividerItem() 0 3 1
A addHeaderItem() 0 3 1
A addCircularLabelItem() 0 3 1
A addMiniAvatarImageItem() 0 3 1
B addItems() 0 11 5
A getItem() 0 3 1
A count() 0 3 1
A asDropdown() 0 10 2
A setVertical() 0 3 1
A setInline() 0 3 1
A setSimple() 0 3 1
A asButton() 0 7 2
B asSelect() 0 13 5
A asSearch() 0 4 1
A setSelect() 0 17 4
A asSubmenu() 0 7 2
A setPointing() 0 3 1
A setValue() 0 4 1
B applyValue() 0 12 5
A run() 4 10 3
A setCompact() 0 3 1
A setAction() 0 3 1
A setFullTextSearch() 0 3 1
A compile() 0 4 1
A getInput() 0 3 1
A addAction() 0 3 1
A jsAddItem() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like HtmlDropdown often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HtmlDropdown, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Ajax\semantic\html\modules;
4
5
use Ajax\semantic\html\base\HtmlSemDoubleElement;
6
use Ajax\semantic\html\content\HtmlDropdownItem;
7
use Ajax\semantic\html\elements\HtmlIcon;
8
use Ajax\common\html\html5\HtmlInput;
9
use Ajax\service\JArray;
10
use Ajax\semantic\html\base\constants\Direction;
11
use Ajax\semantic\html\base\traits\LabeledIconTrait;
12
use Ajax\JsUtils;
13
use Ajax\semantic\html\collections\form\traits\FieldTrait;
14
15
class HtmlDropdown extends HtmlSemDoubleElement {
16
	use FieldTrait,LabeledIconTrait {
17
		addIcon as addIconP;
18
	}
19
	protected $mClass="menu";
20
	protected $mTagName="div";
21
	protected $items=array ();
22
	protected $_params=array("action"=>"nothing","on"=>"hover");
23
	protected $input;
24
	protected $value;
25
	protected $_associative;
26
	protected $_multiple;
27
28
	public function __construct($identifier, $value="", $items=array(),$associative=true) {
29
		parent::__construct($identifier, "div");
30
		$this->_template=include dirname(__FILE__).'/../templates/tplDropdown.php';
31
		$this->setProperty("class", "ui dropdown");
32
		$this->_multiple=false;
33
		$content=[];
34
		if(isset($value)){
35
			if($value instanceof HtmlSemDoubleElement){
36
				$text=$value;
37
			}else{
38
				$text=new HtmlSemDoubleElement("text-".$this->identifier,"div");
39
				$text->setClass("text");
40
				$this->setValue($value);
41
			}
42
			$content=["text"=>$text];
43
		}
44
		$content["arrow"]=new HtmlIcon("", "dropdown");
45
		$this->content=$content;
46
		$this->tagName="div";
47
		$this->_associative=$associative;
48
		$this->addItems($items);
49
	}
50
51
	public function getField(){
52
		return $this->input;
53
	}
54
55
	public function getDataField(){
56
		return $this->input;
57
	}
58
59
	public function addItem($item,$value=NULL,$image=NULL,$description=NULL){
60
		$itemO=$this->beforeAddItem($item,$value,$image,$description);
61
		$this->items[]=$itemO;
62
		return $itemO;
63
	}
64
65
	public function addIcon($icon,$before=true,$labeled=false){
66
		$this->removeArrow();
67
		$this->addIconP($icon,$before,$labeled);
68
		return $this->getElementById("text-".$this->identifier, $this->content)->setWrapAfter("");
69
	}
70
71
	public function addIcons($icons){
72
		$count=$this->count();
73
		for ($i=0;$i<\sizeof($icons) && $i<$count;$i++){
74
			$this->getItem($i)->addIcon($icons[$i]);
75
		}
76
	}
77
78
	/**
79
	 * Insert an item at a position
80
	 * @param mixed $item
81
	 * @param number $position
82
	 * @return \Ajax\semantic\html\content\HtmlDropdownItem|unknown
83
	 */
84
	public function insertItem($item,$position=0){
85
		$itemO=$this->beforeAddItem($item);
86
		 $start = array_slice($this->items, 0, $position);
87
		 $end = array_slice($this->items, $position);
88
		 $start[] = $item;
89
		 $this->items=array_merge($start, $end);
90
		 return $itemO;
91
	}
92
93
	protected function removeArrow(){
94
		if(\sizeof($this->content)>1){
95
			unset($this->content["arrow"]);
96
			$this->content=\array_values($this->content);
97
		}
98
	}
99
100
	protected function beforeAddItem($item,$value=NULL,$image=NULL,$description=NULL){
101
		$itemO=$item;
102
		if(\is_array($item)){
103
			$description=JArray::getValue($item, "description", 3);
104
			$value=JArray::getValue($item, "value", 1);
105
			$image=JArray::getValue($item, "image", 2);
106
			$item=JArray::getValue($item, "item", 0);
107
		}
108
		if(!$item instanceof HtmlDropdownItem){
109
			$itemO=new HtmlDropdownItem("dd-item-".$this->identifier."-".\sizeof($this->items),$item,$value,$image,$description);
110
		}elseif($itemO instanceof HtmlDropdownItem){
111
			$this->addToProperty("class", "vertical");
112
		}
113
		return $itemO;
114
	}
115
116
	/* (non-PHPdoc)
117
	 * @see \Ajax\bootstrap\html\base\BaseHtml::fromDatabaseObject()
118
	 */
119
	public function fromDatabaseObject($object, $function) {
120
		$this->addItem($function($object));
121
	}
122
123
	public function addInput($name){
124
		if(!isset($name))
125
			$name="input-".$this->identifier;
126
		$this->setAction("activate");
127
		$this->input=new HtmlInput($name,"hidden");
128
	}
129
130
	/**
131
	 * Adds a search input item
132
	 * @param string $placeHolder
133
	 * @param string $icon
134
	 * @return \Ajax\semantic\html\content\HtmlDropdownItem
135
	 */
136
	public function addSearchInputItem($placeHolder=NULL,$icon=NULL){
137
		return $this->addItem(HtmlDropdownItem::searchInput($placeHolder,$icon));
138
	}
139
140
	/**
141
	 * Adds a divider item
142
	 * @return \Ajax\semantic\html\content\HtmlDropdownItem
143
	 */
144
	public function addDividerItem(){
145
		return $this->addItem(HtmlDropdownItem::divider());
146
	}
147
148
	/**
149
	 * Adds an header item
150
	 * @param string $caption
151
	 * @param string $icon
152
	 * @return \Ajax\semantic\html\content\HtmlDropdownItem|unknown
153
	 */
154
	public function addHeaderItem($caption=NULL,$icon=NULL){
155
		return $this->addItem(HtmlDropdownItem::header($caption,$icon));
156
	}
157
158
	/**
159
	 * Adds an item with a circular label
160
	 * @param string $caption
161
	 * @param string $color
162
	 * @return \Ajax\semantic\html\content\HtmlDropdownItem|unknown
163
	 */
164
	public function addCircularLabelItem($caption,$color){
165
		return $this->addItem(HtmlDropdownItem::circular($caption, $color));
166
	}
167
168
	/**
169
	 * @param string $caption
170
	 * @param string $image
171
	 * @return \Ajax\semantic\html\content\HtmlDropdownItem
172
	 */
173
	public function addMiniAvatarImageItem($caption,$image){
174
		return $this->addItem(HtmlDropdownItem::avatar($caption, $image));
0 ignored issues
show
Documentation introduced by
$caption is of type string, but the function expects a object<Ajax\semantic\html\content\unknown>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$image is of type string, but the function expects a object<Ajax\semantic\html\content\unknown>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
175
	}
176
177
	public function addItems($items){
178
		if(\is_array($items) && $this->_associative){
179
			foreach ($items as $k=>$v){
180
				$this->addItem($v)->setData($k);
181
			}
182
		}else{
183
			foreach ($items as $item){
184
				$this->addItem($item);
185
			}
186
		}
187
	}
188
189
	public function getItem($index){
190
		return $this->items[$index];
191
	}
192
193
	/**
194
	 * @return int
195
	 */
196
	public function count(){
197
		return \sizeof($this->items);
198
	}
199
	/**
200
	 * @param boolean $dropdown
201
	 */
202
	public function asDropdown($dropdown){
203
		if($dropdown===false){
204
			$this->_template=include dirname(__FILE__).'/../templates/tplDropdownMenu.php';
205
			$dropdown="menu";
206
		}else{
207
			$dropdown="dropdown";
208
			$this->mClass="menu";
209
		}
210
		return $this->addToPropertyCtrl("class", $dropdown,array("menu","dropdown"));
211
	}
212
213
	public function setVertical(){
214
		return $this->addToPropertyCtrl("class", "vertical",array("vertical"));
215
	}
216
217
	public function setInline(){
218
		return $this->addToPropertyCtrl("class", "inline",["inline"]);
219
	}
220
221
	public function setSimple(){
222
		return $this->addToPropertyCtrl("class", "simple",array("simple"));
223
	}
224
225
	public function asButton($floating=false){
226
		$this->removeArrow();
227
		if($floating)
228
			$this->addToProperty("class", "floating");
229
		$this->removePropertyValue("class", "selection");
230
		return $this->addToProperty("class", "button");
231
	}
232
233
	public function asSelect($name=NULL,$multiple=false,$selection=true){
234
		$this->_multiple=$multiple;
235
		if(isset($name))
236
			$this->addInput($name);
237
		if($multiple){
238
			$this->addToProperty("class", "multiple");
239
		}
240
		if ($selection){
241
			if($this->propertyContains("class", "button")===false)
242
				$this->addToPropertyCtrl("class", "selection",array("selection"));
243
		}
244
		return $this;
245
	}
246
247
	public function asSearch($name=NULL,$multiple=false,$selection=true){
248
		$this->asSelect($name,$multiple,$selection);
249
		return $this->addToProperty("class", "search");
250
	}
251
252
	public function setSelect($name=NULL,$multiple=false){
253
		if(!isset($name))
254
			$name="select-".$this->identifier;
255
		$this->input=null;
256
		if($multiple){
257
			$this->setProperty("multiple", true);
258
			$this->addToProperty("class", "multiple");
259
		}
260
		$this->setAction("activate");
261
		$this->tagName="select";
262
		$this->setProperty("name", $name);
263
		$this->content=null;
264
		foreach ($this->items as $item){
265
			$item->asOption();
266
		}
267
		return $this;
268
	}
269
270
	public function asSubmenu($pointing=NULL){
271
		$this->setClass("ui dropdown link item");
272
		if(isset($pointing)){
273
			$this->setPointing($pointing);
274
		}
275
		return $this;
276
	}
277
278
	public function setPointing($value=Direction::NONE){
279
		return $this->addToPropertyCtrl("class", $value." pointing",Direction::getConstantValues("pointing"));
280
	}
281
282
	public function setValue($value){
283
		$this->value=$value;
284
		return $this;
285
	}
286
	private function applyValue(){
287
		$value=$this->value;
288
		if(isset($this->input) && isset($value)){
289
			$this->input->setProperty("value", $value);
290
		}else{
291
			$this->setProperty("value", $value);
292
		}
293
			$textElement=$this->getElementById("text-".$this->identifier, $this->content);
294
			if(isset($textElement) && !$this->_multiple)
295
				$textElement->setContent($value);
296
		return $this;
297
	}
298
299
	/*
300
	 * (non-PHPdoc)
301
	 * @see BaseHtml::run()
302
	 */
303
	public function run(JsUtils $js) {
304
		if($this->propertyContains("class", "simple")===false){
305 View Code Duplication
			if(isset($this->_bsComponent)===false){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
				$this->_bsComponent=$js->semantic()->dropdown("#".$this->identifier,$this->_params);
307
				$this->_bsComponent->setItemSelector(".item");
308
			}
309
			$this->addEventsOnRun($js);
310
			return $this->_bsComponent;
311
		}
312
	}
313
314
	public function setCompact(){
315
		return $this->addToPropertyCtrl("class", "compact", array("compact"));
316
	}
317
318
	public function setAction($action){
319
		$this->_params["action"]=$action;
320
	}
321
322
	public function setFullTextSearch($value){
323
		$this->_params["fullTextSearch"]=$value;
324
	}
325
326
	public function compile(JsUtils $js=NULL, &$view=NULL) {
327
		$this->applyValue();
328
		return parent::compile($js,$view);
329
	}
330
331
	public function getInput() {
332
		return $this->input;
333
	}
334
	public function addAction($action, $direction=Direction::RIGHT, $icon=NULL, $labeled=false) {
335
		return $this->_addAction($this, $action,$direction,$icon,$labeled);
0 ignored issues
show
Bug introduced by
The method _addAction() does not exist on Ajax\semantic\html\modules\HtmlDropdown. Did you maybe mean addAction()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
336
	}
337
338
	public function jsAddItem($caption){
339
		$js="var first=$('#{$this->identifier} .item').first();if(first!=undefined){var new =first.clone();first.parent().append(new);first.html('{$caption}};')";
340
		return $js;
341
	}
342
}
343