之前写过一个browserSync的脚本编译jade/sass/coffee,在多个项目中拷贝脚本文件比较麻烦,业余封装了一个npm插件,这里介绍一下用法。
项目地址:https://github.com/thinkjs/autocommand-cli,插件使用typescript开发,欢迎有兴趣的同学一起完善。
mkdir example && cd example
mkdir _source _source/jade _source/sass _source/live
mkdir html html/css html/js
_source目录存放源代码,_source/jade、_source/sass、_source/live分别存放jade、sass、livescript源文件。html目录存放生成结果,html、html/css、html/js分别存放html、css、js文件。
echo '{}' > package.json
npm install thinkjs/autocommand-cli#latest jade node-sass livescript --save-dev
如果网络不好,建议使用cnpm替代
"scripts": {
"acmd": "acmd",
"start": "acmd watch",
"build": "acmd run"
},
完整的package.json如下
{
"scripts": {
"acmd": "acmd",
"start": "acmd watch",
"build": "acmd run"
},
"devDependencies": {
"autocommand-cli": "thinkjs/autocommand-cli#latest",
"jade": "^1.11.0",
"livescript": "^1.5.0",
"node-sass": "^3.9.3"
}
}
npm run acmd -- config -i
默认在当前目录下创建名为_config的配置文件,编辑该配置文件
修改file字段内容为:
"file": ["_source/**/*.{jade,sass,ls}"],
修改define字段内容为:
"define": {
"_source/": {
".jade": {
"file": "html/#{fileName}.html",
"command": "jade -Po html/ #{file}"
},
".sass": {
"file": "html/css/#{fileName}.css",
"command": "node-sass --output-style compact #{file} html/css/#{fileName}.css"
},
".ls": {
"file": "html/js/#{fileName}.js",
"command": "lsc -co html/js/ #{file}"
}
}
},
修改 browserSync > init下的server为:
"server": {
"baseDir": "html/"
},
完整配置文件如下:
{
// 侦听的文件
"file": ["_source/**/*.{jade,sass,ls}"],
// 过滤
"ignore": ["_*.*", "node_modules/"],
// 变量
"variable": {
"localBin": "~/node_modules/.bin"
},
// 环境变量
"environment": {
":PATH": "#{localBin}"
},
// 定义
"define": {
"_source/": {
".jade": {
"file": "html/#{fileName}.html",
"command": "jade -Po html/ #{file}"
},
".sass": {
"file": "html/css/#{fileName}.css",
"command": "node-sass --output-style compact #{file} html/css/#{fileName}.css"
},
".ls": {
"file": "html/js/#{fileName}.js",
"command": "lsc -co html/js/ #{file}"
}
}
},
// browserSync配置
"browserSync": {
// 初始化配置
"init": {
"server": {
"baseDir": "html/"
},
"open": false,
"ui": false
},
// 启动livereload
"reload": true
}
}
// vim: se sw=2 ts=2 sts=2 ft=javascript et:
创建_source/jade/index.jade,内容如下:
doctype html
html
head
title jade page
body
h1 hello world
在terminal中运行 npm start,浏览器访问 http://localhost:3000,可查看页面。
创建_source/sass/style.sass,内容如下:
html, body padding: 0 margin: 0 font-size: 16px h1 color: green
编辑_source/jade/index.jade加入对style.sass的引用,在head中插入以下内容:
link(href="css/style.css" rel="stylesheet")
保存查看浏览器,内容已自动刷新。
创建_source/sass/script.ls,内容如下:
console.log \hello
编辑_source/jade/index.jade加入对script.ls的引用,在body中插下以下内容:
script(src="js/index.js")
保存可查看浏览器中的控制台输出。
有时需要对项目的所有源文件进行编译,使用如下命令:
npm run build
比如需要编译所有的sass文件,可以使用如下命令:
npm run build -- -f _source/sass/*.sass
-f选项支持glob表达式,使用引号括起即可,例如编译jade和sass可以使用如下命令:
npm run build -- -f '_source/**/*.{jade,sass}'
如果使用git管理文件,需要编译git中已经修改的文件,可以使用如下命令:
git status -uno | awk '{print $2}' | npm run build -- -o
使用find查找sass进行编译,命令如下:
find _source -type f | grep *.sass | npm run build -- -o
autocommand-cli项目最初的出发点是为了锻炼一下typescript的能力,经过半年的迭代,功能已经逐渐完善。命令行参数解析、文件改动侦听、http服务器功能都使用现成npm插件实现。前端的工具链不断完善,各种工具层出不穷,花大量时间造一个自己用起来的顺手的工具还是有点奢侈:)
近期公司站点通过联通4G网站访问时被运营商插入广告代码,通过手机抓包定位到运营商值入的广告代码。
植入的广告代码通过在固定url的请求中添加内容插行一段js,通过body.appendChild在页面引入一段js代码。想到一个应急方案,通过改写appendChild来拦截广告,经测试有效。代码如下:
(function() {
var rootEl = top.document.body;
var originAppendChild = rootEl.appendChild;
try {
rootEl.appendChild = function (elem) {
var src = '';
if (typeof elem == 'object' && elem != null && elem.getAttribute) {
src = elem.getAttribute('src');
}
if (typeof src == 'string' && src.length) {
if ( src.match(/^(?:http(?:s)?:)?\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
|| src.match(/cifenqi\.com/) != null) {
return;
}
}
originAppendChild.apply(rootEl, [].slice.call(arguments, 0));
}
}
catch(e) { }
})();
ISP广告干的是非法勾当多使用ip地址,因此直接屏蔽ip地址的元素。此方法只能应急可被绕过,难怪某度、某宝纷纷启用全站https。
之前因为vim上的TFS插件一直不好使,自己写了一个简单的插件在vim上签出TFS源文件。为了在NERDTree上面集成了菜单功能,研究了下NERDTree插件。年底忙里偷闲整理了一些关于NERDTree的知识点,分享出来。
1. 添加NERDTree文件菜单
在vim插件目录创建一个nerdtree_plugin文件夹,在其中新建vim脚本文件,使用NERDTreeAddMenuItem函数添加一项按钮项。参数格式:
{'text': 'menu text', 'shortcut': 'short key', 'callback':'callback function name'}
示例:
call NERDTreeAddMenuItem({ 'text': 'TFS Check (o)ut', 'shortcut': 'o', callback': 'TFSCheckoutCallback' })
2. 获取当前文件节点
let currentNode = g:NERDTreeFileNode.GetSelected()
3.获取文件路径
let currentFile = currentNode.path._strForGlob()
4.更新文件状态
cal currentNode.path.refresh()
cal NERDTreeRender()
# 当我们通过currentNode.path.refresh() 可以刷新文件节点,之后通过NERDTreeRender重绘获得更新。当通过操作修改了文件的属性后需要通过此操作更新文件状态。
附插件git地址:https://github.com/thinkjs/TFSCheckout
NERDTree FileNode参考:https://github.com/scrooloose/nerdtree/blob/5.0.0/lib/nerdtree/tree_file_node.vim
NERDTree Pathp参考:https://github.com/scrooloose/nerdtree/blob/5.0.0/lib/nerdtree/path.vim
NERDTree Public API参考:https://github.com/scrooloose/nerdtree/blob/5.0.0/plugin/NERD_tree.vim#L164
之前使用预处理器一直是基于vim插件autocommand,最近和同事协作开发,vim入门太陡峭,抽了点时间用browser-sync实现了类似功能。具体功能如下:自动检测jade、sass、livescript等源码文件的改动,编译对应的html、css、js,更新改动到浏览器中打开的页面。
明确几个定义:
1. browser-sync nodejs插件,官方说明 Time-saving synchronised browser testing。
2. 前端预处理器 jade、sass、livescript。
3. 工作流 使用browser-sync整合jade、sass、livescript完成开发的一套流程。
依赖:
nodejs
npm package: jade、livescript、browser-sync
ruby
gem package: sass
工作目录:
说明:_source目录下存放预处理器源代码,html目录存放生成的html、css、js等。
在workspace目录下创建 browserSync.ls 具体代码如下:
path = require \path
exec = require \child_process .exec
browserSync = require \browser-sync .create!
# config {{{
outputDir = 'html'
cssOutputDir = "#outputDir/css"
jsOutputDir = "#outputDir/js"
reloadWatchFile = ''
# "#outputDir/*.html"
# "#jsOutputDir/*.js"
# "#outputDir/img/*.*"
compileWatchFile =
"_source/jade/*.jade"
"_source/sass/*.sass"
"_source/live/*.ls"
autoCompileFile = true
# }}}
# getTimeToken {{{
getTimeToken = ->
currDate = new Date()
hours = currDate.getHours()
minutes = currDate.getMinutes()
seconds = currDate.getSeconds()
if hours < 10
hours = "0#hours"
if minutes < 10
minutes = "0#minutes"
if seconds < 10
seconds = "0#seconds"
"#hours:#minutes:#seconds"
# }}}
# compileTask {{{
getCompileCmdAndFileName = (file, ext) ->
filename = path.basename file, ext
switch ext
case '.jade' then
compileFileName = "#outputDir/#{filename}.html"
cmd = "jade -Po #outputDir #file"
case '.sass' then
compileFileName = "#cssOutputDir/#{filename}.css"
cmd = "sass --sourcemap=none --style compact #file|sed '/^@charset/d'>#compileFileName"
case '.coffee' then
compileFileName = "#jsOutputDir/#{filename}.js"
cmd = "coffee --no-header -bco #jsOutputDir #file"
case '.ls' then
compileFileName = "#jsOutputDir/#{filename}.js"
cmd = "lsc --no-header -bco #jsOutputDir #file"
default
compileFileName = cmd = ''
[cmd, compileFileName]
compileTask = (file, ext, reload) !->
cmdIndex = -1
try
[cmd, filename] = getCompileCmdAndFileName file, ext
if not cmd or not filename
console.log "cmd not define. file: #file ext: #ext"
# exec callback
execCallback = (err, stdo, stde) !->
if err is null and not stde
if cmdIndex is -1
console.log "[#{getTimeToken!}] compiled #filename"
reload filename if reload
else
execCmd()
else
console.log err || stde
# execute command
do execCmd = !->
if Array.isArray cmd
currCmd = cmd[++cmdIndex]
if cmd.length <= cmdIndex+1
``cmdIndex = -1;``
else
currCmd = cmd
if currCmd
exec currCmd, execCallback
compileCallback = (file) !->
ext = path.extname file
filename = path.basename file
# ignore partial file
if filename.charAt(0) is '_'
return undefined
switch ext
case '.jade', '.coffee', '.ls', '.sass'
# compileTask file, ext
# case '.sass'
compileTask file, ext, browserSync.reload
default
console.log 'unknown file type.'
# }}}
# browserSync {{{
browserSync.init do
server:
baseDir: outputDir
index: \index.html
open: false
if reloadWatchFile and reloadWatchFile.length
browserSync.watch reloadWatchFile
.on \change, browserSync.reload
wacher = browserSync.watch compileWatchFile
.on \change, compileCallback
# auto compile file
if autoCompileFile
wacher.on \add, compileCallback
# }}}
# vim: set sw=2 ts=2 sts=2 et fdm=marker:
使用方法如下:
cd workspace
lsc browserSync.ls
* lsc 是 livescript的命令,使用npm 全局安装 livescript 后方可使用。
配置说明:
outputDir html文件的输出目录,web server的根目录。
cssOutputDir css文件输出目录。
jsOutputDir js文件输出目录。
reloadWatchFile 检测改动的目录,如果文件发生变更,会更新到打开的页面中。html/css/js已自动更新,默认可留空。
compileWatchFile 监测的源码文件,如果发生变化执行编译任务。
autoCompileFile 自动编译文件,设为true时,每次启动会把所有的源码文件编译一遍。
vim-coloresque是一款高亮插件,将css/sass/less中的颜色用背景色高亮出来方便预览。用了一段时间发现代码补全一定程度上受影响,自己对插件进行了一些优化,修改后git地址:https://github.com/thinkjs/vim-coloresque。
看插件github上的issue,插件中对isk这个选项的设置影响了补全,全并且影响了vim全局的keyword识别,直接注释掉isk+=-、isk+=#、 isk+=# 解决了补全问题但针对colorDict的高亮识别又发生错误,例如样式名为 .red或.common-red-btn,此处的red会被高亮。作者使用"\<"单词边界来识别colorDict,修改isk以后单词边界识别出错高亮出错,想到用正则表达式来调整此处的colorDict匹配。
匹配规则如下:
1. 前字符为行首或空白或:(冒号)
2. 后字符为行尾或空白或;(分号)
正则如下:
'\(^\|\s\|:\)\@<=\c'._color.'\($\|\s\|;\)\@='
vim中使用@<=作逆序环视、@=做顺序环视(vim中使用:h @<=查看详细介绍),修改后效果不错,符合我的应用场景。diff代码如下:
From f162c7a118caba4af655741feeae11720329bb4e Mon Sep 17 00:00:00 2001
From: leaf
Date: Wed, 21 Jan 2015 16:27:40 +0800
Subject: [PATCH] remove "isk" change, improve color dict regexp
---
after/syntax/css/vim-coloresque.vim | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/after/syntax/css/vim-coloresque.vim b/after/syntax/css/vim-coloresque.vim
index c80a9a5..b60f863 100644
--- a/after/syntax/css/vim-coloresque.vim
+++ b/after/syntax/css/vim-coloresque.vim
@@ -122,9 +122,9 @@ function! s:VimCssInit(update)
if a:update==1
call s:ClearMatches()
endif
- :set isk+=-
- :set isk+=#
- :set isk+=.
+ " :set isk+=-
+ " :set isk+=#
+ " :set isk+=.
if len(keys(b:color_pattern))>0
call s:RestoreColors()
@@ -301,7 +301,10 @@ function! s:AdditionalColors()
"let w:colorDictRegExp = '\('
for _color in keys(w:colorDict)
"let w:colorDictRegExp.='\<'._color.'\>\|'
- call s:MatchColorValue(strpart(w:colorDict[tolower(_color)], 1), '\<\c'._color.'\>')
+ " old
+ " call s:MatchColorValue(strpart(w:colorDict[tolower(_color)], 1), '\<\c'._color.'\>')
+ " new regexp
+ call s:MatchColorValue(strpart(w:colorDict[tolower(_color)], 1), '\(^\|\s\|:\)\@<=\c'._color.'\($\|\s\|;\)\@=')
endfor
"let w:colorDictRegExp=strpart(w:colorDictRegExp, 0, len(w:colorDictRegExp)-2).'\)\c'
endfunction
--
1.7.11.1
编辑缩进嵌套的文件时想找到对应的层级比较困难,写了一个函数,使用cc选项设定一条辅助线,标识到指定的缩进层级。代码如下:
" --------------------------------------------------
" [参考线切换] {{{
" --------------------------------------------------
fu! ReferenceLine(t)
if exists('w:ccnum')
let ccnum=w:ccnum
elsei exists('b:ccnum')
let ccnum=b:ccnum
else
let ccnum=0
en
let oldcc=ccnum
" let ccc=&cc
" ec oldcc
let ccc=','.&cc.','
" add/sub
if a:t=='add' || a:t=='sub'
" check old cc
if match(ccc, ','.oldcc.',')<0
let oldcc=0
let ccnum=0
en
" step
let csw=&sw
if a:t=='add'
let ccnum=ccnum + csw
elsei a:t=='sub'
let ccnum=ccnum - csw
if ccnum < 0 | let ccnum=0 | en
en
if oldcc > 0 | let ccc=substitute(ccc, ','.oldcc.',', ',', '') | en
let ccc=ccc.ccnum
" ec ccc
" ec ccnum
let ccc=substitute(ccc, '^0,\|,0,\|,0$', ',', 'g')
let ccc=substitute(ccc, '^,\+\|,\+$', '', 'g')
" ec ccc
let w:ccnum=ccnum
let b:ccnum=ccnum
exec "setl cc=".ccc
" del
elsei a:t=='del'
let ccc=substitute(ccc, ','.oldcc.',', ',', '')
let ccc=substitute(ccc, '^,\+\|,\+$', '', 'g')
" ec ccc
let w:ccnum=0
let b:ccnum=0
exec "setl cc=".ccc
en
endf
nn <silent> <A-u> :call ReferenceLine('sub')<CR>
nn <silent> <A-o> :call ReferenceLine('add')<CR>
" }}}
使用方法:
Alt+o 增加参考线、Alt+u 减少参考线,最后两行是键映射,可以按照需求自行修改。
设定参考线后,如果想要设定cc做列宽参照,请使用set cc+=<num>来设定。
vim-addon-manager是一款功能非常强大的vim插件管理工具,但其依赖git, hg, svn, curl, unzip等工具在win平台配置较繁琐,一直没在win平台中使用这种方案。这两天在试着在win平台配置,分享一下配置过程。
1. 依赖程序
1.1 git 主页 http://msysgit.github.io/
下载地址:https://github.com/msysgit/msysgit/releases/download/Git-1.9.2-preview20140411/Git-1.9.2-preview20140411.exe
1.2 svn 主页 http://www.sliksvn.com/
下载地址:http://www.sliksvn.com/pub/Slik-Subversion-1.8.9-x64.msi
1.3 hg 主页 http://mercurial.selenic.com/
下载地址:http://mercurial.selenic.com/release/windows/Mercurial-3.0.exe
msysgit包中带有curl以及unzip等工具
2. 添加环境变量
编辑vim配置文件($VIM/.vimrc)为vim指定相关环境变量(将C:\Program Files替换成自己的安装路径)
if(has('win32') && match($PATH,'SlikSvn')<0)
let $PATH=$PATH.';C:\Program Files\SlikSvn\bin;C:\Program Files\Mercurial;C:\Program Files\Git\bin'
en
3. 下载vim-addon-manager并配置
从https://github.com/MarcWeber/vim-addon-manager 获取源代码到插件目录,在vimrc中添加配置。
附官方建议配置:
fun! SetupVAM()
let c = get(g:, 'vim_addon_manager', {})
let g:vim_addon_manager = c
let c.plugin_root_dir = expand('$HOME', 1) . '/.vim/vim-addons'
" most used options you may want to use:
" let c.log_to_buf = 1
" let c.auto_install = 0
let &rtp.=(empty(&rtp)?'':',').c.plugin_root_dir.'/vim-addon-manager'
if !isdirectory(c.plugin_root_dir.'/vim-addon-manager/autoload')
execute '!git clone --depth=1 git://github.com/MarcWeber/vim-addon-manager '
\ shellescape(c.plugin_root_dir.'/vim-addon-manager', 1)
endif
call vam#ActivateAddons([], {'auto_install' : 0})
endfun
call SetupVAM()
VAMActivate matchit.zip vim-addon-commenting
不建议将所有配置内容都放在vimrc中,向大家推荐我整理的vim配置分割方案:http://zshou.i11r.com/posts/39266.html
1.配置用户超时
<Project>/config/initializers/devise.rb
编辑 # config.timeout_in = 30.minutes 这一行,修改为想要配置的时间
<Project>/app/models/user.rb
配置devise :database_authenticatable, :registerable,这一行,加上 :timeoutable
2.设置用户单处登陆
原理:devise在用户登陆后会设置users表中的current_sign_in_at字段为最后一次登陆的时间,用户每次登陆后在session中保存current_sign_in_at字段,在application中校验session中current_sign_in_at字段,如果为空或者小于current_user对象的current_sign_in_at则强制注销
1) 在application_controller基类中定义after_sign_in_path_for方法在当中设置session对象
def after_sign_in_path_for(resource)
session[:current_sign_in_at] = current_user.current_sign_in_at
root_path
end
2) 定义has_signed取代:authenticate_user!判断用户登陆
def has_signed
unless user_signed_in?
redirect_to root_path
else
if session[:current_sign_in_at] == nil or session[:current_sign_in_at] < current_user.current_sign_in_at
redirect_to destroy_user_session_path
end
end
end
3)在需要判断用户登陆的controller中添加before_action :has_signed过滤器。
给rails系统加i18n,根据浏览器的头去识别用户语言。记录一下相关实现:
1. 添加i18n配置
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
config.i18n.available_locales = [:en, :cn]
config.i18n.default_locale = :cn
2. 获取语言设置
# app/controller/application_controller.rb
before_action :set_locale
# set local {{{
def set_locale
I18n.locale = get_locale
end
# }}}
# locale {{{
def get_locale
locale = I18n.default_locale
if params[:locale]
locale = params[:locale].downcase
elsif cookies[:locale]
locale = cookies[:locale]
elsif locale = get_accept_language
locale
end
# set cookies
if cookies[:locale] != locale
cookies[:locale] = locale
end
locale
end
def get_accept_language
begin
first_lang = request.env['HTTP_ACCEPT_LANGUAGE'].split(';').first
default_lang = first_lang[0,2].downcase
if default_lang == 'zh'
first_lang[3,2].downcase
else
default_lang
end
rescue
nil
end
end
# }}}
说明:locale的值优先从url地址读取,次之从cookie,最后从浏览器头判断。英文浏览器的头为Accept-Language:en-US,en;,取en即前两位。中文浏览器头为Accept-Language:zh-CN,zh;,取cn即3-5位,转为小写。
3.编辑语言文件
# config/locales/en.yml
en:
hello: "Hello world"
# config/locales/cn.yml
cn:
hello: "你好"
4. 在模板文件中应用
# app/views/home/index.html.erb
<p><%= t('hello'); %></p>
<ul>
<% I18n.available_locales.each.each do |locale| %>
<li><%= link_to locale, params.merge(locale: locale) %></li>
<% end %>
</ul>
说明:点击生成的链接可以手动切换语言,测试的时候方便一些。
devise默认已经有较好的ajax支持了,只需要简单的配置一下即可使用ajax登录/注册。
1. 修改config/application.rb加下以下配置,启用json输出。
# devise respond_to json config.to_prepare do DeviseController.respond_to :html, :json end
2. 修改form标签,添加data-remote, data-type,设置id
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :remote => true,
html: {id: 'ajax_user_signin', data: {type: :json}}) do |f| %>
3. 设置ajax头
$.ajaxSetup({
beforeSend: function(xhr){
var token;
token = $('meta[name="csrf-token"]').attr('content');
if (token) {
xhr.setRequestHeader('X-CSRF-Token', token);
}
}
});
4. js处理代码
$('#ajax_user_signin').on('ajax:complete', function(e, xhr, type){
if (type === 'success') {
location.href = '/';
} else {
try {
alert(xhr.responseJSON.error);
// this.reset();
} catch (e$) {
e = e$;
}
}
});
关于注册:
注册的方法和登录基本相同,给form_for添加data-remote、data-type和id等属性。需要注意的是注册返回的表单验证是多项错误,使用xhr.resonseJSON.errors获取错误集合。