├── components
├── gf-aaa
│ ├── gf-aaa.html
│ └── gf-aaa.js
├── common-sidebar
│ ├── common-sidebar.html
│ └── common-sidebar.js
├── common-header
│ ├── common-header.js
│ └── common-header.html
├── gf-demo-fast
│ ├── gf-demo-fast.js
│ └── gf-demo-fast.html
├── gf-dashboard
│ ├── gf-dashboard.js
│ └── gf-dashboard.html
├── doc-ms-table
│ ├── doc-ms-table.html
│ └── doc-ms-table.js
├── doc-ms-form
│ ├── doc-ms-form.js
│ └── doc-ms-form.html
├── gf-demo
│ ├── gf-demo.js
│ └── gf-demo.html
├── gf-demo-redux
│ ├── gf-demo-redux.html
│ └── gf-demo-redux.js
└── common-curd
│ └── common-curd.js
├── vendor
└── jquery
│ ├── jquery.minicolors.png
│ └── jquery.minicolors.js
├── services
├── configService.js
├── filterService.js
├── ajaxService.js
├── routerService.js
├── storeService.js
└── menuService.js
├── tsconfig.json
├── mock
├── server.conf
├── update.json
├── file
│ └── insert.js
├── loged.json
└── sample.json
├── pages
├── rxjs-demo
│ ├── rxjs-demo.html
│ └── rxjs-demo.js
└── redux-demo
│ ├── redux-demo.html
│ └── redux-demo.js
├── typings
├── index.d.ts
├── mmRouter.d.ts
└── avalon.d.ts
├── .deploy.sh
├── .travis.yml
├── LICENSE
├── index.js
├── .gitignore
├── package.json
├── README.md
├── static
├── uilog.js
├── mod.js
└── polyfill
│ ├── respond.src.js
│ └── html5shiv.js
├── index.html
└── fis-conf.js
/components/gf-aaa/gf-aaa.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{@text}}
4 |
5 |
--------------------------------------------------------------------------------
/vendor/jquery/jquery.minicolors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxapp/ms-bus/HEAD/vendor/jquery/jquery.minicolors.png
--------------------------------------------------------------------------------
/services/configService.js:
--------------------------------------------------------------------------------
1 | export const pageSize = 10;
2 |
3 | export const domain = '__DOMAIN__';
4 |
5 | export const serviceUrl = '__SERVICE_URL__';
--------------------------------------------------------------------------------
/components/common-sidebar/common-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es3",
4 | "module": "commonjs"
5 | },
6 | "typeRoots": [
7 | "typings",
8 | "../node_modules/@types"
9 | ]
10 | }
--------------------------------------------------------------------------------
/components/gf-aaa/gf-aaa.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | export const name = 'gf-aaa';
4 |
5 | avalon.component(name, {
6 | template: __inline('./gf-aaa.html'),
7 | defaults: {
8 | text: 'aaa'
9 | }
10 | });
--------------------------------------------------------------------------------
/mock/server.conf:
--------------------------------------------------------------------------------
1 | rewrite ^\/api\/demo$ /mock/sample.json
2 | rewrite ^\/api\/demo/update$ /mock/update.json
3 | rewrite ^\/api\/loged$ /mock/loged.json
4 |
5 | rewrite ^\/api\/file\/uploadFile$ /mock/file/insert.js
6 |
7 | rewrite ^/search/repositories$ /mock/github/repository.json
--------------------------------------------------------------------------------
/mock/update.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "0",
3 | "t": {
4 | "region_id": "635",
5 | "region_parent_id": "67",
6 | "region_name": "永昌县",
7 | "region_nick": null,
8 | "region_type": "2",
9 | "region_enble": 1
10 | }
11 | }
--------------------------------------------------------------------------------
/mock/file/insert.js:
--------------------------------------------------------------------------------
1 | module.exports = function(req, res, next) {
2 | res.set('Content-Type', 'text/html');
3 | // res.setStatus('404');
4 | setTimeout(function () {
5 | res.json({
6 | "url": "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
7 | });
8 | }, 500);
9 | };
--------------------------------------------------------------------------------
/components/common-header/common-header.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import ajax from '../../services/ajaxService';
3 |
4 | export const name = 'common-header';
5 |
6 | avalon.component(name, {
7 | template: __inline('./common-header.html'),
8 | defaults: {
9 | currentUserName: '',
10 | logout() {
11 | global.sessionStorage.removeItem('adminSession');
12 | global.location.href = '/login.html';
13 | }
14 | }
15 | });
--------------------------------------------------------------------------------
/components/gf-demo-fast/gf-demo-fast.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | import 'ane';
4 | import curdComponent from '../common-curd/common-curd';
5 | import { demo as demoStore } from '../../services/storeService';
6 |
7 | export const name = 'gf-demo-fast';
8 |
9 | curdComponent.extend({
10 | displayName: name,
11 | template: __inline('./gf-demo-fast.html'),
12 | defaults: {
13 | $store: demoStore,
14 | pattern: /^\d+-\d+-\d+( \d+:\d+:\d+)?$/
15 | }
16 | });
--------------------------------------------------------------------------------
/services/filterService.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | avalon.filters.showPrices = function (priceList) {
4 | let prices = '';
5 | if (!priceList) {
6 | return prices;
7 | }
8 | for (let i = 0; i < priceList.length; i++) {
9 | if (i !== 0) {
10 | prices += ',';
11 | }
12 | prices += priceList[i].discount_price + '/' + priceList[i].count_unit;
13 | }
14 | return prices;
15 | }
16 |
17 | avalon.filters.decodeHTML = function (str) {
18 | return decodeURIComponent(str);
19 | }
--------------------------------------------------------------------------------
/pages/rxjs-demo/rxjs-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | RxJS Demo
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // FIS3 inline syntax
6 | declare var __inline
7 |
8 | // runtime global
9 |
10 | interface MyWindow extends Window {
11 | Promise: Promise,
12 | $,
13 | jQuery,
14 | __REDUX_DEVTOOLS_EXTENSION__
15 | }
16 |
17 | declare var global: MyWindow
18 |
19 | declare var require
20 |
21 | declare var module: {
22 | exports: any
23 | }
24 |
25 | declare var exports: any
26 |
27 | interface JQueryStatic {
28 | ajaxFileUpload
29 | }
--------------------------------------------------------------------------------
/.deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 使用fis进行构建
4 | fis3 release gh-pages -cd ./output
5 |
6 | if [ -d output ]; then
7 | # 提交代码至gh-pages分支
8 | echo "➥ Commit files"
9 | git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/${REPO_SLUG}.git ${REPO_SLUG} > /dev/null
10 | rm -rf ${REPO_SLUG}/*
11 | cp -rf output/* ${REPO_SLUG}
12 | cd ${REPO_SLUG}
13 | git status
14 | git config user.email "travis@travis-ci.org"
15 | git config user.name "travis-ci"
16 | git add -A
17 | git commit -m "[ci skip] publish gh-pages"
18 | git push -fq origin gh-pages > /dev/null
19 | else
20 | echo '➥ Fail'
21 | exit 1
22 | fi
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6'
4 | env:
5 | global:
6 | - GH_PAGES_DEPLOY: YES
7 | - PATH: node_modules/.bin/:$PATH
8 | - REPO_SLUG: xxapp/ms-bus
9 | #- secure: dRG3aZiBO9p7oxHXJ/JLI6I6IbmFFrFs380R17upqlWZpCFnCTyrHVm+nqXJEOWn3cpwtMs2lZRffXUgA/a9nhoecsK7YTbaZBJFGn4Oz2nIupz9eq9xDq2NiJwfj3s9qUBYANaVXc9n2crX60zgmZTW0msVhl19Bl/8dYLOoMdhtUZyfBp0drYY0lGMAdD0fha16FbsKwu/eTpXXpip7sIFfNoDYNG65Yrrsy89rQShI3CuGDVuTBjJYqA+aJr04i8I2OcXK0Qf5YOmU1R6UJv/m6iRB1tJguVun4psylH6iXkHtnWipNpkKHUJ1nNv5W5Zz0ru0SD4ZF7ZksO9FCyoTlyavSD1h5XIEsirJTBByJ8MEaC4hOx9/2JwFcqL9ZZn2tgyIshCVUQpCyrJ7Rcyt8yj2PiaI0/IwOr6nLFPkspR3JL5700qAhMdTQynl/1KMdUPjQHSsVUky9AKeOCRSUBX89TyILHnismv6sb3ny2Yq8Eao3Ped2o0Xne/F8Lq8TdSR/Pxlu7QYfjQz5agaAmeO/z+HOYh5sx16viDcxZzWa9n9HLbbP1eZwdg+Xj5eYvlcHGS9mIR0J+GNXbotaR+XuWc4TJikOEwT/SzUtM9hRaHhnUwuf+Ffe3jMxFS33MRBeLjPn5onObc/QN5y0ftUTAdMrh3FwztEAg=
10 | install:
11 | - npm install fis3
12 | - fis3 -v
13 | - npm i
14 | script: sh .deploy.sh
15 | cache:
16 | directories:
17 | - node_modules
18 | branches:
19 | only:
20 | - master
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 PIEr_xx
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import 'es5-shim';
2 | import 'es6-promise/dist/es6-promise.auto';
3 | import jQuery from 'jquery';
4 | global.$ = global.jQuery = jQuery;
5 | require('bootstrap');
6 | // 提前禁止avalon对Object.create的实现
7 | if (!Object.create) {
8 | Object.create = function () {
9 | function F() {}
10 |
11 | return function (o) {
12 | F.prototype = o;
13 | return new F();
14 | };
15 | }();
16 | }
17 | var avalon = require('avalon2');
18 | if (avalon.msie < 8) {
19 | Object.defineProperty = function (obj, property, meta) {
20 | obj[property] = meta.value;
21 | }
22 | }
23 | require('es5-shim/es5-sham');
24 | require('./services/routerService');
25 | let { breadcrumb } = require('./services/storeService');
26 | require('ane/dist/layout');
27 |
28 | const root = avalon.define({
29 | $id: 'root',
30 | currentPage: '',
31 | breadcrumb: [],
32 | user: {}
33 | });
34 | breadcrumb.list$.subscribe(v => {
35 | root.breadcrumb = v;
36 | });
37 | avalon.history.start({
38 | fireAnchor: false
39 | });
40 | if (!/#!/.test(global.location.hash)) {
41 | avalon.router.navigate('/', 2);
42 | }
43 | avalon.scan(document.body);
--------------------------------------------------------------------------------
/components/gf-dashboard/gf-dashboard.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import { createForm, notification } from 'ane';
3 | import ajax from '../../services/ajaxService'
4 |
5 | export const name = 'gf-dashboard';
6 |
7 | avalon.component(name, {
8 | template: __inline('./gf-dashboard.html'),
9 | defaults: {
10 | show: false,
11 | message: '欢迎',
12 | masterpiece: ['ane', 'ms-bus'],
13 | handleCancel(e) {
14 | //console.log(e);
15 | this.show = false;
16 | },
17 | handleSelectChange(e) {
18 | console.log(e.target.value);
19 | },
20 | fetchOptions(query) {
21 | return ajax({
22 | url: 'https://randomuser.me/api/?results=5',
23 | }).then(body => {
24 | return body.data.results.map(user => ({
25 | label: `${user.name.first}${user.name.last}`,
26 | value: user.login.username
27 | }));
28 | });
29 | }
30 | }
31 | });
32 |
33 | avalon.define({
34 | $id: 'dashboard_from',
35 | title: 'Title',
36 | $form: createForm({
37 | onFieldsChange(fields) {
38 | console.log(this.record);
39 | }
40 | })
41 | });
--------------------------------------------------------------------------------
/components/common-sidebar/common-sidebar.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | import * as menuService from '../../services/menuService';
4 | import { menu as menuStore } from '../../services/storeService';
5 | import 'ane';
6 |
7 | avalon.effect('collapse', {
8 | enter(elem, done) {
9 | $(elem).slideDown(200, done);
10 | },
11 | leave(elem, done) {
12 | $(elem).slideUp(200, done);
13 | }
14 | });
15 |
16 | export const name = 'common-sidebar';
17 |
18 | avalon.component(name, {
19 | template: __inline('./common-sidebar.html'),
20 | defaults: {
21 | menu: [],
22 | selectedKeys: ['dashboard'],
23 | openKeys: [],
24 | handleMenuClick(item, key, keyPath) {
25 | avalon.history.setHash(item.uri);
26 | },
27 | handleOpenChange(openKeys) {
28 | this.openKeys = openKeys.slice(-1);
29 | },
30 | onInit(event) {
31 | menuService.menu.then((menu) => {
32 | this.menu = menu;
33 | });
34 | menuStore.selectedKeys$.subscribe(v => {
35 | this.selectedKeys = v;
36 | });
37 | menuStore.openKeys$.subscribe(v => {
38 | this.openKeys = v;
39 | });
40 | }
41 | }
42 | });
--------------------------------------------------------------------------------
/components/gf-dashboard/gf-dashboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{@message}}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
同意
16 |
不同意
17 |
18 |
19 | Ane
20 | ms-bus
21 | up-loader
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/pages/redux-demo/redux-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Redux Demo
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | title
18 | Hello
19 |
20 |
25 |
26 | Clicked: {{value}} times
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Nodejs
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 | *.pid.lock
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # nyc test coverage
20 | .nyc_output
21 |
22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
23 | .grunt
24 |
25 | # node-waf configuration
26 | .lock-wscript
27 |
28 | # Compiled binary addons (http://nodejs.org/api/addons.html)
29 | build/Release
30 |
31 | # Dependency directories
32 | node_modules
33 | jspm_packages
34 |
35 | # Optional npm cache directory
36 | .npm
37 |
38 | # Optional eslint cache
39 | .eslintcache
40 |
41 | # Optional REPL history
42 | .node_repl_history
43 |
44 | ## MacOS
45 | *.DS_Store
46 | .AppleDouble
47 | .LSOverride
48 |
49 | # Icon must end with two \r
50 | Icon
51 |
52 |
53 | # Thumbnails
54 | ._*
55 |
56 | # Files that might appear in the root of a volume
57 | .DocumentRevisions-V100
58 | .fseventsd
59 | .Spotlight-V100
60 | .TemporaryItems
61 | .Trashes
62 | .VolumeIcon.icns
63 | .com.apple.timemachine.donotpresent
64 |
65 | # Directories potentially created on remote AFP share
66 | .AppleDB
67 | .AppleDesktop
68 | Network Trash Folder
69 | Temporary Items
70 | .apdisk
71 |
72 | ## VSC
73 | .vscode/*
74 | !.vscode/settings.json
75 | !.vscode/tasks.json
76 | !.vscode/launch.json
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ms-bus",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "fis3 server start -p 8081 & fis3 release -w"
9 | },
10 | "author": "",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/xxapp/msBus.git"
14 | },
15 | "license": "ISC",
16 | "dependencies": {
17 | "@types/jquery": "^2.0.41",
18 | "@types/rx": "^4.1.1",
19 | "addeventlistener-with-dispatch": "^1.0.2",
20 | "ane": "^0.1.1",
21 | "avalon2": "^2.2.7",
22 | "bootstrap": "^3.2.0",
23 | "es5-shim": "^4.5.9",
24 | "es6-promise": "^4.1.0",
25 | "font-awesome": "^4.7.0",
26 | "jquery": "^1.12.4",
27 | "mmRouter": "^0.9.6",
28 | "moment": "^2.17.1",
29 | "redux": "^3.6.0",
30 | "redux-thunk": "^2.2.0",
31 | "rx": "^4.1.0"
32 | },
33 | "devDependencies": {
34 | "babel-plugin-add-module-exports": "^0.2.1",
35 | "babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
36 | "babel-plugin-transform-es3-property-literals": "^6.22.0",
37 | "fis-parser-babel-6.x": "^6.24.1",
38 | "fis3-hook-commonjs": "^0.1.25",
39 | "fis3-hook-node_modules": "^2.2.8",
40 | "fis3-packager-deps-pack": "^0.1.2",
41 | "fis3-postpackager-loader": "^2.1.3",
42 | "fis3-postprocessor-component-view": "^1.0.8",
43 | "fis3-preprocessor-js-require-css": "^0.1.1"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pages/redux-demo/redux-demo.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 | import avalon from 'avalon2';
3 |
4 | import jQuery from 'jquery';
5 | global.$ = global.jQuery = jQuery;
6 | import 'bootstrap/dist/css/bootstrap.css';
7 | import 'bootstrap';
8 | import { createForm } from 'ane';
9 |
10 | function counter(state = 0, action) {
11 | switch (action.type) {
12 | case 'INCREMENT':
13 | return state + 1
14 | case 'DECREMENT':
15 | return state - 1
16 | default:
17 | return state
18 | }
19 | }
20 | const store = createStore(
21 | counter,
22 | global.__REDUX_DEVTOOLS_EXTENSION__ && global.__REDUX_DEVTOOLS_EXTENSION__()
23 | )
24 | function render() {
25 | vm.value = store.getState();
26 | }
27 | store.subscribe(render)
28 |
29 | const vm = avalon.define({
30 | $id: 'demo',
31 | value: 0,
32 | title: 'hello',
33 | show: false,
34 | $form: createForm({
35 | onFieldsChange(fields) {
36 | console.log(fields);
37 | console.log(this.record);
38 | }
39 | }),
40 | increment() {
41 | store.dispatch({ type: 'INCREMENT' });
42 | },
43 | decrement() {
44 | store.dispatch({ type: 'DECREMENT' });
45 | },
46 | incrementIfOdd() {
47 | if (store.getState() % 2 !== 0) {
48 | store.dispatch({ type: 'INCREMENT' });
49 | }
50 | },
51 | incrementAsync() {
52 | setTimeout(() => {
53 | store.dispatch({ type: 'INCREMENT' });
54 | }, 1000);
55 | }
56 | });
--------------------------------------------------------------------------------
/typings/mmRouter.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | /**
4 | * avalon.history.start 配置项
5 | */
6 | interface mmHistoryOptions {
7 | /**
8 | * 根路径
9 | */
10 | root?: string,
11 | /**
12 | * 是否使用HTML 5 history
13 | */
14 | html5?: boolean,
15 | /**
16 | * hash 前缀
17 | */
18 | hashPrefix?: '!',
19 | /**
20 | * 滚动
21 | */
22 | autoScroll?: boolean,
23 | fireAnchor: boolean
24 | }
25 |
26 | interface AvalonStatic {
27 | history: {
28 | hash: string,
29 | check: () => void,
30 | start: (options: mmHistoryOptions) => void,
31 | stop: () => void,
32 | setHash: (s: string, replace?: boolean) => void,
33 | writeFrame: (s: string) => void,
34 | syncHash: () => this,
35 | getPath: () => string,
36 | onHashChanged: (hash: string, clickMode: number) => void
37 | },
38 | router: {
39 | getLastHash: () => string,
40 | setLastHash: (path: string) => void,
41 | /**
42 | * 当目标页面不匹配我们所有路由规则时, 就会执行此回调.有点像404
43 | * @param cb 回调
44 | */
45 | error: (cb?: () => void) => void,
46 | /**
47 | * 添加 一个路由规则与对象的回调, cb为rule规则中捕捉的参数
48 | * @param rule 路由规则
49 | * @param cb 匹配时的回调
50 | */
51 | add: (rule: string, cb?: (rule: object, args) => any, opts?) => void,
52 | /**
53 | * 手动触发对应的回调
54 | * @param hash 路径
55 | * @param mode 0或undefined, 不改变URL, 不产生历史实体, 执行回调; 1, 改变URL, 不产生历史实体, 执行回调; 2, 改变URL, 产生历史实体, 执行回调
56 | */
57 | navigate: (hash: string, mode: number) => string
58 | }
59 | }
--------------------------------------------------------------------------------
/mock/loged.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "0",
3 | "t": {
4 | "name": "qweqw",
5 | "functions": [
6 | {
7 | "id": 9,
8 | "name": "例子一级",
9 | "parent_id": 0,
10 | "menu_index": 12,
11 | "status": "N",
12 | "developer": null,
13 | "code": "demo1",
14 | "uri": null,
15 | "icon_url": "fa fa-book",
16 | "open_mode": "N",
17 | "dispatch_mode": "T"
18 | },
19 | {
20 | "id": 9,
21 | "name": "例子",
22 | "parent_id": 0,
23 | "menu_index": 12,
24 | "status": "N",
25 | "developer": null,
26 | "code": "demo",
27 | "uri": "/demo",
28 | "icon_url": "fa fa-home",
29 | "open_mode": "N",
30 | "dispatch_mode": "T"
31 | },
32 | {
33 | "id": 1928,
34 | "name": "商品",
35 | "parent_id": 602,
36 | "menu_index": 3,
37 | "status": "N",
38 | "developer": null,
39 | "code": "item",
40 | "uri": "/item",
41 | "icon_url": "fa fa-home",
42 | "open_mode": "C",
43 | "dispatch_mode": "T"
44 | }
45 | ],
46 | "allowedFunctions": {
47 | "all": true,
48 | "demo1": true,
49 | "demo": true,
50 | "item": true
51 | },
52 | "allowed": {
53 | "demo.query": true,
54 | "demo.add": true,
55 | "demo.edit": true,
56 | "demo.delete": true
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 非常抱歉,此项目已不再被维护。这个项目陪我度过很艰难的一段时间,有些不舍但是要说再见了。
2 |
3 | :oncoming_bus:ms-bus:oncoming_bus:
4 |
5 |
8 |
9 | 中文名:巴适
10 |
11 |
12 | ## 开始
13 |
14 | 1. 首先安装配置 node 环境, 需要 6.x 版本。
15 |
16 | 2. 全局安装构建工具 FIS3。
17 | ``` bash
18 | npm install fis3 -g
19 | ```
20 | 3. 克隆项目到本地,并安装依赖模块
21 | ``` bash
22 | git clone https://github.com/xxapp/ms-bus.git
23 |
24 | cd ms-bus
25 |
26 | npm install
27 | ```
28 | 4. 日常运行项目
29 | ``` bash
30 | npm run dev
31 | ```
32 |
33 | ## 目录结构
34 |
35 | ```
36 | - components // 将页面按功能和业务切分后的模块
37 | + common-header // 命名规范:[业务名称]-[模块名称]
38 | - gf-user // gf 业务下的 user 模块
39 | - gf-user.html // 模块的页面结构
40 | - gf-user.js // 模块的业务逻辑
41 | - gf-user.css // 模块的表现样式
42 | + mock // 模拟后端服务的数据
43 | + pages // 除 index.html 的完整 HTML 页面,用于多页面应用
44 | - services // 超脱页面的业务逻辑模块
45 | - ajaxService.js // 封装 ajax 方法,规范请求参数和响应数据的格式, 根据响应结果显示提示信息
46 | - configService.js // 应用的配置项,可在构建时动态替换配置项
47 | - filterService.js // 自定义的 Avalon2 过滤器
48 | - menuService.js // 功能菜单的逻辑,权限过滤
49 | - routerService.js // 路由配置
50 | - storeService.js // 数据服务,包括后端数据交互和全局状态管理
51 | - static // 非 commonjs 模块化的资源
52 | - mod.js
53 | - typings // 如果使用 TS 且有必要,就存在这个目录
54 | - index.d.ts
55 | + vendor // 不能通过 npm 安装的模块
56 | - index.html // 应用主页面
57 | - index.js // 应用启动,包括 polyfill/必要的依赖/root VM/路由启动
58 | ```
59 |
60 | ## 浏览器支持
61 |
62 | 现代浏览器、IE8 及以上
63 |
64 | ## 鸣谢
65 |
66 | [活儿好又性感的在线 Mock 平台 - Easy Mock](https://www.easy-mock.com/) 提供模拟数据支持
67 |
--------------------------------------------------------------------------------
/pages/rxjs-demo/rxjs-demo.js:
--------------------------------------------------------------------------------
1 | import 'es5-shim';
2 | import 'es6-promise/dist/es6-promise.auto';
3 | import 'addeventlistener-with-dispatch/src/addeventlistener-with-dispatch';
4 |
5 | import avalon from 'avalon2';
6 | if (avalon.msie === 8) {
7 | Object.defineProperty = function (obj, property, meta) {
8 | obj[property] = meta.value;
9 | }
10 | }
11 |
12 | import Rx from 'rx';
13 |
14 | avalon.define({
15 | $id: 'demo',
16 | text: 'Click Me',
17 | click(e) {
18 | }
19 | });
20 |
21 | // const button = document.getElementsByTagName('button')[0];
22 |
23 | // const source = Rx.Observable.fromEvent(button, 'click')
24 | // .map(e => (e.target as HTMLButtonElement).value)
25 | // .debounce(500);
26 |
27 | // const subscription = source.subscribeOnNext(value => console.log(`Input value: %${value}`));
28 |
29 | // const throttled = Rx.Observable.fromEvent(window, 'resize').throttle(250);
30 |
31 | // throttled.subscribeOnNext(e => {
32 | // console.log(window.innerHeight, window.innerWidth);
33 | // });
34 |
35 | // setTimeout(() => {
36 | // subscription.dispose();
37 | // }, 2000)
38 |
39 | // const source = Rx.Observable.interval(1000).bufferWithTime(3000).map(arr => arr.reduce((acc, x) => acc + x, 0));
40 |
41 | // const subscription = source.subscribe(
42 | // x => console.log(`onNext: ${x}`),
43 | // e => console.log(`onError: ${e}`),
44 | // () => console.log('onCompleted'));
45 |
46 | const codes = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
47 |
48 | function isKonamiCode(buffer) {
49 | console.log(buffer.toString());
50 | return codes.toString() === buffer.toString();
51 | }
52 |
53 | const keys = Rx.Observable.fromEvent(document, 'keyup')
54 | .map(e => e.keyCode)
55 | .bufferWithCount(10, 1)
56 | .filter(isKonamiCode)
57 | .subscribeOnNext(() => console.log('KONAMI!'));
--------------------------------------------------------------------------------
/components/doc-ms-table/doc-ms-table.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
本地分页
5 |
6 |
7 |
8 |
9 | {{record.name}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
远程数据分页
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/static/uilog.js:
--------------------------------------------------------------------------------
1 | window.myLog = function() {
2 | if (!window.myLog._div) { window.myLog.createDiv(); }
3 |
4 | var logEntry = document.createElement('span');
5 | for (var i=0; i < arguments.length; i++) {
6 | logEntry.innerHTML += window.myLog.toJson(arguments[i]) + '
';
7 | }
8 | logEntry.innerHTML += '
';
9 |
10 | window.myLog._div.appendChild(logEntry);
11 | }
12 | window.myLog.createDiv = function() {
13 | window.myLog._div = document.body.appendChild(document.createElement('div'));
14 | var props = {
15 | position:'absolute', top:'10px', right:'10px', background:'#333', border:'5px solid #333',
16 | color: 'white', width: '400px', height: '300px', overflow: 'auto', fontFamily: 'courier new',
17 | fontSize: '11px', whiteSpace: 'nowrap'
18 | }
19 | for (var key in props) { window.myLog._div.style[key] = props[key]; }
20 | }
21 | window.myLog.toJson = function(obj) {
22 | if (typeof window.uneval == 'function') { return uneval(obj); }
23 | if (typeof obj == 'object') {
24 | if (!obj) { return 'null'; }
25 | var list = [];
26 | if (obj instanceof Array) {
27 | for (var i=0;i < obj.length;i++) { list.push(this.toJson(obj[i])); }
28 | return '[' + list.join(',') + ']';
29 | } else {
30 | for (var prop in obj) { list.push('"' + prop + '":' + this.toJson(obj[prop])); }
31 | return '{' + list.join(',') + '}';
32 | }
33 | } else if (typeof obj == 'string') {
34 | return '"' + obj.replace(/(["'])/g, '\\$1') + '"';
35 | } else {
36 | return new String(obj);
37 | }
38 | }
39 |
40 | window.myLog('log statement');
41 | window.myLog('logging an object', { name: 'Marcus', likes: 'js' });
42 |
43 | (function () {
44 | var matches = window.navigator.userAgent.match(/MSIE ([^;]*)/);
45 | if (!matches) { return ; }
46 | var ieVersion = parseInt(matches[1], 10);
47 | if (ieVersion < 9) {
48 | window.console = {
49 | log: window.myLog
50 | }
51 | }
52 | })();
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Bus Admin
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 | MS-BUS SPA 脚手架
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | - {{item.title}}
36 |
37 |
40 |
41 |
42 |
43 |
44 |
48 |
49 |
--------------------------------------------------------------------------------
/mock/sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "0",
3 | "message": "获取城市列表成功!",
4 | "sessionId": null,
5 | "currentPage": 0,
6 | "pageSize": 0,
7 | "pageCount": 0,
8 | "total": 25,
9 | "rows": [
10 | {
11 | "region_id": "10",
12 | "region_parent_id": "1",
13 | "region_name": "河北",
14 | "region_type": "2",
15 | "region_enble": 1,
16 | "suites": [{
17 | "name": "Tom"
18 | }, {
19 | "name": "Jerry"
20 | }, {
21 | "name": "Jimmy"
22 | }]
23 | },
24 | {
25 | "region_id": "635",
26 | "region_parent_id": "67",
27 | "region_name": "永昌县",
28 | "region_type": "2",
29 | "region_enble": 1,
30 | "suites": [{
31 | "name": "Tom"
32 | }]
33 | },
34 | {
35 | "region_id": "636",
36 | "region_parent_id": "68",
37 | "region_name": "肃州区",
38 | "region_type": "2",
39 | "region_enble": 1,
40 | "suites": [{
41 | "name": "Tom"
42 | }]
43 | },
44 | {
45 | "region_id": "637",
46 | "region_parent_id": "68",
47 | "region_name": "玉门市",
48 | "region_type": "2",
49 | "region_enble": 1,
50 | "suites": [{
51 | "name": "Tom"
52 | }]
53 | },
54 | {
55 | "region_id": "638",
56 | "region_parent_id": "68",
57 | "region_name": "敦煌市",
58 | "region_type": "2",
59 | "region_enble": 1,
60 | "suites": [{
61 | "name": "Tom"
62 | }]
63 | },
64 | {
65 | "region_id": "639",
66 | "region_parent_id": "68",
67 | "region_name": "金塔县",
68 | "region_type": "2",
69 | "region_enble": 1,
70 | "suites": [{
71 | "name": "Tom"
72 | }]
73 | }
74 | ]
75 | }
--------------------------------------------------------------------------------
/components/doc-ms-table/doc-ms-table.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import * as bootbox from 'bootbox';
3 |
4 | import ajax from '../../services/ajaxService';
5 | import { message } from "ane";
6 |
7 | export const name = 'doc-ms-table';
8 |
9 | avalon.component(name, {
10 | template: __inline('./doc-ms-table.html'),
11 | defaults: {
12 | list: avalon.range(29).map(n => ({
13 | id: n, name: `老狼${n}`, address: '深山', province: '老林'
14 | })),
15 | remoteList: [],
16 | loading: false,
17 | pagination: {
18 | pageSize: 6, total: 0
19 | },
20 | fetch(params = {}) {
21 | this.loading = true;
22 | ajax({
23 | url: '/api/demo',
24 | method: 'get',
25 | data: {
26 | ...params
27 | }
28 | }).then(data => {
29 | this.pagination.total = data.total;
30 | data.list[0].region_parent_id = Date.now();
31 | this.remoteList = data.list;
32 | this.loading = false;
33 | });
34 | },
35 | handleTableChange(pagination) {
36 | if (this.pagination.hasOwnProperty('current')) {
37 | this.pagination.current = pagination.current;
38 | }
39 | this.fetch({
40 | start: pagination.pageSize * (pagination.current - 1),
41 | limit: pagination.pageSize
42 | });
43 | },
44 | actions(type, text, record, index) {
45 | if (type == 'delete') {
46 | this.list.removeAll(el => el.id == record.id );
47 | message.success({
48 | content: '删除成功'
49 | });
50 | }
51 | },
52 | handleSelect(record, selected, selectedRows) {
53 | console.log(record, selected, selectedRows);
54 | },
55 | handleSelectAll(selected, selectedRows) {
56 | console.log(selected, selectedRows);
57 | },
58 | handleSelectionChange(selectedRowKeys, selectedRows) {
59 | console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
60 | },
61 | handleChange(e) {
62 |
63 | },
64 | onInit(event) {
65 | this.fetch();
66 | }
67 | }
68 | });
--------------------------------------------------------------------------------
/services/ajaxService.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import jQuery from 'jquery';
3 | global.$ = global.jQuery = jQuery;
4 |
5 | import { notification } from 'ane';
6 | import { serviceUrl } from './configService';
7 |
8 | // 拦截ajax请求,检测是否超时,以重新登录
9 | $(document).ajaxComplete((event, xhr, settings) => {
10 | if (xhr.status === 200) {
11 | if (settings.dataType === 'json' && xhr.responseJSON !== void 0) {
12 | let result = xhr.responseJSON;
13 | if (result.code === '20' || result.code === '21') {
14 | if (prompt("Session已经失效,请重新登录")) {
15 | global.location.href = "/login.html";
16 | };
17 | } else if (result.error) {
18 | notification.error({
19 | message: result.error.message
20 | });
21 | }
22 | }
23 | } else if (xhr.status === undefined) {
24 | } else {
25 | notification.error({
26 | message: '请求错误,请联系管理员'
27 | });
28 | }
29 | });
30 |
31 | export default function (options) {
32 | const defaultOptions = {
33 | dataType: 'json',
34 | cache: true,
35 | jsonp: 'callback'
36 | };
37 | options.data = processRequest(options.data);
38 | options.url = /^\w+:\/\//.test(options.url) ? options.url : serviceUrl + options.url;
39 |
40 | if (serviceUrl) {
41 | defaultOptions.dataType = 'jsonp';
42 | options.data.jsonp_param_name = 'callback';
43 | }
44 |
45 | return $.ajax({ ...defaultOptions, ...options }).then(processResponse);
46 | };
47 |
48 | // 标准化传给后台的参数
49 | function processRequest(r) {
50 | const str = r || {};
51 | if (str.start || str.limit) {
52 | str.page = {
53 | start: str.start || 0,
54 | limit: str.limit || 15
55 | };
56 | delete str.start;
57 | delete str.limit;
58 | }
59 | return {
60 | params: JSON.stringify(str)
61 | };
62 | }
63 |
64 | // 标准化后台相应数据格式
65 | function processResponse(r) {
66 | let str = {};
67 | if (r.rows) {
68 | str = r;
69 | str.code = '0';
70 | str.list = r.rows;
71 | delete str.rows;
72 | } else {
73 | if (!r.error) {
74 | str.code = '0';
75 | str.data = r;
76 | } else {
77 | str.code = '1';
78 | str.message = r.message || r.error;
79 | }
80 | }
81 | return str;
82 | }
--------------------------------------------------------------------------------
/services/routerService.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import 'mmRouter';
3 | import { menu as menuStore } from './storeService';
4 |
5 | function getPage(component) {
6 | const html = ``;
7 | return html
8 | }
9 |
10 | function applyRouteConfig(config, parentRoute, accPath = '') {
11 | config.map(function (route) {
12 | let components = {};
13 | if (route.component) {
14 | components.currentPage = route.component;
15 | }
16 | if (route.components) {
17 | components = route.components;
18 | }
19 | avalon.router.add(accPath + route.path, function () {
20 | Object.keys(components).map(viewName => {
21 | let component = components[viewName];
22 | if (typeof component === 'function') {
23 | component(function (m) {
24 | menuStore.selectedKeys$.onNext([m.name]);
25 | avalon.vmodels[parentRoute.name][viewName] = getPage(m.name);
26 | });
27 | } else {
28 | avalon.vmodels[parentRoute.name][viewName] = getPage(component.name);
29 | }
30 | });
31 | });
32 | // TODO 支持嵌套路由
33 | //route.children && applyRouteConfig(route.children, route, accPath + route.path);
34 | });
35 | }
36 |
37 | require('/components/common-header');
38 | require('/components/common-sidebar');
39 |
40 | const routeConfig = [{
41 | path: '/',
42 | component(resolve) {
43 | require.async('/components/gf-dashboard', resolve);
44 | }
45 | }, {
46 | path: '/aaa',
47 | component(resolve) {
48 | require.async('/components/gf-aaa', resolve);
49 | }
50 | }, {
51 | path: '/demo',
52 | component(resolve) {
53 | require.async('/components/gf-demo', resolve);
54 | }
55 | }, {
56 | path: '/demo-redux',
57 | component(resolve) {
58 | require.async('/components/gf-demo-redux', resolve);
59 | }
60 | }, {
61 | path: '/demo-fast',
62 | component(resolve) {
63 | require.async('/components/gf-demo-fast', resolve);
64 | }
65 | }, {
66 | path: '/doc-ms-table',
67 | component(resolve) {
68 | require.async('/components/doc-ms-table', resolve);
69 | }
70 | }, {
71 | path: '/doc-ms-form',
72 | component(resolve) {
73 | require.async('/components/doc-ms-form', resolve);
74 | }
75 | }];
76 |
77 | applyRouteConfig(routeConfig, {
78 | name: 'root'
79 | });
--------------------------------------------------------------------------------
/components/doc-ms-form/doc-ms-form.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | import ajax from '../../services/ajaxService';
4 | import { serviceUrl } from "../../services/configService";
5 | import { createForm, message } from "ane";
6 |
7 | export const name = 'doc-ms-form';
8 |
9 | avalon.component(name, {
10 | template: __inline('./doc-ms-form.html'),
11 | defaults: {
12 | $form: createForm({
13 | record: initialData()
14 | }),
15 | record: initialData(),
16 | json: '',
17 | expire: 0,
18 | regionData: [
19 | {key: 1, title: "重庆", children: []},
20 | {key: 2, title: "四川", children: [
21 | {key: 4, title: '成都', children: []},
22 | {key: 5, title: '绵阳', children: []},
23 | {key: 6, title: '攀枝花', children: []}
24 | ]},
25 | {key: 3, title: "浙江", children: [
26 | {key: 7, title: '杭州', children: []},
27 | {key: 8, title: '温州', children: []}
28 | ]}
29 | ],
30 | fileUploadUrl: serviceUrl + '/api/file/uploadFile',
31 | addEducation() {
32 | this.record.education.push('');
33 | },
34 | removeEducation(school) {
35 | this.record.education.remove(school);
36 | },
37 | handleBeforeUpload(file) {
38 | if (file.type !== 'image/jpeg' && file.type !== 'image/png') {
39 | message.error({
40 | content: '只能选择jpg或者png类型的图片!'
41 | });
42 | return false;
43 | }
44 | if (file.size / 1024 / 1024 > 1) {
45 | message.error({
46 | content: '选择的图片必须小于1MB!'
47 | });
48 | return false;
49 | }
50 | return true;
51 | },
52 | handleChange(e) {
53 | console.log(e.target.value);
54 | },
55 | submit() {
56 | // if (!avalon.vmodels['doc_form'].validate()) {
57 | // return false;
58 | // }
59 | this.$form.validateFields();
60 | },
61 | onInit(event) {
62 | this.$form.onFieldsChange = (fields, record) => {
63 | this.json = JSON.stringify(record, null, 2);
64 | }
65 | this.$watch('expire', v => {
66 | console.log(v);
67 | })
68 | }
69 | }
70 | });
71 |
72 | function initialData() {
73 | return {
74 | name: '123',
75 | gender: 'F',
76 | masterpiece: ['ms-bus'],
77 | location: 4,
78 | birthday: '2017/03/25',
79 | bankai: '2017-10-10 12:12:12',
80 | hobby: ['code'],
81 | avatar: '',
82 | education: ['常乐男子职业技术学院'],
83 | bio: '',
84 | attachment: ['https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png']
85 | };
86 | }
--------------------------------------------------------------------------------
/components/common-header/common-header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/services/storeService.js:
--------------------------------------------------------------------------------
1 | import ajax from './ajaxService';
2 | import { getKeyPath } from './menuService';
3 |
4 | export const demo = {
5 | key: 'region_id',
6 | initialData: function () {
7 | return {
8 | region_enable: 0,
9 | region_id: '',
10 | region_name: '',
11 | region_parent_id: '',
12 | region_type: '',
13 | suites: [{
14 | name: ''
15 | }]
16 | };
17 | },
18 | fetch: function (params) {
19 | return ajax({
20 | url: '/api/demo',
21 | type: 'get',
22 | data: params
23 | });
24 | },
25 | create: function (params) {
26 | return ajax({
27 | url: '/api/demo/update',
28 | type: 'post',
29 | data: params
30 | });
31 | },
32 | update: function (params) {
33 | return ajax({
34 | url: '/api/demo/update',
35 | type: 'post',
36 | data: params
37 | });
38 | },
39 | remove: function (id) {
40 | return ajax({
41 | url: '/api/demo/update',
42 | type: 'post',
43 | data: {
44 | id: id
45 | }
46 | });
47 | }
48 | };
49 |
50 | export const file = {
51 | insert: function (params) {
52 | $.ajaxFileUpload({
53 | url: '/api/file/uploadFile',
54 | secureuri: false,
55 | fileElementId: params.fileElementId,
56 | dataType: 'json',
57 | success: params.success
58 | });
59 | }
60 | };
61 |
62 | export const github = {
63 | limit: 30,
64 | repository: function (params) {
65 | return ajax({
66 | url: "/search/repositories",
67 | type: 'get',
68 | data: params
69 | });
70 | },
71 | processRequest: function (params) {
72 | return {
73 | q: params.query,
74 | start: (params.page - 1) * this.limit,
75 | limit: this.limit
76 | };
77 | },
78 | processResponse: function (data) {
79 | data = data.data;
80 | data.rows = data.items;
81 | data.total = data.total_count;
82 | return data;
83 | }
84 | };
85 |
86 | export const menu = {
87 | selectedKeys$: Observable(),
88 | openKeys$: Observable()
89 | };
90 | menu.selectedKeys$.subscribe(v => {
91 | v[0] && getKeyPath(v[0]).then(result => {
92 | menu.openKeys$.onNext(result.map(item => item.key));
93 | breadcrumb.list$.onNext(result.reverse());
94 | });
95 | });
96 |
97 | export const breadcrumb = {
98 | list$: Observable()
99 | };
100 |
101 | function Observable() {
102 | return {
103 | onNextCbList: [],
104 | subscribe(onNext) {
105 | this.onNextCbList.push(onNext);
106 | },
107 | onNext(value) {
108 | this.onNextCbList.forEach(cb => {
109 | if (typeof cb === 'function') {
110 | cb(value);
111 | }
112 | });
113 | }
114 | };
115 | }
--------------------------------------------------------------------------------
/components/gf-demo/gf-demo.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | import { createForm, message } from "ane";
4 |
5 | import { demo as demoStore } from '../../services/storeService';
6 |
7 | export const name = 'gf-demo';
8 |
9 | avalon.component(name, {
10 | template: __inline('./gf-demo.html'),
11 | defaults: {
12 | show: false,
13 | list: [],
14 | $searchForm: createForm({ autoAsyncChange: false }),
15 | pagination: {
16 | current: 1, pageSize: 6, total: 0
17 | },
18 | pattern: /^\d+-\d+-\d+( \d+:\d+:\d+)?$/,
19 | search() {
20 | this.$searchForm.validateFields().then(isAllValid => {
21 | if (isAllValid) {
22 | this.fetch();
23 | }
24 | });
25 | },
26 | fetch() {
27 | const page = {
28 | start: this.pagination.pageSize * (this.pagination.current - 1),
29 | limit: this.pagination.pageSize
30 | };
31 | demoStore.fetch({...this.$searchForm.record, ...page}).then(data => {
32 | this.pagination.total = data.total;
33 | this.list = data.list;
34 | });
35 | },
36 | actions(type, text, record, index) {
37 | if (type === 'add') {
38 | form.isEdit = false;
39 | form.title = '新增';
40 | form.record = demoStore.initialData();
41 | this.show = true;
42 | } else if (type === 'edit') {
43 | form.isEdit = true;
44 | form.title = '修改';
45 | form.record = record;
46 | this.show = true;
47 | } else if (type === 'delete') {
48 | demoStore.remove(record.region_id).then(result => {
49 | if (result.code === '0') {
50 | message.success({
51 | content: '删除成功'
52 | });
53 | }
54 | });
55 | }
56 | },
57 | handleOk() {
58 | form.$form.validateFields().then(isAllValid => {
59 | if (isAllValid) {
60 | if (form.isEdit) {
61 | demoStore.update(form.$form.record).then(result => {
62 | this.fetch();
63 | });
64 | } else {
65 | demoStore.create(form.$form.record).then(result => {
66 | this.fetch();
67 | });
68 | }
69 | this.show = false;
70 | }
71 | })
72 | },
73 | handleTableChange(pagination) {
74 | this.pagination.current = pagination.current;
75 | this.fetch();
76 | },
77 | onInit(event) {
78 | this.fetch();
79 | }
80 | }
81 | });
82 | var form = avalon.define({
83 | $id: 'demo_form',
84 | title: '',
85 | isEdit: false,
86 | $form: createForm({
87 | record: demoStore.initialData(),
88 | onFieldsChange(fields, record) {
89 | //avalon.mix(form.record, record);
90 | }
91 | }),
92 | record: demoStore.initialData()
93 | });
--------------------------------------------------------------------------------
/typings/avalon.d.ts:
--------------------------------------------------------------------------------
1 | interface avalonComponentRecycleFn {
2 | (event: {
3 | target: Element,
4 | type: string,
5 | vmodel
6 | });
7 | }
8 |
9 | interface avalonComponent {
10 | (name: string, component: {
11 | template: string,
12 | defaults: {
13 | onInit: avalonComponentRecycleFn,
14 | onReady: avalonComponentRecycleFn,
15 | onViewChange: avalonComponentRecycleFn,
16 | onDispose: avalonComponentRecycleFn
17 | }
18 | }): any
19 | }
20 |
21 | interface avalonInstance {
22 | /**
23 | * 用于获取或修改样式,自动修正厂商前缀及加px,与jQuery的css方法一样智能
24 | */
25 | css(name: string, value: string | number): number | avalonInstance,
26 | /**
27 | * 取得目标的高,不带单位,如果目标为window,则取得窗口的高,为document取得页面的高
28 | */
29 | height(value?: string | number) : number,
30 | /**
31 | * 取得元素的位置, 如 {top:111, left: 222}
32 | */
33 | offset(): { top: number, left: number }
34 | }
35 |
36 | interface AvalonStatic {
37 | /**
38 | * 定义ViewModel,需要指定$id
39 | */
40 | define(definition): any;
41 | /**
42 | * 定义avalon组件
43 | */
44 | component(name: string, component): any;
45 | /**
46 | * 扫描元素,与ViewModel绑定
47 | */
48 | scan(node: Element|string, vm?, beforeReady?: () => void): any;
49 | /**
50 | * 定义指令
51 | */
52 | directive(name: string, options): any;
53 | /**
54 | * 指令集合
55 | */
56 | directives: any[];
57 | /**
58 | * avalon动画
59 | */
60 | effect(name: string, opts?: any): any;
61 | /**
62 | * 判断一个元素是否包含另一个元素
63 | */
64 | contains(root: Element, el: Element): boolean
65 | /**
66 | * 给元素绑定事件
67 | */
68 | bind(elem: Node, type: string, fn: (e) => boolean | void)
69 | /**
70 | * 移除一个元素的事件
71 | */
72 | unbind(elem: Node, type?: string, fn?: (e) => boolean | void)
73 | root: HTMLElement;
74 | /**
75 | * ViewModel 列表
76 | */
77 | vmodels: any;
78 | /**
79 | * 过滤器列表
80 | */
81 | filters: any
82 | /**
83 | * 用于合并多个对象或深克隆,类似于jQuery.extend
84 | */
85 | mix(target: any, object1?: any, ...objectN: any[]): any;
86 | mix(deep: boolean, target: any, object1?: any, ...objectN: any[]): any,
87 | /**
88 | * no operation
89 | */
90 | noop(): void,
91 | /**
92 | * virtual dom
93 | */
94 | vdom(vnode: any, method: string): void
95 | /**
96 | * 生成指定长度数组
97 | */
98 | range(start: number, end?: number, step?: number): number[]
99 | /**
100 | * IE版本
101 | */
102 | msie: number,
103 | /**
104 | * 打印日志
105 | */
106 | log(message?: any, ...optionalParams: any[]): void;
107 | /**
108 | * 打印错误信息
109 | */
110 | error(message?: any, ...optionalParams: any[]): void;
111 | /**
112 | * 注册文档加载完成事件
113 | */
114 | ready(fn): void;
115 | /**
116 | * 将字符串安全格式化为正则表达式的源码
117 | */
118 | escapeRegExp(target): string;
119 | /**
120 | * 构造avalon实例
121 | */
122 | (el: Node): avalonInstance
123 | }
124 |
125 | declare module 'avalon2' {
126 | export = avalon
127 | }
128 |
129 | declare var avalon: AvalonStatic
--------------------------------------------------------------------------------
/services/menuService.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import ajax from './ajaxService';
3 |
4 | const menu = [{
5 | key: 'gf-dashboard',
6 | title: '主页',
7 | icon: 'fa fa-home',
8 | uri: '/'
9 | }, {
10 | key: 'demo1',
11 | title: '例子一级',
12 | icon: 'fa fa-home',
13 | children: [{
14 | key: 'gf-demo',
15 | title: '例子',
16 | icon: 'fa fa-home',
17 | uri: '/demo'
18 | }, {
19 | key: 'gf-demo-redux',
20 | title: 'redux例子',
21 | icon: 'fa fa-home',
22 | uri: '/demo-redux'
23 | }, {
24 | key: 'gf-demo-fast',
25 | title: '快速CURD例子',
26 | icon: 'fa fa-home',
27 | uri: '/demo-fast'
28 | }]
29 | }, {
30 | key: 'doc-ms',
31 | title: '组件文档',
32 | icon: 'fa fa-book',
33 | children: [{
34 | key: 'doc-ms-table',
35 | title: 'Table',
36 | uri: '/doc-ms-table'
37 | }, {
38 | key: 'doc-ms-form',
39 | title: 'Form',
40 | uri: '/doc-ms-form'
41 | }]
42 | }, {
43 | key: 'rxjs-demo-page',
44 | title: 'RxJS Demo Page',
45 | icon: 'fa fa-circle',
46 | uri: '/pages/rxjs-demo/rxjs-demo.html',
47 | target: '_blank'
48 | }];
49 |
50 | // 根据权限过滤菜单
51 | const menuPromise = new Promise((rs, rj) => {
52 | ajax({
53 | url: '/api/loged',
54 | type: 'get'
55 | }).then((result) => {
56 | if (result.code === '0') {
57 | $('#loadImg').css('display', 'none');
58 | $('.login-area').removeClass('hidden').addClass('animated flipInX');
59 | travelMenu(menu, result.data.t.functions, result.data.t.allowedFunctions);
60 | avalon.mix(avalon.vmodels.root, { user: result.data.t });
61 | rs(menu.slice(0));
62 | } else {
63 | rj(result);
64 | }
65 | });
66 | });
67 |
68 | function travelMenu(menulet, functions, allowedFunctions) {
69 | if (!menulet) {
70 | return ;
71 | }
72 | for (let i = 0, item; item = menulet[i++]; ) {
73 | let hasPermission = false;
74 | for (let j = 0, func; func = functions[j++]; ) {
75 | if (func.code === item.name && (allowedFunctions[func.code])) {
76 | item.uri = func.uri || item.uri || 'javascript:;';
77 | item.icon = func.icon_url || item.icon;
78 | item.target = item.target || '_self';
79 | item.children = item.children || [];
80 | item.opened = false;
81 | hasPermission = true;
82 | break;
83 | }
84 | if (allowedFunctions['all']) {
85 | hasPermission = true;
86 | }
87 | }
88 | item.show = hasPermission === true;
89 |
90 | travelMenu(item.children, functions, allowedFunctions);
91 | }
92 | }
93 |
94 | function walkMenu(menu, key, process, level = 1) {
95 | let finded = false;
96 | for (let i = 0; i < menu.length; i++) {
97 | const item = menu[i];
98 | process(item);
99 | if (item.key === key) {
100 | finded = true;
101 | break;
102 | }
103 | if (item.children && walkMenu(item.children, key, process, level + 1)) {
104 | finded = true;
105 | break;
106 | }
107 | process('', true);
108 | };
109 | return finded;
110 | }
111 | function getKeyPath(key) {
112 | return menuPromise.then((menu) => {
113 | const keyPath = [];
114 |
115 | walkMenu(menu.toJSON ? menu.toJSON() : menu, key, function (item, shift) {
116 | if (shift) {
117 | keyPath.shift();
118 | } else {
119 | keyPath.unshift(item);
120 | }
121 | })
122 |
123 | return keyPath;
124 | });
125 | }
126 | export {
127 | getKeyPath,
128 | menuPromise as menu
129 | }
--------------------------------------------------------------------------------
/fis-conf.js:
--------------------------------------------------------------------------------
1 | // npm install [-g] fis3-hook-commonjs
2 | fis.hook('commonjs', {
3 | paths: {
4 | 'moment-locale': './node_modules/moment/locale/zh-cn.js',
5 | redux: './node_modules/redux/dist/redux.js',
6 | bootstrap: './node_modules/bootstrap/dist/js/bootstrap.js'
7 | },
8 | extList: ['.js']
9 | });
10 |
11 | fis.set('project.ignore', ['node_modules/**', 'output/**', '.git/**', 'fis-conf.js',
12 | 'README.md', 'readme.txt', 'cmd.cmd', 'package.json', 'LICENSE']);
13 | fis.set('baseurl', '');
14 |
15 | fis.unhook('components');
16 | fis.hook('node_modules', {
17 | ignoreDevDependencies: true,
18 | shimBuffer: false,
19 | shimProcess: false,
20 | shutup: true
21 | });
22 |
23 | // 默认情况下不添加hash
24 | ['/{pages,components,services,vendor}/**.{ts,js}', '/*.{ts,js}', '/node_modules/babel-runtime/**.{ts,js}'].forEach(function (blob) {
25 | fis.match(blob, {
26 | preprocessor: fis.plugin('js-require-css'),
27 | parser: fis.plugin('babel-6.x', {
28 | plugins: ['transform-es3-property-literals', 'transform-es3-member-expression-literals', 'add-module-exports']
29 | }),
30 | rExt: '.js'
31 | });
32 | });
33 | fis.match('**', {
34 | useHash: false,
35 | release: false
36 | });
37 | fis.match('**.js.map', {
38 | release: '/static/$0'
39 | });
40 | fis.match('/*.html', {
41 | release: '/$0'
42 | });
43 | fis.match('/pages/**/*.html', {
44 | release: '/$0'
45 | });
46 | fis.match('/*.{ts,js}', {
47 | isMod: true,
48 | release: '/static/$0'
49 | });
50 | fis.match('/pages/**/*.{ts,js}', {
51 | isMod: true,
52 | release: '/static/$0'
53 | });
54 | fis.match('/{node_modules,components}/**/*.{ts,js}', {
55 | isMod: true, // 设置 comp 下都是一些组件,组件建议都是匿名方式 define
56 | release: '/static/$0'
57 | });
58 | fis.match('/components/**/*.html', {
59 | postprocessor: fis.plugin('component-view', { }),
60 | release: false
61 | });
62 | fis.match('/{node_modules,components}/**/*.{css,eot,png,gif,svg,ttf,woff,woff2,map}', {
63 | release: '/static/$0'
64 | });
65 | fis.match('/services/*.{ts,js}', {
66 | isMod: true,
67 | release: '/static/$0'
68 | });
69 | fis.media('dev').match('/services/configService.{ts,js}', {
70 | postprocessor: function (content, file, settings) {
71 | return content
72 | .replace('__DOMAIN__', '')
73 | .replace('__SERVICE_URL__', '');
74 | }
75 | });
76 | fis.match('/vendor/**/*.{ts,js}', {
77 | isMod: true,
78 | release: '/static/$0'
79 | });
80 | fis.match('/static/**', {
81 | release: '/static/$0'
82 | });
83 | // mock假数据
84 | fis.media('dev').match('/mock/**', {
85 | release: '/$0'
86 | });
87 |
88 | fis.match('::package', {
89 | // npm install [-g] fis3-postpackager-loader
90 | // 分析 __RESOURCE_MAP__ 结构,来解决资源加载问题
91 | postpackager: fis.plugin('loader', {
92 | resourceType: 'commonJs',
93 | useInlineMap: true,
94 | obtainStyle: false
95 | })
96 | })
97 |
98 | fis.media('gh-pages')
99 | .match('**', {
100 | domain: '/ms-bus'
101 | })
102 | .match('/services/configService.{ts,js}', {
103 | postprocessor: function (content, file, settings) {
104 | return content
105 | .replace('__DOMAIN__', '/ms-bus')
106 | .replace('__SERVICE_URL__', 'https://www.easy-mock.com/mock/58ff1b7c5e43ae5dbea5eff3');
107 | }
108 | })
109 | .match('*.{js,ts}', {
110 | optimizer: fis.plugin('uglify-js')
111 | })
112 | .match('*.{css}', {
113 | optimizer: fis.plugin('clean-css')
114 | })
115 | .match('app.js', {
116 | release: '/$0'
117 | })
118 | .match('vendor.js', {
119 | release: '/$0'
120 | })
121 | .match('app.css', {
122 | release: '/$0'
123 | })
124 | .match('::package', {
125 | packager: fis.plugin('deps-pack', {
126 | useTrack: false,
127 | 'app.js': [
128 | 'index.js',
129 | 'index.js:deps',
130 | '!node_modules/**',
131 | '!node_modules/**:deps'
132 | ],
133 | 'vendor.js': [
134 | 'index.js',
135 | 'index.js:deps'
136 | ],
137 | 'app.css': [
138 | 'index.js:deps'
139 | ]
140 | }),
141 | });
142 | // .match('/mock/**', {
143 | // release: '/$0'
144 | // });
--------------------------------------------------------------------------------
/components/gf-demo-fast/gf-demo-fast.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
50 |
51 |
52 |
53 |
56 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/components/gf-demo/gf-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
50 |
51 |
52 |
53 |
56 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/components/gf-demo-redux/gf-demo-redux.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
52 |
53 |
54 |
55 |
58 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/components/common-curd/common-curd.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 |
3 | import { createForm, notification, message } from "ane";
4 |
5 | export default avalon.component('common-curd', {
6 | template: ' ',
7 | defaults: {
8 | show: false,
9 | loading: false,
10 | list: [],
11 | $searchForm: createForm({ autoAsyncChange: false }),
12 | pagination: {
13 | current: 1, pageSize: 6, total: 0
14 | },
15 | $dialogs: {
16 | main: null
17 | },
18 | search() {
19 | this.$searchForm.validateFields().then(isAllValid => {
20 | if (isAllValid) {
21 | this.fetch();
22 | }
23 | });
24 | },
25 | fetch() {
26 | const page = {
27 | start: this.pagination.pageSize * (this.pagination.current - 1),
28 | limit: this.pagination.pageSize
29 | };
30 | this.loading = true;
31 | this.$store.fetch({...this.$searchForm.record, ...page}).then(data => {
32 | this.pagination.total = data.total;
33 | this.list = data.list;
34 | this.loading = false;
35 | }, () => {
36 | this.loading = false;
37 | });
38 | },
39 | handleTableChange(pagination) {
40 | this.pagination.current = pagination.current;
41 | this.fetch();
42 | },
43 | handle: {},
44 | _handle: {
45 | add(text, record, index) {
46 | this.$dialogs.main.beginCreate(this.$store.initialData());
47 | this.show = true;
48 | },
49 | edit(text, record, index) {
50 | this.$dialogs.main.beginUpdate(record);
51 | this.show = true;
52 | },
53 | del(text, record, index) {
54 | this.$store.remove(record.region_id).then(result => {
55 | if (result.code === '0') {
56 | message.success({
57 | content: '删除成功'
58 | });
59 | }
60 | });
61 | },
62 | },
63 | actions(type, ...args) {
64 | this.handle[type] && this.handle[type].apply(this, args);
65 | },
66 | handleOk() {
67 | this.$dialogs.main.submit().then(([isEdit, record]) => {
68 | if (typeof isEdit === 'boolean') {
69 | this.show = false;
70 | if (isEdit) {
71 | return this.$store.update(record);
72 | } else {
73 | return this.$store.create(record);
74 | }
75 | }
76 | }).then(result => {
77 | if (result !== undefined && result.code === '0') {
78 | this.fetch();
79 | }
80 | }).catch(err => {
81 | avalon.log(err);
82 | });
83 | },
84 | _initMainDialog() {
85 | if (this.$dialogs.main === null) {
86 | this.$dialogs.main = avalon.define({
87 | $id: this.$id + '_dialog_main',
88 | title: '新增',
89 | isEdit: false,
90 | $form: createForm({
91 | record: this.$store.initialData()
92 | }),
93 | record: this.$store.initialData(),
94 | beginCreate(record) {
95 | this.isEdit = false;
96 | this.title = '新增';
97 | this.record = record;
98 | },
99 | beginUpdate(record) {
100 | this.isEdit = true;
101 | this.title = '修改';
102 | this.record = record;
103 | },
104 | submit() {
105 | return this.$form.validateFields().then(isAllValid => {
106 | if (isAllValid) {
107 | return [this.isEdit, this.$form.record];
108 | } else {
109 | return Promise.reject('表单验证未通过');
110 | }
111 | })
112 | }
113 | });
114 | }
115 | },
116 | _disposeDialogs() {
117 | Object.keys(this.$dialogs).map(name => {
118 | let dialog = this.$dialogs[name];
119 | if (dialog) {
120 | delete this.$dialogs[name];
121 | delete avalon.vmodels[dialog.$id];
122 | }
123 | });
124 | },
125 | onInit(event) {
126 | this.fetch();
127 | this._initMainDialog();
128 | this.handle = avalon.mix(this._handle, this.handle);
129 | },
130 | onDispose(event) {
131 | this._disposeDialogs();
132 | }
133 | }
134 | });
--------------------------------------------------------------------------------
/components/doc-ms-form/doc-ms-form.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
32 |
33 |
34 |
35 |
42 |
43 |
44 |
52 | Ane
53 | ms-bus
54 | up-loader
55 |
56 |
57 |
58 |
67 |
68 |
69 |
77 |
78 |
79 |
80 | 选择图片
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | 选择文件
89 |
90 |
91 |
92 |
{{@json}}
93 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/components/gf-demo-redux/gf-demo-redux.js:
--------------------------------------------------------------------------------
1 | import avalon from 'avalon2';
2 | import { createStore, combineReducers, applyMiddleware } from 'redux';
3 | import thunk from 'redux-thunk';
4 |
5 | import { createForm, message } from "ane";
6 |
7 | import { demo as demoStore } from '../../services/storeService';
8 |
9 | export const name = 'gf-demo-redux';
10 |
11 | function fetch(params) {
12 | return (dispatch, getState) => {
13 | const { page, pageSize, search } = getState().region;
14 | demoStore.fetch({
15 | ...search,
16 | start: pageSize * (params.page - 1),
17 | limit: pageSize
18 | }).then(data => {
19 | dispatch({
20 | type: 'region/fetch',
21 | payload: {
22 | list: data.list,
23 | total: data.total,
24 | page: params.page || page
25 | }
26 | });
27 | });
28 | }
29 | }
30 | function insert(params?) {
31 | return (dispatch, getState) => {
32 | const { page, record } = getState().region;
33 | demoStore.create(record).then(data => {
34 | dispatch({ type: 'region/closeDialog' });
35 | dispatch(fetch({ page }));
36 | });
37 | }
38 | }
39 | function update(params?) {
40 | return (dispatch, getState) => {
41 | const { page, record } = getState().region;
42 | demoStore.update(record).then(data => {
43 | dispatch(fetch({ page }));
44 | });
45 | }
46 | }
47 | function del(params) {
48 | return (dispatch, getState) => {
49 | const { page } = getState().region;
50 | demoStore.remove(params).then(result => {
51 | if (result.code === '0') {
52 | message.success({
53 | content: '删除成功'
54 | });
55 | }
56 | dispatch(fetch({ page }));
57 | });
58 | }
59 | }
60 |
61 | interface RegionEntity {
62 | region_id: string,
63 | region_name: string,
64 | region_parent_id: string,
65 | suites: {
66 | name: string
67 | }[]
68 | }
69 |
70 | interface Region {
71 | show: boolean,
72 | isEdit: boolean,
73 | list: RegionEntity[],
74 | total: number,
75 | page: number,
76 | pageSize: number,
77 | search: {
78 | region_id: string,
79 | region_name: {
80 | firstName: string
81 | },
82 | startDate: string,
83 | endDate: string
84 | },
85 | record: RegionEntity
86 | }
87 | interface All {
88 | region: Region
89 | }
90 |
91 | const region = function regionReducer(state: Region, action): Region {
92 | if (state === undefined) {
93 | state = {
94 | show: false,
95 | isEdit: false,
96 | list: [],
97 | total: 0,
98 | page: 1,
99 | pageSize: 6,
100 | search: null,
101 | record: null
102 | };
103 | }
104 | switch (action.type) {
105 | case 'region/closeDialog':
106 | return {
107 | ...state,
108 | show: false
109 | };
110 | case 'region/fetch':
111 | return {
112 | ...state,
113 | ...action.payload
114 | };
115 | case 'region/readyForAdd':
116 | return {
117 | ...state,
118 | isEdit: false,
119 | show: true
120 | };
121 | case 'region/readyForEdit':
122 | return {
123 | ...state,
124 | isEdit: true,
125 | show: true
126 | };
127 | case 'region/changeSearch':
128 | return {
129 | ...state,
130 | search: action.payload
131 | }
132 | case 'region/changeRecord':
133 | return {
134 | ...state,
135 | record: action.payload
136 | }
137 | default:
138 | return state;
139 | }
140 | }
141 | const store = createStore(
142 | combineReducers({
143 | region
144 | }),
145 | global.__REDUX_DEVTOOLS_EXTENSION__ && global.__REDUX_DEVTOOLS_EXTENSION__(),
146 | applyMiddleware(thunk)
147 | );
148 |
149 | avalon.component(name, {
150 | template: __inline('./gf-demo-redux.html'),
151 | defaults: {
152 | show: false,
153 | isEdit: false,
154 | list: [],
155 | $searchForm: createForm({
156 | onFieldsChange(fields, record) {
157 | store.dispatch({ type: 'region/changeSearch', payload: record });
158 | }
159 | }),
160 | pagination: {
161 | current: 1, total: 0, pageSize: 6
162 | },
163 | pattern: /^\d+-\d+-\d+( \d+:\d+:\d+)?$/,
164 | search() {
165 | this.fetch();
166 | },
167 | fetch(params = {}) {
168 | store.dispatch(fetch(params));
169 | },
170 | actions(type, text, record, index) {
171 | if (type === 'add') {
172 | form.title = '新增';
173 | form.record = demoStore.initialData();
174 | store.dispatch({ type: 'region/readyForAdd' });
175 | } else if (type === 'edit') {
176 | form.title = '修改';
177 | form.record = record;
178 | store.dispatch({ type: 'region/readyForEdit' });
179 | } else if (type === 'delete') {
180 | store.dispatch(del(record.region_id));
181 | }
182 | },
183 | handleOk() {
184 | form.$form.validateFields().then(isAllValid => {
185 | if (isAllValid) {
186 | if (this.isEdit) {
187 | store.dispatch(update());
188 | } else {
189 | store.dispatch(insert());
190 | }
191 | store.dispatch({ type: 'region/closeDialog' });
192 | }
193 | })
194 | },
195 | handleCancel() {
196 | store.dispatch({ type: 'region/closeDialog' });
197 | },
198 | handleTableChange(pagination) {
199 | this.fetch({ page: pagination.current });
200 | },
201 | mapStateToVm() {
202 | const {
203 | show, isEdit, list, total, page, pageSize, search, record
204 | } = store.getState().region;
205 | this.list = list;
206 | this.pagination.total = total;
207 | this.pagination.current = page;
208 | this.pagination.pageSize = pageSize;
209 | this.isEdit = isEdit;
210 | this.show = show;
211 | },
212 | onInit(event) {
213 | this.mapStateToVm();
214 | store.subscribe(() => {
215 | this.mapStateToVm();
216 | });
217 | this.fetch();
218 | },
219 | onReady(event) {
220 | }
221 | }
222 | });
223 | const form = avalon.define({
224 | $id: 'demo_redux_form',
225 | title: '',
226 | $form: createForm({
227 | record: demoStore.initialData(),
228 | onFieldsChange(fields, record) {
229 | //avalon.mix(form.record, record);
230 | store.dispatch({ type: 'region/changeRecord', payload: record });
231 | }
232 | }),
233 | record: demoStore.initialData()
234 | });
--------------------------------------------------------------------------------
/static/mod.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file: mod.js
3 | * @author fis
4 | * ver: 1.0.13
5 | * update: 2016/01/27
6 | * https://github.com/fex-team/mod
7 | */
8 | var require;
9 |
10 | /* eslint-disable no-unused-vars */
11 | var define;
12 |
13 | (function (global) {
14 |
15 | // 避免重复加载而导致已定义模块丢失
16 | if (require) {
17 | return;
18 | }
19 |
20 | var head = document.getElementsByTagName('head')[0];
21 | var loadingMap = {};
22 | var factoryMap = {};
23 | var modulesMap = {};
24 | var scriptsMap = {};
25 | var resMap = {};
26 | var pkgMap = {};
27 |
28 | var createScripts = function(queues, onerror){
29 |
30 | var docFrag = document.createDocumentFragment();
31 |
32 | for(var i = 0, len = queues.length; i < len; i++){
33 | var id = queues[i].id;
34 | var url = queues[i].url;
35 |
36 | if (url in scriptsMap) {
37 | continue;
38 | }
39 |
40 | scriptsMap[url] = true;
41 |
42 | var script = document.createElement('script');
43 | if (onerror) {
44 | (function(script, id){
45 | var tid = setTimeout(function(){
46 | onerror(id);
47 | }, require.timeout);
48 |
49 | script.onerror = function () {
50 | clearTimeout(tid);
51 | onerror(id);
52 | };
53 |
54 | var onload = function () {
55 | clearTimeout(tid);
56 | };
57 |
58 | if ('onload' in script) {
59 | script.onload = onload;
60 | }
61 | else {
62 | script.onreadystatechange = function () {
63 | if (this.readyState === 'loaded' || this.readyState === 'complete') {
64 | onload();
65 | }
66 | };
67 | }
68 | })(script, id);
69 | }
70 | script.type = 'text/javascript';
71 | script.src = url;
72 |
73 | docFrag.appendChild(script);
74 | }
75 |
76 | head.appendChild(docFrag);
77 | };
78 |
79 | var loadScripts = function(ids, callback, onerror){
80 | var queues = [];
81 | for(var i = 0, len = ids.length; i < len; i++){
82 | var id = ids[i];
83 | var queue = loadingMap[id] || (loadingMap[id] = []);
84 | queue.push(callback);
85 |
86 | //
87 | // resource map query
88 | //
89 | var res = resMap[id] || resMap[id + '.js'] || {};
90 | var pkg = res.pkg;
91 | var url;
92 |
93 | if (pkg) {
94 | url = pkgMap[pkg].url || pkgMap[pkg].uri;
95 | }
96 | else {
97 | url = res.url || res.uri || id;
98 | }
99 |
100 | queues.push({
101 | id: id,
102 | url: url
103 | });
104 | }
105 |
106 | createScripts(queues, onerror);
107 | };
108 |
109 | define = function (id, factory) {
110 | id = id.replace(/\.js$/i, '');
111 | factoryMap[id] = factory;
112 |
113 | var queue = loadingMap[id];
114 | if (queue) {
115 | for (var i = 0, n = queue.length; i < n; i++) {
116 | queue[i]();
117 | }
118 | delete loadingMap[id];
119 | }
120 | };
121 |
122 | require = function (id) {
123 |
124 | // compatible with require([dep, dep2...]) syntax.
125 | if (id && id.splice) {
126 | return require.async.apply(this, arguments);
127 | }
128 |
129 | id = require.alias(id);
130 |
131 | var mod = modulesMap[id];
132 | if (mod) {
133 | return mod.exports;
134 | }
135 |
136 | //
137 | // init module
138 | //
139 | var factory = factoryMap[id];
140 | if (!factory) {
141 | throw '[ModJS] Cannot find module `' + id + '`';
142 | }
143 |
144 | mod = modulesMap[id] = {
145 | exports: {}
146 | };
147 |
148 | //
149 | // factory: function OR value
150 | //
151 | var ret = (typeof factory === 'function') ? factory.apply(mod, [require, mod.exports, mod]) : factory;
152 |
153 | if (ret) {
154 | mod.exports = ret;
155 | }
156 |
157 | return mod.exports;
158 | };
159 |
160 | require.async = function (names, onload, onerror) {
161 | if (typeof names === 'string') {
162 | names = [names];
163 | }
164 |
165 | var needMap = {};
166 | var needNum = 0;
167 | var needLoad = [];
168 |
169 | function findNeed(depArr) {
170 | var child;
171 |
172 | for (var i = 0, n = depArr.length; i < n; i++) {
173 | //
174 | // skip loading or loaded
175 | //
176 | var dep = require.alias(depArr[i]);
177 |
178 | if (dep in needMap) {
179 | continue;
180 | }
181 |
182 | needMap[dep] = true;
183 |
184 | if (dep in factoryMap) {
185 | // check whether loaded resource's deps is loaded or not
186 | child = resMap[dep] || resMap[dep + '.js'];
187 | if (child && 'deps' in child) {
188 | findNeed(child.deps);
189 | }
190 | continue;
191 | }
192 |
193 | needLoad.push(dep);
194 | needNum++;
195 |
196 | child = resMap[dep] || resMap[dep + '.js'];
197 | if (child && 'deps' in child) {
198 | findNeed(child.deps);
199 | }
200 | }
201 | }
202 |
203 | function updateNeed() {
204 | if (0 === needNum--) {
205 | var args = [];
206 | for (var i = 0, n = names.length; i < n; i++) {
207 | args[i] = require(names[i]);
208 | }
209 |
210 | onload && onload.apply(global, args);
211 | }
212 | }
213 |
214 | findNeed(names);
215 | loadScripts(needLoad, updateNeed, onerror);
216 | updateNeed();
217 | };
218 |
219 | require.ensure = function(names, callback) {
220 | require.async(names, function() {
221 | callback && callback.call(this, require);
222 | });
223 | };
224 |
225 | require.resourceMap = function (obj) {
226 | var k;
227 | var col;
228 |
229 | // merge `res` & `pkg` fields
230 | col = obj.res;
231 | for (k in col) {
232 | if (col.hasOwnProperty(k)) {
233 | resMap[k] = col[k];
234 | }
235 | }
236 |
237 | col = obj.pkg;
238 | for (k in col) {
239 | if (col.hasOwnProperty(k)) {
240 | pkgMap[k] = col[k];
241 | }
242 | }
243 | };
244 |
245 | require.loadJs = function (url) {
246 | if (url in scriptsMap) {
247 | return;
248 | }
249 |
250 | scriptsMap[url] = true;
251 |
252 | var script = document.createElement('script');
253 | script.type = 'text/javascript';
254 | script.src = url;
255 | head.appendChild(script);
256 | };
257 |
258 | require.loadCss = function (cfg) {
259 | if (cfg.content) {
260 | var sty = document.createElement('style');
261 | sty.type = 'text/css';
262 |
263 | if (sty.styleSheet) { // IE
264 | sty.styleSheet.cssText = cfg.content;
265 | }
266 | else {
267 | sty.innerHTML = cfg.content;
268 | }
269 | head.appendChild(sty);
270 | }
271 | else if (cfg.url) {
272 | var link = document.createElement('link');
273 | link.href = cfg.url;
274 | link.rel = 'stylesheet';
275 | link.type = 'text/css';
276 | head.appendChild(link);
277 | }
278 | };
279 |
280 |
281 | require.alias = function (id) {
282 | return id.replace(/\.js$/i, '');
283 | };
284 |
285 | require.timeout = 5000;
286 |
287 | })(this);
288 |
--------------------------------------------------------------------------------
/static/polyfill/respond.src.js:
--------------------------------------------------------------------------------
1 | /*! Respond.js v1.4.2: min/max-width media query polyfill
2 | * Copyright 2014 Scott Jehl
3 | * Licensed under MIT
4 | * http://j.mp/respondjs */
5 |
6 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
7 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
8 | (function(w) {
9 | "use strict";
10 | w.matchMedia = w.matchMedia || function(doc, undefined) {
11 | var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, fakeBody = doc.createElement("body"), div = doc.createElement("div");
12 | div.id = "mq-test-1";
13 | div.style.cssText = "position:absolute;top:-100em";
14 | fakeBody.style.background = "none";
15 | fakeBody.appendChild(div);
16 | return function(q) {
17 | div.innerHTML = '';
18 | docElem.insertBefore(fakeBody, refNode);
19 | bool = div.offsetWidth === 42;
20 | docElem.removeChild(fakeBody);
21 | return {
22 | matches: bool,
23 | media: q
24 | };
25 | };
26 | }(w.document);
27 | })(this);
28 |
29 | (function(w) {
30 | "use strict";
31 | var respond = {};
32 | w.respond = respond;
33 | respond.update = function() {};
34 | var requestQueue = [], xmlHttp = function() {
35 | var xmlhttpmethod = false;
36 | try {
37 | xmlhttpmethod = new w.XMLHttpRequest();
38 | } catch (e) {
39 | xmlhttpmethod = new w.ActiveXObject("Microsoft.XMLHTTP");
40 | }
41 | return function() {
42 | return xmlhttpmethod;
43 | };
44 | }(), ajax = function(url, callback) {
45 | var req = xmlHttp();
46 | if (!req) {
47 | return;
48 | }
49 | req.open("GET", url, true);
50 | req.onreadystatechange = function() {
51 | if (req.readyState !== 4 || req.status !== 200 && req.status !== 304) {
52 | return;
53 | }
54 | callback(req.responseText);
55 | };
56 | if (req.readyState === 4) {
57 | return;
58 | }
59 | req.send(null);
60 | }, isUnsupportedMediaQuery = function(query) {
61 | return query.replace(respond.regex.minmaxwh, "").match(respond.regex.other);
62 | };
63 | respond.ajax = ajax;
64 | respond.queue = requestQueue;
65 | respond.unsupportedmq = isUnsupportedMediaQuery;
66 | respond.regex = {
67 | media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,
68 | keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,
69 | comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,
70 | urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,
71 | findStyles: /@media *([^\{]+)\{([\S\s]+?)$/,
72 | only: /(only\s+)?([a-zA-Z]+)\s?/,
73 | minw: /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,
74 | maxw: /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,
75 | minmaxwh: /\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,
76 | other: /\([^\)]*\)/g
77 | };
78 | respond.mediaQueriesSupported = w.matchMedia && w.matchMedia("only all") !== null && w.matchMedia("only all").matches;
79 | if (respond.mediaQueriesSupported) {
80 | return;
81 | }
82 | var doc = w.document, docElem = doc.documentElement, mediastyles = [], rules = [], appendedEls = [], parsedSheets = {}, resizeThrottle = 30, head = doc.getElementsByTagName("head")[0] || docElem, base = doc.getElementsByTagName("base")[0], links = head.getElementsByTagName("link"), lastCall, resizeDefer, eminpx, getEmValue = function() {
83 | var ret, div = doc.createElement("div"), body = doc.body, originalHTMLFontSize = docElem.style.fontSize, originalBodyFontSize = body && body.style.fontSize, fakeUsed = false;
84 | div.style.cssText = "position:absolute;font-size:1em;width:1em";
85 | if (!body) {
86 | body = fakeUsed = doc.createElement("body");
87 | body.style.background = "none";
88 | }
89 | docElem.style.fontSize = "100%";
90 | body.style.fontSize = "100%";
91 | body.appendChild(div);
92 | if (fakeUsed) {
93 | docElem.insertBefore(body, docElem.firstChild);
94 | }
95 | ret = div.offsetWidth;
96 | if (fakeUsed) {
97 | docElem.removeChild(body);
98 | } else {
99 | body.removeChild(div);
100 | }
101 | docElem.style.fontSize = originalHTMLFontSize;
102 | if (originalBodyFontSize) {
103 | body.style.fontSize = originalBodyFontSize;
104 | }
105 | ret = eminpx = parseFloat(ret);
106 | return ret;
107 | }, applyMedia = function(fromResize) {
108 | var name = "clientWidth", docElemProp = docElem[name], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now = new Date().getTime();
109 | if (fromResize && lastCall && now - lastCall < resizeThrottle) {
110 | w.clearTimeout(resizeDefer);
111 | resizeDefer = w.setTimeout(applyMedia, resizeThrottle);
112 | return;
113 | } else {
114 | lastCall = now;
115 | }
116 | for (var i in mediastyles) {
117 | if (mediastyles.hasOwnProperty(i)) {
118 | var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null, maxnull = max === null, em = "em";
119 | if (!!min) {
120 | min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1);
121 | }
122 | if (!!max) {
123 | max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1);
124 | }
125 | if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) {
126 | if (!styleBlocks[thisstyle.media]) {
127 | styleBlocks[thisstyle.media] = [];
128 | }
129 | styleBlocks[thisstyle.media].push(rules[thisstyle.rules]);
130 | }
131 | }
132 | }
133 | for (var j in appendedEls) {
134 | if (appendedEls.hasOwnProperty(j)) {
135 | if (appendedEls[j] && appendedEls[j].parentNode === head) {
136 | head.removeChild(appendedEls[j]);
137 | }
138 | }
139 | }
140 | appendedEls.length = 0;
141 | for (var k in styleBlocks) {
142 | if (styleBlocks.hasOwnProperty(k)) {
143 | var ss = doc.createElement("style"), css = styleBlocks[k].join("\n");
144 | ss.type = "text/css";
145 | ss.media = k;
146 | head.insertBefore(ss, lastLink.nextSibling);
147 | if (ss.styleSheet) {
148 | ss.styleSheet.cssText = css;
149 | } else {
150 | ss.appendChild(doc.createTextNode(css));
151 | }
152 | appendedEls.push(ss);
153 | }
154 | }
155 | }, translate = function(styles, href, media) {
156 | var qs = styles.replace(respond.regex.comments, "").replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0;
157 | href = href.substring(0, href.lastIndexOf("/"));
158 | var repUrls = function(css) {
159 | return css.replace(respond.regex.urls, "$1" + href + "$2$3");
160 | }, useMedia = !ql && media;
161 | if (href.length) {
162 | href += "/";
163 | }
164 | if (useMedia) {
165 | ql = 1;
166 | }
167 | for (var i = 0; i < ql; i++) {
168 | var fullq, thisq, eachq, eql;
169 | if (useMedia) {
170 | fullq = media;
171 | rules.push(repUrls(styles));
172 | } else {
173 | fullq = qs[i].match(respond.regex.findStyles) && RegExp.$1;
174 | rules.push(RegExp.$2 && repUrls(RegExp.$2));
175 | }
176 | eachq = fullq.split(",");
177 | eql = eachq.length;
178 | for (var j = 0; j < eql; j++) {
179 | thisq = eachq[j];
180 | if (isUnsupportedMediaQuery(thisq)) {
181 | continue;
182 | }
183 | mediastyles.push({
184 | media: thisq.split("(")[0].match(respond.regex.only) && RegExp.$2 || "all",
185 | rules: rules.length - 1,
186 | hasquery: thisq.indexOf("(") > -1,
187 | minw: thisq.match(respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || ""),
188 | maxw: thisq.match(respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "")
189 | });
190 | }
191 | }
192 | applyMedia();
193 | }, makeRequests = function() {
194 | if (requestQueue.length) {
195 | var thisRequest = requestQueue.shift();
196 | ajax(thisRequest.href, function(styles) {
197 | translate(styles, thisRequest.href, thisRequest.media);
198 | parsedSheets[thisRequest.href] = true;
199 | w.setTimeout(function() {
200 | makeRequests();
201 | }, 0);
202 | });
203 | }
204 | }, ripCSS = function() {
205 | for (var i = 0; i < links.length; i++) {
206 | var sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
207 | if (!!href && isCSS && !parsedSheets[href]) {
208 | if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
209 | translate(sheet.styleSheet.rawCssText, href, media);
210 | parsedSheets[href] = true;
211 | } else {
212 | if (!/^([a-zA-Z:]*\/\/)/.test(href) && !base || href.replace(RegExp.$1, "").split("/")[0] === w.location.host) {
213 | if (href.substring(0, 2) === "//") {
214 | href = w.location.protocol + href;
215 | }
216 | requestQueue.push({
217 | href: href,
218 | media: media
219 | });
220 | }
221 | }
222 | }
223 | }
224 | makeRequests();
225 | };
226 | ripCSS();
227 | respond.update = ripCSS;
228 | respond.getEmValue = getEmValue;
229 | function callMedia() {
230 | applyMedia(true);
231 | }
232 | if (w.addEventListener) {
233 | w.addEventListener("resize", callMedia, false);
234 | } else if (w.attachEvent) {
235 | w.attachEvent("onresize", callMedia);
236 | }
237 | })(this);
--------------------------------------------------------------------------------
/static/polyfill/html5shiv.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | ;(function(window, document) {
5 | /*jshint evil:true */
6 | /** version */
7 | var version = '3.7.3';
8 |
9 | /** Preset options */
10 | var options = window.html5 || {};
11 |
12 | /** Used to skip problem elements */
13 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
14 |
15 | /** Not all elements can be cloned in IE **/
16 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
17 |
18 | /** Detect whether the browser supports default html5 styles */
19 | var supportsHtml5Styles;
20 |
21 | /** Name of the expando, to work with multiple documents or to re-shiv one document */
22 | var expando = '_html5shiv';
23 |
24 | /** The id for the the documents expando */
25 | var expanID = 0;
26 |
27 | /** Cached data for each document */
28 | var expandoData = {};
29 |
30 | /** Detect whether the browser supports unknown elements */
31 | var supportsUnknownElements;
32 |
33 | (function() {
34 | try {
35 | var a = document.createElement('a');
36 | a.innerHTML = '';
37 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
38 | supportsHtml5Styles = ('hidden' in a);
39 |
40 | supportsUnknownElements = a.childNodes.length == 1 || (function() {
41 | // assign a false positive if unable to shiv
42 | (document.createElement)('a');
43 | var frag = document.createDocumentFragment();
44 | return (
45 | typeof frag.cloneNode == 'undefined' ||
46 | typeof frag.createDocumentFragment == 'undefined' ||
47 | typeof frag.createElement == 'undefined'
48 | );
49 | }());
50 | } catch(e) {
51 | // assign a false positive if detection fails => unable to shiv
52 | supportsHtml5Styles = true;
53 | supportsUnknownElements = true;
54 | }
55 |
56 | }());
57 |
58 | /*--------------------------------------------------------------------------*/
59 |
60 | /**
61 | * Creates a style sheet with the given CSS text and adds it to the document.
62 | * @private
63 | * @param {Document} ownerDocument The document.
64 | * @param {String} cssText The CSS text.
65 | * @returns {StyleSheet} The style element.
66 | */
67 | function addStyleSheet(ownerDocument, cssText) {
68 | var p = ownerDocument.createElement('p'),
69 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
70 |
71 | p.innerHTML = 'x';
72 | return parent.insertBefore(p.lastChild, parent.firstChild);
73 | }
74 |
75 | /**
76 | * Returns the value of `html5.elements` as an array.
77 | * @private
78 | * @returns {Array} An array of shived element node names.
79 | */
80 | function getElements() {
81 | var elements = html5.elements;
82 | return typeof elements == 'string' ? elements.split(' ') : elements;
83 | }
84 |
85 | /**
86 | * Extends the built-in list of html5 elements
87 | * @memberOf html5
88 | * @param {String|Array} newElements whitespace separated list or array of new element names to shiv
89 | * @param {Document} ownerDocument The context document.
90 | */
91 | function addElements(newElements, ownerDocument) {
92 | var elements = html5.elements;
93 | if(typeof elements != 'string'){
94 | elements = elements.join(' ');
95 | }
96 | if(typeof newElements != 'string'){
97 | newElements = newElements.join(' ');
98 | }
99 | html5.elements = elements +' '+ newElements;
100 | shivDocument(ownerDocument);
101 | }
102 |
103 | /**
104 | * Returns the data associated to the given document
105 | * @private
106 | * @param {Document} ownerDocument The document.
107 | * @returns {Object} An object of data.
108 | */
109 | function getExpandoData(ownerDocument) {
110 | var data = expandoData[ownerDocument[expando]];
111 | if (!data) {
112 | data = {};
113 | expanID++;
114 | ownerDocument[expando] = expanID;
115 | expandoData[expanID] = data;
116 | }
117 | return data;
118 | }
119 |
120 | /**
121 | * returns a shived element for the given nodeName and document
122 | * @memberOf html5
123 | * @param {String} nodeName name of the element
124 | * @param {Document|DocumentFragment} ownerDocument The context document.
125 | * @returns {Object} The shived element.
126 | */
127 | function createElement(nodeName, ownerDocument, data){
128 | if (!ownerDocument) {
129 | ownerDocument = document;
130 | }
131 | if(supportsUnknownElements){
132 | return ownerDocument.createElement(nodeName);
133 | }
134 | if (!data) {
135 | data = getExpandoData(ownerDocument);
136 | }
137 | var node;
138 |
139 | if (data.cache[nodeName]) {
140 | node = data.cache[nodeName].cloneNode();
141 | } else if (saveClones.test(nodeName)) {
142 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
143 | } else {
144 | node = data.createElem(nodeName);
145 | }
146 |
147 | // Avoid adding some elements to fragments in IE < 9 because
148 | // * Attributes like `name` or `type` cannot be set/changed once an element
149 | // is inserted into a document/fragment
150 | // * Link elements with `src` attributes that are inaccessible, as with
151 | // a 403 response, will cause the tab/window to crash
152 | // * Script elements appended to fragments will execute when their `src`
153 | // or `text` property is set
154 | return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
155 | }
156 |
157 | /**
158 | * returns a shived DocumentFragment for the given document
159 | * @memberOf html5
160 | * @param {Document} ownerDocument The context document.
161 | * @returns {Object} The shived DocumentFragment.
162 | */
163 | function createDocumentFragment(ownerDocument, data){
164 | if (!ownerDocument) {
165 | ownerDocument = document;
166 | }
167 | if(supportsUnknownElements){
168 | return ownerDocument.createDocumentFragment();
169 | }
170 | data = data || getExpandoData(ownerDocument);
171 | var clone = data.frag.cloneNode(),
172 | i = 0,
173 | elems = getElements(),
174 | l = elems.length;
175 | for(;i'),
122 | defaults = $.minicolors.defaults;
123 |
124 | // Do nothing if already initialized
125 | if( input.data('minicolors-initialized') ) return;
126 |
127 | // Handle settings
128 | settings = $.extend(true, {}, defaults, settings);
129 |
130 | // The wrapper
131 | minicolors
132 | .addClass('minicolors-theme-' + settings.theme)
133 | .toggleClass('minicolors-with-opacity', settings.opacity);
134 |
135 | // Custom positioning
136 | if( settings.position !== undefined ) {
137 | $.each(settings.position.split(' '), function() {
138 | minicolors.addClass('minicolors-position-' + this);
139 | });
140 | }
141 |
142 | // The input
143 | input
144 | .addClass('minicolors-input')
145 | .data('minicolors-initialized', false)
146 | .data('minicolors-settings', settings)
147 | .prop('size', 7)
148 | .wrap(minicolors)
149 | .after(
150 | '' +
151 | '
' +
154 | '
' +
157 | '
' +
158 | '
' +
159 | '
' +
160 | '
' +
161 | '
'
162 | );
163 |
164 | // The swatch
165 | if( !settings.inline ) {
166 | input.after('');
167 | input.next('.minicolors-swatch').on('click', function(event) {
168 | event.preventDefault();
169 | input.focus();
170 | });
171 | }
172 |
173 | // Prevent text selection in IE
174 | input.parent().find('.minicolors-panel').on('selectstart', function() { return false; }).end();
175 |
176 | // Inline controls
177 | if( settings.inline ) input.parent().addClass('minicolors-inline');
178 |
179 | updateFromInput(input, false);
180 |
181 | input.data('minicolors-initialized', true);
182 |
183 | }
184 |
185 | // Returns the input back to its original state
186 | function destroy(input) {
187 |
188 | var minicolors = input.parent();
189 |
190 | // Revert the input element
191 | input
192 | .removeData('minicolors-initialized')
193 | .removeData('minicolors-settings')
194 | .removeProp('size')
195 | .removeClass('minicolors-input');
196 |
197 | // Remove the wrap and destroy whatever remains
198 | minicolors.before(input).remove();
199 |
200 | }
201 |
202 | // Shows the specified dropdown panel
203 | function show(input) {
204 |
205 | var minicolors = input.parent(),
206 | panel = minicolors.find('.minicolors-panel'),
207 | settings = input.data('minicolors-settings');
208 |
209 | // Do nothing if uninitialized, disabled, inline, or already open
210 | if( !input.data('minicolors-initialized') ||
211 | input.prop('disabled') ||
212 | minicolors.hasClass('minicolors-inline') ||
213 | minicolors.hasClass('minicolors-focus')
214 | ) return;
215 |
216 | hide();
217 |
218 | minicolors.addClass('minicolors-focus');
219 | panel
220 | .stop(true, true)
221 | .fadeIn(settings.showSpeed, function() {
222 | if( settings.show ) settings.show.call(input.get(0));
223 | });
224 |
225 | }
226 |
227 | // Hides all dropdown panels
228 | function hide() {
229 |
230 | $('.minicolors-focus').each( function() {
231 |
232 | var minicolors = $(this),
233 | input = minicolors.find('.minicolors-input'),
234 | panel = minicolors.find('.minicolors-panel'),
235 | settings = input.data('minicolors-settings');
236 |
237 | panel.fadeOut(settings.hideSpeed, function() {
238 | if( settings.hide ) settings.hide.call(input.get(0));
239 | minicolors.removeClass('minicolors-focus');
240 | });
241 |
242 | });
243 | }
244 |
245 | // Moves the selected picker
246 | function move(target, event, animate) {
247 |
248 | var input = target.parents('.minicolors').find('.minicolors-input'),
249 | settings = input.data('minicolors-settings'),
250 | picker = target.find('[class$=-picker]'),
251 | offsetX = target.offset().left,
252 | offsetY = target.offset().top,
253 | x = Math.round(event.pageX - offsetX),
254 | y = Math.round(event.pageY - offsetY),
255 | duration = animate ? settings.animationSpeed : 0,
256 | wx, wy, r, phi;
257 |
258 | // Touch support
259 | if( event.originalEvent.changedTouches ) {
260 | x = event.originalEvent.changedTouches[0].pageX - offsetX;
261 | y = event.originalEvent.changedTouches[0].pageY - offsetY;
262 | }
263 |
264 | // Constrain picker to its container
265 | if( x < 0 ) x = 0;
266 | if( y < 0 ) y = 0;
267 | if( x > target.width() ) x = target.width();
268 | if( y > target.height() ) y = target.height();
269 |
270 | // Constrain color wheel values to the wheel
271 | if( target.parent().is('.minicolors-slider-wheel') && picker.parent().is('.minicolors-grid') ) {
272 | wx = 75 - x;
273 | wy = 75 - y;
274 | r = Math.sqrt(wx * wx + wy * wy);
275 | phi = Math.atan2(wy, wx);
276 | if( phi < 0 ) phi += Math.PI * 2;
277 | if( r > 75 ) {
278 | r = 75;
279 | x = 75 - (75 * Math.cos(phi));
280 | y = 75 - (75 * Math.sin(phi));
281 | }
282 | x = Math.round(x);
283 | y = Math.round(y);
284 | }
285 |
286 | // Move the picker
287 | if( target.is('.minicolors-grid') ) {
288 | picker
289 | .stop(true)
290 | .animate({
291 | top: y + 'px',
292 | left: x + 'px'
293 | }, duration, settings.animationEasing, function() {
294 | updateFromControl(input, target);
295 | });
296 | } else {
297 | picker
298 | .stop(true)
299 | .animate({
300 | top: y + 'px'
301 | }, duration, settings.animationEasing, function() {
302 | updateFromControl(input, target);
303 | });
304 | }
305 |
306 | }
307 |
308 | // Sets the input based on the color picker values
309 | function updateFromControl(input, target) {
310 |
311 | function getCoords(picker, container) {
312 |
313 | var left, top;
314 | if( !picker.length || !container ) return null;
315 | left = picker.offset().left;
316 | top = picker.offset().top;
317 |
318 | return {
319 | x: left - container.offset().left + (picker.outerWidth() / 2),
320 | y: top - container.offset().top + (picker.outerHeight() / 2)
321 | };
322 |
323 | }
324 |
325 | var hue, saturation, brightness, x, y, r, phi,
326 |
327 | hex = input.val(),
328 | opacity = input.attr('data-opacity'),
329 |
330 | // Helpful references
331 | minicolors = input.parent(),
332 | settings = input.data('minicolors-settings'),
333 | swatch = minicolors.find('.minicolors-swatch'),
334 |
335 | // Panel objects
336 | grid = minicolors.find('.minicolors-grid'),
337 | slider = minicolors.find('.minicolors-slider'),
338 | opacitySlider = minicolors.find('.minicolors-opacity-slider'),
339 |
340 | // Picker objects
341 | gridPicker = grid.find('[class$=-picker]'),
342 | sliderPicker = slider.find('[class$=-picker]'),
343 | opacityPicker = opacitySlider.find('[class$=-picker]'),
344 |
345 | // Picker positions
346 | gridPos = getCoords(gridPicker, grid),
347 | sliderPos = getCoords(sliderPicker, slider),
348 | opacityPos = getCoords(opacityPicker, opacitySlider);
349 |
350 | // Handle colors
351 | if( target.is('.minicolors-grid, .minicolors-slider') ) {
352 |
353 | // Determine HSB values
354 | switch(settings.control) {
355 |
356 | case 'wheel':
357 | // Calculate hue, saturation, and brightness
358 | x = (grid.width() / 2) - gridPos.x;
359 | y = (grid.height() / 2) - gridPos.y;
360 | r = Math.sqrt(x * x + y * y);
361 | phi = Math.atan2(y, x);
362 | if( phi < 0 ) phi += Math.PI * 2;
363 | if( r > 75 ) {
364 | r = 75;
365 | gridPos.x = 69 - (75 * Math.cos(phi));
366 | gridPos.y = 69 - (75 * Math.sin(phi));
367 | }
368 | saturation = keepWithin(r / 0.75, 0, 100);
369 | hue = keepWithin(phi * 180 / Math.PI, 0, 360);
370 | brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
371 | hex = hsb2hex({
372 | h: hue,
373 | s: saturation,
374 | b: brightness
375 | });
376 |
377 | // Update UI
378 | slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
379 | break;
380 |
381 | case 'saturation':
382 | // Calculate hue, saturation, and brightness
383 | hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
384 | saturation = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
385 | brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
386 | hex = hsb2hex({
387 | h: hue,
388 | s: saturation,
389 | b: brightness
390 | });
391 |
392 | // Update UI
393 | slider.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: brightness }));
394 | minicolors.find('.minicolors-grid-inner').css('opacity', saturation / 100);
395 | break;
396 |
397 | case 'brightness':
398 | // Calculate hue, saturation, and brightness
399 | hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
400 | saturation = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
401 | brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
402 | hex = hsb2hex({
403 | h: hue,
404 | s: saturation,
405 | b: brightness
406 | });
407 |
408 | // Update UI
409 | slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
410 | minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (brightness / 100));
411 | break;
412 |
413 | default:
414 | // Calculate hue, saturation, and brightness
415 | hue = keepWithin(360 - parseInt(sliderPos.y * (360 / slider.height()), 10), 0, 360);
416 | saturation = keepWithin(Math.floor(gridPos.x * (100 / grid.width())), 0, 100);
417 | brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
418 | hex = hsb2hex({
419 | h: hue,
420 | s: saturation,
421 | b: brightness
422 | });
423 |
424 | // Update UI
425 | grid.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: 100 }));
426 | break;
427 |
428 | }
429 |
430 | // Adjust case
431 | input.val( convertCase(hex, settings.letterCase) );
432 |
433 | }
434 |
435 | // Handle opacity
436 | if( target.is('.minicolors-opacity-slider') ) {
437 | if( settings.opacity ) {
438 | opacity = parseFloat(1 - (opacityPos.y / opacitySlider.height())).toFixed(2);
439 | } else {
440 | opacity = 1;
441 | }
442 | if( settings.opacity ) input.attr('data-opacity', opacity);
443 | }
444 |
445 | // Set swatch color
446 | swatch.find('SPAN').css({
447 | backgroundColor: hex,
448 | opacity: opacity
449 | });
450 |
451 | // Handle change event
452 | doChange(input, hex, opacity);
453 |
454 | }
455 |
456 | // Sets the color picker values from the input
457 | function updateFromInput(input, preserveInputValue) {
458 |
459 | var hex,
460 | hsb,
461 | opacity,
462 | x, y, r, phi,
463 |
464 | // Helpful references
465 | minicolors = input.parent(),
466 | settings = input.data('minicolors-settings'),
467 | swatch = minicolors.find('.minicolors-swatch'),
468 |
469 | // Panel objects
470 | grid = minicolors.find('.minicolors-grid'),
471 | slider = minicolors.find('.minicolors-slider'),
472 | opacitySlider = minicolors.find('.minicolors-opacity-slider'),
473 |
474 | // Picker objects
475 | gridPicker = grid.find('[class$=-picker]'),
476 | sliderPicker = slider.find('[class$=-picker]'),
477 | opacityPicker = opacitySlider.find('[class$=-picker]');
478 |
479 | // Determine hex/HSB values
480 | hex = convertCase(parseHex(input.val(), true), settings.letterCase);
481 | if( !hex ){
482 | hex = convertCase(parseHex(settings.defaultValue, true), settings.letterCase);
483 | }
484 | hsb = hex2hsb(hex);
485 |
486 | // Update input value
487 | if( !preserveInputValue ) input.val(hex);
488 |
489 | // Determine opacity value
490 | if( settings.opacity ) {
491 | // Get from data-opacity attribute and keep within 0-1 range
492 | opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1);
493 | if( isNaN(opacity) ) opacity = 1;
494 | input.attr('data-opacity', opacity);
495 | swatch.find('SPAN').css('opacity', opacity);
496 |
497 | // Set opacity picker position
498 | y = keepWithin(opacitySlider.height() - (opacitySlider.height() * opacity), 0, opacitySlider.height());
499 | opacityPicker.css('top', y + 'px');
500 | }
501 |
502 | // Update swatch
503 | swatch.find('SPAN').css('backgroundColor', hex);
504 |
505 | // Determine picker locations
506 | switch(settings.control) {
507 |
508 | case 'wheel':
509 | // Set grid position
510 | r = keepWithin(Math.ceil(hsb.s * 0.75), 0, grid.height() / 2);
511 | phi = hsb.h * Math.PI / 180;
512 | x = keepWithin(75 - Math.cos(phi) * r, 0, grid.width());
513 | y = keepWithin(75 - Math.sin(phi) * r, 0, grid.height());
514 | gridPicker.css({
515 | top: y + 'px',
516 | left: x + 'px'
517 | });
518 |
519 | // Set slider position
520 | y = 150 - (hsb.b / (100 / grid.height()));
521 | if( hex === '' ) y = 0;
522 | sliderPicker.css('top', y + 'px');
523 |
524 | // Update panel color
525 | slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
526 | break;
527 |
528 | case 'saturation':
529 | // Set grid position
530 | x = keepWithin((5 * hsb.h) / 12, 0, 150);
531 | y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
532 | gridPicker.css({
533 | top: y + 'px',
534 | left: x + 'px'
535 | });
536 |
537 | // Set slider position
538 | y = keepWithin(slider.height() - (hsb.s * (slider.height() / 100)), 0, slider.height());
539 | sliderPicker.css('top', y + 'px');
540 |
541 | // Update UI
542 | slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: hsb.b }));
543 | minicolors.find('.minicolors-grid-inner').css('opacity', hsb.s / 100);
544 | break;
545 |
546 | case 'brightness':
547 | // Set grid position
548 | x = keepWithin((5 * hsb.h) / 12, 0, 150);
549 | y = keepWithin(grid.height() - Math.ceil(hsb.s / (100 / grid.height())), 0, grid.height());
550 | gridPicker.css({
551 | top: y + 'px',
552 | left: x + 'px'
553 | });
554 |
555 | // Set slider position
556 | y = keepWithin(slider.height() - (hsb.b * (slider.height() / 100)), 0, slider.height());
557 | sliderPicker.css('top', y + 'px');
558 |
559 | // Update UI
560 | slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
561 | minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (hsb.b / 100));
562 | break;
563 |
564 | default:
565 | // Set grid position
566 | x = keepWithin(Math.ceil(hsb.s / (100 / grid.width())), 0, grid.width());
567 | y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
568 | gridPicker.css({
569 | top: y + 'px',
570 | left: x + 'px'
571 | });
572 |
573 | // Set slider position
574 | y = keepWithin(slider.height() - (hsb.h / (360 / slider.height())), 0, slider.height());
575 | sliderPicker.css('top', y + 'px');
576 |
577 | // Update panel color
578 | grid.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: 100 }));
579 | break;
580 |
581 | }
582 |
583 | // Fire change event, but only if minicolors is fully initialized
584 | if( input.data('minicolors-initialized') ) {
585 | doChange(input, hex, opacity);
586 | }
587 |
588 | }
589 |
590 | // Runs the change and changeDelay callbacks
591 | function doChange(input, hex, opacity) {
592 |
593 | var settings = input.data('minicolors-settings'),
594 | lastChange = input.data('minicolors-lastChange');
595 |
596 | // Only run if it actually changed
597 | if( !lastChange || lastChange.hex !== hex || lastChange.opacity !== opacity ) {
598 |
599 | // Remember last-changed value
600 | input.data('minicolors-lastChange', {
601 | hex: hex,
602 | opacity: opacity
603 | });
604 |
605 | // Fire change event
606 | if( settings.change ) {
607 | if( settings.changeDelay ) {
608 | // Call after a delay
609 | clearTimeout(input.data('minicolors-changeTimeout'));
610 | input.data('minicolors-changeTimeout', setTimeout( function() {
611 | settings.change.call(input.get(0), hex, opacity);
612 | }, settings.changeDelay));
613 | } else {
614 | // Call immediately
615 | settings.change.call(input.get(0), hex, opacity);
616 | }
617 | }
618 | input.trigger('change').trigger('input');
619 | }
620 |
621 | }
622 |
623 | // Generates an RGB(A) object based on the input's value
624 | function rgbObject(input) {
625 | var hex = parseHex($(input).val(), true),
626 | rgb = hex2rgb(hex),
627 | opacity = $(input).attr('data-opacity');
628 | if( !rgb ) return null;
629 | if( opacity !== undefined ) $.extend(rgb, { a: parseFloat(opacity) });
630 | return rgb;
631 | }
632 |
633 | // Genearates an RGB(A) string based on the input's value
634 | function rgbString(input, alpha) {
635 | var hex = parseHex($(input).val(), true),
636 | rgb = hex2rgb(hex),
637 | opacity = $(input).attr('data-opacity');
638 | if( !rgb ) return null;
639 | if( opacity === undefined ) opacity = 1;
640 | if( alpha ) {
641 | return 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')';
642 | } else {
643 | return 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
644 | }
645 | }
646 |
647 | // Converts to the letter case specified in settings
648 | function convertCase(string, letterCase) {
649 | return letterCase === 'uppercase' ? string.toUpperCase() : string.toLowerCase();
650 | }
651 |
652 | // Parses a string and returns a valid hex string when possible
653 | function parseHex(string, expand) {
654 | string = string.replace(/[^A-F0-9]/ig, '');
655 | if( string.length !== 3 && string.length !== 6 ) return '';
656 | if( string.length === 3 && expand ) {
657 | string = string[0] + string[0] + string[1] + string[1] + string[2] + string[2];
658 | }
659 | return '#' + string;
660 | }
661 |
662 | // Keeps value within min and max
663 | function keepWithin(value, min, max) {
664 | if( value < min ) value = min;
665 | if( value > max ) value = max;
666 | return value;
667 | }
668 |
669 | // Converts an HSB object to an RGB object
670 | function hsb2rgb(hsb) {
671 | var rgb = {};
672 | var h = Math.round(hsb.h);
673 | var s = Math.round(hsb.s * 255 / 100);
674 | var v = Math.round(hsb.b * 255 / 100);
675 | if(s === 0) {
676 | rgb.r = rgb.g = rgb.b = v;
677 | } else {
678 | var t1 = v;
679 | var t2 = (255 - s) * v / 255;
680 | var t3 = (t1 - t2) * (h % 60) / 60;
681 | if( h === 360 ) h = 0;
682 | if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; }
683 | else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; }
684 | else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; }
685 | else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; }
686 | else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; }
687 | else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; }
688 | else { rgb.r = 0; rgb.g = 0; rgb.b = 0; }
689 | }
690 | return {
691 | r: Math.round(rgb.r),
692 | g: Math.round(rgb.g),
693 | b: Math.round(rgb.b)
694 | };
695 | }
696 |
697 | // Converts an RGB object to a hex string
698 | function rgb2hex(rgb) {
699 | var hex = [
700 | rgb.r.toString(16),
701 | rgb.g.toString(16),
702 | rgb.b.toString(16)
703 | ];
704 | $.each(hex, function(nr, val) {
705 | if (val.length === 1) hex[nr] = '0' + val;
706 | });
707 | return '#' + hex.join('');
708 | }
709 |
710 | // Converts an HSB object to a hex string
711 | function hsb2hex(hsb) {
712 | return rgb2hex(hsb2rgb(hsb));
713 | }
714 |
715 | // Converts a hex string to an HSB object
716 | function hex2hsb(hex) {
717 | var hsb = rgb2hsb(hex2rgb(hex));
718 | if( hsb.s === 0 ) hsb.h = 360;
719 | return hsb;
720 | }
721 |
722 | // Converts an RGB object to an HSB object
723 | function rgb2hsb(rgb) {
724 | var hsb = { h: 0, s: 0, b: 0 };
725 | var min = Math.min(rgb.r, rgb.g, rgb.b);
726 | var max = Math.max(rgb.r, rgb.g, rgb.b);
727 | var delta = max - min;
728 | hsb.b = max;
729 | hsb.s = max !== 0 ? 255 * delta / max : 0;
730 | if( hsb.s !== 0 ) {
731 | if( rgb.r === max ) {
732 | hsb.h = (rgb.g - rgb.b) / delta;
733 | } else if( rgb.g === max ) {
734 | hsb.h = 2 + (rgb.b - rgb.r) / delta;
735 | } else {
736 | hsb.h = 4 + (rgb.r - rgb.g) / delta;
737 | }
738 | } else {
739 | hsb.h = -1;
740 | }
741 | hsb.h *= 60;
742 | if( hsb.h < 0 ) {
743 | hsb.h += 360;
744 | }
745 | hsb.s *= 100/255;
746 | hsb.b *= 100/255;
747 | return hsb;
748 | }
749 |
750 | // Converts a hex string to an RGB object
751 | function hex2rgb(hex) {
752 | hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
753 | return {
754 | r: hex >> 16,
755 | g: (hex & 0x00FF00) >> 8,
756 | b: (hex & 0x0000FF)
757 | };
758 | }
759 |
760 | // Handle events
761 | $(document)
762 | // Hide on clicks outside of the control
763 | .on('mousedown.minicolors touchstart.minicolors', function(event) {
764 | if( !$(event.target).parents().add(event.target).hasClass('minicolors') ) {
765 | hide();
766 | }
767 | })
768 | // Start moving
769 | .on('mousedown.minicolors touchstart.minicolors', '.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider', function(event) {
770 | var target = $(this);
771 | event.preventDefault();
772 | $(document).data('minicolors-target', target);
773 | move(target, event, true);
774 | })
775 | // Move pickers
776 | .on('mousemove.minicolors touchmove.minicolors', function(event) {
777 | var target = $(document).data('minicolors-target');
778 | if( target ) move(target, event);
779 | })
780 | // Stop moving
781 | .on('mouseup.minicolors touchend.minicolors', function() {
782 | $(this).removeData('minicolors-target');
783 | })
784 | // Show panel when swatch is clicked
785 | .on('mousedown.minicolors touchstart.minicolors', '.minicolors-swatch', function(event) {
786 | var input = $(this).parent().find('.minicolors-input');
787 | event.preventDefault();
788 | show(input);
789 | })
790 | // Show on focus
791 | .on('focus.minicolors', '.minicolors-input', function() {
792 | var input = $(this);
793 | if( !input.data('minicolors-initialized') ) return;
794 | show(input);
795 | })
796 | // Fix hex on blur
797 | .on('blur.minicolors', '.minicolors-input', function() {
798 | var input = $(this),
799 | settings = input.data('minicolors-settings');
800 | if( !input.data('minicolors-initialized') ) return;
801 |
802 | // Parse Hex
803 | input.val(parseHex(input.val(), true));
804 |
805 | // Is it blank?
806 | if( input.val() === '' ) input.val(parseHex(settings.defaultValue, true));
807 |
808 | // Adjust case
809 | input.val( convertCase(input.val(), settings.letterCase) );
810 |
811 | })
812 | // Handle keypresses
813 | .on('keydown.minicolors', '.minicolors-input', function(event) {
814 | var input = $(this);
815 | if( !input.data('minicolors-initialized') ) return;
816 | switch(event.keyCode) {
817 | case 9: // tab
818 | hide();
819 | break;
820 | case 13: // enter
821 | case 27: // esc
822 | hide();
823 | input.blur();
824 | break;
825 | }
826 | })
827 | // Update on keyup
828 | .on('keyup.minicolors', '.minicolors-input', function() {
829 | var input = $(this);
830 | if( !input.data('minicolors-initialized') ) return;
831 | updateFromInput(input, true);
832 | })
833 | // Update on paste
834 | .on('paste.minicolors', '.minicolors-input', function() {
835 | var input = $(this);
836 | if( !input.data('minicolors-initialized') ) return;
837 | setTimeout( function() {
838 | updateFromInput(input, true);
839 | }, 1);
840 | });
841 |
842 | })(jQuery);
--------------------------------------------------------------------------------