├── .gitignore ├── Gruntfile.js ├── README.md ├── assets ├── images │ └── toolbar │ │ ├── brackets │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ └── 4.png │ │ ├── button │ │ ├── brackets.png │ │ ├── frac.png │ │ ├── pi.png │ │ ├── script.png │ │ ├── sqrt.png │ │ └── tick.png │ │ ├── char │ │ ├── greek │ │ │ ├── alpha.png │ │ │ ├── beta.png │ │ │ ├── chi.png │ │ │ ├── delta.png │ │ │ ├── epsilon.png │ │ │ ├── eta.png │ │ │ ├── gamma.png │ │ │ ├── iota.png │ │ │ ├── kappa.png │ │ │ ├── lambda.png │ │ │ ├── mu.png │ │ │ ├── nu.png │ │ │ ├── omega.png │ │ │ ├── omicron.png │ │ │ ├── phi.png │ │ │ ├── pi.png │ │ │ ├── psi.png │ │ │ ├── rho.png │ │ │ ├── sigma.png │ │ │ ├── tau.png │ │ │ ├── theta.png │ │ │ ├── upsilon.png │ │ │ ├── xi.png │ │ │ └── zeta.png │ │ └── math │ │ │ ├── div.png │ │ │ ├── eq.png │ │ │ ├── equiv.png │ │ │ ├── geq.png │ │ │ ├── gg.png │ │ │ ├── gt.png │ │ │ ├── infty.png │ │ │ ├── leq.png │ │ │ ├── ll.png │ │ │ ├── lt.png │ │ │ ├── mp.png │ │ │ ├── pm.png │ │ │ ├── sim.png │ │ │ ├── simeq.png │ │ │ ├── tanhao.png │ │ │ └── times.png │ │ ├── frac │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── c1.png │ │ ├── c2.png │ │ ├── c4.png │ │ └── c5.png │ │ ├── script │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── c1.png │ │ ├── c2.png │ │ └── c3.png │ │ ├── sqrt │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── c1.png │ │ └── c2.png │ │ └── ys │ │ ├── 1.png │ │ ├── 2.png │ │ └── 3.png └── styles │ ├── base.css │ ├── page.css │ └── ui.css ├── c.js ├── dev-lib ├── cmd-define.js ├── dev-define.js ├── dev-start.js ├── exports.js ├── jquery-1.11.0.min.js ├── kity-formula.all.js ├── kity-formula.all.min.js ├── kityformula-parser.all.js ├── kityformula-parser.all.min.js ├── kitygraph.all.js ├── kitygraph.all.min.js ├── sea-debug.js ├── sea.js └── sea.js.map ├── dist ├── kf-editor.all.js └── kf-editor.all.min.js ├── index.html ├── package.json └── src ├── base ├── common.js ├── event │ ├── event.js │ └── kfevent.js └── utils.js ├── editor └── editor.js ├── jquery.js ├── kf-ext ├── def.js ├── expression │ └── placeholder.js ├── extension.js └── operator │ └── placeholder.js ├── kf.js ├── kity.js ├── parse ├── def.js └── parser.js ├── position └── position.js ├── render └── render.js ├── syntax ├── move.js └── syntax.js └── ui ├── control └── zoom.js ├── toolbar-ele-list.js ├── toolbar └── toolbar.js ├── ui-impl ├── area.js ├── box.js ├── button.js ├── def │ ├── box-type.js │ ├── ele-type.js │ └── item-type.js ├── delimiter.js ├── drapdown-box.js ├── list.js ├── ui-utils.js └── ui.js └── ui.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.sublime-project 3 | *.sublime-workspace 4 | *.node_module 5 | node_modules -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function (grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | 7 | // Metadata. 8 | pkg: grunt.file.readJSON('package.json'), 9 | 10 | // Task configuration. 11 | "transport": { 12 | 13 | options: { 14 | 15 | // module path 16 | paths: [ 'src' ], 17 | debug: false 18 | 19 | }, 20 | 21 | cmd: { 22 | 23 | files: [ { 24 | 25 | cwd: 'src', 26 | src: '**/*.js', 27 | dest: '.build_tmp' 28 | 29 | } ] 30 | 31 | } 32 | 33 | }, 34 | 35 | concat: { 36 | 37 | options: { 38 | 39 | paths: [ 'src' ], 40 | include: 'all', 41 | noncmd: true 42 | 43 | }, 44 | 45 | cmd: { 46 | 47 | files: { 48 | '.build_tmp/kf-editor-o.js': '.build_tmp/**/*.js' 49 | } 50 | 51 | }, 52 | 53 | full: { 54 | 55 | options: { 56 | 57 | banner: '/*!\n' + 58 | ' * ====================================================\n' + 59 | ' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 60 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 61 | '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' + 62 | ' * GitHub: <%= pkg.repository.url %> \n' + 63 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 64 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n' + 65 | ' * ====================================================\n' + 66 | ' */\n\n' + 67 | '(function () {\n', 68 | 69 | footer: '})();' 70 | 71 | }, 72 | 73 | files: { 74 | 'dist/kf-editor.all.js': [ 'dev-lib/cmd-define.js', '.build_tmp/kf-editor-o.js', 'dev-lib/exports.js' ] 75 | } 76 | 77 | } 78 | 79 | }, 80 | 81 | uglify: { 82 | 83 | options: { 84 | 85 | banner: '/*!\n' + 86 | ' * ====================================================\n' + 87 | ' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 88 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 89 | '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' + 90 | ' * GitHub: <%= pkg.repository.url %> \n' + 91 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 92 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n' + 93 | ' * ====================================================\n' + 94 | ' */\n' 95 | 96 | }, 97 | 98 | minimize: { 99 | 100 | files: { 101 | 'dist/kf-editor.all.min.js': 'dist/kf-editor.all.js' 102 | } 103 | 104 | } 105 | 106 | }, 107 | 108 | clean: { 109 | 110 | tmp: [ '.build_tmp' ] 111 | 112 | } 113 | 114 | }); 115 | 116 | // These plugins provide necessary tasks. 117 | grunt.loadNpmTasks( 'grunt-cmd-transport' ); 118 | grunt.loadNpmTasks( 'grunt-cmd-concat' ); 119 | grunt.loadNpmTasks('grunt-contrib-uglify'); 120 | grunt.loadNpmTasks('grunt-contrib-clean'); 121 | 122 | // Default task. 123 | grunt.registerTask( 'default', [ 'transport:cmd', 'concat:cmd', 'concat:full', 'uglify:minimize', 'clean:tmp' ] ); 124 | 125 | }; 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | KityFormula Editor 2 | ======= 3 | 基于 SVG 的公式编辑器,百度前端富应用小组开发 4 | 5 | 当前文档工作未完善,有疑惑之处请发邮件联系我们。 6 | 7 | email:kity@baidu.com -------------------------------------------------------------------------------- /assets/images/toolbar/brackets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/brackets/1.png -------------------------------------------------------------------------------- /assets/images/toolbar/brackets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/brackets/2.png -------------------------------------------------------------------------------- /assets/images/toolbar/brackets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/brackets/3.png -------------------------------------------------------------------------------- /assets/images/toolbar/brackets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/brackets/4.png -------------------------------------------------------------------------------- /assets/images/toolbar/button/brackets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/button/brackets.png -------------------------------------------------------------------------------- /assets/images/toolbar/button/frac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/button/frac.png -------------------------------------------------------------------------------- /assets/images/toolbar/button/pi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/button/pi.png -------------------------------------------------------------------------------- /assets/images/toolbar/button/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/button/script.png -------------------------------------------------------------------------------- /assets/images/toolbar/button/sqrt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/button/sqrt.png -------------------------------------------------------------------------------- /assets/images/toolbar/button/tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/button/tick.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/alpha.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/beta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/beta.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/chi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/chi.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/delta.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/epsilon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/epsilon.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/eta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/eta.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/gamma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/gamma.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/iota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/iota.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/kappa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/kappa.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/lambda.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/mu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/mu.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/nu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/nu.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/omega.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/omega.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/omicron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/omicron.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/phi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/phi.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/pi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/pi.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/psi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/psi.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/rho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/rho.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/sigma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/sigma.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/tau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/tau.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/theta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/theta.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/upsilon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/upsilon.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/xi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/xi.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/greek/zeta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/greek/zeta.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/div.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/div.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/eq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/eq.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/equiv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/equiv.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/geq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/geq.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/gg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/gg.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/gt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/gt.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/infty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/infty.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/leq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/leq.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/ll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/ll.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/lt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/lt.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/mp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/mp.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/pm.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/sim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/sim.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/simeq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/simeq.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/tanhao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/tanhao.png -------------------------------------------------------------------------------- /assets/images/toolbar/char/math/times.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/char/math/times.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/1.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/2.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/3.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/c1.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/c2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/c2.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/c4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/c4.png -------------------------------------------------------------------------------- /assets/images/toolbar/frac/c5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/frac/c5.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/1.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/2.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/3.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/4.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/c1.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/c2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/c2.png -------------------------------------------------------------------------------- /assets/images/toolbar/script/c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/script/c3.png -------------------------------------------------------------------------------- /assets/images/toolbar/sqrt/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/sqrt/1.png -------------------------------------------------------------------------------- /assets/images/toolbar/sqrt/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/sqrt/2.png -------------------------------------------------------------------------------- /assets/images/toolbar/sqrt/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/sqrt/3.png -------------------------------------------------------------------------------- /assets/images/toolbar/sqrt/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/sqrt/4.png -------------------------------------------------------------------------------- /assets/images/toolbar/sqrt/c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/sqrt/c1.png -------------------------------------------------------------------------------- /assets/images/toolbar/sqrt/c2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/sqrt/c2.png -------------------------------------------------------------------------------- /assets/images/toolbar/ys/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/ys/1.png -------------------------------------------------------------------------------- /assets/images/toolbar/ys/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/ys/2.png -------------------------------------------------------------------------------- /assets/images/toolbar/ys/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fex-team/kityformula-editor/365b8cf7a674ba7b7f4bdc53a392c7c026856908/assets/images/toolbar/ys/3.png -------------------------------------------------------------------------------- /assets/styles/base.css: -------------------------------------------------------------------------------- 1 | 2 | .kf-editor { 3 | width: 100%; 4 | height: 100%; 5 | 6 | position: fixed; 7 | top: 0; 8 | left: 0; 9 | 10 | overflow: hidden; 11 | z-index: 2; 12 | } 13 | 14 | .kf-editor-toolbar { 15 | width: 100%; 16 | height: 80px; 17 | padding: 5px; 18 | background: -webkit-linear-gradient(top, white, white 60%, rgba(248, 248, 248, 0.72) 80%, rgba(218, 218, 218, 0.72)); 19 | position: absolute; 20 | top: 0; 21 | left: 0; 22 | z-index: 3; 23 | } 24 | 25 | .kf-editor-edit-area { 26 | position: relative; 27 | top: 0; 28 | left: 0; 29 | 30 | z-index: 2; 31 | /*background-color: white;*/ 32 | /*background-size: 21px 21px;*/ 33 | /*background-position: 0 0,10px 10px;*/ 34 | /*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);*/ 35 | /*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);*/ 36 | } 37 | 38 | .kf-editor-input-box { 39 | width: 1000px; 40 | position: fixed; 41 | top: 0; 42 | left: 0; 43 | z-index: 999999; 44 | } -------------------------------------------------------------------------------- /assets/styles/page.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | width: 100%; 4 | height: 100%; 5 | padding: 0; 6 | margin: 0; 7 | } -------------------------------------------------------------------------------- /assets/styles/ui.css: -------------------------------------------------------------------------------- 1 | /* 按钮 */ 2 | .kf-editor-ui-button { 3 | width: 50px; 4 | height: 100%; 5 | font-size: 10px; 6 | display: inline-block; 7 | border: 1px solid transparent; 8 | cursor: default; 9 | position: relative; 10 | top: 0; 11 | left: 0; 12 | vertical-align: top; 13 | } 14 | 15 | .kf-editor-ui-overlap-button { 16 | width: 100%; 17 | background: #8f8f8f; 18 | position: relative; 19 | top: 0; 20 | left: 0; 21 | z-index: 2; 22 | } 23 | 24 | .kf-editor-ui-button-icon { 25 | width: 32px; 26 | height: 32px; 27 | margin: 2px auto; 28 | } 29 | 30 | .kf-editor-ui-button-label { 31 | text-align: center; 32 | display: block; 33 | padding: 8px 0; 34 | } 35 | 36 | .kf-editor-ui-overlap-button .kf-editor-ui-button-label { 37 | padding: 5px 5px; 38 | text-align: left; 39 | color: white; 40 | font-size: 14px; 41 | } 42 | 43 | .kf-editor-ui-overlap-button .kf-editor-ui-button-label:HOVER { 44 | color: #ffbf00; 45 | } 46 | 47 | .kf-editor-ui-button-sign { 48 | border: 5px solid transparent; 49 | border-top-color: #2d2d2d; 50 | width: 0; 51 | height: 0; 52 | margin: 0 auto; 53 | } 54 | 55 | .kf-editor-ui-button-mount-point { 56 | display: none; 57 | position: absolute; 58 | top: 81px; 59 | left: -1px; 60 | } 61 | 62 | .kf-editor-ui-overlap-button .kf-editor-ui-button-mount-point { 63 | width: 100%; 64 | height: 1000px; 65 | } 66 | 67 | .kf-editor-ui-overlap-button .kf-editor-ui-button-mount-point { 68 | top: 27px; 69 | } 70 | 71 | .kf-editor-ui-button:HOVER { 72 | background: -webkit-linear-gradient( top, rgba(255, 180, 11, 0.41), rgba(255, 180, 11, 0.61), rgba(255, 180, 11, 0.41) ); 73 | border: 1px solid #ffbf00; 74 | box-shadow: inset 0 0 1px 1px white; 75 | } 76 | 77 | .kf-editor-ui-overlap-button:HOVER { 78 | background: #8f8f8f; 79 | border-color: transparent; 80 | box-shadow: none; 81 | } 82 | 83 | .kf-editor-ui-overlap-button.kf-editor-ui-button-in { 84 | background: #8f8f8f!important; 85 | border-color: transparent!important; 86 | box-shadow: none!important; 87 | } 88 | 89 | .kf-editor-toolbar .kf-editor-ui-button-in { 90 | background: -webkit-linear-gradient( top, rgba(255, 180, 30, 0.81), rgba(255, 177, 24, 0.91), rgba(255, 180, 30, 0.81) ); 91 | border-color: #b88212; 92 | box-shadow: none; 93 | } 94 | 95 | /* 分割符 */ 96 | .kf-editor-ui-delimiter { 97 | width: 11px; 98 | height: 100%; 99 | display: inline-block; 100 | } 101 | 102 | .kf-editor-ui-delimiter-line { 103 | width: 1px; 104 | height: 100%; 105 | margin: 0 auto; 106 | background: -webkit-linear-gradient(top, rgba(233, 233, 233, 0.11), rgba(92, 92, 92, 0.20) 60%, rgba(92, 92, 92, 0.41) 80%, rgba(123, 123, 123, 0.50)); 107 | } 108 | 109 | /* box */ 110 | .kf-editor-ui-box { 111 | border: 1px solid #c2c2c2; 112 | box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.13); 113 | background: white; 114 | } 115 | 116 | .kf-editor-ui-box-group-title { 117 | background: #e1e1e1; 118 | padding: 5px; 119 | } 120 | 121 | .kf-editor-ui-box-group-item-container { 122 | padding: 5px; 123 | } 124 | 125 | .kf-editor-ui-overlap-container { 126 | overflow: hidden; 127 | } 128 | 129 | .kf-editor-ui-box-item { 130 | border: 1px solid transparent; 131 | padding: 5px; 132 | display: inline-block; 133 | } 134 | 135 | .kf-editor-ui-box-item:HOVER { 136 | background: -webkit-linear-gradient( top, rgba(255, 180, 11, 0.41), rgba(255, 180, 11, 0.61), rgba(255, 180, 11, 0.41) ); 137 | border: 1px solid #ffbf00; 138 | box-shadow: inset 0 0 1px 1px white; 139 | } 140 | 141 | .kf-editor-ui-area .kf-editor-ui-box-item { 142 | position: relative; 143 | top: 0; 144 | left: 0; 145 | width: 32px; 146 | height: 32px; 147 | border: 0; 148 | margin: 0 5px 5px 0; 149 | padding: 0; 150 | z-index: 1; 151 | } 152 | 153 | .kf-editor-ui-area .kf-editor-ui-box-item img { 154 | width: 32px; 155 | height: 32px; 156 | } 157 | 158 | 159 | .kf-editor-ui-box-item-label { 160 | margin-bottom: 5px; 161 | } 162 | 163 | .kf-editor-ui-box-item-content { 164 | background: white; 165 | border: 1px solid black; 166 | } 167 | 168 | .kf-editor-ui-area .kf-editor-ui-box-item-content { 169 | position: absolute; 170 | top: 0; 171 | left: 0; 172 | } 173 | 174 | .kf-editor-ui-area .kf-editor-ui-box-item-content:HOVER { 175 | border: 2px solid #ffbd19; 176 | top: -1px; 177 | left: -1px; 178 | } 179 | 180 | .kf-editor-ui-box-item-val { 181 | padding: 5px; 182 | margin-bottom: 5px; 183 | line-height: 0; 184 | } 185 | 186 | .kf-editor-ui-area .kf-editor-ui-box-item-val { 187 | padding: 0; 188 | margin: 0; 189 | } 190 | 191 | /* area */ 192 | .kf-editor-ui-area { 193 | height: 90%; 194 | display: inline-block; 195 | cursor: default; 196 | position: relative; 197 | top: 0; 198 | left: 0; 199 | vertical-align: top; 200 | } 201 | 202 | .kf-editor-ui-area-container { 203 | width: 510px; 204 | height: 100%; 205 | display: inline-block; 206 | border: 1px solid #D3D3D3; 207 | border-right: 0; 208 | vertical-align: top; 209 | } 210 | 211 | .kf-editor-ui-area-button { 212 | width: 15px; 213 | height: 100%; 214 | line-height: 70px; 215 | text-align: center; 216 | color: #5c5c5c; 217 | display: inline-block; 218 | border: 1px solid #D3D3D3; 219 | } 220 | 221 | .kf-editor-ui-area-button:HOVER { 222 | border-color: #ffbf00; 223 | background: rgba(255, 207, 15, 0.5); 224 | } 225 | 226 | .kf-editor-ui-area-mount { 227 | position: absolute; 228 | top: 0; 229 | left: 0; 230 | display: none; 231 | } 232 | 233 | .kf-editor-ui-overlap-title { 234 | width: 100%; 235 | line-height: 1.5; 236 | } 237 | 238 | /* list */ 239 | .kf-editor-ui-list { 240 | background: white; 241 | padding: 5px; 242 | border: 1px solid #b1b1b1; 243 | position: relative; 244 | top: 0; 245 | left: 0; 246 | } 247 | 248 | .kf-editor-ui-list-bg { 249 | width: 1px; 250 | height: 100%; 251 | position: absolute; 252 | top: 0; 253 | left: 34px; 254 | background: rgba(238, 238, 238, 0.84); 255 | box-shadow: 0px 0 1px 1px rgba(224, 224, 224, 0.16); 256 | z-index: 1; 257 | } 258 | 259 | .kf-editor-ui-list-item-container { 260 | position: relative; 261 | top: 0; 262 | left: 0; 263 | z-index: 2; 264 | } 265 | 266 | .kf-editor-ui-list-item { 267 | line-height: 16px; 268 | padding: 2px 5px; 269 | border: 1px solid transparent; 270 | } 271 | 272 | .kf-editor-ui-list-item:HOVER { 273 | border-color: #ffbd19; 274 | background: rgba(255, 207, 15, 0.5); 275 | } 276 | 277 | .kf-editor-ui-list-item-icon { 278 | width: 16px; 279 | height: 16px; 280 | display: inline-block; 281 | vertical-align: bottom; 282 | margin-right: 10px; 283 | border: 1px solid rgba(255, 207, 15, 0.8); 284 | visibility: hidden; 285 | } 286 | 287 | /* area 内容区 */ 288 | .kf-editor-ui-area-item { 289 | width: 24px; 290 | height: 24px; 291 | margin: 5px 0 0 5px; 292 | position: relative; 293 | top: 0; 294 | left: 0; 295 | display: inline-block; 296 | } 297 | 298 | .kf-editor-ui-area-item-content { 299 | border: 1px solid black; 300 | position: absolute; 301 | top: -1px; 302 | left: -1px; 303 | line-height: 0; 304 | } 305 | 306 | .kf-editor-ui-area-item-content:HOVER { 307 | border: 2px solid #ffbd19; 308 | top: -2px; 309 | left: -2px; 310 | } 311 | 312 | .kf-editor-ui-area-item-content img { 313 | width: 24px; 314 | height: 24px; 315 | } -------------------------------------------------------------------------------- /dev-lib/cmd-define.js: -------------------------------------------------------------------------------- 1 | /** 2 | * cmd 内部定义 3 | * build用 4 | */ 5 | 6 | // 模块存储 7 | var _modules = {}; 8 | 9 | function define ( id, deps, factory ) { 10 | 11 | _modules[ id ] = { 12 | 13 | exports: {}, 14 | value: null, 15 | factory: null 16 | 17 | }; 18 | 19 | if ( arguments.length === 2 ) { 20 | 21 | factory = deps; 22 | 23 | } 24 | 25 | if ( _modules.toString.call( factory ) === '[object Object]' ) { 26 | 27 | _modules[ id ][ 'value' ] = factory; 28 | 29 | } else if ( typeof factory === 'function' ) { 30 | 31 | _modules[ id ][ 'factory' ] = factory; 32 | 33 | } else { 34 | 35 | throw new Error( 'define函数未定义的行为' ); 36 | 37 | } 38 | 39 | } 40 | 41 | function require ( id ) { 42 | 43 | var module = _modules[ id ], 44 | exports = null; 45 | 46 | if ( !module ) { 47 | 48 | return null; 49 | 50 | } 51 | 52 | if ( module.value ) { 53 | 54 | return module.value; 55 | 56 | } 57 | 58 | exports = module.factory.call( null, require, module.exports, module ); 59 | 60 | // return 值不为空, 则以return值为最终值 61 | if ( exports ) { 62 | 63 | module.exports = exports; 64 | 65 | } 66 | 67 | module.value = module.exports; 68 | 69 | return module.value; 70 | 71 | } 72 | 73 | function use ( id ) { 74 | 75 | return require( id ); 76 | 77 | } -------------------------------------------------------------------------------- /dev-lib/dev-define.js: -------------------------------------------------------------------------------- 1 | /** 2 | * cmd 内部定义 3 | * 开发用 4 | */ 5 | 6 | ( function ( global ) { 7 | 8 | var _modules = {}, 9 | loaded = {}; 10 | 11 | global.inc = { 12 | 13 | base: '', 14 | 15 | config: function ( options ) { 16 | 17 | this.base = options.base || ''; 18 | 19 | }, 20 | 21 | use: function ( id ) { 22 | 23 | return require( id ); 24 | 25 | }, 26 | 27 | remove: function ( node ) { 28 | 29 | node.parentNode.removeChild( node ); 30 | 31 | } 32 | 33 | }; 34 | 35 | global.define = function ( id, deps, f ) { 36 | 37 | var argLen = arguments.length, 38 | module = null; 39 | 40 | switch ( argLen ) { 41 | 42 | case 1: 43 | var scriptNode = document.getElementsByTagName( 'script' ); 44 | f = id; 45 | id = scriptNode[ scriptNode.length - 1 ].getAttribute( "data-id" ); 46 | break; 47 | 48 | case 2: 49 | if ( typeof id === 'string' ) { 50 | 51 | f = deps; 52 | 53 | } else { 54 | 55 | var scriptNode = document.getElementsByTagName( 'script' ); 56 | f = deps; 57 | id = scriptNode[ scriptNode.length - 1 ].getAttribute( "data-id" ); 58 | 59 | } 60 | 61 | break; 62 | 63 | } 64 | 65 | module = _modules[ id ] = { 66 | 67 | exports: {}, 68 | value: null, 69 | factory: null 70 | 71 | }; 72 | 73 | loadDeps( f ); 74 | 75 | if ( typeof f === 'function' ) { 76 | 77 | module.factory = f; 78 | 79 | } else { 80 | 81 | module.value = f; 82 | 83 | } 84 | 85 | } 86 | 87 | function require ( id ) { 88 | 89 | var exports = {}, 90 | module = _modules[ id ]; 91 | 92 | if ( module.value ) { 93 | 94 | return module.value; 95 | 96 | } 97 | 98 | exports = module.factory( require, module.exports, module ); 99 | 100 | if ( exports ) { 101 | 102 | module.exports = exports; 103 | 104 | } 105 | 106 | module.value = module.exports; 107 | module.exports = null; 108 | module.factory = null; 109 | 110 | return module.value; 111 | 112 | } 113 | 114 | function loadDeps ( factory ) { 115 | 116 | var deps = null, 117 | pathname = location.pathname, 118 | uri = location.protocol + '//' + location.host; 119 | 120 | pathname = pathname.split( '/'); 121 | 122 | if ( pathname[ pathname.length - 1 ] !== '' ) { 123 | 124 | pathname[ pathname.length - 1 ] = ''; 125 | 126 | } 127 | 128 | uri += pathname.join( '/' ); 129 | 130 | if ( typeof factory === 'function' ) { 131 | 132 | deps = loadDepsByFunction( factory ); 133 | 134 | } else { 135 | 136 | // 未处理object的情况 137 | return; 138 | 139 | } 140 | 141 | for ( var i = 0, len = deps.length; i < len; i++ ) { 142 | 143 | var key = deps[ i ]; 144 | 145 | if ( loaded[ key ] ) { 146 | continue; 147 | } 148 | 149 | loaded[ key ] = true; 150 | 151 | document.write( '' ); 152 | 153 | } 154 | 155 | } 156 | 157 | function loadDepsByFunction ( factory ) { 158 | 159 | var content = factory.toString(), 160 | match = null, 161 | deps = [], 162 | pattern = /require\s*\(\s*([^)]+?)\s*\)/g; 163 | 164 | while ( match = pattern.exec( content ) ) { 165 | 166 | deps.push( match[ 1 ].replace( /'|"/g, '' ) ); 167 | 168 | } 169 | 170 | return deps; 171 | 172 | } 173 | 174 | } )( this ); -------------------------------------------------------------------------------- /dev-lib/dev-start.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hn on 13-12-4. 3 | */ 4 | // 启动脚本 5 | inc.use( 'kf.start' ); -------------------------------------------------------------------------------- /dev-lib/exports.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 模块暴露 3 | */ 4 | 5 | ( function ( global ) { 6 | 7 | define( 'kf.start', function ( require ) { 8 | 9 | var KFEditor = require( "editor/editor" ); 10 | 11 | // 注册组件 12 | KFEditor.registerComponents( "ui", require( "ui/ui" ) ); 13 | KFEditor.registerComponents( "parser", require( "parse/parser" ) ); 14 | KFEditor.registerComponents( "render", require( "render/render" ) ); 15 | KFEditor.registerComponents( "position", require( "position/position" ) ); 16 | KFEditor.registerComponents( "syntax", require( "syntax/syntax" ) ); 17 | 18 | kf.Editor = KFEditor; 19 | 20 | } ); 21 | 22 | // build环境中才含有use 23 | try { 24 | use( 'kf.start' ); 25 | } catch ( e ) { 26 | } 27 | 28 | } )( this ); 29 | -------------------------------------------------------------------------------- /dev-lib/kityformula-parser.all.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ==================================================== 3 | * kityformula-editor - v1.0.0 - 2014-04-03 4 | * https://github.com/HanCong03/kityformula-editor 5 | * GitHub: https://github.com/kitygraph/kityformula-editor.git 6 | * Copyright (c) 2014 Baidu Kity Group; Licensed MIT 7 | * ==================================================== 8 | */ 9 | !function(){function a(a,b,c){if(d[a]={exports:{},value:null,factory:null},2===arguments.length&&(c=b),"[object Object]"===d.toString.call(c))d[a].value=c;else{if("function"!=typeof c)throw new Error("define函数未定义的行为");d[a].factory=c}}function b(a){var c=d[a],e=null;return c?c.value?c.value:(e=c.factory.call(null,b,c.exports,c),e&&(c.exports=e),c.value=c.exports,c.value):null}function c(a){return b(a)}var d={};a("assembly",[],function(){function a(a,b){this.formula=new kf.Formula(a,b)}function b(a,b,e,f,g){var i,j=null,k=null,l=[],m=b.operand||[],n=null;if(e.operand=[],-1===b.name.indexOf("text")){for(var o=0,p=m.length;p>o;o++)j=m[o],j!==h?j?"string"==typeof j?(m[o]="brackets"===b.name&&2>o?j:c("text",j),e.operand.push(m[o])):(e.operand.push({}),m[o]=arguments.callee(a.operand[o],j,e.operand[e.operand.length-1],f,g)):(m[o]=c("empty"),e.operand.push(m[o])):(l.push(o),g.hasOwnProperty("startOffset")||(g.startOffset=o),g.endOffset=o,b.attr&&b.attr.id&&(g.groupId=b.attr.id));for(;o=l.length;)o=l[o-1],m.splice(o,1),l.length--,a.operand.splice(o,1)}if(n=d(b.name),!n)throw new Error("operator type error: not found "+b.operator);i=function(){},i.prototype=n.prototype,k=new i,n.apply(k,m),e.func=k;for(var q in b.callFn)b.callFn.hasOwnProperty(q)&&k[q]&&k[q].apply(k,b.callFn[q]);return b.attr&&(b.attr.id&&(f[b.attr.id]={objGroup:k,strGroup:a}),k.setAttr(b.attr)),k}function c(a,b){switch(a){case"empty":return new kf.EmptyExpression;case"text":return new kf.TextExpression(b)}}function d(a){return g[a]||kf[a.replace(/^[a-z]/i,function(a){return a.toUpperCase()}).replace(/-([a-z])/gi,function(a,b){return b.toUpperCase()})+"Expression"]}function e(a){var b={};if("[object Array]"==={}.toString.call(a)){b=[];for(var c=0,d=a.length;d>c;c++)b[c]=f(a[c])}else for(var e in a)a.hasOwnProperty(e)&&(b[e]=f(a[e]));return b}function f(a){return a?"object"!=typeof a?a:e(a):a}var g={},h="";return a.prototype.generateBy=function(a){var c=a.tree,d={},f={},g={};return"string"!=typeof c?(this.formula.appendExpression(b(c,e(c),d,g,f)),{select:f,parsedTree:c,tree:d,mapping:g}):(d=new kf.TextExpression(c),void this.formula.appendExpression(d))},a.prototype.regenerateBy=function(a){return this.formula.clearExpressions(),this.generateBy(a)},{use:function(b,c){return new a(b,c)}}}),a("impl/latex/base/latex-utils",["impl/latex/base/rpn","impl/latex/base/utils","impl/latex/define/type","impl/latex/base/tree","impl/latex/handler/combination"],function(a){return{toRPNExpression:a("impl/latex/base/rpn"),generateTree:a("impl/latex/base/tree")}}),a("impl/latex/base/rpn",["impl/latex/base/utils","impl/latex/define/operator","impl/latex/define/func","impl/latex/handler/func","impl/latex/define/type"],function(a){function b(a){for(var b=[],c=null;void 0!==(c=a.pop());)if(c&&"object"==typeof c&&c.sign===!1){var d=c.handler(c,[],b.reverse());b.unshift(d),b.reverse()}else b.push(c);return b.reverse()}var c=a("impl/latex/base/utils");return function(d){var e=[],f=(a("impl/latex/define/type"),null);for(d=b(d);f=d.shift();)"combination"===f.name&&1===f.operand.length&&"brackets"===f.operand[0].name&&(f=f.operand[0]),e.push(c.isArray(f)?arguments.callee(f):f);return e}}),a("impl/latex/base/tree",["impl/latex/define/type","impl/latex/handler/combination","impl/latex/base/utils","impl/latex/define/operator","impl/latex/define/func","impl/latex/handler/func"],function(a){var b=(a("impl/latex/define/type"),a("impl/latex/handler/combination")),c=a("impl/latex/base/utils");return function(a){for(var d=null,e=[],f=0,g=a.length;g>f;f++)c.isArray(a[f])&&(a[f]=arguments.callee(a[f]));for(;d=a.shift();)e.push("object"==typeof d&&d.handler?d.handler(d,e,a):d);return b(e)}}),a("impl/latex/base/utils",["impl/latex/define/operator","impl/latex/handler/script","impl/latex/handler/func","impl/latex/define/type","impl/latex/handler/fraction","impl/latex/handler/sqrt","impl/latex/handler/summation","impl/latex/handler/integration","impl/latex/handler/brackets","impl/latex/define/func","impl/latex/handler/lib/int-extract"],function(a){var b=a("impl/latex/define/operator"),c=a("impl/latex/define/func"),d=a("impl/latex/handler/func"),e={getLatexType:function(a){return a=a.replace(/^\\/,""),b[a]?"operator":c[a]?"function":"text"},isArray:function(a){return a&&"[object Array]"===Object.prototype.toString.call(a)},getDefine:function(a){return e.extend({},b[a.replace("\\","")])},getFuncDefine:function(a){return{name:"function",params:a.replace(/^\\/,""),handler:d}},getBracketsDefine:function(a,c){return e.extend({params:[a,c]},b.brackets)},extend:function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}};return e}),a("impl/latex/define/brackets",[],function(){var a=!0;return{".":a,"{":a,"}":a,"[":a,"]":a,"(":a,")":a,"|":a}}),a("impl/latex/define/func",[],function(){return{sin:1,cos:1,arccos:1,cosh:1,det:1,inf:1,limsup:1,Pr:1,tan:1,arcsin:1,cot:1,dim:1,ker:1,ln:1,sec:1,tanh:1,arctan:1,coth:1,exp:1,lg:1,log:1,arg:1,csc:1,gcd:1,lim:1,max:1,sinh:1,cos:1,deg:1,hom:1,liminf:1,min:1,sup:1}}),a("impl/latex/define/operator",["impl/latex/handler/script","impl/latex/handler/func","impl/latex/handler/lib/int-extract","impl/latex/define/type","impl/latex/handler/fraction","impl/latex/handler/sqrt","impl/latex/handler/summation","impl/latex/handler/integration","impl/latex/handler/brackets","impl/latex/define/brackets"],function(a){var b=a("impl/latex/handler/script"),c=(a("impl/latex/handler/func"),a("impl/latex/define/type"));return{"^":{name:"superscript",type:c.OP,handler:b},_:{name:"subscript",type:c.OP,handler:b},frac:{name:"fraction",type:c.FN,sign:!1,handler:a("impl/latex/handler/fraction")},sqrt:{name:"radical",type:c.FN,sign:!1,handler:a("impl/latex/handler/sqrt")},sum:{name:"summation",type:c.FN,traversal:"rtl",handler:a("impl/latex/handler/summation")},"int":{name:"integration",type:c.FN,traversal:"rtl",handler:a("impl/latex/handler/integration")},brackets:{name:"brackets",type:"TYPE.FN",handler:a("impl/latex/handler/brackets")}}}),a("impl/latex/define/pre",["impl/latex/pre/sqrt","impl/latex/pre/int"],function(a){return{sqrt:a("impl/latex/pre/sqrt"),"int":a("impl/latex/pre/int")}}),a("impl/latex/define/reverse",["impl/latex/reverse/combination","impl/latex/reverse/fraction","impl/latex/reverse/func","impl/latex/reverse/integration","impl/latex/reverse/subscript","impl/latex/reverse/superscript","impl/latex/reverse/script","impl/latex/reverse/sqrt","impl/latex/reverse/summation","impl/latex/reverse/brackets"],function(a){return{combination:a("impl/latex/reverse/combination"),fraction:a("impl/latex/reverse/fraction"),"function":a("impl/latex/reverse/func"),integration:a("impl/latex/reverse/integration"),subscript:a("impl/latex/reverse/subscript"),superscript:a("impl/latex/reverse/superscript"),script:a("impl/latex/reverse/script"),radical:a("impl/latex/reverse/sqrt"),summation:a("impl/latex/reverse/summation"),brackets:a("impl/latex/reverse/brackets")}}),a("impl/latex/define/type",[],function(){return{OP:1,FN:2}}),a("impl/latex/handler/brackets",["impl/latex/define/brackets"],function(a){var b=a("impl/latex/define/brackets");return function(a,c,d){for(var e=0,f=a.params.length;f>e;e++)if(!(a.params[e]in b))throw new Error("Brackets: invalid params");return a.operand=a.params,a.params[2]=d.shift(),delete a.handler,delete a.params,a}}),a("impl/latex/handler/combination",[],function(){return function(){return 0===arguments[0].length?null:{name:"combination",operand:arguments[0]}}}),a("impl/latex/handler/fraction",[],function(){return function(a,b,c){var d=c.shift(),e=c.shift();if(void 0===d||void 0===e)throw new Error("Frac: Syntax Error");return a.operand=[d,e],delete a.handler,a}}),a("impl/latex/handler/func",["impl/latex/handler/lib/int-extract"],function(a){var b=a("impl/latex/handler/lib/int-extract");return function(a,c,d){var e=b(d);return a.operand=[a.params,e.exp,e.sup,e.sub],delete a.params,delete a.handler,a}}),a("impl/latex/handler/integration",["impl/latex/handler/lib/int-extract"],function(a){var b=a("impl/latex/handler/lib/int-extract");return function(a,c,d){var e=d.shift(),f=b(d);return a.operand=[f.exp,f.sup,f.sub],a.callFn={setType:[0|e]},delete a.handler,a}}),a("impl/latex/handler/lib/int-extract",[],function(){return function(a){var b=a.shift()||null,c=null,d=null;return null!==b&&("string"==typeof b?(d=b,b=null):"superscript"===b.name?(b=a.shift()||null,b&&(c=a.shift()||null,c&&("subscript"===c.name?(c=a.shift()||null,d=a.shift()||null):(d=c,c=null)))):"subscript"===b.name?(c=a.shift()||null,c&&(b=a.shift()||null,b&&("superscript"===b.name?(b=a.shift()||null,d=a.shift()||null):(d=b,b=null)))):(d=b,b=null)),{sub:c,sup:b,exp:d}}}),a("impl/latex/handler/script",[],function(){return function(a,b,c){var d=b.pop(),e=c.shift()||null;if(!e)throw new Error("Missing script");if(d=d||"",d.name===a.name||"script"===d.name)throw new Error("script error");return"subscript"===d.name?(d.name="script",d.operand[2]=d.operand[1],d.operand[1]=e,d):"superscript"===d.name?(d.name="script",d.operand[2]=e,d):(a.operand=[d,e],delete a.handler,a)}}),a("impl/latex/handler/sqrt",[],function(){return function(a,b,c){var d=c.shift(),e=c.shift();return a.operand=[e,d],delete a.handler,a}}),a("impl/latex/handler/summation",["impl/latex/handler/lib/int-extract"],function(a){var b=a("impl/latex/handler/lib/int-extract");return function(a,c,d){var e=b(d);return a.operand=[e.exp,e.sup,e.sub],delete a.handler,a}}),a("impl/latex/latex",["parser","impl/latex/base/latex-utils","impl/latex/base/rpn","impl/latex/base/tree","impl/latex/define/pre","impl/latex/pre/sqrt","impl/latex/pre/int","impl/latex/serialization","impl/latex/define/reverse","impl/latex/define/operator","impl/latex/handler/script","impl/latex/handler/func","impl/latex/define/type","impl/latex/handler/fraction","impl/latex/handler/sqrt","impl/latex/handler/summation","impl/latex/handler/integration","impl/latex/handler/brackets","impl/latex/reverse/combination","impl/latex/reverse/fraction","impl/latex/reverse/func","impl/latex/reverse/integration","impl/latex/reverse/subscript","impl/latex/reverse/superscript","impl/latex/reverse/script","impl/latex/reverse/sqrt","impl/latex/reverse/summation","impl/latex/reverse/brackets","impl/latex/base/utils","impl/latex/define/func"],function(a){function b(a){var b=k.getLatexType(a);switch(b){case"operator":return k.getDefine(a);case"function":return k.getFuncDefine(a);default:return c(a)}}function c(a){return 0===a.indexOf("\\")?a+"\\":a}function d(a){return a.replace(/\\\s+/,"").replace(/\s*([^a-z0-9\s])\s*/gi,function(a,b){return b})}var e=a("parser").Parser,f=a("impl/latex/base/latex-utils"),g=a("impl/latex/define/pre"),h=a("impl/latex/serialization"),i=a("impl/latex/define/operator"),j=a("impl/latex/define/reverse"),k=a("impl/latex/base/utils"),l="￸",m="",n=new RegExp(l+"|"+m,"g"),o=new RegExp(l,"g"),p=new RegExp(m,"g");e.register("latex",e.implement({parse:function(a){var b=this.split(this.format(a));return b=this.parseToGroup(b),b=this.parseToStruct(b),this.generateTree(b)},serialization:function(a,b){return h(a,b)},expand:function(a){var b=a.parse,c=null,d=a.pre,e=a.reverse;for(var f in b)b.hasOwnProperty(f)&&(c=f.replace(/\\/g,""),i[c]=b[f]);for(var f in e)e.hasOwnProperty(f)&&(j[f.replace(/\\/g,"")]=e[f]);if(d)for(var f in d)d.hasOwnProperty(f)&&(g[f.replace(/\\/g,"")]=d[f])},format:function(a){a=d(a),a=a.replace(n,"").replace(/\\{/gi,l).replace(/\\}/gi,m);for(var b in g)g.hasOwnProperty(b)&&(a=g[b](a));return a},split:function(a){var b=[],c=/(?:\\[a-z]+\s*)|(?:[{}]\s*)|(?:[^\\{}]\s*)/gi,d=/^\s+|\s+$/g,e=null;for(a=a.replace(d,"");e=c.exec(a);)e=e[0].replace(d,""),e&&b.push(e);return b},generateTree:function(a){for(var b=[],c=null;c=a.shift();)b.push(k.isArray(c)?this.generateTree(c):c);return b=f.toRPNExpression(b),f.generateTree(b)},parseToGroup:function(a){for(var b=[],c=[b],d=0,e=0,f=0,g=a.length;g>f;f++)switch(a[f]){case"{":d++,c.push(b),b.push([]),b=b[b.length-1];break;case"}":d--,b=c.pop();break;case"\\left":e++,c.push(b),b.push([[]]),b=b[b.length-1][0],b.type="brackets",f++,b.leftBrackets=a[f].replace(o,"{").replace(p,"}");break;case"\\right":e--,f++,b.rightBrackets=a[f].replace(o,"{").replace(p,"}"),b=c.pop();break;default:b.push(a[f].replace(o,"{").replace(p,"}"))}if(0!==d)throw new Error("Group Error!");if(0!==e)throw new Error("Brackets Error!");return c[0]},parseToStruct:function(a){for(var c=[],d=0,e=a.length;e>d;d++)k.isArray(a[d])?"brackets"===a[d].type?(c.push(k.getBracketsDefine(a[d].leftBrackets,a[d].rightBrackets)),c.push(this.parseToStruct(a[d]))):c.push(this.parseToStruct(a[d])):c.push(b(a[d]));return c}}))}),a("impl/latex/pre/int",[],function(){return function(a){return a.replace(/\\(i+)nt(\b|[^a-zA-Z])/g,function(a,b,c){return"\\int "+b.length+c})}}),a("impl/latex/pre/sqrt",[],function(){return function(a){return a.replace(/\\sqrt\s*((?:\[[^\]]*\])?)/g,function(a,b){return"\\sqrt{"+b.replace(/^\[|\]$/g,"")+"}"})}}),a("impl/latex/reverse/brackets",[],function(){return function(a){return["\\left",a[0],a[2],"\\right",a[1]].join(" ")}}),a("impl/latex/reverse/combination",[],function(){new RegExp("","g");return function(a){return this.attr["data-root"]||this.attr["data-placeholder"]?a.join(""):"{"+a.join("")+"}"}}),a("impl/latex/reverse/fraction",[],function(){return function(a){return"\\frac "+a[0]+" "+a[1]}}),a("impl/latex/reverse/func",[],function(){return function(a){var b=["\\"+a[0]];return a[2]&&b.push("^"+a[2]),a[3]&&b.push("_"+a[3]),b.push(" "+a[1]),b.join("")}}),a("impl/latex/reverse/integration",[],function(){return function(a){var b=["\\int"];return a[1]&&b.push("^"+a[1]),a[2]&&b.push("_"+a[2]),b.push(" "+a[0]),a.join("")}}),a("impl/latex/reverse/script",[],function(){return function(a){return a[0]+"^"+a[1]+"_"+a[2]}}),a("impl/latex/reverse/sqrt",[],function(){return function(a){var b=["\\sqrt"];return a[1]&&b.push("["+a[1]+"]"),b.push(" "+a[0]),b.join("")}}),a("impl/latex/reverse/subscript",[],function(){return function(a){return a[0]+"_"+a[1]}}),a("impl/latex/reverse/summation",[],function(){return function(a){var b=["\\sum"];return a[1]&&b.push("^"+a[1]),a[2]&&b.push("_"+a[2]),b.push(" "+a[0]),b.join("")}}),a("impl/latex/reverse/superscript",[],function(){return function(a){return a[0]+"^"+a[1]}}),a("impl/latex/serialization",["impl/latex/define/reverse","impl/latex/reverse/combination","impl/latex/reverse/fraction","impl/latex/reverse/func","impl/latex/reverse/integration","impl/latex/reverse/subscript","impl/latex/reverse/superscript","impl/latex/reverse/script","impl/latex/reverse/sqrt","impl/latex/reverse/summation","impl/latex/reverse/brackets"],function(a){function b(a,e){var f=[],g=null;if("object"!=typeof a)return a.replace(d,function(a,b){return b+" "});"combination"===a.name&&1===a.operand.length&&"combination"===a.operand[0].name&&(a=a.operand[0]),g=a.operand;for(var h=0,i=g.length;i>h;h++)f.push(g[h]?b(g[h]):g[h]);return c[a.name].call(a,f,e)}var c=a("impl/latex/define/reverse"),d=/(\\[\w]+)\\/g;return function(a,c){return b(a,c)}}),a("parser",[],function(a,b,c){function d(a){this.impl=new a,this.conf={}}function e(){this.conf={}}var f={},g={},h={extend:function(a,b){var c=null;b=[].slice.call(arguments,1);for(var d=0,e=b.length;e>d;d++){c=b[d];for(var f in c)c.hasOwnProperty(f)&&(a[f]=c[f])}},setData:function(a,b,c){if("string"==typeof b)a[b]=c;else{if("object"!=typeof b)throw new Error("invalid option");for(c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}}},i={use:function(a){if(!g[a])throw new Error("unknown parser type");return this.proxy(g[a])},config:function(a,b){return h.setData(f,a,b),this},register:function(a,b){return g[a.toLowerCase()]=b,this},implement:function(a){var b=function(){},c=a.constructor||function(){},d=function(){e.call(this),c.call(this)};b.prototype=e.prototype,d.prototype=new b,delete a.constructor;for(var f in a)"constructor"!==f&&a.hasOwnProperty(f)&&(d.prototype[f]=a[f]);return d},proxy:function(a){return new d(a)}};h.extend(d.prototype,{config:function(a,b){h.setData(this.conf,a,b)},set:function(a,b){this.impl.set(a,b)},parse:function(a){var b={config:{},tree:this.impl.parse(a)};return h.extend(b.config,f,this.conf),b},serialization:function(a,b){return this.impl.serialization(a,b)},expand:function(a){this.impl.expand(a)}}),h.extend(e.prototype,{set:function(a,b){h.extend(this.conf,a,b)},parse:function(){throw new Error("Abstract function")}}),c.exports={Parser:i,ParserInterface:e}}),function(b){a("kf.start",function(a){var c=a("parser").Parser;a("impl/latex/latex"),b.kf.Parser=c,b.kf.Assembly=a("assembly")});try{c("kf.start")}catch(d){}}(this)}(); -------------------------------------------------------------------------------- /dev-lib/sea.js: -------------------------------------------------------------------------------- 1 | /*! Sea.js 2.1.1 | seajs.org/LICENSE.md 2 | //# sourceMappingURL=sea.js.map 3 | */(function(t,u){function v(b){return function(c){return Object.prototype.toString.call(c)==="[object "+b+"]"}}function Q(){return w++}function I(b,c){var a;a=b.charAt(0);if(R.test(b))a=b;else if("."===a){a=(c?c.match(E)[0]:h.cwd)+b;for(a=a.replace(S,"/");a.match(J);)a=a.replace(J,"/")}else a="/"===a?(a=h.cwd.match(T))?a[0]+b.substring(1):b:h.base+b;return a}function K(b,c){if(!b)return"";var a=b,d=h.alias,a=b=d&&F(d[a])?d[a]:a,d=h.paths,g;if(d&&(g=a.match(U))&&F(d[g[1]]))a=d[g[1]]+g[2];g=a;var e=h.vars; 4 | e&&-11*navigator.userAgent.replace(/.*AppleWebKit\/(\d+)\..*/,"$1"),Z=/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g,$=/\\\\/g,r=f.cache={},C,G={},H={},D={},j=e.STATUS={FETCHING:1, 8 | SAVED:2,LOADING:3,LOADED:4,EXECUTING:5,EXECUTED:6};e.prototype.resolve=function(){for(var b=this.dependencies,c=[],a=0,d=b.length;a=j.LOADING)){this.status=j.LOADING;var b=this.resolve();m("load",b);for(var c=this._remain=b.length,a,d=0;d=j.EXECUTING)return this.exports;this.status=j.EXECUTING;var c=this.uri;b.resolve=function(a){return e.resolve(a,c)};b.async=function(a,g){e.use(a,g,c+"_async_"+w++);return b};var a=this.factory,a= 12 | x(a)?a(b,this.exports={},this):a;a===u&&(a=this.exports);null===a&&!O.test(c)&&m("error",this);delete this.factory;this.exports=a;this.status=j.EXECUTED;m("exec",this);return a};e.resolve=function(b,c){var a={id:b,refUri:c};m("resolve",a);return a.uri||K(a.id,c)};e.define=function(b,c,a){var d=arguments.length;1===d?(a=b,b=u):2===d&&(a=c,A(b)?(c=b,b=u):c=u);if(!A(c)&&x(a)){var g=[];a.toString().replace($,"").replace(Z,function(a,b,c){c&&g.push(c)});c=g}d={id:b,uri:e.resolve(b),deps:c,factory:a};if(!d.uri&& 13 | n.attachEvent){var f=W();f&&(d.uri=f.src)}m("define",d);d.uri?e.save(d.uri,d):C=d};e.save=function(b,c){var a=e.get(b);a.status 2 | 3 | 4 | 公式编辑器 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 |
49 | 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kityformula-editor", 3 | "title": "Kityformula-Editor", 4 | "description": "HTML(SVG) Formula display solutions", 5 | "version": "1.0.0", 6 | "homepage": "https://github.com/kitygraph/formula", 7 | "author": { 8 | "name": "Baidu Kity Group", 9 | "url": "http://ueditor.baidu.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/kitygraph/formula.git" 14 | }, 15 | "keywords": [ 16 | "formula", 17 | "kity", 18 | "svg", 19 | "graphic", 20 | "javascript", 21 | "library" 22 | ], 23 | "bugs": { 24 | "url": "http://www.ueditorbbs.com/forum.php" 25 | }, 26 | "licenses": [ 27 | { 28 | "type": "MIT", 29 | "url": "https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt" 30 | } 31 | ], 32 | "dependencies": {}, 33 | "devDependencies": { 34 | "grunt": "~0.4.1", 35 | "grunt-cmd-transport": "~0.3.0", 36 | "grunt-cmd-concat": "~0.2.5", 37 | "grunt-contrib-concat": "~0.3.0", 38 | "grunt-contrib-uglify": "~0.2.6", 39 | "grunt-contrib-clean": "~0.5.0" 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /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 | isArray: function ( obj ) { 42 | return obj && ({}).toString.call( obj ) === "[object Array]"; 43 | }, 44 | 45 | isString: function ( obj ) { 46 | return typeof obj === "string"; 47 | }, 48 | 49 | proxy: function ( fn, context ) { 50 | 51 | return function () { 52 | return fn.apply( context, arguments ); 53 | }; 54 | 55 | }, 56 | 57 | each: function ( obj, fn ) { 58 | 59 | if ( !obj ) { 60 | return; 61 | } 62 | 63 | if ( 'length' in obj && typeof obj.length === "number" ) { 64 | 65 | for ( var i = 0, len = obj.length; i < len; i++ ) { 66 | 67 | if ( fn.call( null, obj[ i ], i, obj ) === false ) { 68 | break; 69 | } 70 | 71 | } 72 | 73 | } else { 74 | 75 | for ( var key in obj ) { 76 | 77 | if ( obj.hasOwnProperty( key ) ) { 78 | if ( fn.call( null, obj[ key ], key, obj ) === false ) { 79 | break; 80 | } 81 | } 82 | 83 | } 84 | 85 | } 86 | 87 | } 88 | }; 89 | 90 | function copy ( isDeep, target, source, count ) { 91 | 92 | count = count | 0; 93 | 94 | if ( count > MAX_COPY_DEEP ) { 95 | return source; 96 | } 97 | 98 | count++; 99 | 100 | commonUtils.each( source, function ( value, index, origin ) { 101 | 102 | if ( isDeep ) { 103 | 104 | if ( !value || ( typeof value !== "object" && typeof value !== "function" ) ) { 105 | target[ index ] = value; 106 | } else { 107 | target[ index ] = target[ index ] || ( commonUtils.isArray( value ) ? [] : {} ); 108 | target[ index ] = copy( isDeep, target[ index ], value, count ); 109 | } 110 | 111 | } else { 112 | target[ index ] = value; 113 | } 114 | 115 | } ); 116 | 117 | return target; 118 | 119 | } 120 | 121 | return commonUtils; 122 | 123 | } ); -------------------------------------------------------------------------------- /src/base/event/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hn on 14-3-17. 3 | */ 4 | 5 | define( function ( require, exports, modules ) { 6 | 7 | var EVENT_LISTENER = {}, 8 | eid = 0, 9 | Utils = this, 10 | BEFORE_RESULT = true, 11 | KFEvent = require( "base/event/kfevent" ), 12 | commonUtils = require( "base/common" ), 13 | EVENT_HANDLER = function ( e ) { 14 | 15 | var type = e.type, 16 | target = e.target, 17 | eid = this.__kfe_eid, 18 | hasAutoTrigger = /^(?:before|after)/.test( type ), 19 | HANDLER_LIST = EVENT_LISTENER[ eid ][ type ]; 20 | 21 | if ( !hasAutoTrigger ) { 22 | 23 | EventListener.trigger( target, 'before' + type ); 24 | 25 | if ( BEFORE_RESULT === false ) { 26 | BEFORE_RESULT = true; 27 | return false; 28 | } 29 | 30 | } 31 | 32 | commonUtils.each( HANDLER_LIST, function ( handler, index ) { 33 | 34 | if ( !handler ) { 35 | return; 36 | } 37 | 38 | if ( handler.call( target, e ) === false ) { 39 | BEFORE_RESULT = false; 40 | return BEFORE_RESULT; 41 | } 42 | 43 | } ); 44 | 45 | if ( !hasAutoTrigger ) { 46 | 47 | EventListener.trigger( target, 'after' + type ); 48 | 49 | } 50 | 51 | }; 52 | 53 | var EventListener = { 54 | 55 | addEvent: function ( target, type, handler ) { 56 | 57 | var hasHandler = true, 58 | eventCache = null; 59 | 60 | if ( !target.__kfe_eid ) { 61 | hasHandler = false; 62 | target.__kfe_eid = generateId(); 63 | EVENT_LISTENER[ target.__kfe_eid ] = {}; 64 | } 65 | 66 | eventCache = EVENT_LISTENER[ target.__kfe_eid ]; 67 | 68 | if ( !eventCache[ type ] ) { 69 | hasHandler = false; 70 | eventCache[ type ] = []; 71 | } 72 | 73 | eventCache[ type ].push( handler ); 74 | 75 | if ( hasHandler ) { 76 | return; 77 | } 78 | 79 | target.addEventListener( type, EVENT_HANDLER, false ); 80 | 81 | }, 82 | 83 | trigger: function ( target, type, e ) { 84 | 85 | e = e || KFEvent.createEvent( type, e ); 86 | 87 | target.dispatchEvent( e ); 88 | 89 | } 90 | 91 | }; 92 | 93 | function generateId () { 94 | 95 | return ++eid; 96 | 97 | } 98 | 99 | return EventListener; 100 | 101 | } ); -------------------------------------------------------------------------------- /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/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/editor/editor.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 编辑器主体结构 3 | */ 4 | 5 | define( function ( require ) { 6 | 7 | var kity = require( "kity" ), 8 | Utils = require( "base/utils" ), 9 | defaultOpt = { 10 | controller: { 11 | zoom: true, 12 | maxzoom: 5, 13 | minzoom: 0.5 14 | }, 15 | formula: { 16 | fontsize: 50, 17 | autoresize: false 18 | } 19 | 20 | }; 21 | 22 | var COMPONENTS = {}; 23 | 24 | var KFEditor = kity.createClass( 'KFEditor', { 25 | 26 | constructor: function ( container, opt ) { 27 | 28 | this.options = Utils.extend( true, {}, defaultOpt, opt ); 29 | 30 | this.container = container; 31 | this.services = {}; 32 | this.commands = {}; 33 | 34 | this.initComponents(); 35 | 36 | }, 37 | 38 | getContainer: function () { 39 | return this.container; 40 | }, 41 | 42 | getOptions: function () { 43 | return this.options; 44 | }, 45 | 46 | initComponents: function () { 47 | 48 | var _self = this; 49 | 50 | Utils.each( COMPONENTS, function ( component ) { 51 | 52 | new component( _self ); 53 | 54 | } ); 55 | 56 | }, 57 | 58 | requestService: function ( serviceName, args ) { 59 | 60 | var serviceObject = getService.call( this, serviceName ); 61 | 62 | return serviceObject.service[ serviceObject.key ].apply( serviceObject.provider, [].slice.call( arguments, 1 ) ); 63 | 64 | }, 65 | 66 | request: function ( serviceName ) { 67 | 68 | var serviceObject = getService.call( this, serviceName ); 69 | 70 | return serviceObject.service; 71 | 72 | }, 73 | 74 | registerService: function ( serviceName, provider, serviceObject ) { 75 | 76 | var key = null; 77 | 78 | for ( key in serviceObject ) { 79 | 80 | if ( serviceObject[ key ] && serviceObject.hasOwnProperty( key ) ) { 81 | serviceObject[ key ] = Utils.proxy( serviceObject[ key ], provider ); 82 | } 83 | 84 | } 85 | 86 | this.services[ serviceName ] = { 87 | provider: provider, 88 | key: key, 89 | service: serviceObject 90 | }; 91 | 92 | }, 93 | 94 | registerCommand: function ( commandName, executor, execFn ) { 95 | 96 | this.commands[ commandName ] = { 97 | executor: executor, 98 | execFn: execFn 99 | }; 100 | 101 | }, 102 | 103 | execCommand: function ( commandName, args ) { 104 | 105 | var commandObject = this.commands[ commandName ]; 106 | 107 | if ( !commandObject ) { 108 | throw new Error( 'KFEditor: not found command, ' + commandName ); 109 | } 110 | 111 | return commandObject.execFn.apply( commandObject.executor, [].slice.call( arguments, 1 ) ); 112 | 113 | } 114 | 115 | } ); 116 | 117 | function getService ( serviceName ) { 118 | 119 | var serviceObject = this.services[ serviceName ]; 120 | 121 | if ( !serviceObject ) { 122 | throw new Error( 'KFEditor: not found service, ' + serviceName ); 123 | } 124 | 125 | return serviceObject; 126 | 127 | } 128 | 129 | Utils.extend( KFEditor, { 130 | 131 | registerComponents: function ( name, component ) { 132 | 133 | COMPONENTS[ name ] = component; 134 | 135 | } 136 | 137 | } ); 138 | 139 | 140 | return KFEditor; 141 | 142 | } ); 143 | -------------------------------------------------------------------------------- /src/jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hn on 14-3-31. 3 | */ 4 | 5 | define( function () { 6 | return window.jQuery; 7 | } ); -------------------------------------------------------------------------------- /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.2)', 9 | allSelectColor: 'rgba(42, 106, 189, 0.6)' 10 | }; 11 | 12 | } ); -------------------------------------------------------------------------------- /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.box.setAttr( "data-type", null ); 25 | this.setOperator( new PlaceholderOperator() ); 26 | 27 | }, 28 | 29 | select: function () { 30 | 31 | this.getOperator().select(); 32 | 33 | }, 34 | 35 | selectAll: function () { 36 | 37 | this.getOperator().selectAll(); 38 | 39 | }, 40 | 41 | unselect: function () { 42 | this.getOperator().unselect(); 43 | } 44 | 45 | } ); 46 | 47 | } ); -------------------------------------------------------------------------------- /src/kf-ext/extension.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 公式扩展接口 3 | */ 4 | 5 | define( function ( require ) { 6 | 7 | var kf = require( "kf" ), 8 | kity = require( "kity" ), 9 | SELECT_COLOR = require( "kf-ext/def" ).selectColor, 10 | ALL_SELECT_COLOR = require( "kf-ext/def" ).allSelectColor; 11 | 12 | function ext ( parser ) { 13 | 14 | kf.PlaceholderExpression = require( "kf-ext/expression/placeholder" ); 15 | 16 | kf.Expression.prototype.select = function () { 17 | 18 | this.box.fill( SELECT_COLOR ); 19 | 20 | }; 21 | 22 | kf.Expression.prototype.selectAll = function () { 23 | this.box.fill( ALL_SELECT_COLOR ); 24 | }; 25 | 26 | kf.Expression.prototype.unselect = function () { 27 | 28 | this.box.fill( "transparent" ); 29 | 30 | } 31 | 32 | // 扩展解析和逆解析 33 | parser.getKFParser().expand( { 34 | 35 | parse: { 36 | "placeholder": { 37 | name: "placeholder", 38 | handler: function ( info ) { 39 | 40 | delete info.handler; 41 | info.operand = []; 42 | 43 | return info; 44 | 45 | }, 46 | sign: false 47 | } 48 | }, 49 | 50 | reverse: { 51 | 52 | "placeholder": function () { 53 | 54 | return "\\placeholder"; 55 | 56 | } 57 | 58 | } 59 | 60 | } ); 61 | 62 | } 63 | 64 | return { 65 | ext: ext 66 | }; 67 | 68 | } ); 69 | -------------------------------------------------------------------------------- /src/kf-ext/operator/placeholder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 占位符操作符 3 | */ 4 | 5 | define( function ( require, exports, modules ) { 6 | 7 | var kity = require( "kity" ), 8 | SELECT_COLOR = require( "kf-ext/def" ).selectColor, 9 | ALL_SELECT_COLOR = require( "kf-ext/def" ).allSelectColor; 10 | 11 | return kity.createClass( 'PlaceholderOperator', { 12 | 13 | base: require( "kf" ).Operator, 14 | 15 | constructor: function () { 16 | 17 | this.opShape = null; 18 | this.callBase( "Placeholder" ); 19 | 20 | }, 21 | 22 | applyOperand: function () { 23 | 24 | this.setBoxSize( 17, 27 ); 25 | this.opShape = generateOPShape(); 26 | this.addOperatorShape( this.opShape ); 27 | 28 | }, 29 | 30 | select: function () { 31 | 32 | this.opShape.fill( SELECT_COLOR ); 33 | 34 | }, 35 | 36 | selectAll: function () { 37 | 38 | this.opShape.fill( ALL_SELECT_COLOR ); 39 | 40 | }, 41 | 42 | unselect: function () { 43 | 44 | this.opShape.fill( "transparent" ); 45 | 46 | } 47 | 48 | } ); 49 | 50 | function generateOPShape () { 51 | 52 | var w = 13, 53 | h = 17, 54 | shape = null; 55 | 56 | shape = new kity.Rect( w, h, 0, 0 ).stroke( "black" ).fill( "transparent" ).translate( 2, 6 ); 57 | shape.setAttr( "stroke-dasharray", "1, 2" ); 58 | 59 | return shape; 60 | 61 | } 62 | 63 | } ); -------------------------------------------------------------------------------- /src/kf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hn on 14-3-12. 3 | */ 4 | 5 | define( function () { 6 | 7 | return window.kf; 8 | 9 | } ); -------------------------------------------------------------------------------- /src/kity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 数学公式Latex语法解析器 3 | */ 4 | 5 | define( function () { 6 | 7 | return window.kity; 8 | 9 | } ); 10 | 11 | -------------------------------------------------------------------------------- /src/parse/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 | 19 | }; 20 | 21 | } ); -------------------------------------------------------------------------------- /src/parse/parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 数学公式解析器 3 | */ 4 | 5 | define( function ( require ) { 6 | 7 | var KFParser = require( "kf" ).Parser, 8 | kity = require( "kity" ), 9 | COMPARISON_TABLE = require( "parse/def" ), 10 | PID_PREFIX = "_kf_editor_", 11 | GROUP_TYPE = "kf-editor-group", 12 | V_GROUP_TYPE = "kf-editor-virtual-group", 13 | PID = 0; 14 | 15 | var Parser = kity.createClass( "Parser", { 16 | 17 | constructor: function ( kfEditor ) { 18 | 19 | this.kfEditor = kfEditor; 20 | 21 | this.callBase(); 22 | // kityformula 解析器 23 | this.kfParser = KFParser.use( "latex" ); 24 | 25 | this.initKFormulExtension(); 26 | 27 | this.pid = generateId(); 28 | this.groupRecord = 0; 29 | 30 | this.tree = null; 31 | 32 | this.isResetId = true; 33 | 34 | this.initServices(); 35 | 36 | }, 37 | 38 | parse: function ( str, isResetId ) { 39 | 40 | var parsedResult = null; 41 | 42 | this.isResetId = !!isResetId; 43 | 44 | if ( this.isResetId ) { 45 | this.resetGroupId(); 46 | } 47 | 48 | parsedResult = this.kfParser.parse( str ); 49 | 50 | // 对解析出来的结果树做适当的处理,使得编辑器能够更容易地识别当前表达式的语义 51 | supplementTree( this, parsedResult.tree ); 52 | 53 | return parsedResult; 54 | 55 | }, 56 | 57 | // 序列化, parse的逆过程 58 | serialization: function ( tree ) { 59 | 60 | return this.kfParser.serialization( tree ); 61 | 62 | }, 63 | 64 | initServices: function () { 65 | 66 | this.kfEditor.registerService( "parser.parse", this, { 67 | parse: this.parse 68 | } ); 69 | 70 | this.kfEditor.registerService( "parser.latex.serialization", this, { 71 | serialization: this.serialization 72 | } ); 73 | 74 | }, 75 | 76 | getKFParser: function () { 77 | 78 | return this.kfParser; 79 | 80 | }, 81 | 82 | // 初始化KF扩展 83 | initKFormulExtension: function () { 84 | 85 | require( "kf-ext/extension" ).ext( this ); 86 | 87 | }, 88 | 89 | resetGroupId: function () { 90 | this.groupRecord = 0; 91 | }, 92 | 93 | getGroupId: function () { 94 | return this.pid + "_" + ( ++this.groupRecord ); 95 | } 96 | 97 | } ); 98 | 99 | // 把解析树丰富成公式编辑器的语义树, 该语义化的树同时也是合法的解析树 100 | function supplementTree ( parser, tree, parentTree ) { 101 | 102 | var currentOperand = null, 103 | // 只有根节点才没有parentTree 104 | isRoot = !parentTree; 105 | 106 | tree.attr = tree.attr || {}; 107 | 108 | tree.attr.id = parser.getGroupId(); 109 | 110 | // 组类型已经存在则不用再处理 111 | if ( !tree.attr[ "data-type" ] ) { 112 | 113 | tree.attr[ "data-type" ] = GROUP_TYPE; 114 | 115 | if ( COMPARISON_TABLE[ tree.name ] ) { 116 | tree.attr[ "data-type" ] = V_GROUP_TYPE; 117 | } 118 | 119 | } 120 | 121 | if ( isRoot ) { 122 | // 如果isResetId为false, 表示当前生成的是子树 123 | // 则不做data-root标记, 同时更改该包裹的类型为V_GROUP_TYPE 124 | if ( !parser.isResetId ) { 125 | tree.attr[ "data-type" ] = V_GROUP_TYPE; 126 | } else { 127 | tree.attr[ "data-root" ] = "true"; 128 | } 129 | } 130 | 131 | if ( tree.name === "brackets" ) { 132 | tree.attr[ "data-brackets" ] = "true"; 133 | } 134 | 135 | for ( var i = 0, len= tree.operand.length; i < len; i++ ) { 136 | 137 | currentOperand = tree.operand[ i ]; 138 | 139 | if ( !currentOperand ) { 140 | 141 | tree.operand[ i ] = currentOperand; 142 | 143 | } else { 144 | 145 | if ( COMPARISON_TABLE[ tree.name ] ) { 146 | 147 | if ( typeof currentOperand === "string" ) { 148 | 149 | // brackets树的前两个节点不用处理 150 | if ( tree.name !== "brackets" || i > 1 ) { 151 | tree.operand[ i ] = { 152 | name: "combination", 153 | operand: [ currentOperand ], 154 | attr: { 155 | id: parser.getGroupId(), 156 | "data-type": GROUP_TYPE 157 | } 158 | }; 159 | } 160 | 161 | } else { 162 | 163 | // 包裹函数的参数 164 | if ( currentOperand.name !== "combination" ) { 165 | 166 | tree.operand[ i ] = { 167 | name: "combination", 168 | operand: [ null ], 169 | attr: { 170 | id: parser.getGroupId(), 171 | "data-type": GROUP_TYPE 172 | } 173 | }; 174 | 175 | // 占位符特殊处理 176 | if ( currentOperand.name === "placeholder" ) { 177 | tree.operand[ i ].operand[ 0 ] = { 178 | name: "combination", 179 | operand: [ currentOperand ], 180 | attr: { 181 | id: parser.getGroupId(), 182 | "data-type": GROUP_TYPE, 183 | "data-placeholder": "true" 184 | } 185 | }; 186 | currentOperand.attr = { 187 | id: parser.getGroupId() 188 | }; 189 | } else { 190 | tree.operand[ i ].operand[ 0 ] = supplementTree( parser, currentOperand, tree.operand[ i ] ); 191 | } 192 | 193 | } else { 194 | 195 | currentOperand.attr = { 196 | "data-type": GROUP_TYPE 197 | }; 198 | 199 | tree.operand[ i ] = supplementTree( parser, currentOperand, tree ); 200 | 201 | } 202 | 203 | } 204 | 205 | } else { 206 | 207 | if ( typeof currentOperand === "string" ) { 208 | tree.operand[ i ] = currentOperand; 209 | } else { 210 | 211 | // // 重置组类型 212 | // if ( !isRoot && tree.operand.length === 1 ) { 213 | // tree.attr[ "data-type" ] = V_GROUP_TYPE; 214 | // } 215 | 216 | // 占位符附加包裹 217 | if ( currentOperand.name === "placeholder" ) { 218 | tree.operand[ i ] = { 219 | name: "combination", 220 | operand: [ currentOperand ], 221 | attr: { 222 | id: parser.getGroupId(), 223 | "data-type": GROUP_TYPE, 224 | "data-placeholder": "true" 225 | } 226 | }; 227 | currentOperand.attr = { 228 | id: parser.getGroupId() 229 | }; 230 | } else { 231 | tree.operand[ i ] = supplementTree( parser, currentOperand, tree ); 232 | } 233 | 234 | } 235 | 236 | } 237 | 238 | } 239 | 240 | } 241 | 242 | return tree; 243 | 244 | } 245 | 246 | function generateId () { 247 | return PID_PREFIX + ( ++PID ); 248 | } 249 | 250 | return Parser; 251 | 252 | } ); 253 | 254 | -------------------------------------------------------------------------------- /src/position/position.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 定位模块 3 | */ 4 | 5 | 6 | define( function ( require ) { 7 | 8 | var kity = require( "kity" ), 9 | 10 | // 表达式的内容组"标签" 11 | CONTENT_DATA_TYPE = "kf-editor-exp-content-box", 12 | 13 | PositionComponenet = kity.createClass( 'PositionComponenet', { 14 | 15 | constructor: function ( kfEditor ) { 16 | 17 | this.kfEditor = kfEditor; 18 | 19 | this.initServices(); 20 | 21 | }, 22 | 23 | initServices: function () { 24 | 25 | this.kfEditor.registerService( "position.get.group", this, { 26 | getGroupByTarget: this.getGroupByTarget 27 | } ); 28 | 29 | this.kfEditor.registerService( "position.get.parent.group", this, { 30 | getParentGroupByTarget: this.getParentGroupByTarget 31 | } ); 32 | 33 | this.kfEditor.registerService( "position.get.wrap", this, { 34 | getWrap: this.getWrap 35 | } ); 36 | 37 | this.kfEditor.registerService( "position.get.group.info", this, { 38 | getGroupInfoByNode: this.getGroupInfoByNode 39 | } ); 40 | 41 | this.kfEditor.registerService( "position.get.parent.info", this, { 42 | getParentInfoByNode: this.getParentInfoByNode 43 | } ); 44 | 45 | }, 46 | 47 | getGroupByTarget: function ( target ) { 48 | 49 | var groupDom = getGroup( target, false, false ); 50 | 51 | if ( groupDom ) { 52 | return this.kfEditor.requestService( "syntax.get.group.content", groupDom.id ); 53 | } 54 | 55 | return null; 56 | 57 | }, 58 | 59 | getParentGroupByTarget: function ( target ) { 60 | 61 | var groupDom = getGroup( target, true, false ); 62 | 63 | if ( groupDom ) { 64 | return this.kfEditor.requestService( "syntax.get.group.content", groupDom.id ); 65 | } 66 | 67 | return null; 68 | 69 | }, 70 | 71 | getWrap: function ( node ) { 72 | 73 | return getGroup( node, true, true ); 74 | 75 | }, 76 | 77 | /** 78 | * 给定一个节点, 获取其节点所属的组及其在该组内的偏移 79 | * @param target 目标节点 80 | */ 81 | getGroupInfoByNode: function ( target ) { 82 | 83 | var result = null, 84 | oldTarget = null; 85 | 86 | oldTarget = target; 87 | while ( target = getGroup( target, true, false ) ) { 88 | 89 | if ( target.getAttribute( "data-type" ) === "kf-editor-group" ) { 90 | break; 91 | } 92 | 93 | oldTarget = target 94 | 95 | } 96 | 97 | result = { 98 | group: this.kfEditor.requestService( "syntax.get.group.content", target.id ) 99 | }; 100 | 101 | result.index = result.group.content.indexOf( oldTarget ); 102 | 103 | return result; 104 | 105 | }, 106 | 107 | /** 108 | * 给定一个节点, 获取其节点所属的直接包含组及其在该直接包含组内的偏移 109 | * @param target 目标节点 110 | */ 111 | getParentInfoByNode: function ( target ) { 112 | 113 | var group = getGroup( target, true, false ); 114 | 115 | group = this.kfEditor.requestService( "syntax.get.group.content", group.id ); 116 | 117 | return { 118 | group: group, 119 | index: group.content.indexOf( target ) 120 | }; 121 | 122 | } 123 | 124 | } ); 125 | 126 | /** 127 | * 获取给定节点元素所属的组 128 | * @param node 当前点击的节点 129 | * @param isAllowVirtual 是否允许选择虚拟组 130 | * @param isAllowWrap 是否允许选择目标节点的最小包裹单位 131 | * @returns {*} 132 | */ 133 | function getGroup ( node, isAllowVirtual, isAllowWrap ) { 134 | 135 | var tagName = null; 136 | 137 | if ( !node.ownerSVGElement ) { 138 | return null; 139 | } 140 | 141 | node = node.parentNode; 142 | 143 | tagName = node.tagName.toLowerCase(); 144 | 145 | if ( node && tagName !== "body" && tagName !== "svg" ) { 146 | 147 | if ( node.getAttribute( "data-type" ) === "kf-editor-group" ) { 148 | return node; 149 | } 150 | 151 | if ( isAllowVirtual && node.getAttribute( "data-type" ) === "kf-editor-virtual-group" ) { 152 | return node; 153 | } 154 | 155 | if ( isAllowWrap && node.getAttribute( "data-flag" ) !== null ) { 156 | return node; 157 | } 158 | 159 | return getGroup( node, isAllowVirtual, isAllowWrap ); 160 | 161 | } else { 162 | return null; 163 | } 164 | 165 | } 166 | 167 | function getWrap ( isAllowWrap ) { 168 | 169 | } 170 | 171 | return PositionComponenet; 172 | 173 | } ); -------------------------------------------------------------------------------- /src/render/render.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hn on 14-3-17. 3 | */ 4 | 5 | define( function ( require ) { 6 | 7 | var kity = require( "kity" ), 8 | 9 | Assembly = require( "kf" ).Assembly, 10 | 11 | DEFAULT_OPTIONS = { 12 | autoresize: false, 13 | fontsize: 30, 14 | padding: [ 20, 50 ] 15 | }, 16 | 17 | RenderComponenet = kity.createClass( 'RenderComponent', { 18 | 19 | constructor: function ( kfEditor ) { 20 | 21 | this.kfEditor = kfEditor; 22 | this.assembly = null; 23 | this.formula = null; 24 | 25 | this.canvasZoom = 1; 26 | 27 | this.record = { 28 | select: {}, 29 | cursor: {} 30 | }; 31 | 32 | this.initCanvas(); 33 | 34 | this.initServices(); 35 | this.initCommands(); 36 | 37 | }, 38 | 39 | initCanvas: function () { 40 | 41 | var canvasContainer = this.kfEditor.requestService( "ui.get.canvas.container" ); 42 | 43 | this.assembly = Assembly.use( canvasContainer, DEFAULT_OPTIONS ); 44 | this.formula = this.assembly.formula; 45 | 46 | }, 47 | 48 | initServices: function () { 49 | 50 | this.kfEditor.registerService( "render.relocation", this, { 51 | relocation: this.relocation 52 | } ); 53 | 54 | this.kfEditor.registerService( "render.select.group.content", this, { 55 | selectGroupContent: this.selectGroupContent 56 | } ); 57 | 58 | this.kfEditor.registerService( "render.select.group", this, { 59 | selectGroup: this.selectGroup 60 | } ); 61 | 62 | this.kfEditor.registerService( "render.select.group.all", this, { 63 | selectAllGroup: this.selectAllGroup 64 | } ); 65 | 66 | this.kfEditor.registerService( "render.select.current.cursor", this, { 67 | selectCurrentCursor: this.selectCurrentCursor 68 | } ); 69 | 70 | this.kfEditor.registerService( "render.reselect", this, { 71 | reselect: this.reselect 72 | } ); 73 | 74 | this.kfEditor.registerService( "render.clear.select", this, { 75 | clearSelect: this.clearSelect 76 | } ); 77 | 78 | this.kfEditor.registerService( "render.set.canvas.zoom", this, { 79 | setCanvasZoom: this.setCanvasZoom 80 | } ); 81 | 82 | this.kfEditor.registerService( "render.get.canvas.zoom", this, { 83 | getCanvasZoom: this.getCanvasZoom 84 | } ); 85 | 86 | this.kfEditor.registerService( "render.get.paper.offset", this, { 87 | getPaperOffset: this.getPaperOffset 88 | } ); 89 | 90 | this.kfEditor.registerService( "render.draw", this, { 91 | render: this.render 92 | } ); 93 | 94 | this.kfEditor.registerService( "render.insert.string", this, { 95 | insertString: this.insertString 96 | } ); 97 | 98 | this.kfEditor.registerService( "render.insert.group", this, { 99 | insertGroup: this.insertGroup 100 | } ); 101 | 102 | this.kfEditor.registerService( "render.get.paper", this, { 103 | getPaper: this.getPaper 104 | } ); 105 | 106 | }, 107 | 108 | initCommands: function () { 109 | 110 | this.kfEditor.registerCommand( "render", this, this.render ); 111 | 112 | this.kfEditor.registerCommand( "getPaper", this, this.getPaper ); 113 | 114 | }, 115 | 116 | relocation: function () { 117 | 118 | var formulaSpace = this.formula.container.getRenderBox(), 119 | viewPort = this.formula.getViewPort(); 120 | 121 | viewPort.center.x = formulaSpace.width / 2; 122 | viewPort.center.y = formulaSpace.height / 2; 123 | 124 | this.formula.setViewPort( viewPort ); 125 | 126 | }, 127 | 128 | selectGroup: function ( groupId ) { 129 | 130 | var groupObject = this.kfEditor.requestService( "syntax.get.group.object", groupId ), 131 | isPlaceholder = this.kfEditor.requestService( "syntax.valid.placeholder", groupId ); 132 | 133 | this.clearSelect(); 134 | 135 | if ( groupObject.node.getAttribute( "data-root" ) ) { 136 | // 根节点不着色 137 | return; 138 | } 139 | 140 | // 占位符着色 141 | if ( isPlaceholder ) { 142 | // 替换占位符包裹组为占位符本身 143 | groupObject = this.kfEditor.requestService( "syntax.get.group.object", groupObject.operands[ 0 ].node.id ); 144 | } 145 | 146 | this.record.select.lastSelect = groupObject; 147 | 148 | groupObject.select(); 149 | 150 | }, 151 | 152 | selectGroupContent: function ( group ) { 153 | 154 | // 处理占位符 155 | if ( group.groupObj.getAttribute( "data-placeholder" ) !== null ) { 156 | group = { 157 | id: group.content[ 0 ].id 158 | }; 159 | } 160 | 161 | var groupObject = this.kfEditor.requestService( "syntax.get.group.object", group.id ); 162 | 163 | this.clearSelect(); 164 | 165 | this.record.select.lastSelect = groupObject; 166 | 167 | if ( groupObject.node.getAttribute( "data-root" ) ) { 168 | // 根节点不着色 169 | return; 170 | } 171 | groupObject.select(); 172 | 173 | }, 174 | 175 | selectAllGroup: function ( group ) { 176 | 177 | // 处理占位符 178 | if ( group.groupObj.getAttribute( "data-placeholder" ) !== null ) { 179 | group = { 180 | id: group.content[ 0 ].id 181 | }; 182 | } 183 | 184 | var groupObject = this.kfEditor.requestService( "syntax.get.group.object", group.id ); 185 | 186 | this.clearSelect(); 187 | 188 | this.record.select.lastSelect = groupObject; 189 | 190 | groupObject.selectAll(); 191 | 192 | }, 193 | 194 | selectCurrentCursor: function () { 195 | 196 | var cursorInfo = this.kfEditor.requestService( "syntax.get.record.cursor" ), 197 | group = this.kfEditor.requestService( "syntax.get.group.object", cursorInfo.groupId ), 198 | box = null, 199 | offset = -1, 200 | width = 0, 201 | height = group.getRenderBox().height, 202 | startIndex = Math.min( cursorInfo.startOffset, cursorInfo.endOffset ), 203 | endIndex = Math.max( cursorInfo.startOffset, cursorInfo.endOffset ); 204 | 205 | this.clearSelect(); 206 | 207 | // 更新记录 208 | this.record.select.lastSelect = group; 209 | 210 | for ( var i = startIndex, len = endIndex; i < len; i++ ) { 211 | 212 | box = group.getOperand( i ).getRenderBox(); 213 | 214 | if ( offset == -1 ) { 215 | offset = box.x; 216 | } 217 | 218 | width += box.width; 219 | 220 | } 221 | 222 | group.setBoxSize( width, height ); 223 | group.selectAll(); 224 | group.getBox().translate( offset, 0 ); 225 | 226 | }, 227 | 228 | reselect: function () { 229 | 230 | var cursorInfo = this.kfEditor.requestService( "syntax.get.record.cursor" ), 231 | groupObject = null; 232 | 233 | groupObject = this.kfEditor.requestService( "syntax.get.group.object", cursorInfo.groupId ); 234 | 235 | this.clearSelect(); 236 | 237 | this.record.select.lastSelect = groupObject; 238 | 239 | if ( groupObject.node.getAttribute( "data-root" ) ) { 240 | // 根节点不着色 241 | return; 242 | } 243 | groupObject.select(); 244 | 245 | }, 246 | 247 | clearSelect: function () { 248 | 249 | var box = null, 250 | transform = null, 251 | currentSelect = this.record.select.lastSelect; 252 | 253 | if ( !currentSelect ) { 254 | return; 255 | } 256 | 257 | currentSelect.unselect(); 258 | box = currentSelect.getRenderBox(); 259 | currentSelect.setBoxSize( box.width, box.height ); 260 | 261 | transform = currentSelect.getBox().getTransform(); 262 | 263 | if ( transform ) { 264 | transform.m.e = 0; 265 | transform.m.f = 0; 266 | } 267 | 268 | currentSelect.getBox().setTransform( transform ); 269 | 270 | }, 271 | 272 | getPaper: function () { 273 | return this.formula; 274 | }, 275 | 276 | render: function ( latexStr ) { 277 | 278 | var parsedTree = this.kfEditor.requestService( "parser.parse", latexStr, true ), 279 | objTree = this.assembly.regenerateBy( parsedTree ); 280 | 281 | // 更新语法模块所维护的树 282 | this.kfEditor.requestService( "syntax.update.objtree", objTree ); 283 | this.relocation(); 284 | 285 | }, 286 | 287 | setCanvasZoom: function ( zoom ) { 288 | 289 | var viewPort = this.formula.getViewPort(); 290 | 291 | this.canvasZoom = zoom; 292 | viewPort.zoom = zoom; 293 | 294 | this.formula.setViewPort( viewPort ); 295 | 296 | }, 297 | 298 | getCanvasZoom: function () { 299 | return this.canvasZoom; 300 | } 301 | 302 | } ); 303 | 304 | return RenderComponenet; 305 | 306 | } ); -------------------------------------------------------------------------------- /src/syntax/move.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 光标控制 3 | */ 4 | 5 | define( function ( require, exports, module ) { 6 | 7 | var kity = require( "kity" ), 8 | DIRECTION = { 9 | LEFT: 'left', 10 | RIGHT: 'right' 11 | }; 12 | 13 | return kity.createClass( "MoveComponent", { 14 | 15 | constructor: function ( parentComponent, kfEditor ) { 16 | 17 | this.parentComponent = parentComponent; 18 | this.kfEditor = kfEditor; 19 | 20 | }, 21 | 22 | leftMove: function () { 23 | 24 | var cursorInfo = this.parentComponent.getCursorRecord(); 25 | 26 | cursorInfo = updateCursorGoLeft.call( this, cursorInfo ); 27 | 28 | // cursorInfo 为null则不用处理 29 | if ( cursorInfo ) { 30 | this.parentComponent.updateCursor( cursorInfo.groupId, cursorInfo.startOffset, cursorInfo.endOffset ); 31 | } 32 | 33 | }, 34 | 35 | rightMove: function () { 36 | 37 | var cursorInfo = this.parentComponent.getCursorRecord(); 38 | 39 | cursorInfo = updateCursorGoRight.call( this, cursorInfo ); 40 | 41 | // cursorInfo 为null则不用处理 42 | if ( cursorInfo ) { 43 | this.parentComponent.updateCursor( cursorInfo.groupId, cursorInfo.startOffset, cursorInfo.endOffset ); 44 | } 45 | 46 | } 47 | 48 | } ); 49 | 50 | 51 | function updateCursorGoLeft ( cursorInfo ) { 52 | 53 | var prevGroupNode = null, 54 | syntaxComponent = this.parentComponent, 55 | containerInfo = null; 56 | 57 | if ( cursorInfo.startOffset === cursorInfo.endOffset ) { 58 | 59 | containerInfo = syntaxComponent.getGroupContent( cursorInfo.groupId ); 60 | 61 | if ( isPlaceholderNode( containerInfo.groupObj ) ) { 62 | return locateOuterIndex( this, containerInfo.groupObj, DIRECTION.LEFT ); 63 | } 64 | 65 | if ( cursorInfo.startOffset > 0 ) { 66 | 67 | prevGroupNode = containerInfo.content[ cursorInfo.startOffset - 1 ]; 68 | 69 | if ( isGroupNode( prevGroupNode ) ) { 70 | cursorInfo = locateIndex( this, prevGroupNode, DIRECTION.LEFT ); 71 | } else { 72 | cursorInfo.startOffset -= 1; 73 | cursorInfo.endOffset = cursorInfo.startOffset; 74 | } 75 | 76 | // 跳出当前容器, 回溯 77 | } else { 78 | 79 | cursorInfo = locateOuterIndex( this, containerInfo.groupObj, DIRECTION.LEFT ); 80 | 81 | } 82 | 83 | } else { 84 | 85 | cursorInfo.startOffset = Math.min( cursorInfo.startOffset, cursorInfo.endOffset ); 86 | // 收缩 87 | cursorInfo.endOffset = cursorInfo.startOffset; 88 | 89 | } 90 | 91 | return cursorInfo; 92 | 93 | } 94 | 95 | function updateCursorGoRight ( cursorInfo ) { 96 | 97 | var nextGroupNode = null, 98 | syntaxComponent = this.parentComponent, 99 | containerInfo = null; 100 | 101 | if ( cursorInfo.startOffset === cursorInfo.endOffset ) { 102 | 103 | containerInfo = syntaxComponent.getGroupContent( cursorInfo.groupId ); 104 | 105 | if ( isPlaceholderNode( containerInfo.groupObj ) ) { 106 | return locateOuterIndex( this, containerInfo.groupObj, DIRECTION.RIGHT ); 107 | } 108 | 109 | if ( cursorInfo.startOffset < containerInfo.content.length ) { 110 | 111 | nextGroupNode = containerInfo.content[ cursorInfo.startOffset ]; 112 | 113 | // 进入容器内部 114 | if ( isGroupNode( nextGroupNode ) ) { 115 | cursorInfo = locateIndex( this, nextGroupNode, DIRECTION.RIGHT ); 116 | } else { 117 | cursorInfo.startOffset += 1; 118 | cursorInfo.endOffset = cursorInfo.startOffset; 119 | } 120 | 121 | // 跳出当前容器, 回溯 122 | } else { 123 | 124 | cursorInfo = locateOuterIndex( this, containerInfo.groupObj, DIRECTION.RIGHT ); 125 | 126 | } 127 | 128 | } else { 129 | 130 | cursorInfo.endOffset = Math.max( cursorInfo.startOffset, cursorInfo.endOffset ); 131 | // 收缩 132 | cursorInfo.startOffset = cursorInfo.endOffset; 133 | 134 | } 135 | 136 | return cursorInfo; 137 | 138 | } 139 | 140 | /** 141 | * 组内寻址, 入组 142 | */ 143 | function locateIndex ( moveComponent, groupNode, dir ) { 144 | 145 | switch ( dir ) { 146 | 147 | case DIRECTION.LEFT: 148 | return locateLeftIndex( moveComponent, groupNode ); 149 | 150 | case DIRECTION.RIGHT: 151 | return locateRightIndex( moveComponent, groupNode ); 152 | 153 | } 154 | 155 | throw new Error( "undefined move direction!" ); 156 | 157 | } 158 | 159 | /** 160 | * 组外寻址, 出组 161 | */ 162 | function locateOuterIndex ( moveComponent, groupNode, dir ) { 163 | 164 | switch ( dir ) { 165 | 166 | case DIRECTION.LEFT: 167 | return locateOuterLeftIndex( moveComponent, groupNode ); 168 | 169 | case DIRECTION.RIGHT: 170 | return locateOuterRightIndex( moveComponent, groupNode ); 171 | 172 | } 173 | 174 | throw new Error( "undefined move direction!" ); 175 | 176 | } 177 | 178 | // 左移内部定位 179 | function locateLeftIndex ( moveComponent, groupNode ) { 180 | 181 | var syntaxComponent = moveComponent.parentComponent, 182 | groupInfo = null, 183 | groupElement = null; 184 | 185 | if ( isPlaceholderNode( groupNode ) || isEmptyNode( groupNode ) ) { 186 | return locateOuterLeftIndex( moveComponent, groupNode ); 187 | } 188 | 189 | if ( isGroupNode( groupNode ) ) { 190 | 191 | groupInfo = syntaxComponent.getGroupContent( groupNode.id ); 192 | // 容器内部中末尾的元素 193 | groupElement = groupInfo.content[ groupInfo.content.length - 1 ]; 194 | 195 | // 空检测 196 | if ( isEmptyNode( groupElement ) ) { 197 | 198 | // 做跳出处理 199 | return locateOuterLeftIndex( moveComponent, groupElement ); 200 | 201 | } 202 | 203 | // 待定位的组本身就是一个容器, 则检测其内部结构是否还包含容器 204 | if ( isContainerNode( groupNode ) ) { 205 | 206 | // 进入到占位符包裹容器内 207 | if ( isPlaceholderNode( groupElement ) ) { 208 | 209 | return { 210 | groupId: groupElement.id, 211 | startOffset: 0, 212 | endOffset: 0 213 | }; 214 | 215 | // 内部元素仍然是一个容器并且只有这一个内部元素,则进行递归处理 216 | } else if ( isContainerNode( groupElement ) && groupInfo.content.length === 1 ) { 217 | return locateLeftIndex( moveComponent, groupElement ); 218 | } 219 | 220 | return { 221 | groupId: groupNode.id, 222 | startOffset: groupInfo.content.length, 223 | endOffset: groupInfo.content.length 224 | }; 225 | 226 | // 仅是一个组, 进入组内部处理, 找到目标容器 227 | } else { 228 | 229 | while ( !isContainerNode( groupElement ) && !isEmptyNode( groupElement ) && !isPlaceholderNode( groupElement ) ) { 230 | groupInfo = syntaxComponent.getGroupContent( groupElement.id ); 231 | groupElement = groupInfo.content[ groupInfo.content.length - 1 ]; 232 | } 233 | 234 | if ( isEmptyNode( groupElement ) ) { 235 | return locateOuterLeftIndex( moveComponent, groupElement ); 236 | } 237 | 238 | if ( isPlaceholderNode( groupElement ) ) { 239 | return { 240 | groupId: groupElement.id, 241 | startOffset: groupInfo.content.length, 242 | endOffset: groupInfo.content.length 243 | }; 244 | } 245 | 246 | return locateLeftIndex( moveComponent, groupElement ); 247 | 248 | } 249 | 250 | } 251 | 252 | return null; 253 | 254 | } 255 | 256 | // 左移外部定位 257 | function locateOuterLeftIndex ( moveComponent, groupNode ) { 258 | 259 | var kfEditor = moveComponent.kfEditor, 260 | syntaxComponent = moveComponent.parentComponent, 261 | outerGroupInfo = null, 262 | groupInfo = null; 263 | 264 | // 根容器, 不用再跳出 265 | if ( isRootNode( groupNode ) ) { 266 | return null; 267 | } 268 | 269 | outerGroupInfo = kfEditor.requestService( "position.get.parent.info", groupNode ); 270 | 271 | while ( outerGroupInfo.index === 0 ) { 272 | 273 | if ( isRootNode( outerGroupInfo.group.groupObj ) ) { 274 | return { 275 | groupId: outerGroupInfo.group.id, 276 | startOffset: 0, 277 | endOffset: 0 278 | }; 279 | } 280 | 281 | // 如果父组是一个容器, 并且该容器包含不止一个节点, 则跳到父组开头 282 | if ( isContainerNode( outerGroupInfo.group.groupObj ) && outerGroupInfo.group.content.length > 1 ) { 283 | return { 284 | groupId: outerGroupInfo.group.id, 285 | startOffset: 0, 286 | endOffset: 0 287 | }; 288 | } 289 | 290 | outerGroupInfo = kfEditor.requestService( "position.get.parent.info", outerGroupInfo.group.groupObj ); 291 | 292 | } 293 | 294 | groupNode = outerGroupInfo.group.content[ outerGroupInfo.index - 1 ]; 295 | 296 | // 定位到的组是一个容器, 则定位到容器尾部 297 | if ( isGroupNode( groupNode ) ) { 298 | 299 | // 容器节点 300 | if ( isContainerNode( groupNode ) ) { 301 | 302 | // 进入容器内部 303 | return locateLeftIndex( moveComponent, groupNode ); 304 | 305 | // 组节点 306 | } else { 307 | 308 | return locateLeftIndex( moveComponent, groupNode ); 309 | 310 | } 311 | 312 | return { 313 | groupId: groupNode.id, 314 | startOffset: groupInfo.content.length, 315 | endOffset: groupInfo.content.length 316 | }; 317 | 318 | } 319 | 320 | if ( isEmptyNode( groupNode ) ) { 321 | return locateOuterLeftIndex( moveComponent, groupNode ); 322 | } 323 | 324 | return { 325 | groupId: outerGroupInfo.group.id, 326 | startOffset: outerGroupInfo.index, 327 | endOffset: outerGroupInfo.index 328 | }; 329 | 330 | } 331 | 332 | // 右移内部定位 333 | function locateRightIndex ( moveComponent, groupNode ) { 334 | 335 | var syntaxComponent = moveComponent.parentComponent, 336 | groupInfo = null, 337 | groupElement = null; 338 | 339 | if ( isGroupNode( groupNode ) ) { 340 | 341 | groupInfo = syntaxComponent.getGroupContent( groupNode.id ); 342 | // 容器内部中末尾的元素 343 | groupElement = groupInfo.content[ 0 ]; 344 | 345 | // 待定位的组本身就是一个容器, 则检测其内部结构是否还包含容器 346 | if ( isContainerNode( groupNode ) ) { 347 | 348 | // 内部元素仍然是一个容器 349 | if ( isContainerNode( groupElement ) ) { 350 | // 递归处理 351 | return locateRightIndex( moveComponent, groupElement ); 352 | } 353 | 354 | return { 355 | groupId: groupNode.id, 356 | startOffset: 0, 357 | endOffset: 0 358 | }; 359 | 360 | // 仅是一个组, 进入组内部处理, 找到目标容器 361 | } else { 362 | 363 | while ( !isContainerNode( groupElement ) && !isPlaceholderNode( groupElement ) && !isEmptyNode( groupElement ) ) { 364 | groupInfo = syntaxComponent.getGroupContent( groupElement.id ); 365 | groupElement = groupInfo.content[ 0 ]; 366 | } 367 | 368 | // 定位到占位符内部 369 | if ( isPlaceholderNode( groupElement ) ) { 370 | return { 371 | groupId: groupElement.id, 372 | startOffset: 0, 373 | endOffset: 0 374 | }; 375 | } else if ( isEmptyNode( groupElement ) ) { 376 | return locateOuterRightIndex( moveComponent, groupElement ); 377 | } else { 378 | return locateRightIndex( moveComponent, groupElement ); 379 | } 380 | 381 | } 382 | 383 | } 384 | 385 | return null; 386 | 387 | } 388 | 389 | // 右移外部定位 390 | function locateOuterRightIndex ( moveComponent, groupNode ) { 391 | 392 | var kfEditor = moveComponent.kfEditor, 393 | syntaxComponent = moveComponent.parentComponent, 394 | outerGroupInfo = null, 395 | groupInfo = null; 396 | 397 | // 根容器, 不用再跳出 398 | if ( isRootNode( groupNode ) ) { 399 | return null; 400 | } 401 | 402 | outerGroupInfo = kfEditor.requestService( "position.get.parent.info", groupNode ); 403 | 404 | // 仍然需要回溯 405 | while ( outerGroupInfo.index === outerGroupInfo.group.content.length - 1 ) { 406 | 407 | if ( isRootNode( outerGroupInfo.group.groupObj ) ) { 408 | return { 409 | groupId: outerGroupInfo.group.id, 410 | startOffset: outerGroupInfo.group.content.length, 411 | endOffset: outerGroupInfo.group.content.length 412 | }; 413 | } 414 | 415 | // 如果父组是一个容器, 并且该容器包含不止一个节点, 则跳到父组末尾 416 | if ( isContainerNode( outerGroupInfo.group.groupObj ) && outerGroupInfo.group.content.length > 1 ) { 417 | return { 418 | groupId: outerGroupInfo.group.id, 419 | startOffset: outerGroupInfo.group.content.length, 420 | endOffset: outerGroupInfo.group.content.length 421 | }; 422 | } 423 | 424 | outerGroupInfo = kfEditor.requestService( "position.get.parent.info", outerGroupInfo.group.groupObj ); 425 | 426 | } 427 | 428 | groupNode = outerGroupInfo.group.content[ outerGroupInfo.index + 1 ]; 429 | 430 | // 空节点处理 431 | if ( isEmptyNode( groupNode ) ) { 432 | return locateOuterRightIndex( moveComponent, groupNode ); 433 | } 434 | 435 | // 定位到的组是一个容器, 则定位到容器内部开头位置上 436 | if ( isContainerNode( groupNode ) ) { 437 | 438 | return { 439 | groupId: groupNode.id, 440 | startOffset: 0, 441 | endOffset: 0 442 | }; 443 | 444 | } 445 | 446 | return { 447 | groupId: outerGroupInfo.group.id, 448 | startOffset: outerGroupInfo.index + 1, 449 | endOffset: outerGroupInfo.index + 1 450 | }; 451 | 452 | } 453 | 454 | function isRootNode ( node ) { 455 | 456 | return !!node.getAttribute( "data-root" ); 457 | 458 | } 459 | 460 | function isContainerNode ( node ) { 461 | return node.getAttribute( "data-type" ) === "kf-editor-group" 462 | } 463 | 464 | function isGroupNode ( node ) { 465 | var dataType = node.getAttribute( "data-type" ); 466 | return dataType === "kf-editor-group" || dataType === "kf-editor-virtual-group"; 467 | } 468 | 469 | function isPlaceholderNode ( node ) { 470 | return !!node.getAttribute( "data-placeholder" ); 471 | } 472 | 473 | function isEmptyNode ( node ) { 474 | return node.getAttribute( "data-flag" ) === "Empty"; 475 | } 476 | 477 | } ); -------------------------------------------------------------------------------- /src/syntax/syntax.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 语法控制单元 3 | */ 4 | 5 | define( function ( require ) { 6 | 7 | var kity = require( "kity" ), 8 | 9 | MoveComponent = require( "syntax/move" ), 10 | 11 | CURSOR_CHAR = "\uF155", 12 | 13 | SyntaxComponenet = kity.createClass( 'SyntaxComponenet', { 14 | 15 | constructor: function ( kfEditor ) { 16 | 17 | this.kfEditor = kfEditor; 18 | 19 | // 数据记录表 20 | this.record = { 21 | 22 | // 光标位置 23 | cursor: { 24 | group: null, 25 | startOffset: -1, 26 | endOffset: -1 27 | } 28 | 29 | }; 30 | 31 | // 子组件结构 32 | this.components = {}; 33 | 34 | // 对象树 35 | this.objTree = null; 36 | 37 | this.initComponents(); 38 | this.initServices(); 39 | 40 | }, 41 | 42 | initComponents: function () { 43 | this.components[ 'move' ] = new MoveComponent( this, this.kfEditor ); 44 | }, 45 | 46 | initServices: function () { 47 | 48 | this.kfEditor.registerService( "syntax.update.objtree", this, { 49 | updateObjTree: this.updateObjTree 50 | } ); 51 | 52 | this.kfEditor.registerService( "syntax.get.objtree", this, { 53 | getObjectTree: this.getObjectTree 54 | } ); 55 | 56 | this.kfEditor.registerService( "syntax.get.group.object", this, { 57 | getGroupObject: this.getGroupObject 58 | } ); 59 | 60 | this.kfEditor.registerService( "syntax.valid.placeholder", this, { 61 | isPlaceholder: this.isPlaceholder 62 | } ); 63 | 64 | this.kfEditor.registerService( "syntax.valid.brackets", this, { 65 | isBrackets: this.isBrackets 66 | } ); 67 | 68 | this.kfEditor.registerService( "syntax.get.group.content", this, { 69 | getGroupContent: this.getGroupContent 70 | } ); 71 | 72 | this.kfEditor.registerService( "syntax.update.record.cursor", this, { 73 | updateCursor: this.updateCursor 74 | } ); 75 | 76 | this.kfEditor.registerService( "syntax.update.selection", this, { 77 | updateSelection: this.updateSelection 78 | } ); 79 | 80 | this.kfEditor.registerService( "syntax.get.record.cursor", this, { 81 | getCursorRecord: this.getCursorRecord 82 | } ); 83 | 84 | this.kfEditor.registerService( "syntax.get.latex.info", this, { 85 | getLatexInfo: this.getLatexInfo 86 | } ); 87 | 88 | this.kfEditor.registerService( "syntax.insert.string", this, { 89 | insertString: this.insertString 90 | } ); 91 | 92 | this.kfEditor.registerService( "syntax.insert.group", this, { 93 | insertGroup: this.insertGroup 94 | } ); 95 | 96 | this.kfEditor.registerService( "syntax.serialization", this, { 97 | serialization: this.serialization 98 | } ); 99 | 100 | this.kfEditor.registerService( "syntax.cursor.move.left", this, { 101 | leftMove: this.leftMove 102 | } ); 103 | 104 | this.kfEditor.registerService( "syntax.cursor.move.right", this, { 105 | rightMove: this.rightMove 106 | } ); 107 | 108 | }, 109 | 110 | updateObjTree: function ( objTree ) { 111 | 112 | var selectInfo = objTree.select; 113 | 114 | if ( selectInfo && selectInfo.groupId ) { 115 | this.updateCursor( selectInfo.groupId, selectInfo.startOffset, selectInfo.endOffset ); 116 | } 117 | 118 | this.objTree = objTree; 119 | 120 | }, 121 | 122 | // 验证给定ID的组是否是占位符 123 | isPlaceholder: function ( groupId ) { 124 | return !!this.objTree.mapping[ groupId ].objGroup.node.getAttribute( "data-placeholder" ); 125 | }, 126 | 127 | isBrackets: function ( groupId ) { 128 | return !!this.objTree.mapping[ groupId ].objGroup.node.getAttribute( "data-brackets" ); 129 | }, 130 | 131 | getObjectTree: function () { 132 | return this.objTree; 133 | }, 134 | 135 | getGroupObject: function ( id ) { 136 | 137 | return this.objTree.mapping[ id ].objGroup || null; 138 | 139 | }, 140 | 141 | getCursorRecord: function () { 142 | 143 | return kity.Utils.extend( {}, this.record.cursor ) || null; 144 | 145 | }, 146 | 147 | getGroupContent: function ( groupId ) { 148 | 149 | var groupInfo = this.objTree.mapping[ groupId ], 150 | content = [], 151 | operands = groupInfo.objGroup.operands, 152 | offset = operands.length - 1, 153 | isLtr = groupInfo.strGroup.traversal !== "rtl"; 154 | 155 | kity.Utils.each( operands, function ( operand, i ) { 156 | 157 | if ( isLtr ) { 158 | content.push( operand.node ); 159 | } else { 160 | content[ offset - i ] = operand.node; 161 | } 162 | 163 | } ); 164 | 165 | return { 166 | id: groupId, 167 | traversal: groupInfo.strGroup.traversal || "ltr", 168 | groupObj: groupInfo.objGroup.node, 169 | content: content 170 | }; 171 | 172 | }, 173 | 174 | updateSelection: function ( group ) { 175 | 176 | var groupObj = this.objTree.mapping[ group.id ], 177 | curStrGroup = groupObj.strGroup, 178 | parentGroup = null, 179 | parentGroupObj = null, 180 | resultStr = null, 181 | startOffset = -1, 182 | endOffset = -1; 183 | 184 | parentGroup = group; 185 | parentGroupObj = groupObj; 186 | 187 | if ( curStrGroup.name === "combination" ) { 188 | 189 | this.record.cursor = { 190 | groupId: parentGroup.id, 191 | startOffset: 0, 192 | endOffset: curStrGroup.operand.length 193 | }; 194 | 195 | // 字符内容处理 196 | curStrGroup.operand.unshift( CURSOR_CHAR ); 197 | curStrGroup.operand.push( CURSOR_CHAR ); 198 | 199 | } else { 200 | 201 | // 函数处理, 找到函数所处的最大范围 202 | while ( parentGroupObj.strGroup.name !== "combination" || parentGroup.content === 1 ) { 203 | 204 | group = parentGroup; 205 | groupObj = parentGroupObj; 206 | 207 | parentGroup = this.kfEditor.requestService( "position.get.parent.group", groupObj.objGroup.node ); 208 | parentGroupObj = this.objTree.mapping[ parentGroup.id ]; 209 | 210 | } 211 | 212 | var parentIndex = [].indexOf.call( parentGroup.content, group.groupObj ); 213 | 214 | this.record.cursor = { 215 | groupId: parentGroup.id, 216 | startOffset: parentIndex, 217 | endOffset: parentIndex + 1 218 | }; 219 | 220 | // 在当前函数所在的位置作标记 221 | parentGroupObj.strGroup.operand.splice( parentIndex+1, 0, CURSOR_CHAR ); 222 | parentGroupObj.strGroup.operand.splice( parentIndex, 0, CURSOR_CHAR ); 223 | 224 | } 225 | 226 | // 返回结构树进过序列化后所对应的latex表达式, 同时包含有当前光标定位点信息 227 | resultStr = this.kfEditor.requestService( "parser.latex.serialization", this.objTree.parsedTree ); 228 | 229 | startOffset = resultStr.indexOf( CURSOR_CHAR ); 230 | resultStr = resultStr.replace( CURSOR_CHAR, "" ); 231 | endOffset = resultStr.indexOf( CURSOR_CHAR ); 232 | 233 | parentGroupObj.strGroup.operand.splice( this.record.cursor.startOffset, 1 ); 234 | parentGroupObj.strGroup.operand.splice( this.record.cursor.endOffset, 1 ); 235 | 236 | return { 237 | str: resultStr, 238 | startOffset: startOffset, 239 | endOffset: endOffset 240 | } 241 | 242 | }, 243 | 244 | getLatexInfo: function () { 245 | 246 | var cursor = this.record.cursor, 247 | objGroup = this.objTree.mapping[ cursor.groupId ], 248 | curStrGroup = objGroup.strGroup, 249 | resultStr = null, 250 | strStartIndex = -1, 251 | strEndIndex = -1, 252 | isPlaceholder = !!curStrGroup.attr[ "data-placeholder" ]; 253 | 254 | // 格式化偏移值, 保证在处理操作数时, 标记位置不会出错 255 | strStartIndex = Math.min( cursor.endOffset, cursor.startOffset ); 256 | strEndIndex = Math.max( cursor.endOffset, cursor.startOffset ); 257 | 258 | if ( !isPlaceholder ) { 259 | 260 | curStrGroup.operand.splice( strEndIndex, 0, CURSOR_CHAR ); 261 | curStrGroup.operand.splice( strStartIndex, 0, CURSOR_CHAR ); 262 | strEndIndex += 1; 263 | 264 | } else { 265 | // 找到占位符的包裹元素 266 | curStrGroup = this.kfEditor.requestService( "position.get.parent.group", objGroup.objGroup.node ) 267 | curStrGroup = this.objTree.mapping[ curStrGroup.id ].strGroup; 268 | curStrGroup.operand.unshift( CURSOR_CHAR ); 269 | curStrGroup.operand.push( CURSOR_CHAR ); 270 | } 271 | 272 | // 返回结构树进过序列化后所对应的latex表达式, 同时包含有当前光标定位点信息 273 | resultStr = this.kfEditor.requestService( "parser.latex.serialization", this.objTree.parsedTree ); 274 | 275 | if ( !isPlaceholder ) { 276 | curStrGroup.operand.splice( strEndIndex, 1 ); 277 | curStrGroup.operand.splice( strStartIndex, 1 ); 278 | } else { 279 | curStrGroup.operand.shift(); 280 | curStrGroup.operand.pop(); 281 | } 282 | 283 | strStartIndex = resultStr.indexOf( CURSOR_CHAR ); 284 | // 清除掉一个符号 285 | resultStr = resultStr.replace( CURSOR_CHAR, "" ); 286 | strEndIndex = resultStr.lastIndexOf( CURSOR_CHAR ); 287 | 288 | return { 289 | str: resultStr, 290 | startOffset: strStartIndex, 291 | endOffset: strEndIndex 292 | } 293 | 294 | }, 295 | 296 | // 更新光标记录, 同时更新数据 297 | updateCursor: function ( groupId, startOffset, endOffset ) { 298 | 299 | if ( endOffset === undefined ) { 300 | endOffset = startOffset; 301 | } 302 | 303 | this.record.cursor = { 304 | groupId: groupId, 305 | startOffset: startOffset, 306 | endOffset: endOffset 307 | }; 308 | 309 | window.tt = this.record.cursor; 310 | 311 | }, 312 | 313 | serialization: function () { 314 | 315 | // 返回结构树进过序列化后所对应的latex表达式, 同时包含有当前光标定位点信息 316 | return this.kfEditor.requestService( "parser.latex.serialization", this.objTree.parsedTree ); 317 | 318 | 319 | }, 320 | 321 | insertGroup: function ( latexStr ) { 322 | 323 | var parsedResult = this.kfEditor.requestService( "parser.parse", latexStr ), 324 | subtree = parsedResult.tree; 325 | 326 | this.insertSubtree( subtree ); 327 | 328 | }, 329 | 330 | leftMove: function () { 331 | this.components.move.leftMove(); 332 | }, 333 | 334 | rightMove: function () { 335 | this.components.move.rightMove(); 336 | }, 337 | 338 | insertSubtree: function ( subtree ) { 339 | 340 | var cursorInfo = this.record.cursor, 341 | // 当前光标信息所在的子树 342 | startOffset = 0, 343 | endOffset = 0, 344 | currentTree = null, 345 | diff = 0; 346 | 347 | if ( this.isPlaceholder( cursorInfo.groupId ) ) { 348 | // 当前在占位符内,所以用子树替换占位符 349 | this.replaceTree( subtree ); 350 | 351 | } else { 352 | 353 | startOffset = Math.min( cursorInfo.startOffset, cursorInfo.endOffset ); 354 | endOffset = Math.max( cursorInfo.startOffset, cursorInfo.endOffset ); 355 | diff = endOffset - startOffset; 356 | 357 | currentTree = this.objTree.mapping[ cursorInfo.groupId ].strGroup; 358 | 359 | // 插入子树 360 | currentTree.operand.splice( startOffset, diff, subtree ); 361 | 362 | // 更新光标记录 363 | cursorInfo.startOffset += 1; 364 | cursorInfo.endOffset = cursorInfo.startOffset; 365 | } 366 | 367 | }, 368 | 369 | replaceTree: function ( subtree ) { 370 | 371 | var cursorInfo = this.record.cursor, 372 | groupNode = this.objTree.mapping[ cursorInfo.groupId ].objGroup.node, 373 | parentInfo = this.kfEditor.requestService( "position.get.parent.info", groupNode ), 374 | currentTree = this.objTree.mapping[ parentInfo.group.id ].strGroup; 375 | 376 | // 替换占位符为子树 377 | currentTree.operand[ parentInfo.index ] = subtree; 378 | 379 | // 更新光标 380 | cursorInfo.groupId = parentInfo.group.id; 381 | cursorInfo.startOffset = parentInfo.index + 1; 382 | cursorInfo.endOffset = parentInfo.index + 1; 383 | 384 | } 385 | 386 | }); 387 | 388 | return SyntaxComponenet; 389 | 390 | } ); -------------------------------------------------------------------------------- /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: 0.5, 12 | max: 5 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/toolbar-ele-list.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * toolbar元素列表定义 3 | */ 4 | 5 | define( function ( require ) { 6 | 7 | var UI_ELE_TYPE = require( "ui/ui-impl/def/ele-type" ), 8 | BOX_TYPE = require( "ui/ui-impl/def/box-type" ), 9 | kity = require( "kity" ); 10 | 11 | var config = [ { 12 | type: UI_ELE_TYPE.DRAPDOWN_BOX, 13 | options: { 14 | button: { 15 | label: "预设", 16 | icon: "assets/images/toolbar/button/pi.png" 17 | }, 18 | box: { 19 | group: [ { 20 | title: "预设公式", 21 | content: [ { 22 | label: "二次公式", 23 | item: { 24 | show: '', 25 | val: "x=\\frac {-b\\pm\\sqrt {b^2-4ac}}{2a}" 26 | } 27 | }, { 28 | label: "二项式定理", 29 | item: { 30 | show: '', 31 | val: "{\\left(x+a\\right)}^2=\\sum^n_{k=0}{\\left(^n_k\\right)x^ka^{n-k}}" 32 | } 33 | }, { 34 | label: "勾股定理", 35 | item: { 36 | show: '', 37 | val: "a^2+b^2=c^2" 38 | } 39 | } ] 40 | } ] 41 | } 42 | } 43 | }, { 44 | type: UI_ELE_TYPE.DELIMITER 45 | }, { 46 | type: UI_ELE_TYPE.AREA, 47 | options: { 48 | button: { 49 | icon: "assets/images/toolbar/char/button.png" 50 | }, 51 | box: { 52 | width: 527, 53 | type: BOX_TYPE.OVERLAP, 54 | group: [ { 55 | title: "基础数学", 56 | content: [ { 57 | item: { 58 | show: '', 59 | val: "\\pm" 60 | } 61 | }, { 62 | item: { 63 | show: '', 64 | val: "\\infty" 65 | } 66 | }, { 67 | item: { 68 | show: '', 69 | val: "=" 70 | } 71 | }, { 72 | item: { 73 | show: '', 74 | val: "\\sim" 75 | } 76 | }, { 77 | item: { 78 | show: '', 79 | val: "\\times" 80 | } 81 | }, { 82 | item: { 83 | show: '', 84 | val: "\\div" 85 | } 86 | }, { 87 | item: { 88 | show: '', 89 | val: "!" 90 | } 91 | }, { 92 | item: { 93 | show: '', 94 | val: "<" 95 | } 96 | }, { 97 | item: { 98 | show: '', 99 | val: "\\ll" 100 | } 101 | }, { 102 | item: { 103 | show: '', 104 | val: ">" 105 | } 106 | }, { 107 | item: { 108 | show: '', 109 | val: "\\gg" 110 | } 111 | }, { 112 | item: { 113 | show: '', 114 | val: "\\leq" 115 | } 116 | }, { 117 | item: { 118 | show: '', 119 | val: "\\geq" 120 | } 121 | }, { 122 | item: { 123 | show: '', 124 | val: "\\mp" 125 | } 126 | }, { 127 | item: { 128 | show: '', 129 | val: "\\simeq" 130 | } 131 | }, { 132 | item: { 133 | show: '', 134 | val: "\\equiv" 135 | } 136 | } ] 137 | }, { 138 | title: "希腊字母", 139 | content: [] 140 | } ] 141 | } 142 | } 143 | }, { 144 | type: UI_ELE_TYPE.DELIMITER 145 | }, { 146 | type: UI_ELE_TYPE.DRAPDOWN_BOX, 147 | options: { 148 | button: { 149 | label: "分数", 150 | icon: "assets/images/toolbar/button/frac.png" 151 | }, 152 | box: { 153 | width: 378, 154 | group: [ { 155 | title: "分数", 156 | content: [ { 157 | item: { 158 | show: '', 159 | val: "\\frac \\placeholder\\placeholder" 160 | } 161 | }, { 162 | item: { 163 | show: '', 164 | val: "\\placeholder/\\placeholder" 165 | } 166 | } ] 167 | }, { 168 | title: "常用分数", 169 | content: [ { 170 | item: { 171 | show: '', 172 | val: "\\frac {dy}{dx}" 173 | } 174 | }, { 175 | item: { 176 | show: '', 177 | val: "\\frac {\\Delta y}{\\Delta x}" 178 | } 179 | }, { 180 | item: { 181 | show: '', 182 | val: "\\frac {\\delta y}{\\delta x}" 183 | } 184 | }, { 185 | item: { 186 | show: '', 187 | val: "\\frac \\pi 2" 188 | } 189 | } ] 190 | } ] 191 | } 192 | } 193 | }, { 194 | type: UI_ELE_TYPE.DRAPDOWN_BOX, 195 | options: { 196 | button: { 197 | label: "上下标", 198 | icon: "assets/images/toolbar/button/script.png" 199 | }, 200 | box: { 201 | width: 378, 202 | group: [ { 203 | title: "上标和下标", 204 | content: [ { 205 | item: { 206 | show: '', 207 | val: "\\placeholder^\\placeholder" 208 | } 209 | }, { 210 | item: { 211 | show: '', 212 | val: "\\placeholder_\\placeholder" 213 | } 214 | }, { 215 | item: { 216 | show: '', 217 | val: "\\placeholder^\\placeholder_\\placeholder" 218 | } 219 | }, { 220 | item: { 221 | show: '', 222 | val: "^\\placeholder_\\placeholder\\placeholder" 223 | } 224 | } ] 225 | }, { 226 | title: "常用的上标和下标", 227 | content: [ { 228 | item: { 229 | show: '', 230 | val: "e^{-i\\omega t}" 231 | } 232 | }, { 233 | item: { 234 | show: '', 235 | val: "x^2" 236 | } 237 | }, { 238 | item: { 239 | show: '', 240 | val: "^n_1Y" 241 | } 242 | } ] 243 | } ] 244 | } 245 | } 246 | }, { 247 | type: UI_ELE_TYPE.DRAPDOWN_BOX, 248 | options: { 249 | button: { 250 | label: "根式", 251 | icon: "assets/images/toolbar/button/sqrt.png" 252 | }, 253 | box: { 254 | width: 378, 255 | group: [ { 256 | title: "根式", 257 | content: [ { 258 | item: { 259 | show: '', 260 | val: "\\sqrt \\placeholder" 261 | } 262 | }, { 263 | item: { 264 | show: '', 265 | val: "\\sqrt [\\placeholder] \\placeholder" 266 | } 267 | }, { 268 | item: { 269 | show: '', 270 | val: "\\sqrt [2] \\placeholder" 271 | } 272 | }, { 273 | item: { 274 | show: '', 275 | val: "\\sqrt [3] \\placeholder" 276 | } 277 | } ] 278 | }, { 279 | title: "常用根式", 280 | content: [ { 281 | item: { 282 | show: '', 283 | val: "\\frac {-b\\pm\\sqrt{b^2-4ac}}{2a}" 284 | } 285 | }, { 286 | item: { 287 | show: '', 288 | val: "\\sqrt {a^2+b^2}" 289 | } 290 | } ] 291 | } ] 292 | } 293 | } 294 | }, { 295 | type: UI_ELE_TYPE.DRAPDOWN_BOX, 296 | options: { 297 | button: { 298 | label: "括号", 299 | icon: "assets/images/toolbar/button/brackets.png" 300 | }, 301 | box: { 302 | width: 378, 303 | group: [ { 304 | title: "方括号", 305 | content: [ { 306 | item: { 307 | show: '', 308 | val: "\\left(\\placeholder\\right)" 309 | } 310 | }, { 311 | item: { 312 | show: '', 313 | val: "\\left[\\placeholder\\right]" 314 | } 315 | }, { 316 | item: { 317 | show: '', 318 | val: "\\left\\{\\placeholder\\right\\}" 319 | } 320 | }, { 321 | item: { 322 | show: '', 323 | val: "\\left|\\placeholder\\right|" 324 | } 325 | } ] 326 | } ] 327 | } 328 | } 329 | } ]; 330 | 331 | // 初始化希腊字符配置 332 | ( function () { 333 | 334 | var greekList = [ "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega" ], 335 | greekConf = config[ 2 ].options.box.group[ 1 ].content; 336 | 337 | kity.Utils.each( greekList, function ( greekChar, index ) { 338 | 339 | greekConf.push( { 340 | item: { 341 | show: '', 342 | val: "\\" + greekChar 343 | } 344 | } ); 345 | 346 | } ); 347 | 348 | } )(); 349 | 350 | return config; 351 | 352 | } ); -------------------------------------------------------------------------------- /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 ( kfEditor, uiComponent, 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.initEvent(); 30 | 31 | }, 32 | 33 | initEvent: function () { 34 | 35 | var _self = this; 36 | 37 | // 通知所有组件关闭 38 | $$.on( this.kfEditor.getContainer(), "mousedown", function () { 39 | _self.notify( "closeAll" ); 40 | } ); 41 | 42 | // 订阅数据选择主题 43 | $$.subscribe( "data.select", function ( data ) { 44 | 45 | _self.insertSource( data ); 46 | 47 | } ); 48 | 49 | }, 50 | 51 | insertSource: function ( val ) { 52 | 53 | this.kfEditor.requestService( "control.insert.group", val ); 54 | 55 | }, 56 | 57 | // 接受到关闭通知 58 | notify: function ( type ) { 59 | 60 | var caller = null; 61 | 62 | switch ( type ) { 63 | 64 | // 关闭所有组件 65 | case "closeAll": 66 | // 关闭其他组件 67 | case "closeOther": 68 | this.closeElement( arguments[ 1 ] ); 69 | return; 70 | 71 | } 72 | 73 | }, 74 | 75 | closeElement: function ( exception ) { 76 | 77 | kity.Utils.each( this.elements, function ( ele ) { 78 | 79 | if ( ele != exception ) { 80 | ele.hide && ele.hide(); 81 | } 82 | 83 | } ); 84 | 85 | }, 86 | 87 | initToolbarElements: function () { 88 | 89 | var elements = this.elements, 90 | doc = this.uiComponent.toolbarContainer.ownerDocument, 91 | _self = this; 92 | 93 | kity.Utils.each( this.elementList, function ( eleInfo, i ) { 94 | 95 | var ele = createElement( eleInfo.type, doc, eleInfo.options ); 96 | elements.push( ele ); 97 | _self.appendElement( ele ); 98 | 99 | } ); 100 | 101 | }, 102 | 103 | appendElement: function ( uiElement ) { 104 | 105 | uiElement.setToolbar( this ); 106 | uiElement.attachTo( this.uiComponent.toolbarContainer ); 107 | 108 | } 109 | 110 | } ); 111 | 112 | function createElement ( type, doc, options ) { 113 | 114 | switch ( type ) { 115 | 116 | case UI_ELE_TYPE.DRAPDOWN_BOX: 117 | return createDrapdownBox( doc, options ); 118 | 119 | case UI_ELE_TYPE.DELIMITER: 120 | return createDelimiter( doc ); 121 | 122 | case UI_ELE_TYPE.AREA: 123 | return createArea( doc, options ); 124 | 125 | 126 | } 127 | 128 | } 129 | 130 | function createDrapdownBox ( doc, options ) { 131 | 132 | return new UiImpl.DrapdownBox( doc, options ); 133 | 134 | } 135 | 136 | function createDelimiter ( doc ) { 137 | return new UiImpl.Delimiter( doc ); 138 | } 139 | 140 | function createArea ( doc, options ) { 141 | return new UiImpl.Area( doc, options ); 142 | } 143 | 144 | return Tollbar; 145 | 146 | } ); -------------------------------------------------------------------------------- /src/ui/ui-impl/area.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 | Box = require( "ui/ui-impl/box" ), 15 | 16 | Area = kity.createClass( "Area", { 17 | 18 | constructor: function ( doc, options ) { 19 | 20 | this.options = options; 21 | 22 | this.doc = doc; 23 | this.toolbar = null; 24 | 25 | this.element = this.createArea(); 26 | this.container = this.createContainer(); 27 | this.button = this.createButton(); 28 | this.mountPoint = this.createMountPoint(); 29 | 30 | this.boxObject = this.createBox(); 31 | this.mergeElement(); 32 | this.mount(); 33 | 34 | this.setListener(); 35 | this.initEvent(); 36 | 37 | this.updateContent(); 38 | 39 | }, 40 | 41 | initEvent: function () { 42 | 43 | var _self = this; 44 | 45 | $$.on( this.button, "click", function () { 46 | 47 | _self.showMount(); 48 | 49 | } ); 50 | 51 | $$.delegate( this.container, ".kf-editor-ui-area-item", "mousedown", function ( e ) { 52 | 53 | e.preventDefault(); 54 | 55 | if ( e.which !== 1 ) { 56 | return; 57 | } 58 | 59 | $$.publish( "data.select", this.getAttribute( "data-value" ) ); 60 | 61 | } ); 62 | 63 | this.boxObject.initEvent(); 64 | 65 | }, 66 | 67 | setListener: function () { 68 | 69 | var _self = this; 70 | 71 | this.boxObject.setSelectHandler( function ( val ) { 72 | // 发布 73 | $$.publish( "data.select", val ); 74 | _self.hide(); 75 | } ); 76 | 77 | // 内容面板切换 78 | this.boxObject.setChangeHandler( function ( index ) { 79 | _self.updateContent(); 80 | } ); 81 | 82 | }, 83 | 84 | createArea: function () { 85 | 86 | var areaNode = $$.ele( this.doc, "div", { 87 | className: PREFIX + "area" 88 | }); 89 | 90 | if ( "width" in this.options ) { 91 | areaNode.style.width = this.options.width + "px"; 92 | } 93 | 94 | return areaNode; 95 | 96 | }, 97 | 98 | updateContent: function () { 99 | 100 | var items = this.boxObject.getOverlapContent(), 101 | newContent = []; 102 | 103 | // 清空原有内容 104 | this.container.innerHTML = ""; 105 | 106 | kity.Utils.each( items, function ( item, index ) { 107 | 108 | item = item.item; 109 | 110 | newContent.push( '
'+ item.show +'
' ); 111 | 112 | } ); 113 | 114 | this.container.innerHTML = newContent.join( "" ); 115 | 116 | }, 117 | 118 | // 挂载 119 | mount: function () { 120 | 121 | this.boxObject.mountTo( this.mountPoint ); 122 | 123 | }, 124 | 125 | showMount: function () { 126 | this.mountPoint.style.display = "block"; 127 | }, 128 | 129 | hideMount: function () { 130 | this.mountPoint.style.display = "none"; 131 | }, 132 | 133 | hide: function () { 134 | this.hideMount(); 135 | this.boxObject.hide(); 136 | }, 137 | 138 | createButton: function () { 139 | 140 | return $$.ele( this.doc, "div", { 141 | className: PREFIX + "area-button", 142 | content: "▼" 143 | } ); 144 | 145 | }, 146 | 147 | createMountPoint: function () { 148 | 149 | return $$.ele( this.doc, "div", { 150 | className: PREFIX + "area-mount" 151 | } ); 152 | 153 | }, 154 | 155 | createBox: function () { 156 | 157 | return new Box( this.doc, this.options.box ); 158 | 159 | }, 160 | 161 | createContainer: function () { 162 | 163 | var node = $$.ele( this.doc, "div", { 164 | className: PREFIX + "area-container" 165 | } ); 166 | 167 | return node; 168 | 169 | }, 170 | 171 | mergeElement: function () { 172 | 173 | this.element.appendChild( this.container ); 174 | this.element.appendChild( this.button ); 175 | this.element.appendChild( this.mountPoint ); 176 | 177 | }, 178 | 179 | setToolbar: function ( toolbar ) { 180 | this.toolbar = toolbar; 181 | }, 182 | 183 | attachTo: function ( container ) { 184 | container.appendChild( this.element ); 185 | } 186 | 187 | } ); 188 | 189 | return Area; 190 | 191 | } ); -------------------------------------------------------------------------------- /src/ui/ui-impl/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 | PREFIX = "kf-editor-ui-", 10 | 11 | // UiUitls 12 | $$ = require( "ui/ui-impl/ui-utils" ), 13 | 14 | BOX_TYPE = require( "ui/ui-impl/def/box-type" ), 15 | 16 | ITEM_TYPE = require( "ui/ui-impl/def/item-type" ), 17 | 18 | Button = require( "ui/ui-impl/button" ), 19 | 20 | List = require( "ui/ui-impl/list" ), 21 | 22 | Box = kity.createClass( "Box", { 23 | 24 | constructor: function ( doc, options ) { 25 | 26 | this.options = options; 27 | 28 | this.options.type = this.options.type || BOX_TYPE.DETACHED; 29 | 30 | this.doc = doc; 31 | 32 | this.overlapButtonObject = null; 33 | 34 | this.overlapIndex = -1; 35 | 36 | this.element = this.createBox(); 37 | this.groupContainer = this.createGroupContainer(); 38 | this.itemGroups = this.createItemGroup(); 39 | 40 | this.mergeElement(); 41 | 42 | }, 43 | 44 | createBox: function () { 45 | 46 | var boxNode = $$.ele( this.doc, "div", { 47 | className: PREFIX + "box" 48 | } ); 49 | 50 | if ( "width" in this.options ) { 51 | boxNode.style.width = this.options.width + "px"; 52 | } 53 | 54 | return boxNode; 55 | 56 | }, 57 | 58 | initEvent: function () { 59 | 60 | var className = "." + PREFIX + "box-item", 61 | _self = this; 62 | 63 | $$.delegate( this.groupContainer, className, "mousedown", function ( e ) { 64 | 65 | e.preventDefault(); 66 | 67 | if ( e.which !== 1 ) { 68 | return; 69 | } 70 | 71 | _self.onselectHandler && _self.onselectHandler( this.getAttribute( "data-value" ) ); 72 | 73 | } ); 74 | 75 | $$.on( this.element, "mousedown", function ( e ) { 76 | 77 | e.stopPropagation(); 78 | e.preventDefault(); 79 | 80 | } ); 81 | 82 | }, 83 | 84 | getNode: function () { 85 | return this.element; 86 | }, 87 | 88 | setSelectHandler: function ( onselectHandler ) { 89 | this.onselectHandler = onselectHandler; 90 | }, 91 | 92 | setChangeHandler: function ( changeHandler ) { 93 | this.onchangeHandler = changeHandler; 94 | }, 95 | 96 | onchangeHandler: function ( index ) {}, 97 | 98 | createGroupContainer: function () { 99 | 100 | return $$.ele( this.doc, "div", { 101 | className: PREFIX + "box-container" 102 | } ); 103 | 104 | }, 105 | 106 | createItemGroup: function () { 107 | 108 | var groups = this.createGroup(); 109 | 110 | switch ( this.options.type ) { 111 | 112 | case BOX_TYPE.DETACHED: 113 | return groups; 114 | 115 | case BOX_TYPE.OVERLAP: 116 | return this.createOverlapGroup( groups ); 117 | 118 | } 119 | 120 | return null; 121 | 122 | }, 123 | 124 | hide: function () { 125 | this.overlapButtonObject && this.overlapButtonObject.hideMount(); 126 | }, 127 | 128 | getOverlapContent: function () { 129 | 130 | // 只有重叠式才可以获取重叠内容 131 | if ( this.options.type !== BOX_TYPE.OVERLAP ) { 132 | return null; 133 | } 134 | 135 | return this.options.group[ this.overlapIndex ].content; 136 | 137 | }, 138 | 139 | createOverlapGroup: function ( groups ) { 140 | 141 | var list = this.getGroupList(), 142 | _self = this, 143 | overlapContainer = createOverlapContainer( this.doc), 144 | overlapButtonObject = createOverlapButton( this.doc ), 145 | overlapListObject = createOverlapList( this.doc, list ); 146 | 147 | this.overlapButtonObject = overlapButtonObject; 148 | 149 | // 组合选择组件 150 | overlapButtonObject.mount( overlapListObject ); 151 | 152 | overlapButtonObject.initEvent(); 153 | overlapListObject.initEvent(); 154 | 155 | // 切换面板 156 | overlapListObject.setSelectHandler( function ( index, oldIndex ) { 157 | 158 | _self.overlapIndex = index; 159 | 160 | overlapButtonObject.setLabel( list.items[index] + " ▼" ); 161 | overlapButtonObject.hideMount(); 162 | 163 | // 切换内容 164 | groups[ oldIndex ].style.display = "none"; 165 | groups[ index ].style.display = "block"; 166 | 167 | _self.onchangeHandler( index ); 168 | 169 | } ); 170 | 171 | overlapContainer.appendChild( overlapButtonObject.getNode() ); 172 | 173 | kity.Utils.each( groups, function ( group, index ) { 174 | 175 | if ( index > 0 ) { 176 | group.style.display = "none"; 177 | } 178 | 179 | overlapContainer.appendChild( group ); 180 | 181 | } ); 182 | 183 | overlapListObject.select( 0 ); 184 | 185 | return [ overlapContainer ]; 186 | 187 | }, 188 | 189 | // 获取group的list列表, 该类表满足box的group参数格式 190 | getGroupList: function () { 191 | 192 | var lists = []; 193 | 194 | kity.Utils.each( this.options.group, function ( group, index ) { 195 | 196 | lists.push( group.title ); 197 | 198 | } ); 199 | 200 | return { 201 | width: 150, 202 | items: lists 203 | }; 204 | 205 | }, 206 | 207 | createGroup: function () { 208 | 209 | var doc = this.doc, 210 | groups = [], 211 | groupNode = null, 212 | groupTitle = null, 213 | itemType = BOX_TYPE.DETACHED === this.options.type ? ITEM_TYPE.BIG : ITEM_TYPE.SMALL, 214 | itemContainer = null; 215 | 216 | groupNode = $$.ele( this.doc, "div", { 217 | className: PREFIX + "box-group" 218 | } ); 219 | 220 | itemContainer = groupNode.cloneNode( false ); 221 | itemContainer.className = PREFIX + "box-group-item-container"; 222 | 223 | kity.Utils.each( this.options.group, function ( group, i ) { 224 | 225 | groupNode = groupNode.cloneNode( false ); 226 | itemContainer = itemContainer.cloneNode( false ); 227 | 228 | groupTitle = $$.ele( doc, "div", { 229 | className: PREFIX + "box-group-title", 230 | content: group.title 231 | } ); 232 | 233 | groupNode.appendChild( groupTitle ); 234 | groupNode.appendChild( itemContainer ); 235 | 236 | kity.Utils.each( createItems( doc, group.content, itemType ), function ( item ) { 237 | 238 | item.appendTo( itemContainer ); 239 | 240 | } ); 241 | 242 | groups.push( groupNode ); 243 | 244 | } ); 245 | 246 | return groups; 247 | 248 | }, 249 | 250 | mergeElement: function () { 251 | 252 | var groupContainer = this.groupContainer; 253 | 254 | this.element.appendChild( groupContainer ); 255 | 256 | kity.Utils.each( this.itemGroups, function ( group ) { 257 | 258 | groupContainer.appendChild( group ); 259 | 260 | } ); 261 | 262 | }, 263 | 264 | mountTo: function ( container ) { 265 | container.appendChild( this.element ); 266 | }, 267 | 268 | appendTo: function ( container ) { 269 | container.appendChild( this.element ); 270 | } 271 | 272 | } ), 273 | 274 | BoxItem = kity.createClass( "BoxItem", { 275 | 276 | constructor: function ( type, doc, options ) { 277 | 278 | this.type = type; 279 | this.doc = doc; 280 | this.options = options; 281 | 282 | this.element = this.createItem(); 283 | 284 | // 项的label是可选的 285 | this.labelNode = this.createLabel(); 286 | this.contentNode = this.createContent(); 287 | 288 | this.mergeElement(); 289 | 290 | }, 291 | 292 | getNode: function () { 293 | return this.element; 294 | }, 295 | 296 | createItem: function () { 297 | 298 | var itemNode = $$.ele( this.doc, "div", { 299 | className: PREFIX + "box-item" 300 | } ); 301 | 302 | return itemNode; 303 | 304 | }, 305 | 306 | createLabel: function () { 307 | 308 | var labelNode = null; 309 | 310 | if ( !( "label" in this.options ) ) { 311 | return; 312 | } 313 | 314 | labelNode = $$.ele( this.doc, "div", { 315 | className: PREFIX + "box-item-label", 316 | content: this.options.label 317 | } ); 318 | 319 | return labelNode; 320 | 321 | }, 322 | 323 | getContent: function () { 324 | 325 | 326 | 327 | }, 328 | 329 | createContent: function () { 330 | 331 | switch ( this.type ) { 332 | 333 | case ITEM_TYPE.BIG: 334 | return this.createBigContent(); 335 | 336 | case ITEM_TYPE.SMALL: 337 | return this.createSmallContent(); 338 | 339 | } 340 | 341 | }, 342 | 343 | createBigContent: function () { 344 | 345 | var doc = this.doc, 346 | contentNode = $$.ele( doc, "div", { 347 | className: PREFIX + "box-item-content" 348 | }), 349 | cls = PREFIX + "box-item-val", 350 | tmpContent = this.options.item, 351 | tmpNode = null; 352 | 353 | if ( typeof tmpContent === "string" ) { 354 | tmpContent = { 355 | show: tmpContent, 356 | val: tmpContent 357 | }; 358 | } 359 | 360 | tmpNode = $$.ele( doc, "div", { 361 | className: cls 362 | } ); 363 | 364 | tmpNode.innerHTML = tmpContent.show; 365 | // 附加属性到项的根节点上 366 | this.element.setAttribute( "data-value", tmpContent.val ); 367 | 368 | contentNode.appendChild( tmpNode ); 369 | 370 | return contentNode; 371 | 372 | }, 373 | 374 | createSmallContent: function () { 375 | 376 | var doc = this.doc, 377 | contentNode = $$.ele( doc, "div", { 378 | className: PREFIX + "box-item-content" 379 | }), 380 | cls = PREFIX + "box-item-val", 381 | tmpContent = this.options.item, 382 | tmpNode = null; 383 | 384 | if ( typeof tmpContent === "string" ) { 385 | tmpContent = { 386 | show: tmpContent, 387 | val: tmpContent 388 | }; 389 | } 390 | 391 | tmpNode = $$.ele( doc, "div", { 392 | className: cls 393 | } ); 394 | 395 | tmpNode.innerHTML = tmpContent.show; 396 | // 附加属性到项的根节点上 397 | this.element.setAttribute( "data-value", tmpContent.val ); 398 | 399 | contentNode.appendChild( tmpNode ); 400 | 401 | return contentNode; 402 | 403 | }, 404 | 405 | mergeElement: function () { 406 | 407 | if ( this.labelNode ) { 408 | this.element.appendChild( this.labelNode ); 409 | } 410 | 411 | this.element.appendChild( this.contentNode ); 412 | 413 | }, 414 | 415 | appendTo: function ( container ) { 416 | container.appendChild( this.element ); 417 | } 418 | 419 | } ); 420 | 421 | 422 | function createItems ( doc, group, type ) { 423 | 424 | var items = []; 425 | 426 | kity.Utils.each( group, function ( itemVal, i ) { 427 | 428 | items.push( new BoxItem( type, doc, itemVal) ); 429 | 430 | } ); 431 | 432 | return items; 433 | 434 | } 435 | 436 | // 为重叠式box创建容器 437 | function createOverlapContainer ( doc ) { 438 | 439 | return $$.ele( doc, "div", { 440 | className: PREFIX + "overlap-container" 441 | } ); 442 | 443 | } 444 | 445 | function createOverlapButton ( doc ) { 446 | 447 | return new Button( doc, { 448 | sign: false, 449 | className: "overlap-button", 450 | label: "" 451 | } ); 452 | 453 | } 454 | 455 | function createOverlapList ( doc, list ) { 456 | return new List( doc, list ); 457 | } 458 | 459 | return Box; 460 | 461 | } ); -------------------------------------------------------------------------------- /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 | // UiUitls 12 | $$ = require( "ui/ui-impl/ui-utils" ), 13 | 14 | Button = kity.createClass( "Button", { 15 | 16 | constructor: function ( doc, options ) { 17 | 18 | this.options = options; 19 | 20 | // 事件状态, 是否已经初始化 21 | this.eventState = false; 22 | 23 | this.doc = doc; 24 | 25 | this.element = this.createButton(); 26 | 27 | this.icon = this.createIcon(); 28 | this.label = this.createLabel(); 29 | this.sign = this.createSign(); 30 | this.mountPoint = this.createMountPoint(); 31 | 32 | this.mergeElement(); 33 | 34 | }, 35 | 36 | initEvent: function () { 37 | 38 | var _self = this; 39 | 40 | if ( this.eventState ) { 41 | return; 42 | } 43 | 44 | this.eventState = true; 45 | 46 | $$.on( this.element, "mousedown", function ( e ) { 47 | 48 | e.preventDefault(); 49 | e.stopPropagation(); 50 | 51 | if ( e.which !== 1 ) { 52 | return; 53 | } 54 | 55 | _self.toggleSelect(); 56 | _self.toggleMountElement(); 57 | 58 | } ); 59 | 60 | }, 61 | 62 | toggleMountElement: function () { 63 | 64 | var state = this.mountPoint.style.display || "none"; 65 | this.mountPoint.style.display = state === "none" ? "block" : "none"; 66 | 67 | }, 68 | 69 | setLabel: function ( labelText ) { 70 | this.label.innerHTML = labelText; 71 | }, 72 | 73 | toggleSelect: function () { 74 | this.element.classList.toggle( PREFIX + "button-in" ); 75 | }, 76 | 77 | unselect: function () { 78 | this.element.classList.remove( PREFIX + "button-in" ); 79 | }, 80 | 81 | select: function () { 82 | this.element.classList.add( PREFIX + "button-in" ); 83 | }, 84 | 85 | show: function () { 86 | this.select(); 87 | this.showMount(); 88 | }, 89 | 90 | hide: function () { 91 | this.unselect(); 92 | this.hideMount(); 93 | }, 94 | 95 | showMount: function () { 96 | this.mountPoint.style.display = "block"; 97 | }, 98 | 99 | hideMount: function () { 100 | this.mountPoint.style.display = "none"; 101 | }, 102 | 103 | getNode: function () { 104 | return this.element; 105 | }, 106 | 107 | mount: function ( source ) { 108 | source.mountTo( this.mountPoint ); 109 | }, 110 | 111 | createButton: function () { 112 | 113 | var buttonNode = $$.ele( this.doc, "div", { 114 | className: PREFIX + "button" 115 | } ); 116 | 117 | // 附加className 118 | if ( this.options.className ) { 119 | buttonNode.className += " " + PREFIX + this.options.className; 120 | } 121 | 122 | return buttonNode; 123 | 124 | }, 125 | 126 | createIcon: function () { 127 | 128 | if ( !this.options.icon ) { 129 | return null; 130 | } 131 | 132 | var iconNode = $$.ele( this.doc, "div", { 133 | className: PREFIX + "button-icon" 134 | } ); 135 | 136 | iconNode.style.backgroundImage = "url(" + this.options.icon + ")"; 137 | 138 | return iconNode; 139 | 140 | }, 141 | 142 | createLabel: function () { 143 | 144 | var labelNode = $$.ele( this.doc, "div", { 145 | className: PREFIX + "button-label", 146 | content: this.options.label 147 | } ); 148 | 149 | 150 | return labelNode; 151 | 152 | }, 153 | 154 | createSign: function () { 155 | 156 | if ( !this.options.sign ) { 157 | return null; 158 | } 159 | 160 | return $$.ele( this.doc, "div", { 161 | className: PREFIX + "button-sign" 162 | } ); 163 | 164 | }, 165 | 166 | createMountPoint: function () { 167 | 168 | return $$.ele( this.doc, "div", { 169 | className: PREFIX + "button-mount-point" 170 | } ); 171 | 172 | }, 173 | 174 | mergeElement: function () { 175 | 176 | this.icon && this.element.appendChild( this.icon ); 177 | this.element.appendChild( this.label ); 178 | this.sign && this.element.appendChild( this.sign ); 179 | this.element.appendChild( this.mountPoint ); 180 | 181 | } 182 | 183 | } ); 184 | 185 | return Button; 186 | 187 | } ); -------------------------------------------------------------------------------- /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/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 | }; 12 | 13 | } ); -------------------------------------------------------------------------------- /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 | } ); -------------------------------------------------------------------------------- /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/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 | PREFIX = "kf-editor-ui-", 10 | 11 | // UiUitls 12 | $$ = require( "ui/ui-impl/ui-utils" ), 13 | 14 | Button = require( "ui/ui-impl/button" ), 15 | Box = require( "ui/ui-impl/box" ), 16 | 17 | DrapdownBox = kity.createClass( "DrapdownBox", { 18 | 19 | constructor: function ( doc, options ) { 20 | 21 | this.options = options; 22 | this.toolbar = null; 23 | this.doc = doc; 24 | 25 | this.buttonElement = this.createButton(); 26 | 27 | this.element = this.buttonElement.getNode(); 28 | 29 | this.boxElement = this.createBox(); 30 | 31 | this.buttonElement.mount( this.boxElement ); 32 | 33 | this.initEvent(); 34 | 35 | }, 36 | 37 | initEvent: function () { 38 | 39 | var _self = this; 40 | 41 | // 通知工具栏互斥 42 | $$.on( this.element, "mousedown", function ( e ) { 43 | 44 | e.preventDefault(); 45 | e.stopPropagation(); 46 | 47 | _self.toolbar.notify( "closeOther", _self ); 48 | 49 | } ); 50 | 51 | 52 | this.buttonElement.initEvent(); 53 | this.boxElement.initEvent(); 54 | 55 | this.boxElement.setSelectHandler( function ( val ) { 56 | // 发布 57 | $$.publish( "data.select", val ); 58 | _self.buttonElement.hide(); 59 | } ); 60 | 61 | }, 62 | 63 | setToolbar: function ( toolbar ) { 64 | 65 | this.toolbar = toolbar; 66 | 67 | }, 68 | 69 | createButton: function () { 70 | 71 | return new Button( this.doc, this.options.button ); 72 | 73 | }, 74 | 75 | show: function () { 76 | this.buttonElement.show(); 77 | }, 78 | 79 | hide: function () { 80 | this.buttonElement.hide(); 81 | }, 82 | 83 | createBox: function () { 84 | 85 | return new Box( this.doc, this.options.box ); 86 | 87 | }, 88 | 89 | attachTo: function ( container ) { 90 | 91 | container.appendChild( this.element ); 92 | 93 | } 94 | 95 | }); 96 | 97 | return DrapdownBox; 98 | 99 | } ); -------------------------------------------------------------------------------- /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 | onselectHandler: function ( index, oldIndex ) {}, 34 | 35 | setSelectHandler: function ( selectHandler ) { 36 | this.onselectHandler = selectHandler; 37 | }, 38 | 39 | createBox: function () { 40 | 41 | var boxNode = $$.ele( this.doc, "div", { 42 | className: PREFIX + "list" 43 | } ), 44 | 45 | // 创建背景 46 | bgNode = $$.ele( this.doc, "div", { 47 | className: PREFIX + "list-bg" 48 | } ); 49 | 50 | if ( "width" in this.options ) { 51 | boxNode.style.width = this.options.width + "px"; 52 | } 53 | 54 | boxNode.appendChild( bgNode ); 55 | 56 | return boxNode; 57 | 58 | }, 59 | 60 | select: function ( index ) { 61 | 62 | var oldSelect = this.currentSelect; 63 | 64 | if ( oldSelect === -1 ) { 65 | oldSelect = index; 66 | } 67 | 68 | this.unselect( oldSelect ); 69 | 70 | this.currentSelect = index; 71 | 72 | this.itemGroups.items[ index ].firstChild.style.visibility = "visible"; 73 | 74 | this.onselectHandler( index, oldSelect ); 75 | 76 | }, 77 | 78 | unselect: function ( index ) { 79 | 80 | this.itemGroups.items[ index ].firstChild.style.visibility = "hidden"; 81 | 82 | }, 83 | 84 | initEvent: function () { 85 | 86 | var className = "." + PREFIX + "list-item", 87 | _self = this; 88 | 89 | $$.delegate( this.itemGroups.container, className, "mousedown", function ( e ) { 90 | 91 | e.preventDefault(); 92 | 93 | if ( e.which !== 1 ) { 94 | return; 95 | } 96 | 97 | _self.select( this.getAttribute( "data-index" ) ); 98 | 99 | } ); 100 | 101 | $$.on( this.element, "mousedown", function ( e ) { 102 | 103 | e.stopPropagation(); 104 | e.preventDefault(); 105 | 106 | } ); 107 | 108 | }, 109 | 110 | createItems: function () { 111 | 112 | var doc = this.doc, 113 | groupNode = null, 114 | itemNode = null, 115 | iconNode = null, 116 | items = [], 117 | itemContainer = null; 118 | 119 | groupNode = $$.ele( this.doc, "div", { 120 | className: PREFIX + "list-item" 121 | } ); 122 | 123 | itemContainer = groupNode.cloneNode( false ); 124 | itemContainer.className = PREFIX + "list-item-container"; 125 | 126 | kity.Utils.each( this.options.items, function ( itemText, i ) { 127 | 128 | itemNode = groupNode.cloneNode( false ); 129 | 130 | iconNode = groupNode.cloneNode( false ); 131 | iconNode.className = PREFIX + "list-item-icon"; 132 | iconNode.innerHTML = ''; 133 | 134 | itemNode.appendChild( iconNode ); 135 | itemNode.appendChild( $$.ele( doc, "text", itemText ) ); 136 | 137 | itemNode.setAttribute( "data-index", i ); 138 | 139 | items.push( itemNode ); 140 | itemContainer.appendChild( itemNode ); 141 | 142 | } ); 143 | 144 | return { 145 | container: itemContainer, 146 | items: items 147 | }; 148 | 149 | }, 150 | 151 | mergeElement: function () { 152 | 153 | this.element.appendChild( this.itemGroups.container ); 154 | 155 | }, 156 | 157 | mountTo: function ( container ) { 158 | container.appendChild( this.element ); 159 | } 160 | 161 | } ); 162 | 163 | return List; 164 | 165 | } ); -------------------------------------------------------------------------------- /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 | return { 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 | on: function ( target, type, fn ) { 31 | $( target ).on( type, fn ); 32 | return this; 33 | }, 34 | 35 | delegate: function ( target, selector, type, fn ) { 36 | 37 | $( target ).delegate( selector, type, fn ); 38 | return this; 39 | 40 | }, 41 | 42 | publish: function ( topic, args ) { 43 | 44 | var callbackList = TOPIC_POOL[ topic ]; 45 | 46 | if ( !callbackList ) { 47 | return; 48 | } 49 | 50 | args = [].slice.call( arguments, 1 ); 51 | 52 | kity.Utils.each( callbackList, function ( callback ) { 53 | 54 | callback.apply( null, args ); 55 | 56 | } ); 57 | 58 | }, 59 | 60 | subscribe: function ( topic, callback ) { 61 | 62 | if ( !TOPIC_POOL[ topic ] ) { 63 | 64 | TOPIC_POOL[ topic ] = []; 65 | 66 | } 67 | 68 | TOPIC_POOL[ topic ].push( callback ); 69 | 70 | } 71 | 72 | }; 73 | 74 | } ); -------------------------------------------------------------------------------- /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 | 13 | }; 14 | 15 | } ); -------------------------------------------------------------------------------- /src/ui/ui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hn on 14-3-17. 3 | */ 4 | 5 | define( function ( require, exports, modules ) { 6 | 7 | var kity = require( "kity"), 8 | 9 | Utils = require( "base/utils" ), 10 | 11 | Toolbar = require( "ui/toolbar/toolbar" ), 12 | // 控制组件 13 | ScrollZoom = require( "ui/control/zoom" ), 14 | 15 | ELEMENT_LIST = require( "ui/toolbar-ele-list" ), 16 | 17 | UIComponent = kity.createClass( 'UIComponent', { 18 | 19 | constructor: function ( kfEditor ) { 20 | 21 | var currentDocument = null; 22 | 23 | this.container = kfEditor.getContainer(); 24 | 25 | currentDocument = this.container.ownerDocument; 26 | 27 | // ui组件实例集合 28 | this.components = {}; 29 | 30 | this.kfEditor = kfEditor; 31 | 32 | this.resizeTimer = null; 33 | 34 | this.toolbarContainer = createToolbarContainer( currentDocument ); 35 | this.editArea = createEditArea( currentDocument ); 36 | this.canvasContainer = createCanvasContainer( currentDocument ); 37 | 38 | this.updateContainerSize( this.container, this.toolbarContainer, this.editArea, this.canvasContainer ); 39 | 40 | this.container.appendChild( this.toolbarContainer ); 41 | this.editArea.appendChild( this.canvasContainer ); 42 | this.container.appendChild( this.editArea ); 43 | 44 | this.initCanvas(); 45 | this.initComponents(); 46 | 47 | this.initServices(); 48 | 49 | this.initResizeEvent(); 50 | 51 | }, 52 | 53 | // 组件实例化 54 | initComponents: function () { 55 | 56 | // 工具栏组件 57 | this.components.toolbar = new Toolbar( this.kfEditor, this, ELEMENT_LIST ); 58 | this.components.scrollZoom = new ScrollZoom( this, this.kfEditor, this.canvasContainer ); 59 | 60 | }, 61 | 62 | initCanvas: function () { 63 | 64 | }, 65 | 66 | updateContainerSize: function ( container, toolbar, editArea, canvasContainer ) { 67 | 68 | var containerBox = container.getBoundingClientRect(); 69 | 70 | toolbar.style.width = containerBox.width - 12 + "px"; 71 | toolbar.style.height = 80 + "px"; 72 | 73 | editArea.style.marginTop = 80 + "px"; 74 | editArea.style.width = containerBox.width + "px"; 75 | editArea.style.height = containerBox.height - 80 + "px"; 76 | 77 | }, 78 | 79 | // 初始化服务 80 | initServices: function () { 81 | 82 | this.kfEditor.registerService( "ui.get.canvas.container", this, { 83 | getCanvasContainer: SERVICE_LIST.getCanvasContainer 84 | } ); 85 | 86 | this.kfEditor.registerService( "ui.canvas.container.event", this, { 87 | on: SERVICE_LIST.addEvent, 88 | off: SERVICE_LIST.removeEvent, 89 | trigger: SERVICE_LIST.trigger, 90 | fire: SERVICE_LIST.trigger 91 | } ); 92 | 93 | }, 94 | 95 | initResizeEvent: function () { 96 | 97 | var _self = this; 98 | 99 | this.canvasContainer.ownerDocument.defaultView.onresize = function () { 100 | 101 | window.clearTimeout( _self.resizeTimer ); 102 | 103 | _self.resizeTimer = window.setTimeout( function () { 104 | _self.kfEditor.requestService( "render.relocation" ); 105 | }, 80 ); 106 | 107 | }; 108 | 109 | } 110 | 111 | } ), 112 | 113 | SERVICE_LIST = { 114 | 115 | getCanvasContainer: function () { 116 | 117 | return this.canvasContainer; 118 | 119 | }, 120 | 121 | addEvent: function ( type, handler ) { 122 | 123 | Utils.addEvent( this.canvasContainer, type, handler ); 124 | 125 | }, 126 | 127 | removeEvent: function () {}, 128 | 129 | trigger: function ( type ) { 130 | 131 | Utils.trigger( this.canvasContainer, type ); 132 | 133 | } 134 | 135 | }; 136 | 137 | 138 | function createToolbarContainer ( doc ) { 139 | var container = doc.createElement( "div" ); 140 | container.className = "kf-editor-toolbar"; 141 | return container; 142 | } 143 | 144 | function createEditArea ( doc ) { 145 | var container = doc.createElement( "div" ); 146 | container.className = "kf-editor-edit-area"; 147 | container.style.width = "80%"; 148 | container.style.height = "800px"; 149 | return container; 150 | } 151 | 152 | function createCanvasContainer ( doc ) { 153 | var container = doc.createElement( "div" ); 154 | container.className = "kf-editor-canvas-container"; 155 | return container; 156 | } 157 | 158 | return UIComponent; 159 | 160 | } ); --------------------------------------------------------------------------------