summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xdocs/CHANGELOG1
-rw-r--r--html5_player.php33
-rw-r--r--lib/class/stream.class.php4
-rw-r--r--lib/class/stream_playlist.class.php12
-rw-r--r--lib/javascript/html5_player.js204
-rw-r--r--lib/preferences.php3
-rw-r--r--play/index.php11
-rw-r--r--server/stream.ajax.php1
-rw-r--r--templates/create_html5_player.inc.php39
-rw-r--r--templates/show_html5_player.inc.php76
-rw-r--r--templates/show_playtype_switch.inc.php1
-rw-r--r--themes/classic/templates/default.css66
-rw-r--r--themes/fresh/templates/default.css66
13 files changed, 509 insertions, 8 deletions
diff --git a/docs/CHANGELOG b/docs/CHANGELOG
index 684edf8d..aedf7c9d 100755
--- a/docs/CHANGELOG
+++ b/docs/CHANGELOG
@@ -4,6 +4,7 @@
--------------------------------------------------------------------------
v.3.6-FUTURE
+ - Added an HTML5 player (patch by Holger Brunn)
- Changed the way themes handle RTL languages
- Fixed a display problem with the Penguin theme by adding a new CSS class
(patch by Fred Thomsen)
diff --git a/html5_player.php b/html5_player.php
new file mode 100644
index 00000000..d740f15a
--- /dev/null
+++ b/html5_player.php
@@ -0,0 +1,33 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/**
+ *
+ * LICENSE: GNU General Public License, version 2 (GPLv2)
+ * Copyright 2001 - 2013 Ampache.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+require_once 'lib/init.php';
+
+// Switch on actions
+switch ($_REQUEST['action']) {
+ default:
+ require_once Config::get('prefix') . '/templates/show_html5_player.inc.php';
+ break;
+} // end switch
+
+
+?>
diff --git a/lib/class/stream.class.php b/lib/class/stream.class.php
index 338898a2..7304891c 100644
--- a/lib/class/stream.class.php
+++ b/lib/class/stream.class.php
@@ -144,8 +144,8 @@ class Stream {
* This is a rather complex function that starts the transcoding or
* resampling of a song and returns the opened file handle.
*/
- public static function start_transcode($song) {
- $transcode_settings = $song->get_transcode_settings();
+ public static function start_transcode($song, $type = null) {
+ $transcode_settings = $song->get_transcode_settings($type);
// Bail out early if we're unutterably broken
if ($transcode_settings == false) {
debug_event('stream', 'Transcode requested, but get_transcode_settings failed', 2);
diff --git a/lib/class/stream_playlist.class.php b/lib/class/stream_playlist.class.php
index 6d85f8f0..1f8b4b88 100644
--- a/lib/class/stream_playlist.class.php
+++ b/lib/class/stream_playlist.class.php
@@ -160,6 +160,7 @@ class Stream_Playlist {
case 'democratic':
case 'localplay':
case 'xspf_player':
+ case 'html5_player':
// These are valid, but witchy
$redirect = false;
unset($ext);
@@ -396,6 +397,15 @@ class Stream_Playlist {
} // create_xspf_player
/**
+ * create_html5_player
+ *
+ * Creates an html5 player.
+ */
+ public function create_html5_player() {
+ require Config::get('prefix') . '/templates/create_html5_player.inc.php';
+ }
+
+ /**
* create_localplay
* This calls the Localplay API to add the URLs and then start playback
*/
@@ -450,7 +460,5 @@ class Stream_Playlist {
echo $url->url . "\n";
}
} // create_ram
-
}
-
?>
diff --git a/lib/javascript/html5_player.js b/lib/javascript/html5_player.js
new file mode 100644
index 00000000..4bed561c
--- /dev/null
+++ b/lib/javascript/html5_player.js
@@ -0,0 +1,204 @@
+/* vim:set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */
+/**
+ *
+ * LICENSE: GNU General Public License, version 2 (GPLv2)
+ * Copyright 2013 Ampache.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+var current_playlist_item = null;
+
+function play_item(event)
+{
+ stop();
+ current_playlist_item = event.findElement().getStorage().get('playlist_item');
+ play();
+}
+function adjust_buttons()
+{
+ if(!current_playlist_item.player.paused)
+ {
+ $('play').addClassName('inactive');
+ $('pause').removeClassName('inactive');
+ $('stop').removeClassName('inactive');
+ }
+ else
+ {
+ $('play').removeClassName('inactive');
+ $('pause').addClassName('inactive');
+ $('stop').addClassName('inactive');
+ }
+}
+function stop(event)
+{
+ if(current_playlist_item)
+ {
+ current_playlist_item.player.pause();
+ current_playlist_item.player.currentTime = 0;
+ if(current_playlist_item.player.currentTime)
+ {
+ var src=current_playlist_item.player.src;
+ current_playlist_item.player.src=null;
+ current_playlist_item.player.src=src;
+ }
+ current_playlist_item.element.removeClassName('playing');
+ adjust_buttons();
+ }
+}
+function pause(event)
+{
+ if(current_playlist_item)
+ {
+ current_playlist_item.player.pause();
+ adjust_buttons();
+ }
+}
+function play(event)
+{
+ if(current_playlist_item)
+ {
+ $('title').update(current_playlist_item.info_url);
+ $('title').select('a')[0].writeAttribute('target', '_new');
+ $('album').update(current_playlist_item.f_album_link);
+ //$('album').select('a')[0].writeAttribute('target', '_new');
+ $('artist').update(current_playlist_item.author);
+ //$('artist').select('a')[0].writeAttribute('target', '_new');
+$('albumart').update(new Element('img', {src: current_playlist_item.albumart_url}));
+ current_playlist_item.player.writeAttribute('preload', 'auto');
+ current_playlist_item.player.play();
+ if(current_playlist_item.element.offsetTop - $('playlist').offsetTop - $('playlist').scrollTop > $('playlist').measure('height') || current_playlist_item.element.offsetTop - $('playlist').offsetTop - $('playlist').scrollTop < 0)
+ {
+ $('playlist').scrollTop = current_playlist_item.element.offsetTop - $('playlist').offsetTop;
+ }
+ current_playlist_item.element.addClassName('playing');
+ adjust_buttons();
+ }
+}
+function next(event)
+{
+ if(current_playlist_item && current_playlist_item.next)
+ {
+ stop();
+ current_playlist_item = current_playlist_item.next;
+ play();
+ }
+}
+function previous(event)
+{
+ if(current_playlist_item && current_playlist_item.previous)
+ {
+ stop();
+ current_playlist_item = current_playlist_item.previous;
+ play();
+ }
+}
+function seconds_to_string(seconds)
+{
+ return Math.floor(seconds / 60) + ":" + (Math.floor(seconds % 60) < 10 ? '0' : '') + Math.floor(seconds % 60);
+}
+function timeupdate(event)
+{
+ if(current_playlist_item)
+ {
+ $('progress_text').update(seconds_to_string(current_playlist_item.player.currentTime) + "/" + seconds_to_string(current_playlist_item.time));
+ if(current_playlist_item.player.currentTime > current_playlist_item.time / 2)
+ {
+ if(current_playlist_item.next)
+ {
+ current_playlist_item.next.player.writeAttribute('preload', 'auto');
+ }
+ }
+ //fix for chrome where ended is not thrown properly
+ if(current_playlist_item.player.currentTime >= current_playlist_item.time)
+ {
+ ended(event);
+ }
+ }
+}
+function ended(event)
+{
+ if(current_playlist_item && current_playlist_item.next)
+ {
+ stop();
+ current_playlist_item = current_playlist_item.next;
+ play();
+ }
+}
+function search(event)
+{
+ var search = new RegExp(".*" + event.findElement().value + ".*", "i");
+ for(var item = $('playlist').firstDescendant(); item; item = item.next())
+ {
+ if(!search.test(item.textContent != undefined ? item.textContent : item.innerText))
+ {
+ item.hide();
+ }
+ else
+ {
+ item.show();
+ }
+ }
+}
+function clear_search(event)
+{
+ event.findElement().value = "";
+ search(event);
+}
+document.observe("dom:loaded", function()
+{
+ var last_item = null, first_item = null;
+ for(id in playlist_items)
+ {
+ var li = new Element('li');
+ $('playlist').insert(li);
+ playlist_items[id].play_url += '&transcode_to=' + (Prototype.Browser.IE || Prototype.Browser.WebKit || Prototype.Browser.MobileSafari ? 'mp3' : 'ogg');
+ li.update(playlist_items[id].title);
+playlist_items[id].player = new Element("audio", {preload: Prototype.Browser.IE ? 'auto' : 'none', src : playlist_items[id].play_url});
+ li.insert(playlist_items[id].player);
+ li.getStorage().set('playlist_item', playlist_items[id]);
+ li.observe('click', play_item);
+ playlist_items[id].player.observe('ended', ended);
+ playlist_items[id].player.observe('timeupdate', timeupdate);
+ playlist_items[id].element = li;
+ if(last_item)
+ {
+ last_item.next = playlist_items[id];
+ }
+ if(first_item == null)
+ {
+ first_item = playlist_items[id];
+ }
+ playlist_items[id].previous = last_item;
+ last_item = playlist_items[id];
+ }
+ if(first_item)
+ {
+ first_item.previous = last_item;
+ last_item.next = first_item;
+ current_playlist_item = first_item;
+ play();
+ }
+ $('stop').observe('click', stop);
+ $('play').observe('click', play);
+ $('pause').observe('click', pause);
+ $('next').observe('click', next);
+ $('previous').observe('click', previous);
+ $('input_search').observe('keyup', search);
+ $('input_search').observe('html5_player:clear_search', clear_search);
+ $('input_search').observe('focus', clear_search);
+ $('clear_search').observe('click', function() {
+ $('input_search').fire('html5_player:clear_search')
+ });
+});
diff --git a/lib/preferences.php b/lib/preferences.php
index 6fe5c178..eeef5646 100644
--- a/lib/preferences.php
+++ b/lib/preferences.php
@@ -178,6 +178,7 @@ function create_preference_input($name,$value) {
if ($value == 'localplay') { $is_local = 'selected="selected"'; }
elseif ($value == 'democratic') { $is_vote = 'selected="selected"'; }
elseif ($value == 'xspf_player') { $is_xspf_player = 'selected="selected"'; }
+ elseif ($value == 'html5_player') { $is_html5_player = 'selected="selected"'; }
else { $is_stream = "selected=\"selected\""; }
echo "<select name=\"$name\">\n";
echo "\t<option value=\"\">" . T_('None') . "</option>\n";
@@ -191,6 +192,7 @@ function create_preference_input($name,$value) {
echo "\t<option value=\"localplay\" $is_local>" . T_('Localplay') . "</option>\n";
}
echo "\t<option value=\"xspf_player\" $is_xspf_player>" . T_('Flash Player') . "</option>\n";
+ echo "\t<option value=\"html5_player\" $is_html5_player>" . _('HTML5 Player') . "</option>\n";
echo "</select>\n";
break;
case 'playlist_type':
@@ -210,7 +212,6 @@ function create_preference_input($name,$value) {
echo '<select name="' . $name . '">' . "\n";
foreach ($languages as $lang=>$name) {
$selected = ($lang == $value) ? 'selected="selected"' : '';
-
echo "\t<option value=\"$lang\" " . $selected . ">$name</option>\n";
} // end foreach
echo "</select>\n";
diff --git a/play/index.php b/play/index.php
index 498b7a2d..9c22f909 100644
--- a/play/index.php
+++ b/play/index.php
@@ -41,6 +41,7 @@ $sid = scrub_in($_REQUEST['ssid']);
$xml_rpc = scrub_in($_REQUEST['xml_rpc']);
$video = make_bool($_REQUEST['video']);
$type = scrub_in($_REQUEST['type']);
+$transcode_to = scrub_in($_REQUEST['transcode_to']);
if ($type == 'playlist') {
$playlist_type = scrub_in($_REQUEST['playlist_type']);
@@ -314,9 +315,15 @@ if (Config::get('downsample_remote')) {
// Determine whether to transcode
$transcode = false;
$transcode_cfg = Config::get('transcode');
+// transcode_to should only have an effect if the song is the wrong format
+$transcode_to = $transcode_to == $media->type ? null : $transcode_to;
$valid_types = $media->get_stream_types();
if ($transcode_cfg != 'never' && in_array('transcode', $valid_types)) {
- if ($transcode_cfg == 'always') {
+ if ($transcode_to) {
+ $transcode = true;
+ debug_event('play', 'Transcoding due to explicit request for ' . $transcode_to, 5);
+ }
+ else if ($transcode_cfg == 'always') {
$transcode = true;
debug_event('play', 'Transcoding due to always', 5);
}
@@ -335,7 +342,7 @@ if ($transcode_cfg != 'never' && in_array('transcode', $valid_types)) {
if ($transcode) {
header('Accept-Ranges: none');
- $transcoder = Stream::start_transcode($media);
+ $transcoder = Stream::start_transcode($media, $transcode_to);
$fp = $transcoder['handle'];
$media_name = $media->f_artist_full . " - " . $media->title . "." . $transcoder['format'];
}
diff --git a/server/stream.ajax.php b/server/stream.ajax.php
index c08d6cb3..1ad0b237 100644
--- a/server/stream.ajax.php
+++ b/server/stream.ajax.php
@@ -45,6 +45,7 @@ switch ($_REQUEST['action']) {
$new = $_POST['type'];
break;
case 'xspf_player':
+ case 'html5_player':
$new = $_POST['type'];
// Rien a faire
break;
diff --git a/templates/create_html5_player.inc.php b/templates/create_html5_player.inc.php
new file mode 100644
index 00000000..de3e036c
--- /dev/null
+++ b/templates/create_html5_player.inc.php
@@ -0,0 +1,39 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/**
+ *
+ * LICENSE: GNU General Public License, version 2 (GPLv2)
+ * Copyright 2001 - 2013 Ampache.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+?>
+<html>
+<head>
+<title><?php echo Config::get('site_title'); ?></title>
+<script language="javascript" type="text/javascript">
+<!-- begin
+function PlayerPopUp(URL) {
+ window.open(URL, 'HTML5_player', 'width=700,height=210,scrollbars=0,toolbar=0,location=0,directories=0,status=0,resizable=0');
+ window.location = '<?php echo return_referer() ?>';
+ return false;
+}
+// end -->
+</script>
+</head>
+<body onLoad="javascript:PlayerPopUp('<?php echo Config::get('web_path')?>/html5_player.php<?php echo '?playlist_id=' . $this->id ?>')">
+</body>
+</html>
diff --git a/templates/show_html5_player.inc.php b/templates/show_html5_player.inc.php
new file mode 100644
index 00000000..505909b2
--- /dev/null
+++ b/templates/show_html5_player.inc.php
@@ -0,0 +1,76 @@
+<?php
+/* vim:set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */
+/**
+ *
+ * LICENSE: GNU General Public License, version 2 (GPLv2)
+ * Copyright 2013 Ampache.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+header('Cache-Control: no-cache');
+header('Pragma: no-cache');
+header('Expires: ' . gmdate(DATE_RFC1123, time()-1));
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title><?php echo Config::get('site_title'); ?></title>
+<link rel="stylesheet" href="<?php echo Config::get('web_path').Config::get('theme_path').'/templates/'.'default.css'; ?>" type="text/css" media="screen" />
+<script src="<?php echo Config::get('web_path'); ?>/modules/prototype/prototype.js" language="javascript" type="text/javascript"></script>
+<script type="text/javascript">
+var playlist_items={
+<?php
+$i = 0;
+$playlist = new Stream_Playlist(scrub_in($_REQUEST['playlist_id']));
+foreach($playlist->urls as $item)
+{
+ echo ($i++ > 0 ? ',' : '') . $i . ': {';
+ foreach(array('id', 'title', 'type', 'album', 'time', 'author', 'info_url') as $member)
+ {
+ echo $member . ': "' . addslashes($item->$member) . '",';
+ }
+ echo 'play_url: "' . $item->url . '",';
+ echo 'albumart_url: "' . $item->image_url . '",';
+ echo 'media_type: "' . $type . '"}';
+}
+?>
+};
+</script>
+<script src="<?php echo Config::get('web_path'); ?>/lib/javascript/html5_player.js" language="javascript" type="text/javascript"></script>
+</head>
+<body id="html5_player">
+ <div id="player">
+ <div id="albumart"></div>
+ <div id="search">
+ <input id="input_search" type="text" value="<?php echo T_('search') ?>"/>
+ <div id="clear_search"><?php echo T_('clear') ?></div>
+ </div>
+ <div id="title"><?php echo T_('Loading...') ?></div>
+ <div id="album"><?php echo T_('Loading...') ?></div>
+ <div id="artist"><?php echo T_('Loading...') ?></div>
+ <div id="progress_text"><?php echo T_('Loading...') ?></div>
+ <div id="stop"><?php echo T_('Stop') ?></div>
+ <div id="play"><?php echo T_('Play') ?></div>
+ <div id="pause"><?php echo T_('Pause') ?></div>
+ <div id="previous"><?php echo T_('Previous') ?></div>
+ <div id="next"><?php echo T_('Next') ?></div>
+ </div>
+ <div>
+ <ul id="playlist">
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/templates/show_playtype_switch.inc.php b/templates/show_playtype_switch.inc.php
index e2bf8f11..ce30bac2 100644
--- a/templates/show_playtype_switch.inc.php
+++ b/templates/show_playtype_switch.inc.php
@@ -37,6 +37,7 @@ if (Preference::has_access('play_type')) {
<option value="democratic" <?php echo $is_democratic; ?>><?php echo T_('Democratic'); ?></option>
<?php } ?>
<option value="xspf_player" <?php echo $is_xspf_player; ?>><?php echo T_('Flash Player'); ?></option>
+ <option value="html5_player" <?php echo $is_html5_player; ?>><?php echo _('HTML5 Player'); ?></option>
</select>
<?php echo Ajax::observe('play_type_select','change',Ajax::action('?page=stream&action=set_play_type','play_type_select','play_type_form'),'1'); ?>
</form>
diff --git a/themes/classic/templates/default.css b/themes/classic/templates/default.css
index f65e984c..c9e7592c 100644
--- a/themes/classic/templates/default.css
+++ b/themes/classic/templates/default.css
@@ -696,6 +696,71 @@ td.lp_current a {
}
/************************************************/
+/* HTML5 Player */
+/************************************************/
+#html5_player #albumart img
+{
+ width: 200px;
+ float: left;
+ margin: 5px;
+}
+#html5_player #title
+{
+ padding-top: 5px;
+}
+#html5_player #artist, #html5_player #album
+{
+ display: inline;
+}
+#html5_player #artist:before
+{
+ content: ' by ';
+}
+#html5_player #progress_text
+{
+ margin: 10px 0px 10px 0px;
+}
+#html5_player #stop, #html5_player #play,#html5_player #pause,#html5_player #next,#html5_player #previous,#html5_player #clear_search
+{
+ display: inline;
+ border: thin solid black;
+ cursor: pointer;
+ padding: 2px;
+ margin-right: 5px;
+}
+#html5_player #playlist
+{
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: absolute;
+ top: 105px;
+ left: 210px;
+ right: 5px;
+ bottom: 5px;
+}
+#html5_player #playlist li
+{
+ cursor: pointer;
+}
+#html5_player #playlist li.playing
+{
+ font-weight: bold;
+}
+#html5_player #stop.inactive, #html5_player #pause.inactive, #html5_player #play.inactive
+{
+ display: none;
+}
+#html5_player #search
+{
+ top: 54px;
+ right: 5px;
+ position: absolute;
+}
+#html5_player #search input
+{
+ background: transparent;
+}
+/************************************************/
/* Styles for Login template */
/************************************************/
#loginPage #maincontainer{
@@ -863,3 +928,4 @@ textarea:focus{
color: #c0c0c0;
}
+
diff --git a/themes/fresh/templates/default.css b/themes/fresh/templates/default.css
index 89ef4cb5..4ab2b2b2 100644
--- a/themes/fresh/templates/default.css
+++ b/themes/fresh/templates/default.css
@@ -993,7 +993,71 @@ table.tabledata .cel_php_setting, table.tabledata .cel_configuration, .cel_prefe
width: 200px;
}
-
+/************************************************/
+/* HTML5 Player */
+/************************************************/
+#html5_player #albumart img
+{
+ width: 200px;
+ float: left;
+ margin: 5px;
+}
+#html5_player #title
+{
+ padding-top: 5px;
+}
+#html5_player #artist, #html5_player #album
+{
+ display: inline;
+}
+#html5_player #artist:before
+{
+ content: ' by ';
+}
+#html5_player #progress_text
+{
+ margin: 10px 0px 10px 0px;
+}
+#html5_player #stop, #html5_player #play,#html5_player #pause,#html5_player #next,#html5_player #previous,#html5_player #clear_search
+{
+ display: inline;
+ border: thin solid black;
+ cursor: pointer;
+ padding: 2px;
+ margin-right: 5px;
+}
+#html5_player #playlist
+{
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: absolute;
+ top: 85px;
+ left: 210px;
+ right: 5px;
+ bottom: 5px;
+}
+#html5_player #playlist li
+{
+ cursor: pointer;
+}
+#html5_player #playlist li.playing
+{
+ font-weight: bold;
+}
+#html5_player #stop.inactive, #html5_player #pause.inactive, #html5_player #play.inactive
+{
+ display: none;
+}
+#html5_player #search
+{
+ top: 56px;
+ right: 5px;
+ position: absolute;
+}
+#html5_player #search input
+{
+ background: transparent;
+}
/***********************************************
Other
***********************************************/