From 971e2d08b72bb34e75d1355d32709f5cd3dc0498 Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 9 Apr 2011 02:27:09 +0800 Subject: [PATCH 01/61] Read Me text added --- README | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README b/README index e69de29bb..f8ae662ed 100644 --- a/README +++ b/README @@ -0,0 +1,25 @@ +# Luracast Restler + +Restler is a simple and effective multi-protocol REST API Server written in PHP. + +* [Developer Home](http://luracast.com/products/restler/) + +## Features + +* Light weight +* Flexible +* Customizable +* Supports HTTP request methods GET, POST, PUT, and DELETE +* Clients can use X-HTTP-Method-Override header +* Two way format conversion +* Pluggable Formatters +* Comes with JSON, and XML formats +* Pluggable Authentication schemes +* Comes with Digest Authentication Example +* URL to Function mapping +* URL part to Function parameter mapping +* Supports URLEncoded format for simplified input +* Query parameters to Function parameter mapping +* Source code distributed under LGPL + +more information is available on the [features page](http://luracast.com/products/restler/features/) \ No newline at end of file From 229a3b7787be4dabd287eef4ecad317686f6f38f Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 9 Apr 2011 02:30:40 +0800 Subject: [PATCH 02/61] Change of extension --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From 7b7aa6ab9c3ad45886182990aea299b273cbb924 Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 9 Apr 2011 02:34:07 +0800 Subject: [PATCH 03/61] Sharing the Restler v1.0 Beta source files --- restler.php | 891 ++++++++++++++++++++++++++++++++++++++++++++++++++ xmlformat.php | 193 +++++++++++ 2 files changed, 1084 insertions(+) create mode 100644 restler.php create mode 100644 xmlformat.php diff --git a/restler.php b/restler.php new file mode 100644 index 000000000..47dedfbb8 --- /dev/null +++ b/restler.php @@ -0,0 +1,891 @@ + + * + * @category Framework + * @package restler + * @author Jac Wright + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + * @version 1.0.17 beta + */ + +class Restler +{ + /** + * URL of the currently mapped service + * @var string + */ + public $url; + + /** + * Http request method of the current request. + * Any value between [GET, PUT, POST, DELETE] + * @var string + */ + public $request_method; + + /** + * Requested data format. Instance of the current format class + * which implements the iFormat interface + * @var iFormat + * @example jsonFormat, xmlFormat, yamlFormat etc + */ + public $request_format; + + /** + * Data sent to the service + * @var string + */ + public $request_data; + + /** + * Used in production mode to store the URL Map to disk + * @var string + */ + public $cache_dir = '.'; + + /** + * Response data format. Instance of the current format class + * which implements the iFormat interface + * @var iFormat + * @example jsonFormat, xmlFormat, yamlFormat etc + */ + public $response_format; + + /////////////////////////////////////// + + /** + * When set to false, it will run in debug mode and parse the + * class files every time to map it to the URL + * @var boolean + */ + protected $production_mode; + + + /** + * Associated array that maps urls to their respective service and function + * @var array + */ + protected $url_map = array(); + + /** + * Associated array that maps formats to their respective format class name + * @var array + */ + protected $format_map = array(); + + /** + * Instance of the current api service class + * @var object + */ + protected $service_class; + + /** + * Name of the api method being called + * @var string + */ + protected $service_method; + + protected $auth_classes = array(); + protected $error_classes = array(); + + /** + * Caching of url map is enabled or not + * @var boolean + */ + protected $cached; + + /** + * Constructor + * @param boolean $production_mode When set to false, it will run in + * debug mode and parse the class files every time to map it to the URL + */ + public function __construct($production_mode = false) + { + $this->production_mode = $production_mode; + $this->cache_dir = $this->cache_dir == '.' ? getcwd() : $this->cache_dir; + } + + /** + * Store the url map cache if needed + */ + public function __destruct() + { + if ($this->production_mode && !$this->cached) { + if (function_exists('apc_store')) { + apc_store('urlMap', $this->url_map); + } else { + file_put_contents($this->cache_dir . '/urlMap.cache', serialize($this->url_map)); + } + } + } + + /** + * Use it in production mode to refresh the url map cache + */ + public function refreshCache() + { + $this->url_map = array(); + $this->cached = false; + } + + /** + * Call this method and pass all the formats that should be + * supported by the API. Accepts multiple parameters + * @param string class name of the format class (iFormat) + * @example $restler->setSupportedFormats('JsonFormat', 'XmlFormat'...); + */ + public function setSupportedFormats() + { + $args = func_get_args(); + foreach ($args as $class) { + if (is_string($class) && !class_exists($class)){ + throw new Exception('Invalid format class'); + } elseif (!is_string($class) && !is_object($class)) { + throw new Exception('Invalid format class; must be a classname or object'); + } + /** + * Format Instance + * @var iFormat + */ + $obj = is_string($class) ? new $class() : $class; + if(! $obj instanceof iFormat){ + throw new Exception('Invalid format class; must be implementing iFormat'); + } + foreach ($obj->getMIMEMap() as $key => $value) { + if(!isset($this->format_map[$key]))$this->format_map[$key]=$class; + if(!isset($this->format_map[$value]))$this->format_map[$value]=$class; + } + } + $this->format_map['default']=$args[0]; + } + + /** + * Add api classes throgh this function. All the public methods which have + * url comment will be exposed as the public api. + * All the protected methods with url comment will exposed as protected api + * which will require authentication + * @param string $class name of the service class + * @param string $basePath optional url prefix for mapping + * @throws Exception when supplied with invalid class name + */ + public function addAPIClass($class, $basePath = '') + { + $this->loadCache(); + if (!$this->cached) { + if (is_string($class) && !class_exists($class)){ + throw new Exception('Invalid method or class'); + } elseif (!is_string($class) && !is_object($class)) { + throw new Exception('Invalid method or class; must be a classname or object'); + } + + if (strlen($basePath) > 0 && $basePath[0] == '/') { + $basePath = substr($basePath, 1); + } + if (strlen($basePath) > 0 && $basePath[strlen($basePath) - 1] != '/') { + $basePath .= '/'; + } + + $this->generateMap($class, $basePath); + } + } + + /** + * protected methods will need atleast one authentication class to be set + * in order to allow that method to be executed + * @param string $class name of the authentication class + */ + public function addAuthenticationClass($class) + { + $this->auth_classes[] = $class; + $this->addAPIClass($class); + } + + /** + * Add class for custom error handling + * @param string $class name of the error handling class + */ + public function addErrorClass($class) + { + $this->errorClasses[] = $class; + } + + /** + * Convenience method to respond with an error message + * @param int $statusCode http error code + * @param string $errorMessage optional custom error message + */ + public function handleError($statusCode, $errorMessage = null) + { + $method = "handle$statusCode"; + foreach ($this->error_classes as $class) { + $obj = is_string($class) ? new $class() : $class; + if (!method_exists($obj, $method)) { + $obj->$method(); + return; + } + } + $message = $this->codes[$statusCode] . (!$errorMessage || $this->production_mode ? '' : ': ' . $errorMessage); + + $this->setStatus($statusCode); + $this->sendData(array('error' => array('code' => $statusCode, 'message' => $message))); + } + + /** + * Main function for processing the api request + * and return the response + * @throws Exception when the api service class is missing + * @throws RestException to send error response + */ + public function handle() + { + $this->url = $this->getPath(); + $this->request_method = $this->getRequestMethod(); + + if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); + $this->response_format = $this->getResponseFormat(); + $this->request_format = $this->getRequestFormat(); + if($this->request_format==null)$this->request_format = $this->response_format; + //echo $this->request_format; + + if($this->request_method == 'PUT' || $this->request_method == 'POST') { + $this->request_data = $this->getRequestData(); + } + list($class, $method, $params, $is_public) = $this->mapUrlToMethod(); + + if($class) { + if(is_string($class) && class_exists($class)){ + $this->service_class=$obj=new $class(); + $this->service_method=$method; + }else{ + throw new Exception("Class $class does not exist"); + } + }else{ + $this->handleError(404); + return; + } + $obj->restler = $this; + + $pre_process = $this->request_format->getExtension().'_'.$method; + if(method_exists($obj,$pre_process)) { + call_user_func_array(array($obj, $pre_process), $params); + } + try { + if($is_public) { + $result = call_user_func_array(array($obj,$method), $params); + }else{ + $auth_method = 'isAuthenticated'; + if(!count($this->auth_classes))throw new RestException(401); + foreach ($this->auth_classes as $auth_class) { + $auth_obj = is_string($auth_class) ? new $auth_class() : $auth_class; + if (!method_exists($auth_obj, $auth_method) || !$auth_obj->$auth_method()) { + throw new RestException(401); + } + } + $reflection_method = new ReflectionMethod($class, $method); + $reflection_method->setAccessible(true); + $result = $reflection_method->invokeArgs($obj, $params); + } + } catch (RestException $e) { + $this->handleError($e->getCode(), $e->getMessage()); + } + if (isset($result) && $result !== null) { + $this->sendData($result); + } + } + + /** + * Encodes the response in the prefered format + * and sends back + * @param $data array php data + */ + public function sendData($data) + { + $data = $this->response_format->encode($data, !$this->production_mode); + $post_process = $this->service_method .'_'.$this->response_format->getExtension(); + if(isset($this->service_class) && method_exists($this->service_class,$post_process)){ + $data = call_user_method($post_process, $this->service_class, $data); + } + header("Cache-Control: no-cache, must-revalidate"); + header("Expires: 0"); + header('Content-Type: ' . $this->response_format->getMIME()); + echo $data; + } + + /** + * Sets the HTTP response status + * @param int $code response code + */ + public function setStatus($code) + { + header("{$_SERVER['SERVER_PROTOCOL']} $code ".$this->codes[strval($code)]); + } + /////////////////////////////////////////////////////////////// + + /** + * Parses the requst url and get the api path + * @return string api path + */ + protected function getPath() + { + $path = substr(preg_replace('/(\.\w+)|(\?.*$)/', '', $_SERVER['REQUEST_URI']), 1); + if ($path[strlen($path) - 1] == '/') { + $path = substr($path, 0, -1); + } + return $path; + } + + /** + * Parses the request to figure out the http request type + * @return string which will be one of the following + * [GET, POST, PUT, DELETE] + * @example GET + */ + protected function getRequestMethod() + { + $method = $_SERVER['REQUEST_METHOD']; + if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ + $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; + }elseif ($method == 'POST' && isset($_GET['method'])){ + switch ($_GET['method']){ + case 'PUT': + case 'DELETE': + $method = $_GET['method']; + } + } + return $method; + } + + /** + * Parses the request to figure out format of the request data + * @return iFormat any class that implements iFormat + * @example JsonFormat + */ + protected function getRequestFormat(){ + $format=null; + //check if client has sent any information on request format + if(isset($_SERVER['CONTENT_TYPE'])){ + $mime = $_SERVER['CONTENT_TYPE']; + if($mime==UrlEncodedFormat::MIME){ + $format = new UrlEncodedFormat(); + }else{ + if(isset($this->format_map[$mime])){ + $format = $this->format_map[$mime]; + $format = is_string($format) ? new $format: $format; + $format->setMIME($accept); + return $format; + } + } + } + return $format; + } + + /** + * Parses the request to figure out the best format for response + * @return iFormat any class that implements iFormat + * @example JsonFormat + */ + protected function getResponseFormat() + { + //check if client has specified an extension + /** + * @var iFormat + */ + $format; + $extension = array_pop(explode('.', parse_url($_SERVER['REQUEST_URI'],PHP_URL_PATH))); + if($extension && isset($this->format_map[$extension])){ + $format = $this->format_map[$extension]; + $format = is_string($format) ? new $format: $format; + $format->setExtension($extension); + //echo "Extension $extension"; + return $format; + } + //check if client has sent list of accepted data formats + if(isset($_SERVER['HTTP_ACCEPT'])){ + $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); + foreach ($accepts as $accept) { + if($extension && isset($this->format_map[$accept])){ + $format = $this->format_map[$accept]; + $format = is_string($format) ? new $format: $format; + $format->setMIME($accept); + //echo "MIME $accept"; + return $format; + } + } + } + $format = $this->format_map['default']; + //echo "DEFAULT ".$this->format_map['default']; + return is_string($format) ? new $format: $format; + + } + + /** + * Parses the request data and returns it + * @return array php data + */ + protected function getRequestData() + { + try{ + $r = file_get_contents('php://input'); + if(is_null($r))return $_GET; + return $this->request_format->decode($r); + } catch (RestException $e) { + $this->handleError($e->getCode(), $e->getMessage()); + } + } + + protected function loadCache() + { + if ($this->cached !== null) { + return; + } + + $this->cached = false; + + if ($this->production_mode) { + if (function_exists('apc_fetch')) { + $map = apc_fetch('urlMap'); + } elseif (file_exists($this->cache_dir . '/urlMap.cache')) { + $map = unserialize(file_get_contents($this->cache_dir . '/urlMap.cache')); + } + if (isset($map) && is_array($map)) { + $this->url_map = $map; + $this->cached = true; + } + } else { + if (function_exists('apc_store')) { + apc_delete('urlMap'); + } else { + @unlink($this->cache_dir . '/urlMap.cache'); + } + } + } + + + protected function mapUrlToMethod() + { + if(!isset($this->url_map[$this->request_method])){ + return array(null,null,null,null,null); + } + $urls = $this->url_map[$this->request_method]; + if (!$urls)return array(null,null,null,null,null); + + $found=false; + + foreach ($urls as $url => $call) { + $params = array('data'=>$this->request_data); + if(is_array($this->request_data))$params+=$this->request_data; + //use query parameters + $params+=$_GET; + $args = $call[2]; + //if it has url based parameters + if (strstr($url, ':')) { + $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); + if (preg_match(":^$regex$:", $this->url, $matches)) { + foreach ($matches as $arg => $match) { + //echo "$arg => $match $args[$arg] \n"; + if (isset($args[$arg]))$params[$arg] = $match; + } + $found=true; + break; + } + }elseif ($url == $this->url){ + $found=true; + break; + } + } + if($found){ + $optional_index = $call[4]; + if($optional_index){ + $p=array_fill(0, $optional_index, null); + }elseif (count($params)){ + $p=array_fill(0, count($params), null); + }else{ + $p=array(); + } + foreach ($args as $key => $value) { + //echo "$key => $value \n"; + if(isset($params[$key]))$p[$value] = $params[$key]; + } + $call[2]=$p; + return $call; + + } + } + + protected function generateMap($class, $basePath = '') + { + if (is_object($class)) { + $reflection = new ReflectionObject($class); + } elseif (class_exists($class)) { + $reflection = new ReflectionClass($class); + } + + $methods = $reflection->getMethods( + ReflectionMethod::IS_PUBLIC + + ReflectionMethod::IS_PROTECTED + ); + + foreach ($methods as $method) { + $doc = $method->getDocComment(); + if (preg_match_all('/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { + + $params = $method->getParameters(); + + foreach ($matches as $match) { + $httpMethod = $match[1]; + $url = $basePath . $match[2]; + if (strlen($url)>0 && $url[strlen($url) - 1] == '/') { + $url = substr($url, 0, -1); + } + $call = array($class, $method->getName()); + $args = array(); + $optional_index = $method->getNumberOfRequiredParameters(); + foreach ($params as $param){ + $args[$param->getName()] = $param->getPosition(); + } + /* + foreach ($params as $param) { + $args[$param->getName()] = $param->getPosition(); + if(!isset($optional_index) && $param->isOptional()){ + $optional_index = $param->getPosition(); + } + } + if(!isset($optional_index))$optional_index=count($params); + */ + $call[] = $args; + $call[] = $method->isPublic(); + $call[] = @$optional_index; + + $this->url_map[$httpMethod][$url] = $call; + } + } + } + } + + private $codes = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => '(Unused)', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); +} +/** + * Special Exception for raising API errors + * that can be used in API methods + * @category Framework + * @package restler + * @subpackage exception + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class RestException extends Exception +{ + + public function __construct($code, $message = null) + { + parent::__construct($message, $code); + } + +} + +/** + * Conveniance function that converts the given object + * in to associative array + * @param object $object that needs to be converted + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +function object_to_array($object, $utf_encode=true) +{ + if(is_array($object) || is_object($object)) + { + $array = array(); + foreach($object as $key => $value) + { + $value = object_to_array($value, $utf_encode); + if($utf_encode && is_string($value)){ + $value = utf8_encode($value); + } + $array[$key] = $value; + } + return $array; + } + return $object; +} + +/** + * Interface for creating authentication classes + * @category Framework + * @package restler + * @subpackage auth + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +interface iAuthenticate +{ + /** + * Auth function that is called when a protected method is requested + * @return boolean true or false + */ + public function isAuthenticated(); +} + + +/** + * Interface for creating custom data formats + * like xml, json, yaml, amf etc + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +interface iFormat +{ + /** + * Get Extension => MIME type mappings as an associative array + * @return array list of mime strings for the format + * @example array('json'=>'application/json'); + */ + public function getMIMEMap(); + + /** + * Set the selected MIME type + * @param string $mime MIME type + */ + public function setMIME($mime); + /** + * Get selected MIME type + */ + public function getMIME(); + + /** + * Set the selected file extension + * @param string $extension file extension + */ + public function setExtension($extension); + /** + * Get the selected file extension + * @return string file extension + */ + public function getExtension(); + + + /** + * Encode the given data in the format + * @param array $data resulting data that needs to + * be encoded in the given format + * @param boolean $human_readable set to true when restler + * is not running in production mode. Formatter has to + * make the encoded output more human readable + * @return string encoded string + */ + public function encode($data, $human_readable=false); + + /** + * Decode the given data from the format + * @param string $data data sent from client to + * the api in the given format. + * @return array associative array of the parsed data + */ + public function decode($data); +} + +/** + * URL Encoded String Format + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class UrlEncodedFormat implements iFormat +{ + const MIME = 'application/x-www-form-urlencoded'; + const EXTENSION = 'post'; + public function getMIMEMap() + { + return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); + } + public function getMIME(){ + return UrlEncodedFormat::MIME; + } + public function getExtension(){ + return UrlEncodedFormat::EXTENSION; + } + public function setMIME($mime){ + //do nothing + } + public function setExtension($extension){ + //do nothing + } + public function encode($data, $human_readable=false){ + return http_build_query($data); + } + public function decode($data){ + parse_str($data,$r); + return $r; + } + public function __toString(){ + return $this->getExtension(); + } +} + +/** + * Javascript Object Notation Format + * @category Framework + * @package restler + * @subpackage format + * @author R.Arul Kumaran + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class JsonFormat implements iFormat +{ + const MIME ='application/json'; + const EXTENSION = 'json'; + public function getMIMEMap() + { + return array(JsonFormat::EXTENSION=>JsonFormat::MIME); + } + public function getMIME(){ + return JsonFormat::MIME; + } + public function getExtension(){ + return JsonFormat::EXTENSION; + } + public function setMIME($mime){ + //do nothing + } + public function setExtension($extension){ + //do nothing + } + public function encode($data, $human_readable=false){ + return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); + } + public function decode($data){ + return json_decode($data); + } + + /** + * Pretty print JSON string + * @param string $json + * @return string formated json + */ + private function json_format($json) + { + $tab = " "; + $new_json = ""; + $indent_level = 0; + $in_string = false; + + $len = strlen($json); + + for($c = 0; $c < $len; $c++) { + $char = $json[$c]; + switch($char) { + case '{': + case '[': + if(!$in_string) { + $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); + $indent_level++; + } else { + $new_json .= $char; + } + break; + case '}': + case ']': + if(!$in_string) { + $indent_level--; + $new_json .= "\n" . str_repeat($tab, $indent_level) . $char; + } else { + $new_json .= $char; + } + break; + case ',': + if(!$in_string) { + $new_json .= ",\n" . str_repeat($tab, $indent_level); + } else { + $new_json .= $char; + } + break; + case ':': + if(!$in_string) { + $new_json .= ": "; + } else { + $new_json .= $char; + } + break; + case '"': + if($c > 0 && $json[$c-1] != '\\') { + $in_string = !$in_string; + } + default: + $new_json .= $char; + break; + } + } + + return $new_json; + } + + public function __toString(){ + return $this->getExtension(); + } +} \ No newline at end of file diff --git a/xmlformat.php b/xmlformat.php new file mode 100644 index 000000000..a3244416a --- /dev/null +++ b/xmlformat.php @@ -0,0 +1,193 @@ + + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class XmlFormat implements iFormat +{ + public static $parse_attributes=true; + public static $parse_namespaces=false; + public static $attribute_names=array('xmlns'); + /** + * Default name for the root node. + * @var string $rootNodeName + */ + public static $root_name='result'; + public static $default_tag_name='item'; + + const MIME ='application/xml'; + const EXTENSION = 'xml'; + + public function getMIMEMap() + { + return array(XmlFormat::EXTENSION=>XmlFormat::MIME); + } + public function getMIME(){ + return XmlFormat::MIME; + } + public function getExtension(){ + return XmlFormat::EXTENSION; + } + public function setMIME($mime){ + //do nothing + } + public function setExtension($extension){ + //do nothing + } + + public function encode($data, $human_readable=false){ + return $this->toXML( object_to_array($data, false),XmlFormat::$root_name, $human_readable); + } + + public function decode($data){ + try { + if($data=='')return array(); + return $this->toArray($data); + } catch (Exception $e) { + throw new RestException(400, "Error decoding request. ".$e->getMessage()); + } + } + + public function __toString(){ + return $this->getExtension(); + } + + /** + * determine if a variable is an associative array + */ + public function isAssoc( $array ) { + return (is_array($array) && 0 !== count(array_diff_key($array, array_keys(array_keys($array))))); + } + + /** + * The main function for converting to an XML document. + * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document. + * @param array $data + * @param string $rootNodeName - what you want the root node to be - defaults to data. + * @param SimpleXMLElement $xml - should only be used recursively + * @return string XML + * @link http://snipplr.com/view/3491/convert-php-array-to-xml-or-simple-xml-object-if-you-wish/ + */ + public function toXML( $data, $root_node_name = 'result', $human_readable=false, &$xml=null) { + + // turn off compatibility mode as simple xml throws a wobbly if you don't. + if ( ini_get('zend.ze1_compatibility_mode') == 1 ) ini_set ( 'zend.ze1_compatibility_mode', 0 ); + if ( is_null( $xml ) ) $xml = @simplexml_load_string( "<$root_node_name/>" ); + + if(is_array($data)){ + $numeric=0; + // loop through the data passed in. + foreach( $data as $key => $value ) { + + // no numeric keys in our xml please! + if ( is_numeric( $key ) ) { + $numeric = 1; + $key = XmlFormat::$root_name == $root_node_name ? XmlFormat::$default_tag_name : $root_node_name; + } + + // delete any char not allowed in XML element names + $key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key); + + // if there is another array found recrusively call this function + if ( is_array( $value ) ) { + $node = $this->isAssoc( $value ) || $numeric ? $xml->addChild( $key ) : $xml; + + // recrusive call. + if ( $numeric ) $key = 'anon'; + $this->toXML($value, $key, $human_readable, $node); + } else { + + // add single node. + $value = utf8_encode(htmlentities($value)); + //$xml->addChild( $key, $value ); + in_array($key,XmlFormat::$attribute_names) ? $xml->addAttribute($key,$value) : $xml->addChild( $key, $value); + } + } + }else{ //if given data is a string or number + //simply wrap it as text node to root + if(is_bool($data)) $data = $data ? 'true' : 'false'; + $xml->{0} = $data; + } + if(!$human_readable){ + return $xml->saveXML(); + }else{ + $dom = dom_import_simplexml($xml)->ownerDocument; + $dom->formatOutput = true; + return $dom->saveXML(); + } + } + + /** + * Convert an XML document to a multi dimensional array + * Pass in an XML document (or SimpleXMLElement object) and this + * recrusively loops through and builds a representative array + * + * @param string $xml - XML document - can optionally be a SimpleXMLElement object + * @return array ARRAY + * @link http://snipplr.com/view/3491/convert-php-array-to-xml-or-simple-xml-object-if-you-wish/ + */ + public function toArray( $xml, $firstCall=true) { + if ( is_string( $xml ) ) $xml = new SimpleXMLElement( $xml ); + $children = $xml->children(); + if ( !$children ) { + $r = (string) $xml; + if($r=='true' || $r=='false')$r=$r=='true'; + return $r; + } + $arr = array(); + + if($firstCall){ + //reset the attribute names list + XmlFormat::$attribute_names=array(); + XmlFormat::$root_name = $xml->getName(); + if (XmlFormat::$parse_namespaces){ + foreach($xml->getDocNamespaces(TRUE) as $namepace => $uri) { + $arr[$namepace=='' ? 'xmlns' : 'xmlns:'.$namepace] = (string)$uri; + } + } + } + if(XmlFormat::$parse_attributes){ + foreach($xml->attributes() as $attName => $attValue) { + $arr[$attName] = (string)$attValue; + //add to attribute list for round trip support + XmlFormat::$attribute_names[]=$attName; + } + } + foreach ( $children as $key => $node ) { + $node = $this->toArray($node, false); + // support for 'anon' non-associative arrays + if ( $key == 'anon' ) $key = count( $arr ); + + // if the node is already set, put it into an array + if ( isset( $arr[$key] ) ) { + if ( !is_array( $arr[$key] ) || @$arr[$key][0] == null ) $arr[$key] = array( $arr[$key] ); + $arr[$key][] = $node; + } else { + $arr[$key] = $node; + } + } + return $arr; + } + + /** + * When you decode an XML its structure is copied to the static vars + * we can use this function to echo them out and then copy paste inside + * our service methods + * @return string PHP source code to reproduce the configuration + */ + public static function exportCurrentSettings() { + $s = 'XmlFormat::$root_name = "'.(XmlFormat::$root_name)."\";\n"; + $s .= 'XmlFormat::$attribute_names = '.(var_export(XmlFormat::$attribute_names, true)).";\n"; + $s .= 'XmlFormat::$default_tag_name = "'.XmlFormat::$default_tag_name."\";\n"; + $s .= 'XmlFormat::$parse_attributes = '.(XmlFormat::$parse_attributes ? 'true' : 'false').";\n"; + $s .= 'XmlFormat::$parse_namespaces = '.(XmlFormat::$parse_namespaces ? 'true' : 'false').";\n\n\n"; + return $s; + } + +} From d6a48e8eca7d93e4173ad6c46c421a875286f661 Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 9 Apr 2011 02:45:55 +0800 Subject: [PATCH 04/61] Barebones Hello World example that shows the simplicity and power of Restler is added --- examples/helloworld/README.txt | 31 +++++++ examples/helloworld/digestauthentication.php | 85 ++++++++++++++++++++ examples/helloworld/index.php | 11 +++ examples/helloworld/simpleservice.php | 41 ++++++++++ 4 files changed, 168 insertions(+) create mode 100644 examples/helloworld/README.txt create mode 100644 examples/helloworld/digestauthentication.php create mode 100644 examples/helloworld/index.php create mode 100644 examples/helloworld/simpleservice.php diff --git a/examples/helloworld/README.txt b/examples/helloworld/README.txt new file mode 100644 index 000000000..0394a5dde --- /dev/null +++ b/examples/helloworld/README.txt @@ -0,0 +1,31 @@ +UNZIP AND COPY THE FILES into your web root (including the .htaccess file) +Assuming that you are testing it on localhost try the following urls + + http://localhost/ + should return "HelloWorld" + + http://localhost/sum + should return 0 + + http://localhost/sum.json + should return 0 + + http://localhost/sum.xml + should return the following xml (view source if required) + + 0 + + http://localhost/sum.json?num1=10&num2=4 + should return 14 + + http://localhost/multiply.json/4/2 + should return 8 + + http://localhost/protectedsum.json?n1=5&n2=4 + should ask for username and password + give admin and mypass respectively + then it should return 9 + +Now take a look at simpleservice.php to understand how the mapping is done + +Keep this as the starting point and build your own :) \ No newline at end of file diff --git a/examples/helloworld/digestauthentication.php b/examples/helloworld/digestauthentication.php new file mode 100644 index 000000000..feb435ace --- /dev/null +++ b/examples/helloworld/digestauthentication.php @@ -0,0 +1,85 @@ + + * @copyright 2010 Luracast + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://luracast.com/products/restler/ + */ +class DigestAuthentication implements iAuthenticate +{ + public $realm = 'Restricted API'; + public static $user; + public $restler; + + public function isAuthenticated() + { + //user => password hardcoded for convenience + $users = array('admin' => 'mypass', 'guest' => 'guest'); + + if (empty($_SERVER['PHP_AUTH_DIGEST'])) { + header('HTTP/1.1 401 Unauthorized'); + header('WWW-Authenticate: Digest realm="'.$this->realm.'",qop="auth",nonce="'. + uniqid().'",opaque="'.md5($this->realm).'"'); + die('Digest Authorisation Required'); + } + + // analyze the PHP_AUTH_DIGEST variable + if (!($data = DigestAuthentication::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || + !isset($users[$data['username']])) + { + throw new RestException(401, 'Wrong Credentials!'); + } + + + // generate the valid response + $A1 = md5($data['username'] . ':' . $this->realm . ':' . $users[$data['username']]); + $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']); + $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2); + + if ($data['response'] != $valid_response) + { + throw new RestException(401, 'Wrong Credentials!'); + } + // ok, valid username & password + DigestAuthentication::$user=$data['username']; + return true; + } + + /** + * Logs user out of the digest authentication by bringing the login dialog again + * ignore the dialog to logout + * + * @url GET /user/login + * @url GET /user/logout + */ + public function logout() + { + header('HTTP/1.1 401 Unauthorized'); + header('WWW-Authenticate: Digest realm="'.$this->realm.'",qop="auth",nonce="'. + uniqid().'",opaque="'.md5($this->realm).'"'); + die('Digest Authorisation Required'); + } + + + // function to parse the http auth header + private function http_digest_parse($txt) + { + // protect against missing data + $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); + $data = array(); + $keys = implode('|', array_keys($needed_parts)); + + preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[3] ? $m[3] : $m[4]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + } +} \ No newline at end of file diff --git a/examples/helloworld/index.php b/examples/helloworld/index.php new file mode 100644 index 000000000..355711b86 --- /dev/null +++ b/examples/helloworld/index.php @@ -0,0 +1,11 @@ +setSupportedFormats('JsonFormat', 'XmlFormat'); +$restler->addAPIClass('SimpleService'); +$restler->addAuthenticationClass('DigestAuthentication'); +$restler->handle(); \ No newline at end of file diff --git a/examples/helloworld/simpleservice.php b/examples/helloworld/simpleservice.php new file mode 100644 index 000000000..dc8823b55 --- /dev/null +++ b/examples/helloworld/simpleservice.php @@ -0,0 +1,41 @@ + Date: Sat, 9 Apr 2011 02:53:18 +0800 Subject: [PATCH 05/61] htaccess file that is required for the helloword example is now added --- examples/helloworld/.htaccess | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 examples/helloworld/.htaccess diff --git a/examples/helloworld/.htaccess b/examples/helloworld/.htaccess new file mode 100644 index 000000000..b766fc6f0 --- /dev/null +++ b/examples/helloworld/.htaccess @@ -0,0 +1,8 @@ +DirectoryIndex index.php + + RewriteEngine On + RewriteRule ^$ index.php [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + \ No newline at end of file From df61f9dd20f06d30b27883dcd581f3ad69b962e6 Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 9 Apr 2011 02:55:39 +0800 Subject: [PATCH 06/61] improving the documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8ae662ed..c7424399a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Luracast Restler -Restler is a simple and effective multi-protocol REST API Server written in PHP. +Restler is a simple and effective multi-protocol REST API Server written in PHP. Just deal with your business logic in php, restler will take care of the REST! * [Developer Home](http://luracast.com/products/restler/) From 6407133588a6dda68a93e6a25a9d300400c53edf Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 9 Apr 2011 13:34:54 +0800 Subject: [PATCH 07/61] Moved the restler bundle to subfolder so that it can be downloaded without examples and formats --- restler.php => restler/restler.php | 0 xmlformat.php => restler/xmlformat.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename restler.php => restler/restler.php (100%) rename xmlformat.php => restler/xmlformat.php (100%) diff --git a/restler.php b/restler/restler.php similarity index 100% rename from restler.php rename to restler/restler.php diff --git a/xmlformat.php b/restler/xmlformat.php similarity index 100% rename from xmlformat.php rename to restler/xmlformat.php From 889859562f4f72c898dea8a78ff215aa766bc554 Mon Sep 17 00:00:00 2001 From: Luracast Date: Sun, 8 May 2011 01:50:40 +0800 Subject: [PATCH 08/61] Restler is now improved to support hosting in subfolders and mapping the paths relative to gateway (index.php) optional parameter support is greatly improved helloworld example updated --- examples/helloworld/README.txt | 12 ++++----- examples/helloworld/index.php | 2 ++ examples/helloworld/simpleservice.php | 1 - restler/restler.php | 39 +++++++++++---------------- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/examples/helloworld/README.txt b/examples/helloworld/README.txt index 0394a5dde..3ddaecd13 100644 --- a/examples/helloworld/README.txt +++ b/examples/helloworld/README.txt @@ -4,24 +4,24 @@ Assuming that you are testing it on localhost try the following urls http://localhost/ should return "HelloWorld" - http://localhost/sum + http://localhost/sum and http://localhost/index.php/sum should return 0 - http://localhost/sum.json + http://localhost/sum.json and http://localhost/index.php/sum.json should return 0 - http://localhost/sum.xml + http://localhost/sum.xml and http://localhost/index.php/sum.xml should return the following xml (view source if required) 0 - http://localhost/sum.json?num1=10&num2=4 + http://localhost/sum.json?num1=10&num2=4 and http://localhost/index.php/sum.json?num1=10&num2=4 should return 14 - http://localhost/multiply.json/4/2 + http://localhost/multiply.json/4/2 and http://localhost/index.php/multiply.json/4/2 should return 8 - http://localhost/protectedsum.json?n1=5&n2=4 + http://localhost/protectedsum.json?n1=5&n2=4 and http://localhost/index.php/protectedsum.json?n1=5&n2=4 should ask for username and password give admin and mypass respectively then it should return 9 diff --git a/examples/helloworld/index.php b/examples/helloworld/index.php index 355711b86..e9af3f165 100644 --- a/examples/helloworld/index.php +++ b/examples/helloworld/index.php @@ -3,6 +3,8 @@ //rester and related classes should be //placed in include path +set_include_path(get_include_path() . PATH_SEPARATOR . '../../restler'); + spl_autoload_register(); $restler = new Restler(); $restler->setSupportedFormats('JsonFormat', 'XmlFormat'); diff --git a/examples/helloworld/simpleservice.php b/examples/helloworld/simpleservice.php index dc8823b55..ce5e053cb 100644 --- a/examples/helloworld/simpleservice.php +++ b/examples/helloworld/simpleservice.php @@ -3,7 +3,6 @@ class SimpleService { /** * HelloWorld * @url GET / - * @url GET /index */ function helloworld() { return "HelloWorld"; diff --git a/restler/restler.php b/restler/restler.php index 47dedfbb8..30a7e05ce 100644 --- a/restler/restler.php +++ b/restler/restler.php @@ -10,7 +10,7 @@ * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ - * @version 1.0.17 beta + * @version 1.0.18 beta */ class Restler @@ -332,10 +332,12 @@ public function setStatus($code) */ protected function getPath() { - $path = substr(preg_replace('/(\.\w+)|(\?.*$)/', '', $_SERVER['REQUEST_URI']), 1); - if ($path[strlen($path) - 1] == '/') { - $path = substr($path, 0, -1); - } + $sn = trim($_SERVER['SCRIPT_NAME'], '/'); + $path = $_SERVER['REQUEST_URI']; + $path = str_replace($sn, '', $path); + $path = str_replace(dirname($sn), '', $path); + $path = trim($path,'/'); + $path = preg_replace('/(\.\w+)|(\?.*$)/', '', $path); return $path; } @@ -469,10 +471,10 @@ protected function loadCache() protected function mapUrlToMethod() { if(!isset($this->url_map[$this->request_method])){ - return array(null,null,null,null,null); + return array(null,null,null,null,null,null); } $urls = $this->url_map[$this->request_method]; - if (!$urls)return array(null,null,null,null,null); + if (!$urls)return array(null,null,null,null,null,null); $found=false; @@ -499,14 +501,7 @@ protected function mapUrlToMethod() } } if($found){ - $optional_index = $call[4]; - if($optional_index){ - $p=array_fill(0, $optional_index, null); - }elseif (count($params)){ - $p=array_fill(0, count($params), null); - }else{ - $p=array(); - } + $p = is_null($call[5]) ? array() : $call[5]; foreach ($args as $key => $value) { //echo "$key => $value \n"; if(isset($params[$key]))$p[$value] = $params[$key]; @@ -544,22 +539,18 @@ protected function generateMap($class, $basePath = '') } $call = array($class, $method->getName()); $args = array(); + $defaults = array(); $optional_index = $method->getNumberOfRequiredParameters(); foreach ($params as $param){ $args[$param->getName()] = $param->getPosition(); - } - /* - foreach ($params as $param) { - $args[$param->getName()] = $param->getPosition(); - if(!isset($optional_index) && $param->isOptional()){ - $optional_index = $param->getPosition(); - } + if($param->isDefaultValueAvailable()){ + $defaults[$param->getPosition()]=$param->getDefaultValue(); } - if(!isset($optional_index))$optional_index=count($params); - */ + } $call[] = $args; $call[] = $method->isPublic(); $call[] = @$optional_index; + $call[] = $defaults; $this->url_map[$httpMethod][$url] = $call; } From 662cf6b979fe9c4d5ca6bab0f3a726c8dca0c86c Mon Sep 17 00:00:00 2001 From: Luracast Date: Tue, 10 May 2011 17:03:53 +0800 Subject: [PATCH 09/61] Fixes the Version 1.0.18 bug which makes the API calls to fail when they contain an extention (example http://rest.ler/helloworld.json) --- restler/restler.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/restler/restler.php b/restler/restler.php index 30a7e05ce..9ee7ee25b 100644 --- a/restler/restler.php +++ b/restler/restler.php @@ -10,7 +10,7 @@ * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ - * @version 1.0.18 beta + * @version 1.0.19 beta */ class Restler @@ -332,12 +332,18 @@ public function setStatus($code) */ protected function getPath() { - $sn = trim($_SERVER['SCRIPT_NAME'], '/'); + $sn = trim($_SERVER['SCRIPT_NAME'],'/'); $path = $_SERVER['REQUEST_URI']; - $path = str_replace($sn, '', $path); - $path = str_replace(dirname($sn), '', $path); + if(strpos($path, $sn)===false){ + $sn = dirname($sn); + if(count($sn)>1) + $path = str_replace($sn, '', $path); + }else{ + $path = str_replace($sn, '', $path); + } $path = trim($path,'/'); $path = preg_replace('/(\.\w+)|(\?.*$)/', '', $path); + //echo $path; return $path; } From 1ea211966e37962c64a20c0427c1e2ebc3b488d8 Mon Sep 17 00:00:00 2001 From: Luracast Date: Wed, 11 May 2011 15:03:54 +0800 Subject: [PATCH 10/61] Bug fix for the relative path mapping fails issue that was not fixed 1.0.19 --- restler/restler.php | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/restler/restler.php b/restler/restler.php index 9ee7ee25b..93d7dc2cf 100644 --- a/restler/restler.php +++ b/restler/restler.php @@ -10,7 +10,7 @@ * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ - * @version 1.0.19 beta + * @version 1.0.20 beta */ class Restler @@ -324,24 +324,35 @@ public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ".$this->codes[strval($code)]); } + /** + * Compare two strings and remove the common + * sub string from the first string and return it + * @param string $first + * @param string $second + * @param string $char optional, set it as + * blank string for char by char comparison + * @return string + */ + public function removeCommonPath($first, $second, $char='/'){ + $first = explode($char, $first); + $second = explode($char, $second); + while (count($second)){ + if($first[0]==$second[0]){ + array_shift($first); + } else break; + array_shift($second); + } + return implode($char, $first); + } + /////////////////////////////////////////////////////////////// - /** * Parses the requst url and get the api path * @return string api path */ protected function getPath() { - $sn = trim($_SERVER['SCRIPT_NAME'],'/'); - $path = $_SERVER['REQUEST_URI']; - if(strpos($path, $sn)===false){ - $sn = dirname($sn); - if(count($sn)>1) - $path = str_replace($sn, '', $path); - }else{ - $path = str_replace($sn, '', $path); - } - $path = trim($path,'/'); + $path = $this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']); $path = preg_replace('/(\.\w+)|(\?.*$)/', '', $path); //echo $path; return $path; From ce53f941cdf6fbd261ae1fc863693b38be104c40 Mon Sep 17 00:00:00 2001 From: Luracast Date: Thu, 25 Aug 2011 00:39:30 +0800 Subject: [PATCH 11/61] Luracast Restler 2.0 is a major rewrite to use convention over configuration and it is optimized for performance. Here are some of the major changes and improvements. * PHPDoc comments to map a method to URI is now optional. * All public methods that does not begin with an underscore are mapped automatically to the method name (`gateway\classname\methodname\param1\...`) * If we do not specify the second parameter for `$restler->addAPIClass` it will be mapped to the class name instead of mapping it to the root * Restler 2 is written for PHP 5.3 and above but it make use of compat.php and work on any version of PHP starting from PHP 5.0 --- README.html | 64 ++ README.md | 41 +- .../{helloworld => _001_helloworld}/.htaccess | 0 examples/_001_helloworld/index.php | 14 + examples/_001_helloworld/readme.html | 107 ++ examples/_001_helloworld/readme.md | 63 ++ examples/_001_helloworld/say.php | 6 + examples/_002_minimal/.htaccess | 8 + examples/_002_minimal/index.php | 23 + examples/_002_minimal/math.php | 9 + examples/_002_minimal/readme.html | 103 ++ examples/_002_minimal/readme.md | 64 ++ examples/_003_multiformat/.htaccess | 8 + examples/_003_multiformat/bmi.php | 37 + examples/_003_multiformat/index.php | 72 ++ examples/_003_multiformat/readme.html | 128 +++ examples/_003_multiformat/readme.md | 86 ++ examples/_004_error_response/.htaccess | 8 + examples/_004_error_response/currency.php | 11 + examples/_004_error_response/index.php | 44 + examples/_004_error_response/readme.html | 102 ++ examples/_004_error_response/readme.md | 59 ++ examples/_005_protected_api/.htaccess | 8 + examples/_005_protected_api/index.php | 43 + examples/_005_protected_api/readme.html | 106 ++ examples/_005_protected_api/readme.md | 63 ++ examples/_005_protected_api/secured.php | 10 + examples/_005_protected_api/simple.php | 15 + examples/_005_protected_api/simpleauth.php | 10 + examples/_006_crud/.htaccess | 8 + examples/_006_crud/author.php | 33 + examples/_006_crud/author.sql | 4 + examples/_006_crud/author.sqlite | Bin 0 -> 163840 bytes examples/_006_crud/filename.sqlite | Bin 0 -> 4096 bytes examples/_006_crud/index.php | 121 +++ examples/_006_crud/readme.html | 177 ++++ examples/_006_crud/readme.md | 133 +++ examples/_006_crud/routes.php | 160 +++ examples/_006_crud/sessiondb.php | 57 + examples/_006_crud/sqlitedb.php | 68 ++ examples/helloworld/README.txt | 31 - examples/helloworld/digestauthentication.php | 85 -- examples/helloworld/index.php | 13 - examples/helloworld/simpleservice.php | 40 - examples/index.html | 87 ++ examples/resources/Luracast.gif | Bin 0 -> 8033 bytes examples/resources/Restler2.gif | Bin 0 -> 12476 bytes examples/resources/ZeroClipboard.swf | Bin 0 -> 1071 bytes examples/resources/closelabel.png | Bin 0 -> 168 bytes examples/resources/facebox.css | 80 ++ examples/resources/facebox.js | 309 ++++++ examples/resources/getsource.php | 41 + examples/resources/hacks.css | 13 + examples/resources/jquery-1.6.2.min.js | 18 + examples/resources/jquery.snippet.min.css | 40 + examples/resources/jquery.snippet.min.js | 12 + examples/resources/loading.gif | Bin 0 -> 2767 bytes examples/resources/markdown.css | 403 ++++++++ restler/amfformat/Zend/Amf/Constants.php | 87 ++ .../Zend/Amf/Parse/Amf3/Deserializer.php | 424 ++++++++ .../Zend/Amf/Parse/Amf3/Serializer.php | 507 +++++++++ .../amfformat/Zend/Amf/Parse/Deserializer.php | 65 ++ .../amfformat/Zend/Amf/Parse/InputStream.php | 39 + .../amfformat/Zend/Amf/Parse/OutputStream.php | 49 + .../amfformat/Zend/Amf/Parse/Serializer.php | 59 ++ .../amfformat/Zend/Amf/Parse/TypeLoader.php | 231 +++++ .../amfformat/Zend/Amf/Util/BinaryStream.php | 285 +++++ .../Amf/Value/Messaging/AbstractMessage.php | 92 ++ .../Value/Messaging/AcknowledgeMessage.php | 60 ++ .../Zend/Amf/Value/Messaging/AsyncMessage.php | 43 + .../Amf/Value/Messaging/CommandMessage.php | 119 +++ .../Zend/Amf/Value/Messaging/ErrorMessage.php | 67 ++ .../Amf/Value/Messaging/RemotingMessage.php | 73 ++ restler/amfformat/amfformat.php | 70 ++ restler/compat.php | 58 ++ restler/plistformat/.svn/entries | 166 +++ .../CFBinaryPropertyList.php.svn-base | 970 ++++++++++++++++++ .../text-base/CFPropertyList.php.svn-base | 587 +++++++++++ .../.svn/text-base/CFType.php.svn-base | 742 ++++++++++++++ .../text-base/CFTypeDetector.php.svn-base | 167 +++ .../.svn/text-base/IOException.php.svn-base | 99 ++ .../text-base/PListException.php.svn-base | 22 + restler/plistformat/CFBinaryPropertyList.php | 970 ++++++++++++++++++ restler/plistformat/CFPropertyList.php | 586 +++++++++++ restler/plistformat/CFType.php | 742 ++++++++++++++ restler/plistformat/CFTypeDetector.php | 167 +++ restler/plistformat/IOException.php | 99 ++ restler/plistformat/PListException.php | 22 + restler/plistformat/plistformat.php | 86 ++ restler/restler.php | 941 +++++++++++------ restler/xmlformat.php | 82 +- restler/yamlformat/LICENSE | 19 + restler/yamlformat/README.markdown | 15 + restler/yamlformat/sfyaml.php | 137 +++ restler/yamlformat/sfyamlInline.php | 442 ++++++++ restler/yamlformat/sfyamldumper.php | 60 ++ restler/yamlformat/sfyamlparser.php | 622 +++++++++++ restler/yamlformat/yamlformat.php | 47 + 98 files changed, 12833 insertions(+), 530 deletions(-) create mode 100644 README.html rename examples/{helloworld => _001_helloworld}/.htaccess (100%) mode change 100644 => 100755 create mode 100755 examples/_001_helloworld/index.php create mode 100755 examples/_001_helloworld/readme.html create mode 100755 examples/_001_helloworld/readme.md create mode 100755 examples/_001_helloworld/say.php create mode 100755 examples/_002_minimal/.htaccess create mode 100755 examples/_002_minimal/index.php create mode 100755 examples/_002_minimal/math.php create mode 100755 examples/_002_minimal/readme.html create mode 100755 examples/_002_minimal/readme.md create mode 100755 examples/_003_multiformat/.htaccess create mode 100755 examples/_003_multiformat/bmi.php create mode 100755 examples/_003_multiformat/index.php create mode 100755 examples/_003_multiformat/readme.html create mode 100755 examples/_003_multiformat/readme.md create mode 100755 examples/_004_error_response/.htaccess create mode 100755 examples/_004_error_response/currency.php create mode 100755 examples/_004_error_response/index.php create mode 100755 examples/_004_error_response/readme.html create mode 100755 examples/_004_error_response/readme.md create mode 100755 examples/_005_protected_api/.htaccess create mode 100755 examples/_005_protected_api/index.php create mode 100755 examples/_005_protected_api/readme.html create mode 100755 examples/_005_protected_api/readme.md create mode 100755 examples/_005_protected_api/secured.php create mode 100755 examples/_005_protected_api/simple.php create mode 100755 examples/_005_protected_api/simpleauth.php create mode 100755 examples/_006_crud/.htaccess create mode 100755 examples/_006_crud/author.php create mode 100755 examples/_006_crud/author.sql create mode 100755 examples/_006_crud/author.sqlite create mode 100755 examples/_006_crud/filename.sqlite create mode 100755 examples/_006_crud/index.php create mode 100755 examples/_006_crud/readme.html create mode 100755 examples/_006_crud/readme.md create mode 100755 examples/_006_crud/routes.php create mode 100755 examples/_006_crud/sessiondb.php create mode 100755 examples/_006_crud/sqlitedb.php delete mode 100644 examples/helloworld/README.txt delete mode 100644 examples/helloworld/digestauthentication.php delete mode 100644 examples/helloworld/index.php delete mode 100644 examples/helloworld/simpleservice.php create mode 100755 examples/index.html create mode 100755 examples/resources/Luracast.gif create mode 100755 examples/resources/Restler2.gif create mode 100755 examples/resources/ZeroClipboard.swf create mode 100755 examples/resources/closelabel.png create mode 100755 examples/resources/facebox.css create mode 100755 examples/resources/facebox.js create mode 100755 examples/resources/getsource.php create mode 100755 examples/resources/hacks.css create mode 100755 examples/resources/jquery-1.6.2.min.js create mode 100755 examples/resources/jquery.snippet.min.css create mode 100755 examples/resources/jquery.snippet.min.js create mode 100755 examples/resources/loading.gif create mode 100755 examples/resources/markdown.css create mode 100644 restler/amfformat/Zend/Amf/Constants.php create mode 100644 restler/amfformat/Zend/Amf/Parse/Amf3/Deserializer.php create mode 100644 restler/amfformat/Zend/Amf/Parse/Amf3/Serializer.php create mode 100644 restler/amfformat/Zend/Amf/Parse/Deserializer.php create mode 100644 restler/amfformat/Zend/Amf/Parse/InputStream.php create mode 100644 restler/amfformat/Zend/Amf/Parse/OutputStream.php create mode 100644 restler/amfformat/Zend/Amf/Parse/Serializer.php create mode 100644 restler/amfformat/Zend/Amf/Parse/TypeLoader.php create mode 100644 restler/amfformat/Zend/Amf/Util/BinaryStream.php create mode 100644 restler/amfformat/Zend/Amf/Value/Messaging/AbstractMessage.php create mode 100644 restler/amfformat/Zend/Amf/Value/Messaging/AcknowledgeMessage.php create mode 100644 restler/amfformat/Zend/Amf/Value/Messaging/AsyncMessage.php create mode 100644 restler/amfformat/Zend/Amf/Value/Messaging/CommandMessage.php create mode 100644 restler/amfformat/Zend/Amf/Value/Messaging/ErrorMessage.php create mode 100644 restler/amfformat/Zend/Amf/Value/Messaging/RemotingMessage.php create mode 100644 restler/amfformat/amfformat.php create mode 100644 restler/compat.php create mode 100644 restler/plistformat/.svn/entries create mode 100644 restler/plistformat/.svn/text-base/CFBinaryPropertyList.php.svn-base create mode 100644 restler/plistformat/.svn/text-base/CFPropertyList.php.svn-base create mode 100644 restler/plistformat/.svn/text-base/CFType.php.svn-base create mode 100644 restler/plistformat/.svn/text-base/CFTypeDetector.php.svn-base create mode 100644 restler/plistformat/.svn/text-base/IOException.php.svn-base create mode 100644 restler/plistformat/.svn/text-base/PListException.php.svn-base create mode 100644 restler/plistformat/CFBinaryPropertyList.php create mode 100644 restler/plistformat/CFPropertyList.php create mode 100644 restler/plistformat/CFType.php create mode 100644 restler/plistformat/CFTypeDetector.php create mode 100644 restler/plistformat/IOException.php create mode 100644 restler/plistformat/PListException.php create mode 100644 restler/plistformat/plistformat.php create mode 100755 restler/yamlformat/LICENSE create mode 100755 restler/yamlformat/README.markdown create mode 100755 restler/yamlformat/sfyaml.php create mode 100755 restler/yamlformat/sfyamlInline.php create mode 100755 restler/yamlformat/sfyamldumper.php create mode 100755 restler/yamlformat/sfyamlparser.php create mode 100644 restler/yamlformat/yamlformat.php diff --git a/README.html b/README.html new file mode 100644 index 000000000..9cb4eb882 --- /dev/null +++ b/README.html @@ -0,0 +1,64 @@ + + + + +Luracast Restler 2.0 Example:- CRUD + + + + +
+

Luracast Restler 2.0

+ +

Restler is a simple and effective multi-protocol REST API Server written in PHP. +Just deal with your business logic in php, restler will take care of the REST!

+ + + +

Features

+ +
    +
  • Light weight
  • +
  • Flexible
  • +
  • Customizable
  • +
  • Supports HTTP request methods GET, POST, PUT, and DELETE
  • +
  • Clients can use X-HTTP-Method-Override header
  • +
  • Two way format conversion
  • +
  • Pluggable Formatters
  • +
  • Comes with JSON, XML, Yaml, Amf, and Plist(both XML and Binary) formats
  • +
  • Pluggable Authentication schemes
  • +
  • Comes with many Examples +that can be tried on your localhost to get started
  • +
  • URL to Method mapping
  • +
  • URL part to Method parameter mapping
  • +
  • Supports URLEncoded format for simplified input
  • +
  • Query parameters to Method parameter mapping
  • +
  • Source code distributed under LGPL
  • +
+ +

Changes from Restler 1.0

+ +

Restler 2.0 is a major rewrite to use convention over configuration and it is optimized +for performance. Here are some of the major changes and improvements

+ +
    +
  • PHPDoc comments to map a method to URI is now optional.
  • +
  • All public methods that does not begin with an underscore are mapped +automatically to the method name (gateway\classname\methodname\param1\...)
  • +
  • If we do not specify the second parameter for $restler->addAPIClass it will be mapped to the +class name instead of mapping it to the root
  • +
  • Restler 2 is written for PHP 5.3 and above but it make use of compat.php and work on +any version of PHP starting from PHP 5.0
  • +
+ +

more information is available on the +features page +

+ +

diff --git a/README.md b/README.md index c7424399a..3963f7c10 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ -# Luracast Restler +Luracast Restler 2.0 +==================== -Restler is a simple and effective multi-protocol REST API Server written in PHP. Just deal with your business logic in php, restler will take care of the REST! +Restler is a simple and effective multi-protocol REST API Server written in PHP. +Just deal with your business logic in php, restler will take care of the REST! * [Developer Home](http://luracast.com/products/restler/) +* [Live Examples](http://bit.ly/RestlerLiveExamples) +* [Taking Care of the REST - Presentation](http://bit.ly/TakingCareOfTheREST) +* [Open Sourced Real World Example - SingMood](http://bit.ly/SingMood) +* [Updates on Twitter](http://twitter.com/Luracast) -## Features +Features +-------- * Light weight * Flexible @@ -13,13 +20,29 @@ Restler is a simple and effective multi-protocol REST API Server written in PHP. * Clients can use X-HTTP-Method-Override header * Two way format conversion * Pluggable Formatters -* Comes with JSON, and XML formats +* Comes with JSON, XML, Yaml, Amf, and Plist(both XML and Binary) formats * Pluggable Authentication schemes -* Comes with Digest Authentication Example -* URL to Function mapping -* URL part to Function parameter mapping +* Comes with many [Examples](http://bit.ly/RestlerLiveExamples) + that can be tried on your localhost to get started +* URL to Method mapping +* URL part to Method parameter mapping * Supports URLEncoded format for simplified input -* Query parameters to Function parameter mapping +* Query parameters to Method parameter mapping * Source code distributed under LGPL -more information is available on the [features page](http://luracast.com/products/restler/features/) \ No newline at end of file +Changes from Restler 1.0 +------------------------ + +Restler 2.0 is a major rewrite to use convention over configuration and it is optimized +for performance. Here are some of the major changes and improvements + +* PHPDoc comments to map a method to URI is now optional. +* All public methods that does not begin with an underscore are mapped + automatically to the method name (`gateway\classname\methodname\param1\...`) +* If we do not specify the second parameter for `$restler->addAPIClass` it will be mapped to the + class name instead of mapping it to the root +* Restler 2 is written for PHP 5.3 and above but it make use of compat.php and work on + any version of PHP starting from PHP 5.0 + +more information is available on the +[features page](http://luracast.com/products/restler/features/) \ No newline at end of file diff --git a/examples/helloworld/.htaccess b/examples/_001_helloworld/.htaccess old mode 100644 new mode 100755 similarity index 100% rename from examples/helloworld/.htaccess rename to examples/_001_helloworld/.htaccess diff --git a/examples/_001_helloworld/index.php b/examples/_001_helloworld/index.php new file mode 100755 index 000000000..34d44b636 --- /dev/null +++ b/examples/_001_helloworld/index.php @@ -0,0 +1,14 @@ +addAPIClass('Say'); +$r->handle(); \ No newline at end of file diff --git a/examples/_001_helloworld/readme.html b/examples/_001_helloworld/readme.html new file mode 100755 index 000000000..8ed7126da --- /dev/null +++ b/examples/_001_helloworld/readme.html @@ -0,0 +1,107 @@ + + + +Luracast Restler 2.0 Example:- Hello World Example + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/_001_helloworld/readme.md b/examples/_001_helloworld/readme.md new file mode 100755 index 000000000..7ec0dfba9 --- /dev/null +++ b/examples/_001_helloworld/readme.md @@ -0,0 +1,63 @@ +Hello World Example +------------------- + +Basic hello world example to get started with Restler 2.0 +> This API Server is made using the following php files/folders + +> * index.php (gateway) +> * say.php (api) +> * restler.php (framework) + +This API Server exposes the following URIs + + GET say/hello ⇠ Say::hello() + GET say/hello/:to ⇠ Say::hello() + + +Try the following links in your browser + +GET [say/hello](say/hello) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Hello world!" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [say/hello/restler2.0](say/hello/restler2.0) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Hello Restler2.0!" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [say/hello?to=R.Arul%20Kumaran](say/hello?to=R.Arul%20Kumaran) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Hello R.Arul Kumaran!" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +If the above links fail, it could be due to missing `.htaccess` file or URL Rewriting is not supported in your server. +Try the following links instead + +GET [index.php/say/hello](index.php/say/hello) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Hello world!" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [index.php/say/hello/restler2.0](index.php/say/hello/restler2.0) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Hello Restler2.0!" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [index.php/say/hello?to=R.Arul%20Kumaran](index.php/say/hello?to=R.Arul%20Kumaran) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"Hello R.Arul Kumaran!" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +*[index.php]: _001_helloworld/index.php +*[say.php]: _001_helloworld/say.php +*[restler.php]: ../restler/restler.php diff --git a/examples/_001_helloworld/say.php b/examples/_001_helloworld/say.php new file mode 100755 index 000000000..dda8e1eac --- /dev/null +++ b/examples/_001_helloworld/say.php @@ -0,0 +1,6 @@ + + RewriteEngine On + RewriteRule ^$ index.php [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + \ No newline at end of file diff --git a/examples/_002_minimal/index.php b/examples/_002_minimal/index.php new file mode 100755 index 000000000..b041048cb --- /dev/null +++ b/examples/_002_minimal/index.php @@ -0,0 +1,23 @@ +addAPIClass('Math'); +$r->handle(); \ No newline at end of file diff --git a/examples/_002_minimal/math.php b/examples/_002_minimal/math.php new file mode 100755 index 000000000..9a17b3b8b --- /dev/null +++ b/examples/_002_minimal/math.php @@ -0,0 +1,9 @@ +($n1*$n2)); + } +} \ No newline at end of file diff --git a/examples/_002_minimal/readme.html b/examples/_002_minimal/readme.html new file mode 100755 index 000000000..6df5e42a5 --- /dev/null +++ b/examples/_002_minimal/readme.html @@ -0,0 +1,103 @@ + + + +Luracast Restler 2.0 Example:- Minimal Example + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/_002_minimal/readme.md b/examples/_002_minimal/readme.md new file mode 100755 index 000000000..1d45643ad --- /dev/null +++ b/examples/_002_minimal/readme.md @@ -0,0 +1,64 @@ +Minimal Example +--------------- + +Shows the bare minimum code needed to get your RESTful api server up and running +> This API Server is made using the following php files/folders + +> * index.php (gateway) +> * math.php (api) +> * restler.php (framework) + +This API Server exposes the following URIs + + GET math/add ⇠ Math::add() + GET math/add/:n1 ⇠ Math::add() + GET math/add/:n1/:n2 ⇠ Math::add() + GET math/multiply ⇠ Math::multiply() + GET math/multiply/:n1 ⇠ Math::multiply() + GET math/multiply/:n1/:n2 ⇠ Math::multiply() + + +Try the following links in your browser + +GET [math/add](index.php/math/add) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [math/add/4/3](index.php/math/add/4/3) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +7 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [math/add?n1=6&n2=4](index.php/math/add?n1=6&n2=4) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +10 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [math/multiply](index.php/math/multiply) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{"result":10} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [math/multiply/4/3](index.php/math/multiply/4/3) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{"result":12} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [math/multiply?n2=4](index.php/math/multiply?n2=4) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{"result":20} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +*[index.php]: _002_minimal/index.php +*[math.php]: _002_minimal/math.php +*[restler.php]: ../restler/restler.php diff --git a/examples/_003_multiformat/.htaccess b/examples/_003_multiformat/.htaccess new file mode 100755 index 000000000..b766fc6f0 --- /dev/null +++ b/examples/_003_multiformat/.htaccess @@ -0,0 +1,8 @@ +DirectoryIndex index.php + + RewriteEngine On + RewriteRule ^$ index.php [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + \ No newline at end of file diff --git a/examples/_003_multiformat/bmi.php b/examples/_003_multiformat/bmi.php new file mode 100755 index 000000000..be8d96fe4 --- /dev/null +++ b/examples/_003_multiformat/bmi.php @@ -0,0 +1,37 @@ +bmi = round($kg/($meter*$meter),2); + $lb = round($kg/0.45359237,2); + + if($result->bmi<18.5){ + $result->message = 'Underweight'; + }elseif ($result->bmi<=24.9){ + $result->message = 'Normal weight'; + }elseif ($result->bmi<=29.9){ + $result->message = 'Overweight'; + }else{ + $result->message = 'Obesity'; + } + $result->metric = array('height'=>"$cm centimeter", 'weight'=>"$weight kilograms"); + $result->imperial = array('height'=>"$feet feet $inches inches", 'weight'=>"$lb pounds"); + return $result; + } +} \ No newline at end of file diff --git a/examples/_003_multiformat/index.php b/examples/_003_multiformat/index.php new file mode 100755 index 000000000..9d9ab6611 --- /dev/null +++ b/examples/_003_multiformat/index.php @@ -0,0 +1,72 @@ + + + 31.77 + Obesity + + 162.6 centimeter + 84 kilograms + + + 5 feet 4 inches + 185.19 pounds + +. + + Example 3: GET bmi.json returns + + { + "bmi": 31.77, + "message": "Obesity", + "metric": { + "height": "162.6 centimeter", + "weight": "84 kilograms" + }, + "imperial": { + "height": "5 feet 4 inches", + "weight": "185.19 pounds" + } +} +. + */ + +require_once '../../restler/restler.php'; + +#set autoloader +#do not use spl_autoload_register with out parameter +#it will disable the autoloading of formats +spl_autoload_register('spl_autoload'); + +$r = new Restler(); +$r->setSupportedFormats('JsonFormat', 'XmlFormat'); +$r->addAPIClass('BMI'); +$r->handle(); \ No newline at end of file diff --git a/examples/_003_multiformat/readme.html b/examples/_003_multiformat/readme.html new file mode 100755 index 000000000..5755f6cae --- /dev/null +++ b/examples/_003_multiformat/readme.html @@ -0,0 +1,128 @@ + + + +Luracast Restler 2.0 Example:- Multi-format + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/_003_multiformat/readme.md b/examples/_003_multiformat/readme.md new file mode 100755 index 000000000..b236d0703 --- /dev/null +++ b/examples/_003_multiformat/readme.md @@ -0,0 +1,86 @@ +Multi-format +------------ + +This BMI calculator service shows how you can serve data in different +formats using Rester. This example uses JsonFormat (default) and XmlFormat. + +First format specified in `Restler::setSuportedFormats` is used as the default +format when client does not specify the format. + +Client can specify the format either using extension like .json or specify +the MIME type in HTTP Accept Header +> This API Server is made using the following php files/folders + +> * index.php (gateway) +> * bmi.php (api) +> * restler.php (framework) +> * xmlformat.php (format) + +This API Server exposes the following URIs + + GET bmi ⇠ BMI::index() + GET bmi/:height ⇠ BMI::index() + GET bmi/:height/:weight ⇠ BMI::index() + + +Try the following links in your browser + +GET [bmi](index.php/bmi) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + "bmi": 31.77, + "message": "Obesity", + "metric": { + "height": "162.6 centimeter", + "weight": "84 kilograms" + }, + "imperial": { + "height": "5 feet 4 inches", + "weight": "185.19 pounds" + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [bmi.xml](index.php/bmi.xml) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + 31.77 + Obesity + + 162.6 centimeter + 84 kilograms + + + 5 feet 4 inches + 185.19 pounds + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [bmi.json](index.php/bmi.json) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + "bmi": 31.77, + "message": "Obesity", + "metric": { + "height": "162.6 centimeter", + "weight": "84 kilograms" + }, + "imperial": { + "height": "5 feet 4 inches", + "weight": "185.19 pounds" + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +*[index.php]: _003_multiformat/index.php +*[bmi.php]: _003_multiformat/bmi.php +*[restler.php]: ../restler/restler.php +*[xmlformat.php]: ../restler/xmlformat.php diff --git a/examples/_004_error_response/.htaccess b/examples/_004_error_response/.htaccess new file mode 100755 index 000000000..b766fc6f0 --- /dev/null +++ b/examples/_004_error_response/.htaccess @@ -0,0 +1,8 @@ +DirectoryIndex index.php + + RewriteEngine On + RewriteRule ^$ index.php [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + \ No newline at end of file diff --git a/examples/_004_error_response/currency.php b/examples/_004_error_response/currency.php new file mode 100755 index 000000000..9f5de0e4d --- /dev/null +++ b/examples/_004_error_response/currency.php @@ -0,0 +1,11 @@ +addAPIClass('Currency'); +$r->handle(); \ No newline at end of file diff --git a/examples/_004_error_response/readme.html b/examples/_004_error_response/readme.html new file mode 100755 index 000000000..348f80669 --- /dev/null +++ b/examples/_004_error_response/readme.html @@ -0,0 +1,102 @@ + + + +Luracast Restler 2.0 Example:- Error Response + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/_004_error_response/readme.md b/examples/_004_error_response/readme.md new file mode 100755 index 000000000..fba9f3c5d --- /dev/null +++ b/examples/_004_error_response/readme.md @@ -0,0 +1,59 @@ +Error Response +-------------- + +API methods can make use of RestException class to provide +error information to the user. + +use `throw new RestException($http_status_code)` to send the error response +to the client. + +For the list of HTTP Status codes and their meaning take a look at +[Wikipedia](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes) +> This API Server is made using the following php files/folders + +> * index.php (gateway) +> * currency.php (api) +> * restler.php (framework) + +This API Server exposes the following URIs + + GET currency/format ⇠ Currency::format() + GET currency/format/:number ⇠ Currency::format() + + +Try the following links in your browser + +GET [currency/format](index.php/currency/format) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + "error": { + "code": 400, + "message": "Bad Request" + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [currency/format/not_a_number](index.php/currency/format/not_a_number) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + "error": { + "code": 412, + "message": "Precondition Failed: not a valid number" + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [currency/format?number=55](index.php/currency/format?number=55) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"USD55.00" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +*[index.php]: _004_error_response/index.php +*[currency.php]: _004_error_response/currency.php +*[restler.php]: ../restler/restler.php diff --git a/examples/_005_protected_api/.htaccess b/examples/_005_protected_api/.htaccess new file mode 100755 index 000000000..b766fc6f0 --- /dev/null +++ b/examples/_005_protected_api/.htaccess @@ -0,0 +1,8 @@ +DirectoryIndex index.php + + RewriteEngine On + RewriteRule ^$ index.php [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + \ No newline at end of file diff --git a/examples/_005_protected_api/index.php b/examples/_005_protected_api/index.php new file mode 100755 index 000000000..352c86336 --- /dev/null +++ b/examples/_005_protected_api/index.php @@ -0,0 +1,43 @@ +addAPIClass('Simple',''); +$r->addAPIClass('Secured'); +$r->addAuthenticationClass('SimpleAuth'); +$r->handle(); \ No newline at end of file diff --git a/examples/_005_protected_api/readme.html b/examples/_005_protected_api/readme.html new file mode 100755 index 000000000..c86aefcf0 --- /dev/null +++ b/examples/_005_protected_api/readme.html @@ -0,0 +1,106 @@ + + + +Luracast Restler 2.0 Example:- Protected API + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/_005_protected_api/readme.md b/examples/_005_protected_api/readme.md new file mode 100755 index 000000000..a118d9463 --- /dev/null +++ b/examples/_005_protected_api/readme.md @@ -0,0 +1,63 @@ +Protected API +------------- + +Not all the API exposed needs to be public, we need to protect some of our API. +Here are three ways to protect a method + +1. Change it to a `protected function` +2. Add a PHPDoc comment `@protected` to the method +3. Add `@protected` comment to the class to protect all methods of that class + +In order to provide access to those protected methods we use a class that implements `iAuthenticate`. Also note that +An Authentication class is also an API class so all public methods that does not begin with `_` will be exposed as API +for example [SimpleAuth::key](simpleauth/key). It can be used to create login/logout methods +> This API Server is made using the following php files/folders + +> * index.php (gateway) +> * simple.php (api) +> * secured.php (api) +> * simpleauth.php (auth) +> * restler.php (framework) + +This API Server exposes the following URIs + + GET normal ⇠ Simple::normal() + GET restricted ⇠ Simple::restricted() + GET restricted2 ⇠ Simple::restricted2() + GET secured ⇠ Secured::index() + GET simpleauth/key ⇠ SimpleAuth::key() + + +Try the following links in your browser + +GET [restricted](index.php/restricted) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + "error": { + "code": 401, + "message": "Unauthorized" + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [restricted?key=rEsTlEr2](index.php/restricted?key=rEsTlEr2) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"protected method" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [secured?key=rEsTlEr2](index.php/secured?key=rEsTlEr2) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"protected class" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +*[index.php]: _005_protected_api/index.php +*[simple.php]: _005_protected_api/simple.php +*[secured.php]: _005_protected_api/secured.php +*[simpleauth.php]: _005_protected_api/simpleauth.php +*[restler.php]: ../restler/restler.php diff --git a/examples/_005_protected_api/secured.php b/examples/_005_protected_api/secured.php new file mode 100755 index 000000000..dd4326d19 --- /dev/null +++ b/examples/_005_protected_api/secured.php @@ -0,0 +1,10 @@ + + RewriteEngine On + RewriteRule ^$ index.php [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [QSA,L] + \ No newline at end of file diff --git a/examples/_006_crud/author.php b/examples/_006_crud/author.php new file mode 100755 index 000000000..49a85a63d --- /dev/null +++ b/examples/_006_crud/author.php @@ -0,0 +1,33 @@ +dp = new SessionDB(); + } + + function get($id=NULL) { + return is_null($id) ? $this->dp->getAll() : $this->dp->get($id); + } + function post($request_data=NULL) { + return $this->dp->insert($this->_validate($request_data)); + } + function put($id=NULL, $request_data=NULL) { + return $this->dp->update($id, $this->_validate($request_data)); + } + function delete($id=NULL) { + return $this->dp->delete($id); + } + + private function _validate($data){ + $author=array(); + foreach (Author::$FIELDS as $field) { +//you may also vaildate the data here + if(!isset($data[$field]))throw new RestException(417,"$field field missing"); + $author[$field]=$data[$field]; + } + return $author; + } +} \ No newline at end of file diff --git a/examples/_006_crud/author.sql b/examples/_006_crud/author.sql new file mode 100755 index 000000000..9556f9c96 --- /dev/null +++ b/examples/_006_crud/author.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS "author"; +CREATE TABLE "author" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "name" VARCHAR NOT NULL , "email" VARCHAR NOT NULL UNIQUE ); +INSERT INTO "author" VALUES(1,'Jac Wright','jacwright@gmail.com'); +INSERT INTO "author" VALUES(2,'Arul Kumaran','arul@luracast.com'); diff --git a/examples/_006_crud/author.sqlite b/examples/_006_crud/author.sqlite new file mode 100755 index 0000000000000000000000000000000000000000..d959af334d065a839a84e71dee026b09e8281086 GIT binary patch literal 163840 zcmeI*&u$w<7y$5H+YMEvoDhm4gfy`flqjZE$OAN{vf7Z;ZH)tpI7Cy&O}1Lw%6hHl zR0DTjh?hX(gpjy!xAnoYvuF2l^vb6&hJ@&T3=Y&*hmj<6dRt-hU3BCwXcSa zx0bwGTpP#Hur(c?u74ZS_m-BI)9*gKs2OMF$)vdKYd0!1FDIK}t(hG)!%nx~dD!WN zr@fuW?cPDy?Hq*A-tRxz=~lNr?sWShbf5G?cYkj$v_kXzfz9w`yZ7KxyLV${IEq>R zmZh-Y-Fdd(3F{lRg{Aw;X_AePhG$nc)yqsS#)p@$b+~zXu2&zUu~>Dr`47qb*Qyf) z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkL{KPRwKYb2}p+QlpnyR%U& z;y70Mc0McOAWqA>gUM(mZ6qsOpT|M?s>qH{%9nBQ>LS@b9>pv_zvND}@7nyQWd3{A z2?7KN5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn?uzEY-KtmvQi_$c|6S z?c-6*^1Fk{DE***FO9`4-_B=69K>mPK3iL?_MOiENUA0X5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7e?|4QIaT3=4%tUQ?%wZ&@Z>HO!UYJva(0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PHuy0w1T1<@@i)S$Q%krl)yU4iBrrBpV+M O&(5=I`f&5pu=y91*~Ur$ literal 0 HcmV?d00001 diff --git a/examples/_006_crud/filename.sqlite b/examples/_006_crud/filename.sqlite new file mode 100755 index 0000000000000000000000000000000000000000..be89fb66dae6a1174e9a2ed5c64ac977243e886d GIT binary patch literal 4096 zcmdPWQV7Y&ELKR%%t=*9&d)1J%*-oRNX%0R4)n<^NmVe?GgL@PEJ;jCEKXI>(qhmk zeSC`n3c7(b3yd>LkA}c#2n@*(h+yDg5MVe1%*zsB_ct&wFoGD6ylub$M5aj9W7!;}C?HUPEqN&L+ znahM3Z0#s98UmvsKtc#`Ferdkb3yea8MqrpfO9Pa!-LU0MM6l9Djf}hp%((M{12(` b*}*xVnU&p;!D2N3553qNb@6Bj5FY{nVe&{n literal 0 HcmV?d00001 diff --git a/examples/_006_crud/index.php b/examples/_006_crud/index.php new file mode 100755 index 000000000..aa2eb85d0 --- /dev/null +++ b/examples/_006_crud/index.php @@ -0,0 +1,121 @@ +addAPIClass('Author'); +$r->handle(); \ No newline at end of file diff --git a/examples/_006_crud/readme.html b/examples/_006_crud/readme.html new file mode 100755 index 000000000..35608357c --- /dev/null +++ b/examples/_006_crud/readme.html @@ -0,0 +1,177 @@ + + + +Luracast Restler 2.0 Example:- CRUD + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/_006_crud/readme.md b/examples/_006_crud/readme.md new file mode 100755 index 000000000..4e0059815 --- /dev/null +++ b/examples/_006_crud/readme.md @@ -0,0 +1,133 @@ +CRUD +---- + +Create, Retrive, Update and Delete using +HTTP methods POST, GET, PUT and DELETE respectively. +For simplicity and making it work out of the box it is using +a session based fake database, thus depending on a client that +supports PHP Session Cookies. You may use [REST Console](https://chrome.google.com/webstore/detail/faceofpmfclkengnkgkgjkcibdbhemoc#) +an extension for Chrome or [RESTClient](https://addons.mozilla.org/en-US/firefox/addon/restclient/) +a firefox extension +> This API Server is made using the following php files/folders + +> * index.php (gateway) +> * author.php (api) +> * sessiondb.php (helper) +> * restler.php (framework) + +This API Server exposes the following URIs + + GET author ⇠ Author::get() + GET author/:id ⇠ Author::get() + POST author ⇠ Author::post() + PUT author ⇠ Author::put() + PUT author/:id ⇠ Author::put() + DELETE author ⇠ Author::delete() + DELETE author/:id ⇠ Author::delete() + + +Try the following links in your browser + +GET [author](index.php/author) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[ + { + "id": 1, + "name": "Jac Wright", + "email": "jacwright@gmail.com" + }, + { + "id": 2, + "name": "Arul Kumaran", + "email": "arul@luracast.com" + } +] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GET [author/2](index.php/author/2) +: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{ + "id": 2, + "name": "Arul Kumaran", + "email": "arul@luracast.com" +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +###Creating new Author + +Typical post request to create a new author will be any of the following + + +**Using query parameters** + + POST /examples/_006_crud/index.php/author?name=Another&email=another@email.com HTTP/1.1 + Host: restler2.dev + Content-Length: 0 + Accept-Language: en + X-Requested-With: XMLHttpRequest + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1 + Content-Type: application/x-www-form-urlencoded; charset=UTF-8 + Accept: */* + Accept-Encoding: gzip,deflate,sdch + Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 + Cookie: PHPSESSID=dcdfec433e86c1a6730f75303187071f + +**Using post vars** + + POST /examples/_006_crud/index.php/author HTTP/1.1 + Host: restler2.dev + Content-Length: 36 + Accept-Language: en + X-Requested-With: XMLHttpRequest + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1 + Content-Type: application/x-www-form-urlencoded; charset=UTF-8 + Accept: */* + Accept-Encoding: gzip,deflate,sdch + Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 + Cookie: PHPSESSID=dcdfec433e86c1a6730f75303187071f + + name=Another&email=another@email.com + +**Using JSON data** + + POST /examples/_006_crud/index.php/author HTTP/1.1 + Host: restler2.dev + Content-Length: 46 + Origin: chrome-extension://faceofpmfclkengnkgkgjkcibdbhemoc + Accept-Language: en + X-Requested-With: XMLHttpRequest + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1 + Content-Type: application/json; charset=UTF-8 + Accept: */* + Accept-Encoding: gzip,deflate,sdch + Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 + Cookie: PHPSESSID=dcdfec433e86c1a6730f75303187071f + + {"name":"Another","email":"another@email.com"} + + +and the response could be + + HTTP/1.1 200 OK + Date: Fri, 19 Aug 2011 16:34:41 GMT + Server: Apache/2.2.19 (Unix) mod_ssl/2.2.19 OpenSSL/0.9.8r DAV/2 PHP/5.3.6 with Suhosin-Patch + X-Powered-By: Luracast Restler v2.0.0 + Expires: 0 + Cache-Control: no-cache, must-revalidate + Pragma: no-cache + Content-Length: 66 + Content-Type: application/json + + { + "name": "Another", + "email": "another@email.com", + "id": 7 + } + +*[index.php]: _006_crud/index.php +*[author.php]: _006_crud/author.php +*[sessiondb.php]: _006_crud/sessiondb.php +*[restler.php]: ../restler/restler.php diff --git a/examples/_006_crud/routes.php b/examples/_006_crud/routes.php new file mode 100755 index 000000000..bcd27129c --- /dev/null +++ b/examples/_006_crud/routes.php @@ -0,0 +1,160 @@ + 'Author', + 'method_name' => 'get', + 'arguments' => + array ( + 'id' => 0, + ), + 'defaults' => + array ( + 0 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); + +#==== GET author/:id + +$o['GET']['author/:id']=array ( + 'class_name' => 'Author', + 'method_name' => 'get', + 'arguments' => + array ( + 'id' => 0, + ), + 'defaults' => + array ( + 0 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); + + +############### POST ############### + +$o['POST']=array(); + +#==== POST author + +$o['POST']['author']=array ( + 'class_name' => 'Author', + 'method_name' => 'post', + 'arguments' => + array ( + 'request_data' => 0, + ), + 'defaults' => + array ( + 0 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); + + +############### PUT ############### + +$o['PUT']=array(); + +#==== PUT author + +$o['PUT']['author']=array ( + 'class_name' => 'Author', + 'method_name' => 'put', + 'arguments' => + array ( + 'id' => 0, + 'request_data' => 1, + ), + 'defaults' => + array ( + 0 => NULL, + 1 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); + +#==== PUT author/:id + +$o['PUT']['author/:id']=array ( + 'class_name' => 'Author', + 'method_name' => 'put', + 'arguments' => + array ( + 'id' => 0, + 'request_data' => 1, + ), + 'defaults' => + array ( + 0 => NULL, + 1 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); + + +############### DELETE ############### + +$o['DELETE']=array(); + +#==== DELETE author + +$o['DELETE']['author']=array ( + 'class_name' => 'Author', + 'method_name' => 'delete', + 'arguments' => + array ( + 'id' => 0, + ), + 'defaults' => + array ( + 0 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); + +#==== DELETE author/:id + +$o['DELETE']['author/:id']=array ( + 'class_name' => 'Author', + 'method_name' => 'delete', + 'arguments' => + array ( + 'id' => 0, + ), + 'defaults' => + array ( + 0 => NULL, + ), + 'metadata' => + array ( + ), + 'method_flag' => 0, + ); +return $o; \ No newline at end of file diff --git a/examples/_006_crud/sessiondb.php b/examples/_006_crud/sessiondb.php new file mode 100755 index 000000000..1cef0dcec --- /dev/null +++ b/examples/_006_crud/sessiondb.php @@ -0,0 +1,57 @@ +$rec) { + if ($rec['id'] == $id) { + return $index; + } + } + return FALSE; + } + function get($id) { + $index = $this->find($id); + if($index===FALSE)return FALSE; + return $_SESSION['rs'][$index]; + } + function getAll() { + return $_SESSION['rs']; + } + public function insert($rec) { + $rec['id']=$this->pk(); + array_push($_SESSION['rs'], $rec); + return $rec; + } + public function update($id, $rec) { + $index = $this->find($id); + if($index===FALSE)return FALSE; + $rec['id']=$id; + $_SESSION['rs'][$index]=$rec; + return $rec; + } + public function delete($id) { + $index = $this->find($id); + if($index===FALSE)return FALSE; + return array_shift(array_splice($_SESSION['rs'], $index, 1)); + } +} + +// Sample data. +function getData() { + return array( + array('id'=>1, 'name'=>'Jac Wright', 'email'=>'jacwright@gmail.com'), + array('id'=>2, 'name'=>'Arul Kumaran', 'email'=>'arul@luracast.com' ), + ); +} \ No newline at end of file diff --git a/examples/_006_crud/sqlitedb.php b/examples/_006_crud/sqlitedb.php new file mode 100755 index 000000000..85f1caf7e --- /dev/null +++ b/examples/_006_crud/sqlitedb.php @@ -0,0 +1,68 @@ +query('SELECT requests FROM tablename WHERE id = 1'); + if ($q === false) { + $db->queryExec('CREATE TABLE tablename (id int, requests int, PRIMARY KEY (id)); INSERT INTO tablename VALUES (1,1)'); + $hits = 1; + } else { + $result = $q->fetchSingle(); + $hits = $result+1; + } + $db->queryExec("UPDATE tablename SET requests = '$hits' WHERE id = 1"); + } else { + die($err); + } + $this->db = new SQLite3('author.sqlite'); + } + private function find($id){ + foreach ($_SESSION['rs'] as $index => $rec) { + if ($rec['id'] == $id) { + return $index; + } + } + return FALSE; + } + function get($id) { + $results = $this->db->query("SELECT * FROM author"); + $r=array(); + while ($row = $results->fetchArray()) { + print_r($row); + $r[]=$row; + } + return $row; + } + function getAll() { + return $_SESSION['rs']; + } + public function insert($rec) { + $rec['id']=$this->pk(); + array_push($_SESSION['rs'], $rec); + return $rec; + } + public function update($id, $rec) { + $index = $this->find($id); + if($index===FALSE)return FALSE; + $rec['id']=$id; + $_SESSION['rs'][$index]=$rec; + return $rec; + } + public function delete($id) { + $index = $this->find($id); + if($index===FALSE)return FALSE; + return array_shift(array_splice($_SESSION['rs'], $index, 1)); + } +} + +// Sample data. +function getData() { + return array( + array('id' => 1, 'name' => 'Jac Wright', 'email' => 'jacwright@gmail.com'), + array('id' => 2, 'name' => 'Arul Kumaran', 'email' => 'arul@luracast.com' ), + ); +} \ No newline at end of file diff --git a/examples/helloworld/README.txt b/examples/helloworld/README.txt deleted file mode 100644 index 3ddaecd13..000000000 --- a/examples/helloworld/README.txt +++ /dev/null @@ -1,31 +0,0 @@ -UNZIP AND COPY THE FILES into your web root (including the .htaccess file) -Assuming that you are testing it on localhost try the following urls - - http://localhost/ - should return "HelloWorld" - - http://localhost/sum and http://localhost/index.php/sum - should return 0 - - http://localhost/sum.json and http://localhost/index.php/sum.json - should return 0 - - http://localhost/sum.xml and http://localhost/index.php/sum.xml - should return the following xml (view source if required) - - 0 - - http://localhost/sum.json?num1=10&num2=4 and http://localhost/index.php/sum.json?num1=10&num2=4 - should return 14 - - http://localhost/multiply.json/4/2 and http://localhost/index.php/multiply.json/4/2 - should return 8 - - http://localhost/protectedsum.json?n1=5&n2=4 and http://localhost/index.php/protectedsum.json?n1=5&n2=4 - should ask for username and password - give admin and mypass respectively - then it should return 9 - -Now take a look at simpleservice.php to understand how the mapping is done - -Keep this as the starting point and build your own :) \ No newline at end of file diff --git a/examples/helloworld/digestauthentication.php b/examples/helloworld/digestauthentication.php deleted file mode 100644 index feb435ace..000000000 --- a/examples/helloworld/digestauthentication.php +++ /dev/null @@ -1,85 +0,0 @@ - - * @copyright 2010 Luracast - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link http://luracast.com/products/restler/ - */ -class DigestAuthentication implements iAuthenticate -{ - public $realm = 'Restricted API'; - public static $user; - public $restler; - - public function isAuthenticated() - { - //user => password hardcoded for convenience - $users = array('admin' => 'mypass', 'guest' => 'guest'); - - if (empty($_SERVER['PHP_AUTH_DIGEST'])) { - header('HTTP/1.1 401 Unauthorized'); - header('WWW-Authenticate: Digest realm="'.$this->realm.'",qop="auth",nonce="'. - uniqid().'",opaque="'.md5($this->realm).'"'); - die('Digest Authorisation Required'); - } - - // analyze the PHP_AUTH_DIGEST variable - if (!($data = DigestAuthentication::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || - !isset($users[$data['username']])) - { - throw new RestException(401, 'Wrong Credentials!'); - } - - - // generate the valid response - $A1 = md5($data['username'] . ':' . $this->realm . ':' . $users[$data['username']]); - $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']); - $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2); - - if ($data['response'] != $valid_response) - { - throw new RestException(401, 'Wrong Credentials!'); - } - // ok, valid username & password - DigestAuthentication::$user=$data['username']; - return true; - } - - /** - * Logs user out of the digest authentication by bringing the login dialog again - * ignore the dialog to logout - * - * @url GET /user/login - * @url GET /user/logout - */ - public function logout() - { - header('HTTP/1.1 401 Unauthorized'); - header('WWW-Authenticate: Digest realm="'.$this->realm.'",qop="auth",nonce="'. - uniqid().'",opaque="'.md5($this->realm).'"'); - die('Digest Authorisation Required'); - } - - - // function to parse the http auth header - private function http_digest_parse($txt) - { - // protect against missing data - $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); - $data = array(); - $keys = implode('|', array_keys($needed_parts)); - - preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); - - foreach ($matches as $m) { - $data[$m[1]] = $m[3] ? $m[3] : $m[4]; - unset($needed_parts[$m[1]]); - } - - return $needed_parts ? false : $data; - } -} \ No newline at end of file diff --git a/examples/helloworld/index.php b/examples/helloworld/index.php deleted file mode 100644 index e9af3f165..000000000 --- a/examples/helloworld/index.php +++ /dev/null @@ -1,13 +0,0 @@ -setSupportedFormats('JsonFormat', 'XmlFormat'); -$restler->addAPIClass('SimpleService'); -$restler->addAuthenticationClass('DigestAuthentication'); -$restler->handle(); \ No newline at end of file diff --git a/examples/helloworld/simpleservice.php b/examples/helloworld/simpleservice.php deleted file mode 100644 index ce5e053cb..000000000 --- a/examples/helloworld/simpleservice.php +++ /dev/null @@ -1,40 +0,0 @@ - + + +Luracast Restler 2.0 Example:- + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/resources/Luracast.gif b/examples/resources/Luracast.gif new file mode 100755 index 0000000000000000000000000000000000000000..98f92aac5a99ce8178b04105fb38a4776cf750d6 GIT binary patch literal 8033 zcmWldcRbXOeP;HZ@XJbB+KafMekQRZXE}FfytuE7rJJ9vmE~u(Q*Q zr7o)|E-x>i`q<>Eh$+fRo0*z^eUk&=gnH%JOj%jyU9Yu>qv>?IySuxhq9T}=cXE9E znyO-;oXDn$6b%9?)slK5$N%)nljH165nf&iQDI#bC9_x>At@+wt-7SzE)< zz(6P#o31F5nv&9vL}qEpv0^b>YTV{V`Wo^QL4g6w>U{rFBPGtCn^WTC=Hcn;?8uVh z^1@=o)iu==ZbrfIJr8LF+&VWxI)U3JSsp@f0qgSTyiJok(QvKNjx&P?6Z6{(iM<|*Rj)zmpz zSqNrk_AJ;&Q`PSey^iyrqL2tRU0o?9Wj%GZ(vp&n_I6WkO-@090zCzKV=2A2V_8#> zUR}_vAv!Azj2X*Q?L^lq!l>0Q3O zdE5Lqr6hr1@JWe7otx{|HO2cF=HtA?&CSibuFl^>ygMYAEVWdvxHu`wxJD%MHwct! zplqtC7Gj_ldOz?s3(KsnNtdm$1TW9*>@5DG%1LoSfTqIgW!c`|UL+6CQ%01flI*mW z$mD%jdTy?Zy=@!@Bm5f*AOJcb{r@EVKNA3w4n!&G>D3Z-dEk$6=b9&qQvcUexX$e_ z(w&03AQ)>S|5`tVRm%mMRQIwkl@TVwllaZWG#TUg%A04pWHbkVZ#LgH@Qy{Hmgs4* zN!IqjV}=;X@aB>`)1~GydXyH}&WAFq*L@AG7d>yjxq4f(MDoFxsbXSdHbt^YaR-nrHC533DIvZevT--ptDzp@n?bUTdA!Z&`0K zJ^$_YF79)-&lA%eyi!&KvSl;=t>4yTQ$_l}7KJUF`XPw1pip6e>(6yQ9QQBibk+rr zayQGjV{LzEwP|HD)*N+DvTTvE=ZV%fy>M$g`4(p||H;cJ`1u^2ZxDvhhcJN~TQbjZ zS7C30Xo;5#5IHV|!U?n+z8@gWypKO{55~1pb-Ud$nT_mG5+Mz&W_kq!lNQ>X&zw9D zK}l@aeaojAcqOjeeoiB!d#;KWgqOSuu$+ciM=@N0%-7uJWq^B$@Z4)k7l*aE7e2TgBbd8}7P# zk$QiBZxZAtYU`Uj%;M6}?kw?fqZ}GslG=3kRJX84D=YmaD~-LPj*g-eQOh3av8CPqQ}rCy)G z>!Mf@O~s9PZT5u(flHofd;-%CZkL}kHq+}UfxmaXETQ(q_t^v3J>jjYEkIRnDb}PKw`)s z`a$qL9bdt!)tmY`??5jiK zAU9UI#ew+Wn!pv62fCOQUNgJTo9{Kub+B&kg~Wbad1J(_V|Cx+L0#x|whwX9(6#h< z9;>KkJ7Wne;WgfMLi{k@ZdOjp8;3Nhg0RLEy~p8u_?|RB+bizxX2~}8P99i$V>hqI zDq9r-=K-+>k#xREki%NNqA{g5g$g{nOg!8m5o9B)L6r#ijh?(28}}4Z*`o}zAAR6g z9MpnFKU|!iTWay?rjAMa7GP1o83cZ2m4f99 zn6DJ%;EO`CEv>1a=IYlj(V*(hU}zXnrrKkhB7M#^`+-TB`m}9=woWCv)x7vzV6Gmq zRhC2#EgribO&@8RA?J>L5P-!Ob?XXG6@hgPKH;Cd6e@>+W_3uC#m*_ob{}5FRZ(8ii9XEKN-qFiz0dLxo}<`ky6ZW?s9wrs`!PhB9<&9c+sYw5gnIq-e_&^ z6X8sOH1{j84VXVyW^?_=-9>jm&4Q@yS(j@W?qSX~4Gu0s^Z5C$^R5tgYK8aP zQ?sXZ&zZVp7#PASZQ}6N<%jm?%r> zjF(rtoD@~A-PhBi)uma*pM+cuj#BEomM9yl#@I%FfBog&v=hEIp_roY!tXXi(y9Y!g^y$RH ztRN2VZSuBMaWtO)p!so{Io{eB4-ltv$3zJR;9se3^{2D-Uowp=C|o;6%rqYEDKalR zlat~HP$QKYT)NLf+`X*FdfTL_{%Kka__^JORA@~V=g18!m1blI@qXn@%IXn})FZ{D zQeHqzcAocO4K{(`u#ck@lvv0Qs6T=}>a2%M=kN`WJw)d@GZ9{>FX&vo--wVkLj8A2 z`|??l_fi)SX|IuTm8>sK=7|5St;aaqY$Oy-UBT@|^Lch2fy%GNDB%8VoTne?bLj0I zMkQ2!_zoU3rH>m5X0vE8ZTuPls@NZ;)!?u5y(+26RFoFvk0R#ftEjfC{G4asf?cB*?>@1RlW2 z1nHkT7tA#fY`YOmUW3#gTEGbUNj7l^V^9j60Ch@;%^WaEIz$#=8LF{}QUIv~M@C|q z-YYxaqA`*HIFmZkU_ju#xg=m(*PqcHED`d_A)Y&lRFX5zX!|}1hq_2(_~r^9RS7vI-K%^EJ*7cO zIK<){shtWgDaVBP!JC5N*oiO*8G6n(GlUGCKA&+VF;p%$`|}-gFSquR-c4eJ~SKa`khF0v@6 zv3coy={JO!&mhRm{L6I4vtax@{3bE zDg?(k>Q|s?R~mwY3*w5CzaU%)3>x^HpC-jW5{r#03JgOae@sf^J34{YF{lU&NpB+#VAtH$ebHTwi$ZxJ}KPw0UYp4U9*w@X6Zww{NS(A%RCCjz_ zAgfvBiFTC@b^!YXvn4HsjCzw)1ZoCQ57dFALwK_vxW}%lKd-7Uxk`WN^(!3w0vTDG zmt8ISl9NC&q#}iEfaOyP4{vql7s|S1H4KNeS*ZNjQmlzWMc}L6hk)6rWlOopZ>X9i z8scE5OzwB(v>k;dgrZF@{+O4&n3u<6SB?5ZVKl22$6>}7%FKpp*powffj9SP3-g>I#k#Z>G zL$N=0Qn*wxDvVj2g!+Y~28qRr3!%l7JPNBM_>bz_>-LSb%OEoxn7y^&2QsU6p>bib zK##FWov{|iSZ|t4frQqZGnP{%!R;K?w1wILskh=Z%)`OAM8?`MzNXuY{{f2?l9lz| z!+;^R$*Z#7oa`fKUwzZ8QJf4UlZ)LLo3le(NQ{kU&zpUB8xlgB<5KF;IB@21bE(FE z>J#NTZLQh%wKbl_P`;Pv+nRjL+DuMcUhY8Kbh4jEhcUC5c~R? zKMm2(+q~_e6T9syIHde;+szbkFRnd1g)&@O|1qyVmR?zKm#;H=xTSHoeGb(z$=Dhf z`b?kJJgf1RDX4i_qpfDAX=Atr^!y!xueQ44oli>Vg+DDj!=2Sbog!4qk<{B&o$47{ z#mZufFCW!pr-AM49k+KIxVPkX#QwK=p+U<9zDHZy9z6|Y5F0o~H(RGE=(0d%*} zQn#Xc&-QR<^Pld?P^!QP^dInUc99C3po$+d^dOu}mQqpK?@cuCNDd~fACoP@LRUUN zUEg}zc()Cn->K%%1Jmr09|0_B(5-;(l?5tOKH%Bjr%wj-5Bv0Sm;h-Ew@yE|U;hlb z|4|iqrU)Ey)*l&$k;Dxg>kPz&4J3vQaMN+jB6zA0e_v5$&u&|%fF{h!t5q+xPk)cH zihG~mK3Kp%v`PRM=L2H3eV$8we(3(drJ?Gxp&EyNZUAaY?U6U9lB+r`af3T%-MtfS zIcI}Kn(tpN4T=#*io=G=tA_mf`>P%L1Ehx^YL0$z7@g6Crcj~eyN)Y1=PSUcQ??hvJPn)@Zv zlE-q}2j4pY{Y!&;OB1ip#<28)A=bZrwcg|P-jiJ6lMEAJVW3-#GAtuKB^VA7933{F zfa$bPUHvqL{xqf}FkT@&`HeQB<~XVGY5JjgzYY#8eC5Nk^atUNVHMH`!GaHBT2sPg z)Oy9B1k;cQ|IEJTwAGcFk);t^rqM9U%xOL#RNHOjI2+qB>+CoyR50%$GZiN=Yl(*~ zq2_j~CIYX_J@`9uBt5R8HCc-ujp&#ON`;yi%^Fc=)A#3B!e;Lj%tHzm5@le*q^Ubq z69GOS74|0%^FQ7g9kMBy4rf}ppAU49A;qH$l+lI6v=0@Og(}L@TK=pGae}>es)1>` zDQ)Co!N+gyL#hQczvyA(c3MzLB1mLn>8{U`G1DiZj-^V1kIPKU8U;Wv8FJ_EQgS$Lzhn8$=rTH;X5zSPk_J;coFy>L%!Pj}XApJ{`EZCe1HFNq>RJhwxyJil=J!arXrofNdI{ix;rw`04l+I!PJd;QhhgJau^pH@`& zcLMW)VobNEAZ}Ed+rSE&ydi(xo=>sUf_LtYC6Nr#yef2XXRRMLM1(od{czFXIvUU_#?A%s-Zfmmw4 zkHnn3%J{}F$V}RYBwd2_MV`HRaW;7GY?%Hd)8h9i=I^tWvx>;GfpMsCE!^^u;eft7 zwdp(b^4*TxO5f{tq%Rg3a_{fXi__$lf0H$5jTXO0#(#ZX`8RTlzB7KN20$KLGv%NC z1+IxiQK2vjsANbc7u?%eSZe*V8JyJneK1rs;h(l6TE0 z9^C#}z0S5}|0sDs?{a$)i7l(ex)rEij|QpPJ|Srp!&3H3h3gG|9~R1rIDID@t>)|h zDmw5jqx3%pLb^+3Xi4vRObsL!Frr)d5sht>8I<|xysGPM|)`*k06UO~(G>c*F5#%4ibK*yY$f&rohh-ml)|Vzy88u z$vHst$W8Cip{~$Du3v)rckzd*h8W=+sASu9vXhexk>x_43$#wy!vo0XoHvlp=d9@I*j=kn^c-$T>Yg>`1AUPvhbvQUZry?GY8RXf{zd zy%qyXvf%r`ysUg>#`k<^ike#*CpOC%3!lXmwY61ogx-3j!aD^r%w!Sw6?;jeu_pR~NQ?#{3PV5e&p?+0pI%%}NDo7=+31QwGUw+y zPOn*LfBQrJCvBtoesb6e^gkz1NmEr_PL-|1a54)pw(5%IYp0zkY^FQpPk4z#h5w9j zVjeaF9BXF6Qu)p5+yU~5mBT_$$X#0dI0!ls)SY{>l>tQ^M!^iS*p=tvni4kFq>1K+Q& zIe|;5o*2qCS!9|PZ}=JzL7ob99Tc7{`k4ffUNx@X02%ZFPhSZL*-^lF9e8=pF;xNz z&fT`zlo=#th+{!Q2M+H0hyDpceqbRMKM-Q~yl&K*D{QEWCL`1`vGDOrMn93ug;1-; z4kcY0(bwhTyq0FSN=VS$SQZ{jY%ql4_!-uh!Uy~k#5YJW{cA-f13E=dm%F_C^X@Qz zC1;3lQn~Y+b^2sU%&yM(+#@S-CB8hurJH_CEEEnHw?A^AS+XL9$i{T)dV|~qWlSgs z1ij+G{#X`Rhyfc9luSYTMYFB=>1tm!v2qC)1%FBe9ANnC6OTctJ{rjBfN1rV=Q?nL zBe=Af6rKI=v!tgRvgMJjqLc}<^*-45q)t>UC$xxB$OAy1O~!+uV7N}X^epxf^89ro zizAYHL7J2bHz})wb|-*rqLMX=qB-C+oSDdTdl4HHhXLS=(|0agLJ?~XZ&ZO)kBoPW zZr07*_jphD^q^3sK<)}9)LvoJxcNI%?fs+Y^Drr!E;Og_{~{4?)!fCBEgd&lK96>t?^)&~dGmXT&cifU&2|3W=3bcy|5Ta1H6ga# ze%@K_x|B4un^?;Hz~y*_tP*}VJ{6v!o9POvbt7()w>}Qvf(PdGYHrGA@eI2g1?J7P VyDR_qar9n%VE$+REfN4|{2$~qqzM23 literal 0 HcmV?d00001 diff --git a/examples/resources/Restler2.gif b/examples/resources/Restler2.gif new file mode 100755 index 0000000000000000000000000000000000000000..19dd0ad5ec5162f58c0643d255e38e57c16e419a GIT binary patch literal 12476 zcmaL6XIvBAyEZyWCcRRG&?I!EhAN%Viy#4!ra+Kl5P?vXVw;2zItE1q5ex_jC_Yh9 zQBXq@utr5iMU7a%9vh;fygvKv|9;Phv(J2(S?iwry35?xTC;v@`33koI40}_b^_l4 z07Rmz_MAMTyyBOxS9(^($d#`NiQ77k-uh`D9_rv9y*}rgvQ^Kihf2#d?$B4ec!qsO z(P9(wgF=(*n+N5{<=dd9gRP@#8oJ*U%TWf0d9e%VH_wOqbY?bvDj&shD7qwjw$XM}4QpTKD~ z5HyLDWox6-g|(+2;0+jF;eiG=XD__`sbKqEfnKvFqwAl$zcBjdbk_xfMZaWjbK$XdI&%tjLeKRT!8lvjdSK|y$#b%_jM9R&=^cB#Mo@030XE=3G(R6S9xdVrWK4ElsRriQJ z)_nt14UMh;V2xUP9~zt3-@3Q7VBlF(clJQ*Xhq||AEM=Ng_ZNh-an1~#&7n5->iQAX{IksdCvL@0br;FykC{2uPmmfvt(Z;X zy9=65wkv&q8Tcip>>3*T#0=Z`9Hv}f*<(vHod=hHCKv{<&x-Yqyy70)*mG+@-R3vm zEQ{AH6b;^1G;cn9`L~9^%9C_C~6t@lQe-ro0wzvBSS!f_sNx}apE zK~{bZS3P%W_K%WHP2PFfH(}Ou)$?R1B!tCTV zg{#983)2!ElXTtPXfF9q`RSbWWe){H&9H)F&-Grpf4arV_+y9C!b!q>llAGqL z>+$ak+P^-%GP9Fub{2H=M7q5d&ED3+%9ieE>u5%^rd!c1t*kBSE6uIwPJgLGx1#;` zrTf=gc2bH{n78kL`}*5+)n(`Aa-1wJ^YZd6@@yxnCBE^ zcNJJ$$Tu?YtcA?CfnE?Ei!H&P?2ro}7{U zA8gY9#oGU`*ni28&iU)vJ2^XbYjTorc4j*5zmj%J{oiA;`Csk*H#X^ikHz+X#ajLi z#`53N{=Z87AJJd_{M-B=;{NUY5A~BX{_;KhFR@`ue||6i`uStw`~0`BUp{}5fBf+N z-P<>>=Vo8MeDQqdne6G4$J37}=j-F`<>|q2cXM@dc5-xBX>Vt1WBqqfS(uxdnyfH3GBnWF z)77DAFW1u4P*sw`7hQdFRjNkjr3hsB^#NDu-4J3Z+90RPSr0ATj-?cYBpfLQ{x zu$rZcb2uypyRL(yYYHdEwx*58yh62>Sj`4W1w} z^0;IV#J{d>PJZ(&;V%46jc>V=f|{{ZHps_oxK)mmVn-{~X4Ai7!t{&w7>0HyoqM4g z)L@%FGdXd`NGkHK(p`IHa>b-l*pZkEKlM%TAIC|$p!<2*H32neMGyEmQ7OnsE!p*laxGIw`E+bv zkliY6^AD0m#@{R%WID8iaimN_RqQd5$R_q##%G_zjfS?+wo16>c=-Y zvVOz$W#eJFw|%xM>3BNTH}QQV=F|jh%md<5JF<}NaFd{kxXp&=_qA@f@0~K(c?oHx zrx$FI@Rq6M>cC03DKNM$^?T$`;xv?qX2t%74Mqi;5&d^d{y7=RIeWUBJ&eRX zoqZJd>ZenR*dgLrO5)gRq3=n@&@-FqgOxu&)u_&N%{I3;1gsbtk(baHLKKadabC+{^^dftJC+wMU!36k{#4f8y;F(+;~Q@ z(`sfiUF%bXk;U+U#$nqKZ%yCz+jk`PZ-@^%6XYI3t9B~^TDWvu*$jk$fZHRl+jm!E zbmN0^0@ae`*)92Z>fU`#J>G3(ch;z|!uXo1{PNtr;kC8LUnsxb?ATbWe7nM}kj+56 zrZQ-yv@-qAL-W&_hsH^P#zh`Vq~6uQyWe}Jdru$vZLSi%woo2-w=7;NInHuBV9~x3 z+L7{7zG01ip#p)p8KArrjn4BZd<6$NSC8#$Lz`ouP|wtyV9u>@x-z8H*4q^h#A*A&*=u1D6^VcI(1V@TemK z%p9w7jcw|Vb*WFEMkw_yJbM=S=||IVbx9x9H7LybYkdEvsAK!17>eqWVr#`u!+K6k z-X{uDVFd%`i2GD(IW=hH%>okn^m09Yov7AV>dGpCgc7*wHFb$kwg*3L>}YMfpuOHY zhM_&g9GM!o4G`%slfoqJERam;-fmRxo&Pbq;hsn zxVR%LY^))QEFZ+IeKAwtOfPY~F&M;zT2-a*e6;z`idFkQRe72jXg#-r;}{S;XHnl| z%@0YYeyhp>agoz|3e0&*7k;Mnh)cQ-SJD*}5y`92Efp>K_}W8}!hr_A7trqY%)=zn zBgj+ric~NIM;vUEQL3n8`IK9=Et>6`=1%a6J$Ja4`9&Dtz}tHcD5L1o5$)xYujejqQm=<3F)!Pk zpY_6D9^y^+r%+a{xRGBOR{Lbp(D>~_z;LxSuVb;(S_Esz-3?nP$QOint-8Y>XM(aB?Ajs0;5EZG^T74Sb!B6e(=8G)Ke4iiu zomIlNsD+{VDY&K+1(OqZMjOr^v!D9;;A0He{j!Fodzcuyi}FODWpZIxL};_NLHH$h zkbcrhtbf!ntc+@w;DrrzHayc+-F}!`w5`esclIpP;8=FN5EdaGv`hJcC|fRrkwtgO z8z96o9_NAgOf}WP5w61dCI^F7>g^ zZ|+a{8*=t8nX(XGEHJDXz^VjYc^oL$!1j68wt4%QH2Lg&G=Z{%AFr!nSArv1aN;SRu$ z_>17+&k}OgD~=oypo2CSDTU`>#%yI4Z)3$FcFk(#E~Xb85THRwnq56 zm|hE{W^C$Jd&)P#;uc@PHR_Rjww5?YL7bWLAsMRLZ;cTN7Fs@|rp5G%I(eaaaPYFX z(L-P7PUa>zTS@=iQVKlvt{pi#=hs}Fl924nr_#d))ZiAJy=v8) zar8x{iPd!MOoF^ziP}x!yd!~fh|$yWJmOmN_91gje(zvU_Zhen?ViHH4hA;-9^V|! z$ccTv&1s{w1i^kbm0%Pjd1@KwQ8wZV4EK&s;dAFZ@{bff8&4Roj6>cZm z?07XlU1`(KPYuYfJOu2F*{5m9KR%b;kp z6nGD3crIaUBabzo3(zd&@QfQ>-}!I*vNurWgQV&YcG%Lnn!3dvKmX^Kuitv*=QQ%X z2zBgP>WMI8!5vH~!eT8AbCZF*ugVRga~HoTWHT`kW8-UO#Js=?m#s7-f>9|RS5nq9 zX4%(8c_sI^9Rgsyv2BSm-bFO*;uxoRmgN6E9+krSghub8>FRmvF67c9a$xBY?m^;? zLu25cy~Gtz9Gr!>8QW-YWXUMkyP{JI=FRfF(z*Pg#IrRRqmbWw_bm9EVh72dE_nc&AGC zm-v|G+NA^JG7A2QBt^-AyKg1~FNbqQ@Ihgkv!}h=XPXH)RwyFd$_XlCMT#3)(*W$- z5kc_1ZHWvlY91ELD%Y38YGvE*)5{Ny&UeM0@AlhX$SByd- zTl@t;hX8AzMR=8mSh;1V1Z4zjY|8S#cqbz544SbPd5J14Ub=CUocHa*)YLidsM8nN}I_l`i@1~qtL3TA}KtE zq5G|KRsV?p6FKT81p_#80l?lBfUDKR?s6*a(6vpvwfnL#BoR2WF;`z&7Fbei<*;x2 z*=?EGS*l%oQTTl20EisXcuXbm@J|OtCiAe$TScFwl%^h`C=c3hM ze-}Fz1Y$!N=Dbj|Px+kUOgQ($~A6%mxZ8@QGL22&~ zOiYj%-76<-mw*7&0dS}#3L6{Sumi%%SCL8Iks$>au-lvSId=KPZ7VrGOmLdk z-r;~~hdOUQAU<9KntVi@Rxu}GK~SkC@K8l*yg_$FYh2W8?Y<1AGvH;cy{R}LEk<#X zfpDgP2Oz}F1T(Ai*p-8{x&-*3h*((8SxoJ^`2Em*pgLAUyz&DUCvNkRA+b2*V;S-> z9l>NA8#|w;&Frj|3!XWk?n^jra@0Wyj^@YLQEyH_cQfULw(gG7LCmf3?h*jL++WEq z;mGMtNBh)KR#V&BF|aj`h&xV*@o11DJ~n=Z=q5w9%Mk(yHo*}yg4^TN z`^Y#Z7z@CtOwvn$pw9$tXvoKsQvq_sV;Sk{Lt={zDG-9!)p2W61x!x$hKZhfsG!N@ z_@Pa^)v=SFyry5;n@=74^AOPV{PY=3rXxGw>RPVEmO>e5e2Nt10{`R6PEC$I=x|w1hk)@oj&r*cYv3 zo@U$QOz6MQTk{Bc^Zc=*E5xFQ!~hzSDkZL#fi0o|ycicC#aTnJql|{i^FV2LuN#vz z9stvp{G%;uImiLgh~_T){wrb3_+D+>X>*>(WM5)zyu8$zyz6BdvBB<96yJTmXiD= zT~vaL=rkV?quc1+y1xc*C=3nD32-LrwTA86qgsVRG{nHe&mXRpiOiT{6uzC*FP6xV zcp2zP2M}W5v>2x;LsDr-Qz15PiH7uK;v{0AtdUUoXk>iFAXiGN5<(00gR304+ofbn zPDcVBu$#qhVdBH<$QxxsGv|M`922 z>x=$HSF$P8B8TZu58}C90^%5Pnj=t#V3Ds$4Gb8bb&bL##R&;`0Qpjk>ycwnO$2u- zZZ!n+en`YKt|KJUb}{@ktBomvYtu1JlGvl2d`aNi3QT-FrJ*a4Z00T6;mehB(ZA|6SYNMKdsWz zZw+h!!c;&#iS$nA_u_UYh&njj!zAr^2SV%d%=ciOoa7_Mt(M|GiUA+t@KJgHZqd}P z_4tLi$kQxLEFG)GLY(ou7c($=G5?;H7-I?H*Z{Di>GsM_8t6s`A|B(q zIFpx5pZEx|1!)bnQkb`RLh?MKX?t@Koxl^qo-iIQR?(D1nI3hRJPH2Wo+01EYVFzj z19IfuH>5v>S4kDHYrzAaV)%p*yJ``mDaN^p;ax)9?=-9?3){oOy3-KWbO0mA?5!I2 zWR2gP80kl&?HTuyn|Aa(7#*j8WzvEb>D_;%F4v~;>f&Y|gu5d}e<_r0xvf^fM8(sw zPbNj~lyibc!nSp!`xoFl7VA|n05N|sIM=qP>Dbv=ygLiK4}iUU|I&5cT_*F`8pyQ2 z_d-@dGa$k*+IL4VAN`S$wddf!Sm3>{1cZgrKTGS4@^jz?bmB`|c`H*KxQ@h>Tx$Ss zg$c1o#qiPTOB0{PNp6>cU{^E(b3SnmEZ^yHUjL)zP#gfjZUo=;kz-8d zm@eS1H4BTOzo}!4eB``&`iJ!F2F#QRB4m$mnuDFy!!|7N6f46lX?7|99v~+9GV*>@ zp?3b6#kVT`62ZE@!lRjl64Cv}Pw(;+)pKM(Kdn9VCxT=O2Czn6q+ffw^FxCeUh|uT z5P~BNLJtI((nn-ZaF6Bh1D0e+@5Qkm;fUuBf-eL7dGX;?-fT7&aH3XV~-^1Szz7hue6lodM*Lx7#iOFp-DNEo*Oa zYG+&}+c%-4F?GPC*0^SNF0-qar+z~~D`82{A~M9WUyqd-YD$m_-rhjYHTqyK?I5;z ziqxGH5qb)do;IV9RI>ADsP28b zVOfe|(hwu1mQyYQ%Jrj+;pH)Wwhx9NlCYMk9h7rLCnBu$wKh~X4l06S(TUuk8$&Fv zb_DHyi9TQ0x-UrNAV6&KJ+bmYD37RjfRj?{Tb4j$aaCe!$6FDr>*@O@!n1^mrMRs# z6ZM-uVD#&<*bfq@Jdd@!+Spo=!DLO|gO}=g<+2V;ZqIvEQOwS$WbUcuE86j(NoXmx z_ex;$h~161E69Bs120t^K2T^vv(LT;neiv(FEisS=YA0_JzuwCbPftV%Jm0? zI{)ZK7$+l1%cGx-gwLqkYJs<3<6t1Yv(4v7yV$!N_GCpykKsxB%s0bI&hIS`PUR2p z_W+?pyv8y4tak4m`?B|E3Y>_YYnn?XD%eSu9&GOLrHzH=ZyNqAccI`7%5)7PzkUlJk~aZNT>R&?8~vxsr8JRV8$p~M5-FG z0H=>do+h<>akscP6yuoc=<2-$o!-)w?!aX$P<(jktG}LlG)-wU2aFK6BCFVvk17#| zO5cMS3d*>&>d&J_E(w^#Tpu+e3&ON16qT~qD^9o6Y1a->(ub6`M6mWF%|sfe<Wv#lAYlx1OvypS|Rgk#f{{%#JQ4uJGQ2-TUsAyHi%V&1O%sM;Bnbp2hWS z<7%#QD1rs20}ZEje@PxroDmVqszw_QWK8GE+wc~%t0qHWTo`I-;t zBFPCzN6}$5s}Hr6kB}q{(|i!UQ$}iVox~y>bQ3IQ$cDx&r5rh6IKo+Dr3oYBAOZ3Q zM__9<2yW<`ac4GM!Onj%J`Q%8-SWT6i{lp2>-nlvcZsKgEa0>}jxuqcOBDvIiAXYLIUUEZAeO{ad1>VSzC%@Ipxu`Zxs^MT22C$t4OFP}v&gnIjg) zpnf&W1gUGc)aCPO)--X&Ra;VT9m=i~$MC;P&P_yph+|F};;R*}_ePwMVYpJC{3Wa#~<({;4&ME(}- z#jT|*5srrVDz(1a=*VV}b6OsZ|g zSlhP%g_DO-6rSeBdcbi#3wZkD`Q|-8{9HYSDDt5}W(wD@4$e!Xq9>L0X9N*cTudC~xzbai zTCa6h+rM-$WS0}yfFG5=)CZ7Q?-R27TEp?1bB?dk+{p92TeNlgnWZmNtaoHFq+vsU zM(|C07~ufJ<<)pvU-38`kvohwjT-C^vr4XXPtnbDKm|q)I^>nkBbx7(y04bw^Okxk zCaj?Wm=F=Yt&g&d##^@ZEb!`|Z38dfZ>~U?ZZ10Y3>N;L*PPb?vicG&j{-CB!smuh zL>6k1(LND;DRERme~%J(*vk4=qxSX-m^cx5)sBZaba@)SIp1Q%uGw$Ha%7dXNcT-vh~W>keH5IGcVkoCkT{n?je*uR7hfh8jr>B)ZUfvQ zuFo9LVgVvUJT-rK(j27DKCG+`B$yQIM zG~hT_^#RKyaC|-F!sZqVAz9b%T~avQ)<%g5UiyXRI=P{0ySX+435f`^S_$@8NueB} zRSzms5g7|m8m5Qb%O#bfP8%1WQ}0+>R?j`P4hhl(Th$=l1sK7f7fR<<$OVS}C{`!3 zGxeZ_@$m!ebWI(Oxyu0`W|5BU!jm;aaQzacIsR&~E~MeYwQ(AB8Zg^@arU_x=+7sgW6{KZw9D$26vF&DVH9Net~ zTr1IqQwco095Bm8BB7BdzlsdDAHmFlK4M+uoZXF04D`TINbW`Ev69dcgiEL7m>ME* z0b+8{iVRfbv|uZh&yiLHH&V1AIEL1n8sOaKp?U|8G+~fq9R&Iiu(bg;+;G9mTLW0A zFgfYGcLBX@p?E2(5in=Ne4=RU4xEbrC0cFPmo@E~^A*S)L9F;ZuHDjm>eHtg)= z;!bg+<;83Xq|M&1+bM)a6{qx7bv__s7*bAmu{H#1sDVIVQNRMEKL@X^aSIb&=-y0|J)@IPK^P`TmY@5F&{E?) za6)-BCw#(q6IVsTzq9^ozyhcO!Bwb;yg8m#Cl@ci3hSQ;Ej^zI`qn)NaCRxx2fUZM zOnITQB2pBXB$1@CMlD4A&V$zq`k`RqX&VXLO3E9Hq1{{zlIr`NRK9*H=<1!`u7fdG ztrkG*?br8f7Om*KUNY4aE%s9sb_T8M4;mgmYI3=Du$xKcn@9wnES}EM_4)h>@)R<2+s((>r5;gy z=A7hKpcn^3C4YgJta2zh%#WRe>^r$0!V9tfJ|WIbS!xk!3Xv&4Hx2;lGT3q<>TME& z%(RhC4rRLp)aqZqJuN->Myki=`!Ye>MJR-W4($`{U*v~4+*00Gx`glKwJ!pBY(X<< z6qkKnmvb$D0kYtrG4-HcJv?)&$Yh*HkK&^%2zFo4ne{x9w0N0(*=CxCTPN3f4$xwS zrIzwd9pF(yCbFE7eeCwFkuc&Y`1!?ci{D{BbNqD1@J`V{8jTyZgPf_f`x6^*>l4sR z1KdrEgYxx`4gq;mMy-`1X0C)y=czM_>}3d(P9A7dwk2wk#3?dl2z;1u4=T)qidrAd z_sJKSh`5GTe8o9%8O_A5QSxzwnd`mklkMHcnJ~+5VLnlJSJQas`9(eu^d*VwQgL^M z82(9Glhw^#$v_=_gDQ^cymf^U$SJX+FjYj*Qh*5DLWlS3G&)Oa4Tao9jEcwKqdF0k z-&wX-i%aw86J&_xrCclxjOaw$F~X20S3`^MhwZy>9=vwjg~(Nt_sQxC>B@aeL8fD1?=D8Fw{ zzW)PN4BV|A6*2|l`*^;kP-xgG)6v`cFQdbI?r$5tzx{6HBTX@hR_v0C69i9Fp7NHIbm86w~$X1~*JZ8d(t6fy&XqKIA}w~%jbA%A%pbGqto z8vQ}}4A>|`p@Yj4nuKQS367?GlNx1wSR*Vi;tCKhJ+jcHSw~CE`8Q+SXicw@;FaMp`A+y2z z#(#;e{EDnf5g|RO`?mIpb>{SuOfYh8+$v5~xc06MDsJg?f--S8&vo^geXQH<$dGj` zAPVggmt5t)+XHBDalRQce#+{19QNYNv2~T9Bw<26moW!LLzRuEf$V&^4-<&!d!exR z+0*w=)#qP?#NFW~^VI1N*T+@nQ&A>#vbF~~;!n!jQ@j;auwaTyXRXJ{SfT87-&`Mz z8b>qLPzBU|tUpapPry8*c#1Umvd-w_I8n;#=Nr!ie@{wGt;6t^5!~j`2iO7yfT+mi zrO&44|?=W)1+h({O)!iDHi zAvzobK5i&BPZ8D4|6NdW0XB@x1QPyiM5?oS%f%X~N%&7bo^>AA;clKm!SFb;XncyN_d3yPYB`x|{d-gh?x$&OT z*$bt`$7i;zj?ZT!+E%~mUM#VXg3_oBp2+*hxq$|r%!prT@LY#UG3r7buuQwABS7-d}K)*qq)myPFC#fFjDnMXMc(445zmP&z48eDdev!IX;sGWkf`rt}D9n^fh20n#mWHon2P!7*Su42Cz%H zIu5Ur>^~xLelRdzPa;@=(+-i>!vG2^5+q{Zd6qGT$c97tO6*4{xG|JzzI?-$D(rlPc#<{r6fb7iW+~f9Ba9h@9x^|8&15v8~c}e#>8g7zfpP2(c znQGV1oA_|l)IaUZ@%GYd+2)y<7PB?ws9Cc1XWNe#Od{VFF8n=4NWYNGL6$>^gU8jM zes&9K&8W^GkA3urPxpL0(e%{1f`Bl_SzbIyaZFVYWRGM>DhM4ei^@wMR5 z*IjHtNp{H>LYlL3eL5k7DZX*c>(9ck0pC+1cWrs?n-hKMeJ~o@zp4n$Et-O+iA)mx&E zd<)*lTT_23Q?p=?c7Yw+TjkYvi|;uc)x03p{N0&bt>zxp9xG_P^rLpM$oApckfUGg z$lvQU@_ttr#Grw6ZJ)M~!uI&Wwtvv(Oc2}t_3)n`N%3~|Muo?27TWzPV32LN|M>Ti LxAl-Qfb;(WO<9`* literal 0 HcmV?d00001 diff --git a/examples/resources/ZeroClipboard.swf b/examples/resources/ZeroClipboard.swf new file mode 100755 index 0000000000000000000000000000000000000000..13bf8e396202964e0048333d878f4b949a2f5e6a GIT binary patch literal 1071 zcmV+~1kn3KS5pay1^@tfoPAa6Qrkup-d$aeB-A-xFjKf1te)pFM-&Q&_dOT zp~-XxqP4X~YJ}vGWC;KAD1C=MKwiO_PG6^Vbs@z~r#qgr-}!Xr?4IxJ9P1kT4dpSa zmlT9hja*+}e;Cbih*6`(JT|+I(1(#NIVSiTL~Dq=|Lb=d5tOYL`L;_#dyQQ%FAAmI zcth~a_gzLk@xphk!Y?fFYp&C2`ZTZ#X}INt9hY9ojZWZ1Om23g$oC2%i(XLAs&#|V z5ArS7X}yhomj%SJGT~|rnZj|zM|I&j59e1QKqGxQN5!(ijWrx1S)ZN!RwWBwC`$uYc z!)028S7F4?l?H2dd39HKImh$+mv#S~I-YjmQ;P-rUfUM~-;Xr+ldpAXK+hS!b|@Ro zUs)@fv!kf9RjpFXZ?d(Pe_q{bY*sgP{Ykaib==7Da_N!X$Z^AwK5e&BZ5R56j>T=;_5DD$nR8}GiWShym;5A&x*eM;)Us-}<67Eb+@9n>sdlhm z`(coON!$a6H-ML=9U8}t-8aV1yD!xY9v@|7-FWq*lEUMka&b=Hq$X{>72}4eNl_P+ zccPJWGtXb!r#GbVPIO$}sN%oME%Yf<`b@|2f6G5y#$}-l zAR|D=L6`ti0Wt|N4P*v{Ss-&j?gE(yvY_TMkQE@SK-Pd%f#~WwXExMLZXW@84CD#m zFMxar!IU~i}ZGSTvX;GX_!`A@yKkIbSu*e=l_b9m*BF@hOB8SS;p?XkU4 z+#Y{FagI+_hF#pQ*y^c#B7H9*TQ=n-IvJZOQ*KYMV&e{u!2((~XOiIAy*Zr0yBr$x zqA6D~T{u}ZWn+;Cn@jC`refSD34E}PZ{YGaxq%P2g&VlCEyl30qM2Z<#-M2CQxMno zG_4J8H7mc8(VenQzJ;=@j=BiTh*RubMeS$61ORcM^S6!u6l;=?s|@ py1A~K8@jovn~!u;;=k8uI$3rc`gC{*rT-s&a~N%N=5J58nk1s|4c7nw literal 0 HcmV?d00001 diff --git a/examples/resources/closelabel.png b/examples/resources/closelabel.png new file mode 100755 index 0000000000000000000000000000000000000000..c339e59333e4c3b5bc5fd910796fda5469a2515f GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1B_t#Wus+)Bu;yq3!!CzC+*<6@{_r1Jzd&eL zz_O}6Q6nrOf&;;F`V=YhW2-H5dcFFa p:first-child{ + margin-top:0; +} +#facebox .content > p:last-child{ + margin-bottom:0; +} + +#facebox .close{ + position:absolute; + top:5px; + right:5px; + padding:2px; + background:#fff; +} +#facebox .close img{ + opacity:0.3; +} +#facebox .close:hover img{ + opacity:1.0; +} + +#facebox .loading { + text-align: center; +} + +#facebox .image { + text-align: center; +} + +#facebox img { + border: 0; + margin: 0; +} + +#facebox_overlay { + position: fixed; + top: 0px; + left: 0px; + height:100%; + width:100%; +} + +.facebox_hide { + z-index:-100; +} + +.facebox_overlayBG { + background-color: #000; + z-index: 99; +} \ No newline at end of file diff --git a/examples/resources/facebox.js b/examples/resources/facebox.js new file mode 100755 index 000000000..b764dbf89 --- /dev/null +++ b/examples/resources/facebox.js @@ -0,0 +1,309 @@ +/* + * Facebox (for jQuery) + * version: 1.2 (05/05/2008) + * @requires jQuery v1.2 or later + * + * Examples at http://famspam.com/facebox/ + * + * Licensed under the MIT: + * http://www.opensource.org/licenses/mit-license.php + * + * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ] + * + * Usage: + * + * jQuery(document).ready(function() { + * jQuery('a[rel*=facebox]').facebox() + * }) + * + * Terms + * Loads the #terms div in the box + * + * Terms + * Loads the terms.html page in the box + * + * Terms + * Loads the terms.png image in the box + * + * + * You can also use it programmatically: + * + * jQuery.facebox('some html') + * jQuery.facebox('some html', 'my-groovy-style') + * + * The above will open a facebox with "some html" as the content. + * + * jQuery.facebox(function($) { + * $.get('blah.html', function(data) { $.facebox(data) }) + * }) + * + * The above will show a loading screen before the passed function is called, + * allowing for a better ajaxy experience. + * + * The facebox function can also display an ajax page, an image, or the contents of a div: + * + * jQuery.facebox({ ajax: 'remote.html' }) + * jQuery.facebox({ ajax: 'remote.html' }, 'my-groovy-style') + * jQuery.facebox({ image: 'stairs.jpg' }) + * jQuery.facebox({ image: 'stairs.jpg' }, 'my-groovy-style') + * jQuery.facebox({ div: '#box' }) + * jQuery.facebox({ div: '#box' }, 'my-groovy-style') + * + * Want to close the facebox? Trigger the 'close.facebox' document event: + * + * jQuery(document).trigger('close.facebox') + * + * Facebox also has a bunch of other hooks: + * + * loading.facebox + * beforeReveal.facebox + * reveal.facebox (aliased as 'afterReveal.facebox') + * init.facebox + * afterClose.facebox + * + * Simply bind a function to any of these hooks: + * + * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... }) + * + */ +(function($) { + $.facebox = function(data, klass) { + $.facebox.loading() + + if (data.ajax) fillFaceboxFromAjax(data.ajax, klass) + else if (data.image) fillFaceboxFromImage(data.image, klass) + else if (data.div) fillFaceboxFromHref(data.div, klass) + else if ($.isFunction(data)) data.call($) + else $.facebox.reveal(data, klass) + } + + /* + * Public, $.facebox methods + */ + + $.extend($.facebox, { + settings: { + opacity : 0.2, + overlay : true, + loadingImage : '../resources/loading.gif', + closeImage : '../resources/closelabel.png', + imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ], + faceboxHtml : '\ + ' + }, + + loading: function() { + init() + if ($('#facebox .loading').length == 1) return true + showOverlay() + + $('#facebox .content').empty() + $('#facebox .body').children().hide().end(). + append('
') + + $('#facebox').css({ + top: getPageScroll()[1] + (getPageHeight() / 10), + left: $(window).width() / 2 - 205 + }).show() + + $(document).bind('keydown.facebox', function(e) { + if (e.keyCode == 27) $.facebox.close() + return true + }) + $(document).trigger('loading.facebox') + }, + + reveal: function(data, klass) { + $(document).trigger('beforeReveal.facebox') + if (klass) $('#facebox .content').addClass(klass) + $('#facebox .content').append(data) + $('#facebox .loading').remove() + $('#facebox .body').children().fadeIn('normal') + $('#facebox').css('left', $(window).width() / 2 - ($('#facebox .popup').width() / 2)) + $(document).trigger('reveal.facebox').trigger('afterReveal.facebox') + }, + + close: function() { + $(document).trigger('close.facebox') + return false + } + }) + + /* + * Public, $.fn methods + */ + + $.fn.facebox = function(settings) { + if ($(this).length == 0) return + + init(settings) + + function clickHandler() { + $.facebox.loading(true) + + // support for rel="facebox.inline_popup" syntax, to add a class + // also supports deprecated "facebox[.inline_popup]" syntax + var klass = this.rel.match(/facebox\[?\.(\w+)\]?/) + if (klass) klass = klass[1] + + fillFaceboxFromHref(this.href, klass) + return false + } + + return this.bind('click.facebox', clickHandler) + } + + /* + * Private methods + */ + + // called one time to setup facebox on this page + function init(settings) { + if ($.facebox.settings.inited) return true + else $.facebox.settings.inited = true + + $(document).trigger('init.facebox') + makeCompatible() + + var imageTypes = $.facebox.settings.imageTypes.join('|') + $.facebox.settings.imageTypesRegexp = new RegExp('\.(' + imageTypes + ')$', 'i') + + if (settings) $.extend($.facebox.settings, settings) + $('body').append($.facebox.settings.faceboxHtml) + + var preload = [ new Image(), new Image() ] + preload[0].src = $.facebox.settings.closeImage + preload[1].src = $.facebox.settings.loadingImage + + $('#facebox').find('.b:first, .bl').each(function() { + preload.push(new Image()) + preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1') + }) + + $('#facebox .close').click($.facebox.close) + $('#facebox .close_image').attr('src', $.facebox.settings.closeImage) + } + + // getPageScroll() by quirksmode.com + function getPageScroll() { + var xScroll, yScroll; + if (self.pageYOffset) { + yScroll = self.pageYOffset; + xScroll = self.pageXOffset; + } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict + yScroll = document.documentElement.scrollTop; + xScroll = document.documentElement.scrollLeft; + } else if (document.body) {// all other Explorers + yScroll = document.body.scrollTop; + xScroll = document.body.scrollLeft; + } + return new Array(xScroll,yScroll) + } + + // Adapted from getPageSize() by quirksmode.com + function getPageHeight() { + var windowHeight + if (self.innerHeight) { // all except Explorer + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowHeight = document.body.clientHeight; + } + return windowHeight + } + + // Backwards compatibility + function makeCompatible() { + var $s = $.facebox.settings + + $s.loadingImage = $s.loading_image || $s.loadingImage + $s.closeImage = $s.close_image || $s.closeImage + $s.imageTypes = $s.image_types || $s.imageTypes + $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml + } + + // Figures out what you want to display and displays it + // formats are: + // div: #id + // image: blah.extension + // ajax: anything else + function fillFaceboxFromHref(href, klass) { + // div + if (href.match(/#/)) { + var url = window.location.href.split('#')[0] + var target = href.replace(url,'') + if (target == '#') return + $.facebox.reveal($(target).html(), klass) + + // image + } else if (href.match($.facebox.settings.imageTypesRegexp)) { + fillFaceboxFromImage(href, klass) + // ajax + } else { + fillFaceboxFromAjax(href, klass) + } + } + + function fillFaceboxFromImage(href, klass) { + var image = new Image() + image.onload = function() { + $.facebox.reveal('
', klass) + } + image.src = href + } + + function fillFaceboxFromAjax(href, klass) { + $.get(href, function(data) { $.facebox.reveal(data, klass) }) + } + + function skipOverlay() { + return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null + } + + function showOverlay() { + if (skipOverlay()) return + + if ($('#facebox_overlay').length == 0) + $("body").append('
') + + $('#facebox_overlay').hide().addClass("facebox_overlayBG") + .css('opacity', $.facebox.settings.opacity) + .click(function() { $(document).trigger('close.facebox') }) + .fadeIn(200) + return false + } + + function hideOverlay() { + if (skipOverlay()) return + + $('#facebox_overlay').fadeOut(200, function(){ + $("#facebox_overlay").removeClass("facebox_overlayBG") + $("#facebox_overlay").addClass("facebox_hide") + $("#facebox_overlay").remove() + }) + + return false + } + + /* + * Bindings + */ + + $(document).bind('close.facebox', function() { + $(document).unbind('keydown.facebox') + $('#facebox').fadeOut(function() { + $('#facebox .content').removeClass().addClass('content') + $('#facebox .loading').remove() + $(document).trigger('afterClose.facebox') + }) + hideOverlay() + }) + +})(jQuery); diff --git a/examples/resources/getsource.php b/examples/resources/getsource.php new file mode 100755 index 000000000..e3f8d3723 --- /dev/null +++ b/examples/resources/getsource.php @@ -0,0 +1,41 @@ +'.htmlspecialchars($text).""); +}else { + die('no file specified'); +} +function strip_comments($fileStr) { + $newStr=''; + $commentTokens = array(T_COMMENT); + + //if (defined('T_DOC_COMMENT')) + //$commentTokens[] = T_DOC_COMMENT; // PHP 5 + + $tokens = token_get_all($fileStr); + + foreach ($tokens as $token) { + if (is_array($token)) { + if (in_array($token[0], $commentTokens)) + continue; + + $token = $token[1]; + } + + $newStr .= $token; + } + + return $newStr; +} \ No newline at end of file diff --git a/examples/resources/hacks.css b/examples/resources/hacks.css new file mode 100755 index 000000000..ddc417196 --- /dev/null +++ b/examples/resources/hacks.css @@ -0,0 +1,13 @@ +.snippet-wrap pre.sh_sourceCode { + padding: inherit; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; +} + +.snippet-wrap .snippet-menu,.snippet-wrap .snippet-hide { + font-size: .5em; +} \ No newline at end of file diff --git a/examples/resources/jquery-1.6.2.min.js b/examples/resources/jquery-1.6.2.min.js new file mode 100755 index 000000000..48590ecb9 --- /dev/null +++ b/examples/resources/jquery-1.6.2.min.js @@ -0,0 +1,18 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. +shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j +)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/examples/resources/jquery.snippet.min.css b/examples/resources/jquery.snippet.min.css new file mode 100755 index 000000000..fec562a4d --- /dev/null +++ b/examples/resources/jquery.snippet.min.css @@ -0,0 +1,40 @@ +.sh_acid .sh_sourceCode{background-color:#eee;color:#000;font-weight:400;font-style:normal;}.sh_acid .sh_sourceCode .sh_type{color:#8080c0;font-weight:700;font-style:normal;}.sh_acid .sh_sourceCode .sh_comment{color:#ff8000;font-weight:400;font-style:normal;}.sh_acid .sh_sourceCode .sh_number{color:purple;font-weight:700;font-style:normal;}.sh_acid .sh_sourceCode .sh_preproc{color:#0080c0;font-weight:700;font-style:normal;}.sh_acid .sh_sourceCode .sh_function{color:#046;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode{background-color:#80bfff;color:#400080;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_type{color:#3f2bf0;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_comment{color:#df0959;font-weight:400;font-style:italic;}.sh_berries-light .sh_sourceCode{background-color:#d7ffff;color:#47406d;font-weight:400;font-style:normal;}.sh_berries-light .sh_sourceCode .sh_type{color:#8b51c0;font-weight:400;font-style:normal;}.sh_berries-light .sh_sourceCode .sh_comment{color:#9c3caf;font-weight:400;font-style:italic;}.sh_bipolar .sh_sourceCode{background-color:#000;color:#d149a6;font-weight:400;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_type{color:#aadd8b;font-weight:700;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_comment{color:#35d6e5;font-weight:400;font-style:italic;}.sh_bipolar .sh_sourceCode .sh_preproc{color:#07f6bf;font-weight:400;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_function{color:#d51993;font-weight:700;font-style:normal;}.sh_blacknblue .sh_sourceCode{background-color:#000;color:#2346d5;font-weight:400;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_type{color:#c06cf8;font-weight:700;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_comment{color:#6fb2c4;font-weight:400;font-style:italic;}.sh_blacknblue .sh_sourceCode .sh_number{color:#c4ac60;font-weight:400;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_preproc{color:#8080ff;font-weight:400;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_function{color:#1c96ed;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode{background-color:#fff;color:#401e7a;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_type{color:#f7b92c;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_comment{color:#38ad24;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_number{color:#32ba06;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_preproc{color:#5374b0;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_function{color:#d11ced;font-weight:400;font-style:normal;}.sh_contrast .sh_sourceCode{background-color:#ed6104;color:#00f;font-weight:400;font-style:normal;}.sh_contrast .sh_sourceCode .sh_number{color:#971ad8;font-weight:400;font-style:normal;}.sh_contrast .sh_sourceCode .sh_preproc{color:#7b44e0;font-weight:400;font-style:normal;}.sh_contrast .sh_sourceCode .sh_function{color:#fff700;font-weight:700;font-style:normal;}.sh_darkblue .sh_sourceCode{background-color:#000040;color:#C7C7C7;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_type{color:#60ff60;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_comment{color:#80a0ff;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_number{color:#42cad9;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_preproc{color:#ff80ff;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode .sh_number{color:#619de7;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode .sh_preproc{color:#1da3cf;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode .sh_function{color:#f34627;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode{background-color:#af947e;color:#fffdec;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_type{color:#fef8bc;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_comment{color:#a00000;font-weight:400;font-style:italic;}.sh_desert .sh_sourceCode .sh_number{color:#3e3f25;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_preproc{color:#833914;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_function{color:#f3d266;font-weight:700;font-style:normal;}.sh_dull .sh_sourceCode{background-color:#bfbfbf;color:#656565;font-weight:400;font-style:normal;}.sh_dull .sh_sourceCode .sh_type{color:#3241c6;font-weight:400;font-style:normal;}.sh_dull .sh_sourceCode .sh_comment{color:#d11d20;font-weight:400;font-style:italic;}.sh_dull .sh_sourceCode .sh_number{color:#16930d;font-weight:400;font-style:normal;}.sh_dull .sh_sourceCode .sh_preproc{color:#003;font-weight:400;font-style:normal;}.sh_dull .sh_sourceCode .sh_function{color:#38255c;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode{background-color:#ffff80;color:#2C7B34;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode .sh_type{color:#ed0f55;font-weight:700;font-style:normal;}.sh_easter .sh_sourceCode .sh_comment{color:#24c815;font-weight:400;font-style:italic;}.sh_easter .sh_sourceCode .sh_number{color:#e11a70;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode .sh_preproc{color:#1583b1;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode .sh_function{color:#1d45d6;font-weight:400;font-style:normal;}.sh_emacs .sh_sourceCode .sh_type{color:#208920;font-weight:400;font-style:normal;}.sh_emacs .sh_sourceCode .sh_comment{color:#ac2020;font-weight:400;font-style:italic;}.sh_golden .sh_sourceCode{background-color:#000;color:#db0;font-weight:400;font-style:normal;}.sh_golden .sh_sourceCode .sh_type{color:#ffed8a;font-weight:400;font-style:normal;}.sh_golden .sh_sourceCode .sh_comment{color:#978345;font-weight:400;font-style:italic;}.sh_golden .sh_sourceCode .sh_preproc{color:#fda;font-weight:400;font-style:normal;}.sh_golden .sh_sourceCode .sh_function{color:#db0;font-weight:700;font-style:normal;}.sh_greenlcd .sh_sourceCode{background-color:#003400;color:#0b0;font-weight:400;font-style:normal;}.sh_greenlcd .sh_sourceCode .sh_type{color:#00ed00;font-weight:400;font-style:normal;}.sh_greenlcd .sh_sourceCode .sh_function{color:#c0ff73;font-weight:400;font-style:normal;}.sh_ide-anjuta .sh_sourceCode .sh_type{color:navy;font-weight:400;font-style:normal;}.sh_ide-anjuta .sh_sourceCode .sh_comment{color:red;font-weight:400;font-style:italic;}.sh_ide-anjuta .sh_sourceCode .sh_preproc{color:#678000;font-weight:400;font-style:normal;}.sh_ide-codewarrior .sh_sourceCode .sh_type{color:#4c73a6;font-weight:400;font-style:normal;}.sh_ide-codewarrior .sh_sourceCode .sh_comment{color:#b30000;font-weight:400;font-style:normal;}.sh_ide-devcpp .sh_sourceCode .sh_comment{color:navy;font-weight:400;font-style:italic;}.sh_ide-eclipse .sh_sourceCode .sh_comment{color:#717ab3;font-weight:400;font-style:normal;}.sh_ide-eclipse .sh_sourceCode .sh_preproc{color:#3f5fbf;font-weight:400;font-style:normal;}.sh_ide-kdev .sh_sourceCode .sh_type{color:#600000;font-weight:400;font-style:normal;}.sh_ide-kdev .sh_sourceCode .sh_comment{color:#bfbfbf;font-weight:400;font-style:italic;}.sh_ide-msvcpp .sh_sourceCode .sh_comment{color:green;font-weight:400;font-style:italic;}.sh_kwrite .sh_sourceCode .sh_type{color:#830000;font-weight:400;font-style:normal;}.sh_kwrite .sh_sourceCode .sh_comment{color:#838183;font-weight:400;font-style:italic;}.sh_kwrite .sh_sourceCode .sh_number{color:#2928ff;font-weight:400;font-style:normal;}.sh_kwrite .sh_sourceCode .sh_preproc{color:#008200;font-weight:400;font-style:normal;}.sh_kwrite .sh_sourceCode .sh_function{color:#010181;font-weight:400;font-style:normal;}.sh_navy .sh_sourceCode{background-color:#000035;color:#008bff;font-weight:400;font-style:normal;}.sh_navy .sh_sourceCode .sh_type{color:#e1e72f;font-weight:400;font-style:normal;}.sh_navy .sh_sourceCode .sh_comment{color:#fb0;font-weight:400;font-style:italic;}.sh_navy .sh_sourceCode .sh_number{color:#f87ff4;font-weight:400;font-style:normal;}.sh_nedit .sh_sourceCode .sh_comment{color:#000;font-weight:400;font-style:italic;}.sh_nedit .sh_sourceCode .sh_preproc{color:#27408b;font-weight:400;font-style:normal;}.sh_night .sh_sourceCode{background-color:#004;color:#d0f;font-weight:400;font-style:normal;}.sh_night .sh_sourceCode .sh_type{color:#f1157c;font-weight:700;font-style:normal;}.sh_night .sh_sourceCode .sh_number{color:#8ee119;font-weight:400;font-style:normal;}.sh_night .sh_sourceCode .sh_preproc{color:#0b0;font-weight:400;font-style:normal;}.sh_night .sh_sourceCode .sh_function{color:#ff06cd;font-weight:700;font-style:normal;}.sh_pablo .sh_sourceCode .sh_type{color:#00c000;font-weight:700;font-style:normal;}.sh_pablo .sh_sourceCode .sh_comment{color:gray;font-weight:400;font-style:normal;}.sh_pablo .sh_sourceCode .sh_function{color:#ff22b9;font-weight:400;font-style:normal;}.sh_peachpuff .sh_sourceCode{background-color:#ffdab9;color:#000;font-weight:400;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_type{color:#2e8b57;font-weight:700;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_comment{color:#406090;font-weight:400;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_preproc{color:#cd00cd;font-weight:400;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_function{color:#521cc7;font-weight:400;font-style:normal;}.sh_rand01 .sh_sourceCode{background-color:#fff;color:#121b28;font-weight:400;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_type{color:#c42638;font-weight:700;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_number{color:#0da344;font-weight:400;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_preproc{color:#620ac6;font-weight:400;font-style:normal;}.sh_the .sh_sourceCode .sh_type{color:#808;font-weight:400;font-style:normal;}.sh_typical .sh_sourceCode .sh_number{color:#a900a9;font-weight:400;font-style:normal;}.sh_typical .sh_sourceCode .sh_preproc{color:#00b800;font-weight:400;font-style:normal;}.sh_vampire .sh_sourceCode{background-color:#000;color:red;font-weight:400;font-style:normal;}.sh_vampire .sh_sourceCode .sh_type{color:#F35E1E;font-weight:700;font-style:normal;}.sh_vampire .sh_sourceCode .sh_function{color:#7bc710;font-weight:400;font-style:normal;}.sh_whatis .sh_sourceCode{background-color:#000;color:#0f0;font-weight:400;font-style:normal;}.sh_whatis .sh_sourceCode .sh_function{color:#e721d3;font-weight:400;font-style:normal;}.sh_whitengrey .sh_sourceCode{background-color:#fff;color:#696969;font-weight:400;font-style:normal;}.sh_whitengrey .sh_sourceCode .sh_type{color:#696969;font-weight:400;font-style:normal;}.sh_whitengrey .sh_sourceCode .sh_comment{color:#1326a2;font-weight:400;font-style:italic;}.sh_whitengrey .sh_sourceCode .sh_preproc{color:#470000;font-weight:400;font-style:normal;}.sh_zellner .sh_sourceCode .sh_preproc{color:#a020f0;font-weight:400;font-style:normal;}.sh_acid,.sh_berries-dark,.sh_berries-light,.sh_bipolar,.sh_blacknblue,.sh_bright,.sh_contrast,.sh_darkblue,.sh_darkness,.sh_desert,.sh_dull,.sh_easter,.sh_emacs,.sh_golden,.sh_greenlcd,.sh_ide-anjuta,.sh_ide-codewarrior,.sh_ide-devcpp,.sh_ide-eclipse,.sh_ide-kdev,.sh_ide-msvcpp,.sh_kwrite,.sh_matlab,.sh_navy,.sh_nedit,.sh_neon,.sh_night,.sh_pablo,.sh_peachpuff,.sh_print,.sh_rand01,.sh_the,.sh_typical,.sh_vampire,.sh_vim-dark,.sh_vim,.sh_whatis,.sh_whitengrey,.sh_zellner{background:none;border:0 none;margin:0;padding:0;}.sh_acid .sh_sourceCode .sh_keyword,.sh_acid .sh_sourceCode .sh_date,.sh_acid .sh_sourceCode .sh_time,.sh_acid .sh_sourceCode .sh_file,.sh_acid .sh_sourceCode .sh_difflines,.sh_acid .sh_sourceCode .sh_property{color:#bb7977;font-weight:700;font-style:normal;}.sh_acid .sh_sourceCode .sh_string,.sh_acid .sh_sourceCode .sh_regexp,.sh_acid .sh_sourceCode .sh_url,.sh_acid .sh_sourceCode .sh_ip,.sh_acid .sh_sourceCode .sh_name,.sh_acid .sh_sourceCode .sh_newfile,.sh_acid .sh_sourceCode .sh_value{color:#a68500;font-weight:400;font-style:normal;}.sh_acid .sh_sourceCode .sh_specialchar,.sh_acid .sh_sourceCode .sh_oldfile{color:#f0f;font-weight:700;font-style:normal;}.sh_acid .sh_sourceCode .sh_symbol,.sh_acid .sh_sourceCode .sh_cbracket{color:#ff0080;font-weight:700;font-style:normal;}.sh_acid .sh_sourceCode .sh_variable,.sh_acid .sh_sourceCode .sh_selector{color:#0080c0;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_keyword,.sh_berries-dark .sh_sourceCode .sh_date,.sh_berries-dark .sh_sourceCode .sh_time,.sh_berries-dark .sh_sourceCode .sh_file,.sh_berries-dark .sh_sourceCode .sh_difflines,.sh_berries-dark .sh_sourceCode .sh_property{color:#3f2bf0;font-weight:700;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_string,.sh_berries-dark .sh_sourceCode .sh_regexp,.sh_berries-dark .sh_sourceCode .sh_url,.sh_berries-dark .sh_sourceCode .sh_ip,.sh_berries-dark .sh_sourceCode .sh_name,.sh_berries-dark .sh_sourceCode .sh_newfile,.sh_berries-dark .sh_sourceCode .sh_value{color:#c40000;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_specialchar,.sh_berries-dark .sh_sourceCode .sh_oldfile{color:#77379a;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_number,.sh_berries-light .sh_sourceCode .sh_number{color:#20755a;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_preproc,.sh_berries-light .sh_sourceCode .sh_preproc{color:#0628cb;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_symbol,.sh_berries-dark .sh_sourceCode .sh_cbracket,.sh_contrast .sh_sourceCode .sh_comment,.sh_emacs .sh_sourceCode .sh_number,.sh_emacs .sh_sourceCode .sh_preproc,.sh_emacs .sh_sourceCode .sh_function,.sh_ide-anjuta .sh_sourceCode .sh_function,.sh_ide-codewarrior .sh_sourceCode .sh_number,.sh_ide-codewarrior .sh_sourceCode .sh_function,.sh_ide-devcpp .sh_sourceCode .sh_type,.sh_ide-devcpp .sh_sourceCode .sh_function,.sh_ide-eclipse .sh_sourceCode .sh_number,.sh_ide-eclipse .sh_sourceCode .sh_function,.sh_ide-kdev .sh_sourceCode .sh_function,.sh_ide-msvcpp .sh_sourceCode .sh_string,.sh_ide-msvcpp .sh_sourceCode .sh_regexp,.sh_ide-msvcpp .sh_sourceCode .sh_specialchar,.sh_ide-msvcpp .sh_sourceCode .sh_number,.sh_ide-msvcpp .sh_sourceCode .sh_function,.sh_ide-msvcpp .sh_sourceCode .sh_url,.sh_ide-msvcpp .sh_sourceCode .sh_ip,.sh_ide-msvcpp .sh_sourceCode .sh_name,.sh_ide-msvcpp .sh_sourceCode .sh_oldfile,.sh_ide-msvcpp .sh_sourceCode .sh_newfile,.sh_ide-msvcpp .sh_sourceCode .sh_value,.sh_matlab .sh_sourceCode .sh_type,.sh_matlab .sh_sourceCode .sh_specialchar,.sh_matlab .sh_sourceCode .sh_number,.sh_matlab .sh_sourceCode .sh_function,.sh_matlab .sh_sourceCode .sh_oldfile,.sh_nedit .sh_sourceCode .sh_function,.sh_print .sh_sourceCode .sh_string,.sh_print .sh_sourceCode .sh_regexp,.sh_print .sh_sourceCode .sh_specialchar,.sh_print .sh_sourceCode .sh_number,.sh_print .sh_sourceCode .sh_url,.sh_print .sh_sourceCode .sh_ip,.sh_print .sh_sourceCode .sh_name,.sh_print .sh_sourceCode .sh_oldfile,.sh_print .sh_sourceCode .sh_newfile,.sh_print .sh_sourceCode .sh_value,.sh_the .sh_sourceCode .sh_specialchar,.sh_the .sh_sourceCode .sh_function,.sh_the .sh_sourceCode .sh_oldfile,.sh_vim .sh_sourceCode .sh_function,.sh_whitengrey .sh_sourceCode .sh_function{color:#000;font-weight:400;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_function,.sh_berries-light .sh_sourceCode .sh_function{color:#901164;font-weight:700;font-style:normal;}.sh_berries-dark .sh_sourceCode .sh_variable,.sh_berries-dark .sh_sourceCode .sh_selector{color:#a91ea7;font-weight:400;font-style:normal;}.sh_berries-light .sh_sourceCode .sh_keyword,.sh_berries-light .sh_sourceCode .sh_date,.sh_berries-light .sh_sourceCode .sh_time,.sh_berries-light .sh_sourceCode .sh_file,.sh_berries-light .sh_sourceCode .sh_difflines,.sh_berries-light .sh_sourceCode .sh_property{color:#2cae1e;font-weight:700;font-style:normal;}.sh_berries-light .sh_sourceCode .sh_string,.sh_berries-light .sh_sourceCode .sh_regexp,.sh_berries-light .sh_sourceCode .sh_specialchar,.sh_berries-light .sh_sourceCode .sh_url,.sh_berries-light .sh_sourceCode .sh_ip,.sh_berries-light .sh_sourceCode .sh_name,.sh_berries-light .sh_sourceCode .sh_oldfile,.sh_berries-light .sh_sourceCode .sh_newfile,.sh_berries-light .sh_sourceCode .sh_value{color:#5f81b3;font-weight:400;font-style:normal;}.sh_berries-light .sh_sourceCode .sh_symbol,.sh_berries-light .sh_sourceCode .sh_cbracket{color:#d2073b;font-weight:400;font-style:normal;}.sh_berries-light .sh_sourceCode .sh_variable,.sh_berries-light .sh_sourceCode .sh_selector{color:#0628cb;font-weight:700;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_keyword,.sh_bipolar .sh_sourceCode .sh_date,.sh_bipolar .sh_sourceCode .sh_time,.sh_bipolar .sh_sourceCode .sh_file,.sh_bipolar .sh_sourceCode .sh_difflines,.sh_bipolar .sh_sourceCode .sh_property{color:#ee85e2;font-weight:700;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_string,.sh_bipolar .sh_sourceCode .sh_regexp,.sh_bipolar .sh_sourceCode .sh_specialchar,.sh_bipolar .sh_sourceCode .sh_url,.sh_bipolar .sh_sourceCode .sh_ip,.sh_bipolar .sh_sourceCode .sh_name,.sh_bipolar .sh_sourceCode .sh_oldfile,.sh_bipolar .sh_sourceCode .sh_newfile,.sh_bipolar .sh_sourceCode .sh_value{color:#9ef457;font-weight:400;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_number,.sh_golden .sh_sourceCode .sh_number,.sh_greenlcd .sh_sourceCode .sh_number,.sh_navy .sh_sourceCode .sh_string,.sh_navy .sh_sourceCode .sh_regexp,.sh_navy .sh_sourceCode .sh_specialchar,.sh_navy .sh_sourceCode .sh_symbol,.sh_navy .sh_sourceCode .sh_function,.sh_navy .sh_sourceCode .sh_cbracket,.sh_navy .sh_sourceCode .sh_url,.sh_navy .sh_sourceCode .sh_ip,.sh_navy .sh_sourceCode .sh_name,.sh_navy .sh_sourceCode .sh_oldfile,.sh_navy .sh_sourceCode .sh_newfile,.sh_navy .sh_sourceCode .sh_value,.sh_night .sh_sourceCode .sh_string,.sh_night .sh_sourceCode .sh_regexp,.sh_night .sh_sourceCode .sh_url,.sh_night .sh_sourceCode .sh_ip,.sh_night .sh_sourceCode .sh_name,.sh_night .sh_sourceCode .sh_newfile,.sh_night .sh_sourceCode .sh_value,.sh_vampire .sh_sourceCode .sh_specialchar,.sh_vampire .sh_sourceCode .sh_variable,.sh_vampire .sh_sourceCode .sh_oldfile,.sh_vampire .sh_sourceCode .sh_selector,.sh_vim-dark .sh_sourceCode .sh_function{color:#fff;font-weight:400;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_symbol,.sh_bipolar .sh_sourceCode .sh_cbracket{color:#348fef;font-weight:400;font-style:normal;}.sh_bipolar .sh_sourceCode .sh_variable,.sh_bipolar .sh_sourceCode .sh_selector{color:#72d42c;font-weight:700;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_keyword,.sh_blacknblue .sh_sourceCode .sh_date,.sh_blacknblue .sh_sourceCode .sh_time,.sh_blacknblue .sh_sourceCode .sh_file,.sh_blacknblue .sh_sourceCode .sh_difflines,.sh_blacknblue .sh_sourceCode .sh_property{color:#1ededc;font-weight:700;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_string,.sh_blacknblue .sh_sourceCode .sh_regexp,.sh_blacknblue .sh_sourceCode .sh_url,.sh_blacknblue .sh_sourceCode .sh_ip,.sh_blacknblue .sh_sourceCode .sh_name,.sh_blacknblue .sh_sourceCode .sh_newfile,.sh_blacknblue .sh_sourceCode .sh_value{color:#cfc631;font-weight:400;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_specialchar,.sh_blacknblue .sh_sourceCode .sh_symbol,.sh_blacknblue .sh_sourceCode .sh_cbracket,.sh_blacknblue .sh_sourceCode .sh_oldfile{color:#ccc6c6;font-weight:400;font-style:normal;}.sh_blacknblue .sh_sourceCode .sh_variable,.sh_blacknblue .sh_sourceCode .sh_selector{color:#ecea26;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_keyword,.sh_bright .sh_sourceCode .sh_date,.sh_bright .sh_sourceCode .sh_time,.sh_bright .sh_sourceCode .sh_file,.sh_bright .sh_sourceCode .sh_difflines,.sh_bright .sh_sourceCode .sh_property{color:#ff3030;font-weight:700;font-style:normal;}.sh_bright .sh_sourceCode .sh_string,.sh_bright .sh_sourceCode .sh_regexp,.sh_bright .sh_sourceCode .sh_specialchar,.sh_bright .sh_sourceCode .sh_url,.sh_bright .sh_sourceCode .sh_ip,.sh_bright .sh_sourceCode .sh_name,.sh_bright .sh_sourceCode .sh_oldfile,.sh_bright .sh_sourceCode .sh_newfile,.sh_bright .sh_sourceCode .sh_value{color:#1861a7;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_symbol,.sh_bright .sh_sourceCode .sh_cbracket{color:#3030ee;font-weight:400;font-style:normal;}.sh_bright .sh_sourceCode .sh_variable,.sh_bright .sh_sourceCode .sh_selector,.sh_emacs .sh_sourceCode .sh_variable,.sh_emacs .sh_sourceCode .sh_selector,.sh_ide-anjuta .sh_sourceCode .sh_variable,.sh_ide-anjuta .sh_sourceCode .sh_selector,.sh_ide-devcpp .sh_sourceCode .sh_number,.sh_ide-eclipse .sh_sourceCode .sh_string,.sh_ide-eclipse .sh_sourceCode .sh_regexp,.sh_ide-eclipse .sh_sourceCode .sh_specialchar,.sh_ide-eclipse .sh_sourceCode .sh_url,.sh_ide-eclipse .sh_sourceCode .sh_ip,.sh_ide-eclipse .sh_sourceCode .sh_name,.sh_ide-eclipse .sh_sourceCode .sh_oldfile,.sh_ide-eclipse .sh_sourceCode .sh_newfile,.sh_ide-eclipse .sh_sourceCode .sh_value,.sh_ide-kdev .sh_sourceCode .sh_number,.sh_ide-msvcpp .sh_sourceCode .sh_type,.sh_ide-msvcpp .sh_sourceCode .sh_preproc,.sh_matlab .sh_sourceCode .sh_keyword,.sh_matlab .sh_sourceCode .sh_date,.sh_matlab .sh_sourceCode .sh_time,.sh_matlab .sh_sourceCode .sh_file,.sh_matlab .sh_sourceCode .sh_variable,.sh_matlab .sh_sourceCode .sh_difflines,.sh_matlab .sh_sourceCode .sh_selector,.sh_matlab .sh_sourceCode .sh_property,.sh_pablo .sh_sourceCode .sh_specialchar,.sh_pablo .sh_sourceCode .sh_oldfile,.sh_the .sh_sourceCode .sh_keyword,.sh_the .sh_sourceCode .sh_date,.sh_the .sh_sourceCode .sh_time,.sh_the .sh_sourceCode .sh_file,.sh_the .sh_sourceCode .sh_variable,.sh_the .sh_sourceCode .sh_difflines,.sh_the .sh_sourceCode .sh_selector,.sh_the .sh_sourceCode .sh_property,.sh_typical .sh_sourceCode .sh_type,.sh_vim-dark .sh_sourceCode .sh_comment,.sh_vim .sh_sourceCode .sh_comment,.sh_zellner .sh_sourceCode .sh_type{color:#00f;font-weight:400;font-style:normal;}.sh_contrast .sh_sourceCode .sh_keyword,.sh_contrast .sh_sourceCode .sh_type,.sh_contrast .sh_sourceCode .sh_date,.sh_contrast .sh_sourceCode .sh_time,.sh_contrast .sh_sourceCode .sh_file,.sh_contrast .sh_sourceCode .sh_difflines,.sh_contrast .sh_sourceCode .sh_property,.sh_darkblue .sh_sourceCode .sh_function,.sh_neon .sh_sourceCode .sh_function,.sh_night .sh_sourceCode .sh_keyword,.sh_night .sh_sourceCode .sh_date,.sh_night .sh_sourceCode .sh_time,.sh_night .sh_sourceCode .sh_file,.sh_night .sh_sourceCode .sh_difflines,.sh_night .sh_sourceCode .sh_property,.sh_vampire .sh_sourceCode .sh_keyword,.sh_vampire .sh_sourceCode .sh_date,.sh_vampire .sh_sourceCode .sh_time,.sh_vampire .sh_sourceCode .sh_file,.sh_vampire .sh_sourceCode .sh_difflines,.sh_vampire .sh_sourceCode .sh_property,.sh_whatis .sh_sourceCode .sh_type{color:#fff;font-weight:700;font-style:normal;}.sh_contrast .sh_sourceCode .sh_string,.sh_contrast .sh_sourceCode .sh_regexp,.sh_contrast .sh_sourceCode .sh_specialchar,.sh_contrast .sh_sourceCode .sh_url,.sh_contrast .sh_sourceCode .sh_ip,.sh_contrast .sh_sourceCode .sh_name,.sh_contrast .sh_sourceCode .sh_oldfile,.sh_contrast .sh_sourceCode .sh_newfile,.sh_contrast .sh_sourceCode .sh_value{color:#11f80c;font-weight:400;font-style:normal;}.sh_contrast .sh_sourceCode .sh_symbol,.sh_contrast .sh_sourceCode .sh_cbracket,.sh_golden .sh_sourceCode .sh_variable,.sh_golden .sh_sourceCode .sh_selector{color:#dedede;font-weight:700;font-style:normal;}.sh_contrast .sh_sourceCode .sh_variable,.sh_contrast .sh_sourceCode .sh_selector{color:#11f80c;font-weight:700;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_keyword,.sh_darkblue .sh_sourceCode .sh_date,.sh_darkblue .sh_sourceCode .sh_time,.sh_darkblue .sh_sourceCode .sh_file,.sh_darkblue .sh_sourceCode .sh_difflines,.sh_darkblue .sh_sourceCode .sh_property{color:#ffff60;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_string,.sh_darkblue .sh_sourceCode .sh_regexp,.sh_darkblue .sh_sourceCode .sh_url,.sh_darkblue .sh_sourceCode .sh_ip,.sh_darkblue .sh_sourceCode .sh_name,.sh_darkblue .sh_sourceCode .sh_newfile,.sh_darkblue .sh_sourceCode .sh_value{color:#ffa0a0;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_specialchar,.sh_darkblue .sh_sourceCode .sh_oldfile{color:orange;font-weight:400;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_symbol,.sh_darkblue .sh_sourceCode .sh_cbracket{color:#d8e91b;font-weight:700;font-style:normal;}.sh_darkblue .sh_sourceCode .sh_variable,.sh_darkblue .sh_sourceCode .sh_selector,.sh_darkness .sh_sourceCode .sh_variable,.sh_darkness .sh_sourceCode .sh_selector{color:#26e0e7;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode,.sh_neon .sh_sourceCode,.sh_pablo .sh_sourceCode,.sh_vim-dark .sh_sourceCode{background-color:#000;color:#fff;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode .sh_keyword,.sh_darkness .sh_sourceCode .sh_date,.sh_darkness .sh_sourceCode .sh_time,.sh_darkness .sh_sourceCode .sh_file,.sh_darkness .sh_sourceCode .sh_difflines,.sh_darkness .sh_sourceCode .sh_property{color:#ff0;font-weight:700;font-style:normal;}.sh_darkness .sh_sourceCode .sh_type,.sh_pablo .sh_sourceCode .sh_preproc,.sh_vim-dark .sh_sourceCode .sh_type,.sh_vim .sh_sourceCode .sh_type{color:#0f0;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode .sh_string,.sh_darkness .sh_sourceCode .sh_regexp,.sh_darkness .sh_sourceCode .sh_url,.sh_darkness .sh_sourceCode .sh_ip,.sh_darkness .sh_sourceCode .sh_name,.sh_darkness .sh_sourceCode .sh_newfile,.sh_darkness .sh_sourceCode .sh_value{color:#abab00;font-weight:700;font-style:normal;}.sh_darkness .sh_sourceCode .sh_specialchar,.sh_darkness .sh_sourceCode .sh_oldfile,.sh_greenlcd .sh_sourceCode .sh_preproc,.sh_night .sh_sourceCode .sh_comment,.sh_whatis .sh_sourceCode .sh_preproc{color:#bfbfbf;font-weight:400;font-style:normal;}.sh_darkness .sh_sourceCode .sh_comment,.sh_greenlcd .sh_sourceCode .sh_comment{color:#888;font-weight:400;font-style:italic;}.sh_darkness .sh_sourceCode .sh_symbol,.sh_darkness .sh_sourceCode .sh_cbracket,.sh_kwrite .sh_sourceCode .sh_specialchar,.sh_kwrite .sh_sourceCode .sh_oldfile,.sh_neon .sh_sourceCode .sh_number,.sh_zellner .sh_sourceCode .sh_string,.sh_zellner .sh_sourceCode .sh_regexp,.sh_zellner .sh_sourceCode .sh_specialchar,.sh_zellner .sh_sourceCode .sh_number,.sh_zellner .sh_sourceCode .sh_url,.sh_zellner .sh_sourceCode .sh_ip,.sh_zellner .sh_sourceCode .sh_name,.sh_zellner .sh_sourceCode .sh_oldfile,.sh_zellner .sh_sourceCode .sh_newfile,.sh_zellner .sh_sourceCode .sh_value{color:#f0f;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_keyword,.sh_desert .sh_sourceCode .sh_date,.sh_desert .sh_sourceCode .sh_time,.sh_desert .sh_sourceCode .sh_file,.sh_desert .sh_sourceCode .sh_difflines,.sh_desert .sh_sourceCode .sh_property{color:#fef8bc;font-weight:700;font-style:normal;}.sh_desert .sh_sourceCode .sh_string,.sh_desert .sh_sourceCode .sh_regexp,.sh_desert .sh_sourceCode .sh_specialchar,.sh_desert .sh_sourceCode .sh_url,.sh_desert .sh_sourceCode .sh_ip,.sh_desert .sh_sourceCode .sh_name,.sh_desert .sh_sourceCode .sh_oldfile,.sh_desert .sh_sourceCode .sh_newfile,.sh_desert .sh_sourceCode .sh_value{color:#f6f647;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_symbol,.sh_desert .sh_sourceCode .sh_cbracket{color:#66574f;font-weight:400;font-style:normal;}.sh_desert .sh_sourceCode .sh_variable,.sh_desert .sh_sourceCode .sh_selector,.sh_ide-devcpp .sh_sourceCode .sh_keyword,.sh_ide-devcpp .sh_sourceCode .sh_date,.sh_ide-devcpp .sh_sourceCode .sh_time,.sh_ide-devcpp .sh_sourceCode .sh_file,.sh_ide-devcpp .sh_sourceCode .sh_variable,.sh_ide-devcpp .sh_sourceCode .sh_difflines,.sh_ide-devcpp .sh_sourceCode .sh_selector,.sh_ide-devcpp .sh_sourceCode .sh_property,.sh_kwrite .sh_sourceCode .sh_keyword,.sh_kwrite .sh_sourceCode .sh_date,.sh_kwrite .sh_sourceCode .sh_time,.sh_kwrite .sh_sourceCode .sh_file,.sh_kwrite .sh_sourceCode .sh_variable,.sh_kwrite .sh_sourceCode .sh_difflines,.sh_kwrite .sh_sourceCode .sh_selector,.sh_kwrite .sh_sourceCode .sh_property,.sh_nedit .sh_sourceCode .sh_keyword,.sh_nedit .sh_sourceCode .sh_symbol,.sh_nedit .sh_sourceCode .sh_cbracket,.sh_nedit .sh_sourceCode .sh_date,.sh_nedit .sh_sourceCode .sh_time,.sh_nedit .sh_sourceCode .sh_file,.sh_nedit .sh_sourceCode .sh_difflines,.sh_nedit .sh_sourceCode .sh_property,.sh_print .sh_sourceCode .sh_keyword,.sh_print .sh_sourceCode .sh_type,.sh_print .sh_sourceCode .sh_preproc,.sh_print .sh_sourceCode .sh_symbol,.sh_print .sh_sourceCode .sh_cbracket,.sh_print .sh_sourceCode .sh_date,.sh_print .sh_sourceCode .sh_time,.sh_print .sh_sourceCode .sh_file,.sh_print .sh_sourceCode .sh_variable,.sh_print .sh_sourceCode .sh_difflines,.sh_print .sh_sourceCode .sh_selector,.sh_print .sh_sourceCode .sh_property,.sh_rand01 .sh_sourceCode .sh_function,.sh_typical .sh_sourceCode .sh_function,.sh_zellner .sh_sourceCode .sh_function{color:#000;font-weight:700;font-style:normal;}.sh_dull .sh_sourceCode .sh_keyword,.sh_dull .sh_sourceCode .sh_date,.sh_dull .sh_sourceCode .sh_time,.sh_dull .sh_sourceCode .sh_file,.sh_dull .sh_sourceCode .sh_difflines,.sh_dull .sh_sourceCode .sh_property{color:#353535;font-weight:700;font-style:normal;}.sh_dull .sh_sourceCode .sh_string,.sh_dull .sh_sourceCode .sh_regexp,.sh_dull .sh_sourceCode .sh_specialchar,.sh_dull .sh_sourceCode .sh_url,.sh_dull .sh_sourceCode .sh_ip,.sh_dull .sh_sourceCode .sh_name,.sh_dull .sh_sourceCode .sh_oldfile,.sh_dull .sh_sourceCode .sh_newfile,.sh_dull .sh_sourceCode .sh_value{color:#059;font-weight:400;font-style:normal;}.sh_dull .sh_sourceCode .sh_symbol,.sh_dull .sh_sourceCode .sh_cbracket{color:#222;font-weight:400;font-style:normal;}.sh_dull .sh_sourceCode .sh_variable,.sh_dull .sh_sourceCode .sh_selector{color:#ae5a16;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode .sh_keyword,.sh_easter .sh_sourceCode .sh_date,.sh_easter .sh_sourceCode .sh_time,.sh_easter .sh_sourceCode .sh_file,.sh_easter .sh_sourceCode .sh_difflines,.sh_easter .sh_sourceCode .sh_property{color:#1d45d6;font-weight:700;font-style:normal;}.sh_easter .sh_sourceCode .sh_string,.sh_easter .sh_sourceCode .sh_regexp,.sh_easter .sh_sourceCode .sh_specialchar,.sh_easter .sh_sourceCode .sh_url,.sh_easter .sh_sourceCode .sh_ip,.sh_easter .sh_sourceCode .sh_name,.sh_easter .sh_sourceCode .sh_oldfile,.sh_easter .sh_sourceCode .sh_newfile,.sh_easter .sh_sourceCode .sh_value{color:#ca4be3;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode .sh_symbol,.sh_easter .sh_sourceCode .sh_cbracket{color:#fa4700;font-weight:400;font-style:normal;}.sh_easter .sh_sourceCode .sh_variable,.sh_easter .sh_sourceCode .sh_selector{color:#26aae7;font-weight:700;font-style:normal;}.sh_emacs .sh_sourceCode,.sh_ide-anjuta .sh_sourceCode,.sh_ide-codewarrior .sh_sourceCode,.sh_ide-devcpp .sh_sourceCode,.sh_ide-eclipse .sh_sourceCode,.sh_ide-kdev .sh_sourceCode,.sh_ide-msvcpp .sh_sourceCode,.sh_kwrite .sh_sourceCode,.sh_matlab .sh_sourceCode,.sh_nedit .sh_sourceCode,.sh_print .sh_sourceCode,.sh_the .sh_sourceCode,.sh_typical .sh_sourceCode,.sh_vim .sh_sourceCode,.sh_zellner .sh_sourceCode{background-color:#fff;color:#000;font-weight:400;font-style:normal;}.sh_emacs .sh_sourceCode .sh_keyword,.sh_emacs .sh_sourceCode .sh_date,.sh_emacs .sh_sourceCode .sh_time,.sh_emacs .sh_sourceCode .sh_file,.sh_emacs .sh_sourceCode .sh_difflines,.sh_emacs .sh_sourceCode .sh_property{color:#9c20ee;font-weight:700;font-style:normal;}.sh_emacs .sh_sourceCode .sh_string,.sh_emacs .sh_sourceCode .sh_regexp,.sh_emacs .sh_sourceCode .sh_specialchar,.sh_emacs .sh_sourceCode .sh_url,.sh_emacs .sh_sourceCode .sh_ip,.sh_emacs .sh_sourceCode .sh_name,.sh_emacs .sh_sourceCode .sh_oldfile,.sh_emacs .sh_sourceCode .sh_newfile,.sh_emacs .sh_sourceCode .sh_value{color:#bd8d8b;font-weight:400;font-style:normal;}.sh_golden .sh_sourceCode .sh_keyword,.sh_golden .sh_sourceCode .sh_date,.sh_golden .sh_sourceCode .sh_time,.sh_golden .sh_sourceCode .sh_file,.sh_golden .sh_sourceCode .sh_difflines,.sh_golden .sh_sourceCode .sh_property{color:#ffed8a;font-weight:700;font-style:normal;}.sh_golden .sh_sourceCode .sh_string,.sh_golden .sh_sourceCode .sh_regexp,.sh_golden .sh_sourceCode .sh_specialchar,.sh_golden .sh_sourceCode .sh_url,.sh_golden .sh_sourceCode .sh_ip,.sh_golden .sh_sourceCode .sh_name,.sh_golden .sh_sourceCode .sh_oldfile,.sh_golden .sh_sourceCode .sh_newfile,.sh_golden .sh_sourceCode .sh_value,.sh_ide-devcpp .sh_sourceCode .sh_string,.sh_ide-devcpp .sh_sourceCode .sh_regexp,.sh_ide-devcpp .sh_sourceCode .sh_specialchar,.sh_ide-devcpp .sh_sourceCode .sh_url,.sh_ide-devcpp .sh_sourceCode .sh_ip,.sh_ide-devcpp .sh_sourceCode .sh_name,.sh_ide-devcpp .sh_sourceCode .sh_oldfile,.sh_ide-devcpp .sh_sourceCode .sh_newfile,.sh_ide-devcpp .sh_sourceCode .sh_value,.sh_ide-kdev .sh_sourceCode .sh_string,.sh_ide-kdev .sh_sourceCode .sh_regexp,.sh_ide-kdev .sh_sourceCode .sh_specialchar,.sh_ide-kdev .sh_sourceCode .sh_url,.sh_ide-kdev .sh_sourceCode .sh_ip,.sh_ide-kdev .sh_sourceCode .sh_name,.sh_ide-kdev .sh_sourceCode .sh_oldfile,.sh_ide-kdev .sh_sourceCode .sh_newfile,.sh_ide-kdev .sh_sourceCode .sh_value,.sh_kwrite .sh_sourceCode .sh_string,.sh_kwrite .sh_sourceCode .sh_regexp,.sh_kwrite .sh_sourceCode .sh_url,.sh_kwrite .sh_sourceCode .sh_ip,.sh_kwrite .sh_sourceCode .sh_name,.sh_kwrite .sh_sourceCode .sh_newfile,.sh_kwrite .sh_sourceCode .sh_value,.sh_pablo .sh_sourceCode .sh_symbol,.sh_pablo .sh_sourceCode .sh_cbracket,.sh_the .sh_sourceCode .sh_number,.sh_the .sh_sourceCode .sh_preproc,.sh_typical .sh_sourceCode .sh_string,.sh_typical .sh_sourceCode .sh_regexp,.sh_typical .sh_sourceCode .sh_symbol,.sh_typical .sh_sourceCode .sh_cbracket,.sh_typical .sh_sourceCode .sh_url,.sh_typical .sh_sourceCode .sh_ip,.sh_typical .sh_sourceCode .sh_name,.sh_typical .sh_sourceCode .sh_newfile,.sh_typical .sh_sourceCode .sh_value,.sh_vim-dark .sh_sourceCode .sh_string,.sh_vim-dark .sh_sourceCode .sh_regexp,.sh_vim-dark .sh_sourceCode .sh_number,.sh_vim-dark .sh_sourceCode .sh_url,.sh_vim-dark .sh_sourceCode .sh_ip,.sh_vim-dark .sh_sourceCode .sh_name,.sh_vim-dark .sh_sourceCode .sh_newfile,.sh_vim-dark .sh_sourceCode .sh_value,.sh_vim .sh_sourceCode .sh_string,.sh_vim .sh_sourceCode .sh_regexp,.sh_vim .sh_sourceCode .sh_number,.sh_vim .sh_sourceCode .sh_url,.sh_vim .sh_sourceCode .sh_ip,.sh_vim .sh_sourceCode .sh_name,.sh_vim .sh_sourceCode .sh_newfile,.sh_vim .sh_sourceCode .sh_value,.sh_whatis .sh_sourceCode .sh_comment,.sh_zellner .sh_sourceCode .sh_comment{color:red;font-weight:400;font-style:normal;}.sh_golden .sh_sourceCode .sh_symbol,.sh_golden .sh_sourceCode .sh_cbracket,.sh_vampire .sh_sourceCode .sh_preproc{color:#ababab;font-weight:400;font-style:normal;}.sh_greenlcd .sh_sourceCode .sh_keyword,.sh_greenlcd .sh_sourceCode .sh_date,.sh_greenlcd .sh_sourceCode .sh_time,.sh_greenlcd .sh_sourceCode .sh_file,.sh_greenlcd .sh_sourceCode .sh_difflines,.sh_greenlcd .sh_sourceCode .sh_property{color:#00ed00;font-weight:700;font-style:normal;}.sh_greenlcd .sh_sourceCode .sh_string,.sh_greenlcd .sh_sourceCode .sh_regexp,.sh_greenlcd .sh_sourceCode .sh_specialchar,.sh_greenlcd .sh_sourceCode .sh_url,.sh_greenlcd .sh_sourceCode .sh_ip,.sh_greenlcd .sh_sourceCode .sh_name,.sh_greenlcd .sh_sourceCode .sh_oldfile,.sh_greenlcd .sh_sourceCode .sh_newfile,.sh_greenlcd .sh_sourceCode .sh_value{color:#dfdfdf;font-weight:400;font-style:normal;}.sh_greenlcd .sh_sourceCode .sh_symbol,.sh_greenlcd .sh_sourceCode .sh_cbracket{color:#2fe7a9;font-weight:400;font-style:normal;}.sh_greenlcd .sh_sourceCode .sh_variable,.sh_greenlcd .sh_sourceCode .sh_selector{color:#beef13;font-weight:400;font-style:normal;}.sh_ide-anjuta .sh_sourceCode .sh_keyword,.sh_ide-anjuta .sh_sourceCode .sh_date,.sh_ide-anjuta .sh_sourceCode .sh_time,.sh_ide-anjuta .sh_sourceCode .sh_file,.sh_ide-anjuta .sh_sourceCode .sh_difflines,.sh_ide-anjuta .sh_sourceCode .sh_property{color:navy;font-weight:700;font-style:normal;}.sh_ide-anjuta .sh_sourceCode .sh_string,.sh_ide-anjuta .sh_sourceCode .sh_regexp,.sh_ide-anjuta .sh_sourceCode .sh_specialchar,.sh_ide-anjuta .sh_sourceCode .sh_url,.sh_ide-anjuta .sh_sourceCode .sh_ip,.sh_ide-anjuta .sh_sourceCode .sh_name,.sh_ide-anjuta .sh_sourceCode .sh_oldfile,.sh_ide-anjuta .sh_sourceCode .sh_newfile,.sh_ide-anjuta .sh_sourceCode .sh_value{color:#db0;font-weight:400;font-style:normal;}.sh_ide-anjuta .sh_sourceCode .sh_number,.sh_whitengrey .sh_sourceCode .sh_string,.sh_whitengrey .sh_sourceCode .sh_regexp,.sh_whitengrey .sh_sourceCode .sh_specialchar,.sh_whitengrey .sh_sourceCode .sh_url,.sh_whitengrey .sh_sourceCode .sh_ip,.sh_whitengrey .sh_sourceCode .sh_name,.sh_whitengrey .sh_sourceCode .sh_oldfile,.sh_whitengrey .sh_sourceCode .sh_newfile,.sh_whitengrey .sh_sourceCode .sh_value{color:#080;font-weight:400;font-style:normal;}.sh_ide-codewarrior .sh_sourceCode .sh_keyword,.sh_ide-codewarrior .sh_sourceCode .sh_preproc,.sh_ide-codewarrior .sh_sourceCode .sh_date,.sh_ide-codewarrior .sh_sourceCode .sh_time,.sh_ide-codewarrior .sh_sourceCode .sh_file,.sh_ide-codewarrior .sh_sourceCode .sh_variable,.sh_ide-codewarrior .sh_sourceCode .sh_difflines,.sh_ide-codewarrior .sh_sourceCode .sh_selector,.sh_ide-codewarrior .sh_sourceCode .sh_property{color:#0000b3;font-weight:400;font-style:normal;}.sh_ide-codewarrior .sh_sourceCode .sh_string,.sh_ide-codewarrior .sh_sourceCode .sh_regexp,.sh_ide-codewarrior .sh_sourceCode .sh_specialchar,.sh_ide-codewarrior .sh_sourceCode .sh_url,.sh_ide-codewarrior .sh_sourceCode .sh_ip,.sh_ide-codewarrior .sh_sourceCode .sh_name,.sh_ide-codewarrior .sh_sourceCode .sh_oldfile,.sh_ide-codewarrior .sh_sourceCode .sh_newfile,.sh_ide-codewarrior .sh_sourceCode .sh_value{color:#666;font-weight:400;font-style:normal;}.sh_ide-devcpp .sh_sourceCode .sh_preproc,.sh_ide-kdev .sh_sourceCode .sh_preproc{color:green;font-weight:400;font-style:normal;}.sh_ide-eclipse .sh_sourceCode .sh_keyword,.sh_ide-eclipse .sh_sourceCode .sh_type,.sh_ide-eclipse .sh_sourceCode .sh_date,.sh_ide-eclipse .sh_sourceCode .sh_time,.sh_ide-eclipse .sh_sourceCode .sh_file,.sh_ide-eclipse .sh_sourceCode .sh_variable,.sh_ide-eclipse .sh_sourceCode .sh_difflines,.sh_ide-eclipse .sh_sourceCode .sh_selector,.sh_ide-eclipse .sh_sourceCode .sh_property{color:#7f0055;font-weight:700;font-style:normal;}.sh_ide-kdev .sh_sourceCode .sh_keyword,.sh_ide-kdev .sh_sourceCode .sh_date,.sh_ide-kdev .sh_sourceCode .sh_time,.sh_ide-kdev .sh_sourceCode .sh_file,.sh_ide-kdev .sh_sourceCode .sh_variable,.sh_ide-kdev .sh_sourceCode .sh_difflines,.sh_ide-kdev .sh_sourceCode .sh_selector,.sh_ide-kdev .sh_sourceCode .sh_property{color:#600000;font-weight:700;font-style:normal;}.sh_ide-msvcpp .sh_sourceCode .sh_keyword,.sh_ide-msvcpp .sh_sourceCode .sh_date,.sh_ide-msvcpp .sh_sourceCode .sh_time,.sh_ide-msvcpp .sh_sourceCode .sh_file,.sh_ide-msvcpp .sh_sourceCode .sh_variable,.sh_ide-msvcpp .sh_sourceCode .sh_difflines,.sh_ide-msvcpp .sh_sourceCode .sh_selector,.sh_ide-msvcpp .sh_sourceCode .sh_property,.sh_typical .sh_sourceCode .sh_keyword,.sh_typical .sh_sourceCode .sh_date,.sh_typical .sh_sourceCode .sh_time,.sh_typical .sh_sourceCode .sh_file,.sh_typical .sh_sourceCode .sh_difflines,.sh_typical .sh_sourceCode .sh_property{color:#00f;font-weight:700;font-style:normal;}.sh_matlab .sh_sourceCode .sh_string,.sh_matlab .sh_sourceCode .sh_regexp,.sh_matlab .sh_sourceCode .sh_url,.sh_matlab .sh_sourceCode .sh_ip,.sh_matlab .sh_sourceCode .sh_name,.sh_matlab .sh_sourceCode .sh_newfile,.sh_matlab .sh_sourceCode .sh_value{color:maroon;font-weight:400;font-style:normal;}.sh_navy .sh_sourceCode .sh_keyword,.sh_navy .sh_sourceCode .sh_date,.sh_navy .sh_sourceCode .sh_time,.sh_navy .sh_sourceCode .sh_file,.sh_navy .sh_sourceCode .sh_difflines,.sh_navy .sh_sourceCode .sh_property{color:#f8c50b;font-weight:700;font-style:normal;}.sh_navy .sh_sourceCode .sh_preproc,.sh_vampire .sh_sourceCode .sh_string,.sh_vampire .sh_sourceCode .sh_regexp,.sh_vampire .sh_sourceCode .sh_number,.sh_vampire .sh_sourceCode .sh_url,.sh_vampire .sh_sourceCode .sh_ip,.sh_vampire .sh_sourceCode .sh_name,.sh_vampire .sh_sourceCode .sh_newfile,.sh_vampire .sh_sourceCode .sh_value,.sh_whitengrey .sh_sourceCode .sh_number{color:#b0f;font-weight:400;font-style:normal;}.sh_navy .sh_sourceCode .sh_variable,.sh_navy .sh_sourceCode .sh_selector{color:#13d8ef;font-weight:400;font-style:normal;}.sh_nedit .sh_sourceCode .sh_type,.sh_peachpuff .sh_sourceCode .sh_keyword,.sh_peachpuff .sh_sourceCode .sh_date,.sh_peachpuff .sh_sourceCode .sh_time,.sh_peachpuff .sh_sourceCode .sh_file,.sh_peachpuff .sh_sourceCode .sh_difflines,.sh_peachpuff .sh_sourceCode .sh_property{color:#a52a2a;font-weight:700;font-style:normal;}.sh_nedit .sh_sourceCode .sh_string,.sh_nedit .sh_sourceCode .sh_regexp,.sh_nedit .sh_sourceCode .sh_number,.sh_nedit .sh_sourceCode .sh_url,.sh_nedit .sh_sourceCode .sh_ip,.sh_nedit .sh_sourceCode .sh_name,.sh_nedit .sh_sourceCode .sh_newfile,.sh_nedit .sh_sourceCode .sh_value{color:#006400;font-weight:400;font-style:normal;}.sh_nedit .sh_sourceCode .sh_specialchar,.sh_nedit .sh_sourceCode .sh_oldfile{color:#2e8b57;font-weight:400;font-style:normal;}.sh_nedit .sh_sourceCode .sh_variable,.sh_nedit .sh_sourceCode .sh_selector{color:#dda0dd;font-weight:700;font-style:normal;}.sh_neon .sh_sourceCode .sh_keyword,.sh_neon .sh_sourceCode .sh_date,.sh_neon .sh_sourceCode .sh_time,.sh_neon .sh_sourceCode .sh_file,.sh_neon .sh_sourceCode .sh_difflines,.sh_neon .sh_sourceCode .sh_property{color:#0ff;font-weight:700;font-style:normal;}.sh_neon .sh_sourceCode .sh_type,.sh_whatis .sh_sourceCode .sh_number{color:#ff0;font-weight:400;font-style:normal;}.sh_neon .sh_sourceCode .sh_string,.sh_neon .sh_sourceCode .sh_regexp,.sh_neon .sh_sourceCode .sh_specialchar,.sh_neon .sh_sourceCode .sh_url,.sh_neon .sh_sourceCode .sh_ip,.sh_neon .sh_sourceCode .sh_name,.sh_neon .sh_sourceCode .sh_oldfile,.sh_neon .sh_sourceCode .sh_newfile,.sh_neon .sh_sourceCode .sh_value{color:#cd00ff;font-weight:400;font-style:normal;}.sh_neon .sh_sourceCode .sh_comment,.sh_the .sh_sourceCode .sh_comment{color:#0f0;font-weight:400;font-style:italic;}.sh_neon .sh_sourceCode .sh_preproc,.sh_whatis .sh_sourceCode .sh_string,.sh_whatis .sh_sourceCode .sh_regexp,.sh_whatis .sh_sourceCode .sh_specialchar,.sh_whatis .sh_sourceCode .sh_url,.sh_whatis .sh_sourceCode .sh_ip,.sh_whatis .sh_sourceCode .sh_name,.sh_whatis .sh_sourceCode .sh_oldfile,.sh_whatis .sh_sourceCode .sh_newfile,.sh_whatis .sh_sourceCode .sh_value{color:#fb0;font-weight:400;font-style:normal;}.sh_neon .sh_sourceCode .sh_symbol,.sh_neon .sh_sourceCode .sh_cbracket{color:#ee5896;font-weight:700;font-style:normal;}.sh_neon .sh_sourceCode .sh_variable,.sh_neon .sh_sourceCode .sh_selector{color:#ef1347;font-weight:700;font-style:normal;}.sh_night .sh_sourceCode .sh_specialchar,.sh_night .sh_sourceCode .sh_oldfile{color:#82d66d;font-weight:400;font-style:normal;}.sh_night .sh_sourceCode .sh_symbol,.sh_night .sh_sourceCode .sh_cbracket{color:#e7ee5c;font-weight:700;font-style:normal;}.sh_night .sh_sourceCode .sh_variable,.sh_night .sh_sourceCode .sh_selector{color:#7aec27;font-weight:700;font-style:normal;}.sh_pablo .sh_sourceCode .sh_keyword,.sh_pablo .sh_sourceCode .sh_date,.sh_pablo .sh_sourceCode .sh_time,.sh_pablo .sh_sourceCode .sh_file,.sh_pablo .sh_sourceCode .sh_difflines,.sh_pablo .sh_sourceCode .sh_property{color:#c0c000;font-weight:700;font-style:normal;}.sh_pablo .sh_sourceCode .sh_string,.sh_pablo .sh_sourceCode .sh_regexp,.sh_pablo .sh_sourceCode .sh_number,.sh_pablo .sh_sourceCode .sh_url,.sh_pablo .sh_sourceCode .sh_ip,.sh_pablo .sh_sourceCode .sh_name,.sh_pablo .sh_sourceCode .sh_newfile,.sh_pablo .sh_sourceCode .sh_value,.sh_whatis .sh_sourceCode .sh_symbol,.sh_whatis .sh_sourceCode .sh_cbracket{color:#0ff;font-weight:400;font-style:normal;}.sh_pablo .sh_sourceCode .sh_variable,.sh_pablo .sh_sourceCode .sh_selector{color:#0000c0;font-weight:700;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_string,.sh_peachpuff .sh_sourceCode .sh_regexp,.sh_peachpuff .sh_sourceCode .sh_number,.sh_peachpuff .sh_sourceCode .sh_url,.sh_peachpuff .sh_sourceCode .sh_ip,.sh_peachpuff .sh_sourceCode .sh_name,.sh_peachpuff .sh_sourceCode .sh_newfile,.sh_peachpuff .sh_sourceCode .sh_value{color:#c00058;font-weight:400;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_specialchar,.sh_peachpuff .sh_sourceCode .sh_oldfile{color:#6a5acd;font-weight:400;font-style:normal;}.sh_peachpuff .sh_sourceCode .sh_variable,.sh_peachpuff .sh_sourceCode .sh_selector{color:#275fec;font-weight:700;font-style:normal;}.sh_print .sh_sourceCode .sh_comment,.sh_typical .sh_sourceCode .sh_comment{color:#666;font-weight:400;font-style:italic;}.sh_rand01 .sh_sourceCode .sh_keyword,.sh_rand01 .sh_sourceCode .sh_date,.sh_rand01 .sh_sourceCode .sh_time,.sh_rand01 .sh_sourceCode .sh_file,.sh_rand01 .sh_sourceCode .sh_difflines,.sh_rand01 .sh_sourceCode .sh_property{color:#0a7f6d;font-weight:700;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_string,.sh_rand01 .sh_sourceCode .sh_regexp,.sh_rand01 .sh_sourceCode .sh_url,.sh_rand01 .sh_sourceCode .sh_ip,.sh_rand01 .sh_sourceCode .sh_name,.sh_rand01 .sh_sourceCode .sh_newfile,.sh_rand01 .sh_sourceCode .sh_value{color:#2b83ba;font-weight:400;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_specialchar,.sh_rand01 .sh_sourceCode .sh_oldfile{color:#a764cb;font-weight:400;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_comment,.sh_vampire .sh_sourceCode .sh_comment{color:#ababab;font-weight:400;font-style:italic;}.sh_rand01 .sh_sourceCode .sh_symbol,.sh_rand01 .sh_sourceCode .sh_cbracket{color:#0000de;font-weight:400;font-style:normal;}.sh_rand01 .sh_sourceCode .sh_variable,.sh_rand01 .sh_sourceCode .sh_selector{color:#e12f76;font-weight:700;font-style:normal;}.sh_the .sh_sourceCode .sh_string,.sh_the .sh_sourceCode .sh_regexp,.sh_the .sh_sourceCode .sh_url,.sh_the .sh_sourceCode .sh_ip,.sh_the .sh_sourceCode .sh_name,.sh_the .sh_sourceCode .sh_newfile,.sh_the .sh_sourceCode .sh_value{color:#008;font-weight:400;font-style:normal;}.sh_typical .sh_sourceCode .sh_specialchar,.sh_typical .sh_sourceCode .sh_oldfile{color:#C42DA8;font-weight:400;font-style:normal;}.sh_typical .sh_sourceCode .sh_variable,.sh_typical .sh_sourceCode .sh_selector{color:#ec7f15;font-weight:400;font-style:normal;}.sh_vampire .sh_sourceCode .sh_symbol,.sh_vampire .sh_sourceCode .sh_cbracket{color:#F3E651;font-weight:400;font-style:normal;}.sh_vim-dark .sh_sourceCode .sh_keyword,.sh_vim-dark .sh_sourceCode .sh_date,.sh_vim-dark .sh_sourceCode .sh_time,.sh_vim-dark .sh_sourceCode .sh_file,.sh_vim-dark .sh_sourceCode .sh_variable,.sh_vim-dark .sh_sourceCode .sh_difflines,.sh_vim-dark .sh_sourceCode .sh_selector,.sh_vim-dark .sh_sourceCode .sh_property,.sh_vim .sh_sourceCode .sh_keyword,.sh_vim .sh_sourceCode .sh_date,.sh_vim .sh_sourceCode .sh_time,.sh_vim .sh_sourceCode .sh_file,.sh_vim .sh_sourceCode .sh_variable,.sh_vim .sh_sourceCode .sh_difflines,.sh_vim .sh_sourceCode .sh_selector,.sh_vim .sh_sourceCode .sh_property{color:#B26818;font-weight:400;font-style:normal;}.sh_vim-dark .sh_sourceCode .sh_specialchar,.sh_vim-dark .sh_sourceCode .sh_preproc,.sh_vim-dark .sh_sourceCode .sh_oldfile,.sh_vim .sh_sourceCode .sh_specialchar,.sh_vim .sh_sourceCode .sh_preproc,.sh_vim .sh_sourceCode .sh_oldfile{color:#f2f;font-weight:400;font-style:normal;}.sh_whatis .sh_sourceCode .sh_keyword,.sh_whatis .sh_sourceCode .sh_date,.sh_whatis .sh_sourceCode .sh_time,.sh_whatis .sh_sourceCode .sh_file,.sh_whatis .sh_sourceCode .sh_difflines,.sh_whatis .sh_sourceCode .sh_property{color:#fa5a03;font-weight:700;font-style:normal;}.sh_whatis .sh_sourceCode .sh_variable,.sh_whatis .sh_sourceCode .sh_selector{color:#efe219;font-weight:700;font-style:normal;}.sh_whitengrey .sh_sourceCode .sh_keyword,.sh_whitengrey .sh_sourceCode .sh_date,.sh_whitengrey .sh_sourceCode .sh_time,.sh_whitengrey .sh_sourceCode .sh_file,.sh_whitengrey .sh_sourceCode .sh_variable,.sh_whitengrey .sh_sourceCode .sh_difflines,.sh_whitengrey .sh_sourceCode .sh_selector,.sh_whitengrey .sh_sourceCode .sh_property{color:#696969;font-weight:700;font-style:normal;}.sh_zellner .sh_sourceCode .sh_keyword,.sh_zellner .sh_sourceCode .sh_date,.sh_zellner .sh_sourceCode .sh_time,.sh_zellner .sh_sourceCode .sh_file,.sh_zellner .sh_sourceCode .sh_difflines,.sh_zellner .sh_sourceCode .sh_property{color:#a52a2a;font-weight:400;font-style:normal;}.sh_zellner .sh_sourceCode .sh_variable,.sh_zellner .sh_sourceCode .sh_selector{color:#225f2d;font-weight:400;font-style:normal;} + +.snippet-wrap {position:relative;} +*:first-child+html .snippet-wrap {display:inline-block;} +* html .snippet-wrap {display:inline-block;} +.snippet-reveal{text-decoration:underline;} +.snippet-wrap .snippet-menu, .snippet-wrap .snippet-hide {position:absolute; top:10px; right:15px; font-size:.9em;z-index:1;background-color:transparent;} +.snippet-wrap .snippet-hide {top:auto; bottom:10px;} +*:first-child+html .snippet-wrap .snippet-hide {bottom:25px;} +* html .snippet-wrap .snippet-hide {bottom:25px;} +.snippet-wrap .snippet-menu pre, .snippet-wrap .snippet-hide pre {background-color:transparent; margin:0; padding:0;} +.snippet-wrap .snippet-menu a, .snippet-wrap .snippet-hide a {padding:0 5px; text-decoration:underline;} +.snippet-wrap pre.sh_sourceCode{padding:1em;line-height:1.8em;overflow:auto;position:relative; +-moz-border-radius:15px; +-webkit-border-radius:15px; +border-radius:15px; +box-shadow: 2px 2px 5px #000; +-moz-box-shadow: 2px 2px 5px #000; +-webkit-box-shadow: 2px 2px 5px #000;} +.snippet-wrap pre.snippet-textonly {padding:2em;} +*:first-child+html .snippet-wrap pre.snippet-formatted {padding:2em 1em;} +* html .snippet-wrap pre.snippet-formatted {padding:2em 1em;} +.snippet-reveal pre.sh_sourceCode {padding:.5em 1em; text-align:right;} +.snippet-wrap .snippet-num li{padding-left:1.5em;} +.snippet-wrap .snippet-no-num{list-style:none; padding:.6em 1em; margin:0;} +.snippet-wrap .snippet-no-num li {list-style:none; padding-left:0;} +.snippet-wrap .snippet-num {margin:1em 0 1em 1em; padding-left:3em;} +.snippet-wrap .snippet-num li {list-style:decimal-leading-zero outside none;} +.snippet-wrap .snippet-no-num li.box {padding:0 6px; margin-left:-6px;} +.snippet-wrap .snippet-num li.box {border:1px solid; list-style-position:inside; margin-left:-3em; padding-left:6px;} +*:first-child+html .snippet-wrap .snippet-num li.box {margin-left:-2.4em;} +* html .snippet-wrap .snippet-num li.box {margin-left:-2.4em;} +.snippet-wrap li.box-top {border-width:1px 1px 0 !important;} +.snippet-wrap li.box-bot {border-width:0 1px 1px !important;} +.snippet-wrap li.box-mid {border-width:0 1px !important;} +.snippet-wrap .snippet-num li .box-sp {width:18px; display:inline-block;} +*:first-child+html .snippet-wrap .snippet-num li .box-sp {width:27px;} +* html .snippet-wrap .snippet-num li .box-sp {width:27px;} +.snippet-wrap .snippet-no-num li.box {border:1px solid;} +.snippet-wrap .snippet-no-num li .box-sp {display:none;} \ No newline at end of file diff --git a/examples/resources/jquery.snippet.min.js b/examples/resources/jquery.snippet.min.js new file mode 100755 index 000000000..cf840643f --- /dev/null +++ b/examples/resources/jquery.snippet.min.js @@ -0,0 +1,12 @@ +/* + * Snippet :: jQuery Syntax Highlighter v2.0.0 + * http://steamdev.com/snippet + * + * Copyright 2011, SteamDev + * Released under the MIT license. + * http://www.opensource.org/licenses/mit-license.php + * + * Date: Wed Jan 19, 2011 + */ + +(function(a){window.log=function(){log.history=log.history||[];log.history.push(arguments);if(this.console){console.log(Array.prototype.slice.call(arguments))}};a.fn.snippet=function(e,c){if(typeof e=="object"){c=e}if(typeof e=="string"){e=e.toLowerCase()}var d={style:"random",showNum:true,transparent:false,collapse:false,menu:true,showMsg:"Expand Code",hideMsg:"Collapse Code",clipboard:"",startCollapsed:true,startText:false,box:"",boxColor:"",boxFill:""};var b=["acid","berries-dark","berries-light","bipolar","blacknblue","bright","contrast","darkblue","darkness","desert","dull","easter","emacs","golden","greenlcd","ide-anjuta","ide-codewarrior","ide-devcpp","ide-eclipse","ide-kdev","ide-msvcpp","kwrite","matlab","navy","nedit","neon","night","pablo","peachpuff","print","rand01","the","typical","vampire","vim","vim-dark","whatis","whitengrey","zellner"];if(c){a.extend(d,c)}return this.each(function(){var H=d.style.toLowerCase();if(d.style=="random"){var D=Math.floor(Math.random()*(b.length));H=b[D]}var u=a(this);var y=this.nodeName.toLowerCase();if(y=="pre"){if(u.data("orgHtml")==undefined||u.data("orgHtml")==null){var f=u.html();u.data("orgHtml",f)}if(!u.parent().hasClass("snippet-wrap")){if(typeof e!="string"){if(u.attr("class").length>0){var t=' class="'+u.attr("class")+'"'}else{var t=""}if(u.attr("id").length>0){var J=' id="'+u.attr("id")+'"'}else{var J=""}var A="Snippet Error: You must specify a language on inital usage of Snippet. Reference ";console.log(A);return false}u.addClass("sh_"+e).addClass("snippet-formatted").wrap("
");u.removeAttr("style");sh_highlightDocument();if(d.showNum){var v=u.html();v=v.replace(/\n/g,"
  • ");v="
    1. "+v+"
    ";while(v.indexOf("
  • ")!=-1){v=v.replace("
  • ","")}}else{var v=u.html();v=v.replace(/\n/g,"
  • ");v="
    • "+v+"
    ";while(v.indexOf("
  • ")!=-1){v=v.replace("
  • ","")}}v=v.replace(/\t/g,"    ");u.html(v);while(u.find("li").eq(0).html()==""){u.find("li").eq(0).remove()}u.find("li").each(function(){if(a(this).html().length<2){var i=(a(this).html()).replace(/\s/g,"");if(i==""){if(a.browser.opera){a(this).html(" ")}else{a(this).html(" ")}}}});var w="";var r="";u.parent().append(w);u.parent().prepend(r);u.parent().hover(function(){a(this).find(".snippet-menu").fadeIn("fast")},function(){a(this).find(".snippet-menu").fadeOut("fast")});if(d.clipboard!=""&&d.clipboard!=false){var j=u.parent().find("a.snippet-copy");j.show();j.parents(".snippet-menu").show();var s=u.parents(".snippet-wrap").find(".snippet-textonly").text();ZeroClipboard.setMoviePath(d.clipboard);var G=new ZeroClipboard.Client();G.setText(s);G.glue(j[0],j.parents(".snippet-menu")[0]);G.addEventListener("complete",function(i,o){if(o.length>500){o=o.substr(0,500)+"...\n\n("+(o.length-500)+" characters not shown)"}alert("Copied text to clipboard:\n\n "+o)});j.parents(".snippet-menu").hide()}else{u.parent().find("a.snippet-copy").hide()}u.parent().find("a.snippet-text").click(function(){var o=a(this).parents(".snippet-wrap").find(".snippet-formatted");var i=a(this).parents(".snippet-wrap").find(".snippet-textonly");o.toggle();i.toggle();if(i.is(":visible")){a(this).html("html")}else{a(this).html("text")}a(this).blur();return false});u.parent().find("a.snippet-window").click(function(){var i=a(this).parents(".snippet-wrap").find(".snippet-textonly").html();snippetPopup(i);a(this).blur();return false});if(!d.menu){u.prev(".snippet-menu").find("pre,.snippet-clipboard").hide()}if(d.collapse){var n=u.parent().attr("class");var h="";var E="";u.parents(".snippet-container").append(h);u.parent().append(E);var z=u.parents(".snippet-container");if(d.startCollapsed){z.find(".snippet-reveal").show();z.find(".snippet-wrap").eq(0).hide()}else{z.find(".snippet-reveal").hide();z.find(".snippet-wrap").eq(0).show()}z.find("a.snippet-toggle").click(function(){z.find(".snippet-wrap").toggle();return false})}if(d.transparent){var k={"background-color":"transparent","box-shadow":"none","-moz-box-shadow":"none","-webkit-box-shadow":"none"};u.css(k);u.next(".snippet-textonly").css(k);u.parents(".snippet-container").find(".snippet-reveal pre").css(k)}if(d.startText){u.hide();u.next(".snippet-textonly").show();u.parent().find(".snippet-text").html("html")}if(d.box!=""){var m=" ";var C=d.box.split(",");for(var B=0;B");var q=u.find("li").eq(0);q.unwrap()}}else{var F=u.find("li").eq(0).parent();if(F.hasClass("snippet-num")){F.wrap("
      ");var q=u.find("li").eq(0);q.unwrap()}}if(d.box!=""){var m=" ";var C=d.box.split(",");for(var B=0;B' elements are currently unsupported.";console.log(A);return false}})}})(jQuery);function snippetPopup(a){top.consoleRef=window.open("","myconsole","width=600,height=300,left=50,top=50,menubar=0,toolbar=0,location=0,status=0,scrollbars=1,resizable=1");top.consoleRef.document.writeln("Snippet :: Code View :: "+location.href+'
      '+a+"
      ");top.consoleRef.document.close()}var ZeroClipboard={version:"1.0.7",clients:{},moviePath:"ZeroClipboard.swf",nextId:1,$:function(a){if(typeof(a)=="string"){a=document.getElementById(a)}if(!a.addClass){a.hide=function(){this.style.display="none"};a.show=function(){this.style.display=""};a.addClass=function(b){this.removeClass(b);this.className+=" "+b};a.removeClass=function(d){var e=this.className.split(/\s+/);var b=-1;for(var c=0;c-1){e.splice(b,1);this.className=e.join(" ")}return this};a.hasClass=function(b){return !!this.className.match(new RegExp("\\s*"+b+"\\s*"))}}return a},setMoviePath:function(a){this.moviePath=a},dispatch:function(d,b,c){var a=this.clients[d];if(a){a.receiveEvent(b,c)}},register:function(b,a){this.clients[b]=a},getDOMObjectPosition:function(c,a){var b={left:0,top:0,width:c.width?c.width:c.offsetWidth,height:c.height?c.height:c.offsetHeight};while(c&&(c!=a)){b.left+=c.offsetLeft;b.top+=c.offsetTop;c=c.offsetParent}return b},Client:function(a){this.handlers={};this.id=ZeroClipboard.nextId++;this.movieId="ZeroClipboardMovie_"+this.id;ZeroClipboard.register(this.id,this);if(a){this.glue(a)}}};ZeroClipboard.Client.prototype={id:0,ready:false,movie:null,clipText:"",handCursorEnabled:true,cssEffects:true,handlers:null,glue:function(d,b,e){this.domElement=ZeroClipboard.$(d);var f=99;if(this.domElement.style.zIndex){f=parseInt(this.domElement.style.zIndex,10)+1}if(typeof(b)=="string"){b=ZeroClipboard.$(b)}else{if(typeof(b)=="undefined"){b=document.getElementsByTagName("body")[0]}}var c=ZeroClipboard.getDOMObjectPosition(this.domElement,b);this.div=document.createElement("div");this.div.className="snippet-clipboard";var a=this.div.style;a.position="absolute";a.left=""+c.left+"px";a.top=""+c.top+"px";a.width=""+c.width+"px";a.height=""+c.height+"px";a.zIndex=f;if(typeof(e)=="object"){for(addedStyle in e){a[addedStyle]=e[addedStyle]}}b.appendChild(this.div);this.div.innerHTML=this.getHTML(c.width,c.height)},getHTML:function(d,a){var c="";var b="id="+this.id+"&width="+d+"&height="+a;if(navigator.userAgent.match(/MSIE/)){var e=location.href.match(/^https/i)?"https://":"http://";c+=''}else{c+=''}return c},hide:function(){if(this.div){this.div.style.left="-2000px"}},show:function(){this.reposition()},destroy:function(){if(this.domElement&&this.div){this.hide();this.div.innerHTML="";var a=document.getElementsByTagName("body")[0];try{a.removeChild(this.div)}catch(b){}this.domElement=null;this.div=null}},reposition:function(c){if(c){this.domElement=ZeroClipboard.$(c);if(!this.domElement){this.hide()}}if(this.domElement&&this.div){var b=ZeroClipboard.getDOMObjectPosition(this.domElement);var a=this.div.style;a.left=""+b.left+"px";a.top=""+b.top+"px"}},setText:function(a){this.clipText=a;if(this.ready){this.movie.setText(a)}},addEventListener:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");if(!this.handlers[a]){this.handlers[a]=[]}this.handlers[a].push(b)},setHandCursor:function(a){this.handCursorEnabled=a;if(this.ready){this.movie.setHandCursor(a)}},setCSSEffects:function(a){this.cssEffects=!!a},receiveEvent:function(d,f){d=d.toString().toLowerCase().replace(/^on/,"");switch(d){case"load":this.movie=document.getElementById(this.movieId);if(!this.movie){var c=this;setTimeout(function(){c.receiveEvent("load",null)},1);return}if(!this.ready&&navigator.userAgent.match(/Firefox/)&&navigator.userAgent.match(/Windows/)){var c=this;setTimeout(function(){c.receiveEvent("load",null)},100);this.ready=true;return}this.ready=true;try{this.movie.setText(this.clipText)}catch(h){}try{this.movie.setHandCursor(this.handCursorEnabled)}catch(h){}break;case"mouseover":if(this.domElement&&this.cssEffects){this.domElement.addClass("hover");if(this.recoverActive){this.domElement.addClass("active")}}break;case"mouseout":if(this.domElement&&this.cssEffects){this.recoverActive=false;if(this.domElement.hasClass("active")){this.domElement.removeClass("active");this.recoverActive=true}this.domElement.removeClass("hover")}break;case"mousedown":if(this.domElement&&this.cssEffects){this.domElement.addClass("active")}break;case"mouseup":if(this.domElement&&this.cssEffects){this.domElement.removeClass("active");this.recoverActive=false}break}if(this.handlers[d]){for(var b=0,a=this.handlers[d].length;b=2&&f.charAt(0)==="<"&&f.charAt(f.length-1)===">"){f=f.substr(1,f.length-2)}if(sh_isEmailAddress(f)){f="mailto:"+f}e[h-2].node.href=f}function sh_konquerorExec(c){var d=[""];d.index=c.length;d.input=c;return d}function sh_highlightString(X,ah){if(/Konqueror/.test(navigator.userAgent)){if(!ah.konquered){for(var T=0;TQ){ab(ao.substring(Q,U.index),null)}var aq=a[ae];var P=aq[1];var au;if(P instanceof Array){for(var r=0;r0){var h=f.split(" ");for(var j=0;j0){g.push(h[j])}}}return g}function sh_addClass(h,f){var g=sh_getClasses(h);for(var e=0;e element with class="'+a+'", but no such language exists');continue}}break}}}if(!this.sh_languages){this.sh_languages={}}sh_languages.c=[[[/\/\/\//g,"sh_comment",1],[/\/\//g,"sh_comment",7],[/\/\*\*/g,"sh_comment",8],[/\/\*/g,"sh_comment",9],[/(\bstruct)([ \t]+)([A-Za-z0-9_]+)/g,["sh_keyword","sh_normal","sh_classname"],-1],[/^[ \t]*#(?:[ \t]*include)/g,"sh_preproc",10,1],[/^[ \t]*#(?:[ \t]*[A-Za-z0-9_]*)/g,"sh_preproc",-1],[/\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,"sh_number",-1],[/"/g,"sh_string",13],[/'/g,"sh_string",14],[/\b(?:__asm|__cdecl|__declspec|__export|__far16|__fastcall|__fortran|__import|__pascal|__rtti|__stdcall|_asm|_cdecl|__except|_export|_far16|_fastcall|__finally|_fortran|_import|_pascal|_stdcall|__thread|__try|asm|auto|break|case|catch|cdecl|const|continue|default|do|else|enum|extern|for|goto|if|pascal|register|return|sizeof|static|struct|switch|typedef|union|volatile|while)\b/g,"sh_keyword",-1],[/\b(?:bool|char|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/g,"sh_type",-1],[/~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g,"sh_symbol",-1],[/\{|\}/g,"sh_cbracket",-1],[/(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g,"sh_function",-1],[/([A-Za-z](?:[^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]|[_])*)((?:<.*>)?)(\s+(?=[*&]*[A-Za-z][^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]*\s*[`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\[\]]+))/g,["sh_usertype","sh_usertype","sh_normal"],-1]],[[/$/g,null,-2],[/(?:?)|(?:?)/g,"sh_url",-1],[/<\?xml/g,"sh_preproc",2,1],[//g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g,"sh_keyword",6,1],[/&(?:[A-Za-z0-9]+);/g,"sh_preproc",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*/g,"sh_keyword",6,1],[/@[A-Za-z]+/g,"sh_type",-1],[/(?:TODO|FIXME|BUG)(?:[:]?)/g,"sh_todo",-1]],[[/\?>/g,"sh_preproc",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/\\(?:\\|")/g,null,-1],[/"/g,"sh_string",-2]],[[/>/g,"sh_preproc",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/-->/g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[/ + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/_002_minimal/readme.html b/examples/_002_minimal/readme.html index 6df5e42a5..e54a3f873 100755 --- a/examples/_002_minimal/readme.html +++ b/examples/_002_minimal/readme.html @@ -3,7 +3,7 @@ Luracast Restler 2.0 Example:- Minimal Example - + @@ -23,22 +23,31 @@ }); }) + + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/_003_multiformat/readme.html b/examples/_003_multiformat/readme.html index 5755f6cae..c2c7f927e 100755 --- a/examples/_003_multiformat/readme.html +++ b/examples/_003_multiformat/readme.html @@ -3,7 +3,7 @@ Luracast Restler 2.0 Example:- Multi-format - + @@ -23,22 +23,31 @@ }); }) + + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/_004_error_response/index.php b/examples/_004_error_response/index.php index 9367c9217..8a3415cd7 100755 --- a/examples/_004_error_response/index.php +++ b/examples/_004_error_response/index.php @@ -5,11 +5,11 @@ Description: API methods can make use of RestException class to provide error information to the user. - use `throw new RestException($http_status_code)` to send the error response + use `throw new RestException($httpStatusCode)` to send the error response to the client. For the list of HTTP Status codes and their meaning take a look at - [Wikipedia](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes). + [Wikipedia](http://en.wikipedia.org/wiki/List_of_httpStatusCodes). Example 1: GET currency/format returns diff --git a/examples/_004_error_response/readme.html b/examples/_004_error_response/readme.html index 348f80669..70a625269 100755 --- a/examples/_004_error_response/readme.html +++ b/examples/_004_error_response/readme.html @@ -3,7 +3,7 @@ Luracast Restler 2.0 Example:- Error Response - + @@ -23,32 +23,41 @@ }); }) + + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/_004_error_response/readme.md b/examples/_004_error_response/readme.md index fba9f3c5d..254549a63 100755 --- a/examples/_004_error_response/readme.md +++ b/examples/_004_error_response/readme.md @@ -4,11 +4,11 @@ Error Response API methods can make use of RestException class to provide error information to the user. -use `throw new RestException($http_status_code)` to send the error response +use `throw new RestException($httpStatusCode)` to send the error response to the client. For the list of HTTP Status codes and their meaning take a look at -[Wikipedia](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes) +[Wikipedia](http://en.wikipedia.org/wiki/List_of_httpStatusCodes) > This API Server is made using the following php files/folders > * index.php (gateway) diff --git a/examples/_005_protected_api/readme.html b/examples/_005_protected_api/readme.html index c86aefcf0..59b00e71c 100755 --- a/examples/_005_protected_api/readme.html +++ b/examples/_005_protected_api/readme.html @@ -3,7 +3,7 @@ Luracast Restler 2.0 Example:- Protected API - + @@ -23,22 +23,31 @@ }); }) + + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/_006_crud/author.sql b/examples/_006_crud/author.sql deleted file mode 100755 index 9556f9c96..000000000 --- a/examples/_006_crud/author.sql +++ /dev/null @@ -1,4 +0,0 @@ -DROP TABLE IF EXISTS "author"; -CREATE TABLE "author" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "name" VARCHAR NOT NULL , "email" VARCHAR NOT NULL UNIQUE ); -INSERT INTO "author" VALUES(1,'Jac Wright','jacwright@gmail.com'); -INSERT INTO "author" VALUES(2,'Arul Kumaran','arul@luracast.com'); diff --git a/examples/_006_crud/author.sqlite b/examples/_006_crud/author.sqlite deleted file mode 100755 index d959af334d065a839a84e71dee026b09e8281086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163840 zcmeI*&u$w<7y$5H+YMEvoDhm4gfy`flqjZE$OAN{vf7Z;ZH)tpI7Cy&O}1Lw%6hHl zR0DTjh?hX(gpjy!xAnoYvuF2l^vb6&hJ@&T3=Y&*hmj<6dRt-hU3BCwXcSa zx0bwGTpP#Hur(c?u74ZS_m-BI)9*gKs2OMF$)vdKYd0!1FDIK}t(hG)!%nx~dD!WN zr@fuW?cPDy?Hq*A-tRxz=~lNr?sWShbf5G?cYkj$v_kXzfz9w`yZ7KxyLV${IEq>R zmZh-Y-Fdd(3F{lRg{Aw;X_AePhG$nc)yqsS#)p@$b+~zXu2&zUu~>Dr`47qb*Qyf) z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkL{KPRwKYb2}p+QlpnyR%U& z;y70Mc0McOAWqA>gUM(mZ6qsOpT|M?s>qH{%9nBQ>LS@b9>pv_zvND}@7nyQWd3{A z2?7KN5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn?uzEY-KtmvQi_$c|6S z?c-6*^1Fk{DE***FO9`4-_B=69K>mPK3iL?_MOiENUA0X5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7e?|4QIaT3=4%tUQ?%wZ&@Z>HO!UYJva(0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PHuy0w1T1<@@i)S$Q%krl)yU4iBrrBpV+M O&(5=I`f&5pu=y91*~Ur$ diff --git a/examples/_006_crud/filename.sqlite b/examples/_006_crud/filename.sqlite deleted file mode 100755 index be89fb66dae6a1174e9a2ed5c64ac977243e886d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmdPWQV7Y&ELKR%%t=*9&d)1J%*-oRNX%0R4)n<^NmVe?GgL@PEJ;jCEKXI>(qhmk zeSC`n3c7(b3yd>LkA}c#2n@*(h+yDg5MVe1%*zsB_ct&wFoGD6ylub$M5aj9W7!;}C?HUPEqN&L+ znahM3Z0#s98UmvsKtc#`Ferdkb3yea8MqrpfO9Pa!-LU0MM6l9Djf}hp%((M{12(` b*}*xVnU&p;!D2N3553qNb@6Bj5FY{nVe&{n diff --git a/examples/_006_crud/readme.html b/examples/_006_crud/readme.html index 35608357c..4fc3e4968 100755 --- a/examples/_006_crud/readme.html +++ b/examples/_006_crud/readme.html @@ -3,7 +3,7 @@ Luracast Restler 2.0 Example:- CRUD - + @@ -23,22 +23,31 @@ }); }) + + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/_006_crud/readme.md b/examples/_006_crud/readme.md index 4e0059815..2b9a7a3f6 100755 --- a/examples/_006_crud/readme.md +++ b/examples/_006_crud/readme.md @@ -17,13 +17,15 @@ a firefox extension This API Server exposes the following URIs - GET author ⇠ Author::get() - GET author/:id ⇠ Author::get() - POST author ⇠ Author::post() - PUT author ⇠ Author::put() - PUT author/:id ⇠ Author::put() - DELETE author ⇠ Author::delete() - DELETE author/:id ⇠ Author::delete() + GET author ⇠ Author::get() + GET author/:id ⇠ Author::get() + POST author ⇠ Author::post() + POST author/:request_data ⇠ Author::post() + PUT author ⇠ Author::put() + PUT author/:id ⇠ Author::put() + PUT author/:id/:request_data ⇠ Author::put() + DELETE author ⇠ Author::delete() + DELETE author/:id ⇠ Author::delete() Try the following links in your browser diff --git a/examples/_006_crud/sqlitedb.php b/examples/_006_crud/sqlitedb.php deleted file mode 100755 index 85f1caf7e..000000000 --- a/examples/_006_crud/sqlitedb.php +++ /dev/null @@ -1,68 +0,0 @@ -query('SELECT requests FROM tablename WHERE id = 1'); - if ($q === false) { - $db->queryExec('CREATE TABLE tablename (id int, requests int, PRIMARY KEY (id)); INSERT INTO tablename VALUES (1,1)'); - $hits = 1; - } else { - $result = $q->fetchSingle(); - $hits = $result+1; - } - $db->queryExec("UPDATE tablename SET requests = '$hits' WHERE id = 1"); - } else { - die($err); - } - $this->db = new SQLite3('author.sqlite'); - } - private function find($id){ - foreach ($_SESSION['rs'] as $index => $rec) { - if ($rec['id'] == $id) { - return $index; - } - } - return FALSE; - } - function get($id) { - $results = $this->db->query("SELECT * FROM author"); - $r=array(); - while ($row = $results->fetchArray()) { - print_r($row); - $r[]=$row; - } - return $row; - } - function getAll() { - return $_SESSION['rs']; - } - public function insert($rec) { - $rec['id']=$this->pk(); - array_push($_SESSION['rs'], $rec); - return $rec; - } - public function update($id, $rec) { - $index = $this->find($id); - if($index===FALSE)return FALSE; - $rec['id']=$id; - $_SESSION['rs'][$index]=$rec; - return $rec; - } - public function delete($id) { - $index = $this->find($id); - if($index===FALSE)return FALSE; - return array_shift(array_splice($_SESSION['rs'], $index, 1)); - } -} - -// Sample data. -function getData() { - return array( - array('id' => 1, 'name' => 'Jac Wright', 'email' => 'jacwright@gmail.com'), - array('id' => 2, 'name' => 'Arul Kumaran', 'email' => 'arul@luracast.com' ), - ); -} \ No newline at end of file diff --git a/examples/index.html b/examples/index.html index dce26a468..61cda3716 100755 --- a/examples/index.html +++ b/examples/index.html @@ -3,8 +3,9 @@ Luracast Restler 2.0 Example:- - + + + + +
      +© 2010 +
      \ No newline at end of file diff --git a/examples/resources/Luracast.png b/examples/resources/Luracast.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce8e0d1eb588637f89435f1965f6ee6e5cd0cc5 GIT binary patch literal 2543 zcmb7Ec{r5&9v@uUMM8GrmNmOGl7z8Lj~I+jmJ_lK4kl(Aam#ir5hb#f^_;{JEeF|) z29X#X>oiG*F%2_mEZOf*ucvd*^W6K#{XFkG@ALfL@Avcld_LdznHwm38zFvaei#fU zgtSFCfoCna6?wV9?@el%3wUrvSRm27yu9RThYv6qXC}%HZMFLio12^K>+35kD~pSZ zJMV|L|LhqV85tfPo<$kX*y(_h+1XieJ}@w_fb;o)P@Oy}+kX@W&OsN8#e!aKHa*>_ zdo=H2GHEO`ZXhwHCpL28;GbH;L+bqfN*tgVnzFXGMkbRdCME_42iG4Ik&YdJsMu^a zgTVk)TW?>j-OT`91InDB3zD=m(GSkIh|O!6X+Xe;jwtD$+@Kc}Kmx#YNDtsyT3P~! zbUK|zqXEL1nVG4nDQM_6sdH>>3~~nq07qZ*?=YkMXU=L<&M1@Nvg7*VU^F0y1OrKc z2as&GH-IRB9cT)00S@V2$T>$NkOnXd)q|j>r>6le@V>|MO(Bp3^g{x`Xy9VAtsV$o zuPpsqk`EYwqbbW{5Hn;4tN_XZTEVof<~tL4FbHHjVRQtV1;PjNKqZv?#qy@USI)i{Civ=XUHrF4Y&ZI0V6;*h>k*`K;l7kK-Ks3 z_KdVQK+}Pjz3JaZgB`JOUJDqPd3Vb_4QONF5g4JK(*rpJFOc8&<|-g_NE=uv^r>() z!KcBuO2#?aJHtf(g3>}=xa`CKFAxAy{wMPH@%uiS_tEz^0Pp_+=6?YQ^#6w` zM!^`^ zUV(F%KaSuX?gumX4ZGy0XdM{tM+gfKj!?98HawwQrC`f|!MI-`5$5O%iHo_(x#EV0 zlaHs4E7g0ke8yCBM4KIdRZ@~5h7u(@4@GN^%SD^>L_ZF5z5Sq~q(l8Rr@BV7=97w; z^dJ1@&bdxchevewrpigtT`Vg{gaKl1|blb}%W!q{9gN=mmka-jqKYfZ~p*-Vetd(nu zXEzK~G)VV6$fKhegNmzd-qRh=b2Z)T7gT<%8?-nWQ~uT>;nXD}sasN;x#73|lSqTX_Om>r(fk$okaMkAPg z0t=)sw!X`LL*n&8JD)isf+S+y1eLokAW)4q+h|n!MHH5AW3JL!&QKt}RLn-c6-Q5g zv9i3pys{GFnJA2xO{+CIba_E3EweEtYuw64wi2!@ArK#7>gDEvwZ5D=&>L@ae)Xx4 zXct_${wSZCOZ*;%SoTL1+7`+!Uqk5Gtq@hGJGzyP>u8VQLgMF#Lt-;IR~$?`CA5F~ z(q7%#`uNes?ZB8_s=7gSa(Twk)q;8A>27~%p?OwRiLFM^dC?BEYC)^YP<)BqDnk*S z8F;qt2Ik4j6lz0bUy@yL*z@raf(5;WkxW_L%yM|_wh|Px%LpE0PDtaF-WGIq4crj) z{>`?jCr=*Mjogs#!~JsVk5rVszl1a&^R)0UM+jf2I~r!5 z(IdGr_lFMQWb+FX^1SEHTjzDE;VRg#l>D#pn&Q#fA^N|0XhpUNZ!84%Pm&MnbXI$| zT}~bdonoY!tgf@mukAK9iO&<$GwQti3-Z+RUJ9 z1{|7eF`O$)UCCRHZq#7Bs7_)c4%hHK?OVl8r&4WJN`;ons&1S;W0R>G^0GIh@I#(f zE#462Fsa8=s7e<+nUsY7LnQfK5lI{aw->CN%SP4QYvgfUXLCM`P)wEdCEOgwsgEn9 zkLW}c`W(P8f=Y^NtyT*fGF>m3`a3reA7JsMF86xmr7V|1|Er~UTw%&eZ}eXI7X|qS z(YFY(1Lt3erl()Y=r7Uk%@ZJ9!S?wSYaQ1m8M5jAk&1dDoC<1H@hz^poP5WR*;Q&5 z+mv!j8B2v*=FU1+#mxl%)It6EUh~agg$z(;|H9TBXzx_kpB{ZyJ9g)SQ_hPnQbUv`$bZ@n$zx`;;Epu_{f@p@jqd@xzc-_J|q_ulRohX5xO) literal 0 HcmV?d00001 diff --git a/examples/resources/Luracast_dark.png b/examples/resources/Luracast_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..ea3bf268804b15a621f0c75ad9d2f93a0fe81cf1 GIT binary patch literal 1489 zcmcJP`!^E|7{@1>x!+1yF_cpYbIWx!<}&weMq=i&#o1xZ+%HM)x7XpgrQAZ8OO54P zNnRnfA>>xX>rRMjjJ12nt;BYu4B_#v`;s5Y=(=$ojA_a=G0}3D^p`@g( zjX=1#5C{-0i+-ZIv@}aFwoHCj%L+E0EG+4{|BH*1WJTA84rN_k$mtF%K$PyfY7vq>rDXQYylDhN4q7@7d;?@uL<7@EWp`ZQ@MEiT;d6*{0@&0VLA-V2%DJ;kYqF` z{G*t3Su~ZC3fey+X&b9wmpqMl?zl=~6O$}c;S{^qK%7Uh27_fUo*6zwZXaWVbb9{??;d~Z6Fy<|#j?syJXQDY zF6E&Q+4fAx*{z-g93r66zh&5sn4J{Wf9j00dNY=$dr+_GRVI2R^2>s9Lz-5xT3eM& z99Yz3po`WRZc^*^R|`}IRk&6oW4TLv9D&Ky^|~E%FC#iV5YiNeDa>&W$i@;)f?F%U zeV}G9YgV*t%FI1*m?Ew*E9v;KVGf_WqA3RHcLd5UOKhD~HZ+j&O0EjGZp8Y4`4e%0 zkfQeC7w>BaO*@j>eDw~5-WZ6n7sWwwr&3`5S7J@7czRnky37|lQ1E_A0;fMg&^S{G z+c=dl$C|ln#8)tvn;((M1pfTD>7}J{+e`!cMd%sJ>{0POY3lomY5E^~KcimyxXuxuk3n z_fdT{w+6l(l~znq5OY`|*)-K;@^8t~jfy__5`5OG0`ZNHnH4kWZe~knVtcMp#{O=uWBwYf?wALK_D7=D>tc6W{2K-+$-m;CfOl?{iyiNK z`+M|2MN+VKmliP6bM7|81Ud!>OVa@s%O1r%JpDzgEb18O(vhPBk-_m*RIgo8g1t!8 zqowcSj47t0$PvqUXk7l_iT;U{zl6}i%eFb%x$Gu6@f7V)z2Berw#inn*>ZBGLF-0L zaM;670Um1!-TWaS1!9!J7W`hfqOb;d&B$b%u-eoVv?Q-;xLEiU3jeJ{${Y7wJH0+G z9i1be*L(;WqlkMCP3F5d_8FAh()nS_{kNl`cg%SY6FjoIlGQ`H_-`ya3q=uhHxrGl z%%E46({H|h8}yhHmC8EQH=CM)6lE-ADsJ*Hrh|ArwX0e@{k+wO@Eze8smZI0leL9| z1;$U0*8xF}TZKmhk1M58t{XeyooO~#U9}E?kH(R;D$i=dDOA|y9F|#|t~Et9%zmj$ z&EiIVh|nv8tX1$&MQnGq&kN_i8uz|!_&s8Qdi^=-=LX~CN`C12T{j#ZJkiBeJMNu* zd0#v;HcIwmtXwH@O{+6Fq`na4*L4>bCnToTrrv{mmA*-6%M)@?RP$+Bg)34k8#-?` zx|FFK-t76RT5R~Mll-R4aTEADlCn2ZWZqfWU$Gq-D(j5h%p1&DTu~Cfu%QTvf*I1D zyG;xhs71EgnRPaj@V~20>w@{d){vgQ#QdzKsU2MkiA(<8-|LDfC@`1uQf94GFYDZQ zy)b8zKDKnSBC!%<%R1EC@fi4##m?6I&A4g`NDIs##n*Ita9a { + color: #333; + background: none; + font-weight: bold; +} + +.collapsed { + display: none; +} + +nav.floating-menu { + height: 32px; + top: 0; + left: 0; + padding: 0; + margin: 0; + position: fixed; + background: #000000; + border: 0; + width: 100%; + z-index: 100; + overflow: hidden; + background: -moz-linear-gradient(bottom, #aaa, #000 50%); + background: -webkit-gradient(linear, center bottom, center top, from(#aaa), + color-stop(50%, #000) ); + -moz-box-shadow: 0 0 10px #000000; + -webkit-box-shadow: 0 0 10px #000000; + box-shadow: 0 0 10px #000000; +} + +nav.floating-menu ul { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; +} + +nav.floating-menu li { + float: left; +} + +nav.floating-menu a:link, +nav.floating-menu a:visited { + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + font-weight: bold; + display: block; + width: 120px; + color: #cccccc; + text-align: center; + padding: 4px; + text-decoration: none; + text-transform: none; + border-left: 1px solid #222; + border-right: 1px solid #bbb; +} + +nav.floating-menu a:hover, +nav.floating-menu a:active, +nav.floating-menu a.active { + color: #222; + background-color: #ddd; + border-top: 3px solid #aa0000; + background: -moz-linear-gradient(bottom, #ddd, #aaa 50%); + background: -webkit-gradient(linear, center bottom, center top, from(#ddd), + color-stop(50%, #aaa) ); + -moz-box-shadow: 0 0 10px #000000; +} +nav.floating-menu a.active { + border-top: 3px solid #ff0000; +} From e2913d4f18e6720c411f20fa18a9f75223b441ee Mon Sep 17 00:00:00 2001 From: Luracast Date: Thu, 22 Sep 2011 01:34:21 +0800 Subject: [PATCH 15/61] Improved the minimal example to include parameter validation, .htaccess file now makes sure that php display_errors is turned off in the server --- examples/_001_helloworld/.htaccess | 3 +++ examples/_002_minimal/.htaccess | 3 +++ examples/_002_minimal/math.php | 29 +++++++++++++++++++++----- examples/_003_multiformat/.htaccess | 3 +++ examples/_004_error_response/.htaccess | 3 +++ examples/_005_protected_api/.htaccess | 3 +++ examples/_006_crud/.htaccess | 3 +++ 7 files changed, 42 insertions(+), 5 deletions(-) diff --git a/examples/_001_helloworld/.htaccess b/examples/_001_helloworld/.htaccess index b766fc6f0..22c547c25 100755 --- a/examples/_001_helloworld/.htaccess +++ b/examples/_001_helloworld/.htaccess @@ -5,4 +5,7 @@ DirectoryIndex index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] + + + php_flag display_errors Off \ No newline at end of file diff --git a/examples/_002_minimal/.htaccess b/examples/_002_minimal/.htaccess index b766fc6f0..22c547c25 100755 --- a/examples/_002_minimal/.htaccess +++ b/examples/_002_minimal/.htaccess @@ -5,4 +5,7 @@ DirectoryIndex index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] + + + php_flag display_errors Off \ No newline at end of file diff --git a/examples/_002_minimal/math.php b/examples/_002_minimal/math.php index 9a17b3b8b..63eed0a43 100755 --- a/examples/_002_minimal/math.php +++ b/examples/_002_minimal/math.php @@ -1,9 +1,28 @@ _validate($n1, $n2); + return $n1 + $n2; } - function multiply($n1=5, $n2=2) { - return array('result'=>($n1*$n2)); + function multiply ($n1 = 5, $n2 = 2) + { + $this->_validate(func_get_args()); + return array('result' => ($n1 * $n2)); + } + private function _validate ($numbers) + { + foreach ($numbers as $n) { + if (! is_numeric($n)) { + throw new RestException(400, 'parameter is not a number'); + } + if (is_infinite($n)) { + throw new RestException(400, 'parameter is not finite'); + } + if ($n > 5000) { + throw new RestException(416); + } + } } } \ No newline at end of file diff --git a/examples/_003_multiformat/.htaccess b/examples/_003_multiformat/.htaccess index b766fc6f0..22c547c25 100755 --- a/examples/_003_multiformat/.htaccess +++ b/examples/_003_multiformat/.htaccess @@ -5,4 +5,7 @@ DirectoryIndex index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] + + + php_flag display_errors Off \ No newline at end of file diff --git a/examples/_004_error_response/.htaccess b/examples/_004_error_response/.htaccess index b766fc6f0..22c547c25 100755 --- a/examples/_004_error_response/.htaccess +++ b/examples/_004_error_response/.htaccess @@ -5,4 +5,7 @@ DirectoryIndex index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] + + + php_flag display_errors Off \ No newline at end of file diff --git a/examples/_005_protected_api/.htaccess b/examples/_005_protected_api/.htaccess index b766fc6f0..22c547c25 100755 --- a/examples/_005_protected_api/.htaccess +++ b/examples/_005_protected_api/.htaccess @@ -5,4 +5,7 @@ DirectoryIndex index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] + + + php_flag display_errors Off \ No newline at end of file diff --git a/examples/_006_crud/.htaccess b/examples/_006_crud/.htaccess index b766fc6f0..22c547c25 100755 --- a/examples/_006_crud/.htaccess +++ b/examples/_006_crud/.htaccess @@ -5,4 +5,7 @@ DirectoryIndex index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] + + + php_flag display_errors Off \ No newline at end of file From b079fd29e553fa3d370f8814a09342e47b8fbdfa Mon Sep 17 00:00:00 2001 From: Luracast Date: Fri, 23 Sep 2011 20:27:46 +0800 Subject: [PATCH 16/61] Fixed a bug in the minimal example that caused Math::add function to skip validation --- examples/_002_minimal/math.php | 3 +- examples/resources/markdown.css | 403 -------------------------------- 2 files changed, 2 insertions(+), 404 deletions(-) delete mode 100755 examples/resources/markdown.css diff --git a/examples/_002_minimal/math.php b/examples/_002_minimal/math.php index 63eed0a43..c4f9a5524 100755 --- a/examples/_002_minimal/math.php +++ b/examples/_002_minimal/math.php @@ -1,9 +1,10 @@ _validate($n1, $n2); + $this->_validate(func_get_args()); return $n1 + $n2; } function multiply ($n1 = 5, $n2 = 2) diff --git a/examples/resources/markdown.css b/examples/resources/markdown.css deleted file mode 100755 index be0ee0477..000000000 --- a/examples/resources/markdown.css +++ /dev/null @@ -1,403 +0,0 @@ -body { - margin: 0 auto; - font-family: Georgia, Palatino, serif; - color: #444444; - line-height: 1; - max-width: 960px; - padding: 0 30px 30px; - background-color: #E6E2DF; -} - -h1,h2,h3,h4 { - color: #111111; - font-weight: 400; -} - -h1,h2,h3,h4,h5,p { - margin-bottom: 24px; - padding: 0; -} - -h1 { - font-size: 48px; -} - -h2 { - font-size: 36px; - margin: 24px 0 6px; - margin-top: .5em !important; - border-top: 4px solid #E0E0E0 !important; - padding-top: .5em !important; -} - -h3 { - font-size: 24px; -} - -h4 { - font-size: 21px; -} - -h5 { - font-size: 18px; -} - -a { - color: #0099ff; - padding: 0; - vertical-align: baseline; - margin: 0; - text-decoration: none; -} - -a:hover,a:active,a:focus { - background-color: #0099ff; - text-decoration: none; - color: white; -} - -ul,ol { - padding: 4px 20px 0px; - margin: 0; -} - -li { - line-height: 24px; -} - -blockquote ul { - margin: 0 auto; - padding: 0; - overflow: hidden; -} - -blockquote ul li { - text-align: left; - float: left; - list-style: none; - height: 30px; - width: 228px; -} - -p,article.ul,article.ol { - font-size: 16px; - line-height: 24px; - max-width: 540px; -} - -article pre { - padding: 13px; - /* - hyphens: all; - word-break-inside: hyphenate; - overflow : hyphenate; - hyphenate-character : "\2010"; - white-space : pre-wrap; - white-space: -moz-pre-wrap; - white-space: -o-pre-wrap; - hyphenate-character: "\2010"; - */ - background: #f3f3f3; - border-style: dashed; - border-color: #DADADA; - border-width: 1px; - max-width: 600 px; - overflow: auto; -} - -code { - font-family: Consolas, Monaco, Andale Mono, monospace; - background: #ffffdd; - font-size: smaller; -} - -pre code { - line-height: 1.5; - background-color: transparent; - font-size: 13px; -} - -abbr { - border-radius: 3px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - -khtml-border-radius: 3px; - background: blue; - opacity: 0.3; - color: white; - padding: 4px; - cursor: help; -} - -abbr:HOVER { - opacity: 0.6; - background: black; -} - -a abbr,a abbr:HOVER { - background: inherit; - color: inherit; - opacity: inherit; - border: inherit; - cursor: inherit; - padding: inherit; -} - -aside { - display: block; - float: right; - width: 390px; -} - -blockquote { - margin: 1em 2em; - max-width: 476px; - background: #ECEDE5; - border-left: 10px solid #ccc; - padding: .5em 10px; -} - -blockquote p { - color: #666; - max-width: 460px; - margin: 0; -} - -hr { - width: 540px; - text-align: left; - margin: 0 auto 0 0; - color: #999; -} - -dl { - margin: 2em 0; - padding: 0; - max-width: 476px; -} - -dt { /* - background-color: #131210; - font-weight: bold; - color: #959289; - text-align: center; - */ - font-family: Fixed, monospace; - padding: .5em; - border: 1px solid #131210; - border-right: 1px solid #131210; - border-top: 1px solid #131210; - padding: .5em; -} - -dd { - margin: 0 0 1em 0; - /* - background: #DBD8D8; - font-style: italic; - text-align: center; - */ - padding: 1em .5em; - border-left: 1px solid #131210; - border-right: 1px solid #131210; - border-bottom: 1px solid #131210; -} - -dd pre { - border: none; - background-color: transparent; -} -/** - * Left side - */ -#left { - position: fixed; - top: 0; - bottom: 0; - left: 0; - height: auto; - margin: 0; - padding: 7px; - overflow: auto; - background: black; - line-height: 1.6; -} - -#left img { - margin-top: -10px; - margin-left: -10px; - margin-bottom: 0px; - padding-top: 0px; - padding-left: 0px; -} - -#left h3 { - color: white; - border-bottom: 1px solid #aaaaaa; - border-top: 1px solid #aaaaaa; -} - -#left a { - color: #bbccff; -} - -#left a:hover { - color: white; -} - -#left ul { - list-style: none; - padding: 0 0 0 0px; - margin: 0; - color: white; -} - -#left li { - white-space: nowrap; -} - -#left ul ul { - padding-left: 10px; -} - -#left .active>a { - color: #333; - background: none; - font-weight: bold; -} - -.collapsed { - display: none; -} - -/** - * Right side - */ -#right { - margin-left: 120px; - min-height: 600px; - background-repeat: no-repeat; - background-position: top right; - -moz-box-shadow: 5px 5px 5px #ccc; - -webkit-box-shadow: 5px 5px 5px #ccc; - box-shadow: 5px 5px 5px #ccc; -} - -ol.toc li { - clear: left; - border-bottom: dashed 1px #aaa; - height: 1.05em; - margin-top: 20px; - position: relative; - margin-top: 20px; - height: 1.05em; -} - -ol.toc li:HOVER { - color: #0099ff; - border-bottom: dashed 1px #0099ff; -} - -ol.toc li:HOVER small { - color: #7FCCFF; -} - -ol.toc a { - text-decoration: none; - color: inherit; -} - -ol.toc right,ol.toc span { - background: #fff; - padding: 0 3px 0 0; - float: left; - position: absolute; - text-decoration: none; -} - -ol.toc right { - padding: 0 0 0 3px; - right: 0; -} - -ol.toc a:HOVER { - -} - -ol.toc ol { - list-style: lower-roman; - margin: 1.5em 0 1em 5%; - padding: 0; - background: #fff; - float: left; - display: block; - width: 95%; -} - -ol.toc small { - color: #a999a9; -} - -article { - background: white; - position: relative; - padding: 25px 50px; - margin: 20px 0; - max-width: 550px; - /* - padding: 25px 210px 0 30px; - */ -} - -article:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 0; - height: 0; - border-top: 10px solid #E6E2DF; - border-left: 10px solid #E6E2DF; - border-bottom: 10px solid #d9d3cf; - border-right: 10px solid #d9d3cf; -} - -.menu { - margin: 0; - padding: 0; - width: px; - list-style: none; - background: rgb(); -} - -.menu li { - padding: 0; - margin: 0 0 1px 0; - height: 40px; - display: block; -} - -.menu li a { - text-align: ; - height: 40px; - padding: 0px 25px; - font: 16px Verdana, Arial, Helvetica, sans-serif; - color: rgb(); - display: block; - background: url('verMenuImages.png') 0px 0px no-repeat; - text-decoration: none; -} - -.menu li a:hover { - background: url('verMenuImages.png') 0px -40px no-repeat; - color: rgb(); -} - -.menu li a.active,.menu li a.active:hover { - background: url('verMenuImages.png') 0px -80px no-repeat; - color: rgb(); -} - -.menu li a span { - line-height: 40px; -} \ No newline at end of file From a752f6b61edbe0ca3f4d0b24655b7046ff955dcb Mon Sep 17 00:00:00 2001 From: Luracast Date: Thu, 29 Sep 2011 11:39:24 +0800 Subject: [PATCH 17/61] API class was not getting an reference for Restler instance which is fixed in this patch and fixes #2 issue which required us to use spl_autoload or include_once to add the format file which is in the same folder as restler.php --- restler/restler.php | 9 ++++++--- restler_minified/restler.php | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/restler/restler.php b/restler/restler.php index d80989d73..5f36ae5b3 100644 --- a/restler/restler.php +++ b/restler/restler.php @@ -10,10 +10,10 @@ * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ - * @version 2.0.1 + * @version 2.0.2 */ class Restler { - const VERSION = '2.0.1'; + const VERSION = '2.0.2'; /** * URL of the currently mapped service * @var string @@ -339,6 +339,7 @@ public function handle () { $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); + $object->restler=$this; if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); @@ -1179,7 +1180,9 @@ function autoload_formats($class_name) if (file_exists($file)) { require_once($file); - } + } elseif (file_exists("$class_name.php")) { + require_once ("$class_name.php"); + } } spl_autoload_register('autoload_formats'); /** diff --git a/restler_minified/restler.php b/restler_minified/restler.php index 8d2cc0a06..a9f9324ab 100644 --- a/restler_minified/restler.php +++ b/restler_minified/restler.php @@ -1,6 +1,6 @@ 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); protected $cached; public function __construct ($production_mode = FALSE) { $this->production_mode = $production_mode; $this->cache_dir = getcwd(); $this->base_dir = RESTLER_PATH; } public function __destruct () { if ($this->production_mode && !$this->cached) { $this->saveCache(); } } public function refreshCache () { $this->routes = array(); $this->cached = FALSE; } public function setSupportedFormats () { $args = func_get_args(); $extensions = array(); foreach ($args as $class_name) { if(!is_string($class_name) || !class_exists($class_name)){ throw new Exception("$class_name is not a vaild Format Class."); } $obj = new $class_name; if(! $obj instanceof iFormat){ throw new Exception('Invalid format class; must implement '. 'iFormat interface'); } foreach ($obj->getMIMEMap() as $extension => $mime) { if(!isset($this->format_map[$extension])) $this->format_map[$extension]=$class_name; if(!isset($this->format_map[$mime])) $this->format_map[$mime]=$class_name; $extensions[".$extension"]=TRUE; } } $this->format_map['default']=$args[0]; $this->format_map['extensions']=array_keys($extensions); } public function addAPIClass($class_name, $base_path = NULL) { if(!class_exists($class_name)){ throw new Exception("API class $class_name is missing."); } $this->loadCache(); if(!$this->cached){ if(is_null($base_path))$base_path=strtolower($class_name); $base_path = trim($base_path,'/'); if(strlen($base_path)>0)$base_path .= '/'; $this->generateMap($class_name, $base_path); } } public function addAuthenticationClass ($class_name, $base_path = NULL) { $this->auth_classes[] = $class_name; $this->addAPIClass($class_name, $base_path); } public function addErrorClass ($class_name) { $this->error_classes[] = $class_name; } public function handleError ($status_code, $error_message = NULL) { $method = "handle$status_code"; $handled = FALSE; foreach ($this->error_classes as $class_name) { if (method_exists($class_name, $method)) { $obj = new $class_name(); $obj->restler = $this; $obj->$method(); $handled = TRUE; } } if($handled)return; $message = $this->codes[$status_code] . (!$error_message ? '' : ': ' . $error_message); $this->setStatus($status_code); $this->sendData( call_user_func( array($this->response, '__formatError'), $status_code, $message) ); } public function handle () { if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); $this->url = $this->getPath(); $this->request_method = $this->getRequestMethod(); $this->response_format = $this->getResponseFormat(); $this->request_format = $this->getRequestFormat(); if(is_null($this->request_format)){ $this->request_format = $this->response_format; } if($this->request_method == 'PUT' || $this->request_method == 'POST'){ $this->request_data = $this->getRequestData(); } $o = $this->mapUrlToMethod(); if(!isset($o->class_name)){ $this->handleError(404); }else{ try { if($o->method_flag){ $auth_method = '__isAuthenticated'; if(!count($this->auth_classes))throw new RestException(401); foreach ($this->auth_classes as $auth_class) { $auth_obj = new $auth_class(); $auth_obj->restler=$this; $this->applyClassMetadata($auth_class, $auth_obj, $o); if (!method_exists($auth_obj, $auth_method)) { throw new RestException(401, 'Authentication Class '. 'should implement iAuthenticate'); }elseif(!$auth_obj->$auth_method()){ throw new RestException(401); } } } $this->applyClassMetadata(get_class($this->request_format), $this->request_format, $o); $pre_process = '_'.$this->request_format->getExtension().'_'. $o->method_name; $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); } switch ($o->method_flag) { case 3: $reflection_method = new ReflectionMethod($object, $o->method_name); $reflection_method->setAccessible(TRUE); $result = $reflection_method->invokeArgs($object, $o->arguments); break; case 2: case 1: default: $result = call_user_func_array(array($object, $o->method_name), $o->arguments); } } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } if (isset($result) && $result !== NULL) { $this->sendData($result); } } public function sendData($data) { $data = $this->response_format->encode($data, !$this->production_mode); $post_process = '_'.$this->service_method .'_'. $this->response_format->getExtension(); if(isset($this->service_class_instance) && method_exists($this->service_class_instance,$post_process)){ $data = call_user_func(array($this->service_class_instance, $post_process), $data); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: 0"); header('Content-Type: ' . $this->response_format->getMIME()); header("X-Powered-By: Luracast Restler v".Restler::VERSION); die($data); } public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ". $this->codes[strval($code)]); } public function removeCommonPath($first, $second, $char='/'){ $first = explode($char, $first); $second = explode($char, $second); while (count($second)){ if($first[0]==$second[0]){ array_shift($first); } else break; array_shift($second); } return implode($char, $first); } public function saveCache() { $file = $this->cache_dir . '/routes.php'; $s = '$o=array();'.PHP_EOL; foreach ($this->routes as $key => $value) { $s .= PHP_EOL.PHP_EOL.PHP_EOL."############### $key ###############" .PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\']=array();'; foreach ($value as $ke => $va) { $s .= PHP_EOL.PHP_EOL."#==== $key $ke".PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\'][\''.$ke.'\']='.str_replace(PHP_EOL, PHP_EOL."\t", var_export($va, TRUE)).';'; } } $s .= PHP_EOL.'return $o;'; $r=@file_put_contents($file, "cache_dir' needs to have ". "the permissions set to read/write/execute for everyone in order ". "to save cache and improve performance."); } protected function getPath () { $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])); $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); $path = str_replace($this->format_map['extensions'], '', $path); return $path; } protected function getRequestMethod () { $method = $_SERVER['REQUEST_METHOD']; if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } return $method; } protected function getRequestFormat () { $format=NULL; if(isset($_SERVER['CONTENT_TYPE'])){ $mime = explode(';', $_SERVER['CONTENT_TYPE']); $mime = $mime[0]; if($mime==UrlEncodedFormat::MIME){ $format = new UrlEncodedFormat(); }else{ if(isset($this->format_map[$mime])){ $format = $this->format_map[$mime]; $format = is_string($format) ? new $format: $format; $format->setMIME($mime); } } } return $format; } protected function getResponseFormat () { $format; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); if($extension && isset($this->format_map[$extension])){ $format = $this->format_map[$extension]; $format = is_string($format) ? new $format: $format; $format->setExtension($extension); return $format; } if(isset($_SERVER['HTTP_ACCEPT'])){ $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); foreach ($accepts as $accept) { if($extension && isset($this->format_map[$accept])){ $format = $this->format_map[$accept]; $format = is_string($format) ? new $format: $format; $format->setMIME($accept); return $format; } } } $format = $this->format_map['default']; return is_string($format) ? new $format: $format; } protected function getRequestData() { try{ $r = file_get_contents('php://input'); if(is_null($r))return $_GET; $r =$this->request_format->decode($r); return is_null($r) ? array(): $r; } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } protected function mapUrlToMethod () { if(!isset($this->routes[$this->request_method])){ return array(); } $urls = $this->routes[$this->request_method]; if(!$urls)return array(); $found = FALSE; $this->request_data += $_GET; $params = array('request_data'=>$this->request_data); $params += $this->request_data; foreach ($urls as $url => $call) { $call = (object)$call; if(strstr($url, ':')){ $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); if (preg_match(":^$regex$:", $this->url, $matches)) { foreach ($matches as $arg => $match) { if (isset($call->arguments[$arg])){ $params[$arg] = $match; } } $found = TRUE; break; } }elseif ($url == $this->url){ $found = TRUE; break; } } if($found){ $p = $call->defaults; foreach ($call->arguments as $key => $value) { if(isset($params[$key]))$p[$value] = $params[$key]; } $call->arguments=$p; return $call; } } protected function applyClassMetadata($class_name, $instance, $method_info){ if(isset($method_info->metadata[$class_name]) && is_array($method_info->metadata[$class_name])){ foreach ($method_info->metadata[$class_name] as $property => $value){ if(property_exists($class_name, $property)){ $reflection_property = new ReflectionProperty($class_name, $property); $reflection_property->setValue($instance, $value); } } } } protected function loadCache() { if ($this->cached !== NULL) { return; } $file = $this->cache_dir . '/routes.php'; $this->cached = FALSE; if ($this->production_mode) { if (file_exists($file)) { $routes = include($file); } if (isset($routes) && is_array($routes)) { $this->routes = $routes; $this->cached = TRUE; } } else { } } protected function generateMap ($class_name, $base_path = "") { $reflection = new ReflectionClass($class_name); $class_metadata = parse_doc($reflection->getDocComment()); $methods = $reflection->getMethods( ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $doc = $method->getDocComment(); $arguments = array(); $defaults = array(); $metadata = $class_metadata+parse_doc($doc); $params = $method->getParameters(); $position=0; foreach ($params as $param){ $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; $position++; } $method_flag = $method->isProtected() ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), 'arguments'=>$arguments, 'defaults'=>$defaults, 'metadata'=>$metadata, 'method_flag'=>$method_flag ); $method_url = strtolower($method->getName()); if (preg_match_all( '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $http_method = $match[1]; $url = rtrim($base_path . $match[2],'/'); $this->routes[$http_method][$url] = $call; } }elseif($method_url[0] != '_'){ if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', $method_url, $matches)) { $http_method = strtoupper($matches[0][0]); $method_url = substr($method_url, strlen($http_method)); }else{ $http_method = 'GET'; } $url = $base_path. ($method_url=='index' || $method_url=='default' ? '' : $method_url); $url = rtrim($url,'/'); $this->routes[$http_method][$url] = $call; foreach ($params as $param){ if($param->getName()=='request_data'){ break; } $url .= $url=='' ? ':' : '/:'; $url .= $param->getName(); $this->routes[$http_method][$url] = $call; } } } } } if(version_compare(PHP_VERSION, '5.3.0') < 0){ require_once 'compat.php'; } class RestException extends Exception { public function __construct($http_status_code, $error_message = NULL) { parent::__construct($error_message, $http_status_code); } } interface iRespond { public function __formatResponse($result); public function __formatError($status_code, $message); } class DefaultResponse implements iRespond { function __formatResponse($result) { return $result; } function __formatError($statusCode, $message) { return array('error' => array('code' => $statusCode, 'message' => $message)); } } interface iAuthenticate { public function __isAuthenticated(); } interface iFormat { public function getMIMEMap(); public function setMIME($mime); public function getMIME(); public function setExtension($extension); public function getExtension(); public function encode($data, $human_readable=FALSE); public function decode($data); } class UrlEncodedFormat implements iFormat { const MIME = 'application/x-www-form-urlencoded'; const EXTENSION = 'post'; public function getMIMEMap() { return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); } public function getMIME(){ return UrlEncodedFormat::MIME; } public function getExtension(){ return UrlEncodedFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return http_build_query($data); } public function decode($data){ parse_str($data,$r); return $r; } public function __toString(){ return $this->getExtension(); } } class JsonFormat implements iFormat { const MIME ='application/json'; const EXTENSION = 'json'; public function getMIMEMap() { return array(JsonFormat::EXTENSION=>JsonFormat::MIME); } public function getMIME(){ return JsonFormat::MIME; } public function getExtension(){ return JsonFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); } public function decode($data){ return object_to_array(json_decode($data)); } private function json_format($json) { $tab = " "; $new_json = ""; $indent_level = 0; $in_string = FALSE; $len = strlen($json); for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $indent_level--; $new_json .= "\n".str_repeat($tab, $indent_level).$char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",\n" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c==0){ $in_string = TRUE; }elseif($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } public function __toString(){ return $this->getExtension(); } } class DocParser { private $params=array(); function parse($doc='') { if($doc==''){ return $this->params; } if(preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) return $this->params; $comment = trim($comment[1]); if(preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) return $this->params; $this->parseLines($lines[1]); return $this->params; } private function parseLines($lines) { foreach($lines as $line) { $parsedLine = $this->parseLine($line); if($parsedLine === false && !isset($this->params['description'])) { if(isset($desc)){ $this->params['description'] = implode(PHP_EOL, $desc); } $desc = array(); } elseif($parsedLine !== false) { $desc[] = $parsedLine; } } $desc = implode(' ', $desc); if(!empty($desc))$this->params['long_description'] = $desc; } private function parseLine($line) { $line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { if(strpos($line, ' ')>0){ $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2); }else{ $param = substr($line, 1); $value = ''; } if($this->setParam($param, $value)) return false; } return $line; } private function setParam($param, $value) { if($param == 'param' || $param == 'return') $value = $this->formatParamOrReturn($value); if($param == 'class') list($param, $value) = $this->formatClass($value); if(empty($this->params[$param])) { $this->params[$param] = $value; } else if($param == 'param'){ $arr = array($this->params[$param], $value); $this->params[$param] = $arr; } else { $this->params[$param] = $value + $this->params[$param]; } return true; } private function formatClass($value) { $r = preg_split("[\(|\)]",$value); if(is_array($r)){ $param = $r[0]; parse_str($r[1],$value); foreach ($value as $key => $val) { $val = explode(',', $val); if(count($val)>1)$value[$key]=$val; } }else{ $param='Unknown'; } return array($param, $value); } private function formatParamOrReturn($string) { $pos = strpos($string, ' '); $type = substr($string, 0, $pos); return '(' . $type . ')' . substr($string, $pos+1); } } function parse_doc($php_doc_comment){ $p = new DocParser(); return $p->parse($php_doc_comment); $p = new Parser($php_doc_comment); return $p; $php_doc_comment = preg_replace("/(^[\\s]*\\/\\*\\*) + class Restler { const VERSION = '2.0.2'; public $url; public $request_method; public $request_format; public $request_data=array(); public $cache_dir; public $base_dir; public $response = 'DefaultResponse'; public $response_format; protected $production_mode; protected $routes= array(); protected $format_map = array(); protected $service_class_instance; protected $service_method; protected $auth_classes = array(); protected $error_classes = array(); private $codes = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); protected $cached; public function __construct ($production_mode = FALSE) { $this->production_mode = $production_mode; $this->cache_dir = getcwd(); $this->base_dir = RESTLER_PATH; } public function __destruct () { if ($this->production_mode && !$this->cached) { $this->saveCache(); } } public function refreshCache () { $this->routes = array(); $this->cached = FALSE; } public function setSupportedFormats () { $args = func_get_args(); $extensions = array(); foreach ($args as $class_name) { if(!is_string($class_name) || !class_exists($class_name)){ throw new Exception("$class_name is not a vaild Format Class."); } $obj = new $class_name; if(! $obj instanceof iFormat){ throw new Exception('Invalid format class; must implement '. 'iFormat interface'); } foreach ($obj->getMIMEMap() as $extension => $mime) { if(!isset($this->format_map[$extension])) $this->format_map[$extension]=$class_name; if(!isset($this->format_map[$mime])) $this->format_map[$mime]=$class_name; $extensions[".$extension"]=TRUE; } } $this->format_map['default']=$args[0]; $this->format_map['extensions']=array_keys($extensions); } public function addAPIClass($class_name, $base_path = NULL) { if(!class_exists($class_name)){ throw new Exception("API class $class_name is missing."); } $this->loadCache(); if(!$this->cached){ if(is_null($base_path))$base_path=strtolower($class_name); $base_path = trim($base_path,'/'); if(strlen($base_path)>0)$base_path .= '/'; $this->generateMap($class_name, $base_path); } } public function addAuthenticationClass ($class_name, $base_path = NULL) { $this->auth_classes[] = $class_name; $this->addAPIClass($class_name, $base_path); } public function addErrorClass ($class_name) { $this->error_classes[] = $class_name; } public function handleError ($status_code, $error_message = NULL) { $method = "handle$status_code"; $handled = FALSE; foreach ($this->error_classes as $class_name) { if (method_exists($class_name, $method)) { $obj = new $class_name(); $obj->restler = $this; $obj->$method(); $handled = TRUE; } } if($handled)return; $message = $this->codes[$status_code] . (!$error_message ? '' : ': ' . $error_message); $this->setStatus($status_code); $this->sendData( call_user_func( array($this->response, '__formatError'), $status_code, $message) ); } public function handle () { if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); $this->url = $this->getPath(); $this->request_method = $this->getRequestMethod(); $this->response_format = $this->getResponseFormat(); $this->request_format = $this->getRequestFormat(); if(is_null($this->request_format)){ $this->request_format = $this->response_format; } if($this->request_method == 'PUT' || $this->request_method == 'POST'){ $this->request_data = $this->getRequestData(); } $o = $this->mapUrlToMethod(); if(!isset($o->class_name)){ $this->handleError(404); }else{ try { if($o->method_flag){ $auth_method = '__isAuthenticated'; if(!count($this->auth_classes))throw new RestException(401); foreach ($this->auth_classes as $auth_class) { $auth_obj = new $auth_class(); $auth_obj->restler=$this; $this->applyClassMetadata($auth_class, $auth_obj, $o); if (!method_exists($auth_obj, $auth_method)) { throw new RestException(401, 'Authentication Class '. 'should implement iAuthenticate'); }elseif(!$auth_obj->$auth_method()){ throw new RestException(401); } } } $this->applyClassMetadata(get_class($this->request_format), $this->request_format, $o); $pre_process = '_'.$this->request_format->getExtension().'_'. $o->method_name; $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); $object->restler=$this; if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); } switch ($o->method_flag) { case 3: $reflection_method = new ReflectionMethod($object, $o->method_name); $reflection_method->setAccessible(TRUE); $result = $reflection_method->invokeArgs($object, $o->arguments); break; case 2: case 1: default: $result = call_user_func_array(array($object, $o->method_name), $o->arguments); } } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } if (isset($result) && $result !== NULL) { $this->sendData($result); } } public function sendData($data) { $data = $this->response_format->encode($data, !$this->production_mode); $post_process = '_'.$this->service_method .'_'. $this->response_format->getExtension(); if(isset($this->service_class_instance) && method_exists($this->service_class_instance,$post_process)){ $data = call_user_func(array($this->service_class_instance, $post_process), $data); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: 0"); header('Content-Type: ' . $this->response_format->getMIME()); header("X-Powered-By: Luracast Restler v".Restler::VERSION); die($data); } public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ". $this->codes[strval($code)]); } public function removeCommonPath($first, $second, $char='/'){ $first = explode($char, $first); $second = explode($char, $second); while (count($second)){ if($first[0]==$second[0]){ array_shift($first); } else break; array_shift($second); } return implode($char, $first); } public function saveCache() { $file = $this->cache_dir . '/routes.php'; $s = '$o=array();'.PHP_EOL; foreach ($this->routes as $key => $value) { $s .= PHP_EOL.PHP_EOL.PHP_EOL."############### $key ###############" .PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\']=array();'; foreach ($value as $ke => $va) { $s .= PHP_EOL.PHP_EOL."#==== $key $ke".PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\'][\''.$ke.'\']='.str_replace(PHP_EOL, PHP_EOL."\t", var_export($va, TRUE)).';'; } } $s .= PHP_EOL.'return $o;'; $r=@file_put_contents($file, "cache_dir' needs to have ". "the permissions set to read/write/execute for everyone in order ". "to save cache and improve performance."); } protected function getPath () { $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])); $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); $path = str_replace($this->format_map['extensions'], '', $path); return $path; } protected function getRequestMethod () { $method = $_SERVER['REQUEST_METHOD']; if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } return $method; } protected function getRequestFormat () { $format=NULL; if(isset($_SERVER['CONTENT_TYPE'])){ $mime = explode(';', $_SERVER['CONTENT_TYPE']); $mime = $mime[0]; if($mime==UrlEncodedFormat::MIME){ $format = new UrlEncodedFormat(); }else{ if(isset($this->format_map[$mime])){ $format = $this->format_map[$mime]; $format = is_string($format) ? new $format: $format; $format->setMIME($mime); } } } return $format; } protected function getResponseFormat () { $format; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); if($extension && isset($this->format_map[$extension])){ $format = $this->format_map[$extension]; $format = is_string($format) ? new $format: $format; $format->setExtension($extension); return $format; } if(isset($_SERVER['HTTP_ACCEPT'])){ $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); foreach ($accepts as $accept) { if($extension && isset($this->format_map[$accept])){ $format = $this->format_map[$accept]; $format = is_string($format) ? new $format: $format; $format->setMIME($accept); return $format; } } } $format = $this->format_map['default']; return is_string($format) ? new $format: $format; } protected function getRequestData() { try{ $r = file_get_contents('php://input'); if(is_null($r))return $_GET; $r =$this->request_format->decode($r); return is_null($r) ? array(): $r; } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } protected function mapUrlToMethod () { if(!isset($this->routes[$this->request_method])){ return array(); } $urls = $this->routes[$this->request_method]; if(!$urls)return array(); $found = FALSE; $this->request_data += $_GET; $params = array('request_data'=>$this->request_data); $params += $this->request_data; foreach ($urls as $url => $call) { $call = (object)$call; if(strstr($url, ':')){ $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); if (preg_match(":^$regex$:", $this->url, $matches)) { foreach ($matches as $arg => $match) { if (isset($call->arguments[$arg])){ $params[$arg] = $match; } } $found = TRUE; break; } }elseif ($url == $this->url){ $found = TRUE; break; } } if($found){ $p = $call->defaults; foreach ($call->arguments as $key => $value) { if(isset($params[$key]))$p[$value] = $params[$key]; } $call->arguments=$p; return $call; } } protected function applyClassMetadata($class_name, $instance, $method_info){ if(isset($method_info->metadata[$class_name]) && is_array($method_info->metadata[$class_name])){ foreach ($method_info->metadata[$class_name] as $property => $value){ if(property_exists($class_name, $property)){ $reflection_property = new ReflectionProperty($class_name, $property); $reflection_property->setValue($instance, $value); } } } } protected function loadCache() { if ($this->cached !== NULL) { return; } $file = $this->cache_dir . '/routes.php'; $this->cached = FALSE; if ($this->production_mode) { if (file_exists($file)) { $routes = include($file); } if (isset($routes) && is_array($routes)) { $this->routes = $routes; $this->cached = TRUE; } } else { } } protected function generateMap ($class_name, $base_path = "") { $reflection = new ReflectionClass($class_name); $class_metadata = parse_doc($reflection->getDocComment()); $methods = $reflection->getMethods( ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $doc = $method->getDocComment(); $arguments = array(); $defaults = array(); $metadata = $class_metadata+parse_doc($doc); $params = $method->getParameters(); $position=0; foreach ($params as $param){ $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; $position++; } $method_flag = $method->isProtected() ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), 'arguments'=>$arguments, 'defaults'=>$defaults, 'metadata'=>$metadata, 'method_flag'=>$method_flag ); $method_url = strtolower($method->getName()); if (preg_match_all( '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $http_method = $match[1]; $url = rtrim($base_path . $match[2],'/'); $this->routes[$http_method][$url] = $call; } }elseif($method_url[0] != '_'){ if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', $method_url, $matches)) { $http_method = strtoupper($matches[0][0]); $method_url = substr($method_url, strlen($http_method)); }else{ $http_method = 'GET'; } $url = $base_path. ($method_url=='index' || $method_url=='default' ? '' : $method_url); $url = rtrim($url,'/'); $this->routes[$http_method][$url] = $call; foreach ($params as $param){ if($param->getName()=='request_data'){ break; } $url .= $url=='' ? ':' : '/:'; $url .= $param->getName(); $this->routes[$http_method][$url] = $call; } } } } } if(version_compare(PHP_VERSION, '5.3.0') < 0){ require_once 'compat.php'; } class RestException extends Exception { public function __construct($http_status_code, $error_message = NULL) { parent::__construct($error_message, $http_status_code); } } interface iRespond { public function __formatResponse($result); public function __formatError($status_code, $message); } class DefaultResponse implements iRespond { function __formatResponse($result) { return $result; } function __formatError($statusCode, $message) { return array('error' => array('code' => $statusCode, 'message' => $message)); } } interface iAuthenticate { public function __isAuthenticated(); } interface iFormat { public function getMIMEMap(); public function setMIME($mime); public function getMIME(); public function setExtension($extension); public function getExtension(); public function encode($data, $human_readable=FALSE); public function decode($data); } class UrlEncodedFormat implements iFormat { const MIME = 'application/x-www-form-urlencoded'; const EXTENSION = 'post'; public function getMIMEMap() { return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); } public function getMIME(){ return UrlEncodedFormat::MIME; } public function getExtension(){ return UrlEncodedFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return http_build_query($data); } public function decode($data){ parse_str($data,$r); return $r; } public function __toString(){ return $this->getExtension(); } } class JsonFormat implements iFormat { const MIME ='application/json'; const EXTENSION = 'json'; public function getMIMEMap() { return array(JsonFormat::EXTENSION=>JsonFormat::MIME); } public function getMIME(){ return JsonFormat::MIME; } public function getExtension(){ return JsonFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); } public function decode($data){ return object_to_array(json_decode($data)); } private function json_format($json) { $tab = " "; $new_json = ""; $indent_level = 0; $in_string = FALSE; $len = strlen($json); for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $indent_level--; $new_json .= "\n".str_repeat($tab, $indent_level).$char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",\n" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c==0){ $in_string = TRUE; }elseif($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } public function __toString(){ return $this->getExtension(); } } class DocParser { private $params=array(); function parse($doc='') { if($doc==''){ return $this->params; } if(preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) return $this->params; $comment = trim($comment[1]); if(preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) return $this->params; $this->parseLines($lines[1]); return $this->params; } private function parseLines($lines) { foreach($lines as $line) { $parsedLine = $this->parseLine($line); if($parsedLine === false && !isset($this->params['description'])) { if(isset($desc)){ $this->params['description'] = implode(PHP_EOL, $desc); } $desc = array(); } elseif($parsedLine !== false) { $desc[] = $parsedLine; } } $desc = implode(' ', $desc); if(!empty($desc))$this->params['long_description'] = $desc; } private function parseLine($line) { $line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { if(strpos($line, ' ')>0){ $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2); }else{ $param = substr($line, 1); $value = ''; } if($this->setParam($param, $value)) return false; } return $line; } private function setParam($param, $value) { if($param == 'param' || $param == 'return') $value = $this->formatParamOrReturn($value); if($param == 'class') list($param, $value) = $this->formatClass($value); if(empty($this->params[$param])) { $this->params[$param] = $value; } else if($param == 'param'){ $arr = array($this->params[$param], $value); $this->params[$param] = $arr; } else { $this->params[$param] = $value + $this->params[$param]; } return true; } private function formatClass($value) { $r = preg_split("[\(|\)]",$value); if(is_array($r)){ $param = $r[0]; parse_str($r[1],$value); foreach ($value as $key => $val) { $val = explode(',', $val); if(count($val)>1)$value[$key]=$val; } }else{ $param='Unknown'; } return array($param, $value); } private function formatParamOrReturn($string) { $pos = strpos($string, ' '); $type = substr($string, 0, $pos); return '(' . $type . ')' . substr($string, $pos+1); } } function parse_doc($php_doc_comment){ $p = new DocParser(); return $p->parse($php_doc_comment); $p = new Parser($php_doc_comment); return $p; $php_doc_comment = preg_replace("/(^[\\s]*\\/\\*\\*) |(^[\\s]\\*\\/) |(^[\\s]*\\*?\\s) |(^[\\s]*) - |(^[\\t]*)/ixm", "", $php_doc_comment); $php_doc_comment = str_replace("\r", "", $php_doc_comment); $php_doc_comment = preg_replace("/([\\t])+/", "\t", $php_doc_comment); return explode("\n", $php_doc_comment); $php_doc_comment = trim(preg_replace('/\r?\n *\* */', ' ', $php_doc_comment)); return $php_doc_comment; preg_match_all('/@([a-z]+)\s+(.*?)\s*(?=$|@[a-z]+\s)/s', $php_doc_comment, $matches); return array_combine($matches[1], $matches[2]); } function object_to_array($object, $utf_encode=TRUE) { if(is_array($object) || is_object($object)) { $array = array(); foreach($object as $key => $value) { $value = object_to_array($value, $utf_encode); if($utf_encode && is_string($value)){ $value = utf8_encode($value); } $array[$key] = $value; } return $array; } return $object; } function autoload_formats($class_name) { $class_name=strtolower($class_name); $file = RESTLER_PATH."/$class_name/$class_name.php"; if (file_exists($file)) { require_once($file); } } spl_autoload_register('autoload_formats'); if(!function_exists('isRestlerCompatibilityModeEnabled')){ function isRestlerCompatibilityModeEnabled(){ return FALSE; } } define('RESTLER_PATH', dirname(__FILE__)); \ No newline at end of file + |(^[\\t]*)/ixm", "", $php_doc_comment); $php_doc_comment = str_replace("\r", "", $php_doc_comment); $php_doc_comment = preg_replace("/([\\t])+/", "\t", $php_doc_comment); return explode("\n", $php_doc_comment); $php_doc_comment = trim(preg_replace('/\r?\n *\* */', ' ', $php_doc_comment)); return $php_doc_comment; preg_match_all('/@([a-z]+)\s+(.*?)\s*(?=$|@[a-z]+\s)/s', $php_doc_comment, $matches); return array_combine($matches[1], $matches[2]); } function object_to_array($object, $utf_encode=TRUE) { if(is_array($object) || is_object($object)) { $array = array(); foreach($object as $key => $value) { $value = object_to_array($value, $utf_encode); if($utf_encode && is_string($value)){ $value = utf8_encode($value); } $array[$key] = $value; } return $array; } return $object; } function autoload_formats($class_name) { $class_name=strtolower($class_name); $file = RESTLER_PATH."/$class_name/$class_name.php"; if (file_exists($file)) { require_once($file); } elseif (file_exists("$class_name.php")) { require_once ("$class_name.php"); } } spl_autoload_register('autoload_formats'); if(!function_exists('isRestlerCompatibilityModeEnabled')){ function isRestlerCompatibilityModeEnabled(){ return FALSE; } } define('RESTLER_PATH', dirname(__FILE__)); \ No newline at end of file From 9746eb821561013fb961f79eefbc135ab74a2b13 Mon Sep 17 00:00:00 2001 From: Luracast Date: Thu, 29 Sep 2011 18:32:32 +0800 Subject: [PATCH 18/61] deleting .svn in plistformat folder. It fixes #4 issue --- restler/plistformat/.svn/entries | 166 --- .../CFBinaryPropertyList.php.svn-base | 970 ------------------ .../text-base/CFPropertyList.php.svn-base | 587 ----------- .../.svn/text-base/CFType.php.svn-base | 742 -------------- .../text-base/CFTypeDetector.php.svn-base | 167 --- .../.svn/text-base/IOException.php.svn-base | 99 -- .../text-base/PListException.php.svn-base | 22 - 7 files changed, 2753 deletions(-) delete mode 100644 restler/plistformat/.svn/entries delete mode 100644 restler/plistformat/.svn/text-base/CFBinaryPropertyList.php.svn-base delete mode 100644 restler/plistformat/.svn/text-base/CFPropertyList.php.svn-base delete mode 100644 restler/plistformat/.svn/text-base/CFType.php.svn-base delete mode 100644 restler/plistformat/.svn/text-base/CFTypeDetector.php.svn-base delete mode 100644 restler/plistformat/.svn/text-base/IOException.php.svn-base delete mode 100644 restler/plistformat/.svn/text-base/PListException.php.svn-base diff --git a/restler/plistformat/.svn/entries b/restler/plistformat/.svn/entries deleted file mode 100644 index 74a1b625b..000000000 --- a/restler/plistformat/.svn/entries +++ /dev/null @@ -1,166 +0,0 @@ -10 - -dir -0 -svn://localhost/Restler/trunk/Restler/plistformat -svn://localhost/Restler -add - - - - - - - - - - - - - - - - - - - -ce0eef8e-4246-4db1-a4b2-968e926b47c2 - -CFPropertyList.php -file - - - -add - -0f1e8ab2446ca3efc549a0827098f007 - - - - - - - - - - - -copied -svn://localhost/Restler/trunk/Restler/plist/CFPropertyList.php -12 - -CFType.php -file - - - -add - -cdea3ed5c0b042bb991dae717782054b - - - - - - - - - - - -copied -svn://localhost/Restler/trunk/Restler/plist/CFType.php -12 - -PListException.php -file - - - -add - -6aab48e2e1e2d38666e6dc0ebc644358 - - - - - - - - - - - -copied -svn://localhost/Restler/trunk/Restler/plist/PListException.php -12 - -CFBinaryPropertyList.php -file - - - -add - -03b16947dab62b9f6484f8abdab84666 - - - - - - - - - - - -copied -svn://localhost/Restler/trunk/Restler/plist/CFBinaryPropertyList.php -12 - -CFTypeDetector.php -file - - - -add - -1626382122aedd2aacb0fb6bede301ea - - - - - - - - - - - -copied -svn://localhost/Restler/trunk/Restler/plist/CFTypeDetector.php -12 - -IOException.php -file - - - -add - -0a452a75451b9e95e07cdc25e96cf1ea - - - - - - - - - - - -copied -svn://localhost/Restler/trunk/Restler/plist/IOException.php -12 - diff --git a/restler/plistformat/.svn/text-base/CFBinaryPropertyList.php.svn-base b/restler/plistformat/.svn/text-base/CFBinaryPropertyList.php.svn-base deleted file mode 100644 index f946193da..000000000 --- a/restler/plistformat/.svn/text-base/CFBinaryPropertyList.php.svn-base +++ /dev/null @@ -1,970 +0,0 @@ - - * @author Christian Kruse - * @package plist - * @version $Id$ - */ - -/** - * Facility for reading and writing binary PropertyLists. Ported from {@link http://www.opensource.apple.com/source/CF/CF-476.15/CFBinaryPList.c CFBinaryPList.c}. - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @example example-read-02.php Read a Binary PropertyList - * @example example-read-03.php Read a PropertyList without knowing the type - */ -abstract class CFBinaryPropertyList { - /** - * Content of the plist (unparsed string) - * @var string - */ - protected $content = NULL; - - /** - * position in the (unparsed) string - * @var integer - */ - protected $pos = 0; - - /** - * Table containing uniqued objects - * @var array - */ - protected $uniqueTable = Array(); - - /** - * Number of objects in file - * @var integer - */ - protected $countObjects = 0; - - /** - * The length of all strings in the file (byte length, not character length) - * @var integer - */ - protected $stringSize = 0; - - /** - * The length of all ints in file (byte length) - * @var integer - */ - protected $intSize = 0; - - /** - * The length of misc objects (i.e. not integer and not string) in file - * @var integer - */ - protected $miscSize = 0; - - /** - * Number of object references in file (needed to calculate reference byte length) - * @var integer - */ - protected $objectRefs = 0; - - /** - * Number of objects written during save phase; needed to calculate the size of the object table - * @var integer - */ - protected $writtenObjectCount = 0; - - /** - * Table containing all objects in the file - */ - protected $objectTable = Array(); - - /** - * The size of object references - */ - protected $objectRefSize = 0; - - /** - * The „offsets” (i.e. the different entries) in the file - */ - protected $offsets = Array(); - - /** - * Read a „null type” (filler byte, true, false, 0 byte) - * @param $length The byte itself - * @return the byte value (e.g. CFBoolean(true), CFBoolean(false), 0 or 15) - * @throws PListException on encountering an unknown null type - */ - protected function readBinaryNullType($length) { - switch($length) { - case 0: return 0; // null type - case 8: return new CFBoolean(false); - case 9: return new CFBoolean(true); - case 15: return 15; // fill type - } - - throw new PListException("unknown null type: $length"); - } - - /** - * Create an 64 bit integer using bcmath or gmp - * @param int $hi The higher word - * @param int $lo The lower word - * @return mixed The integer (as int if possible, as string if not possible) - * @throws PListException if neither gmp nor bc available - */ - protected static function make64Int($hi,$lo) { - // on x64, we can just use int - if(PHP_INT_SIZE > 4) return (((int)$hi)<<32) | ((int)$lo); - - // lower word has to be unsigned since we don't use bitwise or, we use bcadd/gmp_add - $lo = sprintf("%u", $lo); - - // use GMP or bcmath if possible - if(function_exists("gmp_mul")) return gmp_strval(gmp_add(gmp_mul($hi, "4294967296"), $lo)); - - if(function_exists("bcmul")) return bcadd(bcmul($hi,"4294967296"), $lo); - - if(class_exists('Math_BigInteger')) { - $bi = new Math_BigInteger($hi); - return $bi->multiply("4294967296")->add($lo)->toString(); - } - - throw new PListException("either gmp or bc has to be installed, or the Math_BigInteger has to be available!"); - } - - /** - * Read an integer value - * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” - * @return CFNumber The integer value - * @throws PListException if integer val is invalid - * @throws IOException if read error occurs - * @uses make64Int() to overcome PHP's big integer problems - */ - protected function readBinaryInt($length) { - if($length > 3) throw new PListException("Integer greater than 8 bytes: $length"); - - $nbytes = 1 << $length; - - $val = null; - if(strlen($buff = substr($this->content, $this->pos, $nbytes)) != $nbytes) throw IOException::readError(""); - $this->pos += $nbytes; - - switch($length) { - case 0: - $val = unpack("C", $buff); - $val = $val[1]; - break; - case 1: - $val = unpack("n", $buff); - $val = $val[1]; - break; - case 2: - $val = unpack("N", $buff); - $val = $val[1]; - break; - case 3: - $words = unpack("Nhighword/Nlowword",$buff); - //$val = $words['highword'] << 32 | $words['lowword']; - $val = self::make64Int($words['highword'],$words['lowword']); - break; - } - - return new CFNumber($val); - } - - /** - * Read a real value - * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” - * @return CFNumber The real value - * @throws PListException if real val is invalid - * @throws IOException if read error occurs - */ - protected function readBinaryReal($length) { - if($length > 3) throw new PListException("Real greater than 8 bytes: $length"); - - $nbytes = 1 << $length; - $val = null; - if(strlen($buff = substr($this->content,$this->pos, $nbytes)) != $nbytes) throw IOException::readError(""); - $this->pos += $nbytes; - - switch($length) { - case 0: // 1 byte float? must be an error - case 1: // 2 byte float? must be an error - $x = $length + 1; - throw new PListException("got {$x} byte float, must be an error!"); - case 2: - $val = unpack("f", strrev($buff)); - $val = $val[1]; - break; - case 3: - $val = unpack("d", strrev($buff)); - $val = $val[1]; - break; - } - - return new CFNumber($val); - } - - /** - * Read a date value - * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” - * @return CFDate The date value - * @throws PListException if date val is invalid - * @throws IOException if read error occurs - */ - protected function readBinaryDate($length) { - if($length > 3) throw new PListException("Date greater than 8 bytes: $length"); - - $nbytes = 1 << $length; - $val = null; - if(strlen($buff = substr($this->content, $this->pos, $nbytes)) != $nbytes) throw IOException::readError(""); - $this->pos += $nbytes; - - switch($length) { - case 0: // 1 byte CFDate is an error - case 1: // 2 byte CFDate is an error - $x = $length + 1; - throw new PListException("{$x} byte CFdate, error"); - - case 2: - $val = unpack("f", strrev($buff)); - $val = $val[1]; - break; - case 3: - $val = unpack("d", strrev($buff)); - $val = $val[1]; - break; - } - - return new CFDate($val,CFDate::TIMESTAMP_APPLE); - } - - /** - * Read a data value - * @param integer $length The length (in bytes) of the integer value, coded as „set bit $length to 1” - * @return CFData The data value - * @throws IOException if read error occurs - */ - protected function readBinaryData($length) { - if($length == 0) $buff = ""; - else { - $buff = substr($this->content, $this->pos, $length); - if(strlen($buff) != $length) throw IOException::readError(""); - $this->pos += $length; - } - - return new CFData($buff,false); - } - - /** - * Read a string value, usually coded as utf8 - * @param integer $length The length (in bytes) of the string value - * @return CFString The string value, utf8 encoded - * @throws IOException if read error occurs - */ - protected function readBinaryString($length) { - if($length == 0) $buff = ""; - else { - if(strlen($buff = substr($this->content, $this->pos, $length)) != $length) throw IOException::readError(""); - $this->pos += $length; - } - - if(!isset($this->uniqueTable[$buff])) $this->uniqueTable[$buff] = true; - return new CFString($buff); - } - - /** - * Convert the given string from one charset to another. - * Trying to use MBString, Iconv, Recode - in that particular order. - * @param string $string the string to convert - * @param string $fromCharset the charset the given string is currently encoded in - * @param string $toCharset the charset to convert to, defaults to UTF-8 - * @return string the converted string - * @throws PListException on neither MBString, Iconv, Recode being available - */ - public static function convertCharset($string, $fromCharset, $toCharset='UTF-8') { - if(function_exists('mb_convert_encoding')) return mb_convert_encoding($string, $toCharset, $fromCharset); - if(function_exists('iconv')) return iconv($fromCharset, $toCharset, $string); - if(function_exists('recode_string')) return recode_string($fromCharset .'..'. $toCharset, $string); - - throw new PListException('neither iconv nor mbstring supported. how are we supposed to work on strings here?'); - } - - /** - * Count characters considering character set - * Trying to use MBString, Iconv - in that particular order. - * @param string $string the string to convert - * @param string $charset the charset the given string is currently encoded in - * @return integer The number of characters in that string - * @throws PListException on neither MBString, Iconv being available - */ - public static function charsetStrlen($string,$charset="UTF-8") { - if(function_exists('mb_strlen')) return mb_strlen($string, $charset); - if(function_exists('iconv_strlen')) return iconv_strlen($string,$charset); - - throw new PListException('neither iconv nor mbstring supported. how are we supposed to work on strings here?'); - } - - /** - * Read a unicode string value, coded as UTF-16BE - * @param integer $length The length (in bytes) of the string value - * @return CFString The string value, utf8 encoded - * @throws IOException if read error occurs - */ - protected function readBinaryUnicodeString($length) { - /* The problem is: we get the length of the string IN CHARACTERS; - since a char in UTF-16 can be 16 or 32 bit long, we don't really know - how long the string is in bytes */ - if(strlen($buff = substr($this->content, $this->pos, 2*$length)) != 2*$length) throw IOException::readError(""); - $this->pos += 2 * $length; - - if(!isset($this->uniqueTable[$buff])) $this->uniqueTable[$buff] = true; - return new CFString(self::convertCharset($buff, "UTF-16BE", "UTF-8")); - } - - /** - * Read an array value, including contained objects - * @param integer $length The number of contained objects - * @return CFArray The array value, including the objects - * @throws IOException if read error occurs - */ - protected function readBinaryArray($length) { - $ary = new CFArray(); - - // first: read object refs - if($length != 0) { - if(strlen($buff = substr($this->content, $this->pos, $length * $this->objectRefSize)) != $length * $this->objectRefSize) throw IOException::readError(""); - $this->pos += $length * $this->objectRefSize; - - $objects = unpack($this->objectRefSize == 1 ? "C*" : "n*", $buff); - - // now: read objects - for($i=0;$i<$length;++$i) { - $object = $this->readBinaryObjectAt($objects[$i+1]+1,$this->objectRefSize); - $ary->add($object); - } - } - - return $ary; - } - - /** - * Read a dictionary value, including contained objects - * @param integer $length The number of contained objects - * @return CFDictionary The dictionary value, including the objects - * @throws IOException if read error occurs - */ - protected function readBinaryDict($length) { - $dict = new CFDictionary(); - - // first: read keys - if($length != 0) { - if(strlen($buff = substr($this->content, $this->pos, $length * $this->objectRefSize)) != $length * $this->objectRefSize) throw IOException::readError(""); - $this->pos += $length * $this->objectRefSize; - $keys = unpack(($this->objectRefSize == 1 ? "C*" : "n*"), $buff); - - // second: read object refs - if(strlen($buff = substr($this->content, $this->pos, $length * $this->objectRefSize)) != $length * $this->objectRefSize) throw IOException::readError(""); - $this->pos += $length * $this->objectRefSize; - $objects = unpack(($this->objectRefSize == 1 ? "C*" : "n*"), $buff); - - // read real keys and objects - for($i=0;$i<$length;++$i) { - $key = $this->readBinaryObjectAt($keys[$i+1]+1); - $object = $this->readBinaryObjectAt($objects[$i+1]+1); - $dict->add($key->getValue(),$object); - } - } - - return $dict; - } - - /** - * Read an object type byte, decode it and delegate to the correct reader function - * @return mixed The value of the delegate reader, so any of the CFType subclasses - * @throws IOException if read error occurs - */ - function readBinaryObject() { - // first: read the marker byte - if(strlen($buff = substr($this->content,$this->pos,1)) != 1) throw IOException::readError(""); - $this->pos++; - - $object_length = unpack("C*", $buff); - $object_length = $object_length[1] & 0xF; - $buff = unpack("H*", $buff); - $buff = $buff[1]; - - $object_type = substr($buff, 0, 1); - if($object_type != "0" && $object_length == 15) { - $object_length = $this->readBinaryObject($this->objectRefSize); - $object_length = $object_length->getValue(); - } - - $retval = null; - switch($object_type) { - case '0': // null, false, true, fillbyte - $retval = $this->readBinaryNullType($object_length); - break; - case '1': // integer - $retval = $this->readBinaryInt($object_length); - break; - case '2': // real - $retval = $this->readBinaryReal($object_length); - break; - case '3': // date - $retval = $this->readBinaryDate($object_length); - break; - case '4': // data - $retval = $this->readBinaryData($object_length); - break; - case '5': // byte string, usually utf8 encoded - $retval = $this->readBinaryString($object_length); - break; - case '6': // unicode string (utf16be) - $retval = $this->readBinaryUnicodeString($object_length); - break; - case 'a': // array - $retval = $this->readBinaryArray($object_length); - break; - case 'd': // dictionary - $retval = $this->readBinaryDict($object_length); - break; - } - - return $retval; - } - - /** - * Read an object type byte at position $pos, decode it and delegate to the correct reader function - * @param integer $pos The table position in the offsets table - * @return mixed The value of the delegate reader, so any of the CFType subclasses - */ - function readBinaryObjectAt($pos) { - $this->pos = $this->offsets[$pos]; - return $this->readBinaryObject(); - } - - /** - * Parse a binary plist string - * @return void - * @throws IOException if read error occurs - */ - public function parseBinaryString() { - $this->uniqueTable = Array(); - $this->countObjects = 0; - $this->stringSize = 0; - $this->intSize = 0; - $this->miscSize = 0; - $this->objectRefs = 0; - - $this->writtenObjectCount = 0; - $this->objectTable = Array(); - $this->objectRefSize = 0; - - $this->offsets = Array(); - - // first, we read the trailer: 32 byte from the end - $buff = substr($this->content,-32); - - $infos = unpack("x6/Coffset_size/Cobject_ref_size/x4/Nnumber_of_objects/x4/Ntop_object/x4/Ntable_offset",$buff); - - // after that, get the offset table - $coded_offset_table = substr($this->content,$infos['table_offset'],$infos['number_of_objects'] * $infos['offset_size']); - if(strlen($coded_offset_table) != $infos['number_of_objects'] * $infos['offset_size']) throw IOException::readError(""); - $this->countObjects = $infos['number_of_objects']; - - // decode offset table - $formats = Array("","C*","n*",NULL,"N*"); - if($infos['offset_size'] == 3) { # since PHP does not support parenthesis in pack/unpack expressions, - # "(H6)*" does not work and we have to work round this by repeating the - # expression as often as it fits in the string - $this->offsets = array(NULL); - while($coded_offset_table) { - $str = unpack("H6",$coded_offset_table); - $this->offsets[] = hexdec($str[1]); - $coded_offset_table = substr($coded_offset_table,3); - } - } - else $this->offsets = unpack($formats[$infos['offset_size']],$coded_offset_table); - - $this->uniqueTable = Array(); - $this->objectRefSize = $infos['object_ref_size']; - - $top = $this->readBinaryObjectAt($infos['top_object']+1); - $this->add($top); - } - - /** - * Read a binary plist stream - * @param resource $stream The stream to read - * @return void - * @throws IOException if read error occurs - */ - function readBinaryStream($stream) { - $str = stream_get_contents($stream); - $this->parseBinary($str); - } - - /** - * parse a binary plist string - * @param string $content The stream to read, defaults to {@link $this->content} - * @return void - * @throws IOException if read error occurs - */ - function parseBinary($content=NULL) { - if($content !== NULL) $this->content = $content; - $this->pos = 0; - - $this->parseBinaryString(); - } - - /** - * Read a binary plist file - * @param string $file The file to read - * @return void - * @throws IOException if read error occurs - */ - function readBinary($file) { - if(!($fd = fopen($file,"rb"))) throw new IOException("Could not open file {$file}!"); - $this->readBinaryStream($fd); - fclose($fd); - } - - /** - * calculate the bytes needed for a size integer value - * @param integer $int The integer value to calculate - * @return integer The number of bytes needed - */ - public static function bytesSizeInt($int) { - $nbytes = 0; - - if($int > 0xE) $nbytes += 2; // 2 size-bytes - if($int > 0xFF) $nbytes += 1; // 3 size-bytes - if($int > 0xFFFF) $nbytes += 2; // 5 size-bytes - - return $nbytes; - } - - /** - * Calculate the byte needed for a „normal” integer value - * @param integer $int The integer value - * @return integer The number of bytes needed + 1 (because of the „marker byte”) - */ - public static function bytesInt($int) { - $nbytes = 1; - - if($int > 0xFF) $nbytes += 1; // 2 byte integer - if($int > 0xFFFF) $nbytes += 2; // 4 byte integer - if($int > 0xFFFFFFFF) $nbytes += 4; // 8 byte integer - if($int < 0) $nbytes += 7; // 8 byte integer (since it is signed) - - return $nbytes + 1; // one „marker” byte - } - - /** - * „pack” a value (i.e. write the binary representation as big endian to a string) with the specified size - * @param integer $nbytes The number of bytes to pack - * @param integer $int the integer value to pack - * @return string The packed value as string - */ - public static function packItWithSize($nbytes,$int) { - $formats = Array("C", "n", "N", "N"); - $format = $formats[$nbytes-1]; - $ret = ''; - - if($nbytes == 3) return substr(pack($format, $int), -3); - return pack($format, $int); - } - - /** - * Calculate the bytes needed to save the number of objects - * @param integer $count_objects The number of objects - * @return integer The number of bytes - */ - public static function bytesNeeded($count_objects) { - $nbytes = 0; - - while($count_objects >= 1) { - $nbytes++; - $count_objects /= 256; - } - - return $nbytes; - } - - /** - * Code an integer to byte representation - * @param integer $int The integer value - * @return string The packed byte value - */ - public static function intBytes($int) { - $intbytes = ""; - - if($int > 0xFFFF) $intbytes = "\x12".pack("N", $int); // 4 byte integer - elseif($int > 0xFF) $intbytes = "\x11".pack("n", $int); // 2 byte integer - else $intbytes = "\x10".pack("C", $int); // 8 byte integer - - return $intbytes; - } - - /** - * Code an type byte, consisting of the type marker and the length of the type - * @param string $type The type byte value (i.e. "d" for dictionaries) - * @param integer $type_len The length of the type - * @return string The packed type byte value - */ - public static function typeBytes($type,$type_len) { - $optional_int = ""; - - if($type_len < 15) $type .= sprintf("%x", $type_len); - else { - $type .= "f"; - $optional_int = self::intBytes($type_len); - } - - return pack("H*", $type).$optional_int; - } - - /** - * Count number of objects and create a unique table for strings - * @param $value The value to count and unique - * @return void - */ - protected function uniqueAndCountValues($value) { - // no uniquing for other types than CFString and CFData - if($value instanceof CFNumber) { - $val = $value->getValue(); - if(intval($val) == $val && !is_float($val) && strpos($val,'.') === false) $this->intSize += self::bytesInt($val); - else $this->miscSize += 9; // 9 bytes (8 + marker byte) for real - $this->countObjects++; - return; - } - elseif($value instanceof CFDate) { - $this->miscSize += 9; // since date in plist is real, we need 9 byte (8 + marker byte) - $this->countObjects++; - return; - } - elseif($value instanceof CFBoolean) { - $this->countObjects++; - $this->miscSize += 1; - return; - } - elseif($value instanceof CFArray) { - $cnt = 0; - foreach($value as $v) { - ++$cnt; - $this->uniqueAndCountValues($v); - $this->objectRefs++; // each array member is a ref - } - - $this->countObjects++; - $this->intSize += self::bytesSizeInt($cnt); - $this->miscSize++; // marker byte for array - return; - } - elseif($value instanceof CFDictionary) { - $cnt = 0; - foreach($value as $k => $v) { - ++$cnt; - if(!isset($this->uniqueTable[$k])) { - $this->uniqueTable[$k] = 0; - $len = self::binaryStrlen($k); - $this->stringSize += $len + 1; - $this->intSize += self::bytesSizeInt(self::charsetStrlen($k,'UTF-8')); - } - - $this->objectRefs += 2; // both, key and value, are refs - $this->uniqueTable[$k]++; - $this->uniqueAndCountValues($v); - } - - $this->countObjects++; - $this->miscSize++; // marker byte for dict - $this->intSize += self::bytesSizeInt($cnt); - return; - } - elseif($value instanceOf CFData) { - $val = $value->getValue(); - $len = strlen($val); - $this->intSize += self::bytesSizeInt($len); - $this->miscSize += $len + 1; - $this->countObjects++; - return; - } - else $val = $value->getValue(); - - if(!isset($this->uniqueTable[$val])) { - $this->uniqueTable[$val] = 0; - $len = self::binaryStrlen($val); - $this->stringSize += $len + 1; - $this->intSize += self::bytesSizeInt(self::charsetStrlen($val,'UTF-8')); - } - $this->uniqueTable[$val]++; - } - - /** - * Convert CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray - * @return string The binary plist content - */ - public function toBinary() { - $this->uniqueTable = Array(); - $this->countObjects = 0; - $this->stringSize = 0; - $this->intSize = 0; - $this->miscSize = 0; - $this->objectRefs = 0; - - $this->writtenObjectCount = 0; - $this->objectTable = Array(); - $this->objectRefSize = 0; - - $this->offsets = Array(); - - $binary_str = "bplist00"; - $value = $this->getValue(true); - $this->uniqueAndCountValues($value); - - $this->countObjects += count($this->uniqueTable); - $this->objectRefSize = self::bytesNeeded($this->countObjects); - $file_size = $this->stringSize + $this->intSize + $this->miscSize + $this->objectRefs * $this->objectRefSize + 40; - $offset_size = self::bytesNeeded($file_size); - $table_offset = $file_size - 32; - - $this->objectTable = Array(); - $this->writtenObjectCount = 0; - $this->uniqueTable = Array(); // we needed it to calculate several values - $value->toBinary($this); - - $object_offset = 8; - $offsets = Array(); - - for($i=0;$iobjectTable);++$i) { - $binary_str .= $this->objectTable[$i]; - $offsets[$i] = $object_offset; - $object_offset += strlen($this->objectTable[$i]); - } - - for($i=0;$iobjectRefSize); - $binary_str .= pack("x4N", $this->countObjects); - $binary_str .= pack("x4N", 0); - $binary_str .= pack("x4N", $table_offset); - - return $binary_str; - } - - /** - * Counts the number of bytes the string will have when coded; utf-16be if non-ascii characters are present. - * @param string $val The string value - * @return integer The length of the coded string in bytes - */ - protected static function binaryStrlen($val) { - for($i=0;$i= 128) { - $val = self::convertCharset($val, 'UTF-8', 'UTF-16BE'); - return strlen($val); - } - } - - return strlen($val); - } - - /** - * Uniques and transforms a string value to binary format and adds it to the object table - * @param string $val The string value - * @return integer The position in the object table - */ - public function stringToBinary($val) { - $saved_object_count = -1; - - if(!isset($this->uniqueTable[$val])) { - $saved_object_count = $this->writtenObjectCount++; - $this->uniqueTable[$val] = $saved_object_count; - $utf16 = false; - - for($i=0;$i= 128) { - $utf16 = true; - break; - } - } - - if($utf16) { - $bdata = self::typeBytes("6", mb_strlen($val,'UTF-8')); // 6 is 0110, unicode string (utf16be) - $val = self::convertCharset($val, 'UTF-8', 'UTF-16BE'); - $this->objectTable[$saved_object_count] = $bdata.$val; - } - else { - $bdata = self::typeBytes("5", strlen($val)); // 5 is 0101 which is an ASCII string (seems to be ASCII encoded) - $this->objectTable[$saved_object_count] = $bdata.$val; - } - } - else $saved_object_count = $this->uniqueTable[$val]; - - return $saved_object_count; - } - - /** - * Codes an integer to binary format - * @param integer $value The integer value - * @return string the coded integer - */ - protected function intToBinary($value) { - $nbytes = 0; - if($value > 0xFF) $nbytes = 1; // 1 byte integer - if($value > 0xFFFF) $nbytes += 1; // 4 byte integer - if($value > 0xFFFFFFFF) $nbytes += 1; // 8 byte integer - if($value < 0) $nbytes = 3; // 8 byte integer, since signed - - $bdata = self::typeBytes("1", $nbytes); // 1 is 0001, type indicator for integer - $buff = ""; - - if($nbytes < 3) { - if($nbytes == 0) $fmt = "C"; - elseif($nbytes == 1) $fmt = "n"; - else $fmt = "N"; - - $buff = pack($fmt, $value); - } - else { - if(PHP_INT_SIZE > 4) { - // 64 bit signed integer; we need the higher and the lower 32 bit of the value - $high_word = $value >> 32; - $low_word = $value & 0xFFFFFFFF; - } - else { - // since PHP can only handle 32bit signed, we can only get 32bit signed values at this point - values above 0x7FFFFFFF are - // floats. So we ignore the existance of 64bit on non-64bit-machines - if($value < 0) $high_word = 0xFFFFFFFF; - else $high_word = 0; - $low_word = $value; - } - $buff = pack("N", $high_word).pack("N", $low_word); - } - - return $bdata.$buff; - } - - /** - * Codes a real value to binary format - * @param float $val The real value - * @return string The coded real - */ - protected function realToBinary($val) { - $bdata = self::typeBytes("2", 3); // 2 is 0010, type indicator for reals - return $bdata.strrev(pack("d", (float)$val)); - } - - /** - * Converts a numeric value to binary and adds it to the object table - * @param numeric $value The numeric value - * @return integer The position in the object table - */ - public function numToBinary($value) { - $saved_object_count = $this->writtenObjectCount++; - - $val = ""; - if(intval($value) == $value && !is_float($value) && strpos($value,'.') === false) $val = $this->intToBinary($value); - else $val = $this->realToBinary($value); - - $this->objectTable[$saved_object_count] = $val; - return $saved_object_count; - } - - /** - * Convert date value (apple format) to binary and adds it to the object table - * @param integer $value The date value - * @return integer The position of the coded value in the object table - */ - public function dateToBinary($val) { - $saved_object_count = $this->writtenObjectCount++; - - $hour = gmdate("H",$val); - $min = gmdate("i",$val); - $sec = gmdate("s",$val); - $mday = gmdate("j",$val); - $mon = gmdate("n",$val); - $year = gmdate("Y",$val); - - $val = gmmktime($hour,$min,$sec,$mon,$mday,$year) - CFDate::DATE_DIFF_APPLE_UNIX; // CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT - - $bdata = self::typeBytes("3", 3); // 3 is 0011, type indicator for date - $this->objectTable[$saved_object_count] = $bdata.strrev(pack("d", $val)); - - return $saved_object_count; - } - - /** - * Convert a bool value to binary and add it to the object table - * @param bool $val The boolean value - * @return integer The position in the object table - */ - public function boolToBinary($val) { - $saved_object_count = $this->writtenObjectCount++; - $this->objectTable[$saved_object_count] = $val ? "\x9" : "\x8"; // 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false - return $saved_object_count; - } - - /** - * Convert data value to binary format and add it to the object table - * @param string $val The data value - * @return integer The position in the object table - */ - public function dataToBinary($val) { - $saved_object_count = $this->writtenObjectCount++; - - $bdata = self::typeBytes("4", strlen($val)); // a is 1000, type indicator for data - $this->objectTable[$saved_object_count] = $bdata.$val; - - return $saved_object_count; - } - - /** - * Convert array to binary format and add it to the object table - * @param CFArray $val The array to convert - * @return integer The position in the object table - */ - public function arrayToBinary($val) { - $saved_object_count = $this->writtenObjectCount++; - - $bdata = self::typeBytes("a", count($val->getValue())); // a is 1010, type indicator for arrays - - foreach($val as $v) { - $bval = $v->toBinary($this); - $bdata .= self::packItWithSize($this->objectRefSize, $bval); - } - - $this->objectTable[$saved_object_count] = $bdata; - return $saved_object_count; - } - - /** - * Convert dictionary to binary format and add it to the object table - * @param CFDictionary $val The dict to convert - * @return integer The position in the object table - */ - public function dictToBinary($val) { - $saved_object_count = $this->writtenObjectCount++; - $bdata = self::typeBytes("d", count($val->getValue())); // d=1101, type indicator for dictionary - - foreach($val as $k => $v) { - $str = new CFString($k); - $key = $str->toBinary($this); - $bdata .= self::packItWithSize($this->objectRefSize, $key); - } - - foreach($val as $k => $v) { - $bval = $v->toBinary($this); - $bdata .= self::packItWithSize($this->objectRefSize, $bval); - } - - $this->objectTable[$saved_object_count] = $bdata; - return $saved_object_count; - } - -} - - -?> diff --git a/restler/plistformat/.svn/text-base/CFPropertyList.php.svn-base b/restler/plistformat/.svn/text-base/CFPropertyList.php.svn-base deleted file mode 100644 index 6366be48c..000000000 --- a/restler/plistformat/.svn/text-base/CFPropertyList.php.svn-base +++ /dev/null @@ -1,587 +0,0 @@ - - * @author Christian Kruse - * @package plist - * @version $Id$ - * @example example-read-01.php Read an XML PropertyList - * @example example-read-02.php Read a Binary PropertyList - * @example example-read-03.php Read a PropertyList without knowing the type - * @example example-create-01.php Using the CFPropertyList API - * @example example-create-02.php Using {@link CFTypeDetector} - * @example example-create-03.php Using {@link CFTypeDetector} with {@link CFDate} and {@link CFData} - * @example example-modify-01.php Read, modify and save a PropertyList - */ - -/** - * Require IOException, PListException, CFType and CFBinaryPropertyList - */ -$plistDirectory = dirname(__FILE__); -require_once($plistDirectory.'/IOException.php'); -require_once($plistDirectory.'/PListException.php'); -require_once($plistDirectory.'/CFType.php'); -require_once($plistDirectory.'/CFBinaryPropertyList.php'); -require_once($plistDirectory.'/CFTypeDetector.php'); - -/** - * Property List - * Interface for handling reading, editing and saving Property Lists as defined by Apple. - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @example example-read-01.php Read an XML PropertyList - * @example example-read-02.php Read a Binary PropertyList - * @example example-read-03.php Read a PropertyList without knowing the type - * @example example-create-01.php Using the CFPropertyList API - * @example example-create-02.php Using {@link CFTypeDetector} - * @example example-create-03.php Using {@link CFTypeDetector} with {@link CFDate} and {@link CFData} - * @example example-create-04.php Using and extended {@link CFTypeDetector} - */ -class CFPropertyList extends CFBinaryPropertyList implements Iterator { - /** - * Format constant for binary format - * @var integer - */ - const FORMAT_BINARY = 1; - - /** - * Format constant for xml format - * @var integer - */ - const FORMAT_XML = 2; - - /** - * Format constant for automatic format recognizing - * @var integer - */ - const FORMAT_AUTO = 0; - - /** - * Path of PropertyList - * @var string - */ - protected $file = null; - - /** - * Path of PropertyList - * @var integer - */ - protected $format = null; - - /** - * CFType nodes - * @var array - */ - protected $value = array(); - - /** - * Position of iterator {@link http://php.net/manual/en/class.iterator.php} - * @var integer - */ - protected $iteratorPosition = 0; - - /** - * List of Keys for numerical iterator access {@link http://php.net/manual/en/class.iterator.php} - * @var array - */ - protected $iteratorKeys = null; - - /** - * List of NodeNames to ClassNames for resolving plist-files - * @var array - */ - protected static $types = array( - 'string' => 'CFString', - 'real' => 'CFNumber', - 'integer' => 'CFNumber', - 'date' => 'CFDate', - 'true' => 'CFBoolean', - 'false' => 'CFBoolean', - 'data' => 'CFData', - 'array' => 'CFArray', - 'dict' => 'CFDictionary' - ); - - - /** - * Create new CFPropertyList. - * If a path to a PropertyList is specified, it is loaded automatically. - * @param string $file Path of PropertyList - * @param integer $format he format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link FORMAT_AUTO} - * @throws IOException if file could not be read by {@link load()} - * @uses $file for storing the current file, if specified - * @uses load() for loading the plist-file - */ - public function __construct($file=null,$format=self::FORMAT_AUTO) { - $this->file = $file; - $this->format = $format; - if($this->file) $this->load(); - } - - /** - * Load an XML PropertyList. - * @param string $file Path of PropertyList, defaults to {@link $file} - * @return void - * @throws IOException if file could not be read - * @throws DOMException if XML-file could not be read properly - * @uses load() to actually load the file - */ - public function loadXML($file=null) { - $this->load($file,CFPropertyList::FORMAT_XML); - } - - /** - * Load an XML PropertyList. - * @param resource $stream A stream containing the xml document. - * @return void - * @throws IOException if stream could not be read - * @throws DOMException if XML-stream could not be read properly - */ - public function loadXMLStream($stream) { - if(($contents = stream_get_contents($stream)) === FALSE) throw IOException::notReadable(''); - $this->parse($content,CFPropertyList::FORMAT_XML); - } - - /** - * Load an binary PropertyList. - * @param string $file Path of PropertyList, defaults to {@link $file} - * @return void - * @throws IOException if file could not be read - * @throws PListException if binary plist-file could not be read properly - * @uses load() to actually load the file - */ - public function loadBinary($file=null) { - $this->load($file,CFPropertyList::FORMAT_BINARY); - } - - /** - * Load an binary PropertyList. - * @param stream $stream Stream containing the PropertyList - * @return void - * @throws IOException if file could not be read - * @throws PListException if binary plist-file could not be read properly - * @uses parse() to actually load the file - */ - public function loadBinaryStream($stream) { - if(($contents = stream_get_contents($stream)) === FALSE) throw IOException::notReadable(''); - $this->parse($content,CFPropertyList::FORMAT_BINARY); - } - - /** - * Load a plist file. - * Load and import a plist file. - * @param string $file Path of PropertyList, defaults to {@link $file} - * @param integer $format The format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link $format} - * @return void - * @throws PListException if file format version is not 00 - * @throws IOException if file could not be read - * @throws DOMException if plist file could not be parsed properly - * @uses $file if argument $file was not specified - * @uses $value reset to empty array - * @uses import() for importing the values - */ - public function load($file=null,$format=null) { - $file = $file ? $file : $this->file; - $format = $format !== null ? $format : $this->format; - $this->value = array(); - - if(!is_readable($file)) throw IOException::notReadable($file); - - switch($format) { - case CFPropertyList::FORMAT_BINARY: - $this->readBinary($file); - break; - case CFPropertyList::FORMAT_AUTO: // what we now do is ugly, but neccessary to recognize the file format - $fd = fopen($file,"rb"); - if(($magic_number = fread($fd,8)) === false) throw IOException::notReadable($file); - fclose($fd); - - $filetype = substr($magic_number,0,6); - $version = substr($magic_number,-2); - - if($filetype == "bplist") { - if($version != "00") throw new PListException("Wrong file format version! Expected 00, got $version!"); - $this->readBinary($file); - break; - } - // else: xml format, break not neccessary - case CFPropertyList::FORMAT_XML: - $doc = new DOMDocument(); - if(!$doc->load($file)) throw new DOMException(); - $this->import($doc->documentElement, $this); - break; - } - } - - /** - * Parse a plist string. - * Parse and import a plist string. - * @param string $str String containing the PropertyList, defaults to {@link $content} - * @param integer $format The format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link $format} - * @return void - * @throws PListException if file format version is not 00 - * @throws IOException if file could not be read - * @throws DOMException if plist file could not be parsed properly - * @uses $content if argument $str was not specified - * @uses $value reset to empty array - * @uses import() for importing the values - */ - public function parse($str=NULL,$format=NULL) { - $format = $format !== null ? $format : $this->format; - $str = $str !== null ? $str : $this->content; - $this->value = array(); - - switch($format) { - case CFPropertyList::FORMAT_BINARY: - $this->parseBinary($str); - break; - case CFPropertyList::FORMAT_AUTO: // what we now do is ugly, but neccessary to recognize the file format - if(($magic_number = substr($str,0,8)) === false) throw IOException::notReadable(""); - - $filetype = substr($magic_number,0,6); - $version = substr($magic_number,-2); - - if($filetype == "bplist") { - if($version != "00") throw new PListException("Wrong file format version! Expected 00, got $version!"); - $this->parseBinary($str); - break; - } - // else: xml format, break not neccessary - case CFPropertyList::FORMAT_XML: - $doc = new DOMDocument(); - if(!$doc->loadXML($str)) throw new DOMException(); - $this->import($doc->documentElement, $this); - break; - } - } - - /** - * Convert a DOMNode into a CFType. - * @param DOMNode $node Node to import children of - * @param CFDictionary|CFArray|CFPropertyList $parent - * @return void - */ - protected function import(DOMNode $node, $parent) { - // abort if there are no children - if(!$node->childNodes->length) return; - - foreach($node->childNodes as $n) { - // skip if we can't handle the element - if(!isset(self::$types[$n->nodeName])) continue; - - $class = self::$types[$n->nodeName]; - $key = null; - - // find previous if possible - $ps = $n->previousSibling; - while($ps && $ps->nodeName == '#text' && $ps->previousSibling) $ps = $ps->previousSibling; - - // read if possible - if($ps && $ps->nodeName == 'key') $key = $ps->firstChild->nodeValue; - - switch($n->nodeName) { - case 'date': - $value = new $class(CFDate::dateValue($n->nodeValue)); - break; - case 'data': - $value = new $class($n->nodeValue,true); - break; - case 'string': - $value = new $class($n->nodeValue); - break; - - case 'real': - case 'integer': - $value = new $class($n->nodeName == 'real' ? floatval($n->nodeValue) : intval($n->nodeValue)); - break; - - case 'true': - case 'false': - $value = new $class($n->nodeName == 'true'); - break; - - case 'array': - case 'dict': - $value = new $class(); - $this->import($n, $value); - break; - } - - // Dictionaries need a key - if($parent instanceof CFDictionary) $parent->add($key, $value); - // others don't - else $parent->add($value); - } - } - - /** - * Convert CFPropertyList to XML and save to file. - * @param string $file Path of PropertyList, defaults to {@link $file} - * @return void - * @throws IOException if file could not be read - * @uses $file if $file was not specified - */ - public function saveXML($file) { - $this->save($file,CFPropertyList::FORMAT_XML); - } - - /** - * Convert CFPropertyList to binary format (bplist00) and save to file. - * @param string $file Path of PropertyList, defaults to {@link $file} - * @return void - * @throws IOException if file could not be read - * @uses $file if $file was not specified - */ - public function saveBinary($file) { - $this->save($file,CFPropertyList::FORMAT_BINARY); - } - - /** - * Convert CFPropertyList to XML or binary and save to file. - * @param string $file Path of PropertyList, defaults to {@link $file} - * @param string $format Format of PropertyList, defaults to {@link $format} - * @return void - * @throws IOException if file could not be read - * @throws PListException if evaluated $format is neither {@link FORMAT_XML} nor {@link FORMAL_BINARY} - * @uses $file if $file was not specified - * @uses $format if $format was not specified - */ - public function save($file=null,$format=null) { - $file = $file ? $file : $this->file; - $format = $format ? $format : $this->format; - - if( !in_array( $format, array( self::FORMAT_BINARY, self::FORMAT_XML ) ) ) - throw new PListException( "format {$format} is not supported, use CFPropertyList::FORMAT_BINARY or CFPropertyList::FORMAT_XML" ); - - if(!file_exists($file)) { - // dirname("file.xml") == "" and is treated as the current working directory - if(!is_writable(dirname($file))) throw IOException::notWritable($file); - } - else if(!is_writable($file)) throw IOException::notWritable($file); - - $content = $format == self::FORMAT_BINARY ? $this->toBinary() : $this->toXML(); - - $fh = fopen($file, 'wb'); - fwrite($fh,$content); - fclose($fh); - } - - /** - * Convert CFPropertyList to XML - * @param bool $formatted Print plist formatted (i.e. with newlines and whitespace indention) if true; defaults to false - * @return string The XML content - */ - public function toXML($formatted=false) { - $domimpl = new DOMImplementation(); - // - $dtd = $domimpl->createDocumentType('plist', '-//Apple Computer//DTD PLIST 1.0//EN', 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'); - $doc = $domimpl->createDocument(null, "plist", $dtd); - $doc->encoding = "UTF-8"; - - // format output - if($formatted) { - $doc->formatOutput = true; - $doc->preserveWhiteSpace = true; - } - - // get documentElement and set attribs - $plist = $doc->documentElement; - $plist->setAttribute('version', '1.0'); - - // add PropertyList's children - $plist->appendChild($this->getValue(true)->toXML($doc)); - - return $doc->saveXML(); - } - - - /************************************************************************************************ - * M A N I P U L A T I O N - ************************************************************************************************/ - - /** - * Add CFType to collection. - * @param CFType $value CFType to add to collection - * @return void - * @uses $value for adding $value - */ - public function add(CFType $value=null) { - // anything but CFType is null, null is an empty string - sad but true - if( !$value ) - $value = new CFString(); - - $this->value[] = $value; - } - - /** - * Get CFType from collection. - * @param integer $key Key of CFType to retrieve from collection - * @return CFType CFType found at $key, null else - * @uses $value for retrieving CFType of $key - */ - public function get($key) { - if(isset($this->value[$key])) return $this->value[$key]; - return null; - } - - /** - * Generic getter (magic) - * - * @param integer $key Key of CFType to retrieve from collection - * @return CFType CFType found at $key, null else - * @author Sean Coates - * @link http://php.net/oop5.overloading - */ - public function __get($key) { - return $this->get($key); - } - - /** - * Remove CFType from collection. - * @param integer $key Key of CFType to removes from collection - * @return CFType removed CFType, null else - * @uses $value for removing CFType of $key - */ - public function del($key) { - if(isset($this->value[$key])) { - $t = $this->value[$key]; - unset($this->value[$key]); - return $t; - } - - return null; - } - - /** - * Empty the collection - * @return array the removed CFTypes - * @uses $value for removing CFType of $key - */ - public function purge() { - $t = $this->value; - $this->value = array(); - return $t; - } - - /** - * Get first (and only) child, or complete collection. - * @param string $cftype if set to true returned value will be CFArray instead of an array in case of a collection - * @return CFType|array CFType or list of CFTypes known to the PropertyList - * @uses $value for retrieving CFTypes - */ - public function getValue($cftype=false) { - if(count($this->value) === 1) { - $t = array_values( $this->value ); - return $t[0]; - } - if($cftype) { - $t = new CFArray(); - foreach( $this->value as $value ) { - if( $value instanceof CFType ) $t->add($value); - } - return $t; - } - return $this->value; - } - - /** - * Create CFType-structure from guessing the data-types. - * The functionality has been moved to the more flexible {@link CFTypeDetector} facility. - * @param mixed $value Value to convert to CFType - * @param boolean $autoDictionary if true {@link CFArray}-detection is bypassed and arrays will be returned as {@link CFDictionary}. - * @return CFType CFType based on guessed type - * @uses CFTypeDetector for actual type detection - * @deprecated - */ - public static function guess($value, $autoDictionary=false) { - static $t = null; - if( $t === null ) - $t = new CFTypeDetector( $autoDictionary ); - - return $t->toCFType( $value ); - } - - - /************************************************************************************************ - * S E R I A L I Z I N G - ************************************************************************************************/ - - /** - * Get PropertyList as array. - * @return mixed primitive value of first (and only) CFType, or array of primitive values of collection - * @uses $value for retrieving CFTypes - */ - public function toArray() { - $a = array(); - foreach($this->value as $value) $a[] = $value->toArray(); - if(count($a) === 1) return $a[0]; - - return $a; - } - - - /************************************************************************************************ - * I T E R A T O R I N T E R F A C E - ************************************************************************************************/ - - /** - * Rewind {@link $iteratorPosition} to first position (being 0) - * @link http://php.net/manual/en/iterator.rewind.php - * @return void - * @uses $iteratorPosition set to 0 - * @uses $iteratorKeys store keys of {@link $value} - */ - public function rewind() { - $this->iteratorPosition = 0; - $this->iteratorKeys = array_keys($this->value); - } - - /** - * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition} - * @link http://php.net/manual/en/iterator.current.php - * @return CFType current Item - * @uses $iteratorPosition identify current key - * @uses $iteratorKeys identify current value - */ - public function current() { - return $this->value[$this->iteratorKeys[$this->iteratorPosition]]; - } - - /** - * Get Iterator's current key identified by {@link $iteratorPosition} - * @link http://php.net/manual/en/iterator.key.php - * @return string key of the current Item - * @uses $iteratorPosition identify current key - * @uses $iteratorKeys identify current value - */ - public function key() { - return $this->iteratorKeys[$this->iteratorPosition]; - } - - /** - * Increment {@link $iteratorPosition} to address next {@see CFType} - * @link http://php.net/manual/en/iterator.next.php - * @return void - * @uses $iteratorPosition increment by 1 - */ - public function next() { - $this->iteratorPosition++; - } - - /** - * Test if {@link $iteratorPosition} addresses a valid element of {@link $value} - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean true if current position is valid, false else - * @uses $iteratorPosition test if within {@link $iteratorKeys} - * @uses $iteratorPosition test if within {@link $value} - */ - public function valid() { - return isset($this->iteratorKeys[$this->iteratorPosition]) && isset($this->value[$this->iteratorKeys[$this->iteratorPosition]]); - } - -} - - -?> \ No newline at end of file diff --git a/restler/plistformat/.svn/text-base/CFType.php.svn-base b/restler/plistformat/.svn/text-base/CFType.php.svn-base deleted file mode 100644 index 246c22177..000000000 --- a/restler/plistformat/.svn/text-base/CFType.php.svn-base +++ /dev/null @@ -1,742 +0,0 @@ - - * @author Christian Kruse - * @package plist - * @subpackage plist.types - * @version $Id$ - */ - -/** - * Base-Class of all CFTypes used by CFPropertyList - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - * @version $Id$ - * @example example-create-01.php Using the CFPropertyList API - * @example example-create-02.php Using CFPropertyList::guess() - * @example example-create-03.php Using CFPropertyList::guess() with {@link CFDate} and {@link CFData} - */ -abstract class CFType { - /** - * CFType nodes - * @var array - */ - protected $value = null; - - /** - * Create new CFType. - * @param mixed $value Value of CFType - */ - public function __construct($value=null) { - $this->setValue($value); - } - - /************************************************************************************************ - * M A G I C P R O P E R T I E S - ************************************************************************************************/ - - /** - * Get the CFType's value - * @return mixed CFType's value - */ - public function getValue() { - return $this->value; - } - - /** - * Set the CFType's value - * @return void - */ - public function setValue($value) { - $this->value = $value; - } - - /************************************************************************************************ - * S E R I A L I Z I N G - ************************************************************************************************/ - - /** - * Get XML-Node. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName Name of element to create - * @return DOMNode Node created based on CType - * @uses $value as nodeValue - */ - public function toXML(DOMDocument $doc, $nodeName) { - $text = $doc->createTextNode($this->value); - $node = $doc->createElement($nodeName); - $node->appendChild($text); - return $node; - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public abstract function toBinary(CFBinaryPropertyList &$bplist); - - /** - * Get CFType's value. - * @return mixed primitive value - * @uses $value for retrieving primitive of CFType - */ - public function toArray() { - return $this->getValue(); - } - -} - -/** - * String Type of CFPropertyList - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFString extends CFType { - /** - * Get XML-Node. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <string>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - return parent::toXML($doc, 'string'); - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->stringToBinary($this->value); - } -} - -/** - * Number Type of CFPropertyList - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFNumber extends CFType { - /** - * Get XML-Node. - * Returns <real> if $value is a float, <integer> if $value is an integer. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <real> or <integer>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - $ret = 'real'; - if(intval($this->value) == $this->value && !is_float($this->value) && strpos($this->value,'.') === false) { - $this->value = intval($this->value); - $ret = 'integer'; - } - return parent::toXML($doc, $ret); - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->numToBinary($this->value); - } -} - -/** - * Date Type of CFPropertyList - * Note: CFDate uses Unix timestamp (epoch) to store dates internally - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFDate extends CFType { - const TIMESTAMP_APPLE = 0; - const TIMESTAMP_UNIX = 1; - const DATE_DIFF_APPLE_UNIX = 978307200; - - /** - * Create new Date CFType. - * @param integer $value timestamp to set - * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_APPLE} - * @uses setValue() to convert the timestamp - */ - function __construct($value,$format=CFDate::TIMESTAMP_UNIX) { - $this->setValue($value,$format); - } - - /** - * Set the Date CFType's value. - * @param integer $value timestamp to set - * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_UNIX} - * @return void - * @uses TIMESTAMP_APPLE to determine timestamp type - * @uses TIMESTAMP_UNIX to determine timestamp type - * @uses DATE_DIFF_APPLE_UNIX to convert Apple-timestamp to Unix-timestamp - */ - function setValue($value,$format=CFDate::TIMESTAMP_UNIX) { - if($format == CFDate::TIMESTAMP_UNIX) $this->value = $value; - else $this->value = $value + CFDate::DATE_DIFF_APPLE_UNIX; - } - - /** - * Get the Date CFType's value. - * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_UNIX} - * @return integer Unix timestamp - * @uses TIMESTAMP_APPLE to determine timestamp type - * @uses TIMESTAMP_UNIX to determine timestamp type - * @uses DATE_DIFF_APPLE_UNIX to convert Unix-timestamp to Apple-timestamp - */ - function getValue($format=CFDate::TIMESTAMP_UNIX) { - if($format == CFDate::TIMESTAMP_UNIX) return $this->value; - else return $this->value - CFDate::DATE_DIFF_APPLE_UNIX; - } - - /** - * Get XML-Node. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <date>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - $text = $doc->createTextNode(gmdate("Y-m-d\TH:i:s\Z",$this->getValue())); - $node = $doc->createElement("date"); - $node->appendChild($text); - return $node; - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->dateToBinary($this->value); - } - - /** - * Create a UNIX timestamp from a PList date string - * @param string $val The date string (e.g. "2009-05-13T20:23:43Z") - * @return integer The UNIX timestamp - * @throws PListException when encountering an unknown date string format - */ - public static function dateValue($val) { - //2009-05-13T20:23:43Z - if(!preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/',$val,$matches)) throw new PListException("Unknown date format: $val"); - return gmmktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]); - } -} - -/** - * Boolean Type of CFPropertyList - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFBoolean extends CFType { - /** - * Get XML-Node. - * Returns <true> if $value is a true, <false> if $value is false. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <true> or <false>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - return $doc->createElement($this->value ? 'true' : 'false'); - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->boolToBinary($this->value); - } - -} - -/** - * Data Type of CFPropertyList - * Note: Binary data is base64-encoded. - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFData extends CFType { - /** - * Create new Data CFType - * @param string $value data to be contained by new object - * @param boolean $already_coded if true $value will not be base64-encoded, defaults to false - */ - public function __construct($value=null,$already_coded=false) { - if($already_coded) $this->value = $value; - else $this->setValue($value); - } - - /** - * Set the CFType's value and base64-encode it. - * Note: looks like base64_encode has troubles with UTF-8 encoded strings - * @return void - */ - public function setValue($value) { - //if(function_exists('mb_check_encoding') && mb_check_encoding($value, 'UTF-8')) $value = utf8_decode($value); - $this->value = base64_encode($value); - } - - /** - * Get base64 encoded data - * @return string The base64 encoded data value - */ - public function getCodedValue() { - return $this->value; - } - - /** - * Get the base64-decoded CFType's value. - * @return mixed CFType's value - */ - public function getValue() { - return base64_decode($this->value); - } - - /** - * Get XML-Node. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <data>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - return parent::toXML($doc, 'data'); - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->dataToBinary($this->getValue()); - } -} - -/** - * Array Type of CFPropertyList - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFArray extends CFType implements Iterator, ArrayAccess { - /** - * Position of iterator {@link http://php.net/manual/en/class.iterator.php} - * @var integer - */ - protected $iteratorPosition = 0; - - - /** - * Create new CFType. - * @param array $value Value of CFType - */ - public function __construct($value=array()) { - $this->value = $value; - } - - /** - * Set the CFType's value - * Note: this dummy does nothing - * @return void - */ - public function setValue($value) { - } - - /** - * Add CFType to collection. - * @param CFType $value CFType to add to collection, defaults to null which results in an empty {@link CFString} - * @return void - * @uses $value for adding $value - */ - public function add(CFType $value=null) { - // anything but CFType is null, null is an empty string - sad but true - if( !$value ) - $value = new CFString(); - - $this->value[] = $value; - } - - /** - * Get CFType from collection. - * @param integer $key Key of CFType to retrieve from collection - * @return CFType CFType found at $key, null else - * @uses $value for retrieving CFType of $key - */ - public function get($key) { - if(isset($this->value[$key])) return $this->value[$key]; - return null; - } - - /** - * Remove CFType from collection. - * @param integer $key Key of CFType to removes from collection - * @return CFType removed CFType, null else - * @uses $value for removing CFType of $key - */ - public function del($key) { - if(isset($this->value[$key])) unset($this->value[$key]); - } - - - /************************************************************************************************ - * S E R I A L I Z I N G - ************************************************************************************************/ - - /** - * Get XML-Node. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <array>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - $node = $doc->createElement('array'); - - foreach($this->value as $value) $node->appendChild($value->toXML($doc)); - return $node; - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->arrayToBinary($this); - } - - /** - * Get CFType's value. - * @return array primitive value - * @uses $value for retrieving primitive of CFType - */ - public function toArray() { - $a = array(); - foreach($this->value as $value) $a[] = $value->toArray(); - return $a; - } - - - /************************************************************************************************ - * I T E R A T O R I N T E R F A C E - ************************************************************************************************/ - - /** - * Rewind {@link $iteratorPosition} to first position (being 0) - * @link http://php.net/manual/en/iterator.rewind.php - * @return void - * @uses $iteratorPosition set to 0 - */ - public function rewind() { - $this->iteratorPosition = 0; - } - - /** - * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition} - * @link http://php.net/manual/en/iterator.current.php - * @return CFType current Item - * @uses $iteratorPosition identify current key - */ - public function current() { - return $this->value[$this->iteratorPosition]; - } - - /** - * Get Iterator's current key identified by {@link $iteratorPosition} - * @link http://php.net/manual/en/iterator.key.php - * @return string key of the current Item - * @uses $iteratorPosition identify current key - */ - public function key() { - return $this->iteratorPosition; - } - - /** - * Increment {@link $iteratorPosition} to address next {@see CFType} - * @link http://php.net/manual/en/iterator.next.php - * @return void - * @uses $iteratorPosition increment by 1 - */ - public function next() { - $this->iteratorPosition++; - } - - /** - * Test if {@link $iteratorPosition} addresses a valid element of {@link $value} - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean true if current position is valid, false else - * @uses $iteratorPosition test if within {@link $iteratorKeys} - * @uses $iteratorPosition test if within {@link $value} - */ - public function valid() { - return isset($this->value[$this->iteratorPosition]); - } - - /************************************************************************************************ - * ArrayAccess I N T E R F A C E - ************************************************************************************************/ - - /** - * Determine if the array's key exists - * @param string $key the key to check - * @return bool true if the offset exists, false if not - * @link http://php.net/manual/en/arrayaccess.offsetexists.php - * @uses $value to check if $key exists - * @author Sean Coates - */ - public function offsetExists($key) { - return isset($this->value[$key]); - } - - /** - * Fetch a specific key from the CFArray - * @param string $key the key to check - * @return mixed the value associated with the key; null if the key is not found - * @link http://php.net/manual/en/arrayaccess.offsetget.php - * @uses get() to get the key's value - * @author Sean Coates - */ - public function offsetGet($key) { - return $this->get($key); - } - - /** - * Set a value in the array - * @param string $key the key to set - * @param string $value the value to set - * @return void - * @link http://php.net/manual/en/arrayaccess.offsetset.php - * @uses setValue() to set the key's new value - * @author Sean Coates - */ - public function offsetSet($key, $value) { - return $this->setValue($value); - } - - /** - * Unsets a value in the array - * Note: this dummy does nothing - * @param string $key the key to set - * @return void - * @link http://php.net/manual/en/arrayaccess.offsetunset.php - * @author Sean Coates - */ - public function offsetUnset($key) { - - } - - -} - -/** - * Array Type of CFPropertyList - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - * @subpackage plist.types - */ -class CFDictionary extends CFType implements Iterator { - /** - * Position of iterator {@link http://php.net/manual/en/class.iterator.php} - * @var integer - */ - protected $iteratorPosition = 0; - - /** - * List of Keys for numerical iterator access {@link http://php.net/manual/en/class.iterator.php} - * @var array - */ - protected $iteratorKeys = null; - - - /** - * Create new CFType. - * @param array $value Value of CFType - */ - public function __construct($value=array()) { - $this->value = $value; - } - - /** - * Set the CFType's value - * Note: this dummy does nothing - * @return void - */ - public function setValue($value) { - } - - /** - * Add CFType to collection. - * @param string $key Key to add to collection - * @param CFType $value CFType to add to collection, defaults to null which results in an empty {@link CFString} - * @return void - * @uses $value for adding $key $value pair - */ - public function add($key, CFType $value=null) { - // anything but CFType is null, null is an empty string - sad but true - if( !$value ) - $value = new CFString(); - - $this->value[$key] = $value; - } - - /** - * Get CFType from collection. - * @param string $key Key of CFType to retrieve from collection - * @return CFType CFType found at $key, null else - * @uses $value for retrieving CFType of $key - */ - public function get($key) { - if(isset($this->value[$key])) return $this->value[$key]; - return null; - } - - /** - * Generic getter (magic) - * @param integer $key Key of CFType to retrieve from collection - * @return CFType CFType found at $key, null else - * @link http://php.net/oop5.overloading - * @uses get() to retrieve the key's value - * @author Sean Coates - */ - public function __get($key) { - return $this->get($key); - } - - /** - * Remove CFType from collection. - * @param string $key Key of CFType to removes from collection - * @return CFType removed CFType, null else - * @uses $value for removing CFType of $key - */ - public function del($key) { - if(isset($this->value[$key])) unset($this->value[$key]); - } - - - /************************************************************************************************ - * S E R I A L I Z I N G - ************************************************************************************************/ - - /** - * Get XML-Node. - * @param DOMDocument $doc DOMDocument to create DOMNode in - * @param string $nodeName For compatibility reasons; just ignore it - * @return DOMNode <dict>-Element - */ - public function toXML(DOMDocument $doc,$nodeName="") { - $node = $doc->createElement('dict'); - - foreach($this->value as $key => $value) { - $node->appendChild($doc->createElement('key', $key)); - $node->appendChild($value->toXML($doc)); - } - - return $node; - } - - /** - * convert value to binary representation - * @param CFBinaryPropertyList The binary property list object - * @return The offset in the object table - */ - public function toBinary(CFBinaryPropertyList &$bplist) { - return $bplist->dictToBinary($this); - } - - /** - * Get CFType's value. - * @return array primitive value - * @uses $value for retrieving primitive of CFType - */ - public function toArray() { - $a = array(); - - foreach($this->value as $key => $value) $a[$key] = $value->toArray(); - return $a; - } - - - /************************************************************************************************ - * I T E R A T O R I N T E R F A C E - ************************************************************************************************/ - - /** - * Rewind {@link $iteratorPosition} to first position (being 0) - * @link http://php.net/manual/en/iterator.rewind.php - * @return void - * @uses $iteratorPosition set to 0 - * @uses $iteratorKeys store keys of {@link $value} - */ - public function rewind() { - $this->iteratorPosition = 0; - $this->iteratorKeys = array_keys($this->value); - } - - /** - * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition} - * @link http://php.net/manual/en/iterator.current.php - * @return CFType current Item - * @uses $iteratorPosition identify current key - * @uses $iteratorKeys identify current value - */ - public function current() { - return $this->value[$this->iteratorKeys[$this->iteratorPosition]]; - } - - /** - * Get Iterator's current key identified by {@link $iteratorPosition} - * @link http://php.net/manual/en/iterator.key.php - * @return string key of the current Item - * @uses $iteratorPosition identify current key - * @uses $iteratorKeys identify current value - */ - public function key() { - return $this->iteratorKeys[$this->iteratorPosition]; - } - - /** - * Increment {@link $iteratorPosition} to address next {@see CFType} - * @link http://php.net/manual/en/iterator.next.php - * @return void - * @uses $iteratorPosition increment by 1 - */ - public function next() { - $this->iteratorPosition++; - } - - /** - * Test if {@link $iteratorPosition} addresses a valid element of {@link $value} - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean true if current position is valid, false else - * @uses $iteratorPosition test if within {@link $iteratorKeys} - * @uses $iteratorPosition test if within {@link $value} - */ - public function valid() { - return isset($this->iteratorKeys[$this->iteratorPosition]) && isset($this->value[$this->iteratorKeys[$this->iteratorPosition]]); - } - -} - -?> \ No newline at end of file diff --git a/restler/plistformat/.svn/text-base/CFTypeDetector.php.svn-base b/restler/plistformat/.svn/text-base/CFTypeDetector.php.svn-base deleted file mode 100644 index 996439326..000000000 --- a/restler/plistformat/.svn/text-base/CFTypeDetector.php.svn-base +++ /dev/null @@ -1,167 +0,0 @@ - - * @author Christian Kruse - * @package plist - * @subpackage plist.types - * @example example-create-02.php Using {@link CFTypeDetector} - * @example example-create-03.php Using {@link CFTypeDetector} with {@link CFDate} and {@link CFData} - * @example example-create-04.php Using and extended {@link CFTypeDetector} - */ -class CFTypeDetector { - - /** - * flag stating if all arrays should automatically be converted to {@link CFDictionary} - * @var boolean - */ - protected $autoDictionary = false; - - /** - * flag stating if exceptions should be suppressed or thrown - * @var boolean - */ - protected $suppressExceptions = false; - - /** - * name of a method that will be used for array to object conversations - * @var boolean - */ - protected $objectToArrayMethod = false; - - - /** - * Create new CFTypeDetector - * @param boolean $autoDicitionary if set to true all arrays will be converted to {@link CFDictionary} - * @param boolean $suppressExceptions if set to true toCFType() will not throw any exceptions - * @param boolean $objectToArrayMethod if non-null, this method will be called on objects (if possible) to convert the object to an array - */ - public function __construct($autoDicitionary=false,$suppressExceptions=false,$objectToArrayMethod=null) { - $this->autoDicitionary = $autoDicitionary; - $this->suppressExceptions = $suppressExceptions; - $this->objectToArrayMethod = $objectToArrayMethod; - } - - /** - * Determine if an array is associative or numerical. - * Numerical Arrays have incrementing index-numbers that don't contain gaps. - * @param array $value Array to check indexes of - * @return boolean true if array is associative, false if array has numeric indexes - */ - protected function isAssociativeArray($value) { - $numericKeys = true; - $previousKey = null; - foreach($value as $key => $v) { - if(!is_numeric($key) || ($previousKey !== null && $previousKey != $key-1)) { - $numericKeys = false; - break; - } - - $previousKey = $key; - } - return !$numericKeys; - } - - /** - * Get the default value - * @return CFType the default value to return if no suitable type could be determined - */ - protected function defaultValue() { - return new CFString(); - } - - /** - * Create CFType-structure by guessing the data-types. - * {@link CFArray}, {@link CFDictionary}, {@link CFBoolean}, {@link CFNumber} and {@link CFString} can be created, {@link CFDate} and {@link CFData} cannot. - *
      Note:Distinguishing between {@link CFArray} and {@link CFDictionary} is done by examining the keys. - * Keys must be strictly incrementing integers to evaluate to a {@link CFArray}. - * Since PHP does not offer a function to test for associative arrays, - * this test causes the input array to be walked twice and thus work rather slow on large collections. - * If you work with large arrays and can live with all arrays evaluating to {@link CFDictionary}, - * feel free to set the appropriate flag. - *
      Note: If $value is an instance of CFType it is simply returned. - *
      Note: If $value is neither a CFType, array, numeric, boolean nor string, it is omitted. - * @param mixed $value Value to convert to CFType - * @param boolean $autoDictionary if true {@link CFArray}-detection is bypassed and arrays will be returned as {@link CFDictionary}. - * @return CFType CFType based on guessed type - * @uses isAssociativeArray() to check if an array only has numeric indexes - */ - public function toCFType($value) { - switch(true) { - case $value instanceof CFType: - return $value; - break; - - case is_object($value): - // DateTime should be CFDate - if(class_exists( 'DateTime' ) && $value instanceof DateTime){ - return new CFDate($value->getTimestamp()); - } - - // convert possible objects to arrays, arrays will be arrays - if($this->objectToArrayMethod && is_callable(array($value, $this->objectToArrayMethod))){ - $value = call_user_func( array( $value, $this->objectToArrayMethod ) ); - } - - if(!is_array($value)){ - if($this->suppressExceptions) - return $this->defaultValue(); - - throw new PListException('Could not determine CFType for object of type '. get_class($value)); - } - /* break; omitted */ - - case $value instanceof Iterator: - case is_array($value): - // test if $value is simple or associative array - if(!$this->autoDictionary) { - if(!$this->isAssociativeArray($value)) { - $t = new CFArray(); - foreach($value as $v) $t->add($this->toCFType($v)); - return $t; - } - } - - $t = new CFDictionary(); - foreach($value as $k => $v) $t->add($k, $this->toCFType($v)); - - return $t; - break; - - case is_numeric($value): - return new CFNumber($value); - break; - - case is_bool($value): - return new CFBoolean($value); - break; - - case is_string($value): - return new CFString($value); - break; - - case is_null($value): - return new CFString(); - break; - - case is_resource($value): - if( $this->suppressExceptions ) - return $this->defaultValue(); - - throw new PListException('Could not determine CFType for resource of type '. get_resource_type($value)); - break; - - default: - if( $this->suppressExceptions ) - return $this->defaultValue(); - - throw new PListException('Could not determine CFType for '. gettype($value)); - break; - } - } - -} - -?> \ No newline at end of file diff --git a/restler/plistformat/.svn/text-base/IOException.php.svn-base b/restler/plistformat/.svn/text-base/IOException.php.svn-base deleted file mode 100644 index 991855394..000000000 --- a/restler/plistformat/.svn/text-base/IOException.php.svn-base +++ /dev/null @@ -1,99 +0,0 @@ - - * @author Christian Kruse - * @package plist - * @version $Id$ - */ - -/** - * Basic Input / Output Exception - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - */ -class IOException extends Exception { - /** - * Flag telling the File could not be found - */ - const NOT_FOUND = 1; - - /** - * Flag telling the File is not readable - */ - const NOT_READABLE = 2; - - /** - * Flag telling the File is not writable - */ - const NOT_WRITABLE = 3; - - /** - * Flag telling there was a read error - */ - const READ_ERROR = 4; - - /** - * Flag telling there was a read error - */ - const WRITE_ERROR = 5; - - /** - * Create new IOException - * @param string $path Source of the problem - * @param integer $type Type of the problem - */ - public function __construct($path, $type=null) { - parent::__construct( $path, $type ); - } - - /** - * Create new FileNotFound-Exception - * @param string $path Source of the problem - * @return IOException new FileNotFound-Exception - */ - public static function notFound($path) { - return new IOException( $path, self::NOT_FOUND ); - } - - /** - * Create new FileNotReadable-Exception - * @param string $path Source of the problem - * @return IOException new FileNotReadable-Exception - */ - public static function notReadable($path) { - return new IOException( $path, self::NOT_READABLE ); - } - - /** - * Create new FileNotWritable-Exception - * @param string $path Source of the problem - * @return IOException new FileNotWritable-Exception - */ - public static function notWritable($path) { - return new IOException( $path, self::NOT_WRITABLE ); - } - - /** - * Create new ReadError-Exception - * @param string $path Source of the problem - * @return IOException new ReadError-Exception - */ - public static function readError($path) { - return new IOException( $path, self::READ_ERROR ); - } - - /** - * Create new WriteError-Exception - * @param string $path Source of the problem - * @return IOException new WriteError-Exception - */ - public static function writeError($path) { - return new IOException( $path, self::WRITE_ERROR ); - } -} - - -?> \ No newline at end of file diff --git a/restler/plistformat/.svn/text-base/PListException.php.svn-base b/restler/plistformat/.svn/text-base/PListException.php.svn-base deleted file mode 100644 index b738d2382..000000000 --- a/restler/plistformat/.svn/text-base/PListException.php.svn-base +++ /dev/null @@ -1,22 +0,0 @@ - - * @author Christian Kruse - * @package plist - * @version $Id$ - */ - -/** - * Exception for errors with the PList format - * @author Rodney Rehm - * @author Christian Kruse - * @package plist - */ -class PListException extends Exception { - -} - - -?> \ No newline at end of file From 4cd3439876118599b117bf062b02efaa509f0c26 Mon Sep 17 00:00:00 2001 From: Luracast Date: Sat, 15 Oct 2011 00:18:34 +0800 Subject: [PATCH 19/61] cleaned up the code to remove comments with # and initialize variable at line 506 properly with the value null and unwanted object creation --- restler/restler.php | 12 ++++++------ restler_minified/restler.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/restler/restler.php b/restler/restler.php index 5f36ae5b3..d838f4a56 100644 --- a/restler/restler.php +++ b/restler/restler.php @@ -10,10 +10,10 @@ * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ - * @version 2.0.2 + * @version 2.0.3 */ class Restler { - const VERSION = '2.0.2'; + const VERSION = '2.0.3'; /** * URL of the currently mapped service * @var string @@ -503,7 +503,7 @@ protected function getResponseFormat () { /** * @var iFormat */ - $format; + $format=NULL; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); @@ -529,7 +529,7 @@ protected function getResponseFormat () { } $format = $this->format_map['default']; //echo "DEFAULT ".$this->format_map['default']; - return is_string($format) ? new $format: $format; + return $format; } /** @@ -632,7 +632,7 @@ protected function loadCache() $this->cached = TRUE; } } else { - #@unlink($this->cache_dir . "/$name.php"); + //@unlink($this->cache_dir . "/$name.php"); } } @@ -663,7 +663,7 @@ protected function generateMap ($class_name, $base_path = "") { (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); - #take note of the order + //take note of the order $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), diff --git a/restler_minified/restler.php b/restler_minified/restler.php index a9f9324ab..1d51cc071 100644 --- a/restler_minified/restler.php +++ b/restler_minified/restler.php @@ -1,5 +1,5 @@ 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); protected $cached; public function __construct ($production_mode = FALSE) { $this->production_mode = $production_mode; $this->cache_dir = getcwd(); $this->base_dir = RESTLER_PATH; } public function __destruct () { if ($this->production_mode && !$this->cached) { $this->saveCache(); } } public function refreshCache () { $this->routes = array(); $this->cached = FALSE; } public function setSupportedFormats () { $args = func_get_args(); $extensions = array(); foreach ($args as $class_name) { if(!is_string($class_name) || !class_exists($class_name)){ throw new Exception("$class_name is not a vaild Format Class."); } $obj = new $class_name; if(! $obj instanceof iFormat){ throw new Exception('Invalid format class; must implement '. 'iFormat interface'); } foreach ($obj->getMIMEMap() as $extension => $mime) { if(!isset($this->format_map[$extension])) $this->format_map[$extension]=$class_name; if(!isset($this->format_map[$mime])) $this->format_map[$mime]=$class_name; $extensions[".$extension"]=TRUE; } } $this->format_map['default']=$args[0]; $this->format_map['extensions']=array_keys($extensions); } public function addAPIClass($class_name, $base_path = NULL) { if(!class_exists($class_name)){ throw new Exception("API class $class_name is missing."); } $this->loadCache(); if(!$this->cached){ if(is_null($base_path))$base_path=strtolower($class_name); $base_path = trim($base_path,'/'); if(strlen($base_path)>0)$base_path .= '/'; $this->generateMap($class_name, $base_path); } } public function addAuthenticationClass ($class_name, $base_path = NULL) { $this->auth_classes[] = $class_name; $this->addAPIClass($class_name, $base_path); } public function addErrorClass ($class_name) { $this->error_classes[] = $class_name; } public function handleError ($status_code, $error_message = NULL) { $method = "handle$status_code"; $handled = FALSE; foreach ($this->error_classes as $class_name) { if (method_exists($class_name, $method)) { $obj = new $class_name(); $obj->restler = $this; $obj->$method(); $handled = TRUE; } } if($handled)return; $message = $this->codes[$status_code] . (!$error_message ? '' : ': ' . $error_message); $this->setStatus($status_code); $this->sendData( call_user_func( array($this->response, '__formatError'), $status_code, $message) ); } public function handle () { if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); $this->url = $this->getPath(); $this->request_method = $this->getRequestMethod(); $this->response_format = $this->getResponseFormat(); $this->request_format = $this->getRequestFormat(); if(is_null($this->request_format)){ $this->request_format = $this->response_format; } if($this->request_method == 'PUT' || $this->request_method == 'POST'){ $this->request_data = $this->getRequestData(); } $o = $this->mapUrlToMethod(); if(!isset($o->class_name)){ $this->handleError(404); }else{ try { if($o->method_flag){ $auth_method = '__isAuthenticated'; if(!count($this->auth_classes))throw new RestException(401); foreach ($this->auth_classes as $auth_class) { $auth_obj = new $auth_class(); $auth_obj->restler=$this; $this->applyClassMetadata($auth_class, $auth_obj, $o); if (!method_exists($auth_obj, $auth_method)) { throw new RestException(401, 'Authentication Class '. 'should implement iAuthenticate'); }elseif(!$auth_obj->$auth_method()){ throw new RestException(401); } } } $this->applyClassMetadata(get_class($this->request_format), $this->request_format, $o); $pre_process = '_'.$this->request_format->getExtension().'_'. $o->method_name; $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); $object->restler=$this; if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); } switch ($o->method_flag) { case 3: $reflection_method = new ReflectionMethod($object, $o->method_name); $reflection_method->setAccessible(TRUE); $result = $reflection_method->invokeArgs($object, $o->arguments); break; case 2: case 1: default: $result = call_user_func_array(array($object, $o->method_name), $o->arguments); } } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } if (isset($result) && $result !== NULL) { $this->sendData($result); } } public function sendData($data) { $data = $this->response_format->encode($data, !$this->production_mode); $post_process = '_'.$this->service_method .'_'. $this->response_format->getExtension(); if(isset($this->service_class_instance) && method_exists($this->service_class_instance,$post_process)){ $data = call_user_func(array($this->service_class_instance, $post_process), $data); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: 0"); header('Content-Type: ' . $this->response_format->getMIME()); header("X-Powered-By: Luracast Restler v".Restler::VERSION); die($data); } public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ". $this->codes[strval($code)]); } public function removeCommonPath($first, $second, $char='/'){ $first = explode($char, $first); $second = explode($char, $second); while (count($second)){ if($first[0]==$second[0]){ array_shift($first); } else break; array_shift($second); } return implode($char, $first); } public function saveCache() { $file = $this->cache_dir . '/routes.php'; $s = '$o=array();'.PHP_EOL; foreach ($this->routes as $key => $value) { $s .= PHP_EOL.PHP_EOL.PHP_EOL."############### $key ###############" .PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\']=array();'; foreach ($value as $ke => $va) { $s .= PHP_EOL.PHP_EOL."#==== $key $ke".PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\'][\''.$ke.'\']='.str_replace(PHP_EOL, PHP_EOL."\t", var_export($va, TRUE)).';'; } } $s .= PHP_EOL.'return $o;'; $r=@file_put_contents($file, "cache_dir' needs to have ". "the permissions set to read/write/execute for everyone in order ". "to save cache and improve performance."); } protected function getPath () { $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])); $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); $path = str_replace($this->format_map['extensions'], '', $path); return $path; } protected function getRequestMethod () { $method = $_SERVER['REQUEST_METHOD']; if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } return $method; } protected function getRequestFormat () { $format=NULL; if(isset($_SERVER['CONTENT_TYPE'])){ $mime = explode(';', $_SERVER['CONTENT_TYPE']); $mime = $mime[0]; if($mime==UrlEncodedFormat::MIME){ $format = new UrlEncodedFormat(); }else{ if(isset($this->format_map[$mime])){ $format = $this->format_map[$mime]; $format = is_string($format) ? new $format: $format; $format->setMIME($mime); } } } return $format; } protected function getResponseFormat () { $format; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); if($extension && isset($this->format_map[$extension])){ $format = $this->format_map[$extension]; $format = is_string($format) ? new $format: $format; $format->setExtension($extension); return $format; } if(isset($_SERVER['HTTP_ACCEPT'])){ $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); foreach ($accepts as $accept) { if($extension && isset($this->format_map[$accept])){ $format = $this->format_map[$accept]; $format = is_string($format) ? new $format: $format; $format->setMIME($accept); return $format; } } } $format = $this->format_map['default']; return is_string($format) ? new $format: $format; } protected function getRequestData() { try{ $r = file_get_contents('php://input'); if(is_null($r))return $_GET; $r =$this->request_format->decode($r); return is_null($r) ? array(): $r; } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } protected function mapUrlToMethod () { if(!isset($this->routes[$this->request_method])){ return array(); } $urls = $this->routes[$this->request_method]; if(!$urls)return array(); $found = FALSE; $this->request_data += $_GET; $params = array('request_data'=>$this->request_data); $params += $this->request_data; foreach ($urls as $url => $call) { $call = (object)$call; if(strstr($url, ':')){ $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); if (preg_match(":^$regex$:", $this->url, $matches)) { foreach ($matches as $arg => $match) { if (isset($call->arguments[$arg])){ $params[$arg] = $match; } } $found = TRUE; break; } }elseif ($url == $this->url){ $found = TRUE; break; } } if($found){ $p = $call->defaults; foreach ($call->arguments as $key => $value) { if(isset($params[$key]))$p[$value] = $params[$key]; } $call->arguments=$p; return $call; } } protected function applyClassMetadata($class_name, $instance, $method_info){ if(isset($method_info->metadata[$class_name]) && is_array($method_info->metadata[$class_name])){ foreach ($method_info->metadata[$class_name] as $property => $value){ if(property_exists($class_name, $property)){ $reflection_property = new ReflectionProperty($class_name, $property); $reflection_property->setValue($instance, $value); } } } } protected function loadCache() { if ($this->cached !== NULL) { return; } $file = $this->cache_dir . '/routes.php'; $this->cached = FALSE; if ($this->production_mode) { if (file_exists($file)) { $routes = include($file); } if (isset($routes) && is_array($routes)) { $this->routes = $routes; $this->cached = TRUE; } } else { } } protected function generateMap ($class_name, $base_path = "") { $reflection = new ReflectionClass($class_name); $class_metadata = parse_doc($reflection->getDocComment()); $methods = $reflection->getMethods( ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $doc = $method->getDocComment(); $arguments = array(); $defaults = array(); $metadata = $class_metadata+parse_doc($doc); $params = $method->getParameters(); $position=0; foreach ($params as $param){ $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; $position++; } $method_flag = $method->isProtected() ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), 'arguments'=>$arguments, 'defaults'=>$defaults, 'metadata'=>$metadata, 'method_flag'=>$method_flag ); $method_url = strtolower($method->getName()); if (preg_match_all( '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $http_method = $match[1]; $url = rtrim($base_path . $match[2],'/'); $this->routes[$http_method][$url] = $call; } }elseif($method_url[0] != '_'){ if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', $method_url, $matches)) { $http_method = strtoupper($matches[0][0]); $method_url = substr($method_url, strlen($http_method)); }else{ $http_method = 'GET'; } $url = $base_path. ($method_url=='index' || $method_url=='default' ? '' : $method_url); $url = rtrim($url,'/'); $this->routes[$http_method][$url] = $call; foreach ($params as $param){ if($param->getName()=='request_data'){ break; } $url .= $url=='' ? ':' : '/:'; $url .= $param->getName(); $this->routes[$http_method][$url] = $call; } } } } } if(version_compare(PHP_VERSION, '5.3.0') < 0){ require_once 'compat.php'; } class RestException extends Exception { public function __construct($http_status_code, $error_message = NULL) { parent::__construct($error_message, $http_status_code); } } interface iRespond { public function __formatResponse($result); public function __formatError($status_code, $message); } class DefaultResponse implements iRespond { function __formatResponse($result) { return $result; } function __formatError($statusCode, $message) { return array('error' => array('code' => $statusCode, 'message' => $message)); } } interface iAuthenticate { public function __isAuthenticated(); } interface iFormat { public function getMIMEMap(); public function setMIME($mime); public function getMIME(); public function setExtension($extension); public function getExtension(); public function encode($data, $human_readable=FALSE); public function decode($data); } class UrlEncodedFormat implements iFormat { const MIME = 'application/x-www-form-urlencoded'; const EXTENSION = 'post'; public function getMIMEMap() { return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); } public function getMIME(){ return UrlEncodedFormat::MIME; } public function getExtension(){ return UrlEncodedFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return http_build_query($data); } public function decode($data){ parse_str($data,$r); return $r; } public function __toString(){ return $this->getExtension(); } } class JsonFormat implements iFormat { const MIME ='application/json'; const EXTENSION = 'json'; public function getMIMEMap() { return array(JsonFormat::EXTENSION=>JsonFormat::MIME); } public function getMIME(){ return JsonFormat::MIME; } public function getExtension(){ return JsonFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); } public function decode($data){ return object_to_array(json_decode($data)); } private function json_format($json) { $tab = " "; $new_json = ""; $indent_level = 0; $in_string = FALSE; $len = strlen($json); for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $indent_level--; $new_json .= "\n".str_repeat($tab, $indent_level).$char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",\n" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c==0){ $in_string = TRUE; }elseif($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } public function __toString(){ return $this->getExtension(); } } class DocParser { private $params=array(); function parse($doc='') { if($doc==''){ return $this->params; } if(preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) return $this->params; $comment = trim($comment[1]); if(preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) return $this->params; $this->parseLines($lines[1]); return $this->params; } private function parseLines($lines) { foreach($lines as $line) { $parsedLine = $this->parseLine($line); if($parsedLine === false && !isset($this->params['description'])) { if(isset($desc)){ $this->params['description'] = implode(PHP_EOL, $desc); } $desc = array(); } elseif($parsedLine !== false) { $desc[] = $parsedLine; } } $desc = implode(' ', $desc); if(!empty($desc))$this->params['long_description'] = $desc; } private function parseLine($line) { $line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { if(strpos($line, ' ')>0){ $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2); }else{ $param = substr($line, 1); $value = ''; } if($this->setParam($param, $value)) return false; } return $line; } private function setParam($param, $value) { if($param == 'param' || $param == 'return') $value = $this->formatParamOrReturn($value); if($param == 'class') list($param, $value) = $this->formatClass($value); if(empty($this->params[$param])) { $this->params[$param] = $value; } else if($param == 'param'){ $arr = array($this->params[$param], $value); $this->params[$param] = $arr; } else { $this->params[$param] = $value + $this->params[$param]; } return true; } private function formatClass($value) { $r = preg_split("[\(|\)]",$value); if(is_array($r)){ $param = $r[0]; parse_str($r[1],$value); foreach ($value as $key => $val) { $val = explode(',', $val); if(count($val)>1)$value[$key]=$val; } }else{ $param='Unknown'; } return array($param, $value); } private function formatParamOrReturn($string) { $pos = strpos($string, ' '); $type = substr($string, 0, $pos); return '(' . $type . ')' . substr($string, $pos+1); } } function parse_doc($php_doc_comment){ $p = new DocParser(); return $p->parse($php_doc_comment); $p = new Parser($php_doc_comment); return $p; $php_doc_comment = preg_replace("/(^[\\s]*\\/\\*\\*) + class Restler { const VERSION = '2.0.3'; public $url; public $request_method; public $request_format; public $request_data=array(); public $cache_dir; public $base_dir; public $response = 'DefaultResponse'; public $response_format; protected $production_mode; protected $routes= array(); protected $format_map = array(); protected $service_class_instance; protected $service_method; protected $auth_classes = array(); protected $error_classes = array(); private $codes = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); protected $cached; public function __construct ($production_mode = FALSE) { $this->production_mode = $production_mode; $this->cache_dir = getcwd(); $this->base_dir = RESTLER_PATH; } public function __destruct () { if ($this->production_mode && !$this->cached) { $this->saveCache(); } } public function refreshCache () { $this->routes = array(); $this->cached = FALSE; } public function setSupportedFormats () { $args = func_get_args(); $extensions = array(); foreach ($args as $class_name) { if(!is_string($class_name) || !class_exists($class_name)){ throw new Exception("$class_name is not a vaild Format Class."); } $obj = new $class_name; if(! $obj instanceof iFormat){ throw new Exception('Invalid format class; must implement '. 'iFormat interface'); } foreach ($obj->getMIMEMap() as $extension => $mime) { if(!isset($this->format_map[$extension])) $this->format_map[$extension]=$class_name; if(!isset($this->format_map[$mime])) $this->format_map[$mime]=$class_name; $extensions[".$extension"]=TRUE; } } $this->format_map['default']=$args[0]; $this->format_map['extensions']=array_keys($extensions); } public function addAPIClass($class_name, $base_path = NULL) { if(!class_exists($class_name)){ throw new Exception("API class $class_name is missing."); } $this->loadCache(); if(!$this->cached){ if(is_null($base_path))$base_path=strtolower($class_name); $base_path = trim($base_path,'/'); if(strlen($base_path)>0)$base_path .= '/'; $this->generateMap($class_name, $base_path); } } public function addAuthenticationClass ($class_name, $base_path = NULL) { $this->auth_classes[] = $class_name; $this->addAPIClass($class_name, $base_path); } public function addErrorClass ($class_name) { $this->error_classes[] = $class_name; } public function handleError ($status_code, $error_message = NULL) { $method = "handle$status_code"; $handled = FALSE; foreach ($this->error_classes as $class_name) { if (method_exists($class_name, $method)) { $obj = new $class_name(); $obj->restler = $this; $obj->$method(); $handled = TRUE; } } if($handled)return; $message = $this->codes[$status_code] . (!$error_message ? '' : ': ' . $error_message); $this->setStatus($status_code); $this->sendData( call_user_func( array($this->response, '__formatError'), $status_code, $message) ); } public function handle () { if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); $this->url = $this->getPath(); $this->request_method = $this->getRequestMethod(); $this->response_format = $this->getResponseFormat(); $this->request_format = $this->getRequestFormat(); if(is_null($this->request_format)){ $this->request_format = $this->response_format; } if($this->request_method == 'PUT' || $this->request_method == 'POST'){ $this->request_data = $this->getRequestData(); } $o = $this->mapUrlToMethod(); if(!isset($o->class_name)){ $this->handleError(404); }else{ try { if($o->method_flag){ $auth_method = '__isAuthenticated'; if(!count($this->auth_classes))throw new RestException(401); foreach ($this->auth_classes as $auth_class) { $auth_obj = new $auth_class(); $auth_obj->restler=$this; $this->applyClassMetadata($auth_class, $auth_obj, $o); if (!method_exists($auth_obj, $auth_method)) { throw new RestException(401, 'Authentication Class '. 'should implement iAuthenticate'); }elseif(!$auth_obj->$auth_method()){ throw new RestException(401); } } } $this->applyClassMetadata(get_class($this->request_format), $this->request_format, $o); $pre_process = '_'.$this->request_format->getExtension().'_'. $o->method_name; $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); $object->restler=$this; if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); } switch ($o->method_flag) { case 3: $reflection_method = new ReflectionMethod($object, $o->method_name); $reflection_method->setAccessible(TRUE); $result = $reflection_method->invokeArgs($object, $o->arguments); break; case 2: case 1: default: $result = call_user_func_array(array($object, $o->method_name), $o->arguments); } } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } if (isset($result) && $result !== NULL) { $this->sendData($result); } } public function sendData($data) { $data = $this->response_format->encode($data, !$this->production_mode); $post_process = '_'.$this->service_method .'_'. $this->response_format->getExtension(); if(isset($this->service_class_instance) && method_exists($this->service_class_instance,$post_process)){ $data = call_user_func(array($this->service_class_instance, $post_process), $data); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: 0"); header('Content-Type: ' . $this->response_format->getMIME()); header("X-Powered-By: Luracast Restler v".Restler::VERSION); die($data); } public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ". $this->codes[strval($code)]); } public function removeCommonPath($first, $second, $char='/'){ $first = explode($char, $first); $second = explode($char, $second); while (count($second)){ if($first[0]==$second[0]){ array_shift($first); } else break; array_shift($second); } return implode($char, $first); } public function saveCache() { $file = $this->cache_dir . '/routes.php'; $s = '$o=array();'.PHP_EOL; foreach ($this->routes as $key => $value) { $s .= PHP_EOL.PHP_EOL.PHP_EOL."############### $key ###############" .PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\']=array();'; foreach ($value as $ke => $va) { $s .= PHP_EOL.PHP_EOL."#==== $key $ke".PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\'][\''.$ke.'\']='.str_replace(PHP_EOL, PHP_EOL."\t", var_export($va, TRUE)).';'; } } $s .= PHP_EOL.'return $o;'; $r=@file_put_contents($file, "cache_dir' needs to have ". "the permissions set to read/write/execute for everyone in order ". "to save cache and improve performance."); } protected function getPath () { $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])); $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); $path = str_replace($this->format_map['extensions'], '', $path); return $path; } protected function getRequestMethod () { $method = $_SERVER['REQUEST_METHOD']; if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } return $method; } protected function getRequestFormat () { $format=NULL; if(isset($_SERVER['CONTENT_TYPE'])){ $mime = explode(';', $_SERVER['CONTENT_TYPE']); $mime = $mime[0]; if($mime==UrlEncodedFormat::MIME){ $format = new UrlEncodedFormat(); }else{ if(isset($this->format_map[$mime])){ $format = $this->format_map[$mime]; $format = is_string($format) ? new $format: $format; $format->setMIME($mime); } } } return $format; } protected function getResponseFormat () { $format=NULL; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); if($extension && isset($this->format_map[$extension])){ $format = $this->format_map[$extension]; $format = is_string($format) ? new $format: $format; $format->setExtension($extension); return $format; } if(isset($_SERVER['HTTP_ACCEPT'])){ $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); foreach ($accepts as $accept) { if($extension && isset($this->format_map[$accept])){ $format = $this->format_map[$accept]; $format = is_string($format) ? new $format: $format; $format->setMIME($accept); return $format; } } } $format = $this->format_map['default']; return $format; } protected function getRequestData() { try{ $r = file_get_contents('php://input'); if(is_null($r))return $_GET; $r =$this->request_format->decode($r); return is_null($r) ? array(): $r; } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } protected function mapUrlToMethod () { if(!isset($this->routes[$this->request_method])){ return array(); } $urls = $this->routes[$this->request_method]; if(!$urls)return array(); $found = FALSE; $this->request_data += $_GET; $params = array('request_data'=>$this->request_data); $params += $this->request_data; foreach ($urls as $url => $call) { $call = (object)$call; if(strstr($url, ':')){ $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); if (preg_match(":^$regex$:", $this->url, $matches)) { foreach ($matches as $arg => $match) { if (isset($call->arguments[$arg])){ $params[$arg] = $match; } } $found = TRUE; break; } }elseif ($url == $this->url){ $found = TRUE; break; } } if($found){ $p = $call->defaults; foreach ($call->arguments as $key => $value) { if(isset($params[$key]))$p[$value] = $params[$key]; } $call->arguments=$p; return $call; } } protected function applyClassMetadata($class_name, $instance, $method_info){ if(isset($method_info->metadata[$class_name]) && is_array($method_info->metadata[$class_name])){ foreach ($method_info->metadata[$class_name] as $property => $value){ if(property_exists($class_name, $property)){ $reflection_property = new ReflectionProperty($class_name, $property); $reflection_property->setValue($instance, $value); } } } } protected function loadCache() { if ($this->cached !== NULL) { return; } $file = $this->cache_dir . '/routes.php'; $this->cached = FALSE; if ($this->production_mode) { if (file_exists($file)) { $routes = include($file); } if (isset($routes) && is_array($routes)) { $this->routes = $routes; $this->cached = TRUE; } } else { } } protected function generateMap ($class_name, $base_path = "") { $reflection = new ReflectionClass($class_name); $class_metadata = parse_doc($reflection->getDocComment()); $methods = $reflection->getMethods( ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $doc = $method->getDocComment(); $arguments = array(); $defaults = array(); $metadata = $class_metadata+parse_doc($doc); $params = $method->getParameters(); $position=0; foreach ($params as $param){ $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; $position++; } $method_flag = $method->isProtected() ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), 'arguments'=>$arguments, 'defaults'=>$defaults, 'metadata'=>$metadata, 'method_flag'=>$method_flag ); $method_url = strtolower($method->getName()); if (preg_match_all( '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $http_method = $match[1]; $url = rtrim($base_path . $match[2],'/'); $this->routes[$http_method][$url] = $call; } }elseif($method_url[0] != '_'){ if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', $method_url, $matches)) { $http_method = strtoupper($matches[0][0]); $method_url = substr($method_url, strlen($http_method)); }else{ $http_method = 'GET'; } $url = $base_path. ($method_url=='index' || $method_url=='default' ? '' : $method_url); $url = rtrim($url,'/'); $this->routes[$http_method][$url] = $call; foreach ($params as $param){ if($param->getName()=='request_data'){ break; } $url .= $url=='' ? ':' : '/:'; $url .= $param->getName(); $this->routes[$http_method][$url] = $call; } } } } } if(version_compare(PHP_VERSION, '5.3.0') < 0){ require_once 'compat.php'; } class RestException extends Exception { public function __construct($http_status_code, $error_message = NULL) { parent::__construct($error_message, $http_status_code); } } interface iRespond { public function __formatResponse($result); public function __formatError($status_code, $message); } class DefaultResponse implements iRespond { function __formatResponse($result) { return $result; } function __formatError($statusCode, $message) { return array('error' => array('code' => $statusCode, 'message' => $message)); } } interface iAuthenticate { public function __isAuthenticated(); } interface iFormat { public function getMIMEMap(); public function setMIME($mime); public function getMIME(); public function setExtension($extension); public function getExtension(); public function encode($data, $human_readable=FALSE); public function decode($data); } class UrlEncodedFormat implements iFormat { const MIME = 'application/x-www-form-urlencoded'; const EXTENSION = 'post'; public function getMIMEMap() { return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); } public function getMIME(){ return UrlEncodedFormat::MIME; } public function getExtension(){ return UrlEncodedFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return http_build_query($data); } public function decode($data){ parse_str($data,$r); return $r; } public function __toString(){ return $this->getExtension(); } } class JsonFormat implements iFormat { const MIME ='application/json'; const EXTENSION = 'json'; public function getMIMEMap() { return array(JsonFormat::EXTENSION=>JsonFormat::MIME); } public function getMIME(){ return JsonFormat::MIME; } public function getExtension(){ return JsonFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); } public function decode($data){ return object_to_array(json_decode($data)); } private function json_format($json) { $tab = " "; $new_json = ""; $indent_level = 0; $in_string = FALSE; $len = strlen($json); for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $indent_level--; $new_json .= "\n".str_repeat($tab, $indent_level).$char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",\n" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c==0){ $in_string = TRUE; }elseif($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } public function __toString(){ return $this->getExtension(); } } class DocParser { private $params=array(); function parse($doc='') { if($doc==''){ return $this->params; } if(preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) return $this->params; $comment = trim($comment[1]); if(preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) return $this->params; $this->parseLines($lines[1]); return $this->params; } private function parseLines($lines) { foreach($lines as $line) { $parsedLine = $this->parseLine($line); if($parsedLine === false && !isset($this->params['description'])) { if(isset($desc)){ $this->params['description'] = implode(PHP_EOL, $desc); } $desc = array(); } elseif($parsedLine !== false) { $desc[] = $parsedLine; } } $desc = implode(' ', $desc); if(!empty($desc))$this->params['long_description'] = $desc; } private function parseLine($line) { $line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { if(strpos($line, ' ')>0){ $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2); }else{ $param = substr($line, 1); $value = ''; } if($this->setParam($param, $value)) return false; } return $line; } private function setParam($param, $value) { if($param == 'param' || $param == 'return') $value = $this->formatParamOrReturn($value); if($param == 'class') list($param, $value) = $this->formatClass($value); if(empty($this->params[$param])) { $this->params[$param] = $value; } else if($param == 'param'){ $arr = array($this->params[$param], $value); $this->params[$param] = $arr; } else { $this->params[$param] = $value + $this->params[$param]; } return true; } private function formatClass($value) { $r = preg_split("[\(|\)]",$value); if(is_array($r)){ $param = $r[0]; parse_str($r[1],$value); foreach ($value as $key => $val) { $val = explode(',', $val); if(count($val)>1)$value[$key]=$val; } }else{ $param='Unknown'; } return array($param, $value); } private function formatParamOrReturn($string) { $pos = strpos($string, ' '); $type = substr($string, 0, $pos); return '(' . $type . ')' . substr($string, $pos+1); } } function parse_doc($php_doc_comment){ $p = new DocParser(); return $p->parse($php_doc_comment); $p = new Parser($php_doc_comment); return $p; $php_doc_comment = preg_replace("/(^[\\s]*\\/\\*\\*) |(^[\\s]\\*\\/) |(^[\\s]*\\*?\\s) |(^[\\s]*) From c4717ca20b0c00c0eb8d047f244ad31240448212 Mon Sep 17 00:00:00 2001 From: Luracast Date: Tue, 18 Oct 2011 20:56:05 +0800 Subject: [PATCH 20/61] Fixes #11 a bug that is introduced in v2.0.3 which makes restler fail with the error "get_class() expects param 1 to be object, string given in restler.php on line 335" --- restler/restler.php | 6 +++--- restler_minified/restler.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/restler/restler.php b/restler/restler.php index d838f4a56..d072c6908 100644 --- a/restler/restler.php +++ b/restler/restler.php @@ -10,10 +10,10 @@ * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ - * @version 2.0.3 + * @version 2.0.4 */ class Restler { - const VERSION = '2.0.3'; + const VERSION = '2.0.4'; /** * URL of the currently mapped service * @var string @@ -527,7 +527,7 @@ protected function getResponseFormat () { } } } - $format = $this->format_map['default']; + $format = new $this->format_map['default']; //echo "DEFAULT ".$this->format_map['default']; return $format; } diff --git a/restler_minified/restler.php b/restler_minified/restler.php index 1d51cc071..92c262fb0 100644 --- a/restler_minified/restler.php +++ b/restler_minified/restler.php @@ -1,5 +1,5 @@ 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); protected $cached; public function __construct ($production_mode = FALSE) { $this->production_mode = $production_mode; $this->cache_dir = getcwd(); $this->base_dir = RESTLER_PATH; } public function __destruct () { if ($this->production_mode && !$this->cached) { $this->saveCache(); } } public function refreshCache () { $this->routes = array(); $this->cached = FALSE; } public function setSupportedFormats () { $args = func_get_args(); $extensions = array(); foreach ($args as $class_name) { if(!is_string($class_name) || !class_exists($class_name)){ throw new Exception("$class_name is not a vaild Format Class."); } $obj = new $class_name; if(! $obj instanceof iFormat){ throw new Exception('Invalid format class; must implement '. 'iFormat interface'); } foreach ($obj->getMIMEMap() as $extension => $mime) { if(!isset($this->format_map[$extension])) $this->format_map[$extension]=$class_name; if(!isset($this->format_map[$mime])) $this->format_map[$mime]=$class_name; $extensions[".$extension"]=TRUE; } } $this->format_map['default']=$args[0]; $this->format_map['extensions']=array_keys($extensions); } public function addAPIClass($class_name, $base_path = NULL) { if(!class_exists($class_name)){ throw new Exception("API class $class_name is missing."); } $this->loadCache(); if(!$this->cached){ if(is_null($base_path))$base_path=strtolower($class_name); $base_path = trim($base_path,'/'); if(strlen($base_path)>0)$base_path .= '/'; $this->generateMap($class_name, $base_path); } } public function addAuthenticationClass ($class_name, $base_path = NULL) { $this->auth_classes[] = $class_name; $this->addAPIClass($class_name, $base_path); } public function addErrorClass ($class_name) { $this->error_classes[] = $class_name; } public function handleError ($status_code, $error_message = NULL) { $method = "handle$status_code"; $handled = FALSE; foreach ($this->error_classes as $class_name) { if (method_exists($class_name, $method)) { $obj = new $class_name(); $obj->restler = $this; $obj->$method(); $handled = TRUE; } } if($handled)return; $message = $this->codes[$status_code] . (!$error_message ? '' : ': ' . $error_message); $this->setStatus($status_code); $this->sendData( call_user_func( array($this->response, '__formatError'), $status_code, $message) ); } public function handle () { if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); $this->url = $this->getPath(); $this->request_method = $this->getRequestMethod(); $this->response_format = $this->getResponseFormat(); $this->request_format = $this->getRequestFormat(); if(is_null($this->request_format)){ $this->request_format = $this->response_format; } if($this->request_method == 'PUT' || $this->request_method == 'POST'){ $this->request_data = $this->getRequestData(); } $o = $this->mapUrlToMethod(); if(!isset($o->class_name)){ $this->handleError(404); }else{ try { if($o->method_flag){ $auth_method = '__isAuthenticated'; if(!count($this->auth_classes))throw new RestException(401); foreach ($this->auth_classes as $auth_class) { $auth_obj = new $auth_class(); $auth_obj->restler=$this; $this->applyClassMetadata($auth_class, $auth_obj, $o); if (!method_exists($auth_obj, $auth_method)) { throw new RestException(401, 'Authentication Class '. 'should implement iAuthenticate'); }elseif(!$auth_obj->$auth_method()){ throw new RestException(401); } } } $this->applyClassMetadata(get_class($this->request_format), $this->request_format, $o); $pre_process = '_'.$this->request_format->getExtension().'_'. $o->method_name; $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); $object->restler=$this; if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); } switch ($o->method_flag) { case 3: $reflection_method = new ReflectionMethod($object, $o->method_name); $reflection_method->setAccessible(TRUE); $result = $reflection_method->invokeArgs($object, $o->arguments); break; case 2: case 1: default: $result = call_user_func_array(array($object, $o->method_name), $o->arguments); } } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } if (isset($result) && $result !== NULL) { $this->sendData($result); } } public function sendData($data) { $data = $this->response_format->encode($data, !$this->production_mode); $post_process = '_'.$this->service_method .'_'. $this->response_format->getExtension(); if(isset($this->service_class_instance) && method_exists($this->service_class_instance,$post_process)){ $data = call_user_func(array($this->service_class_instance, $post_process), $data); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: 0"); header('Content-Type: ' . $this->response_format->getMIME()); header("X-Powered-By: Luracast Restler v".Restler::VERSION); die($data); } public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ". $this->codes[strval($code)]); } public function removeCommonPath($first, $second, $char='/'){ $first = explode($char, $first); $second = explode($char, $second); while (count($second)){ if($first[0]==$second[0]){ array_shift($first); } else break; array_shift($second); } return implode($char, $first); } public function saveCache() { $file = $this->cache_dir . '/routes.php'; $s = '$o=array();'.PHP_EOL; foreach ($this->routes as $key => $value) { $s .= PHP_EOL.PHP_EOL.PHP_EOL."############### $key ###############" .PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\']=array();'; foreach ($value as $ke => $va) { $s .= PHP_EOL.PHP_EOL."#==== $key $ke".PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\'][\''.$ke.'\']='.str_replace(PHP_EOL, PHP_EOL."\t", var_export($va, TRUE)).';'; } } $s .= PHP_EOL.'return $o;'; $r=@file_put_contents($file, "cache_dir' needs to have ". "the permissions set to read/write/execute for everyone in order ". "to save cache and improve performance."); } protected function getPath () { $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])); $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); $path = str_replace($this->format_map['extensions'], '', $path); return $path; } protected function getRequestMethod () { $method = $_SERVER['REQUEST_METHOD']; if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } return $method; } protected function getRequestFormat () { $format=NULL; if(isset($_SERVER['CONTENT_TYPE'])){ $mime = explode(';', $_SERVER['CONTENT_TYPE']); $mime = $mime[0]; if($mime==UrlEncodedFormat::MIME){ $format = new UrlEncodedFormat(); }else{ if(isset($this->format_map[$mime])){ $format = $this->format_map[$mime]; $format = is_string($format) ? new $format: $format; $format->setMIME($mime); } } } return $format; } protected function getResponseFormat () { $format=NULL; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); if($extension && isset($this->format_map[$extension])){ $format = $this->format_map[$extension]; $format = is_string($format) ? new $format: $format; $format->setExtension($extension); return $format; } if(isset($_SERVER['HTTP_ACCEPT'])){ $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); foreach ($accepts as $accept) { if($extension && isset($this->format_map[$accept])){ $format = $this->format_map[$accept]; $format = is_string($format) ? new $format: $format; $format->setMIME($accept); return $format; } } } $format = $this->format_map['default']; return $format; } protected function getRequestData() { try{ $r = file_get_contents('php://input'); if(is_null($r))return $_GET; $r =$this->request_format->decode($r); return is_null($r) ? array(): $r; } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } protected function mapUrlToMethod () { if(!isset($this->routes[$this->request_method])){ return array(); } $urls = $this->routes[$this->request_method]; if(!$urls)return array(); $found = FALSE; $this->request_data += $_GET; $params = array('request_data'=>$this->request_data); $params += $this->request_data; foreach ($urls as $url => $call) { $call = (object)$call; if(strstr($url, ':')){ $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); if (preg_match(":^$regex$:", $this->url, $matches)) { foreach ($matches as $arg => $match) { if (isset($call->arguments[$arg])){ $params[$arg] = $match; } } $found = TRUE; break; } }elseif ($url == $this->url){ $found = TRUE; break; } } if($found){ $p = $call->defaults; foreach ($call->arguments as $key => $value) { if(isset($params[$key]))$p[$value] = $params[$key]; } $call->arguments=$p; return $call; } } protected function applyClassMetadata($class_name, $instance, $method_info){ if(isset($method_info->metadata[$class_name]) && is_array($method_info->metadata[$class_name])){ foreach ($method_info->metadata[$class_name] as $property => $value){ if(property_exists($class_name, $property)){ $reflection_property = new ReflectionProperty($class_name, $property); $reflection_property->setValue($instance, $value); } } } } protected function loadCache() { if ($this->cached !== NULL) { return; } $file = $this->cache_dir . '/routes.php'; $this->cached = FALSE; if ($this->production_mode) { if (file_exists($file)) { $routes = include($file); } if (isset($routes) && is_array($routes)) { $this->routes = $routes; $this->cached = TRUE; } } else { } } protected function generateMap ($class_name, $base_path = "") { $reflection = new ReflectionClass($class_name); $class_metadata = parse_doc($reflection->getDocComment()); $methods = $reflection->getMethods( ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $doc = $method->getDocComment(); $arguments = array(); $defaults = array(); $metadata = $class_metadata+parse_doc($doc); $params = $method->getParameters(); $position=0; foreach ($params as $param){ $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; $position++; } $method_flag = $method->isProtected() ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), 'arguments'=>$arguments, 'defaults'=>$defaults, 'metadata'=>$metadata, 'method_flag'=>$method_flag ); $method_url = strtolower($method->getName()); if (preg_match_all( '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $http_method = $match[1]; $url = rtrim($base_path . $match[2],'/'); $this->routes[$http_method][$url] = $call; } }elseif($method_url[0] != '_'){ if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', $method_url, $matches)) { $http_method = strtoupper($matches[0][0]); $method_url = substr($method_url, strlen($http_method)); }else{ $http_method = 'GET'; } $url = $base_path. ($method_url=='index' || $method_url=='default' ? '' : $method_url); $url = rtrim($url,'/'); $this->routes[$http_method][$url] = $call; foreach ($params as $param){ if($param->getName()=='request_data'){ break; } $url .= $url=='' ? ':' : '/:'; $url .= $param->getName(); $this->routes[$http_method][$url] = $call; } } } } } if(version_compare(PHP_VERSION, '5.3.0') < 0){ require_once 'compat.php'; } class RestException extends Exception { public function __construct($http_status_code, $error_message = NULL) { parent::__construct($error_message, $http_status_code); } } interface iRespond { public function __formatResponse($result); public function __formatError($status_code, $message); } class DefaultResponse implements iRespond { function __formatResponse($result) { return $result; } function __formatError($statusCode, $message) { return array('error' => array('code' => $statusCode, 'message' => $message)); } } interface iAuthenticate { public function __isAuthenticated(); } interface iFormat { public function getMIMEMap(); public function setMIME($mime); public function getMIME(); public function setExtension($extension); public function getExtension(); public function encode($data, $human_readable=FALSE); public function decode($data); } class UrlEncodedFormat implements iFormat { const MIME = 'application/x-www-form-urlencoded'; const EXTENSION = 'post'; public function getMIMEMap() { return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); } public function getMIME(){ return UrlEncodedFormat::MIME; } public function getExtension(){ return UrlEncodedFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return http_build_query($data); } public function decode($data){ parse_str($data,$r); return $r; } public function __toString(){ return $this->getExtension(); } } class JsonFormat implements iFormat { const MIME ='application/json'; const EXTENSION = 'json'; public function getMIMEMap() { return array(JsonFormat::EXTENSION=>JsonFormat::MIME); } public function getMIME(){ return JsonFormat::MIME; } public function getExtension(){ return JsonFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); } public function decode($data){ return object_to_array(json_decode($data)); } private function json_format($json) { $tab = " "; $new_json = ""; $indent_level = 0; $in_string = FALSE; $len = strlen($json); for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $indent_level--; $new_json .= "\n".str_repeat($tab, $indent_level).$char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",\n" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c==0){ $in_string = TRUE; }elseif($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } public function __toString(){ return $this->getExtension(); } } class DocParser { private $params=array(); function parse($doc='') { if($doc==''){ return $this->params; } if(preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) return $this->params; $comment = trim($comment[1]); if(preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) return $this->params; $this->parseLines($lines[1]); return $this->params; } private function parseLines($lines) { foreach($lines as $line) { $parsedLine = $this->parseLine($line); if($parsedLine === false && !isset($this->params['description'])) { if(isset($desc)){ $this->params['description'] = implode(PHP_EOL, $desc); } $desc = array(); } elseif($parsedLine !== false) { $desc[] = $parsedLine; } } $desc = implode(' ', $desc); if(!empty($desc))$this->params['long_description'] = $desc; } private function parseLine($line) { $line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { if(strpos($line, ' ')>0){ $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2); }else{ $param = substr($line, 1); $value = ''; } if($this->setParam($param, $value)) return false; } return $line; } private function setParam($param, $value) { if($param == 'param' || $param == 'return') $value = $this->formatParamOrReturn($value); if($param == 'class') list($param, $value) = $this->formatClass($value); if(empty($this->params[$param])) { $this->params[$param] = $value; } else if($param == 'param'){ $arr = array($this->params[$param], $value); $this->params[$param] = $arr; } else { $this->params[$param] = $value + $this->params[$param]; } return true; } private function formatClass($value) { $r = preg_split("[\(|\)]",$value); if(is_array($r)){ $param = $r[0]; parse_str($r[1],$value); foreach ($value as $key => $val) { $val = explode(',', $val); if(count($val)>1)$value[$key]=$val; } }else{ $param='Unknown'; } return array($param, $value); } private function formatParamOrReturn($string) { $pos = strpos($string, ' '); $type = substr($string, 0, $pos); return '(' . $type . ')' . substr($string, $pos+1); } } function parse_doc($php_doc_comment){ $p = new DocParser(); return $p->parse($php_doc_comment); $p = new Parser($php_doc_comment); return $p; $php_doc_comment = preg_replace("/(^[\\s]*\\/\\*\\*) + class Restler { const VERSION = '2.0.4'; public $url; public $request_method; public $request_format; public $request_data=array(); public $cache_dir; public $base_dir; public $response = 'DefaultResponse'; public $response_format; protected $production_mode; protected $routes= array(); protected $format_map = array(); protected $service_class_instance; protected $service_method; protected $auth_classes = array(); protected $error_classes = array(); private $codes = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); protected $cached; public function __construct ($production_mode = FALSE) { $this->production_mode = $production_mode; $this->cache_dir = getcwd(); $this->base_dir = RESTLER_PATH; } public function __destruct () { if ($this->production_mode && !$this->cached) { $this->saveCache(); } } public function refreshCache () { $this->routes = array(); $this->cached = FALSE; } public function setSupportedFormats () { $args = func_get_args(); $extensions = array(); foreach ($args as $class_name) { if(!is_string($class_name) || !class_exists($class_name)){ throw new Exception("$class_name is not a vaild Format Class."); } $obj = new $class_name; if(! $obj instanceof iFormat){ throw new Exception('Invalid format class; must implement '. 'iFormat interface'); } foreach ($obj->getMIMEMap() as $extension => $mime) { if(!isset($this->format_map[$extension])) $this->format_map[$extension]=$class_name; if(!isset($this->format_map[$mime])) $this->format_map[$mime]=$class_name; $extensions[".$extension"]=TRUE; } } $this->format_map['default']=$args[0]; $this->format_map['extensions']=array_keys($extensions); } public function addAPIClass($class_name, $base_path = NULL) { if(!class_exists($class_name)){ throw new Exception("API class $class_name is missing."); } $this->loadCache(); if(!$this->cached){ if(is_null($base_path))$base_path=strtolower($class_name); $base_path = trim($base_path,'/'); if(strlen($base_path)>0)$base_path .= '/'; $this->generateMap($class_name, $base_path); } } public function addAuthenticationClass ($class_name, $base_path = NULL) { $this->auth_classes[] = $class_name; $this->addAPIClass($class_name, $base_path); } public function addErrorClass ($class_name) { $this->error_classes[] = $class_name; } public function handleError ($status_code, $error_message = NULL) { $method = "handle$status_code"; $handled = FALSE; foreach ($this->error_classes as $class_name) { if (method_exists($class_name, $method)) { $obj = new $class_name(); $obj->restler = $this; $obj->$method(); $handled = TRUE; } } if($handled)return; $message = $this->codes[$status_code] . (!$error_message ? '' : ': ' . $error_message); $this->setStatus($status_code); $this->sendData( call_user_func( array($this->response, '__formatError'), $status_code, $message) ); } public function handle () { if(empty($this->format_map))$this->setSupportedFormats('JsonFormat'); $this->url = $this->getPath(); $this->request_method = $this->getRequestMethod(); $this->response_format = $this->getResponseFormat(); $this->request_format = $this->getRequestFormat(); if(is_null($this->request_format)){ $this->request_format = $this->response_format; } if($this->request_method == 'PUT' || $this->request_method == 'POST'){ $this->request_data = $this->getRequestData(); } $o = $this->mapUrlToMethod(); if(!isset($o->class_name)){ $this->handleError(404); }else{ try { if($o->method_flag){ $auth_method = '__isAuthenticated'; if(!count($this->auth_classes))throw new RestException(401); foreach ($this->auth_classes as $auth_class) { $auth_obj = new $auth_class(); $auth_obj->restler=$this; $this->applyClassMetadata($auth_class, $auth_obj, $o); if (!method_exists($auth_obj, $auth_method)) { throw new RestException(401, 'Authentication Class '. 'should implement iAuthenticate'); }elseif(!$auth_obj->$auth_method()){ throw new RestException(401); } } } $this->applyClassMetadata(get_class($this->request_format), $this->request_format, $o); $pre_process = '_'.$this->request_format->getExtension().'_'. $o->method_name; $this->service_method = $o->method_name; if($o->method_flag==2)$o=unprotect($o); $object = $this->service_class_instance = new $o->class_name(); $object->restler=$this; if(method_exists($o->class_name, $pre_process)) { call_user_func_array(array($object, $pre_process), $o->arguments); } switch ($o->method_flag) { case 3: $reflection_method = new ReflectionMethod($object, $o->method_name); $reflection_method->setAccessible(TRUE); $result = $reflection_method->invokeArgs($object, $o->arguments); break; case 2: case 1: default: $result = call_user_func_array(array($object, $o->method_name), $o->arguments); } } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } if (isset($result) && $result !== NULL) { $this->sendData($result); } } public function sendData($data) { $data = $this->response_format->encode($data, !$this->production_mode); $post_process = '_'.$this->service_method .'_'. $this->response_format->getExtension(); if(isset($this->service_class_instance) && method_exists($this->service_class_instance,$post_process)){ $data = call_user_func(array($this->service_class_instance, $post_process), $data); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: 0"); header('Content-Type: ' . $this->response_format->getMIME()); header("X-Powered-By: Luracast Restler v".Restler::VERSION); die($data); } public function setStatus($code) { header("{$_SERVER['SERVER_PROTOCOL']} $code ". $this->codes[strval($code)]); } public function removeCommonPath($first, $second, $char='/'){ $first = explode($char, $first); $second = explode($char, $second); while (count($second)){ if($first[0]==$second[0]){ array_shift($first); } else break; array_shift($second); } return implode($char, $first); } public function saveCache() { $file = $this->cache_dir . '/routes.php'; $s = '$o=array();'.PHP_EOL; foreach ($this->routes as $key => $value) { $s .= PHP_EOL.PHP_EOL.PHP_EOL."############### $key ###############" .PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\']=array();'; foreach ($value as $ke => $va) { $s .= PHP_EOL.PHP_EOL."#==== $key $ke".PHP_EOL.PHP_EOL; $s .= '$o[\''.$key.'\'][\''.$ke.'\']='.str_replace(PHP_EOL, PHP_EOL."\t", var_export($va, TRUE)).';'; } } $s .= PHP_EOL.'return $o;'; $r=@file_put_contents($file, "cache_dir' needs to have ". "the permissions set to read/write/execute for everyone in order ". "to save cache and improve performance."); } protected function getPath () { $path = urldecode($this->removeCommonPath($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])); $path = preg_replace('/(\/*\?.*$)|(\/$)/', '', $path); $path = str_replace($this->format_map['extensions'], '', $path); return $path; } protected function getRequestMethod () { $method = $_SERVER['REQUEST_METHOD']; if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])){ $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } return $method; } protected function getRequestFormat () { $format=NULL; if(isset($_SERVER['CONTENT_TYPE'])){ $mime = explode(';', $_SERVER['CONTENT_TYPE']); $mime = $mime[0]; if($mime==UrlEncodedFormat::MIME){ $format = new UrlEncodedFormat(); }else{ if(isset($this->format_map[$mime])){ $format = $this->format_map[$mime]; $format = is_string($format) ? new $format: $format; $format->setMIME($mime); } } } return $format; } protected function getResponseFormat () { $format=NULL; $extension = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); $extension = array_pop($extension); if($extension && isset($this->format_map[$extension])){ $format = $this->format_map[$extension]; $format = is_string($format) ? new $format: $format; $format->setExtension($extension); return $format; } if(isset($_SERVER['HTTP_ACCEPT'])){ $accepts = explode(',', $_SERVER['HTTP_ACCEPT']); foreach ($accepts as $accept) { if($extension && isset($this->format_map[$accept])){ $format = $this->format_map[$accept]; $format = is_string($format) ? new $format: $format; $format->setMIME($accept); return $format; } } } $format = new $this->format_map['default']; return $format; } protected function getRequestData() { try{ $r = file_get_contents('php://input'); if(is_null($r))return $_GET; $r =$this->request_format->decode($r); return is_null($r) ? array(): $r; } catch (RestException $e) { $this->handleError($e->getCode(), $e->getMessage()); } } protected function mapUrlToMethod () { if(!isset($this->routes[$this->request_method])){ return array(); } $urls = $this->routes[$this->request_method]; if(!$urls)return array(); $found = FALSE; $this->request_data += $_GET; $params = array('request_data'=>$this->request_data); $params += $this->request_data; foreach ($urls as $url => $call) { $call = (object)$call; if(strstr($url, ':')){ $regex = preg_replace('/\\\:([^\/]+)/', '(?P<$1>[^/]+)', preg_quote($url)); if (preg_match(":^$regex$:", $this->url, $matches)) { foreach ($matches as $arg => $match) { if (isset($call->arguments[$arg])){ $params[$arg] = $match; } } $found = TRUE; break; } }elseif ($url == $this->url){ $found = TRUE; break; } } if($found){ $p = $call->defaults; foreach ($call->arguments as $key => $value) { if(isset($params[$key]))$p[$value] = $params[$key]; } $call->arguments=$p; return $call; } } protected function applyClassMetadata($class_name, $instance, $method_info){ if(isset($method_info->metadata[$class_name]) && is_array($method_info->metadata[$class_name])){ foreach ($method_info->metadata[$class_name] as $property => $value){ if(property_exists($class_name, $property)){ $reflection_property = new ReflectionProperty($class_name, $property); $reflection_property->setValue($instance, $value); } } } } protected function loadCache() { if ($this->cached !== NULL) { return; } $file = $this->cache_dir . '/routes.php'; $this->cached = FALSE; if ($this->production_mode) { if (file_exists($file)) { $routes = include($file); } if (isset($routes) && is_array($routes)) { $this->routes = $routes; $this->cached = TRUE; } } else { } } protected function generateMap ($class_name, $base_path = "") { $reflection = new ReflectionClass($class_name); $class_metadata = parse_doc($reflection->getDocComment()); $methods = $reflection->getMethods( ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $doc = $method->getDocComment(); $arguments = array(); $defaults = array(); $metadata = $class_metadata+parse_doc($doc); $params = $method->getParameters(); $position=0; foreach ($params as $param){ $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; $position++; } $method_flag = $method->isProtected() ? (isRestlerCompatibilityModeEnabled() ? 2 : 3) : (isset($metadata['protected']) ? 1 : 0); $call = array( 'class_name'=>$class_name, 'method_name'=>$method->getName(), 'arguments'=>$arguments, 'defaults'=>$defaults, 'metadata'=>$metadata, 'method_flag'=>$method_flag ); $method_url = strtolower($method->getName()); if (preg_match_all( '/@url\s+(GET|POST|PUT|DELETE|HEAD|OPTIONS)[ \t]*\/?(\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $http_method = $match[1]; $url = rtrim($base_path . $match[2],'/'); $this->routes[$http_method][$url] = $call; } }elseif($method_url[0] != '_'){ if (preg_match_all('/^(GET|POST|PUT|DELETE|HEAD|OPTIONS)/i', $method_url, $matches)) { $http_method = strtoupper($matches[0][0]); $method_url = substr($method_url, strlen($http_method)); }else{ $http_method = 'GET'; } $url = $base_path. ($method_url=='index' || $method_url=='default' ? '' : $method_url); $url = rtrim($url,'/'); $this->routes[$http_method][$url] = $call; foreach ($params as $param){ if($param->getName()=='request_data'){ break; } $url .= $url=='' ? ':' : '/:'; $url .= $param->getName(); $this->routes[$http_method][$url] = $call; } } } } } if(version_compare(PHP_VERSION, '5.3.0') < 0){ require_once 'compat.php'; } class RestException extends Exception { public function __construct($http_status_code, $error_message = NULL) { parent::__construct($error_message, $http_status_code); } } interface iRespond { public function __formatResponse($result); public function __formatError($status_code, $message); } class DefaultResponse implements iRespond { function __formatResponse($result) { return $result; } function __formatError($statusCode, $message) { return array('error' => array('code' => $statusCode, 'message' => $message)); } } interface iAuthenticate { public function __isAuthenticated(); } interface iFormat { public function getMIMEMap(); public function setMIME($mime); public function getMIME(); public function setExtension($extension); public function getExtension(); public function encode($data, $human_readable=FALSE); public function decode($data); } class UrlEncodedFormat implements iFormat { const MIME = 'application/x-www-form-urlencoded'; const EXTENSION = 'post'; public function getMIMEMap() { return array(UrlEncodedFormat::EXTENSION=>UrlEncodedFormat::MIME); } public function getMIME(){ return UrlEncodedFormat::MIME; } public function getExtension(){ return UrlEncodedFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return http_build_query($data); } public function decode($data){ parse_str($data,$r); return $r; } public function __toString(){ return $this->getExtension(); } } class JsonFormat implements iFormat { const MIME ='application/json'; const EXTENSION = 'json'; public function getMIMEMap() { return array(JsonFormat::EXTENSION=>JsonFormat::MIME); } public function getMIME(){ return JsonFormat::MIME; } public function getExtension(){ return JsonFormat::EXTENSION; } public function setMIME($mime){ } public function setExtension($extension){ } public function encode($data, $human_readable=FALSE){ return $human_readable ? $this->json_format(json_encode(object_to_array($data))) : json_encode(object_to_array($data)); } public function decode($data){ return object_to_array(json_decode($data)); } private function json_format($json) { $tab = " "; $new_json = ""; $indent_level = 0; $in_string = FALSE; $len = strlen($json); for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $indent_level--; $new_json .= "\n".str_repeat($tab, $indent_level).$char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",\n" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c==0){ $in_string = TRUE; }elseif($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } public function __toString(){ return $this->getExtension(); } } class DocParser { private $params=array(); function parse($doc='') { if($doc==''){ return $this->params; } if(preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) return $this->params; $comment = trim($comment[1]); if(preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) return $this->params; $this->parseLines($lines[1]); return $this->params; } private function parseLines($lines) { foreach($lines as $line) { $parsedLine = $this->parseLine($line); if($parsedLine === false && !isset($this->params['description'])) { if(isset($desc)){ $this->params['description'] = implode(PHP_EOL, $desc); } $desc = array(); } elseif($parsedLine !== false) { $desc[] = $parsedLine; } } $desc = implode(' ', $desc); if(!empty($desc))$this->params['long_description'] = $desc; } private function parseLine($line) { $line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { if(strpos($line, ' ')>0){ $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2); }else{ $param = substr($line, 1); $value = ''; } if($this->setParam($param, $value)) return false; } return $line; } private function setParam($param, $value) { if($param == 'param' || $param == 'return') $value = $this->formatParamOrReturn($value); if($param == 'class') list($param, $value) = $this->formatClass($value); if(empty($this->params[$param])) { $this->params[$param] = $value; } else if($param == 'param'){ $arr = array($this->params[$param], $value); $this->params[$param] = $arr; } else { $this->params[$param] = $value + $this->params[$param]; } return true; } private function formatClass($value) { $r = preg_split("[\(|\)]",$value); if(is_array($r)){ $param = $r[0]; parse_str($r[1],$value); foreach ($value as $key => $val) { $val = explode(',', $val); if(count($val)>1)$value[$key]=$val; } }else{ $param='Unknown'; } return array($param, $value); } private function formatParamOrReturn($string) { $pos = strpos($string, ' '); $type = substr($string, 0, $pos); return '(' . $type . ')' . substr($string, $pos+1); } } function parse_doc($php_doc_comment){ $p = new DocParser(); return $p->parse($php_doc_comment); $p = new Parser($php_doc_comment); return $p; $php_doc_comment = preg_replace("/(^[\\s]*\\/\\*\\*) |(^[\\s]\\*\\/) |(^[\\s]*\\*?\\s) |(^[\\s]*) From f8fac79bb50a99416212e57dcb126f8d6d5e7a5d Mon Sep 17 00:00:00 2001 From: Luracast Date: Sun, 30 Oct 2011 23:45:22 +0800 Subject: [PATCH 21/61] improved the examples documentation, corrected the stack overflow support url in the live examples --- examples/_001_helloworld/readme.html | 2 +- examples/_001_helloworld/readme.md | 0 examples/_002_minimal/readme.html | 2 +- examples/_002_minimal/readme.md | 0 examples/_003_multiformat/readme.html | 2 +- examples/_004_error_response/readme.html | 2 +- examples/_005_protected_api/readme.html | 2 +- examples/_006_crud/index.php | 44 +++++++++++++-- examples/_006_crud/readme.html | 66 +++++++++++++++++------ examples/_006_crud/readme.md | 59 +++++++++++++++----- examples/index.html | 33 ++++++++---- examples/resources/restler_flow.png | Bin 0 -> 98875 bytes 12 files changed, 166 insertions(+), 46 deletions(-) mode change 100755 => 100644 examples/_001_helloworld/readme.md mode change 100755 => 100644 examples/_002_minimal/readme.md mode change 100755 => 100644 examples/index.html create mode 100644 examples/resources/restler_flow.png diff --git a/examples/_001_helloworld/readme.html b/examples/_001_helloworld/readme.html index 89c910261..91aea97f0 100755 --- a/examples/_001_helloworld/readme.html +++ b/examples/_001_helloworld/readme.html @@ -35,7 +35,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • diff --git a/examples/_001_helloworld/readme.md b/examples/_001_helloworld/readme.md old mode 100755 new mode 100644 diff --git a/examples/_002_minimal/readme.html b/examples/_002_minimal/readme.html index e54a3f873..835b8d1aa 100755 --- a/examples/_002_minimal/readme.html +++ b/examples/_002_minimal/readme.html @@ -35,7 +35,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • diff --git a/examples/_002_minimal/readme.md b/examples/_002_minimal/readme.md old mode 100755 new mode 100644 diff --git a/examples/_003_multiformat/readme.html b/examples/_003_multiformat/readme.html index c2c7f927e..d75e12251 100755 --- a/examples/_003_multiformat/readme.html +++ b/examples/_003_multiformat/readme.html @@ -35,7 +35,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • diff --git a/examples/_004_error_response/readme.html b/examples/_004_error_response/readme.html index 70a625269..d8394abf1 100755 --- a/examples/_004_error_response/readme.html +++ b/examples/_004_error_response/readme.html @@ -35,7 +35,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • diff --git a/examples/_005_protected_api/readme.html b/examples/_005_protected_api/readme.html index 59b00e71c..347604cd6 100755 --- a/examples/_005_protected_api/readme.html +++ b/examples/_005_protected_api/readme.html @@ -35,7 +35,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • diff --git a/examples/_006_crud/index.php b/examples/_006_crud/index.php index aa2eb85d0..4aef23de3 100755 --- a/examples/_006_crud/index.php +++ b/examples/_006_crud/index.php @@ -4,12 +4,48 @@ Tagline: using POST, GET, PUT and DELETE. Description: Create, Retrive, Update and Delete using HTTP methods POST, GET, PUT and DELETE respectively. - For simplicity and making it work out of the box it is using + +**How the automatic routing is done?** + +Restler uses *get, put, post, and delete* as prefix to map PHP methods to +respective HTTP methods. When they are the only method names they map at +the class level similar to *index* and *default* + + GET/POST/PUT/DELETE class_name + + +GET is the default HTTP method so all public functions without any of +these prefixes will be mapped to GET request. This means functions +*getResult* and *result* will both be mapped to + + GET class_name/result + +Similarly method *postSomething* will be mapped to + + POST class_name/something + +For simplicity and making it work out of the box this example is using a session based fake database, thus depending on a client that - supports PHP Session Cookies. You may use [REST Console](https://chrome.google.com/webstore/detail/faceofpmfclkengnkgkgjkcibdbhemoc#) - an extension for Chrome or [RESTClient](https://addons.mozilla.org/en-US/firefox/addon/restclient/) - a firefox extension. + supports PHP Session Cookies. You may use + [REST Console](https://chrome.google.com/webstore/detail/faceofpmfclkengnkgkgjkcibdbhemoc#) + an extension for Chrome or + [RESTClient](https://addons.mozilla.org/en-US/firefox/addon/restclient/) + a firefox extension. + Alternatively you can use [cURL](http://en.wikipedia.org/wiki/CURL) on the command line. + + curl-X POST http://help.luracast.com/restler/examples/_006_crud/index.php/author -H "Content-Type: application/json" -d '{"name": "Another", "email": "another@email.com"}' + + { + "name": "Another", + "email": "another@email.com", + "id": 5 + } + +But since the session wont be working next request wont reflect the +change done by previous request, anyway you get the idea. +. + Example 1: GET author returns [ diff --git a/examples/_006_crud/readme.html b/examples/_006_crud/readme.html index 4fc3e4968..9c1794a42 100755 --- a/examples/_006_crud/readme.html +++ b/examples/_006_crud/readme.html @@ -35,7 +35,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • @@ -51,12 +51,50 @@

      Examples

      CRUD

      Create, Retrive, Update and Delete using -HTTP methods POST, GET, PUT and DELETE respectively. -For simplicity and making it work out of the box it is using +HTTP methods POST, GET, PUT and DELETE respectively.

      + +

      How the automatic routing is done?

      + +

      Restler uses get, put, post, and delete as prefix to map PHP methods to +respective HTTP methods. When they are the only method names they map at +the class level similar to index and default

      + +
      GET/POST/PUT/DELETE class_name
      +
      + +

      GET is the default HTTP method so all public functions without any of +these prefixes will be mapped to GET request. This means functions +getResult and result will both be mapped to

      + +
      GET class_name/result
      +
      + +

      Similarly method postSomething will be mapped to

      + +
      POST class_name/something
      +
      + +

      For simplicity and making it work out of the box this example is using a session based fake database, thus depending on a client that -supports PHP Session Cookies. You may use REST Console -an extension for Chrome or RESTClient -a firefox extension

      +supports PHP Session Cookies. You may use +REST Console +an extension for Chrome or +RESTClient +a firefox extension.

      + +

      Alternatively you can use cURL on the command line.

      + +
      curl-X POST http://help.luracast.com/restler/examples/_006_crud/index.php/author -H "Content-Type: application/json" -d '{"name": "Another", "email": "another@email.com"}'
      +
      +{
      + "name": "Another",
      + "email": "another@email.com",
      + "id": 5
      +}
      +
      + +

      But since the session wont be working next request wont reflect the +change done by previous request, anyway you get the idea.

      This API Server is made using the following php files/folders

      @@ -71,15 +109,13 @@

      CRUD

      This API Server exposes the following URIs

      -
      GET    author                   ⇠ Author::get()
      -GET    author/:id               ⇠ Author::get()
      -POST   author                   ⇠ Author::post()
      -POST   author/:request_data     ⇠ Author::post()
      -PUT    author                   ⇠ Author::put()
      -PUT    author/:id               ⇠ Author::put()
      -PUT    author/:id/:request_data ⇠ Author::put()
      -DELETE author                   ⇠ Author::delete()
      -DELETE author/:id               ⇠ Author::delete()
      +
      GET    author     ⇠ Author::get()
      +GET    author/:id ⇠ Author::get()
      +POST   author     ⇠ Author::post()
      +PUT    author     ⇠ Author::put()
      +PUT    author/:id ⇠ Author::put()
      +DELETE author     ⇠ Author::delete()
      +DELETE author/:id ⇠ Author::delete()
       

      Try the following links in your browser

      diff --git a/examples/_006_crud/readme.md b/examples/_006_crud/readme.md index 2b9a7a3f6..b54de64e5 100755 --- a/examples/_006_crud/readme.md +++ b/examples/_006_crud/readme.md @@ -3,11 +3,46 @@ CRUD Create, Retrive, Update and Delete using HTTP methods POST, GET, PUT and DELETE respectively. -For simplicity and making it work out of the box it is using + +**How the automatic routing is done?** + +Restler uses *get, put, post, and delete* as prefix to map PHP methods to +respective HTTP methods. When they are the only method names they map at +the class level similar to *index* and *default* + + GET/POST/PUT/DELETE class_name + + +GET is the default HTTP method so all public functions without any of +these prefixes will be mapped to GET request. This means functions +*getResult* and *result* will both be mapped to + + GET class_name/result + +Similarly method *postSomething* will be mapped to + + POST class_name/something + +For simplicity and making it work out of the box this example is using a session based fake database, thus depending on a client that -supports PHP Session Cookies. You may use [REST Console](https://chrome.google.com/webstore/detail/faceofpmfclkengnkgkgjkcibdbhemoc#) -an extension for Chrome or [RESTClient](https://addons.mozilla.org/en-US/firefox/addon/restclient/) -a firefox extension +supports PHP Session Cookies. You may use +[REST Console](https://chrome.google.com/webstore/detail/faceofpmfclkengnkgkgjkcibdbhemoc#) +an extension for Chrome or +[RESTClient](https://addons.mozilla.org/en-US/firefox/addon/restclient/) +a firefox extension. + +Alternatively you can use [cURL](http://en.wikipedia.org/wiki/CURL) on the command line. + + curl-X POST http://help.luracast.com/restler/examples/_006_crud/index.php/author -H "Content-Type: application/json" -d '{"name": "Another", "email": "another@email.com"}' + + { + "name": "Another", + "email": "another@email.com", + "id": 5 + } + +But since the session wont be working next request wont reflect the +change done by previous request, anyway you get the idea. > This API Server is made using the following php files/folders > * index.php (gateway) @@ -17,15 +52,13 @@ a firefox extension This API Server exposes the following URIs - GET author ⇠ Author::get() - GET author/:id ⇠ Author::get() - POST author ⇠ Author::post() - POST author/:request_data ⇠ Author::post() - PUT author ⇠ Author::put() - PUT author/:id ⇠ Author::put() - PUT author/:id/:request_data ⇠ Author::put() - DELETE author ⇠ Author::delete() - DELETE author/:id ⇠ Author::delete() + GET author ⇠ Author::get() + GET author/:id ⇠ Author::get() + POST author ⇠ Author::post() + PUT author ⇠ Author::put() + PUT author/:id ⇠ Author::put() + DELETE author ⇠ Author::delete() + DELETE author/:id ⇠ Author::delete() Try the following links in your browser diff --git a/examples/index.html b/examples/index.html old mode 100755 new mode 100644 index 61cda3716..346317a98 --- a/examples/index.html +++ b/examples/index.html @@ -30,7 +30,7 @@
    • Home
    • Examples
    • -
    • Support
    • +
    • Support
    • GitHub
    • Twitter
    • @@ -42,11 +42,17 @@

      Examples