1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace Kotchasan;
12:
13: use Kotchasan\Http\Uri;
14:
15: 16: 17: 18: 19: 20: 21:
22: class DataTable extends \Kotchasan\KBase
23: {
24: 25: 26: 27: 28:
29: private $id;
30: 31: 32: 33: 34:
35: private $model;
36: 37: 38: 39: 40: 41:
42: private $datas;
43: 44: 45: 46: 47:
48: private $cache = false;
49: 50: 51: 52: 53:
54: private $fields = array();
55: 56: 57: 58: 59: 60:
61: private $checkCol = -1;
62: 63: 64: 65: 66: 67:
68: public $hideCheckbox = false;
69: 70: 71: 72: 73:
74: private $fullWidth = true;
75: 76: 77: 78: 79:
80: private $border = false;
81: 82: 83: 84: 85:
86: private $pmButton = false;
87: 88: 89: 90: 91:
92: private $responsive = false;
93: 94: 95: 96: 97:
98: private $showCaption = true;
99: 100: 101: 102: 103: 104:
105: private $action;
106: 107: 108: 109: 110: 111: 112:
113: public $actions = array();
114: 115: 116: 117: 118: 119:
120: private $actionCallback;
121: 122: 123: 124: 125: 126:
127: private $actionConfirm;
128: 129: 130: 131: 132: 133: 134: 135: 136:
137: private $onRow;
138: 139: 140: 141: 142: 143: 144:
145: private $onBeforeDelete;
146: 147: 148: 149: 150: 151:
152: private $onDelete;
153: 154: 155: 156: 157: 158: 159:
160: private $onAddRow;
161: 162: 163: 164: 165: 166:
167: private $onInitRow;
168: 169: 170: 171: 172: 173: 174: 175:
176: public $defaultFilters = array();
177: 178: 179: 180: 181: 182:
183: public $filters = array();
184: 185: 186: 187: 188:
189: public $hideColumns = array();
190: 191: 192: 193: 194:
195: public $columns = array();
196: 197: 198: 199: 200:
201: public $headers = array();
202: 203: 204: 205: 206: 207:
208: public $searchColumns = array();
209: 210: 211: 212: 213: 214: 215:
216: public $autoSearch = true;
217: 218: 219: 220: 221: 222: 223: 224:
225: public $searchForm = 'auto';
226: 227: 228: 229: 230:
231: public $search = '';
232: 233: 234: 235: 236: 237:
238: public $perPage = null;
239: 240: 241: 242: 243:
244: public $page = 1;
245: 246: 247: 248: 249: 250:
251: public $sort = null;
252: 253: 254: 255: 256: 257:
258: public $defaultSort = null;
259: 260: 261: 262: 263:
264: protected $sorts = array();
265: 266: 267: 268: 269:
270: public $buttons = array();
271: 272: 273: 274: 275: 276: 277: 278: 279: 280:
281: private $onCreateButton;
282: 283: 284: 285: 286: 287: 288:
289: private $onCreateHeader;
290: 291: 292: 293: 294: 295: 296:
297: private $onCreateFooter;
298: 299: 300: 301: 302:
303: private $dragColumn = -1;
304: 305: 306: 307: 308: 309:
310: private $primaryKey = 'id';
311: 312: 313: 314: 315:
316: private $javascript = array();
317: 318: 319: 320: 321: 322: 323:
324: public $enableJavascript = true;
325: 326: 327: 328: 329:
330: private $uri;
331: 332: 333: 334: 335:
336: public $entriesList = array(10, 20, 30, 40, 50, 100);
337: 338: 339: 340: 341:
342: private $debug = false;
343: 344: 345: 346: 347:
348: private $explain = false;
349:
350: 351: 352: 353: 354:
355: public function __construct($param)
356: {
357: $this->id = 'datatable';
358: foreach ($param as $key => $value) {
359: $this->{$key} = $value;
360: }
361: if (empty($this->uri)) {
362: $this->uri = self::$request->getUri();
363: } elseif (is_string($this->uri)) {
364: $this->uri = Uri::createFromUri($this->uri);
365: }
366:
367: if ($this->perPage !== null) {
368: $count = self::$request->globals(array('POST', 'GET'), 'count', $this->perPage)->toInt();
369: if (in_array($count, $this->entriesList)) {
370: $this->perPage = $count;
371: $this->uri = $this->uri->withParams(array('count' => $count));
372: }
373: }
374:
375: if (isset($this->model)) {
376:
377: $model = new \Kotchasan\Model();
378: $model = $model->db()->createQuery()->select();
379:
380: if (is_string($this->model)) {
381:
382: $rs = new \Kotchasan\Orm\Recordset($this->model);
383:
384: $this->model = $model->from(array($rs->toQueryBuilder(), 'Z9'));
385: } else {
386: $this->model = $model->from(array($this->model, 'Z9'));
387: }
388:
389: if ($this->explain) {
390: $first = $this->model->copy()->explain()->first();
391: } else {
392: $first = $this->model->copy()->first($this->fields);
393: }
394:
395: if ($first) {
396: foreach ($first as $k => $v) {
397: $this->columns[$k] = array('text' => $k);
398: }
399: } elseif (!empty($this->fields)) {
400: foreach ($this->fields as $field) {
401: if (is_array($field)) {
402: $this->columns[$field[1]] = array('text' => $field[1]);
403: } elseif (is_string($field) && preg_match('/(.*?[`\s]+)?([a-z0-9_]+)`?$/i', $field, $match)) {
404: $this->columns[$match[2]] = array('text' => $match[2]);
405: }
406: }
407: }
408: } elseif (isset($this->datas)) {
409:
410: $this->columns = array();
411: if (!empty($this->datas)) {
412: foreach (reset($this->datas) as $key => $value) {
413: $this->columns[$key] = array('text' => $key);
414: }
415: }
416: }
417:
418: $autoSort = false;
419:
420: if (!empty($this->columns)) {
421: $headers = array();
422: foreach ($this->columns as $field => $attributes) {
423: if (!in_array($field, $this->hideColumns)) {
424: if (isset($this->headers[$field])) {
425: $headers[$field] = $this->headers[$field];
426: if (!isset($headers[$field]['text'])) {
427: $headers[$field]['text'] = $field;
428: }
429: if (isset($headers[$field]['sort'])) {
430: $autoSort = true;
431: }
432: } else {
433: $headers[$field]['text'] = $field;
434: }
435: }
436: }
437: $this->headers = $headers;
438: }
439:
440: if ($autoSort) {
441: $this->sort = self::$request->globals(array('POST', 'GET'), 'sort', $this->sort)->topic();
442: }
443: if (!empty($this->sort)) {
444: $this->uri = $this->uri->withParams(array('sort' => $this->sort));
445: }
446:
447: $this->search = self::$request->globals(array('POST', 'GET'), 'search')->text();
448: }
449:
450: 451: 452: 453: 454:
455: public function script($script)
456: {
457: $this->javascript[] = $script;
458: }
459:
460: 461: 462: 463: 464: 465:
466: public function render()
467: {
468: if (!empty($this->actions) && $this->checkCol == -1) {
469: $this->checkCol = 1;
470: }
471: $url_query = array();
472: $hidden_fields = array();
473: $query_string = array();
474: parse_str($this->uri->getQuery(), $query_string);
475: self::$request->map($url_query, $query_string);
476: foreach ($url_query as $key => $value) {
477:
478: if (!preg_match('/.*?([0-9]+|username|password|token|time|search|count|page|action).*?/', $key)) {
479: $hidden_fields[$key] = '<input type="hidden" name="'.$key.'" value="'.htmlspecialchars($value).'">';
480: }
481: }
482: if (isset($this->model)) {
483:
484: $qs = array();
485: foreach ($this->defaultFilters as $array) {
486: $qs[] = $array;
487: }
488: }
489:
490: $content = array('<div class="datatable" id="'.$this->id.'">');
491:
492: $form = array();
493: if ($this->perPage !== null) {
494: $entries = Language::get('entries');
495: $options = array();
496: foreach ($this->entriesList as $c) {
497: if ($c == 0) {
498: $options[0] = Language::get('all items');
499: } else {
500: $options[$c] = $c.' '.$entries;
501: }
502: }
503: $form[] = $this->addFilter(array(
504: 'name' => 'count',
505: 'text' => Language::get('Show'),
506: 'value' => $this->perPage,
507: 'options' => $options,
508: ));
509: }
510:
511: foreach ($this->filters as $key => $items) {
512: $form[] = $this->addFilter($items);
513: if (isset($items['name'])) {
514: unset($hidden_fields[$items['name']]);
515: }
516: if (!isset($items['default'])) {
517: $items['default'] = '';
518: }
519:
520: if (!empty($items['options']) && isset($items['value']) && $items['value'] !== $items['default'] && in_array($items['value'], array_keys($items['options']), true)) {
521: if (isset($items['onFilter'])) {
522: $q = call_user_func($items['onFilter'], $key, $items['value']);
523: if ($q) {
524: $qs[] = $q;
525: }
526: } elseif (is_string($key)) {
527: $qs[] = array($key, $items['value']);
528: }
529: }
530: }
531: if ($this->model) {
532: if (!empty($qs)) {
533: $this->model->andWhere($qs);
534: }
535: }
536:
537: if (!empty($form)) {
538: $form[] = '<fieldset class=go>';
539: $form[] = '<button type=submit class="button go">'.Language::get('Go').'</button>';
540: $form[] = implode('', $hidden_fields);
541: $form[] = '</fieldset>';
542: }
543:
544: if ($this->searchForm === true || ($this->searchForm === 'auto' && !empty($this->searchColumns))) {
545: if (!empty($this->search)) {
546: if ($this->autoSearch) {
547: if (isset($this->model)) {
548: $sh = array();
549: foreach ($this->searchColumns as $key) {
550: $sh[] = array($key, 'LIKE', '%'.$this->search.'%');
551: }
552: $this->model->andWhere($sh, 'OR');
553: } elseif (isset($this->datas)) {
554:
555: $this->datas = ArrayTool::filter($this->datas, $this->search);
556: }
557: }
558: $this->uri = $this->uri->withParams(array('search' => $this->search));
559: }
560: $form[] = '</div>';
561: $form[] = '<div class="table_search">';
562: $form[] = '<fieldset class=search>';
563: $form[] = '<label accesskey=f><input type=text name=search value="'.$this->search.'" placeholder="'.Language::get('Search').'"></label>';
564: $form[] = '<button type=submit></button>';
565: $form[] = '<button type=submit class=clear_search>x</button>';
566: $form[] = '</fieldset>';
567: }
568: if (!$this->explain && !empty($form)) {
569: $content[] = '<form class="table_nav clear" method="get" action="'.$this->uri.'"><div>'.implode('', $form).'</div></form>';
570: }
571: if (isset($this->model)) {
572: if ($this->explain) {
573:
574: $count = 0;
575: } else {
576:
577: $this->model->select($this->fields);
578:
579: $model = new \Kotchasan\Model();
580: $query = $model->db()->createQuery()
581: ->selectCount()
582: ->from(array($this->model, 'Z'));
583: if ($this->cache) {
584: $query->cacheOn();
585: }
586: $result = $query->toArray()->execute();
587: $count = empty($result) ? 0 : $result[0]['count'];
588: }
589: } elseif (!empty($this->datas)) {
590:
591: $count = count($this->datas);
592: } else {
593:
594: $count = 0;
595: }
596:
597: if ($this->perPage > 0) {
598:
599: $this->page = max(1, self::$request->globals(array('POST', 'GET'), 'page', 1)->toInt());
600:
601: $totalpage = round($count / $this->perPage);
602: $totalpage += ($totalpage * $this->perPage < $count) ? 1 : 0;
603: $this->page = max(1, $this->page > $totalpage ? $totalpage : $this->page);
604: $start = $this->perPage * ($this->page - 1);
605:
606: $s = $start < 0 ? 0 : $start + 1;
607: $e = min($count, $s + $this->perPage - 1);
608: } else {
609: $start = 0;
610: $totalpage = 1;
611: $this->page = 1;
612: $s = 1;
613: $e = $count;
614: $this->perPage = 0;
615: }
616:
617: if ($this->showCaption) {
618: if (empty($this->search)) {
619: $caption = Language::get('All :count entries, displayed :start to :end, page :page of :total pages');
620: } else {
621: $caption = Language::get('Search <strong>:search</strong> found :count entries, displayed :start to :end, page :page of :total pages');
622: }
623: $caption = str_replace(array(':search', ':count', ':start', ':end', ':page', ':total'), array($this->search, number_format($count), number_format($s), number_format($e), number_format($this->page), number_format($totalpage)), $caption);
624: }
625:
626: $orders = array();
627: if (!empty($this->defaultSort)) {
628: foreach (explode(',', $this->defaultSort) as $sort) {
629: if (preg_match('/^([a-z0-9_\-]+)([\s]+(desc|asc))?$/i', trim($sort), $match)) {
630: $sortType = isset($match[3]) && strtolower($match[3]) == 'desc' ? 'desc' : 'asc';
631: $orders[] = $match[1].' '.$sortType;
632: $this->sorts[$match[1]] = $sortType;
633: }
634: }
635: }
636: if (!empty($this->sort)) {
637: $sorts = array();
638: foreach (explode(',', $this->sort) as $sort) {
639: if (preg_match('/^([a-z0-9_\-]+)([\s]+(desc|asc))?$/i', trim($sort), $match)) {
640: if (isset($this->headers[$match[1]]['sort'])) {
641: $sort = $this->headers[$match[1]]['sort'];
642: } elseif (isset($this->columns[$match[1]])) {
643: $sort = $match[1];
644: } elseif ($this->model && isset($this->columns[$match[1]])) {
645: $sort = $match[1];
646: } else {
647: $sort = null;
648: }
649: if ($sort) {
650: $sortType = isset($match[3]) && strtolower($match[3]) == 'desc' ? 'desc' : 'asc';
651: $this->sorts[$sort] = $sortType;
652: $sorts[] = $sort.' '.$sortType;
653: }
654: }
655: }
656: $this->sort = implode(',', $sorts);
657: $orders = array_merge($orders, $sorts);
658: }
659:
660: if (isset($this->model)) {
661: if (!empty($orders)) {
662: $this->model->order($orders);
663: }
664: } elseif (!empty($this->sorts)) {
665: reset($this->sorts);
666: $sort = key($this->sorts);
667: $this->datas = ArrayTool::sort($this->datas, $sort, $this->sorts[$sort]);
668: }
669: if (isset($this->model)) {
670: if ($this->explain) {
671: $this->model->explain();
672: }
673: if ($this->debug === true) {
674:
675: $this->debug = $this->model->toArray()->limit($this->perPage, $start)->text();
676: }
677:
678: $this->datas = $this->model->toArray()->limit($this->perPage, $start)->execute();
679:
680: $end = $this->perPage + 1;
681:
682: $start = -1;
683: } elseif (isset($this->datas)) {
684: if ($this->debug === true) {
685:
686: $this->debug = var_export($this->datas, true);
687: }
688:
689: $end = $start + $this->perPage - 1;
690:
691: $start = $start - 2;
692: } else {
693: $end = 0;
694: }
695:
696: $prop = array();
697: $c = array();
698: if (isset($this->class)) {
699: $c[] = $this->class;
700: }
701: if ($this->border) {
702: $c[] = 'border';
703: }
704: if ($this->responsive) {
705: $c[] = 'responsive-v';
706: }
707: if ($this->fullWidth) {
708: $c[] = 'fullwidth';
709: }
710: if (count($c) > 0) {
711: $prop[] = ' class="'.implode(' ', $c).'"';
712: }
713:
714: $content[] = '<div class="tablebody"><table'.implode('', $prop).'>';
715: if ($this->showCaption) {
716: $content[] = '<caption>'.$caption.'</caption>';
717: }
718: $colCount = 0;
719:
720: $thead = null;
721: if (!$this->explain && isset($this->onCreateHeader)) {
722: $thead = call_user_func($this->onCreateHeader);
723: } elseif (!empty($this->headers)) {
724: $row = array();
725: $i = 0;
726: $colspan = 0;
727: foreach ($this->headers as $key => $attributes) {
728: if ($colspan === 0) {
729: if (!$this->explain) {
730: if (!$this->hideCheckbox && $i == $this->checkCol) {
731: $row[] = '<th class="check-column"><a class="checkall icon-uncheck"></a></th>';
732: ++$colCount;
733: }
734: if ($i == $this->dragColumn) {
735: $row[] = '<th></th>';
736: ++$colCount;
737: }
738: }
739: if (isset($attributes['colspan'])) {
740: $colspan = $attributes['colspan'] - 1;
741: }
742: $row[] = $this->th($i, $key, $attributes);
743: ++$i;
744: } else {
745: --$colspan;
746: }
747: ++$colCount;
748: }
749: if (!$this->explain && !$this->hideCheckbox && $colCount == $this->checkCol) {
750: $row[] = '<th class="check-column"><a class="checkall icon-uncheck"></a></th>';
751: ++$colCount;
752: }
753: if ($colspan === 0) {
754: if (!empty($this->buttons)) {
755: $row[] = $this->th($i, '', array('text' => ''));
756: ++$colCount;
757: ++$i;
758: }
759: } else {
760: --$colspan;
761: }
762: if ($colspan === 0) {
763: if ($this->pmButton) {
764: $row[] = $this->th($i, '', array('text' => ''));
765: ++$colCount;
766: }
767: } else {
768: --$colspan;
769: }
770: $thead = '<tr>'.implode('', $row).'</tr>';
771: }
772: if (!empty($thead) && is_string($thead)) {
773: $content[] = '<thead>'.$thead.'</thead>';
774: }
775:
776: if (!empty($this->datas)) {
777: $content[] = '<tbody>'.$this->tbody($start, $end).'</tbody>';
778: }
779: if (!$this->explain) {
780:
781: $tfoot = null;
782: if (isset($this->onCreateFooter)) {
783: $tfoot = call_user_func($this->onCreateFooter);
784: } elseif (!$this->hideCheckbox && $this->checkCol > -1) {
785: $tfoot = '<tr>';
786: $tfoot .= '<td colspan="'.$this->checkCol.'"></td>';
787: $tfoot .= '<td class="check-column"><a class="checkall icon-uncheck"></a></td>';
788: $tfoot .= '<td colspan="'.($colCount - $this->checkCol - 1).'"></td>';
789: $tfoot .= '</tr>';
790: }
791: if (!empty($tfoot) && is_string($tfoot)) {
792: $content[] = '<tfoot>'.$tfoot.'</tfoot>';
793: }
794: }
795: $content[] = '</table></div>';
796: $table_nav = array();
797: if (!empty($this->actions) && is_array($this->actions)) {
798: foreach ($this->actions as $item) {
799: $table_nav[] = $this->addAction($item);
800: }
801: }
802: if (!empty($this->addNew) && is_array($this->addNew)) {
803: $prop = array();
804: foreach ($this->addNew as $k => $v) {
805: if ($k != 'text') {
806: $prop[$k] = $k.'="'.$v.'"';
807: }
808: }
809: $text = isset($this->addNew['text']) ? $this->addNew['text'] : '';
810: if (preg_match('/^((.*)\s+)?(icon-[a-z0-9\-_]+)(\s+(.*))?$/', $this->addNew['class'], $match)) {
811: $prop['class'] = 'class="'.trim($match[2].' '.(isset($match[5]) ? $match[5] : '')).'"';
812: if (isset($prop['href'])) {
813: $table_nav[] = '<a '.implode(' ', $prop).'><span class="'.$match[3].'">'.$text.'</span></a>';
814: } else {
815: $table_nav[] = '<button '.implode(' ', $prop).' type="button"><span class="'.$match[3].'">'.$text.'</span></button>';
816: }
817: } elseif (isset($prop['href'])) {
818: $table_nav[] = '<a '.implode(' ', $prop).'>'.$text.'</a>';
819: } else {
820: $table_nav[] = '<button '.implode(' ', $prop).' type="button">'.$text.'</button>';
821: }
822: }
823: if (!$this->explain) {
824: if (!empty($table_nav)) {
825: $content[] = '<div class="table_nav clear action">'.implode('', $table_nav).'</div>';
826: }
827:
828: if ($this->perPage > 0) {
829: $content[] = '<div class="splitpage">'.$this->uri->pagination($totalpage, $this->page).'</div>';
830: }
831: }
832: $content[] = '</div>';
833: if ($this->enableJavascript && !$this->explain) {
834: $script = array(
835: 'page' => $this->page,
836: 'search' => $this->search,
837: 'sort' => $this->sort,
838: 'action' => $this->action,
839: 'actionCallback' => $this->actionCallback,
840: 'actionConfirm' => $this->actionConfirm,
841: 'onBeforeDelete' => $this->onBeforeDelete,
842: 'onDelete' => $this->onDelete,
843: 'onInitRow' => $this->onInitRow,
844: 'onAddRow' => $this->onAddRow,
845: 'pmButton' => $this->pmButton,
846: 'dragColumn' => $this->dragColumn,
847: 'debug' => is_string($this->debug) ? $this->debug : '',
848: );
849: $this->javascript[] = 'var table = new GTable("'.$this->id.'", '.json_encode($script).');';
850: }
851: if (!empty($this->javascript)) {
852: $content[] = "<script>\n".implode("\n", $this->javascript)."\n</script>";
853: }
854: return implode("\n", $content);
855: }
856:
857: 858: 859: 860: 861: 862: 863:
864: private function tbody($start, $end)
865: {
866: $row = array();
867: $n = 0;
868: foreach ($this->datas as $o => $items) {
869: if ($this->perPage <= 0 || ($n > $start && $n < $end)) {
870: $src_items = $items;
871:
872: $id = isset($items[$this->primaryKey]) ? $items[$this->primaryKey] : $o;
873: $prop = (object) array(
874: 'id' => $this->id.'_'.$id,
875: );
876: $buttons = array();
877: if (!$this->explain) {
878: if (!empty($this->buttons)) {
879: foreach ($this->buttons as $btn => $attributes) {
880: if (isset($this->onCreateButton)) {
881:
882: $attributes = call_user_func($this->onCreateButton, $btn, $attributes, $items);
883: }
884: if ($attributes && $attributes !== false) {
885: $buttons[] = $this->button($btn, $attributes, $items);
886: }
887: }
888: }
889: if (isset($this->onRow)) {
890: $items = call_user_func($this->onRow, $items, $o, $prop);
891: }
892: if (isset($this->dragColumn)) {
893: $prop->class = (empty($prop->class) ? 'sort' : $prop->class.' sort');
894: }
895: }
896:
897: $p = array();
898: foreach ($prop as $k => $v) {
899: $p[] = $k.'="'.$v.'"';
900: }
901: $row[] = '<tr '.implode(' ', $p).'>';
902:
903: $i = 0;
904: foreach ($this->headers as $field => $attributes) {
905: if (!empty($field) && !in_array($field, $this->hideColumns)) {
906: if (!$this->explain) {
907: if (!$this->hideCheckbox && $i == $this->checkCol) {
908: $row[] = '<td headers="r'.$id.'" class="check-column"><a id="check_'.$id.'" class="icon-uncheck"></a></td>';
909: }
910: if ($i == $this->dragColumn) {
911: $row[] = '<td class=center><a id="move_'.$id.'" title="'.Language::get('Drag and drop to reorder').'" class="icon-move"></a></td>';
912: }
913: }
914: $properties = isset($this->cols[$field]) ? $this->cols[$field] : array();
915: $text = isset($items[$field]) ? $items[$field] : '';
916: $th = isset($attributes['text']) ? $attributes['text'] : $field;
917: $row[] = $this->td($id, $i, $properties, $text, $th);
918: ++$i;
919: }
920: }
921: if (!$this->explain) {
922: if (!$this->hideCheckbox && $i == $this->checkCol) {
923: $row[] = '<td headers="r'.$id.'" class="check-column"><a id="check_'.$id.'" class="icon-uncheck"></a></td>';
924: ++$i;
925: }
926: if (!empty($this->buttons)) {
927: if (!empty($buttons)) {
928: $patt = array();
929: $replace = array();
930: $keys = array_keys($src_items);
931: rsort($keys);
932: foreach ($keys as $k) {
933: if (!is_array($src_items[$k])) {
934: $patt[] = ":$k";
935: $replace[] = $src_items[$k];
936: }
937: }
938: $prop = array('class' => 'buttons');
939: if (isset($this->cols['buttons']) && isset($this->cols['buttons']['class'])) {
940: $prop = array('class' => $this->cols['buttons']['class'].' buttons');
941: }
942: $row[] = str_replace($patt, $replace, $this->td($id, $i, $prop, implode('', $buttons), ''));
943: } else {
944: $row[] = $this->td($id, $i, array(), '', '');
945: }
946: }
947: if ($this->pmButton) {
948: $row[] = '<td class="icons"><div><a class="icon-plus" title="'.Language::get('Add New').'"></a><a class="icon-minus" title="'.Language::get('Remove').'"></a></div></td>';
949: }
950: }
951: $row[] = '</tr>';
952: }
953: ++$n;
954: }
955: return implode("\n", $row);
956: }
957:
958: 959: 960: 961: 962: 963: 964: 965: 966:
967: private function th($i, $column, $properties)
968: {
969: $c = array();
970: $c['id'] = 'id="c'.$i.'"';
971: if (!empty($properties['sort'])) {
972: $sort = isset($this->sorts[$properties['sort']]) ? $this->sorts[$properties['sort']] : 'none';
973: $properties['class'] = 'sort_'.$sort.' col_'.$column.(empty($properties['class']) ? '' : ' '.$properties['class']);
974: }
975: foreach ($properties as $key => $value) {
976: if ($key !== 'sort' && $key !== 'text') {
977: $c[$key] = $key.'="'.$value.'"';
978: }
979: }
980: return '<th '.implode(' ', $c).'>'.(isset($properties['text']) ? $properties['text'] : $column).'</th>';
981: }
982:
983: 984: 985: 986: 987: 988: 989: 990: 991: 992:
993: private function td($id, $i, $properties, $text, $th)
994: {
995: $c = array('data-text' => 'data-text="'.$th.'"');
996: foreach ($properties as $key => $value) {
997: $c[$key] = $key.'="'.$value.'"';
998: }
999: $c = implode(' ', $c);
1000: if ($i == 0) {
1001: $c .= ' id="r'.$id.'" headers="c'.$i.'"';
1002: return '<th '.$c.'>'.$text.'</th>';
1003: } else {
1004: $c .= ' headers="c'.$i.' r'.$id.'"';
1005: return '<td '.$c.'>'.$text.'</td>';
1006: }
1007: }
1008:
1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017:
1018: private function button($btn, $properties, $items)
1019: {
1020: if (isset($properties['submenus'])) {
1021: $innerHTML = '';
1022: $li = '';
1023: $attributes = array();
1024: foreach ($properties as $name => $item) {
1025: if ($name == 'submenus') {
1026: foreach ($item as $btn => $menu) {
1027: $prop = array();
1028: if (isset($this->onCreateButton)) {
1029:
1030: $menu = call_user_func($this->onCreateButton, $btn, $menu, $items);
1031: }
1032: if ($menu && $menu !== false) {
1033: $text = '';
1034: foreach ($menu as $key => $value) {
1035: if ($key == 'text') {
1036: $text = $value;
1037: } else {
1038: $prop[$key] = $key.'="'.$value.'"';
1039: }
1040: }
1041: $li .= '<li><a '.implode(' ', $prop).'>'.$text.'</a></li>';
1042: }
1043: }
1044: } elseif ($name == 'text') {
1045: $innerHTML = $item;
1046: } elseif ($name == 'class') {
1047: $attributes['class'] = 'class="'.$item.' menubutton"';
1048: } else {
1049: $attributes[$name] = $name.'="'.$item.'"';
1050: }
1051: }
1052: if (!isset($attributes['class'])) {
1053: $attributes['class'] = 'class="menubutton"';
1054: }
1055: if (!isset($attributes['tabindex'])) {
1056: $attributes['tabindex'] = 'tabindex="0"';
1057: }
1058:
1059: return '<div '.implode(' ', $attributes).'>'.$innerHTML.'<ul>'.$li.'</ul></div>';
1060: } else {
1061: $prop = array();
1062: foreach ($properties as $key => $value) {
1063: if ($key === 'id') {
1064: $prop[$key] = $key.'="'.$btn.'_'.$value.'"';
1065: } elseif ($key !== 'text') {
1066: $prop[$key] = $key.'="'.$value.'"';
1067: }
1068: }
1069: if (!empty($properties['class']) && preg_match('/(.*)\s?(icon\-[a-z0-9\-_]+)($|\s(.*))/', $properties['class'], $match)) {
1070: $class = array();
1071: foreach (array(1, 4) as $i) {
1072: if (!empty($match[$i])) {
1073: $class[] = $match[$i];
1074: }
1075: }
1076: if (empty($properties['text'])) {
1077: $class[] = 'notext';
1078: $prop['class'] = 'class="'.implode(' ', $class).'"';
1079: return '<a '.implode(' ', $prop).'><span class="'.$match[2].'"></span></a>';
1080: } else {
1081: $prop['class'] = 'class="'.implode(' ', $class).'"';
1082: if (!isset($prop['title'])) {
1083: $prop['title'] = 'title="'.strip_tags($properties['text']).'"';
1084: }
1085:
1086: return '<a '.implode(' ', $prop).'><span class="'.$match[2].' button_w_text"><span class=mobile>'.$properties['text'].'</span></span></a>';
1087: }
1088: } else {
1089: return '<a'.(empty($prop) ? '' : ' '.implode(' ', $prop)).'></a>';
1090: }
1091: }
1092: }
1093:
1094: 1095: 1096: 1097: 1098: 1099: 1100:
1101: private function addAction($item)
1102: {
1103: if (isset($item['class']) && preg_match('/^((.*)\s+)?(icon-[a-z0-9\-_]+)(\s+(.*))?$/', $item['class'], $match)) {
1104: $match[2] = trim($match[2].' '.(isset($match[5]) ? $match[5] : ''));
1105: }
1106: if (isset($item['options'])) {
1107:
1108: $rows = array();
1109: foreach ($item['options'] as $key => $text) {
1110: $rows[] = '<option value="'.$key.'">'.$text.'</option>';
1111: }
1112:
1113: return '<fieldset><select id="'.$item['id'].'">'.implode('', $rows).'</select><label for="'.$item['id'].'" class="button '.$item['class'].' action"><span>'.$item['text'].'</span></label></fieldset>';
1114: } elseif (isset($item['type'])) {
1115: $prop = array(
1116: 'type="'.$item['type'].'"',
1117: );
1118: $prop2 = array(
1119: 'button' => 'class="button action"',
1120: );
1121: foreach ($item as $key => $value) {
1122: if ($key == 'id') {
1123: $prop[] = 'id="'.$value.'"';
1124: $prop2[] = 'for="'.$value.'"';
1125: } elseif ($key == 'class') {
1126: $prop2['button'] = 'class="button '.$value.' action"';
1127: } elseif (!in_array($key, array('type', 'text'))) {
1128: $prop[] = $key.'="'.$value.'"';
1129: }
1130: }
1131: return '<fieldset><input '.implode(' ', $prop).'><label '.implode(' ', $prop2).'><span>'.$item['text'].'</span></label></fieldset>';
1132: } else {
1133:
1134: $prop = array();
1135: $text = isset($item['text']) ? $item['text'] : '';
1136: if ($text != '' && !isset($item['title'])) {
1137: $item['title'] = strip_tags($text);
1138: }
1139: if (isset($item['class'])) {
1140: if (empty($match[3])) {
1141: $prop[] = 'class="'.$item['class'].'"';
1142: } else {
1143: $text = '<span class="'.$match[3].'">'.$text.'</span>';
1144: $prop[] = 'class="'.$match[2].'"';
1145: }
1146: }
1147: foreach ($item as $k => $v) {
1148: if ($k != 'class' && $k != 'text') {
1149: $prop[] = $k.'="'.$v.'"';
1150: }
1151: }
1152: if (isset($item['href'])) {
1153:
1154: return '<a '.implode(' ', $prop).'>'.$text.'</a>';
1155: } else {
1156:
1157: return '<button '.implode(' ', $prop).' type="button">'.$text.'</button>';
1158: }
1159: }
1160: }
1161:
1162: 1163: 1164: 1165: 1166: 1167: 1168:
1169: private function addFilter($item)
1170: {
1171: if (isset($item['datalist'])) {
1172: $item['type'] = 'text';
1173: }
1174: if (isset($item['type'])) {
1175: $prop = array();
1176: $datalist = '';
1177: foreach ($item as $key => $value) {
1178: if ($key == 'datalist') {
1179: foreach ($value as $k => $v) {
1180: $datalist .= '<option value="'.$k.'">'.$v.'</option>';
1181: }
1182: } elseif ($key != 'text' && $key != 'default' && !is_array($value)) {
1183: $prop[$key] = $key.'="'.$value.'"';
1184: }
1185: }
1186: if ($datalist != '' && isset($item['name'])) {
1187: if (!isset($item['id'])) {
1188: $item['id'] = $item['name'];
1189: $prop['id'] = 'id="'.$item['name'].'"';
1190: }
1191: $prop['autocomplete'] = 'autocomplete="off"';
1192: $this->javascript[] = 'new Datalist("'.$item['id'].'");';
1193: $prop['list'] = 'list="'.$item['name'].'-datalist"';
1194: $datalist = '<datalist id="'.$item['name'].'-datalist">'.$datalist.'</datalist>';
1195: } else {
1196: $datalist = '';
1197: }
1198: $row = '<fieldset><label>'.(isset($item['text']) ? $item['text'] : '').' <input '.implode(' ', $prop).'>'.$datalist.'</label></fieldset>';
1199: } elseif (isset($item['items'])) {
1200: $button = '';
1201: foreach ($item['items'] as $link) {
1202: foreach ($link as $key => $value) {
1203: if ($key != 'text') {
1204: $prop[$key] = $key.'="'.$value.'"';
1205: }
1206: }
1207: if (isset($item['name'])) {
1208: $prop['name'] = 'name="'.$item['name'].'"';
1209: }
1210: if (isset($item['href'])) {
1211: $button .= '<a '.implode(' ', $prop).'>'.(isset($link['text']) ? $link['text'] : '').'</a>';
1212: } else {
1213: $button .= '<button '.implode(' ', $prop).'>'.(isset($link['text']) ? $link['text'] : '').'</button>';
1214: }
1215: }
1216: $row = '<fieldset class="buttons"><label>'.(isset($item['text']) ? $item['text'] : '').'</label>'.$button.'</fieldset>';
1217: } elseif (isset($item['href'])) {
1218: foreach ($item as $key => $value) {
1219: if ($key != 'text') {
1220: $prop[$key] = $key.'="'.$value.'"';
1221: }
1222: }
1223: $row = '<a '.implode(' ', $prop).'>'.(isset($item['text']) ? $item['text'] : '').'</a>';
1224: } else {
1225: $prop = array();
1226: foreach ($item as $key => $value) {
1227: if ($key != 'options' && $key != 'value' && $key != 'text' && $key != 'default' && !is_array($value)) {
1228: $prop[$key] = $key.'="'.$value.'"';
1229: }
1230: }
1231: $row = '<fieldset><label>'.(isset($item['text']) ? $item['text'] : '').' <select '.implode(' ', $prop).'>';
1232: if (!empty($item['options'])) {
1233: foreach ($item['options'] as $key => $text) {
1234: $sel = isset($item['value']) && (string) $key == $item['value'] ? ' selected' : '';
1235: $row .= '<option value="'.$key.'"'.$sel.'>'.$text.'</option>';
1236: }
1237: }
1238: $row .= '</select></label></fieldset>';
1239: }
1240: return $row;
1241: }
1242: }
1243: