--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/abook.php Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,85 @@
+<?php
+
+const FILE = "var/abook.txt";
+const DB = "sqlite:var/abook.sqlite";
+//const ABOOK = DB;
+
+set_error_handler(create_function(
+ '$errno, $errstr, $errfile, $errline, $errcontext',
+ 'if (!error_reporting()) return;
+ throw new ErrorException($errstr, 0, $errno, $errfile, $errline);'), -1);
+
+spl_autoload_register(create_function(
+ '$class',
+ '$file = "php/class.$class.php";
+ if (!file_exists($file)) return;
+ require_once $file;'));
+
+require_once 'Twig-1.3.0/lib/Twig/Autoloader.php';
+Twig_Autoloader::register();
+$twig = new Twig_Environment(new Twig_Loader_Filesystem("templates/twig"));
+
+if (isset($_REQUEST['action'])) {
+
+ try {
+
+ switch ($_SERVER['PATH_INFO']) {
+ case "/db": $abook = new Address_Book_DB(DB); break;
+ case "/file":
+ default: $abook = new Address_Book_File(FILE); break;
+ };
+
+ switch (@$_REQUEST['action']) {
+ // Nach dem Eintragen bleiben wir auf der Eintragsseite,
+ // aber wir verhindern Duplikate, die mit RELOAD passieren
+ case 'add': if ($abook->add_entry($_REQUEST)) {
+ header("Location: $_SERVER[PHP_SELF]?action=add");
+ exit(0);
+ }
+ echo $twig->render("add.html", array());
+ exit;
+ break;
+
+ // Suchen…
+ case 'search': $entries = null;
+ $error = null;
+ try {
+ $entries = $abook->search_entries($_REQUEST['pattern']);
+ }
+ catch (Address_Book_Exception $e) {
+ $error = $e->getMessage();
+ }
+
+ if (@$_REQUEST['format'] == 'table') {
+ if (is_numeric($_REQUEST['max'])) {
+ $left = count($entries) - $_REQUEST['max'];
+ if ($left > 0)
+ $entries = array_slice($entries, 0, $_REQUEST['max']);
+ else
+ $left = NULL;
+ }
+ echo $twig->render("results.html",
+ array('entries' => $entries,
+ 'left' => $left, 'error' => $error));
+ exit;
+ }
+ echo $twig->render("search.html",
+ array('entries' => $entries, 'error' => $error));
+ exit;
+ }
+ break;
+
+ }
+ catch (Address_Book_Exception $e) {
+ header("Content-Type: text/plain; charset=utf-8");
+ print "Address Book Exception: " . $e->getMessage();
+ exit;
+ }
+ catch (Exception $e) {
+ header("Content-Type: text/plain; charset=utf-8");
+ print "Ohoh\n\n" . $e;
+ exit;
+ }
+}
+
+echo $twig->render("search.html", array());
--- a/class.Address_Book_DB.php Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-<?
-require_once "interface.Address_Book.php";
-
-class Address_Book_DB implements Address_Book {
- const INSERT_ENTRY = 'INSERT INTO data (name, tel, mail)
- VALUES(:name, :tel, :mail)';
- const SELECT_ENTRY = 'SELECT name AS NAME, tel AS TEL, mail AS MAIL
- FROM data WHERE name LIKE :name
- OR tel LIKE :tel
- OR mail LIKE :mail';
-
-
- private $dbh;
-
- public function __construct($dsn, $user = null, $pass = null) {
- $this->dbh = new PDO($dsn, $user, $pass,
- array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
-
- $this->dbh->exec("CREATE TABLE IF NOT EXISTS data
- (name TEXT, tel TEXT, mail TEXT)");
-
- $this->dbh->beginTransaction();
-
- $sth = $this->dbh->prepare("SELECT COUNT(*) AS COUNT FROM data");
- $sth->execute();
- $r = $sth->fetch();
-
- if ($r['COUNT'] == 0)
- $this->insert(array("Hans Hanson", "0815", "hans@hanson.de"));
- $this->dbh->commit();
- }
-
- private function insert($entry) {
- static $sth = null;
-
- if ($sth === null)
- $sth = $this->dbh->prepare(self::INSERT_ENTRY);
-
- $sth->execute(array("name" => $entry[0],
- "tel" => $entry[1],
- "mail" => $entry[2]));
- }
-
- public function get_all_entries() {
- $entries = array();
- fseek($this->fh, 0, SEEK_SET);
- while($line = stream_get_line($this->fh, 0, self::RS)) {
- if ($line === FALSE) break;
- $entries[] = explode(self::FS, $line);
- }
- return $entries;
- }
-
- public function add_entry($entry) {
- $fields = array('name', 'tel', 'mail');
- $new = array();
- foreach($fields as $key) {
- if (!isset($entry[$key])) return;
- $new[$key] = $entry[$key];
- trim($new[$key]);
- preg_replace('/['.self::RS.self::FS.' ]+/', ' ', $new[$key]);
- if (empty($new[$key])) return;
- }
-
- flock($this->fh, LOCK_EX);
- fputs($this->fh, join(self::FS, $new) . self::RS);
- flock($this->fh, LOCK_UN);
- header("Location: $_SERVER[PHP_SELF]?action=add");
- exit;
- }
-
- public function search_entries($pattern) {
- static $sth = null;
-
- if ($sth === null)
- $sth = $this->dbh->prepare(self::SELECT_ENTRY);
-
- $pattern = trim($pattern);
- if (empty($pattern)) return;
-
- $pattern = trim($pattern, '%');
- $pattern = "%$pattern%";
-
- $sth->execute(array("name" => $pattern,
- "tel" => $pattern,
- "mail" => $pattern));
- $entries = array();
- while ($r = $sth->fetch()) {
- $entries[] = array('name' => $r['NAME'],
- 'tel' => $r['TEL'],
- 'mail' => $r['MAIL']);
- }
- return $entries;
- }
-
- public function delete_entries($ids) { }
-}
-
-?>
--- a/class.Address_Book_File.php Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-<?
-require_once "interface.Address_Book.php";
-
-class Address_Book_File implements Address_Book {
- const RS = "\n";
- const FS = "\t";
-
- private $fh;
- public function __construct($file) {
- @mkdir(dirname($file));
- $this->fh = fopen($file, "a+b");
- if (!$this->fh) {
- throw new Exception("Oooops, Problem mit $file");
- }
- flock($this->fh, LOCK_EX);
- $stat = fstat($this->fh);
- if ($stat['size'] == 0) {
- fputs($this->fh, join(self::FS, array("Hans Hanson", "0815", "hans@hanson.de"))
- . self::RS);
- }
- flock($this->fh, LOCK_SH);
- fseek($this->fh, 0, SEEK_SET);
- }
-
- public function get_all_entries() {
- $entries = array();
- fseek($this->fh, 0, SEEK_SET);
- while($line = stream_get_line($this->fh, 0, self::RS)) {
- if ($line === FALSE) break;
- $entries[] = explode(self::FS, $line);
- }
- return $entries;
- }
-
- public function add_entry($entry) {
- $fields = array('name', 'tel', 'mail');
- $new = array();
- foreach($fields as $key) {
- if (!isset($entry[$key])) return;
- $new[$key] = trim($entry[$key]);
- $new[$key] = preg_replace('/['.self::RS.self::FS.' ]+/', ' ', $new[$key]);
- if (empty($new[$key])) return;
- }
-
- flock($this->fh, LOCK_EX);
- fputs($this->fh, join(self::FS, $new) . self::RS);
- flock($this->fh, LOCK_UN);
- }
-
- public function search_entries($pattern) {
- $pattern = trim($pattern);
- if (empty($pattern)) return array();
- fseek($this->fh, 0, SEEK_SET);
- $pattern = preg_replace('|/|', '\/', $pattern);
- $entries = array();
- while ($line = stream_get_line($this->fh, 0, self::RS)) {
- if ($line === FALSE) break;
- if (!preg_match("/$pattern/i", $line)) continue;
- $entry = explode(self::FS, $line);
- $entries[] = array('name' => $entry[0],
- 'tel' => $entry[1],
- 'mail' => $entry[2]);
- }
- return $entries;
- }
-
- public function delete_entries($ids) { }
-}
-
-?>
--- a/index.php Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-<?php
-
-const FILE = "var/abook.txt";
-const DB = "sqlite:var/abook.sqlite";
-const ABOOK = DB;
-
-set_error_handler(create_function(
- '$errno, $errstr, $errfile, $errline, $errcontext',
- 'if (!error_reporting()) return;
- throw new ErrorException($errstr, 0, $errno, $errfile, $errline);'), -1);
-
-spl_autoload_register(create_function(
- '$class',
- '$file = "class.$class.php";
- if (!file_exists($file)) return;
- require_once $file;'));
-
-require_once 'Twig-1.3.0/lib/Twig/Autoloader.php';
-Twig_Autoloader::register();
-$twig = new Twig_Environment(new Twig_Loader_Filesystem("templates"));
-
-if (isset($_REQUEST['action'])) {
-
- try {
- if (preg_match('/^[\w]+\//', ABOOK))
- $abook = new Address_Book_File(FILE);
- else
- $abook = new Address_Book_DB(DB);
-
- switch (@$_REQUEST['action']) {
- // Nach dem Eintragen bleiben wir auf der Eintragsseite,
- // aber wir verhindern Duplikate, die mit RELOAD passieren
- case 'add': if ($abook->add_entry($_REQUEST)) {
- header("Location: $_SERVER[PHP_SELF]?action=add");
- exit(0);
- }
- echo $twig->render("add.html", array());
- exit;
- break;
-
- // Suchen…
- case 'search': $entries = null;
- $error = null;
- try {
- $entries = $abook->search_entries($_REQUEST['pattern']);
- }
- catch (Address_Book_Exception $e) {
- $error = $e->getMessage();
- }
-
- if (@$_REQUEST['format'] == 'table') {
- if (is_numeric($_REQUEST['max'])) {
- $left = count($entries) - $_REQUEST['max'];
- if ($left > 0)
- $entries = array_slice($entries, 0, $_REQUEST['max']);
- else
- $left = NULL;
- }
- echo $twig->render("results.html",
- array('entries' => $entries,
- 'left' => $left, 'error' => $error));
- exit;
- }
- echo $twig->render("search.html",
- array('entries' => $entries, 'error' => $error));
- exit;
- }
- break;
-
- }
- catch (Address_Book_Exception $e) {
- header("Content-Type: text/plain; charset=utf-8");
- print "Address Book Exception: " . $e->getMessage();
- exit;
- }
- catch (Exception $e) {
- header("Content-Type: text/plain; charset=utf-8");
- print "Ohoh\n\n" . $e;
- exit;
- }
-}
-
-echo $twig->render("search.html", array());
--- a/interface.Address_Book.php Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-<?
-interface Address_Book {
- function add_entry($entry);
- function search_entries($pattern);
- function delete_entries($ids);
- function get_all_entries();
-}
-?>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/php/class.Address_Book_DB.php Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,99 @@
+<?
+require_once "interface.Address_Book.php";
+
+class Address_Book_DB implements Address_Book {
+ const INSERT_ENTRY = 'INSERT INTO data (name, tel, mail)
+ VALUES(:name, :tel, :mail)';
+ const SELECT_ENTRY = 'SELECT name AS NAME, tel AS TEL, mail AS MAIL
+ FROM data WHERE name LIKE :name
+ OR tel LIKE :tel
+ OR mail LIKE :mail';
+
+
+ private $dbh;
+
+ public function __construct($dsn, $user = null, $pass = null) {
+ $this->dbh = new PDO($dsn, $user, $pass,
+ array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+
+ $this->dbh->exec("CREATE TABLE IF NOT EXISTS data
+ (name TEXT, tel TEXT, mail TEXT)");
+
+ $this->dbh->beginTransaction();
+
+ $sth = $this->dbh->prepare("SELECT COUNT(*) AS COUNT FROM data");
+ $sth->execute();
+ $r = $sth->fetch();
+
+ if ($r['COUNT'] == 0)
+ $this->insert(array("Hans Hanson", "0815", "hans@hanson.de"));
+ $this->dbh->commit();
+ }
+
+ private function insert($entry) {
+ static $sth = null;
+
+ if ($sth === null)
+ $sth = $this->dbh->prepare(self::INSERT_ENTRY);
+
+ $sth->execute(array("name" => $entry[0],
+ "tel" => $entry[1],
+ "mail" => $entry[2]));
+ }
+
+ public function get_all_entries() {
+ $entries = array();
+ fseek($this->fh, 0, SEEK_SET);
+ while($line = stream_get_line($this->fh, 0, self::RS)) {
+ if ($line === FALSE) break;
+ $entries[] = explode(self::FS, $line);
+ }
+ return $entries;
+ }
+
+ public function add_entry($entry) {
+ $fields = array('name', 'tel', 'mail');
+ $new = array();
+ foreach($fields as $key) {
+ if (!isset($entry[$key])) return;
+ $new[$key] = $entry[$key];
+ trim($new[$key]);
+ preg_replace('/['.self::RS.self::FS.' ]+/', ' ', $new[$key]);
+ if (empty($new[$key])) return;
+ }
+
+ flock($this->fh, LOCK_EX);
+ fputs($this->fh, join(self::FS, $new) . self::RS);
+ flock($this->fh, LOCK_UN);
+ header("Location: $_SERVER[PHP_SELF]?action=add");
+ exit;
+ }
+
+ public function search_entries($pattern) {
+ static $sth = null;
+
+ if ($sth === null)
+ $sth = $this->dbh->prepare(self::SELECT_ENTRY);
+
+ $pattern = trim($pattern);
+ if (empty($pattern)) return;
+
+ $pattern = trim($pattern, '%');
+ $pattern = "%$pattern%";
+
+ $sth->execute(array("name" => $pattern,
+ "tel" => $pattern,
+ "mail" => $pattern));
+ $entries = array();
+ while ($r = $sth->fetch()) {
+ $entries[] = array('name' => $r['NAME'],
+ 'tel' => $r['TEL'],
+ 'mail' => $r['MAIL']);
+ }
+ return $entries;
+ }
+
+ public function delete_entries($ids) { }
+}
+
+?>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/php/class.Address_Book_File.php Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,70 @@
+<?
+require_once "interface.Address_Book.php";
+
+class Address_Book_File implements Address_Book {
+ const RS = "\n";
+ const FS = "\t";
+
+ private $fh;
+ public function __construct($file) {
+ @mkdir(dirname($file));
+ $this->fh = fopen($file, "a+b");
+ if (!$this->fh) {
+ throw new Exception("Oooops, Problem mit $file");
+ }
+ flock($this->fh, LOCK_EX);
+ $stat = fstat($this->fh);
+ if ($stat['size'] == 0) {
+ fputs($this->fh, join(self::FS, array("Hans Hanson", "0815", "hans@hanson.de"))
+ . self::RS);
+ }
+ flock($this->fh, LOCK_SH);
+ fseek($this->fh, 0, SEEK_SET);
+ }
+
+ public function get_all_entries() {
+ $entries = array();
+ fseek($this->fh, 0, SEEK_SET);
+ while($line = stream_get_line($this->fh, 0, self::RS)) {
+ if ($line === FALSE) break;
+ $entries[] = explode(self::FS, $line);
+ }
+ return $entries;
+ }
+
+ public function add_entry($entry) {
+ $fields = array('name', 'tel', 'mail');
+ $new = array();
+ foreach($fields as $key) {
+ if (!isset($entry[$key])) return;
+ $new[$key] = trim($entry[$key]);
+ $new[$key] = preg_replace('/['.self::RS.self::FS.' ]+/', ' ', $new[$key]);
+ if (empty($new[$key])) return;
+ }
+
+ flock($this->fh, LOCK_EX);
+ fputs($this->fh, join(self::FS, $new) . self::RS);
+ flock($this->fh, LOCK_UN);
+ }
+
+ public function search_entries($pattern) {
+ $pattern = trim($pattern);
+ if (empty($pattern)) return array();
+ fseek($this->fh, 0, SEEK_SET);
+ $pattern = preg_replace('|/|', '\/', $pattern);
+ $entries = array();
+ while ($line = stream_get_line($this->fh, 0, self::RS)) {
+ if ($line === FALSE) break;
+ if (!preg_match("/$pattern/i", $line)) continue;
+ $entry = explode(self::FS, $line);
+ $entries[] = array('name' => $entry[0],
+ 'tel' => $entry[1],
+ 'mail' => $entry[2]);
+ }
+ return $entries;
+ }
+
+ public function delete_entries($ids) { }
+}
+
+?>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/php/interface.Address_Book.php Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,8 @@
+<?
+interface Address_Book {
+ function add_entry($entry);
+ function search_entries($pattern);
+ function delete_entries($ids);
+ function get_all_entries();
+}
+?>
--- a/templates/add.html Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-<?=header("Content-Type: text/html; charset=UTF-8");?>
-<html>
-<head>
-
-<style type="text/css">
- form label { display:block; float:left; width:10ex; }
-</style>
-
-</head><body>
-
-[ <a href="?">Home</a> ]
-
-<h1>Adressbuch</h1>
-
- <p>
- <form>
- <label for=name>Name</label>
- <input id=name type=text name=name /><br>
- <label for=tel>Telefon</label>
- <input id=tel type=text name=tel /><br>
- <label for=mail>Mail</label>
- <input id=mail type=text name=mail /><br>
- <input type=hidden name=action value=add />
- <input type=submit />
- </form>
-
-</body>
--- a/templates/results.html Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-<html>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<body>
-
-{% if error %}
-<font color=red> {{ error }} </font>
-{% else %}
-{% for entry in entries %}
-
- {% if loop.first %}
- <table>
- <tr><th>Name</th><th>Telefon</th><th>Mail</th></tr>
- {% endif %}
-
- <tr><td>{{entry.name}}</td>
- <td>{{entry.tel}}</td>
- <td>{{entry.mail}}</td>
- </tr>
-
- {% if loop.last %}
- </table>
- <font color=red>{{ left ? left ~ ' weitere Einträge' : '' }}</font>
- {% endif %}
-{% else %}
- Sorry, keine Einträge gefunden.
-{% endfor %}
-{% endif %}
-</body>
-</html>
--- a/templates/search.html Wed Oct 26 21:46:50 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-<html>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<head>
-
-<script type=text/javascript>
- // source: http://de.wikibooks.org/wiki/Websiteentwicklung:_AJAX:_Erstes_Programm
- var _ajax;
-
- function got_answer() {
- if (_ajax.readyState == 4 && _ajax.status == 200) {
- document.getElementById('result').innerHTML = _ajax.responseText;
- }
- }
-
- function search_entries(value) {
- value = encodeURIComponent(value);
- request = "?action=search&format=table&max=3&pattern=" + value;
- _ajax = new XMLHttpRequest();
- _ajax.onreadystatechange = got_answer;
- _ajax.open("GET", request, true);
- _ajax.send();
- }
-</script>
-
-<style type="text/css">
- form label { display:block; float:left; width:10ex; }
-</style>
-
-</head><body>
-
-[ <a href="?action=add">Add Entries</a> ]
-
-<h1>Adressbuch</h1>
-
- <form autocomplete=off>
- <label for=pattern>Suche</label>
- <input id=pattern type=text name=pattern
- onKeyUp="search_entries(this.value)"
- onChange="submit()") />
- <input type=hidden name=action value=search autocomplete=off />
- <noscript><input type=submit /></noscript>
- </form>
-
- <div id=result>
- {% include "results.html" %}
- </div>
-
-</body>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/twig/add.html Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,27 @@
+<?=header("Content-Type: text/html; charset=UTF-8");?>
+<html>
+<head>
+
+<style type="text/css">
+ form label { display:block; float:left; width:10ex; }
+</style>
+
+</head><body>
+
+[ <a href="?">Home</a> ]
+
+<h1>Adressbuch</h1>
+
+ <p>
+ <form>
+ <label for=name>Name</label>
+ <input id=name type=text name=name /><br>
+ <label for=tel>Telefon</label>
+ <input id=tel type=text name=tel /><br>
+ <label for=mail>Mail</label>
+ <input id=mail type=text name=mail /><br>
+ <input type=hidden name=action value=add />
+ <input type=submit />
+ </form>
+
+</body>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/twig/results.html Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,29 @@
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<body>
+
+{% if error %}
+<font color=red> {{ error }} </font>
+{% else %}
+{% for entry in entries %}
+
+ {% if loop.first %}
+ <table>
+ <tr><th>Name</th><th>Telefon</th><th>Mail</th></tr>
+ {% endif %}
+
+ <tr><td>{{entry.name}}</td>
+ <td>{{entry.tel}}</td>
+ <td>{{entry.mail}}</td>
+ </tr>
+
+ {% if loop.last %}
+ </table>
+ <font color=red>{{ left ? left ~ ' weitere Einträge' : '' }}</font>
+ {% endif %}
+{% else %}
+ Sorry, keine Einträge gefunden.
+{% endfor %}
+{% endif %}
+</body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/twig/search.html Thu Oct 27 16:56:26 2011 +0200
@@ -0,0 +1,48 @@
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<head>
+
+<script type=text/javascript>
+ // source: http://de.wikibooks.org/wiki/Websiteentwicklung:_AJAX:_Erstes_Programm
+ var _ajax;
+
+ function got_answer() {
+ if (_ajax.readyState == 4 && _ajax.status == 200) {
+ document.getElementById('result').innerHTML = _ajax.responseText;
+ }
+ }
+
+ function search_entries(value) {
+ value = encodeURIComponent(value);
+ request = "?action=search&format=table&max=3&pattern=" + value;
+ _ajax = new XMLHttpRequest();
+ _ajax.onreadystatechange = got_answer;
+ _ajax.open("GET", request, true);
+ _ajax.send();
+ }
+</script>
+
+<style type="text/css">
+ form label { display:block; float:left; width:10ex; }
+</style>
+
+</head><body>
+
+[ <a href="?action=add">Add Entries</a> ]
+
+<h1>Adressbuch</h1>
+
+ <form autocomplete=off>
+ <label for=pattern>Suche</label>
+ <input id=pattern type=text name=pattern
+ onKeyUp="search_entries(this.value)"
+ onChange="submit()") />
+ <input type=hidden name=action value=search autocomplete=off />
+ <noscript><input type=submit /></noscript>
+ </form>
+
+ <div id=result>
+ {% include "results.html" %}
+ </div>
+
+</body>