高原反应
了。在出发前调整好身体状态、避免感冒,也可以适当的吃一些预防高反的药。我是从成都出发坐火车走青藏线到拉萨,两天发一趟。
第二天中午到达西宁站,换车。
新车上的床头有氧气口。
从西宁站出发没多久,就会经过青海湖。
天上的云一朵一朵的很矮,伸手就能摸到。
餐车上的川味盒饭,20一份。
到达格尔木。
过了格尔木再经过可可西里、唐古拉山口,就正式进入西藏地接。可惜我这趟车在过可可西里和唐古拉山的时候是晚上,啥也看不见了。听说从兰州出发的那趟列车是早上经过可可西里,运气好的话还能看见藏羚羊。
翻过唐古拉山就是另一番天地。
第三天中午到达拉萨。拉萨城市的规模大致相当于四川的一个普通地级市。
火车站的出租车要凑满了人才会走。我和另外三个藏人一车,一路上其他人全用藏语交流,剩下我一脸蒙蔽。
酒店楼下的煎饼果子,来一套!!!
酒店的话建议找离市中心近的。因为从拉萨出发的周边线路,基本都是在布达拉宫集合。
到了酒店就开始有点高反
了,喝了一杯葡萄糖,直接躺到下午6点。醒来之后感觉好了许多,然后收拾东西出门觅食。
找了一家牛肉汤锅店,点了最小份,因为服务员说素菜可以免费加。可能是这里的人对“小份”这个词有什么误解,最后我连锅里的牛肉都没吃完(:з」∠)
酒足饭饱后就原路往回溜达,期间路过一家火锅店,放着一首用藏语唱的《演员》,有一种腾格尔唱《菊花台》的感觉,可惜没有录下来。
来拉萨之前还有点担心这里的治安问题,来了之后发现这里绝对是中国最安全的地方之一。每一个路口都有民警执勤点,靠近市中心还有武警把守。特别是布达拉宫,安保级别跟天安门差不多了。
刚到高原最难熬的就是第一个晚上,伴随着气短和头晕基本一夜没睡。
建议第一晚不要洗澡,西藏早晚温差大,容易感冒;同时不要饮酒。
第四天,因为晚上没睡好,直接睡到11点。起来收拾东西准备去大昭寺。
因为酒店里八廓街很近,出门直接步行1km就到了。
八廓街算是拉萨的一个特色步行街,里面有很多卖藏区特产的,其中冬虫夏草
最多。
大昭寺位于八廓街的中心,是拉萨仅此于布达拉宫的大寺庙,在这里能看到很多来朝圣的人群。
大昭寺宫殿
中午在八廓街寻了一家小店,一个盖饭 + 一壶奶茶
吃完饭出来瞎逛,走到了衙门口
路过天桥来一张
第五天出发去羊卓雍戳,在网上报的一日游团。
一辆GL8一车10人左右,从布达拉宫出发,大部分时间都花在路上,一小段高速,后面全是盘山公路。
2元/人的厕所
山顶远眺羊卓雍戳
从山顶下来天气变阴了
返回布达拉宫已经快夕阳西下了,
第六天出发布达拉宫,依旧是在网上报的一日游团包导游。布达拉宫内部不允许拍照,所以想看的只能自己去,不过里面基本也都是佛像与活佛金身
去珠峰大本营有多种路线可以选择,大部分是环线包车。
我在网上报的珠峰大本营专线游:第一天早上坐火车到日喀则,再包车直达珠峰大本营,晚上到大本营。
珠峰大本营海拔5200米,有简易的帐篷旅馆和绒布寺招待所。前往珠峰大本营需要边防证,需要提前办理。
坐火车出发前往日喀则
中午到达日喀则,吃过午饭坐上包车出发,途中经过拉孜
经过嘉措拉山口,正式进入保护区
上面的字已经看不清了
国家公园门口,进去需要单独购买门票
进入国家公园后一路沿着山路向上行驶
来到加乌拉山口。这里是远眺珠峰的最佳位置,远处最高的山峰就是珠峰了
因为加乌拉山口耽误了一些时间,到达珠峰大本营已经晚上9点了,没能看到夕阳下珠峰的”日照金山”
大本营的帐篷旅馆
围着一个火炉的大通铺,男女混住
大本营上没啥能吃的,幸好带了一盒泡面
晚上的大本营外面一片漆黑,是真的伸手不见五指的那种黑,所以大家都跑到外面拍星空。
由于第一次拍星空经验不足,导致前面拍了很多照片不是太暗就是噪点太多,最后拍到凌晨1点过实在刚不住了
![](/assets/images/tibet/2018-06-07 21_22_08.gif)
在5000米的地方睡觉基本睡不着,翻个身都要喘两口大气。
一晚上迷迷糊糊的熬到了天亮,赶紧起床又去拍照
![](/assets/images/tibet/2018-06-07 21_52_52.gif)
一早吃过早饭就开始返程了,返回日喀则再坐火车回到拉萨,第二天坐飞机回成都。
下次再来打算自驾了,从成都一路开到拉萨。只是可能没有那么长的假期了,看看退休之后能不能再来一次吧。
]]>活着就有希望
一开始我并不理解,为何一个18岁的少年会一边写着代码一边语重心长地说出这样的话。
在我有限的记忆里,每当他说出这句话,我总能从他小得快看不见的眼睛中察觉到一丝睿智,但微扬的嘴角又让我觉得这一切只是他对生活的嘲讽。
正如《活着》中福贵说过
做人不能忘记四条,话不要说错,床不要睡错,门槛不要踏错,口袋不要摸错。
书中的福贵,年轻时便睡错了床、踏错了门槛、说错了话、摸错了口袋。祖辈的基业被他从牛输成了羊,羊输成了鹅,鹅输成了鸡,到最后连鸡也没有了。
就是这样一个“败家子”,经历了上个世纪中华大地最动荡的几十年,身边的亲人一个个离他而去,而他依然活着。
人们常说上过真正战场的人,才能真正理解生命的意义。
春生,和福贵一起从死人堆里爬出来的战友,解放后当上了县长,文革中被批斗成“走资派”。那天春生来和福贵告别,福贵不停开导春生要他活下去,春生也答应了福贵。
最后春生还是自杀了。我想不通春生为什么会自杀,早已经历过生死的人为什么不能忍受一时之辱。后来我突然明白了,在战场上只要你活着就有希望,但是在那个时代,活着看不到希望。
是什么让福贵一直坚持下去的?找了很久,好像并没有什么是福贵身上特有的。唯一特有的是他悲惨的一生。每当悲剧来临,福贵就这样默默承受着,作为一个普通人,他除了接受命运的安排和自我安慰之外,还能干什么。这不就是一个真实的普通人吗?真实的有点可怕,你明知这一切只是小说虚构出来的,但你却真实地感受到他的存在。真实到你会认为这一切有一天会发生在你的头上。
书中最后,福贵和他的老牛(也叫福贵)伴着夕阳的余晖远去,两个福贵在生命最后的里程中找到了依靠。夕阳落下,大地和黑夜融为一体,两个垂暮的生命走向他们的归宿。
写这篇文章的时候已是深夜,此时此刻我在希望着什么?
需求不再改?代码不再出bug?线上不再报故障?
漆黑的夜中,能看见几颗星,这就是希望吧。
]]>关于Docker-“快速部署”、“隔离”、“镜像”、“容器”这些关键词想必你一定听过。Docker可以将你的基础配置和应用服务隔离开来,打包你的环境配置并实现快速部署。通过“镜像”,我们可以快速的将一个应用部署到多个服务器上,而“容器”则是用来承载这些应用的。
使用Docker能给我们带来哪些好处:
本篇文章主要介绍如何使用docker,创建自己的镜像,运行容器等。具体使用准则参考官方文档。
镜像可以理解为应用的一个快照。里面保存着该应用运行所需要的各个配置、依赖、环境参数等。镜像还有一个非常关键的概念便是可以叠加。镜像使用了一种叫union file system的技术,将不同镜像按照层级叠加起来(可以理解成一种依赖关系)。
docker的容器则可理解为一个基础版的Linux系统。容器会根据镜像中的配置、资源在镜像的上层再添加一个应用运行的读写层。
Docker本身并不支持直接在Mac OS上运行,不过Docker社区提供了一个工具boot2docker(实际是在Mac OS上创建一个虚拟机)。目前官方已将boot2docker整合到了官方工具Docker Toolbox中。
具体安装流程可参考Docker Mac OS 安装
注意:直接在终端是无法运行docker的,需要进入boot2docker中。
docker-machine start default // 让boot2docker启动一个虚拟机作为docker的运行环境 - default为虚拟机名docker-machine ssh default // 进入boot2docker生成的虚拟机
Docker官方提供了一个类似github的Image管理仓库,你可以像使用github一样下载一份别人的Image。
我们下载一个 alexwhen/docker-2048 镜像作为示例
docker pull alexwhen/docker-2048
docker images 列出本地所有可用的Image,包括镜像名、TAG、创建时间和大小等信息。
现在我们可以用 alexwhen/docker-2048 镜像运行一个容器。
docker run -d -p 8080:80 alexwhen/docker-2048
-d 让Container以后台进程运行,**-p** 指定8080端口的请求转发到Container的80端口。
通过 docker ps 检查我们容器是否运行正常。
如何访问我们的应用呢?注意在Mac OS下,我们的docker是运行在boot2docker里的,所以需要链接虚拟机地址才能访问docker中的应用。
退出boot2docker 执行 docker-machine ls
显示boot2docker地址 192.168.99.100 访问 http://192.168.99.100:8080
下面我们可以进入Container,来看看Container内部是如何运作的。
docker run -ti -p 8080:80 alexwhen/docker-2048 /bin/sh
应用代码 cd /usr/share/nginx/html
nginx配置 vi /etc/nginx/nginx.conf
server { listen 80; server_name localhost; location / { root html; index index.html index.htm; }}
当容器以 alexwhen/docker-2048 镜像启动时,会启动一个nginx并监听Container的80端口。
当我们访问 http://192.168.99.100:8080 时,boot2docker会将8080端口的请求转发到Container的80端口,进而访问到应用。
其实上面的例子并不够典型,因为具体的应用代码是保存在镜像中的,但是应用的代码是经常更新的,所以不适合放在镜像中。
一种解决方案应该是将具体的应用代码放在宿主机,然后挂载到容器上运行。
docker run -d -v /usr/data:/home/data -p 8080:80 alexwhen/docker-2048
这会把本地目录 /usr/data 挂载到容器 /home/data 目录。
另一种方案是在容器内生成 数据卷,然后用它来做数据持久化。
生成数据卷
docker run -d -v /usr/data --name mydata -p 8080:80 alexwhen/docker-2048
在另外一个容器挂载刚才生成的数据卷
docker run -d -v --volumes-from mydata --name mydb alexwhen/docker-2048
在云计算和分布式越来越主流的今天,快速、安全、稳定的实现大规模部署成为一个共同关注的问题。各家的解决方案层出不穷,而Docker似乎在各方需求间找到了平衡点,以一种“倚天不出,谁与争锋”的王霸之气大有一统江湖之势,拭目以待吧。
]]>sudo rm /usr/local/mysqlsudo rm -rf /usr/local/mysql*sudo rm -rf /Library/StartupItems/MySQLCOMsudo rm -rf /Library/PreferencePanes/My*vim /etc/hostconfig and removed the line MYSQLCOM=-YES-rm -rf ~/Library/PreferencePanes/My*sudo rm -rf /Library/Receipts/mysql*sudo rm -rf /Library/Receipts/MySQL*sudo rm -rf /var/db/receipts/com.mysql.*
]]>bizfe平台的nginx日志由于一直写在一个文件里,长年累月后导致该日志文件过于臃肿,对日志数据的查找和分析带来很多不便。
cronolog日志分割工具
wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/vps/cronolog-1.6.2.tar.gztar zxvf cronolog-1.6.2.tar.gzcd cronolog-1.6.2./configuremakemake install
cd /home/service/nginx/logsmkfifo /home/service/nginx/logs/bizfe.access.pipe.logmkdir pipe /*用来存放分割后的日志*/
access_log logs/bizfe.access.pipe.log;./nginx -s reload
以下命令将日志以天为单位分割
nohup cat /home/service/nginx/logs/bizfe.access.pipe.log | nohup /usr/sbin/cronolog /home/service/nginx/logs/pipe/bizfe.access.%Y%m%d.log &
]]> crontab -e //编辑任务 -l //显示已有任务 -r //删除所有任务
在mac下可能会遇见无法添加任务的情况
crontab: "/usr/bin/vi" exited with status 1
这是因为环境变量$EDITOR 是 vi
重新设置成 vim 就行了
]]>来看下面一个栗子:
var o = {}; function A(){ try{ o.show(); }catch(err){ } } function B(){ try{ A(); console.log("你看不见我") }catch(err){ console.log(err) } } B();//输出结果:"你看不见我"
上面的栗子说明 try/catch 在捕获到错误后阻止了错误向上传递。你只能手动抛出错误让上层函数捕获。
var o = {}; function A(){ try{ o.show(); }catch(err){ throw err } } function B(){ try{ A(); console.log("你看不见我") }catch(err){ console.log(err) } } B();//输出结果:"[TypeError: Object #<Object> has no method 'show']"
再看看文章开头提到的问题 在promise中的错误为什么在外层函数捕获不到?
###真相只有一个
promise模块核心代码core.js:
... /*直接跳至关键处*/ var cb = state ? deferred.onFulfilled : deferred.onRejected if (cb === null) { (state ? deferred.resolve : deferred.reject)(value) return } var ret /* #A */ try { ret = cb(value) } catch (e) { deferred.reject(e) return } /* #B */ deferred.resolve(ret) ...
注意 #A 与 #B 之间的代码。没错就是它!!!这里 catch 错误后并没有将其抛出,而是传递给了下级 reject 并结束当前函数。
好了,现在菊势基本明了了。问题根源便是 Promise 始终劫持着错误对象没有抛出,导致外层函数捕获不到错误。
那么再往下看 Promise.catch 方法又是如何捕获错误的?
Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); }
原来 Promise.catch 不过是 Promise.then(null,reject) 的缩写版。
其实 Promise 作者是建议在所有 Promise 对象后加上 Promise.done 方法。Promise.catch 方法只是为了与ES6标准规范保持一致而添加的。
'use strict'; var Promise = require('./core.js') var asap = require('asap') module.exports = Promise Promise.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this self.then(null, function (err) { asap(function () { throw err }) }) }
Promise.done 的作用与 Promise.then 基本一致(注意done并没有 return Promise对象,所以 done 只能放在最后),只是在后面加了一层抛出捕获到的错误。
]]>同步单元测试:
var should = require('should');/*断言库*/ function A(x){ return x ? true : false } describe('function A test',function(){ it('when x = 1,return true',function(){ A(1).should.be.true; }); it('when x = 0,return false',function(){ A(0).should.be.false; }); });
异步单元测试:
var should = require('should'); var fs = require('fs'); describe('fs.readFile is ok',function(done){ fs.readFile('/test.txt','utf-8',function(err,data){ should.not.exist(err); done(); }); });
同步与异步测试区别就在于 describe 的回调函数是否有形参 done
Promise 规范的目的是为了让程序员能以同步代码的形式来写异步代码。直观的表现就是,本来无限嵌套的回调函数变成了链式调用。
Promise规范来书写的接口:
var Promise = require('promise'); var fs = require('fs'); /*Promise的实现方式有很多种,这里使用promise库*/ function fileRead(filePath,encode){ return new Promise(function(resole,reject){ fs.readFile(filePath,encode?encode:'utf-8',function(err,data){ if(err){ reject(err); }else{ resolve(data); } }); }); } /*调用*/ fileRead('/test.txt') .then(function(data){ //do something with data ... },function(err){ //log err ... }); /*测试*/ var should = require('should'); describe('fileRead is ok',function(done){ fileRead('/test.txt') .then(function(result){ result.should.be.String; done(); },function(err){ done(err); }) });
跑一下测试。嗯,好像没问题。收拾东西下班!!!
###wait a minute
注意测试代码中这一行代码
result.should.be.String;
这行代码的目的是检测 result 是否是字符串。如果 result 不是字符串,那应该会被测试框架捕获到。
稍微修改下
[1,2,3].should.be.String; /*这里只是为了测试错误是否能被捕获到,所以用[1,2,3]替换了result*/
###纳尼!!!
测试如我所料没有通过,但是报的错误是 timeout 是什么鬼啊!!!
好像一不小心又发现了一个知识点啊。嗯,今天晚上一定要把它解决了。
兴奋的我带着电脑回到家中玩了一个晚上的魔兽。。。
明天周六,我花一天时间肯定能搞定!!!(这话估计也就我自己会信╮(╯▽╰)╭)
就这样,这个问题陪伴着我度过了一个愉快而又充实的3.8妇女节(什么鬼。。。摔)
周一上班的路上突然想到会不会跟 promise 的实现有关。来到公司,遂看了 promise 的core.js代码。
经过一番倒腾,大致理顺了 promise 的核心思路。 promise 是通过内部的一个handler构造函数来控制回调链的传递。并且你所传入的回调函数并非直接由handler控制,而是由两个叫 resolve 和 reject 的方法控制。
所以我猜测错误可能是被 promise 捕获了,但是没有被测试框架捕获。(不要问我为什么,我也不知道)
于是用尽了各种办法,最终还是没能证明上面的猜测(一股弱者的气息。。。)
###google大法好
这是stackoverflow上相同的问题,证实了我的猜测。
被采纳的答案其实也没能说清其中原由。
答案中引用的when.js的doc文档中也是一句话带过 Errors in an asynchronous operation always occur in a different call stack than the the one that initiated the operation
修改测试代码:
var should = require('should'); describe('fileRead is ok',function(done){ fileRead('/test.txt') .then(function(result){ [1,2,3].should.be.String; done(); },done).catch(done); });
顺利捕获断言错误。至于为什么promise阻断了测试框架捕获错误,还有待研究。
]]>我的手机是Nexus 4,几乎所有的Google服务都不能用(那还要亲儿子干嘛!!!),所以自己搞个代理不能再等了。
ShadowSocks有很多中版本,PythonNodejsGoC,我用的是Python版。
wget --no-check-certificate https://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.eggchmod +x setuptools-0.6c11-py2.7.egg ./setuptools-0.6c11-py2.6.egg
wget --no-check-certificate https://pypi.python.org/packages/source/p/pip/pip-1.4.tar.gztar -zxvf ./pip-1.4.tar.gzcd pip-1.4sudo python setup.py install
sudo apt-get install libevent-devsudo apt-get install python-devpip install gevent
sudo apt-get install libssl-devsudo apt-get install swigpip install M2Crypto
pip install shadowsocks
config.json是ShadowSocks Server端的配置文件
vim ~/ShadowSocks/config.json
config.json配置文件格式:
{"server":"my_server_ip",//服务器IP"server_port":8388,//服务器端口"local_port":1080,//本地端口(配置客户端时需要用到)"password":"barfoo!",//密码"timeout":600,//超市时间"method":"aes-256-cfb"//加密方法,推荐"aes-256-cfb"}
cd到config.json所在目录
nohup ssserver > log &
之所以选用ShadowSocks主要是看重其对客户端强大的支持,几乎所有你能想到的系统都用对应的客户端。
客户端的配置几乎于配置文件的内容一样,只需要将配置文件中配置项对应填入即可。
使用之后,感觉比同等环境下的VPN快了不少。用手机到Google play上更新App尤为明显。
PS:用aws的同学注意 config.json 中的 server 要填内网地址
]]>不过想要操作textarea可是一件麻烦事。由于获取选中区域的接口属于BOM,不同浏览器下的接口差别很大。
先来看看IE下的接口 document.selection
clear : Clears the contents of the selection.
createRange : Creates a TextRange object from the current text selection, or a controlRange collection from a control selection.
createRangeCollection : Creates a TextRange object collection from the current selection.
empty : Cancels the current selection, sets the selection type to none, and sets the item property to null.
type : Retrieves the type of selection.
typeDetail : Retrieves the name of the selection type.
值得注意的是,从IE11开始不再支持selection对象.请使用getSelection对象.
再来看看FF提供的document.getSelection
getRangeAt : Returns a range object representing one of the ranges currently selected.
注意,用户用鼠标在同一页面永远都只有一个选择区域,所以一般情况getRangeAt只能接收0这一个参数.但用脚本可以创建多个选择区域(Chrome下通过脚本也只能创建一个选择区域)
collapse(parentNode,offset) : Collapses the current selection to a single point.
压缩选择区域至单个光标(在输入框中)位置
parentNode:光标将要移动到的目标元素
offset:光标在目标元素中所在偏移量.这里的偏移量会有两个值”0”和”1”,分别表示parentNode文本的起始位置和结束位置.
extend(parentNode,offset):Moves the focus of the selection to a specified point.
parentNode:光标将要移动到的目标元素
offset:光标在目标元素中所在偏移量.这里的偏移量会有两种情况:
1.目标元素只有textNode.这时offset的值表示光标在字符串中的位置
2.目标元素含有非textNode子元素.这时offset的值表示光标定位到第offset个元素的末尾.
modify(alert,direction,granularity) : Changes the current selection.
改变选择区域
alert:声明修改类型(move || extend)
direction:移动(合并)的方向.可能的值forward,backward,left,right.
granularity:移动(合并)的单位.可能的值charracter,word,sentence,line,paragraph,lineboundary,sentenceboundary,paragraphboundary,documentboundary.(注意,FF不支持sentence,paragraph,sentenceboundary,paragraphboundary,documentboundary.值得一提的是在被选择内容为中文时,FF不支持”granularity=word“,但在Chrome上能正常使用.)
collapseToStart : Collapses the selection to the start of the first range in the selection.
将选择区域压缩至区域开始的位置
collapseToEnd : Collapses the selection to the end of the last range in the selection.
将选择区域压缩至区域结束的位置
selectAllChildren(parentNode) : Adds all the children of the specified node to the selection.
选择parentNode的所有子节点内容
addRange(range) : A Range object that will be added to the selection.
将指定的区域添加到已选择区域
range:指定区域range可以通过document.createRange创建,也可以通过getRangeAt从已有区域选择
removeRange(range) : Removes a range from the selection.
从选择区域中删除指定的区域
removeAllRanges : Removes all ranges from the selection.
删除所有选择区域
deleteFromDocument : Deletes the selection’s content from the document.
删除选择区域中的文字(只能删除文字,不会删除DOM元素)
selectionLanguageChangae : Modifies the cursor Bidi level after a change in keyboard direction.
在写这篇文章时,这个方法貌似已经不存在了,但官方文档还保留了这个方法.
toString : Returns a string currently being represented by the selection object, i.e. the currently selected text.
输出当前选择区域文本
containNode(aNode,aPartlyContained) : Indicates if a certain node is part of the selection.
检测某些节点,当它属于选择区域时返回true,否则返回false
aNode:待检测的节点
aPartlyContained:检测类型
1.aPartlyContained=true:当aNode的部分或全部属于选择区域的一部分时,则返回true
1.aPartlyContained=false:当aNode整个节点属于选择区域的一部分时,则返回true(aNode等于选择区域时也返回false,也就是说aNode必须小于选择区域才会返回true)
anchorNode : Returns the Node in which the selection begins.
返回选择区域开始位置所在的节点
anchorOffset : Returns a number representing the offset of the selection’s anchor within the anchorNode. If anchorNode is a text node, this is the number of characters within anchorNode preceding the anchor. If anchorNode is an element, this is the number of child nodes of the anchorNode preceding the anchor.
返回选择区域开始的位置.如果选择区域是纯文本,返回选择区域中第一个字符的位置.如果选择区域是元素,返回那些在选择区域前面的同时也属于anchorNode的子节点的个数.
focusNode : Returns the Node in which the selection ends.
返回选择区域结束位置所在的节点
focusOffset : Returns a number representing the offset of the selection’s anchor within the focusNode. If focusNode is a text node, this is the number of characters within focusNode preceding the focus. If focusNode is an element, this is the number of child nodes of the focusNode preceding the focus.
参照anchorOffset
ifCollapsed : Returns a Boolean indicating whether the selection’s start and end points are at the same position.
判断选择区域开始于结束位置是否相同
rangeCount : Returns the number of ranges in the selection.
返回getSelection中的range数
/*向输入框当前位置插入字符*/function InsertString(ele, str){ var r = null,newstart = 0,tb = ele.nodeType == 1 ? ele : docuemnt.body; tb.focus(); if (document.all){ r = document.selection.createRange(); document.selection.empty(); r.text = str; r.collapse(); r.select(); }else{ newstart = tb.selectionStart+str.length; tb.value=tb.value.substr(0,tb.selectionStart)+str+tb.value.substring(tb.selectionEnd); tb.selectionStart = newstart; tb.selectionEnd = newstart; }}/*获取当前选择区域文字*/function GetSelection(ele){ var sel = '',r = null,tb = ele.nodeType == 1 ? ele : docuemnt.body; if (document.all){ r = document.selection.createRange(); document.selection.empty(); sel = r.text; }else{ sel = tb.value.substring(tb.selectionStart, tb.selectionEnd); } return sel;}function ShowSelection(ele){ var sel = GetSelection(ele); alert('选中的文本是:'+sel);}/*获取光标位置*/function getAnchor(ele){ var index = 0,r = null,tb = ele.nodeType == 1 ? ele : document.body; if(document.all){ r = document.selection.createRange(); tb.focus(); r.moveStart('character', -tb.value.length); index = r.text.length; }else{ index = tb.selectionStart } return index}
]]>事件触发器就是用来触发某个元素下的某个事件,IE下fireEvent方法,高级浏览器(chrome,firefox等)有dispatchEvent方法。
一般我们在元素上绑定事件后,是靠用户在这些元素上的鼠标行为来捕获或者触发事件的,或者自带的浏览器行为事件,比如click,mouseover,load等等,有些时候我们需要自定义事件或者在特定的情况下需要触发这些事件。这个时候我们可以使用IE下fireEvent方法,高级浏览器(chrome,firefox等)有dispatchEvent方法。
在IE下:
//document上绑定自定义事件ondataavailabledocument.attachEvent('ondataavailable', function (event) { alert(event.eventType);});var obj=document.getElementById("obj");//obj元素上绑定click事件obj.attachEvent('onclick', function (event) { alert(event.eventType);});//调用document对象的createEventObject方法得到一个event的对象实例。var event = document.createEventObject();event.eventType = 'message';//触发document上绑定的自定义事件ondataavailabledocument.fireEvent('ondataavailable', event);//触发obj元素上绑定click事件document.getElementById("test").onclick = function () { obj.fireEvent('onclick', event);};
再看看高级浏览器(chrome,firefox等):
//document上绑定自定义事件ondataavailabledocument.addEventListener('ondataavailable', function (event) { alert(event.eventType);}, false);var obj = document.getElementById("obj");//obj元素上绑定click事件obj.addEventListener('click', function (event) { alert(event.eventType);}, false);//调用document对象的 createEvent 方法得到一个event的对象实例。var event = document.createEvent('HTMLEvents');// initEvent接受3个参数:// 事件类型,是否冒泡,是否阻止浏览器的默认行为event.initEvent("ondataavailable", true, true);event.eventType = 'message';//触发document上绑定的自定义事件ondataavailabledocument.dispatchEvent(event);var event1 = document.createEvent('HTMLEvents');event1.initEvent("click", true, true);event1.eventType = 'message';//触发obj元素上绑定click事件document.getElementById("test").onclick = function () { obj.dispatchEvent(event1);};
]]>1、当被clone的节点包含script标签时,clone后script标签是否会再次被执行
内嵌script标签:
```[html]在所有浏览器中alert都只执行一次
外链script标签:
```[html]在非IE浏览器中alert只执行一次
在IE中,只有IE6会执行两次
解决方法:在将clone后的节点加入DOM前,手动删除掉里面的script标签
2.当被clone节点被绑定了事件处理函数时,事件处理函数是否会被一同clone
HTML事件处理绑定:
```[html]在所有浏览器中,click事件均被复制
DOM0级事件处理绑定:
```[html]在所有浏览器中,点击第一个div会有alert,点击第二个div无反应
DOM2级事件处理绑定:
```[html]在非IE浏览器下 点击第二个div不会执行alert
但是在IE6、7、8中 点击第二个div则会执行alert
在《精通javascript》一书中,作者推荐一种Dean Edwards提出的跨浏览器事件绑定/删除事件解决方案
function addEvent(element, type, handler) { // 为每一个事件处理函数赋予一个独立ID if (!handler.$$guid) handler.$$guid = addEvent.guid++; // 为元素建立一个事件类型的散列表(元素的所有事件类型都保存在该对象中) if (!element.events) element.events = {}; // 为每一个元素/事件建立一个事件函数处理的散列表(同一事件类型的不同处理函数保存在该对象中) var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; // 存储已有事件处理函数(如果存在一个) if (element["on" + type]) { handlers[0] = element["on" + type]; } } // 在散列表中存储该事件类型的处理函数 handlers[handler.$$guid] = handler; // 注册一个全局处理函数来处理所有函数 element["on" + type] = handleEvent;}// 创建独立ID计数器addEvent.guid = 1; function removeEvent(element, type, handler) { // 从散列表中删除事件处理函数 if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; }}; function handleEvent(event) { // 获取event对象 (IE 中使用全局event对象) event = event || window.event; // 获取对应事件的处理函数散列表 var handlers = this.events[event.type]; // 依次执行处理函数散列表中的函数 for (var i in handlers) { this.$$handleEvent = handlers; handlers[i].apply(this,[event]); }};
为了弥补element['on'+type]无法绑定多个处理函数的缺点,addEvent将所有事件类型存储在element的events对象中,events中的每一个事件类型以同样的形式存储着该类型下所有的处理函数
```[javascript]element.events={ click:{ 0:function(){...}, 1:function(){...}, ... }, mousedown:{...}, ...}```总结:
1.在使用cloneNode()时,最好在插入前将clone出来的节点中的script手动清除掉,以避免脚本可能被重复执行。
2.使用cloneNode()会将通过attachEvent绑定的事件复制到clone出来的节点上,可以通过使用跨浏览器的事件绑定解决方案来统一让绑定的事件不被复制。
]]>里面有这样一段:
var $=document.getElementById;var b=$('header');
秉着’实践出真理’的原则,自己敲一敲代码跑了一下。
在chrome、firefox、IE9下运行时,会抛出错误:
Illegal invocation chrome
‘getElementById’ called on an object that does not implement interface Document. firefox
再到IE6~8下,却能够顺畅运行。
Illegal invocation意为非法调用
‘getElementById’ called on an object that does not implement interface Document.意为’”getElementById”被一个不能实现document接口的对象调用’(乱翻译的,不过大致意思应该是这样。。。)
说明肯能是作用域的问题
var $=document.getElementById;var b=$.call(document,'header');alert(b);
这次在非IE6~8下成功运行
总结:
在IE6~8下document.getElementById与作用域无关,也就是说在何处调用不会影响函数本身。
在其他浏览器(包括IE9及以上)document.getElementById与作用域有关,也就是说getElementById函数内部this必须指向document
。getElementsByTagName也有同样的情况。
1、 传递参数时需要使用encodeURIComponent,这样组合的url才不会被#等特殊字符截断。
例如:
document.write('<a href="http://www.cpuele.com?aid=7&u='+encodeURIComponent(http://www.cpuele.com/index.htm)+'">退出</a>')
2、 进行url跳转时可以整体使用encodeURI
例如:
Location.href=encodeURI(http://www.cpuele.com/do/s?word=恒特电器&ct=21);
3、 js使用数据时可以使用escape
例如:搜藏中history纪录。
4、 escape对0-255以外的unicode值进行编码时输出%u****格式,其它情况下escape,encodeURI,encodeURIComponent编码结果相同。
注意:
最多使用的应为encodeURIComponent,它是将中文、韩文等特殊字符转换成utf-8格式的url编码,所以如果给后台传递参数需要使用encodeURIComponent时需要后台解码对utf-8支持(form中的编码方式和当前页面编码方式相同)
escape不编码字符有69个:*,+,-,.,/,@,_,0-9,a-z,A-ZencodeURI不编码字符有82个:!,#,$,&,',(,),*,+,,,-,.,/,:,;,=,?,@,_,~,0-9,a-z,A-ZencodeURIComponent不编码字符有71个:!, ',(,),*,-,.,_,~,0-9,a-z,A-Z
]]>在我们的白社会里,需要嵌入第三方应用,而嵌入的方式是使用 iframe,为了页面美观,这里就有一个最简单的需求:iframe 的高度需要跟随其本身内容的变化而实时变化,这就要求主页面根据 iframe 的内容实时的去设置其样式 height 值,但是因为第三方应用和白社会不属于同一个域,所以给实现带来了一点小小的麻烦,所以才有以下的一些讨论…
其实这里需要解决的是,在一个页面 A 中嵌入一个iframe B,A 和 B 不属于同一个域,但是 A 和 B 需要进行一些必要的通信,传递少量的数据信息,所以问题的实质就是主页面与跨域 iframe 之间怎么通信,也就是怎么传递数据信息
下面就针对两种不同的需求,总结一些比较简单,常用和稳定的解决方案。
这种方式,是主页面需要给 iframe B 传递数据,然后 iframe B 获得到数据后进行特定的处理
实现的技巧就是利用 location 对象的 hash 值,通过它传递通信数据,我们只需要在主页面A中设置 iframe B 的 src 后面多加个 #data 字符串(data就是你要传递的数据)
然后在 iframe B 中通过某种方式能即时的获取到这儿 data 就可以了,其实常用的一种方式就是:
这种方式,是 iframe B 需要给主页面传递数据,然后主页面根据获得到数据后进行特定的处理
实现的技巧就是利用一个代理 iframeC,它嵌入到 iframe B 中,并且和主页面A必须保持是同域,然后我们通过它充分利用上面第一种通信方式的实现原理就能把 iframe B 的数据传递给 iframeC,接下来的问题就是怎么让iframeC把数据传递给主页面A
因为,iframeC 和主页面是同域的,所以它们之间传递数据就变得简单多了,我们这里的方式就是使用一个经常使用的属性 window.top (也可以使用window.parent.parent),它返回对载入浏览器得最顶层 window 对象的引用,这样我们就能直接条用主页面A中方法啦,
当然还有其他一些方式,也都测试过,不是浏览器兼容性不好,就是实现起来复杂,通过以上方式就能很方便的在跨域的 iframe 和主页面之间传递数据了,当然也就能解决上面提到的设置 iframe 高度的问题了,但是这种实现方式的前提也是最大的缺点就是 iframe 中的内容必须是我们可控的,但是至少我们这种实现方式是建立在浏览器的安全规则之上的,没有破坏应用本身的安全性。
上面的分析,其实只是一个简单的原理,在白社会里,虽然我们目前的需求还仅仅是实现第三方 iframe 形式的 App 的高度自适应,但是我们在实现的时候尽量考虑到了易用,可扩展性和可维护性,比如:
<script type="text/javascript">function init(){ document.domain = 'bai.sohu.com'; alert('我是主框架,嵌入了第三方应用IframeB,下面开始加载应用'); var iframeTag = document.getElementById('frameB'), iframeSrc = 'http://test.com/iframePage.html'; iframeTag.src = iframeSrc; iframeTag.style.display = 'block';}function callback(h){ var iframeB = document.getElementById('frameB'); alert('IframeC调用我(主框架)接口,把IframeB的高度传给我,具体值是:' + h); iframeB.style.height= h + 10 + 'px'; iframeB.src += '#'+ h;}</script><body onload="init();"> <p>我是主页框架,我的域是:bai.sohu.com</p> <iframe id="frameB" style="display:none;"></iframe></body>
<script type="text/javascript">function init(){ alert('我是第三方App,下面开始创建和主框架同域的通信通道IframeC,并设置它的src,用#号传递高度值'); var iframeTag = document.getElementById('frameC'), iframeSrc = 'http://bai.sohu.com/iframePageC.html#', pageHeight = document.documentElement.scrollHeight || document.body.scrollHeight; iframeTag.src = iframeSrc + pageHeight; iframeTag.style.display = 'block'; window.setTimeout(function(){ alert('主页面设置我(IframeB)的src,通过Hash(#)给我传递它收到的高度:' + location.hash); },2000);}</script>
<script type="text/javascript">document.domain = 'bai.sohu.com';alert('我(IframeC)收到iframeB通过参数(#)给我传递高度值,我现在调用主页面方法去设置IframeB的高度');top.callback(window.location.href.split('#')[1]);</script>
]]>/castsequence [<条件选项>] reset=<#>/target/combat <法术1>, <法术2>, <法术3>
“啥?你在说啥?”
它设定了一个法术列表,当你第一次点击这个宏时,它施放列表中的第一个法术,第二次点击的时候,施放第二个法术,以此类推。 当它施放完最后一个时,又回到起始点开始循环。
“我还是不太明白耶。”
OK,假设你是一个法师,有时候你需要冰霜新星后立刻闪现开,你没法同时做到这些事情,现在你可以设定一个序列去做!
/castsequence 冰霜新星, 闪现
就这么简单,第一次你按这个宏,你施放冰霜新星,再按一次,闪现。
“那如果冰霜新星在冷却中怎么办?它会直接施放闪现么?还是不施放冰霜新星,等第二次按这个宏的时候,再施放闪现?”
都不是。如果法术无法施放(冷却,超出距离,法力不足等等情况下),序列就不会走到下一个法术,下一次你再点这个宏的时候,它会再度尝试施放冰霜新星。(抵抗,躲闪,招架等并不会算作无法施放,这些情况下法术/技能确实施放了,只不过没有命中。)
如果我没有处于战斗中怎么办?我不想在非战斗情况下浪费一个冰霜新星。”
你可以把之前所有的宏条件选项应用于此(可惜的是,只能应用于整个序列,而不能为每个法术单独设定)。
“呃,其实刚才我只是随便说说,OK,那如果我在冰霜新星后不想闪现了怎么办?下一次我想施放冰霜新星时,它会不会还是停滞在施放闪现的设定上?”
这完全没问题,我们会用到重置这个设定。因为冰霜新星一般的冷却时间为24秒,我们可以设定序列在24秒内不被使用就重置它。
/castsequence reset=24 冰霜新星, 闪现
当你第一次点击这个宏时,它会施放冰霜新星,如果你决定不闪现了,等过了24秒后,它会重置到冰霜新星的状态,因为冰霜新星的冷却时间为24秒,那时候它就可以被重新使用了,真棒!
“好吧,这真的很厉害,不过我实际上是个术士啊。”
当然,作为一个术士,我相信你会一直重复上三种DoT,现在你可以为它们设置一个序列以节省按钮空间啦。
/castsequence 腐蚀, 献祭, 痛苦诅咒
就这么简单,你可以连续点击三次这个宏来上这三种DoT。
“但如果在我施放完这些前目标就死了呢?我又卡在这个序列里了,我可不想设什么定时器!”
那也没关系,我们可以用其他的条件选项。如果你设定“reset=target”,那序列将在你改变目标时就重置到起始位置。你也可以设置“reset=combat”,每当你脱离战斗,这个序列就会被重置了。如果你使用“/”控制符(你还记得吧,之前提到过),你也可以将这些重置条件选项组合起来!
/castsequence reset=combat/target 腐蚀, 献祭, 痛苦诅咒
就这全了,这下子每当你改变目标(比如原来的目标快死了,你选中的新的目标)或脱离了战斗(可能目标在你施放完所有DoT前就死了),它将会把整个序列重置,让你可以从头开始。
“OK,真棒,我想我现在弄明白了!给我点时间设置新的宏….搞定!”
好极了,我很高兴能帮上忙。
“那…来决斗一盘吧?”
呃,不了,我从不和术士决斗。
“切,胆小鬼。”
另外本人是巨魔暗M,暗道理说MS不用写什么宏,不过本人觉得/castsequence有必要
/castsequence reset=3 心灵之火,暗影守卫
大概这个宏是在PVP中使用,特别是战场,死了之后一般都加这2个技能,一键搞定
/castsequence reset=3 真言术·痛,吸血鬼拥抱
实现PK中一键上完暗M的DOT
另外可根据自己设置恐吓后心爆宏之类`
IE的盒模型宽(高)度 = width(height)
w3c标准盒模型宽(高)度 = width(height) + padding
IE中AJAX 如果用get方式发送请求 URL后跟的值必须经过encodeURIComponent转码,否则在IE下可能导致AJAX无效(理论上只要是通过GET方式传送的数据都应该使用encodeURIComponent)。
要取textarea中的值 若使用innerHTML只能取到初始值。当textarea中的值改变之后用innerHTML就取不到现在的值,需要用value。
当文件使用 utf-8+BOM 编码时,文件开头会自带一个空白符。这会导致DOCTYPE申明无法被解析,需要将文件保存为 utf-8 无BOM编码。
在IE6下 a 标签设置 href="javascript:void(0)" onclick 事件将不能触发。有两种解决方法:
<a style="cursor:pointer" onclick="alert('d')"></a> 直接不设置href
<a href="javascript:void(0)" onclick="alert('d');return false"></a> 通过return false阻止触发a标签的默认事件
IE下操作粘贴板:
获取粘贴板内容 window.clipboardData.getData("Text")
设置粘贴板内容 window.clipboardData.setData("Text",要复制的内容)
判断IE6最简短的代码
!-[1,]&&!window.XMLHttpRequest
!-[1,]利用了IE与标准浏览器在处理数组的toString方法的差异来判断是否为IE浏览器(PS:只能判断IE6,7,8),!window.XMLHttpRequest则排出掉IE7及以上版本。
水平margin不重叠,浮动元素上下margin不重叠,常规折行上下margin重叠。
]]><p>There are no scrollbars on this page in sane browsers</p>
html, body, p {margin: 0; padding: 0;}body {overflow: hidden;}p {width: 5000px; height: 5000px;}
IE6 IE7下不生效(IE6下横向纵向滚动条都在 IE7下纵向滚动条还在)
原因:
明智的浏览器(ex. chrome and firefox)会初始付值给html{overflow:visible;}
IE6 初始付值html{overflow-x:auto;overflow-y:scroll;}
IE7 初始付值html{overflow-x:visible;overflow-y:scroll;}
只有dom根结点(也就是html根节点)设置html{overflow:visible;}的时候,浏览器才会将body元素中的overflow值应用到视图区。
举个例子说:
设置了body{overflow:hidden}还会出现滚动条,不过这个滚动条不是body的,是html的
只有你设置html{overflow:visible;} body{overflow的值}才能传递到html中去
这样html的值就变成了{overflow:hidden}ok没有滚动条了
这样就很明了啦,并不是bug,而是浏览器初始值不同产生的问题。
]]>安装rvm(ruby管理工具)
curl -L https://get.rvm.io | bash -s stablesource ~/.rvm/script/rvmrvm install requirement
安装ruby
rvm install rubyrvm use ruby --default
安装rubygems
rvm rubygems current
安装rails
gem install rails
gem install jekyll
OK~现在你已经成功安装jekyll了(如果不出什么意外的话)
那么来试试
jekyll new myBlogcd myBlogjekyll server
然后你就能通过 http://localhost:4000 访问jekyll为你生成的blog了。
在github上创建一个项目。进入项目setting
Automatic page generator 能帮你自动生成一些固定模板样式页面
在本地搞个blog项目,代码写得飞起~~~
当你觉得自己的blog已经完美之后,你可以通过jekyll build来让jekyll帮你生成最后的静态代码。这些代码会保存在./_site下。最后你也只需要将./_site下的文件pull到github上
jekyll build
当然在你调试的过程中需要不停地预览你的blog,这时你应该会用到
jekyll server
jekyll server包含了上面的jekyll build,之后会起一个本地服务通过http://localhost:4000来预览./_site中的页面
]]>