2010年9月28日星期二

DEDE GBK如何转UTF-8

第一步:直接用 dede 后台备份数据库!
第二步:然后用小程序转为 utf-8 编码!
第三步:并把SQL里的 CHARSET=GBK; 替换成 CHARSET=utf8; 
第四步:然后在UTF8版后台还原 !
第五步:完成!
 
备注:为防万一,请备份整站数据!
 
以下是一个网友写的详细步骤如下(方法 一样,他写得比较详细),原文引用如下:

织梦GBK与UTF8版本互转使用到了两个工具 phpmyadmin 和 Dreamweaver(或UltraEdit) ,其他的工具没有试验过,但是原理是一样的,如果你不会使用这两工具,可以用其他的试试
下面说明方法( 这里我假设是GBK转UTF8 )

一、转换数据

1、在phpmyadmin里面,选择“导出”,将“另存为文件”框勾选上,然后选择“执行”,将数据库导出到本地

2、用Dreamweaver(或UltraEdit)打开你刚导出的文件,查找CHARSET=gbk全部替换成CHARSET=utf8 查找gb2312全部替换成utf8

3、保存

4、将你原来的数据全部删除,然后将刚保存的文件用phpmyadmin导入进去,OK,数据库转换好啦

二、转换文件

1、 如果你没有对默认的文件做任何更改,就只要传个全新的UTF8程序上去,然后更改data目录下common.inc.php文件的数据库链接信息即可

2、如果你改过文件,就把你改过的文件用第一大步的第2小点的方法转换成utf8编码,然后传上去就OK

2010年9月27日星期一

CuteFTP、FlashFXP、FileZilla 基本应用比较

说明:随着未来版本的更新,各软件的特性可能会有变化。

本文从应用上简单比较了 CuteFTPFlashFXPFileZilla 这三款最流行的 FTP 客户端工具,对我们选择产品应该有个导向性作用。

易用性

  • CuteFTP 差
  • FlashFXP 好
  • FileZilla 一般

站点加密或启动 FTP 工具时输入密码

  • CuteFTP 支持
  • FlashFXP 支持
  • FileZilla 不支持

队列保存(方便下次启动时继续传送队列)

  • CuteFTP 支持
  • FlashFXP 支持
  • FileZilla 不支持

多文件同时传送

  • CuteFTP 支持
  • FlashFXP 不支持
  • FileZilla 支持

价格

  • CuteFTP $59.99
  • FlashFXP $25
  • FileZilla 免费

总结

CuteFTP 应用起来的确不方便,而且网上还有很多人反应其一个 DLL 删不掉,并且其价格对中国人来说实在太高了,为了一个 FTP 客户端工具竟然要花费两百多元。

FlashFXP 这个工具用起来很方便,唯一不足的就是不支持多文件同时传送,如果我有很多文件要传送,要花费很多的时间,很不划算。

FileZilla 是款免费的软件,缺点有两个:站点密码这些是明文存储在计算机中的;不支持队列保存,如果突然断电,下次继续传时操作要麻烦些。

综上所述,我还是选择了 FileZilla:一是由于我的计算机不是公用的,我认为其站点管理中的站点密码被窃取的风险在我能承受的范围内;二是由于我认为出现断电、当机、应用程序无响应的机率比较低,不足以影响我传送文件;三是 FileZilla 是免费的。

2010年9月26日星期日

DedeCMS常用SQL语句收录

功能型例子

功能说明:清空所有注册会员内容
相关语句:

DELETE FROM `2d30_member` WHERE `2d30_member`.`mid` != 1;
TRUNCATE TABLE `2d30_member_flink`;
TRUNCATE TABLE `2d30_member_person`;
TRUNCATE TABLE `2d30_member_space`;
TRUNCATE TABLE `2d30_member_tj`;
 

数据调用型例子

调用说明:所有发布的文章总数目
相关标签:
{dede:sql sql='select count(*) as cc from 2d30_archives'}
    [field:cc/]
{/dede:sql}
 

功能型SQL语句整理:
功能说明:添加自定义属性
相关语句:
 

insert into `2d30_arcatt`(sortid,att,attname) values(9,'d','评论');
alter table `2d30_archives` modify `flag` set ('c','h','p','f','s','j','a','b','d') default NULL;
 

提供者:cunzhangwang

功能说明:批量为作者和来源赋值
相关语句:
 

UPDATE dede_archives SET writer='要赋的值' WHERE writer='';
UPDATE dede_archives SET source='要赋的值' WHERE source='';

功能说明:删除指定IP的评论
相关语句:
 

DELETE FROM `dede_feedback` WHERE `dede_feedback`.`ip` = '000.000.000.000'
 
000.000.000.000 为垃圾评论发布者的IP
 
功能说明:清空文章中关键字字段
相关语句:
 

update dede_archives set keywords=''


功能说明:批量替换发布时间,入库时间,更新时间
相关语句:
 

第一步。在后台新增一个文章。
得到一个时间,比如2009-01-13 14:13:32,这可以通过管理文章那里看到。
第二步,后台执行SQL语句SELECT * FROM dede_archives order by id DESC limit 1
这样你可以看到你刚才新加加的文章一所有字段值。
观察以下的数据:
pubdate:1231846313
senddate:1231846313
sortrank:1231846313
其中1231846313就是时间数据了。
然后就是替换了。
UPDATE dede_archives SET sortrank = 1231846313;
UPDATE dede_archives SET senddate = 1231846313;
UPDATE dede_archives SET pubdate = 1231846313;

 

功能说明:批量修改栏目为动态或者静态
相关语句:
UPDATE `dede_arctype` SET `isdefault` = '-1' 动态
UPDATE `dede_arctype` SET `isdefault` = '1' 静态

 

功能说明:文章内容批量替换SQL语句
相关语句:
update `2d30_addonarticle` set body=REPLACE(body,'论坛','社区') where body like "%论坛%"
以上SQL语句作用是查找所有文章带有“论坛”的词组,并将论坛替换为“社区” 


--------------------------------------------------------------------------------

数据调用SQL语句整理:
标签说明:常用内容统计代码
相关标签:
・共有文章:** 篇
{dede:sql sql="select count(*) as c from 2d30_archives where channel=1"}・共有文章:[field:c /]篇{/dede:sql}
・共有图集:** 个
{dede:sql sql="select count(*) as c from 2d30_archives where channel=2"}・共有图集:[field:c /]个{/dede:sql}
・共有软件:** 个
{dede:sql sql="select count(*) as c from 2d30_archives where channel=3"}・共有软件:[field:c /]个{/dede:sql}
・共有评论:**条
{dede:sql sql="select count(*) as c from 2d30_feedback"}・共有评论:[field:c /]条{/dede:sql}
・共有会员:**名
{dede:sql sql="select count(mid) as c from 2d30_member "}・共有会员:[field:c /]名{/dede:sql}
・文章阅读:** 人次
{dede:sql sql="select sum(click) as c from 2d30_archives"}文章阅读:[field:c /]人次{/dede:sql}
・今日更新:**篇
{dede:sql sql="SELECT count( * ) AS c FROM 2d30_archives WHERE pubdate > UNIX_TIMESTAMP( CURDATE( ) ) "}今日更新:[field:c /]篇{/dede:sql}
总共留言:{dede:sql sql="select count(*) as cc From 2d30_guestbook"}[field:cc/]{/dede:sql}条
 

 

调用说明:调用Discuz论坛附件带图片的贴子
相关标签:
{dede:sql sql="SELECT` cdb_p_w_uploads`.`aid`, `cdb_p_w_uploads`.`p_w_upload`,`cdb_threads`.`tid`, `cdb_threads`.`fid`, `cdb_threads`.`subject` FROM `cdb_p_w_uploads` LEFT JOIN `cdb_threads` ON `cdb_threads`.`tid`=`cdb_p_w_uploads`.`tid` WHERE `cdb_p_w_uploads`.`readperm`='0' AND `displayorder`>='0' AND `filetype`='p_w_picpath/pjpeg' GROUP BY tid LIMIT 0,2"}
<li><A href="/bbs/viewthread.php?tid=[field:tid /]"><IMG src="/bbs/p_w_uploads/[field:p_w_upload/]"></A></li>
<li><A href="/bbs/viewthread.php?tid=[field:tid /]">[field:subject function="cn_substr('@me',30)" /]</A></li>
{/dede:sql}

 

调用说明:调用UCHOME最新日志
相关标签:
{dede:sql sql="Select subject,viewnum,blogid,uid From uchome_blog order by blogid desc limit 0,8"}
<li><A href="http://www.backbak.cn/space.php?uid=[field:uid/]&do=blog&id=[field:blogid/]">[field:subject function="cn_substr('@me',24)" /]</A></li>
{/dede:sql}

 

调用说明:会员积分排行
相关标签:
{dede:sql sql="Select id,userid,uname,scores From 2d30_member order by scores desc limit 0,10"}
<dd><span class="name"><a href='[field:global name='cfg_cmspath'/]/member/?[field:userid/]/'>[field:uname/]</a>
</span><span class="jifen">积分[field:scores/]</span></dd>
{/dede:sql}

 

调用说明:企业最新产品调用方法(图片+标题)
相关标签:
{dede:sql sql="SELECT a.id,a.litpic,a.title FROM 2d30_addonproduct p left join 2d30_archives a on a.id = p.aid order by a.id desc LIMIT 0 , 4"}
<li><div><a href="/plus/view.php?aid=[field:aid/]" title="[field:title/]"><img src="[field:litpic/]" alt="[field:title/]"/></a></div><a href="/plus/view.php?aid=[field:aid/]" title="[field:title/]">[field:title/]</a></li>
{/dede:sql}

 

调用说明:调用最新加入企业及企业所属行业代码到首页
相关标签:
{dede:sql sql="SELECT m.mid,m.mtype,m.userid,m.matt,c.mid,c.company,c.comface,d.ename,d.evalue,d.egroup FROM dede_sys_enum as d ,dede_member as m left join dede_member_company c on m.mid = c.mid where m.mtype ='企业' and m.matt = 1 and c.vocation=d.evalue and d.egroup='vocation' LIMIT 0 , 10"}
[field:company/]
[field:ename/]
{/dede:sql}

 

调用说明:推荐会员(带用户头像)
相关标签:
{dede:sql sql="SELECT ID,type,userid,uname,matt,spacep_w_picpath
FROM 2d30_member
where matt = 1 and type=0
LIMIT 0 , 10"}
头像:<img src="[field:spacep_w_picpath runphp='yes']
if(!@me)@me = 'http://bbs.dedecms.com/p_w_picpath/post/smile/default/14.gif';
[/field:spacep_w_picpath]" />
用户名:<a href='[field:global name='cfg_cmspath'/]/member/?[field:userid/]/'>[field:uname/]</a>
{/dede:sql}

[field:spacep_w_picpath runphp='yes']
if(!@me)@me = 'http://bbs.dedecms.com/p_w_picpath/post/smile/default/14.gif';
[/field:spacep_w_picpath]
为用户头像图片
[img]http://bbs.dedecms.com/p_w_picpath/post/smile/default/14.gif[/img]
是如果用户头像为空的话要显示的图像 这个大家自己改吧

 

调用说明:推荐企业
相关标签:
{dede:sql sql="SELECT m.ID,m.type,m.userid,m.matt,m.spacep_w_picpath,c.id,c.comname
FROM 2d30_member m left join 2d30_member_cominfo c on m.ID = c.id
where m.type = 1 and m.matt = 1
LIMIT 0 , 10"}
头像:<img src="[field:spacep_w_picpath runphp='yes']
if(!@me)@me = 'http://bbs.dedecms.com/p_w_picpath/post/smile/default/14.gif';
[/field:spacep_w_picpath]" />
用户名:<a href='[field:global name='cfg_cmspath'/]/member/?[field:userid/]/'>[field:comname/]</a>
{/dede:sql}
 

 

调用说明:UCenter Home会员调用(带头像)
相关标签:
{dede:sql sql="SELECT * FROM `uchome_space` WHERE `avatar` =1 LIMIT 0 , 10"}
<a href="你安装UCenter Home的地址/space.php?uid=[field:uid/]">
<img src='你安装UCenter的地址/avatar.php?uid=[field:uid/]&size=[field:small/]' border='0' width='99' height='88' >
<br />
[field:username/]</a>
{/dede:sql}

windows下使用git管理github项目

在项目版本控制管理中,我想大家都熟悉CVS或着SVN了,对于新兴的git大多人还是抱着观望的态度,本文简单介绍如何在 windows下使用git及github仓库管理项目。

第一,下载并安装git

目前windows版本的git有几种实现,但我们选择msysgit发行版,这是目前做得兼容性最好的。下载地址:http://code.google.com/p/msysgit/downloads/list

下载完成后双击安装文件并按照提示完成git的安装。git的安装过程十分简单,安装完毕后会提示你做初步的配置工作,这里我们全部按照默认值即可 (PS:在选择git bash时我选择了类unix提示界面)。

安装完毕后git bash启动界面如下所示:

 

第二,创建github账号



github是一个类似sf的免费项目管理及分享的服务平台,要想使用github提供的服务,你必须先注册成为github注册用户。 github的网址是:

http://github.com

第三,建立本地git仓库



首先,git要求使用者必须提供自己的身份标识,为此我们需要在git bash中执行以下命令:



git config --global user.name 'Mc.Test'


git config --global user.email Test.Mc@gmail.com



其次,选择git仓库目录



我们假设将git仓库目录放在D盘的OPENSource目录下,可以通过在git bash中执行以下命令完成:



cd /d


mkdir OPENSource



注:git bash支持大多linux bash终端命令,你可以自己尝试更多终端操作。



第三,建立项目并初始化git仓库



我们的第一个项目是一个使用Python语言将XML文件转换成Python字典(dict)对象的实现:

Python-XML2Dict

,通过执行以下命令完成此步骤:



mkdir Python-XML2Dict


cd Python-XML2Dict


git init



执行此操作后,git将在Python-XML2Dict目录下创建一个隐藏目录(.git),这个目录就是git用来管理软件版本的仓库。



第四,使用git管理项目



现在我们可以开始使用git管理我们的项目了,我们的项目非常简单,只包含3个文件:README、__init__.py和 encoder.py。我们将三个文件复制到Python-XML2Dict目录并将其纳入git管理:



#cp /e/workspace/python-module/xml2dict/* .


git add README __init__.py encoder.py


git commit -m "这是我们第一次初始化项目"



git add命令可以将参数指定的文件添加到git仓库索引中,如果你一次添加太多文件可以使用:git add . 命令全部添加。



git commit命令才是真正的将文件添加到git仓库中去,-m选项允许在命令行后直接给出每次添加的简短说明(PS:我们强烈推荐你再每次提交时都给出一 个说明,以便项目版本的查阅等),如果没有给出此命令将调用默认文本编辑器以便你添加说明。



第五,将项目提交到github管理



要想将本地的git项目提交到github我们需要先在github添加此项目,登入github主页点击“create a new one"根据提示完成项目创建。刚创建好的项目不包含任何文件,我们需要稍作设置后方可使用。



1,在git bash中执行以下命令创建密钥:



ssh-keygen -C 'Mc.Test@gmail.com' -t rsa



一路按Enter键即可,当然如果你想选择使用密码功能,那么在提示输入密码是选择你自己的密码。过程如下图所示:



 

2,找到刚才生成的密钥(id_rsa.pub)文件并用文本编辑器打开,然后复制里面的内容。接着转到github站点项目编辑(edit),找 到”Deploy keys“选项后点击”add another deploy key“并将刚才复制的内容黏贴保存。



3,测试SSH连接。在git bash中执行以下命令:



SSH -v git@github.com



如果提示你的密钥不正确,那么你需要重新确认上一步的操作是否完整无误。



4,如果上一步测试无错,那么现在就可以将本地的文件提交到github仓库了。在git bash中执行以下命令:



git push origin master



如果一切顺利,恭喜你!~



推荐一篇git使用指南:

http://www.linuxgem.org/2008/8/1/git-tutorial.4889.html

Git 和Github初次使用

1. GIT

相比CVS/SVN,Git 的优势:
- 支持离线开发,离线Repository
- 强大的分支功能,适合多个独立开发者协作
- 速度块

更多的细节参见 http://mgcore.com/viewthread.php?tid=15556

Git使用指南 http://www.linuxgem.org/user_files/linuxgem/Image/git-tutor.pdf

2. GitHub

GitHub是一个托管Git (开源或闭源)项目的网站,闭源收费,最低7$/月起,免费的300G空间。价格表如下:



使用GitHub步骤:
1、申请GitHub帐户 xxx ,创建名为new-project的新Repository

2、安装Git客户端(Linux)
#yum install git git-gui

3、 生成密钥对,这样项目可以push到 GitHub上
#ssh-keygen -t rsa -C "xxx@gmail.com"
4、将.ssh/id_rsa.pub拷贝到GitHub网站


5、为了方便,设置ssh不输入口令
# eval `ssh-agent`
# ssh-add
(输入passphrase)

6、测试是否能联通GitHub
#ssh git@github.com
如果配置正确,显示
ERROR: Hi xxx! You've successfully authenticated, but GitHub does not provide shell access
Connection to github.com closed.

7、设置Git全局用户配置
# git config --global user.name "xxx"
# git config --global user.email xxx@gmail.com

8、创建本地新项目工作树
# mkdir new-project
# cd new-project
# git init
# touch README
# git add README
# git commit -m 'first commit'
定义远程服务器别名origin
#  git remote add origin git@github.com:xxx/new-project.git  
本地和远程合并,本地默认分支为master
# git push origin master 

GitHub网站上就可以看见了, http://github.com/xxx/new-project

9. 更新文件
# vi README
自动commit更改文件
# git commit -a    
更新至远程
# git push origin master

10. 创建和合并分支
#git branch 显示当前分支是master
#git branch new-feature  创建分支
# git checkout new-feature 切换到新分支
# vi page_cache.inc.php
# git add page_cache.inc.php
Commit 到本地GIT
# git commit -a -m "added initial version of page cache"
合并到远程服务器
# git push origin new-feature

如果new-feature分支成熟了,觉得有必要合并进master
#git checkout master
#git merge new-feature
#git branch
#git push
则master中也合并了new-feature 的代码

再登录到GitHub可以看见"Switch Branches"下的分支选项:



GitHub还有一个很实用的功能,查看开发进程网络图(Network):

2010年9月25日星期六

Dreamhost空间续费如何享受优惠

1、登陆Dreamhost控制面板后,点左侧的Support,选择Contact Support;

2、选择Sales and Billing进入下一步;

3、选择Account Status下的Upcoming Renewal;

4、在新的对话框里面写上"你的抱怨,你的不满,你的牢骚,最后点明因为使用多年,搬家又麻烦,所以续费是否能多优惠点",放心,他们在这个问题上 很快就会给你回复并给你一个具体的优惠的金额,如果你感觉还是不行那就继续"砍价",至于能砍多少这个我也说不好,等协商好了,他们会直接在你账号里面加 上金额,等续费的时候自然就省钱了!

温馨提示:在提交问题前的几个选项需要注意以下两个问题:

(1)Please select the type of this request: 选择问题类型选择倒数第3个就可以,这里面特别需要注意,如果你的空间出现问题,你非常着急那就直接选最下面那个,回复很快的!

(2)Please select your general expertise in the area of this request:你要求技术员如何回答,一般默认就可以!

附:为照顾英文不好的朋友,写了篇蹩脚的英文例子共享下:

标题:Can you give me discount before my renew?

Hello,

Can you give me discount before my renew? I love your hosting, but i feel the renew price is too higher, because of i do not want to move date to another hosting company, so i wish you can give me discount before my renew, whether can please give me reply, thank you.

2010年9月24日星期五

给Micolog添加代码高亮

想给Micoblog添加代码高亮,选来选去选中了Google的google-code-prettify。准备整合进Micolog,却发现 vvonder修改后的Micolog已经整合了,不过版本比较老。我就顺手更新了一下版本。

  第一步,下载google-code- prettify(http://code.google.com/p/google-code-prettify/)。我下载的是prettify- 21-Jul-2010.zip (http://code.google.com/p/google-code-prettify/downloads/list)。

   第二步,解压下载的压缩包,里面有个名为src的文件夹,src里面有一个prettify.css和N多个.js文件,把src下面所有的文件统统复制 到Micoblog的相应文件夹(micolog\static\prettify)中,并覆盖原来的文件。压缩包内还有一个README-zh- Hans.html文件,详细说明了如何使用prettify。如果使用的是没有带prettify的Micoblog版本,可以参见该文档略做修改即 可,修改的地方都是在文件base.html内。

  第三步,上传到GAE上,就大功告成了。

  最后,给出一个 Python代码高亮的例子。


def hello:
   
print("Across the Great Wall")
   
print("we can reach every corner in the world.")

给Micolog修改favicon.ico

今天想起给Micolog修改favicon.ico。首先当然是Google了。在网上找到的答案基本上都是这样的

"找到micolog\static\images目录里的favicon.ico,用自己喜欢的favicon.ico 替换,然后再Deploy一下就可以了!"

  试了一下,肯定失败了赛~,要不然就没有这篇博客了。于是开始看最终生成的源代码,其中关于 是favicon.ico的那段代码是这个样子的:

<link rel="shortcut icon" href="/favicon.ico" />

   想想不对啊,ico文件明明是放在micolog\static\images里面的,而不是micolog\下面啊。于是替换掉 favicon.ico后,再将base.html(在主题文件夹里面)中,上面那一句修改为:

<link rel="shortcut icon" href="/static/images/favicon.ico" />

   上传到GAE上面。OK,搞定收工!

忘记 wordpress密码的解决方法

忘记wordpress密码解决方法有很多种,推荐以下两种 找回:
1、使用找回密码功能。
2、到phpmyadmin里,找到WP数据库的wp_users表,在这里可以看到默认的用户admin,找到user_pass这个域,把它原来的一 长串数据删掉,写上你的密码,比如123456,这时,你会看到一个函数的下边框,把它选择为MD5,再保存。这样,再通过你的域名/wp-admin 访问到管理入口,用这个密码就可以登陆了。忘记 wordpress密码

其他几种忘记wordpress密码的找回方法

1. Wordpress内置的找加密码方法忘记 wordpress密码

如果你的admin帐户的电子邮件地址是正确的, 那就根据普通的找回密码步骤, 在Wordpress的登录页点击"忘记密码?" 然后输入admin或电子邮箱地址. 接着, 你会收到密码重启邮件, 点击里面的重启链接. 稍后你又会收到一封包含用户名和密码的邮件.

这跟所有的Wordpress用户找回密码的步聚都是一样. 我们下面介绍的另外4种方法, 是指你有可能没有修改admin帐户的电子邮箱, 你也就无法接受密码.

2. 通过执行Mysql语句修改Wordpress密码

使用Phpmyadmin之类的工具, 登录你的数据库管理, 执行如下语句:

"update user set password=password("新密码") where user='用户名';"

即可更新你的Wordpress密码.

3. 通过PHP文件修改Wordpress密码忘记 wordpress密码

新建一个pwd.php文件,并在其中加入:

echo md5('你的密码');忘记wordpress 密码

上传到主机根目录下,执行http://你的域名/pwd.php,然后连到你的数据库执行.

update wp_users set user_pass='执行pwd.php显示的字符串' where user_login='admin';

OK,现在可以用自己设定的密码进入管理员帐户了。忘记 wordpress密码

4. 通过 password-resetter 文件找回密码

下载: password-resetter

使用方法:

  1. 将password-resetter.zip解压;
  2. 上传password-resetter.php到WordPress根目录 (注意: 这不是wordpress插件! )
  3. 运行http://你的域名/password-resetter.php
  4. 在Set admin password:后面输入你要重置的管理员密码!然后点提交查询内容就可以了
  5. 记得找回密码后, 删除服务器上的password-resetter.php (不然会被他人利用)

via Information life

5. 通过修改MD5值找回Wordpress密码

WorePress安装后都是自动给出密码的,我一直是自动登陆,所以一个月后居然把密码忘记了。WP使用的是MD5保存密码,所以密码不可逆。想 到后台 可以使用数据库,可以通过数据库找回密码,登陆phpmyadmin,然后如图,登陆后第一步是选择数据库.一般虚拟主机都带有phpmyadmin.

修改MD5密码为:"5d41402abc4b2a76b9719d911017c592″
然后回到WordPress登陆页面,使用密码"hello" 登陆。忘记wordpress

2010年9月23日星期四

【总结】IE和Firefox的Javascript兼容性总结

  长久以来JavaScript兼容性一直是Web开发者的一个主要问题。在正式规范、事实标准以及各种实现之间的存在的差异让许多开发者日夜煎 熬。为此,主要从以下几方面差异总结IE和Firefox的Javascript兼容性:

  一、函 数和方法差异

  二、样 式访问和设置

  三、DOM 方法及对象引用

  四、事 件处理

  五、其 他差异的兼容处理

 

一、函数和方法差异

1. getYear()方法

【分析说明】先看一下以下代码:

var year= new Date().getYear();
document.write(year);

  在IE中得到的日期是"2010",在Firefox中看到的日期是"110",主要是因为在 Firefox 里面 getYear 返回的是 "当前年份-1900" 的值。

【兼容处理】

  加上对年份的判断,如:

var year= new Date().getYear();
year
= (year<1900?(1900+year):year);
document.write(year);

  也可以通过 getFullYear getUTCFullYear 去调用:

var year = new Date().getFullYear();
document.write(year);

 

2. eval()函数

【分析说明】在IE中,可以使用eval("idName")或getElementById("idName")来取得id为idName的 HTML对象;Firefox下只能使用getElementById("idName")来取得id为idName的HTML对象。

【兼容处理】统一用getElementById("idName")来取得id为idName的HTML对象。

 

3. const声明

【分析说明】在 IE 中不能使用 const 关键字。如:

const constVar = 32;

在IE中这是语法错误。

【兼容处理】不使用 const ,以 var 代替。

 

4. var

【分析说明】请看以下代码:

echo=function(str){
document.write(str);
}
pre>

  这个函数在IE上运行正常,Firefox下却报错了。

【兼容处理】而在echo前加上var就正常了,这个就是我们提到var的目的。

 

5. const 问题

【分析说明】在 IE 中不能使用 const 关键字。如 const constVar = 32; 在IE中这是语法错误。

【解决方法】不使用 const ,以 var 代替。

 

二、样式访问和设置

1. CSS的"float"属性

【分析说明】Javascript访问一个给定CSS 值的最基本句法是:object.style.property,但部分CSS属性跟Javascript中的保留字命名相同, 如"float","for","class"等,不同浏览器写法不同。

在IE中这样写:

document.getElementById("header").style.styleFloat = "left";

在Firefox中这样写:

document.getElementById("header").style.cssFloat = "left";

【兼容处理】在写之前加一个判断,判断浏览器是否是IE:

if(document.all){
  document.getElementById("header").style.styleFloat = "left";
}
else{
  document.getElementById("header").style.cssFloat = "left";
}

 

2. 访问<label>标签中的"for"

【分析说明】和"float"属性一样,同样需要使用不现的句法区分来访问<label>标签中的"for"。

在IE中这样写:

var myObject = document.getElementById("myLabel");
var myAttribute = myObject.getAttribute("htmlFor");

在Firefox中这样写:

var myObject = document.getElementById("myLabel");
var myAttribute = myObject.getAttribute("for");

【兼容处理】解决的方法也是先 判断浏览器类型。

 

3. 访问和设置class属性

【分析说明】同样由于class是Javascript保留字的原因,这两种浏览器使用不同的 JavaScript 方法来获取这个属性。

IE8.0之前的所有IE版本的写法:

var myObject = document.getElementById("header");
var myAttribute = myObject.getAttribute("className");

适用于IE8.0 以及 firefox的写法:

var myObject = document.getElementById("header");
var myAttribute = myObject.getAttribute("class");

  另外,在使用setAttribute()设置Class属性的时候,两种浏览器也存在同样的差异。

  setAttribute("className",value);

  这种写法适用于IE8.0之前的所有IE版本,注意:IE8.0也不支持"className"属性了。

  setAttribute("class",value);适用于IE8.0 以及 firefox。

【兼容处理】

方法一,两种都写上:

var myObject = document.getElementById("header");
myObject.setAttribute(
"class","classValue");
myObject.setAttribute(
"className","classValue");
 //设置header的class为classValue

方法二,IE和FF都支持object.className,所以可以这样写:

var myObject = document.getElementById("header");
myObject.className
="classValue";//设置header的class为classValue

方法三,先判断浏览器类型,再根据浏览器类型采用对应的写法。

 

4. 对象宽高赋值问题

【分析说明】FireFox中类似 obj.style.height = imgObj.height 的语句无效。

【兼容处理】统一使用 obj.style.height = imgObj.height + ‘px’;

 

三、DOM方法及对象引用

1. getElementById

【分析说明】先来看一组代码:

<!-- input对象访问1 -->
<input id="id" type="button"
value
="click me" ōnclick="alert(id.value)"/>

  在Firefox中,按钮没反应,在IE中,就可以,因为对于IE来说,一个HTML 元素的 ID 可以直接在脚本中当作变量名来使用,而Firefox中不可以。

【兼容处理】尽量采用W3C DOM 的写法,访问对象的时候,用document.getElementById("id") 以ID来访问对 象,且一个ID在页面中必须是唯一的,同样在以标签名来访问对象的时候,用document.getElementsByTagName("div") [0] 。该方式得到较多浏览器的支持。

<!-- input对象访问2 -->
<input id="id" type="button" value="click me"
  onclick="alert(document.getElementById('id').value)" />

 

2. 集合类对象访问

【分析说明】IE下,可以使用()或[]获取集合类对象;Firefox下,只能使用[]获取集合类对象。如:

document.write(document.forms("formName").src);
//该写法在IE下能访问到Form对象的scrc属性

 【兼容处理】将document.forms("formName")改为 document.forms["formName"]。统一使用[]获取集合类对象。

 

3. frame的引用

【分析说明】IE可以通过id或者name访问这个frame对应的window对象,而Firefox只可以通过name来访问这个frame对 应的window对象。

  例如如果上述frame标签写在最上层的window里面的htm里面,那么可以这样访问:

IE: window.top.frameId或者window.top.frameName来访问这个window对象;

Firefox:只能这样window.top.frameName来访问这个window对象。

【兼容处理】使用frame的name来访问frame对象,另外,在IE和Firefox中都可以使用 window.document.getElementById(”frameId”)来访问这个frame对象。

 

4. parentElement

【分析说明】IE中支持使用parentElement和parentNode获取父节点。而Firefox只可以使用parentNode。 

【兼容处理】因为firefox与IE都支持DOM,因此统一使用parentNode来访问父节点。

 

5. table操作

【分析说明】IE下table中无论是用innerHTML还是appendChild插入<tr>都没有效果,而其他浏览器却显示正 常。

【兼容处理】解决的方法是,将<tr>加到table的<tbody>元素中,如下面所示:

var row = document.createElement("tr");
var cell = document.createElement("td");
var cell_text = document.createTextNode("插入的内容");
cell.appendChild(cell_text);
row.appendChild(cell);
document.getElementsByTagName(
"tbody")[0].appendChild(row);

 

6. 移除节点removeNode()和removeChild()

【分析说明】appendNode在IE和Firefox下都能正常使用,但是removeNode只能在IE下用。

  removeNode方法的功能是删除一个节点,语法为node.removeNode(false)或者 node.removeNode(true),返回值是被删除的节点。

  removeNode(false)表示仅仅删除指定节点,然后这个节点的原孩子节点提升为原双亲节点的孩子节点。

  removeNode(true)表示删除指定节点及其所有下属节点。被删除的节点成为了孤立节点,不再具有有孩子节点和双亲节点。

【兼容处理】Firefox中节点没有removeNode方法,只能用removeChild方法代替,先回到父节点,在从父节点上移除要移除的 节点。

node.parentNode.removeChild(node); 
 // 为了在ie和firefox下都能正常使用,取上一层的父结点,然后remove。

 

7. childNodes获取的节点

【分析说明】childNodes的下标的含义在IE和Firefox中不同,看一下下面的代码:

<ul id="main">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<input type=button value="click me!" onclick=
"alert(document.getElementById('main').childNodes.length)">

分别用IE和Firefox运行,IE的结果是3,而Firefox则是7。Firefox使用DOM规范,"#text"表示文本(实际是无意义 的空格和换行等)在Firefox里也会被解析成一个节点,在IE里只有有实际意义的文本才会解析成"#text"。

【兼容处理】

方法一,获取子节点时,可以通过node.getElementsByTagName()来回避这个问题。但是 getElementsByTagName对复杂的DOM结构遍历明显不如用childNodes,因为childNodes能更好的处理DOM的层次结 构。

方法二,在实际运用中,Firefox在遍历子节点时,不妨在for循环里加上:

if(childNode.nodeName=="#text") continue;//或者使用nodeType == 1。

这样可以跳过一些文本节点。

延伸阅读

  《IE和FireFox中的childNodes 区别

 

8. Firefox不能对innerText支持

【分析说明】Firefox不支持innerText,它支持textContent来实现innerText,不过textContent没有像 innerText一样考虑元素的display方式,所以不完全与IE兼容。如果不用textContent,字符串里面不包含HTML代码也可以用 innerHTML代替。也可以用js写个方法实现,可参考《为firefox实现innerText属性》一文。

【兼容处理】通过判断浏览器类型来兼容:

if(document.all){
document.getElementById(
'element').innerText = "my text";
}
else{
document.getElementById(
'element').textContent = "my text";
}

 

四、事件处理

  如果在使用javascript的时候涉及到event处理,就需要知道event在不同的浏览器中的差异,主要的JavaScript的事件 模型有三种(参考《Supporting Three Event Models at Once》),它们分别是NN4、IE4+和W3C/Safar。

 

1. window.event

【分析说明】先看一段代码

function et()
{
alert(event);
//IE: [object]
}

  以上代码在IE运行的结果是[object],而在Firefox无法运行。

  因为在IE中event作为window对象的一个属性可以直接使用,但是在Firefox中却使用了W3C的模型,它是通过传参的方法来传播 事件的,也就是说你需要为你的函数提供一个事件响应的接口。

【兼容处理】添加对event判断,根据浏览器的不同来得到正确的event:

function et()
{
evt
=evt?evt:(window.event?window.event:null); 
   //兼容IE和Firefox
alert(evt);
}

 

2. 键盘值的取得

【分析说明】IE和Firefox获取键盘值的方法不同,可以理解,Firefox下的event.which与IE下的 event.keyCode相当。关于彼此不同,可参考《键盘事件中keyCode、which和charCode 的兼容性测试

【兼容处理】

function myKeyPress(evt){
//兼容IE和Firefox获得keyBoardEvent对象
evt
= (evt) ? evt : ((window.event) ? window.event : ""
  //兼容IE和Firefox获得keyBoardEvent对象的键值
var key = evt.keyCode?evt.keyCode:evt.which; 
if(evt.ctrlKey && (key == 13 || key == 10)){ 
       //同时按下了Ctrl和回车键
//do something;
}
}

 

3. 事件源的获取

【分析说明】在使用事件委托的时候,通过事件源获取来判断事件到底来自哪个元素,但是,在IE下,event对象有srcElement属性,但是 没有target属性;Firefox下,even对象有target属性,但是没有srcElement属性。 

【兼容处理】

ele=function(evt){ //捕获当前事件作用的对象
evt=evt||window.event;
  return
   (obj=event.srcElement?event.srcElement:event.target;);
}

 

4. 事件监听

【分析说明】在事件监听处理方面,IE提供了attachEvent和detachEvent两个接口,而Firefox提供的是 addEventListener和removeEventListener。

【兼容处理】最简单的兼容性处理就是封装这两套接口:

function addEvent(elem, eventName, handler) {
  
if (elem.attachEvent) {
    elem.attachEvent(
"on" + eventName, function(){
                    handler.call(elem)});
     //此处使用回调函数call(),让this指向elem
  } else if (elem.addEventListener) {
    elem.addEventListener(eventName, handler,
false);
  }
}
function removeEvent(elem, eventName, handler) {
  
if (elem.detachEvent) {
    elem.detachEvent(
"on" + eventName, function(){
                    handler.call(elem)});
     //此处使用回调函数call(),让this指向elem
  } else if (elem.removeEventListener) {
    elem.removeEventListener(eventName, handler,
false);
  }
}

  需要特别注意,Firefox下,事件处理函数中的this指向被监听元素本身,而在IE下则不然,可使用回调函数call,让当前上下文指向 监听的元素。

 

5. 鼠标位置

【分析说明】IE下,even对象有x,y属性,但是没有pageX,pageY属性;Firefox下,even对象有pageX,pageY属 性,但是没有x,y属性。 

【兼容处理】使用mX(mX = event.x ? event.x : event.pageX;)来代替IE下的event.x或者Firefox下的event.pageX。复杂点还要考虑绝对位置。

function getAbsPoint(e){
var x = e.offsetLeft, y = e.offsetTop;
while (e = e.offsetParent) {
x
+= e.offsetLeft;
y
+= e.offsetTop;
}
alert(
"x:" + x + "," + "y:" + y);
}

 

五、其他差异的兼容处理

1. XMLHttpRequest

【分析说明】new ActiveXObject("Microsoft.XMLHTTP");只在IE中起作用,Firefox不支持,但支持XMLHttpRequest。

【兼容处理】

function createXHR() {
var xhr=null;
if(window.XMLHttpRequest){
xhr
=new ActiveXObject("Msxml2.XMLHTTP");
}
else{
try {
xhr
=new ActiveXObject("Microsoft.XMLHTTP");
}
catch() {
xhr
=null;
}
}
if(!xhr)return;
return xhr;
}

 

2. 模态和非模态窗口

【分析说明】IE中可以通过showModalDialog和showModelessDialog打开模态和非模态窗口,但是Firefox不支 持。 

【解决办法】直接使用window.open(pageURL,name,parameters)方式打开新窗口。 如果需要传递参数,可以使用 frame或者iframe。

 

3. input.type属性问题

IE下 input.type属性为只读,但是Firefox下可以修改

 

4. 对select元素的option操作

设置options,IE和Firefox写法不同:

Firefox:可直接设置

option.text = 'foooooooo';

IE:只能设置

option.innerHTML = 'fooooooo';

删除一个select的option的方法:

Firefox:可以

select.options.remove(selectedIndex);

IE7:可以用

select.options[i] = null;

IE6:需要写

select.options[i].outerHTML = null;

 

5. img对象alt和title的解析

【分析说明】img对象有alt和title两个属性,区别在于,alt:当照片不存在或者load错误时的提示。

title:照片的tip说明, 在IE中如果没有定义title,alt也可以作为img的tip使用,但是在Firefox中,两者完全按照标 准中的定义使用 

在定义img对象时。

【兼容处理】最好将alt和title对象都写全,保 证在各种浏览器中都能正常使用 。

 

6. img的src刷新问题

【分析说明】先看一下代码:

<img id="pic" onclick= "this.src= 'a.jpg'"
  src="aa.jpg" style="cursor: pointer"/>

在IE 下,这段代码可以用来刷新图片,但在FireFox下不行。主要是缓存问题。

【兼容处理】在地址后面加个随机数就解决了: 

<img id="pic" onclick= "javascript:this.src=this.src+'?'
     +Math.random()"src="a.jpg" style="cursor: pointer"/>

 

总结

  IE和Firefox的Javascript方面存在着不少的差异,要做到兼容,我觉得很有必要把一些常见的整理成一个js库,如DOM的操 作,事件的处理,XMLHttpRequest请求等,或者也可以选择使用现有的一些库(如jQuery,YUI,ExtJs等),不过我觉得还是有必要 了解一下这些差异,这样对于我们参加兼容性和可用性代码很有帮助。

  办法总比问题多,无论浏览器兼容如何折腾人,做前端开发的总能迎刃而解的!

 

本文地址:http://www.cnblogs.com/wiky/archive/2010/01/09/IE-and-Firefox-Javascript-compatibility.html 

PS:本文由维奇总结,如有转载请注明出处,谢谢!

jQuery 层次选择器

层次选择器:
  层次选择器是通过元素之间的层次关系来获取特定的元素,如后代元素、子元素、相邻元素、和兄弟元素等
  后代元素
   ancestor descendant
   获 取ancestor<祖先>下的descendant<后代>元素<不分级,只要在ancestor里面就行>
   集合元素
   $("div span")选取div里面的所有span元素
  子元素
   parent > child
   获取parent<父>下的child<子>子代元素<分级,只能在parent的 下面是他的子元素>
   集合元素
   $("div > span")选取div下元素名称为span的子元素
  相邻元素
   prev + next
   选取紧 接在prev元素后的next元素
   集合元素
   $(.one + div)选取class为one的下一个div元素
  兄弟元素
   prev~siblings
   选取 prev元素后面的所有siblings元素
   集合元素
   $("#two~div")选取id为two的元素后面的所有div兄 弟元素
 PS:$("prev + next")可以用next()代替
   $("prev ~ siblings")可以用nextAll()代替
   $("prev ~ siblings")和siblings()的区别是siblings()获取所有的兄弟元素,不分先后;
      $("prev ~siblings")要求是指定元素“prev”后面的兄弟元素

2010年9月20日星期一

如何选择国外VPS

国内网络环境日益恶劣,众多站长将站"移民"海外。选择一款优质高速的主机十分重要。由于虚拟主机的主机资源、支持环境等种种的限制,很多站长选择 VPS(Visual Private Server),这篇文章,我们就来一起说说如何选择国外VPS。

  • 本文只讨论Linux VPS,Windows的不在讨论范围内;
  • 本文下载速度与ping延迟都是用北京电信网络测试的。

1. 选择哪国的VPS?

除去像showfom小 朋友这样追求FlagFox那个小旗子癖之外呢,大部分同学追求的不过是有两点,一点是速度,另一点便是稳定。周边向个国家和地区的速度都不错,但是由于 价格过高以及语言上沟壑,买的人并不多。其中日本和香港服务器是购买的比最多的,其它都比较少了。

我国周边速度一般来说是:香港>台 湾>日本>韩国>新加坡>马来西亚,不是绝对的,距离有远近,速度有不同。除去我国周边的国家,速度还不错的,首选的就是美国 了。08年投入使用的TPE光缆,带宽达5T多,使美国的主机不再慢。再除去美国,加拿大的西部的主机也是可以考虑的。

速度说完了,该说下价格,我国及我国周边的VPS都是差不多,就一个字――"贵"。美国的是 全球互联网的中心,主机业务十分发达,机房超多,VPS商更是多如牛毛,价格自然是很便宜了。最便宜的每月$5左右就可以拿下,这也是众多站长"移民"美 国的重要原因。

2. 怎样选择VPS商?

2.1 看口碑

选主机商,首先看口碑(down机频率、 ticket处理是否及时、是否丢失过客户数据等等),这个我不多说,人肉下主机商即可,有个地方可以去看看,那就是WHT(WebHostingTalk), 一个超级热闹的地方,Kangzj就不多说了。

2.2 一ping,二whois,三测试下载

很多VPS商会提供测试 IP,首先ping下,看延迟怎么样。一般说来,美国主机ping都在160ms以上,最最极品160ms多一点的算是极品了。下表简单地说了下,并不精 确:

延迟 (ms)位置(美国)
160-220西岸(以LA为 代表)
220-240中部(以Dallas为代表)
250以上东岸(以WDC为代表)

ping 并不能代表什么,只能说明服务器反应速度,几十毫秒人类根本觉察不到。ping并不是选择服务器的第一标准。香港的ping可以说是所有国外主机当中最好 的,可以在10ms以内(广东),但是香港的国际出口小得可怜,有的时候ping再好,带宽太小,也不能买。

通过IP Whois可以查到IP是哪个机房的,那个机房的速度、稳定性等的评价,在网上评论肯定比那个VPS商要多。通过这种方法还可以找到测试下载,即使VPS 商没有提供测试下载,也能体验下载的速度怎么样。对国内友好的机房汇总:http://www.whcoupon.com/48.html

3. 选何种虚拟技术的VPS?

虚拟技术用得最多的是Xen和OpenVZ。据Rashost讲"基于XEN的 Linux VPS(Para-virtualized VPS, 半虚拟化VPS)的性能要优于其他虚拟化技术",而在一些论坛上也听到过OpenVZ比Xen性能好的讲法,一时分不清谁对谁错。

然而,就 我使用经验来看,Xen性能一般来说要比OpenVZ的好。至于最主要的原因,我想,并不是因为Xen本身的性能有多好,而是Xen不容易超卖(基于 Xen的VPS会像真机器一样用内存Cache磁盘,而OpenVZ的VPS不会)。

还有一点要注意的是,Xen的VPS一般来说可以直接 开pptpd和OpenVPN的VPN,而OpenVZ的VPS只能开OpenVZ的VPN(如果默认没开,需联系客服开通tap/tun和 IPtable)。

Virtuozzo、VMWare是两种收费的虚拟技术,性能上不好评价,价格上多是比前两种贵。还有一种新兴的虚拟技 术叫做KVM,据说VPS之间隔离做得特别好,性能也很不错,不过尚不很成熟。

4. 什么VPS控制面板好用?

这里说的 控制面板可不是主机的控制面板,而是控制VPS的面板,用来重装、重启和进行一些高级设置的面板。在Kangzj看来,面板有就行,VePortal、 SolusVM、Parallel等等或者VPS商自己开发的,功能也就那么几种而已,不会太出奇(Linode的控制面板除外,做得太好了,功能超级超 级强大)。但是话说回来,这面板没有的话,还真是不行,连死机重启都要发ticket,太不方便。

5. 多少内存够用?

这 个很不好说,就以一个PHP网站为例。可以按PV来估算需要内存的大小。一般说来,每天几百IP的网站,128M内存就可以勉强应付。优化设置可以参考张 宴:http://blog.s135.com/post/375/

6. 多少带宽够用?

说实话,只要内存够用,配置的流量一般够用。不要去贪什么不限流量,那都是幻影。万一遇上一个 流量大户(很有可能,因为不限流量最吸引大户的眼光),总是占据带宽,就等着郁闷死吧。一句话,流量不在多,够用就行。至于怎么计算自己的网站大约能消耗 多少流量,可以参考这篇文章:http://kangzj.net/how-many-gigabits-do-i-need/

7. 月付还是年付?

虽然一般来说年付会有优惠,但是仍十分建议月付。为什么,原因有三:

  • 凡是国外主机,IP总有被 封的危险。如果被封,加IP又是一笔费用,如果不能加IP,那这VPS基本上就废了(我用过一家的VPS,就是不允许加或者换IP)。
  • 现 在速度快,一年之中不一定都快。Linode Fremont机房就是活生生的例子,当然Linode可以免费换机房倒还好说。
  • VPS 商携款潜逃也说不定。这样的事情也不是没有先例,虽然是极其少数,但不是没有的,万一人品就到那个份上了……

总之,一句话,这 一年之中可能发生很多你想不到的事,你有可能损失掉这笔钱。一个月一个月的用,感觉不满意了马上换,多舒坦。

8. VPS的CPU限制方式?

虽然最后一个提,但这并不说明这一项不重要,CPU是最容易忽视,但是十分重要的方面。

据我观 察,大约有两种CPU的共享方式,一种是Equal Share,按字面意思,就是大家平分使用(当然也存在可能遇到大户的危险);另一种,限制核数和频率。

限制频率有两种方式,一种是限制单 核,给一个频率(比如500MHz);另一种是给多个核,每个核给一个频率,然后相加(比如,给5个核,每个给100MHz),表面上说起来是一样的。

孰 好孰赖,不是很好比较,大家各自想清楚就O了。

暂时就这么多,谁想到还有什么问题,请留言。

2010年9月19日星期日

High-performance String Concatenation in JavaScript

Concatenation, or joining strings, is an important facility within any programming language. It's especially vital within web applications, since strings are regularly used to generate HTML output. Several languages offer fast string-handling classes such as StringBuilder in .NET and StringBuffer/StringBuilder in Java.

There are a number of ways to concatenate strings in JavaScript:

  1. str = "a" + "b";  
  2. str += "c";  
  3. str = str.concat("d""e");  
 str = "a" + "b"; str += "c"; str = str.concat("d", "e"); 

You can also join an array of strings:

  1. str = ["a""b""c""d""e"].join("");  
 str = ["a", "b", "c", "d", "e"].join(""); 

If you're only joining a few strings, you should use whichever method is most practical. Performance gains or losses will be negligible in all browsers.

Concatenating Many Strings

Consider the following functionally identical examples. The first uses the string concatenation operator:

  1. // standard string append  
  2. var str = "";  
  3. for (var i = 30000; i > 0; i--) {  
  4.     str += "String concatenation. ";  
  5. }  
 // standard string append var str = ""; for (var i = 30000; i > 0; i--) { 	str += "String concatenation. "; } 

The second uses an array join:

  1. // array join  
  2. var str = "", sArr = [];  
  3. for (var i = 30000; i > 0; i--) {  
  4.     sArr[i] = "String concatenation. ";  
  5. }  
  6. str = sArr.join("");  
 // array join var str = "", sArr = []; for (var i = 30000; i > 0; i--) { 	sArr[i] = "String concatenation. "; } str = sArr.join(""); 

Which will execute the fastest?

Some developers will assume the concatenation operator is faster because it uses less code and doesn't require an array that doubles memory requirements. For others, conventional wisdom dictates that array joins are faster—it's more memory efficient within the JavaScript interpreter.

The truth is rather more complex. In all the most recent browsers, either method is fast and will complete within 80ms on a mid-range PC. Here are the results from my own completely unscientific benchmark tests:

  • Chrome 6.0: standard string appends are usually quicker than array joins, but both complete in less than 10ms.
  • Opera 10.6: again, standard appends are quicker, but the difference is marginal—often 15ms compared to 17ms for an array join.
  • Firefox 3.6: the browser normally takes around 30ms for either method. Array joins usually have the edge, but only by a few milliseconds.
  • IE 8.0: a standard append requires 30ms, whereas an array join is more than double—typically 70ms.
  • Safari 5.0.1: bizarrely, a standard append takes no more than 5ms but an array join is more than ten times slower at 55ms.

The latest JavaScript engines are optimized for string concatenation operators. Array joins remain fast, but are without a performance gain.


IE7 is the world's third-most-used browser with a 14% market share. IE6 accounts for another 8%. There's no need to read further if you've dropped support for these aging applications.

Still here? This is the shocker: IE7 and below use a concatenation handler that repeatedly copies strings and causes an exponential increase in time and memory usage. The code using the concatenation operator takes around 2.5 minutes (150,000ms) to execute, and the browser remains unresponsive throughout. By comparison, the array join completes in under 200ms—it's more than 800 times faster.

If you're supporting IE7, array joins remain the best method for concatenating a large number of strings.

PHP开源Apache日志分析工具收集与比较

apache的日志是有专门分析工具的,但是用PHP的就比较少了。或者说我关注的比较少吧,看到这个就记录一下了。可以用于在线分析(但事实上, 我想性能应该不会高到哪里,想来perl版的还是效率高很多)
上原文吧:

我们知道已经有很多像Awtstat这样的使用perl、c或者c++开发的强大的日志分析工具,但是同样也有很多使用PHP开发并且开源的日志分 析软件,今天我就收集了一些与大家分享。

1、LogAnalyzer

LogAnalyzer是 Adiscon 的监控软件产品线中的一部分。可以再Windows以及Unix环境下运行。LogAnalyzer本是是免费的,GPL许可的产品。

LogAnalyzer的原名为phpLogCon,他在2010年的3月29日发布了3.0的稳定版,并且正式改名为LogAnalyzer

程序运行必须有他们制定的数据支持,在Windows环境下, 可以使用MonitorWare AgentWinSysLogEventReport。在 Linux环境下可以使用rsyslog。 现在Yum的源中包含了rsyslog这款软件,源中的版本是3.22.1,官方的最高版本是4.6.4的稳定版以及6.1.0的开发版。

由于不能使用原生的数据进行分析,我觉得算是他的一个缺点。

2、Jawstats

这是一款基于Awstat的PHP开源程序,提供了非常漂亮的分析统计结果的展示界面,支持中文。他的作者是 Jon Come

JAWStats可以减轻AWStats的计算压力,同时安装非常简单,只要稍微修改一下配置文件就可以运行。配置项也可以非常简单。

JAWStats的使用界面也非常人性化,因为作者原来是一个UI工程师。在系统中,我们可以非常容易的在不同月份之间切换,还可以在不同站点的日 志之间进行切换。也可以通过Web界面来进行分析数据的更新,正好切合我们的需求,只在需要看的时候出报表就好了。

JAWStats支持主题,可以进行主题的设计和切换。

总的来说,看完了JAWStats的介绍,觉得是比较推荐的一款产品。

3、Web Analytics. Open Source

一款GPL协议下的开源软件,界面和数据获取方式都是模仿GA的,对于小型站点的分析应该不错。有对于Wordpress和Mediawiki的统 计支持。

4、Log Miner

LogMiner 是一个分析Apache或者IIS日志,或者其他支持 combined 或者 W3C扩展日志格式的服务器。能够提取并展示包括访问量、点击、流量、请求数、访问路径、浏览器和操作系统在内的诸多指标。数据存储采用的是 PostgreSQL,存储比较精简。

Log Miner开发的灵感源自于流行的 Webalizer ,但是有一些主要的不同:

XML/HTML代码
  1. 使用关系型数据库作为后端数据存储,实时生成报表。而 Webalizer 则是生 成 html 文件。基于DBMS数据库的方式能够随时提供不同形式的数据,但是日志文件的解析速度上不如 Webalizer。  
  2. Webalizer只保存最近12个月的数据,之前的数据则无法再查看。  
  3. Webalizer的报告结果是硬编码的,而Logminer则是每个报告对应一个PHP类,我们 可以灵活的进行定义。  
  4. LogMiner提供了比Webalizer更多的报表,比如操作系统和访问路径。  

5、Webalizer

Webalizer虽然不是PHP的,但是上面LogMiner总是拿这个作比较,就顺便说一下。用C写成,日志分析能力非常出众,采用HTML的 报表展示形式,是一个非常流行的日志分析工具。

6、TraceWatch

一个PHP+Mysql的日志分析工具,界面不太喜欢,好像就是路径分析比较有特点,其他的感觉还有点慢,就没什么了。

 

其他还有一些不太知名的工具,就没有一一细看:

AudiStatSlimStatPiwik

结合看到的这些工具,针对我们单位的实际需求,感觉 Awstat + JAWStats 组合更为好一点。因为日志会收集到单独的日志服务器上,所以性能上的消耗也就不会在意了。接下来看看实际的效果吧。

PS:刚才看AWS的文档,看到一个对比,可能对于特性的了解能够更加的一目了然。

 

大小: 937.15 K 尺寸: 139 x 376 浏览: 38 次 点击打开新窗口浏览全图

参考资料:

1、CrunchTools
2、LogAnalyzer
3、Jawstats
4、Awstat
5、Web Analytics . Open Source
6、Log Miner
7、Webalizer
8、TraceWatch

原文来自:http://www.cnblogs.com/cocowool/archive/2010/09/07/1820626.html

JS操作FCKeditor

利用Javascript取和设FCKeditor值也是非常容易的,如下:

// 获取编辑器中HTML内容
function getEditorHTMLContents

(EditorName) {
var oEditor = FCKeditorAPI.GetInstance(EditorName);
return(oEditor.GetXHTML(true));
}

// 获取编辑器中文字内容
function getEditorTextContents

(EditorName) {
var oEditor = FCKeditorAPI.GetInstance(EditorName);
return(oEditor.EditorDocument.body.innerText);
}

// 设置编辑器中内容
function

SetEditorContents(EditorName, ContentStr) {
var oEditor = FCKeditorAPI.GetInstance(EditorName) ;
oEditor.SetHTML(ContentStr) ;
}

FCKeditorAPI是FCKeditor加载后注

册的一个全局对象,利用它我们就可以完成对编辑器的各种操作。

在当前页获得 FCK 编辑器实例:
var Editor = FCKeditorAPI.GetInstance('InstanceName');

从 FCK 编辑器

的弹出窗口中获得 FCK 编辑器实例:
var Editor = window.parent.InnerDialogLoaded().FCK;

从框架页面的子框架中获得其它子框架的 FCK 编辑器实例:
var Editor =

window.FrameName.FCKeditorAPI.GetInstance('InstanceName');

从页面弹出窗口中获得父窗口的 FCK 编辑器实例:
var Editor = opener.FCKeditorAPI.GetInstance

('InstanceName');

获得 FCK 编辑器的内容:
oEditor.GetXHTML(formatted); // formatted 为:true|false,表示是否按HTML格式取出
也可用:
oEditor.GetXHTML();

设置 FCK 编辑器的内容:
oEditor.SetHTML("content", false); // 第二个参数为:true|false,是否以所见即所得方式设置其内容。此方法常用于"设置初始值"或"表单重置"哦作。

插入

内容到 FCK 编辑器:
oEditor.InsertHtml("html"); // "html"为HTML文本

检查 FCK 编辑器内容是否发生变化:
oEditor.IsDirty();

在 FCK 编辑器之外调用 FCK 编辑器工具

条命令:
命令列表如下:
DocProps, Templates, Link, Unlink, Anchor, BulletedList, NumberedList, About, Find, Replace, Image, Flash, SpecialChar, Smiley, Table, TableProp,

TableCellProp, UniversalKey, Style, FontName, FontSize, FontFormat, Source, Preview, Save, NewPage, PageBreak, TextColor, BGColor, PasteText, PasteWord, TableInsertRow,

TableDeleteRows, TableInsertColumn, TableDeleteColumns, TableInsertCell, TableDeleteCells, TableMergeCells, TableSplitCell, TableDelete, Form, Checkbox, Radio, TextField,

Textarea, HiddenField, Button, Select, ImageButton, SpellCheck, FitWindow, Undo, Redo

使用方法如下:
oEditor.Commands.GetCommand('FitWindow').Execute();

=

FCKConfig.BasePath + 'plugins/'
// FCKConfig.Plugins.Add( 'placeholder', 'en,it' ) ;

去掉//后,就相当于把placeholder这个插件功能加上了,fckeditor的插件文件都

在/editor/plugins/文件夹下分类按文件夹放置的,对于fckeditor2.0来说,里面有两个文件夹,也就是有两个官方插 件,placeholder这个文件夹就是我们刚才加上去的,主要用于多参数或单参数自定

义标签的匹配,这个在制作编辑模板时非常管用,要想看具体实例的话,大家可以去下载acms 这个系统查看学习,另一个文件夹tablecommands就是编辑器里的表格编辑用到的了。当然,如果你想制作

自己其它用途的插件,那就只要按照 fckeidtor插件的制作规则制作完放置在/editor/plugins/下就行,然后再在fckeidtor.js里再添加 FCKConfig.Plugins.Add('Plugin Name',',lang,lang');就

可以了。

第二部分 ,如何让编辑器一打开的时候,编辑工具条不出现,等点"展开工具栏"时才出现?Easy,FCKeditor本身提供了这个功能啦,打开 fckconfig.js,找到

FCKConfig.ToolbarStartExpanded = true ;
改成
FCKConfig.ToolbarStartExpanded = false ;
就可以啦!

第三部分,使用自己的表情图标,同样打开fckcofnig.js到最底

部那一段

FCKConfig.SmileyPath = FCKConfig.BasePath + 'images/smiley/msn/' ;
FCKConfig.SmileyImages = ['regular_smile.gif','sad_smile.gif','wink_smile.gif']

;
FCKConfig.SmileyColumns = 8 ;
FCKConfig.SmileyWindowWidth = 320 ;
FCKConfig.SmileyWindowHeight = 240 ;

上面这段已经是我修改过的了,为了我发表此文的版面不会

被撑得太开,我把FCKConfig.SmileyImages那一行改得只有三个表情图了。

第一行,当然是表情图标路径的设置,第二行是相关表情图标文件名的一个List,第三行是指弹出的表情添加窗口最

每行的表情数,下面两个参数是弹出的模态窗口的宽和高喽。

第四部分,文件上传管理部分

此部分可能是大家最为关心的,上一篇文章简单的讲了如何修改来上传文件以及使用

fckeidtor2.0才提供的快速上传功能。再我们继续再深层次的讲解上传功能

FCKConfig.LinkBrowser = true ;
FCKConfig.ImageBrowser = true ;
FCKConfig.FlashBrowser = true ;在

fckconfig.js找到这三句,这三句不是连着的哦,只是我把他们集中到这儿来了,设置为true的意思就是允许使用fckeditor来浏 览服务器端的文件图像以及flash等,这个功能是你插入图片时弹出的窗

口上那个"浏览服务器"按钮可以体现出来,如果你的编辑器只用来自己用或是只在后台管理用,这个功能无疑很好用,因为他让你很直观地对服务器的文件 进行上传操作。但是如果你的系统要面向前

台用户或是像blog这样的系统要用的话,这个安全隐患可就大了哦。于是我们把其一律设置为false;如下

FCKConfig.LinkBrowser = false ;
FCKConfig.ImageBrowser = false

;
FCKConfig.FlashBrowser = false ;

这样一来,我们就只有快速上传可用了啊,好!接下来就来修改,同样以asp为范例进行,进入/editor/filemanager/upload /asp/打开config.asp,修


ConfigUserFilesPath = "/UserFiles/"这个设置是上传文件的总目录,我这里就不动了,你想改自己改了

好,再打开此目录下的upload.asp文件,找到下面这一段

Dim

resourceType
If ( Request.QueryString("Type") <> "" ) Then
resourceType = Request.QueryString("Type")
Else
resourceType = "File"
End If
然后再在其后面添

ConfigUserFilesPath = ConfigUserFilesPath & resourceType &"/"& Year(Date()) &"/"& Month(Date()) &"/"
这样的话,上传的文件就进入"/userfiles/

文件类型(如image或file或flash)/年/月/"这样的文件夹下了,这个设置对单用户来用已经足够了,如果你想给多用户系统用,那就这 样来改

ConfigUserFilesPath = ConfigUserFilesPath

& Session("username") & resourceType &"/"& Year(Date()) &"/"& Month(Date()) &"/"
这样上传的文件就进入"/userfiles/用户目录/文件类型/年/月/"下了,

当然如果你不想这么安排也可以修改成别的,比如说用户目录再深一层等,这里的Session("username")请根据自己的需要进行修改或换 掉。

上传的目录设置完了,但是上传程序还不会自己创建

这些文件夹,如果不存在的话,上传不会成功的,那么我们就得根据上面的上传路径的要求进行递归来生成目录了。

找到这一段

Dim sServerDir
sServerDir = Server.MapPath(

ConfigUserFilesPath )
If ( Right( sServerDir, 1 ) <> "\" ) Then
sServerDir = sServerDir & "\"
End If

把它下面的这两行

Dim oFSO
Set oFSO =

Server.CreateObject( "Scripting.FileSystemObject" )
用下面这一段代码来替换

dim arrPath,strTmpPath,intRow
strTmpPath = ""
arrPath = Split(sServerDir, "\")
Dim

oFSO
Set oFSO = Server.CreateObject( "Scripting.FileSystemObject" )
for intRow = 0 to Ubound(arrPath)
strTmpPath = strTmpPath & arrPath(intRow) & "\"
if

oFSO.folderExists(strTmpPath)=false then
oFSO.CreateFolder(strTmpPath)
end if
next
用这段代码就可以生成你想要的文件夹了,在上传的时候自动生成。

好了,上传文件

的修改到现在可以暂时告一段落了,但是,对于中文用户还存在这么个问题,就是fckeditor的文件上传默认是不改名的,同时还不支持中文文件 名,这样一来是上传的文件会变成".jpg"这样的无法读

的文件,再就是会有重名文件,当然重名这点倒没什么,因为fckeditor会自动改名,会在文件名后加(1)这样来进行标识。但是,我们通常的习 惯是让程序自动生成不重复的文件名

在刚才那

一段代码的下面紧接着就是
' Get the uploaded file name.
sFileName = oUploader.File( "NewFile" ).Name
看清楚了,这个就是文件名啦,我们来把它改掉,当然得有个生成文件名的

函数才行,改成下面这样

'//取得一个不重复的序号
Public Function GetNewID()
dim ranNum
dim dtNow
randomize
dtNow=Now()
ranNum=int(90000*rnd)+10000

GetNewID=year(dtNow) & right("0″ & month(dtNow),2) & right("0″ & day(dtNow),2) & right("0″ & hour(dtNow),2) & right("0″ & minute(dtNow),2)

& right("0″ & second(dtNow),2) & ranNum
End Function

' Get the uploaded file name.
sFileName = GetNewID() &"."& split(oUploader.File( "NewFile"

).Name,".")(1)

这样一来,上传的文件就自动改名生成如20050802122536365.jpg这样的文件名了,是由年月日时分秒以及三位随机数组成的文件名了

FckEditor中文手册

首先,FCKEDITOR的性能是非常好的,用户只需很少的时间就可以载入FCKEDITOR所需文件.对于其他在线编辑器来说,这几乎是个很难解 决的

难题,因为在开启编辑器
时需要装载太多的文件.比如CUTEEDITOR,虽然功能比FCKEDITOR还要强大,可是,它本身也够庞大了,至于FREETEXTBOX等,其易 用性与FCKEDITOR相比,尚有差距,
可以

说,FCKEDITOR是一个别具匠心的在线编辑器,它里面融入了作者高深的面向对象的JAVAscrīpt功力,集易用性与强大的功能与一体.
.与编辑器相关的所有图像,脚本以及调用页
.语言文件
.编辑

器的皮肤文件
.工具样的贴图等
这些将导致在服务器和客户端间产生相当的流量.如果有许多文件被调用,那么即便每个文件很小.也会让用户等得不耐烦.

在2.0版中,开发人员有两种方法

来解决这个问题.
那就是指定装载顺序和脚本压缩

装载顺序
从2.0版开始,编辑器按以下步骤装载资源:
.基本页(就是编辑器所在页)以及装入编辑器的JS脚本
.用来建立编辑器的

脚本
.编辑器的语言和皮肤.
.建立编辑器.
.载入预置的编辑文档内容.
.从现在开始,用户可以阅读和编辑文档了,不过,拖拽支持以及工具栏都是不可用的
.载入编辑器引擎脚本
.

建立工具栏,并且可用
.从现在开始,编辑器的所有功能都已经完整
.载入工具栏图标
脚本压缩

在打包任何新版本时,编辑器的JS脚本将会进行预处理.预处理步骤如下:
.移除所有

代码注释
.移除所有无用的空白字符.
.将脚本合并成几个文件

使用上面的方法,我们可以将脚本文件的大小压缩到原来的50%.
压缩后,原始的代码仍然存在于一个名为_Source的文件

夹中

如何打包?
编辑器已经自带了打包程序,它位于FCKEDITOR的根文件夹中_PACKAGER文件夹中,名为Fckeditor.Packager.exe,将其 复制到FCKEDITOR根文件夹中并运行,即可自
动将JS脚

本打包并压缩
需要注意的是该程序是一个.NET程序,必须安装.NET FRAMEWORK才能使用

想要获取支持?
如果你捐赠15000欧元,你就可以获得1年的免费技术支持(比较贵的说,相当于人民币

15万,不过西欧的费用相当惊人)

如何安装?

1.下载最新版的FCKEDITOR
2.解压缩到你的站点根文件夹中名为FCKEDITOR的文件夹中(名称必须为FCKEDITOR,因为配置文件中已经使用此

名称来标示出FCKEDITOR的位置)
3.现在,编辑器就可以使用了,如果想要查看演示,可以按下面方法访问:

http://<your-site>/FCKeditor/_samples/default.html

注意:你可以将

FCKEDITOR放置到任何文件夹,默认情况下,将其放入到FCKEDITOR文件夹是最为简单的方法.如果你放入的文件夹使用别的名称,请修改 配置文件夹中
编辑器BasePath参数,如下所

示:
oFckeditor.BasePath="/Components/fckeditor/";
另外,FCKEDITOR文件夹中所有以下划线开头的文件夹及文件,都是可选的,可以安全的从你的发布中删除.它们并不是编辑器运行时必需的

如何将FCKEDITOR整合进我的页面?
由于目前的版本提供的FCKEDITOR仅提供了JAVAscrīpt式的整合,因此,这里仅讲述如何应用JAVAscrīpt来整合FCKEDITOR 到站点中,当然,其他各种语言的整合,你

可以
参考_samples文件夹中的例子来完成
1,假如编辑器已经安装在你的站点的/FCKEDITOR/文件夹下.那么,第一步我们需要做的就是在页面的HEAD段中放入scrīpt标记以引入 JAVAscrīpt整合模块

.例如:
<scrīpt type="text/javascrīpt" src="/fckeditor/fckeditor.js"></scrīpt>
其中路径是可更改的
2,现在,FCKEDITOR类已经可以使用了.有两个方法在页面中建立一个

FCKEDITOR编辑器:
方法1:内联方式(建议使用):在页面的FORM标记内需要插入编辑器的地方置入以下代码:
scrīpt type="text/javascrīpt">
var ōFCKeditor = new FCKeditor(

'FCKeditor1′ ) ;
oFCKeditor.Create() ;
</scrīpt>
方法2:TEXTAREA标记替换法(不建议使用):在页面的ONLOAD事件中,添加以下代码以替换一个已经存在的TEXTAREA标记

<html>
<head>
<scrīpt type="text/javascrīpt">
window.onload = function()
{
var ōFCKeditor = new FCKeditor( 'MyTextarea' )

;
oFCKeditor.ReplaceTextarea() ;
}
</scrīpt>
</head>
<body>
<textarea id="MyTextarea" name="MyTextarea">This is <b>the</b>

initial value.</textarea>
</body>
</html>
3.现在,编辑器可以使用了
FCKEDITOR类参考:
下面是用来在页面中建立编辑器的FCKEDITOR类的说明
构造

器:
FCKeditor( instanceName[, width, height, toolbarSet, value] )
instanceName:编辑器的唯一名称(相当于ID)
WIDTH:宽度
HEIGHT:高度
toolbarSet:工具条集合的名称

value:编辑器初始化内容
属性:
instanceName:编辑器实例名
width:宽度,默认值为100%
height:高度,默认值是200
ToolbarSet:工具集名称,参考FCKCONFIG.JS,默认值是

Default
value:初始化编辑器的HTML代码,默认值为空
BasePath:编辑器的基路径,默认为/Fckeditor/文件夹,注意,尽量不要使用相对路径.最好能用相对于站点根路径的表示方法,要以/结尾

CheckBrowser:是否在显示编辑器前检查浏览器兼容性,默认为true
DisplayErrors:是否显示提示错误,默为true;
集合:
Config[Key]=value;
这个集合用于更改配置中某一项的值,如

oFckeditor.Config["DefaultLanguage"]="pt-br";
方法:
Create()
建立并输出编辑器
RepaceTextArea(TextAreaName)
用编辑器来替换对应的文本框
如何配置FCKEDITOR?

FCKEDITOR提供了一套用于定制其外观,特性及行为的设置集.主配置文件名为Fckconfig.js
你既可以编辑主配置文件,也可以自己定义单独的配置文件.配置文件使用JAVAscrīpt语法.
修改后,

在建立编辑器时,可以使用以下语法:
var ōFCKeditor = new FCKeditor( 'FCKeditor1′ ) ;
oFCKeditor.Config['CustomConfigurationsPath'] = '/myconfig.js'

;
oFCKeditor.Create() ;
提醒:当你修改配置后,请清空浏览器缓存以查看效果
配置选项:
AutoDetectLanguage=true/false 自动检测语言
Basehref="" _fcksavedurl="""" 相对链接

的基地址
ContentLangDirection="ltr/rtl" 默认文字方向
ContextMenu=字符串数组,右键菜单的内容
CustomConfigurationsPath="" 自定义配置文件路径和名称
Debug=true/false 是否

开启调试功能,这样,当调用FCKDebug.Output()时,会在调试窗中输出内容
DefaultLanguage="" 缺省语言
EditorAreaCss="" 编辑区的样式表文件
EnableSourceXHTML=true/false 为TRUE时,

当由可视化界面切换到代码页时,把HTML处理成XHTML
EnableXHTML=true/false 是否允许使用XHTML取代HTML
FillEmptyBlocks=true/false 使用这个功能,可以将空的块级元素用空格来替代

FontColors="" 设置显示颜色拾取器时文字颜色列表
FontFormats="" 设置显示在文字格式列表中的命名
FontNames="" 字体列表中的字体名
FontSizes="" 字体大小中的字号列表

我以为比较规范的Drupal主题开发流程

一、工作角色:

前端设计者、主题开发者、美工、产品经理、项目经理、功能开发者

注:有些名字是我自己起的... 并且,尽管有这么多角色分工,但是一般情况下,这些角色会有一个人同时兼任多种角色。传说Drupal适合一个人做,所言不虚...

二、Drupal主题开发流程:

1.前端框架(包括哪几个页面需要设计,每个页面都包含哪些版块内容等)。

负责人:前端设计者(如无意外,应该是主题开发者)。

参与角色:前端设计者,美工,产品经理。

工作细节:前端设计者根据产品经理所提供的项目需求(来源于客户),做出设计框架,交由产品经理过目,通过后,再和美工磨合意见,这样就敲定设计框 架了。

2.PSD设计

负责人:美工。

参与角色:美工。

工作细节:美工根据设计框架,设计出PSD文件,将稿件预览文件先交由前端设计过目,通过后,再由前端设计交由产品经理过目,通过后,产品经理交由 客户过目,最后敲定设计稿。

3.主题架构

负责人:项目经理。

参与角色:主题开发者,项目经理。

工作细节:项目经理根据设计稿和项目需求,给出各内容版块的实现方案(比如某个版块是用区块形式还是直接函数输出),再和主题开发者磨合意见。然 后,主题设计者根据设计稿和内容的实现方案来规划主题的整体架构,包括HTML和CSS的架构,还有部分需要切图的版块要给出HTML代码。

4.项目内容开发

负责人:项目经理。

参与角色:功能开发者,项目经理。

工作细节:在内容开发期间,要使用主题开发者指定的主题。内容开发就是准备好设计稿上需要显示的东西,比如自定义的区块、函数、页面,或者 views生成的区块、页面等。

5.主题开发

负责人:主题开发者。

参与角色:主题开发者。

工作细节:等到网站需要的内容性版块准备好了,主题开发者就可以开始正式开发主题了。有了一个通行性、扩展性都足够强的基础主题,加上主题开发者有 足够的前端设计能力和丰富的主题开发经验,快速开发高质量主题不是问题。

附件里奉上我开发用的基础主题,仅供大家参考,这个基本主题还没有成熟,因为我还不确定它是否需要修改,需要多做几个项目,实践证明,有可能永远不 会停止修改,但最重要的是,你完全掌握了它,你就不怕修改。等用它做到10个以上的至少中型项目之后,我才会以版本来发布,并给出详细的使用文档,还望各 位朋友多给与批评建议,谢谢支持!