├── .edpproj
└── .gitignore
├── .gitignore
├── README.md
├── demo
├── .gitignore
├── ActionPanel.html
├── ChildView.html
├── Test.js
├── TestView.js
├── chi
│ ├── .edpproj
│ │ └── .gitignore
│ ├── README.md
│ ├── index.htm
│ ├── package.json
│ ├── server
│ │ ├── affair.js
│ │ ├── app.js
│ │ ├── database.js
│ │ ├── member.js
│ │ └── package.json
│ └── src
│ │ ├── affair
│ │ ├── Form.js
│ │ ├── FormModel.js
│ │ ├── FormView.js
│ │ ├── List.js
│ │ ├── ListModel.js
│ │ ├── ListView.js
│ │ ├── config.js
│ │ ├── css
│ │ │ └── form.css
│ │ ├── form.tpl
│ │ └── list.tpl
│ │ ├── common
│ │ ├── css
│ │ │ └── operation.css
│ │ ├── esl-1.3.0.min.js
│ │ ├── esl-css.js
│ │ ├── main.js
│ │ └── sugar-1.3.9.min.js
│ │ └── member
│ │ ├── Form.js
│ │ ├── FormModel.js
│ │ ├── FormView.js
│ │ ├── List.js
│ │ ├── ListModel.js
│ │ ├── ListView.js
│ │ ├── config.js
│ │ ├── css
│ │ └── form.css
│ │ ├── form.tpl
│ │ └── list.tpl
└── esl.js
├── doc
├── ActionPanel.md
├── UIModel.md
└── UIView.md
├── edp-build-config.js
├── module.conf
├── package.json
├── src
├── .jshintrc
├── ActionDialog.js
├── ActionPanel.js
├── ChildView.js
├── RemoteTreeStrategy.js
├── UIModel.js
└── UIView.js
└── test
├── UIView.js
├── asset
├── css
│ └── jasmine.css
└── js
│ ├── async.js
│ ├── esl.js
│ ├── jasmine-html.js
│ └── jasmine.js
├── matchers.js
├── run.htm
└── tpl
└── plain.tpl
/.edpproj/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ecomfe/ef/53701663eb471f4a824151262c618458b6487789/.edpproj/.gitignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | dep
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EF (Enterprise Framework)
2 |
3 | EF是[ER](https://github.com/ecomfe/er)与[ESUI](https://github.com/ecomfe/esui)整合而成的高效率业务系统开发框架。
4 |
5 | ## 关于名称
6 |
7 | EF是 **Enterprise Framework** 的简称,表明其作为企业级开发框架的身份。
8 |
9 | 另外该项目负责人很喜欢一部叫EF的动画
10 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | dep
2 | *.css
--------------------------------------------------------------------------------
/demo/ActionPanel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ActionPanel
6 |
7 |
54 |
55 |
56 |
57 |
104 |
105 |
--------------------------------------------------------------------------------
/demo/ChildView.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ChildView
6 |
7 |
54 |
63 |
64 |
65 |
66 | Refresh
67 |
77 |
78 |
--------------------------------------------------------------------------------
/demo/Test.js:
--------------------------------------------------------------------------------
1 | define(function (require) {
2 | var Action = require('er/Action');
3 | var Deferred = require('er/Deferred');
4 | var util = require('er/util');
5 |
6 | function Test() {
7 | Action.apply(this, arguments);
8 | }
9 |
10 | Test.prototype.enter = function () {
11 | console.log('enter test');
12 | var me = this;
13 | setTimeout(
14 | function () {
15 | var event = me.fire('test', { x: 1 });
16 | console.log(event.isDefaultPrevented());
17 | console.log(event.isPropagationStopped());
18 | },
19 | 500
20 | );
21 | return Deferred.resolved();
22 | };
23 |
24 | util.inherits(Test, Action);
25 | return Test;
26 | })
--------------------------------------------------------------------------------
/demo/TestView.js:
--------------------------------------------------------------------------------
1 | define(function (require) {
2 | var View = require('er/View');
3 | var util = require('er/util');
4 |
5 | function TestView() {
6 | View.apply(this, arguments);
7 | }
8 |
9 | TestView.prototype.render = function () {
10 | this.getContainerElement().innerHTML = 'Hello World, it\'s now ' + (new Date()).toLocaleTimeString();;
11 | };
12 |
13 | util.inherits(TestView, View);
14 | return TestView;
15 | })
--------------------------------------------------------------------------------
/demo/chi/.edpproj/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ecomfe/ef/53701663eb471f4a824151262c618458b6487789/demo/chi/.edpproj/.gitignore
--------------------------------------------------------------------------------
/demo/chi/README.md:
--------------------------------------------------------------------------------
1 | # 吃!
2 |
3 | 饭团管理系统
4 |
5 | ## 启动方法
6 |
7 | 1. `cd {ef的根目录}/demo/chi`
8 | 2. `mkdir lib && cd lib`
9 | 3. `git clone git://github.com/ecomfe/er.git`
10 | 4. `git clone git://github.com/ecomfe/esui.git`
11 | 5. `cd {ef的根目录}/demo/chi/server`
12 | 6. `npm install`
13 | 7. `node app.js`
14 | 8. 访问 **http://localhost:8788**
--------------------------------------------------------------------------------
/demo/chi/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 吃!
6 |
7 |
8 |
9 |
10 |
11 |
12 |
61 |
62 |
--------------------------------------------------------------------------------
/demo/chi/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chi",
3 | "version": "0.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "ef": "3.1.x"
7 | },
8 | "edp": {
9 | "wwwroot": "/",
10 | "depDir": "dep",
11 | "srcDir": "src",
12 | "loaderAutoConfig": "js,htm,html,tpl,vm,phtml",
13 | "loaderUrl": "http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-0/esl.js",
14 | "dependencies": {
15 | "ef": "3.1.x"
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/demo/chi/server/affair.js:
--------------------------------------------------------------------------------
1 | var database = require('./database');
2 |
3 | function getAffair(context) {
4 | var member = database.members({ id: +context.member }).get()[0];
5 | var affair = {
6 | id: +context.id,
7 | time: context.time,
8 | member: {
9 | id: member.id,
10 | name: member.name
11 | },
12 | type: +context.type,
13 | amount: +context.amount
14 | };
15 | affair.balance = affair.type === 0
16 | ? member.balance + affair.amount
17 | : member.balance - affair.amount;
18 |
19 | return affair;
20 | }
21 |
22 | exports.list = function (req, res) {
23 | var page = database.getPage('affairs', req.body.page, req.body.pageSize);
24 |
25 | res.writeHead(200, { 'Content-Type': 'application/json' });
26 | res.end(JSON.stringify(page));
27 | };
28 |
29 | exports.save = function (req, res) {
30 | var id = database.affairs().count() + 1;
31 | var affair = getAffair(req.body);
32 | affair.id = id;
33 | database.affairs.insert(affair);
34 |
35 | res.writeHead(200, { 'Content-Type': 'application/json' });
36 | res.end(JSON.stringify({ success: true }));
37 | };
38 |
39 | exports.update = function (req, res) {
40 | var affair = getAffair(req.body);
41 | database.affairs.merge(affair, 'id');
42 |
43 | res.writeHead(200, { 'Content-Type': 'application/json' });
44 | res.end(JSON.stringify({ success: true }));
45 | };
46 |
47 | exports.find = function (req, res) {
48 | var affair = database.affairs({ id: +req.query.id }).get()[0];
49 |
50 | res.writeHead(200, { 'Content-Type': 'application/json' });
51 | res.end(JSON.stringify(affair));
52 | };
--------------------------------------------------------------------------------
/demo/chi/server/app.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Module dependencies.
4 | */
5 |
6 | var express = require('express');
7 | var http = require('http');
8 | var path = require('path');
9 |
10 | var app = express();
11 |
12 | // all environments
13 | app.set('port', process.env.PORT || 8788);
14 | app.use(express.favicon());
15 | app.use(express.logger('dev'));
16 | app.use(express.bodyParser());
17 | app.use(express.methodOverride());
18 | app.use(app.router);
19 | app.use(express.static(path.join(__dirname, '..', '..', '..')));
20 |
21 | // development only
22 | if ('development' == app.get('env')) {
23 | app.use(express.errorHandler());
24 | }
25 | app.get(
26 | '/',
27 | function(req, res) {
28 | res.writeHead(302, { 'Location': '/demo/chi/index.htm' });
29 | res.end();
30 | }
31 | );
32 | var member = require('./member');
33 | app.get('/member/list', member.list);
34 | app.post('/member/save', member.save);
35 | app.post('/member/update', member.update);
36 | app.get('/member/find', member.find);
37 | app.post('/member/remove', member.remove);
38 | var affair = require('./affair');
39 | app.get('/affair/list', affair.list);
40 | app.post('/affair/save', affair.save);
41 | app.post('/affair/update', affair.update);
42 | app.get('/affair/find', affair.find);
43 |
44 | http.createServer(app).listen(app.get('port'), function (){
45 | console.log('open http://localhost:' + app.get('port'));
46 | });
47 |
--------------------------------------------------------------------------------
/demo/chi/server/database.js:
--------------------------------------------------------------------------------
1 | var Taffy = require('taffydb').taffy;
2 |
3 | function random(start, end) {
4 | var value = Math.round(Math.random() * (end - start)) + start;
5 | return value;
6 | }
7 |
8 | var members = Taffy();
9 | members.insert({ id: 1, name: '李享', gender: 0, birthday: '1983-05-28', balance: 233 });
10 | members.insert({ id: 2, name: '石磊', gender: 0, birthday: '1984-07-22', balance: 1024 });
11 | members.insert({ id: 3, name: '李义冬', gender: 1, birthday: '1987-08-13', balance: 198 });
12 | members.insert({ id: 4, name: '刘恺华', gender: 1, birthday: '1986-08-25', balance: -1322 });
13 | members.insert({ id: 5, name: '沈彬', gender: 1, birthday: '1988-03-14', balance: 4 });
14 | members.insert({ id: 6, name: '张立理', gender: 1, birthday: '1986-10-05', balance: -130 });
15 | members.insert({ id: 7, name: '孙金飞', gender: 1, birthday: '1980-03-11', balance: 250 });
16 | members.insert({ id: 8, name: '叶梦秋', gender: 0, birthday: '1987-11-1', balance: 14 });
17 |
18 | var affairs = Taffy();
19 | for (var i = 0; i < 36; i++) {
20 | var member = members({ id: random(1, 8) }).get()[0];
21 | var item = {
22 | id: i + 1,
23 | time: '2013-' + random(1, 12) + '-' + random(1, 28),
24 | member: {
25 | id: member.id,
26 | name: member.name
27 | },
28 | type: random(0, 1),
29 | amount: random(0, 43),
30 | balance: random(-990, 988)
31 | };
32 | affairs.insert(item);
33 | }
34 |
35 | var activities = Taffy();
36 |
37 | var database = module.exports = {
38 | members: members,
39 | affairs: affairs,
40 |
41 | getPage: function (table, page, pageSize) {
42 | page = page ? +page : 1;
43 | pageSize = pageSize ? +pageSize : 10;
44 | if (typeof table === 'string') {
45 | table = database[table];
46 | }
47 | table = table();
48 |
49 | return {
50 | totalCount: table.count(),
51 | page: page,
52 | pageSize: 10,
53 | results: table.start((page - 1) * pageSize).limit(pageSize).get()
54 | };
55 | }
56 | }
--------------------------------------------------------------------------------
/demo/chi/server/member.js:
--------------------------------------------------------------------------------
1 | var database = require('./database');
2 |
3 | function getMember(context) {
4 | var member = {
5 | id: +context.id,
6 | name: context.name,
7 | gender: +context.gender,
8 | birthday: context.birthday
9 | };
10 |
11 | return member;
12 | }
13 |
14 | exports.list = function (req, res) {
15 | var page = database.getPage('members', req.body.page, req.body.pageSize);
16 |
17 | res.writeHead(200, { 'Content-Type': 'application/json' });
18 | res.end(JSON.stringify(page));
19 | };
20 |
21 | exports.save = function (req, res) {
22 | var id = database.members().count() + 1;
23 | var member = getMember(req.body);
24 | member.balance = 0;
25 | member.id = id;
26 | database.members.insert(member);
27 |
28 | res.writeHead(200, { 'Content-Type': 'application/json' });
29 | res.end(JSON.stringify({ success: true }));
30 | };
31 |
32 | exports.update = function (req, res) {
33 | var member = getMember(req.body);
34 | database.members.merge(member, 'id');
35 |
36 | res.writeHead(200, { 'Content-Type': 'application/json' });
37 | res.end(JSON.stringify({ success: true }));
38 | };
39 |
40 | exports.find = function (req, res) {
41 | var member = database.members({ id: +req.query.id }).get()[0];
42 |
43 | res.writeHead(200, { 'Content-Type': 'application/json' });
44 | res.end(JSON.stringify(member));
45 | };
46 |
47 | exports.remove = function (req, res) {
48 | var member = database.members({ id: +req.body.id }).remove();
49 |
50 | res.writeHead(200, { 'Content-Type': 'application/json' });
51 | res.end(JSON.stringify({ success: true }));
52 | };
--------------------------------------------------------------------------------
/demo/chi/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chi-server",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node app.js"
7 | },
8 | "dependencies": {
9 | "express": "3.2.4",
10 | "moment": "*",
11 | "taffydb": "*"
12 | }
13 | }
--------------------------------------------------------------------------------
/demo/chi/src/affair/Form.js:
--------------------------------------------------------------------------------
1 | define(
2 | function(require) {
3 | var Action = require('er/Action');
4 |
5 | function AffairForm() {
6 | Action.apply(this, arguments);
7 | }
8 |
9 | function cancelSubmit() {
10 | this.redirect('/affair/list');
11 | }
12 |
13 | function submitAffair(e) {
14 | this.model.save(e.affair).then(saveCallBack.bind(this));
15 | }
16 |
17 | function saveCallBack(response) {
18 | if (response.success === true) {
19 | this.redirect('/affair/list');
20 | }
21 | else {
22 | alert('出错啦!');
23 | }
24 | }
25 |
26 | AffairForm.prototype.modelType = require('./FormModel');
27 |
28 | AffairForm.prototype.viewType = require('./FormView');
29 |
30 | AffairForm.prototype.initBehavior = function() {
31 | this.view.on('submit', submitAffair.bind(this));
32 | this.view.on('cancel', cancelSubmit.bind(this));
33 | };
34 |
35 | require('er/util').inherits(AffairForm, Action);
36 |
37 | return AffairForm;
38 | }
39 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/FormModel.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var UIModel = require('ef/UIModel');
4 | var datasource = require('er/datasource');
5 |
6 | var types = [
7 | {
8 | text: '充值',
9 | value: 0
10 | },
11 | {
12 | text: '支出',
13 | value: 1
14 | }
15 | ];
16 |
17 | function AffairFormModel() {
18 | UIModel.apply(this, arguments);
19 |
20 | var url = this.get('url');
21 |
22 | this.datasource = {
23 | members: datasource.remote(
24 | '/member/list',
25 | { method: 'GET' }
26 | ),
27 | types: datasource.constant(types)
28 | };
29 |
30 | if (this.get('formType') === 'update') {
31 |
32 | this.datasource.detail = datasource.remote(
33 | '/affair/find',
34 | {
35 | method: 'GET',
36 | data: {
37 | id: url.getQuery('id'),
38 | }
39 | }
40 | );
41 | }
42 | else {
43 | var detail = { balance: 0, member: {} };
44 | this.datasource.detail = datasource.constant(detail);
45 | }
46 | }
47 |
48 | AffairFormModel.prototype.prepare = function () {
49 | var members = this.get('members').results;
50 | var list = members.map(
51 | function (m) { return { text: m.name, value: m.id } });
52 | this.set('members', list);
53 | }
54 |
55 | AffairFormModel.prototype.save = function(data) {
56 | this.fill(data);
57 |
58 | var postData = this.getPart.apply(this, Object.keys(data));
59 | // update请求要多个id字段
60 | if (this.get('formType') === 'update') {
61 | var url = this.get('url');
62 | postData.id = url.getQuery('id');
63 | }
64 |
65 | var ajax = require('er/ajax');
66 | var url = this.get('formType') === 'update'
67 | ? '/affair/update'
68 | : '/affair/save';
69 | return ajax.post(url, postData);
70 | };
71 |
72 | AffairFormModel.prototype.formatters = {
73 | time: UIModel.formatters.time
74 | };
75 |
76 | require('er/util').inherits(AffairFormModel, UIModel);
77 | return AffairFormModel;
78 | }
79 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/FormView.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | require('er/tpl!./form.tpl');
4 | require('esui/Form');
5 | require('esui/Label');
6 | require('esui/Button');
7 | require('esui/TextBox');
8 | require('esui/Select');
9 | require('esui/Calendar');
10 | // css
11 | require('css!./css/form.css');
12 |
13 | var AffairType = require('./config').AffairType;
14 |
15 |
16 | var UIView = require('ef/UIView');
17 |
18 | function AffairFormView() {
19 | UIView.apply(this, arguments);
20 | }
21 |
22 | function submit() {
23 | var form = this.get('form');
24 | var data = form.getData();
25 | this.fire('submit', {affair: data});
26 | }
27 |
28 | function cancel() {
29 | this.fire('cancel');
30 | }
31 |
32 | AffairFormView.prototype.template = 'affairForm';
33 |
34 | AffairFormView.prototype.enterDocument = function() {
35 | UIView.prototype.enterDocument.apply(this, arguments);
36 | var form = this.get('form');
37 | form.on('submit', require('er/util').bind(submit, this));
38 |
39 | var cancelButton = this.get('cancel-button');
40 | cancelButton.on('click', require('er/util').bind(cancel, this))
41 | };
42 |
43 |
44 | require('er/util').inherits(AffairFormView, UIView);
45 | return AffairFormView;
46 | }
47 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/List.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var Action = require('er/Action');
4 |
5 | function AffairList() {
6 | Action.apply(this, arguments);
7 | }
8 |
9 | AffairList.prototype.modelType = require('./ListModel');
10 |
11 | AffairList.prototype.viewType = require('./ListView');
12 |
13 | AffairList.prototype.initBehavior = function () {
14 | var action = this;
15 | this.view.on(
16 | 'modify',
17 | function (e) {
18 | action.redirect('/affair/update~id=' + e.id);
19 | }
20 | );
21 | this.view.on(
22 | 'create',
23 | this.redirect.bind(this, '/affair/create')
24 | );
25 | };
26 |
27 | require('er/util').inherits(AffairList, Action);
28 | return AffairList;
29 | }
30 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/ListModel.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var Model = require('er/Model');
4 | var datasource = require('er/datasource');
5 |
6 | function AffairListModel() {
7 | Model.apply(this, arguments);
8 |
9 | var url = this.get('url');
10 | this.datasource = {
11 | list: datasource.remote(
12 | '/affair/list',
13 | {
14 | method: 'GET',
15 | data: {
16 | page: url.getQuery('page') || 0,
17 | pageSize: url.getQuery('pageSize') || 0
18 | }
19 | }
20 | )
21 | };
22 | }
23 |
24 | require('er/util').inherits(AffairListModel, Model);
25 | return AffairListModel;
26 | }
27 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/ListView.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | require('er/tpl!./list.tpl');
4 | require('esui/Button');
5 | require('esui/Table');
6 | require('esui/extension/Command');
7 |
8 |
9 | var AffairType = require('./config').AffairType;
10 | var tableFields = [
11 | {
12 | title: '时间',
13 | field: 'time' ,
14 | tip :'时间',
15 | width: 50,
16 | content: 'time'
17 | },
18 | {
19 | title: '成员',
20 | field: 'member' ,
21 | tip :'成员姓名',
22 | width: 50,
23 | content: function (item) {
24 | return item.member.name;
25 | }
26 | },
27 | {
28 | title: '类型',
29 | field: 'type' ,
30 | tip :'收支类型',
31 | width: 50,
32 | content: function (item) {
33 | return AffairType[item.type];
34 | }
35 | },
36 | {
37 | title: '金额',
38 | field: 'amount' ,
39 | tip :'金额',
40 | width: 50,
41 | content: 'amount'
42 | },
43 | {
44 | title: '余额',
45 | field: 'balance' ,
46 | tip :'开支后余额',
47 | width: 50,
48 | content: 'balance'
49 | },
50 | {
51 | title: '操作',
52 | width: 150,
53 | content: function (item) {
54 | return '编辑';
56 | }
57 | }
58 | ];
59 |
60 | var UIView = require('ef/UIView');
61 |
62 | function AffairListView() {
63 | UIView.apply(this, arguments);
64 | }
65 |
66 | AffairListView.prototype.template = 'affairListPage';
67 |
68 | AffairListView.prototype.uiProperties = {
69 | list: {
70 | fields: tableFields,
71 | sortable: false,
72 | columnResizable: true,
73 | subrow: false,
74 | followHead: true,
75 | selectMode: 'line'
76 | }
77 | };
78 |
79 | AffairListView.prototype.enterDocument = function () {
80 | UIView.prototype.enterDocument.apply(this, arguments);
81 | this.get('createButton').on(
82 | 'click',
83 | this.fire.bind(this, 'create')
84 | );
85 | }
86 |
87 | require('er/util').inherits(AffairListView, UIView);
88 | return AffairListView;
89 | }
90 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/config.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var actions = [
4 | {
5 | path: '/',
6 | type: 'affair/List'
7 | },
8 | {
9 | path: '/affair/list',
10 | type: 'affair/List'
11 | },
12 | {
13 | path: '/affair/create',
14 | type: 'affair/Form',
15 | args: { formType: 'create' }
16 | },
17 | {
18 | path: '/affair/update',
19 | type: 'affair/Form',
20 | args: { formType: 'update' }
21 | }
22 | ];
23 |
24 | var controller = require('er/controller');
25 | actions.forEach(controller.registerAction);
26 |
27 | var config = {};
28 | config.AffairType = {
29 | 'CONSUMPTION': 0,
30 | 'DEPOSITE': 1,
31 | '0': '充值',
32 | '1': '支出'
33 | };
34 |
35 | return config;
36 | }
37 | );
--------------------------------------------------------------------------------
/demo/chi/src/affair/css/form.css:
--------------------------------------------------------------------------------
1 | /* 清除浮动 */
2 | .form-row,
3 | .form-line,
4 | .form-single-line,
5 | .form-value,
6 | .form-value-mini,
7 | .edit-submit {
8 | zoom:1;
9 | }
10 |
11 | .form-row:after,
12 | .form-line:after,
13 | .form-single-line:after,
14 | .form-value:after,
15 | .form-value-mini:after,
16 | .edit-submit:after {
17 | content:"\0020";
18 | display:block;
19 | height:0;
20 | visibility:hidden;
21 | clear:both;
22 | }
23 |
24 |
25 | /* 表单部分 */
26 | .edit-form {
27 | background:#FFF;
28 | margin-bottom:10px;
29 | padding: 5px;
30 | }
31 |
32 | /* 表单头部 */
33 | .edit-form .form-head {
34 | border-width: 1px 1px 0 1px;
35 | border-style: solid;
36 | border-color: #9ecae8;
37 | background: #c7deed;
38 | height:28px;
39 | }
40 |
41 | .edit-form .form-headcntr {
42 | height:27px;
43 | line-height:27px;
44 | color:#1c4a79;
45 | border-top:1px solid #d8e8f2;
46 | }
47 |
48 | .edit-form .form-headcntr h2 {
49 | padding-left:20px;
50 | font-weight:700;
51 | font-size:12px;
52 | }
53 |
54 |
55 | /* 表单行 */
56 | .form-row {
57 | padding:2px 0;
58 | }
59 |
60 | .form-line,
61 | .form-single-line,
62 | .form-key,
63 | .form-key-mini,
64 | .form-value-mini,
65 | .form-value {
66 | float:left;
67 | display:inline;
68 | line-height:34px;
69 | }
70 |
71 | .form-line,
72 | .form-value {
73 | width: auto;
74 | }
75 |
76 | .form-value-mini {
77 | width:300px;
78 | padding:2px;
79 | }
80 | .ui-dialog-body .form-value {
81 | width:auto;
82 | }
83 |
84 | .form-value-preview {
85 | line-height:0;
86 | }
87 |
88 | /* 表单key */
89 | .form-key,
90 | .form-key-mini {
91 | float:left;
92 | padding:2px 0 3px 10px;
93 | width:85px;
94 | color:#333333;
95 | }
96 |
97 | .form-key-mini {
98 | padding-left:0;
99 | }
100 |
101 |
102 | /* 表单value */
103 | .form-value {
104 | padding:2px;
105 | }
106 | .form-value-caution {
107 | float:left;
108 | color:red;
109 | }
110 |
111 |
112 | .form-tip{
113 | color:#999999;
114 | }
115 | /*有必填标记的表单value*/
116 | .form-value-required{
117 | position:relative;
118 | left:-8px;
119 | }
120 | /*必填标记*/
121 | .required-icon{
122 | float:left;
123 | margin-right:3px;
124 | color:red;
125 | }
126 | /* 单独占一行 */
127 | .form-single-line {
128 | padding:2px 0 2px 36px;
129 | width:500px;
130 | clear:both;
131 | }
132 |
133 | .form-line {
134 | clear:both;
135 | }
136 |
137 | /* 表单内控件样式微调 */
138 | .form-row .ui-checkbox,
139 | .form-row .ui-radiobox {
140 | display: inline;
141 | margin-top:5px;
142 | }
143 |
144 | .form-row .ui-text {
145 | margin-right:8px;
146 | }
147 |
148 | .form-row .ui-checkbox-label,
149 | .form-row .ui-radiobox-label {
150 | display: inline;
151 | line-height: 24px;
152 | margin: 0 8px 0 0px;
153 | }
154 |
155 | /* 表单文字 */
156 | .form-text {
157 | float:left;
158 | display:inline;
159 | margin-right:8px;
160 | line-height:34px;
161 | }
162 |
163 | /* 表单小文字 */
164 | .form-text-s {
165 | font-size:10px;
166 | color:#999;
167 | }
168 | .form-text-arrow{
169 | font-family:simsun;
170 | font-size:12px;
171 | }
172 | /* 表单内容块*/
173 | .form-content-block {
174 | padding-left:98px;
175 | }
176 |
177 | .form-row .ui-combobox {
178 | float:left;
179 | margin-right:5px;
180 | }
181 |
182 | .form-row .ui-select,
183 | .form-row .ui-combobox,
184 | .form-row .ui-button,
185 | .form-row .ui-text,
186 | .form-row .ui-textarea,
187 | .form-row .ui-cal,
188 | .form-row .ui-buttonmenu,
189 | .form-row .ui-combosearchlistsel {
190 | margin-top: 5px;
191 | }
192 |
193 | .form-row .ui-text {
194 | *margin-top:1px;
195 | vertical-align: middle;
196 | *height: 21px;
197 | *line-height: 21px;
198 | }
199 |
200 | .form-row .ui-textarea{
201 | margin-right:5px;
202 | }
203 | /* 提交部分 */
204 | .edit-submit {
205 | /*margin:0 0 0 21px;*/
206 | zoom:1;
207 | }
208 |
209 |
210 | /* 表单验证提示 */
211 | .validate-error {
212 | padding:5px;
213 | display:block;
214 | }
215 | .validate {
216 | /*
217 | background:#FEF7DB;
218 | border:1px solid #F0DDA5;
219 | */
220 | position:relative;
221 | zoom:1;
222 | height: 25px;
223 | }
224 |
225 | .validate-text {
226 | background: none repeat scroll 0 0 #FEDBDC;
227 | border: 1px solid #F0CCCC;
228 | color: #DD6666;
229 | display: block;
230 | height: 25px;
231 | line-height: 25px;
232 | margin-top: 8px;
233 | padding-left: 45px;
234 | }
235 |
236 | .validate-text-nobr {
237 | color: red;
238 | line-height: 34px;
239 | padding-left: 5px;
240 | }
241 |
242 | .validate-icon {
243 | background:#FEDBDC url(../../assets/img/allbgs.png) -23px -208px no-repeat;
244 | position: absolute;
245 | left: 15px;
246 | top: 7px;
247 | width: 14px;
248 | height: 14px;
249 | }
250 |
251 | /* 表单分支链接(支线任务) */
252 | .form-branch-task {
253 | font-family:STHeiti, SimSun;
254 | position:absolute;
255 | right:20px;
256 | top:20px;
257 | }
258 |
259 | /* 选填 ,灰色*/
260 | .form-optional,.form-grey {
261 | float:left;
262 | display:inline;
263 | margin-left:5px;
264 | line-height:24px;
265 | color:#CCC;
266 | }
267 |
268 | .ui-richsel-entry .form-optional {
269 | line-height:22px;
270 | }
271 |
272 | .form-grey{float:none;}
273 | .form-grey2{color:#CCC;}
274 | /* 表单提示信息(先放在这里,组件做好再转移) */
275 | .ui-forminfo {
276 | position:relative;
277 | margin:1px;
278 | background:#EFF7F9;
279 | border:1px solid #C5DFF0;
280 | clear:both;
281 | zoom:1;
282 | }
283 |
284 | .ui-forminfo .ui-link {
285 | text-decoration:underline;
286 | }
287 |
288 | .ui-forminfo-icon {
289 | position:absolute;
290 | width:14px;
291 | height:17px;
292 | background:url(../../assets/img/BlockSprites.png) no-repeat -141px -315px;
293 | left:40px;
294 | top:6px;
295 | }
296 |
297 | .ui-forminfo-text {
298 | padding:4px 10px 4px 64px;
299 | line-height:24px;
300 | color:#8BA9C4;
301 | }
302 |
303 | .ui-forminfo-text strong {
304 | margin:0 3px;
305 | }
306 |
307 | /**
308 | * 表单内的iframe
309 | */
310 | .form-iframe {
311 | width:850px;
312 | height:450px;
313 | overflow-x:hidden;
314 | border:none;
315 | }
316 |
317 | .form-iframe-wrapper {
318 | border-top:1px solid #DCE3E9;
319 | margin:10px;
320 | padding:5px 0;
321 | width:850px;
322 | }
323 |
324 | /**
325 | * 缩进HACK
326 | */
327 | .indent-checkbox {
328 | padding-left: 17px;
329 | *padding-left: 23px;
330 | }
331 | /**
332 | * 表单元素组缩进
333 | */
334 | .form-row-group
335 | {
336 | margin-left: 35px;
337 | }
338 |
--------------------------------------------------------------------------------
/demo/chi/src/affair/form.tpl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/chi/src/affair/list.tpl:
--------------------------------------------------------------------------------
1 |
2 | 新建收支
4 |
5 |
6 |
7 |
9 |
--------------------------------------------------------------------------------
/demo/chi/src/common/css/operation.css:
--------------------------------------------------------------------------------
1 | .operation-remove {
2 | color: red;
3 | cursor: pointer;
4 | }
5 | .operation-modify {
6 | color: #666666;
7 | cursor: pointer;
8 | }
--------------------------------------------------------------------------------
/demo/chi/src/common/esl-1.3.0.min.js:
--------------------------------------------------------------------------------
1 | var define,require;(function(n){function t(){var n=arguments.length;if(n){for(var t,r,i=arguments[--n];n--;){var o=arguments[n];"string"==typeof o?t=o:B(o)&&(r=o)}var a=window.opera;if(!t&&document.attachEvent&&(!a||"[object Opera]"!=""+a)){var u=_();t=u&&u.getAttribute("data-require-id")}r=r||["require","exports","module"],t?e(t,r,i):W.push({deps:r,factory:i})}}function r(n){var t=[];for(var r in N){var e=N[r];e.state==n&&t.push(e)}return t}function e(n,t,r){if(!v(n)){var e={id:n,deps:t,factory:r,exports:{},hardDeps:[],softDeps:[],state:P};N[n]=e}}function i(){var n=[],t=r(P);F(t,function(t){var r=t.deps.slice(0);t.realDeps=r;var e=t.factory,i=/require\(\s*(['"'])([^'"]+)\1\s*\)/g,o=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/gm;"function"==typeof e&&(""+e).replace(o,"").replace(i,function(n,t,e){r.push(e)}),F(r,function(r){var e=z(r);e.resource&&n.push(U(e.module,t.id))}),t.state=C}),y(n,function(){o(t)})}function o(n){var t=[];F(n,function(n){var r=n.id,e=n.realDeps,i=n.hardDeps,o=n.softDeps,u={},f=n.deps;F(f,function(n,t){n=U(n,r),f[t]=n,u[n]||g(r,n,"hardDeps")||(i.push(n),u[n]=1)});for(var c=e.length,s={};c--;){var v=U(e[c],r);!v||v in s||v in V?e.splice(c,1):(s[v]=1,e[c]=v,u[v]||o.unshift(v),t.push(v))}n.state=R,a(n)}),y(t)}function a(t){function r(){var n=1;return F(t.hardDeps,function(t){return n=t in V||p(t),!!n}),n&&F(t.softDeps,function(t){return n=t in V||p(t)||g(i,t),!!n}),n&&(t.state=G),n}function e(){if(!p(i)&&r()){var a=[];F(t.deps,function(n,t){a[t]=o[n]||l(n)});try{var f=t.factory,s="function"==typeof f?f.apply(n,a):f;s!==void 0&&(t.exports=s)}catch(v){if(0===v.message.indexOf("[MODULE_MISS]"))return;throw v}t.state=H,c(e),u(i)}}var i=t.id,o={require:O(i),exports:t.exports,module:t};s(e),e()}function u(n){Q++,F(J,function(t){t&&t(n)}),Q--,f()}function f(){1>Q&&(K.sort(function(n,t){return t-n}),F(K,function(n){J.splice(n,1)}),K=[])}function c(n){F(J,function(t,r){n==t&&K.push(r)})}function s(n){J.push(n)}function v(n){return n in N}function p(n){return v(n)&&N[n].state==H}function l(n){return v(n)?N[n].exports:null}function d(n){return N[n]}function h(n,t){N[n]={exports:t||!0,state:H},u(n)}function m(n){var t=W.slice(0);W.length=0,W=[],F(t,function(t){var r=t.id||n;e(r,t.deps,t.factory)}),i()}function g(n,t,r,e){r=r||"realDeps";var i=d(t),o=i&&i[r];if(e=e||{},e[t])return 0;if(e[t]=1,o)for(var a=o.length;a--;){var u=o[a];if(n==u||g(n,u,r,e))return 1}return 0}function y(t,r,e){function i(){function e(n){var t=1;return F(n,function(n){return a[n]||(a[n]=1,V[n])?void 0:p(n)&&e(d(n).realDeps)?void 0:(t=0,!1)}),t}if(!o){var a={};if(e(t)){o=1,c(i);var u=[];F(t,function(n){u.push(V[n]||l(n))}),r.apply(n,u)}}}if(r=r||Function(),e=e||"","string"==typeof t){if(!p(t))throw Error("[MODULE_MISS]"+t+" is not exists!");return l(t)}if(B(t)){if(0===t.length)return r(),void 0;var o=0;s(i),F(t,function(n){n in V||(n.indexOf("!")>0?D:x)(n,e)}),i()}}function x(n){function t(){var t=r.readyState;(t===void 0||/^(loaded|complete)$/.test(t))&&(r.onload=r.onreadystatechange=null,m(n),delete X[n],r=null)}if(!v(n)&&!X[n]){X[n]=1;var r=document.createElement("script");r.setAttribute("data-require-id",n),r.src=w(n)+".js",r.async=!0,r.readyState?r.onreadystatechange=t:r.onload=t,A(r)}}function D(n,t){function r(t){h(n,t)}function e(e){p(n)||e.load(a,O(t),r,{})}var i=z(n),o=i.module,a=i.resource;r.fromText=function(n,t){Function(t)(),m(n)},p(o)?e(l(o)):y([o],e)}function b(n,t){var r=Y[n];if("string"==typeof r)Y[n]=t;else if(B(r))F(t,function(n){r.push(n)});else for(var e in t)r[e]=t[e]}function k(){Y.baseUrl=Y.baseUrl.replace(/\/$/,"")+"/",q(),E(),S()}function S(){Z=[],F(Y.packages,function(n){var t=n;"string"==typeof n&&(t={name:n.split("/")[0],location:n,main:"main"}),t.location=t.location||t.name,t.main=(t.main||"main").replace(/\.js$/i,""),Z.push(t)}),Z.sort(I("name"))}function q(){nt=M(Y.paths),nt.sort(I())}function E(){tt=[],tt=M(Y.map),tt.sort(I()),F(tt,function(n){var t=n.k;n.v=M(n.v),n.v.sort(I()),n.reg="*"==t?/^/:T(t)})}function w(n){if(!it.test(n))return n;var t=n,r=0;return F(nt,function(n){var e=n.k;return T(e).test(t)?(t=t.replace(e,n.v),r=1,!1):void 0}),r||F(Z,function(r){var e=r.name;return T(e).test(n)?(t=t.replace(e,r.location),!1):void 0}),/^([a-z]{2,10}:\/)?\//i.test(t)||(t=Y.baseUrl+t),t}function O(n){function t(t,e){if("string"==typeof t){var i;return(i=r[t])||(i=y(U(t,n),e,n),r[t]=i),i}if(B(t)){var o=[];F(t,function(t){var r=z(t),e=U(r.module,n);r.resource&&!p(e)&&o.push(e)}),y(o,function(){var r=[];F(t,function(t){r.push(U(t,n))}),y(r,e,n)},n)}}var r={};return t.toUrl=function(t){return w(U(t,n))},t}function U(n,t){if(!n)return"";var r=z(n);if(!r)return n;var e=r.resource,i=$(r.module,t);if(F(Z,function(n){var t=n.name,r=t+"/"+n.main;return t==i?(i=i.replace(t,r),!1):void 0}),i=j(i,t),e){var o=l(i);return e=o.normalize?o.normalize(e,function(n){return U(n,t)}):U(e,t),i+"!"+e}return i}function $(n,t){if(/^\.{1,2}/.test(n)){var r=t.split("/"),e=n.split("/"),i=r.length-1,o=e.length,a=0,u=0;n:for(var f=0;o>f;f++){var c=e[f];switch(c){case"..":if(!(i>a))break n;a++,u++;break;case".":u++;break;default:break n}}return r.length=i-a,e=e.slice(u),r.push.apply(r,e),r.join("/")}return n}function z(n){var t=n.split("!");return it.test(t[0])?{module:t[0],resource:t[1]||""}:null}function j(n,t){return F(tt,function(r){return r.reg.test(t)?(F(r.v,function(t){var r=t.k,e=T(r);return e.test(n)?(n=n.replace(r,t.v),!1):void 0}),!1):void 0}),n}function M(n){var t=[];for(var r in n)n.hasOwnProperty(r)&&t.push({k:r,v:n[r]});return t}function _(){if(rt)return rt;if(et&&"interactive"==et.readyState)return et;for(var n=document.getElementsByTagName("script"),t=n.length;t--;){var r=n[t];if("interactive"==r.readyState)return et=r,r}}function A(n){rt=n;var t=document;(t.getElementsByTagName("head")[0]||t.body).appendChild(n),rt=null}function T(n){return RegExp("^"+n+"(/|$)")}function B(n){return n instanceof Array}function F(n,t){if(B(n))for(var r=0,e=n.length;e>r&&t(n[r],r)!==!1;r++);}function I(n){return n=n||"k",function(t,r){var e=t[n],i=r[n];return"*"==i?-1:"*"==e?1:i.length-e.length}}var L=O("");t.amd={};var N={},P=1,C=2,R=3,G=4,H=5,J=[],K=[],Q=0,V={require:L,exports:1,module:1},W=[],X={},Y={baseUrl:"./",paths:{},config:{},map:{},packages:[]};L.config=function(n){for(var t in Y)n.hasOwnProperty(t)&&b(t,n[t]);k()},k();var Z,nt,tt,rt,et,it=/^[-_a-z0-9\.]+(\/[-_a-z0-9\.]+)*$/i;n.define=t,n.require=L})(this);
--------------------------------------------------------------------------------
/demo/chi/src/common/esl-css.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ESL (Enterprise Standard Loader)
3 | * Copyright 2013 Baidu Inc. All rights reserved.
4 | *
5 | * @file CSS Loader-Plugin
6 | * @author errorrik(errorrik@gmail.com)
7 | */
8 |
9 | // 构建环境暂不支持分析,为了能合并该plugin到loader里,
10 | // 只能暂时使用具名id
11 | define( 'css', {
12 | load: function ( resourceId, req, load, config ) {
13 | var link = document.createElement( 'link' );
14 | link.setAttribute( 'rel', 'stylesheet' );
15 | link.setAttribute( 'type', 'text/css' );
16 | link.setAttribute( 'href', req.toUrl( resourceId ) );
17 |
18 | var parent = document.getElementsByTagName( 'head' )[ 0 ] || document.body;
19 | parent.appendChild( link );
20 |
21 | parent = null;
22 | link = null;
23 |
24 | load( true );
25 | }
26 | } );
27 |
--------------------------------------------------------------------------------
/demo/chi/src/common/main.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | function init() {
4 | require('er/Deferred').syncModeEnabled = true;
5 | require('../affair/config');
6 | require('../member/config');
7 | // rule
8 | require('esui/validator/MaxRule');
9 | require('esui/validator/MinRule');
10 | require('esui/validator/MaxLengthRule');
11 | require('esui/validator/PatternRule');
12 | require('esui/validator/RequiredRule');
13 | require('esui/validator/MaxRule');
14 |
15 | require('er').start();
16 | }
17 |
18 | return {
19 | init: init
20 | };
21 | }
22 | );
--------------------------------------------------------------------------------
/demo/chi/src/common/sugar-1.3.9.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Sugar Library vedge
3 | *
4 | * Freely distributable and licensed under the MIT-style license.
5 | * Copyright (c) 2013 Andrew Plummer
6 | * http://sugarjs.com/
7 | *
8 | * ---------------------------- */
9 | (function(){var k=true,l=null,n=false;function aa(a){return function(){return a}}var p=Object,q=Array,r=RegExp,s=Date,t=String,u=Number,v=Math,ba=p.prototype.toString,ca=typeof global!=="undefined"?global:this,da={},ea=p.defineProperty&&p.defineProperties,x="Array,Boolean,Date,Function,Number,String,RegExp".split(","),ga=fa(x[0]),ha=fa(x[1]),ia=fa(x[2]),y=fa(x[3]),A=fa(x[4]),B=fa(x[5]),C=fa(x[6]);
10 | function fa(a){var b,c;if(/String|Number|Boolean/.test(a))b=a.toLowerCase();c=a==="Array"&&q.isArray||function(d){if(b&&typeof d===b)return k;return ba.call(d)==="[object "+a+"]"};return da[a]=c}function ja(a){if(!a.SugarMethods){ka(a,"SugarMethods",{});D(a,n,n,{extend:function(b,c,d){D(a,d!==n,c,b)},sugarRestore:function(){return la(a,arguments,function(b,c,d){ka(b,c,d.method)})},sugarRevert:function(){return la(a,arguments,function(b,c,d){if(d.qa)ka(b,c,d.Ba);else delete b[c]})}})}}
11 | function D(a,b,c,d){var e=b?a.prototype:a;ja(a);E(d,function(f,h){var i=e[f],j=F(e,f);if(typeof c==="function")h=ma(e[f],h,c);if(c!==n||!e[f])ka(e,f,h);a.SugarMethods[f]={xa:b,method:h,Ba:i,qa:j}})}function G(a,b,c,d,e){var f={};d=B(d)?d.split(","):d;d.forEach(function(h,i){e(f,h,i)});D(a,b,c,f)}function la(a,b,c){var d=b.length===0,e=H(b),f=n;E(a.SugarMethods,function(h,i){if(d||e.indexOf(h)>-1){f=k;c(i.xa?a.prototype:a,h,i)}});return f}
12 | function ma(a,b,c){return function(){return(a&&(c===k||!c.apply(this,arguments))?a:b).apply(this,arguments)}}function ka(a,b,c){if(ea)p.defineProperty(a,b,{value:c,configurable:k,enumerable:n,writable:k});else a[b]=c}function H(a,b){var c=[],d,e;d=0;for(e=a.length;d=b;){e.push(a);c&&c.call(this,a);a+=d||1}return e}function N(a,b,c){c=v[c||"round"];var d=v.pow(10,v.abs(b||0));if(b<0)d=1/d;return c(a*d)/d}function O(a,b){return N(a,b,"floor")}function P(a,b,c,d){d=v.abs(a).toString(d||10);d=ta(b-d.replace(/\.\d+/,"").length,"0")+d;if(c||a<0)d=(a<0?"-":"+")+d;return d}
15 | function ua(a){if(a>=11&&a<=13)return"th";else switch(a%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}}function va(){return"\t\n\u000b\u000c\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u2028\u2029\u3000\ufeff"}function ta(a,b){return q(v.max(0,I(a)?a:1)+1).join(b||"")}function wa(a,b){var c=a.toString().match(/[^/]*$/)[0];if(b)c=(c+b).split("").sort().join("").replace(/([gimy])\1+/g,"$1");return c}
16 | function R(a){B(a)||(a=t(a));return a.replace(/([\\/'*+?|()\[\]{}.^$])/g,"\\$1")}function xa(a,b){var c=typeof a,d,e,f,h,i,j,g;if(c==="string")return a;f=ba.call(a);d=L(a);e=f==="[object Array]";if(a!=l&&d||e){b||(b=[]);if(b.length>1)for(j=b.length;j--;)if(b[j]===a)return"CYC";b.push(a);d=t(a.constructor);h=e?a:p.keys(a).sort();j=0;for(g=h.length;j>0);if(c<0)c=e+c;if(!f&&c<0||f&&c>=e)c=h;for(;f&&c>=0||!f&&c>>0==e&&e!=4294967295&&e>=c&&d.push(parseInt(e));d.sort().each(function(f){return b.call(a,a[f],f,a)});return a}function Ha(a,b,c,d,e){var f,h;T(a,function(i,j,g){if(Ea(i,b,g,[i,j,g])){f=i;h=j;return n}},c,d);return e?h:f}
27 | function Ia(a,b){var c=[],d={},e;T(a,function(f,h){e=b?S(f,b,a,[f,h,a]):f;Ja(d,e)||c.push(f)});return c}function Ka(a,b,c){var d=[],e={};b.each(function(f){Ja(e,f)});a.each(function(f){var h=xa(f),i=!ya(f);if(La(e,h,f,i)!=c){var j=0;if(i)for(h=e[h];je||i&&g0)c=b}catch(d){}a=a.concat(c)});return a}});
31 | D(q,k,n,{find:function(a,b,c){return Ha(this,a,b,c)},findAll:function(a,b,c){var d=[];T(this,function(e,f,h){Ea(e,a,h,[e,f,h])&&d.push(e)},b,c);return d},findIndex:function(a,b,c){a=Ha(this,a,b,c,k);return K(a)?-1:a},count:function(a){if(K(a))return this.length;return this.findAll(a).length},removeAt:function(a,b){var c,d;if(K(a))return this;if(K(b))b=a;c=0;for(d=b-a;c<=d;c++)this.splice(a,1);return this},include:function(a,b){return this.clone().add(a,b)},exclude:function(){return q.prototype.remove.apply(this.clone(),
32 | arguments)},clone:function(){return qa([],this)},unique:function(a){return Ia(this,a)},flatten:function(a){return Ma(this,a)},union:function(){return Ia(this.concat(Na(arguments)))},intersect:function(){return Ka(this,Na(arguments),n)},subtract:function(){return Ka(this,Na(arguments),k)},at:function(){return za(this,arguments)},first:function(a){if(K(a))return this[0];if(a<0)a=0;return this.slice(0,a)},last:function(a){if(K(a))return this[this.length-1];return this.slice(this.length-a<0?0:this.length-
33 | a)},from:function(a){return this.slice(a)},to:function(a){if(K(a))a=this.length;return this.slice(0,a)},min:function(a,b){return Oa(this,a,"min",b)},max:function(a,b){return Oa(this,a,"max",b)},least:function(a,b){return Oa(this.groupBy.apply(this,[a]),"length","min",b)},most:function(a,b){return Oa(this.groupBy.apply(this,[a]),"length","max",b)},sum:function(a){a=a?this.map(a):this;return a.length>0?a.reduce(function(b,c){return b+c}):0},average:function(a){a=a?this.map(a):this;return a.length>0?
34 | a.sum()/a.length:0},inGroups:function(a,b){var c=arguments.length>1,d=this,e=[],f=N(this.length/a,void 0,"ceil");sa(0,a-1,function(h){h=h*f;var i=d.slice(h,h+f);c&&i.lengthh?1:0;return f*(b?-1:1)});return c},randomize:function(){for(var a=this.concat(),b=a.length,c,d;b;){c=v.random()*b|0;
36 | d=a[--b];a[b]=a[c];a[c]=d}return a},zip:function(){var a=H(arguments);return this.map(function(b,c){return[b].concat(a.map(function(d){return c in d?d[c]:l}))})},sample:function(a){var b=this.randomize();return arguments.length>0?b.slice(0,a):b[0]},each:function(a,b,c){T(this,a,b,c);return this},add:function(a,b){if(!A(u(b))||isNaN(b))b=this.length;q.prototype.splice.apply(this,[b,0].concat(a));return this},remove:function(){var a,b=this;H(arguments,function(c){for(a=0;a0&&!y(a[0])},"map,every,all,some,any,none,filter",function(a,b){a[b]=function(c){return this[b](function(d,e){return b==="map"?S(d,c,this,[d,e,this]):Ea(d,c,this,[d,e,this])})}})})();
41 | (function(){q[Va]="A\u00c1\u00c0\u00c2\u00c3\u0104BC\u0106\u010c\u00c7D\u010e\u00d0E\u00c9\u00c8\u011a\u00ca\u00cb\u0118FG\u011eH\u0131I\u00cd\u00cc\u0130\u00ce\u00cfJKL\u0141MN\u0143\u0147\u00d1O\u00d3\u00d2\u00d4PQR\u0158S\u015a\u0160\u015eT\u0164U\u00da\u00d9\u016e\u00db\u00dcVWXY\u00ddZ\u0179\u017b\u017d\u00de\u00c6\u0152\u00d8\u00d5\u00c5\u00c4\u00d6".split("").map(function(b){return b+b.toLowerCase()}).join("");var a={};T("A\u00c1\u00c0\u00c2\u00c3\u00c4,C\u00c7,E\u00c9\u00c8\u00ca\u00cb,I\u00cd\u00cc\u0130\u00ce\u00cf,O\u00d3\u00d2\u00d4\u00d5\u00d6,S\u00df,U\u00da\u00d9\u00db\u00dc".split(","),
42 | function(b){var c=b.charAt(0);T(b.slice(1).split(""),function(d){a[d]=c;a[d.toLowerCase()]=c.toLowerCase()})});q[Qa]=k;q[Ta]=a})();Xa(Ya);Xa(Za,k);Aa($a,ra);
43 | var U,bb,cb=["ampm","hour","minute","second","ampm","utc","offset_sign","offset_hours","offset_minutes","ampm"],db="({t})?\\s*(\\d{1,2}(?:[,.]\\d+)?)(?:{h}([0-5]\\d(?:[,.]\\d+)?)?{m}(?::?([0-5]\\d(?:[,.]\\d+)?){s})?\\s*(?:({t})|(Z)|(?:([+-])(\\d{2,2})(?::?(\\d{2,2}))?)?)?|\\s*({t}))",eb={},fb,gb,hb,ib=[],jb=[{ba:"f{1,4}|ms|milliseconds",format:function(a){return V(a,"Milliseconds")}},{ba:"ss?|seconds",format:function(a){return V(a,"Seconds")}},{ba:"mm?|minutes",format:function(a){return V(a,"Minutes")}},
44 | {ba:"hh?|hours|12hr",format:function(a){a=V(a,"Hours");return a===0?12:a-O(a/13)*12}},{ba:"HH?|24hr",format:function(a){return V(a,"Hours")}},{ba:"dd?|date|day",format:function(a){return V(a,"Date")}},{ba:"dow|weekday",la:k,format:function(a,b,c){a=V(a,"Day");return b.weekdays[a+(c-1)*7]}},{ba:"MM?",format:function(a){return V(a,"Month")+1}},{ba:"mon|month",la:k,format:function(a,b,c){a=V(a,"Month");return b.months[a+(c-1)*12]}},{ba:"y{2,4}|year",format:function(a){return V(a,"FullYear")}},{ba:"[Tt]{1,2}",
45 | format:function(a,b,c,d){if(b.ampm.length==0)return"";a=V(a,"Hours");b=b.ampm[O(a/12)];if(d.length===1)b=b.slice(0,1);if(d.slice(0,1)==="T")b=b.toUpperCase();return b}},{ba:"z{1,4}|tz|timezone",text:k,format:function(a,b,c,d){a=a.getUTCOffset();if(d=="z"||d=="zz")a=a.replace(/(\d{2})(\d{2})/,function(e,f){return P(f,d.length)});return a}},{ba:"iso(tz|timezone)",format:function(a){return a.getUTCOffset(k)}},{ba:"ord",format:function(a){a=V(a,"Date");return a+ua(a)}}],kb=[{$:"year",method:"FullYear",
46 | ja:k,da:function(a){return(365+(a?a.isLeapYear()?1:0:0.25))*24*60*60*1E3}},{$:"month",method:"Month",ja:k,da:function(a,b){var c=30.4375,d;if(a){d=a.daysInMonth();if(b<=d.days())c=d}return c*24*60*60*1E3},error:0.919},{$:"week",method:"ISOWeek",da:aa(6048E5)},{$:"day",method:"Date",ja:k,da:aa(864E5)},{$:"hour",method:"Hours",da:aa(36E5)},{$:"minute",method:"Minutes",da:aa(6E4)},{$:"second",method:"Seconds",da:aa(1E3)},{$:"millisecond",method:"Milliseconds",da:aa(1)}],lb={};
47 | function mb(a){qa(this,a);this.ga=ib.concat()}
48 | mb.prototype={getMonth:function(a){return A(a)?a-1:this.months.indexOf(a)%12},getWeekday:function(a){return this.weekdays.indexOf(a)%7},oa:function(a){var b;return A(a)?a:a&&(b=this.numbers.indexOf(a))!==-1?(b+1)%10:1},ua:function(a){var b=this;return a.replace(r(this.num,"g"),function(c){return b.oa(c)||""})},sa:function(a){return U.units[this.units.indexOf(a)%8]},va:function(a){return this.na(a,a[2]>0?"future":"past")},ra:function(a){return this.na(nb(a),"duration")},wa:function(a){a=a||this.code;
49 | return a==="en"||a==="en-US"?k:this.variant},za:function(a){return a===this.ampm[0]},Aa:function(a){return a&&a===this.ampm[1]},na:function(a,b){var c,d,e=a[0],f=a[1],h=a[2],i=this[b]||this.relative;if(y(i))return i.call(this,e,f,h,b);d=this.units[(this.plural&&e>1?1:0)*8+f]||this.units[f];if(this.capitalizeUnit)d=ob(d);c=this.modifiers.filter(function(j){return j.name=="sign"&&j.value==(h>0?1:-1)})[0];return i.replace(/\{(.*?)\}/g,function(j,g){switch(g){case "num":return e;case "unit":return d;
50 | case "sign":return c.src}})},ta:function(){return this.ma?[this.ma].concat(this.ga):this.ga},addFormat:function(a,b,c,d,e){var f=c||[],h=this,i;a=a.replace(/\s+/g,"[-,. ]*");a=a.replace(/\{([^,]+?)\}/g,function(j,g){var m,o,w,z=g.match(/\?$/);w=g.match(/^(\d+)\??$/);var J=g.match(/(\d)(?:-(\d))?/),M=g.replace(/[^a-z]+$/,"");if(w)m=h.tokens[w[1]];else if(h[M])m=h[M];else if(h[M+"s"]){m=h[M+"s"];if(J){o=[];m.forEach(function(Q,Fa){var W=Fa%(h.units?8:m.length);if(W>=J[1]&&W<=(J[2]||J[1]))o.push(Q)});
51 | m=o}m=pb(m)}if(w)w="(?:"+m+")";else{c||f.push(M);w="("+m+")"}if(z)w+="?";return w});if(b){b=qb(db,h,e);e=["t","[\\s\\u3000]"].concat(h.timeMarker);i=a.match(/\\d\{\d,\d\}\)+\??$/);rb(h,"(?:"+b+")[,\\s\\u3000]+?"+a,cb.concat(f),d);rb(h,a+"(?:[,\\s]*(?:"+e.join("|")+(i?"+":"*")+")"+b+")?",f.concat(cb),d)}else rb(h,a,f,d)}};function sb(a,b){var c;B(a)||(a="");c=lb[a]||lb[a.slice(0,2)];if(b===n&&!c)throw Error("Invalid locale.");return c||bb}
52 | function tb(a,b){function c(g){var m=i[g];if(B(m))i[g]=m.split(",");else m||(i[g]=[])}function d(g,m){g=g.split("+").map(function(o){return o.replace(/(.+):(.+)$/,function(w,z,J){return J.split("|").map(function(M){return z+M}).join("|")})}).join("|");return g.split("|").forEach(m)}function e(g,m,o){var w=[];i[g].forEach(function(z,J){if(m)z+="+"+z.slice(0,3);d(z,function(M,Q){w[Q*o+J]=M.toLowerCase()})});i[g]=w}function f(g,m,o){g="\\d{"+g+","+m+"}";if(o)g+="|(?:"+pb(i.numbers)+")+";return g}function h(g,
53 | m){i[g]=i[g]||m}var i,j;i=new mb(b);c("modifiers");"months,weekdays,units,numbers,articles,tokens,timeMarker,ampm,timeSuffixes,dateParse,timeParse".split(",").forEach(c);j=!i.monthSuffix;e("months",j,12);e("weekdays",j,7);e("units",n,8);e("numbers",n,10);h("code",a);h("date",f(1,2,i.digitDate));h("year","'\\d{2}|"+f(4,4));h("num",function(){var g=["\\d+"].concat(i.articles);if(i.numbers)g=g.concat(i.numbers);return pb(g)}());(function(){var g=[];i.ha={};i.modifiers.push({name:"day",src:"yesterday",
54 | value:-1});i.modifiers.push({name:"day",src:"today",value:0});i.modifiers.push({name:"day",src:"tomorrow",value:1});i.modifiers.forEach(function(m){var o=m.name;d(m.src,function(w){var z=i[o];i.ha[w]=m;g.push({name:o,src:w,value:m.value});i[o]=z?z+"|"+w:w})});i.day+="|"+pb(i.weekdays);i.modifiers=g})();if(i.monthSuffix){i.month=f(1,2);i.months=sa(1,12).map(function(g){return g+i.monthSuffix})}i.full_month=f(1,2)+"|"+pb(i.months);i.timeSuffixes.length>0&&i.addFormat(qb(db,i),n,cb);i.addFormat("{day}",
55 | k);i.addFormat("{month}"+(i.monthSuffix||""));i.addFormat("{year}"+(i.yearSuffix||""));i.timeParse.forEach(function(g){i.addFormat(g,k)});i.dateParse.forEach(function(g){i.addFormat(g)});return lb[a]=i}function rb(a,b,c,d){a.ga.unshift({Da:d,ya:a,Ca:r("^"+b+"$","i"),to:c})}function ob(a){return a.slice(0,1).toUpperCase()+a.slice(1)}function pb(a){return a.filter(function(b){return!!b}).join("|")}
56 | function ub(a,b){var c;if(L(a[0]))return a;else if(A(a[0])&&!A(a[1]))return[a[0]];else if(B(a[0])&&b)return[vb(a[0]),a[1]];c={};gb.forEach(function(d,e){c[d.$]=a[e]});return[c]}function vb(a,b){var c={};if(match=a.match(/^(\d+)?\s?(\w+?)s?$/i)){if(K(b))b=parseInt(match[1])||1;c[match[2].toLowerCase()]=b}return c}
57 | function wb(a,b){var c={},d,e;b.forEach(function(f,h){d=a[h+1];if(!(K(d)||d==="")){if(f==="year")c.Ea=d.replace(/'/,"");e=parseFloat(d.replace(/'/,"").replace(/,/,"."));c[f]=!isNaN(e)?e:d.toLowerCase()}});return c}function xb(a){a=a.trim().replace(/^just (?=now)|\.+$/i,"");return yb(a)}
58 | function yb(a){return a.replace(fb,function(b,c,d){var e=0,f=1,h,i;if(c)return b;d.split("").reverse().forEach(function(j){j=eb[j];var g=j>9;if(g){if(h)e+=f;f*=j/(i||1);i=j}else{if(h===n)f*=10;e+=f*j}h=g});if(h)e+=f;return e})}
59 | function zb(a,b,c,d){var e=new s,f=n,h,i,j,g,m,o,w,z,J;e.utc(d);if(ia(a))e.utc(a.isUTC()).setTime(a.getTime());else if(A(a))e.setTime(a);else if(L(a)){e.set(a,k);g=a}else if(B(a)){h=sb(b);a=xb(a);h&&E(h.ta(),function(M,Q){var Fa=a.match(Q.Ca);if(Fa){j=Q;i=j.ya;g=wb(Fa,j.to,i);g.utc&&e.utc();i.ma=j;if(g.timestamp){g=g.timestamp;return n}if(j.Da&&!B(g.month)&&(B(g.date)||h.wa(b))){z=g.month;g.month=g.date;g.date=z}if(g.year&&g.Ea.length===2)g.year=N(V(new s,"FullYear")/100)*100-N(g.year/100)*100+g.year;
60 | if(g.month){g.month=i.getMonth(g.month);if(g.shift&&!g.unit)g.unit=i.units[7]}if(g.weekday&&g.date)delete g.weekday;else if(g.weekday){g.weekday=i.getWeekday(g.weekday);if(g.shift&&!g.unit)g.unit=i.units[5]}if(g.day&&(z=i.ha[g.day])){g.day=z.value;e.reset();f=k}else if(g.day&&(o=i.getWeekday(g.day))>-1){delete g.day;if(g.num&&g.month){J=function(){var W=e.getWeekday();e.setWeekday(7*(g.num-1)+(W>o?o+7:o))};g.day=1}else g.weekday=o}if(g.date&&!A(g.date))g.date=i.ua(g.date);if(i.Aa(g.ampm)&&g.hour<
61 | 12)g.hour+=12;else if(i.za(g.ampm)&&g.hour===12)g.hour=0;if("offset_hours"in g||"offset_minutes"in g){e.utc();g.offset_minutes=g.offset_minutes||0;g.offset_minutes+=g.offset_hours*60;if(g.offset_sign==="-")g.offset_minutes*=-1;g.minute-=g.offset_minutes}if(g.unit){f=k;w=i.oa(g.num);m=i.sa(g.unit);if(g.shift||g.edge){w*=(z=i.ha[g.shift])?z.value:0;if(m==="month"&&I(g.date)){e.set({day:g.date},k);delete g.date}if(m==="year"&&I(g.month)){e.set({month:g.month,day:g.date},k);delete g.month;delete g.date}}if(g.sign&&
62 | (z=i.ha[g.sign]))w*=z.value;if(I(g.weekday)){e.set({weekday:g.weekday},k);delete g.weekday}g[m]=(g[m]||0)+w}if(g.year_sign==="-")g.year*=-1;hb.slice(1,4).forEach(function(W,fc){var Gb=g[W.$],Hb=Gb%1;if(Hb){g[hb[fc].$]=N(Hb*(W.$==="second"?1E3:60));g[W.$]=O(Gb)}});return n}});if(j)if(f)e.advance(g);else{e._utc&&e.reset();Ab(e,g,k,n,c)}else{if(a!=="now")e=new s(a);d&&e.addMinutes(-e.getTimezoneOffset())}if(g&&g.edge){z=i.ha[g.edge];E(hb.slice(4),function(M,Q){if(I(g[Q.$])){m=Q.$;return n}});if(m===
63 | "year")g.fa="month";else if(m==="month"||m==="week")g.fa="day";e[(z.value<0?"endOf":"beginningOf")+ob(m)]();z.value===-2&&e.reset()}J&&J();e.utc(n)}return{ea:e,set:g}}function nb(a){var b,c=v.abs(a),d=c,e=0;hb.slice(1).forEach(function(f,h){b=O(N(c/f.da()*10)/10);if(b>=1){d=b;e=h+1}});return[d,e,a]}function Bb(a){var b=nb(a.millisecondsFromNow());if(b[1]===6)b[0]=v.abs(a.monthsFromNow());return b}
64 | function Cb(a,b,c,d){var e,f=sb(d),h=r(/^[A-Z]/);if(a.isValid())if(Date[b])b=Date[b];else{if(y(b)){e=Bb(a);b=b.apply(a,e.concat(f))}}else return"Invalid Date";if(!b&&c){e=e||Bb(a);if(e[1]===0){e[1]=1;e[0]=1}return f.va(e)}b=b||"long";b=f[b]||b;jb.forEach(function(i){b=b.replace(r("\\{("+i.ba+")(\\d)?\\}",i.la?"i":""),function(j,g,m){j=i.format(a,f,m||1,g);m=g.length;var o=g.match(/^(.)\1+$/);if(i.la){if(m===3)j=j.slice(0,3);if(o||g.match(h))j=ob(j)}else if(o&&!i.text)j=(A(j)?P(j,m):j.toString()).slice(-m);
65 | return j})});return b}function Db(a,b,c,d){var e,f,h,i=0,j=0,g=0;e=zb(b,l,l,d);if(c>0){j=g=c;f=k}if(!e.ea.isValid())return n;if(e.set&&e.set.fa){kb.forEach(function(m){if(m.$===e.set.fa)i=m.da(e.ea,a-e.ea)-1});b=ob(e.set.fa);if(e.set.edge||e.set.shift)e.ea["beginningOf"+b]();if(e.set.fa==="month")h=e.ea.clone()["endOf"+b]().getTime();if(!f&&e.set.sign&&e.set.fa!="millisecond"){j=50;g=-50}}f=a.getTime();b=e.ea.getTime();h=h||b+i;h=Eb(a,b,h);return f>=b-j&&f<=h+g}
66 | function Eb(a,b,c){b=new Date(b);a=(new Date(c)).utc(a.isUTC());if(V(a,"Hours")!==23){b=b.getTimezoneOffset();a=a.getTimezoneOffset();if(b!==a)c+=(a-b).minutes()}return c}
67 | function Ab(a,b,c,d,e){function f(g){return I(b[g])?b[g]:b[g+"s"]}function h(g){return I(f(g))}var i,j;if(A(b)&&d)b={milliseconds:b};else if(A(b)){a.setTime(b);return a}if(I(b.date))b.day=b.date;E(hb,function(g,m){var o=m.$==="day";if(h(m.$)||o&&h("weekday")){b.fa=m.$;j=+g;return n}else if(c&&m.$!=="week"&&(!o||!h("week")))Fb(a,m.method,o?1:0)});kb.forEach(function(g){var m=g.$;g=g.method;var o;o=f(m);if(!K(o)){if(d){if(m==="week"){o=(b.day||0)+o*7;g="Date"}o=o*d+V(a,g)}else m==="month"&&h("day")&&
68 | Fb(a,"Date",15);Fb(a,g,o);if(d&&m==="month"){m=o;if(m<0)m=m%12+12;m%12!=V(a,"Month")&&Fb(a,"Date",0)}}});if(!d&&!h("day")&&h("weekday")){i=f("weekday");a.setWeekday(i)}(function(){var g=new s;return e===-1&&a>g||e===1&&a1&&this.addWeeks(a-1);b!==1&&this.advance({days:b-1});return this.getTime()}},getISOWeek:function(){var a=this;a=a.clone();var b=V(a,"Day")||7;a.addDays(4-b).reset();return 1+O(a.daysSince(a.clone().beginningOfYear())/7)},getUTCOffset:function(a){var b=
72 | this._utc?0:this.getTimezoneOffset(),c=a===k?":":"";if(!b&&a)return"Z";return P(O(-b/60),2,k)+c+P(v.abs(b%60),2)},utc:function(a){ka(this,"_utc",a===k||arguments.length===0);return this},isUTC:function(){return!!this._utc||this.getTimezoneOffset()===0},advance:function(){var a=ub(arguments,k);return Ab(this,a[0],a[1],1)},rewind:function(){var a=ub(arguments,k);return Ab(this,a[0],a[1],-1)},isValid:function(){return!isNaN(this.getTime())},isAfter:function(a,b){return this.getTime()>s.create(a).getTime()-
73 | (b||0)},isBefore:function(a,b){return this.getTime()d},isLeapYear:function(){var a=V(this,"FullYear");return a%4===0&&a%100!==0||a%400===0},daysInMonth:function(){return 32-V(new s(V(this,"FullYear"),V(this,"Month"),32),"Date")},format:function(a,b){return Cb(this,a,n,b)},relative:function(a,b){if(B(a)){b=a;a=l}return Cb(this,
74 | a,k,b)},is:function(a,b,c){var d,e;if(this.isValid()){if(B(a)){a=a.trim().toLowerCase();e=this.clone().utc(c);switch(k){case a==="future":return this.getTime()>(new s).getTime();case a==="past":return this.getTime()<(new s).getTime();case a==="weekday":return V(e,"Day")>0&&V(e,"Day")<6;case a==="weekend":return V(e,"Day")===0||V(e,"Day")===6;case (d=U.weekdays.indexOf(a)%7)>-1:return V(e,"Day")===d;case (d=U.months.indexOf(a)%12)>-1:return V(e,"Month")===d}}return Db(this,a,b,c)}},reset:function(a){var b=
75 | {},c;a=a||"hours";if(a==="date")a="days";c=kb.some(function(d){return a===d.$||a===d.$+"s"});b[a]=a.match(/^days?/)?1:0;return c?this.set(b,k):this},clone:function(){var a=new s(this.getTime());a.utc(!!this._utc);return a}});s.extend({iso:function(){return this.toISOString()},getWeekday:s.prototype.getDay,getUTCWeekday:s.prototype.getUTCDay});
76 | function Ib(a,b){function c(){return N(this*b)}function d(){return X(arguments)[a.ia](this)}function e(){return X(arguments)[a.ia](-this)}var f=a.$,h={};h[f]=c;h[f+"s"]=c;h[f+"Before"]=e;h[f+"sBefore"]=e;h[f+"Ago"]=e;h[f+"sAgo"]=e;h[f+"After"]=d;h[f+"sAfter"]=d;h[f+"FromNow"]=d;h[f+"sFromNow"]=d;u.extend(h)}u.extend({duration:function(a){return sb(a).ra(this)}});
77 | U=bb=s.addLocale("en",{plural:k,timeMarker:"at",ampm:"am,pm",months:"January,February,March,April,May,June,July,August,September,October,November,December",weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",units:"millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s",numbers:"one,two,three,four,five,six,seven,eight,nine,ten",articles:"a,an,the",tokens:"the,st|nd|rd|th,of","short":"{Month} {d}, {yyyy}","long":"{Month} {d}, {yyyy} {h}:{mm}{tt}",full:"{Weekday} {Month} {d}, {yyyy} {h}:{mm}:{ss}{tt}",
78 | past:"{num} {unit} {sign}",future:"{num} {unit} {sign}",duration:"{num} {unit}",modifiers:[{name:"sign",src:"ago|before",value:-1},{name:"sign",src:"from now|after|from|in|later",value:1},{name:"edge",src:"last day",value:-2},{name:"edge",src:"end",value:-1},{name:"edge",src:"first day|beginning",value:1},{name:"shift",src:"last",value:-1},{name:"shift",src:"the|this",value:0},{name:"shift",src:"next",value:1}],dateParse:["{num} {unit} {sign}","{sign} {num} {unit}","{month} {year}","{shift} {unit=5-7}",
79 | "{0?} {date}{1}","{0?} {edge} of {shift?} {unit=4-7?}{month?}{year?}"],timeParse:["{0} {num}{1} {day} of {month} {year?}","{weekday?} {month} {date}{1?} {year?}","{date} {month} {year}","{date} {month}","{shift} {weekday}","{shift} week {weekday}","{weekday} {2?} {shift} week","{num} {unit=4-5} {sign} {day}","{0?} {date}{1} of {month}","{0?}{month?} {date?}{1?} of {shift} {unit=6-7}"]});hb=kb.concat().reverse();gb=kb.concat();gb.splice(2,1);
80 | G(s,k,n,kb,function(a,b,c){function d(g){g=g/h;var m=g%1,o=b.error||0.999;if(m&&v.abs(m%1)>o)g=N(g);return parseInt(g)}var e=b.$,f=ob(e),h=b.da(),i,j;b.ia="add"+f+"s";i=function(g,m){return d(this.getTime()-s.create(g,m).getTime())};j=function(g,m){return d(s.create(g,m).getTime()-this.getTime())};a[e+"sAgo"]=j;a[e+"sUntil"]=j;a[e+"sSince"]=i;a[e+"sFromNow"]=i;a[b.ia]=function(g,m){var o={};o[e]=g;return this.advance(o,m)};Ib(b,h);c<3&&["Last","This","Next"].forEach(function(g){a["is"+g+f]=function(){return this.is(g+
81 | " "+e)}});if(c<4){a["beginningOf"+f]=function(){var g={};switch(e){case "year":g.year=V(this,"FullYear");break;case "month":g.month=V(this,"Month");break;case "day":g.day=V(this,"Date");break;case "week":g.weekday=0}return this.set(g,k)};a["endOf"+f]=function(){var g={hours:23,minutes:59,seconds:59,milliseconds:999};switch(e){case "year":g.month=11;g.day=31;break;case "month":g.day=this.daysInMonth();break;case "week":g.weekday=6}return this.set(g,k)}}});
82 | U.addFormat("([+-])?(\\d{4,4})[-.]?{full_month}[-.]?(\\d{1,2})?",k,["year_sign","year","month","date"],n,k);U.addFormat("(\\d{1,2})[-.\\/]{full_month}(?:[-.\\/](\\d{2,4}))?",k,["date","month","year"],k);U.addFormat("{full_month}[-.](\\d{4,4})",n,["month","year"]);U.addFormat("\\/Date\\((\\d+(?:\\+\\d{4,4})?)\\)\\/",n,["timestamp"]);U.addFormat(qb(db,U),n,cb);ib=U.ga.slice(0,7).reverse();U.ga=U.ga.slice(7).concat(ib);G(s,k,n,"short,long,full",function(a,b){a[b]=function(c){return Cb(this,b,n,c)}});
83 | "\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07".split("").forEach(function(a,b){if(b>9)b=v.pow(10,b-9);eb[a]=b});"\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19".split("").forEach(function(a,b){eb[a]=b});fb=r("([\u671f\u9031\u5468])?([\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19]+)(?!\u6628)","g");
84 | (function(){var a="today,yesterday,tomorrow,weekday,weekend,future,past".split(","),b=U.weekdays.slice(0,7),c=U.months.slice(0,12);G(s,k,n,a.concat(b).concat(c),function(d,e){d["is"+ob(e)]=function(f){return this.is(e,0,f)}})})();(function(){s.extend({utc:{create:function(){return X(arguments,0,k)},past:function(){return X(arguments,-1,k)},future:function(){return X(arguments,1,k)}}},n,n)})();
85 | s.extend({RFC1123:"{Dow}, {dd} {Mon} {yyyy} {HH}:{mm}:{ss} {tz}",RFC1036:"{Weekday}, {dd}-{Mon}-{yy} {HH}:{mm}:{ss} {tz}",ISO8601_DATE:"{yyyy}-{MM}-{dd}",ISO8601_DATETIME:"{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{fff}{isotz}"},n,n);
86 | DateRange=function(a,b){this.start=s.create(a);this.end=s.create(b)};DateRange.prototype.toString=function(){return this.isValid()?this.start.full()+".."+this.end.full():"Invalid DateRange"};
87 | D(DateRange,k,n,{isValid:function(){return this.start=b.start&&c<=b.end})},every:function(a,b){var c=this.start.clone(),d=[],e=0,f,h;if(B(a)){c.advance(vb(a,0),k);f=vb(a);h=a.toLowerCase()==="day"}else f={milliseconds:a};for(;c<=this.end;){d.push(c);b&&b(c,e);if(h&&V(c,"Hours")===23){c=c.clone();Fb(c,
88 | "Hours",48)}else c=c.clone().advance(f,k);e++}return d},union:function(a){return new DateRange(this.starta.end?this.end:a.end)},intersect:function(a){return new DateRange(this.start>a.start?this.start:a.start,this.endm;)g=Function.prototype.apply.apply(d,e.shift());Jb(c,i,function(){f=n;h()});f=k}};return c},delay:function(a){var b=H(arguments).slice(1);Jb(this,a,this,this,b);return this},throttle:function(a){return this.lazy(a,1)},debounce:function(a){function b(){b.cancel();
92 | Jb(b,a,c,this,arguments)}var c=this;return b},cancel:function(){if(ga(this.timers))for(;this.timers.length>0;)clearTimeout(this.timers.shift());return this},after:function(a){var b=this,c=0,d=[];if(A(a)){if(a===0){b.call();return b}}else a=1;return function(){var e;d.push(H(arguments));c++;if(c==a){e=b.call(this,d);c=0;d=[];return e}}},once:function(){return this.throttle(Infinity)},fill:function(){var a=this,b=H(arguments);return function(){var c=H(arguments);b.forEach(function(d,e){if(d!=l||e>=
93 | c.length)c.splice(e,0,d)});return a.apply(this,c)}}});
94 | function Kb(a,b,c,d,e,f){var h=a.toFixed(20),i=h.search(/\./);h=h.search(/[1-9]/);i=i-h;if(i>0)i-=1;e=v.max(v.min((i/3).floor(),e===n?c.length:e),-d);d=c.charAt(e+d-1);if(i<-9){e=-3;b=i.abs()-9;d=c.slice(0,1)}return(a/(f?(2).pow(10*e):(10).pow(e*3))).round(b||0).format()+d.trim()}
95 | D(u,n,n,{random:function(a,b){var c,d;if(arguments.length==1){b=a;a=0}c=v.min(a||0,K(b)?1:b);d=v.max(a||0,K(b)?1:b)+1;return O(v.random()*(d-c)+c)}});
96 | D(u,k,n,{log:function(a){return v.log(this)/(a?v.log(a):1)},abbr:function(a){return Kb(this,a,"kmbt",0,4)},metric:function(a,b){return Kb(this,a,"n\u03bcm kMGTPE",4,K(b)?1:b)},bytes:function(a,b){return Kb(this,a,"kMGTPE",0,K(b)?4:b,k)+"B"},isInteger:function(){return this%1==0},isOdd:function(){return!isNaN(this)&&!this.isMultipleOf(2)},isEven:function(){return this.isMultipleOf(2)},isMultipleOf:function(a){return this%a===0},format:function(a,b,c){var d,e,f,h="";if(K(b))b=",";if(K(c))c=".";d=(A(a)?
97 | N(this,a||0).toFixed(v.max(a,0)):this.toString()).replace(/^-/,"").split(".");e=d[0];f=d[1];for(d=e.length;d>0;d-=3){if(d2},{startsWith:function(a,b,c){var d=this;if(b)d=d.slice(b);if(K(c))c=k;a=C(a)?a.source.replace("^",""):R(a);return r("^"+a,c?"":"i").test(d)},endsWith:function(a,b,c){var d=this;if(I(b))d=d.slice(0,b);if(K(c))c=k;a=C(a)?a.source.replace("$",""):R(a);return r(a+"$",c?"":"i").test(d)}});
110 | D(t,k,n,{escapeRegExp:function(){return R(this)},escapeURL:function(a){return a?encodeURIComponent(this):encodeURI(this)},unescapeURL:function(a){return a?decodeURI(this):decodeURIComponent(this)},escapeHTML:function(){return this.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},unescapeHTML:function(){return this.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(///g,
111 | "/").replace(/&/g,"&")},encodeBase64:function(){return Rb(this)},decodeBase64:function(){return Sb(this)},each:function(a,b){var c,d,e;if(y(a)){b=a;a=/[\s\S]/g}else if(a)if(B(a))a=r(R(a),"gi");else{if(C(a))a=r(a.source,wa(a,"g"))}else a=/[\s\S]/g;c=this.match(a)||[];if(b){d=0;for(e=c.length;d0?"_":"")+a.toLowerCase()}).replace(/([A-Z\d]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").toLowerCase()},camelize:function(a){return this.underscore().replace(/(^|_)([^_]+)/g,function(b,c,d,e){b=d;b=(c=t.Inflector)&&c.acronyms[b];b=B(b)?b:void 0;e=a!==n||e>0;if(b)return e?b:b.toLowerCase();return e?d.capitalize():d})},spacify:function(){return this.underscore().replace(/_/g," ")},stripTags:function(){var a=this;na(arguments.length>
115 | 0?arguments:[""],function(b){a=a.replace(r("?"+R(b)+"[^<>]*>","gi"),"")});return a},removeTags:function(){var a=this;na(arguments.length>0?arguments:["\\S+"],function(b){b=r("<("+b+")[^<>]*(?:\\/>|>.*?<\\/\\1>)","gi");a=a.replace(b,"")});return a},truncate:function(a,b,c,d){var e="",f="",h=this.toString(),i="["+va()+"]+",j="[^"+va()+"]*",g=r(i+j+"$");d=K(d)?"...":t(d);if(h.length<=a)return h;switch(c){case "left":a=h.length-a;e=d;h=h.slice(a);g=r("^"+j+i);break;case "middle":a=O(a/2);f=d+h.slice(h.length-
116 | a).trimLeft();h=h.slice(0,a);break;default:a=a;f=d;h=h.slice(0,a)}if(b===n&&this.slice(a,a+1).match(/\S/))h=h.remove(g);return e+h+f},pad:function(a,b){return ta(b,a)+this+ta(b,a)},padLeft:function(a,b){return ta(b,a)+this},padRight:function(a,b){return this+ta(b,a)},first:function(a){if(K(a))a=1;return this.substr(0,a)},last:function(a){if(K(a))a=1;return this.substr(this.length-a<0?0:this.length-a)},repeat:function(a){var b="",c=this;if(!A(a)||a<1)return"";for(;a;){if(a&1)b+=c;if(a>>=1)c+=c}return b},
117 | toNumber:function(a){var b=this.replace(/,/g,"");return b.match(/\./)?parseFloat(b):parseInt(b,a||10)},capitalize:function(a){var b;return this.toLowerCase().replace(a?/[\s\S]/g:/^\S/,function(c){var d=c.toUpperCase(),e;e=b?c:d;b=d!==c;return e})},assign:function(){var a={};H(arguments,function(b,c){if(L(b))qa(a,b);else a[c+1]=b});return this.replace(/\{([^{]+?)\}/g,function(b,c){return F(a,c)?a[c]:b})}});D(t,k,n,{insert:t.prototype.add});
118 | (function(a){if(this.btoa){Rb=this.btoa;Sb=this.atob}else{var b=/[^A-Za-z0-9\+\/\=]/g;Rb=function(c){var d="",e,f,h,i,j,g,m=0;do{e=c.charCodeAt(m++);f=c.charCodeAt(m++);h=c.charCodeAt(m++);i=e>>2;e=(e&3)<<4|f>>4;j=(f&15)<<2|h>>6;g=h&63;if(isNaN(f))j=g=64;else if(isNaN(h))g=64;d=d+a.charAt(i)+a.charAt(e)+a.charAt(j)+a.charAt(g)}while(m>4;f=(f&15)<<4|i>>2;h=(i&3)<<6|j;d+=t.fromCharCode(e);if(i!=64)d+=t.fromCharCode(f);if(j!=64)d+=t.fromCharCode(h)}while(g编辑'
51 | + ' | '
52 | + '删除'
54 | }
55 | }
56 | ];
57 |
58 | var UIView = require('ef/UIView');
59 |
60 | function MemberListView() {
61 | UIView.apply(this, arguments);
62 | }
63 |
64 | MemberListView.prototype.template = 'memberListPage';
65 |
66 | MemberListView.prototype.uiProperties = {
67 | memberList: {
68 | fields: tableFields,
69 | sortable: false,
70 | columnResizable: true,
71 | subrow: false,
72 | followHead: true,
73 | selectMode: 'line'
74 | }
75 | };
76 |
77 | MemberListView.prototype.uiEvents = {
78 | 'memberList:command': function (e) {
79 | if (e.name === 'modify') {
80 | this.fire('modifyClicked', {id: e.args});
81 | }
82 | if (e.name === 'remove') {
83 | this.fire('removeClicked', {args: e.args});
84 | }
85 | },
86 |
87 | 'createButton:click': function (e) {
88 | this.fire('createNewMember');
89 | }
90 | };
91 |
92 | require('er/util').inherits(MemberListView, UIView);
93 | return MemberListView;
94 | }
95 | );
--------------------------------------------------------------------------------
/demo/chi/src/member/config.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var actions = [
4 | {
5 | path: '/',
6 | type: 'member/List'
7 | },
8 | {
9 | path: '/member/list',
10 | type: 'member/List'
11 | },
12 | {
13 | path: '/member/create',
14 | type: 'member/Form',
15 | args: { formType: 'create' }
16 | },
17 | {
18 | path: '/member/update',
19 | type: 'member/Form',
20 | args: { formType: 'update' }
21 | }
22 | ];
23 |
24 | var controller = require('er/controller');
25 | actions.forEach(controller.registerAction);
26 |
27 | var config = {};
28 |
29 | config.Gender = {
30 | MALE: 0,
31 | FEMALE: 1,
32 | UNKNOWN: 2,
33 |
34 | 0: '女',
35 | 1: '男',
36 | 2: '保密'
37 | };
38 |
39 | return config;
40 | }
41 | );
--------------------------------------------------------------------------------
/demo/chi/src/member/css/form.css:
--------------------------------------------------------------------------------
1 | /* 清除浮动 */
2 | .form-row,
3 | .form-line,
4 | .form-single-line,
5 | .form-value,
6 | .form-value-mini,
7 | .edit-submit {
8 | zoom:1;
9 | }
10 |
11 | .form-row:after,
12 | .form-line:after,
13 | .form-single-line:after,
14 | .form-value:after,
15 | .form-value-mini:after,
16 | .edit-submit:after {
17 | content:"\0020";
18 | display:block;
19 | height:0;
20 | visibility:hidden;
21 | clear:both;
22 | }
23 |
24 |
25 | /* 表单部分 */
26 | .edit-form {
27 | background:#FFF;
28 | margin-bottom:10px;
29 | padding: 5px;
30 | }
31 |
32 | /* 表单头部 */
33 | .edit-form .form-head {
34 | border-width: 1px 1px 0 1px;
35 | border-style: solid;
36 | border-color: #9ecae8;
37 | background: #c7deed;
38 | height:28px;
39 | }
40 |
41 | .edit-form .form-headcntr {
42 | height:27px;
43 | line-height:27px;
44 | color:#1c4a79;
45 | border-top:1px solid #d8e8f2;
46 | }
47 |
48 | .edit-form .form-headcntr h2 {
49 | padding-left:20px;
50 | font-weight:700;
51 | font-size:12px;
52 | }
53 |
54 |
55 | /* 表单行 */
56 | .form-row {
57 | padding:2px 0;
58 | }
59 |
60 | .form-line,
61 | .form-single-line,
62 | .form-key,
63 | .form-key-mini,
64 | .form-value-mini,
65 | .form-value {
66 | float:left;
67 | display:inline;
68 | line-height:34px;
69 | }
70 |
71 | .form-line,
72 | .form-value {
73 | width: auto;
74 | }
75 |
76 | .form-value-mini {
77 | width:300px;
78 | padding:2px;
79 | }
80 | .ui-dialog-body .form-value {
81 | width:auto;
82 | }
83 |
84 | .form-value-preview {
85 | line-height:0;
86 | }
87 |
88 | /* 表单key */
89 | .form-key,
90 | .form-key-mini {
91 | float:left;
92 | padding:2px 0 3px 10px;
93 | width:85px;
94 | color:#333333;
95 | }
96 |
97 | .form-key-mini {
98 | padding-left:0;
99 | }
100 |
101 |
102 | /* 表单value */
103 | .form-value {
104 | padding:2px;
105 | }
106 | .form-value-caution {
107 | float:left;
108 | color:red;
109 | }
110 |
111 |
112 | .form-tip{
113 | color:#999999;
114 | }
115 | /*有必填标记的表单value*/
116 | .form-value-required{
117 | position:relative;
118 | left:-8px;
119 | }
120 | /*必填标记*/
121 | .required-icon{
122 | float:left;
123 | margin-right:3px;
124 | color:red;
125 | }
126 | /* 单独占一行 */
127 | .form-single-line {
128 | padding:2px 0 2px 36px;
129 | width:500px;
130 | clear:both;
131 | }
132 |
133 | .form-line {
134 | clear:both;
135 | }
136 |
137 | /* 表单内控件样式微调 */
138 | .form-row .ui-checkbox,
139 | .form-row .ui-radiobox {
140 | display: inline;
141 | margin-top:5px;
142 | }
143 |
144 | .form-row .ui-text {
145 | margin-right:8px;
146 | }
147 |
148 | .form-row .ui-checkbox-label,
149 | .form-row .ui-radiobox-label {
150 | display: inline;
151 | line-height: 24px;
152 | margin: 0 8px 0 0px;
153 | }
154 |
155 | /* 表单文字 */
156 | .form-text {
157 | float:left;
158 | display:inline;
159 | margin-right:8px;
160 | line-height:34px;
161 | }
162 |
163 | /* 表单小文字 */
164 | .form-text-s {
165 | font-size:10px;
166 | color:#999;
167 | }
168 | .form-text-arrow{
169 | font-family:simsun;
170 | font-size:12px;
171 | }
172 | /* 表单内容块*/
173 | .form-content-block {
174 | padding-left:98px;
175 | }
176 |
177 | .form-row .ui-combobox {
178 | float:left;
179 | margin-right:5px;
180 | }
181 |
182 | .form-row .ui-select,
183 | .form-row .ui-combobox,
184 | .form-row .ui-button,
185 | .form-row .ui-text,
186 | .form-row .ui-textarea,
187 | .form-row .ui-cal,
188 | .form-row .ui-buttonmenu,
189 | .form-row .ui-combosearchlistsel {
190 | margin-top: 5px;
191 | }
192 |
193 | .form-row .ui-text {
194 | *margin-top:1px;
195 | vertical-align: middle;
196 | *height: 21px;
197 | *line-height: 21px;
198 | }
199 |
200 | .form-row .ui-textarea{
201 | margin-right:5px;
202 | }
203 | /* 提交部分 */
204 | .edit-submit {
205 | /*margin:0 0 0 21px;*/
206 | zoom:1;
207 | }
208 |
209 |
210 | /* 表单验证提示 */
211 | .validate-error {
212 | padding:5px;
213 | display:block;
214 | }
215 | .validate {
216 | /*
217 | background:#FEF7DB;
218 | border:1px solid #F0DDA5;
219 | */
220 | position:relative;
221 | zoom:1;
222 | height: 25px;
223 | }
224 |
225 | .validate-text {
226 | background: none repeat scroll 0 0 #FEDBDC;
227 | border: 1px solid #F0CCCC;
228 | color: #DD6666;
229 | display: block;
230 | height: 25px;
231 | line-height: 25px;
232 | margin-top: 8px;
233 | padding-left: 45px;
234 | }
235 |
236 | .validate-text-nobr {
237 | color: red;
238 | line-height: 34px;
239 | padding-left: 5px;
240 | }
241 |
242 | .validate-icon {
243 | background:#FEDBDC url(../../assets/img/allbgs.png) -23px -208px no-repeat;
244 | position: absolute;
245 | left: 15px;
246 | top: 7px;
247 | width: 14px;
248 | height: 14px;
249 | }
250 |
251 | /* 表单分支链接(支线任务) */
252 | .form-branch-task {
253 | font-family:STHeiti, SimSun;
254 | position:absolute;
255 | right:20px;
256 | top:20px;
257 | }
258 |
259 | /* 选填 ,灰色*/
260 | .form-optional,.form-grey {
261 | float:left;
262 | display:inline;
263 | margin-left:5px;
264 | line-height:24px;
265 | color:#CCC;
266 | }
267 |
268 | .ui-richsel-entry .form-optional {
269 | line-height:22px;
270 | }
271 |
272 | .form-grey{float:none;}
273 | .form-grey2{color:#CCC;}
274 | /* 表单提示信息(先放在这里,组件做好再转移) */
275 | .ui-forminfo {
276 | position:relative;
277 | margin:1px;
278 | background:#EFF7F9;
279 | border:1px solid #C5DFF0;
280 | clear:both;
281 | zoom:1;
282 | }
283 |
284 | .ui-forminfo .ui-link {
285 | text-decoration:underline;
286 | }
287 |
288 | .ui-forminfo-icon {
289 | position:absolute;
290 | width:14px;
291 | height:17px;
292 | background:url(../../assets/img/BlockSprites.png) no-repeat -141px -315px;
293 | left:40px;
294 | top:6px;
295 | }
296 |
297 | .ui-forminfo-text {
298 | padding:4px 10px 4px 64px;
299 | line-height:24px;
300 | color:#8BA9C4;
301 | }
302 |
303 | .ui-forminfo-text strong {
304 | margin:0 3px;
305 | }
306 |
307 | /**
308 | * 表单内的iframe
309 | */
310 | .form-iframe {
311 | width:850px;
312 | height:450px;
313 | overflow-x:hidden;
314 | border:none;
315 | }
316 |
317 | .form-iframe-wrapper {
318 | border-top:1px solid #DCE3E9;
319 | margin:10px;
320 | padding:5px 0;
321 | width:850px;
322 | }
323 |
324 | /**
325 | * 缩进HACK
326 | */
327 | .indent-checkbox {
328 | padding-left: 17px;
329 | *padding-left: 23px;
330 | }
331 | /**
332 | * 表单元素组缩进
333 | */
334 | .form-row-group
335 | {
336 | margin-left: 35px;
337 | }
338 |
--------------------------------------------------------------------------------
/demo/chi/src/member/form.tpl:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/demo/chi/src/member/list.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 新建成员
7 |
8 |
9 |
--------------------------------------------------------------------------------
/doc/ActionPanel.md:
--------------------------------------------------------------------------------
1 | # ActionPanel控件
2 |
3 | `ActionPanel`控件渲染一个`Panel`,但在该面板里加载一个子Action。
4 |
5 | ## 继承关系
6 |
7 | - er/Control
8 | - er/Panel
9 | - ActionPanel
10 |
11 | ## 属性
12 |
13 | ### {string} url
14 |
15 | 指定需要加载的子Action对应的地址。
16 |
17 | ### {Object} actionOptions
18 |
19 | 指定加载子Action时传递的额外参数。
20 |
21 | ### {er/Action} action
22 |
23 | 当子Action加载完毕后,可以使用该属性访问到`Action`对象实例。
24 |
25 | 该属性为 **只读** 。
26 |
27 | ## 事件
28 |
29 | ### actionloaded
30 |
31 | 子Action加载完毕时触发。
--------------------------------------------------------------------------------
/doc/UIModel.md:
--------------------------------------------------------------------------------
1 | # UIModel基类
2 |
3 | `UIModel`是对`er/Model`的继承,用于将ER与ESUI结合起来。
4 |
5 | `UIModel`会重写`set`和`fill`方法,提供数据格式化的功能。
6 |
7 | `UIModel`主要用于表单类的模块,`er/Form`控件的`getData()`方法会返回控件的`rawValue`而不是`value`,`rawValue`是一个对象,并不一定是字符串。但前后端交互的HTTP接口一定使用字符串,因此在获得`rawValue`之后,需要一定的方法将之转换为字符串再通过`Model`的相关方法(如`save`或`update`之类)提交,因此`UIModel`提供`formatters`属性用于配置格式化逻辑。
8 |
9 | ## 继承关系
10 |
11 | - er/Observable
12 | - er/Model
13 | - UIModel
14 |
15 | ## 属性
16 |
17 | ### {Object} UIModel.formatters
18 |
19 | 这是`UIModel`上的静态属性,其内置了几个常用的格式化函数:
20 |
21 | - `date`:将`Date`类型格式化为`YYYY-MM-dd`格式的日期字符串。
22 | - `dateRange`:将`{ {Date} begin, {Date} end }`类型格式化为`YYYY-MM-dd,YYYY-MM-dd`格式的字符串。
23 | - `time`:将`Date`类型格式化为`YYYY-MM-dd HH:mm:ss`格式的日期字符串。
24 | - `timeRange`:将`{ {Date} begin, {Date} end }`类型格式化为`YYYY-MM-dd HH:mm:ss,YYYY-MM-dd HH:mm:ss`格式的字符串。
25 |
26 | ### {Object} formatters
27 |
28 | 通过该属性配置格式化函数,当调用`set`或`fill`将值写入当前`Model`时,会通过此配置查找对应的格式化函数,先经过函数的处理再将值写入。
29 |
30 | 每一个格式化函数应该接受一个值,返回字符串。
31 |
32 | `formatters`属性为一个对象,其键为对应属性的名称,值为格式化函数,如:
33 |
34 | CustomModel.prototype.formatters = {
35 | birthday: UIModel.formatters.date,
36 | gender: function (value) {
37 | return ['male', 'female'][value];
38 | }
39 | };
40 |
41 | 以上配置设定当写入`birthday`属性时,先将值通过内置的`date`函数进行格式化,而当`gender`属性被写入时,使用自定义的函数进行格式化。
42 |
43 | ## 方法
44 |
45 | ### {Object} getPart({string..} keys)
46 |
47 | 由于一个`Model`中会存在许多属性,并不是每一个属性都要在保存、更新时发送到服务器,因此不能直接使用`valueOf()`得到对象,而是需要取出一部分属性,拼装成一个新的对象,通常就是这样的代码:
48 |
49 | var postData = {
50 | name: model.get('name'),
51 | age: model.get('age'),
52 | birthday: model.get('birthday'),
53 | ...
54 | };
55 |
56 | 如果属性较多,会比较累。而使用`getPart`方法,则可以:
57 |
58 | var postData = model.getPart('name', 'age', 'birthday', ...);
59 |
60 | 以节省代码量,另一种方法是,根据`form.getData()`返回的键,一次性获取所有的值:
61 |
62 | var formData = form.getData();
63 | model.fill(formData); // 先写到model中,会经过formatter格式化
64 | var postData = model.getPart(Object.keys(formData)); // 注意Object.keys的兼容性
--------------------------------------------------------------------------------
/doc/UIView.md:
--------------------------------------------------------------------------------
1 | # UIView基类
2 |
3 | `UIView`是对`er/View`的继承,用于将ER与ESUI结合起来。
4 |
5 | `UIView`会重写`enterDocument`方法,使用ESUI对容器进行初始化,以保证控件的渲染。
6 |
7 | ## 继承关系
8 |
9 | - er/Observable
10 | - er/View
11 | - UIView
12 |
13 | ## 属性
14 |
15 | ### {Object} uiProperties
16 |
17 | 通过`uiProperties`属性,可以为控件加上额外的属性。该属性为一个对象,其中的键为对应控件的id,值为控件的额外属性对象,如下代码:
18 |
19 | CustomView.prototype.uiProperties = {
20 | username: {
21 | maxLength: 20,
22 | pattern: '^[a-zA-Z]+$'
23 | }
24 | };
25 |
26 | 则指定id为 **username** 的控件在实例化时额外传递2个属性,分别为`maxLength`和`pattern`。
27 |
28 | ### {Object} uiEvents
29 |
30 | 通过`uiEvents`属性,可以为控件绑定指定的事件。该属性为一个对象,由2种方式声明:
31 |
32 | - 键为`id:eventName`形式的字符串,值为对应事件的处理函数,如:
33 |
34 | CustomView.prototype.uiEvents = {
35 | 'username:input': function (e) {
36 | if (this.getValue().length > 20) {
37 | warn('已经超出' + (this.getValue().length - 20) + '个字符';
38 | }
39 | }
40 | }
41 |
42 | - 键为控件的id,值为一个对象。值对象中的键为事件名称,值为处理函数,如:
43 |
44 | CustomView.prototype.uiEvents = {
45 | username: {
46 | input: function (e) {
47 | if (this.getValue().length > 20) {
48 | warn('已经超出' + (this.getValue().length - 20) + '个字符';
49 | }
50 | }
51 | }
52 | }
53 |
54 | 需要注意的是,在此处声明的事件,运行时的`this`对象均是`View`实例,而非控件的实例。同时,在运行期,`UIView`会克隆该属性,将其中所有的处理函数都进行一次`bind`,将`this`指向自身,因此运行时的`uiEvents`与类声明时的不会相同。
55 |
56 | 如果需要解除某个事件的绑定,可以使用`.on('type', this.uiEvents.xxx)`进行。
57 |
58 | ### {ViewContext} viewContext
59 |
60 | 每一个`UIView`会创建一个单独的`ViewContext`,该视图的所有控件均存放在这个`ViewContext`中。
61 |
62 | 一般情况下没有使用该属性的必要,特殊场景如清空当前视图下的控件,可以使用`this.viewContext.clean();`。
63 |
64 | 在`UIView`销毁时,会同时销毁该`ViewContext`,因此不需要关心控件在离开视图时的销毁工作。
65 |
66 | ## 方法
67 |
68 | ### {Control} get({string} id)
69 |
70 | 该方法即`this.viewContext.get(id)`,用于返回当前视图下指定id的控件实例。
71 |
72 | ### {ViewContext} createViewContext()
73 |
74 | 创建当前`UIView`实例使用的`ViewContext`对象,默认实现是通过`this.name`或者当前构造函数的名字来创建一个`ViewContext`实例,可重写来创建一个`id`稳定的`ViewContext`对象。
75 |
76 | ## 其它
77 |
78 | ### 值替换
79 |
80 | `UIView`为ESUI提供了值替换函数,在HTML中对应`data-ui-*`和`data-ui-extension-*`属性的值,如果以 **@** 为起始,则会替换为`Model`中的对应值,如:
81 |
82 |
83 |
84 | 则在该`Select`控件实例化时,其`datasource`属性的值等于`this.model.get('datasource')`的值,而不是简单的`@users`字符串。
85 |
86 | 以 **@** 为起始的字符串可以是一个深度的属性路径,如`@user.name.first`也是被允许的。
87 |
88 | ### enterDocument
89 |
90 | 如果有需要重写`enterDocument`方法, **必须** 调用`UIView.prototype.enterDocument`,否则ESUI无法正常工作。
--------------------------------------------------------------------------------
/edp-build-config.js:
--------------------------------------------------------------------------------
1 | exports.input = __dirname;
2 |
3 | var path = require( 'path' );
4 | exports.output = path.resolve( __dirname, 'output' );
5 |
6 | // var moduleEntries = 'html,htm,phtml,tpl,vm,js';
7 | // var pageEntries = 'html,htm,phtml,tpl,vm';
8 |
9 | exports.getProcessors = function () {
10 | var lessProcessor = new LessCompiler();
11 | var cssProcessor = new CssCompressor();
12 | var moduleProcessor = new ModuleCompiler();
13 | var jsProcessor = new JsCompressor();
14 | var pathMapperProcessor = new PathMapper();
15 | var addCopyright = new AddCopyright();
16 |
17 | return {
18 | 'default': [ lessProcessor, moduleProcessor, pathMapperProcessor ],
19 | 'release': [
20 | lessProcessor, cssProcessor, moduleProcessor,
21 | jsProcessor, pathMapperProcessor, addCopyright
22 | ]
23 | };
24 | };
25 |
26 | exports.exclude = [
27 | 'tool',
28 | 'doc',
29 | 'test',
30 | 'module.conf',
31 | 'dep/packages.manifest',
32 | 'dep/*/*/test',
33 | 'dep/*/*/doc',
34 | 'dep/*/*/demo',
35 | 'dep/*/*/tool',
36 | 'dep/*/*/*.md',
37 | 'dep/*/*/package.json',
38 | 'edp-*',
39 | '.edpproj',
40 | '.svn',
41 | '.git',
42 | '.gitignore',
43 | '.idea',
44 | '.project',
45 | 'Desktop.ini',
46 | 'Thumbs.db',
47 | '.DS_Store',
48 | '*.tmp',
49 | '*.bak',
50 | '*.swp'
51 | ];
52 |
53 | exports.injectProcessor = function ( processors ) {
54 | for ( var key in processors ) {
55 | global[ key ] = processors[ key ];
56 | }
57 | };
58 |
59 |
--------------------------------------------------------------------------------
/module.conf:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "src",
3 | "paths": {},
4 | "packages": [
5 | {
6 | "name": "eoo",
7 | "location": "../dep/eoo/0.0.5/src",
8 | "main": "oo"
9 | },
10 | {
11 | "name": "er",
12 | "location": "../dep/er/3.1.0-beta.3/src",
13 | "main": "main"
14 | },
15 | {
16 | "name": "esui",
17 | "location": "../dep/esui/3.1.0-beta.3/src",
18 | "main": "main"
19 | },
20 | {
21 | "name": "etpl",
22 | "location": "../dep/etpl/2.1.2/src",
23 | "main": "main"
24 | },
25 | {
26 | "name": "mini-event",
27 | "location": "../dep/mini-event/1.0.0/src",
28 | "main": "main"
29 | },
30 | {
31 | "name": "moment",
32 | "location": "../dep/moment/2.7.0/src",
33 | "main": "moment"
34 | },
35 | {
36 | "name": "underscore",
37 | "location": "../dep/underscore/1.5.2/src",
38 | "main": "underscore"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ef",
3 | "version": "3.1.0-beta.2",
4 | "maintainers": [
5 | {
6 | "name": "otakustay",
7 | "email": "otakustay@gmail.com"
8 | }
9 | ],
10 | "dependencies": {
11 | "er": "3.1.x",
12 | "esui": "3.1.x",
13 | "underscore": "1.5.x",
14 | "mini-event": "1.x",
15 | "etpl": "2.x",
16 | "moment": "2.x",
17 | "eoo": "0.x"
18 | },
19 | "main": "main",
20 | "description": "EF是ER与ESUI整合而成的高效率业务系统开发框架。",
21 | "homepage": "https://github.com/ecomfe/ef",
22 | "edp": {
23 | "wwwroot": "/",
24 | "depDir": "dep",
25 | "srcDir": "src",
26 | "loaderAutoConfig": "js,htm,html,tpl,vm,phtml",
27 | "loaderUrl": "http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-0/esl.js",
28 | "dependencies": {
29 | "er": "3.1.x",
30 | "esui": "3.1.x",
31 | "underscore": "1.5.x",
32 | "mini-event": "1.x",
33 | "etpl": "2.x",
34 | "moment": "2.x",
35 | "eoo": "0.x"
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "immed": true,
3 | "latedef": false,
4 | "newcap": true,
5 | "noarg": true,
6 | "noempty": true,
7 | "nonew": true,
8 | "plusplus": false,
9 | "quotmark": "single",
10 | "regexp": false,
11 | "undef": true,
12 | "unused": "vars",
13 | "strict": false,
14 | "trailing": true,
15 | "maxparams": 20,
16 | "maxdepth": 4,
17 | "maxlen": 120,
18 |
19 | "asi": false,
20 | "boss": false,
21 | "debug": false,
22 | "eqnull": true,
23 | "esnext": false,
24 | "evil": false,
25 | "expr": true,
26 | "funcscope": false,
27 | "globalstrict": false,
28 | "iterator": false,
29 | "lastsemic": false,
30 | "laxbreak": true,
31 | "laxcomma": false,
32 | "loopfunc": false,
33 | "multistr": false,
34 | "onecase": false,
35 | "proto": false,
36 | "regexdash": false,
37 | "scripturl": false,
38 | "smarttabs": false,
39 | "shadow": true,
40 | "sub": false,
41 | "supernew": false,
42 | "validthis": true,
43 |
44 | "browser": true,
45 | "couch": false,
46 | "devel": true,
47 | "dojo": false,
48 | "jquery": true,
49 | "mootools": false,
50 | "node": false,
51 | "nonstandard": false,
52 | "prototypejs": false,
53 | "rhino": false,
54 | "wsh": false,
55 |
56 | "nomen": true,
57 | "onevar": false,
58 | "passfail": false,
59 | "white": false,
60 |
61 | "predef": ["define", "URL"]
62 | }
63 |
--------------------------------------------------------------------------------
/src/ActionDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ecom Framework
3 | * Copyright 2013 Baidu Inc. All rights reserved.
4 | *
5 | * @ignore
6 | * @file ActionDialog
7 | * @author otakustay
8 | */
9 | define(
10 | function (require) {
11 | var ui = require('esui/main');
12 | var Dialog = require('esui/Dialog');
13 |
14 | require('./ActionPanel');
15 |
16 | /**
17 | * @class ef.ActionDialog
18 | *
19 | * 用于加载子Action的面板控件
20 | *
21 | * @constructor
22 | * @extends esui/Panel
23 | */
24 | var exports = {};
25 |
26 | exports.type = 'ActionDialog';
27 | exports.styleType = 'Dialog';
28 |
29 | /**
30 | * 设置HTML内容,`ActionDialog`没有这功能
31 | */
32 | exports.setContent = function () {
33 | };
34 |
35 | /**
36 | * 构建对话框主内容和底部内容
37 | *
38 | * @param {"foot" | "body"} type 面板类型
39 | * @param {HTMLElement} mainDOM body或foot主元素
40 | *
41 | * @return {ef.ActionPanel | esui.Panel} panel
42 | * @protected
43 | */
44 | exports.createBF = function (type, mainDOM) {
45 | if (mainDOM) {
46 | this[type === 'body' ? 'content' : 'foot'] = mainDOM.innerHTML;
47 | }
48 | else {
49 | mainDOM = document.createElement('div');
50 | this.main.appendChild(mainDOM);
51 | }
52 |
53 | this.helper.addPartClasses(type + '-panel', mainDOM);
54 | var properties = {
55 | main: mainDOM
56 | };
57 |
58 | var panelType = 'Panel';
59 | if (type === 'body') {
60 | properties.url = this.url;
61 | properties.actionOptions = this.actionOptions;
62 | panelType = 'ActionPanel';
63 | }
64 |
65 | var panel = ui.create(panelType, properties);
66 | if (type === 'body') {
67 | panel.on(
68 | 'actionattach',
69 | function () {
70 | this.resize();
71 |
72 | if (this.autoClose) {
73 | // 当子Action处理完成后对话框也一起销毁
74 | var action = this.get('action');
75 | if (typeof action.on === 'function') {
76 | // 要阻止默认行为,因为后续整个Action会销毁,有任何进一步的行为(如跳转)都没用
77 | action.on('handlefinish', false);
78 | action.on('handlefinish', this.dispose, this);
79 | }
80 | }
81 |
82 | this.fire('actionattach');
83 | },
84 | this
85 | );
86 |
87 | // 把`ActionPanel`代理的子Action事件再代理出来
88 | panel.on(
89 | '*',
90 | function (e) {
91 | if (e.type.indexOf('action@') === 0) {
92 | // 不像`ActionPanel`,这里不需要修改`type`,所以直接触发
93 | this.fire(e);
94 | }
95 | },
96 | this
97 | );
98 |
99 | // 代理`ActionPanel`的相关事件
100 | var Event = require('mini-event');
101 | Event.delegate(panel, this, 'actionloaded');
102 | Event.delegate(panel, this, 'actionloadfail');
103 | Event.delegate(panel, this, 'actionloadabort');
104 | }
105 |
106 | panel.render();
107 | this.addChild(panel, type);
108 |
109 | return panel;
110 | };
111 |
112 | /**
113 | * 重构
114 | *
115 | * @protected
116 | * @override
117 | */
118 | exports.repaint = require('esui/painters').createRepaint(
119 | Dialog.prototype.repaint,
120 | {
121 | name: ['url', 'actionOptions'],
122 | paint: function (dialog, url, actionOptions) {
123 | // 获取body panel
124 | var body = dialog.getBody();
125 | var properties = {
126 | url: url,
127 | actionOptions: actionOptions
128 | };
129 | body.setProperties(properties);
130 | }
131 | }
132 | );
133 |
134 | /**
135 | * 获取action
136 | *
137 | * @return {er.Action | null}
138 | */
139 | exports.getAction = function () {
140 | var actionPanel = this.getBody();
141 | if (actionPanel) {
142 | return actionPanel.get('action');
143 | }
144 | else {
145 | return null;
146 | }
147 | };
148 |
149 |
150 | /**
151 | * 重新加载管理的子Action(代理Panel的)
152 | */
153 | exports.reload = function () {
154 | var actionPanel = this.getBody();
155 | if (actionPanel) {
156 | actionPanel.reload();
157 | }
158 | };
159 |
160 | var ActionDialog = require('eoo').create(Dialog, exports);
161 | require('esui').register(ActionDialog);
162 | return ActionDialog;
163 | }
164 | );
165 |
--------------------------------------------------------------------------------
/src/ActionPanel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ecom Framework
3 | * Copyright 2013 Baidu Inc. All rights reserved.
4 | *
5 | * @ignore
6 | * @file ActionPanel
7 | * @author otakustay
8 | */
9 | define(
10 | function (require) {
11 | var events = require('er/events');
12 | var Panel = require('esui/Panel');
13 |
14 | /**
15 | * @class ef.ActionPanel
16 | *
17 | * 用于加载子Action的面板控件
18 | *
19 | * @extends esui.Panel
20 | * @constructor
21 | */
22 | var exports = {};
23 |
24 | exports.type = 'ActionPanel';
25 |
26 | /**
27 | * 设置HTML内容,`ActionPanel`没有这功能
28 | */
29 | exports.setContent = function () {
30 | };
31 |
32 | /**
33 | * 加载的Action的类型
34 | *
35 | * @type {string}
36 | */
37 | exports.actionType = null;
38 |
39 | /**
40 | * 加载的Action的实例
41 | *
42 | * @type {er.Action | er.meta.Promise}
43 | * @readonly
44 | */
45 | exports.action = null;
46 |
47 | /**
48 | * 代理子Action的事件
49 | *
50 | * @param {mini-event.Event} e 事件对象
51 | */
52 | function delegateActionEvent(e) {
53 | var event = require('mini-event').fromEvent(e, { preserveData: true, syncState: true });
54 | event.type = 'action@' + e.type;
55 | this.fire(event);
56 | }
57 |
58 | /**
59 | * 把已经加载的子Action赋值到控件上
60 | *
61 | * @param {mini-event.Event} e 事件对象
62 | */
63 | function attachAction(e) {
64 | if (!e.isChildAction || e.container !== this.main.id) {
65 | return;
66 | }
67 |
68 | this.action = e.action;
69 |
70 | // 进入 action 前的处理
71 | this.action.on('enter', this.enterAction, this);
72 |
73 | // 代理所有的子Action的事件
74 | if (typeof this.action.on === 'function') {
75 | this.action.on('*', delegateActionEvent, this);
76 | }
77 |
78 | this.fire('actionattach');
79 | }
80 |
81 | /**
82 | * 通知子Action加载完毕
83 | *
84 | * @param {mini-event.Event} e 事件对象
85 | */
86 | function notifyActionLoadComplete(e) {
87 | if (!e.isChildAction || e.container !== this.main.id) {
88 | return;
89 | }
90 |
91 | this.fire('actionloaded');
92 | }
93 |
94 | /**
95 | * 通知子Action加载失败
96 | *
97 | * @param {mini-event.Event} e 事件对象
98 | * @param {string} e.reason 失败原因
99 | */
100 | function notifyActionLoadFailed(e) {
101 | if (!e.isChildAction || e.container !== this.main.id) {
102 | return;
103 | }
104 |
105 | this.action = null;
106 | this.fire(
107 | 'actionloadfail',
108 | { failType: e.failType, reason: e.reason }
109 | );
110 | }
111 |
112 | /**
113 | * 通知子Action加载中断
114 | *
115 | * @param {mini-event.Event} e 事件对象
116 | * @param {string} e.reason 失败原因
117 | * @inner
118 | */
119 | function notifyActionLoadAborted(e) {
120 | if (!e.isChildAction || e.container !== this.main.id) {
121 | return;
122 | }
123 |
124 | this.fire('actionloadabort');
125 | }
126 |
127 | /**
128 | * 初始化结构
129 | *
130 | * @protected
131 | * @override
132 | */
133 | exports.initStructure = function () {
134 | events.on('enteraction', attachAction, this);
135 | events.on('enteractioncomplete', notifyActionLoadComplete, this);
136 | events.on('actionnotfound', notifyActionLoadFailed, this);
137 | events.on('permissiondenied', notifyActionLoadFailed, this);
138 | events.on('actionfail', notifyActionLoadFailed, this);
139 | events.on('enteractionfail', notifyActionLoadFailed, this);
140 | events.on('actionabort', notifyActionLoadAborted, this);
141 | };
142 |
143 | /**
144 | * 跳转前 hook
145 | */
146 | exports.enterAction = function () {
147 | this.url = this.action.context.url.toString();
148 | };
149 |
150 | /**
151 | * 销毁控件上关联的Action
152 | */
153 | exports.disposeAction = function () {
154 | var Deferred = require('er/Deferred');
155 | var action = this.action;
156 |
157 | if (!action) {
158 | return;
159 | }
160 |
161 | // Action正在加载,正确的`renderChildAction`得到的加载器有`abort`方法
162 | if (Deferred.isPromise(action) && typeof action.abort === 'function') {
163 | action.abort();
164 | }
165 | // 已经加载完的Action,但并不一定会有`leave`或`un`方法
166 | else {
167 | if (typeof action.un === 'function') {
168 | action.un('*', delegateActionEvent, this);
169 | }
170 | if (typeof action.leave === 'function') {
171 | action.leave();
172 | }
173 | }
174 |
175 | this.action = null;
176 | };
177 |
178 | /**
179 | * 重构
180 | *
181 | * @override
182 | * @protected
183 | */
184 | exports.repaint = require('esui/painters').createRepaint(
185 | Panel.prototype.repaint,
186 | {
187 | name: ['url', 'actionOptions'],
188 | paint: function (panel, url, actionOptions) {
189 | panel.disposeAction();
190 |
191 | if (!url) {
192 | return;
193 | }
194 |
195 | if (panel.lazy && panel.helper.isInStage('INITED')) {
196 | return;
197 | }
198 |
199 | var controller = require('er/controller');
200 | panel.action = controller.renderChildAction(
201 | url,
202 | panel.main.id,
203 | actionOptions
204 | );
205 |
206 | // 如果发生错误,因为事件是同步触发的,
207 | // 因此先执行`notifyActionLoadFailed`再赋值,导致没清掉。
208 | // 错误时返回的`Promise`对象是没有`abort`方法的,
209 | // 这种对象我们也不需要,因此直接清掉
210 | if (typeof panel.action.abort !== 'function') {
211 | panel.action = null;
212 | }
213 | }
214 | }
215 | );
216 |
217 | /**
218 | * 销毁控件
219 | *
220 | * @override
221 | */
222 | exports.dispose = function () {
223 | this.disposeAction();
224 |
225 | // 移除注册的一堆方法
226 | events.un('enteraction', attachAction, this);
227 | events.un('enteractioncomplete', notifyActionLoadComplete, this);
228 | events.un('actionnotfound', notifyActionLoadFailed, this);
229 | events.un('permissiondenied', notifyActionLoadFailed, this);
230 | events.un('actionfail', notifyActionLoadFailed, this);
231 | events.un('enteractionfail', notifyActionLoadFailed, this);
232 | events.un('actionabort', notifyActionLoadAborted, this);
233 |
234 | this.$super(arguments);
235 | };
236 |
237 | /**
238 | * 重新加载管理的子Action
239 | *
240 | * @param {Object} [actionOptions] 子Action的额外数据
241 | */
242 | exports.reload = function (actionOptions) {
243 | var url = this.url;
244 | this.url = null;
245 | this.setProperties({ url: url, actionOptions: actionOptions });
246 | };
247 |
248 | var ActionPanel = require('eoo').create(Panel, exports);
249 | require('esui').register(ActionPanel);
250 | return ActionPanel;
251 | }
252 | );
253 |
--------------------------------------------------------------------------------
/src/ChildView.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ecom Framework
3 | * Copyright 2013 Baidu Inc. All rights reserved.
4 | *
5 | * @ignore
6 | * @file ChildView
7 | * @author otakustay
8 | */
9 | define(
10 | function (require) {
11 | var u = require('underscore');
12 | var Control = require('esui/Control');
13 |
14 | /**
15 | * @class ef.ChildView
16 | *
17 | * 子视图控件,用于加载一个{@link er.View}
18 | *
19 | * @constructor
20 | * @extends esui.Control
21 | */
22 | var exports = {};
23 |
24 | /**
25 | * 控件的类型,始终为`"ChildView"`
26 | *
27 | * @type {string}
28 | * @readonly
29 | * @override
30 | */
31 | exports.type = 'ChildView';
32 |
33 | /**
34 | * 重绘
35 | *
36 | * @protected
37 | * @override
38 | */
39 | exports.repaint = require('esui/painters').createRepaint(
40 | Control.prototype.repaint,
41 | {
42 | name: 'viewType',
43 | paint: function (childView, viewType) {
44 | childView.disposeView();
45 |
46 | var Deferred = require('er/Deferred');
47 | childView.view = Deferred.require([viewType]);
48 | childView.view.then(u.bind(childView.fire, childView, 'viewloaded'));
49 | childView.view.then(u.bind(childView.renderView, childView));
50 | }
51 | }
52 | );
53 |
54 | /**
55 | * 销毁对应的视图
56 | *
57 | * 如果在视图模块加载过程中,调用了此方法是没有效果的,加载完后会继续把视图渲染出来
58 | */
59 | exports.disposeView = function () {
60 | var view = this.get('view');
61 |
62 | if (view && typeof view.dispose === 'function') {
63 | view.dispose();
64 | }
65 |
66 | this.view = null;
67 | };
68 |
69 | /**
70 | * 代理View的事件
71 | *
72 | * @param {mini-event.Event} e 事件对象
73 | */
74 | function delegateViewEvents(e) {
75 | var event = require('mini-event').fromEvent(e, { preserveData: true, syncState: true });
76 | event.type = 'view@' + e.type;
77 | this.fire(event);
78 | }
79 |
80 | /**
81 | * 渲染加载完毕的视图对象
82 | *
83 | * @param {Mixed} View 加载完毕的视图构造函数或对象
84 | * @protected
85 | */
86 | exports.renderView = function (View) {
87 | // 仅当渲染完成阶段才会对View进行操作,销毁的时候这里不处理
88 | if (this.helper.isInStage('RENDERED')) {
89 | this.loadedViewModule = View; // 存下来,后面还会用到的
90 | var view = this.view = typeof View === 'function' ? new View() : View;
91 | view.model = this.get('model');
92 | view.container = this.main.id;
93 | view.render();
94 |
95 | this.fire('viewrendered');
96 |
97 | view.on('*', delegateViewEvents, this);
98 | }
99 | };
100 |
101 | /**
102 | * 刷新包含的视图
103 | */
104 | exports.refresh = function () {
105 | var viewModule = this.get('loadedViewModule');
106 | if (!viewModule) {
107 | throw new Error('No view module loaded yet');
108 | }
109 | this.disposeView();
110 | this.renderView(viewModule);
111 | };
112 |
113 | var ChildView = require('eoo').create(Control, exports);
114 | require('esui').register(ChildView);
115 | return ChildView;
116 | }
117 | );
118 |
--------------------------------------------------------------------------------
/src/RemoteTreeStrategy.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var lib = require('esui/lib');
4 | var TreeStrategy = require('esui/TreeStrategy');
5 |
6 | var exports = {};
7 |
8 | exports.constructor = function (options) {
9 | if (options.requestMethod) {
10 | options.requestMethod = options.requestMethod.toLowerCase();
11 | }
12 | TreeStrategy.apply(this, arguments);
13 | this.workingRequests = {};
14 | };
15 |
16 | exports.urlTemplate = '';
17 |
18 | exports.requestMethod = 'get';
19 |
20 | exports.getRequestURL = function (node) {
21 | return lib.format(this.urlTemplate, node);
22 | };
23 |
24 | exports.getRequestData = function (node) {
25 | return null;
26 | };
27 |
28 | exports.requestNodeData = function (node) {
29 | var url = this.getRequestURL(node);
30 | var data = this.getRequestData(node);
31 | var ajax = require('er/ajax');
32 |
33 | return this.requestMethod === 'get'
34 | ? ajax.getJSON(url, data, this.useCache || false)
35 | : ajax.post(url, data, 'json');
36 | };
37 |
38 | function expandNode(tree, strategy, e) {
39 | var node = e.node;
40 | if (node.children) {
41 | tree.expandNode(node.id);
42 | return;
43 | }
44 |
45 | // 如果原来就在请求数据,把原来的断掉
46 | var xhr = tree.workingRequests[node.id];
47 | if (xhr) {
48 | xhr.abort();
49 | }
50 | xhr = this.requestNodeData(node);
51 | tree.workingRequests[node.id] = xhr;
52 | xhr.done(lib.bind(tree.expandNode, tree, node.id));
53 | }
54 |
55 | exports.enableToggleStrategy = function (tree) {
56 | tree.on(
57 | 'expand',
58 | lib.curry(expandNode, tree, this)
59 | );
60 | tree.on(
61 | 'collapse',
62 | function (e) {
63 | this.collapseNode(e.node.id, false);
64 | }
65 | );
66 | };
67 |
68 | var RemoteTreeStrategy = require('eoo').create(TreeStrategy, exports);
69 | lib.inherits(RemoteTreeStrategy, TreeStrategy);
70 | return RemoteTreeStrategy;
71 | }
72 | );
--------------------------------------------------------------------------------
/src/UIModel.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var Model = require('er/Model');
4 |
5 | /**
6 | * @class ef.UIModel
7 | *
8 | * 处理ESUI场景的`Model`实现
9 | *
10 | * @constructor
11 | * @extends er.Model
12 | */
13 | var exports = {};
14 |
15 | /**
16 | * 补0
17 | *
18 | * @param {string|number} s 输入的数字或字符串
19 | * @return {string} 不足2位的补个0
20 | */
21 | function pad(s) {
22 | s = s + '';
23 | return s.length === 1 ? '0' + s : s;
24 | }
25 |
26 | /**
27 | * 内置的格式化函数
28 | *
29 | * @type {Object}
30 | */
31 | var formatters = {
32 | /**
33 | * 格式化日期
34 | *
35 | * @param {Date} date 输入的日期
36 | * @return {string} YYYY-MM-dd格式的字符串
37 | */
38 | date: function (date) {
39 | return date.getFullYear() + '-'
40 | + pad(date.getMonth() + 1) + '-'
41 | + pad(date.getDate());
42 | },
43 | /**
44 | * 格式化日期范围
45 | *
46 | * @param {Object} range 输入的日期范围
47 | * @return {string} 逗号分隔2个日期,均为YYYY-MM-dd格式
48 | */
49 | dateRange: function (range) {
50 | return formatters.date(range.begin)
51 | + ',' + formatters.date(range.end);
52 | },
53 | /**
54 | * 格式化时间
55 | *
56 | * @param {Date} time 输入的时间
57 | * @return {string} YYYY-MM-dd HH:mm:ss格式的字符串
58 | */
59 | time: function (time) {
60 | return formatters.date(time) + ' '
61 | + pad(time.getHours()) + ':'
62 | + pad(time.getMinutes()) + ':'
63 | + pad(time.getSeconds());
64 | },
65 | /**
66 | * 格式化时间范围
67 | *
68 | * @param {Object} range 输入的时间范围
69 | * @return {string} 逗号分隔2个时间,均为YYYY-MM-dd HH:mm:ss格式
70 | */
71 | timeRange: function (range) {
72 | return formatters.time(range.begin)
73 | + ',' + formatters.time(range.end);
74 | }
75 | };
76 |
77 | /**
78 | * 配置值的格式化函数,键为属性名称,值为格式化函数,
79 | * 设置该属性时,值将先经过格式化函数处理
80 | *
81 | * @type {Object}
82 | * @public
83 | */
84 | exports.formatters = {};
85 |
86 | /**
87 | * 设置值
88 | *
89 | * @param {string} name 属性名
90 | * @param {*} value 对应的值
91 | * @param {Object=} options 相关选项
92 | * @param {boolean=} options.silent 如果该值为true则不触发`change`事件
93 | * @public
94 | */
95 | exports.set = function (name, value, options) {
96 | if (this.formatters.hasOwnProperty(name)) {
97 | value = this.formatters[name](value);
98 | }
99 | this.$super([name, value, options]);
100 | };
101 |
102 | /**
103 | * 批量设置值
104 | *
105 | * @param {Object} extension 批量值的存放对象
106 | * @param {Object=} options 相关选项
107 | * @param {boolean=} options.silent 如果该值为true则不触发`change`事件
108 | * @public
109 | */
110 | exports.fill = function (extension, options) {
111 | for (var name in extension) {
112 | if (extension.hasOwnProperty(name)
113 | && this.formatters.hasOwnProperty(name)
114 | ) {
115 | var formatter = this.formatters[name];
116 | var value = extension[name];
117 | extension[name] = formatter(value);
118 | }
119 | }
120 |
121 | this.$super(arguments);
122 | };
123 |
124 | /**
125 | * 根据传入的属性名获取一个组装后的对象
126 | *
127 | * @param {Array. | string...} names 需要的属性名列表
128 | * @return {Object} 包含`names`参数指定的属性的对象
129 | */
130 | exports.getPart = function (names) {
131 | if (Object.prototype.toString.call(names) !== '[object Array]') {
132 | names = [].slice.call(arguments);
133 | }
134 |
135 | var part = {};
136 | for (var i = 0; i < names.length; i++) {
137 | var name = names[i];
138 | part[name] = this.get(name);
139 | }
140 | return part;
141 | };
142 |
143 | var UIModel = require('eoo').create(Model, UIModel);
144 |
145 | UIModel.formatters = formatters;
146 |
147 | return UIModel;
148 | }
149 | );
--------------------------------------------------------------------------------
/src/UIView.js:
--------------------------------------------------------------------------------
1 | define(
2 | function (require) {
3 | var u = require('underscore');
4 | var View = require('er/View');
5 |
6 | require('ef/ActionDialog');
7 |
8 | /**
9 | * @class ef.UIView
10 | *
11 | * 与ESUI结合的`View`基类
12 | *
13 | * @constructor
14 | * @extends er.View
15 | */
16 | var exports = {};
17 |
18 | function getProperty(target, path) {
19 | var value = target;
20 | for (var i = 0; i < path.length; i++) {
21 | value = value[path[i]];
22 | }
23 |
24 | return value;
25 | }
26 |
27 | /**
28 | * 替换元素属性中的特殊值
29 | *
30 | * @param {string} value 需要处理的值
31 | * @return {*} 处理完的值
32 | * @public
33 | */
34 | exports.replaceValue = function (value) {
35 | if (typeof value !== 'string') {
36 | return value;
37 | }
38 |
39 | if (value === '@@' || value === '**') {
40 | return this.model;
41 | }
42 |
43 | var prefix = value.charAt(0);
44 | var actualValue = value.substring(1);
45 |
46 | if (prefix === '@' || prefix === '*') {
47 | var path = actualValue.split('.');
48 | var value = this.model.get(path[0]);
49 | return path.length > 1
50 | ? getProperty(value, path.slice(1))
51 | : value;
52 | }
53 | else {
54 | return value;
55 | }
56 | };
57 |
58 | /**
59 | * 根据id获取当前视图下的控件
60 | *
61 | * @param {string} id 控件的id
62 | * @return {Control=} 对应的控件
63 | * @protected
64 | */
65 | exports.get = function (id) {
66 | return this.viewContext.get(id);
67 | };
68 |
69 | /**
70 | * 根据id获取控件实例,如无相关实例则返回`esui.SafeWrapper`
71 | *
72 | * @param {string} id 控件id
73 | * @return {Control} 根据id获取的控件
74 | */
75 | exports.getSafely = function (id) {
76 | return this.viewContext.getSafely(id);
77 | };
78 |
79 | /**
80 | * 根据name获取当前视图下的控件组
81 | *
82 | * @param {string} name 控件组的名称
83 | * @return {ControlGroup} 对应的控件组
84 | * @protected
85 | */
86 | exports.getGroup = function (name) {
87 | return this.viewContext.getGroup(name);
88 | };
89 |
90 | /**
91 | * 创建一个控件实例
92 | *
93 | * @param {string} type 控件的类型
94 | * @param {Obejct=} options 创建控件时的选项
95 | * @return {Control}
96 | * @proceted
97 | */
98 | exports.create = function (type, options) {
99 | options = options || {};
100 | if (!options.viewContext) {
101 | options.viewContext = this.viewContext;
102 | }
103 | return require('esui').create(type, options);
104 | };
105 |
106 | /**
107 | * 显示一条提示信息
108 | *
109 | * @param {string | Object} content 提示的内容或完整的配置项
110 | * @param {string=} title 提示框的标题,如`content`提供配置项则无此参数
111 | * @return {esui/Dialog}
112 | * @protected
113 | */
114 | exports.alert = function (content, title) {
115 | var options = typeof content === 'string'
116 | ? { title: title || document.title, content: content }
117 | : u.clone(content);
118 | if (!options.viewContext) {
119 | options.viewContext = this.viewContext;
120 | }
121 |
122 | var Dialog = require('esui/Dialog');
123 | return Dialog.alert(options);
124 | };
125 |
126 | /**
127 | * 显示一条确认信息
128 | *
129 | * @param {string | Object} content 提示的内容或完整的配置项
130 | * @param {string=} title 提示框的标题,如`content`提供配置项则无此参数
131 | * @return {esui/Dialog}
132 | * @protected
133 | */
134 | exports.confirm = function (content, title) {
135 | var options = typeof content === 'string'
136 | ? { title: title || document.title, content: content }
137 | : u.clone(content);
138 | if (!options.viewContext) {
139 | options.viewContext = this.viewContext;
140 | }
141 |
142 | var Dialog = require('esui/Dialog');
143 | return Dialog.confirm(options);
144 | };
145 |
146 | /**
147 | * 显示ActionDialog
148 | *
149 | * @param {Object} options 参数
150 | * @return {esui/Dialog}
151 | * @protected
152 | */
153 | exports.popActionDialog = function (options) {
154 | //创建main
155 | var main = document.createElement('div');
156 | document.body.appendChild(main);
157 |
158 | var defaults = {
159 | width: 600,
160 | needFoot: false,
161 | draggable: true,
162 | closeOnHide: true,
163 | autoClose: true,
164 | main: main,
165 | viewContext: this.viewContext
166 | };
167 | options = u.defaults({}, options, defaults);
168 |
169 | var ui = require('esui/main');
170 | var dialog = ui.create('ActionDialog', options);
171 |
172 | dialog.render();
173 | dialog.show();
174 | return dialog;
175 | };
176 |
177 | /*
178 | * 声明控件的事件。该属性有2种方式:
179 | *
180 | * - 以`id:eventName`为键,以处理函数为值。
181 | * - 以`id`为键,值为一个对象,对象中以`eventName`为键,处理函数为值。
182 | *
183 | * 在此处声明的事件,运行时的`this`对象均是`View`实例,而非控件的实例。
184 | *
185 | * 同时,在运行期,`UIView`会克隆该属性,将其中所有的处理函数都进行一次`bind`,
186 | * 将`this`指向自身,因此运行时的`uiEvents`与类声明时的不会相同。
187 | *
188 | * 如果需要解除某个事件的绑定,可以使用`.on('type', this.uiEvents.xxx)`进行。
189 | *
190 | * @type {Object}
191 | * @public
192 | */
193 | exports.uiEvents = null;
194 |
195 | /*
196 | * 获取当前视图关联的控件事件声明。参考`uiEvents`属性
197 | *
198 | * @return {Object}
199 | * @public
200 | */
201 | exports.getUIEvents = function () {
202 | return this.uiEvents || {};
203 | };
204 |
205 | /**
206 | * 声明控件的额外属性。
207 | *
208 | * 这个属性以控件的id为键,以一个对象为值。对象表示要额外附加到控件上的属性。
209 | * 当控件实例化时,会把DOM中声明的属性与此处声明的合并在一起,此处声明的为优先。
210 | *
211 | * @type {Object}
212 | * @public
213 | */
214 | exports.uiProperties = null;
215 |
216 | /**
217 | * 声明当前视图关联的控件的额外属性,参考`uiProperties`属性
218 | *
219 | * @return {Object}
220 | */
221 | exports.getUIProperties = function () {
222 | return this.uiProperties;
223 | };
224 |
225 | /**
226 | * 给指定的控件绑定事件
227 | *
228 | * @param {UIView} view View对象实例
229 | * @param {string} id 控件的id
230 | * @param {string} eventName 事件名称
231 | * @param {function | string} handler 事件处理函数,或者对应的方法名
232 | * @return {function} 绑定到控件上的事件处理函数,不等于`handler`参数
233 | * @inner
234 | */
235 | function bindEventToControl(view, id, eventName, handler) {
236 | if (typeof handler === 'string') {
237 | handler = view[handler];
238 | }
239 |
240 | // TODO: mini-event后续会支持`false`作为处理函数,要考虑下
241 | if (typeof handler !== 'function') {
242 | return handler;
243 | }
244 |
245 | var control = view.get(id);
246 | if (control) {
247 | control.on(eventName, handler, view);
248 | }
249 |
250 | return handler;
251 | }
252 |
253 | /**
254 | * 绑定控件的事件。
255 | *
256 | * @override
257 | * @protected
258 | */
259 | exports.bindEvents = function () {
260 | var events = this.getUIEvents();
261 | if (!events) {
262 | return;
263 | }
264 |
265 | for (var key in events) {
266 | if (!events.hasOwnProperty(key)) {
267 | // 下面逻辑太长了,在这里先中断
268 | continue;
269 | }
270 |
271 | // 可以用`submit:click`的形式在指定控件上绑定指定类型的控件
272 | var segments = key.split(':');
273 | if (segments.length > 1) {
274 | var id = segments[0];
275 | var type = segments[1];
276 | var handler = events[key];
277 | bindEventToControl(this, id, type, handler);
278 | }
279 | // 也可以是一个控件的id,值是对象,里面每一项都是一个事件类型
280 | else {
281 | var map = events[key];
282 |
283 | if (typeof map !== 'object') {
284 | return;
285 | }
286 |
287 | for (var type in map) {
288 | if (map.hasOwnProperty(type)) {
289 | var handler = map[type];
290 | bindEventToControl(this, key, type, handler);
291 | }
292 | }
293 | }
294 | }
295 | };
296 |
297 | var counter = 0x861005;
298 | function getGUID() {
299 | return 'ef-' + counter++;
300 | }
301 |
302 | /**
303 | * 获取当前视图的名称,通常用于生成`ViewContext`
304 | *
305 | * @return {string}
306 | * @protected
307 | */
308 | exports.getViewName = function () {
309 | if (this.name) {
310 | return this.name;
311 | }
312 |
313 | // 从构造函数把名字猜出来
314 | var name = this.constructor && this.constructor.name;
315 | if (!name && this.constructor) {
316 | // 用正则猜名字
317 | var functionString = this.constructor.toString();
318 | var match = /function\s+([^\(]*)/.exec(functionString);
319 | // 去除函数名后面的空格
320 | name = match && match[1].replace(/\s+$/g, '');
321 | }
322 | // 再不行用计数
323 | if (!name) {
324 | name = getGUID();
325 | }
326 |
327 | // 以下代码是一个洁癖和强近症患者所写:
328 |
329 | // 如果名字是XxxView,把最后的View字样去掉
330 | name = name.replace(/View$/, '');
331 | // 从PascalCase转为横线分隔,这里需要注意,连续的大写字母不应该连续分隔
332 | name = name.replace(
333 | /[A-Z]{2,}/g,
334 | function (match) {
335 | // 这里把ABCD这种连续的大写,转成AbcD这种形式。
336 | // 如果`encodeURIComponent`,会变成`encodeUriComponent`,
337 | // 然后加横线后就是`encode-uri-component`得到正确的结果
338 | return match.charAt(0)
339 | + match.slice(1, -1).toLowerCase()
340 | + match.charAt(match.length - 1);
341 | }
342 | );
343 | name = name.replace(
344 | /[A-Z]/g,
345 | function (match) { return '-' + match.toLowerCase(); }
346 | );
347 | if (name.charAt(0) === '-') {
348 | name = name.substring(1);
349 | }
350 |
351 | return name;
352 | };
353 |
354 | /**
355 | * 创建当前`UIView`使用的`ViewContext`对象
356 | *
357 | * @return {ViewContext}
358 | * @public
359 | */
360 | exports.createViewContext = function () {
361 | var ViewContext = require('esui/ViewContext');
362 | var name = this.getViewName();
363 |
364 | return new ViewContext(name || null);
365 | };
366 |
367 | /**
368 | * 当容器渲染完毕后触发,用于控制元素可见性及绑定事件等DOM操作
369 | *
370 | * @override
371 | * @protected
372 | */
373 | exports.enterDocument = function () {
374 | this.viewContext = this.createViewContext();
375 |
376 | var container = this.getContainerElement();
377 | var options = {
378 | viewContext: this.viewContext,
379 | properties: this.getUIProperties(),
380 | valueReplacer: u.bind(this.replaceValue, this)
381 | };
382 | try {
383 | require('esui').init(container, options);
384 | }
385 | catch (ex) {
386 | var error = new Error(
387 | 'ESUI initialization error on view '
388 | + 'because: ' + ex.message
389 | );
390 | error.actualError = ex;
391 | throw error;
392 | }
393 |
394 |
395 | this.bindEvents();
396 | };
397 |
398 | /**
399 | * 销毁当前视图
400 | *
401 | * @override
402 | * @protected
403 | */
404 | exports.dispose = function () {
405 | if (this.viewContext) {
406 | this.viewContext.dispose();
407 | this.viewContext = null;
408 | }
409 | this.$super(arguments);
410 | };
411 |
412 | var UIView = require('eoo').create(View, exports);
413 | return UIView;
414 | }
415 | );
416 |
--------------------------------------------------------------------------------
/test/UIView.js:
--------------------------------------------------------------------------------
1 | define(function (require) {
2 | var UIView = require('ef/UIView');
3 | var template = require('er/template');
4 |
5 | require('er/tpl!./tpl/plain.tpl');
6 |
7 | describe('UIView', function () {
8 | it('should be a constructor', function () {
9 | expect(UIView).toBeOfType('function');
10 | });
11 |
12 | it('should be instantiable', function () {
13 | expect(new UIView()).toBeOfType('object');
14 | });
15 |
16 | describe('when rendered', function () {
17 | var view = new UIView();
18 | view.container = 'container';
19 | view.template = 'plain';
20 | view.render();
21 |
22 | it ('should merge the template specified with `template` property', function () {
23 | expect(container.innerHTML).toContain('\nabc');
24 | view.dispose();
25 | });
26 | });
27 | });
28 | });
--------------------------------------------------------------------------------
/test/asset/css/jasmine.css:
--------------------------------------------------------------------------------
1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
2 |
3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
4 | #HTMLReporter a { text-decoration: none; }
5 | #HTMLReporter a:hover { text-decoration: underline; }
6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; }
9 | #HTMLReporter .version { color: #aaaaaa; }
10 | #HTMLReporter .banner { margin-top: 14px; }
11 | #HTMLReporter .duration { color: #aaaaaa; float: right; }
12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; }
15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; }
17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; }
21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
24 | #HTMLReporter .runningAlert { background-color: #666666; }
25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; }
26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; }
27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
28 | #HTMLReporter .passingAlert { background-color: #a6b779; }
29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
30 | #HTMLReporter .failingAlert { background-color: #cf867e; }
31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; }
32 | #HTMLReporter .results { margin-top: 14px; }
33 | #HTMLReporter #details { display: none; }
34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
38 | #HTMLReporter.showDetails .summary { display: none; }
39 | #HTMLReporter.showDetails #details { display: block; }
40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
41 | #HTMLReporter .summary { margin-top: 14px; }
42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; }
45 | #HTMLReporter .description + .suite { margin-top: 0; }
46 | #HTMLReporter .suite { margin-top: 14px; }
47 | #HTMLReporter .suite a { color: #333333; }
48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; }
49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
51 | #HTMLReporter .resultMessage span.result { display: block; }
52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
53 |
54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; }
56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
61 | #TrivialReporter .runner.running { background-color: yellow; }
62 | #TrivialReporter .options { text-align: right; font-size: .8em; }
63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
64 | #TrivialReporter .suite .suite { margin: 5px; }
65 | #TrivialReporter .suite.passed { background-color: #dfd; }
66 | #TrivialReporter .suite.failed { background-color: #fdd; }
67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
71 | #TrivialReporter .spec.skipped { background-color: #bbb; }
72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
73 | #TrivialReporter .passed { background-color: #cfc; display: none; }
74 | #TrivialReporter .failed { background-color: #fbb; }
75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
77 | #TrivialReporter .resultMessage .mismatch { color: black; }
78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; }
82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
83 |
--------------------------------------------------------------------------------
/test/asset/js/async.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var withoutAsync = {};
3 |
4 | ["it", "beforeEach", "afterEach"].forEach(function(jasmineFunction) {
5 | withoutAsync[jasmineFunction] = jasmine.Env.prototype[jasmineFunction];
6 | return jasmine.Env.prototype[jasmineFunction] = function() {
7 | var args = Array.prototype.slice.call(arguments, 0);
8 | var timeout = null;
9 | if (isLastArgumentATimeout(args)) {
10 | timeout = args.pop();
11 | // The changes to the jasmine test runner causes undef to be passed when
12 | // calling all it()'s now. If the last argument isn't a timeout and the
13 | // last argument IS undefined, let's just pop it off. Since out of bounds
14 | // items are undefined anyways, *hopefully* removing an undef item won't
15 | // hurt.
16 | } else if (args[args.length-1] == undefined) {
17 | args.pop();
18 | }
19 | if (isLastArgumentAnAsyncSpecFunction(args))
20 | {
21 | var specFunction = args.pop();
22 | args.push(function() {
23 | return asyncSpec(specFunction, this, timeout);
24 | });
25 | }
26 | return withoutAsync[jasmineFunction].apply(this, args);
27 | };
28 | });
29 |
30 | function isLastArgumentATimeout(args)
31 | {
32 | return args.length > 0 && (typeof args[args.length-1]) === "number";
33 | }
34 |
35 | function isLastArgumentAnAsyncSpecFunction(args)
36 | {
37 | return args.length > 0 && (typeof args[args.length-1]) === "function" && args[args.length-1].length > 0;
38 | }
39 |
40 | function asyncSpec(specFunction, spec, timeout) {
41 | if (timeout == null) timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL || 1000;
42 | var done = false;
43 | spec.runs(function() {
44 | try {
45 | return specFunction.call(spec, function(error) {
46 | done = true;
47 | if (error != null) return spec.fail(error);
48 | });
49 | } catch (e) {
50 | done = true;
51 | throw e;
52 | }
53 | });
54 | return spec.waitsFor(function() {
55 | if (done === true) {
56 | return true;
57 | }
58 | }, "spec to complete", timeout);
59 | };
60 |
61 | }).call(this);
--------------------------------------------------------------------------------
/test/asset/js/jasmine-html.js:
--------------------------------------------------------------------------------
1 | jasmine.HtmlReporterHelpers = {};
2 |
3 | jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
4 | var el = document.createElement(type);
5 |
6 | for (var i = 2; i < arguments.length; i++) {
7 | var child = arguments[i];
8 |
9 | if (typeof child === 'string') {
10 | el.appendChild(document.createTextNode(child));
11 | } else {
12 | if (child) {
13 | el.appendChild(child);
14 | }
15 | }
16 | }
17 |
18 | for (var attr in attrs) {
19 | if (attr == "className") {
20 | el[attr] = attrs[attr];
21 | } else {
22 | el.setAttribute(attr, attrs[attr]);
23 | }
24 | }
25 |
26 | return el;
27 | };
28 |
29 | jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
30 | var results = child.results();
31 | var status = results.passed() ? 'passed' : 'failed';
32 | if (results.skipped) {
33 | status = 'skipped';
34 | }
35 |
36 | return status;
37 | };
38 |
39 | jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
40 | var parentDiv = this.dom.summary;
41 | var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
42 | var parent = child[parentSuite];
43 |
44 | if (parent) {
45 | if (typeof this.views.suites[parent.id] == 'undefined') {
46 | this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
47 | }
48 | parentDiv = this.views.suites[parent.id].element;
49 | }
50 |
51 | parentDiv.appendChild(childElement);
52 | };
53 |
54 |
55 | jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
56 | for(var fn in jasmine.HtmlReporterHelpers) {
57 | ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
58 | }
59 | };
60 |
61 | jasmine.HtmlReporter = function(_doc) {
62 | var self = this;
63 | var doc = _doc || window.document;
64 |
65 | var reporterView;
66 |
67 | var dom = {};
68 |
69 | // Jasmine Reporter Public Interface
70 | self.logRunningSpecs = false;
71 |
72 | self.reportRunnerStarting = function(runner) {
73 | var specs = runner.specs() || [];
74 |
75 | if (specs.length == 0) {
76 | return;
77 | }
78 |
79 | createReporterDom(runner.env.versionString());
80 | doc.body.appendChild(dom.reporter);
81 | setExceptionHandling();
82 |
83 | reporterView = new jasmine.HtmlReporter.ReporterView(dom);
84 | reporterView.addSpecs(specs, self.specFilter);
85 | };
86 |
87 | self.reportRunnerResults = function(runner) {
88 | reporterView && reporterView.complete();
89 | };
90 |
91 | self.reportSuiteResults = function(suite) {
92 | reporterView.suiteComplete(suite);
93 | };
94 |
95 | self.reportSpecStarting = function(spec) {
96 | if (self.logRunningSpecs) {
97 | self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
98 | }
99 | };
100 |
101 | self.reportSpecResults = function(spec) {
102 | reporterView.specComplete(spec);
103 | };
104 |
105 | self.log = function() {
106 | var console = jasmine.getGlobal().console;
107 | if (console && console.log) {
108 | if (console.log.apply) {
109 | console.log.apply(console, arguments);
110 | } else {
111 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
112 | }
113 | }
114 | };
115 |
116 | self.specFilter = function(spec) {
117 | if (!focusedSpecName()) {
118 | return true;
119 | }
120 |
121 | return spec.getFullName().indexOf(focusedSpecName()) === 0;
122 | };
123 |
124 | return self;
125 |
126 | function focusedSpecName() {
127 | var specName;
128 |
129 | (function memoizeFocusedSpec() {
130 | if (specName) {
131 | return;
132 | }
133 |
134 | var paramMap = [];
135 | var params = jasmine.HtmlReporter.parameters(doc);
136 |
137 | for (var i = 0; i < params.length; i++) {
138 | var p = params[i].split('=');
139 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
140 | }
141 |
142 | specName = paramMap.spec;
143 | })();
144 |
145 | return specName;
146 | }
147 |
148 | function createReporterDom(version) {
149 | dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
150 | dom.banner = self.createDom('div', { className: 'banner' },
151 | self.createDom('span', { className: 'title' }, "Jasmine "),
152 | self.createDom('span', { className: 'version' }, version)),
153 |
154 | dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
155 | dom.alert = self.createDom('div', {className: 'alert'},
156 | self.createDom('span', { className: 'exceptions' },
157 | self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
158 | self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
159 | dom.results = self.createDom('div', {className: 'results'},
160 | dom.summary = self.createDom('div', { className: 'summary' }),
161 | dom.details = self.createDom('div', { id: 'details' }))
162 | );
163 | }
164 |
165 | function noTryCatch() {
166 | return window.location.search.match(/catch=false/);
167 | }
168 |
169 | function searchWithCatch() {
170 | var params = jasmine.HtmlReporter.parameters(window.document);
171 | var removed = false;
172 | var i = 0;
173 |
174 | while (!removed && i < params.length) {
175 | if (params[i].match(/catch=/)) {
176 | params.splice(i, 1);
177 | removed = true;
178 | }
179 | i++;
180 | }
181 | if (jasmine.CATCH_EXCEPTIONS) {
182 | params.push("catch=false");
183 | }
184 |
185 | return params.join("&");
186 | }
187 |
188 | function setExceptionHandling() {
189 | var chxCatch = document.getElementById('no_try_catch');
190 |
191 | if (noTryCatch()) {
192 | chxCatch.setAttribute('checked', true);
193 | jasmine.CATCH_EXCEPTIONS = false;
194 | }
195 | chxCatch.onclick = function() {
196 | window.location.search = searchWithCatch();
197 | };
198 | }
199 | };
200 | jasmine.HtmlReporter.parameters = function(doc) {
201 | var paramStr = doc.location.search.substring(1);
202 | var params = [];
203 |
204 | if (paramStr.length > 0) {
205 | params = paramStr.split('&');
206 | }
207 | return params;
208 | }
209 | jasmine.HtmlReporter.sectionLink = function(sectionName) {
210 | var link = '?';
211 | var params = [];
212 |
213 | if (sectionName) {
214 | params.push('spec=' + encodeURIComponent(sectionName));
215 | }
216 | if (!jasmine.CATCH_EXCEPTIONS) {
217 | params.push("catch=false");
218 | }
219 | if (params.length > 0) {
220 | link += params.join("&");
221 | }
222 |
223 | return link;
224 | };
225 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
226 | jasmine.HtmlReporter.ReporterView = function(dom) {
227 | this.startedAt = new Date();
228 | this.runningSpecCount = 0;
229 | this.completeSpecCount = 0;
230 | this.passedCount = 0;
231 | this.failedCount = 0;
232 | this.skippedCount = 0;
233 |
234 | this.createResultsMenu = function() {
235 | this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
236 | this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
237 | ' | ',
238 | this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
239 |
240 | this.summaryMenuItem.onclick = function() {
241 | dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
242 | };
243 |
244 | this.detailsMenuItem.onclick = function() {
245 | showDetails();
246 | };
247 | };
248 |
249 | this.addSpecs = function(specs, specFilter) {
250 | this.totalSpecCount = specs.length;
251 |
252 | this.views = {
253 | specs: {},
254 | suites: {}
255 | };
256 |
257 | for (var i = 0; i < specs.length; i++) {
258 | var spec = specs[i];
259 | this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
260 | if (specFilter(spec)) {
261 | this.runningSpecCount++;
262 | }
263 | }
264 | };
265 |
266 | this.specComplete = function(spec) {
267 | this.completeSpecCount++;
268 |
269 | if (isUndefined(this.views.specs[spec.id])) {
270 | this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
271 | }
272 |
273 | var specView = this.views.specs[spec.id];
274 |
275 | switch (specView.status()) {
276 | case 'passed':
277 | this.passedCount++;
278 | break;
279 |
280 | case 'failed':
281 | this.failedCount++;
282 | break;
283 |
284 | case 'skipped':
285 | this.skippedCount++;
286 | break;
287 | }
288 |
289 | specView.refresh();
290 | this.refresh();
291 | };
292 |
293 | this.suiteComplete = function(suite) {
294 | var suiteView = this.views.suites[suite.id];
295 | if (isUndefined(suiteView)) {
296 | return;
297 | }
298 | suiteView.refresh();
299 | };
300 |
301 | this.refresh = function() {
302 |
303 | if (isUndefined(this.resultsMenu)) {
304 | this.createResultsMenu();
305 | }
306 |
307 | // currently running UI
308 | if (isUndefined(this.runningAlert)) {
309 | this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
310 | dom.alert.appendChild(this.runningAlert);
311 | }
312 | this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
313 |
314 | // skipped specs UI
315 | if (isUndefined(this.skippedAlert)) {
316 | this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
317 | }
318 |
319 | this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
320 |
321 | if (this.skippedCount === 1 && isDefined(dom.alert)) {
322 | dom.alert.appendChild(this.skippedAlert);
323 | }
324 |
325 | // passing specs UI
326 | if (isUndefined(this.passedAlert)) {
327 | this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
328 | }
329 | this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
330 |
331 | // failing specs UI
332 | if (isUndefined(this.failedAlert)) {
333 | this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
334 | }
335 | this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
336 |
337 | if (this.failedCount === 1 && isDefined(dom.alert)) {
338 | dom.alert.appendChild(this.failedAlert);
339 | dom.alert.appendChild(this.resultsMenu);
340 | }
341 |
342 | // summary info
343 | this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
344 | this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
345 | };
346 |
347 | this.complete = function() {
348 | dom.alert.removeChild(this.runningAlert);
349 |
350 | this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
351 |
352 | if (this.failedCount === 0) {
353 | dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
354 | } else {
355 | showDetails();
356 | }
357 |
358 | dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
359 | };
360 |
361 | return this;
362 |
363 | function showDetails() {
364 | if (dom.reporter.className.search(/showDetails/) === -1) {
365 | dom.reporter.className += " showDetails";
366 | }
367 | }
368 |
369 | function isUndefined(obj) {
370 | return typeof obj === 'undefined';
371 | }
372 |
373 | function isDefined(obj) {
374 | return !isUndefined(obj);
375 | }
376 |
377 | function specPluralizedFor(count) {
378 | var str = count + " spec";
379 | if (count > 1) {
380 | str += "s"
381 | }
382 | return str;
383 | }
384 |
385 | };
386 |
387 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
388 |
389 |
390 | jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
391 | this.spec = spec;
392 | this.dom = dom;
393 | this.views = views;
394 |
395 | this.symbol = this.createDom('li', { className: 'pending' });
396 | this.dom.symbolSummary.appendChild(this.symbol);
397 |
398 | this.summary = this.createDom('div', { className: 'specSummary' },
399 | this.createDom('a', {
400 | className: 'description',
401 | href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
402 | title: this.spec.getFullName()
403 | }, this.spec.description)
404 | );
405 |
406 | this.detail = this.createDom('div', { className: 'specDetail' },
407 | this.createDom('a', {
408 | className: 'description',
409 | href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
410 | title: this.spec.getFullName()
411 | }, this.spec.getFullName())
412 | );
413 | };
414 |
415 | jasmine.HtmlReporter.SpecView.prototype.status = function() {
416 | return this.getSpecStatus(this.spec);
417 | };
418 |
419 | jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
420 | this.symbol.className = this.status();
421 |
422 | switch (this.status()) {
423 | case 'skipped':
424 | break;
425 |
426 | case 'passed':
427 | this.appendSummaryToSuiteDiv();
428 | break;
429 |
430 | case 'failed':
431 | this.appendSummaryToSuiteDiv();
432 | this.appendFailureDetail();
433 | break;
434 | }
435 | };
436 |
437 | jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
438 | this.summary.className += ' ' + this.status();
439 | this.appendToSummary(this.spec, this.summary);
440 | };
441 |
442 | jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
443 | this.detail.className += ' ' + this.status();
444 |
445 | var resultItems = this.spec.results().getItems();
446 | var messagesDiv = this.createDom('div', { className: 'messages' });
447 |
448 | for (var i = 0; i < resultItems.length; i++) {
449 | var result = resultItems[i];
450 |
451 | if (result.type == 'log') {
452 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
453 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
454 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
455 |
456 | if (result.trace.stack) {
457 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
458 | }
459 | }
460 | }
461 |
462 | if (messagesDiv.childNodes.length > 0) {
463 | this.detail.appendChild(messagesDiv);
464 | this.dom.details.appendChild(this.detail);
465 | }
466 | };
467 |
468 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
469 | this.suite = suite;
470 | this.dom = dom;
471 | this.views = views;
472 |
473 | this.element = this.createDom('div', { className: 'suite' },
474 | this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
475 | );
476 |
477 | this.appendToSummary(this.suite, this.element);
478 | };
479 |
480 | jasmine.HtmlReporter.SuiteView.prototype.status = function() {
481 | return this.getSpecStatus(this.suite);
482 | };
483 |
484 | jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
485 | this.element.className += " " + this.status();
486 | };
487 |
488 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
489 |
490 | /* @deprecated Use jasmine.HtmlReporter instead
491 | */
492 | jasmine.TrivialReporter = function(doc) {
493 | this.document = doc || document;
494 | this.suiteDivs = {};
495 | this.logRunningSpecs = false;
496 | };
497 |
498 | jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
499 | var el = document.createElement(type);
500 |
501 | for (var i = 2; i < arguments.length; i++) {
502 | var child = arguments[i];
503 |
504 | if (typeof child === 'string') {
505 | el.appendChild(document.createTextNode(child));
506 | } else {
507 | if (child) { el.appendChild(child); }
508 | }
509 | }
510 |
511 | for (var attr in attrs) {
512 | if (attr == "className") {
513 | el[attr] = attrs[attr];
514 | } else {
515 | el.setAttribute(attr, attrs[attr]);
516 | }
517 | }
518 |
519 | return el;
520 | };
521 |
522 | jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
523 | var showPassed, showSkipped;
524 |
525 | this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
526 | this.createDom('div', { className: 'banner' },
527 | this.createDom('div', { className: 'logo' },
528 | this.createDom('span', { className: 'title' }, "Jasmine"),
529 | this.createDom('span', { className: 'version' }, runner.env.versionString())),
530 | this.createDom('div', { className: 'options' },
531 | "Show ",
532 | showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
533 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
534 | showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
535 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
536 | )
537 | ),
538 |
539 | this.runnerDiv = this.createDom('div', { className: 'runner running' },
540 | this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
541 | this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
542 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
543 | );
544 |
545 | this.document.body.appendChild(this.outerDiv);
546 |
547 | var suites = runner.suites();
548 | for (var i = 0; i < suites.length; i++) {
549 | var suite = suites[i];
550 | var suiteDiv = this.createDom('div', { className: 'suite' },
551 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
552 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
553 | this.suiteDivs[suite.id] = suiteDiv;
554 | var parentDiv = this.outerDiv;
555 | if (suite.parentSuite) {
556 | parentDiv = this.suiteDivs[suite.parentSuite.id];
557 | }
558 | parentDiv.appendChild(suiteDiv);
559 | }
560 |
561 | this.startedAt = new Date();
562 |
563 | var self = this;
564 | showPassed.onclick = function(evt) {
565 | if (showPassed.checked) {
566 | self.outerDiv.className += ' show-passed';
567 | } else {
568 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
569 | }
570 | };
571 |
572 | showSkipped.onclick = function(evt) {
573 | if (showSkipped.checked) {
574 | self.outerDiv.className += ' show-skipped';
575 | } else {
576 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
577 | }
578 | };
579 | };
580 |
581 | jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
582 | var results = runner.results();
583 | var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
584 | this.runnerDiv.setAttribute("class", className);
585 | //do it twice for IE
586 | this.runnerDiv.setAttribute("className", className);
587 | var specs = runner.specs();
588 | var specCount = 0;
589 | for (var i = 0; i < specs.length; i++) {
590 | if (this.specFilter(specs[i])) {
591 | specCount++;
592 | }
593 | }
594 | var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
595 | message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
596 | this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
597 |
598 | this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
599 | };
600 |
601 | jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
602 | var results = suite.results();
603 | var status = results.passed() ? 'passed' : 'failed';
604 | if (results.totalCount === 0) { // todo: change this to check results.skipped
605 | status = 'skipped';
606 | }
607 | this.suiteDivs[suite.id].className += " " + status;
608 | };
609 |
610 | jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
611 | if (this.logRunningSpecs) {
612 | this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
613 | }
614 | };
615 |
616 | jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
617 | var results = spec.results();
618 | var status = results.passed() ? 'passed' : 'failed';
619 | if (results.skipped) {
620 | status = 'skipped';
621 | }
622 | var specDiv = this.createDom('div', { className: 'spec ' + status },
623 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
624 | this.createDom('a', {
625 | className: 'description',
626 | href: '?spec=' + encodeURIComponent(spec.getFullName()),
627 | title: spec.getFullName()
628 | }, spec.description));
629 |
630 |
631 | var resultItems = results.getItems();
632 | var messagesDiv = this.createDom('div', { className: 'messages' });
633 | for (var i = 0; i < resultItems.length; i++) {
634 | var result = resultItems[i];
635 |
636 | if (result.type == 'log') {
637 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
638 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
639 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
640 |
641 | if (result.trace.stack) {
642 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
643 | }
644 | }
645 | }
646 |
647 | if (messagesDiv.childNodes.length > 0) {
648 | specDiv.appendChild(messagesDiv);
649 | }
650 |
651 | this.suiteDivs[spec.suite.id].appendChild(specDiv);
652 | };
653 |
654 | jasmine.TrivialReporter.prototype.log = function() {
655 | var console = jasmine.getGlobal().console;
656 | if (console && console.log) {
657 | if (console.log.apply) {
658 | console.log.apply(console, arguments);
659 | } else {
660 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
661 | }
662 | }
663 | };
664 |
665 | jasmine.TrivialReporter.prototype.getLocation = function() {
666 | return this.document.location;
667 | };
668 |
669 | jasmine.TrivialReporter.prototype.specFilter = function(spec) {
670 | var paramMap = {};
671 | var params = this.getLocation().search.substring(1).split('&');
672 | for (var i = 0; i < params.length; i++) {
673 | var p = params[i].split('=');
674 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
675 | }
676 |
677 | if (!paramMap.spec) {
678 | return true;
679 | }
680 | return spec.getFullName().indexOf(paramMap.spec) === 0;
681 | };
682 |
--------------------------------------------------------------------------------
/test/matchers.js:
--------------------------------------------------------------------------------
1 | beforeEach(function() {
2 | this.addMatchers({
3 | toBeOfType: function(type) {
4 | return {}.toString.call(this.actual).slice(8, -1).toUpperCase() === type.toUpperCase();
5 | }
6 | });
7 | });
--------------------------------------------------------------------------------
/test/run.htm:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | Jasmine Spec Runner
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
62 |
63 |
64 |
65 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/test/tpl/plain.tpl:
--------------------------------------------------------------------------------
1 |
2 | abc
--------------------------------------------------------------------------------