video_embed_field.handlers.inc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. <?php
  2. /**
  3. * @file
  4. * Provide some handlers for video embed field
  5. * Other modules can implement the hook_video_embed_handler_info to provide more
  6. * handlers.
  7. */
  8. /**
  9. * Implements hook_video_embed_handler_info().
  10. */
  11. function video_embed_field_video_embed_handler_info() {
  12. $handlers = array();
  13. $handlers['youtube'] = array(
  14. 'title' => 'Youtube',
  15. 'function' => 'video_embed_field_handle_youtube',
  16. 'thumbnail_function' => 'video_embed_field_handle_youtube_thumbnail',
  17. 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/youtube.jpg',
  18. 'data_function' => 'video_embed_field_handle_youtube_data',
  19. 'form' => 'video_embed_field_handler_youtube_form',
  20. 'form_validate' => 'video_embed_field_handler_youtube_form_validate',
  21. 'domains' => array(
  22. 'youtube.com',
  23. 'youtu.be',
  24. ),
  25. 'defaults' => array(
  26. 'width' => 640,
  27. 'height' => 360,
  28. 'autoplay' => 0,
  29. 'vq' => 'large',
  30. 'rel' => 0,
  31. 'controls' => 1,
  32. 'autohide' => 2,
  33. 'showinfo' => 1,
  34. 'modestbranding' => 0,
  35. 'theme' => 'dark',
  36. 'iv_load_policy' => 1,
  37. 'class' => '',
  38. ),
  39. );
  40. $handlers['vimeo'] = array(
  41. 'title' => 'Vimeo',
  42. 'function' => 'video_embed_field_handle_vimeo',
  43. 'thumbnail_function' => 'video_embed_field_handle_vimeo_thumbnail',
  44. 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/vimeo.jpg',
  45. 'data_function' => '_video_embed_field_get_vimeo_data',
  46. 'form' => 'video_embed_field_handler_vimeo_form',
  47. 'form_validate' => 'video_embed_field_handler_vimeo_form_validate',
  48. 'domains' => array(
  49. 'vimeo.com',
  50. ),
  51. 'defaults' => array(
  52. 'width' => 640,
  53. 'height' => 360,
  54. 'color' => '00adef',
  55. 'portrait' => 1,
  56. 'title' => 1,
  57. 'byline' => 1,
  58. 'autoplay' => 0,
  59. 'loop' => 0,
  60. 'froogaloop' => 0,
  61. 'class' => ''
  62. ),
  63. );
  64. return $handlers;
  65. }
  66. /**
  67. * Helper function to get the youtube video's id.
  68. *
  69. * @param string $url
  70. * The video URL.
  71. *
  72. * @return string|bool
  73. * The video ID, or FALSE in case the ID can't be retrieved from the URL.
  74. */
  75. function _video_embed_field_get_youtube_id($url) {
  76. // Find the ID of the video they want to play from the url.
  77. if (stristr($url, 'http://')) {
  78. $url = substr($url, 7);
  79. }
  80. elseif (stristr($url, 'https://')) {
  81. $url = substr($url, 8);
  82. }
  83. if (stristr($url, 'playlist')) {
  84. // Playlists need the appended ampersand to take the options properly.
  85. $url = $url . '&';
  86. $pos = strripos($url, '?list=');
  87. if ($pos !== FALSE) {
  88. $pos2 = stripos($url, '&');
  89. $pos2++;
  90. }
  91. else {
  92. return FALSE;
  93. }
  94. }
  95. // Alternate playlist link.
  96. elseif (stristr($url, 'view_play_list')) {
  97. $url = $url . '&';
  98. // All playlist ID's are prepended with PL.
  99. if (!stristr($url, '?p=PL')) {
  100. $url = substr_replace($url, 'PL', strpos($url, '?p=') + 3, 0);
  101. }
  102. // Replace the links format with the embed format.
  103. $url = str_ireplace('play_list?p=', 'videoseries?list=', $url);
  104. $pos = strripos($url, 'videoseries?list=');
  105. if ($pos !== FALSE) {
  106. $pos2 = stripos($url, '&');
  107. $pos2++;
  108. }
  109. else {
  110. return FALSE;
  111. }
  112. }
  113. else {
  114. $pos = strripos($url, 'v=');
  115. if ($pos !== FALSE) {
  116. $pos += 2;
  117. $pos2 = stripos($url, '&', $pos);
  118. $pos_hash = stripos($url, '#', $pos);
  119. $pos2 = _video_embed_get_min($pos2, $pos_hash);
  120. }
  121. else {
  122. $pos = strripos($url, '/');
  123. if ($pos !== FALSE) {
  124. $pos++;
  125. $pos2 = stripos($url, '?', $pos);
  126. $pos_hash = stripos($url, '#', $pos);
  127. $pos2 = _video_embed_get_min($pos2, $pos_hash);
  128. }
  129. }
  130. }
  131. if ($pos === FALSE) {
  132. return FALSE;
  133. }
  134. else {
  135. if ($pos2 > 0) {
  136. $id = substr($url, $pos, $pos2 - $pos);
  137. }
  138. else {
  139. $id = substr($url, $pos);
  140. }
  141. }
  142. return check_plain($id);
  143. }
  144. /**
  145. * Handler for Youtube videos.
  146. *
  147. * @param string $url
  148. * The video URL.
  149. * @param array $settings
  150. * The settings array.
  151. *
  152. * @return array
  153. * The video iframe render array.
  154. */
  155. function video_embed_field_handle_youtube($url, $settings) {
  156. $output = array();
  157. if(preg_match('/#t=((?P<min>\d+)m)?((?P<sec>\d+)s)?((?P<tinsec>\d+))?/', $url, $matches)){
  158. if(isset($matches['tinsec'])){
  159. $settings['start'] = $matches['tinsec']; // url already in form #t=125 for 2 minutes and 5 seconds
  160. } else {
  161. // url in form #t=2m5s or with other useless data, this is why we still keep adding the default data..
  162. // give it some default data in case there is no #t=...
  163. $matches += array(
  164. "min" => 0,
  165. "sec" => 0,
  166. );
  167. if ($time = ($matches["min"] * 60) + $matches["sec"]) {
  168. $settings['start'] = $time;
  169. }
  170. }
  171. }
  172. $id = _video_embed_field_get_youtube_id($url);
  173. if (!$id) {
  174. // We can't decode the URL - just return the URL as a link.
  175. $output['#markup'] = l($url, $url);
  176. return $output;
  177. }
  178. // Add class to variable to avoid adding it to URL param string.
  179. $class = $settings['class'];
  180. unset($settings['class']);
  181. // Construct the embed code.
  182. $settings['wmode'] = 'opaque';
  183. $settings_str = _video_embed_code_get_settings_str($settings);
  184. $output['#markup'] = '<iframe class="' . check_plain($class) . '" width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//www.youtube.com/embed/' . $id . '?' . $settings_str . '" frameborder="0" allowfullscreen></iframe>';
  185. return $output;
  186. }
  187. /**
  188. * Gets the thumbnail url for youtube videos.
  189. *
  190. * @param string $url
  191. * The video URL.
  192. *
  193. * @return array
  194. * The video thumbnail information.
  195. */
  196. function video_embed_field_handle_youtube_thumbnail($url) {
  197. $info = array();
  198. $id = _video_embed_field_get_youtube_id($url);
  199. // Playlist.
  200. if (stristr($id, '?list=')) {
  201. // Strip out all but the ID, including the PL behind the ID.
  202. $start = strpos($id, '?list=PL') + 8;
  203. $length = strpos($id, '&') - $start;
  204. $id = substr($id, $start, $length);
  205. $info['id'] = $id;
  206. // Playlist info is stored in XML. The thumbnail is in there.
  207. $xml = drupal_http_request('http://gdata.youtube.com/feeds/api/playlists/' . $id);
  208. if (!isset($xml->error)) {
  209. $xml = new SimpleXMLElement($xml->data);
  210. $media = $xml->children('http://search.yahoo.com/mrss/');
  211. if ($media->group->thumbnail && $media->group->thumbnail[0]->attributes()) {
  212. $attrs = $media->group->thumbnail[0]->attributes();
  213. $info['url'] = (string) $attrs['url'];
  214. }
  215. }
  216. }
  217. // Regular video.
  218. elseif ($id) {
  219. $info['id'] = $id;
  220. $info['url'] = 'http://img.youtube.com/vi/' . $id . '/0.jpg';
  221. }
  222. return $info;
  223. }
  224. /**
  225. * Gets video data for a YouTube video URL.
  226. *
  227. * @param string $url
  228. * A YouTube video URL to get data for
  229. *
  230. * @return array|bool
  231. * An array of video data, or FALSE if unable to fetch data
  232. */
  233. function video_embed_field_handle_youtube_data($url) {
  234. // Get YouTube video ID from URL.
  235. $id = _video_embed_field_get_youtube_id($url);
  236. if ($id) {
  237. $options['v'] = 3;
  238. $options['key'] = variable_get('video_embed_field_youtube_v3_api_key', '');
  239. $options['part'] = 'snippet';
  240. $options['id'] = $id;
  241. $response = drupal_http_request(url('https://www.googleapis.com/youtube/v3/videos', array('query' => $options)));
  242. if (!isset($response->error)) {
  243. $data = json_decode($response->data);
  244. return _video_embed_field_clean_up_youtube_data($data->items);
  245. }
  246. }
  247. return FALSE;
  248. }
  249. /**
  250. * Flattens out some unnecessary nesting in the youtube data.
  251. *
  252. * @param array $data
  253. * The unflattened data.
  254. *
  255. * @return array
  256. * The flattened data.
  257. */
  258. function _video_embed_field_clean_up_youtube_data($data) {
  259. // Make things a bit nicer for people trying to use the data.
  260. foreach ($data as $key => $value) {
  261. if (is_object($value)) {
  262. $temp = (array) $value;
  263. if (isset($temp['$t'])) {
  264. $data[$key] = $temp['$t'];
  265. }
  266. else {
  267. $data[$key] = _video_embed_field_clean_up_youtube_data($temp);
  268. }
  269. }
  270. elseif (is_array($value)) {
  271. $data[$key] = _video_embed_field_clean_up_youtube_data($value);
  272. }
  273. if ($key === 'category') {
  274. $terms = array();
  275. foreach ($data[$key] as $value) {
  276. if (isset($value['scheme']) && $value['scheme'] == 'http://schemas.google.com/g/2005#kind') {
  277. continue;
  278. }
  279. if (isset($value['term'])) {
  280. $terms[] = $value['term'];
  281. }
  282. }
  283. $data['terms'] = $terms;
  284. }
  285. }
  286. return $data;
  287. }
  288. /**
  289. * Defines the form elements for the Youtube configuration form.
  290. *
  291. * @param array $defaults
  292. * The form default values.
  293. *
  294. * @return array
  295. * The provider settings form array.
  296. */
  297. function video_embed_field_handler_youtube_form($defaults) {
  298. $form = array();
  299. $form['width'] = array(
  300. '#type' => 'textfield',
  301. '#size' => '5',
  302. '#title' => t('Player Width'),
  303. '#description' => t('The width of the youtube player.'),
  304. '#default_value' => $defaults['width'],
  305. );
  306. $form['height'] = array(
  307. '#type' => 'textfield',
  308. '#size' => '5',
  309. '#title' => t('Player Height'),
  310. '#description' => t('The height of the youtube player.'),
  311. '#default_value' => $defaults['height'],
  312. );
  313. $form['theme'] = array(
  314. '#type' => 'select',
  315. '#options' => array(
  316. 'dark' => t('Dark'),
  317. 'light' => t('Light'),
  318. ),
  319. '#title' => t('Player theme'),
  320. '#default_value' => $defaults['theme'],
  321. );
  322. $form['autoplay'] = array(
  323. '#type' => 'checkbox',
  324. '#title' => t('Autoplay'),
  325. '#description' => t('Play the video immediately.'),
  326. '#default_value' => $defaults['autoplay'],
  327. );
  328. $form['vq'] = array(
  329. '#type' => 'select',
  330. '#title' => t('Video quality'),
  331. '#options' => array(
  332. 'small' => t('Small (240p)'),
  333. 'medium' => t('Medium (360p)'),
  334. 'large' => t('Large (480p)'),
  335. 'hd720' => t('HD 720p'),
  336. 'hd1080' => t('HD 10800p'),
  337. ),
  338. '#default_value' => $defaults['vq'],
  339. '#description' => t('Attempt to play the video in certain quality if available.'),
  340. );
  341. $form['rel'] = array(
  342. '#type' => 'checkbox',
  343. '#title' => t('Show related videos'),
  344. '#description' => t('Show related videos after the video is finished playing.'),
  345. '#default_value' => $defaults['rel'],
  346. );
  347. $form['showinfo'] = array(
  348. '#type' => 'checkbox',
  349. '#title' => t('Show info'),
  350. '#description' => t('Display information like the video title and rating before the video starts playing.'),
  351. '#default_value' => $defaults['showinfo'],
  352. );
  353. $form['modestbranding'] = array(
  354. '#type' => 'checkbox',
  355. '#title' => t('Hide Youtube logo'),
  356. '#description' => t('Hide the Youtube logo button on the player'),
  357. '#default_value' => $defaults['modestbranding'],
  358. );
  359. $form['iv_load_policy'] = array(
  360. '#type' => 'radios',
  361. '#options' => array(
  362. 1 => t('Show video annotations.'),
  363. 3 => t('Hide video annotations.'),
  364. ),
  365. '#title' => t('Display annotations'),
  366. '#description' => t('Controls the display of annotations over the video content. Only works when using the flash player.'),
  367. '#default_value' => $defaults['iv_load_policy'],
  368. );
  369. $form['controls'] = array(
  370. '#type' => 'radios',
  371. '#options' => array(
  372. 0 => t('Hide video controls.'),
  373. 1 => t('Show video controls. Youtube default.'),
  374. 2 => t('Show video controls with performance improvement for iframe embeds.'),
  375. ),
  376. '#title' => t('Display Youtube player controls'),
  377. '#description' => t('This parameter indicates whether the video player controls will display.'),
  378. '#default_value' => $defaults['controls'],
  379. );
  380. $form['autohide'] = array(
  381. '#type' => 'radios',
  382. '#options' => array(
  383. 0 => t('The video progress bar and player controls will be visible throughout the video.'),
  384. 1 => t('Automatically slide the video progress bar and the player controls out of view a couple of seconds after the video starts playing. They will only reappear if the user moves her mouse over the video player or presses a keyboard key.'),
  385. 2 => t('The video progress bar will fade out but the player controls (play button, volume control, etc.) remain visible.'),
  386. ),
  387. '#title' => t('Autohide progress bar and the player controls'),
  388. '#description' => t('Controls the autohide behavior of the youtube player controls.'),
  389. '#default_value' => $defaults['autohide'],
  390. );
  391. $form['class'] = array(
  392. '#type' => 'textfield',
  393. '#title' => t('Player CSS class'),
  394. '#description' => t('CSS class to add to the player'),
  395. '#default_value' => $defaults['class'],
  396. );
  397. return $form;
  398. }
  399. /**
  400. * Validates the form elements for the Youtube configuration form.
  401. *
  402. * @param array $element
  403. * The form element to validate.
  404. * @param array $form_state
  405. * The form to validate state.
  406. * @param array $form
  407. * The form to validate structure.
  408. */
  409. function video_embed_field_handler_youtube_form_validate($element, &$form_state, $form) {
  410. video_embed_field_validate_dimensions($element);
  411. }
  412. /**
  413. * Helper function to get the Vimeo video's data attributes.
  414. *
  415. * @param string $url
  416. * A Vimeo video URL to get the data from.
  417. *
  418. * @return integer|false
  419. * The video's data attributes, or FALSE if unable to get the video ID.
  420. */
  421. function _video_embed_field_get_vimeo_data($url) {
  422. // Set oembed endpoint
  423. $oembed_endpoint = 'http://vimeo.com/api/oembed';
  424. // Fetch vimeo data
  425. $response = drupal_http_request($oembed_endpoint . '.json?url=' . rawurlencode($url));
  426. try {
  427. return json_decode($response->data, TRUE);
  428. } catch (Exception $e) {
  429. return FALSE;
  430. }
  431. }
  432. /**
  433. * Helper function to get the Vimeo video's data attributes.
  434. *
  435. * @param string $url
  436. * A Vimeo video URL to get the ID of.
  437. *
  438. * @return integer|false
  439. * The video ID, or FALSE if unable to get the video ID.
  440. */
  441. function _video_embed_field_get_vimeo_id($vimeo_data) {
  442. try {
  443. $video_id = $vimeo_data['video_id'];
  444. } catch (Exception $e) {
  445. $video_id = FALSE;
  446. }
  447. return $video_id;
  448. }
  449. /**
  450. * Handler for Vimeo videos.
  451. *
  452. * @param string $url
  453. * The video URL.
  454. * @param array $settings
  455. * The settings array.
  456. *
  457. * @return string
  458. * The video iframe.
  459. */
  460. function video_embed_field_handle_vimeo($url, $settings) {
  461. $vimeo_data = _video_embed_field_get_vimeo_data($url);
  462. // Get ID of video from URL.
  463. $id = _video_embed_field_get_vimeo_id($vimeo_data);
  464. if (empty($id)) {
  465. return array(
  466. '#markup' => l($url, $url),
  467. );
  468. }
  469. // Construct the embed code.
  470. $settings['player_id'] = drupal_html_id('vimeo-' . $id);
  471. if (!empty($settings['froogaloop'])) {
  472. $settings['api'] = 1;
  473. }
  474. unset($settings['froogaloop']);
  475. // Add class to variable to avoid adding it to URL param string.
  476. $class = $settings['class'];
  477. unset($settings['class']);
  478. $settings_str = _video_embed_code_get_settings_str($settings);
  479. return array(
  480. '#markup' => '<iframe class="' . check_plain($class) . '" id="' . $settings['player_id'] . '" width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//player.vimeo.com/video/' . $id .
  481. '?' . $settings_str . '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen></iframe>',
  482. );
  483. }
  484. /**
  485. * Gets the thumbnail url for vimeo videos.
  486. *
  487. * @param string $url
  488. * The video URL.
  489. *
  490. * @return array
  491. * The video thumbnail information.
  492. */
  493. function video_embed_field_handle_vimeo_thumbnail($url) {
  494. $vimeo_data = _video_embed_field_get_vimeo_data($url);
  495. // Get ID of video from URL.
  496. $id = _video_embed_field_get_vimeo_id($vimeo_data);
  497. $info = array(
  498. 'id' => $id,
  499. );
  500. try {
  501. $info['url'] = $vimeo_data['thumbnail_url'];
  502. } catch (Exception $e) {
  503. }
  504. return $info;
  505. }
  506. /**
  507. * Defines the form elements for the Vimeo configuration form.
  508. *
  509. * @param array $defaults
  510. * The form default values.
  511. *
  512. * @return array
  513. * The provider settings form array.
  514. */
  515. function video_embed_field_handler_vimeo_form($defaults) {
  516. $form = array();
  517. $form['width'] = array(
  518. '#type' => 'textfield',
  519. '#size' => '5',
  520. '#title' => t('Player Width'),
  521. '#description' => t('The width of the vimeo player.'),
  522. '#default_value' => $defaults['width'],
  523. );
  524. $form['height'] = array(
  525. '#type' => 'textfield',
  526. '#size' => '5',
  527. '#title' => t('Player Height'),
  528. '#description' => t('The height of the vimeo player.'),
  529. '#default_value' => $defaults['height'],
  530. );
  531. $form['color'] = array(
  532. '#type' => 'select',
  533. '#options' => array(
  534. '00adef' => t('Blue'),
  535. 'ff9933' => t('Orange'),
  536. 'c9ff23' => t('Lime'),
  537. 'ff0179' => t('Fuschia'),
  538. 'ffffff' => t('White'),
  539. ),
  540. '#title' => t('Player Color'),
  541. '#description' => t('The color to use on the vimeo player.'),
  542. '#default_value' => $defaults['color'],
  543. );
  544. $form['portrait'] = array(
  545. '#type' => 'checkbox',
  546. '#title' => t('Overlay Author Thumbnail'),
  547. '#description' => t("Overlay the author's thumbnail before the video is played."),
  548. '#default_value' => $defaults['portrait'],
  549. );
  550. $form['title'] = array(
  551. '#type' => 'checkbox',
  552. '#title' => t("Overlay Video's Title"),
  553. '#description' => t("Overlay the video's title before the video is played."),
  554. '#default_value' => $defaults['title'],
  555. );
  556. $form['byline'] = array(
  557. '#type' => 'checkbox',
  558. '#title' => t("Overlay Video's Byline"),
  559. '#description' => t("Overlay the video's description before the video is played."),
  560. '#default_value' => $defaults['byline'],
  561. );
  562. $form['overridable'] = array(
  563. '#prefix' => '<p class="note"><strong>' . t('Note') . ': </strong><em>',
  564. '#markup' => t('Color, portrait, title and byline can be restricted by Vimeo Plus videos.
  565. Such videos will ignore these settings.'),
  566. '#suffix' => '</em></p>',
  567. );
  568. $form['autoplay'] = array(
  569. '#type' => 'checkbox',
  570. '#title' => t('Autoplay'),
  571. '#description' => t('Play the video immediately.'),
  572. '#default_value' => $defaults['autoplay'],
  573. );
  574. $form['loop'] = array(
  575. '#type' => 'checkbox',
  576. '#title' => t('Loop'),
  577. '#description' => t("Loop the video's playback"),
  578. '#default_value' => $defaults['loop'],
  579. );
  580. $form['froogaloop'] = array(
  581. '#type' => 'checkbox',
  582. '#title' => t('Enable froogaloop support'),
  583. '#description' => t("Enables Froogallop Vimeo's library support"),
  584. '#default_value' => $defaults['loop'],
  585. );
  586. $form['class'] = array(
  587. '#type' => 'textfield',
  588. '#title' => t('Player CSS class'),
  589. '#description' => t('CSS class to add to the player'),
  590. '#default_value' => $defaults['class'],
  591. );
  592. return $form;
  593. }
  594. /**
  595. * Validates the form elements for the Vimeo configuration form.
  596. *
  597. * @param array $element
  598. * The form element to validate.
  599. * @param array $form_state
  600. * The form to validate state.
  601. * @param array $form
  602. * The form to validate structure.
  603. */
  604. function video_embed_field_handler_vimeo_form_validate($element, &$form_state, $form) {
  605. video_embed_field_validate_dimensions($element);
  606. }
  607. /**
  608. * Calculates the min index for use in finding the id of a youtube video.
  609. *
  610. * @param string $pos1
  611. * The first index.
  612. * @param string $pos2
  613. * The second index.
  614. *
  615. * @return string
  616. * The min index.
  617. */
  618. function _video_embed_get_min($pos1, $pos2) {
  619. if (!$pos1) {
  620. return $pos2;
  621. }
  622. elseif (!$pos2) {
  623. return $pos1;
  624. }
  625. else {
  626. return min($pos1, $pos2);
  627. }
  628. }