Electronic Joint Business

Solution for E-Business

javascript

用 vert.x 和 angular.js 构建基础 Axon 框架示例

在邮件列表上我常看到不断有人在问哪可以找到基础示例,最好用到了 vert.x 。由于本人对 Axon 和 vert.x 都略知一二,所以我想略尽薄力。这里我基于 vert.x 创建了个非常基础的 Axon 应用1。它用来维护个人的“待办事项”。这些事项可通过连接的网络浏览器间查阅,用户可以创建新的“待办事项”,也可以把某个事项标为“完成”。

我在浏览 Devoxx 站点时,正好学习了一个 javascript 前端框架 AngularJS 。这对 vert.x 技术栈来说是个很棒的补强。之前我一直使用 Bootstrap 来完成前端的工作,所以这个框架用起来很是顺手。图1-1 演示了本应用的最终效果。如果你想知道这个应用是怎么创建的,请继续阅读。

概述
我们创建的是个待办事项的演示。我们希望可以在网上创建待办事项。待办事项可被存储,当其他人连接到这个 Web 应用时也可以看到相同的待办事项。一旦待办事项被更新,或者将它们标为“完成”,这些变动会被推至其他连接的浏览器。图1-2 给出了示例的设计架构。你可以从 github 上检出这个示例。你可以查阅示例中的 readme 文件来运行本示例。

图1-2 中中包含了大量信息。首先客户端(在本例中是浏览器)通过 vert.x 事件总线与后端通讯。创建新事项或者或标记事项“已完成”都是在总线上发送给某个端点的一条消息。这些消息被侦听该端点的处理器( handler) 检出。接着,处理器通过 Axon 网关发出相应的命令(command) 让命令处理器(command handler)进行后续步骤,命令处理器使用 ToDo 聚合体 (aggregate) 来完成实际操作。Axon 用事件来改变状态。这些事件也为 ToDo 事件侦听器所使用,该组件会发送消息给 vert.x 事件总线以持久化待办事项,还会发送消息给客户端在侦听的主题。在客户端上,我们通过 AngularJS 控制器注册了个侦听器来侦听这些 vert.x 事件并更新数据模型。所有的更改会为视图自动拾取并更改。

听起来挺复杂的,下面我们逐一对某些片段进行讲解。

>>> 阅读全文

 

, , , , , ,

利用 ASP.NET MVC 4 开发 Web 应用 (一)

文章评价:
现在几乎所有的网站都不停地扩张,一旦它开始增长,你就会发现无论是编写、组织和维护一个站点都十分地困难。当我们往项目中添加新功能或添加新开发人员的时候,任何设计拙劣的大型的 Web 应用都可能会去控制。因此本文隐藏的设计思想是任何一个网站的设计,无论是对网页设计师(初级到中级)还是搜索引擎而言,其架构必须是简单、易于理解的。在这篇文章中,我想要设计一个用于维护个人联系方式的站点。任何一个大型社区都可以重用该程序及其附加功能和模块。因此,设计必须很容易适应未来的业务增长。

在这篇文章中,我将讨论如何创建和设计用户界面(UI)的方式,该方式将把 UI 将从业务逻辑分离,可以由独立的设计师/开发人员负责创建。我们将会用到的框架包括 ASP.NET MVC、Knockout、Jquery 和 Bootstrap。

在后续文章中,我们会更多地谈到有关数据库设计以及如何利用 SQL Server 2008 、Entity Framework 、Castle Windsor 依赖注入框架来实现商业逻辑。

分离关注点:主要目标
关键概念是如何剥离大部分或全部商业逻辑。商业逻辑不应和页面捆绑,否则在需要重用另一个页面里的逻辑就会难以处理。很多人会忍不住对代码进行复制粘贴。一旦这样做了,项目就变得难以维护。另一个重要概念是将业务逻辑从数据存取层剥离出来,由于我们将考虑使用 Entity Framework ,这应该不成为问题。我们可以轻松地将所有 EF 的文件放到另一个 Assembly 中,然后添加该 Assembly 的引用即可。顶层设计概念如下所示:

将这一概念贯彻到 Visual Studio 开发工具中,我们得到了最后的解决方案:

>>> 阅读全文

 

, , , , , , , ,

WebGL 教程(三)动起来!

欢迎来到Lesson 3!在这一课中我们将开始尝试让物体运动起来。本节课的内容是基于 NeHe 的 OpenGL 教程的第四课改写的。

Here’s what the lesson looks like when run on a browser that supports WebGL:

Click here and you’ll see the live WebGL version, if you’ve got a browser that supports it; here’s how to get one if you don’t.

More on how it all works below…

The usual warning: these lessons are targeted at people with a reasonable amount of programming knowledge, but no real experience in 3D graphics; the aim is to get you up and running, with a good understanding of what’s going on in the code, so that you can start producing your own 3D Web pages as quickly as possible. If you haven’t read the first and second tutorials already, you should probably do so before reading this one — here I will only explain the differences between the code for lesson 2 and the new code.

>>> 阅读全文

 

, , , ,

WebGL 教程 (二) 添加颜色

文章评价:
英文原文
欢迎来到 WebGL 教程的第二篇。在本文中我们将会研究一下如何给场景中的物体上色。本文的内容是基于 NeHe 的 OpenGL 教程的第三章改写的。

如果你的浏览器支持 WebGL 的话,你可以点击这里 来浏览线上版本。

事先声明,本系列的教程是面向那些具备相应编程知识,但没有实际 3D 图形经验的人群;目标是让学习者创建并运行代码,并且明白代码其中的含义,从而可以快速地创建自己的 3D Web 页面。如果你还没有阅读第一课,请先看一下第一课的内容吧。因为本课中我只会讲解那些与第一课中不同的新知识。

和上一篇文章一样,本教程应该有不少错误和混淆,如果你发现了哪些毗漏,请不吝留言斧正。

有两种方法可以获得上面实例的代码:在浏览线上版本时在页面中选择“查看源代码”;你也可以从 Github 中克隆源代码的压缩包。

>>> 阅读全文

 

, , , , , ,

WebGL 教程 (一)三角形和正方形

文章评价:

欢迎来到我的第一篇 WebGL 教程 !本文借鉴了 NeHe 的 OpenGL 教程第二章,它是学习游戏开发的 3D 图形中好文章。本文将带你学习如何在网页上绘制三角形和正方形。也许这不够酷,但很好地阐述了如何用 WebGL 编程;如果你明白了这部分是如何工作,那么剩下的就相当简单了。

我们先看一下本文在支持 WebGL 的浏览器中的运行效果:

要继续本教程,你需要安装支持 WebGL 的浏览器,比如 IE11、 FireFox、Chrome 或者 Safari 等。 下面我们将继续了解如何用 WebGL 绘制三角形和正方形。

注意: 本教程是面向那些具有一定的编程知识,但没有体验过 3D 图形编程的人群,目标是让你从零开始,透过运行代码深入了解其工作原理,以便能尽快地开始编写自己的 3D 网页。我在自学 WebGL 的时候顺便写了这些教程,难免会有毗漏,欢迎大家斧正。如果您发现哪里存在谬误,请麻烦给我留言并加以指正。

>>> 阅读全文

 

, , , , , , ,

用 WebGL 和 HTML5 创建三维分子查看器

简介
许多人对 OpenGL 相当熟悉,但是对使用 WebGL 就有一点陌生。然而,许多 OpenGL 的概念和格式都可以应用到 WebGL 上。本文打算在 WebGL 编程方面做出一些尝试,可以让你了解 WebGL 的一些难易程度。象我这种略知 OpenGL 而对 WebGL 完全没概念的新手,这个例子也只花了我不到 6 个小时。现在有许多用 Java applet 或 C++ 程序写好的查看器。本项目尝试用 Javascript 和 WebGL 来完成同样的工作。让人惊讶的是,这一点也不难。

背景
WebGL 是一种 3D 绘图标准,这种绘图技术标准允许把 JavaScript 和 OpenGL ES 2.0 结合在一起,通过增加 OpenGL ES 2.0 的一个 JavaScript 绑定,WebGL 可以为 HTML5 Canvas 提供硬件 3D 加速渲染,这样 Web 开发人员就可以借助系统显卡来在浏览器里更流畅地展示 3D 场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL 技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计 3D 网页游戏等等。

如何使用本文的代码

将本文的代码安装到 Web 服务器上,你可以选用本地服务器,也可以选择远程服务器。打开 index.html ,选择 Choose File 按钮,从本机选一个 PDB 文件,然后你就可以看到分子了。

这是一些快捷键:

>>> 阅读全文

 

, , , , , ,

Rails3 技术手册 (一) 概述

英文原文
Ruby on Rails 是用于快速简洁地开发 Web 应用框架。它被 37signal 公司的项目管理工具 Basecamp 中剥离出来,其作者 David Heinemeier Hansson 率先用 Rails 着手解决手边的现实问题。 我们会发现用 Rails 进行开发时充满乐趣, Rails 负担起了开发中那些无聊的琐事,因此开发者能专注于所要解决的问题本身。

你如果不理解这些开发中的琐事,可以看一下下面的例子:编写过 Web 应用的开发者都不可避免地会用到字典结构、缓存、数据持久化、模板系统、测试框架、路由等等组件。Rails 自身就带了这堆让应用上线运行所必需的组件,而且作者在编写它们时充分考虑了彼此的适配问题。以往希望模板语言能和复杂的 ORM 层能在一起工作的麻烦现在再也没有了。一切将是如此简单。

现在开发者无需再为数据库等的组件编写大量繁琐的 XML 配置。Rails 基于约定而非配置,配置因而精简到最少。假如 Animal 类就是关联到数据库的 animals 数据表,为什么还要让开发者花时间写配置文件来链接它们? 这一切就让 Rails 来帮你吧。

简而言之,Rails 让 Web 开发充满乐趣。

架构
Rails 应用开发是基于模型-视图-控制器模式的 (MVC) 的。想了解 Rails 是如何使用 MVC 的, 最简单的办法是透过了解 Rails 请求的生命周期。如图1-1所示:

>>> 阅读全文

 

, , , , , , ,

在 Javascript 中用 Promise 进行异步编程

对 Javascript 这类单线程的语言来说,避免同步执行对创建能即时响应高效执行的应用来说是十分必要的。Windows Library for Javascript (即 WinJS) 提供了一种称为 promise 的机制,大大简化了异步编程。

JavaScript 用异步编程的必然
JavaScript 是单线程的语言。这意味着调用耗时的过程时,其他所有过程的执行会被阻塞,直到该过程结束。假设有个类库用于查询某个 web service,我们这样来调用:

var result = myWebService.get("http://www.contoso.com");

在结果返回前的这段时间,其所在环境内的所有执行都被暂停了。这意味着UI界面失去了响应,动画效果暂停了,应用中的其他代码也无法被执行。

解决这个问题的方法是尽可能的避免同步执行。通常我们会遵循一些基本的模式。这些模式可以归纳外将某项功能的执行推迟到未来某个时点。基于什么样的模式取决于使用何种 API。例如,处理 HTML DOM 的时候,大多数的元素都有一些事件。订阅这些事件允许以异步的形式执行代码。假如上面的 web service 库遵循这样的模式,代码看起来是这样的:

myWebService.addEventListener(‘completed’, function(result) { /* do something */});
myWebService.get("http://www.contoso.com");

现在在这段 javascript 里调用 get 方法就不会阻塞执行了。(这里假定从 Web Service 获得结果的实际工作发生在 Javascript runtime 之外)。实际上,这儿只是初始化了该工作,当它结束时,“结束”事件被触发,处理器函数则被调用。当你有多个函数需要在费时操作结束时才被运行时,这个模式就十分有用。

>>> 阅读全文

 

, , , ,

Windows Runtime中的异步模式

文章评价: 原文
Windows Runtime 提供了一套一致的模式来创建和管理异步操作。

下面的内容介绍了 Windows Runtime 中的异步编程(asynchronous programming),例子包括了 Javascript,C#,Visual Basic 和 Visual C++。

异步编程的好处
由于从 Windows Runtime 的异步编程模型中获益,Metro风格应用总是能对用户的输入及时做出响应。你可以运行费时的任务而不需阻塞 UI 线程,你可以在稍后才获得任务返回的结果。你可以取消任务,也可以当任务在后台运行时获得进度通知。你无需显示地管理线程或者直接和底层的并发(concurrence)实现打交道。

Windows Runtime 的许多特性以异步函数的形式呈现,比如 MediaCapture 和 StorageFile。作为约定,异步函数的名称以“Async”结尾,表明其执行可能发生在调用返回之后。

在 Metro 风格应用中使用异步API,代码可用一致的方法使用非阻塞调用。如果在用户的 API 里实现了异步模式,调用者就可以理解并按预知的形式使用你的代码。Windows Runtime 以贴近每种语言自身的方式提供了异步模式。见下表:

>>> 阅读全文

 

, , , , , , ,

IE 10 开发者指南之索引数据库

Internet Explorer 10 支持索引数据库API(IndexedDB), 来存储结构化数据。不同于cookie 和 DOM 存储,IndexedDB 提供了组合(group)、 遍历、搜索、过滤 Javascript 对象的功能。

IndexedDB API 由多个对象组成,每个都为特定的任务而设计:

  • 数据被保存在对象存储(object stores)里,它是 Javascript 对象的集合,这些对象的属性包含独立的数值。
  • Javascript 对象被保存在对象存储中,也称为记录 (record),每个记录都有一个共同的属性 key path。该属性的值被称为键值(key value) 或者键(key)。键值 (Key values)用于标识对象存储里的单个记录。
  • 索引按照某一属性的值来组织对象,它返回键值的集合,通过键值可以从原来的对象存储中获得独立记录。
  • 游标 (cursor) 是值的集合。当索引定义了游标时,游标代表着索引返回的键值集合。当一个对象存储定义了游标的时候,游标代表着保存在游标中的记录集合。
  • Key Range为索引或对象存储中的记录集定义了值的范围。 Key Rang可以用来过滤游标的结果。
  • 一个请求意味着对数据库中对象采取的不同动作。例如打开数据库会产生一个请求对象,你可以为这个请求对象定义事件处理器来对请求的结果产生反应。
  • 事务管理着操作的上下文,并用于维护数据库活动的一致性。例如,你可以版本变更的事务上下文中创建对象存储。如果事务被放弃,在此事务中的所有操作都被取消。

索引数据库 API 规范定义了两套 API: 同步 API 和异步 API。IE10 支持异步 API。因此,对数据库的操作不会马上被执行,而是让返回对象的操作在后台执行。因此,IndexedDB 是事件驱动 API,你首先创建请求,然后定义事件处理器来响应请求的成功或者失败的结果。

很重要的一点,这些特性在 IE10 和使用 HTML 的 Metro 风格应用是一致的。

使用请求来打开数据库
因为索引数据库 API 规范还在发展中,大部分厂商都为自己的 IndexedDB 实现添加了一个前缀,比方说webkitIndexedDB 或者 mozIndexedDB。对 IE10 来说,则是 msIndexedDB。为了得到正确的结果,我们用一小段检测代码来访问 IndexedDB API ,如例子所示:

>>> 阅读全文

 

, , , , , , ,

ArcGIS Silverlight 客户端指南 (一)

ArcGIS之所以比较普及,原因之一就是它能紧跟计算机发展的趋势,推出适合各种平台的API比如JavaScript API,Java API, Flex API,Silverlight API, 后二者是现在RIA的主流代表,开发工具、设计工具一应俱全,有人也许会问,在Flex API和Silverlight API之间该如何取舍,其实“一法通万法通”,在不同的API之间切换,并不是什么太难的问题。

[b]准备工具[/b]
假设你已经安装了Visual Studio 2010 (最新版本嘛,至少以后几年你翻我的文章还不会过期)的任何一个版本,建议安装Blend设计工具,它可以用来设计非常酷的Silverlight画面。
Silverlight 需要另外安装工程插件,即Silverlight 4的Visual studio工程插件,可以在[url=”http://www.microsoft.com/downloads/details.aspx?familyid=BF5AB940-C011-4BD1-AD98-DA671E491009&displaylang=en”]这里[/url]下载。

最后我们需要ArcGIS SL 客户端API,你需要自己注册一个ESRI Global账户,登陆后,就可以在[url=http://www.esri.com/products/index.html#developer_tools_panel”]这里[/url]下载。

[b]Hello,World[/b]

安装好上面的Silverlight 4插件和ArcGIS Client,就可以创建一个Silverlight Application工程了,并添加ESRI.ArcGIS.Client(版本1.2)的引用。

>>> 阅读全文

 

, , , , ,

用MinGW64编译及使用Spider Monkey引擎

文章评价:

SpiderMonkey 是用 C 语言编写的的 JavaScript 引擎,它支持 JS1.4 和 ECMAScript-262 规范, 可以分析、编译和执行脚本,并根据 JS 数据类型和对象的需要进行内存分配及释放操作。利用该引擎可以让你的应用程序具有解释 JavaScript 脚本的能力,目前已有许多项目都采用了 SpiderMonkey 引擎,比如 MongoDB, K-3D 等等。本文我们会简要介绍 SpiderMonkey 的编译方法,以及如何集成 SpiderMonkey 到您自己的项目中。

编译 Spider Monkey
Spider Moneky 依赖于 NSPR 软件包,NSPR 的全称是 Netscape Portable Runtime,它提供线程、线程池、垃圾收集、mutex 和 condition variable 等等诸多特性,并且独立于平台,是相当有用的一套 API。换句话说,NSPR 是屏蔽不同操作系统的差异性的一套API。

关于 NSPR,另文介绍。

在 Mozilla 站点上可以下载 NSPR 软件包,最新的版本是4.8.6,将它解压到 MSYS 的 $HOME 目录下。

>>> 阅读全文

 

, , , , ,

用Visual Studio开发Firefox扩展 (一) 简介与准备

Firefox插件简介
Extension就是一个zip包,里面有规定的一些必要文件。这个可以下载几个插件看一下他们的目录结构就好了。我下载了一个 QuickNote,看了一下,主要包括一个install.rdf(安装包相关信息和文件资源路径等配置)、chrome.manifest(设置各种 XUL文件路径以及其他的一些资源文件路径)、chrome文件夹(里面放着插件的所有源文件),不过chrome文件夹里面的文件我还没有过多研究。

Plugin好像是另外一码事了,在Mozilla Developer Center的Plugin开发介绍文中有这么一句:Plugins are different from extensions, which modify or enhance the functionality of the browser itself. Plugins are also different from search plugins, which plug additional search engines in the search bar. (插件与扩展不同,它更改或者改善了浏览器自身的功能。插件与搜索插件又不同,搜索插件在搜索工具栏上添加了额外的搜索引擎。)

我的理解,插件可以让你在FF中使用自己的View来展示、用自己编写的二进制文件来做某些事情,不仅仅是利用FF自身支持的一些特性和功能。而扩展相对来说就不需要这些东西,只利用FF的开发API就好了。Firefox插件,也叫Mozilla插件,它是一段程序。通常,界面使用XUL语言(XML的一个子集)或者XUL内嵌HTML编写,而程序逻辑使用ECMAScript (Standard ECMA-262)(俗称JavaScript)编写。

XUL可以是独立的窗体,可以是对话,也可以是overlay。Overlay可以用于修改已经构建好的界面。下面是一段示例的XUL代码:

<overlay id="main-overlay" xmlns:html="http://www.w3.org/1999/xhtml"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <menu id="file_menu">
  <menuitem name="Example One"/>
  <menuitem name="Example Two"/>
  <menuitem name="Example Three"/>
  <menuitem name="Example Four" position="1"/>
 </menu>
</overlay>

而JavaScript可以用来定义程序的行为。CSS可以用于自定义样式,而XPCOM可以用于调用Firefox提供的接口。另外,现在有一些JavaScript Library,可以直接使用而降低代码难度。

>>> 阅读全文

 

, , , , , , ,

面向文档的数据库 CouchDB (四) 视图

视图是 CouchDB 中用来查询和呈现文档的。完成视图的定义之后,视图的运行由专门的视图服务器来完成。 CouchDB 中默认的视图定义语言是 JavaScript 。 CouchDB 中的视图运行使用的是 MapReduce 编程模型。每个视图的定义中至少需要提供 Map 方法,Reduce 方法是可选的。

视图的 MapReduce
Map 方法的参数只有一个,就是当前的文档对象。 Map 方法的实现需要根据文档对象的内容,确定是否要输出结果。如果需要输出的话,可以通过emit来完成。emit方法有两个参数,分别是key和 value,分别表示输出结果的键和值。使用什么样的键和值应该根据视图的实际需要来确定。当希望对文档的某个字段进行排序和过滤操作的时候,应该把该字段作为键(key)或是键的一部分;value的值可以提供给 Reduce 方法使用,也可能会出现在最终的结果中。可以作为键的不仅是简单数据类型,也可以是任意的 JSON 对象。比如emit([doc.title, doc.price], doc)中,使用数组作为键。

通过 Map 方法输出的结果称为中间结果。中间结果可以通过 Reduce 方法来进一步做聚集操作。聚集操作是对结果中键(key)相同的数据集合来进行的。 Reduce 方法的输入不仅是 Map 方法输出的中间结果,也可以是上一次 Reduce 方法的结果,后面这种情况称为 reReduce 。 Reduce 方法的参数有三个:key、values和rereduce,分别表示键、值和是否是 reReduce 。由于 reReduce 情况的存在,Reduce 方法一般需要处理两种情况:

  • * 传入的参数rereduce的值为false:这表明 Reduce 方法的输入是 Map 方法输出的中间结果。参数key的值是一个数组,对应于中间结果中的每条记录。该数组的每个元素都是一个包含两个元素的数组,第一个元素是在 Map 方法中通过emit输出的键(key),第二个元素是记录所在的文档 ID 。参数values的值是一个数组,对应于 Map 方法中通过emit输出的值(value)。
  • * 传入的参数rereduce的值为true:这表明 Reduce 方法的输入是上次 Reduce 方法的输出。参数key的值为null。参数values的值是一个数组,对应于上次 Reduce 方法的输出结果。

下面通过一个实例来说明视图 Map 和 Reduce 的用法。该视图要解决的问题是对图书简介中出现的字符进行计数,这也是一个经典的 MapReduce 编程模型的实例。代码清单 12中给出了该视图的定义。

清单 12. 对图书简介中的字符计数的视图定义

>>> 阅读全文

 

, , , ,

面向文档的数据库 CouchDB (三) 开发Web应用

CouchDB 不仅是一个数据库服务器,同时也是一个应用服务器。在前面对 REST API 的介绍中,说明了如何把 CouchDB 作为一个数据库服务器来使用。下面将介绍如何将 Web 应用运行在 CouchDB 上。

由于 CouchDB 的 REST API 使用 JSON 作为展现形式,因此使用 CouchDB 的 Web 应用只需要编写浏览器端的代码就可以使用 JavaScript 与 CouchDB 进行交互;而 CouchDB 所支持的附件功能,又使得浏览器端的 HTML、JavaScript 和 CSS 代码可以直接存放在 CouchDB 中。这样 CouchDB 中不但保存了 Web 应用的数据,也保存了 Web 应用的逻辑。也就是说,只需要 CouchDB 就可以构建一个完整的 Web 应用运行环境。

在 CouchDB 中,一个 Web 应用对应的是一个设计文档。这个 Web 应用可以操作 CouchDB 中保存的文档型数据。当需要创建新的 Web 应用的时候,只需要创建新的设计文档即可。 CouchDB 使得 Web 应用的部署和管理变得非常简单,只需要通过 REST API 管理设计文档即可。从更大的角度来说,CouchDB 有可能创造一种新的 Web 应用开发模式。在这种模式中,CouchDB 中保存的文档型数据可以为每个应用开发者所使用,开发者在数据之上创建满足各种需求的 Web 应用。

本文中将以一个具体的小型网站作为实例来介绍使用 CouchDB 开发 Web 应用中的细节。该网站是一个类似“豆瓣”的用户点评网站。在该网站中用户可以对图书进行编辑和评价。

使用 CouchApp
CouchApp 是一个开发使用 CouchDB 的 Web 应用的小型框架。它的主要功能是可以把一个文件系统的目录转换成 CouchDB 中的一个设计文档。在开发的时候,可以按照一般 Web 应用的结构来组织文件系统,当需要测试和部署的时候,只需要一条命令就可以把该目录保存到 CouchDB 中。 CouchApp 目前有 Python 和 Ruby 两种语言的版本,本文中使用的是 Python 版本。由于目前 CouchApp 正在开发中,所以最好是从源代码安装。代码清单 7 中给出了 CouchApp 的安装脚本。

>>> 阅读全文

 

, , , ,

Previous Posts