/extensions/DataCenter/DataCenter.ui.php

https://github.com/ChuguluGames/mediawiki-svn · PHP · 1145 lines · 866 code · 44 blank · 235 comment · 66 complexity · 7543ed26bc970702ee580fb8b2e758ec MD5 · raw file

  1. <?php
  2. /**
  3. * UI Class for DataCenter extension
  4. *
  5. * @file
  6. * @ingroup Extensions
  7. */
  8. class DataCenterCss {
  9. /* Static Functions */
  10. public static function toAttributes( array $attributes ) {
  11. $cssOutput = '';
  12. foreach( $attributes as $name => $value ) {
  13. if ( !is_int( $name ) ) {
  14. $cssOutput = $name . ':' . $value . ';';
  15. }
  16. }
  17. return $cssOutput;
  18. }
  19. }
  20. class DataCenterJs {
  21. /* Static Functions */
  22. public static function chain( $functions, $end = true ) {
  23. $jsFunctions = array();
  24. foreach( $functions as $name => $arguments ) {
  25. if ( is_int( $name ) ) {
  26. $jsFunctions[] = sprintf( '%s()', $arguments );
  27. } elseif ( is_array( $arguments ) ) {
  28. $jsFunctions[] = sprintf(
  29. '%s(%s)', $name, implode( ',', $arguments )
  30. );
  31. } else {
  32. $jsFunctions[] = sprintf( '%s(%s)', $name, $arguments );
  33. }
  34. }
  35. return implode( '.', $jsFunctions ) . ( $end ? ';' : '' );
  36. }
  37. /**
  38. * Escapes a JavaScript string to make it safe to use anywhere
  39. * @param $string String: String to escape
  40. */
  41. public static function escape( $string ) {
  42. return Xml::escapeJSString( $string );
  43. }
  44. /**
  45. * Converts a PHP value to a javascript object
  46. * @param value Associative Array to convert
  47. */
  48. public static function toObject( $values ) {
  49. // Arrays
  50. if ( is_array( $values ) ) {
  51. $jsValues = array();
  52. foreach( $values as $key => $value ) {
  53. if ( is_array( $value ) ) {
  54. $jsValues[] = $key . ':' . self::toObject( $value );
  55. } else {
  56. $jsValues[] = $key . ':' . self::toScalar( $value );
  57. }
  58. }
  59. return '{' . implode( ',', $jsValues ) . '}';
  60. }
  61. return 'null';
  62. }
  63. /**
  64. * Converts a PHP value to a javascript array
  65. * @param value Array or Scalar to convert
  66. */
  67. public static function toArray( $values ) {
  68. // Arrays
  69. if ( is_array( $values ) ) {
  70. $jsValues = array();
  71. foreach( $values as $value ) {
  72. $jsValues[] = self::toScalar( $value );
  73. }
  74. return '[' . implode( ',', $jsValues ) . ']';
  75. }
  76. // Scalars
  77. if ( is_scalar( $values ) ) {
  78. return '[' . self::toScalar( $values ) . ']';
  79. }
  80. return 'null';
  81. }
  82. /**
  83. * Converts a PHP value to a javascript value
  84. * @param value Array or Scalar to convert, if value is string
  85. * it will be escaped and surrounded by quotes
  86. * unless it is already surrounded by ' quotes,
  87. * in which case the ' quotes will be removed and
  88. * the value will be used as a statement
  89. */
  90. public static function toScalar( $value ) {
  91. // Arrays
  92. if ( is_array( $value ) ) {
  93. return "'" . self::escape( implode( $value ) ) . "'";
  94. }
  95. // Scalars
  96. if ( is_scalar( $value ) ) {
  97. // Numbers
  98. if ( is_numeric( $value ) ) {
  99. return $value;
  100. }
  101. // Strings
  102. if ( is_string( $value ) ) {
  103. // Checks if string is surrouded with ' quotes
  104. if (
  105. substr( $value, 0, 1 ) == "'" &&
  106. substr( $value, -1, 1 ) == "'"
  107. ) {
  108. // Formats as statement
  109. return trim( $value, "'" );
  110. } else {
  111. // Formats as string
  112. return "'" . self::escape( $value ) . "'";
  113. }
  114. }
  115. // Booleans
  116. if ( is_bool( $value ) ) {
  117. return $value ? 'true' : 'false';
  118. }
  119. // Nulls
  120. if ( is_null( $value ) ) {
  121. return 'null';
  122. }
  123. }
  124. return 'null';
  125. }
  126. /**
  127. * Builds an annonomous javascript function declaration
  128. * @param $arguments Array: array of argument names to accept
  129. * @param $body String: code for body
  130. */
  131. public static function buildFunction( $arguments, $body ) {
  132. if ( is_array( $arguments ) ) {
  133. return sprintf(
  134. 'function(%s){%s}', implode( ',', $arguments ), $body
  135. );
  136. } elseif ( $arguments !== null ) {
  137. return sprintf( 'function(%s){%s}', $arguments, $body );
  138. } else {
  139. return sprintf( 'function(){%s}', $body );
  140. }
  141. }
  142. /**
  143. * Builds an annonomous javascript function declaration
  144. * @param $prototype String
  145. * @param $arguments Array: array of argument names to accept
  146. */
  147. public static function buildInstance( $prototype, $arguments = array() ) {
  148. if ( !is_array( $arguments ) ) {
  149. $arguments = array( $arguments );
  150. }
  151. return sprintf(
  152. 'new %s(%s)', $prototype, implode( ',', $arguments )
  153. );
  154. }
  155. /**
  156. * Creates code to call a javascript function
  157. * @param function String of name of function
  158. * @param arguments Array of argument values to pass
  159. */
  160. public static function callFunction(
  161. $function,
  162. $arguments = array(),
  163. $end = true
  164. ) {
  165. if ( !is_array( $arguments ) ) {
  166. $arguments = array( $arguments );
  167. }
  168. return sprintf(
  169. '%s(%s)', $function, implode( ',', $arguments )
  170. ) . ( $end ? ';' : '' );
  171. }
  172. public static function declareVar( $name, $value = 'null', $end = true ) {
  173. return sprintf( 'var %s=%s', $name, $value ) . ( $end ? ';' : '' );
  174. }
  175. public static function declareVars( array $vars, $end = true ) {
  176. $jsOutput = '';
  177. foreach( $vars as $name => $value ) {
  178. if ( is_int( $name ) ) {
  179. $name = $value;
  180. $value = 'null';
  181. }
  182. $jsOutput .= sprintf(
  183. 'var %s=%s', $name, $value
  184. ) . ( $end ? ';' : '' );
  185. }
  186. return $jsOutput;
  187. }
  188. public static function declareInstance(
  189. $name,
  190. $prototype,
  191. $arguments = array(),
  192. $end = true
  193. ) {
  194. if ( !is_array( $arguments ) ) {
  195. $arguments = array( $arguments );
  196. }
  197. return sprintf(
  198. '%s=new %s(%s)',
  199. $name,
  200. $prototype,
  201. implode( ',', $arguments )
  202. ) . ( $end ? ';' : '' );
  203. }
  204. /**
  205. * Builds JavaScript effect
  206. * @param options Array of effect parameters containing...
  207. * script JavaScript to run, with sprintf syntax for
  208. * including fields in the order listed
  209. * field Singular form of fields
  210. * fields Array of fields to supply to sprintf function
  211. * providing a way to get field information,
  212. * accessable to javascript in order given
  213. *
  214. * Example:
  215. * array(
  216. * 'script' => 'alert( 'Row ID: ' + %d )',
  217. * 'field' => 'id'
  218. * )
  219. * @param row DataCenterDBRow object from which to extract
  220. * fields from
  221. */
  222. public static function buildEffect( $script, $fields = null ) {
  223. // Checks for...
  224. if (
  225. // Required types
  226. ( is_array( $fields ) ) &&
  227. // Required values
  228. ( $script !== null )
  229. ) {
  230. // Loops over each field
  231. foreach ( $fields as $field => $value ) {
  232. // Replaces reference with value
  233. $script = str_replace( '{' . $field . '}', $value, $script );
  234. }
  235. // Returns processed script
  236. return $script;
  237. } else {
  238. // Returns unprocessed script
  239. return $script;
  240. }
  241. }
  242. }
  243. class DataCenterXml {
  244. /* Private Static Members */
  245. /**
  246. * Cached value of the DataCenter special page full URL
  247. */
  248. private static $urlBase;
  249. /* Static Functions */
  250. /**
  251. * Builds an XML string for a complete table tag
  252. * @param attributes Optional Array of XML attributes
  253. * @param content... Any number of Strings of XML content
  254. */
  255. public static function table() {
  256. $arguments = func_get_args();
  257. $attributes = array(
  258. 'cellpadding' => 0, 'cellspacing' => 0, 'border' => 0,
  259. );
  260. if ( count( $arguments ) > 0 && is_array( $arguments[0] ) ) {
  261. $attributes = array_merge( $attributes, $arguments[0] );
  262. array_shift( $arguments );
  263. }
  264. if ( count( $arguments ) > 0 ) {
  265. return self::tag( 'table', $attributes, implode( $arguments ) );
  266. }
  267. return null;
  268. }
  269. /**
  270. * Builds an XML string for a complete table row tag
  271. * @param attributes Optional Array of XML attributes
  272. * @param content... Any number of Strings of XML content
  273. */
  274. public static function row() {
  275. $arguments = func_get_args();
  276. $attributes = array();
  277. if ( count( $arguments ) > 0 && is_array( $arguments[0] ) ) {
  278. $attributes = array_merge( $attributes, $arguments[0] );
  279. array_shift( $arguments );
  280. }
  281. if ( count( $arguments ) > 0 ) {
  282. return self::tag( 'tr', $attributes, implode( $arguments ) );
  283. }
  284. return null;
  285. }
  286. /**
  287. * Builds an XML string for a complete table cell tag
  288. * @param attributes Optional Array of XML attributes
  289. * @param content String of XML content
  290. */
  291. public static function cell() {
  292. $arguments = func_get_args();
  293. $attributes = array();
  294. if ( count( $arguments ) > 0 && is_array( $arguments[0] ) ) {
  295. $attributes = array_merge( $attributes, $arguments[0] );
  296. array_shift( $arguments );
  297. }
  298. if ( count( $arguments ) > 0 ) {
  299. return self::tag( 'td', $attributes, $arguments[0] );
  300. } else {
  301. return self::tag( 'td', $attributes );
  302. }
  303. }
  304. /**
  305. * Builds an XML string for a complete table heading cell tag
  306. * @param attributes Optional Array of XML attributes
  307. * @param content String of XML content
  308. */
  309. public static function headingCell() {
  310. $arguments = func_get_args();
  311. $attributes = array();
  312. if ( count( $arguments ) > 0 && is_array( $arguments[0] ) ) {
  313. $attributes = array_merge( $attributes, $arguments[0] );
  314. array_shift( $arguments );
  315. }
  316. if ( count( $arguments ) > 0 ) {
  317. return self::tag( 'th', $attributes, $arguments[0] );
  318. } else {
  319. return self::tag( 'th', $attributes );
  320. }
  321. }
  322. /**
  323. * Builds an XML string for a complete div
  324. * @param attributes Array of XML attributes
  325. * @param content
  326. */
  327. public static function div() {
  328. $arguments = func_get_args();
  329. $attributes = array();
  330. if ( count( $arguments ) > 0 && is_array( $arguments[0] ) ) {
  331. $attributes = array_merge( $attributes, $arguments[0] );
  332. array_shift( $arguments );
  333. }
  334. if ( count( $arguments ) > 0 ) {
  335. return self::tag( 'div', $attributes, $arguments[0] );
  336. } else {
  337. return self::tag( 'div', $attributes );
  338. }
  339. }
  340. /**
  341. * Builds an XML string for a complete span
  342. * @param attributes Array of XML attributes
  343. * @param content
  344. */
  345. public static function span() {
  346. $arguments = func_get_args();
  347. $attributes = array();
  348. if ( count( $arguments ) > 0 && is_array( $arguments[0] ) ) {
  349. $attributes = array_merge( $attributes, $arguments[0] );
  350. array_shift( $arguments );
  351. }
  352. if ( count( $arguments ) > 0 ) {
  353. return self::tag( 'span', $attributes, $arguments[0] );
  354. } else {
  355. return self::tag( 'span', $attributes );
  356. }
  357. }
  358. /**
  359. * Builds an XML string for a complete input
  360. * @param $attributes Array: XML attributes
  361. */
  362. public static function input( array $attributes = array() ) {
  363. return self::tag( 'div', $attributes );
  364. }
  365. /**
  366. * Builds an XML string for a complete tag
  367. * @param tag Name of tag
  368. * @param attributes Array of XML attributes
  369. * @param contents Array of Strings or String of XML or null to
  370. * make tag self-closing
  371. */
  372. public static function tag(
  373. $tag,
  374. array $attributes = array(),
  375. $contents = null
  376. ) {
  377. if ( is_array( $contents ) && count( $contents ) > 1 ) {
  378. return Xml::tags( $tag, $attributes, implode( $contents ) );
  379. } else {
  380. return Xml::tags(
  381. $tag, $attributes, $contents, ( $contents !== null )
  382. );
  383. }
  384. }
  385. /**
  386. * Builds an XML string for a tag opening
  387. * @param $tag String: name of tag
  388. * @param $attributes Array: XML attributes
  389. */
  390. public static function open( $tag, array $attributes = array() ) {
  391. return Xml::openElement( $tag, $attributes );
  392. }
  393. /**
  394. * Builds an XML string for a tag closing
  395. * @param $tag String: name of tag
  396. */
  397. public static function close( $tag ) {
  398. return Xml::closeElement( $tag );
  399. }
  400. /**
  401. * Builds an XML string for clearing floating
  402. */
  403. public static function clearFloating() {
  404. return Xml::element( 'div', array( 'style' => 'clear:both' ), ' ' );
  405. }
  406. /**
  407. * Builds an XML string for a stand-alone block of javascript
  408. * @param script String of raw text to use as script contents or
  409. * Array of attributes such as src
  410. */
  411. public static function script( $script ) {
  412. if ( is_array( $script ) ) {
  413. return Xml::element(
  414. 'script',
  415. array_merge(
  416. $script,
  417. array(
  418. 'type' => 'text/javascript', 'language' => 'javascript'
  419. )
  420. ),
  421. '//'
  422. );
  423. } else {
  424. return Xml::tags(
  425. 'script',
  426. array( 'type' => 'text/javascript', 'language' => 'javascript' ),
  427. $script
  428. );
  429. }
  430. }
  431. /**
  432. * Builds a URL from link parameters
  433. * @param parameters Array of link parameters containing...
  434. * page String of page name
  435. * type String of type name
  436. * action String of action name
  437. * id Scalar of ID of row
  438. * parameter Scalar parameter or Array of parameters
  439. */
  440. public static function url( array $parameters ) {
  441. global $wgTitle;
  442. // Gets the base url
  443. if ( !self::$urlBase ) {
  444. self::$urlBase = $wgTitle->getFullUrl();
  445. }
  446. $parameters = array_merge(
  447. array(
  448. 'page' => null,
  449. 'type' => null,
  450. 'id' => null,
  451. 'action' => null,
  452. 'parameter' => null,
  453. 'limit' => null,
  454. 'offset' => null,
  455. ),
  456. $parameters
  457. );
  458. $url = self::$urlBase;
  459. // Checks if the page is set now
  460. if ( $parameters['page'] !== null ) {
  461. // Adds page to url
  462. $url .= '/' . $parameters['page'];
  463. if ( $parameters['type'] !== null ) {
  464. // Adds type to url
  465. $url .= ':' . $parameters['type'];
  466. // Checks if object id was given
  467. if ( $parameters['id'] !== null ) {
  468. // Adds id to url
  469. $url .= ':' . $parameters['id'];
  470. }
  471. }
  472. // Checks if action was given
  473. if ( $parameters['action'] !== null ) {
  474. // Adds action to url
  475. $url .= '/' . $parameters['action'];
  476. // Checks if parameter was given
  477. if ( $parameters['parameter'] !== null ) {
  478. if ( is_array( $parameters['parameter'] ) ) { // Adds parameter to url
  479. $url .= ':' . implode( ',', $parameters['parameter'] );
  480. } else {
  481. // Adds parameter to url
  482. $url .= ':' . $parameters['parameter'];
  483. }
  484. }
  485. } elseif ( $parameters['limit'] !== null ) {
  486. $url .= '/';
  487. }
  488. if ( $parameters['limit'] !== null ) {
  489. $url .= '/' . $parameters['limit'];
  490. if ( $parameters['offset'] !== null ) {
  491. $url .= ':' . $parameters['offset'];
  492. }
  493. }
  494. }
  495. // Returns url
  496. return $url;
  497. }
  498. /**
  499. * Builds an XML string for an icon
  500. * @param name Name of icon
  501. * @param enabled Boolean of enabled state
  502. * @param contents Array of Strings or String of XML or null to
  503. * make tag self-closing
  504. */
  505. public static function icon(
  506. $name,
  507. $enabled = true,
  508. array $options = array()
  509. ) {
  510. global $wgScriptPath;
  511. return Xml::element(
  512. 'img',
  513. array_merge(
  514. $options,
  515. array(
  516. 'src' => $wgScriptPath .
  517. '/extensions/DataCenter/Resources/Icons/' . $name .
  518. ( !$enabled ? '-disabled' : '' ) . '.png',
  519. 'border' => 0
  520. )
  521. )
  522. );
  523. }
  524. /**
  525. * Builds an XML string for a link
  526. * @param $label String: raw text to use as label
  527. * @param $parameters Array: link parameters for self::url
  528. */
  529. public static function link( $label = null, array $parameters ) {
  530. return Xml::element(
  531. 'a', array( 'href' => self::url( $parameters ) ), $label
  532. );
  533. }
  534. /**
  535. * Builds an array of XML Attributes for javascript onclick linking
  536. * @param options Mixed Array of link parameters, each
  537. * containing...
  538. * link Array of link parameters for DataCenterUI::url
  539. * field Singular form of fields
  540. * fields List of from => to pairs of fields from row to
  541. * inject into link, with the special from fields of
  542. * '#type' and '#category' also made available
  543. * Example:
  544. * array(
  545. * 'link' => array(
  546. * 'page' => 'assets',
  547. * 'action' => 'view',
  548. * ),
  549. * 'fields' => array(
  550. * '#type' => 'type',
  551. * 'id' => 'id',
  552. * )
  553. * )
  554. * @param row DataCenterDBRow object from which to extract
  555. * field/fields from
  556. */
  557. public static function buildLink( $options, $row = null ) {
  558. // Checks if row was given
  559. if ( isset( $options['page'] ) && $row instanceof DataCenterDBRow ) {
  560. // Transforms options based on row
  561. $fields = array_merge(
  562. $row->get(),
  563. array(
  564. 'type' => $row->getType(),
  565. 'category' => $row->getCategory(),
  566. )
  567. );
  568. // Loops over each field
  569. foreach ( $fields as $key => $value ) {
  570. // Loops over each option
  571. foreach ( $options as $option => $reference ) {
  572. if ( is_array( $options[$option] ) ) {
  573. for ( $i = 0; $i < count( $options[$option] ); $i++ ) {
  574. // Checks if value is reference to row field
  575. if ( '#' . $key == $options[$option][$i] ) {
  576. // Replaces reference with value
  577. $options[$option][$i] = $value;
  578. }
  579. }
  580. } else {
  581. // Checks if value is reference to row field
  582. if ( '#' . $key == $reference ) {
  583. // Replaces reference with value
  584. $options[$option] = $value;
  585. }
  586. }
  587. }
  588. }
  589. }
  590. // Builds javascript for linking
  591. $jsURL = DataCenterJs::escape(
  592. DataCenterXml::url( $options )
  593. );
  594. // Returns XML attributes for link
  595. return array( 'onclick' => "window.location='{$jsURL}'" );
  596. }
  597. /**
  598. * Builds an array of XML Attributes for javascript effects
  599. * @param options Array of Arrays of effect parameters, each
  600. * containing...
  601. * event Name of inline XML event
  602. * script JavaScript to run, with sprintf syntax for
  603. * including fields in the order listed
  604. * field Singular form of fields
  605. * fields Array of fields to supply to sprintf function
  606. * providing a way to get field information,
  607. * accessable to javascript in order given
  608. * Example:
  609. * array(
  610. * array(
  611. * 'event' => 'onmouseover',
  612. * 'script' => 'alert( 'Row ID: ' + %d )',
  613. * 'field' => 'id'
  614. * )
  615. * )
  616. * @param row DataCenterDBRow object from which to extract
  617. * field/fields from
  618. */
  619. public static function buildEffects( array $options, $fields ) {
  620. if ( $fields instanceof DataCenterDBRow ) {
  621. $fields = $fields->get();
  622. }
  623. // Checks for required types
  624. if ( ( is_array( $fields ) ) ) {
  625. $effects = array();
  626. // Loops over each effect
  627. foreach ( $options as $effect ) {
  628. if ( isset( $effect['event'], $effect['script'] ) ) {
  629. // Builds effect
  630. $effects[$effect['event']] = DataCenterJs::buildEffect(
  631. $effect['script'], $fields
  632. );
  633. }
  634. }
  635. // Returns XML attributes for effects
  636. return $effects;
  637. } else {
  638. return array();
  639. }
  640. }
  641. }
  642. abstract class DataCenterRenderable {
  643. /* Abstract Static Function */
  644. /**
  645. * Abstract function for rendering the input
  646. * @param $parameters Array: array of parameters
  647. */
  648. public static function render( array $parameters ) {}
  649. /* Protected Static Functions */
  650. /**
  651. * Builds XML string of begining of input
  652. * @param $class String: CSS class name of widget
  653. */
  654. protected static function begin( $class ) {
  655. return DataCenterXml::open( 'div', array( 'class' => $class ) );
  656. }
  657. /**
  658. * Builds XML string of ending of input
  659. */
  660. protected static function end() {
  661. return DataCenterXml::close( 'div' );
  662. }
  663. }
  664. abstract class DataCenterInput extends DataCenterRenderable {
  665. // Input-related functions to be defined...
  666. }
  667. abstract class DataCenterLayout extends DataCenterRenderable {
  668. // Layout-related functions to be defined...
  669. }
  670. abstract class DataCenterWidget extends DataCenterRenderable {
  671. /* Static Functions */
  672. public static function buildPaging( $page, $num ) {
  673. $range = array( 'limit' => 10, 'offset' => 0 );
  674. if ( isset( $page['limit'] ) && $page['limit'] !== null ) {
  675. $range['limit'] = $page['limit'];
  676. }
  677. if ( isset( $page['offset'] ) && $page['offset'] !== null ) {
  678. $range['offset'] = $page['offset'];
  679. }
  680. $icons = array(
  681. 'first' => array(
  682. 'name' => 'Navigation/First',
  683. 'enabled' => true,
  684. ),
  685. 'previous' => array(
  686. 'name' => 'Navigation/Previous',
  687. 'enabled' => true,
  688. ),
  689. 'last' => array(
  690. 'name' => 'Navigation/Last',
  691. 'enabled' => true,
  692. ),
  693. 'next' => array(
  694. 'name' => 'Navigation/Next',
  695. 'enabled' => true,
  696. ),
  697. );
  698. if ( $num < $range['limit'] ) {
  699. $range['offset'] = 0;
  700. }
  701. if ( $range['offset'] == 0 ) {
  702. $icons['first']['enabled'] = false;
  703. $icons['previous']['enabled'] = false;
  704. }
  705. if ( $range['offset'] + $range['limit'] >= $num ) {
  706. $icons['next']['enabled'] = false;
  707. $icons['last']['enabled'] = false;
  708. }
  709. $xmlOutput = DataCenterXml::open(
  710. 'div', array( 'class' => 'paging', 'align' => 'center' )
  711. );
  712. foreach ( $icons as $icon => $options ) {
  713. $attributes = array(
  714. 'class' => 'icon' . ( !$options['enabled'] ? '-disabled' : '' )
  715. );
  716. $attributes['class'] .= ' ' . $icon;
  717. $iconRange = array( 'limit' => $range['limit'] );
  718. if ( $options['enabled'] ) {
  719. switch ( $icon ) {
  720. case 'first':
  721. $iconRange['offset'] = 0;
  722. break;
  723. case 'previous':
  724. $iconRange['offset'] = max(
  725. $range['offset'] - $range['limit'], 0
  726. );
  727. break;
  728. case 'next':
  729. $iconRange['offset'] = min(
  730. $range['offset'] + $range['limit'], $num - 1
  731. );
  732. break;
  733. case 'last':
  734. $iconRange['offset'] = $num - $range['limit'];
  735. break;
  736. }
  737. $attributes = array_merge(
  738. $attributes, DataCenterXml::buildLink(
  739. array_merge( $page, $iconRange )
  740. )
  741. );
  742. }
  743. $xmlOutput .= DataCenterXml::icon(
  744. $options['name'], $options['enabled'], $attributes
  745. );
  746. }
  747. $xmlOutput .= DataCenterXml::div(
  748. array( 'class' => 'label' ),
  749. DataCenterUI::message( 'label', 'range', $num )
  750. );
  751. $xmlOutput .= DataCenterXml::close( 'div' );
  752. return $xmlOutput;
  753. }
  754. }
  755. class DataCenterUI {
  756. /* Private Static Members */
  757. /**
  758. * Widgets, inputs and layouts are rendered by passing a type, parameters,
  759. * and in the case of layouts contents. These array are used to verify the
  760. * widget, input or layout is available and also as a lookup table to
  761. * determine the corrosponding class to call the render function in.
  762. */
  763. private static $widgets = array(
  764. 'actions' => 'DataCenterWidgetActions',
  765. 'body' => 'DataCenterWidgetBody',
  766. 'details' => 'DataCenterWidgetDetails',
  767. 'export' => 'DataCenterWidgetExport',
  768. 'fieldlinks' => 'DataCenterWidgetFieldLinks',
  769. 'form' => 'DataCenterWidgetForm',
  770. 'gallery' => 'DataCenterWidgetGallery',
  771. 'heading' => 'DataCenterWidgetHeading',
  772. 'history' => 'DataCenterWidgetHistory',
  773. 'map' => 'DataCenterWidgetMap',
  774. 'model' => 'DataCenterWidgetModel',
  775. 'plan' => 'DataCenterWidgetPlan',
  776. 'search' => 'DataCenterWidgetSearch',
  777. 'searchresults' => 'DataCenterWidgetSearchResults',
  778. 'space' => 'DataCenterWidgetSpace',
  779. 'table' => 'DataCenterWidgetTable',
  780. );
  781. private static $inputs = array(
  782. 'boolean' => 'DataCenterInputBoolean',
  783. 'button' => 'DataCenterInputButton',
  784. 'list' => 'DataCenterInputList',
  785. 'number' => 'DataCenterInputNumber',
  786. 'position' => 'DataCenterInputPosition',
  787. 'string' => 'DataCenterInputString',
  788. 'tense' => 'DataCenterInputTense',
  789. 'text' => 'DataCenterInputText',
  790. );
  791. private static $layouts = array(
  792. 'columns' => 'DataCenterLayoutColumns',
  793. 'rows' => 'DataCenterLayoutRows',
  794. 'tabs' => 'DataCenterLayoutTabs',
  795. );
  796. /**
  797. * After the user interface is initialized, an instance of a page class is
  798. * created and rendered. Durring this process both setDestinations,
  799. * addScript and addContent get called. The raw data or rendered XML is
  800. * stored in output so that it can be composited later by render.
  801. */
  802. private static $output = array(
  803. 'search' => '',
  804. 'menu' => '',
  805. 'content' => '',
  806. 'script' => null,
  807. 'scripts' => array(
  808. '<!--[if IE]><script type="text/javascript" src="%s/extensions/DataCenter/Resources/Support/excanvas-compressed.js"></script><![endif]-->',
  809. '/extensions/DataCenter/DataCenter.js',
  810. )
  811. );
  812. /* Static Functions */
  813. /**
  814. * Gets internationalized message
  815. * @param type String of type of message
  816. * @param name String of name of message
  817. * @param arguments String or array of strings of arguments which
  818. * will be passed to MediaWiki's message parser
  819. */
  820. public static function message( $type, $name = null, $arguments = null ) {
  821. if ( !$name ) {
  822. return wfMsg( $type );
  823. }
  824. return wfMsgExt(
  825. "datacenter-ui-{$type}-{$name}",
  826. array( 'parsemag', 'parseinline' ),
  827. $arguments
  828. );
  829. }
  830. /**
  831. * Formats value using various modes
  832. * @param value Scalar of value to format
  833. * @param format String of mode to use, which can be nothing for
  834. * no formatting or any of the following...
  835. * date Localized date and time format from timestamp
  836. */
  837. public static function format( $value, $format ) {
  838. global $wgLang;
  839. // Handles format type
  840. switch ( $format ) {
  841. case 'date':
  842. // Uses localized date formatting
  843. return $wgLang->timeanddate( $value );
  844. break;
  845. case 'option':
  846. return self::message( 'option', $value );
  847. break;
  848. case 'type':
  849. return self::message( 'type', $value );
  850. break;
  851. case 'category':
  852. return self::message( 'category', $value );
  853. break;
  854. case 'side':
  855. return self::message( 'option', $value ? 'back' : 'front' );
  856. break;
  857. case 'angle':
  858. return self::message( 'label', 'degrees-value', $value * 90 );
  859. break;
  860. case 'boolean':
  861. return self::message( 'option', $value ? 'true' : 'false' );
  862. break;
  863. default:
  864. // Performs no formatting
  865. return $value;
  866. }
  867. }
  868. /**
  869. * Renders a final composition from cached output
  870. */
  871. public static function render() {
  872. global $wgOut, $wgScriptPath;
  873. // Adds XML head content
  874. foreach ( self::$output['scripts'] as $url ) {
  875. if ( strpos( $url, 'http://' ) !== false ) {
  876. $wgOut->addScript(
  877. DataCenterXml::script( array( 'src' => $url ) )
  878. );
  879. } elseif ( strpos( $url, '<' ) !== false ) {
  880. $wgOut->addScript( sprintf( $url, $wgScriptPath ) );
  881. } else {
  882. $wgOut->addScriptFile( $wgScriptPath . $url );
  883. }
  884. }
  885. $wgOut->addLink(
  886. array(
  887. 'rel' => 'stylesheet',
  888. 'type' => 'text/css',
  889. 'href' => $wgScriptPath .
  890. '/extensions/DataCenter/DataCenter.css'
  891. )
  892. );
  893. // Adds XML body content
  894. $wgOut->addHTML(
  895. DataCenterXml::div(
  896. array( 'class' => 'datacenter-ui' ),
  897. self::$output['menu'] . self::$output['content']
  898. )
  899. );
  900. if ( self::$output['script'] !== null ) {
  901. $wgOut->addHTML(
  902. DataCenterXml::script( self::$output['script'] )
  903. );
  904. }
  905. }
  906. /**
  907. * Builds string of XML using widget
  908. * @param $name String: name of widget to use
  909. * @param $parameters Array: parameters to pass on to widget
  910. * @return String of widget's XML output
  911. */
  912. public static function renderWidget( $name, array $parameters = array() ) {
  913. if ( isset( self::$widgets[$name] ) ) {
  914. $function = array( self::$widgets[$name], 'render' );
  915. if ( is_callable( $function ) ) {
  916. return call_user_func(
  917. $function,
  918. $parameters
  919. );
  920. }
  921. }
  922. return null;
  923. }
  924. /**
  925. * Builds string of XML using input
  926. * @param $name String: name of input to use
  927. * @param $parameters Array: parameters to pass on to input
  928. * @return String of input's XML output
  929. */
  930. public static function renderInput( $name, array $parameters = array() ) {
  931. if ( isset( self::$inputs[$name] ) ) {
  932. $function = array( self::$inputs[$name], 'render' );
  933. if ( is_callable( $function ) ) {
  934. return call_user_func(
  935. $function,
  936. $parameters
  937. );
  938. }
  939. }
  940. return null;
  941. }
  942. /**
  943. * Builds string of XML using layout
  944. * @param $name String: name of layout to use
  945. * @param $contents Array: array of strings of XML to layout
  946. * @return String of layout's XML output
  947. */
  948. public static function renderLayout( $name, array $contents = array() ) {
  949. if ( isset( self::$layouts[$name] ) ) {
  950. $function = array( self::$layouts[$name], 'render' );
  951. if ( is_callable( $function ) ) {
  952. return call_user_func(
  953. $function,
  954. $contents
  955. );
  956. }
  957. }
  958. return null;
  959. }
  960. /**
  961. * Appends the scripts list, skipping duplicate entries
  962. * @param $url String: fully qualified URL to JavaScript file
  963. */
  964. public static function addScript( $url ) {
  965. if ( !in_array( $url, self::$output['scripts'] ) ) {
  966. self::$output['scripts'][] = $url;
  967. }
  968. }
  969. /**
  970. * Appends content to the output cache
  971. * @param $content String: XML content to append
  972. */
  973. public static function addContent( $content ) {
  974. self::$output['content'] .= $content;
  975. }
  976. /**
  977. * Builds and stores menus in output cache
  978. * @param pages Array of pages for main menu, with keys as page
  979. * names
  980. * @param controller DataCenterController of the current page's
  981. * controller
  982. * @param path Array of link parameters of current path
  983. */
  984. public static function setDestinations(
  985. array $pages,
  986. DataCenterController $controller,
  987. array $path
  988. ) {
  989. global $wgUser;
  990. // Adds main menu
  991. self::$output['menu'] .= DataCenterXml::open(
  992. 'div', array( 'class' => 'menu' )
  993. );
  994. foreach ( $pages as $page => $classes ) {
  995. if ( $classes['display'] ) {
  996. $state = ( $page == $path['page'] ? 'current' : 'normal' );
  997. self::$output['menu'] .= DataCenterXml::div(
  998. array( 'class' => 'item-' . $state ),
  999. DataCenterXml::link(
  1000. self::message( 'page', $page ),
  1001. array( 'page' => $page )
  1002. )
  1003. );
  1004. }
  1005. }
  1006. self::$output['menu'] .= DataCenterXml::close( 'div' );
  1007. // Adds search
  1008. self::$output['menu'] .= DataCenterUI::renderWidget(
  1009. 'search', array()
  1010. );
  1011. // Adds sub menu
  1012. self::$output['menu'] .= DataCenterXml::open(
  1013. 'div', array( 'class' => 'toolbar' )
  1014. );
  1015. // Type tabs
  1016. if ( count( $controller->types ) > 0 ) {
  1017. self::$output['menu'] .= DataCenterXml::div(
  1018. array( 'class' => 'type-label' ),
  1019. self::message( 'label' , 'browse-by' )
  1020. );
  1021. foreach ( $controller->types as $label => $type ) {
  1022. $state = ( $label == $path['type'] ? 'current' : 'normal' );
  1023. self::$output['menu'] .= DataCenterXml::div(
  1024. array( 'class' => 'type-' . $state ),
  1025. DataCenterXml::link(
  1026. self::message( 'type', $label ), $type
  1027. )
  1028. );
  1029. }
  1030. }
  1031. // Trail steps
  1032. $count = 1;
  1033. foreach ( $controller->trail as $label => $step ) {
  1034. $end = ( $count == count( $controller->trail ) ) ? '-end' : '';
  1035. self::$output['menu'] .= DataCenterXml::div(
  1036. array( 'class' => 'breadcrumb' . $end ),
  1037. DataCenterXml::link( $label, $step)
  1038. );
  1039. $count++;
  1040. }
  1041. // Action tabs
  1042. foreach ( $controller->actions as $label => $action ) {
  1043. $state = ( $label == $path['action'] ? 'current' : 'normal' );
  1044. self::$output['menu'] .= DataCenterXml::div(
  1045. array( 'class' => 'action-' . $state ),
  1046. DataCenterXml::link(
  1047. self::message( 'action', $label ),
  1048. $action
  1049. )
  1050. );
  1051. }
  1052. self::$output['menu'] .= DataCenterXml::close( 'div' );
  1053. }
  1054. /**
  1055. * Checks if widget is available
  1056. * @param $name String: name of widget to look for
  1057. * @return Boolean true if exists, false if not
  1058. */
  1059. public static function isWidget( $name ) {
  1060. return isset( self::$widgets[$name] );
  1061. }
  1062. /**
  1063. * Checks if input is available
  1064. * @param $name String: name of input to look for
  1065. * @return Boolean true if exists, false if not
  1066. */
  1067. public static function isInput( $name ) {
  1068. return isset( self::$inputs[$name] );
  1069. }
  1070. /**
  1071. * Checks if layout is available
  1072. * @param $name String: name of layout to look for
  1073. * @return Boolean true if exists, false if not
  1074. */
  1075. public static function isLayout( $name ) {
  1076. return isset( self::$layouts[$name] );
  1077. }
  1078. }