定制SyntaxHighlighter工具栏

本文基于 SyntaxHighlighter 3.0.83 (July 02 2010)

工作需要简单的定制一下SyntaxHighlighter,在代码块首行显示代码的编程语言名.
效果:
SyntaxHighlighter

重点:

  • SyntaxHighlighter.toolbar.items.list此变量存放需要toolbar元素,默认有['expandSource', 'help']
  • 自定义toolbar元素可以参加官方的expandSource元素实现,主要要实现getHtml或execute方法.
    • getHtml返回值将会添加到页面div.toolbar
    • execute用于点击getHtml返回Html所执行的代码
    • 没有getHtml方法时,将会调用defaultGetHtml要方法
官方的expandSource代码
  expandSource: {
    getHtml: function(highlighter) {
      if (highlighter.getParam('collapse') != true) return '';

      var title = highlighter.getParam('title');
      return sh.toolbar.getButtonHtml(highlighter, 'expandSource', title ? title : sh.config.strings.expandSource);
    },

    execute: function(highlighter) {
      var div = getHighlighterDivById(highlighter.id);
      removeClass(div, 'collapsed');
    }
  }

如果有用到jQuery,把调用SyntaxHighlighter.all();你改成SyntaxHighlighter.highlight();应该是更好的选择,因为默认SyntaxHighlighter.all();是需要用的是window.onload,要等页面和图片都加载完才会调用SyntaxHighlighter.highlight();高亮;你可以改用$.ready
如果需要高亮页面后生成的html,也只要调用SyntaxHighlighter.highlight();即可

实现代码:
//自定义配置
function my_syntax_highlighter() {
  SyntaxHighlighter.config.stripBrs = true;
  SyntaxHighlighter.config.useScriptTags = false;
  SyntaxHighlighter.defaults.toolbar = true;
  SyntaxHighlighter.highlight();
}

//统一显示代码名,而不是显示brush name短名称
var human_code_names = {
  "ActionScript3": ["as3", "actionscript3"],
  "Assembly": ["nasm8086", "8086", "nasm", "asm", "masm"],
  "AppleScript": ["applescript"],
  "Bash/shell": ["bash", "shell"],
  "ColdFusion": ["cf", "coldfusion"],
  "C#": ["c-sharp", "csharp"],
  "C/C++": ["cpp", "c"],
  "CSS": ["css"],
  "Delphi/Pascal": ["delphi", "pas", "pascal"],
  "Diff/Patch": ["diff", "patch"],
  "Erlang": ["erl", "erlang"],
  "Groovy": ["groovy"],
  "JavaScript": ["js", "jscript", "javascript"],
  "Java": ["java"],
  "JavaFX": ["jfx", "javafx"],
  "Objective C": ["obj-c", "objc"],
  "Perl": ["perl", "pl"],
  "PHP": ["php"],
  "Plain Text": ["plain", "text"],
  "PowerShell": ["ps", "powershell"],
  "Python": ["py", "python"],
  "Ruby": ["rails", "ror", "ruby"],
  "Sass": ["sass", "scss"],
  "Scala": ["scala"],
  "SQL": ["sql"],
  "Visual Basic": ["vb", "vbnet"],
  "XML/HTML": ["xml", "xhtml", "xslt", "html", "xhtml"]
};

function human_code_name(brush_name) {
  brush_name = $.trim(brush_name);
  var result = null;
  $.each(human_code_names, function(key, value) {
    var hit_flag = false;
    $.each(value, function() {
      if (this == brush_name) {
        hit_flag = true;
        return false;
      }
    });
    if (hit_flag) {
      result = key;
      return false;
    }
  });

  if (result === null) return brush_name;
  else return result;
}

function custom_syntax_highlighter_toolbar() {
  SyntaxHighlighter.toolbar.items.list = ['codeName'];
  SyntaxHighlighter.toolbar.items.codeName = {
    getHtml: function(highlighter) {
      var brush_name = highlighter.params.brush;
      return '<span>' + human_code_name(brush_name) + ' code</span>';
    }
  };
}

$(function() {
  my_syntax_highlighter();
  syntax_highlighter_with_compatible();
});
样式:
div.syntaxhighlighter{
  overflow: auto;
}

.syntaxhighlighter .toolbar{
  width: 99.9% !important;
  height: 24px !important;
  line-height: 24px !important;
  background-color: whiteSmoke !important;
  font-weight: bold !important;
  text-indent: 6px !important;
  color: #333 !important;
  position: static !important;
  right: 0px !important;
  top: 0px !important;
}
.syntaxhighlighter .toolbar span{
  height: 24px !important;
  line-height: 24px !important;
}
.syntaxhighlighter table{
  border: solid 1px #DDD !important;
  padding: 5px 0 !important;
  width: 99.7% !important;
}

Jekyll高亮的另一个选择:JS高亮

Jekyll官方文档指导用户使用Liquid,再配合Pygments实现高亮,但作为markdown的忠实粉丝,怎么能用如此繁锁的语法.

Liquid代码块写法:


    
1
2
3
def foo
puts 'foo'
end

markdown GFM fenced code 代码块写法:

def foo
  puts 'foo'
end

我相信你一眼就会爱上markdown的写法.

markdown高亮可以用Pygments,也可以用前台JS高亮,由于Jekyll使用redcarpet时定制Pygments高亮复杂度过高,所以我选择了简单的JS高亮.

需要用的到gem包版本

  • gem "jekyll", :git => 'git://github.com/chitsaou/jekyll.git', :branch => 'redcarpet-2.0', Jekyll 0.11.0版本不支持redcarpet 2.0以上,官方还没有合并此Pull Request
  • gem "redcarpet", "~> 2.1.1", 2.0以上才支持fenced code

修改Jekyll _config.yml

redcarpet:
  extensions: [fenced_code_blocks]
  render_options:

页面引入highlight.js,当然你也可以换成其它的javascript高亮库,但highlight.js支持自动检测语言,而且主题也多.

<link rel="stylesheet" href="http://yandex.st/highlightjs/7.1/styles/default.min.css">
<script src="http://yandex.st/highlightjs/7.1/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

最后,调用Jekyll需要加--redcarpet参数,例如jekyll --redcarpet --server --auto.
如果问题,可以参考我的Jekyll配置, https://github.com/mangege/mangege

jQuery Validation定制:只有提交时才验证

jQuery Validation默认在失去光标会检测表单元素是否通过验证,没有通过的话则显示错误信息.
需求要求只有按提交才验证并显示错误信息.

jQuery Validation默认支持禁用失去光标时验证,一种简单的实现:

$("#commentForm").validate({onfocusout: false, onkeyup: false, onclick: false});

上面代码会引发一个问题,需要手动点击提交按钮才知道是否验证通过.比如有一个元素已经验证不通过,当我修改为正确的内容,需要再点击提交按钮才知道是否验证通过.
我们可以改成,当表单有未通过元素,失去光标等事件时自动验证表单元素.
最后变成,默认失去光标不会触发表单验证,点击提交按钮,如果表单未通过验证,这时修改表单内容失去光标会验证.

下面这种方式是一种无缝全局修改方式,在引入jQuery Validation的JS再引入此代码,之后直接调用$("#commentForm").validate()即可:

(function($) {

  var validator_defaults_dup = $.extend({}, $.validator.defaults);
  $.extend($.validator.defaults, {
    onfocusout: function() {
      if (this.numberOfInvalids() > 0) {
        validator_defaults_dup.onfocusout.apply(this, arguments);
      }
    },
    onkeyup: function() {
      if (this.numberOfInvalids() > 0) {
        validator_defaults_dup.onfocusout.apply(this, arguments);
      }
    },
    onclick: function() {
      if (this.numberOfInvalids() > 0) {
        validator_defaults_dup.onfocusout.apply(this, arguments);
      }
    }
  });


})(jQuery);

此方式不修改jQuery Validation源码,方便升级

一个由IE8'显示友好 http 错误信息'引起的BUG

使用jQuery Form Plugin上传文件,在IE8下会临时创建一个iframe用于form提交.而就是由这个iframe可能会引发两个BUG

显示文件下载对话框

当html response head的content type设置成IE不支持(没有关联处理程序)会像”application/octet-stream”二进制类型显示下载对话框.

解决办法: 设置content type为”text/plain”,为了保险同时设置jQuery Form Plugin的dataType参数为你的正确类型.

response status 500时出错

重现这个Bug环境需要IE8,同时把浏览器设置”显示友好 http 错误信息”勾上.
另外当上传出错时,response的status设置为500,同时响应内容字节不能超过512字节.当内容小于512字节时,IE8会显示友好错误. https://github.com/malsup/form/issues/214#issuecomment-8189245
这样当错误提示内容小于512,同时选中显示友好http错误信息,上传正常出错时就会无法正常处理.

解决办法: 把错误主体内容用空白或其它字符填充,直大于512.例如Rails:
render :json => {:msg => 'error', :noop => ' '*512}, :status => 500, :content_type => Mime::TEXT

解决dwm6标题乱码

dwm标题不支持显示中文,打了pango补丁就能正常显示了

arch用户直接安装dwm-pango包 https://aur.archlinux.org/packages.php?ID=33193
不过貌似依赖很难编译

ubuntu用户从dwm官方下载源码包,再手动打pango补丁.

  • http://dwm.suckless.org/ 下载dwm6的源码包
  • https://aur.archlinux.org/packages.php?ID=33193 下载pango补丁
  • 解压dwm,再cd到其目录
  • 新开终端解压dwm-pango
  • 在dwm源码目录运行 patch -Np1 -i /tmp/dwm-pango/dwm-6.0-pango.patch, 注意把后面的patch文件路径改成你的
  • 补丁打完了按照自己的需要修改config.h,编译安装就行了
  • 也许需要手动apt-get安装libxinerama-dev和libpango1.0-dev信赖包

密云之行

上周日从四元桥骑行密云水库,地图计算出单程81KM,码表显示全程167KM.共花13个小时,中间就吃了次午饭.

这是买勇士560的第二次长途骑行,第一次是在网上认识了两朋友,五一小长假的第一天跑了一趟潭柘寺.
潭柘寺的G108路线还是比较有感觉,6公里的长坡,下坡的感觉太爽了.不过此路线车貌似比较多,路上还是得注意安全.

此次去密云水库是一个人,本来有一朋友在群里发起,但他在顺义,没有约到碰头的地方,后来想想就算了.
路线是直走G101,也就是京密路.路非常好走,没有一个坡.导致骑行都没有啥感觉.到时密云找水库时,才有点山路.
路上碰到一车队,有四五十岁的骑行去怀柔,有的骑的还是普通的通勤车.

好不容易到了终点,可以水库只能到山顶远远看望.景色还不错.

骑行达到人车合一的境界还差太远了.码表显示的平均时速是15KM/H,用的是前2后3的齿.骑行起来大腿不痛,但因为踏频还没有适合,第二天骑行时微微的感觉关节有点痛.
长距离的骑行感觉货架和骑行裤是很有必要的.背着背包肩膀痛.穿普通的裤子屁股痛,而且汗都湿了裤子,很不舒服.

高强度的运动对减肥还是很有效的,一次骑行下来,肚子上的脂肪都消耗的差不多了.

计划以后每周末都出行一次,为1000+公里的长途旅行做准备.

Rails学习笔记之Typo登陆认证与授权

只是写出自己觉得不错的看点,供新手参考.
由ApplicationController统一include LoginSystem module.
代码放在目录vendor/plugins/typo_login_system

小技巧一:
vendor/plugins/typo_login_system/lib/login_system.rb

def self.included(base)
  base.send :helper_method, :current_user, :logged_in?
end

利用include module的回调方法included,把current_user, logged_in?添加到helper,这样在view也可以调用这两个方法
另外它的login_form_系列方法的实现也很值得看.

小技巧二:

def current_user
  @current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
end

利用 ||= ,把当前用户保存到实例变量@current_user中,这样控制器实例第二次调用的时候,就不需要再运行后面的代码.

Typo::AccessControl的实现有点复杂,它是yield的常用手法的一个实现
授权基本角色,权限控制细粒度到controller,无法控制到action
从vendor/plugins/typo_login_system/lib/access_control.rb 文件的注释示例进行简单的分析.
类方法map需要传一个Hash,:require的值可以是数组或字符串,这里写角色名,每调用一次类方法,会生成一个Mapper实例,yield后,再把Mapper实例保存到@mappers
而@mappers运行时是唯一的,所以当你多次调用类方法map,把它其生成的所有Mapper实例都保存到@mappers这个数组里.

Mapper数据结构::
Mapper
  –roles
  –controllers
  –project_modules
    –controllers

验证是否可以有权限
通过@mappers拿到包含指定role name的所有Mapper实例,把mapper的controllers和其拥有的所有project_modules的controllers合并成一个数组,判断访问的控制器名是否在此数组里

Typo的角色权限定义可以看config/initializers/access_rules.rb

功能不是很强大,但满意一般的小应用场合.

Rails学习笔记之Typo安装

选择Typo作为学习目标.原因有三点:

  1. 更像我们平常开发的Rails应用,对Rails动刀的地方不多,容易看懂.
  2. 博客项目,复杂度相对来说比较简单,功能也不少.
  3. 有测试,分析起来很轻松.

从安装分析起.我也是半吊子,写的不好,请多多指教.
安装路径是/setup,Controller是Setup,从此Controller的RSpec可以看出,进行setup操作,数据库里需要有一条Blog数据,不然会报错的.

GET setup时,会调用ApplicationController的this_blog方法. this_blog方法默认拿到Blog的第一条记录
POST setup用的是同一个方法,用http method来区分,提交表单用post. 成功后会重定向到 /setup/confirm
此操作应该会生成一个用户,和初始化博客设置.

汗,貌似我在翻译Typo的RSpec.Stop,看到觉得不错的再分享.

text_field的placeholder属性挺不错的,输入提示用的,获取到光标时自动隐藏.不过是HTML5特性

generate_password方法用的upto,其实times实现更优美.

1.upto(7) { |i| newpass << chars[rand(chars.size-1)] }

7.times { newpass << chars[rand(chars.size-1)] } 

博客有新家了

重新为自己的博客启用独立域名,其实一两年前用WordPress写过博客,但由于换空间以当时的技术觉得迁移数据麻烦,就转到iteye.

经朋友介绍了解了Jekyll,觉得挺不错的.有以下几点好处:

  1. 不需要数据库,数据直接保存为文件,管理可以用版本软件,存到免费的代码托管. 如githu, google code.
  2. 可以直接把页面托管到git pages,不需要自己维护主机,还支持绑定域名. 我的博客现在就托管要github pages,你可以用ping命令验证一下.
  3. 够简单,写文章的时候直接用vim,还支持markdown等标识语言.

对我来说主要有这三个优点,缺点也是有的.

  1. 熟悉Jekyll的功能需要一定时间,以前用的都是基于界面录入的,换成生成页面的有些思维还真没有转换过来.
  2. 模板写起来有点麻烦,用的是 Liquid 模板引擎,需要熟悉.
  3. 很多功能都要写插件, 比如文章摘要,分类列表页生成. 不过这些功能 octopress 都已经实现了,可以直接参考.
  4. 搜索只能依赖第三方搜索,用google的site参数.

这些缺点随着时间的推移和个人学习进步,有些反而会变成优点.
这一次,不管什么原因,都不需要再搬家了.