Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/lib/txplib_html.php - 1923 lines - 52662 bytes - Summary - Text - Print

Description: Collection of HTML widgets.

   1  <?php
   2  
   3  /*
   4   * Textpattern Content Management System
   5   * http://textpattern.com
   6   *
   7   * Copyright (C) 2016 The Textpattern Development Team
   8   *
   9   * This file is part of Textpattern.
  10   *
  11   * Textpattern is free software; you can redistribute it and/or
  12   * modify it under the terms of the GNU General Public License
  13   * as published by the Free Software Foundation, version 2.
  14   *
  15   * Textpattern is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18   * GNU General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU General Public License
  21   * along with Textpattern. If not, see <http://www.gnu.org/licenses/>.
  22   */
  23  
  24  /**
  25   * Collection of HTML widgets.
  26   *
  27   * @package HTML
  28   */
  29  
  30  /**
  31   * A tab character.
  32   *
  33   * @var string
  34   */
  35  
  36  define("t", "\t");
  37  
  38  /**
  39   * A line feed.
  40   *
  41   * @var string
  42   */
  43  
  44  define("n", "\n");
  45  
  46  /**
  47   * A self-closing HTML line-break tag.
  48   *
  49   * @var string
  50   */
  51  
  52  define("br", "<br />");
  53  
  54  /**
  55   * A non-breaking space as a HTML entity.
  56   *
  57   * @var string
  58   */
  59  
  60  define("sp", "&#160;");
  61  
  62  /**
  63   * An ampersand as a HTML entity.
  64   *
  65   * @var string
  66   */
  67  
  68  define("a", "&#38;");
  69  
  70  /**
  71   * Renders the admin-side footer.
  72   *
  73   * The footer's default markup is provided by a theme. It can be further
  74   * customised via the "admin_side > footer" pluggable UI callback event.
  75   *
  76   * In addition to the pluggable UI, this function also calls callback events
  77   * "admin_side > main_content_end" and "admin_side > body_end".
  78   */
  79  
  80  function end_page()
  81  {
  82      global $event, $app_mode, $theme, $textarray_script;
  83  
  84      if ($app_mode != 'async' && $event != 'tag') {
  85          callback_event('admin_side', 'main_content_end');
  86          echo n.'</main><!-- /txp-body -->'.n.'<footer class="txp-footer">';
  87          echo pluggable_ui('admin_side', 'footer', $theme->footer());
  88          callback_event('admin_side', 'body_end');
  89  
  90          gTxtScript(array(
  91              'password_strength_0',
  92              'password_strength_1',
  93              'password_strength_2',
  94              'password_strength_3',
  95              'password_strength_4',
  96              ),
  97              array(),
  98              array(array('admin', 'admin'), array('new_pass_form', 'change_pass'))
  99          );
 100  
 101          echo script_js('vendors/dropbox/zxcvbn/zxcvbn.js', TEXTPATTERN_SCRIPT_URL, array(array('admin', 'admin'), array('new_pass_form', 'change_pass'))).
 102              script_js('vendors/PrismJS/prism/prism.js', TEXTPATTERN_SCRIPT_URL).
 103              script_js('textpattern.textarray = '.json_encode($textarray_script)).
 104              n.'</footer><!-- /txp-footer -->'.n.'</body>'.n.'</html>';
 105      }
 106  }
 107  
 108  /**
 109   * Renders the user interface for one head cell of columnar data.
 110   *
 111   * @param  string $value   Element text
 112   * @param  string $sort    Sort criterion
 113   * @param  string $event   Event name
 114   * @param  bool   $is_link Include link to admin action in user interface according to the other params
 115   * @param  string $dir     Sort direction, either "asc" or "desc"
 116   * @param  string $crit    Search criterion
 117   * @param  string $method  Search method
 118   * @param  string $class   HTML "class" attribute applied to the resulting element
 119   * @param  string $step    Step name
 120   * @return string HTML
 121   */
 122  
 123  function column_head($value, $sort = '', $event = '', $is_link = '', $dir = '', $crit = '', $method = '', $class = '', $step = 'list')
 124  {
 125      return column_multi_head(array(array(
 126          'value'   => $value,
 127          'sort'    => $sort,
 128          'event'   => $event,
 129          'step'    => $step,
 130          'is_link' => $is_link,
 131          'dir'     => $dir,
 132          'crit'    => $crit,
 133          'method'  => $method,
 134      )), $class);
 135  }
 136  
 137  /**
 138   * Renders the user interface for multiple head cells of columnar data.
 139   *
 140   * @param  array  $head_items An array of hashed elements. Valid keys: 'value', 'sort', 'event', 'is_link', 'dir', 'crit', 'method'
 141   * @param  string $class      HTML "class" attribute applied to the resulting element
 142   * @return string HTML
 143   */
 144  
 145  function column_multi_head($head_items, $class = '')
 146  {
 147      $o = '';
 148      $first_item = true;
 149  
 150      foreach ($head_items as $item) {
 151          if (empty($item)) {
 152              continue;
 153          }
 154  
 155          extract(lAtts(array(
 156              'value'   => '',
 157              'sort'    => '',
 158              'event'   => '',
 159              'step'    => 'list',
 160              'is_link' => '',
 161              'dir'     => '',
 162              'crit'    => '',
 163              'method'  => '',
 164          ), $item));
 165  
 166          $o .= ($first_item) ? '' : ', ';
 167          $first_item = false;
 168  
 169          if ($is_link) {
 170              $o .= href(gTxt($value), array(
 171                  'event'         => $event,
 172                  'step'          => $step,
 173                  'sort'          => $sort,
 174                  'dir'           => $dir,
 175                  'crit'          => $crit,
 176                  'search_method' => $method,
 177              ), array());
 178          } else {
 179              $o .= gTxt($value);
 180          }
 181      }
 182  
 183      return hCell($o, '', array(
 184          'class' => $class,
 185          'scope' => 'col',
 186      ));
 187  }
 188  
 189  /**
 190   * Renders a &lt;th&gt; element.
 191   *
 192   * @param  string       $text    Cell text
 193   * @param  string       $caption Is not used
 194   * @param  string|array $atts    HTML attributes
 195   * @return string HTML
 196   */
 197  
 198  function hCell($text = '', $caption = '', $atts = '')
 199  {
 200      $text = ('' === $text) ? sp : $text;
 201  
 202      return n.tag($text, 'th', $atts);
 203  }
 204  
 205  /**
 206   * Renders a link invoking an admin-side action.
 207   *
 208   * @param  string $event    Event
 209   * @param  string $step     Step
 210   * @param  string $linktext Link text
 211   * @param  string $class    HTML class attribute for link
 212   * @return string HTML
 213   */
 214  
 215  function sLink($event, $step, $linktext, $class = '')
 216  {
 217      if ($linktext === '') {
 218          $linktext = null;
 219      }
 220  
 221      return href($linktext, array(
 222          'event' => $event,
 223          'step'  => $step,
 224      ), array('class' => $class));
 225  }
 226  
 227  /**
 228   * Renders a link with two additional URL parameters.
 229   *
 230   * Renders a link invoking an admin-side action while taking up to two
 231   * additional URL parameters.
 232   *
 233   * @param  string $event    Event
 234   * @param  string $step     Step
 235   * @param  string $thing    URL parameter key #1
 236   * @param  string $value    URL parameter value #1
 237   * @param  string $linktext Link text
 238   * @param  string $thing2   URL parameter key #2
 239   * @param  string $val2     URL parameter value #2
 240   * @param  string $title    Anchor title
 241   * @param  string $class    HTML class attribute
 242   * @return string HTML
 243   */
 244  
 245  function eLink($event, $step, $thing, $value, $linktext, $thing2 = '', $val2 = '', $title = '', $class = '')
 246  {
 247      if ($title) {
 248          $title = gTxt($title);
 249      }
 250  
 251      if ($linktext === '') {
 252          $linktext = null;
 253      } else {
 254          $linktext = escape_title($linktext);
 255      }
 256  
 257      return href($linktext, array(
 258          'event'      => $event,
 259          'step'       => $step,
 260          $thing       => $value,
 261          $thing2      => $val2,
 262          '_txp_token' => form_token(),
 263      ), array(
 264          'class' => $class,
 265          'title' => $title,
 266      ));
 267  }
 268  
 269  /**
 270   * Renders a link with one additional URL parameter.
 271   *
 272   * Renders an link invoking an admin-side action while taking up to one
 273   * additional URL parameter.
 274   *
 275   * @param  string $event Event
 276   * @param  string $step  Step
 277   * @param  string $thing URL parameter key
 278   * @param  string $value URL parameter value
 279   * @param  string $class HTML class attribute
 280   * @return string HTML
 281   */
 282  
 283  function wLink($event, $step = '', $thing = '', $value = '', $class = '')
 284  {
 285      return href(sp.'!'.sp, array(
 286          'event'      => $event,
 287          'step'       => $step,
 288          $thing       => $value,
 289          '_txp_token' => form_token(),
 290      ), array('class' => $class));
 291  }
 292  
 293  /**
 294   * Renders a delete link.
 295   *
 296   * Renders a link invoking an admin-side "delete" action while taking up to two
 297   * additional URL parameters.
 298   *
 299   * @param  string $event     Event
 300   * @param  string $step      Step
 301   * @param  string $thing     URL parameter key #1
 302   * @param  string $value     URL parameter value #1
 303   * @param  string $verify    Show an "Are you sure?" dialogue with this text
 304   * @param  string $thing2    URL parameter key #2
 305   * @param  string $thing2val URL parameter value #2
 306   * @param  bool   $get       If TRUE, uses GET request
 307   * @param  array  $remember  Convey URL parameters for page state. Member sequence is $page, $sort, $dir, $crit, $search_method
 308   * @return string HTML
 309   */
 310  
 311  function dLink($event, $step, $thing, $value, $verify = '', $thing2 = '', $thing2val = '', $get = '', $remember = null)
 312  {
 313      if ($remember) {
 314          list($page, $sort, $dir, $crit, $search_method) = $remember;
 315      }
 316  
 317      if ($get) {
 318          if ($verify) {
 319              $verify = gTxt($verify);
 320          } else {
 321              $verify = gTxt('confirm_delete_popup');
 322          }
 323  
 324          if ($remember) {
 325              return href('×', array(
 326                  'event'         => $event,
 327                  'step'          => $step,
 328                  $thing          => $value,
 329                  $thing2         => $thing2val,
 330                  '_txp_token'    => form_token(),
 331                  'page'          => $page,
 332                  'sort'          => $sort,
 333                  'dir'           => $dir,
 334                  'crit'          => $crit,
 335                  'search_method' => $search_method,
 336              ), array(
 337                  'class'       => 'dlink destroy',
 338                  'title'       => gTxt('delete'),
 339                  'data-verify' => $verify,
 340              ));
 341          }
 342  
 343          return href('×', array(
 344              'event'         => $event,
 345              'step'          => $step,
 346              $thing          => $value,
 347              $thing2         => $thing2val,
 348              '_txp_token'    => form_token(),
 349          ), array(
 350              'class'       => 'dlink destroy',
 351              'title'       => gTxt('delete'),
 352              'data-verify' => $verify,
 353          ));
 354      }
 355  
 356      return join('', array(
 357          n.'<form method="post" action="index.php" data-verify="'.gTxt('confirm_delete_popup').'">',
 358          tag(
 359              span(gTxt('delete'), array('class' => 'ui-icon ui-icon-close')),
 360              'button',
 361              array(
 362                  'class'      => 'destroy',
 363                  'type'       => 'submit',
 364                  'title'      => gTxt('delete'),
 365                  'aria-label' => gTxt('delete'),
 366              )
 367          ),
 368          eInput($event).
 369          sInput($step),
 370          hInput($thing, $value),
 371          ($thing2) ? hInput($thing2, $thing2val) : '',
 372          ($remember) ? hInput('page', $page) : '',
 373          ($remember) ? hInput('sort', $sort) : '',
 374          ($remember) ? hInput('dir', $dir) : '',
 375          ($remember) ? hInput('crit', $crit) : '',
 376          ($remember) ? hInput('search_method', $search_method) : '',
 377          tInput(),
 378          n.'</form>',
 379      ));
 380  }
 381  
 382  /**
 383   * Renders an add link.
 384   *
 385   * This function can be used for invoking an admin-side "add" action while
 386   * taking up to two additional URL parameters.
 387   *
 388   * @param  string $event  Event
 389   * @param  string $step   Step
 390   * @param  string $thing  URL parameter key #1
 391   * @param  string $value  URL parameter value #1
 392   * @param  string $thing2 URL parameter key #2
 393   * @param  string $value2 URL parameter value #2
 394   * @return string HTML
 395   */
 396  
 397  function aLink($event, $step, $thing = '', $value = '', $thing2 = '', $value2 = '')
 398  {
 399      return href('+', array(
 400          'event'      => $event,
 401          'step'       => $step,
 402          $thing       => $value,
 403          $thing2      => $value2,
 404          '_txp_token' => form_token(),
 405      ), array('class' => 'alink'));
 406  }
 407  
 408  /**
 409   * Renders a link invoking an admin-side "previous/next article" action.
 410   *
 411   * @param  string $name  Link text
 412   * @param  string $event Event
 413   * @param  string $step  Step
 414   * @param  int    $id    ID of target Textpattern object (article,...)
 415   * @param  string $title HTML title attribute
 416   * @param  string $rel   HTML rel attribute
 417   * @return string HTML
 418   */
 419  
 420  function prevnext_link($name, $event, $step, $id, $title = '', $rel = '')
 421  {
 422      return href($name, array(
 423          'event' => $event,
 424          'step'  => $step,
 425          'ID'    => $id,
 426      ), array(
 427          'class' => 'navlink',
 428          'title' => $title,
 429          'rel'   => $rel,
 430      ));
 431  }
 432  
 433  /**
 434   * Renders a link invoking an admin-side "previous/next page" action.
 435   *
 436   * @param  string $event         Event
 437   * @param  int    $page          Target page number
 438   * @param  string $label         Link text
 439   * @param  string $type          Direction, either "prev" or "next"
 440   * @param  string $sort          Sort field
 441   * @param  string $dir           Sort direction, either "asc" or "desc"
 442   * @param  string $crit          Search criterion
 443   * @param  string $search_method Search method
 444   * @param  string $step          Step
 445   * @return string HTML
 446   */
 447  
 448  function PrevNextLink($event, $page, $label, $type, $sort = '', $dir = '', $crit = '', $search_method = '', $step = 'list')
 449  {
 450      $theClass = ($type === 'next') ? 'ui-icon-arrowthick-1-e' : 'ui-icon-arrowthick-1-w';
 451      return href(span(
 452              $label, array('class' => 'ui-icon '.$theClass)
 453          ),
 454          array(
 455          'event'         => $event,
 456          'step'          => $step,
 457          'page'          => (int) $page,
 458          'dir'           => $dir,
 459          'crit'          => $crit,
 460          'search_method' => $search_method,
 461      ), array(
 462          'rel'        => $type,
 463          'title'      => $label,
 464          'aria-label' => $label,
 465      ));
 466  }
 467  
 468  /**
 469   * Renders a page navigation form.
 470   *
 471   * @param  string $event         Event
 472   * @param  int    $page          Current page number
 473   * @param  int    $numPages         Total pages
 474   * @param  string $sort          Sort criterion
 475   * @param  string $dir           Sort direction, either "asc" or "desc"
 476   * @param  string $crit          Search criterion
 477   * @param  string $search_method Search method
 478   * @param  int    $total         Total search term hit count [0]
 479   * @param  int    $limit         First visible search term hit number [0]
 480   * @param  string $step             Step
 481   * @param  int    $list          Number of displayed page links discounting jump links, previous and next
 482   * @return string HTML
 483   */
 484  
 485  function nav_form($event, $page, $numPages, $sort = '', $dir = '', $crit = '', $search_method = '', $total = 0, $limit = 0, $step = 'list', $list = 5)
 486  {
 487      $out = array();
 488  
 489      if ($crit != '' && $total > 1) {
 490          $out[] = announce(
 491              gTxt('showing_search_results', array(
 492                  '{from}'  => (($page - 1) * $limit) + 1,
 493                  '{to}'    => min($total, $page * $limit),
 494                  '{total}' => $total,
 495              )),
 496              TEXTPATTERN_ANNOUNCE_REGULAR
 497          );
 498      }
 499  
 500      if ($numPages > 1) {
 501          $nav = array();
 502          $list--;
 503          $page = max(min($page, $numPages), 1);
 504          $start = max(1, min($numPages - $list, $page - floor($list/2)));
 505          $end = min($numPages, $start + $list);
 506  
 507          $parameters = array(
 508              'event'         => $event,
 509              'step'          => $step,
 510              'dir'           => $dir,
 511              'crit'          => $crit,
 512              'search_method' => $search_method,
 513          );
 514  
 515          // Previous page.
 516          if ($page > 1) {
 517              $nav[] = n.PrevNextLink($event, $page - 1, gTxt('prev'), 'prev', $sort, $dir, $crit, $search_method, $step);
 518          } else {
 519              $nav[] = n.span(
 520                  span(gTxt('prev'), array(
 521                      'class' => 'ui-icon ui-icon-arrowthick-1-w',
 522                  )),
 523                  array(
 524                      'class'         => 'disabled',
 525                      'aria-disabled' => 'true',
 526                      'aria-label'    => gTxt('prev'),
 527                  )
 528              );
 529          }
 530  
 531  
 532          $nav[] = form(
 533                  n.tag(gTxt('page'), 'label', array(
 534                      'for' => 'current-page',
 535                  )).
 536                  n.tag_void('input', array(
 537                      'class'     => 'current-page',
 538                      'id'        => 'current-page',
 539                      'name'      => 'page',
 540                      'type'      => 'text',
 541                      'size'      => INPUT_XSMALL,
 542                      'inputmode' => 'numeric',
 543                      'pattern'   => '[0-9]+',
 544                      'value'     => $page,
 545                  )).
 546                  n.gTxt('of').
 547                  n.span($numPages, array('class' => 'total-pages')).
 548                  eInput($event).
 549                  hInput('sort', $sort).
 550                  hInput('dir', $dir).
 551                  hInput('crit', $crit).
 552                  hInput('search_method', $search_method),
 553                  '',
 554                  '',
 555                  'get'
 556              );
 557  
 558          // Next page.
 559          if ($page < $numPages) {
 560              $nav[] = n.PrevNextLink($event, $page + 1, gTxt('next'), 'next', $sort, $dir, $crit, $search_method, $step);
 561          } else {
 562              $nav[] = n.span(
 563                  span(gTxt('next'), array(
 564                      'class' => 'ui-icon ui-icon-arrowthick-1-e',
 565                  )),
 566                  array(
 567                      'class'         => 'disabled',
 568                      'aria-disabled' => 'true',
 569                      'aria-label'    => gTxt('next'),
 570                  )
 571              );
 572          }
 573  
 574          $out[] = n.tag(join($nav).n, 'nav', array('class' => 'prev-next'));
 575      }
 576  
 577      return join('', $out);
 578  }
 579  
 580  /**
 581   * Wraps a collapsible region and group structure around content.
 582   *
 583   * @param  string $id        HTML id attribute for the region wrapper and ARIA label
 584   * @param  string $content   Content to wrap. If empty, only the outer wrapper will be rendered
 585   * @param  string $anchor_id HTML id attribute for the collapsible wrapper
 586   * @param  string $label     L10n label name
 587   * @param  string $pane      Pane reference for maintaining toggle state in prefs. Prefixed with 'pane_', suffixed with '_visible'
 588   * @param  string $class     CSS class name to apply to wrapper
 589   * @param  string $help      Help text item
 590   * @return string HTML
 591   * @since  4.6.0
 592   */
 593  
 594  function wrapRegion($id, $content = '', $anchor_id = '', $label = '', $pane = '', $class = '', $help = '')
 595  {
 596      global $event;
 597      $label = $label ? gTxt($label) : null;
 598  
 599      if ($anchor_id && $pane) {
 600          $visible = get_pref('pane_'.$pane.'_visible');
 601          $heading_class = 'txp-summary'.($visible ? ' expanded' : '');
 602          $display_state = array(
 603              'class' => 'toggle',
 604              'id'    => $anchor_id,
 605              'role'  => 'group',
 606              'style' => $visible ? 'display: block' : 'display: none',
 607          );
 608  
 609          $label = href($label, '#'.$anchor_id, array(
 610              'role'           => 'button',
 611              'data-txp-token' => md5($pane.$event.form_token().get_pref('blog_uid')),
 612              'data-txp-pane'  => $pane,
 613          ));
 614  
 615          $help = '';
 616      } else {
 617          $heading_class = '';
 618          $display_state = array('role' => 'group');
 619      }
 620  
 621      if ($content) {
 622          $content =
 623              hed($label.popHelp($help), 3, array(
 624                  'class' => $heading_class,
 625                  'id'    => $id.'-label',
 626              )).
 627              n.tag($content.n, 'div', $display_state).n;
 628      }
 629  
 630      return n.tag($content, 'section', array(
 631          'class'           => trim('txp-details '.$class),
 632          'id'              => $id,
 633          'aria-labelledby' => $content ? $id.'-label' : '',
 634      ));
 635  }
 636  
 637  /**
 638   * Wraps a region and group structure around content.
 639   *
 640   * @param  string $name    HTML id attribute for the group wrapper and ARIA label
 641   * @param  string $content Content to wrap
 642   * @param  string $label   L10n label name
 643   * @param  string $class   CSS class name to apply to wrapper
 644   * @param  string $help    Help text item
 645   * @return string HTML
 646   * @see    wrapRegion()
 647   * @since  4.6.0
 648   */
 649  
 650  function wrapGroup($id, $content, $label, $class = '', $help = '')
 651  {
 652      return wrapRegion($id, $content, '', $label, '', $class, $help);
 653  }
 654  
 655  /**
 656   * Renders start of a layout &lt;table&gt; element.
 657   *
 658   * @return     string HTML
 659   * @deprecated in 4.4.0
 660   */
 661  
 662  function startSkelTable()
 663  {
 664      return
 665      '<table width="300" cellpadding="0" cellspacing="0" style="border:1px #ccc solid">';
 666  }
 667  
 668  /**
 669   * Renders start of a layout &lt;table&gt; element.
 670   *
 671   * @param  string $id    HTML id attribute
 672   * @param  string $align HTML align attribute
 673   * @param  string $class HTML class attribute
 674   * @param  int    $p     HTML cellpadding attribute
 675   * @param  int    $w     HTML width atttribute
 676   * @return string HTML
 677   * @example
 678   * startTable().
 679   * tr(td('column') . td('column')).
 680   * tr(td('column') . td('column')).
 681   * endTable();
 682   */
 683  
 684  function startTable($id = '', $align = '', $class = '', $p = 0, $w = 0)
 685  {
 686      $atts = join_atts(array(
 687          'class'       => $class,
 688          'id'          => $id,
 689          'cellpadding' => (int) $p,
 690          'width'       => (int) $w,
 691          'align'       => $align,
 692      ), TEXTPATTERN_STRIP_EMPTY);
 693  
 694      return n.'<table'.$atts.'>';
 695  }
 696  
 697  /**
 698   * Renders closing &lt;/table&gt; tag.
 699   *
 700   * @return string HTML
 701   */
 702  
 703  function endTable()
 704  {
 705      return n.'</table>';
 706  }
 707  
 708  /**
 709   * Renders &lt;tr&gt; elements from input parameters.
 710   *
 711   * Takes a list of arguments containing each making a row.
 712   *
 713   * @return string HTML
 714   * @example
 715   * stackRows(
 716   *     td('cell') . td('cell'),
 717   *  td('cell') . td('cell')
 718   * );
 719   */
 720  
 721  function stackRows()
 722  {
 723      foreach (func_get_args() as $a) {
 724          $o[] = tr($a);
 725      }
 726  
 727      return join('', $o);
 728  }
 729  
 730  /**
 731   * Renders a &lt;td&gt; element.
 732   *
 733   * @param  string $content Cell content
 734   * @param  int    $width   HTML width attribute
 735   * @param  string $class   HTML class attribute
 736   * @param  string $id      HTML id attribute
 737   * @return string HTML
 738   */
 739  
 740  function td($content = '', $width = null, $class = '', $id = '')
 741  {
 742      $opts = array(
 743          'class' => $class,
 744          'id'    => $id,
 745      );
 746  
 747      if (is_numeric($width)) {
 748          $opts['width'] = (int) $width;
 749      }
 750  
 751      return tda($content, $opts);
 752  }
 753  
 754  /**
 755   * Renders a &lt;td&gt; element with attributes.
 756   *
 757   * @param  string       $content Cell content
 758   * @param  string|array $atts    Cell attributes
 759   * @return string HTML
 760   */
 761  
 762  function tda($content, $atts = '')
 763  {
 764      $content = ($content === '') ? sp : $content;
 765  
 766      return n.tag($content, 'td', $atts);
 767  }
 768  
 769  /**
 770   * Renders a &lt;td&gt; element with attributes.
 771   *
 772   * This function is identical to tda().
 773   *
 774   * @param  string       $content Cell content
 775   * @param  string|array $atts    Cell attributes
 776   * @return string HTML
 777   * @access private
 778   * @see    tda()
 779   */
 780  
 781  function tdtl($content, $atts = '')
 782  {
 783      return tda($content, $atts);
 784  }
 785  
 786  /**
 787   * Renders a &lt;tr&gt; element with attributes.
 788   *
 789   * @param  string       $content Row content
 790   * @param  string|array $atts    Row attributes
 791   * @return string HTML
 792   */
 793  
 794  function tr($content, $atts = '')
 795  {
 796      return n.tag($content, 'tr', $atts);
 797  }
 798  
 799  /**
 800   * Renders a &lt;td&gt; element with top/left text orientation, colspan and
 801   * other attributes.
 802   *
 803   * @param  string $content Cell content
 804   * @param  int    $span    Cell colspan attribute
 805   * @param  int    $width   Cell width attribute
 806   * @param  string $class   Cell class attribute
 807   * @return string HTML
 808   */
 809  
 810  function tdcs($content, $span, $width = null, $class = '')
 811  {
 812      $opts = array(
 813          'class'   => $class,
 814          'colspan' => (int) $span,
 815      );
 816  
 817      if (is_numeric($width)) {
 818          $opts['width'] = (int) $width;
 819      }
 820  
 821      return tda($content, $opts);
 822  }
 823  
 824  /**
 825   * Renders a &lt;td&gt; element with a rowspan attribute.
 826   *
 827   * @param  string $content Cell content
 828   * @param  int    $span    Cell rowspan attribute
 829   * @param  int    $width   Cell width attribute
 830   * @param  string $class   Cell class attribute
 831   * @return string HTML
 832   */
 833  
 834  function tdrs($content, $span, $width = null, $class = '')
 835  {
 836      $opts = array(
 837          'class'   => $class,
 838          'rowspan' => (int) $span,
 839      );
 840  
 841      if (is_numeric($width)) {
 842          $opts['width'] = (int) $width;
 843      }
 844  
 845      return tda($content, $opts);
 846  }
 847  
 848  /**
 849   * Renders a form label inside a table cell.
 850   *
 851   * @param  string $text     Label text
 852   * @param  string $help     Help text
 853   * @param  string $label_id HTML "for" attribute, i.e. id of corresponding form element
 854   * @return string HTML
 855   */
 856  
 857  function fLabelCell($text, $help = '', $label_id = '')
 858  {
 859      $cell = gTxt($text).' '.popHelp($help);
 860  
 861      if ($label_id) {
 862          $cell = tag($cell, 'label', array('for' => $label_id));
 863      }
 864  
 865      return tda($cell, array('class' => 'cell-label'));
 866  }
 867  
 868  /**
 869   * Renders a form input inside a table cell.
 870   *
 871   * @param  string $name     HTML name attribute
 872   * @param  string $var      Input value
 873   * @param  int    $tabindex HTML tabindex attribute
 874   * @param  int    $size     HTML size attribute
 875   * @param  bool   $help     TRUE to display help link
 876   * @param  string $id       HTML id attribute
 877   * @return string HTML
 878   */
 879  
 880  function fInputCell($name, $var = '', $tabindex = 0, $size = 0, $help = false, $id = '')
 881  {
 882      $pop = ($help) ? popHelp($name) : '';
 883  
 884      return tda(fInput('text', $name, $var, '', '', '', $size, $tabindex, $id).$pop);
 885  }
 886  
 887  /**
 888   * Renders a name-value input control with label.
 889   *
 890   * The rendered input can be customised via the
 891   * '{$event}_ui > inputlabel.{$name}' pluggable UI callback event.
 892   *
 893   * @param  string       $name        Input name
 894   * @param  string       $input       Complete input control widget
 895   * @param  string|array $label       Label text | array (label text, HTML block to append to label)
 896   * @param  string|array $help        Help text item | array(help text item, inline help text)
 897   * @param  string|array $atts        Class name | attribute pairs to assign to container div
 898   * @param  string|array $wraptag_val Tag to wrap the value / label in, or empty to omit
 899   * @return string HTML
 900   * @example
 901   * echo inputLabel('active', yesnoRadio('active'), 'Keep active?');
 902   */
 903  
 904  function inputLabel($name, $input, $label = '', $help = array(), $atts = array(), $wraptag_val = array('div', 'div'))
 905  {
 906      global $event;
 907  
 908      $arguments = compact('name', 'input', 'label', 'help', 'atts', 'wraptag_val');
 909  
 910      $fallback_class = 'txp-form-field edit-'.str_replace('_', '-', $name);
 911      $tools = '';
 912  
 913      if ($atts && is_string($atts)) {
 914          $atts = array('class' => $atts);
 915      } elseif (!$atts) {
 916          $atts = array('class' => $fallback_class);
 917      } elseif (is_array($atts) && !array_key_exists('class', $atts)) {
 918          $atts['class'] = $fallback_class;
 919      }
 920  
 921      if (!is_array($help)) {
 922          $help = array($help);
 923      }
 924  
 925      if (is_array($label)) {
 926          if (isset($label[1])) {
 927              $tools = (string) $label[1];
 928          }
 929  
 930          $label = (string) $label[0];
 931      }
 932  
 933      if (empty($help)) {
 934          $help = array(
 935              0 => '',
 936              1 => ''
 937          );
 938      }
 939  
 940      $inlineHelp = (isset($help[1])) ? $help[1] : '';
 941  
 942      if ($label) {
 943          $labelContent = tag(gTxt($label).popHelp($help[0]), 'label', array('for' => $name)).$tools;
 944      } else {
 945          $labelContent = gTxt($name).popHelp($help[0]).$tools;
 946      }
 947  
 948      if (!is_array($wraptag_val)) {
 949          $wraptag_val = array($wraptag_val, $wraptag_val);
 950      }
 951  
 952      if ($wraptag_val[0]) {
 953          $input = n.tag($input, $wraptag_val[0], array('class' => 'txp-form-field-value'));
 954      }
 955  
 956      if (isset($wraptag_val[1]) && $wraptag_val[1]) {
 957          $labeltag = n.tag($labelContent, $wraptag_val[1], array('class' => 'txp-form-field-label'));
 958      } else {
 959          $labeltag = $labelContent;
 960      }
 961  
 962      $out = n.tag(
 963          $labeltag.
 964          fieldHelp($inlineHelp).
 965          $input.n, 'div', $atts);
 966  
 967      return pluggable_ui($event.'_ui', 'inputlabel.'.$name, $out, $arguments);
 968  }
 969  
 970  /**
 971   * Renders anything as an XML element.
 972   *
 973   * @param  string       $content Enclosed content
 974   * @param  string       $tag     The tag without brackets
 975   * @param  string|array $atts    The element's HTML attributes
 976   * @return string HTML
 977   * @example
 978   * echo tag('Link text', 'a', array('href' => '#', 'class' => 'warning'));
 979   */
 980  
 981  function tag($content, $tag, $atts = '')
 982  {
 983      return empty($tag) || $content === '' ? $content : '<'.$tag.join_atts($atts).'>'.$content.'</'.$tag.'>';
 984  }
 985  
 986  /**
 987   * Renders anything as a HTML void element.
 988   *
 989   * @param  string $tag  The tag without brackets
 990   * @param  string|array $atts HTML attributes
 991   * @return string HTML
 992   * @since  4.6.0
 993   * @example
 994   * echo tag_void('input', array('name' => 'name', 'type' => 'text'));
 995   */
 996  
 997  function tag_void($tag, $atts = '')
 998  {
 999      return '<'.$tag.join_atts($atts).' />';
1000  }
1001  
1002  /**
1003   * Renders anything as a HTML start tag.
1004   *
1005   * @param  string $tag The tag without brackets
1006   * @param  string|array $atts HTML attributes
1007   * @return string A HTML start tag
1008   * @since  4.6.0
1009   * @example
1010   * echo tag_start('section', array('class' => 'myClass'));
1011   */
1012  
1013  function tag_start($tag, $atts = '')
1014  {
1015      return '<'.$tag.join_atts($atts).'>';
1016  }
1017  
1018  /**
1019   * Renders anything as a HTML end tag.
1020   *
1021   * @param  string $tag The tag without brackets
1022   * @return string A HTML end tag
1023   * @since  4.6.0
1024   * @example
1025   * echo tag_end('section');
1026   */
1027  
1028  function tag_end($tag)
1029  {
1030      return '</'.$tag.'>';
1031  }
1032  
1033  /**
1034   * Renders a &lt;p&gt; element.
1035   *
1036   * @param  string       $item Enclosed content
1037   * @param  string|array $atts HTML attributes
1038   * @return string HTML
1039   * @example
1040   * echo graf('This a paragraph.');
1041   */
1042  
1043  function graf($item, $atts = '')
1044  {
1045      return n.tag($item, 'p', $atts);
1046  }
1047  
1048  /**
1049   * Renders a &lt;hx&gt; element.
1050   *
1051   * @param  string       $item  The Enclosed content
1052   * @param  int          $level Heading level 1...6
1053   * @param  string|array $atts  HTML attributes
1054   * @return string HTML
1055   * @example
1056   * echo hed('Heading', 2);
1057   */
1058  
1059  function hed($item, $level, $atts = '')
1060  {
1061      return n.tag($item, 'h'.$level, $atts).n;
1062  }
1063  
1064  /**
1065   * Renders an &lt;a&gt; element.
1066   *
1067   * @param  string       $item Enclosed content
1068   * @param  string|array $href The link target
1069   * @param  string|array $atts HTML attributes
1070   * @return string HTML
1071   */
1072  
1073  function href($item, $href, $atts = '')
1074  {
1075      if (is_array($atts)) {
1076          $atts['href'] = $href;
1077      } else {
1078          if (is_array($href)) {
1079              $href = join_qs($href);
1080          }
1081  
1082          $atts .= ' href="'.$href.'"';
1083      }
1084  
1085      return tag($item, 'a', $atts);
1086  }
1087  
1088  /**
1089   * Renders a &lt;strong&gt; element.
1090   *
1091   * @param  string       $item Enclosed content
1092   * @param  string|array $atts HTML attributes
1093   * @return string HTML
1094   */
1095  
1096  function strong($item, $atts = '')
1097  {
1098      return tag($item, 'strong', $atts);
1099  }
1100  
1101  /**
1102   * Renders a &lt;span&gt; element.
1103   *
1104   * @param  string       $item Enclosed content
1105   * @param  string|array $atts HTML attributes
1106   * @return string HTML
1107   */
1108  
1109  function span($item, $atts = '')
1110  {
1111      return tag($item, 'span', $atts);
1112  }
1113  
1114  /**
1115   * Renders a &lt;pre&gt; element.
1116   *
1117   * @param  string       $item The input string
1118   * @param  string|array $atts HTML attributes
1119   * @return string HTML
1120   * @example
1121   * echo htmlPre('&lt;?php echo "Hello World"; ?&gt;');
1122   */
1123  
1124  function htmlPre($item, $atts = '')
1125  {
1126      if (($item = tag($item, 'code')) === '') {
1127          $item = null;
1128      }
1129  
1130      return tag($item, 'pre', $atts);
1131  }
1132  
1133  /**
1134   * Renders a HTML comment (&lt;!-- --&gt;) element.
1135   *
1136   * @param  string $item The input string
1137   * @return string HTML
1138   * @example
1139   * echo comment('Some HTML comment.');
1140   */
1141  
1142  function comment($item)
1143  {
1144      return '<!-- '.str_replace('--', '- - ', $item).' -->';
1145  }
1146  
1147  /**
1148   * Renders a &lt;small&gt element.
1149   *
1150   * @param  string       $item The input string
1151   * @param  string|array $atts HTML attributes
1152   * @return string HTML
1153   */
1154  
1155  function small($item, $atts = '')
1156  {
1157      return tag($item, 'small', $atts);
1158  }
1159  
1160  /**
1161   * Renders a table data row from an array of content => width pairs.
1162   *
1163   * @param  array        $array Array of content => width pairs
1164   * @param  string|array $atts  Table row atrributes
1165   * @return string A HTML table row
1166   */
1167  
1168  function assRow($array, $atts = '')
1169  {
1170      $out = array();
1171  
1172      foreach ($array as $value => $width) {
1173          $out[] = tda($value, array('width' => (int) $width));
1174      }
1175  
1176      return tr(join('', $out), $atts);
1177  }
1178  
1179  /**
1180   * Renders a table head row from an array of strings.
1181   *
1182   * Takes an argument list of head text strings. i18n is applied to the strings.
1183   *
1184   * @return string HTML
1185   */
1186  
1187  function assHead()
1188  {
1189      $array = func_get_args();
1190      $o = array();
1191  
1192      foreach ($array as $a) {
1193          $o[] = hCell(gTxt($a), '', ' scope="col"');
1194      }
1195  
1196      return tr(join('', $o));
1197  }
1198  
1199  /**
1200   * Renders the ubiquitious popup help button.
1201   *
1202   * The rendered link can be customised via a 'admin_help > {$help_var}'
1203   * pluggable UI callback event.
1204   *
1205   * @param  string $help_var Help topic
1206   * @param  int    $width    Popup window width
1207   * @param  int    $height   Popup window height
1208   * @param  string $class    HTML class
1209   * @return string HTML
1210   */
1211  
1212  function popHelp($help_var, $width = 0, $height = 0, $class = 'pophelp')
1213  {
1214      if (!$help_var) {
1215          return '';
1216      }
1217  
1218      $ui = sp.href('i', HELP_URL.'?item='.urlencode($help_var).'&language='.urlencode(LANG), array(
1219          'class'      => $class,
1220          'rel'        => 'help',
1221          'target'     => '_blank',
1222          'title'      => gTxt('help'),
1223          'aria-label' => gTxt('help'),
1224          'role'       => 'button',
1225          'onclick'    => 'popWin(this.href, '.intval($width).', '.intval($height).'); return false;',
1226      ));
1227  
1228      return pluggable_ui('admin_help', $help_var, $ui, compact('help_var', 'width', 'height', 'class'));
1229  }
1230  
1231  /**
1232   * Renders inline help text.
1233   *
1234   * The help topic is the name of a string that can be found in txp_lang.
1235   *
1236   * The rendered link can be customised via a 'admin_help_field > {$help_var}'
1237   * pluggable UI callback event.
1238   *
1239   * @param  string $help_var   Help topic
1240   * @return string HTML
1241   */
1242  
1243  function fieldHelp($help_var)
1244  {
1245      if (!$help_var) {
1246          return '';
1247      }
1248  
1249      $help_text = gTxt($help_var);
1250  
1251      // If rendered string is the same as the input string, either the l10n
1252      // doesn't exist or the string is missing from txp_lang.
1253      // Either way, no instruction text, no render.
1254      if ($help_var === $help_text) {
1255          return '';
1256      }
1257  
1258      $ui = n.tag($help_text, 'div', array('class' => 'txp-form-field-instructions'));
1259  
1260      return pluggable_ui('admin_help_field', $help_var, $ui, compact('help_var', 'textile'));
1261  }
1262  
1263  /**
1264   * Renders the ubiquitious popup help button with a little less visual noise.
1265   *
1266   * The rendered link can be customised via a 'admin_help > {$help_var}'
1267   * pluggable UI callback event.
1268   *
1269   * @param  string $help_var Help topic
1270   * @param  int    $width    Popup window width
1271   * @param  int    $height   Popup window height
1272   * @return string HTML
1273   */
1274  
1275  function popHelpSubtle($help_var, $width = 0, $height = 0)
1276  {
1277      return popHelp($help_var, $width, $height, 'pophelpsubtle');
1278  }
1279  
1280  /**
1281   * Renders a link that opens a popup tag area.
1282   *
1283   * @param  string $var   Tag name
1284   * @param  string $text  Link text
1285   * @param  array  $atts  Attributes to add to the link
1286   * @return string HTML
1287   */
1288  
1289  function popTag($var, $text, $atts = array())
1290  {
1291      $opts = array(
1292          'event'    => 'tag',
1293          'tag_name' => $var,
1294      ) + $atts;
1295  
1296      return href($text, $opts, array(
1297          'class'  => 'txp-tagbuilder-link',
1298      ));
1299  }
1300  
1301  /**
1302   * Renders a list of tag builder links.
1303   *
1304   * @param  string $type Tag type
1305   * @return string HTML
1306   */
1307  
1308  function popTagLinks($type)
1309  {
1310      global $event;
1311  
1312      include txpath.'/lib/taglib.php';
1313  
1314      $arname = $type.'_tags';
1315  
1316      $out = array();
1317  
1318      foreach ($$arname as $a) {
1319          $out[] = tag(popTag($a, gTxt('tag_'.$a), array('panel' => $event, 'step' => 'build')), 'li');
1320      }
1321  
1322      return n.tag(n.join(n, $out).n, 'ul', array('class' => 'plain-list'));
1323  }
1324  
1325  /**
1326   * Renders an admin-side message text.
1327   *
1328   * @param  string $thing    Subject
1329   * @param  string $thething Predicate (strong)
1330   * @param  string $action   Object
1331   * @return string HTML
1332   */
1333  
1334  function messenger($thing, $thething = '', $action = '')
1335  {
1336      return gTxt($thing).' '.strong($thething).' '.gTxt($action);
1337  }
1338  
1339  /**
1340   * Renders a multi-edit form listing editing methods.
1341   *
1342   * @param  array   $options       array('value' => array( 'label' => '', 'html' => '' ),...)
1343   * @param  string  $event         Event
1344   * @param  string  $step          Step
1345   * @param  int     $page          Page number
1346   * @param  string  $sort          Column sorted by
1347   * @param  string  $dir           Sorting direction
1348   * @param  string  $crit          Search criterion
1349   * @param  string  $search_method Search method
1350   * @return string  HTML
1351   * @example
1352   * echo form(
1353   *     multi_edit(array(
1354   *         'feature' => array('label' => 'Feature', 'html' => yesnoRadio('is_featured', 1)),
1355   *         'delete'  => array('label' => 'Delete'),
1356   *     ))
1357   * );
1358   */
1359  
1360  function multi_edit($options, $event = null, $step = null, $page = '', $sort = '', $dir = '', $crit = '', $search_method = '')
1361  {
1362      $html = $methods = array();
1363      $methods[''] = gTxt('with_selected_option');
1364  
1365      if ($event === null) {
1366          global $event;
1367      }
1368  
1369      if ($step === null) {
1370          $step = $event.'_multi_edit';
1371      }
1372  
1373      callback_event_ref($event.'_ui', 'multi_edit_options', 0, $options);
1374  
1375      foreach ($options as $value => $option) {
1376          if (is_array($option)) {
1377              $methods[$value] = $option['label'];
1378  
1379              if (isset($option['html'])) {
1380                  $html[$value] = n.tag($option['html'], 'div', array(
1381                      'class'             => 'multi-option',
1382                      'data-multi-option' => $value,
1383                  ));
1384              }
1385          } else {
1386              $methods[$value] = $option;
1387          }
1388      }
1389  
1390      return n.tag(
1391          selectInput('edit_method', $methods, '').
1392          eInput($event).
1393          sInput($step).
1394          hInput('page', $page).
1395          ($sort ? hInput('sort', $sort).hInput('dir', $dir) : '').
1396          ($crit !== '' ? hInput('crit', $crit).hInput('search_method', $search_method) : '').
1397          join('', $html).
1398          fInput('submit', '', gTxt('go')), 'div', array('class' => 'multi-edit'));
1399  }
1400  
1401  /**
1402   * Renders a widget to select various amounts to page lists by.
1403   *
1404   * The rendered options can be changed via a '{$event}_ui > pageby_values'
1405   * callback event.
1406   *
1407   * @param  string      $event Event
1408   * @param  int         $val   Current setting
1409   * @param  string|null $step  Step
1410   * @return string HTML
1411   */
1412  
1413  function pageby_form($event, $val, $step = null)
1414  {
1415      $vals = array(15, 25, 50, 100);
1416      callback_event_ref($event.'_ui', 'pageby_values', 0, $vals);
1417  
1418      if ($step === null) {
1419          $step = $event.'_change_pageby';
1420      }
1421  
1422      if (empty($val)) {
1423          $val = $vals[0];
1424      }
1425  
1426      $out = array();
1427  
1428      foreach ($vals as $qty) {
1429          if ($qty == $val) {
1430              $class = 'navlink-active';
1431              $aria_pressed = 'true';
1432          } else {
1433              $class = 'navlink';
1434              $aria_pressed = 'false';
1435          }
1436  
1437          $out[] = href($qty, array(
1438              'event'      => $event,
1439              'step'       => $step,
1440              'qty'        => $qty,
1441              '_txp_token' => form_token(),
1442          ), array(
1443              'class'        => $class,
1444              'title'        => gTxt('view_per_page', array('{page}' => $qty)),
1445              'aria-pressed' => $aria_pressed,
1446              'role'         => 'button',
1447          ));
1448      }
1449  
1450      return n.tag(join('', $out), 'div', array('class' => 'nav-tertiary pageby'));
1451  }
1452  
1453  /**
1454   * Renders an upload form.
1455   *
1456   * The rendered form can be customised via the '{$event}_ui > upload_form'
1457   * pluggable UI callback event.
1458   *
1459   * @param  string       $label         File name label. May be empty
1460   * @param  string       $pophelp       Help item
1461   * @param  string       $step          Step
1462   * @param  string       $event         Event
1463   * @param  string       $id            File id
1464   * @param  int          $max_file_size Maximum allowed file size
1465   * @param  string       $label_id      HTML id attribute for the filename input element
1466   * @param  string       $class         HTML class attribute for the form element
1467   * @param  string|array $wraptag_val   Tag to wrap the value / label in, or empty to omit
1468   * @return string HTML
1469   */
1470  
1471  function upload_form($label, $pophelp = '', $step, $event, $id = '', $max_file_size = 1000000, $label_id = '', $class = '', $wraptag_val = array('div', 'div'))
1472  {
1473      extract(gpsa(array(
1474          'page',
1475          'sort',
1476          'dir',
1477          'crit',
1478          'search_method',
1479      )));
1480  
1481      if (is_array($search_method)) {
1482          $search_method = join(',', $search_method);
1483      }
1484  
1485      if (!$label_id) {
1486          $label_id = $event.'-upload';
1487      }
1488  
1489      if ($wraptag_val) {
1490          $wraptag_class = 'txp-form-field file-uploader';
1491      } else {
1492          $wraptag_class = 'inline-file-uploader';
1493      }
1494  
1495      $argv = func_get_args();
1496  
1497      return pluggable_ui($event.'_ui', 'upload_form',
1498          n.tag(
1499              (!empty($max_file_size) ? hInput('MAX_FILE_SIZE', $max_file_size) : '').
1500              eInput($event).
1501              sInput($step).
1502              hInput('id', $id).
1503              hInput('sort', $sort).
1504              hInput('dir', $dir).
1505              hInput('page', $page).
1506              hInput('search_method', $search_method).
1507              hInput('crit', $crit).
1508              inputLabel(
1509                  $label_id,
1510                  fInput('file', 'thefile', '', '', '', '', '', '', $label_id).
1511                  fInput('submit', '', gTxt('upload')),
1512                  $label,
1513                  array($pophelp, 'instructions_'.$pophelp),
1514                  $wraptag_class,
1515                  $wraptag_val
1516              ).
1517              tInput().n,
1518              'form', array(
1519                  'class'   => 'upload-form'.($class ? ' '.trim($class) : ''),
1520                  'method'  => 'post',
1521                  'enctype' => 'multipart/form-data',
1522                  'action'  => 'index.php',
1523              )
1524          ),
1525          $argv);
1526  }
1527  
1528  /**
1529   * Renders an admin-side search form.
1530   *
1531   * @param  string $event          Event
1532   * @param  string $step           Step
1533   * @param  string $crit           Search criterion
1534   * @param  array  $methods        Valid search methods
1535   * @param  string $method         Actual search method
1536   * @param  string $default_method Default search method
1537   * @return string HTML
1538   */
1539  
1540  function search_form($event, $step, $crit, $methods, $method, $default_method)
1541  {
1542      $method = ($method) ? $method : $default_method;
1543  
1544      return form(
1545          graf(
1546              tag(gTxt('search'), 'label', array('for' => $event.'-search')).
1547              selectInput('search_method', $methods, $method, '', '', $event.'-search').
1548              fInput('text', 'crit', $crit, 'input-medium', '', '', INPUT_MEDIUM).
1549              eInput($event).
1550              sInput($step).
1551              fInput('submit', 'search', gTxt('go'))
1552          ), '', '', 'get', 'search-form');
1553  }
1554  
1555  /**
1556   * Renders a dropdown for selecting Textfilter method preferences.
1557   *
1558   * @param  string $name Element name
1559   * @param  string $val  Current value
1560   * @param  string $id   HTML id attribute for the select input element
1561   * @return string HTML
1562   */
1563  
1564  function pref_text($name, $val, $id = '')
1565  {
1566      $id = ($id) ? $id : $name;
1567      $vals = Txp::get('\Textpattern\Textfilter\Registry')->getMap();
1568  
1569      return selectInput($name, $vals, $val, '', '', $id);
1570  }
1571  
1572  /**
1573   * Attaches a HTML fragment to a DOM node.
1574   *
1575   * @param  string $id        Target DOM node's id
1576   * @param  string $content   HTML fragment
1577   * @param  string $noscript  Noscript alternative
1578   * @param  string $wraptag   Wrapping HTML element
1579   * @param  string $wraptagid Wrapping element's HTML id
1580   * @return string HTML/JS
1581   */
1582  
1583  function dom_attach($id, $content, $noscript = '', $wraptag = 'div', $wraptagid = '')
1584  {
1585      $id = escape_js($id);
1586      $content = escape_js($content);
1587      $wraptag = escape_js($wraptag);
1588      $wraptagid = escape_js($wraptagid);
1589  
1590      $js = <<<EOF
1591          $(function ()
1592          {
1593              $('#{$id}').append($('<{$wraptag} />').attr('id', '{$wraptagid}').html('{$content}'));
1594          });
1595  EOF;
1596  
1597      return script_js($js, (string) $noscript);
1598  }
1599  
1600  /**
1601   * Renders a &lt:script&gt; element.
1602   *
1603   * The $route parameter allows script_js() to be included in fixed page
1604   * locations (e.g. prior to the &lt;/body&gt; tag) but to only render
1605   * its content if the event / step match.
1606   *
1607   * @param  string     $js    JavaScript code
1608   * @param  int|string $flags Flags TEXTPATTERN_SCRIPT_URL | TEXTPATTERN_SCRIPT_ATTACH_VERSION, or noscript alternative if a string
1609   * @param  array      $route Optional events/steps upon which to add the script
1610   * @return string HTML with embedded script element
1611   * @example
1612   * echo script_js('/js/script.js', TEXTPATTERN_SCRIPT_URL);
1613   */
1614  
1615  function script_js($js, $flags = '', $route = array())
1616  {
1617      global $event, $step;
1618  
1619      $targetEvent = empty($route[0]) ? null : (array)$route[0];
1620      $targetStep = empty($route[1]) ? null : (array)$route[1];
1621  
1622      if (($targetEvent === null || in_array($event, $targetEvent)) && ($targetStep === null || in_array($step, $targetStep))) {
1623          if (is_int($flags)) {
1624              if ($flags & TEXTPATTERN_SCRIPT_URL) {
1625                  if ($flags & TEXTPATTERN_SCRIPT_ATTACH_VERSION && strpos(txp_version, '-dev') === false) {
1626                      $ext = pathinfo($js, PATHINFO_EXTENSION);
1627  
1628                      if ($ext) {
1629                          $js = substr($js, 0, (strlen($ext) + 1) * -1);
1630                          $ext = '.'.$ext;
1631                      }
1632  
1633                      $js .= '.v'.txp_version.$ext;
1634                  }
1635  
1636                  return n.tag(null, 'script', array('src' => $js));
1637              }
1638          }
1639  
1640          $js = preg_replace('#<(/?)(script)#i', '\\x3c$1$2', $js);
1641  
1642          $out = n.tag(n.trim($js).n, 'script');
1643  
1644          if ($flags) {
1645              $out .= n.tag(n.trim($flags).n, 'noscript');
1646          }
1647  
1648          return $out;
1649      }
1650  
1651      return '';
1652  }
1653  
1654  /**
1655   * Renders a "Details" toggle checkbox.
1656   *
1657   * @param  string $classname Unique identfier. The cookie's name will be derived from this value
1658   * @param  bool   $form      Create as a stand-along &lt;form&gt; element
1659   * @return string HTML
1660   */
1661  
1662  function toggle_box($classname, $form = false)
1663  {
1664      $name = 'cb_toggle_'.$classname;
1665      $id = escape_js($name);
1666      $class = escape_js($classname);
1667  
1668      $out = checkbox($name, 1, cs('toggle_'.$classname), 0, $name).
1669          n.tag(gTxt('detail_toggle'), 'label', array('for' => $name));
1670  
1671      $js = <<<EOF
1672          $(function ()
1673          {
1674              $('input')
1675                  .filter(function () {
1676                      if ($(this).attr('id') === '{$id}') {
1677                          setClassDisplay('{$class}', $(this).is(':checked'));
1678                          return true;
1679                      }
1680                  })
1681                  .change(function () {
1682                      toggleClassRemember('{$class}');
1683                  });
1684          });
1685  EOF;
1686  
1687      $out .= script_js($js);
1688  
1689      if ($form) {
1690          return form($out);
1691      }
1692  
1693      return $out;
1694  }
1695  
1696  /**
1697   * Renders a checkbox to set/unset a browser cookie.
1698   *
1699   * @param  string $classname Label text. The cookie's name will be derived from this value
1700   * @param  bool   $form      Create as a stand-along &lt;form&gt; element
1701   * @return string HTML
1702   */
1703  
1704  function cookie_box($classname, $form = true)
1705  {
1706      $name = 'cb_'.$classname;
1707      $id = escape_js($name);
1708      $class = escape_js($classname);
1709  
1710      if (cs('toggle_'.$classname)) {
1711          $value = 1;
1712      } else {
1713          $value = 0;
1714      }
1715  
1716      $newvalue = 1 - $value;
1717  
1718      $out = checkbox($name, 1, (bool) $value, 0, $name).
1719          n.tag(gTxt($classname), 'label', array('for' => $name));
1720  
1721      $js = <<<EOF
1722          $(function ()
1723          {
1724              $('input')
1725                  .filter(function () {
1726                      if ($(this).attr('id') === '{$id}') {
1727                          return true;
1728                      }
1729                  })
1730                  .change(function () {
1731                      setClassRemember('{$class}', $newvalue);
1732                      $(this).parents('form').submit();
1733                  });
1734          });
1735  EOF;
1736  
1737      $out .= script_js($js);
1738  
1739      if ($form) {
1740          if (serverSet('QUERY_STRING')) {
1741              $action = 'index.php?'.serverSet('QUERY_STRING');
1742          } else {
1743              $action = 'index.php';
1744          }
1745  
1746          $out .= eInput(gps('event')).tInput();
1747  
1748          return tag($out, 'form', array(
1749              'class'  => $name,
1750              'method' => 'post',
1751              'action' => $action,
1752          ));
1753      }
1754  
1755      return $out;
1756  }
1757  
1758  /**
1759   * Renders a &lt;fieldset&gt; element.
1760   *
1761   * @param  string $content Enclosed content
1762   * @param  string $legend  Legend text
1763   * @param  string $id      HTML id attribute
1764   * @return string HTML
1765   */
1766  
1767  function fieldset($content, $legend = '', $id = '')
1768  {
1769      return tag(trim(tag($legend, 'legend').n.$content), 'fieldset', array('id' => $id));
1770  }
1771  
1772  /**
1773   * Renders a link element to hook up txpAsyncHref() with request parameters.
1774   *
1775   * See this function's JavaScript companion, txpAsyncHref(), in textpattern.js.
1776   *
1777   * @param  string       $item  Link text
1778   * @param  array        $parms Request parameters; array keys are 'event', 'step', 'thing', 'property'
1779   * @param  string|array $atts  HTML attributes
1780   * @return string HTML
1781   * @since  4.5.0
1782   * @example
1783   * echo asyncHref('Disable', array(
1784   *     'event'    => 'myEvent',
1785   *     'step'     => 'myStep',
1786   *     'thing'    => 'status',
1787   *     'property' => 'disable',
1788   * ));
1789   */
1790  
1791  function asyncHref($item, $parms, $atts = '')
1792  {
1793      global $event, $step;
1794  
1795      $parms = lAtts(array(
1796          'event'    => $event,
1797          'step'     => $step,
1798          'thing'    => '',
1799          'property' => '',
1800      ), $parms);
1801  
1802      $class = $parms['step'].' async';
1803  
1804      if (is_array($atts)) {
1805          $atts['class'] = $class;
1806      } else {
1807          $atts .= ' class="'.txpspecialchars($class).'"';
1808      }
1809  
1810      return href($item, join_qs($parms), $atts);
1811  }
1812  
1813  /**
1814   * Renders an array of items as a HTML list.
1815   *
1816   * This function is used for tag handler functions. Creates a HTML list markup
1817   * from an array of items.
1818   *
1819   * @param   array  $list
1820   * @param   string $wraptag    The HTML element
1821   * @param   string $break      The HTML break element
1822   * @param   string $class      Class applied to the wraptag
1823   * @param   string $breakclass Class applied to break tag
1824   * @param   string $atts       HTML attributes applied to the wraptag
1825   * @param   string $breakatts  HTML attributes applied to the break tag
1826   * @param   string $id         HTML id applied to the wraptag
1827   * @return  string HTML
1828   * @package HTML
1829   * @example
1830   * echo doWrap(array('item1', 'item2'), 'div', 'p');
1831   */
1832  
1833  function doWrap($list, $wraptag, $break, $class = '', $breakclass = '', $atts = '', $breakatts = '', $id = '')
1834  {
1835      if (!$list) {
1836          return '';
1837      }
1838  
1839      if ($id) {
1840          $atts .= ' id="'.txpspecialchars($id).'"';
1841      }
1842  
1843      if ($class) {
1844          $atts .= ' class="'.txpspecialchars($class).'"';
1845      }
1846  
1847      if ($breakclass) {
1848          $breakatts .= ' class="'.txpspecialchars($breakclass).'"';
1849      }
1850  
1851      // Non-enclosing breaks.
1852      if (!preg_match('/^\w+$/', $break) or $break == 'br' or $break == 'hr') {
1853          if ($break == 'br' or $break == 'hr') {
1854              $break = "<$break $breakatts/>".n;
1855          }
1856  
1857          return ($wraptag) ?    tag(join($break, $list), $wraptag, $atts) :    join($break, $list);
1858      }
1859  
1860      return ($wraptag)
1861          ? tag(n.tag(join("</$break>".n."<{$break}{$breakatts}>", $list), $break, $breakatts).n, $wraptag, $atts)
1862          : tag(n.join("</$break>".n."<{$break}{$breakatts}>".n, $list).n, $break, $breakatts);
1863  }
1864  
1865  /**
1866   * Renders anything as a HTML tag.
1867   *
1868   * Used for tag handler functions.
1869   *
1870   * If $content is empty, renders a self-closing tag.
1871   *
1872   * @param   string $content The wrapped item
1873   * @param   string $tag     The HTML tag
1874   * @param   string $class   HTML class
1875   * @param   string $atts    HTML attributes
1876   * @param   string $id      HTML id
1877   * @return  string HTML
1878   * @package HTML
1879   * @example
1880   * echo doTag('', 'meta', '', 'name="description" content="Some content"');
1881   */
1882  
1883  function doTag($content, $tag, $class = '', $atts = '', $id = '')
1884  {
1885      if ($id) {
1886          $atts .= ' id="'.txpspecialchars($id).'"';
1887      }
1888  
1889      if ($class) {
1890          $atts .= ' class="'.txpspecialchars($class).'"';
1891      }
1892  
1893      if (!$tag) {
1894          return $content;
1895      }
1896  
1897      return ($content) ? tag($content, $tag, $atts) : "<$tag $atts />";
1898  }
1899  
1900  /**
1901   * Renders a label.
1902   *
1903   * This function is mostly used for rendering headings in tag handler functions.
1904   *
1905   * If no $labeltag is given, label is separated from the content with
1906   * a &lt;br&gt;.
1907   *
1908   * @param   string $label    The label
1909   * @param   string $labeltag The HTML element
1910   * @return  string HTML
1911   * @package HTML
1912   * @example
1913   * echo doLabel('My label', 'h3');
1914   */
1915  
1916  function doLabel($label = '', $labeltag = '')
1917  {
1918      if ($label) {
1919          return (empty($labeltag) ? $label.'<br />' : tag($label, $labeltag));
1920      }
1921  
1922      return '';
1923  }

title

Description

title

Description

title

Description

title

title

Body