├── README for Whispering.txt ├── README.markdown ├── Setup.sql ├── chatrooms.php ├── check.js ├── dbcon.php ├── images ├── bg.jpg └── logo.jpg ├── index.php ├── jumpin.php ├── main.css └── room ├── chat.js ├── chatroom-css.txt ├── chatroom-html.txt ├── chatroom-javascript.txt ├── index.php ├── process.php ├── settings.js ├── smiles ├── bigsmile.png ├── sad.png ├── smile.gif └── tongue.png ├── update.php └── userlist.php /README for Whispering.txt: -------------------------------------------------------------------------------- 1 | ----------- 2 | Add Whispering in Chat v2, by Christian Mehler 3 | -------------- 4 | 5 | 6 | Changes to chat.js: 7 | 8 | function Chat (filetxt, user) { 9 | file = filetxt; 10 | usernameid = user; 11 | this.init = chatInit; 12 | this.update = updateChat; 13 | this.send = sendChat; 14 | this.getState = getStateOfChat; 15 | this.trim = trimstr; 16 | this.getUsers = getuserlist; 17 | } 18 | 19 | function updateChat(){ 20 | 21 | $.ajax({ 22 | 23 | type: "GET", 24 | url: "update.php", 25 | data: { 26 | 'state': state, 27 | 'file' : file, 28 | 'nickname': usernameid 29 | }, 30 | dataType: "json", 31 | ... 32 | 33 | function getuserlist(room, username) { 34 | 35 | roomid = room; 36 | usernameid = username; 37 | 38 | $.ajax({ 39 | type: "GET", 40 | url: "userlist.php", 41 | data: { 42 | 'room' : room, 43 | 'username': username, 44 | 'current' : numOfUsers 45 | 46 | }, 47 | dataType: "json", 48 | cache: false, 49 | success: function(data) { 50 | 51 | if (numOfUsers != data.numOfUsers) { 52 | numOfUsers = data.numOfUsers; 53 | var list = " 54 | 55 | Current Chatters 56 | "; 57 | for (var i = 0; i < data.userlist.length; i++) { 58 | list += ' 59 | '+ data.userlist[i] +" 60 | "; 61 | } 62 | $('#userlist').html($(" 63 | "+ list +" 64 | ")); 65 | } 66 | 67 | setTimeout('getuserlist(roomid, usernameid)', 1); 68 | 69 | }, 70 | }); 71 | 72 | } 73 | 74 | function wisper(to) 75 | { 76 | $('#sendie').val('@'+to+' '+$('#sendie').val()); 77 | } 78 | 79 | 80 | 81 | 82 | 83 | --------- 84 | Changes to process.php (other saving): 85 | --------- 86 | 87 | fwrite(fopen($file, 'a'), $nickname . "~\t~" . $message = str_replace("\n", " ", $message) . "\n"); 88 | 89 | 90 | --------- 91 | Changes to update.php 92 | --------- 93 | 94 | - at the beginning add 95 | $nickname = htmlentities(strip_tags($_GET['nickname']), ENT_QUOTES); 96 | 97 | 98 | -later 99 | ... 100 | if ($state == $count) { 101 | 102 | $log['state'] = $state; 103 | $log['t'] = "continue"; 104 | 105 | } else { 106 | 107 | $text= array(); 108 | $log['state'] = $state + getlines(getfile($file)) - $state; 109 | 110 | foreach (getfile($file) as $line_num => $line) { 111 | if ($line_num >= $state) { 112 | $line = explode("~\t~", $line); 113 | if(substr($line[1], 0, 1)!='@') 114 | $text[] = ''.$line[0].''.$line[1]; 115 | elseif($line[0]==$nickname || substr($line[1], 0, strlen('@'.$nickname))=='@'.$nickname) 116 | $text[] = ''.$line[0].' '.$line[1].''; 117 | } 118 | 119 | $log['text'] = $text; 120 | } 121 | } 122 | 123 | echo json_encode($log); 124 | 125 | ?> -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | A group chat room with PHP, jQuery, and Text Files. 2 | 3 | [Tutorial article for Chat v1](http://css-tricks.com/jquery-php-chat/) 4 | [Tutorial article for Chat v2](http://css-tricks.com/chat2/) 5 | 6 | **WARNING**: Please check out the file at /room/update.php which contained some commented code on only allowing that file to be called 7 | via AJAX and from a certain referrer. Without that precaution, a crafty hacker could gain access to any file on the server. -------------------------------------------------------------------------------- /Setup.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `chat_rooms` ( 2 | `id` tinyint(4) NOT NULL AUTO_INCREMENT, 3 | `name` varchar(20) NOT NULL, 4 | `numofuser` int(10) NOT NULL, 5 | `file` varchar(30) NOT NULL, 6 | PRIMARY KEY (`id`) 7 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ; 8 | 9 | CREATE TABLE IF NOT EXISTS `chat_users` ( 10 | `id` tinyint(10) NOT NULL AUTO_INCREMENT, 11 | `username` varchar(100) NOT NULL, 12 | `status` tinyint(1) NOT NULL, 13 | `time_mod` int(100) NOT NULL, 14 | PRIMARY KEY (`id`), 15 | UNIQUE KEY `username` (`username`) 16 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=23 ; 17 | 18 | CREATE TABLE IF NOT EXISTS `chat_users_rooms` ( 19 | `id` int(100) NOT NULL AUTO_INCREMENT, 20 | `username` varchar(100) NOT NULL, 21 | `room` varchar(100) NOT NULL, 22 | `mod_time` int(40) NOT NULL, 23 | PRIMARY KEY (`id`) 24 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1472 ; 25 | -------------------------------------------------------------------------------- /chatrooms.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Chat Rooms 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 37 | 38 |
39 | 40 |
41 |

Rooms

42 | 54 |
55 |
56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /check.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | $("#userid").keyup(function(event) { 4 | 5 | var username = $(this).val(); 6 | 7 | $.ajax({ 8 | type: "POST", 9 | url: "jumpin.php", 10 | data: { userid : username }, 11 | dataType: "json", 12 | success: function(data){ 13 | $("#status").html(data.result); 14 | 15 | if (data.inuse == "inuse") { 16 | $("#jumpin").val("Check"); 17 | } else { 18 | $("#jumpin").val("Go in!"); 19 | } 20 | 21 | } 22 | }); 23 | 24 | }); 25 | 26 | }); -------------------------------------------------------------------------------- /dbcon.php: -------------------------------------------------------------------------------- 1 | | 66 | // +----------------------------------------------------------------------+ 67 | // 68 | // Kohana Modifications: 69 | // * Changed double quotes to single quotes, changed indenting and spacing 70 | // * Removed magic_quotes stuff 71 | // * Increased regex readability: 72 | // * Used delimeters that aren't found in the pattern 73 | // * Removed all unneeded escapes 74 | // * Deleted U modifiers and swapped greediness where needed 75 | // * Increased regex speed: 76 | // * Made capturing parentheses non-capturing where possible 77 | // * Removed parentheses where possible 78 | // * Split up alternation alternatives 79 | // * Made some quantifiers possessive 80 | 81 | // Fix &entity\n; 82 | $data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data); 83 | $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data); 84 | $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data); 85 | $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8'); 86 | 87 | // Remove any attribute starting with "on" or xmlns 88 | $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data); 89 | 90 | // Remove javascript: and vbscript: protocols 91 | $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data); 92 | $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data); 93 | $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data); 94 | 95 | // Only works in IE: 96 | $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data); 97 | $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data); 98 | $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data); 99 | 100 | // Remove namespaced elements (we do not need them) 101 | $data = preg_replace('#]*+>#i', '', $data); 102 | 103 | do 104 | { 105 | // Remove really unwanted tags 106 | $old_data = $data; 107 | $data = preg_replace('#]*+>#i', '', $data); 108 | } 109 | while ($old_data !== $data); 110 | 111 | return $data; 112 | } 113 | ?> -------------------------------------------------------------------------------- /images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/images/bg.jpg -------------------------------------------------------------------------------- /images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/images/logo.jpg -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Chat2 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 28 | 29 |
30 |
31 | 32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 |
40 | 41 | 42 | 43 |
44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /jumpin.php: -------------------------------------------------------------------------------- 1 | Great! You found a username not in use"; 20 | $data['inuse'] = "notinuse"; 21 | } else { 22 | $data['result'] = "
That username is already in use. (Usernames take 2 minutes without use to expire)
"; 23 | $data['inuse'] = "inuse"; 24 | } 25 | 26 | echo json_encode($data); 27 | 28 | } 29 | 30 | } else { 31 | 32 | $username = cleanInput($_POST['userid']); 33 | 34 | if (checkVar($username)) { 35 | 36 | $getUsers = "SELECT * 37 | FROM chat_users 38 | WHERE username = '$username'"; 39 | 40 | if (!hasData($getUsers)) { 41 | 42 | $now = time(); 43 | 44 | $postUsers = "INSERT INTO `chat_users` ( 45 | `id` , 46 | `username` , 47 | `status` , 48 | `time_mod` 49 | ) 50 | VALUES ( 51 | NULL , '$username', '1', '$now' 52 | )"; 53 | 54 | mysql_query($postUsers); 55 | 56 | $_SESSION['userid'] = $username; 57 | 58 | header('Location: ./chatrooms.php'); 59 | 60 | } else { 61 | 62 | header('Location: ./?error=1'); 63 | 64 | } 65 | 66 | } 67 | 68 | } 69 | 70 | ?> -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | * { margin: 0; padding: 0; } 2 | body { font: 14px/1.4 Georgia, Serif; background: url(images/bg.jpg); color: white; } 3 | 4 | #page-wrap { margin: 0 auto; width: 600px; position: relative; } 5 | 6 | #header h1 a { display: block; width: 173px; height: 79px; background: url(images/logo.jpg) no-repeat; text-indent: -9999px; } 7 | 8 | h1,h2,h3 { margin: 0 0 20px 0; font-weight: normal; } 9 | h1 { font-size: 36px; } 10 | h2 { font-size: 30px } 11 | h3 { font-size: 24px } 12 | a:focus { outline: 0; } 13 | 14 | .message { margin: 15px 0; padding: 10px; -moz-border-radius: 20px; -webkit-border-radius: 20px; font-size: 15px; } 15 | .warning { background: #ffd5d5; border: 3px solid #ff3939; color: #ff3939; } 16 | .success { background: #e5ffdb; border: 3px solid #0dba32; color: #0dba32; } 17 | 18 | label { font-size: 20px; } 19 | 20 | #section { background: rgba(0,0,0,0.2); padding: 20px; } 21 | #userid { font-size: 20px; padding:8px 10px; width: 330px; } 22 | 23 | #you { position: absolute; top: 45px; right: 0; font-size: 20px; background: rgba(0,0,0,0.2); padding: 5px 10px; } 24 | #you span { font: italic 12px Georgia, Serif; } 25 | 26 | #rooms { } 27 | #rooms ul { list-style: none; } 28 | #rooms ul li { margin: 0 0 5px 0; } 29 | #rooms li a { background: rgba(0,0,0,0.2); padding: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; display: block; position: relative; } 30 | #rooms li a:hover { background: none; -moz-box-shadow: 0 0 5px black; -webkit-box-shadow: 0 0 5px black; } 31 | #rooms li a span { position: absolute; top: 3px; right: 10px; } 32 | #rooms li a span strong { color: white; font-size: 20px; } 33 | #rooms a { text-decoration: none; color: #4fb90f; font: 18px;} 34 | 35 | #chat-wrap { border: 1px solid #eee; width: 380px; float:left; } 36 | #chat-area { height: 300px; overflow: auto; padding: 20px; background: white; } 37 | #chat-area span { color: white; background: #333; padding: 4px 8px; -moz-border-radius: 5px; -webkit-border-radius: 8px; margin: 0 5px 0 0; } 38 | #chat-area p { padding: 8px 0; border-bottom: 1px solid #ccc; color: #333; } 39 | 40 | #name-area { top: 12px; right: 0; color: white; font: bold 12px "Lucida Grande", Sans-Serif; text-align: right; } 41 | #name-area span { color: #fa9f00; } 42 | 43 | #send-message-area p { float: left; color: white; padding-top: 27px; font-size: 14px; } 44 | 45 | #sendie { width: 360px; padding: 10px; margin: 5px 0 0 0; font: 12px "Lucida Grande", Sans-Serif; } 46 | 47 | #userlist { float: right; width: 160px; height: 410px; overflow-y: scroll; } 48 | #userlist ul { color: #fff; font-size: 18px; list-style: none; } 49 | #userlist ul li { } 50 | #userlist ul li.head { border-bottom: 1px solid #eee; } 51 | 52 | -------------------------------------------------------------------------------- /room/chat.js: -------------------------------------------------------------------------------- 1 | var state; 2 | var mes; 3 | var file; 4 | var numOfUsers = 0; 5 | var roomid; 6 | var usernameid; 7 | 8 | function Chat (filetxt) { 9 | file = filetxt; 10 | this.init = chatInit; 11 | this.update = updateChat; 12 | this.send = sendChat; 13 | this.getState = getStateOfChat; 14 | this.trim = trimstr; 15 | this.getUsers = getuserlist; 16 | } 17 | 18 | function chatInit(){ 19 | getStateOfChat(); 20 | } 21 | 22 | function wait(){ 23 | updateChat(); 24 | } 25 | 26 | $.ajaxSetup({ 27 | cache: false // for ie 28 | }); 29 | 30 | //gets the state of the chat 31 | function getStateOfChat(){ 32 | $.ajax({ 33 | type: "POST", 34 | url: "process.php", 35 | data: { 36 | 'function': 'getState', 37 | 'file': file 38 | }, 39 | dataType: "json", 40 | 41 | success: function(data){ 42 | state = data.state-5; 43 | updateChat(); 44 | }, 45 | }); 46 | } 47 | 48 | //Updates the chat 49 | function updateChat(){ 50 | 51 | $.ajax({ 52 | 53 | type: "GET", 54 | url: "update.php", 55 | data: { 56 | 'state': state, 57 | 'file' : file 58 | }, 59 | dataType: "json", 60 | cache: false, 61 | success: function(data) { 62 | 63 | if (data.text != null) { 64 | for (var i = 0; i < data.text.length; i++) { 65 | $('#chat-area').append($("

"+ data.text[i] +"

")); 66 | } 67 | 68 | document.getElementById('chat-area').scrollTop = document.getElementById('chat-area').scrollHeight; 69 | 70 | } 71 | 72 | instanse = false; 73 | state = data.state; 74 | setTimeout('updateChat()', 1); 75 | 76 | }, 77 | }); 78 | } 79 | 80 | //send the message 81 | function sendChat(message, nickname) { 82 | 83 | $.ajax({ 84 | type: "POST", 85 | url: "process.php", 86 | data: { 87 | 'function': 'send', 88 | 'message': message, 89 | 'nickname': nickname, 90 | 'file': file 91 | }, 92 | dataType: "json", 93 | success: function(data){ 94 | 95 | }, 96 | }); 97 | 98 | } 99 | 100 | function trimstr(s, limit) { 101 | return s.substring(0, limit); 102 | } 103 | 104 | function getuserlist(room, username) { 105 | 106 | roomid = room; 107 | usernameid = username; 108 | 109 | $.ajax({ 110 | type: "GET", 111 | url: "userlist.php", 112 | data: { 113 | 'room' : room, 114 | 'username': username, 115 | 'current' : numOfUsers 116 | 117 | }, 118 | dataType: "json", 119 | cache: false, 120 | success: function(data) { 121 | 122 | if (numOfUsers != data.numOfUsers) { 123 | numOfUsers = data.numOfUsers; 124 | var list = "
  • Current Chatters
  • "; 125 | for (var i = 0; i < data.userlist.length; i++) { 126 | list += "
  • "+ data.userlist[i] +"
  • "; 127 | } 128 | $('#userlist').html($("")); 129 | } 130 | 131 | setTimeout('getuserlist(roomid, usernameid)', 1); 132 | 133 | }, 134 | }); 135 | 136 | } -------------------------------------------------------------------------------- /room/chatroom-css.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/chatroom-css.txt -------------------------------------------------------------------------------- /room/chatroom-html.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/chatroom-html.txt -------------------------------------------------------------------------------- /room/chatroom-javascript.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/chatroom-javascript.txt -------------------------------------------------------------------------------- /room/index.php: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Welcome to: <?php echo $name; ?> 36 | 37 | 38 | 39 | 40 | 41 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
    54 | 55 | 62 | 63 |
    64 | 65 |

    66 | 67 |
    68 |
    69 |
    70 | 71 |
    72 | 73 |
    74 | 75 |
    76 | 77 |
    78 | 79 |
    80 | 81 | 82 | 83 | 84 | 85 | 90 | -------------------------------------------------------------------------------- /room/process.php: -------------------------------------------------------------------------------- 1 | ", "", "", "", ""); 24 | $reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/"; 25 | $blankexp = "/^\n/"; 26 | $message = htmlentities(strip_tags($_POST['message']), ENT_QUOTES); 27 | 28 | if (!preg_match($blankexp, $message)) { 29 | 30 | if (preg_match($reg_exUrl, $message, $url)) { 31 | $message = preg_replace($reg_exUrl, ''.$url[0].'', $message); 32 | } 33 | $message = preg_replace($patterns, $replacements, $message); 34 | 35 | fwrite(fopen($file, 'a'), "". $nickname . "" . $message = str_replace("\n", " ", $message) . "\n"); 36 | } 37 | 38 | break; 39 | 40 | } 41 | 42 | echo json_encode($log); 43 | 44 | ?> -------------------------------------------------------------------------------- /room/settings.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | $("#sendie").keydown(function(event) { 4 | 5 | var key = event.which; 6 | 7 | // all keys including return 8 | if (key >= 33) { 9 | 10 | var maxLength = $(this).attr("maxlength"); 11 | var length = this.value.length; 12 | 13 | if (length >= maxLength) { 14 | event.preventDefault(); 15 | } 16 | } 17 | }); 18 | 19 | $('#sendie').keyup(function(e) { 20 | 21 | if (e.keyCode == 13) { 22 | 23 | var text = $(this).val(); 24 | var maxLength = $(this).attr("maxlength"); 25 | var length = text.length; 26 | 27 | if (length <= maxLength + 1) { 28 | chat.send(text, name); 29 | $(this).val(""); 30 | } else { 31 | $(this).val(text.substring(0, maxLength)); 32 | } 33 | 34 | } 35 | 36 | }); 37 | 38 | }); -------------------------------------------------------------------------------- /room/smiles/bigsmile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/smiles/bigsmile.png -------------------------------------------------------------------------------- /room/smiles/sad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/smiles/sad.png -------------------------------------------------------------------------------- /room/smiles/smile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/smiles/smile.gif -------------------------------------------------------------------------------- /room/smiles/tongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSS-Tricks/Chat2/01fbad700e70c594fcfabfd08eb2ebefae7057b1/room/smiles/tongue.png -------------------------------------------------------------------------------- /room/update.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | $line) { 59 | if ($line_num >= $state) { 60 | $text[] = $line = str_replace("\n", "", $line); 61 | } 62 | 63 | $log['text'] = $text; 64 | } 65 | } 66 | 67 | echo json_encode($log); 68 | 69 | ?> -------------------------------------------------------------------------------- /room/userlist.php: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------