JavaScript 中几种不同的基于 prototype 继承方式的区别

JavaScript 中几种不同的基于 prototype 继承方式的区别

普通属性的继承

第一种方式

来自于 MDN 对象模型的细节

function Employee1 (name, dept) {
  console.log('Employee constructor')
  this.name = name || "";
  this.dept = dept || "general";
  this.work = function() {console.log("Employee.work")}
  this.workAsEmployee = function() { this.work() }
}

function WorkerBee1 (projs) {
  console.log('WorkerBee constructor')
  this.projects = projs || [];
  this.work = function() {console.log("WorkerBee.work")}
  this.workAsBee = function() { this.work() }
}
WorkerBee1.prototype = new Employee1;

function Engineer1 (mach) {
  console.log('Engineer constructor')
  this.dept = "engineering";
  this.machine = mach || "";
  this.work = function() {console.log("Engineer.work")}
  this.workAsEngineer = function() { this.work() }
}

Engineer1.prototype = new WorkerBee1;

e1 = new Engineer1()
e1.work()
console.log(Employee1.prototype.isPrototypeOf(e1))
console.log(WorkerBee1.prototype.isPrototypeOf(e1))
console.log(e1)

Alt text

第二种方式

来自于 YUI 的实现,利用中间对象传递 prototype

function extend(Child, Parent) {
  var F = function () { };
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Object.defineProperty(Child, "super", { "value": Parent.prototype })
}

function Employee2 (name, dept) {
  console.log('Employee constructor')
  this.name = name || "";
  this.dept = dept || "general";
  this.work = function() {console.log("Employee.work")}
  this.workAsEmployee = function() { this.work() }
}

function WorkerBee2 (projs) {
  console.log('WorkerBee constructor')
  this.projects = projs || [];
  this.work = function() {console.log("WorkerBee.work")}
  this.workAsBee = function() { this.work() }
}
extend(WorkerBee2, Employee2);

function Engineer2 (mach) {
  console.log('Engineer constructor')
  this.dept = "engineering";
  this.machine = mach || "";
  this.work = function() {console.log("Engineer.work")}
  this.workAsEngineer = function() { this.work() }
}
extend(Engineer2, WorkerBee2);

e2 = new Engineer2()
e2.work()

console.log(Employee2.prototype.isPrototypeOf(e2))
console.log(WorkerBee2.prototype.isPrototypeOf(e2))
console.log(e2)

Alt text

可以看到,区别主要在于,直接 Child.prototype = new Parent() 会把定义在 Parent 里面的方法也带到 prototype 里面去。 另外,这种方式并没有执行父类的构造函数。

对于定义在 prototype 里面的方法呢

下面对上面的方法定义进行一点改进,把方法定义在 prototype 里,类似正常的 OO 编程中在类里面定义方法。

第一种方式改进

function Employee3 (name, dept) {
  console.log('Employee constructor')
  this.name = name || "";
  this.dept = dept || "general";
}
Employee3.prototype.work = function() {console.log("Employee.work")}
Employee3.prototype.workAsEmployee = function() { this.work() }

function WorkerBee3 (projs) {
  console.log('WorkerBee constructor')
  this.projects = projs || [];
}
WorkerBee3.prototype = new Employee3;
WorkerBee3.prototype.work = function() {console.log("WorkerBee.work")}
WorkerBee3.prototype.workAsBee = function() { this.work() }

function Engineer3 (mach) {
  console.log('Engineer constructor')
  this.dept = "engineering";
  this.machine = mach || "";
}
Engineer3.prototype = new WorkerBee3;
Engineer3.prototype.work = function() {console.log("Engineer.work")}
Engineer3.prototype.workAsEngineer = function() { this.work() }

e3 = new Engineer3()
e3.work()

console.log(Employee3.prototype.isPrototypeOf(e3))
console.log(WorkerBee3.prototype.isPrototypeOf(e3))
console.log(e3)

Alt text

第二种方式的改进

function extend(Child, Parent) {
  var F = function () { };
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Object.defineProperty(Child, "super", { "value": Parent.prototype })
}

function Employee4 (name, dept) {
  console.log('Employee constructor')
  this.name = name || "";
  this.dept = dept || "general";
}
Employee4.prototype.work = function() {console.log("Employee.work")}
Employee4.prototype.workAsEmployee = function() { this.work() }

function WorkerBee4 (projs) {
  console.log('WorkerBee constructor')
  this.projects = projs || [];

}
extend(WorkerBee4, Employee4);
WorkerBee4.prototype.work = function() {console.log("WorkerBee.work")}
WorkerBee4.prototype.workAsBee = function() { this.work() }

function Engineer4 (mach) {
  console.log('Engineer constructor')
  this.dept = "engineering";
  this.machine = mach || "";
}
extend(Engineer4, WorkerBee4);
Engineer4.prototype.work = function() {console.log("Engineer.work")}
Engineer4.prototype.workAsEngineer = function() { this.work() }

e4 = new Engineer4()
e4.work()

console.log(Employee4.prototype.isPrototypeOf(e4))
console.log(WorkerBee4.prototype.isPrototypeOf(e4))
console.log(e4)

Alt text

注意观察 constructor__proto__ 属性。

要执行所有构造函数

上述第二种方法,都没有执行父类的构造函数,也就没有真正的继承父类的初始化数据。为了弥补这一点,如下两种写法都可以达到目的。

利用 super 变量

function extend(Child, Parent) {
  var F = function () { };
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
}

function Employee5 (name, dept) {
  console.log('Employee constructor')
  this.name = name || "";
  this.dept = dept || "general";
}
Employee5.prototype.work = function() {console.log("Employee.work")}
Employee5.prototype.workAsEmployee = function() { this.work() }

function WorkerBee5 (projs) {
  console.log('WorkerBee constructor')

  this.super = Employee5;
  this.super();

  this.projects = projs || [];

}
extend(WorkerBee5, Employee5);
WorkerBee5.prototype.work = function() {console.log("WorkerBee.work")}
WorkerBee5.prototype.workAsBee = function() { this.work() }

function Engineer5 (mach) {
  console.log('Engineer constructor')

  this.super = WorkerBee5;
  this.super();

  this.dept = "engineering";
  this.machine = mach || "";
}
extend(Engineer5, WorkerBee5);
Engineer5.prototype.work = function() {console.log("Engineer.work")}
Engineer5.prototype.workAsEngineer = function() { this.work() }

e5 = new Engineer5()
e5.work()

console.log(Employee5.prototype.isPrototypeOf(e5))
console.log(WorkerBee5.prototype.isPrototypeOf(e5))
console.log(e5)

Alt text

类似,但是用 Parent.apply 方法

function extend(Child, Parent) {
  var F = function () { };
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
}

function Employee6 (name, dept) {
  console.log('Employee constructor')
  this.name = name || "";
  this.dept = dept || "general";
}
Employee6.prototype.work = function() {console.log("Employee.work")}
Employee6.prototype.workAsEmployee = function() { this.work() }

function WorkerBee6 (projs) {
  console.log('WorkerBee constructor')
  Employee6.apply(this)
  this.projects = projs || [];
}
extend(WorkerBee6, Employee6);
WorkerBee6.prototype.work = function() {console.log("WorkerBee.work")}
WorkerBee6.prototype.workAsBee = function() { this.work() }

function Engineer6 (mach) {
  console.log('Engineer constructor')
  WorkerBee6.apply(this)
  this.dept = "engineering";
  this.machine = mach || "";
}
extend(Engineer6, WorkerBee6);
Engineer6.prototype.work = function() {console.log("Engineer.work")}
Engineer6.prototype.workAsEngineer = function() { this.work() }

e6 = new Engineer6()
e6.work()

console.log(Employee6.prototype.isPrototypeOf(e6))
console.log(WorkerBee6.prototype.isPrototypeOf(e6))
console.log(e6)

Alt text

理解AnguarJS中的模板编译

在一开始学习AngularJS的过程中,模板的工作原理可能是最令人难解的问题之一。在其他的框架中,一般来说模板就是一个字符串,在这个字符串中使用一个特殊的表达方式可以嵌入一个传入的外部对象的数据,其本质上就是一个字符串的操作:输入字符串,输出字符串,然后通过.innerHTML()把所有搞出来的东西塞到DOM里面去。

很明显AngularJS的模板不是这样做的。它有双向数据绑定,可以完美的在视图中与最新的数据模型同步。它还会自动在ng-click上注册事件监听器,甚至可以将视图中输入控件中的数据同步回数据模型。太神奇了!

这个当然不是什么魔术,事实上将一个模板转换为一个带动态绑定和事件监听功能的动态视图,这中间的过程其实很简单。一旦搞明白了这个过程,对于AngularJS应用的理解也会上升一个台阶。

这里有一个模板

<div ng-app>
    <label>Name:</label>
    <input type="text" ng-model="yourName" placeholder="Enter a name here">
    <h1>Hello {{yourName}}!</h1>
</div>

如你所见,这是个最简单的AngularJS的应用页面。

反向控制(IoC)

第一个需要了解的概念是“反向控制”(IoC)。AngularJS不需要手工启动应用,而是假定你会遵循一定的规则在代码中放一些基本的设定。

例如,AngularJS会假定HTML代码中有一个带有ng-app属性的元素,表示你的应用的“根”节点。它表示AngularJS可以接管这个节点以及之下所有子节点。这一点可以保证AngularJS可以和其他的JavaScript框架共存。当AngularJS启动应用的时候,它会遍历DOM内的节点,并查找这个属性,这个在AngularJS术语中叫“指令”(Directive)。注意这个ng-app指令可以放在任何DOM节点上,包括<html><body>标签。

AngularJS还会假定其他的框架不会接触到它的“根”节点下面的所有内容,否则可能会导致未知行为,或者打破双向数据绑定和事件监听器。

深入模板

<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<h1>Hello {{yourName}}!</h1>

这一部分是模板。“根”节点下面的所有内容都会被视为模板,之后将会被编译。模板与数据模型($scope)和控制器一起构成了动态视图,用户可以在浏览器中看到并与之交互。本例中没有控制器和明确的数据模型,但AngularJS会在后台创建对应的内容。

所有的AngularJS都会有一个$rootScope,它会持有所有子scope的引用,它自身保存的数据也可以被所有的$scope共享。本例中,由于没有明确指定控制器,所以模板会被绑定到这个$rootScope上,并设置一个子$scope

在模板中,给输入控件绑定了一个模型(由ng-model指明的yourName),AngularJS会在这个数据控件上注册一个键盘事件监听器,并且将所有输入到这个控件中的内容自动保存到$rootScope.yourName,但我们并没有也并不需要声明这个变量。scope中的变量会被初始化为undefined,就像正常的JavaScript对象一样。

模板中还用到了$interpolate指令,表示为两对大花括号 {{ yourName }}。这个指令会创建一个$watcher,监听模型的变化,并在发生变化的时候将数据更新到视图上。

接下来看看如何将一个DOMElement转换成动态视图。

编译模板

AngularJS找到ng-app指令之后,它会启动并创建一个新的$rootScope,开始编译“根”节点(带ng-app的那个节点)下面所有的子节点。

接下来发生的事情分为两步,编译和链接(这里借用了编译原理中的术语,但本质上是一样的)。更深的介绍可以参考文档AngularJS documentation on the HTML Compiler

首先,Angular的$compile函数将传入的DOMElement作为输入。这一点与其它的框架很不一样,AngularJS会使用浏览器的API遍历整个DOM,而其它模板只是在做字符串替换。如果需要用字符串作为传入的模板,则先要用$angular.element函数将字符串转为DOMElement。这个函数实际上就是jQuery中的$()函数的。

$compile函数会遍历DOM,并查找“指令”(Directive),将找到的每个“指令“添加到一个列表中,整个DOM遍历完成后,再将列表中的”指令“按照“优先级”排序。之后,执行每个“指令”自己的compile函数,让“指令”有机会去修改DOM。每个指令的compile函数会返回一个“链接”函数,该函数会被拼接成一个完整的链接函数,并被返回。

接下来,AngularJS会执行返回的“链接”函数,对应的scope会被传入到这个执行过程中。这一步中,所有的子“链接”函数都会被执行,并绑定在同一个scope上,或依照“指令”的设定创建一个新的scope。所有的“链接”函数执行完毕后,每个“链接”函数都会返回一组DOMElement,这些DOMElement已经完成数据绑定和事件监听,AngularJS会将它们添加到父节点。

上述过程的伪代码可以表示如下

var $compile = ...; //注入到你的代码
var $rootScope = ...; //注入
var parent = ...; //编译过的模板内容会被添加到该DOMElement下
var template = ...; //我们的模板的DOMElement

var linkFn = $compile(template); //编译模板,返回“链接”函数

var element = linkFn(scope); //“链接”,返回处理好的DOMElement

parent.appendChild(element); //将处理好的DOMElement添加到父节点

总结

基本就是这些了。如你所见,AngularJS完全不同于其他的模板系统,它约定了一些规则,并基于这些规则做了一些假定,之后你就不必将精力耗费在启动代码上,而可以放在真正的应用上。这只是AngularJS的一部分,之后会继续讲讲$watch$digest如何保证视图刷新。

via src

在Android中使用OSGi框架(Knopflerfish)

OSGi是用Java实现的一个模块化服务平台。每个模块被称之为Bundle,可以提供服务,也可以在不重启OSGi框架的情况下被安装或卸载。Knopflerfish是一个完全开源的OSGi R4.2标准的实现。

Android能够无缝的集成现有的Java代码,尽管使用的是与现有java字节码格式不兼容的虚拟机Dalvik,但是它可以轻松的将现有的jar文件和类转换为Android使用的Dalvik字节码格式。由于OSGi框架自身和Bundle都只是普通的jar文件,所以他们都应该可以在Android上运行。事实上,大多数时候是没问题的。

注意:这里只是如何在Android中嵌入OSGi系列文章的第一部分

如果只是想让OSGi框架在Android上跑起来,那么只需要编译Knopflerfish的Android版本,复制到设备上,然后就可以通过命令行启动起来了(见上一篇文章这里)。

现在来看看如何将Knopflerfish和一系列Bundle嵌入到Android应用中,并且从应用中启动和管理OSGi框架和Bundle。

通过代码启动OSGi大概需要下面这几个步骤:

  1. 创建framework实例(通过framework factory)
  2. 初始化framework
  3. 设置initlevel,并启动/安装 bunldes
  4. 为所有的initlevel重复前述步骤
  5. 设置startlevel
  6. 启动framework

嵌入Framework

现在创建一个Android应用,包含一个Actviity。然后在app中引入framework.jar,这样就可以通过一个FrameworkFactory创建OSGi的framework实例了。

import org.knopflerfish.framework.FrameworkFactoryImpl;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
...
private Framework mFramework;
...
Dictionary fwprops = new Hashtable();
// add any framework properties to fwprops
FrameworkFactory ff = new FrameworkFactoryImpl();
mFramework = ff.newFramework(fwprops);
try {
    mFramework.init();
} catch (BundleException be) {
    // framework initialization failed
}

引入的jar文件不需要dex化,后面build的时候会自动完成这一步的。

Bundle文件dex化

现在bundle的jar文件可以被添加到应用中了,可以作为raw资源放在res/raw下面,也可以放在assets/bunldes。后面这种方式有一个优势:不需要被重命名,而且res资源的名字数量是有限的。

Bundle的jar文件需要被转换成dex格式,下面这个简单的脚本可以完成这件事:

dexify() {
    for f in $*; do
        tmpdir="`mktemp -d`"
        tmpfile="${tmpdir}/classes.dex"
        dx --dex --output=${tmpfile} ${f}
        aapt add ${f} ${tmpfile}
        rm -f ${tmpfile}
        rmdir ${tmpdir}
    done
}

然后就可以通过命令dexify assets/bundles/*将bundles转换为dex文件。如果是按照Knopflerfish的教程编译的Knopflerfish,那么不需要将这些bundle的jar文件dex化,但是必须从knopflerfish的framework.jar文件中去掉classes.dex。

安装、启动Bundles

下面这段代码可以帮助启动bundle,并设置initlevel/startlevel。

private void startBundle(String bundle) {
    Log.d(TAG, "starting bundle " + bundle);
    InputStream bs;
    try {
        bs = getAssets().open("bundles/" + bundle);
    } catch (IOException e) {
        Log.e(TAG, e.toString());
        return;
    }

    long bid = -1;
    Bundle[] bl = mFramework.getBundleContext().getBundles();
    for (int i = 0; bl != null && i < bl.length; i++) {
        if (bundle.equals(bl[i].getLocation())) {
            bid = bl[i].getBundleId();
        }
    }

    Bundle b = mFramework.getBundleContext().getBundle(bid);
    if (b == null) {
        Log.e(TAG, "can't start bundle " + bundle);
        return;
    }

    try {
        b.start(Bundle.START_ACTIVATION_POLICY);
        Log.d(TAG, "bundle " + b.getSymbolicName() + "/" + b.getBundleId() + "/"
                + b + " started");
    } catch (BundleException be) {
        Log.e(TAG, be.toString());
    }

    try {
        bs.close();
    } catch (IOException e) {
        Log.e(TAG, e.toString());
    }
}

private void installBundle(String bundle) {
    Log.d(TAG, "installing bundle " + bundle);
    InputStream bs;
    try {
        bs = getAssets().open("bundles/" + bundle);
    } catch (IOException e) {
        Log.e(TAG, e.toString());
        return;
    }

    try {
        mFramework.getBundleContext().installBundle(bundle, bs);
        Log.d(TAG, "bundle " + bundle + " installed");
    } catch (BundleException be) {
        Log.e(TAG, be.toString());
    }

    try {
        bs.close();
    } catch (IOException e) {
        Log.e(TAG, e.toString());
    }
}

private void setStartLevel(int startLevel) {
    ServiceReference sr = mFramework.getBundleContext()
        .getServiceReference(StartLevel.class.getName());
    if (sr != null) {
        StartLevel ss =
            (StartLevel)mFramework.getBundleContext().getService(sr);
        ss.setStartLevel(startLevel);
        mFramework.getBundleContext().ungetService(sr);
    } else {
        Log.e(TAG, "No start level service " + startLevel);
    }
}

private void setInitlevel(int level) {
    ServiceReference sr = mFramework.getBundleContext()
        .getServiceReference(StartLevel.class.getName());
    if (sr != null) {
        StartLevel ss =
            (StartLevel)mFramework.getBundleContext().getService(sr);
        ss.setInitialBundleStartLevel(level);
        mFramework.getBundleContext().ungetService(sr);
        Log.d(TAG, "initlevel " + level + " set");
    } else {
        Log.e(TAG, "No start level service " + level);
    }
}

现在可以安装并启动bundle了

setInitlevel(1);
installBundle("event_all-3.0.4.jar");
startBundle("event_all-3.0.4.jar");
// install/start other bundles...

setStartLevel(10);

try {
    mFramework.start();
} catch (BundleException be) {
    Log.e(TAG, be.toString());
    // framework start failed
}

Log.d(TAG, "OSGi framework running, state: " + mFramework.getState());

问题

如果你按照上文所述一步步做下来了,你可能会发现还是没法跑起来。由于framework的classloader是在运行期加载的bundle文件,Dalvik虚拟机会试图将优化过的dex类文件放到一个系统目录下面/data/dalvik-cache,但是没有root权限的普通应用程序是不能写入那儿的。

下回将如何解决这个问题。

via source

在Android中使用OSGi框架(Apache Felix)

本文描述了如何在Android中使用Apache Felix

Dalvik VM

Android允许开发者使用Java开发应用,但出于某些原因,代码实际是运行在名为Dalvik的一个针对移动设备平台的虚拟机上,而不是标准的Java虚拟机。Dalvik并不使用标准的Java字节码格式,而是使用Android SDK中的一个工具dx将由Java编译出来的类文件转换为另外一种类文件格式(.dex格式)。这个转换是在编译期完成的。

准备Bundles

虽然Felix从1.0.3开始内置了Android的支持,但是想要成功的让它跑起来还是需要费点力气。我们仍然需要安装Android SDK,并且PATH环境变量中包含Android SDK的工具目录<android_SDK_HOME>/tools

第一步: 每一个用到的Jar文件,无论是Felix库还是你自己写的Bundle,都需要包含对应的DEX。也就说,需要为jar文件创建对应的dex文件:

dx --dex --output=classes.dex JAR_file.jar

然后将这个dex文件加入到jar文件中:

aapt add JAR_file.jar classes.dex

第二步: 将处理过的jar文件传到模拟器(或真机)中:

adb push JAR_file.jar path_emulator/JAR_file.jar

第三步: 以演示代码为例,准备Felix的jar文件和Bundle的jar文件:

目录结构

osgi-android: /
\- bin
\- bundle
\- conf
\- felix.sh

准备Felix jar文件

export PATH=<path-to-android>/tools:$PATH
cd bin
dx --dex --output=classes.dex felix.jar
aapt add felix.jar classes.dex

准备bundle的jar文件

cd bundle
dx --dex --output=classes.dex     org.apache.felix.shell-1.0.0.jar
aapt add org.apache.felix.shell-1.0.0.jar classes.dex
dx --dex --output=classes.dex org.apache.felix.shell.tui-1.0.0.jar
aapt add org.apache.felix.shell.tui-1.0.0.jar classes.dex
dx --dex --output=classes.dex EnglishDictionary.jar
aapt add EnglishDictionary.jar classes.dex
dx --dex --output=classes.dex FrenchDictionary.jar
aapt add FrenchDictionary.jar classes.dex
dx --dex --output=classes.dex SpellChecker.jar
aapt add SpellChecker.jar classes.dex

复制到模拟器中

cd osgi-android
find * -type f -exec adb push {} /data/felix/{} \;

启动Felix

完成上面的步骤之后,现在可以准备在Android上启动Felix和Bundle了

adb shell
cd /data/felix
sh felix.sh

felix.sh是一个shel脚本,用于启动Felix main class。

/system/bin/dalvikvm -Xbootclasspath:/system/framework/core.jar \

-classpath bin/felix.jar org.apache.felix.main.Main

如果一切顺利,现在你应该能看到Felix的命令行shell了。输入help可以看到命令说明。

现在可以安装EnglishDictionary,FrenchDictionary和SpellChecker来试试看Felix是否工作正常。这里有几个Apache Felix的示例:Apache Felix 教程例子2Apache Felix 教程例子2bApache Felix 教程例子5

  • EnglishDictionary - 提供一个字典服务,支持下面几个词"welcome", "to", "the", "osgi", "tutorial"
  • FrenchDictionary - 提供一个字典服务,支持下面几个词"bienvenue", "au", "tutoriel", "osgi"
  • SpellChecker - 提供一个拼写检查服务,可以检查第一个英文此单的几个单词

在Felix Shell中启动Bundle

start file:bundle/EnglishDictionary.jar
start file:bundle/FrenchDictionary.jar
start file:bundle/SpellChecker.jar

嵌入Felix

Apache Felix也可以被集成到Android的应用中。只需要在Activity的onCreate中嵌入Felix,然后用上面的办法启动bundle即可。

下载

上面的演示代码在此下载

via source

Python on vim

[TOC]

modeline

You can use modeline in python source file to change Vim's setting. Vim will read top and bottom 3 lines comments and use the comment start with vim: as modeline setting. By do this, opener can use same setting to edit this file. This can avoid mixing of space and tab char in source code.

Add the following line to the top or bottom of your python source file.

#vim: ai ts=4 sts=4 et sw=4 ft=python

.vimrc

I am using following settings in my ~/.vimrc for python

" Automatic reloading of .vimrc
autocmd! bufwritepost .vimrc source %


" Better copy & paste
" When you want to paste large blocks of code into vim, press F2 before you
" paste. At the bottom you should see ``-- INSERT (paste) --``.

"" set pastetoggle=<F2>
"" set clipboard=unnamed


" Mouse and backspace
"set mouse=a  " on OSX press ALT and click
set bs=2     " make backspace behave like normal again


" Rebind <Leader> key
" I like to have it here becuase it is easier to reach than the default and
" it is next to ``m`` and ``n`` which I use for navigating between tabs.
let mapleader = ","


" Bind nohl
" Removes highlight of your last search
" ``<C>`` stands for ``CTRL`` and therefore ``<C-n>`` stands for ``CTRL+n``
noremap <C-n> :nohl<CR>
vnoremap <C-n> :nohl<CR>
inoremap <C-n> :nohl<CR>


" Quicksave command
"" noremap <C-Z> :update<CR>
"" vnoremap <C-Z> <C-C>:update<CR>
"" inoremap <C-Z> <C-O>:update<CR>


" Quick quit command
"" noremap <Leader>e :quit<CR>  " Quit current window
"" noremap <Leader>E :qa!<CR>   " Quit all windows


" bind Ctrl+<movement> keys to move around the windows, instead of using Ctrl+w + <movement>
" Every unnecessary keystroke that can be saved is good for your health :)
map <c-j> <c-w>j
map <c-k> <c-w>k
map <c-l> <c-w>l
map <c-h> <c-w>h


" easier moving between tabs
map <Leader>n <esc>:tabprevious<CR>
map <Leader>m <esc>:tabnext<CR>


" map sort function to a key
vnoremap <Leader>s :sort<CR>


" easier moving of code blocks
" Try to go into visual mode (v), thenselect several lines of code here and
" then press ``>`` several times.
"" vnoremap < <gv  " better indentation
"" vnoremap > >gv  " better indentation


" Show whitespace
" MUST be inserted BEFORE the colorscheme command
"" autocmd ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red
"" au InsertLeave * match ExtraWhitespace /\s\+$/


" Color scheme
" mkdir -p ~/.vim/colors && cd ~/.vim/colors
" wget -O wombat256mod.vim http://www.vim.org/scripts/download_script.php?src_id=13400
set t_Co=256
color wombat256mod


" Enable syntax highlighting
" You need to reload this file for the change to apply
filetype off
filetype plugin indent on
syntax on


" Showing line numbers and length
set number  " show line numbers
set tw=79   " width of document (used by gd)
set nowrap  " don't automatically wrap on load
set fo-=t   " don't automatically wrap text when typing
if version > 703
    set colorcolumn=80
endif
highlight ColorColumn ctermbg=233


" easier formatting of paragraphs
vmap Q gq
nmap Q gqap


" Useful settings
set history=700
set undolevels=700


" Real programmers don't use TABs but spaces
set tabstop=4
set softtabstop=4
set shiftwidth=4
set shiftround
set expandtab


" Make search case insensitive
set hlsearch
set incsearch
set ignorecase
set smartcase


" Disable stupid backup and swap files - they trigger too many events
" for file system watchers
set nobackup
set nowritebackup
set noswapfile


" Setup Pathogen to manage your plugins
" mkdir -p ~/.vim/autoload ~/.vim/bundle
" curl -so ~/.vim/autoload/pathogen.vim https://raw.github.com/tpope/vim-pathogen/HEAD/autoload/pathogen.vim
" Now you can install any plugin into a .vim/bundle/plugin-name/ folder
call pathogen#infect()


" ============================================================================
" Python IDE Setup
" ============================================================================


" Settings for vim-powerline
" cd ~/.vim/bundle
" git clone git://github.com/Lokaltog/vim-powerline.git
set laststatus=2


" Settings for ctrlp
" cd ~/.vim/bundle
" git clone https://github.com/kien/ctrlp.vim.git
let g:ctrlp_max_height = 30
set wildignore+=*.pyc
set wildignore+=*_build/*
set wildignore+=*/coverage/*


" Settings for jedi-vim
" sudo easy_install jedi
" cd ~/.vim/bundle
" git clone git://github.com/davidhalter/jedi-vim.git
let g:jedi#related_names_command = "<leader>z"
let g:jedi#popup_on_dot = 0
let g:jedi#popup_select_first = 0
map <Leader>b Oimport ipdb; ipdb.set_trace() # BREAKPOINT<C-c>

" Better navigating through omnicomplete option list
" See http://stackoverflow.com/questions/2170023/how-to-map-keys-for-popup-menu-in-vim
set completeopt=longest,menuone
function! OmniPopup(action)
    if pumvisible()
        if a:action == 'j'
            return "\<C-N>"
        elseif a:action == 'k'
            return "\<C-P>"
        endif
    endif
    return a:action
endfunction

inoremap <silent><C-j> <C-R>=OmniPopup('j')<CR>
inoremap <silent><C-k> <C-R>=OmniPopup('k')<CR>


" Python folding
" mkdir -p ~/.vim/ftplugin
" wget -O ~/.vim/ftplugin/python_editing.vim http://www.vim.org/scripts/download_script.php?src_id=5492
set nofoldenable

Use StackEdit to edit blog

This is the "Welcome" document of StackEdit, posted from StackEdit website.

Welcome to StackEdit!

Hello, I am your first Markdown document within StackEdit1. Don't delete me, I can be helpful. I can be recovered anyway in the Utils tab of the Settings dialog.


Documents

StackEdit stores your documents in your browser local storage, which means all your documents are automatically saved locally and are accessible offline.

NOTE: This also means that your documents are not shared between different browsers or computers and that clearing your browser's data may delete all of them!

Create a document

You can create a new document by clicking the button in the navigation bar. This will switch from the current document to the new one.

Switch to another document

You can list all your local documents and switch from one to another by clicking the button in the navigation bar.

Rename a document

You can rename the current document by clicking the document title in the navigation bar.

Delete a document

You can delete the current document by clicking the button in the navigation bar.

Save a document

You can save the current document to a file using the Save as... sub-menu.

NOTE: See Publish a document section for a description of the different outputs.


Synchronization

StackEdit can be combined with Google Drive and Dropbox to have your documents centralized in the Cloud. The synchronization mechanism will take care of uploading your modifications or downloading the latest version of your documents.

NOTE:

  • Full access to Google Drive or Dropbox is required to be able to import any document in StackEdit.
  • Imported documents are downloaded in your browser and are not transmitted to a server.
  • If you experience problems exporting documents to Google Drive, check and optionally disable browser extensions, such as Disconnect.

Import a document

You can import a document from the Cloud by going to the Google Drive or the Dropbox sub-menu and by clicking Import from.... Once imported, your document will be automatically synchronized with the Google Drive / Dropbox file.

Export a document

You can export any document by going to the Google Drive or the Dropbox sub-menu and by clicking Export to.... Even if your document is already synchronized with Google Drive or Dropbox, you can export it to a another location. StackEdit can synchronize one document with multiple locations.

Synchronize a document

Once your document is linked to a Google Drive or a Dropbox file, StackEdit will periodically (every 3 minutes) synchronize it by downloading/uploading any modification. Any conflict will be detected, and a local copy of your document will be created as a backup if necessary.

If you just have modified your document and you want to force the synchronization, click the button in the navigation bar.

NOTE: The button is disabled when:

  • you are offline,
  • or the document is not synchronized with any location,
  • or the document has not been modified since the last synchronization.

Manage document synchronization

Since one document can be synchronized with multiple locations, you can list and manage synchronized locations by clicking Manage synchronization in the menu. This will open a dialog box allowing you to add or remove synchronization links that are associated to your document.

NOTE: If you delete the file from Google Drive or from Dropbox, the document will no longer be synchronized with that location.


Publication

Once you are happy with your document, you can publish it on different websites directly from StackEdit. As for now, StackEdit can publish on Blogger, Dropbox, Gist, GitHub, Google Drive, Tumblr, WordPress and on any SSH server.

Publish a document

You can publish your document by going to the Publish on sub-menu and by choosing a website. In the dialog box, you can choose the publication format:

  • Markdown, to publish the Markdown text on a website that can interpret it (GitHub for instance),
  • HTML, to publish the document converted into HTML (on a blog for instance),
  • Template, to have a full control of the output.

NOTE: The default template is a simple webpage wrapping your document in HTML format. You can customize it in the Services tab of the Settings dialog.

Update a publication

After publishing, StackEdit will keep your document linked to that publish location so that you can update it easily. Once you have modified your document and you want to update your publication, click on the button in the navigation bar.

NOTE: The button is disabled when:

  • you are offline,
  • or the document has not been published anywhere.

Manage document publication

Since one document can be published on multiple locations, you can list and manage publish locations by clicking Manage publication in the menu. This will open a dialog box allowing you to remove publication links that are associated to your document.

NOTE: In some cases, if you remove the file from the website or the post from the blog, the document will no longer be published on that location.


Markdown Extra

StackEdit supports Markdown Extra, which extends Markdown syntax with some nice features.

NOTE: You can disable any Markdown Extra feature in the Extensions tab of the Settings dialog.

Tables

Markdown Extra has a special syntax for tables:

Item Value
Computer \$1600
Phone \$12
Pipe \$1

You can specify column alignment with one or two colons:

Item Value Qty
Computer \$1600 5
Phone \$12 12
Pipe \$1 234

Definition Lists

Markdown Extra has a special syntax for definition lists too:

Term 1
Term 2
Definition A
Definition B
Term 3

Definition C

Definition D

part of definition D

Fenced code blocks

GitHub's fenced code blocks are also supported with Prettify syntax highlighting:

// Foo var bar = 0;

NOTE: To use Highlight.js instead of Prettify, just configure the Markdown Extra extension in the Settings dialog.

Special Attributes

With Markdown Extra, you can specify class and id attributes on headers and fenced code blocks just like this:

Header example

{#my-id .my-class} var foo = bar;

Then you can create cross-references like this: beginning of the document.

Footnotes

You can create footnotes like this2.

SmartyPants

SmartyPants converts ASCII punctuation characters into "smart" typographic punctuation HTML entities. For example:

ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is an en-dash and --- is an em-dash – is an en-dash and — is an em-dash

Table of contents

You can insert a table of contents using the marker [TOC]:

[TOC]

MathJax

You can render LaTeX mathematical expressions using MathJax, as on math.stackexchange.com:

The Gamma function satisfying $\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N$ is via the Euler integral


\Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

NOTE: When exporting, make sure you include MathJax to render mathematical expression correctly. Your page/template should include something like:

<script type="text/javascript" src="https://stackedit.io/libs/MathJax/MathJax.js?config=TeX-AMS_HTML"></script>

NOTE: You can find more information:

  • about Markdown syntax here,
  • about Markdown Extra extension here,
  • about LaTeX mathematical expressions here,
  • about Prettify syntax highlighting here,
  • about Highlight.js syntax highlighting here.

Written with StackEdit.


  1. StackEdit is a free, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites. 

  2. Here is the text of the footnote

Accessing Facebook by LWP

Usually, we can get the content of a url by the following code in Perl

perl -MLWP::Simple -e 'getprint "http://www.google.com"';

But if you try to use it to access Facebook, like this

perl -MLWP::Simple -e 'getprint "http://www.facebook.com"';

you will get a page "Upadte your browser" instead of the real one. That's because Facebook doesn't like the default UserAgent of LWP or WWW::Curl.

The simplest way to solve it is to change the default LWP user agent string to your own string.

perl -e 'use LWP::Simple qw($ua get); $ua->agent("My agent/1.0"); print get "http://www.facebook.com";'