├── .gitignore
├── README.md
├── component.json
├── fis-conf.js
├── package.json
├── page
├── be
│ ├── spring.vm
│ └── variables.vm
├── doc
│ ├── _standard.md
│ ├── binding.vm
│ ├── layout.vm
│ ├── rewrite.vm
│ ├── standard.vm
│ └── widget.vm
├── examples
│ ├── data.js
│ ├── data.vm
│ ├── form.js
│ ├── form.vm
│ ├── pagination.vm
│ ├── partial.vm
│ ├── partial
│ │ └── form.vm
│ └── spa
│ │ ├── page1.vm
│ │ ├── page2.vm
│ │ ├── page3.vm
│ │ └── spa.js
├── html
│ └── index.html
├── index.vm
├── jsp
│ ├── binding.jsp
│ ├── index.jsp
│ ├── layout.jsp
│ ├── rewrite.jsp
│ └── widget.jsp
├── layout
│ ├── 2columns-with-left-sidebar.jsp
│ ├── 2columns-with-left-sidebar.vm
│ ├── 2columns-with-right-sidebar.jsp
│ ├── 2columns-with-right-sidebar.vm
│ ├── 3columns.jsp
│ ├── 3columns.vm
│ ├── ajax.vm
│ ├── frame.jsp
│ ├── frame.vm
│ ├── front.jsp
│ ├── front.vm
│ ├── smart.vm
│ └── spa.vm
└── velocity
│ ├── index.vm
│ ├── jello.vm
│ ├── tools.vm
│ └── variables.vm
├── server.conf
├── static
├── favicon.ico
├── js
│ ├── README.md
│ ├── esl.js
│ ├── mod-amd.js
│ └── require.js
├── libs
│ ├── README.md
│ ├── _alert.tmpl
│ ├── _confirm.tmpl
│ ├── _modal.tmpl
│ ├── alert.js
│ ├── confirm.js
│ ├── modal.js
│ ├── scrollspy.js
│ └── validator.js
└── scss
│ ├── _callout.scss
│ ├── _highlight.scss
│ ├── _modal.scss
│ ├── global.scss
│ └── normalize.css
├── test
├── data
│ ├── ajax.jsp
│ ├── data.json
│ └── saveform.json
├── delay.jsp
├── mock.jsp
├── page.json
├── page.jsp
└── page
│ ├── doc
│ ├── layout.json
│ └── standard.json
│ ├── examples.json
│ ├── examples
│ ├── data.json
│ ├── pagination.json
│ └── partial.json
│ └── index.json
└── widget
├── footer
├── footer.jsp
└── footer.vm
├── header
├── header.jsp
└── header.vm
└── sidebarmenus
├── sidebarmenus.js
├── sidebarmenus.jsp
├── sidebarmenus.scss
└── sidebarmenus.vm
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | /.idea
4 |
5 | /components
6 |
7 | /node_modules
8 |
9 | /output
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jello-demo
2 | ==========
3 |
4 | Jello demo & doc, you can [preview this online](http://oak.baidu.com/jello-demo/).
5 |
6 | ## 如何使用
7 |
8 | 1. 安装 jello
9 |
10 | ```
11 | npm install -g jello
12 | ```
13 | 2. 安装插件
14 |
15 | ```
16 | npm install -g fis-parser-marked
17 | npm install -g fis-parser-utc
18 | npm install -g fis-parser-sass
19 | npm install -g fis-packager-depscombine
20 | npm install -g fis-postprocessor-amd
21 | ```
22 | 3. git clone 下来此仓库
23 |
24 | ```
25 | git clone https://github.com/2betop/jello-demo.git
26 | ```
27 | 4. 进入 jello-demo 目录后 安装 components
28 |
29 | ```
30 | cd jello-demo
31 | jello install
32 | ```
33 | 5. 进入当前目录后发布代码
34 |
35 | ```
36 | jello release
37 | jello server start
38 | ```
39 | 6. 自动打开浏览器预览页面
40 |
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "protocol": "github",
3 | "gihub": {
4 | "author": "fis-components"
5 | },
6 | "dependencies": [
7 | "jquery@^1.9.1",
8 | "bootstrap@3",
9 | "compass-mixins",
10 | "font-awesome",
11 | "jquery-ui",
12 | "jquery-validation"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/fis-conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | // --------------------------------
4 | // 支持 amd 设置
5 | // --------------------------------
6 | fis.config.set('modules.postprocessor.vm', 'amd');
7 | fis.config.set('modules.postprocessor.js', 'amd');
8 | fis.config.set('modules.postprocessor.jsp', 'amd');
9 | fis.config.set('settings.postprocessor.amd', {
10 |
11 | packages: [
12 |
13 | // 用来存放 libs 库
14 | {
15 | name: 'libs',
16 | location: 'static/libs/',
17 | main: 'index'
18 | }
19 | ]
20 | });
21 |
22 | // --------------------------------
23 | // sass/scss 配置
24 | // --------------------------------
25 |
26 | fis.config.set('modules.parser.sass', 'node-sass');
27 | fis.config.set('modules.parser.scss', 'node-sass');
28 | // 设置 sass 的 include_paths 便于组件引入
29 | fis.config.set('settings.parser.node-sass.include_paths', [
30 | './static/scss',
31 | './components/compass-mixins'
32 | ]);
33 |
34 | // 使用 depscombine 是因为,在配置 pack 的时候,命中的文件其依赖也会打包进来。
35 | fis.config.set('modules.packager', 'depscombine');
36 |
37 | fis.config.set('pack', {
38 |
39 | // css
40 | 'pkg/frame.css': ['page/layout/frame.vm'], // 因为依赖会被打包,所以这个规则会把 frame.vm 依赖的 css 打包在一起。
41 |
42 | // js
43 | // 依赖也会自动打包进来。
44 | 'pkg/boot.js': ['static/js/require.js', 'components/jquery/jquery.js', 'components/bootstrap/js/bootstrap.js'],
45 | 'pkg/app.js': ['page/examples/form.js']
46 | });
47 |
48 |
49 | fis.config.set('roadmap.path', [
50 |
51 | {
52 | reg: /^\/components\/.*\.(?:less|md)$/i,
53 | release: false
54 | },
55 |
56 | {
57 | reg: 'doc/**.md',
58 | release: false
59 | },
60 |
61 | {
62 | reg: /^\/static\/libs\/(.*\.js)$/i,
63 | isMod: true,
64 | release: '${statics}/${namespace}/libs/$1'
65 | }
66 | ].concat(fis.config.get('roadmap.path', [])));
67 |
68 | // markdown 支持,因为需要写文档,不用 markdown 真是不习惯
69 | // npm install -g fis-parser-marked
70 | // use the `fis-parser-marked` plugin to parse *.md file
71 | fis.config.set('modules.parser.md', 'marked');
72 | // *.md will be released as *.html
73 | fis.config.set('roadmap.ext.md', 'html');
74 |
75 | // js 模板支持
76 | fis.config.set('modules.parser.tmpl', 'utc');
77 | // fis.config.set('roadmap.ext.tmpl', 'js');
78 | //
79 |
80 | // 当通过 jello server start --webapp jello-demo
81 | // 设置其他 context path 的时候用。
82 | fis.config.set('roadmap.domain', '/jello-demo');
83 |
84 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jello-demo",
3 | "version": "0.1.0",
4 | "description": "jello-demo",
5 | "keywords": [
6 | "jello-demo"
7 | ],
8 | "license": "MIT"
9 | }
10 |
--------------------------------------------------------------------------------
/page/be/spring.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="spring 整合")
2 | #extends("/page/layout/2columns-with-left-sidebar.vm")
3 |
4 | #block("sidebar")
5 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(4).children")
6 | #end
7 |
8 | #block("content")
9 |
10 |
后端一般都是使用 spring 来开发,所以这里给出 spring 集成方式,其他运行模式请参考。关于 spring 整合的 demo 可以看这里 。
11 |
12 | 对于后端来说,只需关心前端输出的模板文件、静态资源和 map json文件。
13 |
14 | 默认的输出路径是:
15 |
16 |
17 | 模板文件: /WEB-INF/views/XX.vm
18 | 静态资源: /static/XX
19 | map json 文件:/WEB-INF/config/xxx-map.json
20 |
21 |
22 | 为了让 velocity 能正常渲染模板,需要设置模板目录,以及将 fis 提供的自定义 diretives 启动。
23 | 配置内容如下:
24 |
25 | <bean id= "velocityConfigurer" class= "org.springframework.web.servlet.view.velocity.VelocityConfigurer" >
26 | <property name= "resourceLoaderPath" value= "/WEB-INF/views/" />
27 | <property name= "velocityProperties" >
28 | <props>
29 | <prop key= "input.encoding" > utf-8</prop>
30 | <prop key= "output.encoding" > utf-8</prop>
31 | <!--启用 fis 提供的自定义 diretives 启动-->
32 | <prop key= "userdirective" > com.baidu.fis.velocity.directive.Html, com.baidu.fis.velocity.directive.Head, com.baidu.fis.velocity.directive.Body, com.baidu.fis.velocity.directive.Require, com.baidu.fis.velocity.directive.Script, com.baidu.fis.velocity.directive.Style, com.baidu.fis.velocity.directive.Uri, com.baidu.fis.velocity.directive.Widget, com.baidu.fis.velocity.directive.Block, com.baidu.fis.velocity.directive.Extends</prop>
33 | </props>
34 | </property>
35 | </bean>
36 |
37 |
38 | 为了让 fis 自定义的 directive 能够正常读取 map.json 文件,需要添加一个 bean 初始化一下。
39 |
40 | <!--初始 fis 配置-->
41 | <bean id= "fisInit" class= "com.baidu.fis.velocity.spring.FisBean" />
42 |
43 |
44 | 默认 map json 文件是从 /WEB-INF/config 文件夹下读取的,如果有修改存放地址,则需要添加一个 fis.properties 文件到 /WEB-INF/ 目录。
45 | 内容如下:
46 |
47 | # 相对与 WEB-APP 根目录。
48 | mapDir = /velocity/config
49 |
50 |
51 | fis 框架代码可以在此下载 。所有代码开源在 github 上。
52 |
53 |
54 | View Resolver 推荐配置
55 |
56 | <bean id= "viewResolver" class= "org.springframework.web.servlet.view.velocity.VelocityViewResolver" >
57 | <property name= "cache" value= "true" />
58 | <property name= "prefix" value= "" />
59 | <property name= "suffix" value= ".vm" />
60 | <property name= "cacheUnresolved" value= "false" />
61 | <property name= "exposeSpringMacroHelpers" value= "true" />
62 | <property name= "contentType" value= "text/html;charset=UTF-8" />
63 | <property name= "requestContextAttribute" value= "request" />
64 | <property name= "exposeSessionAttributes" value= "true" />
65 | <property name= "attributesMap" >
66 | <map>
67 | <entry key= "esc" ><bean class= "org.apache.velocity.tools.generic.EscapeTool" /></entry>
68 | <entry key= "render" ><bean class= "org.apache.velocity.tools.generic.RenderTool" /></entry>
69 | <entry key= "link" ><bean class= "org.apache.velocity.tools.generic.LinkTool" /></entry>
70 | <entry key= "context" ><bean class= "org.apache.velocity.tools.generic.ContextTool" /></entry>
71 |
72 | <entry key= "jello" ><bean class= "com.baidu.fis.velocity.tools.JelloTool" /> </entry>
73 | </map>
74 | </property>
75 | </bean>
76 |
77 |
78 |
79 |
注意
80 |
cacheUnresolved
一定要设置成false,否则会影响前端分开部署。
81 |
另外这里只启用了部分 velocity tools, 其他 tools 请根据自己需求配置。
82 |
83 |
84 | #end## end of body
85 |
86 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
87 | #require("./spring.vm")
88 | #end
89 |
--------------------------------------------------------------------------------
/page/be/variables.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="Spring 全局变量")
2 | #extends("/page/layout/2columns-with-left-sidebar.vm")
3 |
4 | #block("sidebar")
5 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(4).children")
6 | #end
7 |
8 | #block("content")
9 |
10 | 在 jello 模拟环境下,是有很些全局变量可以使用,整入到后端后不一定有,如何让 spring 开发这些变量?
11 |
12 | ${esc.d}request
13 | 在 spring 的 xml 配置中, viewResolver
下设置<property name= "requestContextAttribute" value= "request" />
14 |
15 | ${esc.d}session
16 | 在 spring 的 xml 配置中, viewResolver
下设置<property name= "exposeSessionAttributes" value= "true" />
17 |
18 | ${esc.d}rc
19 |
20 | 在 spring 的 xml 配置中, viewResolver
下设置<property name= "exposeSpringMacroHelpers" value= "true" />
21 |
22 | 设置了这个后,$!{esc.d}rc 指向一个 RequestContext(名为springBindRequestContext)对象,通过他可以处理表单和验证错误信息,另外利用$!{esc.d}{rc.getMessage("user.name")}读取/WEB-INF/classes/messages.properties本地化信息
23 |
24 | 批量加全局变量
25 | 在 controller 往 ModelMap 可以添加变量,但是如何让所有的 controller 都加上某些全局变量?
26 |
27 | 可以让所有的 controller 都基于某个基类, 然后在基类上,设置 ModelAttribute 变量即可。
28 |
29 | public abstract class Base {
30 | @Autowired
31 | @Qualifier ( "appConfig" )
32 | protected AppConfig appConfig ;
33 |
34 | @ModelAttribute ( "previewServer" )
35 | public String getPreviewServer () {
36 | return appConfig . getImagePreivewUrl ();
37 | }
38 | }
39 |
40 |
41 | 以上的例子,给所有 vm 都添加了 ${esc.d}previewServer 变量。
42 |
43 | #end## end of body
44 |
45 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
46 | #require("./variables.vm")
47 | #end
48 |
--------------------------------------------------------------------------------
/page/doc/_standard.md:
--------------------------------------------------------------------------------
1 | 在 jello 中开发项目需要遵循一定的目录规范。
2 |
3 | 一个完整的项目包括两部分
4 | - 前端部分:包含图片、样式、脚本、模板等一列前端资源。
5 | - 后端部分:各类 java 资源 jar 配置项文件等。
6 |
7 | jello 的开发模式是让前端同学主要负责前端部分,编写 velocity 模板文件、js、css等,脱离后端环境直接在 jello 的环境里面运行。
8 | 后端同学主要负责后端数据的获取和页面渲染逻辑,合并前端编译产出,完成整个项目的开发。
9 |
10 | 这样的好处是只要制定好数据格式便可以并行开发。
11 |
12 |
13 | ## 前端目录
14 |
15 | ```
16 | ├── fis-conf.js
17 | ├── page
18 | │ ├── doc
19 | │ ├── examples
20 | │ ├── index.vm
21 | │ └── layout
22 | ├── server.conf
23 | ├── static
24 | │ ├── favicon.ico
25 | │ ├── js
26 | │ ├── libs
27 | │ └── scss
28 | ├── test
29 | │ ├── page
30 | │ ├── page.json
31 | │ └── page.jsp
32 | └── widget
33 | ├── footer
34 | ├── header
35 | └── sidebarmenus
36 | ```
37 |
38 | ### 说明
39 |
40 | * `fis-conf.js` 用来设置 fis 编译配置。
41 | * `page` 用来存放各类页面级模板文件(.vm), 可以直接在jello 环境下预览。
42 | - `layout` 骨架 vm
43 | * `static` 用来存放各类静态资源,如:JS、CSS、image、swf...
44 | * `test` 用来存放各类测试数据
45 | * `widget` 用来存放页面小片段,方便其他页面引用。
46 |
47 | ## 产出目录
48 |
49 | ```
50 | ├── WEB-INF
51 | │ ├── config
52 | │ │ └── map.json
53 | │ ├── views
54 | │ │ ├── page
55 | │ │ └── widget
56 | ├── static
57 | └── test
58 | ```
59 |
60 | 将 jello 前端模块编译产出目录如上
61 |
62 | * `WEB-INF/config/map.json` 静态资源表,便于后端定位静态资源。
63 | * `WEB-INF/views` velocity 模板文件
64 | * `static` 静态资源,如果静态资源部署到了cdn 服务器上,后端环境则不需要此文件夹
65 | * `test` 用于本地环境调试,后端程序无需关注。
66 |
67 |
68 |
--------------------------------------------------------------------------------
/page/doc/binding.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="数据绑定")
2 | #set($title="数据绑定")
3 |
4 | #extends("/page/layout/2columns-with-left-sidebar.vm")
5 |
6 | #block("sidebar")
7 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(0).children")
8 | #end
9 |
10 | #block("content")
11 | jello 前端项目,只负责模板文件的编写,不负责数据的获取,但是为了让页面能够正常预览,需要提供一种机制,让模板自动关联数据,这样只要前期制定了数据格式,前端完全是可以后端独立开发的。
12 |
13 | 那么,我们其实只用关心数据格式,而不用关心数据的真实性。我们完全可以通过 json 文件指定静态数据即可。
14 |
15 | 如何指定?
16 |
17 | page 目录下的每个模板页面都会自动绑定上 test 目录下同名的 json 数据。
18 |
19 | test/page/index.json
20 |
21 | {
22 | "title" : "This is title." ,
23 | "subtitle" : "This is subtitle."
24 | }
25 |
26 |
27 | page/index.vm
28 | <h1> $ title </h1>
29 | <h2> $ subtitle </h2>
30 |
31 |
32 | 那么页面预览输出的结果是
33 |
34 | <h1> This is title.</h1>
35 | <h2> This is subtitle.</h2>
36 |
37 |
38 | 如何解决公用数据问题?
39 | 页面多了,自然有公用的数据,如果你的数据都是通过 ajax 页面获取,那还好,通过 rewrite 转发就能每次获取同样的数据,如果是模板数据,则需要每次都不听的拷贝。
40 |
41 | 这样确实太麻烦了,如是,我们提供了这么一种机制。如果预览的页面地址是 /page/A/B/C.vm, 这个文件的 json 绑定,可以是多个,顺序为:
42 |
43 |
44 | /test/page.json
45 | /test/page/A.json
46 | /test/page/A/B.json
47 | /test/page/A/B/C.json
48 |
49 |
50 | 多个文件的 json 值会叠加进来。
51 |
52 | 如何动态绑定数据
53 |
54 | json 绑定的是静态数据,如果需要绑定动态的数据,可以通过 jsp 方式绑定,绑定规则是一样的,且绑定时机在 json 绑定之后,可以针对动态数据,在 jsp 中补充。
55 |
56 | <%
57 | request. setAttribute(" title" , " Welcome to jello." );
58 | % >
59 |
60 |
61 |
62 |
63 |
注意
64 |
65 | 在 jsp 中,任何输出都是没有意义的,你可以输出,但是输出没有任何作用,只能给 context 添加数据。
66 |
67 |
68 | #end
69 |
70 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
71 | #require("./binding.vm")
72 | #end
73 |
--------------------------------------------------------------------------------
/page/doc/layout.vm:
--------------------------------------------------------------------------------
1 | #set($layout = "#uri('/page/layout/2columns-with-left-sidebar.vm')")
2 | #set($param = $request.getParameter("layout"))
3 |
4 | #if ($param.equals("right"))
5 | #set($layout = "#uri('/page/layout/2columns-with-right-sidebar.vm')")
6 | #end
7 |
8 | #if ($param.equals("both"))
9 | #set($layout = "#uri('page/layout/3columns.vm')")
10 | #end
11 |
12 | #extends($layout)
13 |
14 | #block("sidebarSecondary")
15 | ## 注意只里面的 with:$sidebar 意思是将当前 sidebar 变量对象中的所有属性作为 widget 里面的局部变量。
16 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "with:$sidebar")
17 | #end
18 |
19 | #block("sidebar")
20 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(0).children")
21 | #end
22 |
23 | #block("content")
24 |
25 |
模板继承思想来源于 smarty , 可以用来更好的复用代码。
26 |
27 |
28 |
29 |
如果之前没有接触过模板继承,请先一定要看下这个说明。
30 |
31 |
32 |
33 |
示例
34 |
35 |
36 |
先点击示例看效果
37 |
请切换下面这三个按钮看效果,同样的内容在不同的骨架下的效果。
38 |
43 |
44 |
45 |
没错,模板继承可以很方便的来定义具有容器功能的模板。子模板可以选择直接填充此类骨架类模板。
46 |
47 |
说明
48 |
49 |
layout.vm
50 |
<!DOCTYPE html>
51 | # html ()
52 |
53 | # head ()
54 | <meta charset="utf-8"/>
55 | <meta content="" name="description">
56 | <meta http-equiv="X-UA-Compatible" content="IE=edge">
57 | <title>Demo</title>
58 | # end
59 |
60 | # body ()
61 | <div id="wrapper">
62 | # block ( "body_content" )
63 | This is body.
64 | # end
65 | </div>
66 | # end
67 | # end
68 |
69 |
70 |
以上示例通过${esc.h}block()
定义了一个区域body_content
,那么在子模板中,就可以像以下方式填充它
71 |
72 |
# extends ( "layout.vm" )
73 | # block ( "body_content" )
74 | <h1>Hello Demo</h1>
75 | # end
76 | # end
77 |
78 |
79 |
80 |
说明
81 |
以上示例,只需关心${esc.h}block()
和${esc.h}extends()
标签,其他标签可以忽略
82 |
83 |
84 |
覆盖模式
85 |
86 |
默认,在子模板中同名 ${esc.h}block()
将会覆盖掉父模板中的同名 ${esc.h}block()
,有时候,我们希望是扩充,而不是生硬的覆盖。
87 |
88 |
新版的 jello 扩充了一个新的 directive
${esc.h}parent()
通过它可以用来引用被覆盖的block
89 |
90 |
91 |
${esc.h}extends("layout.vm")
92 | ${esc.h}block("body_content")
93 | ${esc.h}parent()
94 | <h1>Hello Demo</h1>
95 | ${esc.h}end
96 | ${esc.h}end
97 |
98 |
99 |
100 | 这样 body_content 的输出结果就是这样的。
101 |
102 |
This is body.
103 |
104 | <h1>Hello Demo</h1>
105 |
106 |
107 |
局部变量
108 |
109 |
请直接查看 widget 中的局部变量用法,用法一样。传送门
110 |
111 | #end
112 |
113 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
114 | #require("./layout.vm")
115 | #end
116 |
--------------------------------------------------------------------------------
/page/doc/rewrite.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="页面预览")
2 | #set($title="页面模拟")
3 |
4 | #extends("/page/layout/2columns-with-left-sidebar.vm")
5 |
6 | #block("sidebar")
7 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(0).children")
8 | #end
9 |
10 | #block("content")
11 | 在 page 目录下面的所有 vm 模板文件,只会有两类,一种是骨架模板,而另一种是入口模板。在 jello 环境下,所有入口模板都是可以直接预览的。他的预览规则为 /page/{{vm 路径}}
。
12 |
13 | 比如: page 目录下面的 index.vm 文件,则可以通过 /page/index
(.vm 可以省略)路径预览。
14 |
15 |
16 |
namespace 项目
17 |
如果当前项目设置了 namespace
,则预览地址得加上 namespace
作为前缀。
18 |
假如 namespace
为 home
,预览地址则为:/home/page/index
19 |
20 |
21 | ##
22 | ##
预览 HTML/JSP
23 | ##
jello 除了能预览 vm 页面,一些简单的 html、jsp 页面,也可以像预览 vm 页面一样预览,不同的是,不会自动关联上模拟数据 。
24 | ##
示例
25 | ##
简单 html 页面 ,简单的 jsp 页面
26 | ##
27 |
28 | 页面转发
29 | 这里有个限制就是路径总是以 /page
打头, 且模板路径必须与访问路径一致。这肯定是不能接受的。
30 |
31 | 针对这个问题,我们的解决方法是,提供rewrite
机制。
32 |
33 | 在根目录下面新建一个 server.conf
文件,通过配置此文件来实现页面跳转。
34 |
35 | # 首页
36 | rewrite \/$ /page/index
37 |
38 | #用户查看页面
39 | rewrite \/user/view/(\d*)$ /page/user/view?id=$1
40 |
41 | # home 页面跳转首页
42 | redirect \/home$ /page/index
43 |
44 |
45 | server.conf 中支持两种指令。
46 |
47 | rewrite
页面跳转,但是不改变浏览器地址
48 | redirect
页面跳转,同时改变浏览器地址
49 |
50 |
51 | 语法说明
52 | {{指定}} {{匹配正则}} {{目标地址}}
53 | server.conf 中,不是 rewrite
或者 redirect
打头的都被认为是注释。目标地址支持 $数字 来替换捕获结果。
54 |
55 | jsp 页面转发
56 |
57 | 在 test 目录下面的 .jsp 文件,也是可以在直接在 jello 环境下预览的,如 test 目录下面的 file.jsp 文件可以通过 /test/file.jsp
路径预览。
58 |
59 | 如果再配合rewrite
, 在 jello 环境下就可以模拟任何后端输出。
60 |
61 | rewrite \/testpage$ /test/mock.jsp
62 |
63 |
64 | <%@ page contentType = "text/html;charset=UTF-8" language = "java" %>
65 | <%
66 | response . getWriter (). write ( "hello world" );
67 | %>
68 |
69 |
70 | 示例
71 |
72 | json 页面模拟
73 |
74 | 关于json 页面,其实可以用 jsp 页面模拟,如示例 ,也可以直接在预览 json 文件,只要此文件放在 test 目录下面。如示例 。注意查看 jello-demo
项目下面的 test 目录和 server.conf 文件内容。
75 | #end
76 |
77 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
78 | #require("./rewrite.vm")
79 | #end
80 |
--------------------------------------------------------------------------------
/page/doc/standard.vm:
--------------------------------------------------------------------------------
1 | #extends("/page/layout/3columns.vm")
2 |
3 | #block("sidebar")
4 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(0).children")
5 | #end
6 |
7 | #block("sidebarSecondary")
8 | ## 注意只里面的 with:$sidebar 意思是将当前 sidebar 变量对象中的所有属性作为 widget 里面的局部变量。
9 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "with:$sidebar")
10 | #end
11 |
12 | #block("content")
13 | ## 内嵌 markdown 文件,这是编译期做得事情。
14 |
15 |
16 |
17 | #end
18 |
19 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
20 | #require("./standard.vm")
21 | #end
22 |
--------------------------------------------------------------------------------
/page/doc/widget.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="Widget")
2 | #set($title="Widget")
3 |
4 | #extends("/page/layout/2columns-with-left-sidebar.vm")
5 |
6 | #block("sidebar")
7 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(0).children")
8 | #end
9 |
10 | #block("content")
11 | 页面中比较常用的小的部分,可以抽离成widget ,比如:登陆框、头部菜单、边栏菜单等等
12 |
13 | 抽离成小模板后,可以通过这样的方式引入页面的任意位置。
14 |
15 |
16 |
\#widget("widget/sidebarmenus/sidebarmenus.vm")
17 |
18 |
19 | 局部变量
20 |
21 | 除了模板复用外,有时候变量也需要复用,在通过 \#widget()
引入模板的时候,可以通过以下两种方式指定变量
22 |
23 |
24 | with
将指定变量下面的所有属性做为 widget 中的局部变量使用
25 | var
设置 widget 中局部变量
26 |
27 |
28 | # widget ( "widget/sidebarmenus/sidebarmenus.vm" "with:${esc.d}sidebar" "var:literal=字面量" "var:variable=${esc.d}sidebar" )
29 |
30 |
31 |
32 |
注意
33 |
对于复杂的数据格式,通过var
可能不能直接创建,但是可以先借助\#set()
先设置后,然后再设置给var
34 |
35 |
# set ($ numbers = [ 1 , 2 , 3 ]) ;
36 |
37 | ${esc.h}${esc.h} 这样在 sidebarmenus.vm 模板里面,就可以使用 list 变量,而这个变量的值为数组 [1, 2, 3]
38 | # widget ( "widget/sidebarmenus/sidebarmenus.vm" "var:list=${esc.d}numbers" )
39 |
40 |
41 |
42 | 复杂用法
43 | 有时候 widget 是有填充需求的,实际上widget中也可以定义区块\#block()
, 引用的时候使用\#extends()
替代\#widget()
,效果相同,但是具备划分区域,使用者具有填充功能。
44 | #end
45 |
46 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
47 | #require("./widget.vm")
48 |
49 | #end
50 |
--------------------------------------------------------------------------------
/page/examples/data.js:
--------------------------------------------------------------------------------
1 | require('bootstrap');
2 | var $ = require('jquery');
3 | var alert = require('libs/alert');
4 |
5 | var app = module.exports = function(opt) {
6 |
7 | // from velocity data
8 | var vm = opt.vm;
9 | $(vm.btn).on('click', function() {
10 | alert('' + JSON.stringify(vm.data, null, 4) + ' ');
11 | });
12 |
13 |
14 | var json = opt.json;
15 | $(json.btn).click(function() {
16 | $(json.btn).button('loading');
17 |
18 | $.ajax(json.remote)
19 | .then(function(response) {
20 | alert('返回结果为:
' + JSON.stringify(response, null, 4) + ' ');
21 | })
22 | .fail(function() {
23 | alert('加载失败', 'danger');
24 | })
25 | .always(function() {
26 | $(json.btn).button('reset');
27 | });
28 | });
29 |
30 |
31 | var jsp = opt.jsp;
32 | $(jsp.btn).click(function() {
33 | $(jsp.btn).button('loading');
34 |
35 | $.ajax(jsp.remote)
36 | .then(function(response) {
37 | alert('返回结果为:
' + JSON.stringify(response, null, 4) + ' ');
38 | })
39 | .fail(function() {
40 | alert('加载失败', 'danger');
41 | })
42 | .always(function() {
43 | $(jsp.btn).button('reset');
44 | });
45 | });
46 |
47 | };
--------------------------------------------------------------------------------
/page/examples/data.vm:
--------------------------------------------------------------------------------
1 | #extends("/page/layout/2columns-with-left-sidebar.vm")
2 |
3 | #block("sidebar")
4 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
5 | #end
6 |
7 | #block("content")
8 |
9 | Js 数据来源,主要有两种
10 |
11 | 来源于模板变量
12 | 来源于异步 ajax 请求
13 |
14 |
15 | 关于数据模拟,请查看此处文档说明
16 |
17 | 示例
18 |
19 |
20 |
21 |
页面模板数据
22 |
23 | 显示模板数据
24 |
25 |
26 |
27 |
28 |
ajax 异步数据 - 通过 json 文件模拟
29 |
30 | 显示异步数据
31 |
32 |
33 |
34 |
35 |
ajax 异步数据 - 通过 jsp 文件模拟
36 |
37 | 显示异步数据
38 |
39 |
40 |
41 |
42 | #script()
43 | require(['./data'], function(app) {
44 | app({
45 | vm: {
46 | btn: '#fromVm',
47 |
48 | // \$user 变量数据来源与 /test/page/examples/data.json 模拟。
49 | data: $jello.jsonEncode($user)
50 | },
51 |
52 | json: {
53 | btn: '#fromJson',
54 | remote: '$!{request.contextPath}/json'
55 | },
56 |
57 | jsp: {
58 | btn: '#fromJsp',
59 | remote: '$!{request.contextPath}/jspdata'
60 | }
61 | });
62 | });
63 | #end
64 |
65 |
66 | #end## end of body
67 |
68 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
69 | #require("./data.vm")
70 | #end
71 |
--------------------------------------------------------------------------------
/page/examples/form.js:
--------------------------------------------------------------------------------
1 | // 因为设置了 amd.packager 所以,可以直接通过 libs 引用 /widget/libs 下面的库
2 | require('libs/validator');
3 | var $ = require('jquery');
4 |
5 | module.exports = function(opt) {
6 | var theform = $(opt.selector);
7 | var btn = theform.find(':submit');
8 | var action = theform.attr('action');
9 |
10 | theform.validate({
11 | submitHandler: function() {
12 | // 已经通过了验证
13 | btn.button('loading');
14 |
15 | $
16 | .ajax(action, {
17 | method: 'POST',
18 | data: theform.serialize()
19 | })
20 | .then(function(response) {
21 | alert(response.message);
22 | btn.button('reset');
23 | });
24 | }
25 | });
26 | };
--------------------------------------------------------------------------------
/page/examples/form.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="基本表单验证")
2 | #extends("/page/layout/2columns-with-left-sidebar.vm")
3 |
4 | #block("sidebar")
5 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
6 | #end
7 |
8 | #block("content")
9 |
10 | 此 demo 演示了,如何快捷方便的使用第三方库。
11 |
12 |
35 |
36 | #script()
37 | require(['./form'], function(app) {
38 | app({
39 | selector: '#nodeForm'
40 | });
41 | });
42 | #end
43 |
44 |
45 | #end## end of body
46 |
47 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
48 | #require("./form.vm")
49 | #end
50 |
--------------------------------------------------------------------------------
/page/examples/pagination.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="普通列表页面")
2 | #set($title="普通列表页面")
3 | #extends("/page/layout/2columns-with-left-sidebar.vm")
4 |
5 | #block("sidebar")
6 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
7 | #end
8 |
9 | #block("content")
10 |
11 |
26 |
27 |
28 |
查询结果
29 |
30 |
46 |
47 | #if ($result.isEmpty())
48 |
没有结果
49 | #else
50 |
51 | #foreach( $item in $result )
52 | $esc.html($item)
53 | #end
54 |
55 | #end
56 |
57 |
58 | #end## end of body
59 |
60 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
61 | #require("./pagination.vm")
62 | #end
63 |
--------------------------------------------------------------------------------
/page/examples/partial.vm:
--------------------------------------------------------------------------------
1 | #extends("/page/layout/2columns-with-left-sidebar.vm")
2 |
3 | #block("sidebar")
4 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
5 | #end
6 |
7 | #block("content")
8 |
9 | 当用 ajax 去请求一个页面的时候,往往只需要 html 片段、css、JS 部分。
10 |
11 | 示例
12 |
13 |
14 |
21 |
22 |
23 | #script()
24 | require(['jquery', 'libs/modal'], function($, modal) {
25 | $('#remoteBtn').on('click', function() {
26 | modal($('#remoteBtn').attr('href'));
27 | return false;
28 | });
29 | });
30 | #end
31 |
32 |
33 | #end## end of body
34 |
35 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
36 | #require("./partial.vm")
37 | #end
38 |
--------------------------------------------------------------------------------
/page/examples/partial/form.vm:
--------------------------------------------------------------------------------
1 | ## 如果页面是来自与 ajax 则使用 ajax 骨架
2 | ## 否则使用2栏布局
3 | #set($layout = "#uri('/page/layout/2columns-with-left-sidebar.vm')")
4 | #set($pageTitle="正常打开,会输出所有内容")
5 | #if ($request.getHeader("X-Requested-With").equals("XMLHttpRequest"))
6 | #set($layout = "#uri('/page/layout/ajax.vm')")
7 | #set($pageTitle="在 ajax 请求中只会显示一部分")
8 | #end
9 |
10 | #extends($layout)
11 |
12 |
13 | #block("content")
14 |
37 | #end## end of body
38 |
39 |
40 | #script()
41 | /* fis async 用 `fis async` 告诉编译器,这个 require 以异步方式加载,而不是当程序入口*/
42 | require(['../form'], function(app) {
43 | app({
44 | selector: '#nodeForm'
45 | });
46 | });
47 | #end##endscript
48 |
49 | #require("./form.vm")
50 | #end
51 |
--------------------------------------------------------------------------------
/page/examples/spa/page1.vm:
--------------------------------------------------------------------------------
1 | #set($title = "单页示例")
2 | #set($pageTitle = "单页示例")
3 | #extends("/page/layout/smart.vm")
4 |
5 | #block("sidebar")
6 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
7 | #end
8 |
9 | #block("content")
10 |
15 |
16 | 单页内容一
17 |
18 |
19 |
注意此处内容切换是不会刷新页面的。
20 |
请打开网络面板观察请求。
21 |
22 | #end
23 |
24 | #script()
25 | // fis async
26 | require(['./spa'], function(app) {
27 | app('#pager');
28 | });
29 | #end
30 |
31 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
32 | #require("./page1.vm")
33 | #end
34 |
--------------------------------------------------------------------------------
/page/examples/spa/page2.vm:
--------------------------------------------------------------------------------
1 | #set($title = "单页示例")
2 | #set($pageTitle = "单页示例")
3 | #extends("/page/layout/smart.vm")
4 |
5 | #block("sidebar")
6 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
7 | #end
8 |
9 | #block("content")
10 |
15 |
16 | 单页内容二
17 |
18 |
19 |
注意此处内容切换是不会刷新页面的。
20 |
请打开网络面板观察请求。
21 |
22 | #end
23 |
24 | #script()
25 | // fis async
26 | require(['./spa'], function(app) {
27 | app('#pager');
28 | });
29 | #end
30 |
31 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
32 | #require("./page2.vm")
33 | #end
34 |
--------------------------------------------------------------------------------
/page/examples/spa/page3.vm:
--------------------------------------------------------------------------------
1 | #set($title = "单页示例")
2 | #set($pageTitle = "单页示例")
3 | #extends("/page/layout/smart.vm")
4 |
5 | #block("sidebar")
6 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(1).children")
7 | #end
8 |
9 | #block("content")
10 |
15 |
16 | 单页内容三
17 |
18 |
19 |
注意此处内容切换是不会刷新页面的。
20 |
请打开网络面板观察请求。
21 |
22 | #end
23 |
24 | #script()
25 | // fis async
26 | require(['./spa'], function(app) {
27 | app('#pager');
28 | });
29 | #end
30 |
31 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
32 | #require("./page3.vm")
33 | #end
34 |
--------------------------------------------------------------------------------
/page/examples/spa/spa.js:
--------------------------------------------------------------------------------
1 | var $ = require('jquery');
2 |
3 | var app = module.exports = function(nav) {
4 | var $nav = $(nav);
5 | var $container = $('#content');
6 |
7 | $nav.off('click.spa').on('click.spa', 'a', function() {
8 | var $this = $(this);
9 | var href = $this.attr('href');
10 |
11 | $
12 | .ajax(href)
13 | .then(function(response) {
14 | // 我不确认是否都支持。
15 | history.replaceState && history.replaceState({}, document.title, href);
16 | $container.html(response);
17 | });
18 | return false;
19 | });
20 | };
--------------------------------------------------------------------------------
/page/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 简单的 html 页面
6 |
7 |
8 | 简单的 html 页面
9 | 除了可以预览 vm 页面外,还可以预览简单 html 页面,简单的活动页面可以用 html.
10 |
11 |
--------------------------------------------------------------------------------
/page/index.vm:
--------------------------------------------------------------------------------
1 | #extends("/page/layout/front.vm")
2 |
3 |
4 | #block("content")
5 |
6 |
7 |
8 |
9 |
Jello ['dʒeləu]
10 |
针对服务端为 JAVA + Velocity/jsp 的前端集成解决方案。
11 |
为优化前端开发效率而生,提供前后端开发分离、自动性能优化、模块化开发机制等功能。
12 |
查看更多 »
13 |
14 |
15 |
16 |
17 |
18 |
19 |
前后端分离
20 |
基于 velocity /jsp 模板,实现数据与页面分离
21 |
开发分离,制定数据格式便能脱离后端独立开发。基于 jello 轻松模拟各类数据。
22 |
23 |
24 |
自动性能优化
25 |
基于配置,自动完成资源压缩,合并,优化工作。
26 |
收集 css 和 js,统一于页头和页底输出,优化性能。
27 |
28 |
29 |
模块化开发
30 |
提供 html、css、js 模块化机制,包括 widget 组件化与 js amd 加载机制,让内容更好的拆分与复用。
31 |
可以随意拆分,无需考虑性能问题。
32 |
33 |
34 |
35 |
36 |
37 |
模板继承 & widget 机制
38 |
提供更好的骨架 (layout ) 机制。
39 |
公用部分抽离成 widget 便于多页面间复用。
40 |
41 |
42 |
简化环境安装
43 |
内嵌 j2ee 开发服务器,你无需再折腾 j2ee 环境搭建。直接通过 jello server start 就能开起服务,预览页面。
44 |
45 |
46 |
轻松接入 spring
47 |
提供标准 maven 示例程序 ,拷贝几条配置便能工作。
48 |
49 |
50 |
51 |
52 | #end## end of body
53 |
54 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
55 | #require("./index.vm")
56 | #end
57 |
--------------------------------------------------------------------------------
/page/jsp/binding.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %>
2 | <%@ taglib uri="/fis" prefix="fis"%>
3 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | jello 前端项目,只负责模板文件的编写,不负责数据的获取,但是为了让页面能够正常预览,需要提供一种机制,让模板自动关联数据,这样只要前期制定了数据格式,前端完全是可以后端独立开发的。
16 |
17 | 那么,我们其实只用关心数据格式,而不用关心数据的真实性。我们完全可以通过 json 文件指定静态数据即可。
18 |
19 | 如何指定?
20 |
21 | page 目录下的每个模板页面都会自动绑定上 test 目录下同名的 json 数据。
22 |
23 | test/page/index.json
24 |
25 | {
26 | "title" : "This is title." ,
27 | "subtitle" : "This is subtitle."
28 | }
29 |
30 |
31 | page/index.jsp
32 | <h1> \${ title } </h1>
33 | <h2> \${ subtitle } </h2>
34 |
35 |
36 | 那么页面预览输出的结果是
37 |
38 | <h1> This is title.</h1>
39 | <h2> This is subtitle.</h2>
40 |
41 |
42 | 如何解决公用数据问题?
43 | 页面多了,自然有公用的数据,如果你的数据都是通过 ajax 页面获取,那还好,通过 rewrite 转发就能每次获取同样的数据,如果是模板数据,则需要每次都不听的拷贝。
44 |
45 | 这样确实太麻烦了,如是,我们提供了这么一种机制。如果预览的页面地址是 /page/A/B/C.jsp, 这个文件的 json 绑定,可以是多个,顺序为:
46 |
47 |
48 | /test/page.json
49 | /test/page/A.json
50 | /test/page/A/B.json
51 | /test/page/A/B/C.json
52 |
53 |
54 | 多个文件的 json 值会叠加进来。
55 |
56 | 如何动态绑定数据
57 |
58 | json 绑定的是静态数据,如果需要绑定动态的数据,可以通过 jsp 方式绑定,绑定规则是一样的,且绑定时机在 json 绑定之后,可以针对动态数据,在 jsp 中补充。
59 |
60 | <%
61 | request. setAttribute(" title" , " Welcome to jello." );
62 | % >
63 |
64 |
65 |
66 |
67 |
注意
68 |
69 | 在 jsp 中,任何输出都是没有意义的,你可以输出,但是输出没有任何作用,只能给 context 添加数据。
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/page/jsp/index.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %>
2 | <%@ taglib uri="/fis" prefix="fis"%>
3 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 新版的 Jello 已经完全采用 jsp 作为模板来开发。完全兼容之前云龙老大开发的 jsp 版本 。
15 |
16 | 请先阅读 jello 中目录规范 ,虽然文中的提及的模板是velocity,但是同时适用与 jsp
17 |
18 | jello 为了打通前后端,采用 jsp tag 的方式扩充了自定义的标签,使用前,请先在头部加入。
19 |
20 | <%@ taglib uri =" /fis" prefix =" fis" %>
21 |
22 |
23 |
注意
24 |
虽然这里 prefix
值可以修改,但是千万别改,因为还有一系列编译工具,只识别 fis 打头的便签。
25 |
26 |
27 | 扩展标签
28 |
29 | fis:require
30 | 用来引入资源,不管是 JS 资源还是 CSS 资源,都统一通过 <fis:require id =" xxxx" /> 引入。
31 |
32 | <%-- 引入 js --%>
33 | <fis:require id =" /static/js/dialog.js" />
34 |
35 | <%-- 引入 css --%>
36 | <fis:require id =" ./dialog.css" />
37 |
38 | <%-- 引入 jsp 目录是把目标 jsp 所有依赖的 js 和 css 都引入。 --%>
39 | <fis:require id =" /page/index.jsp" />
40 |
41 | 路径说明:如果是本项目内的资源,请使用绝对路径或者相对路径。如果是跨项目引用。请使用 fis 资源 id。
42 |
43 |
44 |
FIS 资源
45 |
46 | FIS 资源 ID 写法是 [namespace:]文件绝对路径。当项目设置了 namespace
的时候:common:static/js/jquery.js
、当项目没有设置 namespace
的时候:static/js/query.js
47 |
48 |
为什么要用 FIS 资源 ID
因为,我们的静态资源很可能是部署在 CDN 上 或者 文件名加了 md5 截,所以我们需要用 FIS 资源 ID
标识资源,这样我们可以通过查找 jello release 出来的 map.json 表,查找出最终的存放路径。
49 |
50 |
51 | 除了单纯的引入资源之外,有时候,需要控制资源是否在低版本的 IE 中加载。如何控制?请参考以下代码。
52 |
53 | <fis:require id =" /static/scss/ie.scss" prefix =" <!--[if lt IE 8]>" affix =" <![endif]-->" />
54 |
55 | fis:script & fis:style
56 | 引入 js 或者 css, 通过它组织的 js 和 css 可以自动完成性能优化。
57 |
58 | <%-- 内联 js --%>
59 | <fis:script >
60 | console.log('hello world');
61 | </fis:script >
62 |
63 | <%-- 等价与 fis:require --%>
64 | <fis:script src =" /static/lib/dialog.js" ></fis:script >
65 |
66 | <%-- 引入外部资源 --%>
67 | <fis:script src =" //code.jquery.com/jquery-1.11.0.min.js" ></fis:script >
68 |
69 | <%-- 内联 js --%>
70 | <fis:style >
71 | .body {
72 | }
73 | </fis:style >
74 |
75 | <%-- 等价与 fis:require --%>
76 | <fis:style href =" /static/lib/dialog.css" ></fis:style >
77 |
78 | <%-- 引入外部资源 --%>
79 | <fis:style href =" //domain.com/xxx.scss" ></fis:style >
80 |
81 |
82 | fis:html
83 | 用它来代替 html 标签。可以设置任意属性,设置的属性,将会作用 html 标签。
84 |
85 | framework 属性,用来指定前端框架。
86 |
87 | <fis:html framework =" /static/js/mod.js" class =" page-hello-world" >
88 |
89 | </fis:html >
90 |
91 | fis:head & fis:body
92 | 用来代替 head 和 body 标签的。作用其实很简单。fis:head 在 head 的底部插入了。<!--FIS_STYLE_PLACEHOLDER-->, 而 fis:body 给 body 底部插入了 <!--FIS_FRAMEWORK_PLACEHOLDER--> <!--FIS_FRAMEWORK_CONFIG--> <!--FIS_SCRIPT_PLACEHOLDER-->
93 |
94 | 也就是说,如果你自己来控制在对应的位置插入这些标签,你是可以不用写 fis:head 和 fis:body 标签的。
95 |
96 | fis:uri
97 | 把 FIS 资源 ID 转化成实际路径,可以跨模块。与 fis:uri 的区别是,这个只会简单的输出路径,而不会引入此文件。
98 |
99 | <fis:uri name =" ns:static/xxxx/xxx.js" />
100 |
101 | fis:widget
102 | 引入一个模板碎片文件,支持指定局部变量。更多信息请查看此处说明 。
103 |
104 | fis:extends & fis:block & fis:parent
105 |
106 | 请查看模板继承说明 。
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/page/jsp/layout.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ page import="java.util.*" %><%@ taglib uri="/fis" prefix="fis"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%
2 |
3 | String layout = request.getParameter("layout");
4 | String parent = "page/layout/2columns-with-left-sidebar.jsp";
5 |
6 | if (layout != null) {
7 | if (layout.equals("right")) {
8 | parent = "page/layout/2columns-with-right-sidebar.jsp";
9 | } else if (layout.equals("both")) {
10 | parent = "page/layout/3columns.jsp";
11 | }
12 | } else {
13 | layout = "left";
14 | }
15 |
16 | request.setAttribute("layout", layout);
17 | request.setAttribute("parent", parent);
18 | %>
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | <%--with 表示把整个对象里面的成员展开,作为局部变量。--%>
29 |
30 |
31 |
32 |
33 | 模板继承思想来源于 smarty , 可以用来更好的复用代码。
34 |
35 |
36 |
37 |
如果之前没有接触过模板继承,请先一定要看下这个说明。
38 |
39 |
40 |
41 | 示例
42 |
43 |
44 |
先点击示例看效果
45 |
请切换下面这三个按钮看效果,同样的内容在不同的骨架下的效果。
46 |
51 |
52 |
53 | 没错,模板继承可以很方便的来定义具有容器功能的模板。子模板可以选择直接填充此类骨架类模板。
54 |
55 | 说明
56 |
57 | layout.jsp
58 |
59 | <!doctype html>
60 | <html >
61 | <head >
62 | <meta charset =" utf-8" >
63 | <title ><fis:block name =" title" >My Site</fis:block ></title >
64 |
65 | <fis:block name =" head" >
66 | <link rel =" stylesheet" href =" main.css" >
67 | </fis:block >
68 | </head >
69 | <body >
70 | <fis:block name =" content" ></fis:block >
71 | </body >
72 | </html >
73 |
74 | index.jsp
75 | <fis:extends name =" layout.jsp" >
76 | <fis:block name =" title" >My Page</fis:block >
77 |
78 | <fis:block name =" head" >
79 | <fis:parent />
80 | <link rel =" stylesheet" href =" custom.css" >
81 | </fis:block >
82 |
83 | <fis:block name =" content" >
84 | <p >This is just an awesome page.</p >
85 | </fis:block >
86 | </fis:extends >
87 |
88 | 简单点描述就是,父模板挖坑,子模块填坑。特别注意下 fis:parent 的作用,就是把父模板中对应的 block 给拿过来。
89 |
90 | 局部变量
91 |
92 | 请直接查看 widget 中的局部变量用法,用法一样。传送门
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/page/jsp/rewrite.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %>
2 | <%@ taglib uri="/fis" prefix="fis"%>
3 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 在 page 目录下面的所有 jsp 模板文件,只会有两类,一种是骨架模板,而另一种是入口模板。在 jello 环境下,所有入口模板都是可以直接预览的。他的预览规则为 /page/{{jsp 路径}}
。
15 |
16 | 比如: page 目录下面的 index.jsp 文件,则可以通过 /page/index
(.jsp 可以省略)路径预览。
17 |
18 |
19 |
namespace 项目
20 |
如果当前项目设置了 namespace
,则预览地址得加上 namespace
作为前缀。
21 |
假如 namespace
为 home
,预览地址则为:/home/page/index
22 |
23 |
24 | 页面转发
25 | 这里有个限制就是路径总是以 /page
打头, 且模板路径必须与访问路径一致。这肯定是不能接受的。
26 |
27 | 针对这个问题,我们的解决方法是,提供rewrite
机制。
28 |
29 | 在根目录下面新建一个 server.conf
文件,通过配置此文件来实现页面跳转。
30 |
31 | # 首页
32 | rewrite \/$ /page/index
33 |
34 | #用户查看页面
35 | rewrite \/user/view/(\d*)$ /page/user/view?id=$1
36 |
37 | # home 页面跳转首页
38 | redirect \/home$ /page/index
39 |
40 |
41 | server.conf 中支持两种指令。
42 |
43 | rewrite
页面跳转,但是不改变浏览器地址
44 | redirect
页面跳转,同时改变浏览器地址
45 |
46 |
47 | 语法说明
48 | {{指定}} {{匹配正则}} {{目标地址}}
49 | server.conf 中,不是 rewrite
或者 redirect
打头的都被认为是注释。目标地址支持 $数字 来替换捕获结果。
50 |
51 | jsp 页面转发
52 |
53 | 在 test 目录下面的 .jsp 文件,也是可以在直接在 jello 环境下预览的,如 test 目录下面的 file.jsp 文件可以通过 /test/file.jsp
路径预览。
54 |
55 | 如果再配合rewrite
, 在 jello 环境下就可以模拟任何后端输出。
56 |
57 | rewrite \/testpage$ /test/mock.jsp
58 |
59 |
60 | <%@ page contentType = "text/html;charset=UTF-8" language = "java" %>
61 | <%
62 | response . getWriter (). write ( "hello world" );
63 | %>
64 |
65 |
66 | 示例
67 |
68 | json 页面模拟
69 |
70 | 关于json 页面,其实可以用 jsp 页面模拟,如示例 ,也可以直接在预览 json 文件,只要此文件放在 test 目录下面。如示例 。注意查看 jello-demo
项目下面的 test 目录和 server.conf 文件内容。
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/page/jsp/widget.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %>
2 | <%@ taglib uri="/fis" prefix="fis"%>
3 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 页面中比较常用的小的部分,可以抽离成widget ,比如:登陆框、头部菜单、边栏菜单等等
15 |
16 | 抽离成小模板后,可以通过这样的方式引入页面的任意位置。
17 |
18 | <fis:widget name =" /widget/sidebarmenus/sidebarmenus.jsp" />
19 |
20 | 局部变量
21 |
22 | 除了模板复用外,有时候变量也需要复用,在通过 fis:widget
引入模板的时候,可以通过以下两种方式指定变量
23 |
24 |
25 | with
将指定变量下面的所有属性做为 widget 中的局部变量使用
26 | 其他attribute
设置 widget 中局部变量
27 |
28 |
29 | <fis:widget name =" /widget/sidebarmenus/sidebarmenus.jsp" vara =" test string" obja =" \${ reference } " with =" \${ object } " />
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/page/layout/2columns-with-left-sidebar.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ taglib uri="/fis" prefix="fis"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/page/layout/2columns-with-left-sidebar.vm:
--------------------------------------------------------------------------------
1 | ## 两栏布局有左边栏
2 | #extends("./frame.vm")
3 |
4 | #block("body")
5 | #block("header")
6 | #widget("/widget/header/header.vm")
7 | #end
8 |
9 |
10 |
11 |
#block("sidebar")#end
12 |
13 | #if ($pageTitle)
14 |
17 | #end
18 |
#block("content")#end
19 |
20 |
21 |
22 |
23 | #block("footer")
24 | #widget("/widget/footer/footer.vm")
25 | #end
26 | #end
27 |
28 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
29 | #require("./2columns-with-left-sidebar.vm")
30 | #end
31 |
--------------------------------------------------------------------------------
/page/layout/2columns-with-right-sidebar.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ taglib uri="/fis" prefix="fis"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/page/layout/2columns-with-right-sidebar.vm:
--------------------------------------------------------------------------------
1 | ## 两栏布局有右边栏
2 | #extends("./frame.vm")
3 |
4 | #block("body")
5 | #block("header")
6 | #widget("/widget/header/header.vm")
7 | #end
8 |
9 |
10 |
11 |
12 | #if ($pageTitle)
13 |
16 | #end
17 |
#block("content")#end
18 |
19 |
#block("sidebar")#end
20 |
21 |
22 |
23 | #block("footer")
24 | #widget("/widget/footer/footer.vm")
25 | #end
26 | #end
27 |
28 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
29 | #require("./2columns-with-right-sidebar.vm")
30 | #end
31 |
--------------------------------------------------------------------------------
/page/layout/3columns.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ taglib uri="/fis" prefix="fis"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/page/layout/3columns.vm:
--------------------------------------------------------------------------------
1 | ## 两栏布局有左边栏
2 | #extends("./frame.vm")
3 |
4 | #block("body")
5 | #block("header")
6 | #widget("/widget/header/header.vm")
7 | #end
8 |
9 |
10 |
11 |
#block("sidebar")#end
12 |
13 | #if ($pageTitle)
14 |
17 | #end
18 |
#block("content")#end
19 |
20 |
#block("sidebarSecondary")#end
21 |
22 |
23 |
24 | #block("footer")
25 | #widget("/widget/footer/footer.vm")
26 | #end
27 | #end
28 |
29 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
30 | #require("./3columns.vm")
31 | #end
32 |
--------------------------------------------------------------------------------
/page/layout/ajax.vm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
#block("content")
11 | body place holder
12 | #end
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/page/layout/frame.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="/fis" prefix="fis"%>
2 |
3 |
4 |
5 |
6 | ${title} - ${titleAffix}
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | // 启用 bootstrap
29 | require(['bootstrap']);
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/page/layout/frame.vm:
--------------------------------------------------------------------------------
1 |
2 | #html("/static/js/require.js")
3 |
4 | #head()
5 |
6 |
7 |
8 | $title #if( $titleAffix ) - $titleAffix#end
9 |
10 |
11 |
12 |
16 | #require("/static/scss/normalize.css")
17 | #require("bootstrap/css/bootstrap.css")
18 | #require("bootstrap/css/bootstrap-theme.css")
19 | #require("/static/scss/global.scss")
20 | #end## end head
21 |
22 | #body()
23 |
24 | ## 定义一个区域
25 | #block("body")#end
26 |
27 |
28 | #end## end of body
29 |
30 | #script()
31 | // 启用 bootstrap
32 | require(['bootstrap']);
33 | #end
34 |
35 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
36 | #require("./frame.vm")
37 | #end## end of html
38 |
--------------------------------------------------------------------------------
/page/layout/front.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ taglib uri="/fis" prefix="fis"%>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/page/layout/front.vm:
--------------------------------------------------------------------------------
1 | ## 首页布局
2 | #extends("./frame.vm")
3 |
4 | #block("body")
5 | #block("header")
6 | #widget("/widget/header/header.vm")
7 | #end
8 |
9 | #block("content")#end
10 |
11 | #block("footer")
12 | #widget("/widget/footer/footer.vm")
13 | #end
14 | #end
15 |
16 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
17 | #require("./front.vm")
18 | #end
19 |
--------------------------------------------------------------------------------
/page/layout/smart.vm:
--------------------------------------------------------------------------------
1 | ## 只能判断,是否来源于 ajax
2 | #if ($request.getHeader("X-Requested-With").equals("XMLHttpRequest"))
3 | #extends("./spa.vm")#end
4 | #else
5 | #extends("./2columns-with-left-sidebar.vm")#end
6 | #end
7 |
8 |
--------------------------------------------------------------------------------
/page/layout/spa.vm:
--------------------------------------------------------------------------------
1 | ## 单页面骨架,什么也不要,只要 html 内容,样式和 JS
2 |
3 | #block("content")#end
4 |
5 |
6 |
--------------------------------------------------------------------------------
/page/velocity/index.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="velocity 语法")
2 | #extends("/page/layout/2columns-with-left-sidebar.vm")
3 |
4 | #block("sidebar")
5 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(3).children")
6 | #end
7 |
8 | #block("content")
9 |
10 | 基本的逻辑标签不在这里多说了,请查看 velocity 官方文档 。这里重点说一下 velocity 宏 和jello 基于 velocity 扩展的几个标签。
11 |
12 | velocity 宏
13 |
14 | Velocity 里面,不像 smarty 一样,可以写 PHP 函数,不过,它有它自己的机制,比如宏的定义。一些重用且复杂的渲染逻辑,可以封装成 velocity 宏来处理。
15 |
16 | 宏方法定义
17 | # macro ( tablerows $ color $ somelist )
18 | # foreach ( $ something in $ somelist )
19 | <tr><td bgcolor= $ color > $ something </td></tr>
20 | # end
21 | # end
22 |
23 |
24 | 宏方法使用
25 |
26 | # set ( $ greatlakes = [ "Superior" , "Michigan" , "Huron" , "Erie" , "Ontario" ] )
27 | # set ( $ color = "blue" )
28 | <table>
29 | # tablerows ( $ color $ greatlakes )
30 | </table>
31 |
32 |
33 | 输出结果
34 | <table>
35 | <tr><td bgcolor= "blue" > Superior</td></tr>
36 | <tr><td bgcolor= "blue" > Michigan</td></tr>
37 | <tr><td bgcolor= "blue" > Huron</td></tr>
38 | <tr><td bgcolor= "blue" > Erie</td></tr>
39 | <tr><td bgcolor= "blue" > Ontario</td></tr>
40 | </table>
41 |
42 |
43 | 扩展标签
44 |
45 | ${esc.hash}require()
46 | 用来引入资源,不管是 JS 资源还是 CSS 资源,都统一通过 ${esc.hash}require()
引入。
47 |
48 | $esc.hash$esc.hash 引入JS脚本
49 | # require ( "[ns:]static/lib/dialog.js" )
50 |
51 | $esc.hash$esc.hash 引入样式文件
52 | # require ( "[ns:]static/lib/dialog.css" )
53 |
54 | $esc.hash$esc.hash 引入模板文件,作用是把此模本文件的依赖(同名JS和同名CSS)引入进来。
55 | # require ( "[ns:]page/index.vm" )
56 |
57 |
58 | 通过${esc.hash}require()
只能引入存在的 FIS 资源 ID
如果不是 FIS 资源 ID
或者不存在,则不会输出任何东西,同时还会提示 warning。如果想引入一个外部 JS 或者 CSS 请改用 ${esc.hash}script()
或者 ${esc.hash}style()
59 |
60 |
61 |
FIS 资源
62 |
63 | FIS 资源 ID 写法是 [namespace:]文件绝对路径。当项目设置了 namespace
的时候:common:static/js/jquery.js
、当项目没有设置 namespace
的时候:static/js/query.js
64 |
65 |
为什么要用 FIS 资源 ID
因为,我们的静态资源很可能是部署在 CDN 上 或者 文件名加了 md5 截,所以我们需要用 FIS 资源 ID
标识资源,这样我们可以通过查找 jello release 出来的 map.json 表,查找出最终的存放路径。
66 |
67 |
68 |
69 |
更新
70 |
Jello 新版本已经支持基于项目的绝对路径,或者相对路径。为了方便项目间迁移。请使用 namespace 无关的路径。除非你需要跨项目引用。
71 |
72 |
${esc.hash}require() 新加了两个参数, ${esc.d}prefix, ${esc.d}affix。prefix 和 affix 会对应的在标签头部和尾部补充内容。如:
73 |
74 |
${esc.hash}require("/static/scss/ie.scss" "<!--[if lt IE 8]>" "<![endif]-->")
75 |
76 |
77 |
78 | ${esc.hash}script() & ${esc.hash}style()
79 | 用来收集 js 和 css 如:
80 |
81 | $esc.hash$esc.hash 内联脚本
82 | # script ()
83 | console.log('hello world');
84 | # end
85 |
86 | $esc.hash$esc.hash 引入脚本
87 | # script ( "[namespace:]static/lib/dialog.js" ) # end
88 | $esc.hash$esc.hash 等价与 ${esc.hash}require("[namespace:]static/lib/dialog.js");
89 |
90 | $esc.hash$esc.hash 引入外部资源,非 FIS ID
91 | # script ( "//code.jquery.com/jquery-1.11.0.min.js" ) # end
92 |
93 |
94 |
95 |
更新
96 |
${esc.h}script ${esc.h}style 同样支持 prefix 和 affix,请参考 require 中的说明。
97 |
98 |
99 | ${esc.hash}html() & ${esc.hash}head() & ${esc.hash}body()
100 |
101 | 用来代替 html、head、body 标签,通过这些标签组织代码,jello 就知道把收集的 js 和 css 集中在什么地方输出了。
102 |
103 | ${esc.hash}uri()
104 | 把 FIS 资源 ID
转化成实际路径,可以跨模块。与 ${esc.hash}require()
的区别是,这个只会简单的输出路径,而不会引入此文件。
105 |
106 | ${esc.hash}widget()
107 | 引入一个模板碎片文件,支持指定局部变量。更多信息请查看此处说明 。
108 |
109 | ${esc.hash}extends() & ${esc.hash}blocks() & ${esc.h}parent()
110 |
111 | 请查看模板继承说明 。
112 |
113 | #end## end of body
114 |
115 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
116 | #require("./index.vm")
117 | #end
118 |
--------------------------------------------------------------------------------
/page/velocity/jello.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="jello tools")
2 | #extends("/page/layout/2columns-with-left-sidebar.vm")
3 |
4 | #block("sidebar")
5 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(3).children")
6 | #end
7 |
8 | #block("content")
9 |
10 | velocity tools 虽然多,但是好像少了一些东西,比如说将 java 对象转换成 json 给 js 使用。
11 |
12 | jello tools 就是用来弥补 velocity tools 中缺失的东西。目前只有实现了 \$jello.jsonEncode(\$variable)
,即:把 java 对象转换成 json。demo
13 |
14 |
15 |
提交需求?
16 |
请在此页面 提交
17 |
18 |
19 |
20 | #end## end of body
21 |
22 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
23 | #require("./jello.vm")
24 | #end
25 |
--------------------------------------------------------------------------------
/page/velocity/tools.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="velocity tools")
2 | #set($title="velocity tools")
3 |
4 | #extends("/page/layout/2columns-with-left-sidebar.vm")
5 |
6 | #block("sidebar")
7 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(3).children")
8 | #end
9 |
10 | #block("content")
11 |
12 | velocity tools 是 velocity 官方开发一系列帮助开发的工具集,jello 中默认集成了所有的 GenericTools
13 |
14 | 更方便的操作列表(LIST),支持 auto 和 manual两种方式,输入:可遍历的对象,输出:该 List 的迭代器。如果是自动模式,该迭代器在输出完后会自动进入一个元素。
15 | 示例
16 | ## 自动模式
17 | # set ( $ color = $ alternator . auto ( 'red' , 'blue' ) )
18 | ## 手动模式
19 | # set ( $ style = $ alternator . manual ( 'hip' , 'fly' , 'groovy' ) )
20 | ## $color 会每次自动进入下一个,而 $style 则需要手动调用 next
21 | # foreach ( $ i in [ 1 .. 5 ] )
22 | Number $ i is $ color and $ style . I dig $ style . next numbers.
23 | # end
24 |
25 | 输出
26 | Number 1 is red and hip. I dig hip numbers.
27 | Number 2 is blue and fly. I dig fly numbers.
28 | Number 3 is red and groovy. I dig groovy numbers.
29 | Number 4 is blue and hip. I dig hip numbers.
30 | Number 5 is red and fly. I dig fly numbers.
31 |
32 | 查看更多
33 |
34 | 显示类工具,用来更好的显示数据,如:“优美”的输出数组或集合、截断文字长度、或者对 null 数据通其他文字替代。。。
35 | # set ( $ list = [ 1 .. 5 ] )
36 | $ display . list ($ list )
37 | $ display . truncate ( "This is a long string." , 10 )
38 | Not Null: $ display . a
39 | Null: $ display . alt ($ null , "--" )
40 |
41 |
42 | 输出
43 |
44 | 1, 2, 3, 4 and 5
45 | This is...
46 | Not Null: not null
47 | Null: --
48 |
49 |
50 | 查看更多
51 |
52 |
53 |
54 | 一系列数学操作的工具,比起直接用 velocity 语句方便多了。
55 |
56 | 查看更多
57 |
58 |
59 |
60 |
61 | 一系列数字操作的工具,可以用来格式化输出数字。
62 |
63 | $ myNumber -> 13.55
64 | $ number . format ($ myNumber ) -> 13.6
65 | $ number . currency ($ myNumber ) -> $ 13.55
66 | $ number . integer ($ myNumber ) -> 13
67 |
68 |
69 | 查看更多
70 |
71 |
72 |
73 | 一系列日期相关操作的工具,可以用来解析日期,格式化日期,和比较日期。
74 |
75 | Example of formatting the "current" date:
76 | $ date -> Oct 19, 2003 9:54:50 PM
77 | $ date . long -> October 19, 2003 9:54:50 PM PDT
78 | $ date . medium_time -> 9:54:50 PM
79 | $ date . full_date -> Sunday, October 19, 2003
80 | $ date . get ( 'default' , 'short' ) -> Oct 19, 2003 9:54 PM
81 | $ date . get ( 'yyyy-M-d H:m:s' ) -> 2003-10-19 21:54:50
82 |
83 | Example of formatting an arbitrary date:
84 | $ myDate -> Tue Oct 07 03:14:50 PDT 2003
85 | $ date . format ( 'medium' ,$ myDate ) -> Oct 7, 2003 3:14:50 AM
86 |
87 |
88 | $ date . whenIs ( '2005-07-04' ) -> 1 year ago
89 | $ date . whenIs ( '2007-02-15' ). full -> 1 year 32 weeks 2 days 17 hours 38 minutes 44 seconds 178 milliseconds ago
90 | $ date . whenIs ( '2007-02-15' ). days -> -730
91 | $ date . whenIs ($ date . calendar ) -> now
92 | $ date . whenIs ( '2005-07-04' , '2005-07-04' ) -> same time
93 | $ date . difference ( '2005-07-04' , '2005-07-04' ) -> 0 milliseconds
94 | $ date . difference ( '2005-07-04' , '2007-02-15' ). abbr -> 1 yr
95 |
96 |
97 | 查看更多
98 |
99 |
100 |
101 | Java 反射类相关的工具,用来方便文档的编写,一般很少用到。
102 |
103 | 查看更多
104 |
105 |
106 |
107 | 可以用来把字符串装换成其他类型。
108 |
109 | $ convert . toNumber ( '12.6' ) -> 12.6
110 | $ convert . toInt ( '12.6' ) -> 12
111 | $ convert . toNumbers ( '12.6,42' ) -> [12.6, 42]
112 |
113 |
114 |
115 | 查看更多
116 |
117 |
118 |
119 | 用来转义输出 Velocity、 Java、 JavaScript、 HTML、 HTTP、 XML 和 SQL 格式的字符串
120 |
121 | $ velocity -> Please escape $ and # !
122 | $ esc . velocity ($ velocity ) -> Please escape ${ esc . d } and ${ esc . h } !
123 |
124 | $ java -> He didn't say, "Stop!"
125 | $ esc . java ($ java ) -> He didn't say, \"Stop!\"
126 |
127 | $ javascript -> He didn't say, "Stop!"
128 | $ esc . javascript ($ javascript ) -> He didn\'t say, \"Stop!\"
129 |
130 | $ html -> "bread" & "butter"
131 | $ esc . html ($ html ) -> "bread" & "butter"
132 |
133 | $ xml -> "bread" & "butter"
134 | $ esc . xml ($ xml ) -> "bread" & "butter"
135 |
136 | $ sql -> McHale's Navy
137 | $ esc . sql ($ sql ) -> McHale''s Navy
138 |
139 | $ url -> hello here & there
140 | $ esc . url -> hello+here+%26+there
141 |
142 | $ esc . dollar -> $
143 | $ esc . d -> $
144 |
145 | $ esc . hash -> #
146 | $ esc . h -> #
147 |
148 | $ esc . backslash -> \
149 | $ esc . b -> \
150 |
151 | $ esc . quote -> "
152 | $ esc . q -> "
153 |
154 | $ esc . singleQuote -> '
155 | $ esc . s -> '
156 |
157 | $ esc . newline ->
158 |
159 | $ esc . n ->
160 |
161 |
162 | $ esc . exclamation -> !
163 | $ esc . e -> !
164 |
165 |
166 | 查看更多
167 |
168 |
169 |
170 |
171 | 提供一种可以访问到类静态属性的方法
172 |
173 | ## here we access a constant in a class include in the configuration
174 | $ field . COUNTER_NAME
175 |
176 | ## here we dynamically lookup a class' fields to find another constant
177 | $ field . in ( "org.com.SomeClass" ). ANOTHER_CONSTANT
178 |
179 | ## here we pass an object instance in (an Integer in this case) and
180 | ## retrieve a static constant from that instance's class
181 | $ field . in ( 0 ). MIN_VALUE
182 |
183 | ## by default, once we've searched a class' fields, those fields stay
184 | ## available in the tool (change this by storeDynamicLookups="false")
185 | ## so here we get another constant from the Integer class
186 | $ field . MAX_VALUE
187 |
188 |
189 | 查看更多
190 |
191 |
192 |
193 | 提供一种操作列表(Collection)的工具,可以用来检测列表是否为空、是否含有某个值、获取某个成员、设置某个位置的值等等。
194 |
195 | $ primes -> new int[] { 2, 3, 5, 7}
196 | $ lists . size ($ primes ) -> 4
197 | $ lists . get ($ primes , 2 ) -> 5
198 | $ lists . set ($ primes , 2 , 1 ) -> (primes[2] becomes 1)
199 | $ lists . get ($ primes , 2 ) -> 1
200 | $ lists . isEmpty ($ primes ) -> false
201 | $ lists . contains ($ primes , 7 ) -> true
202 |
203 |
204 | 查看更多
205 |
206 |
207 | 只知道是跟 i18n 多语言相关的,其他的没看懂。
208 |
209 | 查看更多
210 |
211 |
212 |
213 | 针对集合(Collection)进行排序操作,支持任何可以被 \#foreach()
操作的对象。
214 |
215 | Single Property Sort
216 | # foreach ($ obj in $ sorter . sort ($ objects , "name" ))
217 | $ obj . name Ordinal= $ obj . ordinal
218 | # end
219 | End
220 |
221 | Multiple Property Sort
222 | # foreach ($ obj in $ sorter . sort ($ objects , [ "name" , "ordinal" ]))
223 | $ obj . name , $ obj . ordinal
224 | # end
225 | End
226 |
227 |
228 | 查看更多
229 |
230 |
231 |
232 |
233 | A convenience tool to use with \#foreach loops. It wraps a list with a custom iterator to provide additional controls and feedback for managing loops.
234 |
235 | Template
236 | ---
237 | # set ( $ list = [ 1 .. 7 ] )
238 | # set ( $ others = [ 3 .. 10 ] )
239 | # foreach ( $ item in $ loop . watch ($ list ). sync ($ others , 'other' ) )
240 | $ item -> $ loop . other
241 | # if ( $ item >= 5 )$ loop . stop () # end
242 | # end
243 |
244 | Output
245 | ------
246 | 1 -> 3
247 | 2 -> 4
248 | 3 -> 5
249 | 4 -> 6
250 | 5 -> 7
251 |
252 |
253 | 查看更多
254 |
255 |
256 |
257 | 提供一种可以访问 context 的方法,通过他知道有哪些变量可以使用。
258 |
259 | # foreach ( $ key in $ context . keys )
260 | $ key = $ context . get ($ key )
261 | # end
262 |
263 |
264 | 查看更多
265 |
266 |
267 |
268 | 相当重要的一个工具,用来操作链接相关的工具,比如如果要生成 pagination 分页的连接,分页参数是 pageSize = xxx; 因为有可能 url 中一几个有这个参数了,或者有条件搜索,还有其他用来过滤的参数,所以得保证生成的分页链接时,这些参数不能丢失。如果不用这个工具,实现起来非常麻烦,但是用它会特别方便。如下:
269 |
270 | # set ($ base = $ link . query ($ request . getQueryString ()))
271 |
272 | <a href=" $ ! { request.contextPath} $ base . set ( 'pageNum' , 1 ) ">第一页</a>
273 |
274 |
275 | 查看更多
276 |
277 |
278 |
279 | 通过它可以把字符串当成 velocity 语句解析。
280 |
281 | Example of eval():
282 | Input
283 | -----
284 | # set ( $ list = [ 1 , 2 , 3 ] )
285 | # set ( $ object = '$list' )
286 | # set ( $ method = 'size()' )
287 | $ render . eval ( "${object}.$method" )
288 |
289 | Output
290 | ------
291 | 3
292 |
293 | Example of recurse():
294 | Input
295 | -----
296 | # macro ( say_hi ) hello world! # end
297 | # set ( $ foo = '#say_hi()' )
298 | # set ( $ bar = '$foo' )
299 | $ render . recurse ($ bar )
300 |
301 | Output
302 | ------
303 | hello world!
304 |
305 |
306 | 查看更多
307 |
308 | #end## end of body
309 |
310 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
311 | #require("./tools.vm")
312 | #end
313 |
--------------------------------------------------------------------------------
/page/velocity/variables.vm:
--------------------------------------------------------------------------------
1 | #set($pageTitle="velocity 全局变量")
2 | #extends("/page/layout/2columns-with-left-sidebar.vm")
3 |
4 | #block("sidebar")
5 | #widget("/widget/sidebarmenus/sidebarmenus.vm" "var:menus=$menus.get(3).children")
6 | #end
7 |
8 | #block("content")
9 |
10 | 为了方便开发,还需要知道 velocity 里面有哪些全局变量可以使用。
11 |
12 | \$request
13 |
14 | 通过它,可以知道当前页面请求,请求的参数是什么,或者知道当前请求头部是什么等等。
15 |
16 | $esc.hash$esc.hash 请求 GET 参数 layout 是否等于 "right"
17 | $ request . getParameter ( "layout" ). equals ( "right" )
18 |
19 | $esc.hash$esc.hash 是否是来源于 ajax 请求?
20 | $ request . getHeader ( "X-Requested-With" ). equals ( "XMLHttpRequest" )
21 |
22 |
23 | 更多方法请查看HttpServletRequest 文档。
24 |
25 | \$response
26 |
27 | 暂没有想到用法,直接查看HttpServletResponse 文档吧。
28 |
29 | Velocity Tools
30 | 请查看velocity tools 。
31 | \$jello
32 | 请查看jello tools 。
33 | 更多变量
34 |
35 |
36 | #end## end of body
37 |
38 | ## 需要依赖一下自己,否则该 vm 中依赖没法自动加载进来。
39 | #require("./variables.vm")
40 | #end
41 |
--------------------------------------------------------------------------------
/server.conf:
--------------------------------------------------------------------------------
1 | # 页面转发
2 | rewrite ^\/abcdefg$ /xxxxxx
3 |
4 |
5 | # 首页
6 | rewrite ^\/$ /page/index
7 |
8 | # 文档页面
9 | redirect ^\/doc$ /doc/standard
10 | rewrite ^\/doc\/standard$ /page/doc/standard
11 | rewrite ^\/doc\/layout$ /page/doc/layout
12 | rewrite ^\/doc\/widget$ /page/doc/widget
13 | rewrite ^\/doc\/rewrite$ /page/doc/rewrite
14 | rewrite ^\/doc\/binding$ /page/doc/binding
15 |
16 | # 示例页面
17 | redirect ^\/examples$ /examples/pagination
18 | rewrite ^\/examples\/pagination$ /page/examples/pagination
19 | rewrite ^\/examples\/form$ /page/examples/form
20 | rewrite ^\/examples\/data$ /page/examples/data
21 | rewrite ^\/examples\/partial$ /page/examples/partial
22 | rewrite ^\/examples\/spa$ /page/examples/spa/page1
23 | rewrite ^\/examples\/spa\/page1$ /page/examples/spa/page1
24 | rewrite ^\/examples\/spa\/page2$ /page/examples/spa/page2
25 | rewrite ^\/examples\/spa\/page3$ /page/examples/spa/page3
26 |
27 | ## 借助 delay.jsp 添加 3 秒延时。
28 | rewrite ^\/examples\/partial/form$ /test/delay.jsp?forward=/page/examples/partial/form
29 |
30 | # jsp
31 | rewrite ^\/jsp$ /jsp/index
32 | rewrite ^\/jsp\/(.*)$ /page/jsp/$1
33 |
34 | # 模板说明页面
35 | redirect ^\/velocity$ /velocity/index
36 | rewrite ^\/velocity\/index$ /page/velocity/index
37 | rewrite ^\/velocity\/variables$ /page/velocity/variables
38 | rewrite ^\/velocity\/tools$ /page/velocity/tools
39 | rewrite ^\/velocity\/jello$ /page/velocity/jello
40 |
41 | # 后端
42 | redirect ^\/be$ /be/spring
43 | rewrite ^\/be\/spring$ /page/be/spring
44 | rewrite ^\/be\/variables$ /page/be/variables
45 |
46 |
47 | # 异步数据
48 | rewrite ^\/json$ /test/data/data.json
49 | rewrite ^\/jspdata$ /test/data/ajax.jsp
50 |
51 |
52 | # 测试页面
53 | rewrite \/testpage$ /test/mock.jsp
54 |
55 | # 模拟表单保存页面
56 | rewrite \/examples\/form\/save$ /test/delay.jsp?forward=/test/data/saveform.json
57 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2betop/jello-demo/748a4bea4559c790ea3a10b60f59b3acfd4f9b94/static/favicon.ico
--------------------------------------------------------------------------------
/static/js/README.md:
--------------------------------------------------------------------------------
1 | 说明
2 | ===============
3 |
4 |
5 | 各种 amd loader, 可以随意选择哪一款。
--------------------------------------------------------------------------------
/static/js/mod-amd.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 针对 fis 量身定做。
3 | * 由于编译期会处理并优化一些事情,所以此 amd loader 不需要考虑所有的用法。
4 | */
5 | var require, define;
6 | (function(undef) {
7 | var defined = {},
8 | waiting = {},
9 | config = {},
10 | defining = {},
11 | slice = [].slice,
12 | hasOwn = Object.prototype.hasOwnProperty,
13 | head = document.getElementsByTagName('head')[0],
14 | timeout = 5000,
15 | ext = '.js',
16 | handlers, req;
17 |
18 | function hasProp(obj, prop) {
19 | return hasOwn.call(obj, prop);
20 | }
21 |
22 | handlers = {
23 | require: function() {
24 | return function() {
25 | return req.apply(undef, arguments);
26 | };
27 | },
28 |
29 | exports: function(name) {
30 | return hasProp(defined, name) ? defined[name] : (defined[name] = {});
31 | },
32 |
33 | module: function(name) {
34 | return {
35 | id: name,
36 | uri: '',
37 | exports: defined[name],
38 | config: function() {
39 | return (config && config.config && config.config[name]) || {};
40 | }
41 | };
42 | }
43 | };
44 |
45 | function callDep(name, callback) {
46 | if (hasProp(waiting, name)) {
47 | var args = waiting[name];
48 |
49 | delete waiting[name];
50 | defining[name] = true;
51 |
52 | if (callback) {
53 | args[2] = (function(old, fn) {
54 | var hacked = function() {
55 | var ret = typeof old === 'function' ? old.apply(this, arguments) : old;
56 |
57 | // 要等 return ret 后 defined 里面才有数据。
58 | setTimeout(fn, 4);
59 | return ret;
60 | };
61 | return (hacked._length = 1, hacked);
62 | })(args[2], callback);
63 |
64 | return main.apply(undef, args);
65 | }
66 |
67 | main.apply(undef, args);
68 | }
69 |
70 | if (!hasProp(defined, name) && !hasProp(defining, name)) {
71 | throw new Error('No ' + name);
72 | }
73 |
74 | return callback ? callback() : defined[name];
75 | }
76 |
77 | function resolve(name) {
78 | var paths = config.paths || {},
79 | path = paths[name] || name;
80 |
81 | return /\.js$/.test(path) ? path : (path + ext);
82 | }
83 |
84 | function loadJs(url, cb) {
85 | var script = document.createElement('script'),
86 | loaded = false,
87 |
88 | clean = function() {
89 | clearTimeout(timer);
90 | script.onload = script.onreadystatechange = script.onerror = null;
91 | head.removeChild(script);
92 | },
93 |
94 | wrap = function() {
95 | if (!loaded && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete')) {
96 | loaded = true;
97 | clean();
98 | cb();
99 | }
100 | },
101 |
102 | onerror = function() {
103 | clean();
104 | throw new Error('Can\'t load ' + url);
105 | },
106 |
107 | timer;
108 |
109 | script.setAttribute('src', url);
110 | script.setAttribute('type', 'text/javascript');
111 | script.onload = script.onreadystatechange = wrap;
112 | script.onerror = onerror;
113 | head.appendChild(script);
114 | timer = setTimeout(onerror, timeout);
115 | }
116 |
117 | function main(name, deps, callback) {
118 | var callbackType = typeof callback,
119 | args = [],
120 | usingExports = false,
121 | i = deps.length,
122 | next, len, cjsModule, depName;
123 |
124 | if (callbackType === 'undefined' || callbackType === 'function') {
125 | deps = !deps.length && (callback.length||callback._length) ? (i = 3, ['require', 'exports', 'module']) : deps;
126 |
127 | next = function() {
128 | var ret = callback ? callback.apply(defined[name], args) : undefined;
129 |
130 | if (name) {
131 | if (cjsModule && cjsModule.exports !== undef &&
132 | cjsModule.exports !== defined[name]) {
133 | defined[name] = cjsModule.exports;
134 | } else if (ret !== undef || !usingExports) {
135 | defined[name] = ret;
136 | }
137 | }
138 | };
139 |
140 | while (i--) {
141 | next = (function(next, depName, i) {
142 | return function() {
143 | var path;
144 |
145 | if (depName === "require") {
146 | args[i] = handlers.require(name);
147 | } else if (depName === "exports") {
148 | args[i] = handlers.exports(name);
149 | usingExports = true;
150 | } else if (depName === "module") {
151 | cjsModule = args[i] = handlers.module(name);
152 | } else if (hasProp(defined, depName) ||
153 | hasProp(waiting, depName) ||
154 | hasProp(defining, depName)) {
155 |
156 | return callDep(depName, function() {
157 | args[i] = callDep(depName);
158 | next();
159 | });
160 | } else {
161 | path = resolve(depName);
162 |
163 | return loadJs(path, function() {
164 | callDep(depName, function() {
165 | args[i] = callDep(depName);
166 | next();
167 | });
168 | });
169 | }
170 | next();
171 | }
172 | })(next, deps[i], i);
173 | }
174 | next();
175 | } else if (name) {
176 | defined[name] = callback;
177 | }
178 | }
179 |
180 | require = req = function(deps, callback) {
181 | if (typeof deps === "string") {
182 | return callDep(deps);
183 | }
184 |
185 | setTimeout(function() {
186 | main(undef, deps, callback);
187 | }, 4);
188 | };
189 |
190 | function extend(a, b) {
191 | var i, v;
192 |
193 | if (!a || !b || typeof b !== 'object') {
194 | return a;
195 | }
196 |
197 | for (i in b) {
198 | if (hasProp(b, i)) {
199 | v = b[i];
200 |
201 | if (typeof v === 'object' && !v.splice) {
202 | extend(a[i] || (a[i] = {}), v);
203 | } else {
204 | a[i] = v;
205 | }
206 | }
207 | }
208 | }
209 |
210 | req.config = function(cfg) {
211 | extend(config, cfg);
212 | };
213 |
214 | // define(id, deps ?, factory)
215 | define = function(name, deps, factory) {
216 | if (!deps.splice) {
217 | factory = deps;
218 | deps = [];
219 | }
220 |
221 | if (!hasProp(defined, name) && !hasProp(waiting, name)) {
222 | waiting[name] = [name, deps, factory];
223 | }
224 | }
225 |
226 | // what does this mean?
227 | define.amd = {
228 | jQuery: true
229 | };
230 | })();
--------------------------------------------------------------------------------
/static/libs/README.md:
--------------------------------------------------------------------------------
1 | 说明
2 | =======================
3 |
4 |
5 | 用来存放项目级别公用组件库
--------------------------------------------------------------------------------
/static/libs/_alert.tmpl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/libs/_confirm.tmpl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/libs/_modal.tmpl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/libs/alert.js:
--------------------------------------------------------------------------------
1 | require('bootstrap');
2 | var modalTplFn = __inline('./_alert.tmpl');
3 |
4 | // errorLevel 至少支持 danger、info、sucess、warning
5 | var alert = module.exports = function(content, errorLevel, title) {
6 | var dom = $(modalTplFn({
7 | title: title || '提示信息',
8 | content: content,
9 | errorLevel: errorLevel || 'info'
10 | }));
11 |
12 | dom
13 | .appendTo('body')
14 | .modal({
15 | backdrop: 'static'
16 | })
17 | .on('hide.bs.modal', function() {
18 | dom.remove();
19 | });
20 | };
--------------------------------------------------------------------------------
/static/libs/confirm.js:
--------------------------------------------------------------------------------
1 | require('bootstrap');
2 | var modalTplFn = __inline('./_confirm.tmpl');
3 | var noop = function() {};
4 | var defaultOptions = {
5 | title: '',
6 | confirmed: noop,
7 | canceled: noop,
8 | confirmTxt: '确认',
9 | cancelTxt: '取消'
10 | }
11 |
12 | var confirm = module.exports = function(content, opt) {
13 | if (typeof opt === 'function') {
14 | opt = {
15 | confirmed: opt
16 | };
17 | }
18 |
19 | opt = $.extend({}, defaultOptions, opt);
20 |
21 | var dom = $(modalTplFn({
22 | content: content,
23 | title: opt.title || '提示信息',
24 | confirmTxt: opt.confirmTxt,
25 | cancelTxt: opt.cancelTxt
26 | }));
27 |
28 | dom
29 | .appendTo('body')
30 | .on('click', '.btn-confirm', function() {
31 | opt.confirmed.apply(this, arguments);
32 | dom.modal('hide');
33 | })
34 | .on('click', '.btn-cancel', opt.canceled)
35 | .modal({
36 | backdrop: 'static'
37 | })
38 | .on('hide.bs.modal', function() {
39 | dom.remove();
40 | });
41 | };
--------------------------------------------------------------------------------
/static/libs/modal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 标记 css 依赖, 需要用到 loading 图标。
3 | * @require font-awesome/css/font-awesome.css
4 | */
5 |
6 | require('bootstrap');
7 | var modalTplFn = __inline('./_modal.tmpl');
8 |
9 | // errorLevel 至少支持 danger、info、sucess、warning
10 | var modal = module.exports = function(url, opt) {
11 | var dom = $(modalTplFn());
12 |
13 | if ($.isPlainObject(url)) {
14 | opt = url;
15 | url = null;
16 | }
17 |
18 | dom
19 | .appendTo('body')
20 | .modal($.extend({
21 | keyboard: false,
22 | backdrop: 'static',
23 | remote: url,
24 | show: true
25 | }, opt || {}))
26 | .on('hide.bs.modal', function() {
27 | dom.remove();
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/static/libs/scrollspy.js:
--------------------------------------------------------------------------------
1 | // fix bootstrap 对中文标题不支持的bug
2 | require('bootstrap');
3 | var $ = require('jquery');
4 | var ScrollSpy = $.fn.scrollspy.Constructor;
5 |
6 |
7 | ScrollSpy.prototype.refresh = function() {
8 | var offsetMethod = 'offset'
9 | var offsetBase = 0
10 | if (!$.isWindow(this.$scrollElement[0])) {
11 | offsetMethod = 'position'
12 | offsetBase = this.$scrollElement.scrollTop()
13 | }
14 | this.offsets = []
15 | this.targets = []
16 | this.scrollHeight = this.getScrollHeight()
17 | var self = this
18 | this.$body.find(this.selector).map(function() {
19 | var $el = $(this)
20 | var href = $el.data('target') || $el.attr('href')
21 | var $href = /^#./.test(href) && (/^#[a-zA-Z_\d]+$/.test(href) ? $(href) : $('[id="' + href.substr(1) + '"]'))
22 | return ($href && $href.length && $href.is(':visible') && [
23 | [$href[offsetMethod]().top + offsetBase, href]
24 | ]) || null
25 | }).sort(function(a, b) {
26 | return a[0] - b[0]
27 | }).each(function() {
28 | self.offsets.push(this[0])
29 | self.targets.push(this[1])
30 | })
31 | };
--------------------------------------------------------------------------------
/static/libs/validator.js:
--------------------------------------------------------------------------------
1 | var $ = require('jquery');
2 | var validator = module.exports = require('jquery-validation');
3 |
4 | require('jquery-validation/localization/messages_zh');
5 |
6 | $.validator.setDefaults({
7 | ignore: 'input[type=hidden]:not(.form-item)',
8 | highlight: function(element) {
9 | var tabcontent = $(element).closest('.tab-pane');
10 |
11 | if (tabcontent.not('.active')) {
12 | $('a[href=#' + tabcontent.attr('id') + ']').tab('show');
13 | }
14 |
15 | $(element).closest('.form-group').addClass('has-error');
16 | },
17 | unhighlight: function(element) {
18 | $(element).closest('.form-group').removeClass('has-error');
19 | },
20 | errorElement: 'span',
21 | errorClass: 'help-block',
22 | errorPlacement: function(error, element) {
23 | if (element.parent('.input-group').length) {
24 | error.insertAfter(element.parent());
25 | } else if(element.is('input[type=radio]') && element.closest('.radio-inline').length) {
26 | error.insertAfter(element.closest('.radio-inline').parent());
27 | } else {
28 | error.insertAfter(element);
29 | }
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/static/scss/_callout.scss:
--------------------------------------------------------------------------------
1 | $brand-primary: #428bca;
2 | $brand-success: #5cb85c;
3 | $brand-info: #5bc0de;
4 | $brand-warning: #f0ad4e;
5 | $brand-danger: #d9534f;
6 |
7 | @mixin bs-callout($color, $bgcolor) {
8 | display: block;
9 | margin: 20px 0;
10 | padding: 15px 30px 15px 15px;
11 | border: 1px solid;
12 | border-left-width: 5px;
13 | border-color: lighten($color, 25%);
14 | border-left-color: $color;
15 | background-color: $bgcolor;
16 | h1, h2, h3, h4, h5, h6 {
17 | margin-top: 0;
18 | color: $color;
19 | }
20 | p:last-child {
21 | margin-bottom: 0;
22 | }
23 | code, .highlight {
24 | background-color: #fff;
25 | }
26 | }
27 |
28 | .bs-callout-primary {
29 | @include bs-callout($brand-primary, lighten($brand-primary, 45%));
30 | }
31 |
32 | .bs-callout-danger {
33 | @include bs-callout($brand-danger, lighten($brand-danger, 30%));
34 | }
35 |
36 | .bs-callout-warning {
37 | @include bs-callout($brand-warning, lighten($brand-warning, 50%));
38 | }
39 |
40 | .bs-callout-info {
41 | @include bs-callout($brand-info, lighten($brand-info, 40%));
42 | }
43 |
44 | .bs-callout-success {
45 | @include bs-callout($brand-success, lighten($brand-success, 40%));
46 | }
47 |
--------------------------------------------------------------------------------
/static/scss/_highlight.scss:
--------------------------------------------------------------------------------
1 | code {
2 | font-family:'Bitstream Vera Sans Mono','Courier', monospace;
3 | }
4 |
5 | .highlight {
6 | margin-top: 1em;
7 | font-family:'Bitstream Vera Sans Mono','Courier', monospace;
8 |
9 | .c { color: #586E75 } /* Comment */
10 | .err { color: #93A1A1 } /* Error */
11 | .g { color: #93A1A1 } /* Generic */
12 | .k { color: #859900 } /* Keyword */
13 | .l { color: #93A1A1 } /* Literal */
14 | .n { color: #93A1A1 } /* Name */
15 | .o { color: #859900 } /* Operator */
16 | .x { color: #CB4B16 } /* Other */
17 | .p { color: #93A1A1 } /* Punctuation */
18 | .cm { color: #586E75 } /* Comment.Multiline */
19 | .cp { color: #859900 } /* Comment.Preproc */
20 | .c1 { color: #586E75 } /* Comment.Single */
21 | .cs { color: #859900 } /* Comment.Special */
22 | .gd { color: #2AA198 } /* Generic.Deleted */
23 | .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */
24 | .gr { color: #DC322F } /* Generic.Error */
25 | .gh { color: #CB4B16 } /* Generic.Heading */
26 | .gi { color: #859900 } /* Generic.Inserted */
27 | .go { color: #93A1A1 } /* Generic.Output */
28 | .gp { color: #93A1A1 } /* Generic.Prompt */
29 | .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */
30 | .gu { color: #CB4B16 } /* Generic.Subheading */
31 | .gt { color: #93A1A1 } /* Generic.Traceback */
32 | .kc { color: #CB4B16 } /* Keyword.Constant */
33 | .kd { color: #268BD2 } /* Keyword.Declaration */
34 | .kn { color: #859900 } /* Keyword.Namespace */
35 | .kp { color: #859900 } /* Keyword.Pseudo */
36 | .kr { color: #268BD2 } /* Keyword.Reserved */
37 | .kt { color: #DC322F } /* Keyword.Type */
38 | .ld { color: #93A1A1 } /* Literal.Date */
39 | .m { color: #2AA198 } /* Literal.Number */
40 | .s { color: #2AA198 } /* Literal.String */
41 | .na { color: #93A1A1 } /* Name.Attribute */
42 | .nb { color: #B58900 } /* Name.Builtin */
43 | .nc { color: #268BD2 } /* Name.Class */
44 | .no { color: #CB4B16 } /* Name.Constant */
45 | .nd { color: #268BD2 } /* Name.Decorator */
46 | .ni { color: #CB4B16 } /* Name.Entity */
47 | .ne { color: #CB4B16 } /* Name.Exception */
48 | .nf { color: #268BD2 } /* Name.Function */
49 | .nl { color: #93A1A1 } /* Name.Label */
50 | .nn { color: #93A1A1 } /* Name.Namespace */
51 | .nx { color: #555 } /* Name.Other */
52 | .py { color: #93A1A1 } /* Name.Property */
53 | .nt { color: #268BD2 } /* Name.Tag */
54 | .nv { color: #268BD2 } /* Name.Variable */
55 | .ow { color: #859900 } /* Operator.Word */
56 | .w { color: #93A1A1 } /* Text.Whitespace */
57 | .mf { color: #2AA198 } /* Literal.Number.Float */
58 | .mh { color: #2AA198 } /* Literal.Number.Hex */
59 | .mi { color: #2AA198 } /* Literal.Number.Integer */
60 | .mo { color: #2AA198 } /* Literal.Number.Oct */
61 | .sb { color: #586E75 } /* Literal.String.Backtick */
62 | .sc { color: #2AA198 } /* Literal.String.Char */
63 | .sd { color: #93A1A1 } /* Literal.String.Doc */
64 | .s2 { color: #2AA198 } /* Literal.String.Double */
65 | .se { color: #CB4B16 } /* Literal.String.Escape */
66 | .sh { color: #93A1A1 } /* Literal.String.Heredoc */
67 | .si { color: #2AA198 } /* Literal.String.Interpol */
68 | .sx { color: #2AA198 } /* Literal.String.Other */
69 | .sr { color: #DC322F } /* Literal.String.Regex */
70 | .s1 { color: #2AA198 } /* Literal.String.Single */
71 | .ss { color: #2AA198 } /* Literal.String.Symbol */
72 | .bp { color: #268BD2 } /* Name.Builtin.Pseudo */
73 | .vc { color: #268BD2 } /* Name.Variable.Class */
74 | .vg { color: #268BD2 } /* Name.Variable.Global */
75 | .vi { color: #268BD2 } /* Name.Variable.Instance */
76 | .il { color: #2AA198 } /* Literal.Number.Integer.Long */
77 | }
78 |
79 | .pl-c {
80 | color: #969896
81 | }
82 |
83 | .pl-c1,.pl-mdh,.pl-mm,.pl-mp,.pl-mr,.pl-s1 .pl-v,.pl-s3,.pl-sc,.pl-sv {
84 | color: #0086b3
85 | }
86 |
87 | .pl-e,.pl-en {
88 | color: #795da3
89 | }
90 |
91 | .pl-s1 .pl-s2,.pl-smi,.pl-smp,.pl-stj,.pl-vo,.pl-vpf {
92 | color: #333
93 | }
94 |
95 | .pl-ent {
96 | color: #63a35c
97 | }
98 |
99 | .pl-k,.pl-s,.pl-st {
100 | color: #a71d5d
101 | }
102 |
103 | .pl-pds,.pl-s1,.pl-s1 .pl-pse .pl-s2,.pl-sr,.pl-sr .pl-cce,.pl-sr .pl-sra,.pl-sr .pl-sre,.pl-src {
104 | color: #df5000
105 | }
106 |
107 | .pl-mo,.pl-v {
108 | color: #1d3e81
109 | }
110 |
111 | .pl-id {
112 | color: #b52a1d
113 | }
114 |
115 | .pl-ii {
116 | background-color: #b52a1d;
117 | color: #f8f8f8
118 | }
119 |
120 | .pl-sr .pl-cce {
121 | color: #63a35c;
122 | font-weight: bold
123 | }
124 |
125 | .pl-ml {
126 | color: #693a17
127 | }
128 |
129 | .pl-mh,.pl-mh .pl-en,.pl-ms {
130 | color: #1d3e81;
131 | font-weight: bold
132 | }
133 |
134 | .pl-mq {
135 | color: #008080
136 | }
137 |
138 | .pl-mi {
139 | color: #333;
140 | font-style: italic
141 | }
142 |
143 | .pl-mb {
144 | color: #333;
145 | font-weight: bold
146 | }
147 |
148 | .pl-md,.pl-mdhf {
149 | background-color: #ffecec;
150 | color: #bd2c00
151 | }
152 |
153 | .pl-mdht,.pl-mi1 {
154 | background-color: #eaffea;
155 | color: #55a532
156 | }
157 |
158 | .pl-mdr {
159 | color: #795da3;
160 | font-weight: bold
161 | }
162 |
163 |
164 |
--------------------------------------------------------------------------------
/static/scss/_modal.scss:
--------------------------------------------------------------------------------
1 | @import "compass/css3";
2 |
3 | .modal {
4 |
5 | .modal-loading {
6 | text-align: center;
7 | min-height: 200px;
8 | line-height: 200px;
9 | font-size: 30px;
10 | }
11 |
12 | .modal-table {
13 | display: table;
14 | width: 100%;
15 | height: 100%;
16 |
17 | .modal-table-cell {
18 | display: table-cell;
19 | vertical-align: middle;
20 | }
21 | }
22 |
23 | .modal-info {
24 | color: #31708f;
25 |
26 | .modal-header {
27 | background: #d9edf7;
28 | @include border-radius(6px 6px 0 0);
29 | }
30 | }
31 | .modal-sucess {
32 | color: #3c763d;
33 |
34 | .modal-header {
35 | background: #d6e9c6;
36 | @include border-radius(6px 6px 0 0);
37 | }
38 | }
39 | .modal-danger {
40 | color: #a94442;
41 |
42 | .modal-header {
43 | background: #f2dede;
44 | @include border-radius(6px 6px 0 0);
45 | }
46 |
47 | }
48 | .modal-warning {
49 | color: #8a6d3b;
50 |
51 | .modal-header {
52 | background: #fcf8e3;
53 | @include border-radius(6px 6px 0 0);
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/static/scss/global.scss:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | }
4 |
5 | .navbar-brand.active {
6 | color: #eee;
7 | }
8 |
9 | #middle {
10 | min-height: 400px;
11 | }
12 |
13 | .markdown {
14 | h2 {
15 | padding-top: 70px;
16 | margin-top: -50px;
17 | }
18 | }
19 |
20 | @import "callout";
21 | @import "highlight";
22 | @import "modal";
23 |
--------------------------------------------------------------------------------
/static/scss/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */
2 |
3 | /**
4 | * 1. Set default font family to sans-serif.
5 | * 2. Prevent iOS text size adjust after orientation change, without disabling
6 | * user zoom.
7 | */
8 |
9 | html {
10 | font-family: sans-serif; /* 1 */
11 | -ms-text-size-adjust: 100%; /* 2 */
12 | -webkit-text-size-adjust: 100%; /* 2 */
13 | }
14 |
15 | /**
16 | * Remove default margin.
17 | */
18 |
19 | body {
20 | margin: 0;
21 | }
22 |
23 | /* HTML5 display definitions
24 | ========================================================================== */
25 |
26 | /**
27 | * Correct `block` display not defined for any HTML5 element in IE 8/9.
28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11
29 | * and Firefox.
30 | * Correct `block` display not defined for `main` in IE 11.
31 | */
32 |
33 | article,
34 | aside,
35 | details,
36 | figcaption,
37 | figure,
38 | footer,
39 | header,
40 | hgroup,
41 | main,
42 | menu,
43 | nav,
44 | section,
45 | summary {
46 | display: block;
47 | }
48 |
49 | /**
50 | * 1. Correct `inline-block` display not defined in IE 8/9.
51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
52 | */
53 |
54 | audio,
55 | canvas,
56 | progress,
57 | video {
58 | display: inline-block; /* 1 */
59 | vertical-align: baseline; /* 2 */
60 | }
61 |
62 | /**
63 | * Prevent modern browsers from displaying `audio` without controls.
64 | * Remove excess height in iOS 5 devices.
65 | */
66 |
67 | audio:not([controls]) {
68 | display: none;
69 | height: 0;
70 | }
71 |
72 | /**
73 | * Address `[hidden]` styling not present in IE 8/9/10.
74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
75 | */
76 |
77 | [hidden],
78 | template {
79 | display: none;
80 | }
81 |
82 | /* Links
83 | ========================================================================== */
84 |
85 | /**
86 | * Remove the gray background color from active links in IE 10.
87 | */
88 |
89 | a {
90 | background-color: transparent;
91 | }
92 |
93 | /**
94 | * Improve readability when focused and also mouse hovered in all browsers.
95 | */
96 |
97 | a:active,
98 | a:hover {
99 | outline: 0;
100 | }
101 |
102 | /* Text-level semantics
103 | ========================================================================== */
104 |
105 | /**
106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
107 | */
108 |
109 | abbr[title] {
110 | border-bottom: 1px dotted;
111 | }
112 |
113 | /**
114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
115 | */
116 |
117 | b,
118 | strong {
119 | font-weight: bold;
120 | }
121 |
122 | /**
123 | * Address styling not present in Safari and Chrome.
124 | */
125 |
126 | dfn {
127 | font-style: italic;
128 | }
129 |
130 | /**
131 | * Address variable `h1` font-size and margin within `section` and `article`
132 | * contexts in Firefox 4+, Safari, and Chrome.
133 | */
134 |
135 | h1 {
136 | font-size: 2em;
137 | margin: 0.67em 0;
138 | }
139 |
140 | /**
141 | * Address styling not present in IE 8/9.
142 | */
143 |
144 | mark {
145 | background: #ff0;
146 | color: #000;
147 | }
148 |
149 | /**
150 | * Address inconsistent and variable font size in all browsers.
151 | */
152 |
153 | small {
154 | font-size: 80%;
155 | }
156 |
157 | /**
158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
159 | */
160 |
161 | sub,
162 | sup {
163 | font-size: 75%;
164 | line-height: 0;
165 | position: relative;
166 | vertical-align: baseline;
167 | }
168 |
169 | sup {
170 | top: -0.5em;
171 | }
172 |
173 | sub {
174 | bottom: -0.25em;
175 | }
176 |
177 | /* Embedded content
178 | ========================================================================== */
179 |
180 | /**
181 | * Remove border when inside `a` element in IE 8/9/10.
182 | */
183 |
184 | img {
185 | border: 0;
186 | }
187 |
188 | /**
189 | * Correct overflow not hidden in IE 9/10/11.
190 | */
191 |
192 | svg:not(:root) {
193 | overflow: hidden;
194 | }
195 |
196 | /* Grouping content
197 | ========================================================================== */
198 |
199 | /**
200 | * Address margin not present in IE 8/9 and Safari.
201 | */
202 |
203 | figure {
204 | margin: 1em 40px;
205 | }
206 |
207 | /**
208 | * Address differences between Firefox and other browsers.
209 | */
210 |
211 | hr {
212 | -moz-box-sizing: content-box;
213 | box-sizing: content-box;
214 | height: 0;
215 | }
216 |
217 | /**
218 | * Contain overflow in all browsers.
219 | */
220 |
221 | pre {
222 | overflow: auto;
223 | }
224 |
225 | /**
226 | * Address odd `em`-unit font size rendering in all browsers.
227 | */
228 |
229 | code,
230 | kbd,
231 | pre,
232 | samp {
233 | font-family: monospace, monospace;
234 | font-size: 1em;
235 | }
236 |
237 | /* Forms
238 | ========================================================================== */
239 |
240 | /**
241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited
242 | * styling of `select`, unless a `border` property is set.
243 | */
244 |
245 | /**
246 | * 1. Correct color not being inherited.
247 | * Known issue: affects color of disabled elements.
248 | * 2. Correct font properties not being inherited.
249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | color: inherit; /* 1 */
258 | font: inherit; /* 2 */
259 | margin: 0; /* 3 */
260 | }
261 |
262 | /**
263 | * Address `overflow` set to `hidden` in IE 8/9/10/11.
264 | */
265 |
266 | button {
267 | overflow: visible;
268 | }
269 |
270 | /**
271 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
272 | * All other form control elements do not inherit `text-transform` values.
273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
274 | * Correct `select` style inheritance in Firefox.
275 | */
276 |
277 | button,
278 | select {
279 | text-transform: none;
280 | }
281 |
282 | /**
283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
284 | * and `video` controls.
285 | * 2. Correct inability to style clickable `input` types in iOS.
286 | * 3. Improve usability and consistency of cursor style between image-type
287 | * `input` and others.
288 | */
289 |
290 | button,
291 | html input[type="button"], /* 1 */
292 | input[type="reset"],
293 | input[type="submit"] {
294 | -webkit-appearance: button; /* 2 */
295 | cursor: pointer; /* 3 */
296 | }
297 |
298 | /**
299 | * Re-set default cursor for disabled elements.
300 | */
301 |
302 | button[disabled],
303 | html input[disabled] {
304 | cursor: default;
305 | }
306 |
307 | /**
308 | * Remove inner padding and border in Firefox 4+.
309 | */
310 |
311 | button::-moz-focus-inner,
312 | input::-moz-focus-inner {
313 | border: 0;
314 | padding: 0;
315 | }
316 |
317 | /**
318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
319 | * the UA stylesheet.
320 | */
321 |
322 | input {
323 | line-height: normal;
324 | }
325 |
326 | /**
327 | * It's recommended that you don't attempt to style these elements.
328 | * Firefox's implementation doesn't respect box-sizing, padding, or width.
329 | *
330 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
331 | * 2. Remove excess padding in IE 8/9/10.
332 | */
333 |
334 | input[type="checkbox"],
335 | input[type="radio"] {
336 | box-sizing: border-box; /* 1 */
337 | padding: 0; /* 2 */
338 | }
339 |
340 | /**
341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain
342 | * `font-size` values of the `input`, it causes the cursor style of the
343 | * decrement button to change from `default` to `text`.
344 | */
345 |
346 | input[type="number"]::-webkit-inner-spin-button,
347 | input[type="number"]::-webkit-outer-spin-button {
348 | height: auto;
349 | }
350 |
351 | /**
352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
354 | * (include `-moz` to future-proof).
355 | */
356 |
357 | input[type="search"] {
358 | -webkit-appearance: textfield; /* 1 */
359 | -moz-box-sizing: content-box;
360 | -webkit-box-sizing: content-box; /* 2 */
361 | box-sizing: content-box;
362 | }
363 |
364 | /**
365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X.
366 | * Safari (but not Chrome) clips the cancel button when the search input has
367 | * padding (and `textfield` appearance).
368 | */
369 |
370 | input[type="search"]::-webkit-search-cancel-button,
371 | input[type="search"]::-webkit-search-decoration {
372 | -webkit-appearance: none;
373 | }
374 |
375 | /**
376 | * Define consistent border, margin, and padding.
377 | */
378 |
379 | fieldset {
380 | border: 1px solid #c0c0c0;
381 | margin: 0 2px;
382 | padding: 0.35em 0.625em 0.75em;
383 | }
384 |
385 | /**
386 | * 1. Correct `color` not being inherited in IE 8/9/10/11.
387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
388 | */
389 |
390 | legend {
391 | border: 0; /* 1 */
392 | padding: 0; /* 2 */
393 | }
394 |
395 | /**
396 | * Remove default vertical scrollbar in IE 8/9/10/11.
397 | */
398 |
399 | textarea {
400 | overflow: auto;
401 | }
402 |
403 | /**
404 | * Don't inherit the `font-weight` (applied by a rule above).
405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
406 | */
407 |
408 | optgroup {
409 | font-weight: bold;
410 | }
411 |
412 | /* Tables
413 | ========================================================================== */
414 |
415 | /**
416 | * Remove most spacing between table cells.
417 | */
418 |
419 | table {
420 | border-collapse: collapse;
421 | border-spacing: 0;
422 | }
423 |
424 | td,
425 | th {
426 | padding: 0;
427 | }
--------------------------------------------------------------------------------
/test/data/ajax.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="application/json; charset=UTF-8" import="java.util.Date" %>{
2 | "code": 0,
3 | "message": "成功",
4 | "data": {
5 | "username": "admin",
6 | "role": "admin",
7 | "nickname": "诸多借口",
8 | "time": "<%= new Date()%>",
9 | "description": "这个是动态的数据,因为是用 jsp 输出的,可以按自己的需求模拟。"
10 | }
11 | }
--------------------------------------------------------------------------------
/test/data/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": 0,
3 | "message": "成功",
4 | "data": {
5 | "username": "admin",
6 | "role": "admin",
7 | "nickname": "诸多借口",
8 | "description": "这是静态数据,写死的不会变"
9 | }
10 | }
--------------------------------------------------------------------------------
/test/data/saveform.json:
--------------------------------------------------------------------------------
1 | {
2 | "error": 0,
3 | "message": "保存成功"
4 | }
--------------------------------------------------------------------------------
/test/delay.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %><%
2 | // sleep 3 second.
3 | Thread.sleep(1000);
4 |
5 | String url = request.getParameter("forward");
6 | if (url != null && !url.isEmpty()) {
7 | request.getRequestDispatcher(url).forward(request, response);
8 | } else {
9 | response.getWriter().write("只负责延时,不负责具体输出,请指定重定向地址。");
10 | }
11 | %>
--------------------------------------------------------------------------------
/test/mock.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 | <%
3 | response.getWriter().write("hello world");
4 | %>
5 |
--------------------------------------------------------------------------------
/test/page.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "jello demo",
3 | "titleAffix": "jello demo",
4 | "pageTitle": "页面标题",
5 |
6 | "menus": [
7 | {
8 | "label": "文档",
9 | "href": "/doc",
10 | "children": [
11 | {
12 | "label": "目录规范",
13 | "href": "/doc/standard"
14 | },
15 |
16 | {
17 | "label": "模板继承",
18 | "href": "/doc/layout"
19 | },
20 |
21 | {
22 | "label": "widget",
23 | "href": "/doc/widget"
24 | },
25 |
26 | {
27 | "label": "页面模拟",
28 | "href": "/doc/rewrite"
29 | },
30 |
31 | {
32 | "label": "数据绑定",
33 | "href": "/doc/binding"
34 | }
35 | ]
36 | },
37 | {
38 | "label": "示例",
39 | "href": "/examples",
40 | "children": [
41 | {
42 | "label": "普通列表页面",
43 | "href": "/examples/pagination"
44 | },
45 | {
46 | "label": "js 数据模拟",
47 | "href": "/examples/data"
48 | },
49 | {
50 | "label": "表单验证",
51 | "href": "/examples/form"
52 | },
53 | {
54 | "label": "ajax 页面",
55 | "href": "/examples/partial"
56 | },
57 | {
58 | "label": "SPA 单页 demo",
59 | "href": "/examples/spa"
60 | }
61 | ]
62 | },
63 |
64 | {
65 | "label": "Jsp",
66 | "href": "/jsp",
67 | "children": [
68 | {
69 | "label": "Jsp 语法说明",
70 | "href": "/jsp/index"
71 | },
72 | {
73 | "label": "jsp 模板继承",
74 | "href": "/jsp/layout"
75 | },
76 | {
77 | "label": "jsp widget",
78 | "href": "/jsp/widget"
79 | },
80 | {
81 | "label": "页面模拟",
82 | "href": "/jsp/rewrite"
83 | },
84 |
85 | {
86 | "label": "数据绑定",
87 | "href": "/jsp/binding"
88 | }
89 | ]
90 | },
91 |
92 | {
93 | "label": "模板技巧",
94 | "href": "/velocity",
95 | "children": [
96 | {
97 | "label": "velocity 语法",
98 | "href": "/velocity/index"
99 | },
100 |
101 | {
102 | "label": "全局变量",
103 | "href": "/velocity/variables"
104 | },
105 |
106 | {
107 | "label": "velocity tools",
108 | "href": "/velocity/tools"
109 | },
110 |
111 | {
112 | "label": "jello tools",
113 | "href": "/velocity/jello"
114 | }
115 | ]
116 | },
117 |
118 | {
119 | "label": "后端使用",
120 | "href": "/be",
121 | "children": [
122 | {
123 | "label": "spring 整合",
124 | "href": "/be/spring"
125 | },
126 |
127 | {
128 | "label": "全局变量",
129 | "href": "/be/variables"
130 | }
131 | ]
132 | }
133 | ]
134 | }
135 |
--------------------------------------------------------------------------------
/test/page.jsp:
--------------------------------------------------------------------------------
1 | <%!
2 | // 将 menus 中当前 url 标记为 active
3 | public Boolean markActiveMenus(JSONArray menus, String path) {
4 | Boolean hasActive = false;
5 |
6 | for (int i = 0, len = menus.size(); i < len; i++) {
7 | JSONObject item = menus.getJSONObject(i);
8 |
9 | Boolean childActive = false;
10 | if (item.containsKey("children")) {
11 | childActive = markActiveMenus(item.getJSONArray("children"), path);
12 | }
13 |
14 | if (childActive || item.containsKey("href") && path.startsWith(item.getString("href"))) {
15 | item.put("active", true);
16 | hasActive = true;
17 | }
18 | }
19 |
20 | return hasActive;
21 | }
22 |
23 | %><%@ page import="org.apache.velocity.context.Context" %>
24 | <%@ page import="com.alibaba.fastjson.JSONArray" %>
25 | <%@ page import="com.alibaba.fastjson.JSONObject" %>
26 | <%
27 |
28 | Context context = (Context)request.getAttribute("context");
29 |
30 |
31 | JSONArray menus = (JSONArray)context.get("menus");
32 | String path = request.getAttribute("javax.servlet.forward.request_uri").toString();
33 | if (path == null) {
34 | path = request.getServletPath();
35 | }
36 |
37 | path = path.substring(request.getContextPath().length());
38 |
39 | context.put("currentUrl", path);
40 |
41 | if (menus != null && path != null) {
42 | markActiveMenus(menus, path);
43 | }
44 | %>
45 |
--------------------------------------------------------------------------------
/test/page/doc/layout.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "模板继承",
3 | "pageTitle": "模板继承",
4 |
5 | "sidebar": {
6 | "menus": [
7 | {
8 | "label": "示例",
9 | "href": "#示例"
10 | },
11 |
12 | {
13 | "label": "说明",
14 | "href": "#说明"
15 | },
16 |
17 | {
18 | "label": "局部变量",
19 | "href": "#局部变量"
20 | }
21 | ]
22 | }
23 | }
--------------------------------------------------------------------------------
/test/page/doc/standard.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "目录规范",
3 | "pageTitle": "目录规范",
4 |
5 | "sidebar": {
6 | "menus": [
7 | {
8 | "label": "前端目录",
9 | "href": "#%E5%89%8D%E7%AB%AF%E7%9B%AE%E5%BD%95"
10 | },
11 |
12 | {
13 | "label": "产出目录",
14 | "href": "#%E4%BA%A7%E5%87%BA%E7%9B%AE%E5%BD%95"
15 | }
16 | ]
17 | }
18 | }
--------------------------------------------------------------------------------
/test/page/examples.json:
--------------------------------------------------------------------------------
1 | {
2 | "action": "/examples/form/save"
3 | }
--------------------------------------------------------------------------------
/test/page/examples/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "js 数据模拟",
3 | "pageTitle": "js 数据模拟",
4 |
5 | "user": {
6 | "name": "张三",
7 | "address": "中国"
8 | }
9 | }
--------------------------------------------------------------------------------
/test/page/examples/pagination.json:
--------------------------------------------------------------------------------
1 | {
2 | "result": [
3 | "结果内容 1",
4 | "结果内容 2",
5 | "结果内容 3",
6 | "结果内容 4",
7 | "结果内容 5",
8 | "结果内容 6",
9 | "结果内容 7",
10 | "结果内容 8",
11 | "结果内容 9",
12 | "结果内容 10"
13 | ]
14 | }
--------------------------------------------------------------------------------
/test/page/examples/partial.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "ajax 页面",
3 | "pageTitle": "ajax 页面"
4 | }
--------------------------------------------------------------------------------
/test/page/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "欢迎查看 jello demo 页面"
3 | }
--------------------------------------------------------------------------------
/widget/footer/footer.jsp:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/widget/footer/footer.vm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/widget/header/header.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/html;charset=utf-8" %><%@ taglib uri="/fis" prefix="fis"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2 |
3 |
4 |
5 |
36 |
37 |
--------------------------------------------------------------------------------
/widget/header/header.vm:
--------------------------------------------------------------------------------
1 | #if ($menus)
2 |
3 | ## velocity 宏示例
4 | #macro( renderMenus $menus $child)
5 | #if( $child )
6 |