├── assets
├── styles
│ ├── ui.less
│ ├── base.less
│ ├── scrollbar.less
│ └── theme
│ │ ├── android.less
│ │ └── pc.less
└── theme
│ └── default
│ ├── images
│ ├── up.png
│ ├── close.png
│ ├── down.png
│ └── open.png
│ └── fui.min.css
├── dist
├── assets
│ └── styles
│ │ ├── ui.css
│ │ ├── base.css
│ │ ├── scrollbar.css
│ │ └── theme
│ │ ├── android.css
│ │ └── pc.css
└── resource
│ ├── KF_AMS_BB.woff
│ ├── KF_AMS_CAL.woff
│ ├── KF_AMS_FRAK.woff
│ ├── KF_AMS_MAIN.woff
│ └── KF_AMS_ROMAN.woff
├── .babelrc
├── images
├── web.png
└── android.png
├── dev-lib
├── dev-start.js
├── exports.js
├── npmEntry.js
├── start.js
├── jhtmls.min.js
└── dev-define.js
├── resource
├── KF_AMS_BB.woff
├── KF_AMS_CAL.woff
├── KF_AMS_FRAK.woff
├── KF_AMS_MAIN.woff
└── KF_AMS_ROMAN.woff
├── src
├── jquery.js
├── kf.js
├── kity.js
├── ui
│ ├── ui-impl
│ │ ├── def
│ │ │ ├── item-type.js
│ │ │ ├── box-type.js
│ │ │ └── ele-type.js
│ │ ├── ui.js
│ │ ├── keyboard
│ │ │ ├── const.js
│ │ │ ├── footer
│ │ │ │ └── index.js
│ │ │ ├── menu
│ │ │ │ └── index.js
│ │ │ ├── page
│ │ │ │ └── index.js
│ │ │ ├── panel
│ │ │ │ ├── index.js
│ │ │ │ ├── pcConst.js
│ │ │ │ ├── androidConst.js
│ │ │ │ └── position.data.js
│ │ │ └── keyboard.js
│ │ ├── delimiter.js
│ │ ├── drapdown-box.js
│ │ ├── ui-utils.js
│ │ ├── list.js
│ │ └── button.js
│ ├── def.js
│ ├── header
│ │ └── header.js
│ ├── control
│ │ └── zoom.js
│ ├── keyboard
│ │ └── keyboard.js
│ └── toolbar
│ │ └── toolbar.js
├── def
│ └── group-type.js
├── kf-ext
│ ├── def.js
│ ├── extension.js
│ ├── expression
│ │ └── placeholder.js
│ └── operator
│ │ └── placeholder.js
├── bundle.js
├── base
│ ├── component.js
│ ├── utils.js
│ ├── event
│ │ ├── kfevent.js
│ │ └── event.js
│ └── common.js
├── parse
│ ├── vgroup-def.js
│ └── parser.js
├── sysconf.js
├── control
│ ├── input-filter.js
│ ├── controller.js
│ ├── listener.js
│ └── location.js
├── editor
│ ├── Message.js
│ ├── factory.js
│ ├── command.md
│ └── editor.js
├── print
│ └── printer.js
├── syntax
│ └── delete.js
└── position
│ └── position.js
├── config
└── conf.js
├── .jshintrc
├── LICENSE
├── README.md
├── package.json
├── .gitignore
├── index.html
└── Gruntfile.js
/assets/styles/ui.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/styles/ui.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "sourceType": "script",
3 | "presets": ["@babel/preset-env"]
4 | }
5 |
--------------------------------------------------------------------------------
/images/web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/images/web.png
--------------------------------------------------------------------------------
/images/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/images/android.png
--------------------------------------------------------------------------------
/dev-lib/dev-start.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 13-12-4.
3 | */
4 | // 启动脚本
5 | inc.use( 'kf.start' );
--------------------------------------------------------------------------------
/resource/KF_AMS_BB.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/resource/KF_AMS_BB.woff
--------------------------------------------------------------------------------
/resource/KF_AMS_CAL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/resource/KF_AMS_CAL.woff
--------------------------------------------------------------------------------
/resource/KF_AMS_FRAK.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/resource/KF_AMS_FRAK.woff
--------------------------------------------------------------------------------
/resource/KF_AMS_MAIN.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/resource/KF_AMS_MAIN.woff
--------------------------------------------------------------------------------
/dist/resource/KF_AMS_BB.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/dist/resource/KF_AMS_BB.woff
--------------------------------------------------------------------------------
/resource/KF_AMS_ROMAN.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/resource/KF_AMS_ROMAN.woff
--------------------------------------------------------------------------------
/dist/resource/KF_AMS_CAL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/dist/resource/KF_AMS_CAL.woff
--------------------------------------------------------------------------------
/dist/resource/KF_AMS_FRAK.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/dist/resource/KF_AMS_FRAK.woff
--------------------------------------------------------------------------------
/dist/resource/KF_AMS_MAIN.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/dist/resource/KF_AMS_MAIN.woff
--------------------------------------------------------------------------------
/dist/resource/KF_AMS_ROMAN.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/dist/resource/KF_AMS_ROMAN.woff
--------------------------------------------------------------------------------
/src/jquery.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-31.
3 | */
4 |
5 | define( function () {
6 | return window.jQuery;
7 | } );
--------------------------------------------------------------------------------
/assets/theme/default/images/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/assets/theme/default/images/up.png
--------------------------------------------------------------------------------
/src/kf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-12.
3 | */
4 |
5 | define( function () {
6 |
7 | return window.kf;
8 |
9 | } );
--------------------------------------------------------------------------------
/assets/theme/default/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/assets/theme/default/images/close.png
--------------------------------------------------------------------------------
/assets/theme/default/images/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/assets/theme/default/images/down.png
--------------------------------------------------------------------------------
/assets/theme/default/images/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarTurboS/Formula-Editor/HEAD/assets/theme/default/images/open.png
--------------------------------------------------------------------------------
/src/kity.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 数学公式Latex语法解析器
3 | */
4 |
5 | define( function () {
6 |
7 | return window.kity;
8 |
9 | } );
10 |
11 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/def/item-type.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 组元素类型定义
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | return {
8 | "BIG": 1,
9 | "SMALL": 2
10 | };
11 |
12 | } );
--------------------------------------------------------------------------------
/dev-lib/exports.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 启动代码
3 | */
4 |
5 | ( function ( global ) {
6 |
7 | // build环境中才含有use
8 | try {
9 | use( 'kf.start' );
10 | } catch ( e ) {
11 | }
12 |
13 | } )( this );
14 |
--------------------------------------------------------------------------------
/src/def/group-type.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 组类型
3 | */
4 |
5 | define( function () {
6 |
7 | return {
8 |
9 | "GROUP": "kf-editor-group",
10 | "VIRTUAL": "kf-editor-virtual-group"
11 |
12 | };
13 |
14 | } );
--------------------------------------------------------------------------------
/src/ui/ui-impl/def/box-type.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * box类型定义
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | return {
8 | // 分离式
9 | "DETACHED": 1,
10 | // 重叠式
11 | "OVERLAP": 2
12 | };
13 |
14 | } );
--------------------------------------------------------------------------------
/src/kf-ext/def.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-18.
3 | */
4 |
5 | define( function () {
6 |
7 | return {
8 | selectColor: 'rgba(42, 106, 189, 0.6)',
9 | allSelectColor: 'rgba(42, 106, 189, 0.6)'
10 | };
11 |
12 | } );
--------------------------------------------------------------------------------
/src/bundle.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-26 11:05:42
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-04-26 11:24:53
6 | * @description: 注册window中三方npm的服务
7 | */
8 | define(function () {
9 | return window.bundle;
10 | });
11 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/def/ele-type.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * toolbar元素类型定义
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | return {
8 | "DRAPDOWN_BOX": 1,
9 | "AREA": 2,
10 | "DELIMITER": 3,
11 | "KEYBOARD": 4
12 | };
13 |
14 | } );
--------------------------------------------------------------------------------
/src/base/component.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 组件抽象类,所有的组件都是该类的子类
3 | * @abstract
4 | */
5 |
6 | define( function ( require ) {
7 |
8 | var kity = require( "kity" );
9 |
10 | return kity.createClass( 'Component', {
11 |
12 | constructor: function () {}
13 |
14 | } );
15 |
16 | } );
--------------------------------------------------------------------------------
/src/base/utils.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 基础工具包
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var Utils = {},
8 | commonUtils = require( "base/common" );
9 |
10 | commonUtils.extend( Utils, commonUtils, require( "base/event/event" ) );
11 |
12 | return Utils;
13 |
14 | } );
15 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/ui.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-31.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | return {
8 |
9 | DrapdownBox: require( "ui/ui-impl/drapdown-box" ),
10 | Delimiter: require( "ui/ui-impl/delimiter" ),
11 | Area: require( "ui/ui-impl/area" ),
12 | Keyboard: require("ui/ui-impl/keyboard/keyboard")
13 | };
14 |
15 | } );
--------------------------------------------------------------------------------
/src/base/event/kfevent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-17.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | return {
8 |
9 | createEvent: function ( type, e ) {
10 |
11 | var evt = document.createEvent( 'Event' );
12 |
13 | evt.initEvent( type, true, true );
14 |
15 | return evt;
16 |
17 | }
18 |
19 | };
20 |
21 | } );
--------------------------------------------------------------------------------
/src/ui/def.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * UI定义
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | return {
8 | // 视窗状态
9 | VIEW_STATE: {
10 | // 内容未超出画布
11 | NO_OVERFLOW: 0,
12 | // 内容溢出
13 | OVERFLOW: 1
14 | },
15 | scrollbar: {
16 | step: 50,
17 | thumbMinSize: 50
18 | }
19 | };
20 |
21 | } );
22 |
--------------------------------------------------------------------------------
/src/parse/vgroup-def.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 虚拟组列表
3 | */
4 |
5 | define( function () {
6 |
7 | return {
8 |
9 | "radical": true,
10 | "fraction": true,
11 | "summation": true,
12 | "integration": true,
13 | "placeholder": true,
14 | "script": true,
15 | "superscript": true,
16 | "subscript": true,
17 | "brackets": true,
18 | "function": true
19 |
20 | };
21 |
22 | } );
--------------------------------------------------------------------------------
/src/sysconf.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 系统配置文件
3 | */
4 |
5 | define( function () {
6 |
7 | return {
8 |
9 | // 光标符号
10 | cursorCharacter: "\uF155",
11 |
12 | // 根占位符内容与颜色
13 | rootPlaceholder: {
14 | color: "#666",
15 | content: "在此处键入公式",
16 | fontsize: 16
17 | },
18 |
19 | scrollbar: {
20 | padding: 5,
21 | step: 150
22 | }
23 |
24 | };
25 |
26 | } );
--------------------------------------------------------------------------------
/config/conf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-7-28.
3 | */
4 |
5 | var KFE_UI_CONFIG = {
6 |
7 | clazz: 'Panel',
8 | className: 'kfe-editor',
9 | widgets: [
10 |
11 | {
12 | clazz: 'Panel',
13 | className: 'kfe-toolbar',
14 | widgets: [ {
15 |
16 | } ]
17 | },
18 |
19 | {
20 | clazz: 'Panel',
21 | className: 'kfe-edit-area'
22 | }
23 |
24 | ]
25 |
26 | };
--------------------------------------------------------------------------------
/dev-lib/npmEntry.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-26 11:07:17
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-08 16:37:17
6 | * @description: 将项目中用的npm模块从该处引入并挂载到window中
7 | */
8 | require('@babel/polyfill');
9 | var WebService = require('@sugarteam/eclass-web-service').default;
10 | var CustomWebService = require('@sugarteam/web-service').default;
11 |
12 | module.exports = window.bundle = {
13 | WebService: WebService,
14 | CustomWebService: CustomWebService,
15 | };
16 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/const.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-16 19:03:59
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-16 10:10:53
6 | */
7 | /*
8 | * @Author: Demian
9 | * @Date: 2020-04-15 10:11:11
10 | * @LastEditor: Demian
11 | * @LastEditTime: 2020-04-16 19:02:30
12 | */
13 |
14 | define(function (require) {
15 | const Constant = {
16 | Type: {
17 | Common: 'common',
18 | Algebra: 'algebra',
19 | Geometry: 'geometry',
20 | Unit: 'unit',
21 | Other: 'other',
22 | Letter: 'letter',
23 | },
24 | };
25 | return Constant;
26 | });
27 |
--------------------------------------------------------------------------------
/src/control/input-filter.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 输入过滤器
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | // 过滤列表, 其中的key对应于键盘事件的keycode, 带有s+字样的key,匹配的是shift+keycode
8 | var LIST = {
9 | 32: "\\,",
10 | "s+219": "\\{",
11 | "s+221": "\\}",
12 | "220": "\\backslash",
13 | "s+51": "\\#",
14 | "s+52": "\\$",
15 | "s+53": "\\%",
16 | "s+54": "\\^",
17 | "s+55": "\\&",
18 | "s+189": "\\_",
19 | "s+192": "\\~"
20 | };
21 |
22 | return {
23 |
24 | getReplaceString: function ( key ) {
25 | return LIST[ key ] || null;
26 | }
27 |
28 | };
29 |
30 | } );
--------------------------------------------------------------------------------
/src/control/controller.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-4-11.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | ListenerComponent = require( "control/listener" ),
10 |
11 | ControllerComponent = kity.createClass( 'ControllerComponent', {
12 |
13 | constructor: function ( kfEditor ) {
14 |
15 | this.kfEditor = kfEditor;
16 |
17 | this.components = {};
18 |
19 | this.initComponents();
20 |
21 | },
22 |
23 | initComponents: function () {
24 |
25 | this.components.listener = new ListenerComponent( this, this.kfEditor );
26 |
27 | }
28 |
29 |
30 | } );
31 |
32 | return ControllerComponent;
33 |
34 | } );
--------------------------------------------------------------------------------
/dev-lib/start.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 启动模块
3 | */
4 |
5 | define( 'kf.start', function ( require ) {
6 |
7 | var KFEditor = require( "editor/editor"),
8 | Factory = require( "editor/factory" );
9 |
10 | // 注册组件
11 | KFEditor.registerComponents( "ui", require( "ui/ui" ) );
12 | KFEditor.registerComponents( "parser", require( "parse/parser" ) );
13 | KFEditor.registerComponents( "render", require( "render/render" ) );
14 | KFEditor.registerComponents( "position", require( "position/position" ) );
15 | KFEditor.registerComponents( "syntax", require( "syntax/syntax" ) );
16 | KFEditor.registerComponents( "control", require( "control/controller" ) );
17 | KFEditor.registerComponents( "print", require( "print/printer" ) );
18 |
19 | kf.EditorFactory = Factory;
20 |
21 | } );
--------------------------------------------------------------------------------
/src/editor/Message.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-29 21:14:35
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-04-30 10:26:17
6 | */
7 | define(function (require) {
8 | const kity = require('kity');
9 | const Messager = kity.createClass('Messager', {
10 | constructor() {},
11 |
12 | getCheckServiceType: function () {
13 | return 'common.requestFunctions';
14 | },
15 |
16 | // 实现webService如何去接收消息
17 | onReceiveMessage: function (messageHandler) {
18 | document.addEventListener('documentMessage', (e) => {
19 | e.detail && e.detail.headers && messageHandler(e.detail);
20 | });
21 | },
22 |
23 | // 实现webService如何去发送消息
24 | sendAction: function ({ type, headers, data }) {
25 | const event = new CustomEvent('documentMessage', {
26 | detail: { type, headers, data },
27 | });
28 | document.dispatchEvent(event);
29 | },
30 | });
31 | return Messager;
32 | });
33 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "maxerr": 50,
3 | "boss": true,
4 | "bitwise": false,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": false,
8 | "forin": true,
9 | "strict": false,
10 | "freeze": true,
11 | "immed": true,
12 | "indent": 4,
13 | "latedef": false,
14 | "newcap": true,
15 | "noarg": true,
16 | "noempty": true,
17 | "nonbsp": true,
18 | "nonew": false,
19 | "plusplus": false,
20 | "undef": true,
21 | "trailing": true,
22 | "debug": false,
23 | "funcscope": true,
24 | "multistr": false,
25 | "proto": true,
26 | "smarttabs": false,
27 | "shadow": true,
28 | "scripturl": false,
29 | "laxcomma": false,
30 | "loopfunc": true,
31 | "onevar": false,
32 | "unused": "vars",
33 | "maxdepth": 5,
34 | "maxstatements": 40,
35 | // "maxlen": 80,
36 | "evil": false,
37 | "funcscope": false,
38 | "lastsemic": false,
39 | "laxbreak": false,
40 | "expr": true,
41 | "quotmark": false,
42 | "predef": [ "define", "unescape", "console" ],
43 | "browser": true
44 | }
--------------------------------------------------------------------------------
/src/control/listener.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-4-11.
3 | */
4 |
5 | define( function ( require, exports, module ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | // 光标定位
10 | LocationComponent = require( "control/location" ),
11 |
12 | // 输入控制组件
13 | InputComponent = require( "control/input" ),
14 |
15 | // 选区
16 | SelectionComponent = require( "control/selection" );
17 |
18 | return kity.createClass( "MoveComponent", {
19 |
20 | constructor: function ( parentComponent, kfEditor ) {
21 |
22 | this.parentComponent = parentComponent;
23 | this.kfEditor = kfEditor;
24 |
25 | this.components = {};
26 |
27 | this.initComponents();
28 |
29 | },
30 |
31 | initComponents: function () {
32 |
33 | this.components.location= new LocationComponent( this, this.kfEditor );
34 | this.components.selection = new SelectionComponent( this, this.kfEditor );
35 | this.components.input = new InputComponent( this, this.kfEditor );
36 |
37 | }
38 |
39 | } );
40 |
41 | } );
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 SugarTeam
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 |
--------------------------------------------------------------------------------
/dist/assets/styles/base.css:
--------------------------------------------------------------------------------
1 | .kf-editor {
2 | width: 100%;
3 | height: 100%;
4 | border: 1px solid #e0e0e0;
5 | overflow: hidden;
6 | z-index: 2;
7 | background-color: #eee;
8 | }
9 | .kf-loading {
10 | width: 864px;
11 | height: 580px;
12 | text-align: center;
13 | line-height: 580px;
14 | position: fixed;
15 | top: 0;
16 | left: 0;
17 | }
18 | .kf-editor-edit-area {
19 | position: relative;
20 | top: 0;
21 | left: 0;
22 | z-index: 1;
23 | height: 0;
24 | background-color: white;
25 | /*background-color: white;*/
26 | /*background-size: 21px 21px;*/
27 | /*background-position: 0 0,10px 10px;*/
28 | /*background-image: -webkit-linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef),-webkit-linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef);*/
29 | /*background-image: linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef),linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef);*/
30 | }
31 | .kf-editor-canvas-container {
32 | width: 100%;
33 | height: 100%;
34 | }
35 | .kf-editor-input-box {
36 | position: fixed;
37 | top: 0;
38 | left: -99999999px;
39 | z-index: 999999;
40 | }
41 |
--------------------------------------------------------------------------------
/dist/assets/styles/scrollbar.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * 滚动条
3 | **/
4 | .kf-editor-edit-scrollbar {
5 | width: 100%;
6 | height: 8px;
7 | position: absolute;
8 | left: 0;
9 | z-index: 994;
10 | }
11 | .kf-editor-ui-left-button {
12 | position: absolute;
13 | top: 0;
14 | left: 0;
15 | width: 5px;
16 | height: 100%;
17 | background: white;
18 | }
19 | .kf-editor-ui-right-button {
20 | position: absolute;
21 | top: 0;
22 | right: 0;
23 | width: 5px;
24 | height: 100%;
25 | background: white;
26 | }
27 | .kf-editor-ui-track {
28 | position: absolute;
29 | top: 0;
30 | left: 5px;
31 | width: 0;
32 | height: 100%;
33 | }
34 | .kf-editor-ui-thumb {
35 | position: absolute;
36 | top: 0;
37 | left: 0;
38 | width: 0;
39 | height: 100%;
40 | background: rgba(0, 0, 0, 0.15);
41 | border-radius: 6px;
42 | }
43 | .kf-editor-ui-thumb-left {
44 | width: 5px;
45 | height: 100%;
46 | position: absolute;
47 | top: 0;
48 | left: 0;
49 | z-index: 1;
50 | }
51 | .kf-editor-ui-thumb-right {
52 | width: 5px;
53 | height: 100%;
54 | position: absolute;
55 | top: 0;
56 | right: 0;
57 | }
58 | .kf-editor-ui-thumb-body {
59 | position: absolute;
60 | top: 0;
61 | left: 5px;
62 | width: 0;
63 | height: 100%;
64 | }
65 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/delimiter.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 分割符
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | PREFIX = "kf-editor-ui-",
10 |
11 | // UiUitls
12 | $$ = require( "ui/ui-impl/ui-utils" ),
13 |
14 | Delimiter = kity.createClass( "Delimiter", {
15 |
16 | constructor: function ( doc ) {
17 |
18 | this.doc = doc;
19 | this.element = this.createDilimiter();
20 |
21 | },
22 |
23 | setToolbar: function ( toolbar ) {
24 | // do nothing
25 | },
26 |
27 | createDilimiter: function () {
28 |
29 | var dilimiterNode = $$.ele( this.doc, "div", {
30 | className: PREFIX + "delimiter"
31 | } );
32 |
33 | dilimiterNode.appendChild( $$.ele( this.doc, "div", {
34 | className: PREFIX + "delimiter-line"
35 | } ) );
36 |
37 | return dilimiterNode;
38 |
39 | },
40 |
41 | attachTo: function ( container ) {
42 |
43 | container.appendChild( this.element );
44 |
45 | }
46 |
47 | });
48 |
49 | return Delimiter;
50 |
51 | } );
--------------------------------------------------------------------------------
/src/editor/factory.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 编辑器工厂方法
3 | * 用于创建编辑器
4 | */
5 |
6 | define( function ( require ) {
7 |
8 | var kity = require( "kity" ),
9 | KFEditor = require( "editor/editor" );
10 |
11 | /* ------------------------------- 编辑器装饰对象 */
12 | function EditorWrapper ( container, options ) {
13 |
14 | var _self = this;
15 | this._callbacks = [];
16 |
17 | this.editor = new KFEditor( container, options );
18 |
19 | this.editor.ready( function () {
20 | _self._trigger();
21 | } );
22 |
23 | }
24 |
25 | EditorWrapper.prototype._trigger = function () {
26 |
27 | var editor = this.editor;
28 |
29 | kity.Utils.each( this._callbacks, function ( cb ) {
30 | cb.call( editor, editor );
31 | } );
32 |
33 | };
34 |
35 | EditorWrapper.prototype.ready = function ( cb ) {
36 |
37 | if ( this.editor.isReady() ) {
38 | cb.call( this.editor, this.editor );
39 | } else {
40 | this._callbacks.push( cb );
41 | }
42 |
43 | };
44 |
45 | return {
46 | create: function ( container, options ) {
47 |
48 | return new EditorWrapper( container, options );
49 |
50 | }
51 | };
52 |
53 | } );
--------------------------------------------------------------------------------
/assets/styles/base.less:
--------------------------------------------------------------------------------
1 | .kf-editor {
2 | width: 100%;
3 | height: 100%;
4 | border: 1px solid #e0e0e0;
5 | overflow: hidden;
6 | z-index: 2;
7 | background-color: #eee;
8 | }
9 |
10 | .kf-loading {
11 | width: 864px;
12 | height: 580px;
13 | text-align: center;
14 | line-height: 580px;
15 | position: fixed;
16 | top: 0;
17 | left: 0;
18 | }
19 |
20 | .kf-editor-edit-area {
21 | position: relative;
22 | top: 0;
23 | left: 0;
24 |
25 | z-index: 1;
26 | height: 0;
27 | background-color: white;
28 | /*background-color: white;*/
29 | /*background-size: 21px 21px;*/
30 | /*background-position: 0 0,10px 10px;*/
31 | /*background-image: -webkit-linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef),-webkit-linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef);*/
32 | /*background-image: linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef),linear-gradient(45deg,#efefef 25%,transparent 25%,transparent 75%,#efefef 75%,#efefef);*/
33 | }
34 |
35 | .kf-editor-canvas-container {
36 | width: 100%;
37 | height: 100%;
38 | }
39 |
40 | .kf-editor-input-box {
41 | position: fixed;
42 | top: 0;
43 | left: -99999999px;
44 | z-index: 999999;
45 | }
46 |
--------------------------------------------------------------------------------
/assets/styles/scrollbar.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * 滚动条
3 | **/
4 |
5 | .kf-editor-edit-scrollbar {
6 | width: 100%;
7 | height: 8px;
8 | position: absolute;
9 | left: 0;
10 | z-index: 994;
11 | }
12 |
13 | .kf-editor-ui-left-button {
14 | position: absolute;
15 | top: 0;
16 | left: 0;
17 | width: 5px;
18 | height: 100%;
19 | background: white;
20 | }
21 |
22 | .kf-editor-ui-right-button {
23 | position: absolute;
24 | top: 0;
25 | right: 0;
26 | width: 5px;
27 | height: 100%;
28 | background: white;
29 | }
30 |
31 | .kf-editor-ui-track {
32 | position: absolute;
33 | top: 0;
34 | left: 5px;
35 | width: 0;
36 | height: 100%;
37 | }
38 |
39 | .kf-editor-ui-thumb {
40 | position: absolute;
41 | top: 0;
42 | left: 0;
43 | width: 0;
44 | height: 100%;
45 | background: rgba(0,0,0,0.15);
46 | border-radius: 6px;
47 | }
48 |
49 | .kf-editor-ui-thumb-left {
50 | width: 5px;
51 | height: 100%;
52 | position: absolute;
53 | top: 0;
54 | left: 0;
55 | z-index: 1;
56 | }
57 |
58 | .kf-editor-ui-thumb-right {
59 | width: 5px;
60 | height: 100%;
61 | position: absolute;
62 | top: 0;
63 | right: 0;
64 | }
65 |
66 | .kf-editor-ui-thumb-body {
67 | position: absolute;
68 | top: 0;
69 | left: 5px;
70 | width: 0;
71 | height: 100%;
72 | }
--------------------------------------------------------------------------------
/dev-lib/jhtmls.min.js:
--------------------------------------------------------------------------------
1 | var jhtmls="undefined"==typeof exports?jhtmls||{}:exports;void function(e){"use strict";function n(e){return String(e).replace(/["<>& ]/g,function(e){return"&"+u[e]+";"})}function t(e){var n=[];return n.push("with(this){"),n.push(e.replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/g,function(e){return['!#{unescape("',escape(e),'")}'].join("")}).replace(/[\r\n]+/g,"\n").replace(/^\n+|\s+$/gm,"").replace(/^([ \w\t_$]*([^&\^?|\n\w\/'"{}\[\]+\-():; \t=\.$_]|:\/\/).*$|^(?!\s*(else|do|try|finally|void|typeof\s[\w$_]*)\s*$)[^'":;{}()\n|=&\/^?]+$)\s?/gm,function(e){return e=e.replace(/&none;/g,"").replace(/["'\\]/g,"\\$&").replace(/\n/g,"\\n").replace(/(!?#)\{(.*?)\}|(!?\$)([a-z_]+\w*(?:\.[a-z_]+\w*)*)/g,function(e,n,t,r,u){if(r&&(n=r,t=u),!t)return"";t=t.replace(/\\n/g,"\n").replace(/\\([\\'"])/g,"$1");var o=/^[a-z$][\w+$]+$/i.test(t)&&!/^(true|false|NaN|null|this)$/.test(t);return["',",o?["typeof ",t,"==='undefined'?'':"].join(""):"","#"===n||"$"===n?"_encode_":"","(",t,"),'"].join("")}),e=["'",e,"'"].join("").replace(/^'',|,''$/g,""),e?["_output_.push(",e,");"].join(""):""})),n.push("}"),new Function("_output_","_encode_","helper","jhtmls",n.join(""))}function r(r,u,o){"function"==typeof r&&(r=String(r).replace(/^[^\{]*\{\s*\/\*!?[ \f\t\v]*\n?|[ \f\t\v]*\*\/[;|\s]*\}$/g,""));var i=t(r),s=function(t,r){r=r||e;var u=[];return i.call(t,u,n,r,e),u.join("")};return arguments.length<=1?s:s(u,o)}var u={'"':"quot","<":"lt",">":"gt","&":"amp"," ":"nbsp"};e.render=r}(jhtmls);
2 | //# sourceMappingURL=dist/jhtmls.min.js.map
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Welcome to formula-editor 👋
2 |
3 | > 基于百度 kityformula-editor 开发的公式编辑器,有 android 和 web 两种模式
4 |
5 | ## 安装依赖
6 |
7 | ```sh
8 | npm install
9 | npm install -g anywhere // 随启随用的静态服务器
10 | ```
11 |
12 | ## 使用
13 |
14 | ```sh
15 | grunt build
16 | anywhere -p {port} // 这里anywhere是为了开静态服务器,预览index.html
17 | ```
18 |
19 | 以 web 为例,url 中传入?device=pc&protocol=documentEvent,然后运行项目。点击键盘中的字符,即可生成对应 latex 公式,点击”确定“按钮导出公式,控制台可以通过以下代码拿到最终的 latex 值和相应的 base64 图片。
20 |
21 | ```js
22 | document.addEventListener('documentMessage', (e) => {
23 | const { type } = e?.detail;
24 | const msg = e?.detail?.data?.body;
25 | if (type !== 'common.setFormula') return;
26 | console.log('msg', msg.formula, msg.formulaSrc); // msg 123 data:image/png;......
27 | });
28 | ```
29 |
30 | 其他信令请参照下方的信令详情
31 |
32 | ## 信令
33 |
34 | 公式编辑器支持 webview, iframe, documentEvent3 种通信方式,详细信令参照:
35 | [信令详情](./src/editor/command.md)
36 |
37 | ## 特性
38 |
39 | 设备类型:device - pc/android
40 |
41 | ```sh
42 | {ip地址}:{port}?device=android
43 | ```
44 |
45 | 协议类型:protocol - iframe/webview/documentEvent
46 |
47 | ```sh
48 | {ip地址}:{port}?protocol=webview
49 | ```
50 |
51 | 设备宽度:width
52 |
53 | ```sh
54 | {ip地址}:{port}?width=1920
55 | ```
56 |
57 | ## 样式
58 |
59 | #### 安卓:
60 |
61 | ```sh
62 | {ip地址}:{port}?device=android&protocol=webview&width=1920
63 | ```
64 |
65 | 
66 |
67 | #### web:
68 |
69 | ```sh
70 | {ip地址}:{port}?device=pc&protocol=documentEvent&width=1920
71 | ```
72 |
73 | 
74 |
75 | 老铁,走过路过给个 ⭐️
76 |
77 | 点个 ⭐️,不迷路
78 |
--------------------------------------------------------------------------------
/src/kf-ext/extension.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 公式扩展接口
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kf = require( "kf" ),
8 | SELECT_COLOR = require( "kf-ext/def" ).selectColor,
9 | ALL_SELECT_COLOR = require( "kf-ext/def" ).allSelectColor;
10 |
11 | function ext ( parser ) {
12 |
13 | kf.PlaceholderExpression = require( "kf-ext/expression/placeholder" );
14 |
15 | kf.Expression.prototype.select = function () {
16 |
17 | this.box.fill( SELECT_COLOR );
18 |
19 | };
20 |
21 | kf.Expression.prototype.selectAll = function () {
22 | this.box.fill( ALL_SELECT_COLOR );
23 | };
24 |
25 | kf.Expression.prototype.unselect = function () {
26 |
27 | this.box.fill( "transparent" );
28 |
29 | };
30 |
31 | // 扩展解析和逆解析
32 | parser.getKFParser().expand( {
33 |
34 | parse: {
35 | "placeholder": {
36 | name: "placeholder",
37 | handler: function ( info ) {
38 |
39 | delete info.handler;
40 | info.operand = [];
41 |
42 | return info;
43 |
44 | },
45 | sign: false
46 | }
47 | },
48 |
49 | reverse: {
50 |
51 | "placeholder": function () {
52 |
53 | return "\\placeholder ";
54 |
55 | }
56 |
57 | }
58 |
59 | } );
60 |
61 | }
62 |
63 | return {
64 | ext: ext
65 | };
66 |
67 | } );
68 |
--------------------------------------------------------------------------------
/src/ui/header/header.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-22 09:53:01
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-06 14:37:39
6 | */
7 | /*
8 | * @Author: Demian
9 | * @Date: 2020-04-14 16:31:36
10 | * @LastEditor: Demian
11 | * @LastEditTime: 2020-04-21 17:25:16
12 | */
13 | define(function (require) {
14 | var kity = require('kity'),
15 | $$ = require('ui/ui-impl/ui-utils'),
16 | Header = kity.createClass('Header', {
17 | constructor: function (uiComponent, kfEditor) {
18 | this.prefix = 'kf-editor-header-container';
19 | this.kfEditor = kfEditor;
20 | this.uiComponent = uiComponent;
21 | this.initKeyboardElements();
22 | this.initEvent();
23 | },
24 |
25 | initEvent: function () {
26 | $$.delegate(this.uiComponent.header, '.' + this.prefix + '-close', 'click', () => {
27 | this.kfEditor.eclassWebService.send({
28 | type: 'common.closeModal',
29 | });
30 | });
31 | },
32 |
33 | initKeyboardElements: function () {
34 | var doc = this.uiComponent.header.ownerDocument;
35 |
36 | var ele = this.createHeader(doc, this.kfEditor);
37 | this.uiComponent.header.appendChild(ele);
38 | },
39 | createHeader: function (doc, kfEditor) {
40 | return $$.ele(doc, 'div', {
41 | className: this.prefix,
42 | content: `
43 | 插入公式
44 |
45 | `,
46 | });
47 | },
48 | });
49 |
50 | return Header;
51 | });
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sugarteam/formula-editor",
3 | "title": "Formula Editor",
4 | "description": "基于百度kityformula-editor的公式编辑器",
5 | "version": "1.0.7-test-1",
6 | "homepage": "https://github.com/SugarTurboS/Formula-Editor",
7 | "author": {
8 | "name": "sugarteam"
9 | },
10 | "keywords": [
11 | "formula",
12 | "svg",
13 | "library"
14 | ],
15 | "licenses": [
16 | {
17 | "type": "MIT",
18 | "url": "https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt"
19 | }
20 | ],
21 | "repository": {
22 | "type": "git",
23 | "url": "git@github.com:SugarTurboS/Formula-Editor.git"
24 | },
25 | "dependencies": {
26 | "@babel/polyfill": "^7.8.7",
27 | "@sugarteam/eclass-web-service": "^1.0.5",
28 | "@sugarteam/web-service": "^1.0.5",
29 | "@types/chai": "^4.2.11"
30 | },
31 | "devDependencies": {
32 | "@babel/core": "^7.9.0",
33 | "@babel/plugin-transform-typeof-symbol": "^7.8.4",
34 | "@babel/preset-env": "^7.9.5",
35 | "babel-plugin-transform-remove-strict-mode": "^0.0.2",
36 | "babel-preset-es2015": "^6.24.1",
37 | "grunt": "~0.4.1",
38 | "grunt-babel": "^8.0.0",
39 | "grunt-browserify": "^5.3.0",
40 | "grunt-contrib-clean": "~0.5.0",
41 | "grunt-contrib-concat": "~0.4.0",
42 | "grunt-contrib-copy": "^1.0.0",
43 | "grunt-contrib-cssmin": "^3.0.0",
44 | "grunt-contrib-jshint": "^2.1.0",
45 | "grunt-contrib-less": "^2.0.0",
46 | "grunt-contrib-uglify": "~0.2.6",
47 | "grunt-module-dependence": "~0.1.1"
48 | },
49 | "files": [
50 | "dist"
51 | ],
52 | "publishConfig": {
53 | "registry": "https://registry.npmjs.com/"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/kf-ext/expression/placeholder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 占位符表达式, 扩展KF自有的Empty表达式
3 | */
4 |
5 |
6 | define( function ( require, exports, module ) {
7 |
8 | var kity = require( "kity" ) ,
9 |
10 | kf = require( "kf" ),
11 |
12 | PlaceholderOperator = require( "kf-ext/operator/placeholder" );
13 |
14 | return kity.createClass( 'PlaceholderExpression', {
15 |
16 | base: kf.CompoundExpression,
17 |
18 | constructor: function () {
19 |
20 | this.callBase();
21 |
22 | this.setFlag( "Placeholder" );
23 |
24 | this.label = null;
25 |
26 | this.box.setAttr( "data-type", null );
27 | this.setOperator( new PlaceholderOperator() );
28 |
29 | },
30 |
31 | setLabel: function ( label ) {
32 | this.label = label;
33 | },
34 |
35 | getLabel: function () {
36 | return this.label;
37 | },
38 |
39 | // 重载占位符的setAttr, 以处理根占位符节点
40 | setAttr: function ( key, val ) {
41 |
42 | if ( key === "label" ) {
43 | this.setLabel( val );
44 | } else {
45 |
46 | if ( key.label ) {
47 | this.setLabel( key.label );
48 | // 删除label
49 | delete key.label;
50 | }
51 | // 继续设置其他属性
52 | this.callBase( key, val );
53 |
54 | }
55 |
56 | },
57 |
58 | select: function () {
59 |
60 | this.getOperator().select();
61 |
62 | },
63 |
64 | selectAll: function () {
65 |
66 | this.getOperator().selectAll();
67 |
68 | },
69 |
70 | unselect: function () {
71 | this.getOperator().unselect();
72 | }
73 |
74 | } );
75 |
76 | } );
--------------------------------------------------------------------------------
/src/ui/control/zoom.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 滚动缩放控制器
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var Utils = require( "base/utils" ),
8 | kity = require( "kity"),
9 |
10 | DEFAULT_OPTIONS = {
11 | min: 1,
12 | max: 2
13 | },
14 |
15 | ScrollZoomController = kity.createClass( 'ScrollZoomController', {
16 |
17 | constructor: function ( parentComponent, kfEditor, target, options ) {
18 |
19 | this.kfEditor = kfEditor;
20 | this.target = target;
21 |
22 | this.zoom = 1;
23 | this.step = 0.05;
24 |
25 | this.options = Utils.extend( {}, DEFAULT_OPTIONS, options );
26 |
27 | this.initEvent();
28 |
29 | },
30 |
31 | initEvent: function () {
32 |
33 | var kfEditor = this.kfEditor,
34 | _self = this,
35 | min = this.options.min,
36 | max = this.options.max,
37 | step = this.step;
38 |
39 | Utils.addEvent( this.target, 'mousewheel', function ( e ) {
40 |
41 | e.preventDefault();
42 |
43 | if ( e.wheelDelta < 0 ) {
44 | // 缩小
45 | _self.zoom -= _self.zoom * step;
46 | } else {
47 | // 放大
48 | _self.zoom += _self.zoom * step;
49 | }
50 |
51 | _self.zoom = Math.max( _self.zoom, min );
52 | _self.zoom = Math.min( _self.zoom, max );
53 |
54 | kfEditor.requestService( "render.set.canvas.zoom", _self.zoom );
55 |
56 | } );
57 |
58 | }
59 |
60 | } );
61 |
62 |
63 | return ScrollZoomController;
64 |
65 | } );
66 |
67 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/footer/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-20 11:00:08
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-04-20 11:37:55
6 | */
7 | /*
8 | * @Author: Demian
9 | * @Date: 2020-04-16 20:03:47
10 | * @LastEditor: Demian
11 | * @LastEditTime: 2020-04-20 10:03:23
12 | */
13 | define(function (require) {
14 | const kity = require('kity');
15 | const $$ = require('ui/ui-impl/ui-utils');
16 | const Footer = kity.createClass('Footer', {
17 | constructor(parentNode, parentProps) {
18 | this.parentNode = parentNode;
19 | this.props = parentProps;
20 | this.prefix = parentProps.prefix + 'keyboard-footer';
21 |
22 | this.state = {};
23 |
24 | this.containerClassName = this.prefix;
25 | this.itemClassName = `${this.prefix}-button`;
26 | this.cancelClassName = `${this.itemClassName}-cancel`;
27 | this.submitClassName = `${this.itemClassName}-submit`;
28 | this._onSubmit = this._onSubmit.bind(this);
29 | this._onCancel = this._onCancel.bind(this);
30 | },
31 | _render: function () {
32 | return $$.ele(this.props.doc, 'div', {
33 | className: this.containerClassName,
34 | content: `
35 |
36 | 取消
37 |
38 |
39 | 确定
40 |
41 | `,
42 | });
43 | },
44 | mount: function () {
45 | const node = this._render();
46 | $$.delegate(this.parentNode, `#${this.cancelClassName}`, 'click', this._onCancel);
47 | $$.delegate(this.parentNode, `#${this.submitClassName}`, 'click', this._onSubmit);
48 | this.parentNode.appendChild(node);
49 | },
50 | _onCancel: function (e) {
51 | console.log('cancel');
52 | this.props.onCancel();
53 | },
54 | _onSubmit: function (e) {
55 | console.log('submit');
56 | this.props.onSubmit();
57 | },
58 | });
59 | return Footer;
60 | });
61 |
--------------------------------------------------------------------------------
/src/base/event/event.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * event模块
3 | */
4 |
5 | /* jshint camelcase: false */
6 |
7 | define( function ( require, exports, modules ) {
8 |
9 | var EVENT_LISTENER = {},
10 | eid = 0,
11 | BEFORE_RESULT = true,
12 | KFEvent = require( "base/event/kfevent" ),
13 | commonUtils = require( "base/common" ),
14 | EVENT_HANDLER = function ( e ) {
15 |
16 | var type = e.type,
17 | target = e.target,
18 | eid = this.__kfe_eid,
19 | hasAutoTrigger = /^(?:before|after)/.test( type ),
20 | HANDLER_LIST = EVENT_LISTENER[ eid ][ type ];
21 |
22 | if ( !hasAutoTrigger ) {
23 |
24 | EventListener.trigger( target, 'before' + type );
25 |
26 | if ( BEFORE_RESULT === false ) {
27 | BEFORE_RESULT = true;
28 | return false;
29 | }
30 |
31 | }
32 |
33 | commonUtils.each( HANDLER_LIST, function ( handler, index ) {
34 |
35 | if ( !handler ) {
36 | return;
37 | }
38 |
39 | if ( handler.call( target, e ) === false ) {
40 | BEFORE_RESULT = false;
41 | return BEFORE_RESULT;
42 | }
43 |
44 | } );
45 |
46 | if ( !hasAutoTrigger ) {
47 |
48 | EventListener.trigger( target, 'after' + type );
49 |
50 | }
51 |
52 | };
53 |
54 | var EventListener = {
55 |
56 | addEvent: function ( target, type, handler ) {
57 |
58 | var hasHandler = true,
59 | eventCache = null;
60 |
61 | if ( !target.__kfe_eid ) {
62 | hasHandler = false;
63 | target.__kfe_eid = generateId();
64 | EVENT_LISTENER[ target.__kfe_eid ] = {};
65 | }
66 |
67 | eventCache = EVENT_LISTENER[ target.__kfe_eid ];
68 |
69 | if ( !eventCache[ type ] ) {
70 | hasHandler = false;
71 | eventCache[ type ] = [];
72 | }
73 |
74 | eventCache[ type ].push( handler );
75 |
76 | if ( hasHandler ) {
77 | return;
78 | }
79 |
80 | target.addEventListener( type, EVENT_HANDLER, false );
81 |
82 | },
83 |
84 | trigger: function ( target, type, e ) {
85 |
86 | e = e || KFEvent.createEvent( type, e );
87 |
88 | target.dispatchEvent( e );
89 |
90 | }
91 |
92 | };
93 |
94 | function generateId () {
95 |
96 | return ++eid;
97 |
98 | }
99 |
100 | return EventListener;
101 |
102 | } );
--------------------------------------------------------------------------------
/src/ui/ui-impl/drapdown-box.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-31.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | // UiUitls
10 | $$ = require( "ui/ui-impl/ui-utils" ),
11 |
12 | Button = require( "ui/ui-impl/button" ),
13 | Box = require( "ui/ui-impl/box" ),
14 |
15 | DrapdownBox = kity.createClass( "DrapdownBox", {
16 |
17 | constructor: function ( doc, options ) {
18 |
19 | this.options = options;
20 | this.toolbar = null;
21 | this.doc = doc;
22 |
23 | this.buttonElement = this.createButton();
24 |
25 | this.element = this.buttonElement.getNode();
26 |
27 | this.boxElement = this.createBox();
28 |
29 | this.buttonElement.mount( this.boxElement );
30 |
31 | this.initEvent();
32 |
33 | },
34 |
35 | initEvent: function () {
36 |
37 | var _self = this;
38 |
39 | // 通知工具栏互斥
40 | $$.on( this.element, "mousedown", function ( e ) {
41 |
42 | e.preventDefault();
43 | e.stopPropagation();
44 |
45 | _self.toolbar.notify( "closeOther", _self );
46 |
47 | } );
48 |
49 |
50 | this.buttonElement.initEvent();
51 | this.boxElement.initEvent();
52 |
53 | this.boxElement.setSelectHandler( function ( val ) {
54 | // 发布
55 | $$.publish( "data.select", val );
56 | _self.buttonElement.hide();
57 | } );
58 |
59 | },
60 |
61 | disable: function () {
62 | this.buttonElement.disable();
63 | },
64 |
65 | enable: function () {
66 | this.buttonElement.enable();
67 | },
68 |
69 | setToolbar: function ( toolbar ) {
70 | this.toolbar = toolbar;
71 | this.buttonElement.setToolbar( toolbar );
72 | this.boxElement.setToolbar( toolbar );
73 | },
74 |
75 | createButton: function () {
76 |
77 | return new Button( this.doc, this.options.button );
78 |
79 | },
80 |
81 | show: function () {
82 | this.buttonElement.show();
83 | },
84 |
85 | hide: function () {
86 | this.buttonElement.hide();
87 | },
88 |
89 | createBox: function () {
90 |
91 | return new Box( this.doc, this.options.box );
92 |
93 | },
94 |
95 | attachTo: function ( container ) {
96 |
97 | container.appendChild( this.element );
98 |
99 | }
100 |
101 | });
102 |
103 | return DrapdownBox;
104 |
105 | } );
--------------------------------------------------------------------------------
/src/print/printer.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 打印服务
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" );
8 |
9 | return kity.createClass( "Printer", {
10 |
11 | constructor: function ( kfEditor ) {
12 |
13 | this.kfEditor = kfEditor;
14 |
15 | this.initServices();
16 |
17 | this.initCommands();
18 |
19 | },
20 |
21 | initServices: function () {
22 |
23 | this.kfEditor.registerService( "print.image", this, {
24 | printImage: this.printImage
25 | } );
26 |
27 | },
28 |
29 | initCommands: function () {
30 |
31 | this.kfEditor.registerCommand( "get.image.data", this, this.getImageData );
32 |
33 | },
34 |
35 | printImage: function ( type ) {
36 |
37 | var formula = this.kfEditor.requestService( "render.get.paper" );
38 |
39 | this._formatCanvas();
40 |
41 | formula.toPNG( function ( dataUrl ) {
42 |
43 | document.body.innerHTML = '
';
44 |
45 | } );
46 |
47 | this._restoreCanvas();
48 |
49 | },
50 |
51 | getImageData: function ( cb ) {
52 |
53 | var canvas = this.kfEditor.requestService( "render.get.canvas" ),
54 | formula = this.kfEditor.requestService( "render.get.paper" );
55 |
56 | this._formatCanvas();
57 |
58 | formula.toPNG( function ( dataUrl ) {
59 |
60 | cb( {
61 | width: canvas.width,
62 | height: canvas.height,
63 | img: dataUrl
64 | } );
65 |
66 | } );
67 |
68 | this._restoreCanvas();
69 |
70 | },
71 |
72 | _formatCanvas: function () {
73 |
74 | var canvas = this.kfEditor.requestService( "render.get.canvas" ),
75 | rect = canvas.container.getRenderBox();
76 |
77 | canvas.node.setAttribute( "width", rect.width );
78 | canvas.node.setAttribute( "height", rect.height );
79 |
80 | this.kfEditor.requestService( "render.clear.canvas.transform" );
81 | this.kfEditor.requestService( "control.cursor.hide" );
82 | this.kfEditor.requestService( "render.clear.select" );
83 |
84 | },
85 |
86 | _restoreCanvas: function () {
87 |
88 | var canvas = this.kfEditor.requestService( "render.get.canvas" );
89 |
90 | canvas.node.setAttribute( "width", "100%" );
91 | canvas.node.setAttribute( "height", "100%" );
92 |
93 | this.kfEditor.requestService( "render.revert.canvas.transform" );
94 | this.kfEditor.requestService( "control.cursor.relocation" );
95 | this.kfEditor.requestService( "render.reselect" );
96 |
97 | }
98 |
99 | } );
100 |
101 | } );
102 |
103 |
104 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ###############
2 | # node #
3 | ###############
4 | node_modules
5 | npm-debug.log
6 |
7 | ###############
8 | # folder #
9 | ###############
10 | .idea
11 | log
12 | upload
13 |
14 |
15 | ###############
16 | # fixed file #
17 | ###############
18 | *.pptx
19 | *.doc
20 | *.docx
21 | *.xml
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | #################
33 | ## Eclipse
34 | #################
35 |
36 | *.pydevproject
37 | .project
38 | .metadata
39 | bin/
40 | tmp/
41 | *.tmp
42 | *.bak
43 | *.swp
44 | *~.nib
45 | local.properties
46 | .classpath
47 | .settings/
48 | .loadpath
49 |
50 | # External tool builders
51 | .externalToolBuilders/
52 |
53 | # Locally stored "Eclipse launch configurations"
54 | *.launch
55 |
56 | # CDT-specific
57 | .cproject
58 |
59 | # PDT-specific
60 | .buildpath
61 |
62 |
63 | #################
64 | ## Visual Studio
65 | #################
66 |
67 | ## Ignore Visual Studio temporary files, build results, and
68 | ## files generated by popular Visual Studio add-ons.
69 |
70 | # User-specific files
71 | *.suo
72 | *.user
73 | *.sln.docstates
74 |
75 | # Build results
76 | [Dd]ebug/
77 | [Rr]elease/
78 | *_i.c
79 | *_p.c
80 | *.ilk
81 | *.meta
82 | *.obj
83 | *.pch
84 | *.pdb
85 | *.pgc
86 | *.pgd
87 | *.rsp
88 | *.sbr
89 | *.tlb
90 | *.tli
91 | *.tlh
92 | *.tmp
93 | *.vspscc
94 | .builds
95 | *.dotCover
96 |
97 | ## TODO: If you have NuGet Package Restore enabled, uncomment this
98 | #packages/
99 |
100 | # Visual C++ cache files
101 | ipch/
102 | *.aps
103 | *.ncb
104 | *.opensdf
105 | *.sdf
106 |
107 | # Visual Studio profiler
108 | *.psess
109 | *.vsp
110 |
111 | # ReSharper is a .NET coding add-in
112 | _ReSharper*
113 |
114 | # Installshield output folder
115 | [Ee]xpress
116 |
117 | # DocProject is a documentation generator add-in
118 | DocProject/buildhelp/
119 | DocProject/Help/*.HxT
120 | DocProject/Help/*.HxC
121 | DocProject/Help/*.hhc
122 | DocProject/Help/*.hhk
123 | DocProject/Help/*.hhp
124 | DocProject/Help/Html2
125 | DocProject/Help/html
126 |
127 | # Click-Once directory
128 | publish
129 |
130 | # Others
131 | [Bb]in
132 | [Oo]bj
133 | sql
134 | TestResults
135 | *.Cache
136 | ClientBin
137 | stylecop.*
138 | ~$*
139 | *.dbmdl
140 | Generated_Code #added for RIA/Silverlight projects
141 |
142 | # Backup & report files from converting an old project file to a newer
143 | # Visual Studio version. Backup files are not needed, because we have git ;-)
144 | _UpgradeReport_Files/
145 | Backup*/
146 | UpgradeLog*.XML
147 |
148 |
149 |
150 | ############
151 | ## Windows
152 | ############
153 |
154 | # Windows image file caches
155 | Thumbs.db
156 |
157 | # Folder config file
158 | Desktop.ini
159 |
160 |
161 | #############
162 | ## Python
163 | #############
164 |
165 | *.py[co]
166 |
167 | # Packages
168 | *.egg
169 | *.egg-info
170 | eggs
171 | parts
172 | bin
173 | var
174 | sdist
175 | develop-eggs
176 | .installed.cfg
177 |
178 | # Installer logs
179 | pip-log.txt
180 |
181 | # Unit test / coverage reports
182 | .coverage
183 | .tox
184 |
185 | #Translations
186 | *.mo
187 |
188 | #Mr Developer
189 | .mr.developer.cfg
190 |
191 | # Mac crap
192 | .DS_Store
193 |
194 | .npmrc
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/menu/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-16 16:11:27
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-18 14:20:00
6 | */
7 | define(function (require) {
8 | const kity = require('kity');
9 | const $$ = require('ui/ui-impl/ui-utils');
10 | const Constant = require('ui/ui-impl/keyboard/const');
11 | const Menu = kity.createClass('Menu', {
12 | constructor(parentNode, parentProps) {
13 | this.parentNode = parentNode;
14 | this.props = parentProps;
15 | this.prefix = parentProps.prefix + 'keyboard-menu';
16 | this.elementList = [
17 | { type: Constant.Type.Common, title: '常用', index: 0 },
18 | { type: Constant.Type.Algebra, title: '代数', index: 1 },
19 | { type: Constant.Type.Geometry, title: '几何', index: 2 },
20 | { type: Constant.Type.Letter, title: '字母', index: 3 },
21 | { type: Constant.Type.Other, title: '其他', index: 4 },
22 | ];
23 |
24 | this.state = {
25 | type: Constant.Type.Common,
26 | };
27 |
28 | this.containerClassName = this.prefix;
29 | this.listClassName = `${this.prefix}-list`;
30 | this.itemClassName = `${this.prefix}-list-item`;
31 | this._onClick = this._onClick.bind(this);
32 | this._initCommand.call(this);
33 | },
34 | _render: function () {
35 | return $$.ele(this.props.doc, 'div', {
36 | className: this.containerClassName,
37 | content: `
38 |
39 | ${this.elementList
40 | .map(
41 | (x) =>
42 | `- ${x.title}
`
45 | )
46 | .join('')}
47 |
48 | `,
49 | });
50 | function isActive(type) {
51 | return type === this.state.type;
52 | }
53 | },
54 | mount: function () {
55 | const node = this._render();
56 | $$.delegate(this.parentNode, '.' + this.itemClassName, 'click', this._onClick);
57 | this.parentNode.appendChild(node);
58 | },
59 | destroy: function () {
60 | $(this.parentNode).find(this.prefix).remove();
61 | },
62 | update: function (nextProps) {
63 | if (!this._shouldUpdate(nextProps)) return;
64 | Object.keys(nextProps)
65 | .filter((x) => x in this.props)
66 | .forEach((x) =>
67 | this._setState({
68 | [x]: nextProps[x],
69 | })
70 | );
71 | const node = this._render();
72 | $('.' + this.prefix).html(node);
73 | },
74 | _shouldUpdate: function (nextProps) {
75 | const isSame = Object.keys(this.state).every((x) => nextProps[x] === this.state[x]);
76 | if (isSame) {
77 | return false;
78 | }
79 | return true;
80 | },
81 | _initCommand: function () {
82 | this.props.kfEditor.registerCommand('menu.clearType', this, function () {
83 | this.props.onClick(Constant.Type.Common);
84 | });
85 | },
86 | _onClick: function (e) {
87 | const val = e.target.dataset.value;
88 | this.props.onClick(val);
89 | },
90 | _setState: function (nextState) {
91 | this.state = {
92 | ...this.state,
93 | ...nextState,
94 | };
95 | },
96 | });
97 | return Menu;
98 | });
99 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/ui-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-4-1.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var $ = require( "jquery" ),
8 | kity = require( "kity" ),
9 | TOPIC_POOL = {};
10 |
11 | var Utils = {
12 |
13 | ele: function ( doc, name, options ) {
14 |
15 | var node = null;
16 |
17 | if ( name === "text" ) {
18 | return doc.createTextNode( options );
19 | }
20 |
21 | node = doc.createElement( name );
22 | options.className && ( node.className = options.className );
23 |
24 | if ( options.content ) {
25 | node.innerHTML = options.content;
26 | }
27 | return node;
28 | },
29 |
30 | getRectBox: function ( node ) {
31 | return node.getBoundingClientRect();
32 | },
33 |
34 | on: function ( target, type, fn ) {
35 | $( target ).on( type, fn );
36 | return this;
37 | },
38 |
39 | delegate: function ( target, selector, type, fn ) {
40 |
41 | $( target ).delegate( selector, type, fn );
42 | return this;
43 |
44 | },
45 |
46 | publish: function ( topic, args ) {
47 |
48 | var callbackList = TOPIC_POOL[ topic ];
49 |
50 | if ( !callbackList ) {
51 | return;
52 | }
53 |
54 | args = [].slice.call( arguments, 1 );
55 |
56 | kity.Utils.each( callbackList, function ( callback ) {
57 |
58 | callback.apply( null, args );
59 |
60 | } );
61 |
62 | },
63 |
64 | subscribe: function ( topic, callback ) {
65 |
66 | if ( !TOPIC_POOL[ topic ] ) {
67 |
68 | TOPIC_POOL[ topic ] = [];
69 |
70 | }
71 |
72 | TOPIC_POOL[ topic ].push( callback );
73 |
74 | },
75 |
76 | getClassList: function ( node ) {
77 |
78 | return node.classList || new ClassList( node );
79 |
80 | }
81 |
82 | };
83 |
84 |
85 | //注意: 仅保证兼容IE9以上
86 | function ClassList ( node ) {
87 |
88 | this.node = node;
89 | this.classes = node.className.replace( /^\s+|\s+$/g, '' ).split( /\s+/ );
90 |
91 | }
92 |
93 | ClassList.prototype = {
94 |
95 | constructor: ClassList,
96 |
97 | contains: function ( className ) {
98 |
99 | return this.classes.indexOf( className ) !== -1;
100 |
101 | },
102 |
103 | add: function ( className ) {
104 |
105 | if ( this.classes.indexOf( className ) == -1 ) {
106 | this.classes.push( className );
107 | }
108 |
109 | this._update();
110 |
111 | return this;
112 |
113 | },
114 |
115 | remove: function ( className ) {
116 |
117 | var index = this.classes.indexOf( className );
118 |
119 | if ( index !== -1 ) {
120 | this.classes.splice( index, 1 );
121 | this._update();
122 | }
123 |
124 | return this;
125 | },
126 |
127 | toggle: function ( className ) {
128 |
129 | var method = this.contains( className ) ? 'remove' : 'add';
130 |
131 | return this[ method ]( className );
132 |
133 | },
134 |
135 | _update: function () {
136 |
137 | this.node.className = this.classes.join( " " );
138 |
139 | }
140 |
141 | };
142 |
143 | return Utils;
144 |
145 | } );
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/page/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-16 20:03:47
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-04-26 18:06:01
6 | */
7 | define(function (require) {
8 | const kity = require('kity');
9 | const $$ = require('ui/ui-impl/ui-utils');
10 | const Page = kity.createClass('Page', {
11 | constructor(parentNode, parentProps) {
12 | this.parentNode = parentNode;
13 | this.props = parentProps;
14 | this.prefix = parentProps.prefix + 'keyboard-page';
15 | this.elementList = [
16 | { type: 'prev', title: '上一页', index: 0 },
17 | { type: 'next', title: '下一页', index: 1 },
18 | ];
19 |
20 | this.state = {
21 | type: this.props.type,
22 | page: this.props.page,
23 | totalPage: this.props.totalPage,
24 | };
25 |
26 | this.containerClassName = this.prefix;
27 | this.listClassName = `${this.prefix}-list`;
28 | this.itemClassName = `${this.prefix}-list-item`;
29 | this._onClick = this._onClick.bind(this);
30 | },
31 | _render: function () {
32 | return $$.ele(this.props.doc, 'div', {
33 | className: this.containerClassName,
34 | content: `
35 |
36 |
39 | ${this.elementList
40 | .map(
41 | (x) =>
42 | ``
45 | )
46 | .join('')}
47 | - 完成
50 |
51 | `,
52 | });
53 | function isDisabled(type) {
54 | if (type === 'prev') {
55 | return this.state.page === 0 && this.state.type === 'common';
56 | } else if (type === 'next') {
57 | return this.state.page === this.state.totalPage - 1 && this.state.type === 'other';
58 | }
59 | }
60 | },
61 | mount: function () {
62 | const node = this._render();
63 | $$.delegate(this.parentNode, '.' + this.itemClassName, 'click', this._onClick);
64 | this.parentNode.appendChild(node);
65 | },
66 | update: function (nextProps) {
67 | if (!this._shouldUpdate(nextProps)) return;
68 | Object.keys(nextProps)
69 | .filter((x) => x in this.state)
70 | .forEach((x) =>
71 | this._setState({
72 | [x]: nextProps[x],
73 | })
74 | );
75 | const node = this._render();
76 | $('.' + this.prefix).html(node);
77 | },
78 | _shouldUpdate: function (nextProps) {
79 | const isSame = Object.keys(this.state).every((x) => nextProps[x] === this.state[x]);
80 | if (isSame) {
81 | return false;
82 | }
83 | return true;
84 | },
85 | _onClick: function (e) {
86 | const val = e.target.dataset.value;
87 | switch (val) {
88 | case 'next':
89 | this.props.onNextPage();
90 | break;
91 | case 'prev':
92 | this.props.onPrevPage();
93 | break;
94 | case 'delete':
95 | this.props.onDelete();
96 | break;
97 | case 'submit':
98 | this.props.onSubmit();
99 | break;
100 | }
101 | },
102 | _setState: function (nextState) {
103 | this.state = {
104 | ...this.state,
105 | ...nextState,
106 | };
107 | },
108 | });
109 | return Page;
110 | });
111 |
--------------------------------------------------------------------------------
/src/kf-ext/operator/placeholder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 占位符操作符
3 | */
4 |
5 | define( function ( require, exports, modules ) {
6 |
7 | var kity = require( "kity" ),
8 | // FILL_COLOR = require( "sysconf" ).rootPlaceholder.color,
9 | SELECT_COLOR = require( "kf-ext/def" ).selectColor,
10 | ALL_SELECT_COLOR = require( "kf-ext/def" ).allSelectColor;
11 |
12 | return kity.createClass( 'PlaceholderOperator', {
13 |
14 | base: require( "kf" ).Operator,
15 |
16 | constructor: function () {
17 |
18 | this.opShape = null;
19 | this.callBase( "Placeholder" );
20 |
21 | },
22 |
23 | applyOperand: function () {
24 |
25 | this.opShape = generateOpShape( this, this.parentExpression.getLabel() );
26 | this.parentExpression.expand( 20, 20 );
27 | this.parentExpression.translateElement( 10, 10 );
28 |
29 | },
30 |
31 | select: function () {
32 |
33 | // 默认placeholder改为光标后, select要置为黑色
34 | var isPlaceholder = !!this.parentExpression.getLabel();
35 | this.opShape.fill( isPlaceholder ? '#000' : SELECT_COLOR );
36 |
37 | },
38 |
39 | selectAll: function () {
40 |
41 | this.opShape.fill( ALL_SELECT_COLOR );
42 |
43 | },
44 |
45 | unselect: function () {
46 |
47 | this.opShape.fill( "transparent" );
48 |
49 | }
50 |
51 | } );
52 |
53 | function generateOpShape ( operator, label ) {
54 |
55 | if ( label !== null ) {
56 | return createCursor( operator, label );
57 | } else {
58 | return createCommonShape( operator );
59 | }
60 |
61 |
62 | }
63 |
64 | // 创建通用图形
65 | function createCommonShape ( operator ) {
66 |
67 | var w = 35,
68 | h = 50,
69 | shape = null;
70 |
71 | shape = new kity.Rect( w, h, 0, 0 ).stroke( "black" ).fill( "transparent" );
72 | shape.setAttr( "stroke-dasharray", "5, 5" );
73 |
74 | operator.addOperatorShape( shape );
75 |
76 | return shape;
77 |
78 | }
79 |
80 | function createCursor ( operator, label) {
81 |
82 | var cursorShape = new kity.Rect( 1, 50, 0, 0 ).fill( "#000" );
83 |
84 | cursorShape.setAttr( "style", "display: block" );
85 |
86 | operator.addOperatorShape( cursorShape );
87 |
88 | return cursorShape;
89 |
90 | }
91 |
92 | // 创建根占位符图形
93 | // function createRootPlaceholder ( operator, label ) {
94 |
95 | // var textShape = new kity.Text( label ).fill( FILL_COLOR ),
96 | // shapeGroup = new kity.Group(),
97 | // padding = 20,
98 | // radius = 7,
99 | // borderBoxShape = new kity.Rect( 0, 0, 0, 0, radius ).stroke( FILL_COLOR ).fill( "transparent" ),
100 | // textBox = null;
101 |
102 | // textShape.setFontSize( 40 );
103 | // shapeGroup.addShape( borderBoxShape );
104 | // shapeGroup.addShape( textShape );
105 | // operator.addOperatorShape( shapeGroup );
106 |
107 | // textBox = textShape.getFixRenderBox();
108 |
109 | // // 宽度要加上padding
110 | // borderBoxShape.stroke( FILL_COLOR ).fill( "transparent" );
111 | // borderBoxShape.setSize( textBox.width + padding * 2, textBox.height + padding * 2 );
112 | // borderBoxShape.setRadius( radius );
113 | // borderBoxShape.setAttr( "stroke-dasharray", "5, 5" );
114 |
115 | // textShape.setAttr( {
116 | // dx: 0-textBox.x,
117 | // dy: 0-textBox.y
118 | // } );
119 |
120 | // textShape.translate( padding, padding );
121 |
122 | // // 对于根占位符, 返回的不是组, 而是组容器内部的虚线框。 以方便选中变色
123 | // return borderBoxShape;
124 |
125 | // }
126 |
127 | } );
--------------------------------------------------------------------------------
/src/ui/keyboard/keyboard.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-14 16:31:36
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-07 10:26:22
6 | */
7 | define(function (require) {
8 | var kity = require('kity'),
9 | UiImpl = require('ui/ui-impl/ui'),
10 | $$ = require('ui/ui-impl/ui-utils'),
11 | Keyboard = kity.createClass('Keyboard', {
12 | constructor: function (uiComponent, kfEditor) {
13 | this.kfEditor = kfEditor;
14 | this.uiComponent = uiComponent;
15 | this.initKeyboardElements();
16 |
17 | // this.initServices();
18 |
19 | this.initEvent();
20 | },
21 |
22 | // initServices: function () {
23 | // this.kfEditor.registerService('ui.toolbar.disable', this, {
24 | // disableToolbar: this.disableToolbar,
25 | // });
26 |
27 | // this.kfEditor.registerService('ui.toolbar.enable', this, {
28 | // enableToolbar: this.enableToolbar,
29 | // });
30 |
31 | // this.kfEditor.registerService('ui.toolbar.close', this, {
32 | // closeToolbar: this.closeToolbar,
33 | // });
34 | // },
35 |
36 | initEvent: function () {
37 | var _self = this;
38 |
39 | $$.on(this.uiComponent.keyboardContainer, 'mousedown', function (e) {
40 | e.preventDefault();
41 | });
42 |
43 | $$.on(this.uiComponent.keyboardContainer, 'mousewheel', function (e) {
44 | e.preventDefault();
45 | });
46 |
47 | // // 通知所有组件关闭
48 | // $$.on(this.kfEditor.getContainer(), 'mousedown', function () {
49 | // _self.notify('closeAll');
50 | // });
51 |
52 | // 订阅数据选择主题
53 | $$.subscribe('panel.select', function (data) {
54 | _self.insertSource(data);
55 | });
56 | },
57 |
58 | insertSource: function (val) {
59 | this.kfEditor.requestService('control.insert.string', val);
60 | this.kfEditor.eclassWebService.send({
61 | type: 'common.selectKey',
62 | data: {
63 | body: {
64 | key: val,
65 | },
66 | },
67 | });
68 | },
69 |
70 | // disableToolbar: function () {
71 | // kity.Utils.each(this.elements, function (ele) {
72 | // ele.disable && ele.disable();
73 | // });
74 | // },
75 |
76 | // enableToolbar: function () {
77 | // kity.Utils.each(this.elements, function (ele) {
78 | // ele.enable && ele.enable();
79 | // });
80 | // },
81 |
82 | // getContainer: function () {
83 | // return this.kfEditor.requestService('ui.get.canvas.container');
84 | // },
85 |
86 | // closeToolbar: function () {
87 | // this.closeElement();
88 | // },
89 |
90 | // 接受到关闭通知
91 | // notify: function (type) {
92 | // switch (type) {
93 | // // 关闭所有组件
94 | // case 'closeAll':
95 | // // 关闭其他组件
96 | // case 'closeOther':
97 | // this.closeElement(arguments[1]);
98 | // return;
99 | // }
100 | // },
101 |
102 | // closeElement: function (exception) {
103 | // kity.Utils.each(this.elements, function (ele) {
104 | // if (ele != exception) {
105 | // ele.hide && ele.hide();
106 | // }
107 | // });
108 | // },
109 |
110 | initKeyboardElements: function () {
111 | var doc = this.uiComponent.keyboardContainer.ownerDocument;
112 |
113 | var ele = createKeyboard(doc, this.kfEditor);
114 | this.appendElement(ele);
115 | },
116 |
117 | appendElement: function (uiElement) {
118 | uiElement.attachTo(this.uiComponent.keyboardContainer);
119 | },
120 | });
121 |
122 | function createKeyboard(doc, kfEditor) {
123 | return new UiImpl.Keyboard(doc, kfEditor);
124 | }
125 |
126 | return Keyboard;
127 | });
128 |
--------------------------------------------------------------------------------
/dev-lib/dev-define.js:
--------------------------------------------------------------------------------
1 | /**
2 | * cmd 内部定义
3 | * 开发用
4 | */
5 |
6 | ( function ( global ) {
7 |
8 | var _modules = {},
9 | // 记录模块执行顺序的key列表
10 | keyList = [],
11 | loaded = {};
12 |
13 | global.inc = {
14 |
15 | base: '',
16 |
17 | config: function ( options ) {
18 |
19 | this.base = options.base || '';
20 |
21 | },
22 |
23 | record: function ( key ) {
24 | keyList.push( key );
25 | },
26 |
27 | use: function ( id ) {
28 |
29 | return require( id );
30 |
31 | },
32 |
33 | remove: function ( node ) {
34 |
35 | node.parentNode.removeChild( node );
36 |
37 | }
38 |
39 | };
40 |
41 | global.define = function ( id, deps, f ) {
42 |
43 | var argLen = arguments.length,
44 | module = null;
45 |
46 | switch ( argLen ) {
47 |
48 | case 1:
49 | f = id;
50 | id = keyList.shift();
51 | break;
52 |
53 | case 2:
54 | if ( typeof id === 'string' ) {
55 |
56 | f = deps;
57 |
58 | } else {
59 |
60 | f = deps;
61 | id = keyList.shift();
62 |
63 | }
64 |
65 | break;
66 |
67 | }
68 |
69 | module = _modules[ id ] = {
70 |
71 | exports: {},
72 | value: null,
73 | factory: null
74 |
75 | };
76 |
77 | loadDeps( f );
78 |
79 | if ( typeof f === 'function' ) {
80 |
81 | module.factory = f;
82 |
83 | } else {
84 |
85 | module.value = f;
86 |
87 | }
88 |
89 | }
90 |
91 | function require ( id ) {
92 |
93 | var exports = {},
94 | module = _modules[ id ];
95 |
96 | if ( module.value ) {
97 |
98 | return module.value;
99 |
100 | }
101 |
102 | exports = module.factory( require, module.exports, module );
103 |
104 | if ( exports ) {
105 |
106 | module.exports = exports;
107 |
108 | }
109 |
110 | module.value = module.exports;
111 | module.exports = null;
112 | module.factory = null;
113 |
114 | return module.value;
115 |
116 | }
117 |
118 | function loadDeps ( factory ) {
119 |
120 | var deps = null,
121 | pathname = location.pathname,
122 | uri = location.protocol + '//' + location.host;
123 |
124 | pathname = pathname.split( '/');
125 |
126 | if ( pathname[ pathname.length - 1 ] !== '' ) {
127 |
128 | pathname[ pathname.length - 1 ] = '';
129 |
130 | }
131 |
132 | uri += pathname.join( '/' );
133 |
134 | if ( typeof factory === 'function' ) {
135 |
136 | deps = loadDepsByFunction( factory );
137 |
138 | } else {
139 |
140 | // 未处理object的情况
141 | return;
142 |
143 | }
144 |
145 | for ( var i = 0, len = deps.length; i < len; i++ ) {
146 |
147 | var key = deps[ i ];
148 |
149 | if ( loaded[ key ] ) {
150 | continue;
151 | }
152 |
153 | loaded[ key ] = true;
154 |
155 | document.write( '' );
156 | document.write( '' );
157 |
158 | }
159 |
160 | }
161 |
162 | function loadDepsByFunction ( factory ) {
163 |
164 | var content = factory.toString(),
165 | match = null,
166 | deps = [],
167 | pattern = /require\s*\(\s*([^)]+?)\s*\)/g;
168 |
169 | while ( match = pattern.exec( content ) ) {
170 |
171 | deps.push( match[ 1 ].replace( /'|"/g, '' ) );
172 |
173 | }
174 |
175 | return deps;
176 |
177 | }
178 |
179 | } )( this );
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/panel/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-16 18:52:57
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-06 15:42:41
6 | */
7 | define(function (require) {
8 | const kity = require('kity');
9 | const $$ = require('ui/ui-impl/ui-utils');
10 | const Panel = kity.createClass('Panel', {
11 | constructor(parentNode, parentProps) {
12 | this.parentNode = parentNode;
13 | this.props = parentProps;
14 | this.prefix = parentProps.prefix + 'keyboard-panel';
15 | this.scrollHeight = parentProps.scrollHeight;
16 | this.rowHeight = parentProps.rowHeight;
17 | // 初始化状态
18 | this.state = {
19 | type: this.props.type,
20 | page: this.props.page,
21 | };
22 |
23 | this.containerClassName = this.prefix;
24 | this.listClassName = `${this.prefix}-list`;
25 | this.itemClassName = `${this.prefix}-list-item`;
26 |
27 | this._onClick = this._onClick.bind(this);
28 | },
29 | _calculateHeight: function (type, page) {
30 | // 计算当前类型的起始行数
31 | const curTypeIndex =
32 | this.props.panelConstant.findIndex((item) => item.type === type) || 0;
33 | const prevList =
34 | this.props.panelConstant.slice(0, curTypeIndex) || this.props.panelConstant[0];
35 | const rows = prevList.reduce((acc, cur, index) => {
36 | const curRows = Math.ceil(cur.items.length / 8);
37 | return acc + curRows;
38 | }, 0);
39 | // 计算当前类型的锚点坐标
40 | const typeHeight = rows * this.rowHeight;
41 | return typeHeight + page * this.scrollHeight;
42 | },
43 | _render: function () {
44 | const list = this.props.panelConstant.reduce((acc, cur, index) => {
45 | const itemLenOfLastRow = cur.items.length % 8;
46 | const blankArr = itemLenOfLastRow ? new Array(8 - itemLenOfLastRow).fill('') : [];
47 | return acc.concat(cur.items, blankArr);
48 | }, []);
49 | const table = list.reduce((acc, cur, index) => {
50 | const row = Math.floor(index / 8);
51 | const col = Math.floor(index % 8);
52 | if (!acc[row]) acc[row] = [];
53 | acc[row][col] = cur;
54 | return acc;
55 | }, []);
56 | return $$.ele(this.props.doc, 'div', {
57 | className: this.containerClassName,
58 | content: `
59 |
65 | ${table
66 | .map(
67 | (row) =>
68 | '' +
69 | row
70 | .map((x) =>
71 | x
72 | ? ` | `
76 | : null
77 | )
78 | .join('') +
79 | '
'
80 | )
81 | .join('')}
82 |
83 | `,
84 | });
85 | },
86 | mount: function () {
87 | const node = this._render();
88 | $$.delegate(this.parentNode, '.' + this.itemClassName, 'click', this._onClick);
89 | this.parentNode.appendChild(node);
90 | },
91 | update: function (nextProps) {
92 | $('#' + this.listClassName).css(
93 | 'top',
94 | `-${this._calculateHeight(nextProps.type, nextProps.page)}px`
95 | );
96 | this._setState({
97 | type: nextProps.type,
98 | page: nextProps.page,
99 | });
100 | },
101 | _onClick: function (e) {
102 | const val = e.target.dataset.value;
103 | this.props.onClick(val);
104 | },
105 | _setState: function (nextState) {
106 | this.state = {
107 | ...this.state,
108 | ...nextState,
109 | };
110 | },
111 | });
112 | return Panel;
113 | });
114 |
--------------------------------------------------------------------------------
/src/base/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-17.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | // copy保护
8 | var MAX_COPY_DEEP = 10,
9 |
10 | commonUtils = {
11 | extend: function ( target, source ) {
12 |
13 | var isDeep = false;
14 |
15 | if ( typeof target === "boolean" ) {
16 | isDeep = target;
17 | target = source;
18 | source = [].splice.call( arguments, 2 );
19 | } else {
20 | source = [].splice.call( arguments, 1 );
21 | }
22 |
23 | if ( !target ) {
24 | throw new Error( 'Utils: extend, target can not be empty' );
25 | }
26 |
27 | commonUtils.each( source, function ( src ) {
28 |
29 | if ( src && typeof src === "object" || typeof src === "function" ) {
30 |
31 | copy( isDeep, target, src );
32 |
33 | }
34 |
35 | } );
36 |
37 | return target;
38 |
39 | },
40 |
41 | /**
42 | * 返回给定节点parent是否包含target节点
43 | * @param parent
44 | * @param target
45 | */
46 | contains: function ( parent, target ) {
47 |
48 | if ( parent.contains ) {
49 | return parent.contains( target );
50 | } else if ( parent.compareDocumentPosition ) {
51 | return !!( parent.compareDocumentPosition( target ) & 16 );
52 | }
53 |
54 | },
55 |
56 | getRect: function ( node ) {
57 | return node.getBoundingClientRect();
58 | },
59 |
60 | isArray: function ( obj ) {
61 | return obj && ({}).toString.call( obj ) === "[object Array]";
62 | },
63 |
64 | isString: function ( obj ) {
65 | return typeof obj === "string";
66 | },
67 |
68 | proxy: function ( fn, context ) {
69 |
70 | return function () {
71 | return fn.apply( context, arguments );
72 | };
73 |
74 | },
75 |
76 | each: function ( obj, fn ) {
77 |
78 | if ( !obj ) {
79 | return;
80 | }
81 |
82 | if ( 'length' in obj && typeof obj.length === "number" ) {
83 |
84 | for ( var i = 0, len = obj.length; i < len; i++ ) {
85 |
86 | if ( fn.call( null, obj[ i ], i, obj ) === false ) {
87 | break;
88 | }
89 |
90 | }
91 |
92 | } else {
93 |
94 | for ( var key in obj ) {
95 |
96 | if ( obj.hasOwnProperty( key ) ) {
97 | if ( fn.call( null, obj[ key ], key, obj ) === false ) {
98 | break;
99 | }
100 | }
101 |
102 | }
103 |
104 | }
105 |
106 | }
107 | };
108 |
109 | function copy ( isDeep, target, source, count ) {
110 |
111 | count = count | 0;
112 |
113 | if ( count > MAX_COPY_DEEP ) {
114 | return source;
115 | }
116 |
117 | count++;
118 |
119 | commonUtils.each( source, function ( value, index, origin ) {
120 |
121 | if ( isDeep ) {
122 |
123 | if ( !value || ( typeof value !== "object" && typeof value !== "function" ) ) {
124 | target[ index ] = value;
125 | } else {
126 | target[ index ] = target[ index ] || ( commonUtils.isArray( value ) ? [] : {} );
127 | target[ index ] = copy( isDeep, target[ index ], value, count );
128 | }
129 |
130 | } else {
131 | target[ index ] = value;
132 | }
133 |
134 | } );
135 |
136 | return target;
137 |
138 | }
139 |
140 | return commonUtils;
141 |
142 | } );
--------------------------------------------------------------------------------
/src/editor/command.md:
--------------------------------------------------------------------------------
1 | # 接口文档
2 |
3 | 公式编辑器借助 web-service 库,通过事件的触发和监听实现数据交互,有 webview 和 documentEvent 两种通信方式。下面的示例均以 documentEvent 作为通信通道,公式编辑器启用 documentEvent 需要在 url 中增加 protocol,详细请参照[readme](../../readme.md)
4 |
5 | ## 信令格式
6 |
7 | 格式如下
8 |
9 | ```js
10 | {
11 | type: string; // 信令的类型
12 | headers: {
13 | reqId: string; // 信令的请求id,每次都随机
14 | }
15 | data: {
16 | body: object, // body中存储具体数据
17 | },
18 | }
19 | ```
20 |
21 | 以导出公式-common.setFormula 为例,控制台输入如下调试语句:
22 |
23 | ```js
24 | document.addEventListener('documentMessage', (e) => {
25 | console.log(e);
26 | });
27 | ```
28 |
29 | 可以看到调试数据如下所示:
30 |
31 | ```js
32 | CustomEvent:
33 | {
34 | ...
35 | detail: {
36 | type: 'common.setFormula',
37 | headers: {
38 | reqId: '......'
39 | },
40 | data: {
41 | body: {
42 | formula: '<>',
43 | formulaSrc: 'data:image/png;base64,......'
44 | }
45 | }
46 | }
47 | }
48 | ```
49 |
50 | ## 事件列表
51 |
52 | 1、关闭弹窗
53 |
54 | - 说明:点击关闭弹窗按钮时触发,使用方可监听该事件,实现页面跳转、弹窗关闭。
55 | - 发布者:公式编辑器
56 | - 订阅者:使用方
57 | - 事件名:common.closeModal
58 | - 使用示例:
59 |
60 | ```js
61 | document.addEventListener('documentMessage', (e) => {
62 | const { type } = e?.detail;
63 | const msg = e?.detail?.data?.body;
64 | if (type !== 'common.closeModal') return;
65 | console.log('关闭');
66 | });
67 | ```
68 |
69 | 2、鼠标点击左侧 tab 切换事件
70 |
71 | - 说明:点击左侧“常用”、“单位”按钮切换字符类型时触发
72 | - 发布者:公式编辑器
73 | - 订阅者:使用方
74 | - 事件名:common.setType
75 | - 属性列表:
76 | |属性名|类型|默认值|备注|
77 | |-|-|-|-|
78 | |type|'common'\|'algebra'\|'geometry'\|'letter'\|'other'|无|当前字符类型|
79 | - 使用示例:
80 |
81 | ```js
82 | document.addEventListener('documentMessage', (e) => {
83 | const { type } = e?.detail;
84 | const msg = e?.detail?.data?.body;
85 | if (type !== 'common.setType') return;
86 | console.log('msg', msg.type); // msg algebra
87 | });
88 | ```
89 |
90 | 3、鼠标点击 latex 字符事件
91 |
92 | - 说明:点击键盘按钮时触发,常用于业务方埋点
93 | - 发布者:公式编辑器
94 | - 订阅者:使用方
95 | - 事件名:common.selectKey
96 | - 属性列表:
97 | |属性名|类型|默认值|备注|
98 | |-|-|-|-|
99 | |key|string|无|键盘字符的 latex 值|
100 | - 使用示例:
101 |
102 | ```js
103 | document.addEventListener('documentMessage', (e) => {
104 | const { type } = e?.detail;
105 | const msg = e?.detail?.data?.body;
106 | if (type !== 'common.selectKey') return;
107 | console.log('msg', msg.key); // msg \delta
108 | });
109 | ```
110 |
111 | 4、导出公式事件
112 |
113 | - 说明:鼠标点击完成按钮,导出编辑器中的公式的 latex 值和 base64 格式的图片
114 | - 发布者:公式编辑器
115 | - 订阅者:使用方
116 | - 事件名:common.setFormula
117 | - 属性列表:
118 | |属性名|类型|默认值|备注|
119 | |-|-|-|-|
120 | |formula|string|无|总 latex 值|
121 | |formulaSrc|string|无|base64 格式的 url|
122 | - 使用示例:
123 |
124 | ```js
125 | document.addEventListener('documentMessage', (e) => {
126 | const { type } = e?.detail;
127 | const msg = e?.detail?.data?.body;
128 | if (type !== 'common.setFormula') return;
129 | console.log('msg', msg.formula, msg.formulaSrc); // msg 123 data:image/png;......
130 | });
131 | ```
132 |
133 | 5、初始化事件
134 |
135 | - 说明:通知使用方,公式编辑器已初始化完成,完成后才可以进行通信。
136 | - 发布者:公式编辑器
137 | - 订阅者:使用方
138 | - 事件名:common.ready
139 | - 使用示例:
140 |
141 | ```js
142 | document.addEventListener('documentMessage', (e) => {
143 | const { type } = e?.detail;
144 | const msg = e?.detail?.data?.body;
145 | if (type !== 'common.ready') return;
146 | console.log('已准备');
147 | });
148 | ```
149 |
150 | 6、设置公式初始值
151 |
152 | - 说明:通知公式编辑器初始 latex 值,一般用于二次编辑 latex 公式
153 | - 发布者:使用者
154 | - 订阅者:公式编辑器
155 | - 事件名:common.readFormula
156 | - 属性列表:
157 | |属性名|类型|默认值|备注|
158 | |-|-|-|-|
159 | |formula|string|无|总 latex 值|
160 | - 使用示例:
161 |
162 | ```js
163 | const event = new CustomEvent('documentMessage', {
164 | detail: {
165 | type: 'common.readFormula',
166 | headers: {
167 | reqId: 1,
168 | },
169 | data: {
170 | body: {
171 | formula: 'abc',
172 | },
173 | },
174 | },
175 | });
176 | document.dispatchEvent(event);
177 | ```
178 |
179 | 7、清空公式编辑器
180 |
181 | - 说明:通知公式编辑器清空内容和左侧tab,一般用于关闭弹窗。
182 | - 发布者:使用者
183 | - 订阅者:公式编辑器
184 | - 事件名:common.clearFormula
185 | - 使用示例:
186 |
187 | ```js
188 | const event = new CustomEvent('documentMessage', {
189 | detail: {
190 | type: 'common.clearFormula',
191 | headers: {
192 | reqId: 2,
193 | },
194 | data: {
195 | body: {},
196 | },
197 | },
198 | });
199 | document.dispatchEvent(event);
200 | ```
201 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/list.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-31.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | PREFIX = "kf-editor-ui-",
10 |
11 | // UiUitls
12 | $$ = require( "ui/ui-impl/ui-utils" ),
13 |
14 | List = kity.createClass( "List", {
15 |
16 | constructor: function ( doc, options ) {
17 |
18 | this.options = options;
19 |
20 | this.doc = doc;
21 |
22 | this.onselectHandler = null;
23 |
24 | this.currentSelect = -1;
25 |
26 | this.element = this.createBox();
27 | this.itemGroups = this.createItems();
28 |
29 | this.mergeElement();
30 |
31 | },
32 |
33 | // 预定义的方法留空
34 | onselectHandler: function ( index, oldIndex ) {},
35 |
36 | setSelectHandler: function ( selectHandler ) {
37 | this.onselectHandler = selectHandler;
38 | },
39 |
40 | createBox: function () {
41 |
42 | var boxNode = $$.ele( this.doc, "div", {
43 | className: PREFIX + "list"
44 | } ),
45 |
46 | // 创建背景
47 | bgNode = $$.ele( this.doc, "div", {
48 | className: PREFIX + "list-bg"
49 | } );
50 |
51 | if ( "width" in this.options ) {
52 | boxNode.style.width = this.options.width + "px";
53 | }
54 |
55 | boxNode.appendChild( bgNode );
56 |
57 | return boxNode;
58 |
59 | },
60 |
61 | select: function ( index ) {
62 |
63 | var oldSelect = this.currentSelect;
64 |
65 | if ( oldSelect === -1 ) {
66 | oldSelect = index;
67 | }
68 |
69 | this.unselect( oldSelect );
70 |
71 | this.currentSelect = index;
72 |
73 | $$.getClassList( this.itemGroups.items[ index ] ).add( PREFIX + "list-item-select" );
74 |
75 | this.onselectHandler( index, oldSelect );
76 |
77 | },
78 |
79 | unselect: function ( index ) {
80 |
81 | $$.getClassList( this.itemGroups.items[ index ] ).remove( PREFIX + "list-item-select" );
82 |
83 | },
84 |
85 | setOffset: function ( x, y ) {
86 | this.element.style.left = x + "px";
87 | this.element.style.top = y + "px";
88 | },
89 |
90 | initEvent: function () {
91 |
92 | var className = "." + PREFIX + "list-item",
93 | _self = this;
94 |
95 | $$.delegate( this.itemGroups.container, className, "mousedown", function ( e ) {
96 |
97 | e.preventDefault();
98 |
99 | if ( e.which !== 1 ) {
100 | return;
101 | }
102 |
103 | _self.select( this.getAttribute( "data-index" ) );
104 |
105 | } );
106 |
107 | $$.on( this.element, "mousedown", function ( e ) {
108 |
109 | e.stopPropagation();
110 | e.preventDefault();
111 |
112 | } );
113 |
114 | },
115 |
116 | getPositionInfo: function () {
117 | return $$.getRectBox( this.element );
118 | },
119 |
120 | createItems: function () {
121 |
122 | var doc = this.doc,
123 | groupNode = null,
124 | itemNode = null,
125 | iconNode = null,
126 | items = [],
127 | itemContainer = null;
128 |
129 | groupNode = $$.ele( this.doc, "div", {
130 | className: PREFIX + "list-item"
131 | } );
132 |
133 | itemContainer = groupNode.cloneNode( false );
134 | itemContainer.className = PREFIX + "list-item-container";
135 |
136 | kity.Utils.each( this.options.items, function ( itemText, i ) {
137 |
138 | itemNode = groupNode.cloneNode( false );
139 |
140 | iconNode = groupNode.cloneNode( false );
141 | iconNode.className = PREFIX + "list-item-icon";
142 |
143 | itemNode.appendChild( iconNode );
144 | itemNode.appendChild( $$.ele( doc, "text", itemText ) );
145 |
146 | itemNode.setAttribute( "data-index", i );
147 |
148 | items.push( itemNode );
149 | itemContainer.appendChild( itemNode );
150 |
151 | } );
152 |
153 | return {
154 | container: itemContainer,
155 | items: items
156 | };
157 |
158 | },
159 |
160 | mergeElement: function () {
161 |
162 | this.element.appendChild( this.itemGroups.container );
163 |
164 | },
165 |
166 | mountTo: function ( container ) {
167 | container.appendChild( this.element );
168 | }
169 |
170 | } );
171 |
172 | return List;
173 |
174 | } );
--------------------------------------------------------------------------------
/assets/styles/theme/android.less:
--------------------------------------------------------------------------------
1 | .android {
2 | width: 1920px;
3 | height: 835px;
4 | position: fixed;
5 | bottom: 0;
6 | background-color: transparent;
7 | user-select: none;
8 | * {
9 | box-sizing: border-box;
10 | margin: 0;
11 | padding: 0;
12 | list-style: none;
13 | }
14 | .kf-editor-edit-area {
15 | width: 100%;
16 | height: auto;
17 | float: left;
18 | padding: 40px;
19 | position: relative;
20 | .kf-editor-edit-scrollbar {
21 | bottom: 0px;
22 | }
23 | .kf-editor-header-container {
24 | display: none;
25 | }
26 | .kf-editor-canvas-wrapper {
27 | width: 1840px;
28 | height: 140px;
29 | background: #f8f8f8;
30 | padding: 0 40px;
31 | float: left;
32 | border-radius: 54px;
33 |
34 | .kf-editor-canvas-container {
35 | width: 100%;
36 | height: 100%;
37 | // margin: 40px;
38 | }
39 | }
40 | }
41 | .kf-editor-edit-keyboard {
42 | width: 1920px;
43 | height: 615px;
44 | float: left;
45 | background: #eceff1;
46 | overflow: hidden;
47 | .kf-editor-ui-keyboard-menu {
48 | width: 274px;
49 | height: 615px;
50 | float: left;
51 | border-right: 1px solid rgba(0, 0, 0, 0.1);
52 | .kf-editor-ui-keyboard-menu-list {
53 | width: 100%;
54 | height: 100%;
55 | padding: 26px;
56 | background: #eceff1;
57 | overflow: hidden;
58 | .kf-editor-ui-keyboard-menu-list-item {
59 | width: 183px;
60 | height: 75px;
61 | margin: 19px;
62 | text-align: center;
63 | line-height: 75px;
64 | font-size: 16px;
65 | color: #49494d;
66 | cursor: pointer;
67 | float: left;
68 | font-size: 34px;
69 | }
70 | .kf-editor-ui-keyboard-menu-list-item-active {
71 | background: #ffab30;
72 | border-radius: 8px;
73 | color: #ffffff;
74 | }
75 | }
76 | }
77 | .kf-editor-ui-keyboard-panel {
78 | width: 1425px;
79 | height: 100%;
80 | float: left;
81 | .kf-editor-ui-keyboard-panel-list {
82 | width: 100%;
83 | height: 100%;
84 | padding: 14px 0 0 33px;
85 | overflow: hidden;
86 | position: relative;
87 | transition: top 0.3s linear;
88 | .kf-editor-ui-keyboard-panel-list-item {
89 | width: 148px;
90 | height: 122px;
91 | margin: 12px;
92 | text-align: center;
93 | line-height: 121px;
94 | font-size: 16px;
95 | color: #49494d;
96 | cursor: pointer;
97 | float: left;
98 | position: relative;
99 | &:active {
100 | &::before {
101 | content: '';
102 | display: block;
103 | position: absolute;
104 | width: 100%;
105 | height: 100%;
106 | background-color: rgba(64, 75, 80, 0.15);
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
113 | .kf-editor-ui-keyboard-page {
114 | width: 212px;
115 | height: 100%;
116 | float: left;
117 | .kf-editor-ui-keyboard-page-list {
118 | width: 100%;
119 | height: 100%;
120 | padding: 14px 0px 26px 0;
121 | overflow: hidden;
122 | .kf-editor-ui-keyboard-page-list-item {
123 | width: 100%;
124 | height: 122px;
125 | margin: 12px;
126 | float: left;
127 | font-size: 46px;
128 | color: #ff9800;
129 | text-align: center;
130 | line-height: 122px;
131 | cursor: pointer;
132 | }
133 | .kf-editor-ui-keyboard-page-list-item-delete {
134 | width: 100%;
135 | height: 122px;
136 | background: url('https://store-g1.seewo.com/easiclass-public/a7ea5932b621487aaa1c8b9dd4e58cb0') center no-repeat;
137 | background-size: initial;
138 | &:active {
139 | background-color: rgba(64, 75, 80, 0.15);
140 | }
141 | }
142 | .kf-editor-ui-keyboard-page-list-item-prev {
143 | position: relative;
144 | &::before {
145 | content: ' ';
146 | display: block;
147 | width: 100%;
148 | height: 100%;
149 | background: url('https://store-g1.seewo.com/easiclass-public/27257523137e47499311266d0f872c8b') center no-repeat;
150 | background-size: initial;
151 | }
152 | &:not(.kf-editor-ui-keyboard-page-list-item-disabled):active {
153 | background-color: rgba(64, 75, 80, 0.15);
154 | }
155 | }
156 | .kf-editor-ui-keyboard-page-list-item-next {
157 | position: relative;
158 | &::before {
159 | content: ' ';
160 | display: block;
161 | width: 100%;
162 | height: 100%;
163 | background: url('https://store-g1.seewo.com/easiclass-public/3c2faacfac52472ea6cb522a34aa04db') center no-repeat;
164 | background-size: initial;
165 | }
166 | &:not(.kf-editor-ui-keyboard-page-list-item-disabled):active {
167 | background-color: rgba(64, 75, 80, 0.15);
168 | }
169 | }
170 | .kf-editor-ui-keyboard-page-list-item-disabled::before {
171 | opacity: 0.5;
172 | }
173 | }
174 | }
175 | .kf-editor-ui-keyboard-footer {
176 | display: none;
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/ui/toolbar/toolbar.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 工具条组件
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | UiImpl = require( "ui/ui-impl/ui" ),
10 |
11 | $$ = require( "ui/ui-impl/ui-utils" ),
12 |
13 | UI_ELE_TYPE = require( "ui/ui-impl/def/ele-type" ),
14 |
15 | Tollbar = kity.createClass( "Tollbar", {
16 |
17 | constructor: function ( uiComponent, kfEditor, elementList ) {
18 |
19 | this.kfEditor = kfEditor;
20 | this.uiComponent = uiComponent;
21 |
22 | // 工具栏元素定义列表
23 | this.elementList = elementList;
24 |
25 | this.elements = [];
26 |
27 | this.initToolbarElements();
28 |
29 | this.initServices();
30 |
31 | this.initEvent();
32 |
33 | },
34 |
35 | initServices: function () {
36 |
37 | this.kfEditor.registerService( "ui.toolbar.disable", this, {
38 | disableToolbar: this.disableToolbar
39 | } );
40 |
41 | this.kfEditor.registerService( "ui.toolbar.enable", this, {
42 | enableToolbar: this.enableToolbar
43 | } );
44 |
45 | this.kfEditor.registerService( "ui.toolbar.close", this, {
46 | closeToolbar: this.closeToolbar
47 | } );
48 |
49 | },
50 |
51 | initEvent: function () {
52 |
53 | var _self = this;
54 |
55 | $$.on( this.uiComponent.toolbarContainer, "mousedown", function ( e ) {
56 | e.preventDefault();
57 | } );
58 |
59 | $$.on( this.uiComponent.toolbarContainer, "mousewheel", function ( e ) {
60 | e.preventDefault();
61 | } );
62 |
63 | // 通知所有组件关闭
64 | $$.on( this.kfEditor.getContainer(), "mousedown", function () {
65 | _self.notify( "closeAll" );
66 | } );
67 |
68 | // 订阅数据选择主题
69 | $$.subscribe( "data.select", function ( data ) {
70 |
71 | _self.insertSource( data );
72 |
73 | } );
74 |
75 | },
76 |
77 | insertSource: function ( val ) {
78 |
79 | this.kfEditor.requestService( "control.insert.string", val );
80 |
81 | },
82 |
83 | disableToolbar: function () {
84 |
85 | kity.Utils.each( this.elements, function ( ele ) {
86 | ele.disable && ele.disable();
87 | } );
88 |
89 | },
90 |
91 | enableToolbar: function () {
92 |
93 | kity.Utils.each( this.elements, function ( ele ) {
94 | ele.enable && ele.enable();
95 | } );
96 |
97 | },
98 |
99 | getContainer: function () {
100 | return this.kfEditor.requestService( "ui.get.canvas.container" );
101 | },
102 |
103 | closeToolbar: function () {
104 | this.closeElement();
105 | },
106 |
107 | // 接受到关闭通知
108 | notify: function ( type ) {
109 |
110 | switch ( type ) {
111 |
112 | // 关闭所有组件
113 | case "closeAll":
114 | // 关闭其他组件
115 | case "closeOther":
116 | this.closeElement( arguments[ 1 ] );
117 | return;
118 |
119 | }
120 |
121 | },
122 |
123 | closeElement: function ( exception ) {
124 |
125 | kity.Utils.each( this.elements, function ( ele ) {
126 |
127 | if ( ele != exception ) {
128 | ele.hide && ele.hide();
129 | }
130 |
131 | } );
132 |
133 | },
134 |
135 | initToolbarElements: function () {
136 |
137 | var elements = this.elements,
138 | doc = this.uiComponent.toolbarContainer.ownerDocument,
139 | _self = this;
140 |
141 | kity.Utils.each( this.elementList, function ( eleInfo, i ) {
142 |
143 | var ele = createElement( eleInfo.type, doc, eleInfo.options );
144 | elements.push( ele );
145 | _self.appendElement( ele );
146 |
147 | } );
148 |
149 | },
150 |
151 | appendElement: function ( uiElement ) {
152 |
153 | uiElement.setToolbar( this );
154 | uiElement.attachTo( this.uiComponent.toolbarContainer );
155 |
156 | }
157 |
158 | } );
159 |
160 | function createElement ( type, doc, options ) {
161 |
162 | switch ( type ) {
163 |
164 | case UI_ELE_TYPE.DRAPDOWN_BOX:
165 | return createDrapdownBox( doc, options );
166 |
167 | case UI_ELE_TYPE.DELIMITER:
168 | return createDelimiter( doc );
169 |
170 | case UI_ELE_TYPE.AREA:
171 | return createArea( doc, options );
172 |
173 |
174 | }
175 |
176 | }
177 |
178 | function createDrapdownBox ( doc, options ) {
179 |
180 | return new UiImpl.DrapdownBox( doc, options );
181 |
182 | }
183 |
184 | function createDelimiter ( doc ) {
185 | return new UiImpl.Delimiter( doc );
186 | }
187 |
188 | function createArea ( doc, options ) {
189 | return new UiImpl.Area( doc, options );
190 | }
191 |
192 | return Tollbar;
193 |
194 | } );
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 公式编辑器
5 |
6 |
7 |
16 |
17 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
153 |
154 |
155 |
156 |
157 | 加载中...
158 |
159 |
160 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/panel/pcConst.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-15 10:11:11
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-18 14:36:33
6 | */
7 |
8 | define(function (require) {
9 | const kity = require('kity'),
10 | CHAR_POSITION = require('ui/ui-impl/keyboard/panel/position.data'),
11 | Constant = [
12 | { type: 'common', title: '常用', index: 0, items: [] },
13 | { type: 'algebra', title: '代数', index: 1, items: [] },
14 | { type: 'geometry', title: '几何', index: 2, items: [] },
15 | { type: 'letter', title: '字母', index: 3, items: [] },
16 | { type: 'other', title: '其他', index: 4, items: [] },
17 | ];
18 |
19 | // ----------------------------取雪碧图icon
20 | // 常用
21 | (function () {
22 | const list = [
23 | '<',
24 | '\\frac \\placeholder\\placeholder',
25 | '\\sqrt \\placeholder',
26 | 'a',
27 | '+',
28 | '7',
29 | '8',
30 | '9',
31 | '>',
32 | '\\left|\\placeholder\\right|',
33 | '\\placeholder^2',
34 | 'b',
35 | '-',
36 | '4',
37 | '5',
38 | '6',
39 | '\\leq',
40 | '\\left(\\placeholder\\right)',
41 | '\\sqrt [3] \\placeholder',
42 | 'x',
43 | '\\pm',
44 | '1',
45 | '2',
46 | '3',
47 | '\\geq',
48 | '%',
49 | '\\placeholder^3',
50 | 'y',
51 | ',',
52 | '0',
53 | '.',
54 | '=',
55 | ];
56 |
57 | Constant[0].items = getIconContents(
58 | list,
59 | 'https://store-g1.seewo.com/easiclass-public/a7da75a0d13c427eb7299fac9f634783'
60 | );
61 | })();
62 | // 代数
63 | (function () {
64 | const list = [
65 | '\\times',
66 | '\\div',
67 | '\\approx',
68 | '\\neq',
69 | '\\sqrt [\\placeholder] \\placeholder',
70 | '\\pi',
71 | '\\delta',
72 | '\\left[\\placeholder\\right]',
73 | '\\placeholder^\\placeholder',
74 | '\\placeholder_\\placeholder',
75 | '{^\\placeholder_\\placeholder\\placeholder}',
76 | '\\placeholder^\\placeholder_\\placeholder',
77 | '\\sum\\placeholder',
78 | '\\sum_\\placeholder\\placeholder',
79 | '\\sum^\\placeholder_\\placeholder\\placeholder',
80 | '\\int \\placeholder',
81 | '\\int^\\placeholder_\\placeholder\\placeholder',
82 | '\\iint\\placeholder',
83 | '\\iint^\\placeholder_\\placeholder\\placeholder',
84 | '\\iiint\\placeholder',
85 | '\\iiint^\\placeholder_\\placeholder\\placeholder',
86 | '\\log\\placeholder',
87 | '\\ln\\placeholder',
88 | '\\land',
89 | '\\lor',
90 | '\\neg',
91 | '\\forall',
92 | '\\exists',
93 | '\\infty',
94 | '\\cup',
95 | '\\cap',
96 | '\\in',
97 | '\\notin',
98 | '\\subset',
99 | '\\subseteq',
100 | '\\supset',
101 | '\\supseteq',
102 | '\\varnothing',
103 | '\\cdot',
104 | '\\colon'
105 | ];
106 |
107 | Constant[1].items = getIconContents(
108 | list,
109 | 'https://store-g1.seewo.com/easiclass-public/a7da75a0d13c427eb7299fac9f634783'
110 | );
111 | })();
112 | // 几何
113 | (function () {
114 | const list = [
115 | '\\sin\\placeholder',
116 | '\\cos\\placeholder',
117 | '\\tan\\placeholder',
118 | '\\sec\\placeholder',
119 | '\\csc\\placeholder',
120 | '\\cot\\placeholder',
121 | '\\arcsin\\placeholder',
122 | '\\arccos\\placeholder',
123 | '\\arctan\\placeholder',
124 | '\\triangle',
125 | '\\sim',
126 | '\\cong',
127 | '\\angle',
128 | '\\bot',
129 | '\\alpha',
130 | '\\beta',
131 | '\\gamma',
132 | '\\theta',
133 | '\\degree',
134 | '\\bigcirc'
135 | ];
136 |
137 | Constant[2].items = getIconContents(
138 | list,
139 | 'https://store-g1.seewo.com/easiclass-public/a7da75a0d13c427eb7299fac9f634783'
140 | );
141 | })();
142 | // 单位
143 | (function () {
144 | const list = [
145 | 'a',
146 | 'b',
147 | 'c',
148 | 'd',
149 | 'e',
150 | 'f',
151 | 'g',
152 | 'h',
153 | 'i',
154 | 'j',
155 | 'k',
156 | 'l',
157 | 'm',
158 | 'n',
159 | 'o',
160 | 'p',
161 | 'q',
162 | 'r',
163 | 's',
164 | 't',
165 | 'u',
166 | 'v',
167 | 'w',
168 | 'x',
169 | 'y',
170 | 'z',
171 | ];
172 |
173 | Constant[3].items = getIconContents(
174 | list,
175 | 'https://store-g1.seewo.com/easiclass-public/a7da75a0d13c427eb7299fac9f634783'
176 | );
177 | })();
178 | // 其他
179 | (function () {
180 | const list = [
181 | '\\Omega',
182 | '\\because',
183 | '\\therefore',
184 | '\\Longrightarrow',
185 | '\\Leftrightarrow',
186 | '\\uparrow',
187 | '\\downarrow',
188 | '\\lambda',
189 | '\\kappa',
190 | '\\mu',
191 | '\\rho',
192 | '\\sigma',
193 | '\\tau',
194 | '\\upsilon',
195 | '\\varphi',
196 | '\\Psi',
197 | '\\omega',
198 | '\\varepsilon',
199 | '\\zeta',
200 | '\\eta',
201 | '\\nu',
202 | '\\xi',
203 | '\\chi',
204 | ];
205 |
206 | Constant[4].items = getIconContents(
207 | list,
208 | 'https://store-g1.seewo.com/easiclass-public/a7da75a0d13c427eb7299fac9f634783'
209 | );
210 | })();
211 |
212 | function getIconContents(keySet, imgSrc) {
213 | const result = [];
214 |
215 | kity.Utils.each(keySet, function (key) {
216 | const point = CHAR_POSITION[key] || { x: 0, y: 0 };
217 | const pos = { x: point.x * 83, y: point.y * 65 };
218 | result.push({
219 | key: key,
220 | img: imgSrc,
221 | pos,
222 | });
223 | });
224 |
225 | return result;
226 | }
227 |
228 | return Constant;
229 | });
230 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/panel/androidConst.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-22 18:00:32
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-18 14:36:50
6 | */
7 |
8 | define(function (require) {
9 | const kity = require('kity'),
10 | CHAR_POSITION = require('ui/ui-impl/keyboard/panel/position.data'),
11 | Constant = [
12 | { type: 'common', title: '常用', index: 0, items: [] },
13 | { type: 'algebra', title: '代数', index: 1, items: [] },
14 | { type: 'geometry', title: '几何', index: 2, items: [] },
15 | { type: 'letter', title: '字母', index: 3, items: [] },
16 | { type: 'other', title: '其他', index: 4, items: [] },
17 | ];
18 |
19 | // ----------------------------取雪碧图icon
20 | // 常用
21 | (function () {
22 | const list = [
23 | '<',
24 | '\\frac \\placeholder\\placeholder',
25 | '\\sqrt \\placeholder',
26 | 'a',
27 | '+',
28 | '7',
29 | '8',
30 | '9',
31 | '>',
32 | '\\left|\\placeholder\\right|',
33 | '\\placeholder^2',
34 | 'b',
35 | '-',
36 | '4',
37 | '5',
38 | '6',
39 | '\\leq',
40 | '\\left(\\placeholder\\right)',
41 | '\\sqrt [3] \\placeholder',
42 | 'x',
43 | '\\pm',
44 | '1',
45 | '2',
46 | '3',
47 | '\\geq',
48 | '%',
49 | '\\placeholder^3',
50 | 'y',
51 | ',',
52 | '0',
53 | '.',
54 | '=',
55 | ];
56 |
57 | Constant[0].items = getIconContents(
58 | list,
59 | 'https://store-g1.seewo.com/easiclass-public/ec4941099e30462b935956b413e3ca8d'
60 | );
61 | })();
62 | // 代数
63 | (function () {
64 | const list = [
65 | '\\times',
66 | '\\div',
67 | '\\approx',
68 | '\\neq',
69 | '\\sqrt [\\placeholder] \\placeholder',
70 | '\\pi',
71 | '\\delta',
72 | '\\left[\\placeholder\\right]',
73 | '\\placeholder^\\placeholder',
74 | '\\placeholder_\\placeholder',
75 | '{^\\placeholder_\\placeholder\\placeholder}',
76 | '\\placeholder^\\placeholder_\\placeholder',
77 | '\\sum\\placeholder',
78 | '\\sum_\\placeholder\\placeholder',
79 | '\\sum^\\placeholder_\\placeholder\\placeholder',
80 | '\\int \\placeholder',
81 | '\\int^\\placeholder_\\placeholder\\placeholder',
82 | '\\iint\\placeholder',
83 | '\\iint^\\placeholder_\\placeholder\\placeholder',
84 | '\\iiint\\placeholder',
85 | '\\iiint^\\placeholder_\\placeholder\\placeholder',
86 | '\\log\\placeholder',
87 | '\\ln\\placeholder',
88 | '\\land',
89 | '\\lor',
90 | '\\neg',
91 | '\\forall',
92 | '\\exists',
93 | '\\infty',
94 | '\\cup',
95 | '\\cap',
96 | '\\in',
97 | '\\notin',
98 | '\\subset',
99 | '\\subseteq',
100 | '\\supset',
101 | '\\supseteq',
102 | '\\varnothing',
103 | '\\cdot',
104 | '\\colon'
105 | ];
106 |
107 | Constant[1].items = getIconContents(
108 | list,
109 | 'https://store-g1.seewo.com/easiclass-public/ec4941099e30462b935956b413e3ca8d'
110 | );
111 | })();
112 | // 几何
113 | (function () {
114 | const list = [
115 | '\\sin\\placeholder',
116 | '\\cos\\placeholder',
117 | '\\tan\\placeholder',
118 | '\\sec\\placeholder',
119 | '\\csc\\placeholder',
120 | '\\cot\\placeholder',
121 | '\\arcsin\\placeholder',
122 | '\\arccos\\placeholder',
123 | '\\arctan\\placeholder',
124 | '\\triangle',
125 | '\\sim',
126 | '\\cong',
127 | '\\angle',
128 | '\\bot',
129 | '\\alpha',
130 | '\\beta',
131 | '\\gamma',
132 | '\\theta',
133 | '\\degree',
134 | '\\bigcirc',
135 | ];
136 |
137 | Constant[2].items = getIconContents(
138 | list,
139 | 'https://store-g1.seewo.com/easiclass-public/ec4941099e30462b935956b413e3ca8d'
140 | );
141 | })();
142 | // 字母
143 | (function () {
144 | const list = [
145 | 'a',
146 | 'b',
147 | 'c',
148 | 'd',
149 | 'e',
150 | 'f',
151 | 'g',
152 | 'h',
153 | 'i',
154 | 'j',
155 | 'k',
156 | 'l',
157 | 'm',
158 | 'n',
159 | 'o',
160 | 'p',
161 | 'q',
162 | 'r',
163 | 's',
164 | 't',
165 | 'u',
166 | 'v',
167 | 'w',
168 | 'x',
169 | 'y',
170 | 'z',
171 | ];
172 |
173 | Constant[3].items = getIconContents(
174 | list,
175 | 'https://store-g1.seewo.com/easiclass-public/ec4941099e30462b935956b413e3ca8d'
176 | );
177 | })();
178 | // 其他
179 | (function () {
180 | const list = [
181 | '\\Omega',
182 | '\\because',
183 | '\\therefore',
184 | '\\Longrightarrow',
185 | '\\Leftrightarrow',
186 | '\\uparrow',
187 | '\\downarrow',
188 | '\\lambda',
189 | '\\kappa',
190 | '\\mu',
191 | '\\rho',
192 | '\\sigma',
193 | '\\tau',
194 | '\\upsilon',
195 | '\\varphi',
196 | '\\Psi',
197 | '\\omega',
198 | '\\varepsilon',
199 | '\\zeta',
200 | '\\eta',
201 | '\\nu',
202 | '\\xi',
203 | '\\chi',
204 | ];
205 |
206 | Constant[4].items = getIconContents(
207 | list,
208 | 'https://store-g1.seewo.com/easiclass-public/ec4941099e30462b935956b413e3ca8d'
209 | );
210 | })();
211 |
212 | function getIconContents(keySet, imgSrc) {
213 | const result = [];
214 |
215 | kity.Utils.each(keySet, function (key) {
216 | const point = CHAR_POSITION[key] || { x: 0, y: 0 };
217 | const pos = { x: point.x * 172, y: point.y * 146 + 26 };
218 | result.push({
219 | key: key,
220 | img: imgSrc,
221 | pos,
222 | });
223 | });
224 |
225 | return result;
226 | }
227 |
228 | return Constant;
229 | });
230 |
--------------------------------------------------------------------------------
/src/syntax/delete.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 删除控制
3 | */
4 |
5 | define( function ( require, exports, module ) {
6 |
7 | var kity = require( "kity" );
8 |
9 | return kity.createClass( "DeleteComponent", {
10 |
11 | constructor: function ( parentComponent, kfEditor ) {
12 |
13 | this.parentComponent = parentComponent;
14 | this.kfEditor = kfEditor;
15 |
16 | },
17 |
18 | deleteGroup: function () {
19 |
20 | var cursorInfo = this.parentComponent.getCursorRecord(),
21 | objTree = this.parentComponent.getObjectTree(),
22 | // 当前的树信息
23 | currentTree = objTree.mapping[ cursorInfo.groupId ].strGroup;
24 |
25 | // 选区长度为0, 则删除前一个组
26 | if ( cursorInfo.startOffset === cursorInfo.endOffset ) {
27 |
28 | // 已经到最前, 需要进一步处理
29 | if ( cursorInfo.startOffset === 0 ) {
30 |
31 | // 根节点时, 直接退出, 不做任何处理
32 | if ( this.parentComponent.isRootTree( currentTree ) ) {
33 | return false;
34 | }
35 |
36 | // 不是根节点时, 选中当前容器的父容器
37 | cursorInfo = this.selectParentContainer( cursorInfo.groupId );
38 | this.parentComponent.updateCursor( cursorInfo );
39 |
40 | return false;
41 |
42 | } else {
43 |
44 | // 还有更多剩余内容, 则直接删除前一个组
45 | if ( currentTree.operand.length > 1 ) {
46 |
47 | cursorInfo = this.deletePrevGroup( currentTree, cursorInfo );
48 |
49 | // 仅有一个需要删除的组存在时的处理
50 | } else {
51 |
52 | // 更新光标位置
53 | cursorInfo.startOffset = 0;
54 | cursorInfo.endOffset = 1;
55 |
56 | // 处理组类型, 选中该组即可
57 | if ( currentTree.operand[ 0 ].attr && this.parentComponent.isGroupNode( currentTree.operand[ 0 ].attr.id ) ) {
58 |
59 | this.parentComponent.updateCursor( cursorInfo );
60 |
61 | return false;
62 |
63 | // 普通元素处理
64 | } else {
65 |
66 | // 替换成占位符
67 | currentTree.operand[ 0 ] = {
68 | name: "placeholder",
69 | operand: []
70 | };
71 | this.parentComponent.updateCursor( cursorInfo );
72 |
73 | return true;
74 |
75 | }
76 |
77 | }
78 |
79 | }
80 |
81 | // 当前是选区
82 | } else {
83 |
84 | // 当前选中占位符的情况
85 | if ( this.parentComponent.isSelectPlaceholder() ) {
86 |
87 | // 如果是根节点, 则不允许删除
88 | if ( this.parentComponent.isRootTree( currentTree ) ) {
89 |
90 | return false;
91 |
92 | // 否则,更新选区到选中该容器
93 | } else {
94 |
95 | cursorInfo = this.selectParentContainer( cursorInfo.groupId );
96 | this.parentComponent.updateCursor( cursorInfo );
97 |
98 | return false;
99 |
100 | }
101 |
102 | // 其他选区正常删除
103 | } else {
104 |
105 | return this.deleteSelection( currentTree, cursorInfo );
106 |
107 | }
108 |
109 | }
110 |
111 | this.parentComponent.updateCursor( cursorInfo );
112 |
113 | // 选区长度为0, 则可以判定当前公式发生了改变
114 | if ( cursorInfo.startOffset === cursorInfo.endOffset ) {
115 | return true;
116 | }
117 |
118 | return false;
119 |
120 | },
121 |
122 | // 删除前一个节点, 返回更新后的光标信息
123 | deletePrevGroup: function ( tree, cursorInfo ) {
124 |
125 | // 待删除的组
126 | var index = cursorInfo.startOffset - 1,
127 | group = tree.operand[ index ];
128 |
129 | // 叶子节点可以直接删除
130 | if ( this.parentComponent.isLeafTree( group ) ) {
131 |
132 | tree.operand.splice( index, 1 );
133 | cursorInfo.startOffset -= 1;
134 | cursorInfo.endOffset -= 1;
135 |
136 | // 否则, 选中该节点
137 | } else {
138 |
139 | cursorInfo.startOffset -= 1;
140 |
141 | }
142 |
143 | return cursorInfo;
144 |
145 | },
146 |
147 | // 删除选区内容
148 | deleteSelection: function ( tree, cursorInfo ) {
149 |
150 | // 选中的是容器内的所有内容
151 | if ( cursorInfo.startOffset === 0 && cursorInfo.endOffset === tree.operand.length ) {
152 |
153 | tree.operand.length = 1;
154 |
155 | tree.operand[ 0 ] = {
156 | name: "placeholder",
157 | operand: []
158 | };
159 |
160 | cursorInfo.endOffset = 1;
161 |
162 | // 否则可以删除当前选中内容
163 | } else {
164 | tree.operand.splice( cursorInfo.startOffset, cursorInfo.endOffset - cursorInfo.startOffset );
165 | cursorInfo.endOffset = cursorInfo.startOffset;
166 | }
167 |
168 | this.parentComponent.updateCursor( cursorInfo );
169 |
170 | return true;
171 |
172 | },
173 |
174 | // 选中给定ID节点的父容器
175 | selectParentContainer: function ( groupId ) {
176 |
177 | var currentGroupNode = this.parentComponent.getGroupObject( groupId ).node,
178 | parentContainerInfo = this.kfEditor.requestService( "position.get.group", currentGroupNode ),
179 | // 当前组在父容器中的索引
180 | index = this.kfEditor.requestService( "position.get.index", parentContainerInfo.groupObj, currentGroupNode );
181 |
182 | // 返回新的光标信息
183 | return {
184 | groupId: parentContainerInfo.id,
185 | startOffset: index,
186 | endOffset: index + 1
187 | };
188 |
189 | }
190 |
191 | } );
192 |
193 |
194 | } );
--------------------------------------------------------------------------------
/dist/assets/styles/theme/android.css:
--------------------------------------------------------------------------------
1 | .android {
2 | width: 1920px;
3 | height: 835px;
4 | position: fixed;
5 | bottom: 0;
6 | background-color: transparent;
7 | user-select: none;
8 | }
9 | .android * {
10 | box-sizing: border-box;
11 | margin: 0;
12 | padding: 0;
13 | list-style: none;
14 | }
15 | .android .kf-editor-edit-area {
16 | width: 100%;
17 | height: auto;
18 | float: left;
19 | padding: 40px;
20 | position: relative;
21 | }
22 | .android .kf-editor-edit-area .kf-editor-edit-scrollbar {
23 | bottom: 0px;
24 | }
25 | .android .kf-editor-edit-area .kf-editor-header-container {
26 | display: none;
27 | }
28 | .android .kf-editor-edit-area .kf-editor-canvas-wrapper {
29 | width: 1840px;
30 | height: 140px;
31 | background: #f8f8f8;
32 | padding: 0 40px;
33 | float: left;
34 | border-radius: 54px;
35 | }
36 | .android .kf-editor-edit-area .kf-editor-canvas-wrapper .kf-editor-canvas-container {
37 | width: 100%;
38 | height: 100%;
39 | }
40 | .android .kf-editor-edit-keyboard {
41 | width: 1920px;
42 | height: 615px;
43 | float: left;
44 | background: #eceff1;
45 | overflow: hidden;
46 | }
47 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-menu {
48 | width: 274px;
49 | height: 615px;
50 | float: left;
51 | border-right: 1px solid rgba(0, 0, 0, 0.1);
52 | }
53 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-menu .kf-editor-ui-keyboard-menu-list {
54 | width: 100%;
55 | height: 100%;
56 | padding: 26px;
57 | background: #eceff1;
58 | overflow: hidden;
59 | }
60 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-menu .kf-editor-ui-keyboard-menu-list .kf-editor-ui-keyboard-menu-list-item {
61 | width: 183px;
62 | height: 75px;
63 | margin: 19px;
64 | text-align: center;
65 | line-height: 75px;
66 | font-size: 16px;
67 | color: #49494d;
68 | cursor: pointer;
69 | float: left;
70 | font-size: 34px;
71 | }
72 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-menu .kf-editor-ui-keyboard-menu-list .kf-editor-ui-keyboard-menu-list-item-active {
73 | background: #ffab30;
74 | border-radius: 8px;
75 | color: #ffffff;
76 | }
77 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-panel {
78 | width: 1425px;
79 | height: 100%;
80 | float: left;
81 | }
82 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-panel .kf-editor-ui-keyboard-panel-list {
83 | width: 100%;
84 | height: 100%;
85 | padding: 14px 0 0 33px;
86 | overflow: hidden;
87 | position: relative;
88 | transition: top 0.3s linear;
89 | }
90 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-panel .kf-editor-ui-keyboard-panel-list .kf-editor-ui-keyboard-panel-list-item {
91 | width: 148px;
92 | height: 122px;
93 | margin: 12px;
94 | text-align: center;
95 | line-height: 121px;
96 | font-size: 16px;
97 | color: #49494d;
98 | cursor: pointer;
99 | float: left;
100 | position: relative;
101 | }
102 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-panel .kf-editor-ui-keyboard-panel-list .kf-editor-ui-keyboard-panel-list-item:active::before {
103 | content: '';
104 | display: block;
105 | position: absolute;
106 | width: 100%;
107 | height: 100%;
108 | background-color: rgba(64, 75, 80, 0.15);
109 | }
110 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page {
111 | width: 212px;
112 | height: 100%;
113 | float: left;
114 | }
115 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list {
116 | width: 100%;
117 | height: 100%;
118 | padding: 14px 0px 26px 0;
119 | overflow: hidden;
120 | }
121 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item {
122 | width: 100%;
123 | height: 122px;
124 | margin: 12px;
125 | float: left;
126 | font-size: 46px;
127 | color: #ff9800;
128 | text-align: center;
129 | line-height: 122px;
130 | cursor: pointer;
131 | }
132 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-delete {
133 | width: 100%;
134 | height: 122px;
135 | background: url('https://store-g1.seewo.com/easiclass-public/a7ea5932b621487aaa1c8b9dd4e58cb0') center no-repeat;
136 | background-size: initial;
137 | }
138 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-delete:active {
139 | background-color: rgba(64, 75, 80, 0.15);
140 | }
141 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-prev {
142 | position: relative;
143 | }
144 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-prev::before {
145 | content: ' ';
146 | display: block;
147 | width: 100%;
148 | height: 100%;
149 | background: url('https://store-g1.seewo.com/easiclass-public/27257523137e47499311266d0f872c8b') center no-repeat;
150 | background-size: initial;
151 | }
152 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-prev:not(.kf-editor-ui-keyboard-page-list-item-disabled):active {
153 | background-color: rgba(64, 75, 80, 0.15);
154 | }
155 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-next {
156 | position: relative;
157 | }
158 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-next::before {
159 | content: ' ';
160 | display: block;
161 | width: 100%;
162 | height: 100%;
163 | background: url('https://store-g1.seewo.com/easiclass-public/3c2faacfac52472ea6cb522a34aa04db') center no-repeat;
164 | background-size: initial;
165 | }
166 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-next:not(.kf-editor-ui-keyboard-page-list-item-disabled):active {
167 | background-color: rgba(64, 75, 80, 0.15);
168 | }
169 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-disabled::before {
170 | opacity: 0.5;
171 | }
172 | .android .kf-editor-edit-keyboard .kf-editor-ui-keyboard-footer {
173 | display: none;
174 | }
175 |
--------------------------------------------------------------------------------
/src/editor/editor.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 编辑器主体结构
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 | Utils = require( "base/utils" ),
9 | bundle = require('bundle'),
10 | Messager = require('editor/Message'),
11 | defaultOpt = {
12 | formula: {
13 | fontsize: 50,
14 | autoresize: false
15 | },
16 | ui: {
17 | zoom: true,
18 | maxzoom: 2,
19 | minzoom: 1
20 | }
21 |
22 | };
23 |
24 | // 同步组件列表
25 | var COMPONENTS = {},
26 | // 异步组件列表
27 | ResourceManager = require( "kf" ).ResourceManager;
28 |
29 | var KFEditor = kity.createClass( 'KFEditor', {
30 |
31 | constructor: function ( container, opt ) {
32 |
33 | this.options = Utils.extend( true, {}, defaultOpt, opt );
34 |
35 | this.FormulaClass = null;
36 | // 就绪状态
37 | this._readyState = false;
38 | this._callbacks = [];
39 |
40 | this.container = container;
41 | this.services = {};
42 | this.commands = {};
43 |
44 | this.initResource();
45 | this.initWebService();
46 | },
47 |
48 | isReady: function () {
49 |
50 | return !!this._readyState;
51 |
52 | },
53 |
54 | triggerReady: function () {
55 |
56 | var cb = null,
57 | _self = this;
58 |
59 | while ( cb = this._callbacks.shift() ) {
60 | cb.call( _self, _self );
61 | }
62 |
63 | },
64 |
65 | ready: function ( cb ) {
66 |
67 | if ( this._readyState ) {
68 | cb.call( this, this );
69 | } else {
70 | this._callbacks.push( cb );
71 | }
72 |
73 | },
74 |
75 | getContainer: function () {
76 | return this.container;
77 | },
78 |
79 | getDocument: function () {
80 | return this.container.ownerDocument;
81 | },
82 |
83 | getFormulaClass: function () {
84 | return this.FormulaClass;
85 | },
86 |
87 | getOptions: function () {
88 | return this.options;
89 | },
90 |
91 | initResource: function () {
92 |
93 | var _self = this;
94 |
95 | ResourceManager.ready( function ( Formula ) {
96 |
97 | _self.FormulaClass = Formula;
98 | _self.initComponents();
99 | _self._readyState = true;
100 | _self.triggerReady();
101 |
102 | }, this.options.resource );
103 |
104 | },
105 |
106 | initWebService: function () {
107 | const { WebService, CustomWebService } = bundle;
108 | if (this.options.ui.protocol === 'webview') {
109 | this.eclassWebService = new WebService('webview');
110 | } else if (this.options.ui.protocol === 'iframe') {
111 | this.eclassWebService = new WebService('iframe');
112 | } else if (this.options.ui.protocol === 'documentEvent'){
113 | this.eclassWebService = new CustomWebService({ messager: new Messager() });
114 | }
115 | this.eclassWebService.on('common.readFormula', (msg) => {
116 | if (msg.body.formula) {
117 | this.execCommand('render', msg.body.formula);
118 | }
119 | this.execCommand('focus', true);
120 | })
121 | this.eclassWebService.on('common.clearFormula', () => {
122 | this.execCommand('render', '\\placeholder');
123 | this.execCommand('menu.clearType');
124 | })
125 | this.registerCommand('ready', this, function () {
126 | this.eclassWebService.send({
127 | type: 'common.ready'
128 | });
129 | })
130 | },
131 |
132 | /**
133 | * 初始化同步组件
134 | */
135 | initComponents: function () {
136 |
137 | var _self = this;
138 |
139 | Utils.each( COMPONENTS, function ( Component, name ) {
140 |
141 | new Component( _self, _self.options[ name ] );
142 |
143 | } );
144 |
145 | },
146 |
147 | requestService: function ( serviceName, args ) {
148 | var serviceObject = getService.call( this, serviceName );
149 |
150 | return serviceObject.service[ serviceObject.key ].apply( serviceObject.provider, [].slice.call( arguments, 1 ) );
151 |
152 | },
153 |
154 | request: function ( serviceName ) {
155 |
156 | var serviceObject = getService.call( this, serviceName );
157 |
158 | return serviceObject.service;
159 |
160 | },
161 |
162 | registerService: function ( serviceName, provider, serviceObject ) {
163 |
164 | var key = null;
165 |
166 | for ( key in serviceObject ) {
167 |
168 | if ( serviceObject[ key ] && serviceObject.hasOwnProperty( key ) ) {
169 | serviceObject[ key ] = Utils.proxy( serviceObject[ key ], provider );
170 | }
171 |
172 | }
173 |
174 | this.services[ serviceName ] = {
175 | provider: provider,
176 | key: key,
177 | service: serviceObject
178 | };
179 |
180 | },
181 |
182 | registerCommand: function ( commandName, executor, execFn ) {
183 |
184 | this.commands[ commandName ] = {
185 | executor: executor,
186 | execFn: execFn
187 | };
188 |
189 | },
190 |
191 | execCommand: function ( commandName, args ) {
192 | console.log('[execCommand]', commandName);
193 | var commandObject = this.commands[ commandName ];
194 |
195 | if ( !commandObject ) {
196 | throw new Error( 'KFEditor: not found command, ' + commandName );
197 | }
198 |
199 | return commandObject.execFn.apply( commandObject.executor, [].slice.call( arguments, 1 ) );
200 |
201 | }
202 |
203 | } );
204 |
205 | function getService ( serviceName ) {
206 |
207 | var serviceObject = this.services[ serviceName ];
208 |
209 | if ( !serviceObject ) {
210 | throw new Error( 'KFEditor: not found service, ' + serviceName );
211 | }
212 |
213 | return serviceObject;
214 |
215 | }
216 |
217 | Utils.extend( KFEditor, {
218 |
219 | registerComponents: function ( name, component ) {
220 |
221 | COMPONENTS[ name ] = component;
222 |
223 | }
224 |
225 | } );
226 |
227 | return KFEditor;
228 |
229 | } );
230 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*global module:false*/
2 | module.exports = function (grunt) {
3 | grunt.initConfig({
4 | // Metadata.
5 | pkg: grunt.file.readJSON('package.json'),
6 |
7 | // npm模块处理
8 | browserify: {
9 | dist: {
10 | files: {
11 | 'dist/npmBundle.js': ['dev-lib/npmEntry.js'],
12 | },
13 | },
14 | },
15 | copy: {
16 | image: {
17 | files: [
18 | {
19 | expand: true,
20 | src: [
21 | 'assets/images/**/*.{png,jpg,jpeg,gif,svg}',
22 | 'assets/images/*.{png,jpg,jpeg,gif,svg}',
23 | 'resource/*',
24 | ],
25 | dest: 'dist/',
26 | },
27 | ],
28 | },
29 | lib: {
30 | files: [
31 | {
32 | expand: true,
33 | cwd: 'lib/',
34 | src: ['*.js'],
35 | dest: 'dist/',
36 | },
37 | ],
38 | },
39 | },
40 |
41 | babel: {
42 | dev: {
43 | options: {
44 | sourceMap: true,
45 | },
46 | files: [
47 | {
48 | expand: true,
49 | src: ['src/**/*.js'], //所有js文件
50 | dest: '.tmp_build/', //输出到此临时目录下
51 | },
52 | ],
53 | },
54 | prod: {
55 | options: {
56 | // sourceMap: true,
57 | },
58 | files: [
59 | {
60 | expand: true,
61 | src: ['src/**/*.js'], //所有js文件
62 | dest: '.tmp_build/', //输出到此临时目录下
63 | },
64 | ],
65 | },
66 | },
67 |
68 | less: {
69 | compile: {
70 | files: [
71 | {
72 | expand: true,
73 | src: ['assets/styles/**/*.less'],
74 | dest: 'dist/',
75 | ext: '.css',
76 | },
77 | ],
78 | },
79 | },
80 | cssmin: {
81 | options: {
82 | stripBanners: true, //合并时允许输出头部信息
83 | banner:
84 | '/*!<%= pkg.file %> - <%= pkg.version %>-' +
85 | '<%=grunt.template.today("yyyy-mm-dd") %> */\n',
86 | },
87 | build: {
88 | src: 'dist/assets/styles/**/*.css', //压缩
89 | dest: 'dist/index.min.css', //dest 是目的地输出
90 | },
91 | },
92 |
93 | // 最终代码合并
94 | concat: {
95 | full: {
96 | options: {
97 | banner:
98 | '/*!\n' +
99 | ' * ====================================================\n' +
100 | ' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
101 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
102 | '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
103 | ' * GitHub: <%= pkg.repository.url %> \n' +
104 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
105 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n' +
106 | ' * ====================================================\n' +
107 | ' */\n\n' +
108 | '(function () {\n',
109 |
110 | footer: '})();',
111 | },
112 |
113 | dest: 'dist/' + getFileName(),
114 | src: ['.tmp_build/kf.tmp.js', 'dev-lib/exports.js'],
115 | },
116 | },
117 |
118 | // 压缩
119 | uglify: {
120 | options: {
121 | banner:
122 | '/*!\n' +
123 | ' * ====================================================\n' +
124 | ' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
125 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
126 | '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
127 | ' * GitHub: <%= pkg.repository.url %> \n' +
128 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
129 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n' +
130 | ' * ====================================================\n' +
131 | ' */\n',
132 |
133 | beautify: {
134 | ascii_only: true,
135 | },
136 | },
137 |
138 | minimize: {
139 | dest: 'dist/' + getFileName(true),
140 | src: 'dist/' + getFileName(),
141 | },
142 | lib: {
143 | expand: true,
144 | cwd: 'dev-lib/',
145 | src: ['kity-formula-render.all.js', 'kity-formula-parser.all.js'],
146 | dest: 'lib/',
147 | ext: '.all.min.js',
148 | },
149 | },
150 |
151 | // 模块依赖合并
152 | dependence: {
153 | replace: {
154 | options: {
155 | base: '.tmp_build/src',
156 | entrance: 'kf.start',
157 | },
158 |
159 | files: [
160 | {
161 | src: ['.tmp_build/**/*.js', 'dev-lib/start.js'],
162 | dest: '.tmp_build/kf.tmp.js',
163 | },
164 | ],
165 | },
166 | },
167 |
168 | // hint检查
169 | jshint: {
170 | options: {
171 | ignores: [
172 | '.tmp_build/src/base/*.js',
173 | '.tmp_build/src/parse/*.js',
174 | '.tmp_build/src/ui/ui-impl/**/*.js',
175 | ],
176 | jshintrc: '.jshintrc',
177 | },
178 | check: ['.tmp_build/**/*.js'],
179 | },
180 |
181 | // 临时目录清理
182 | clean: {
183 | temp: {
184 | src: ['.tmp_build'],
185 | },
186 | dist: {
187 | src: ['dist'],
188 | },
189 | },
190 | });
191 |
192 | function getFileName(isMin) {
193 | return isMin ? 'kityformula-editor.all.min.js' : 'kityformula-editor.all.js';
194 | }
195 |
196 | // These plugins provide necessary tasks.
197 | grunt.loadNpmTasks('grunt-babel');
198 | grunt.loadNpmTasks('grunt-contrib-concat');
199 | grunt.loadNpmTasks('grunt-contrib-uglify');
200 | grunt.loadNpmTasks('grunt-contrib-clean');
201 | grunt.loadNpmTasks('grunt-contrib-jshint');
202 | grunt.loadNpmTasks('grunt-module-dependence');
203 | grunt.loadNpmTasks('grunt-contrib-less');
204 | grunt.loadNpmTasks('grunt-contrib-cssmin');
205 | grunt.loadNpmTasks('grunt-contrib-copy');
206 | grunt.loadNpmTasks('grunt-browserify');
207 |
208 | // task list.
209 | grunt.registerTask('default', [
210 | 'clean:dist',
211 | 'browserify',
212 | 'copy',
213 | 'less',
214 | 'cssmin',
215 | 'babel:dev',
216 | 'jshint',
217 | 'dependence:replace',
218 | 'concat:full',
219 | 'uglify:minimize',
220 | 'clean:temp',
221 | ]);
222 | grunt.registerTask('updateLib', ['uglify:lib']);
223 | grunt.registerTask('build', [
224 | 'clean:dist',
225 | 'browserify',
226 | 'copy',
227 | 'less',
228 | 'cssmin',
229 | 'babel:prod',
230 | 'jshint',
231 | 'dependence:replace',
232 | 'concat:full',
233 | 'uglify:minimize',
234 | 'clean:temp',
235 | ]);
236 | };
237 |
--------------------------------------------------------------------------------
/assets/theme/default/fui.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * ====================================================
3 | * Themes file * Flex UI - v1.0.0 - 2014-07-28
4 | * https://github.com/fex-team/fui
5 | * GitHub: https://github.com/fex-team/fui.git
6 | * Copyright (c) 2014 Baidu Kity Group; Licensed MIT
7 | * ====================================================
8 | */
9 |
10 | .fui-widget{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none;color:#000;line-height:1.5;font-size:12px;font-family:ff-tisa-web-pro-1,ff-tisa-web-pro-2,"Lucida Grande","Hiragino Sans GB","Hiragino Sans GB W3","Microsoft YaHei","WenQuanYi Micro Hei",sans-serif;-webkit-font-smoothing:antialiased;outline:0;display:inline-block;vertical-align:top;position:relative;top:0;left:0}.fui-widget.fui-selectable{-webkit-user-select:text;-khtml-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;-webkit-user-drag:text}.fui-widget *{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none}.fui-widget.fui-disabled{opacity:.3!important}.fui-widget.fui-hide{display:none!important}.fui-widget.fui-mask-animate{-webkit-transition:all .2s}.fui-widget.fui-mask-hint{-webkit-transform:perspective(600px) translateZ(30px)}.fui-container{overflow:hidden;position:relative;top:0;left:0}.fui-container.fui-disabled{opacity:1!important}.fui-button-menu{border:1px solid #fff}.fui-button-menu:HOVER{border-color:#d5e1f2}.fui-button-menu.fui-button-active{border-color:#d5e1f2;background:#d5e1f2}.fui-button-menu.fui-button-active .fui-button{background:#d5e1f2}.fui-button-menu.fui-layout-bottom,.fui-button-menu.fui-layout-top{text-align:center}.fui-button-menu.fui-layout-bottom .fui-open-btn,.fui-button-menu.fui-layout-top .fui-open-btn{display:block}.fui-button{overflow:hidden;cursor:default;font-size:0}.fui-button ._layout .fui-icon,.fui-button ._layout .fui-label{display:block}.fui-button .fui-icon,.fui-button .fui-label{display:inline-block;vertical-align:middle}.fui-button.fui-button-layout-bottom .fui-icon,.fui-button.fui-button-layout-bottom .fui-label,.fui-button.fui-button-layout-top .fui-icon,.fui-button.fui-button-layout-top .fui-label{display:block}.fui-button:HOVER{background-color:#d5e1f2!important;color:#000!important}.fui-button:ACTIVE{background-color:#87a9da!important;color:#000!important}.fui-button.fui-disabled:ACTIVE,.fui-button.fui-disabled:HOVER{background-color:#fff!important;color:#000!important}.fui-colorpicker{background-color:#fff}.fui-colorpicker-container{border:1px solid #d3d3d3}.fui-colorpicker-container .fui-colorpicker-title{background:#eee;padding:2px 4px}.fui-colorpicker-container .fui-colorpicker-colors{margin:0;padding:0;font-size:0;line-height:0}.fui-colorpicker-container .fui-colorpicker-colors-line0{margin-bottom:3px}.fui-colorpicker-container .fui-colorpicker-item{display:inline-block;margin:0 2px;width:13px;height:13px;border-style:solid;border-width:1px}.fui-colorpicker-container .fui-colorpicker-commoncolor,.fui-colorpicker-container .fui-colorpicker-standardcolor{margin:4px 3px;white-space:nowrap}.fui-colorpicker-container .fui-colorpicker-toolbar{margin:4px;height:27px}.fui-colorpicker-container .fui-colorpicker-toolbar .fui-colorpicker-preview{display:inline-block;height:25px;line-height:25px;width:120px;border:1px solid #d3d3d3}.fui-colorpicker-container .fui-colorpicker-toolbar .fui-colorpicker-clear{display:inline-block;height:25px;line-height:25px;width:60px;border:1px solid #d3d3d3;font-size:12px;text-align:center;position:absolute;right:5px;cursor:pointer}.fui-dialog{position:fixed;top:-1000000px;left:-100000px;border:1px solid #B1B1B1;background:#fff}.fui-dialog .fui-panel-content{width:auto!important;height:auto!important;padding:2px}.fui-dialog .fui-dialog-caption{margin:0;padding:5px;font-size:16px;font-weight:400;line-height:1;display:inline-block}.fui-dialog .fui-dialog-head .fui-close-button{float:right}.fui-dialog .fui-dialog-head .fui-close-button .fui-close-button-icon{width:16px;height:16px;background:url(images/close.png) no-repeat}.fui-drop-panel{border:1px solid #d3d3d3;overflow:hidden;position:relative}.fui-drop-panel .fui-drop-panel-content{display:inline-block}.fui-drop-panel .fui-drop-panel-placeholder{display:none}.fui-drop-panel .fui-drop-panel-button{border-left:1px solid #d3d3d3;visibility:visible}.fui-drop-panel .fui-drop-panel-button:HOVER{border-color:#d5e1f2}.fui-drop-panel .fui-drop-panel-button:ACTIVE{border-color:#87a9da}.fui-drop-panel:HOVER{border-color:#d5e1f2}.fui-drop-panel:HOVER .fui-drop-panel-button{border-left-color:#d5e1f2}.fui-drop-panel:ACTIVE{border-color:#87a9da}.fui-drop-panel:ACTIVE .fui-drop-panel-button{border-left-color:#d5e1f2}.fui-drop-panel.fui-drop-panel-open{overflow:visible}.fui-drop-panel.fui-drop-panel-open .fui-drop-panel-content{border:1px solid #d3d3d3;position:absolute;top:-1px;left:-1px}.fui-drop-panel.fui-drop-panel-open .fui-drop-panel-button{visibility:hidden}.fui-drop-panel.fui-drop-panel-open .fui-drop-panel-placeholder{display:inline-block}.fui-drop-panel-popup{border:1px solid #d3d3d3}.fui-drop-panel-popup:HOVER{border-color:#d5e1f2}.fui-drop-panel-popup:HOVER .fui-drop-panel-button{border-left-color:#d5e1f2}.fui-drop-panel-popup:ACTIVE{border-color:#87a9da}.fui-drop-panel-popup:ACTIVE .fui-drop-panel-button{border-left-color:#d5e1f2}.fui-icon{text-align:center;font-size:0}.fui-icon img{display:inline-block}.fui-input-button{border:1px solid #ababab}.fui-input-button .fui-input{vertical-align:middle;border:none!important}.fui-input-button .fui-button{vertical-align:middle}.fui-input-button:ACTIVE,.fui-input-button:HOVER{border-color:#87a9da}.fui-input{border:1px solid #d3d3d3;padding:1px;margin:0}.fui-input:FOCUS,.fui-input:HOVER{border-color:#4d90fe!important}.fui-item{font-size:0}.fui-item .fui-icon,.fui-item .fui-label{vertical-align:middle}.fui-item.fui-item-selected{background:#87a9da}.fui-label-panel .fui-label-panel-label{width:100%;color:#666}.fui-label-panel.fui-no-position .fui-label-panel-label{position:static!important}.fui-label-panel.fui-layout-bottom .fui-label-panel-label{position:absolute;bottom:0;left:0;top:auto;z-index:2}.fui-label{cursor:default;display:inline-block;white-space:nowrap}.fui-mask{position:fixed;z-index:99998}.fui-menu{background-color:#fff;border:1px solid #d3d3d3}.fui-menu .fui-item{padding:2px 5px;display:block!important}.fui-menu .fui-item:HOVER{background:#d5e1f2}.fui-panel{display:inline-block;vertical-align:top;overflow-y:auto;overflow-x:hidden}.fui-panel .fui-panel-content{position:relative;top:0;left:0;width:100%;height:100%}.fui-panel.fui-container-column{font-size:0}.fui-panel.fui-container-column .fui-column{display:block}.fui-ppanel::-webkit-scrollbar{width:15px}.fui-ppanel::-webkit-scrollbar-button:end:decrement,.fui-ppanel::-webkit-scrollbar-button:end:increment,.fui-ppanel::-webkit-scrollbar-button:start:decrement,.fui-ppanel::-webkit-scrollbar-button:start:increment,.fui-ppanel::-webkit-scrollbar-thumb{border:1px solid #e7e7e7}.fui-ppanel.fui-ppanel-position{position:fixed;z-index:99999}.fui-separator{background:#6d6d6d}.fui-spin-button .fui-spin-down-btn .fui-icon,.fui-spin-button .fui-spin-up-btn .fui-icon{width:16px;height:9px;background:url(images/up.png) 3px 1.5px no-repeat}.fui-spin-button .fui-spin-down-btn .fui-icon{background-image:url(images/down.png)}.fui-tabs .fui-selected{background-color:#d5e1f2}.fui-toggle-button.fui-button-pressed{background-color:#aec5e6}.fui-toggle-button.fui-button-pressed.fui-disabled{background-color:#aec5e6!important}
--------------------------------------------------------------------------------
/src/parse/parser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 数学公式解析器
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var KFParser = require( "kf" ).Parser,
8 | kity = require( "kity" ),
9 | CURSOR_CHAR = require( "sysconf" ).cursorCharacter,
10 | VGROUP_LIST = require( "parse/vgroup-def" ),
11 | ROOT_P_TEXT = require( "sysconf" ).rootPlaceholder.content,
12 | COMBINATION_NAME = "combination",
13 | PID_PREFIX = "_kf_editor_",
14 | GROUP_TYPE = require( "def/group-type" ),
15 | PID = 0;
16 |
17 | var Parser = kity.createClass( "Parser", {
18 |
19 | constructor: function ( kfEditor ) {
20 |
21 | this.kfEditor = kfEditor;
22 |
23 | this.callBase();
24 | // kityformula 解析器
25 | this.kfParser = KFParser.use( "latex" );
26 |
27 | this.initKFormulExtension();
28 |
29 | this.pid = generateId();
30 | this.groupRecord = 0;
31 |
32 | this.tree = null;
33 |
34 | this.isResetId = true;
35 |
36 | this.initServices();
37 |
38 | },
39 |
40 | parse: function ( str, isResetId ) {
41 |
42 | var parsedResult = null;
43 |
44 | this.isResetId = !!isResetId;
45 |
46 | if ( this.isResetId ) {
47 | this.resetGroupId();
48 | }
49 |
50 | parsedResult = this.kfParser.parse( str );
51 |
52 | // 对解析出来的结果树做适当的处理,使得编辑器能够更容易地识别当前表达式的语义
53 | supplementTree( this, parsedResult.tree );
54 |
55 | return parsedResult;
56 |
57 | },
58 |
59 | // 序列化, parse的逆过程
60 | serialization: function ( tree ) {
61 |
62 | return this.kfParser.serialization( tree );
63 |
64 | },
65 |
66 | initServices: function () {
67 |
68 | this.kfEditor.registerService( "parser.parse", this, {
69 | parse: this.parse
70 | } );
71 |
72 | this.kfEditor.registerService( "parser.latex.serialization", this, {
73 | serialization: this.serialization
74 | } );
75 |
76 | },
77 |
78 | getKFParser: function () {
79 |
80 | return this.kfParser;
81 |
82 | },
83 |
84 | // 初始化KF扩展
85 | initKFormulExtension: function () {
86 |
87 | require( "kf-ext/extension" ).ext( this );
88 |
89 | },
90 |
91 | resetGroupId: function () {
92 | this.groupRecord = 0;
93 | },
94 |
95 | getGroupId: function () {
96 | return this.pid + "_" + ( ++this.groupRecord );
97 | }
98 |
99 | } );
100 |
101 | // 把解析树丰富成公式编辑器的语义树, 该语义化的树同时也是合法的解析树
102 | function supplementTree ( parser, tree, parentTree ) {
103 |
104 | var currentOperand = null,
105 | // 只有根节点才没有parentTree
106 | isRoot = !parentTree;
107 |
108 | tree.attr = tree.attr || {};
109 |
110 | tree.attr.id = parser.getGroupId();
111 |
112 | if ( isRoot ) {
113 | processRootGroup( parser, tree );
114 | // 根占位符处理, 附加label
115 | } else if ( parentTree.attr[ "data-root" ] && tree.name === "placeholder" && onlyPlaceholder( parentTree.operand ) ) {
116 | tree.attr.label = ROOT_P_TEXT;
117 | }
118 |
119 | for ( var i = 0, len= tree.operand.length; i < len; i++ ) {
120 |
121 | currentOperand = tree.operand[ i ];
122 |
123 | if ( isVirtualGroup( tree ) ) {
124 | // 虚拟组处理
125 | processVirtualGroup( parser, i, tree, currentOperand );
126 | } else {
127 | processGroup( parser, i, tree, currentOperand );
128 | }
129 |
130 | }
131 |
132 | return tree;
133 |
134 | }
135 |
136 | function generateId () {
137 | return PID_PREFIX + ( ++PID );
138 | }
139 |
140 | function processRootGroup ( parser, tree ) {
141 |
142 | // 如果isResetId为false, 表示当前生成的是子树
143 | // 则不做data-root标记, 同时更改该包裹的类型为GROUP_TYPE.VIRTUAL
144 | if ( !parser.isResetId ) {
145 | tree.attr[ "data-type" ] = GROUP_TYPE.VIRTUAL;
146 | } else {
147 | tree.attr[ "data-root" ] = "true";
148 | }
149 |
150 | }
151 |
152 | /**
153 | * 虚拟组处理
154 | * @param parser 解析器实例
155 | * @param index 当前处理的子树所在其父节点的索引位置
156 | * @param tree 需要处理的树父树
157 | * @param subtree 当前需要处理的树
158 | */
159 | function processVirtualGroup ( parser, index, tree, subtree ) {
160 |
161 | // 括号组的前两个元素不用处理
162 | if ( tree.name === "brackets" && index < 2 ) {
163 | return;
164 | // 函数的第一个参数不处理
165 | } else if ( tree.name === "function" && index === 0 ) {
166 | return;
167 | }
168 |
169 | tree.attr[ "data-type" ] = GROUP_TYPE.VIRTUAL;
170 |
171 | if ( !subtree ) {
172 |
173 | tree.operand[ index ] = subtree;
174 |
175 | } else if ( typeof subtree === "string" ) {
176 |
177 | tree.operand[ index ] = createGroup( parser );
178 |
179 | tree.operand[ index ].operand[ 0 ] = subtree;
180 |
181 | } else if ( isPlaceholder( subtree ) ) {
182 |
183 | tree.operand[ index ] = createGroup( parser );
184 |
185 | tree.operand[ index ].operand[ 0 ] = supplementTree( parser, subtree, tree.operand[ index ] );
186 |
187 | } else {
188 |
189 | tree.operand[ index ] = supplementTree( parser, subtree, tree );
190 |
191 | }
192 |
193 | }
194 |
195 | function processGroup ( parser, index, tree, subtree ) {
196 |
197 | tree.attr[ "data-type" ] = GROUP_TYPE.GROUP;
198 |
199 | if ( !subtree || typeof subtree === "string" ) {
200 |
201 | tree.operand[ index ] = subtree;
202 |
203 | // 特殊文本处理, 比如mathcal、mathrm等
204 | } else if ( subtree.name === "text" ) {
205 |
206 | tree.operand[ index ] = subtree;
207 |
208 | } else {
209 |
210 | tree.operand[ index ] = supplementTree( parser, subtree, tree );
211 |
212 | }
213 |
214 | }
215 |
216 | /**
217 | * 判断给定的操作数列表内是否仅有一个占位符存在, 该判断仅支持对根内部的表达式做判断
218 | * @param operands 操作数列表
219 | * @returns {boolean}
220 | */
221 | function onlyPlaceholder ( operands ) {
222 |
223 | var result = 1;
224 |
225 | if ( operands.length > 3 ) {
226 | return false;
227 | }
228 |
229 | for ( var i = 0, len = operands.length; i < len; i++ ) {
230 |
231 | if ( operands[ i ] === CURSOR_CHAR ) {
232 | continue;
233 | }
234 |
235 | if ( operands[ i ] && operands[ i ].name === "placeholder" ) {
236 | result--;
237 | }
238 |
239 | }
240 |
241 | return !result;
242 |
243 | }
244 |
245 | // 判断给定的树是否是一个虚拟组
246 | function isVirtualGroup ( tree ) {
247 |
248 | return !!VGROUP_LIST[ tree.name ];
249 |
250 | }
251 |
252 | // 判断给定的树是否是一个占位符
253 | function isPlaceholder ( tree ) {
254 |
255 | return tree.name === "placeholder";
256 |
257 | }
258 |
259 | // 创建一个新组, 组的内容是空
260 | function createGroup ( parser ) {
261 |
262 | return {
263 | name: COMBINATION_NAME,
264 | attr: {
265 | "data-type": GROUP_TYPE.GROUP,
266 | id: parser.getGroupId()
267 | },
268 | operand: []
269 | };
270 |
271 | }
272 |
273 | return Parser;
274 |
275 | } );
276 |
277 |
--------------------------------------------------------------------------------
/src/position/position.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 定位模块
3 | */
4 |
5 |
6 | define( function ( require ) {
7 |
8 | var kity = require( "kity" ),
9 |
10 | kfUtils = require( "base/utils" ),
11 |
12 | PositionComponenet = kity.createClass( 'PositionComponenet', {
13 |
14 | constructor: function ( kfEditor ) {
15 |
16 | this.kfEditor = kfEditor;
17 |
18 | this.initServices();
19 |
20 | },
21 |
22 | initServices: function () {
23 |
24 | this.kfEditor.registerService( "position.get.group", this, {
25 | getGroupByTarget: this.getGroupByTarget
26 | } );
27 |
28 | this.kfEditor.registerService( "position.get.index", this, {
29 | getIndexByTargetInGroup: this.getIndexByTargetInGroup
30 | } );
31 |
32 | this.kfEditor.registerService( "position.get.location.info", this, {
33 | getLocationInfo: this.getLocationInfo
34 | } );
35 |
36 | this.kfEditor.registerService( "position.get.parent.group", this, {
37 | getParentGroupByTarget: this.getParentGroupByTarget
38 | } );
39 |
40 | this.kfEditor.registerService( "position.get.wrap", this, {
41 | getWrap: this.getWrap
42 | } );
43 |
44 | this.kfEditor.registerService( "position.get.area", this, {
45 | getAreaByCursorInGroup: this.getAreaByCursorInGroup
46 | } );
47 |
48 | this.kfEditor.registerService( "position.get.group.info", this, {
49 | getGroupInfoByNode: this.getGroupInfoByNode
50 | } );
51 |
52 | this.kfEditor.registerService( "position.get.parent.info", this, {
53 | getParentInfoByNode: this.getParentInfoByNode
54 | } );
55 |
56 | },
57 |
58 | getGroupByTarget: function ( target ) {
59 |
60 | var groupDom = getGroup( target, false, false );
61 |
62 | if ( groupDom ) {
63 | return this.kfEditor.requestService( "syntax.get.group.content", groupDom.id );
64 | }
65 |
66 | return null;
67 |
68 | },
69 |
70 | /**
71 | * 根据给定的组节点和目标节点, 获取目标节点在组节点内部的索引
72 | * @param groupNode 组节点
73 | * @param targetNode 目标节点
74 | */
75 | getIndexByTargetInGroup: function ( groupNode, targetNode ) {
76 |
77 | var groupInfo = this.kfEditor.requestService( "syntax.get.group.content", groupNode.id ),
78 | index = -1;
79 |
80 | kity.Utils.each( groupInfo.content, function ( child, i ) {
81 |
82 | index = i;
83 |
84 | if ( kfUtils.contains( child, targetNode ) ) {
85 | return false;
86 | }
87 |
88 | } );
89 |
90 | return index;
91 |
92 | },
93 |
94 | /**
95 | * 根据给定的组节点和给定的偏移值,获取当前偏移值在组中的区域值。
96 | * 该区域值的取值为true时, 表示在右区域, 反之则在左区域
97 | * @param groupNode 组节点
98 | * @param offset 偏移值
99 | */
100 | getAreaByCursorInGroup: function ( groupNode, offset ) {
101 |
102 | var groupRect = kfUtils.getRect( groupNode );
103 |
104 | return groupRect.left + groupRect.width / 2 < offset;
105 |
106 | },
107 |
108 | getLocationInfo: function ( distance, groupInfo ) {
109 |
110 | var index = -1,
111 | children = groupInfo.content,
112 | boundingRect = null;
113 |
114 | for ( var i = children.length - 1, child = null; i >= 0; i-- ) {
115 |
116 | index = i;
117 |
118 | child = children[ i ];
119 |
120 | boundingRect = kfUtils.getRect( child );
121 |
122 | if ( boundingRect.left < distance ) {
123 |
124 | if ( boundingRect.left + boundingRect.width / 2 < distance ) {
125 | index += 1;
126 | }
127 |
128 | break;
129 |
130 | }
131 |
132 | }
133 |
134 | return index;
135 |
136 | },
137 |
138 | getParentGroupByTarget: function ( target ) {
139 |
140 | var groupDom = getGroup( target, true, false );
141 |
142 | if ( groupDom ) {
143 | return this.kfEditor.requestService( "syntax.get.group.content", groupDom.id );
144 | }
145 |
146 | return null;
147 |
148 | },
149 |
150 | getWrap: function ( node ) {
151 |
152 | return getGroup( node, true, true );
153 |
154 | },
155 |
156 | /**
157 | * 给定一个节点, 获取其节点所属的组及其在该组内的偏移
158 | * @param target 目标节点
159 | */
160 | getGroupInfoByNode: function ( target ) {
161 |
162 | var result = {},
163 | containerNode = getGroup( target, false, false ),
164 | containerInfo = null;
165 |
166 | if ( !containerNode ) {
167 | return null;
168 | }
169 |
170 | containerInfo = this.kfEditor.requestService( "syntax.get.group.content", containerNode.id );
171 |
172 | for ( var i = 0, len = containerInfo.content.length; i < len; i++) {
173 |
174 | result.index = i;
175 |
176 | if ( kfUtils.contains( containerInfo.content[ i ], target ) ) {
177 | break;
178 | }
179 |
180 | }
181 |
182 | result.group = containerInfo;
183 |
184 | return result;
185 |
186 | },
187 |
188 | /**
189 | * 给定一个节点, 获取其节点所属的直接包含组及其在该直接包含组内的偏移
190 | * @param target 目标节点
191 | */
192 | getParentInfoByNode: function ( target ) {
193 |
194 | var group = getGroup( target, true, false );
195 |
196 | group = this.kfEditor.requestService( "syntax.get.group.content", group.id );
197 |
198 | return {
199 | group: group,
200 | index: group.content.indexOf( target )
201 | };
202 |
203 | }
204 |
205 | } );
206 |
207 | /**
208 | * 获取给定节点元素所属的组
209 | * @param node 当前点击的节点
210 | * @param isAllowVirtual 是否允许选择虚拟组
211 | * @param isAllowWrap 是否允许选择目标节点的最小包裹单位
212 | * @returns {*}
213 | */
214 | function getGroup ( node, isAllowVirtual, isAllowWrap ) {
215 |
216 | var tagName = null;
217 |
218 | if ( !node.ownerSVGElement ) {
219 | return null;
220 | }
221 |
222 | node = node.parentNode;
223 |
224 | tagName = node.tagName.toLowerCase();
225 |
226 | if ( node && tagName !== "body" && tagName !== "svg" ) {
227 |
228 | if ( node.getAttribute( "data-type" ) === "kf-editor-group" ) {
229 | return node;
230 | }
231 |
232 | if ( isAllowVirtual && node.getAttribute( "data-type" ) === "kf-editor-virtual-group" ) {
233 | return node;
234 | }
235 |
236 | if ( isAllowWrap && node.getAttribute( "data-flag" ) !== null ) {
237 | return node;
238 | }
239 |
240 | return getGroup( node, isAllowVirtual, isAllowWrap );
241 |
242 | } else {
243 | return null;
244 | }
245 |
246 | }
247 |
248 | return PositionComponenet;
249 |
250 | } );
--------------------------------------------------------------------------------
/src/control/location.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 光标定位组件
3 | */
4 |
5 | define( function ( require, exports, module ) {
6 |
7 | var kity = require( "kity" );
8 |
9 | return kity.createClass( "LocationComponent", {
10 |
11 | constructor: function ( parentComponent, kfEditor ) {
12 |
13 | this.parentComponent = parentComponent;
14 | this.kfEditor = kfEditor;
15 |
16 | // 创建光标
17 | this.paper = this.getPaper();
18 | this.cursorShape = this.createCursor();
19 |
20 | this.initServices();
21 |
22 | this.initEvent();
23 |
24 | },
25 |
26 | getPaper: function () {
27 | return this.kfEditor.requestService( "render.get.paper" );
28 | },
29 |
30 | initServices: function () {
31 |
32 | // 重定位光标
33 | this.kfEditor.registerService( "control.cursor.relocation", this, {
34 | relocationCursor: this.updateCursor
35 | } );
36 |
37 | // 清除光标
38 | this.kfEditor.registerService( "control.cursor.hide", this, {
39 | hideCursor: this.hideCursor
40 | } );
41 |
42 | this.kfEditor.registerService( "control.reselect", this, {
43 | reselect: this.reselect
44 | } );
45 |
46 | this.kfEditor.registerService( "control.get.cursor.location", this, {
47 | getCursorLocation: this.getCursorLocation
48 | } );
49 |
50 | },
51 |
52 | createCursor: function () {
53 |
54 | var cursorShape = new kity.Rect( 1, 0, 0, 0 ).fill( "black" );
55 |
56 | cursorShape.setAttr( "style", "display: none" );
57 |
58 | this.paper.addShape( cursorShape );
59 |
60 | return cursorShape;
61 |
62 | },
63 |
64 | // 光标定位监听
65 | initEvent: function () {
66 |
67 | var eventServiceObject = this.kfEditor.request( "ui.canvas.container.event" ),
68 | _self = this;
69 |
70 | eventServiceObject.on( "mousedown", function ( e ) {
71 |
72 | e.preventDefault();
73 |
74 | _self.updateCursorInfo( e );
75 | _self.kfEditor.requestService( "control.update.input" );
76 | _self.reselect();
77 |
78 | } );
79 |
80 | },
81 |
82 | updateCursorInfo: function ( evt ) {
83 |
84 | var wrapNode = null,
85 | groupInfo = null,
86 | index = -1;
87 |
88 | // 有根占位符存在, 所有定位到定位到根占位符内部
89 | if ( this.kfEditor.requestService( "syntax.has.root.placeholder" ) ) {
90 |
91 | this.kfEditor.requestService( "syntax.update.record.cursor", {
92 | groupId: this.kfEditor.requestService( "syntax.get.root.group.info" ).id,
93 | startOffset: 0,
94 | endOffset: 1
95 | } );
96 |
97 | return false;
98 | }
99 |
100 | wrapNode = this.kfEditor.requestService( "position.get.wrap", evt.target );
101 |
102 | // 占位符处理, 选中该占位符
103 | if ( wrapNode && this.kfEditor.requestService( "syntax.is.placeholder.node", wrapNode.id ) ) {
104 | groupInfo = this.kfEditor.requestService( "position.get.group.info", wrapNode );
105 | this.kfEditor.requestService( "syntax.update.record.cursor", groupInfo.group.id, groupInfo.index, groupInfo.index + 1 );
106 | return;
107 | }
108 |
109 | groupInfo = this.kfEditor.requestService( "position.get.group", evt.target );
110 |
111 | if ( groupInfo === null ) {
112 | groupInfo = this.kfEditor.requestService( "syntax.get.root.group.info" );
113 | }
114 |
115 | index = this.getIndex( evt.clientX, groupInfo );
116 |
117 | this.kfEditor.requestService( "syntax.update.record.cursor", groupInfo.id, index );
118 |
119 | },
120 |
121 | hideCursor: function () {
122 | this.cursorShape.setAttr( "style", "display: none" );
123 | },
124 |
125 | // 根据当前的光标信息, 对选区和光标进行更新
126 | reselect: function () {
127 |
128 | var cursorInfo = this.kfEditor.requestService( "syntax.get.record.cursor" ),
129 | groupInfo = null;
130 |
131 | this.hideCursor();
132 |
133 | // 根节点单独处理
134 | if ( this.kfEditor.requestService( "syntax.is.select.placeholder" ) ) {
135 |
136 | groupInfo = this.kfEditor.requestService( "syntax.get.group.content", cursorInfo.groupId );
137 | this.kfEditor.requestService( "render.select.group", groupInfo.content[ cursorInfo.startOffset ].id );
138 | return;
139 |
140 | }
141 |
142 | if ( cursorInfo.startOffset === cursorInfo.endOffset ) {
143 | // 更新光标位置
144 | this.updateCursor();
145 | // 请求背景着色
146 | this.kfEditor.requestService( "render.tint.current.cursor" );
147 | } else {
148 | this.kfEditor.requestService( "render.select.current.cursor" );
149 | }
150 |
151 | },
152 |
153 | updateCursor: function () {
154 |
155 | var cursorInfo = this.kfEditor.requestService( "syntax.get.record.cursor" );
156 |
157 | if ( cursorInfo.startOffset !== cursorInfo.endOffset ) {
158 | this.hideCursor();
159 | return;
160 | }
161 |
162 | var groupInfo = this.kfEditor.requestService( "syntax.get.group.content", cursorInfo.groupId ),
163 | isBefore = cursorInfo.endOffset === 0,
164 | index = isBefore ? 0 : cursorInfo.endOffset - 1,
165 | focusChild = groupInfo.content[ index ],
166 | paperContainerRect = getRect( this.paper.container.node ),
167 | cursorOffset = 0,
168 | focusChildRect = getRect( focusChild ),
169 | cursorTransform = this.cursorShape.getTransform( this.cursorShape ),
170 | canvasZoom = this.kfEditor.requestService( "render.get.canvas.zoom" ),
171 | formulaZoom = this.paper.getZoom();
172 |
173 | this.cursorShape.setHeight( focusChildRect.height / canvasZoom / formulaZoom );
174 |
175 | // 计算光标偏移位置
176 | cursorOffset = isBefore ? ( focusChildRect.left - 2 ) : ( focusChildRect.left + focusChildRect.width - 2 );
177 | cursorOffset -= paperContainerRect.left;
178 |
179 | // 定位光标
180 | cursorTransform.m.e = Math.floor( cursorOffset / canvasZoom / formulaZoom ) + 0.5 ;
181 | cursorTransform.m.f = ( focusChildRect.top - paperContainerRect.top ) / canvasZoom / formulaZoom;
182 |
183 | this.cursorShape.setMatrix( cursorTransform );
184 | this.cursorShape.setAttr( "style", "display: block" );
185 |
186 | },
187 |
188 | getCursorLocation: function () {
189 |
190 | var rect = this.cursorShape.getRenderBox( "paper" );
191 |
192 | return {
193 | x: rect.x,
194 | y: rect.y
195 | };
196 |
197 | },
198 |
199 | getIndex: function ( distance, groupInfo ) {
200 |
201 | var index = -1,
202 | children = groupInfo.content,
203 | boundingRect = null;
204 |
205 | for ( var i = children.length - 1, child = null; i >= 0; i-- ) {
206 |
207 | index = i;
208 |
209 | child = children[ i ];
210 |
211 | boundingRect = getRect( child );
212 |
213 | if ( boundingRect.left < distance ) {
214 |
215 | if ( boundingRect.left + boundingRect.width / 2 < distance ) {
216 | index += 1;
217 | }
218 |
219 | break;
220 |
221 | }
222 |
223 | }
224 |
225 | return index;
226 |
227 | }
228 |
229 | } );
230 |
231 | function getRect ( node ) {
232 | return node.getBoundingClientRect();
233 | }
234 |
235 | } );
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/keyboard.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * 特殊字符区域
3 | */
4 |
5 | define(function (require) {
6 | var kity = require('kity'),
7 | PREFIX = 'kf-editor-ui-',
8 | // UiUitls
9 | $$ = require('ui/ui-impl/ui-utils'),
10 | Menu = require('ui/ui-impl/keyboard/menu/index'),
11 | Panel = require('ui/ui-impl/keyboard/panel/index'),
12 | Page = require('ui/ui-impl/keyboard/page/index'),
13 | Constant = require('ui/ui-impl/keyboard/const'),
14 | PcPanelConstant = require('ui/ui-impl/keyboard/panel/pcConst'),
15 | AndroidPanelConstant = require('ui/ui-impl/keyboard/panel/androidConst'),
16 | Footer = require('ui/ui-impl/keyboard/footer/index'),
17 | Keyboard = kity.createClass('Keyboard', {
18 | constructor: function (doc, kfEditor) {
19 | this.doc = doc;
20 | this.kfEditor = kfEditor;
21 | this.pageSize = this.getDeviceType() === 'android' ? 32 : 40;
22 | this.panelConstant = this.getConstant();
23 | this.typeEnum = {
24 | [Constant.Type.Common]: 0,
25 | [Constant.Type.Algebra]: 1,
26 | [Constant.Type.Geometry]: 2,
27 | [Constant.Type.Letter]: 3,
28 | [Constant.Type.Other]: 4,
29 | 0: Constant.Type.Common,
30 | 1: Constant.Type.Algebra,
31 | 2: Constant.Type.Geometry,
32 | 3: Constant.Type.Letter,
33 | 4: Constant.Type.Other,
34 | };
35 |
36 | this.state = {
37 | type: Constant.Type.Common,
38 | page: 0,
39 | totalPage: this.getTotalPage(this.panelConstant[0].items.length),
40 | };
41 |
42 | this.element = this.render();
43 |
44 | // 完成组件渲染
45 | this.menuChild = new Menu(this.element, {
46 | type: this.state.type,
47 | prefix: PREFIX,
48 | doc: this.doc,
49 | kfEditor: this.kfEditor,
50 | onClick: this.onMenuClick.bind(this),
51 | });
52 | this.panelChild = new Panel(this.element, {
53 | type: this.state.type,
54 | page: this.state.page,
55 | prefix: PREFIX,
56 | doc: this.doc,
57 | kfEditor: this.kfEditor,
58 | panelConstant: this.panelConstant,
59 | rowHeight: this.getDeviceType() === 'android' ? 146 : 63,
60 | scrollHeight: this.getDeviceType() === 'android' ? 146 * 4 : 63 * 5,
61 | onClick: this.onPanelClick.bind(this),
62 | });
63 | this.pageChild = new Page(this.element, {
64 | type: this.state.type,
65 | page: this.state.page,
66 | totalPage: this.state.totalPage,
67 | prefix: PREFIX,
68 | doc: this.doc,
69 | kfEditor: this.kfEditor,
70 | onPrevPage: this.onPrevPage.bind(this),
71 | onNextPage: this.onNextPage.bind(this),
72 | onDelete: this.onDelete.bind(this),
73 | onSubmit: this.onSubmit.bind(this),
74 | });
75 | this.footerChild = new Footer(this.element, {
76 | prefix: PREFIX,
77 | doc: this.doc,
78 | onSubmit: this.onSubmit.bind(this),
79 | onCancel: this.onCancel.bind(this),
80 | });
81 | this.renderKeyboard();
82 | // 通知当前类型
83 | this.sendService();
84 | },
85 |
86 | sendService: function () {
87 | this.kfEditor.eclassWebService.send({
88 | type: 'common.setType',
89 | data: {
90 | body: {
91 | type: this.state.type,
92 | },
93 | },
94 | });
95 | },
96 |
97 | renderKeyboard: function () {
98 | this.menuChild.mount();
99 | this.panelChild.mount();
100 | this.pageChild.mount();
101 | this.footerChild.mount();
102 | },
103 |
104 | onMenuClick: function (val) {
105 | const charCollection = this.panelConstant.find((x) => x.type === val) || {};
106 | const len = charCollection.items ? charCollection.items.length : 0;
107 | this.kfEditor.eclassWebService.send({
108 | type: 'common.setType',
109 | data: {
110 | body: {
111 | type: val,
112 | },
113 | },
114 | });
115 | this.setState({
116 | type: val,
117 | page: 0,
118 | totalPage: this.getTotalPage(len),
119 | });
120 | },
121 |
122 | onPanelClick: function (val) {
123 | $$.publish('panel.select', val);
124 | },
125 |
126 | onPrevPage: function () {
127 | const { page, type } = this.state;
128 | // 如果已到第一页,则自动切换至上一个模式,若已到最顶部模式,则禁止翻页
129 | if (page === 0 && type === Constant.Type.Common) {
130 | return;
131 | }
132 | if (page === 0) {
133 | this._prevMode(type);
134 | return;
135 | }
136 | this.setState({
137 | page: page - 1,
138 | });
139 | },
140 | onNextPage: function () {
141 | const { page, type, totalPage } = this.state;
142 | // 如果已到最后一页,则自动切换至下一个模式,若已到最底部模式,则禁止翻页
143 | if (page === totalPage - 1 && type === Constant.Type.Other) {
144 | return;
145 | }
146 | if (page === totalPage - 1) {
147 | this._nextMode(type);
148 | return;
149 | }
150 | this.setState({
151 | page: page + 1,
152 | });
153 | },
154 |
155 | onDelete: function () {
156 | this.kfEditor.requestService('control.delete.string');
157 | },
158 | onSubmit: function () {
159 | this.kfEditor.execCommand('get.image.data', (data) => {
160 | const formula = this.kfEditor.execCommand('get.source');
161 | this.kfEditor.eclassWebService.send({
162 | type: 'common.setFormula',
163 | data: {
164 | body: {
165 | formulaSrc: data.img,
166 | formula,
167 | },
168 | },
169 | });
170 | });
171 | },
172 | onCancel: function () {
173 | this.kfEditor.eclassWebService.send({
174 | type: 'common.closeModal',
175 | });
176 | },
177 |
178 | render: function () {
179 | const keyboardNode = $$.ele(this.doc, 'div', {
180 | className: PREFIX + 'keyboard',
181 | });
182 |
183 | return keyboardNode;
184 | },
185 |
186 | setState: function (nextState) {
187 | this.state = {
188 | ...this.state,
189 | ...nextState,
190 | };
191 | this.menuChild.update(this.state);
192 | this.panelChild.update(this.state);
193 | this.pageChild.update(this.state);
194 | },
195 |
196 | getTotalPage: function (len) {
197 | return Math.ceil(len / this.pageSize) || 1;
198 | },
199 |
200 | getDeviceType: function () {
201 | return this.kfEditor.options.ui.device;
202 | },
203 |
204 | getConstant: function () {
205 | const deviceType = this.getDeviceType();
206 | switch (deviceType) {
207 | case 'android':
208 | return AndroidPanelConstant;
209 | case 'pc':
210 | return PcPanelConstant;
211 | }
212 | },
213 |
214 | attachTo: function (container) {
215 | container.appendChild(this.element);
216 | },
217 |
218 | _prevMode: function (curType) {
219 | const prevType = this.typeEnum[this.typeEnum[curType] - 1];
220 | const charCollection = this.panelConstant.find((x) => x.type === prevType) || {};
221 | const len = charCollection.items ? charCollection.items.length : 0;
222 | this.setState({
223 | type: prevType,
224 | page: 0,
225 | totalPage: this.getTotalPage(len),
226 | });
227 | return;
228 | },
229 | _nextMode: function (curType) {
230 | const nextType = this.typeEnum[this.typeEnum[curType] + 1];
231 | const charCollection = this.panelConstant.find((x) => x.type === nextType) || {};
232 | const len = charCollection.items ? charCollection.items.length : 0;
233 | this.setState({
234 | type: nextType,
235 | page: 0,
236 | totalPage: this.getTotalPage(len),
237 | });
238 | return;
239 | },
240 | });
241 |
242 | return Keyboard;
243 | });
244 |
--------------------------------------------------------------------------------
/assets/styles/theme/pc.less:
--------------------------------------------------------------------------------
1 | .pc {
2 | width: 864px;
3 | height: 580px;
4 | position: relative;
5 | top: 0;
6 | left: 0;
7 | * {
8 | box-sizing: border-box;
9 | margin: 0;
10 | padding: 0;
11 | list-style: none;
12 | }
13 | .kf-editor-edit-area {
14 | width: 100%;
15 | height: 168px;
16 | float: left;
17 | .kf-editor-edit-scrollbar {
18 | bottom: 0px;
19 | }
20 | .kf-editor-header-container {
21 | width: 100%;
22 | height: 48px;
23 | background: #eeeeee;
24 | overflow: hidden;
25 | padding-left: 16px;
26 | .kf-editor-header-container-title {
27 | font-size: 16px;
28 | color: #616266;
29 | line-height: 48px;
30 | float: left;
31 | }
32 | .kf-editor-header-container-close {
33 | width: 48px;
34 | height: 48px;
35 | float: right;
36 | cursor: pointer;
37 | position: relative;
38 | &::before {
39 | content: '';
40 | display: block;
41 | background: url('https://store-g1.seewo.com/easiclass-public/8747fc92a01246e19d7f9bc9f3868652')
42 | center no-repeat;
43 | background-size: 100%;
44 | position: absolute;
45 | width: 16px;
46 | height: 16px;
47 | top: 16px;
48 | right: 16px;
49 | }
50 | &:hover {
51 | &::before {
52 | background: url('https://store-g1.seewo.com/easiclass-public/c2682d9dcc844986898be60b26ee5823');
53 | }
54 | }
55 | &:active {
56 | &::before {
57 | background: url('https://store-g1.seewo.com/easiclass-public/9550aaa080574f739425122e7908e012');
58 | }
59 | }
60 | }
61 | }
62 | .kf-editor-canvas-wrapper {
63 | width: 100%;
64 | height: calc(100% - 48px);
65 | background: #fff;
66 | float: left;
67 | padding: 0 40px;
68 |
69 | .kf-editor-canvas-container {
70 | width: 100%;
71 | height: 100%;
72 | }
73 | }
74 | }
75 |
76 | .kf-editor-edit-keyboard {
77 | width: 100%;
78 | height: 411px;
79 | border-top: 1px solid #ddd;
80 | box-sizing: border-box;
81 | background-color: white;
82 | * {
83 | list-style: none;
84 | padding: 0;
85 | margin: 0;
86 | box-sizing: border-box;
87 | }
88 | }
89 |
90 | .kf-editor-ui-keyboard {
91 | width: 100%;
92 | height: 100%;
93 | overflow: hidden;
94 | background: #fff;
95 | border-top: 1px solid #e4e4e4;
96 | .kf-editor-ui-keyboard-menu {
97 | width: 118px;
98 | height: 347px;
99 | border-right: 1px solid #e4e4e4;
100 | float: left;
101 | .kf-editor-ui-keyboard-menu-list {
102 | width: 100%;
103 | height: 100%;
104 | padding: 11px;
105 | background: #f4f4f4;
106 | overflow: hidden;
107 | .kf-editor-ui-keyboard-menu-list-item {
108 | width: 72px;
109 | height: 40px;
110 | margin: 12px;
111 | text-align: center;
112 | line-height: 40px;
113 | font-size: 16px;
114 | color: #49494d;
115 | cursor: pointer;
116 | float: left;
117 | }
118 | .kf-editor-ui-keyboard-menu-list-item-active {
119 | background: #198cff;
120 | border-radius: 4px;
121 | color: #ffffff;
122 | }
123 | }
124 | }
125 | .kf-editor-ui-keyboard-panel {
126 | width: 664px;
127 | height: 347px;
128 | float: left;
129 | overflow: hidden;
130 | .kf-editor-ui-keyboard-panel-list {
131 | width: 100%;
132 | position: relative;
133 | top: 0;
134 | transition: 0.3s top linear;
135 | .kf-editor-ui-keyboard-panel-list-item {
136 | width: 83px;
137 | height: 64px;
138 | cursor: pointer;
139 | float: left;
140 | border: solid #e4e4e4;
141 | border-width: 1px 1px 1px 0px;
142 | margin-top: -1px;
143 | position: relative;
144 | &:hover {
145 | &::before {
146 | content: '';
147 | display: block;
148 | position: absolute;
149 | width: 100%;
150 | height: 100%;
151 | background-color: rgba(73, 73, 77, 0.1);
152 | }
153 | }
154 | }
155 | }
156 | }
157 | .kf-editor-ui-keyboard-page {
158 | width: 81px;
159 | height: 347px;
160 | float: left;
161 | margin-left: -1px;
162 | border-left: 1px solid #e4e4e4;
163 | .kf-editor-ui-keyboard-page-list {
164 | .kf-editor-ui-keyboard-page-list-item {
165 | width: 81px;
166 | height: 128px;
167 | cursor: pointer;
168 | }
169 | .kf-editor-ui-keyboard-page-list-item-delete {
170 | width: 81px;
171 | height: 64px;
172 | background: url('https://store-g1.seewo.com/easiclass-public/78c93d3c1204445982c6b5700e1442eb')
173 | center no-repeat;
174 | background-size: initial;
175 | border-bottom: 1px solid #e4e4e4;
176 | &:hover {
177 | background-color: rgba(73, 73, 77, 0.1);
178 | }
179 | }
180 | .kf-editor-ui-keyboard-page-list-item-prev {
181 | border-bottom: 1px solid #e4e4e4;
182 | position: relative;
183 | &::before {
184 | content: ' ';
185 | display: block;
186 | width: 100%;
187 | height: 100%;
188 | background: url('https://store-g1.seewo.com/easiclass-public/4993661fa54249249b152a89cb1cec6b')
189 | center no-repeat;
190 | background-size: initial;
191 | }
192 | &:not(.kf-editor-ui-keyboard-page-list-item-disabled):hover {
193 | background-color: rgba(73, 73, 77, 0.1);
194 | }
195 | }
196 | .kf-editor-ui-keyboard-page-list-item-next {
197 | position: relative;
198 | &:not(.kf-editor-ui-keyboard-page-list-item-disabled):hover {
199 | background-color: rgba(73, 73, 77, 0.1);
200 | }
201 | &::before {
202 | content: ' ';
203 | display: block;
204 | width: 100%;
205 | height: 100%;
206 | background: url('https://store-g1.seewo.com/easiclass-public/f4df574b99d748f0823ccd36362d1a9c')
207 | center no-repeat;
208 | background-size: initial;
209 | }
210 | }
211 | .kf-editor-ui-keyboard-page-list-item-disabled::before {
212 | opacity: 0.5;
213 | }
214 | .kf-editor-ui-keyboard-page-list-item-ok {
215 | display: none;
216 | }
217 | }
218 | }
219 | .kf-editor-ui-keyboard-footer {
220 | width: 100%;
221 | height: 64px;
222 | padding: 14px 0;
223 | float: left;
224 | border-top: 1px solid #e4e4e4;
225 | #kf-editor-ui-keyboard-footer-button-cancel {
226 | width: 120px;
227 | height: 36px;
228 | background: #ffffff;
229 | border: 1px solid #dddddd;
230 | border-radius: 18px;
231 | font-size: 16px;
232 | color: #616266;
233 | text-align: center;
234 | line-height: 34px;
235 | float: left;
236 | margin-left: calc(50% - 128px);
237 | cursor: pointer;
238 | user-select: none;
239 | &:not(:disabled) {
240 | cursor: pointer;
241 | }
242 | &:not(:disabled):hover {
243 | background-color: #ebf5ff;
244 | }
245 | &:not(:disabled):active {
246 | background-color: #dce8f5;
247 | }
248 | }
249 | #kf-editor-ui-keyboard-footer-button-submit {
250 | width: 120px;
251 | height: 36px;
252 | background: #198cff;
253 | border-radius: 18px;
254 | font-size: 16px;
255 | color: #ffffff;
256 | text-align: center;
257 | line-height: 36px;
258 | float: left;
259 | margin-left: 16px;
260 | cursor: pointer;
261 | &:not(:disabled) {
262 | cursor: pointer;
263 | }
264 | &:not(:disabled):hover {
265 | background-color: #3399ff;
266 | }
267 | &:not(:disabled):active {
268 | background-color: #1885f2;
269 | }
270 | }
271 | }
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/src/ui/ui-impl/button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hn on 14-3-31.
3 | */
4 |
5 | define( function ( require ) {
6 |
7 | var kity = require( "kity" ),
8 |
9 | PREFIX = "kf-editor-ui-",
10 |
11 | LIST_OFFSET = 7,
12 |
13 | DEFAULT_OPTIONS = {
14 | iconSize: {
15 | w: 32,
16 | h: 32
17 | }
18 | },
19 |
20 | // UiUitls
21 | $$ = require( "ui/ui-impl/ui-utils" ),
22 |
23 | Button = kity.createClass( "Button", {
24 |
25 | constructor: function ( doc, options ) {
26 |
27 | this.options = kity.Utils.extend( {}, DEFAULT_OPTIONS, options );
28 |
29 | // 事件状态, 是否已经初始化
30 | this.eventState = false;
31 | this.toolbar = null;
32 | this.displayState = false;
33 | this.fixOffset = options.fixOffset || false;
34 |
35 | this.doc = doc;
36 |
37 | this.element = this.createButton();
38 | this.disabled = true;
39 |
40 | // 挂载的对象
41 | this.mountElement = null;
42 |
43 | this.icon = this.createIcon();
44 | this.label = this.createLabel();
45 | this.sign = this.createSign();
46 | this.mountPoint = this.createMountPoint();
47 |
48 | this.mergeElement();
49 |
50 | },
51 |
52 | initEvent: function () {
53 |
54 | var _self = this;
55 |
56 | if ( this.eventState ) {
57 | return;
58 | }
59 |
60 | this.eventState = true;
61 |
62 | $$.on( this.element, "mousedown", function ( e ) {
63 |
64 | e.preventDefault();
65 | e.stopPropagation();
66 |
67 | if ( e.which !== 1 ) {
68 | return;
69 | }
70 |
71 | if ( _self.disabled ) {
72 | return;
73 | }
74 |
75 | _self.toggleSelect();
76 | _self.toggleMountElement();
77 |
78 | } );
79 |
80 | },
81 |
82 | setToolbar: function ( toolbar ) {
83 | this.toolbar = toolbar;
84 | },
85 |
86 | toggleMountElement: function () {
87 |
88 | if ( this.displayState ) {
89 | this.hideMount();
90 | } else {
91 | this.showMount();
92 | }
93 |
94 | },
95 |
96 | setLabel: function ( labelText ) {
97 | var signText = "";
98 | if ( this.sign ) {
99 | signText = '';
100 | }
101 | this.label.innerHTML = labelText + signText;
102 | },
103 |
104 | toggleSelect: function () {
105 | $$.getClassList( this.element ).toggle( PREFIX + "button-in" );
106 | },
107 |
108 | unselect: function () {
109 | $$.getClassList( this.element ).remove( PREFIX + "button-in" );
110 | },
111 |
112 | select: function () {
113 | $$.getClassList( this.element ).add( PREFIX + "button-in" );
114 | },
115 |
116 | show: function () {
117 | this.select();
118 | this.showMount();
119 | },
120 |
121 | hide: function () {
122 | this.unselect();
123 | this.hideMount();
124 | },
125 |
126 | showMount: function () {
127 |
128 | this.displayState = true;
129 | this.mountPoint.style.display = "block";
130 |
131 | if ( this.fixOffset ) {
132 |
133 | var elementRect = this.element.getBoundingClientRect();
134 | this.mountElement.setOffset( elementRect.left + LIST_OFFSET, elementRect.bottom );
135 |
136 | }
137 |
138 | var editorContainer = this.toolbar.getContainer(),
139 | currentBox = null,
140 | containerBox = $$.getRectBox( editorContainer ),
141 | mountEleBox = this.mountElement.getPositionInfo();
142 |
143 | // 修正偏移
144 | if ( mountEleBox.right > containerBox.right ) {
145 | currentBox = $$.getRectBox( this.element );
146 | // 对齐到按钮的右边界
147 | this.mountPoint.style.left = currentBox.right - mountEleBox.right - 1 + "px";
148 | }
149 |
150 | this.mountElement.updateSize && this.mountElement.updateSize();
151 |
152 | },
153 |
154 | hideMount: function () {
155 | this.displayState = false;
156 | this.mountPoint.style.display = "none";
157 | },
158 |
159 | getNode: function () {
160 | return this.element;
161 | },
162 |
163 | mount: function ( element ) {
164 | this.mountElement = element;
165 | element.mountTo( this.mountPoint );
166 | },
167 |
168 | createButton: function () {
169 |
170 | var buttonNode = $$.ele( this.doc, "div", {
171 | className: PREFIX + "button"
172 | } );
173 |
174 | // 附加className
175 | if ( this.options.className ) {
176 | buttonNode.className += " " + PREFIX + this.options.className;
177 | }
178 |
179 | return buttonNode;
180 |
181 | },
182 |
183 | createIcon: function () {
184 |
185 | if ( !this.options.icon ) {
186 | return null;
187 | }
188 |
189 | var iconNode = $$.ele( this.doc, "div", {
190 | className: PREFIX + "button-icon"
191 | } );
192 |
193 | if ( typeof this.options.icon === "string" ) {
194 | iconNode.style.backgroundImage = "url(" + this.options.icon + ") no-repeat";
195 | } else {
196 | iconNode.style.background = getBackgroundStyle( this.options.icon );
197 | }
198 |
199 | if ( this.options.iconSize.w ) {
200 | iconNode.style.width = this.options.iconSize.w + "px";
201 | }
202 |
203 | if ( this.options.iconSize.h ) {
204 | iconNode.style.height = this.options.iconSize.h + "px";
205 | }
206 |
207 | return iconNode;
208 |
209 | },
210 |
211 | createLabel: function () {
212 |
213 | var labelNode = $$.ele( this.doc, "div", {
214 | className: PREFIX + "button-label",
215 | content: this.options.label
216 | } );
217 |
218 |
219 | return labelNode;
220 |
221 | },
222 |
223 | createSign: function () {
224 |
225 | if ( this.options.sign === false ) {
226 | return null;
227 | }
228 |
229 | return $$.ele( this.doc, "div", {
230 | className: PREFIX + "button-sign"
231 | } );
232 |
233 | },
234 |
235 | createMountPoint: function () {
236 |
237 | return $$.ele( this.doc, "div", {
238 | className: PREFIX + "button-mount-point"
239 | } );
240 |
241 | },
242 |
243 | disable: function () {
244 | this.disabled = true;
245 | $$.getClassList( this.element ).remove( PREFIX + "enabled" );
246 | },
247 |
248 | enable: function () {
249 | this.disabled = false;
250 | $$.getClassList( this.element ).add( PREFIX + "enabled" );
251 | },
252 |
253 | mergeElement: function () {
254 |
255 | this.icon && this.element.appendChild( this.icon );
256 | this.element.appendChild( this.label );
257 | this.sign && this.label.appendChild( this.sign );
258 | this.element.appendChild( this.mountPoint );
259 |
260 | }
261 |
262 | } );
263 |
264 | function getBackgroundStyle ( data ) {
265 |
266 | var style = "url( " + data.src + " ) no-repeat ";
267 |
268 | style += -data.x + 'px ';
269 | style += -data.y + 'px';
270 |
271 | return style;
272 |
273 | }
274 |
275 | return Button;
276 |
277 | } );
--------------------------------------------------------------------------------
/src/ui/ui-impl/keyboard/panel/position.data.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Demian
3 | * @Date: 2020-04-22 14:54:26
4 | * @LastEditor: Demian
5 | * @LastEditTime: 2020-05-18 14:37:05
6 | */
7 | define(function () {
8 | return {
9 | '<': {
10 | x: 0,
11 | y: 0,
12 | },
13 | '\\frac \\placeholder\\placeholder': {
14 | x: 1,
15 | y: 0,
16 | },
17 | '\\sqrt \\placeholder': {
18 | x: 2,
19 | y: 0,
20 | },
21 | a: {
22 | x: 3,
23 | y: 0,
24 | },
25 | '+': {
26 | x: 4,
27 | y: 0,
28 | },
29 | '7': {
30 | x: 5,
31 | y: 0,
32 | },
33 | '8': {
34 | x: 6,
35 | y: 0,
36 | },
37 | '9': {
38 | x: 7,
39 | y: 0,
40 | },
41 | '>': {
42 | x: 0,
43 | y: 1,
44 | },
45 | '\\left|\\placeholder\\right|': {
46 | x: 1,
47 | y: 1,
48 | },
49 | '\\placeholder^2': {
50 | x: 2,
51 | y: 1,
52 | },
53 | b: {
54 | x: 3,
55 | y: 1,
56 | },
57 | '-': {
58 | x: 4,
59 | y: 1,
60 | },
61 | '4': {
62 | x: 5,
63 | y: 1,
64 | },
65 | '5': {
66 | x: 6,
67 | y: 1,
68 | },
69 | '6': {
70 | x: 7,
71 | y: 1,
72 | },
73 | '\\leq': {
74 | x: 0,
75 | y: 2,
76 | },
77 | '\\left(\\placeholder\\right)': {
78 | x: 1,
79 | y: 2,
80 | },
81 | '\\sqrt [3] \\placeholder': {
82 | x: 2,
83 | y: 2,
84 | },
85 | x: {
86 | x: 3,
87 | y: 2,
88 | },
89 | '\\pm': {
90 | x: 4,
91 | y: 2,
92 | },
93 | '1': {
94 | x: 5,
95 | y: 2,
96 | },
97 | '2': {
98 | x: 6,
99 | y: 2,
100 | },
101 | '3': {
102 | x: 7,
103 | y: 2,
104 | },
105 | '\\geq': {
106 | x: 0,
107 | y: 3,
108 | },
109 | '%': {
110 | x: 1,
111 | y: 3,
112 | },
113 | '\\placeholder^3': {
114 | x: 2,
115 | y: 3,
116 | },
117 | y: {
118 | x: 3,
119 | y: 3,
120 | },
121 | ',': {
122 | x: 4,
123 | y: 3,
124 | },
125 | '0': {
126 | x: 5,
127 | y: 3,
128 | },
129 | '.': {
130 | x: 6,
131 | y: 3,
132 | },
133 | '=': {
134 | x: 7,
135 | y: 3,
136 | },
137 | '\\times': {
138 | x: 0,
139 | y: 4,
140 | },
141 | '\\div': {
142 | x: 1,
143 | y: 4,
144 | },
145 | '\\approx': {
146 | x: 2,
147 | y: 4,
148 | },
149 | '\\neq': {
150 | x: 3,
151 | y: 4,
152 | },
153 | '\\sqrt [\\placeholder] \\placeholder': {
154 | x: 4,
155 | y: 4,
156 | },
157 | '\\pi': {
158 | x: 5,
159 | y: 4,
160 | },
161 | '\\delta': {
162 | x: 6,
163 | y: 4,
164 | },
165 | '\\left[\\placeholder\\right]': {
166 | x: 7,
167 | y: 4,
168 | },
169 | '\\placeholder^\\placeholder': {
170 | x: 0,
171 | y: 5,
172 | },
173 | '\\placeholder_\\placeholder': {
174 | x: 1,
175 | y: 5,
176 | },
177 | '{^\\placeholder_\\placeholder\\placeholder}': {
178 | x: 2,
179 | y: 5,
180 | },
181 | '\\placeholder^\\placeholder_\\placeholder': {
182 | x: 3,
183 | y: 5,
184 | },
185 | '\\sum\\placeholder': {
186 | x: 4,
187 | y: 5,
188 | },
189 | '\\sum_\\placeholder\\placeholder': {
190 | x: 5,
191 | y: 5,
192 | },
193 | '\\sum^\\placeholder_\\placeholder\\placeholder': {
194 | x: 6,
195 | y: 5,
196 | },
197 | '\\int \\placeholder': {
198 | x: 7,
199 | y: 5,
200 | },
201 | '\\int^\\placeholder_\\placeholder\\placeholder': {
202 | x: 0,
203 | y: 6,
204 | },
205 | '\\iint\\placeholder': {
206 | x: 1,
207 | y: 6,
208 | },
209 | '\\iint^\\placeholder_\\placeholder\\placeholder': {
210 | x: 2,
211 | y: 6,
212 | },
213 | '\\iiint\\placeholder': {
214 | x: 3,
215 | y: 6,
216 | },
217 | '\\iiint^\\placeholder_\\placeholder\\placeholder': {
218 | x: 4,
219 | y: 6,
220 | },
221 | '\\log\\placeholder': {
222 | x: 5,
223 | y: 6,
224 | },
225 | '\\ln\\placeholder': {
226 | x: 6,
227 | y: 6,
228 | },
229 | '\\land': {
230 | x: 7,
231 | y: 6,
232 | },
233 | '\\lor': {
234 | x: 0,
235 | y: 7,
236 | },
237 | '\\neg': {
238 | x: 1,
239 | y: 7,
240 | },
241 | '\\forall': {
242 | x: 2,
243 | y: 7,
244 | },
245 | '\\exists': {
246 | x: 3,
247 | y: 7,
248 | },
249 | '\\infty': {
250 | x: 4,
251 | y: 7,
252 | },
253 | '\\cup': {
254 | x: 5,
255 | y: 7,
256 | },
257 | '\\cap': {
258 | x: 6,
259 | y: 7,
260 | },
261 | '\\in': {
262 | x: 7,
263 | y: 7,
264 | },
265 | '\\notin': {
266 | x: 0,
267 | y: 8,
268 | },
269 | '\\subset': {
270 | x: 1,
271 | y: 8,
272 | },
273 | '\\subseteq': {
274 | x: 2,
275 | y: 8,
276 | },
277 | '\\supset': {
278 | x: 3,
279 | y: 8,
280 | },
281 | '\\supseteq': {
282 | x: 4,
283 | y: 8,
284 | },
285 | '\\varnothing': {
286 | x: 5,
287 | y: 8,
288 | },
289 | '\\cdot': {
290 | x: 6,
291 | y: 8,
292 | },
293 | '\\colon': {
294 | x: 7,
295 | y: 8,
296 | },
297 | '\\sin\\placeholder': {
298 | x: 0,
299 | y: 9,
300 | },
301 | '\\cos\\placeholder': {
302 | x: 1,
303 | y: 9,
304 | },
305 | '\\tan\\placeholder': {
306 | x: 2,
307 | y: 9,
308 | },
309 | '\\sec\\placeholder': {
310 | x: 3,
311 | y: 9,
312 | },
313 | '\\csc\\placeholder': {
314 | x: 4,
315 | y: 9,
316 | },
317 | '\\cot\\placeholder': {
318 | x: 5,
319 | y: 9,
320 | },
321 | '\\arcsin\\placeholder': {
322 | x: 6,
323 | y: 9,
324 | },
325 | '\\arccos\\placeholder': {
326 | x: 7,
327 | y: 9,
328 | },
329 | '\\arctan\\placeholder': {
330 | x: 0,
331 | y: 10,
332 | },
333 | '\\triangle': {
334 | x: 1,
335 | y: 10,
336 | },
337 | '\\sim': {
338 | x: 2,
339 | y: 10,
340 | },
341 | '\\cong': {
342 | x: 3,
343 | y: 10,
344 | },
345 | '\\angle': {
346 | x: 4,
347 | y: 10,
348 | },
349 | '\\bot': {
350 | x: 5,
351 | y: 10,
352 | },
353 | '\\alpha': {
354 | x: 6,
355 | y: 10,
356 | },
357 | '\\beta': {
358 | x: 7,
359 | y: 10,
360 | },
361 | '\\gamma': {
362 | x: 0,
363 | y: 11,
364 | },
365 | '\\theta': {
366 | x: 1,
367 | y: 11,
368 | },
369 | '\\degree': {
370 | x: 2,
371 | y: 11,
372 | },
373 | '\\bigcirc': {
374 | x: 3,
375 | y: 11,
376 | },
377 | a: { x: 0, y: 12 },
378 | b: { x: 1, y: 12 },
379 | c: { x: 2, y: 12 },
380 | d: { x: 3, y: 12 },
381 | e: { x: 4, y: 12 },
382 | f: { x: 5, y: 12 },
383 | g: { x: 6, y: 12 },
384 | h: { x: 7, y: 12 },
385 | i: { x: 0, y: 13 },
386 | j: { x: 1, y: 13 },
387 | k: { x: 2, y: 13 },
388 | l: { x: 3, y: 13 },
389 | m: { x: 4, y: 13 },
390 | n: { x: 5, y: 13 },
391 | o: { x: 6, y: 13 },
392 | p: { x: 7, y: 13 },
393 | q: { x: 0, y: 14 },
394 | r: { x: 1, y: 14 },
395 | s: { x: 2, y: 14 },
396 | t: { x: 3, y: 14 },
397 | u: { x: 4, y: 14 },
398 | v: { x: 5, y: 14 },
399 | w: { x: 6, y: 14 },
400 | x: { x: 7, y: 14 },
401 | y: { x: 0, y: 15 },
402 | z: { x: 1, y: 15 },
403 | '\\Omega': {
404 | x: 0,
405 | y: 16,
406 | },
407 | '\\because': {
408 | x: 1,
409 | y: 16,
410 | },
411 | '\\therefore': {
412 | x: 2,
413 | y: 16,
414 | },
415 | '\\Longrightarrow': {
416 | x: 3,
417 | y: 16,
418 | },
419 | '\\Leftrightarrow': {
420 | x: 4,
421 | y: 16,
422 | },
423 | '\\uparrow': {
424 | x: 5,
425 | y: 16,
426 | },
427 | '\\downarrow': {
428 | x: 6,
429 | y: 16,
430 | },
431 | '\\lambda': {
432 | x: 7,
433 | y: 16,
434 | },
435 | '\\kappa': {
436 | x: 0,
437 | y: 17,
438 | },
439 | '\\mu': {
440 | x: 1,
441 | y: 17,
442 | },
443 | '\\rho': {
444 | x: 2,
445 | y: 17,
446 | },
447 | '\\sigma': {
448 | x: 3,
449 | y: 17,
450 | },
451 | '\\tau': {
452 | x: 4,
453 | y: 17,
454 | },
455 | '\\upsilon': {
456 | x: 5,
457 | y: 17,
458 | },
459 | '\\varphi': {
460 | x: 6,
461 | y: 17,
462 | },
463 | '\\Psi': {
464 | x: 7,
465 | y: 17,
466 | },
467 | '\\omega': {
468 | x: 0,
469 | y: 18,
470 | },
471 | '\\varepsilon': {
472 | x: 1,
473 | y: 18,
474 | },
475 | '\\zeta': {
476 | x: 2,
477 | y: 18,
478 | },
479 | '\\eta': {
480 | x: 3,
481 | y: 18,
482 | },
483 | '\\nu': {
484 | x: 4,
485 | y: 18,
486 | },
487 | '\\xi': {
488 | x: 5,
489 | y: 18,
490 | },
491 | '\\chi': {
492 | x: 6,
493 | y: 18,
494 | },
495 | };
496 | });
497 |
--------------------------------------------------------------------------------
/dist/assets/styles/theme/pc.css:
--------------------------------------------------------------------------------
1 | .pc {
2 | width: 864px;
3 | height: 580px;
4 | position: relative;
5 | top: 0;
6 | left: 0;
7 | }
8 | .pc * {
9 | box-sizing: border-box;
10 | margin: 0;
11 | padding: 0;
12 | list-style: none;
13 | }
14 | .pc .kf-editor-edit-area {
15 | width: 100%;
16 | height: 168px;
17 | float: left;
18 | }
19 | .pc .kf-editor-edit-area .kf-editor-edit-scrollbar {
20 | bottom: 0px;
21 | }
22 | .pc .kf-editor-edit-area .kf-editor-header-container {
23 | width: 100%;
24 | height: 48px;
25 | background: #eeeeee;
26 | overflow: hidden;
27 | padding-left: 16px;
28 | }
29 | .pc .kf-editor-edit-area .kf-editor-header-container .kf-editor-header-container-title {
30 | font-size: 16px;
31 | color: #616266;
32 | line-height: 48px;
33 | float: left;
34 | }
35 | .pc .kf-editor-edit-area .kf-editor-header-container .kf-editor-header-container-close {
36 | width: 48px;
37 | height: 48px;
38 | float: right;
39 | cursor: pointer;
40 | position: relative;
41 | }
42 | .pc .kf-editor-edit-area .kf-editor-header-container .kf-editor-header-container-close::before {
43 | content: '';
44 | display: block;
45 | background: url('https://store-g1.seewo.com/easiclass-public/8747fc92a01246e19d7f9bc9f3868652') center no-repeat;
46 | background-size: 100%;
47 | position: absolute;
48 | width: 16px;
49 | height: 16px;
50 | top: 16px;
51 | right: 16px;
52 | }
53 | .pc .kf-editor-edit-area .kf-editor-header-container .kf-editor-header-container-close:hover::before {
54 | background: url('https://store-g1.seewo.com/easiclass-public/c2682d9dcc844986898be60b26ee5823');
55 | }
56 | .pc .kf-editor-edit-area .kf-editor-header-container .kf-editor-header-container-close:active::before {
57 | background: url('https://store-g1.seewo.com/easiclass-public/9550aaa080574f739425122e7908e012');
58 | }
59 | .pc .kf-editor-edit-area .kf-editor-canvas-wrapper {
60 | width: 100%;
61 | height: calc(100% - 48px);
62 | background: #fff;
63 | float: left;
64 | padding: 0 40px;
65 | }
66 | .pc .kf-editor-edit-area .kf-editor-canvas-wrapper .kf-editor-canvas-container {
67 | width: 100%;
68 | height: 100%;
69 | }
70 | .pc .kf-editor-edit-keyboard {
71 | width: 100%;
72 | height: 411px;
73 | border-top: 1px solid #ddd;
74 | box-sizing: border-box;
75 | background-color: white;
76 | }
77 | .pc .kf-editor-edit-keyboard * {
78 | list-style: none;
79 | padding: 0;
80 | margin: 0;
81 | box-sizing: border-box;
82 | }
83 | .pc .kf-editor-ui-keyboard {
84 | width: 100%;
85 | height: 100%;
86 | overflow: hidden;
87 | background: #fff;
88 | border-top: 1px solid #e4e4e4;
89 | }
90 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-menu {
91 | width: 118px;
92 | height: 347px;
93 | border-right: 1px solid #e4e4e4;
94 | float: left;
95 | }
96 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-menu .kf-editor-ui-keyboard-menu-list {
97 | width: 100%;
98 | height: 100%;
99 | padding: 11px;
100 | background: #f4f4f4;
101 | overflow: hidden;
102 | }
103 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-menu .kf-editor-ui-keyboard-menu-list .kf-editor-ui-keyboard-menu-list-item {
104 | width: 72px;
105 | height: 40px;
106 | margin: 12px;
107 | text-align: center;
108 | line-height: 40px;
109 | font-size: 16px;
110 | color: #49494d;
111 | cursor: pointer;
112 | float: left;
113 | }
114 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-menu .kf-editor-ui-keyboard-menu-list .kf-editor-ui-keyboard-menu-list-item-active {
115 | background: #198cff;
116 | border-radius: 4px;
117 | color: #ffffff;
118 | }
119 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-panel {
120 | width: 664px;
121 | height: 347px;
122 | float: left;
123 | overflow: hidden;
124 | }
125 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-panel .kf-editor-ui-keyboard-panel-list {
126 | width: 100%;
127 | position: relative;
128 | top: 0;
129 | transition: 0.3s top linear;
130 | }
131 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-panel .kf-editor-ui-keyboard-panel-list .kf-editor-ui-keyboard-panel-list-item {
132 | width: 83px;
133 | height: 64px;
134 | cursor: pointer;
135 | float: left;
136 | border: solid #e4e4e4;
137 | border-width: 1px 1px 1px 0px;
138 | margin-top: -1px;
139 | position: relative;
140 | }
141 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-panel .kf-editor-ui-keyboard-panel-list .kf-editor-ui-keyboard-panel-list-item:hover::before {
142 | content: '';
143 | display: block;
144 | position: absolute;
145 | width: 100%;
146 | height: 100%;
147 | background-color: rgba(73, 73, 77, 0.1);
148 | }
149 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page {
150 | width: 81px;
151 | height: 347px;
152 | float: left;
153 | margin-left: -1px;
154 | border-left: 1px solid #e4e4e4;
155 | }
156 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item {
157 | width: 81px;
158 | height: 128px;
159 | cursor: pointer;
160 | }
161 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-delete {
162 | width: 81px;
163 | height: 64px;
164 | background: url('https://store-g1.seewo.com/easiclass-public/78c93d3c1204445982c6b5700e1442eb') center no-repeat;
165 | background-size: initial;
166 | border-bottom: 1px solid #e4e4e4;
167 | }
168 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-delete:hover {
169 | background-color: rgba(73, 73, 77, 0.1);
170 | }
171 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-prev {
172 | border-bottom: 1px solid #e4e4e4;
173 | position: relative;
174 | }
175 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-prev::before {
176 | content: ' ';
177 | display: block;
178 | width: 100%;
179 | height: 100%;
180 | background: url('https://store-g1.seewo.com/easiclass-public/4993661fa54249249b152a89cb1cec6b') center no-repeat;
181 | background-size: initial;
182 | }
183 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-prev:not(.kf-editor-ui-keyboard-page-list-item-disabled):hover {
184 | background-color: rgba(73, 73, 77, 0.1);
185 | }
186 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-next {
187 | position: relative;
188 | }
189 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-next:not(.kf-editor-ui-keyboard-page-list-item-disabled):hover {
190 | background-color: rgba(73, 73, 77, 0.1);
191 | }
192 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-next::before {
193 | content: ' ';
194 | display: block;
195 | width: 100%;
196 | height: 100%;
197 | background: url('https://store-g1.seewo.com/easiclass-public/f4df574b99d748f0823ccd36362d1a9c') center no-repeat;
198 | background-size: initial;
199 | }
200 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-disabled::before {
201 | opacity: 0.5;
202 | }
203 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-page .kf-editor-ui-keyboard-page-list .kf-editor-ui-keyboard-page-list-item-ok {
204 | display: none;
205 | }
206 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer {
207 | width: 100%;
208 | height: 64px;
209 | padding: 14px 0;
210 | float: left;
211 | border-top: 1px solid #e4e4e4;
212 | }
213 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-cancel {
214 | width: 120px;
215 | height: 36px;
216 | background: #ffffff;
217 | border: 1px solid #dddddd;
218 | border-radius: 18px;
219 | font-size: 16px;
220 | color: #616266;
221 | text-align: center;
222 | line-height: 34px;
223 | float: left;
224 | margin-left: calc(50% - 128px);
225 | cursor: pointer;
226 | user-select: none;
227 | }
228 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-cancel:not(:disabled) {
229 | cursor: pointer;
230 | }
231 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-cancel:not(:disabled):hover {
232 | background-color: #ebf5ff;
233 | }
234 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-cancel:not(:disabled):active {
235 | background-color: #dce8f5;
236 | }
237 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-submit {
238 | width: 120px;
239 | height: 36px;
240 | background: #198cff;
241 | border-radius: 18px;
242 | font-size: 16px;
243 | color: #ffffff;
244 | text-align: center;
245 | line-height: 36px;
246 | float: left;
247 | margin-left: 16px;
248 | cursor: pointer;
249 | }
250 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-submit:not(:disabled) {
251 | cursor: pointer;
252 | }
253 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-submit:not(:disabled):hover {
254 | background-color: #3399ff;
255 | }
256 | .pc .kf-editor-ui-keyboard .kf-editor-ui-keyboard-footer #kf-editor-ui-keyboard-footer-button-submit:not(:disabled):active {
257 | background-color: #1885f2;
258 | }
259 |
--------------------------------------------------------------------------------