msgbartop
msgbarbottom

23 七 08 Subversion的属性功能

Subversion的属性是非常好用的功能,它将一些工作自动化,实现为受版本控制的源文件添加元信息的作用。属性是外部不可见的,可以简单认为是附加上在文件上的信息,和文件大小之类的信息是一样的,只不过他是通过subversion来管理的。属性的名称和值可以是你希望的任何值,限制就是名称必须是可读的文本,并且最好的一点是这些属性也是版本化的,就像你的文本文件内容,你可以像提交文本修改一样修改、提交和恢复属性修改,当你更新时也会接收到别人的属性修改—你不必为适应属性改变你的工作流程。
Subversion保留了一组名称以svn:开头的属性,来预定义一些有用的功能。比如你常会看到一些人的源代码底部有像下面之类标识的文字:
引用
$Id: main_window.py 68 2008-06-30 02:05:05Z Len $

这就使用了Subversion 中的 svn:keywords的自动属性,它让将发生在源代码中的一些属性的变化自动地更新到源代码中。这行字的意思是表示,main_windows.py 这个源代码文件最后被用户 len 更新于 2008-6-30 02:05:05Z,修订版本号为 68。要实现这样的自动更新,你只要对需要这样属性的文件上使用下面这行指令。
> svn propset svn:keywords "Id" main_window.py

或者使用TortoiseSVN中的Properties的操作按钮,方便地增加新的属性。接着需要在源代码文件中需要 Subversion 进行自动更新的地方插入 $Id$ 这样的 Keyword,那么在你下次进行提交更新时,该$Id$ 就会被 Subversion 自动替换为$Id: main_window.py 68 2008-06-30 02:05:05Z Len $ 这样的格式。
Subversion 中可以使用的Keyword 包括下面这些:
引用
Id
上面介绍过的综合的格式
LastChangedDate
最后被修改的时间,缩写为 Date。
LastChangedBy
最后修改该源代码文件的用户名,缩写为 Author。
LastChangedRevision
最后修订的版本号,缩写为 Revision。

如果想每次向Subversion服务器提交文件修改时,都要设置文件的属性,则需要进行Subversion配置的修改。配置文件在你用户的主目录下,在Windows下应类似于C:\Documents and Settings\Len\Application Data\Subversion\config文件,Len是Windows用户名,注意Application Data是隐藏文件夹,需要显示全部文件才能看到。接着如下相应的修改,对你想要处理的文件做配置。
enable-auto-props = yes  
[auto-props]  
*.c = svn:keywords=Id  
*.py = svn:keywords=Id

对于开源项目,常见其源文件头部有着版权声明的文本,这些操作大多也是通Subversion的属性功能来完成的,有关更详细的介绍和操作指南,可参见Subversion中文手册中的属性章节。
Tags - , ,

23 七 08 Subversion的属性功能

Subversion的属性是非常好用的功能,它将一些工作自动化,实现为受版本控制的源文件添加元信息的作用。属性是外部不可见的,可以简单认为是附加上在文件上的信息,和文件大小之类的信息是一样的,只不过他是通过subversion来管理的。属性的名称和值可以是你希望的任何值,限制就是名称必须是可读的文本,并且最好的一点是这些属性也是版本化的,就像你的文本文件内容,你可以像提交文本修改一样修改、提交和恢复属性修改,当你更新时也会接收到别人的属性修改—你不必为适应属性改变你的工作流程。
Subversion保留了一组名称以svn:开头的属性,来预定义一些有用的功能。比如你常会看到一些人的源代码底部有像下面之类标识的文字:
引用
$Id: main_window.py 68 2008-06-30 02:05:05Z Len $

这就使用了Subversion 中的 svn:keywords的自动属性,它让将发生在源代码中的一些属性的变化自动地更新到源代码中。这行字的意思是表示,main_windows.py 这个源代码文件最后被用户 len 更新于 2008-6-30 02:05:05Z,修订版本号为 68。要实现这样的自动更新,你只要对需要这样属性的文件上使用下面这行指令。
> svn propset svn:keywords "Id" main_window.py

或者使用TortoiseSVN中的Properties的操作按钮,方便地增加新的属性。接着需要在源代码文件中需要 Subversion 进行自动更新的地方插入 $Id$ 这样的 Keyword,那么在你下次进行提交更新时,该$Id$ 就会被 Subversion 自动替换为$Id: main_window.py 68 2008-06-30 02:05:05Z Len $ 这样的格式。
Subversion 中可以使用的Keyword 包括下面这些:
引用
Id
上面介绍过的综合的格式
LastChangedDate
最后被修改的时间,缩写为 Date。
LastChangedBy
最后修改该源代码文件的用户名,缩写为 Author。
LastChangedRevision
最后修订的版本号,缩写为 Revision。

如果想每次向Subversion服务器提交文件修改时,都要设置文件的属性,则需要进行Subversion配置的修改。配置文件在你用户的主目录下,在Windows下应类似于C:\Documents and Settings\Len\Application Data\Subversion\config文件,Len是Windows用户名,注意Application Data是隐藏文件夹,需要显示全部文件才能看到。接着如下相应的修改,对你想要处理的文件做配置。
enable-auto-props = yes  
[auto-props]  
*.c = svn:keywords=Id  
*.py = svn:keywords=Id

对于开源项目,常见其源文件头部有着版权声明的文本,这些操作大多也是通Subversion的属性功能来完成的,有关更详细的介绍和操作指南,可参见Subversion中文手册中的属性章节。
Tags - , ,

23 七 08 MetaWeblog API中文说明

1、什么是MetaWeblog?
MetaWebBlog API(MWA)是一个Blog程序接口标准,允许外部程序来获取或者设置Blog的文字和熟悉。他建立在XMLRPC接口之上,并且已经有了很多的实现。
2、基本的函数规范
有三个基本的函数规范:
metaWeblog.newPost (blogid, username, password, struct, publish) 返回一个字符串,可能是Blog的ID。
metaWeblog.editPost (postid, username, password, struct, publish) 返回一个Boolean值,代表是否修改成功。
metaWeblog.getPost (postid, username, password) 返回一个Struct。
其中blogid、username、password分别代表Blog的id(注释:如果你有两个Blog,blogid指定你需要编辑的blog)、用户名和密码。
struct的含意:
在newPost和editPost中,struct是一个RSS 2.0规范中里面的定义。的定义如下:
元素 说明 例子
title The title of the item. Venice Film Festival Tries to Quit Sinking
link The URL of the item. http://nytimes.com/2004/12/07FEST.html
description The item synopsis. Some of the most heated chatter at the Venice Film Festival this week was about the way that the arrival of the stars at the Palazzo del Cinema was being staged.
author Email address of the author of the item
category Includes the item in one or more categories
comments URL of a page for comments relating to the item
enclosure Describes a media object that is attached to the item
guid A string that uniquely identifies the item.
pubDate Indicates when the item was published.
source The RSS channel that the item came from.
其中最主要的三个元素是title、link和description。如果Blog工具不支持title和link,description就是目录(Content)。category是一个数组,是这个Post所属的类别。如果类别不存在,服务器端将只处理存在的类别。
3、metaWeblog.newMediaObject
metaWeblog.newMediaObject (blogid, username, password, struct) 返回一个数组
其中blogid、username、password分别代表Blog的id(注释:如果你有两个Blog,blogid指定你需要编辑的blog)、用户名和密码。struct必须包含name, type 和bits三个元素,当然也可以包含其他元素。
name代表数据的名称,type是数据的MIME类型,譬如audio/mpeg 、image/jpeg和video/quicktime。bits是数据的base64编码形式的数据流。
如果调用失败,它显示错误信息。如果调用成功,返回值是一个Struct,里面至少包含一个Url元素,代表数据的HTTP或者FTP Url。
4、metaWeblog.getCategories
metaWeblog.getCategories (blogid, username, password) 返回一个struct。
返回值包含所有的Blog的类别,每一个列别包含description, htmlUrl and rssUrl。
5、metaWeblog.getRecentPosts
metaWeblog.getRecentPosts (blogid, username, password, numberOfPosts) 返回一个结构(struct)的数组(array)。
每一个Struct包含getPost返回值一样的结构。
numberOfPosts是返回的数量。
6、调用验证和错误显示
同Blogger API不同的是,metaWeblog没有APPKEY这个概念,你可以自己设置。
同时建议使用标准的XMLRPC Fault来显示错误。
Tags - , , , , , ,

23 七 08 MetaWeblog API中文说明

1、什么是MetaWeblog?
MetaWebBlog API(MWA)是一个Blog程序接口标准,允许外部程序来获取或者设置Blog的文字和熟悉。他建立在XMLRPC接口之上,并且已经有了很多的实现。
2、基本的函数规范
有三个基本的函数规范:
metaWeblog.newPost (blogid, username, password, struct, publish) 返回一个字符串,可能是Blog的ID。
metaWeblog.editPost (postid, username, password, struct, publish) 返回一个Boolean值,代表是否修改成功。
metaWeblog.getPost (postid, username, password) 返回一个Struct。
其中blogid、username、password分别代表Blog的id(注释:如果你有两个Blog,blogid指定你需要编辑的blog)、用户名和密码。
struct的含意:
在newPost和editPost中,struct是一个RSS 2.0规范中里面的定义。的定义如下:
元素 说明 例子
title The title of the item. Venice Film Festival Tries to Quit Sinking
link The URL of the item. http://nytimes.com/2004/12/07FEST.html
description The item synopsis. Some of the most heated chatter at the Venice Film Festival this week was about the way that the arrival of the stars at the Palazzo del Cinema was being staged.
author Email address of the author of the item
category Includes the item in one or more categories
comments URL of a page for comments relating to the item
enclosure Describes a media object that is attached to the item
guid A string that uniquely identifies the item.
pubDate Indicates when the item was published.
source The RSS channel that the item came from.
其中最主要的三个元素是title、link和description。如果Blog工具不支持title和link,description就是目录(Content)。category是一个数组,是这个Post所属的类别。如果类别不存在,服务器端将只处理存在的类别。
3、metaWeblog.newMediaObject
metaWeblog.newMediaObject (blogid, username, password, struct) 返回一个数组
其中blogid、username、password分别代表Blog的id(注释:如果你有两个Blog,blogid指定你需要编辑的blog)、用户名和密码。struct必须包含name, type 和bits三个元素,当然也可以包含其他元素。
name代表数据的名称,type是数据的MIME类型,譬如audio/mpeg 、image/jpeg和video/quicktime。bits是数据的base64编码形式的数据流。
如果调用失败,它显示错误信息。如果调用成功,返回值是一个Struct,里面至少包含一个Url元素,代表数据的HTTP或者FTP Url。
4、metaWeblog.getCategories
metaWeblog.getCategories (blogid, username, password) 返回一个struct。
返回值包含所有的Blog的类别,每一个列别包含description, htmlUrl and rssUrl。
5、metaWeblog.getRecentPosts
metaWeblog.getRecentPosts (blogid, username, password, numberOfPosts) 返回一个结构(struct)的数组(array)。
每一个Struct包含getPost返回值一样的结构。
numberOfPosts是返回的数量。
6、调用验证和错误显示
同Blogger API不同的是,metaWeblog没有APPKEY这个概念,你可以自己设置。
同时建议使用标准的XMLRPC Fault来显示错误。
Tags - , , , , , ,

20 七 08 PHP模拟POST行为代码总结

GET行为比较简单,POST比较复杂一些。这里提供两种方法供选择:

第一:手写代码
第二:利用HttpClient php类库

第一种方法:代码如下:
<?PHP        
    $flag = 0;    
    //要post的数据    
$argv = array(    
     'var1'=>'abc',    
     'var2'=>'你好吗');    
//构造要post的字符串    
foreach ($argv as $key=>$value) {    
     if ($flag!=0) {    
             $params .= "&";    
             $flag = 1;    
     }    
     $params.= $key."="; $params.= urlencode($value);    
     $flag = 1;    
     }    
     $length = strlen($params);    
         //创建socket连接    
     $fp = fsockopen("127.0.0.1",80,$errno,$errstr,10) or exit($errstr."--->".$errno);    
     //构造post请求的头    
     $header = "POST /mobile/try.php HTTP/1.1";    
     $header .= "Host:127.0.0.1";    
     $header .= "Referer:/mobile/sendpost.php";    
     $header .= "Content-Type: application/x-www-form-urlencoded";    
     $header .= "Content-Length: ".$length."";    
     $header .= "Connection: Close";    
     //添加post的字符串    
     $header .= $params."";    
     //发送post的数据    
     fputs($fp,$header);    
     $inheader = 1;    
     while (!feof($fp)) {    
             $line = fgets($fp,1024); //去除请求包的头只显示页面的返回数据    
             if ($inheader && ($line == "\n" || $line == "")) {    
                 $inheader = 0;    
             }    
             if ($inheader == 0) {    
                 echo $line;    
             }    
     }    
fclose($fp);    
?>

第二种方法是:使用httpclient类,代码如下:
$pageContents = HttpClient::quickPost('http://example.com/someForm', array(    
    'name' => 'Some Name',    
    'email' => 'email@example.com'  
));

使用附件的类库,也可以去官方下载最新的类库,官方地址为:http://scripts.incutio.com/httpclient/index.php
附加一些php httpclient的其他几个用法

静态方法获取网页
$pageContents = HttpClient::quickGet('http://example.com/');

Get方法获取
$client = new HttpClient('example.com');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

带调试的Get方法获取
$client = new HttpClient('example.com');    
$client->setDebug(true);    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

带自动转向的Get方法
$client = new HttpClient('www.amazon.com');    
$client->setDebug(true);    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

检查页面是否存在
$client = new HttpClient('example.com');    
$client->setDebug(true);    
if (!$client->get('/thispagedoesnotexist')) {    
    die('An error occurred: '.$client->getError());    
}    
if ($client->getStatus() == '404') {    
    echo 'Page does not exist!';    
}    
$pageContents = $client->getContent();

伪造客户端
$client = new HttpClient('example.com');    
$client->setDebug(true);    
$client->setUserAgent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.3a) Gecko/20021207');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

登录验证并请求一个网页
$client = new HttpClient('example.com');    
$client->post('/login.php', array(    
    'username' => 'Simon',    
    'password' => 'ducks'  
));    
if (!$client->get('/private.php')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

HTTP授权
$client = new HttpClient('example.com');    
$client->setAuthorization('Username', 'Password');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

输出头信息
$client = new HttpClient('example.com');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
print_r($client->getHeaders());

设置一个域内重定向最多次数
$client = new HttpClient('www.amazon.com');    
$client->setDebug(true);    
$client->setMaxRedirects(3);    
$client->get('/');

Tags - , , , , ,

20 七 08 PHP模拟POST行为代码总结

GET行为比较简单,POST比较复杂一些。这里提供两种方法供选择:

第一:手写代码
第二:利用HttpClient php类库

第一种方法:代码如下:
<?PHP        
    $flag = 0;    
    //要post的数据    
$argv = array(    
     'var1'=>'abc',    
     'var2'=>'你好吗');    
//构造要post的字符串    
foreach ($argv as $key=>$value) {    
     if ($flag!=0) {    
             $params .= "&";    
             $flag = 1;    
     }    
     $params.= $key."="; $params.= urlencode($value);    
     $flag = 1;    
     }    
     $length = strlen($params);    
         //创建socket连接    
     $fp = fsockopen("127.0.0.1",80,$errno,$errstr,10) or exit($errstr."--->".$errno);    
     //构造post请求的头    
     $header = "POST /mobile/try.php HTTP/1.1";    
     $header .= "Host:127.0.0.1";    
     $header .= "Referer:/mobile/sendpost.php";    
     $header .= "Content-Type: application/x-www-form-urlencoded";    
     $header .= "Content-Length: ".$length."";    
     $header .= "Connection: Close";    
     //添加post的字符串    
     $header .= $params."";    
     //发送post的数据    
     fputs($fp,$header);    
     $inheader = 1;    
     while (!feof($fp)) {    
             $line = fgets($fp,1024); //去除请求包的头只显示页面的返回数据    
             if ($inheader && ($line == "\n" || $line == "")) {    
                 $inheader = 0;    
             }    
             if ($inheader == 0) {    
                 echo $line;    
             }    
     }    
fclose($fp);    
?>

第二种方法是:使用httpclient类,代码如下:
$pageContents = HttpClient::quickPost('http://example.com/someForm', array(    
    'name' => 'Some Name',    
    'email' => 'email@example.com'  
));

使用附件的类库,也可以去官方下载最新的类库,官方地址为:http://scripts.incutio.com/httpclient/index.php
附加一些php httpclient的其他几个用法

静态方法获取网页
$pageContents = HttpClient::quickGet('http://example.com/');

Get方法获取
$client = new HttpClient('example.com');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

带调试的Get方法获取
$client = new HttpClient('example.com');    
$client->setDebug(true);    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

带自动转向的Get方法
$client = new HttpClient('www.amazon.com');    
$client->setDebug(true);    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

检查页面是否存在
$client = new HttpClient('example.com');    
$client->setDebug(true);    
if (!$client->get('/thispagedoesnotexist')) {    
    die('An error occurred: '.$client->getError());    
}    
if ($client->getStatus() == '404') {    
    echo 'Page does not exist!';    
}    
$pageContents = $client->getContent();

伪造客户端
$client = new HttpClient('example.com');    
$client->setDebug(true);    
$client->setUserAgent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.3a) Gecko/20021207');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

登录验证并请求一个网页
$client = new HttpClient('example.com');    
$client->post('/login.php', array(    
    'username' => 'Simon',    
    'password' => 'ducks'  
));    
if (!$client->get('/private.php')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

HTTP授权
$client = new HttpClient('example.com');    
$client->setAuthorization('Username', 'Password');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
$pageContents = $client->getContent();

输出头信息
$client = new HttpClient('example.com');    
if (!$client->get('/')) {    
    die('An error occurred: '.$client->getError());    
}    
print_r($client->getHeaders());

设置一个域内重定向最多次数
$client = new HttpClient('www.amazon.com');    
$client->setDebug(true);    
$client->setMaxRedirects(3);    
$client->get('/');

Tags - , , , , ,

18 七 08 5个有趣的浏览器地址栏JavaScript代码

这篇分享几个在地址栏实现的Javascript有趣效果和应用。能在浏览器地址栏实现的效果太多了,字体放大、显示所有图片、显示Cookie等等。
1、编辑网页
在地址栏输入下面的代码按enter,网页上所有元素都能变成可编辑状态,你可以移动、调整元素大小。如果你只是讨厌某个网站想发泄一下,我建议你使用NetDisater。
代码如下:
javascript:document.body.contentEditable='true'; document.designMode='on'; void 0


2、无敌风火轮
在地址栏运行下面的代码可使页面上所有图片元素一个接一个地转圈。
改变代码里的“img”成任何网页上有的字符,可以使这些字符做无敌风火轮运动。
代码如下:
javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.getElementsByTagName("img"); DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=(Math.sin(R*x1+i*x2+x3)*x4+x5)+"px"; DIS.top=(Math.cos(R*y1+i*y2+y3)*y4+y5)+"px"}R++}setInterval('A()',5); void(0);


3、晃来晃去
不但是你,浏览器也不是那么喜欢这个javascript。在地址栏运行这个代码后,浏览器会迅速地晃来晃去。
代码如下:
javascript:function flood(n) {if (self.moveBy) {for (i = 200; i &gt; 0;i--){for (j = n; j &gt; 0; j--) {self.moveBy(1,i); self.moveBy(i,0);self.moveBy(0,-i); self.moveBy(-i,0); } } }}flood(6);{ var inp = "D-X !msagro na dah tsuj resworb rouY"; var outp = ""; for (i = 0; i &lt;= inp.length; i++) {outp =inp.charAt (i) + outp ; } alert(outp) ;}; reverse

如果这个代码无效,请将">"改成">","&It;'改成"<"。

4、计算器
在地址栏输入下面的代码,可以实现简单的四则运算:
javascript: alert(34343+3434-222);

事实上这个代码可以继续简化,比如简化成这样:
javascript: 34343+3434-222


5、防钓鱼验证
某些钓鱼网站提供的URL和网页本身的URL是不一致的,你可以用下面的代码进行验证,当两个URL相差太大的时候,你就要稍加小心了:
javascript:alert("The actual URL is:tt" + location.protocol + "//" + location.hostname + "/" + "nThe address URL is:tt" + location.href + "n" + "nIf the server names do not match, this may be a spoof.");

Tags - , , ,

18 七 08 5个有趣的浏览器地址栏JavaScript代码

这篇分享几个在地址栏实现的Javascript有趣效果和应用。能在浏览器地址栏实现的效果太多了,字体放大、显示所有图片、显示Cookie等等。
1、编辑网页
在地址栏输入下面的代码按enter,网页上所有元素都能变成可编辑状态,你可以移动、调整元素大小。如果你只是讨厌某个网站想发泄一下,我建议你使用NetDisater。
代码如下:
javascript:document.body.contentEditable='true'; document.designMode='on'; void 0


2、无敌风火轮
在地址栏运行下面的代码可使页面上所有图片元素一个接一个地转圈。
改变代码里的“img”成任何网页上有的字符,可以使这些字符做无敌风火轮运动。
代码如下:
javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.getElementsByTagName("img"); DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=(Math.sin(R*x1+i*x2+x3)*x4+x5)+"px"; DIS.top=(Math.cos(R*y1+i*y2+y3)*y4+y5)+"px"}R++}setInterval('A()',5); void(0);


3、晃来晃去
不但是你,浏览器也不是那么喜欢这个javascript。在地址栏运行这个代码后,浏览器会迅速地晃来晃去。
代码如下:
javascript:function flood(n) {if (self.moveBy) {for (i = 200; i &gt; 0;i--){for (j = n; j &gt; 0; j--) {self.moveBy(1,i); self.moveBy(i,0);self.moveBy(0,-i); self.moveBy(-i,0); } } }}flood(6);{ var inp = "D-X !msagro na dah tsuj resworb rouY"; var outp = ""; for (i = 0; i &lt;= inp.length; i++) {outp =inp.charAt (i) + outp ; } alert(outp) ;}; reverse

如果这个代码无效,请将">"改成">","&It;'改成"<"。

4、计算器
在地址栏输入下面的代码,可以实现简单的四则运算:
javascript: alert(34343+3434-222);

事实上这个代码可以继续简化,比如简化成这样:
javascript: 34343+3434-222


5、防钓鱼验证
某些钓鱼网站提供的URL和网页本身的URL是不一致的,你可以用下面的代码进行验证,当两个URL相差太大的时候,你就要稍加小心了:
javascript:alert("The actual URL is:tt" + location.protocol + "//" + location.hostname + "/" + "nThe address URL is:tt" + location.href + "n" + "nIf the server names do not match, this may be a spoof.");

Tags - , , ,