├── .gitignore
├── src
├── hacks
│ ├── README.md
│ ├── polyfill.js
│ ├── interceptador.js
│ ├── ie.js
│ └── chrome.js
├── lexml
│ ├── ArticulacaoInvalidaException.js
│ └── importarDeLexML.js
├── editor-articulacao.js
├── editor-articulacao-css-shadow.js
├── validacao
│ ├── ValidadorCaixaAlta.js
│ ├── ValidadorPontuacaoArtigoParagrafo.js
│ ├── ValidadorIniciarLetraMaiuscula.js
│ ├── ValidadorPontuacaoEnumeracao.js
│ ├── ProblemaValidacao.js
│ ├── ValidadorSentencaUnica.js
│ ├── ValidadorEnumeracaoElementos.js
│ ├── Validador.js
│ ├── ValidadorCitacao.js
│ └── ValidacaoController.js
├── eventos
│ ├── ArticulacaoChangeEvent.js
│ ├── ContextoArticulacaoAtualizadoEvent.js
│ ├── TransformacaoAutomaticaEvent.js
│ └── EventoInterno.js
├── transformacaoAutomatica
│ ├── transformacoes
│ │ ├── AoFecharAspas.js
│ │ ├── DoisPontos.js
│ │ ├── AoIniciarAspas.js
│ │ ├── PontoFinal.js
│ │ ├── RecuarComEnterEmDispositivoVazio.js
│ │ └── Transformacao.js
│ └── transformacaoAutomatica.js
├── editor-html.js
├── opcoesPadrao.js
├── util.js
├── ComponenteEdicao.js
├── ControleAlteracao.js
├── editor-articulacao-css.js
├── ContextoArticulacao.js
├── ClipboardController.js
└── editor-articulacao.d.ts
├── bower.json
├── .github
└── workflows
│ └── node.js.yml
├── package.json
├── webpack.config.js
├── empacotamento
├── karma.js
├── angular1.js
└── plain-js.js
├── protractor-conf.js
├── test
├── protractor
│ ├── validacao.spec.js
│ ├── utilitariosTeste.js
│ ├── teste.html
│ └── formatacaoDispositivos.spec.js
└── karma
│ ├── validacao.spec.js
│ ├── clipboardController.spec.js
│ ├── exportacaoParaLexML.spec.js
│ └── importacaoDeLexML.spec.js
├── karma.conf.js
├── Gruntfile.js
├── exemplo
└── exemplo.html
├── COPYING.LESSER
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .factorypath
3 | .project
4 | .settings
5 | /.vscode
6 | /build/*.js
7 | /build/*.map
8 | /coverage
9 | /node_modules
10 | debug.log
11 |
--------------------------------------------------------------------------------
/src/hacks/README.md:
--------------------------------------------------------------------------------
1 | # Hacks para navegadores
2 |
3 | Qualquer código que seja exclusivamente para tratar um comportamento específico
4 | de um determinado navegador deve ser externalizado, de forma a melhorar
5 | a legibilidade do código e manutenibilidade, de forma a permitir a
6 | validação dos hacks em versões novas dos navegadores. Tais códigos devem
7 | ser separados por navegador, de forma a agrupar qualquer intervenção
8 | específica para um navegador.
9 |
10 | # Polyfill
11 |
12 | Polyfill deve também ser implementado à parte, no arquivo `polyfill.js`.
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "silegismg-editor-articulacao",
3 | "description": "Biblioteca javasciprt de edição da articulação de uma norma ou projeto de lei, conforme formato especificado pelo LexML.",
4 | "main": "src/editor-articulacao.js",
5 | "authors": [
6 | "Assembleia Legislativa de Minas Gerais (ALMG)"
7 | ],
8 | "license": "LGPL",
9 | "keywords": [
10 | "silegismg",
11 | "lexml",
12 | "articulacao",
13 | "editor",
14 | "almg",
15 | "assembleia",
16 | "camara",
17 | "senado",
18 | "legislativa",
19 | "legislativo",
20 | "lexedit"
21 | ],
22 | "homepage": "",
23 | "ignore": [
24 | "**/.*",
25 | "node_modules",
26 | "bower_components",
27 | "test",
28 | "tests"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Teste de Unidade
5 |
6 | on:
7 | push:
8 | branches: [ master, develop ]
9 | pull_request:
10 | branches: [ master, develop ]
11 |
12 | jobs:
13 | test:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v2
18 | - name: Use Node.js
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: 18
22 | cache: 'npm'
23 | - run: npm ci
24 | - name: jshint
25 | run: npx grunt jshint
26 | - name: unit tests
27 | run: npx grunt karma:unit
28 |
29 |
--------------------------------------------------------------------------------
/src/lexml/ArticulacaoInvalidaException.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | class ArticulacaoInvalidaException {
19 | constructor(dispositivo, mensagem) {
20 | this.dispositivo = dispositivo;
21 | this.mensagem = mensagem;
22 | }
23 |
24 | get tipo() {
25 | return dispositivo.tagName;
26 | }
27 | }
28 |
29 | export default ArticulacaoInvalidaException;
--------------------------------------------------------------------------------
/src/editor-articulacao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import ComponenteEdicao from './ComponenteEdicao';
19 | import EditorArticulacaoController from './EditorArticulacaoController';
20 | import interpretadorArticulacao from './interpretadorArticulacao';
21 |
22 | export default {
23 | ComponenteEdicao: ComponenteEdicao,
24 | EditorArticulacaoController: EditorArticulacaoController,
25 | interpretadorArticulacao: interpretadorArticulacao
26 | };
27 |
28 | export { ComponenteEdicao, EditorArticulacaoController, interpretadorArticulacao };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "silegismg-editor-articulacao",
3 | "version": "1.0.10",
4 | "description": "Editor de Articulação",
5 | "main": "src/editor-articulacao.js",
6 | "types": "src/editor-articulacao.d.ts",
7 | "scripts": {
8 | "build": "npx grunt build",
9 | "test": "npx grunt test",
10 | "start": "npx grunt"
11 | },
12 | "keywords": [
13 | "silegismg",
14 | "lexml",
15 | "articulacao",
16 | "editor",
17 | "almg",
18 | "assembleia",
19 | "camara",
20 | "senado",
21 | "legislativa",
22 | "legislativo",
23 | "lexedit"
24 | ],
25 | "author": "Assembleia Legislativa de Minas Gerais (ALMG)",
26 | "license": "LGPL-3.0",
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/silegis-mg/editor-articulacao.git"
30 | },
31 | "devDependencies": {
32 | "grunt-contrib-jshint": "^3.2.0",
33 | "grunt-karma": "^4.0.2",
34 | "grunt-webpack": "^6.0.0",
35 | "jasmine": "^2.99.0",
36 | "jshint": "^2.13.4",
37 | "karma-chrome-launcher": "^3.2.0",
38 | "karma-firefox-launcher": "^2.1.2",
39 | "karma-jasmine": "^5.1.0",
40 | "karma-webpack": "^5.0.0",
41 | "matchdep": "^2.0.0",
42 | "webpack": "^5.88.2",
43 | "webpack-dev-server": "^4.15.1"
44 | },
45 | "dependencies": {}
46 | }
47 |
--------------------------------------------------------------------------------
/src/editor-articulacao-css-shadow.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 | const cssShadow = `
18 | :host {
19 | display: block;
20 | text-indent: 4ex;
21 | }
22 |
23 | :host([disabled]) {
24 | color: gray;
25 | }
26 |
27 | .silegismg-editor-articulacao p {
28 | text-indent: inherit !important;
29 | }
30 |
31 | .silegismg-editor-articulacao p:before {
32 | color: var(--rotulo-cor, currentColor);
33 | font-weight: var(--rotulo-peso, bolder);
34 | background: var(--rotulo-background, inherit);
35 | }
36 | `;
37 |
38 | export default cssShadow;
--------------------------------------------------------------------------------
/src/validacao/ValidadorCaixaAlta.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 |
20 | class ValidadorIniciarLetraMaiuscula extends Validador {
21 | constructor() {
22 | super(['artigo', 'paragrafo'], 'Artigos e parágrafos não devem ser escritos usando apenas caixa alta.');
23 | }
24 |
25 | validar(dispositivo) {
26 | var texto = dispositivo.textContent;
27 |
28 | return texto.length === 0 || texto.toUpperCase() !== texto;
29 | }
30 | }
31 |
32 | export default ValidadorIniciarLetraMaiuscula;
--------------------------------------------------------------------------------
/src/validacao/ValidadorPontuacaoArtigoParagrafo.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 |
20 | class ValidadorPontuacaoArtigoParagrafo extends Validador {
21 | constructor() {
22 | super(['artigo', 'paragrafo'], 'Artigos e parágrafos devem ser terminados com ponto final (.) ou dois pontos (:), sem espaço antes da pontuação.');
23 | }
24 |
25 | validar(dispositivo) {
26 | return /.*\S+[:.]$/.test(dispositivo.textContent.trim());
27 | }
28 | }
29 |
30 | export default ValidadorPontuacaoArtigoParagrafo;
--------------------------------------------------------------------------------
/src/validacao/ValidadorIniciarLetraMaiuscula.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 |
20 | class ValidadorIniciarLetraMaiuscula extends Validador {
21 | constructor() {
22 | super(['artigo', 'paragrafo'], 'Artigos e parágrafos devem ser iniciados com letra maiúscula.');
23 | }
24 |
25 | validar(dispositivo) {
26 | var texto = dispositivo.textContent.trim();
27 |
28 | return texto.charAt(0).toUpperCase() === texto.charAt(0);
29 | }
30 | }
31 |
32 | export default ValidadorIniciarLetraMaiuscula;
--------------------------------------------------------------------------------
/src/validacao/ValidadorPontuacaoEnumeracao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 |
20 | class ValidadorPontuacaoEnumeracao extends Validador {
21 | constructor() {
22 | super(['inciso', 'alinea', 'item'], 'Enumerações devem ser terminadas com ponto final (.), ponto e vírgula (;) ou dois pontos (:), sem espaço antes da pontuação.');
23 | }
24 |
25 | validar(dispositivo) {
26 | return /.*\S+(?:[;.:]|; ou|; e)$/.test(dispositivo.textContent.trim());
27 | }
28 | }
29 |
30 | export default ValidadorPontuacaoEnumeracao;
--------------------------------------------------------------------------------
/src/eventos/ArticulacaoChangeEvent.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import EventoInterno from './EventoInterno';
19 |
20 | /**
21 | * Evento de notificação de atualização do conteúdo da articulação.
22 | */
23 | class ArticulacaoChangeEvent extends EventoInterno {
24 | /**
25 | * Constrói o evento.
26 | *
27 | * @param {EditorArticulacaoController} editorArticulacaoCtrl
28 | */
29 | constructor(editorArticulacaoCtrl) {
30 | super('change', {
31 | bubbles: true,
32 | cancelable: false
33 | });
34 | }
35 | }
36 |
37 | export default ArticulacaoChangeEvent;
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 | const path = require('path');
18 |
19 | module.exports = function (empacotamento) {
20 | var entry = './empacotamento/' + empacotamento + '.js';
21 | var sufixo = empacotamento;
22 |
23 | return {
24 | entry: entry,
25 | output: {
26 | path: __dirname + '/build',
27 | filename: 'silegismg-editor-articulacao-' + sufixo + '.js'
28 | },
29 | mode: 'production',
30 | devServer: {
31 | static: {
32 | directory: path.join(__dirname, 'test/puppeteer')
33 | },
34 | port: 9000
35 | }
36 | };
37 | };
38 |
--------------------------------------------------------------------------------
/src/eventos/ContextoArticulacaoAtualizadoEvent.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import EventoInterno from './EventoInterno';
19 |
20 | /**
21 | * Evento de notificação de atualização do contexto de edição.
22 | * Útil para atualização da interface de usuário, habilitando ou
23 | * desabilitando botões.
24 | */
25 | class ContextoArticulacaoAtualizadoEvent extends EventoInterno {
26 | /**
27 | * Constrói o evento.
28 | *
29 | * @param {ContextoArticulacao} contexto
30 | */
31 | constructor(contexto) {
32 | super('contexto', {
33 | detail: contexto
34 | });
35 | }
36 | }
37 |
38 | export default ContextoArticulacaoAtualizadoEvent;
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacoes/AoFecharAspas.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { TransformacaoDoProximo } from './Transformacao';
19 |
20 | /**
21 | * Quando usuário encerra as aspas na continuação do caput de um artigo,
22 | * então cria-se um novo artigo.
23 | */
24 | class AoFecharAspas extends TransformacaoDoProximo {
25 | constructor() {
26 | super('"\n', '".\n');
27 | }
28 |
29 | get tipoTransformacao() {
30 | return 'AoFecharAspas';
31 | }
32 |
33 | proximoTipo(editor, ctrl, contexto) {
34 | return contexto.cursor.artigo && contexto.cursor.continuacao ? 'artigo' : null;
35 | }
36 | }
37 |
38 | export default AoFecharAspas;
--------------------------------------------------------------------------------
/src/validacao/ProblemaValidacao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Representa um problema que descreve a invalidação de um dispositivo.
20 | */
21 | class ProblemaValidacao {
22 | /**
23 | * Constrói o problema.
24 | *
25 | * @param {Element} dispositivo Dispositivo inválido.
26 | * @param {String} tipo Tipo do validador (nome da classe) que considerou o dispositivo inválido.
27 | * @param {String} descricao Descrição para o problema.
28 | */
29 | constructor(dispositivo, tipo, descricao) {
30 | this.dispositivo = dispositivo;
31 | this.tipo = tipo;
32 | this.descricao = descricao;
33 | }
34 | }
35 |
36 | export default ProblemaValidacao;
--------------------------------------------------------------------------------
/empacotamento/karma.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import importarDeLexML from '../src/lexml/importarDeLexML';
19 | import exportarParaLexML from '../src/lexml/exportarParaLexML';
20 | import interpretadorArticulacao from '../src/interpretadorArticulacao';
21 | import { transformarTextoPuro, transformar } from '../src/ClipboardController';
22 | import ValidacaoController from '../src/validacao/ValidacaoController';
23 |
24 | window.importarDeLexML = importarDeLexML;
25 | window.exportarParaLexML = exportarParaLexML;
26 | window.interpretadorArticulacao = interpretadorArticulacao;
27 | window.clipboardControllerModule = {
28 | interpretarTextoPuro: transformarTextoPuro,
29 | transformar: transformar
30 | };
31 | window.ValidacaoController = ValidacaoController;
--------------------------------------------------------------------------------
/src/validacao/ValidadorSentencaUnica.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 |
20 | class ValidadorSentencaUnica extends Validador {
21 | constructor() {
22 | super(['artigo', 'paragrafo', 'inciso', 'alinea', 'item'], 'Dispositivos devem conter uma única sentença.');
23 | }
24 |
25 | validar(dispositivo) {
26 | let texto = dispositivo.textContent.trim();
27 | let regexp = /[.;:]\s*(\S)/g;
28 | let m;
29 | let valido = true;
30 |
31 | for (let m = regexp.exec(texto); m && valido; m = regexp.exec(texto)) {
32 | valido = m[1].toLowerCase() === m[1];
33 | }
34 |
35 | return valido;
36 | }
37 | }
38 |
39 | export default ValidadorSentencaUnica;
--------------------------------------------------------------------------------
/src/validacao/ValidadorEnumeracaoElementos.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 | import { encontrarDispositivoAnteriorDoTipo, encontrarDispositivoPosteriorDoTipo, getTiposSuperiores } from '../util';
20 |
21 | class ValidadorEnumeracaoElementos extends Validador {
22 | constructor() {
23 | super(['inciso', 'alinea', 'item'], 'Enumerações devem possuir mais de um elemento.');
24 | }
25 |
26 | validar(dispositivo) {
27 | var tipo = dispositivo.getAttribute('data-tipo');
28 | var superiores = getTiposSuperiores(tipo);
29 |
30 | return !!encontrarDispositivoAnteriorDoTipo(dispositivo, [tipo], superiores) || !!encontrarDispositivoPosteriorDoTipo(dispositivo, [tipo], superiores);
31 | }
32 | }
33 |
34 | export default ValidadorEnumeracaoElementos;
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacoes/DoisPontos.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { TransformacaoDoProximo } from './Transformacao';
19 |
20 | /**
21 | * Ao terminar um dispositivo com dois pontos (:), assume-se
22 | * que será feita uma enumeração e, portanto, transforma
23 | * o próximo dispositivo em inciso, alínea ou item, conforme
24 | * contexto.
25 | */
26 | class DoisPontos extends TransformacaoDoProximo {
27 | constructor() {
28 | super(':\n');
29 | }
30 |
31 | get tipoTransformacao() {
32 | return 'DoisPontos';
33 | }
34 |
35 | proximoTipo(editor, ctrl, contexto) {
36 | return {
37 | artigo: 'inciso',
38 | inciso: 'alinea',
39 | alinea: 'item',
40 | paragrafo: 'inciso'
41 | }[contexto.cursor.tipo];
42 | }
43 | }
44 |
45 | export default DoisPontos;
--------------------------------------------------------------------------------
/src/eventos/TransformacaoAutomaticaEvent.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import EventoInterno from './EventoInterno';
19 |
20 | /**
21 | * Evento de notificação de transformação automática ocorrida
22 | * em dispositivo.
23 | */
24 | class TransformacaoAutomaticaEvent extends EventoInterno {
25 | /**
26 | * Constrói o evento.
27 | *
28 | * @param {EditorArticulacaoController} editorArticulacaoCtrl
29 | */
30 | constructor(editorArticulacaoCtrl, tipoAnterior, novoTipo, transformacao) {
31 | super('transformacao', {
32 | detail: {
33 | automatica: true,
34 | tipoAnterior: tipoAnterior,
35 | novoTipo: novoTipo,
36 | transformacao: transformacao,
37 | editorArticulacaoCtrl
38 | }
39 | });
40 | }
41 | }
42 |
43 | export default TransformacaoAutomaticaEvent;
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacoes/AoIniciarAspas.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { Transformacao } from './Transformacao';
19 | import TransformacaoAutomaticaEvent from '../../eventos/TransformacaoAutomaticaEvent';
20 |
21 | /**
22 | * Quando usuário cria um novo artigo e o inicia com aspas,
23 | * este é transformado em continuação do caput do artigo.
24 | */
25 | class AoIniciarAspas extends Transformacao {
26 | constructor() {
27 | super('\n"');
28 | }
29 |
30 | get tipoTransformacao() {
31 | return 'AoIniciarAspas';
32 | }
33 |
34 | transformar(editor, ctrl, contexto) {
35 | if (contexto.cursor.tipoAnterior === 'artigo') {
36 | ctrl.alterarTipoDispositivoSelecionado('continuacao');
37 | ctrl.dispatchEvent(new TransformacaoAutomaticaEvent(ctrl, 'artigo', 'continuacao', this.tipoTransformacao));
38 | }
39 |
40 | return null;
41 | }
42 | }
43 |
44 | export default AoIniciarAspas;
--------------------------------------------------------------------------------
/protractor-conf.js:
--------------------------------------------------------------------------------
1 |
2 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
3 | *
4 | * This file is part of Editor-Articulacao.
5 | *
6 | * Editor-Articulacao is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, version 3.
9 | *
10 | * Editor-Articulacao is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with Editor-Articulacao. If not, see .
17 | */
18 |
19 | exports.config = {
20 | framework: 'jasmine',
21 | specs: ['test/protractor/*.spec.js'],
22 | directConnect: true,
23 | multiCapabilities: [
24 | {
25 | browserName: 'chrome',
26 | chromeOptions: {
27 | args: ['--no-sandbox', '--headless']
28 | }
29 | }/*, { https://github.com/angular/protractor/issues/4177
30 | 'browserName': 'firefox'
31 | }*/
32 | ],
33 | localSeleniumStandaloneOpts: {
34 | seleniumArgs: ['-browserTimeout=60']
35 | },
36 | baseUrl: 'http://localhost:8075/',
37 | onPrepare: function () {
38 | browser.ignoreSynchronization = true;
39 | browser.manage().timeouts().pageLoadTimeout(40000);
40 | browser.manage().timeouts().implicitlyWait(25000);
41 |
42 | browser.driver.manage().window().setSize(1024, 768);
43 | browser.driver.get('http://localhost:8075/protractor/teste.html');
44 | },
45 | maxSessions: 1
46 | };
47 |
--------------------------------------------------------------------------------
/test/protractor/validacao.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | const { $describe } = require('./utilitariosTeste');
19 |
20 | $describe('Formatação do editor de articulação', it => {
21 | it('Deve validar todos os dispositivos ao colar', () => {
22 | browser.executeScript(function () {
23 | const selecao = this.getSelection();
24 | selecao.removeAllRanges();
25 |
26 | const range = document.createRange();
27 | range.selectNodeContents(document.querySelector('[contenteditable]').firstElementChild);
28 | selecao.addRange(range);
29 |
30 | let dataTrans = new DataTransfer();
31 |
32 | dataTrans.items.add('Primeira linha\nSegunda linha\nTerceira linha\nQuarta linha\nQuinta linha', 'text/plain');
33 |
34 | document.querySelector('[contenteditable]').dispatchEvent(new ClipboardEvent('paste', {
35 | clipboardData: dataTrans
36 | }));
37 | });
38 |
39 | expect(element.all(by.css('p[data-invalido]')).count()).toBe(4);
40 | })
41 | });
--------------------------------------------------------------------------------
/src/validacao/Validador.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Classe abstrata para validar dispositivos.
20 | */
21 | class Validador {
22 | /**
23 | * Constrói o validador.
24 | *
25 | * @param {String[]} tipos Tipos de dispositivos que serão validados.
26 | * @param {String} descricao Descrição da validação.
27 | */
28 | constructor(tipos, descricao) {
29 | this.tipos = tipos instanceof Array ? new Set(tipos) : new Set([tipos]);
30 | this.descricao = descricao;
31 | }
32 |
33 | /**
34 | * Verifiac se o validador se aplica ao dispositivo.
35 | *
36 | * @param {Element} dispositivo Dispositivo a ser validado.
37 | */
38 | aplicaSeA(dispositivo) {
39 | return this.tipos.has(dispositivo.getAttribute('data-tipo'));
40 | }
41 |
42 | /**
43 | * Realiza a validação do dispositivo.
44 | *
45 | * @param {Element} dispositivo Dispositivo a ser validado.
46 | * @returns {Boolean} Verdadeiro se o dispositivo estiver válido.
47 | */
48 | validar(dispositivo) {
49 | throw 'Não implementado.';
50 | }
51 | }
52 |
53 | export default Validador;
--------------------------------------------------------------------------------
/src/eventos/EventoInterno.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Representa um evento interno, a ser disparado pelo EditorArticulacaoController.dispatchEvent.
20 | * Como o IE 11 possui problemas para herdar classes de CustomEvent, foi criada esta classe
21 | * intermediária para gerar o CustomEvent.
22 | */
23 | class EventoInterno {
24 | constructor(nome, dados) {
25 | this.nome = nome;
26 | this.dados = dados;
27 | }
28 |
29 | getCustomEvent() {
30 | return new CustomEvent(this.nome, this.dados);
31 | }
32 | }
33 |
34 | // Polyfill de https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
35 | (function () {
36 | if (typeof window.CustomEvent === "function") return false;
37 |
38 | function CustomEvent(event, params) {
39 | params = params || { bubbles: false, cancelable: false, detail: undefined };
40 | var evt = document.createEvent('CustomEvent');
41 | evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
42 | return evt;
43 | }
44 |
45 | CustomEvent.prototype = window.Event.prototype;
46 |
47 | window.CustomEvent = CustomEvent;
48 | })();
49 |
50 | export default EventoInterno;
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacoes/PontoFinal.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { TransformacaoDoProximo } from './Transformacao';
19 |
20 | /**
21 | * Ao finalizar o dispositivo com ponto final (.) e o usuário
22 | * estiver no contexto de uma enumeração, então encerra-se ela,
23 | * transformando o próximo dispositivo em um nível anterior
24 | * (ex.: alínea para inciso).
25 | */
26 | class PontoFinal extends TransformacaoDoProximo {
27 | constructor() {
28 | super('.\n');
29 | }
30 |
31 | get tipoTransformacao() {
32 | return 'PontoFinal';
33 | }
34 |
35 | proximoTipo(editor, ctrl, contexto) {
36 | return {
37 | get inciso() {
38 | let dispositivo = contexto.cursor.dispositivo;
39 | let tipo;
40 |
41 | do {
42 | dispositivo = dispositivo.previousElementSibling;
43 | tipo = dispositivo.getAttribute('data-tipo');
44 | } while (tipo !== 'artigo' && tipo !== 'paragrafo');
45 |
46 | return tipo;
47 | },
48 | alinea: 'inciso',
49 | item: 'alinea'
50 | }[contexto.cursor.tipo];
51 | }
52 | }
53 |
54 | export default PontoFinal;
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | let webpackConfig = require("./webpack.config.js")('karma', true);
19 |
20 | module.exports = function (config) {
21 | config.set({
22 | files: ['empacotamento/karma.js', 'test/karma/**/*.js'],
23 | frameworks: ['jasmine'],
24 | preprocessors: {
25 | 'empacotamento/karma.js': ['webpack']
26 | },
27 | webpack: webpackConfig,
28 | port: 11111,
29 | colors: true,
30 | logLevel: config.LOG_WARN,
31 | autoWatch: true,
32 | browsers: ['ChromeNoSandbox', 'FirefoxHeadless'],
33 | customLaunchers: { // https://github.com/karma-runner/karma-chrome-launcher/issues/73
34 | ChromeNoSandbox: {
35 | base: 'ChromeHeadless',
36 | flags: ['--no-sandbox', '--disable-extensions']
37 | },
38 | FirefoxHeadless: {
39 | base: 'Firefox',
40 | flags: [ '-headless' ],
41 | }
42 | },
43 | singleRun: false,
44 | browserNoActivityTimeout: 300000,
45 | browserDisconnectTimeout: 300000,
46 | browserConsoleLogOptions: {
47 | level: "error",
48 | format: "%b %T: %m",
49 | terminal: true
50 | },
51 | browserDisconnectTolerance: 5,
52 | captureTimeout: 120000,
53 | concurrency: 1
54 | });
55 | };
56 |
--------------------------------------------------------------------------------
/test/protractor/utilitariosTeste.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | module.exports = { $describe, escrever }
19 |
20 | function $describe(texto, fn) {
21 | describe(texto, function () {
22 | var editor = element(by.id('articulacao'));
23 | var testes = 0, totalTestes = 0;
24 |
25 | function $it(descricao, funcao) {
26 | it(descricao, function () {
27 | testes++;
28 |
29 | browser.executeScript(function (texto) {
30 | titulo.textContent = texto;
31 | }, `Teste ${testes}/${totalTestes}: ${descricao}`);
32 |
33 | funcao.apply(this, arguments);
34 | });
35 |
36 | totalTestes++;
37 | }
38 |
39 | beforeEach(function () {
40 | browser.actions()
41 | .mouseMove(editor)
42 | .click()
43 | .perform();
44 | });
45 |
46 | afterEach(function () {
47 | browser.executeScript(function () {
48 | ctrl.lexml = '';
49 | });
50 | });
51 |
52 | fn($it);
53 | });
54 | }
55 | function escrever(buffer, rapido) {
56 | for (let i = 0; i < buffer.length; i++) {
57 | browser.actions().sendKeys(buffer[i]).perform();
58 | browser.sleep(25);
59 | }
60 |
61 | if (!rapido) {
62 | browser.sleep(1000);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/empacotamento/angular1.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 |
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import EditorArticulacaoController from '../src/EditorArticulacaoController';
19 | import interpretadorArticulacao from '../src/interpretadorArticulacao';
20 |
21 | window.angular.module('silegismg-editor-articulacao', [])
22 | .directive('silegismgEditorArticulacaoConteudo', editorArticulacaoConteudoDirective)
23 | .service('silegismgInterpretadorArticulacaoService', interpretadorArticulacaoService);
24 |
25 | function editorArticulacaoConteudoDirective() {
26 | return {
27 | restrict: 'EAC',
28 | require: 'ngModel',
29 | scope: {
30 | opcoes: ''
31 | },
32 | link: function(scope, element, attrs, ngModel) {
33 | ngModel.$render = () => {
34 | scope.ctrl.lexml = ngModel.$viewValue;
35 | };
36 |
37 | element[0].addEventListener('change', () => {
38 | try {
39 | ngModel.$setViewValue(scope.ctrl.lexml.outerHTML, 'change');
40 | ngModel.$setValidity('lexml', true);
41 | } catch (e) {
42 | ngModel.$setValidity('lexml', false);
43 | throw e;
44 | }
45 | });
46 |
47 | scope.$on('$destroy', () => scope.ctrl.desregistrar());
48 | },
49 | controller: ['$element', '$scope', ($element, $scope) => new EditorArticulacaoController($element[0], $scope.opcoes)],
50 | controllerAs: 'ctrl'
51 | };
52 | }
53 |
54 | function interpretadorArticulacaoService() {
55 | return interpretadorArticulacao;
56 | }
--------------------------------------------------------------------------------
/test/karma/validacao.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | describe('Validação', function() {
19 | 'use strict';
20 |
21 | var ctrl = new window.ValidacaoController({});
22 |
23 | function criarDispositivo(tipo, texto) {
24 | var dispositivo = document.createElement('p');
25 |
26 | dispositivo.setAttribute('data-tipo', tipo);
27 | dispositivo.textContent = texto;
28 |
29 | return dispositivo;
30 | }
31 |
32 | function testar(tipo, texto/*, ...erros*/) {
33 | var dispositivo = criarDispositivo(tipo, texto);
34 | var validacao = ctrl.validar(dispositivo);
35 | var erros = [];
36 |
37 | Array.prototype.push.apply(erros, arguments);
38 |
39 | erros.splice(0, 2);
40 |
41 | expect(validacao.length).toBe(erros.length);
42 |
43 | for (let i = 0; i < validacao.length; i++) {
44 | expect(validacao[i].tipo).toBe(erros[i]);
45 | }
46 | }
47 |
48 | describe('Validador de sentença única', function() {
49 | it ('Único período deve ser válido.', function() {
50 | testar('artigo', 'Esta é uma sentença única.')
51 | });
52 |
53 | it ('Abreviação deve ser válida.', function() {
54 | testar('artigo', 'Esta é um teste de citação do art. 5º da constituição.')
55 | });
56 |
57 | it ('Dois períodos devem ser inválidos.', function() {
58 | testar('artigo', 'Este é um teste. Este é outro.', 'sentencaUnica');
59 | });
60 |
61 | it ('Dois períodos com abreviação no primeiro deve tornar artigo inválido.', function() {
62 | testar('artigo', 'Este é um teste de citação do art. 5º da constituição. Este é outro.', 'sentencaUnica');
63 | });
64 | });
65 | });
--------------------------------------------------------------------------------
/src/validacao/ValidadorCitacao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Validador from './Validador';
19 | import { encontrarDispositivoAnteriorDoTipo, encontrarDispositivoPosteriorDoTipo } from '../util';
20 |
21 | class ValidadorCitacao extends Validador {
22 | constructor() {
23 | super('continuacao', 'Citações devem estar entre aspas e terminar com ponto final (.).');
24 | }
25 |
26 | validar(dispositivo) {
27 | let texto = dispositivo.textContent.trim();
28 | let inicio = seNulo(encontrarDispositivoAnteriorDoTipo(dispositivo, ['artigo']), posterior => posterior.nextElementSibling, dispositivo);
29 |
30 | if (dispositivo === inicio) {
31 | return /^["“]/.test(texto);
32 | }
33 |
34 | let termino = seNulo(encontrarDispositivoPosteriorDoTipo(dispositivo, ['continuacao', 'artigo', 'paragrafo', 'inciso']),
35 | posterior => posterior.getAttribute('data-tipo') === 'continuacao' ? posterior : posterior.previousElementSibling,
36 | dispositivo);
37 |
38 | if (dispositivo === termino) {
39 | return /.+[”"]\.$/.test(texto);
40 | }
41 |
42 | // Se estamos no meio da citação, não devem haver aspas.
43 | return !/[“”"]/.test(texto);
44 | }
45 | }
46 |
47 | /**
48 | * Se o objeto não estiver nulo, então executa uma função consumidora deste objeto.
49 | * Caso o contrário, retorna um outro valor.
50 | *
51 | * @param {*} obj Objeto a ser verificado.
52 | * @param {*} fnSeNaoNulo Função cujo resultado será retornado caso o objeto seja diferente de nulo.
53 | * @param {*} seNulo Valor retornado caso seja nulo.
54 | */
55 | function seNulo(obj, fnSeNaoNulo, seNulo) {
56 | return obj ? fnSeNaoNulo(obj) : seNulo;
57 | }
58 |
59 | export default ValidadorCitacao;
--------------------------------------------------------------------------------
/empacotamento/plain-js.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /* Cria uma função global, chamada silegismgEditorArticulacao,
19 | * que permite transformar um DIV em um editor de articulação.
20 | * A função cria e retorna uma nova instância de EditorArticulacaoController.
21 | */
22 |
23 | import ComponenteEdicao from '../src/ComponenteEdicao';
24 | import EditorArticulacaoController from '../src/EditorArticulacaoController';
25 | import interpretadorArticulacao from '../src/interpretadorArticulacao';
26 |
27 | /**
28 | * Prepara um elemento do DOM como um editor de articulação.
29 | *
30 | * @param {Element} elemento
31 | */
32 | function criarControllerEditorArticulacao(elemento, opcoes) {
33 | elemento.ctrlArticulacao = new EditorArticulacaoController(elemento, opcoes);
34 |
35 | Object.defineProperty(elemento, 'lexml', {
36 | get: function () {
37 | return this.ctrlArticulacao.lexml;
38 | },
39 | set: function (valor) {
40 | this.ctrlArticulacao.lexml = valor;
41 | }
42 | });
43 |
44 | return elemento.ctrlArticulacao;
45 | }
46 |
47 | function prepararEditorArticulacaoCompleto(elemento, opcoes) {
48 | elemento.componenteEdicao = new ComponenteEdicao(elemento, opcoes);
49 |
50 | Object.defineProperty(elemento, 'lexml', {
51 | get: function () {
52 | return this.componenteEdicao.ctrl.lexml;
53 | },
54 | set: function (valor) {
55 | this.componenteEdicao.ctrl.lexml = valor;
56 | }
57 | });
58 |
59 | return elemento.componenteEdicao;
60 | }
61 |
62 | window.silegismgEditorArticulacao = prepararEditorArticulacaoCompleto;
63 | window.silegismgEditorArticulacaoController = criarControllerEditorArticulacao;
64 | window.silegismgInterpretadorArticulacao = interpretadorArticulacao;
--------------------------------------------------------------------------------
/test/protractor/teste.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacoes/RecuarComEnterEmDispositivoVazio.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import Transformacao from './Transformacao';
19 |
20 | /**
21 | * Quando usuário der um enter em uma linha vazia, então
22 | * transforma-se o dispositivo para um nível anterior
23 | * (ex.: de alínea para inciso).
24 | */
25 | class RecuarComEnterEmDispositivoVazio extends Transformacao {
26 | constructor() {
27 | super('\n');
28 | }
29 |
30 | get tipoTransformacao() {
31 | return 'RecuarComEnterEmDispositivoVazio';
32 | }
33 |
34 | /**
35 | * Efetua a transformação.
36 | *
37 | * @param {Element} elementoEditor Elemento em que está o editor de articulação.
38 | * @param {EditorArticulacaoController} ctrl Controlador do editor.
39 | * @param {ContextoArticulacao} contexto Contexto atual.
40 | * @param {String} sequencia Sequência que disparou a transformação.
41 | * @param {KeyboardEvent} event Evento do teclado.
42 | */
43 | transformar(elementoEditor, ctrl, contexto, sequencia, event) {
44 | if (contexto.cursor.dispositivo.textContent.trim().length === 0) {
45 | let novoTipo = {
46 | item: 'alinea',
47 | alinea: 'inciso',
48 | get inciso() {
49 | for (let dispositivo = contexto.cursor.dispositivoAnterior; dispositivo; dispositivo = dispositivo.previousElementSibling) {
50 | let tipo = dispositivo.getAttribute('data-tipo');
51 |
52 | if (tipo === 'artigo' || tipo === 'paragrafo') {
53 | return tipo;
54 | }
55 | }
56 |
57 | return 'artigo';
58 | }
59 | }[contexto.cursor.tipo] || 'artigo';
60 |
61 | ctrl.alterarTipoDispositivoSelecionado(novoTipo);
62 | event.preventDefault();
63 | }
64 | }
65 | }
66 |
67 | export default RecuarComEnterEmDispositivoVazio;
--------------------------------------------------------------------------------
/src/hacks/polyfill.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | function polyfill() {
19 | // IE 11 não tem Object.assing
20 |
21 | // Object polyfill from https://developer.mozilla.org/en-US/docs/Glossary/Polyfill
22 | if (!Object.assign) {
23 | Object.defineProperty(Object, 'assign', {
24 | enumerable: false,
25 | configurable: true,
26 | writable: true,
27 | value: function (target) {
28 | 'use strict';
29 | if (target === undefined || target === null) {
30 | throw new TypeError('Cannot convert first argument to object');
31 | }
32 |
33 | var to = Object(target);
34 | for (var i = 1; i < arguments.length; i++) {
35 | var nextSource = arguments[i];
36 | if (nextSource === undefined || nextSource === null) {
37 | continue;
38 | }
39 | nextSource = Object(nextSource);
40 |
41 | var keysArray = Object.keys(Object(nextSource));
42 | for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
43 | var nextKey = keysArray[nextIndex];
44 | var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
45 | if (desc !== undefined && desc.enumerable) {
46 | to[nextKey] = nextSource[nextKey];
47 | }
48 | }
49 | }
50 | return to;
51 | }
52 | });
53 | }
54 |
55 | // IE 11 não tem lastElementChild no DocumentFragment
56 | if (!('lastElementChild' in DocumentFragment.prototype)) {
57 | Object.defineProperty(DocumentFragment.prototype, 'lastElementChild', {
58 | get: function () {
59 | var ultimo = this.lastChild;
60 |
61 | while (ultimo && ultimo.nodeType !== Node.ELEMENT_NODE) {
62 | ultimo = ultimo.previousSibling;
63 | }
64 |
65 | return ultimo;
66 | }
67 | });
68 | }
69 | }
70 |
71 | export default polyfill;
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | module.exports = function (grunt) {
19 | require("matchdep").filterAll("grunt-*").forEach(grunt.loadNpmTasks);
20 | var webpackConfig = require("./webpack.config.js");
21 |
22 | grunt.initConfig({
23 | karma: {
24 | unit: {
25 | configFile: 'karma.conf.js',
26 | background: false,
27 | singleRun: true
28 | },
29 | debug: {
30 | configFile: 'karma.conf.js',
31 | background: false,
32 | singleRun: false
33 | }
34 | },
35 | webpack: {
36 | buildPlain: webpackConfig('plain-js'),
37 | buildAngular1: webpackConfig('angular1'),
38 | buildPlainPolyfill: webpackConfig('plain-js', false, true),
39 | buildAngular1Polyfill: webpackConfig('angular1', false, true),
40 | "build-dev": webpackConfig('plain-js', true)
41 | },
42 | "webpack-dev-server": {
43 | options: webpackConfig('plain-js', true),
44 | start: {
45 | }
46 | },
47 | watch: {
48 | app: {
49 | files: ["src/**/*"],
50 | tasks: ["webpack:build-dev", "jshint", "karma:unit"],
51 | options: {
52 | spawn: false,
53 | }
54 | }
55 | },
56 | jshint: {
57 | all: ['*.js', 'src/**/*.js', 'empacotamento/**/*.js'],
58 | options: {
59 | browser: true,
60 | esversion: 6
61 | }
62 | }
63 | });
64 |
65 | // The development server (the recommended option for development)
66 | grunt.registerTask("default", ["webpack-dev-server:start"]);
67 |
68 | // Build and watch cycle (another option for development)
69 | // Advantage: No server required, can run app from filesystem
70 | // Disadvantage: Requests are not blocked until bundle is available,
71 | // can serve an old app on too fast refresh
72 | grunt.registerTask("dev", ["jshint", "webpack:build-dev", "watch:app"]);
73 |
74 | // Production build
75 | grunt.registerTask("build", ["webpack:buildPlain", "webpack:buildAngular1", "webpack:buildPlainPolyfill", "webpack:buildAngular1Polyfill"]);
76 | grunt.registerTask("build-plain", ["webpack:buildPlain"]);
77 | grunt.registerTask("build-plain-polyfill", ["webpack:buildPlainPolyfill"]);
78 | grunt.registerTask("build-angular1", ["webpack:buildAngular1"]);
79 |
80 | grunt.registerTask('test', ['jshint', 'karma:unit']);
81 |
82 | grunt.registerTask('debug', ['karma:debug']);
83 | };
84 |
--------------------------------------------------------------------------------
/src/hacks/interceptador.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Intercepta um método após sua execução.
20 | *
21 | * @param {Object} objeto Objeto ou classe a ser interceptada. Se o tipo for uma classe, a interceptação ocorrerá sobre o prototype.
22 | * @param {String} metodo Método a ser interceptado.
23 | * @param {function} interceptador Função interceptadora, que receberá o objeto, o valor retornado e os argumentos.
24 | */
25 | function interceptar(objeto, metodo, interceptador) {
26 | if (!objeto) throw 'Objeto não fornecido.';
27 | if (!metodo) throw 'Método não fornecido.';
28 | if (!interceptador) throw 'Interceptador não fornecido.';
29 |
30 | if (typeof objeto === 'function') {
31 | interceptar(objeto.prototype, metodo, interceptador);
32 | } else {
33 | let metodoOriginal = objeto[metodo];
34 |
35 | if (!metodoOriginal) {
36 | throw 'Método não encontrado: ' + metodo;
37 | }
38 |
39 | objeto[metodo] = function() {
40 | return interceptador(this, metodoOriginal, arguments);
41 | };
42 | }
43 | }
44 |
45 | /**
46 | * Intercepta um método após sua execução.
47 | *
48 | * @param {Object} objeto Objeto ou classe a ser interceptada. Se o tipo for uma classe, a interceptação ocorrerá sobre o prototype.
49 | * @param {String} metodo Método a ser interceptado.
50 | * @param {function} interceptador Função interceptadora, que receberá o objeto, o valor retornado e os argumentos.
51 | */
52 | function interceptarApos(objeto, metodo, interceptador) {
53 | if (!objeto) throw 'Objeto não fornecido.';
54 | if (!metodo) throw 'Método não fornecido.';
55 | if (!interceptador) throw 'Interceptador não fornecido.';
56 |
57 | if (typeof objeto === 'function') {
58 | interceptarApos(objeto.prototype, metodo, interceptador);
59 | } else {
60 | let metodoOriginal = objeto[metodo];
61 |
62 | if (!metodoOriginal) {
63 | throw 'Método não encontrado: ' + metodo;
64 | }
65 |
66 | objeto[metodo] = function() {
67 | var resultado = metodoOriginal.apply(this, arguments);
68 |
69 | return interceptador(this, resultado, arguments);
70 | };
71 | }
72 | }
73 |
74 | export { interceptar, interceptarApos };
75 | export default {};
--------------------------------------------------------------------------------
/src/hacks/ie.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { interceptar, interceptarApos } from './interceptador';
19 |
20 | function hackIE(controller) {
21 | controller.limpar = function() {
22 | // IE 11 precisa de conteúdo dentro do elemento para que o mesmo ganhe foco e seja editável
23 | this._elemento.innerHTML = '
';
24 | };
25 |
26 | // Toggle do classList não funciona no IE 11
27 | // https://connect.microsoft.com/IE/Feedback/details/878564/
28 | interceptar(DOMTokenList.prototype, 'toggle', (classList, metodo, argumentos) => {
29 | if (argumentos.length === 1) {
30 | metodo.apply(classList, argumentos);
31 | } else if (argumentos[1]) {
32 | classList.add(argumentos[0]);
33 | } else {
34 | classList.remove(argumentos[0]);
35 | }
36 | });
37 |
38 | // Construtor do Set não recebe parâmetros no IE 11
39 | if (new Set([1, 2]).size === 0) {
40 | substituirSet();
41 | }
42 |
43 | // Garante sempre que o dispositivo tenha algum conteúdo, para evitar que o IE tenha um parágrafo não editável.
44 | interceptarApos(controller, '_normalizarDispositivo', function(controller, resultado, argumentos) {
45 | let dispositivo = argumentos[0];
46 |
47 | if (dispositivo && !dispositivo.firstElementChild && dispositivo.textContent === '') {
48 | dispositivo.innerHTML = ' ';
49 | }
50 | });
51 | }
52 |
53 | function substituirSet() {
54 | function substituirMetodo(metodo) {
55 | if (typeof SetNativo.prototype[metodo] === 'function') {
56 | window.Set.prototype[metodo] = function() { return this.$set[metodo].apply(this.$set, arguments); };
57 | }
58 | }
59 |
60 | let SetNativo = window.Set;
61 |
62 | window.Set = function(iteravel) {
63 | this.$set = new SetNativo();
64 |
65 | if (iteravel) {
66 | for (let i in iteravel) {
67 | this.$set.add(iteravel[i]);
68 | }
69 | }
70 | };
71 |
72 | let metodos = ['add', 'clear', 'delete', 'entries', 'forEach', 'has', 'keys', 'values', '@@iterator'];
73 |
74 | for (let i = 0; i < metodos.length; i++) {
75 | substituirMetodo(metodos[i]);
76 | }
77 | }
78 |
79 | export default hackIE;
--------------------------------------------------------------------------------
/src/editor-html.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 | const editorHtml = `
18 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | `;
79 |
80 | export default editorHtml;
--------------------------------------------------------------------------------
/src/opcoesPadrao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Definição padrão das opções do editor de articulação.
20 | */
21 | export default {
22 | /**
23 | * Determina o escapamento de caracteres de código alto unicode durante a exportação
24 | * para lexmlString.
25 | */
26 | escaparXML: false,
27 |
28 | /**
29 | * Determina o sufixo para os rótulos dos dispositivos.
30 | */
31 | rotulo: {
32 | separadorArtigo: ' \u2013',
33 | separadorArtigoSemOrdinal: ' \u2013',
34 | separadorParagrafo: ' \u2013',
35 | separadorParagrafoSemOrdinal: ' \u2013',
36 | separadorParagrafoUnico: ' \u2013',
37 | separadorInciso: ' \u2013',
38 | separadorAlinea: ')',
39 | separadorItem: ')'
40 | },
41 |
42 | /**
43 | * Determina se deve adotar o Shadow DOM, se suportado pelo navegador.
44 | */
45 | shadowDOM: false,
46 |
47 | /**
48 | * Determina se deve permitir a edição, ou se o componente será somente para leitura.
49 | */
50 | somenteLeitura: false,
51 |
52 | /**
53 | * Determina se o editor de articulação deve aplicar transformação automática.
54 | */
55 | transformacaoAutomatica: true,
56 |
57 | /**
58 | * Determina se deve validar o conteúdo atribuído ao componente.
59 | */
60 | validarAoAtribuir: true,
61 |
62 | /**
63 | * Determina as validações que devem ocorrer.
64 | * Nenhuma validação ocorre se o editor for somente para leitura.
65 | */
66 | validacao: {
67 | /**
68 | * Determina se deve validar o uso de caixa alta.
69 | */
70 | caixaAlta: true,
71 |
72 | /**
73 | * Determina se deve validar o uso de aspas em citações.
74 | */
75 | citacao: true,
76 |
77 | /**
78 | * Determina se deve validar a presença de múltiplos elementos em uma enumeração.
79 | */
80 | enumeracaoElementos: true,
81 |
82 | /**
83 | * Determina se deve validar o uso de letra maiúscula no caput do artigo e em parágrafos.
84 | */
85 | inicialMaiuscula: true,
86 |
87 | /**
88 | * Determina se deve validar as pontuações.
89 | */
90 | pontuacao: true,
91 |
92 | /**
93 | * Determina se deve validar pontuação de enumeração.
94 | */
95 | pontuacaoEnumeracao: true,
96 |
97 | /**
98 | * Determina se deve exigir sentença única no dispositivo.
99 | */
100 | sentencaUnica: true
101 | }
102 | };
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Obtém o dispositivo anterior, de determinado tipo.
20 | *
21 | * @param {Element} dispositivo
22 | * @param {String[]} pontosParada Tipos de dispositivos desejados.
23 | * @param {String[]} pontosInterrupcao Tipos de dispositivos que, se encontrados, interromperá a obtenção, retornando nulo.
24 | * @returns {Element} Dispositivo, se encontrado, ou nulo.
25 | */
26 | function encontrarDispositivoAnteriorDoTipo(dispositivo, pontosParada, pontosInterrupcao) {
27 | while (!dispositivo.hasAttribute('data-tipo')) {
28 | dispositivo = dispositivo.parentNode;
29 | }
30 |
31 | let setParada = new Set(pontosParada);
32 | let setInterrupcao = new Set(pontosInterrupcao);
33 |
34 | for (let prev = dispositivo.previousElementSibling; prev; prev = prev.previousElementSibling) {
35 | let tipoAnterior = prev.getAttribute('data-tipo');
36 |
37 | if (setParada.has(tipoAnterior)) {
38 | return prev;
39 | } else if (setInterrupcao.has(tipoAnterior)) {
40 | return null;
41 | }
42 | }
43 |
44 | return null;
45 | }
46 |
47 | /**
48 | * Obtém o dispositivo posterior, de determinado tipo.
49 | *
50 | * @param {Element} dispositivo
51 | * @param {String[]} pontosParada Tipos de dispositivos desejados.
52 | * @param {String[]} pontosInterrupcao Tipos de dispositivos que, se encontrados, interromperá a obtenção, retornando nulo.
53 | * @returns {Element} Dispositivo, se encontrado, ou nulo.
54 | */
55 | function encontrarDispositivoPosteriorDoTipo(elemento, pontosParada, pontosInterrupcao) {
56 | while (!elemento.hasAttribute('data-tipo')) {
57 | elemento = elemento.parentNode;
58 | }
59 |
60 | let setParada = new Set(pontosParada);
61 | let setInterrupcao = new Set(pontosInterrupcao);
62 |
63 | for (let proximo = elemento.nextElementSibling; proximo; proximo = proximo.nextElementSibling) {
64 | let tipoProximo = proximo.getAttribute('data-tipo');
65 |
66 | if (setParada.has(tipoProximo)) {
67 | return proximo;
68 | } else if (setInterrupcao.has(tipoProximo)) {
69 | return null;
70 | }
71 | }
72 |
73 | return null;
74 | }
75 |
76 | /**
77 | * Obtém os tipos de dispositivos em níveis superiores.
78 | *
79 | * @param {String} tipo Tipo cujos tipos superiores serão obtidos.
80 | */
81 | function getTiposSuperiores(tipo) {
82 | return {
83 | inciso: ['artigo', 'paragrafo'],
84 | alinea: ['artigo', 'paragrafo', 'inciso'],
85 | item: ['artigo', 'paragrafo', 'inciso', 'alinea']
86 | }[tipo] || [];
87 | }
88 |
89 | export { encontrarDispositivoAnteriorDoTipo, encontrarDispositivoPosteriorDoTipo, getTiposSuperiores };
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacoes/Transformacao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import TransformacaoAutomaticaEvent from '../../eventos/TransformacaoAutomaticaEvent';
19 |
20 | /**
21 | * Definição abstrata de uma transformação a ser realizada na
22 | * seleção da articulação.
23 | */
24 | class Transformacao {
25 | constructor(/*sequencias*/) {
26 | this.sequencias = [];
27 |
28 | for (let i = 0; i < arguments.length; i++) {
29 | let sequencia = arguments[i];
30 | this.sequencias.push(sequencia);
31 |
32 | if (sequencia.indexOf('\n') >= 0) {
33 | this.sequencias.push(sequencia.replace(/\n/g, '\r'));
34 | }
35 | }
36 | }
37 |
38 | get tipoTransformacao() {
39 | // Deve-se sobrescrever este getter, pois na minificação de js, perde-se o nome do construtor!
40 | return this.constructor.name;
41 | }
42 |
43 | /**
44 | * Efetua a transformação.
45 | *
46 | * @param {Element} elementoEditor Elemento em que está o editor de articulação.
47 | * @param {EditorArticulacaoController} ctrl Controlador do editor.
48 | * @param {ContextoArticulacao} contexto Contexto atual.
49 | * @param {String} sequencia Sequência que disparou a transformação.
50 | * @param {KeyboardEvent} event Evento do teclado.
51 | */
52 | transformar(elementoEditor, ctrl, contexto, sequencia, event) {
53 | throw 'Método não implementado';
54 | }
55 | }
56 |
57 | /**
58 | * Definição abstrata de uma transformação a ser realizada no
59 | * próximo dispositivo selecionado, após o disparo do evento
60 | * 'keyup'. Útil para transformações a ser executadas após
61 | * a criação de um novo dispositivo.
62 | */
63 | class TransformacaoDoProximo extends Transformacao {
64 | transformar(editor, ctrl, contexto) {
65 | let novoTipo = this.proximoTipo(editor, ctrl, contexto);
66 |
67 | if (novoTipo) {
68 | onKeyUp(editor, contexto.cursor.elemento, () => {
69 | let tipoAnterior = ctrl.contexto.cursor.tipo;
70 | ctrl.alterarTipoDispositivoSelecionado(novoTipo);
71 | ctrl.dispatchEvent(new TransformacaoAutomaticaEvent(ctrl, tipoAnterior, novoTipo, this.tipoTransformacao));
72 | });
73 | }
74 | }
75 |
76 | proximoTipo(editor, ctrl, contexto) {
77 | throw 'Método não implementado';
78 | }
79 | }
80 |
81 | function onKeyUp(editor, elementoAtual, callback) {
82 | var handler = function(event) {
83 | callback(event);
84 | editor.removeEventListener('keyup', handler);
85 | };
86 |
87 | editor.addEventListener('keyup', handler);
88 | }
89 |
90 | export { Transformacao, TransformacaoDoProximo };
91 | export default Transformacao;
--------------------------------------------------------------------------------
/exemplo/exemplo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Clique aqui para atualizar o lexml.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
42 |
43 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/transformacaoAutomatica/transformacaoAutomatica.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import DoisPontos from './transformacoes/DoisPontos';
19 | import PontoFinal from './transformacoes/PontoFinal';
20 | import AoIniciarAspas from './transformacoes/AoIniciarAspas';
21 | import AoFecharAspas from './transformacoes/AoFecharAspas';
22 | import RecuarComEnterEmDispositivoVazio from './transformacoes/RecuarComEnterEmDispositivoVazio';
23 |
24 | /**
25 | * Adiciona a transformação automática ao editor de articulação.
26 | *
27 | * @param {EditorArticulacaoController} editorArticulacaoCtrl
28 | * @param {Element} elemento
29 | */
30 | function adicionarTransformacaoAutomatica(editorArticulacaoCtrl, elemento) {
31 | var parser = {}, estado = [];
32 |
33 | adicionarParser(parser, new DoisPontos());
34 | adicionarParser(parser, new PontoFinal());
35 | adicionarParser(parser, new AoIniciarAspas());
36 | adicionarParser(parser, new AoFecharAspas());
37 | adicionarParser(parser, new RecuarComEnterEmDispositivoVazio());
38 |
39 | editorArticulacaoCtrl.registrarEventListener('keypress', event => estado = processarEstado(event, parser, estado, editorArticulacaoCtrl, elemento));
40 | editorArticulacaoCtrl.registrarEventListener('mouseup', event => estado = []);
41 | editorArticulacaoCtrl.registrarEventListener('touchend', event => estado = []);
42 | }
43 |
44 | /**
45 | * Adiciona um transformador ao parser.
46 | *
47 | * @param {Object} parser
48 | * @param {Transformacao} transformador
49 | */
50 | function adicionarParser(parser, transformador) {
51 | transformador.sequencias.forEach(function(sequencia) {
52 | var i, pAtual = parser, c;
53 | var handler = transformador.transformar.bind(transformador);
54 |
55 | for (i = 0; i < sequencia.length; i++) {
56 | c = sequencia.charCodeAt(i);
57 |
58 | if (!pAtual[c]) {
59 | pAtual[c] = {};
60 | }
61 |
62 | pAtual = pAtual[c];
63 | }
64 |
65 | let objHandler = {
66 | sequencia: sequencia,
67 | handler: handler
68 | };
69 |
70 | if (pAtual.$handler) {
71 | pAtual.$handler.push(objHandler);
72 | } else {
73 | pAtual.$handler = [objHandler];
74 | }
75 | });
76 | }
77 |
78 | /**
79 | * Realiza o parsing da edição.
80 | */
81 | function processarEstado(event, _parser, _estadoParser, controller, elemento) {
82 | var novoEstado = [],
83 | c = event.charCode || event.keyCode;
84 |
85 | _estadoParser.forEach(function (estado) {
86 | if (estado[c]) {
87 | novoEstado.push(estado[c]);
88 | }
89 | });
90 |
91 | if (_parser[c]) {
92 | novoEstado.push(_parser[c]);
93 | }
94 |
95 | novoEstado.forEach(function (estado) {
96 | if (estado.$handler) {
97 | estado.$handler.forEach(objHandler => objHandler.handler(elemento, controller, controller.contexto, objHandler.sequencia.replace(/\r/g, '\n'), event));
98 | }
99 | });
100 |
101 | return novoEstado;
102 | }
103 |
104 | export default adicionarTransformacaoAutomatica;
--------------------------------------------------------------------------------
/src/validacao/ValidacaoController.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import ProblemaValidacao from './ProblemaValidacao';
19 | import ValidadorCaixaAlta from './ValidadorCaixaAlta';
20 | import ValidadorCitacao from './ValidadorCitacao';
21 | import ValidadorEnumeracaoElementos from './ValidadorEnumeracaoElementos';
22 | import ValidadorIniciarLetraMaiuscula from './ValidadorIniciarLetraMaiuscula';
23 | import ValidadorPontuacaoArtigoParagrafo from './ValidadorPontuacaoArtigoParagrafo';
24 | import ValidadorPontuacaoEnumeracao from './ValidadorPontuacaoEnumeracao';
25 | import ValidadorSentencaUnica from './ValidadorSentencaUnica';
26 |
27 | /**
28 | * Controler para realizar validação nos dispositivos.
29 | */
30 | class ValidacaoController {
31 | constructor(opcoes) {
32 | this.validadores = [];
33 |
34 | if (opcoes !== false) {
35 | habilitar(opcoes, 'caixaAlta', this.validadores, ValidadorCaixaAlta);
36 | habilitar(opcoes, 'citacao', this.validadores, ValidadorCitacao);
37 | habilitar(opcoes, 'enumeracaoElementos', this.validadores, ValidadorEnumeracaoElementos);
38 | habilitar(opcoes, 'inicialMaiuscula', this.validadores, ValidadorIniciarLetraMaiuscula);
39 | habilitar(opcoes, 'pontuacao', this.validadores, ValidadorPontuacaoArtigoParagrafo);
40 | habilitar(opcoes, 'pontuacaoEnumeracao', this.validadores, ValidadorPontuacaoEnumeracao);
41 | habilitar(opcoes, 'sentencaUnica', this.validadores, ValidadorSentencaUnica);
42 | }
43 | }
44 |
45 | /**
46 | * Realiza a validação do dispositivo.
47 | *
48 | * @param {Element} dispositivo Dispositivo a ser validado.
49 | * @returns {ProblemaValidacao[]} Problemas encontrados.
50 | */
51 | validar(dispositivo) {
52 | if (!dispositivo) {
53 | return;
54 | }
55 |
56 | let problemas = [];
57 |
58 | if (dispositivo.textContent.trim().length > 0) {
59 | this.validadores.forEach(item => {
60 | var validador = item.validador;
61 | var opcao = item.opcao;
62 |
63 | if (validador.aplicaSeA(dispositivo) && !validador.validar(dispositivo)) {
64 | problemas.push(new ProblemaValidacao(dispositivo, opcao, validador.descricao));
65 | }
66 | });
67 |
68 | if (problemas.length > 0) {
69 | dispositivo.setAttribute('data-invalido', problemas.reduce((prev, problema) => prev ? prev + ' ' + problema.descricao : problema.descricao, null));
70 | } else {
71 | dispositivo.removeAttribute('data-invalido');
72 | }
73 | }
74 |
75 | return problemas;
76 | }
77 | }
78 |
79 | /**
80 | * Habilita um validador.
81 | *
82 | * @param {Object} opcoes Conjunto de opções fornecidas para o controller de validação.
83 | * @param {String} nomeOpcao Nome da opção para habilitar um validador.
84 | * @param {Validador[]} validadores Vetor de validadores.
85 | * @param {Class} Validador Classe do validador.
86 | */
87 | function habilitar(opcoes, nomeOpcao, validadores, Validador) {
88 | if (opcoes === true || opcoes[nomeOpcao] !== false) {
89 | validadores.push({ validador: new Validador(), opcao: nomeOpcao });
90 | }
91 | }
92 |
93 | export default ValidacaoController;
--------------------------------------------------------------------------------
/src/ComponenteEdicao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import html from './editor-html';
19 | import EditorArticulacaoController from './EditorArticulacaoController';
20 |
21 | /**
22 | * Transforma um elemento do DOM em um editor de articulação com
23 | * barra de ferramentas.
24 | */
25 | class ComponenteEdicao {
26 | constructor(elemento, opcoes) {
27 | var container, botoes, containerBotoes, ctrl;
28 |
29 | /* Se houver suporte ao shadow-dom, então vamos usá-lo
30 | * para garantir o isolamento da árvore interna do componente
31 | * e possíveis problemas com CSS.
32 | */
33 | if (opcoes.shadowDOM && 'attachShadow' in elemento) {
34 | let shadow = elemento.attachShadow({ mode: (typeof opcoes.shadowDOM === 'string' ? opcoes.shadowDOM : 'open') });
35 |
36 | shadow.innerHTML = html.toString();
37 |
38 | container = shadow.querySelector('.silegismg-editor-conteudo');
39 | containerBotoes = shadow.querySelector('.silegismg-editor-botoes');
40 | botoes = containerBotoes.querySelectorAll('button[data-tipo-destino]');
41 |
42 | elemento.addEventListener('focus', focusEvent => container.focus());
43 | elemento.focus = function () { container.focus(); };
44 | elemento.ctrlArticulacao = this.ctrl = ctrl = new EditorArticulacaoController(container, opcoes);
45 | } else {
46 | elemento.innerHTML = html.toString();
47 | container = elemento.querySelector('.silegismg-editor-conteudo');
48 | containerBotoes = elemento.querySelector('.silegismg-editor-botoes');
49 | botoes = elemento.querySelectorAll('button[data-tipo-destino]');
50 | elemento.ctrlArticulacao = this.ctrl = ctrl = new EditorArticulacaoController(container, opcoes);
51 | }
52 |
53 | this.ctrl.dispatchEvent = eventoInterno => elemento.dispatchEvent(eventoInterno.getCustomEvent());
54 |
55 | function alterarDispositivo(event) {
56 | ctrl.alterarTipoDispositivoSelecionado(event.target.getAttribute('data-tipo-destino'));
57 | container.focus();
58 | }
59 |
60 | // Trata cliques nos botões de formatação.
61 | for (let i = 0; i < botoes.length; i++) {
62 | botoes[i].addEventListener('click', alterarDispositivo.bind(this));
63 | }
64 |
65 | /* Monitora atualização do contexto do usuário no editor de articulação,
66 | * controlando habilitação de botões de transformação de dispositivo.
67 | *
68 | * A atualização é feita por meio do evento "contexto" no DOM.
69 | */
70 | elemento.addEventListener('contexto', function (evento) {
71 | /* Os botões de transformação de dispositivo só sõ habilitados se for possível
72 | * usar o dispositivo onde o cursor estiver.
73 | */
74 | for (let i = 0; i < botoes.length; i++) {
75 | botoes[i].disabled = !evento.detail.permissoes[botoes[i].getAttribute('data-tipo-destino')];
76 | botoes[i].classList.remove('atual');
77 | }
78 |
79 | let tipoAtual = containerBotoes.querySelector('button[data-tipo-destino="' + evento.detail.cursor.tipo + '"]');
80 |
81 | if (tipoAtual) {
82 | tipoAtual.classList.add('atual');
83 | }
84 | });
85 |
86 | elemento.addEventListener('scroll', function (evento) {
87 | containerBotoes.style.position = 'relative';
88 | containerBotoes.style.top = elemento.scrollTop + 'px';
89 | });
90 | }
91 | }
92 |
93 | export default ComponenteEdicao;
--------------------------------------------------------------------------------
/test/karma/clipboardController.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | describe('ClipboardController', function() {
19 | 'use strict';
20 |
21 | describe('transformar', function() {
22 | var transformar = window.clipboardControllerModule.transformar;
23 |
24 | it('Única linha deve ser um nó textual', function() {
25 | var fragmento = transformar('linha única', 'artigo');
26 |
27 | expect(fragmento.childNodes.length).toBe(1);
28 | expect(fragmento.firstChild.nodeType).toBe(Node.TEXT_NODE);
29 | expect(fragmento.firstChild.textContent).toBe('linha única');
30 | });
31 |
32 | it('Deve inserir a primeira linha em um TextNode e a segunda linha em P', function() {
33 | var fragmento = transformar('linha 1\nlinha 2', 'artigo');
34 |
35 | expect(fragmento.childNodes.length).toBe(2);
36 | expect(fragmento.firstChild.nodeType).toBe(Node.TEXT_NODE);
37 | expect(fragmento.firstChild.textContent).toBe('linha 1');
38 | expect(fragmento.lastChild.outerHTML).toBe('
linha 2
');
39 | });
40 |
41 | it('Deve inserir a primeira linha em um TextNode e a segunda e terceira linha em P', function() {
42 | var fragmento = transformar('linha 1\nlinha 2\nlinha 3', 'artigo');
43 |
44 | expect(fragmento.childNodes.length).toBe(3);
45 | expect(fragmento.firstChild.nodeType).toBe(Node.TEXT_NODE);
46 | expect(fragmento.firstChild.textContent).toBe('linha 1');
47 | expect(fragmento.firstElementChild.outerHTML).toBe('
');
81 | });
82 |
83 | it('Deve corrigir formatação de parágrafo único', function() {
84 | const fragmento = transformar(`
85 | Art. 1º - Teste.
86 | § 1º - Teste.
87 | Art. 2º - Teste.
88 | § 1º - Teste.
89 | § 2º - Teste.
90 | Art. 3º - Teste.
91 | `, false);
92 |
93 | const paragrafos = fragmento.querySelectorAll('p[data-tipo="paragrafo"]');
94 |
95 | expect(paragrafos[0].classList.contains('unico')).toBe(true);
96 | expect(paragrafos[1].classList.contains('unico')).toBe(false);
97 | expect(paragrafos[2].classList.contains('unico')).toBe(false);
98 | });
99 | });
100 | });
--------------------------------------------------------------------------------
/test/karma/exportacaoParaLexML.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | describe('Importação do LexML', function () {
19 | 'use strict';
20 |
21 | var exportarParaLexML = window.exportarParaLexML;
22 |
23 | it('Deve exportar os artigos 1 a 3 da constituição federal', function () {
24 | var div = document.createElement('div');
25 | div.innerHTML = '
Dos Princípios Fundamentais
A República Federativa do Brasil, formada pela união indissolúvel dos Estados e Municípios e do Distrito Federal, constitui-se em Estado democrático de direito e tem como fundamentos:
a soberania;
a cidadania;
a dignidade da pessoa humana;
os valores sociais do trabalho e da livre iniciativa;
o pluralismo político.
Todo o poder emana do povo, que o exerce por meio de representantes eleitos ou diretamente, nos termos desta Constituição.
São Poderes da União, independentes e harmônicos entre si, o Legislativo, o Executivo e o Judiciário.
Constituem objetivos fundamentais da República Federativa do Brasil:
construir uma sociedade livre, justa e solidária;
garantir o desenvolvimento nacional;
erradicar a pobreza e a marginalização e reduzir as desigualdades sociais e regionais;
promover o bem de todos, sem preconceitos de origem, raça, sexo, cor, idade e quaisquer outras formas de discriminação.
A República Federativa do Brasil, formada pela união indissolúvel dos Estados e Municípios e do Distrito Federal, constitui-se em Estado democrático de direito e tem como fundamentos:
I –
a soberania;
II –
a cidadania;
III –
a dignidade da pessoa humana;
IV –
os valores sociais do trabalho e da livre iniciativa;
V –
o pluralismo político.
Parágrafo único –
Todo o poder emana do povo, que o exerce por meio de representantes eleitos ou diretamente, nos termos desta Constituição.
Art. 2º –
São Poderes da União, independentes e harmônicos entre si, o Legislativo, o Executivo e o Judiciário.
Art. 3º –
Constituem objetivos fundamentais da República Federativa do Brasil:
I –
construir uma sociedade livre, justa e solidária;
II –
garantir o desenvolvimento nacional;
III –
erradicar a pobreza e a marginalização e reduzir as desigualdades sociais e regionais;
IV –
promover o bem de todos, sem preconceitos de origem, raça, sexo, cor, idade e quaisquer outras formas de discriminação.
');
29 | });
30 |
31 | it('Deve exportar citação dentro do caput', function () {
32 | var div = document.createElement('div');
33 | div.innerHTML = '
');
53 | });
54 | });
--------------------------------------------------------------------------------
/src/lexml/importarDeLexML.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Transforma o LexML no conteúdo para editor de articulação.
20 | *
21 | * @param {Element} elemento
22 | * @param {DocumentFragment} resultado
23 | * @returns {DocumentFragment}
24 | */
25 | function importarDeLexML(elemento, resultado) {
26 | if (!resultado) {
27 | resultado = document.createDocumentFragment();
28 | }
29 |
30 | if (!elemento) {
31 | return resultado;
32 | } else if (elemento instanceof NodeList || elemento instanceof HTMLCollection) {
33 | for (let i = 0; i < elemento.length; i++) {
34 | importarDeLexML(elemento[i], resultado);
35 | }
36 | } else if (typeof elemento === 'string') {
37 | let container = document.createElement('html');
38 | container.innerHTML = elemento.replace(/<([^ >]+)\s*\/>/g, '<$1>$1>'); // HTML não suporta notação como no XML.
39 | importarDeLexML(container.querySelector('body').children, resultado);
40 | } else {
41 | switch (elemento.tagName) {
42 | case 'ARTICULACAO':
43 | importarDeLexML(elemento.children, resultado);
44 | break;
45 |
46 | case 'TITULO':
47 | case 'CAPITULO':
48 | case 'SECAO':
49 | case 'SUBSECAO':
50 | resultado.appendChild(criarAgrupador(elemento.tagName.toLowerCase(), elemento.querySelector('NomeAgrupador').innerHTML));
51 | importarDeLexML(elemento.children, resultado);
52 | break;
53 |
54 | case 'NOMEAGRUPADOR':
55 | case 'ROTULO':
56 | case 'P':
57 | // Ignora.
58 | break;
59 |
60 | case 'ARTIGO':
61 | clonar(elemento, 'Caput > p', idx => idx === 0 ? 'artigo' : 'continuacao', resultado);
62 |
63 | if (/\d+º?-[A-Z]/.test(elemento.querySelector('Rotulo').textContent)) {
64 | resultado.lastElementChild.classList.add('emenda');
65 | }
66 |
67 | let incisos = elemento.querySelectorAll('Caput > Inciso');
68 | importarDeLexML(incisos, resultado);
69 |
70 | let paragrafos = elemento.querySelectorAll('Paragrafo');
71 | let dispositivoAtual = resultado.lastElementChild;
72 |
73 | importarDeLexML(paragrafos, resultado);
74 |
75 | if (paragrafos.length === 1) {
76 | dispositivoAtual.nextElementSibling.classList.add('unico');
77 | }
78 | break;
79 |
80 | case 'INCISO':
81 | case 'ALINEA':
82 | case 'ITEM':
83 | case 'PARAGRAFO':
84 | let p = obterP(elemento);
85 | clonar(p, null, elemento.tagName.toLowerCase(), resultado);
86 | importarDeLexML(elemento.children, resultado);
87 | break;
88 |
89 | default:
90 | throw 'Elemento não esperado: ' + elemento.tagName;
91 | }
92 | }
93 |
94 | return resultado;
95 | }
96 |
97 | /**
98 | * Cria um elemento agrupador.
99 | *
100 | * @param {String} agrupador Tipo do agrupador.
101 | * @param {String} nome Nome do agrupador.
102 | * @returns {Element} Agrupador
103 | */
104 | function criarAgrupador(agrupador, nome) {
105 | let elemento = document.createElement('p');
106 | elemento.setAttribute('data-tipo', agrupador);
107 | elemento.innerHTML = nome;
108 | return elemento;
109 | }
110 |
111 | /**
112 | * Obtém o primeiro elemento P filho.
113 | *
114 | * @param {Element} elemento
115 | * @returns {Element}
116 | */
117 | function obterP(elemento) {
118 | let p = elemento.firstElementChild;
119 |
120 | while (p.tagName !== 'P') {
121 | p = p.nextElementSibling;
122 | }
123 |
124 | return p;
125 | }
126 |
127 | /**
128 | * Clona um elemento.
129 | *
130 | * @param {Element} elemento Elemento que será clonado ou que contém os elementos a serem clonados, se a query for especificada.
131 | * @param {String} query Query a ser executada para filtrar os elementos filhos.
132 | * @param {String|function} tipoFinal Tipo final do elemento clonado (string) ou uma função que recebe (índice, elemento) e retorna o tipo em string.
133 | * @param {DocumentFragment} resultado
134 | */
135 | function clonar(elemento, query, tipoFinal, resultado) {
136 | let fnTipo;
137 |
138 | switch (typeof tipoFinal) {
139 | case 'string':
140 | fnTipo = () => tipoFinal;
141 | break;
142 |
143 | case 'function':
144 | fnTipo = tipoFinal;
145 | break;
146 |
147 | default:
148 | throw new Error('Tipo final não é literal nem função.');
149 | }
150 |
151 | if (query) {
152 | var itens = elemento.querySelectorAll(query);
153 |
154 | for (let i = 0; i < itens.length; i++) {
155 | let novoItem = itens[i].cloneNode(true);
156 | novoItem.setAttribute('data-tipo', fnTipo(i, novoItem));
157 | resultado.appendChild(novoItem);
158 | }
159 | } else {
160 | let novoItem = elemento.cloneNode(true);
161 | novoItem.setAttribute('data-tipo', fnTipo(0, novoItem));
162 | resultado.appendChild(novoItem);
163 | }
164 |
165 | return resultado;
166 | }
167 |
168 | export default importarDeLexML;
--------------------------------------------------------------------------------
/src/ControleAlteracao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import ArticulacaoChangeEvent from './eventos/ArticulacaoChangeEvent';
19 |
20 | /**
21 | * Monitora alterações no editor de articulação e dispara o evento change.
22 | */
23 | class ControleAlteracao {
24 | constructor(editorCtrl) {
25 | this._editorCtrl = editorCtrl;
26 |
27 | editorCtrl.registrarEventListener('focus', event => {
28 | this._comFoco = true;
29 |
30 | this.iniciar(event.target, editorCtrl);
31 | }, true);
32 |
33 | editorCtrl.registrarEventListener('blur', event => {
34 | this._comFoco = false;
35 |
36 | this.finalizar(event.target, editorCtrl);
37 | this.comprometer();
38 | }, true);
39 | }
40 |
41 | get alterado() {
42 | throw 'Não implementado.';
43 | }
44 |
45 | set alterado(valor) {
46 | throw 'Não implementado.';
47 | }
48 |
49 | /**
50 | * Inicia a monitoração de alterações.
51 | *
52 | * @param {Element} elemento
53 | * @param {EditorArticulacaoController} editorCtrl Editor de articulação
54 | */
55 | iniciar(elemento, editorCtrl) {
56 | throw 'Não implementado';
57 | }
58 |
59 | /**
60 | * Finaliza a monitoração de alterações.
61 | *
62 | * @param {Element} elemento
63 | * @param {EditorArticulacaoController} editorCtrl Editor de articulação
64 | */
65 | finalizar(elemento, editorCtrl) {
66 | throw 'Não implementado';
67 | }
68 |
69 | comprometer() {
70 | if (this.alterado) {
71 | this.alterado = false;
72 | this._editorCtrl.dispatchEvent(new ArticulacaoChangeEvent(this._editorCtrl));
73 | }
74 | }
75 | }
76 |
77 | /**
78 | * Monitora as alterações utilizando MutationObserver.
79 | */
80 | class ControleAlteracaoMutationObserver extends ControleAlteracao {
81 | constructor(editorCtrl) {
82 | super(editorCtrl);
83 |
84 | this._observer = new MutationObserver(this._mutationCallback.bind(this));
85 | this._iniciado = false;
86 | this._alterado = false;
87 | }
88 |
89 | get alterado() {
90 | return this._alterado;
91 | }
92 |
93 | set alterado(valor) {
94 | this._alterado = valor;
95 |
96 | if (!this._alterado && !this._conectado && this._comFoco) {
97 | this.iniciar(this._editorCtrl._elemento, this._editorCtrl);
98 | }
99 |
100 | if (this._alterado && !this._comFoco) {
101 | this.comprometer();
102 | }
103 |
104 | if (this._alterado) {
105 | this.finalizar();
106 | }
107 | }
108 |
109 | /**
110 | * Inicia a monitoração de alterações.
111 | *
112 | * @param {Element} elemento
113 | * @param {EditorArticulacaoController} editorCtrl Editor de articulação
114 | */
115 | iniciar(elemento, editorCtrl) {
116 | if (!this._conectado) {
117 | this._observer.observe(elemento, {
118 | childList: true,
119 | attributes: true,
120 | characterData: true,
121 | subtree: true
122 | });
123 | this._conectado = true;
124 | }
125 | this._iniciado = true;
126 | }
127 |
128 | /**
129 | * Finaliza a monitoração de alterações.
130 | *
131 | * @param {Element} elemento
132 | * @param {EditorArticulacaoController} editorCtrl Editor de articulação
133 | */
134 | finalizar(elemento, editorCtrl) {
135 | if (this._conectado) {
136 | this._observer.disconnect();
137 | this._conectado = false;
138 | }
139 |
140 | this._iniciado = false;
141 | }
142 |
143 | _mutationCallback() {
144 | try {
145 | this._conectado = false;
146 | this._observer.disconnect();
147 | } finally {
148 | this.alterado = true;
149 | }
150 | }
151 | }
152 |
153 | /**
154 | * Monitora as alterações comparando o lexml no foco e no blur.
155 | */
156 | class ControleAlteracaoComparativo extends ControleAlteracao {
157 | constructor(editorCtrl) {
158 | super(editorCtrl);
159 |
160 | this._alteradoCache = false;
161 | }
162 |
163 | get alterado() {
164 | if (this._alteradoCache) {
165 | return true;
166 | } else {
167 | this._alteradoCache = this._editorCtrl.lexmlString !== this._lexml;
168 | return this._alteradoCache;
169 | }
170 | }
171 |
172 | set alterado(valor) {
173 | this._lexml = valor ? '' : this._editorCtrl.lexmlString;
174 | this._alteradoCache = !!valor;
175 |
176 | if (valor && !this._comFoco) {
177 | this.comprometer();
178 | }
179 | }
180 |
181 | /**
182 | * Inicia a monitoração de alterações.
183 | *
184 | * @param {Element} elemento
185 | * @param {EditorArticulacaoController} editorCtrl Editor de articulação
186 | */
187 | iniciar(elemento, editorCtrl) {
188 | this._lexml = editorCtrl.lexmlString;
189 | }
190 |
191 | /**
192 | * Finaliza a monitoração de alterações.
193 | *
194 | * @param {Element} elemento
195 | * @param {EditorArticulacaoController} editorCtrl Editor de articulação
196 | */
197 | finalizar(elemento, editorCtrl) {
198 | }
199 | }
200 |
201 | function criarControleAlteracao(editorCtrl) {
202 | if ('MutationObserver' in window) {
203 | return new ControleAlteracaoMutationObserver(editorCtrl);
204 | } else {
205 | return new ControleAlteracaoComparativo(editorCtrl);
206 | }
207 | }
208 |
209 | export default criarControleAlteracao;
--------------------------------------------------------------------------------
/src/editor-articulacao-css.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 | const css = `
18 | .silegismg-editor-articulacao {
19 | counter-reset: parte livro titulo capitulo secao subsecao artigo;
20 | outline: none;
21 | }
22 |
23 | .silegismg-editor-articulacao p:before {
24 | font-weight: bolder;
25 | animation: .25s ease-in rotulo;
26 | }
27 |
28 | .silegismg-editor-articulacao p[data-tipo]:before {
29 | display: inline-block; /* Resolve problema de cursor antes do rótulo no chrome */
30 | margin-right: 1ex;
31 | text-indent: 0;
32 | }
33 |
34 | .silegismg-editor-articulacao p {
35 | text-indent: 4ex;
36 | text-align: justify;
37 | border-left: solid 2px transparent;
38 | padding-left: 1ex;
39 | }
40 |
41 | .silegismg-editor-articulacao p[data-invalido] {
42 | border-left-color: red;
43 | }
44 |
45 | .silegismg-editor-articulacao p[data-invalido]:after {
46 | display: block;
47 | content: attr(data-invalido);
48 | color: red;
49 | text-indent: 0;
50 | margin-left: auto;
51 | font-size: 90%;
52 | }
53 |
54 | .silegismg-editor-articulacao p[data-tipo="titulo"]:before {
55 | content: 'Título ' counter(titulo, upper-roman);
56 | counter-increment: titulo;
57 | display: block;
58 | text-align: center;
59 | margin-right: 0;
60 | text-indent: 0;
61 | }
62 |
63 | .silegismg-editor-articulacao p[data-tipo="titulo"] {
64 | counter-reset: capitulo;
65 | text-transform: uppercase;
66 | font-weight: bolder;
67 | font-size: 110%;
68 | text-align: center;
69 | text-indent: 0;
70 | }
71 |
72 | .silegismg-editor-articulacao p[data-tipo="capitulo"]:before {
73 | content: 'Capítulo ' counter(capitulo, upper-roman);
74 | counter-increment: capitulo;
75 | display: block;
76 | text-align: center;
77 | font-weight: normal;
78 | margin-right: 0;
79 | text-indent: 0;
80 | }
81 |
82 | .silegismg-editor-articulacao p[data-tipo="capitulo"] {
83 | counter-reset: secao;
84 | text-transform: uppercase;
85 | font-size: 110%;
86 | text-align: center;
87 | text-indent: 0;
88 | }
89 |
90 | .silegismg-editor-articulacao p[data-tipo="secao"]:before {
91 | content: 'Seção ' counter(secao, upper-roman);
92 | counter-increment: secao;
93 | display: block;
94 | text-align: center;
95 | margin-right: 0;
96 | text-indent: 0;
97 | }
98 |
99 | .silegismg-editor-articulacao p[data-tipo="secao"] {
100 | counter-reset: subsecao;
101 | font-weight: bolder;
102 | font-size: 110%;
103 | text-align: center;
104 | text-indent: 0;
105 | }
106 |
107 | .silegismg-editor-articulacao p[data-tipo="subsecao"]:before {
108 | content: 'Subseção ' counter(subsecao, upper-roman);
109 | counter-increment: subsecao;
110 | display: block;
111 | text-align: center;
112 | margin-right: 0;
113 | text-indent: 0;
114 | }
115 |
116 | .silegismg-editor-articulacao p[data-tipo="subsecao"] {
117 | font-weight: bolder;
118 | font-size: 110%;
119 | text-align: center;
120 | text-indent: 0;
121 | }
122 |
123 |
124 | .silegismg-editor-articulacao p[data-tipo="artigo"]:before {
125 | content: 'Art. ' counter(artigo) 'º' '\${separadorArtigo}';
126 | counter-increment: artigo;
127 | }
128 |
129 | .silegismg-editor-articulacao p[data-tipo="artigo"].emenda:before {
130 | content: 'Art. ' counter(artigo) 'º-' counter(emenda, upper-latin) '\${separadorArtigo}';
131 | counter-increment: emenda;
132 | }
133 |
134 | /* A partir do artigo 10, não se usa "º" */
135 |
136 | .silegismg-editor-articulacao p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda):before {
137 | content: 'Art. ' counter(artigo) '\${separadorArtigoSemOrdinal}';
138 | }
139 |
140 | .silegismg-editor-articulacao p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"]:not(.emenda) ~ p[data-tipo="artigo"].emenda:before {
141 | content: 'Art. ' counter(artigo) '-' counter(emenda, upper-latin) '\${separadorArtigoSemOrdinal}';
142 | }
143 |
144 | .silegismg-editor-articulacao p[data-tipo='artigo'] {
145 | counter-reset: paragrafo inciso emenda;
146 | }
147 |
148 | .silegismg-editor-articulacao p[data-tipo='artigo'].emenda {
149 | counter-reset: paragrafo inciso;
150 | }
151 |
152 | .silegismg-editor-articulacao p[data-tipo='paragrafo'] {
153 | counter-reset: inciso;
154 | }
155 |
156 | .silegismg-editor-articulacao p[data-tipo='paragrafo']:before {
157 | content: '§ ' counter(paragrafo) 'º' '\${separadorParagrafo}';
158 | counter-increment: paragrafo;
159 | }
160 |
161 | .silegismg-editor-articulacao p.semOrdinal[data-tipo='paragrafo']:before {
162 | content: '§ ' counter(paragrafo) '\${separadorParagrafoSemOrdinal}';
163 | counter-increment: paragrafo;
164 | }
165 |
166 | .silegismg-editor-articulacao p[data-tipo='paragrafo'].unico:before {
167 | content: 'Parágrafo único\${separadorParagrafoUnico}';
168 | }
169 |
170 | .silegismg-editor-articulacao p[data-tipo='inciso'] {
171 | counter-reset: alinea;
172 | }
173 |
174 | .silegismg-editor-articulacao p[data-tipo='inciso']:before {
175 | content: counter(inciso, upper-roman) '\${separadorInciso}';
176 | counter-increment: inciso;
177 | }
178 |
179 | .silegismg-editor-articulacao p[data-tipo='alinea'] {
180 | counter-reset: item;
181 | }
182 |
183 | .silegismg-editor-articulacao p[data-tipo='alinea']:before {
184 | content: counter(alinea, lower-latin) '\${separadorAlinea}';
185 | counter-increment: alinea;
186 | }
187 |
188 | .silegismg-editor-articulacao p[data-tipo='item']:before {
189 | content: counter(item) '\${separadorItem}';
190 | counter-increment: item;
191 | }
192 |
193 |
194 | /* O primeiro artigo não deve ter margem superior */
195 |
196 | .silegismg-editor-articulacao > *:first-child {
197 | margin-top: 0;
198 | }
199 |
200 |
201 | /* O último artigo não deve ter margem inferior */
202 |
203 | .silegismg-editor-articulacao > *:last-child {
204 | margin-bottom: 0;
205 | }
206 |
207 | @keyframes rotulo {
208 | 0% {
209 | opacity: 0;
210 | }
211 | 10% {
212 | opacity: 0;
213 | }
214 | 100% {
215 | opacity: 1;
216 | }
217 | }
218 | `;
219 |
220 | export default css;
--------------------------------------------------------------------------------
/COPYING.LESSER:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/test/karma/importacaoDeLexML.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | describe('Importação do LexML', function () {
19 | 'use strict';
20 |
21 | var importarDeLexML = window.importarDeLexML;
22 |
23 | it('Deve importar os artigos 1 a 3 da constituição federal', function () {
24 | var articulacao = `
25 |
26 | Título I
27 | Dos Princípios Fundamentais
28 |
29 | Art. 1º
30 |
31 |
A República Federativa do Brasil, formada pela união indissolúvel dos Estados e Municípios e do Distrito Federal, constitui-se em Estado democrático de direito e tem como fundamentos:
32 |
33 | I -
34 |
a soberania;
35 |
36 |
37 | II -
38 |
a cidadania;
39 |
40 |
41 | III -
42 |
a dignidade da pessoa humana;
43 |
44 |
45 | IV -
46 |
os valores sociais do trabalho e da livre iniciativa;
47 |
48 |
49 | V -
50 |
o pluralismo político.
51 |
52 |
53 |
54 | Parágrafo único.
55 |
Todo o poder emana do povo, que o exerce por meio de representantes eleitos ou diretamente, nos termos desta Constituição.
56 |
57 |
58 |
59 | Art. 2º
60 |
61 |
São Poderes da União, independentes e harmônicos entre si, o Legislativo, o Executivo e o Judiciário.
62 |
63 |
64 |
65 | Art. 3º
66 |
67 |
Constituem objetivos fundamentais da República Federativa do Brasil:
68 |
69 | I -
70 |
construir uma sociedade livre, justa e solidária;
71 |
72 |
73 | II -
74 |
garantir o desenvolvimento nacional;
75 |
76 |
77 | III -
78 |
erradicar a pobreza e a marginalização e reduzir as desigualdades sociais e regionais;
79 |
80 |
81 | IV -
82 |
promover o bem de todos, sem preconceitos de origem, raça, sexo, cor, idade e quaisquer outras formas de discriminação.
A República Federativa do Brasil, formada pela união indissolúvel dos Estados e Municípios e do Distrito Federal, constitui-se em Estado democrático de direito e tem como fundamentos:
a soberania;
a cidadania;
a dignidade da pessoa humana;
os valores sociais do trabalho e da livre iniciativa;
o pluralismo político.
Todo o poder emana do povo, que o exerce por meio de representantes eleitos ou diretamente, nos termos desta Constituição.
São Poderes da União, independentes e harmônicos entre si, o Legislativo, o Executivo e o Judiciário.
Constituem objetivos fundamentais da República Federativa do Brasil:
construir uma sociedade livre, justa e solidária;
garantir o desenvolvimento nacional;
erradicar a pobreza e a marginalização e reduzir as desigualdades sociais e regionais;
promover o bem de todos, sem preconceitos de origem, raça, sexo, cor, idade e quaisquer outras formas de discriminação.
');
93 | });
94 |
95 | it('Deve importar citações dentro de artigo', function () {
96 | var articulacao = `
97 |
98 | Art. 1º –
99 |
100 |
Ficam acrescidos ao art. 2º da Lei nº 1.234, de 31 de fevereiro de 2018, os incisos III e IV:
101 |
"Art. 2º - (...)
102 |
III - testar a importação;
103 |
IV - outro teste.".
104 |
105 |
106 |
107 | Art. 2º –
108 |
109 |
Esta lei entra em vigor na data de sua publicação.
');
152 | });
153 | });
--------------------------------------------------------------------------------
/src/ContextoArticulacao.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | /**
19 | * Representa o contexto do usuário no editor de articulação.
20 | * Possui dois atributos: cursor, contendo o contexto no cursor,
21 | * e as permissões de alteração de dispositivo na seleção.
22 | */
23 | class ContextoArticulacao {
24 | constructor(elementoArticulacao, dispositivo) {
25 | let cursor = {
26 | italico: dispositivo.tagName === 'I',
27 | desconhecido: false,
28 | titulo: false,
29 | capitulo: false,
30 | secao: false,
31 | subsecao: false,
32 | artigo: false,
33 | continuacao: false,
34 | paragrafo: false,
35 | inciso: false,
36 | alinea: false,
37 | item: false,
38 | raiz: false,
39 | elemento: dispositivo,
40 | get tipo() {
41 | return this.dispositivo ? this.dispositivo.getAttribute('data-tipo') : 'desconhecido';
42 | }
43 | };
44 |
45 | while (dispositivo && dispositivo !== elementoArticulacao && !dispositivo.hasAttribute('data-tipo')) {
46 | dispositivo = dispositivo.parentElement;
47 | }
48 |
49 | cursor.dispositivo = dispositivo !== elementoArticulacao ? dispositivo : null;
50 |
51 | while (dispositivo && dispositivo.getAttribute('data-tipo') === 'continuacao') {
52 | dispositivo = dispositivo.previousElementSibling;
53 | cursor.continuacao = true;
54 | }
55 |
56 | if (!dispositivo) {
57 | cursor.desconhecido = true;
58 | } else if (dispositivo === elementoArticulacao) {
59 | dispositivo.raiz = true;
60 | } else if (dispositivo.hasAttribute('data-tipo')) {
61 | cursor[dispositivo.getAttribute('data-tipo')] = true;
62 | } else {
63 | cursor.desconhecido = true;
64 | }
65 |
66 | let primeiroDoTipo, tipoAnterior;
67 |
68 | Object.defineProperty(cursor, 'primeiroDoTipo', {
69 | get: function() {
70 | if (primeiroDoTipo === undefined) {
71 | primeiroDoTipo = dispositivo && verificarPrimeiroDoTipo(dispositivo);
72 | }
73 |
74 | return primeiroDoTipo;
75 | }
76 | });
77 |
78 | cursor.dispositivoAnterior = cursor.continuacao ? dispositivo : dispositivo && obterDispositivoAnterior(dispositivo, elementoArticulacao);
79 | cursor.tipoAnterior = cursor.dispositivoAnterior && cursor.dispositivoAnterior.getAttribute('data-tipo');
80 |
81 | let matches = Element.prototype.matches || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.onMatchesSelector || function() { return true; };
82 |
83 | function possuiAnterior(dispositivo, tipo) {
84 | /* Implementação falha/incompleta. Uma subseção deve existir depois de uma seção,
85 | * mas não deveria permitir capítulo + seção + artigo + capítulo + subseção.
86 | *
87 | * Cuidado pois esta implementação não pode ser cara!
88 | */
89 | return dispositivo && matches.call(dispositivo, 'p[data-tipo="' + tipo + '"] ~ *');
90 | }
91 |
92 | let anteriorAgrupador = cursor.tipoAnterior === 'titulo' || cursor.tipoAnterior === 'capitulo' || cursor.tipoAnterior === 'secao' || cursor.tipoAnterior === 'subsecao';
93 |
94 | let permissoes = {
95 | titulo: !anteriorAgrupador,
96 | capitulo: !anteriorAgrupador || cursor.tipoAnterior === 'titulo',
97 | get secao() {
98 | return (!anteriorAgrupador || cursor.tipoAnterior === 'capitulo') && possuiAnterior(cursor.dispositivo, 'capitulo');
99 | },
100 | get subsecao() {
101 | return (!anteriorAgrupador || cursor.tipoAnterior === 'secao') && possuiAnterior(cursor.dispositivo, 'secao');
102 | },
103 | artigo: true /*cursor.tipoAnterior !== 'titulo' - No manual de redação parlamentar da ALMG, dá a entender que o título é um agrupamento de capítulos, mas a constituição possui Art 1º. logo após o título.*/,
104 | continuacao: cursor.tipoAnterior === 'artigo',
105 | inciso: cursor.tipoAnterior && !anteriorAgrupador && (!cursor.artigo || (cursor.artigo && !cursor.primeiroDoTipo)),
106 | paragrafo: cursor.tipoAnterior && !anteriorAgrupador && (!cursor.artigo || (cursor.artigo && (!cursor.primeiroDoTipo || cursor.continuacao))),
107 | alinea: (cursor.inciso && !cursor.primeiroDoTipo) || cursor.tipoAnterior === 'inciso' || cursor.tipoAnterior === 'alinea' || cursor.tipoAnterior === 'item',
108 | item: (cursor.alinea && !cursor.primeiroDoTipo) || cursor.tipoAnterior === 'alinea' || cursor.tipoAnterior === 'item'
109 | };
110 |
111 | Object.defineProperty(this, 'cursor', { value: cursor });
112 | Object.defineProperty(this, 'permissoes', { value: permissoes });
113 |
114 | Object.freeze(this);
115 | Object.freeze(cursor);
116 | Object.freeze(permissoes);
117 | }
118 |
119 | comparar(obj2) {
120 | for (let i in this.cursor) {
121 | if (this.cursor[i] !== obj2.cursor[i]) {
122 | return true;
123 | }
124 | }
125 |
126 | for (let i in this.permissoes) {
127 | if (this.permissoes[i] !== obj2.permissoes[i]) {
128 | return true;
129 | }
130 | }
131 |
132 | return false;
133 | }
134 |
135 | /**
136 | * Determina se o contexto atual é válido.
137 | *
138 | * @returns {Boolean} Verdadeiro, se o contexto estiver válido.
139 | */
140 | get valido() {
141 | return this.permissoes[this.cursor.tipo];
142 | }
143 | }
144 |
145 | /**
146 | * Determina se o dispositivo é o primeiro do tipo.
147 | *
148 | * @param {Element} dispositivo
149 | * @returns {Boolean} Verdadeiro, se for o primeiro do tipo.
150 | */
151 | function verificarPrimeiroDoTipo(dispositivo) {
152 | var tipo = dispositivo.getAttribute('data-tipo');
153 |
154 | if (!tipo) {
155 | return null;
156 | }
157 |
158 | while (tipo === 'continuacao') {
159 | dispositvo = dispositivo.previousElementSibling;
160 | tipo = dispositivo.getAttribute('data-tipo');
161 | }
162 |
163 | let pontosParagem = ({
164 | parte: [],
165 | livro: [],
166 | titulo: [],
167 | capitulo: ['titulo'],
168 | secao: ['capitulo', 'titulo'],
169 | subsecao: ['secao', 'capitulo', 'titulo'],
170 | artigo: [],
171 | paragrafo: ['artigo'],
172 | inciso: ['paragrafo', 'artigo'],
173 | alinea: ['inciso'],
174 | item: ['alinea']
175 | })[tipo].reduce((prev, item) => {
176 | prev[item] = true;
177 | return prev;
178 | }, {});
179 |
180 | for (let prev = dispositivo.previousElementSibling; prev; prev = prev.previousElementSibling) {
181 | let tipoAnterior = prev.getAttribute('data-tipo');
182 |
183 | if (tipoAnterior === tipo) {
184 | return false;
185 | } else if (pontosParagem[tipoAnterior]) {
186 | return true;
187 | }
188 | }
189 |
190 | return true;
191 | }
192 |
193 | /**
194 | * Obtém o tipo de dispositivo anterior.
195 | *
196 | * @param {Element} dispositivo
197 | * @returns {Element} Elemento do dispositivo anterior.
198 | */
199 | function obterDispositivoAnterior(dispositivo, elementoArticulacao) {
200 | while (dispositivo && !dispositivo.hasAttribute('data-tipo') && dispositivo !== elementoArticulacao) {
201 | dispositivo = dispositivo.parentElement;
202 | }
203 |
204 | if (!dispositivo || dispositivo === elementoArticulacao) {
205 | return null;
206 | }
207 |
208 | for (let anterior = dispositivo.previousElementSibling; anterior; anterior = anterior.previousElementSibling) {
209 | if (anterior.hasAttribute('data-tipo')) {
210 | let tipo = anterior.getAttribute('data-tipo');
211 |
212 | if (tipo !== 'continuacao') {
213 | return anterior;
214 | }
215 | }
216 | }
217 |
218 | return null;
219 | }
220 |
221 | export default ContextoArticulacao;
--------------------------------------------------------------------------------
/src/hacks/chrome.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { interceptar } from './interceptador';
19 |
20 | function hackChrome(controller) {
21 | interceptar(controller, 'alterarTipoDispositivoSelecionado', hackAlterarTipo);
22 |
23 | controller.registrarEventListener('keydown', event => hackInterceptarKeydown(event, controller));
24 |
25 | /* A partir do Chrome 62, começou a ter problema no foco após limpar o editor.
26 | * O problema foi evitado adicionando um \n após o ou substituindo o conteúdo
27 | * por " "" como feito no IE.
28 | */
29 | controller.limpar = function() {
30 | this._elemento.innerHTML = '
\n
';
31 | };
32 | }
33 |
34 | /**
35 | * O chrome faz cache do counter-reset e, quando um counter-reset é introduzido
36 | * (por mudança de css), os elementos seguintes não são afetados.
37 | *
38 | * @param {EditorArticulacaoController} ctrl
39 | * @param {function} metodo
40 | * @param {Array} argumentos
41 | */
42 | function hackAlterarTipo(ctrl, metodo, argumentos) {
43 | let dispositivo = ctrl.contexto.cursor.dispositivo;
44 | let contadoresAntigos = extrairContadores(dispositivo);
45 | let retorno = metodo.apply(ctrl, argumentos);
46 | let contadoresNovos = extrairContadores(dispositivo, contadoresAntigos.hash);
47 |
48 | /* Para cada contador novo, vamos redefinir o tipo dos próximos elementos,
49 | * até que se encontre em um elemento antigo o reinício do contador em questão.
50 | */
51 | for (let i = 0, proximo = ctrl.contexto.cursor.dispositivo.nextElementSibling; proximo && i < contadoresNovos.total; proximo = proximo.nextElementSibling) {
52 | let tipo = proximo.getAttribute('data-tipo');
53 | let contadoresProximo = extrairContadores(proximo);
54 |
55 | if (contadoresNovos.hash[tipo]) {
56 | redefinirContador(proximo);
57 | }
58 |
59 | // Não há necessidade de reiniciar contadores quando o próximo já o reiniciava.
60 | for (let contador in contadoresProximo.hash) {
61 | if (contadoresNovos.hash[contador]) {
62 | contadoresNovos.hash[contador] = false;
63 | i++;
64 | }
65 | }
66 | }
67 |
68 | return retorno;
69 | }
70 |
71 | /**
72 | * Extrai os contadores reinciiados por este elemento.
73 | *
74 | * @param {Element} dispositivo
75 | * @param {Object} contadoresDesconsiderar Hash contendo os contadores que não deverão ser considerados.
76 | */
77 | function extrairContadores(dispositivo, contadoresDesconsiderar) {
78 | let counterReset = getComputedStyle(dispositivo).counterReset;
79 | let contadores = {};
80 | let cnt = 0;
81 |
82 | for (let regexp = /(.+?)\s\d+\s*/g, m = regexp.exec(counterReset); m; m = regexp.exec(counterReset)) {
83 | let contador = m[1];
84 |
85 | if (!contadoresDesconsiderar || !contadoresDesconsiderar[contador]) {
86 | contadores[contador] = true;
87 | cnt++;
88 | }
89 | }
90 |
91 | return { total: cnt, hash: contadores };
92 | }
93 |
94 | /**
95 | * Efetua a redefinição do contador.
96 | *
97 | * @param {Element} dispositivo
98 | */
99 | function redefinirContador(dispositivo) {
100 | let tipo = dispositivo.getAttribute('data-tipo');
101 | dispositivo.removeAttribute('data-tipo');
102 | setTimeout(() => dispositivo.setAttribute('data-tipo', tipo));
103 | }
104 |
105 | /**
106 | * O Chrome tem um problema de desempenho que fica evidente ao importar
107 | * o LexML da Constituição Federal, selecionar todo o texto no editor
108 | * de articulação, e pressionar qualquer tecla que iria remover o texto
109 | * selecionado (seja por exclusão ou substituição).
110 | *
111 | * Para cada elemento no DOM a ser excluído ou substituído, o navegador
112 | * processa os estilos do próximo elemento (conforme ferramenta de profile
113 | * do próprio navegador), tornando a operação O(n²). Para contornar o problema,
114 | * realizamos a exclusão do conteúdo via API, interceptando o keydown.
115 | *
116 | * @param {KeyboardEvent} keyboardEvent
117 | * @param {EditorArticulacaoController} editorCtrl
118 | */
119 | function hackInterceptarKeydown(keyboardEvent, editorCtrl) {
120 | /* Somente serão tratadas as alterações de conteúdo. Portanto,
121 | * se houver qualquer tecla modificativa (ctrl, alt ou meta),
122 | * o evento será ignorado. O evento só será tratado se a tecla for
123 | * de conteúdo (letra, número ou enter), ou remoção (delete, backspace).
124 | */
125 | if (!keyboardEvent.ctrlKey && !keyboardEvent.altKey && !keyboardEvent.metaKey &&
126 | keyboardEvent.key.length === 1 || keyboardEvent.key === 'Delete' || keyboardEvent.key === 'Backspace' ||
127 | keyboardEvent.key === 'Enter') {
128 |
129 | let selection = editorCtrl.getSelection();
130 | let range = selection.getRangeAt(0);
131 |
132 | // Se não houver nada selecionado, então não há problema de desempenho.
133 | if (!range.collapsed) {
134 | let inicio = range.startContainer.parentNode;
135 | let final = range.endContainer.parentNode;
136 |
137 | range.deleteContents();
138 |
139 | /* Se a tecla for de remoção, então evitamos a ação padrão.
140 | * Entretanto, se for de conteúdo, então deixamos que a ação
141 | * padrão seja executada, para que o novo conteúdo seja inserido
142 | * no lugar do conteúdo excluído.
143 | */
144 | if (keyboardEvent.key === 'Delete' || keyboardEvent.key === 'Backspace') {
145 | keyboardEvent.preventDefault();
146 | }
147 |
148 | /* O range da seleção, ainda que seja de todo o conteúdo, não abrange
149 | * o elemento inicial e o final. Isto é, se o usuário selecionar tudo
150 | * usando Ctrl+A e prosseguir com a exclusão com a tecla Delete,
151 | * o conteúdo selecionado correpsonderá ao primeiro nó textual do primeiro
152 | * P até o último nó textual do útlimo P.
153 | *
154 | *
nó textual
155 | *
nó textual intermediário
156 | *
último nó textual<-- final da seleção -->
157 | *
158 | * Assim, a exclusão irá remover todos os nós textuais e os elementos intermediários,
159 | * mantendo, entretanto, o primeiro e último elemento (veja ilustração acima,
160 | * em que a seleção foi demarcada em comentário).
161 | *
162 | * Para contornar esta situação, realizamos manualmente a exclusão dos elementos,
163 | * se o elemento inicial da seleção for diferente do final, condicionando
164 | * a exclusão à presença de conteúdo. Deve-se executar o procedimento somente
165 | * se o início for diferente do final, pois a seleção pode ser apenas de
166 | * conteúdo intermediário, como ilustrado a seguir:
167 | *
168 | *
este é um exemplo.
169 | */
170 | if (inicio !== final) {
171 | if (inicio != editorCtrl._elemento && inicio.textContent.length === 0) {
172 | inicio.remove();
173 | }
174 |
175 | if (final != editorCtrl._elemento && final.textContent.length === 0) {
176 | final.remove();
177 | }
178 |
179 | /* Caso todo o conteúdo estivesse selecionado, o editor de articulação
180 | * passará a ter nenhum elemento neste momento. Neste caso,
181 | * deve-se recriar o conteúdo mínimo.
182 | */
183 | if (editorCtrl._elemento.children.length === 0) {
184 | editorCtrl.limpar();
185 | }
186 | }
187 | // ... mas se a seleção for todo o conteúdo de um único elemento...
188 | else if (inicio.textContent.length === 0 && inicio.children.length === 0) {
189 | /* então deve-se garantir o conteúdo mínimo, para que o cursor do parágrafo
190 | * fique posicionado corretamente.
191 | */
192 | inicio.innerHTML = ' ';
193 | }
194 | }
195 | }
196 | }
197 |
198 | export default hackChrome;
--------------------------------------------------------------------------------
/src/ClipboardController.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | import { interceptarApos } from './hacks/interceptador';
19 | import { interpretarArticulacao, transformarQuebrasDeLinhaEmP } from './interpretadorArticulacao';
20 |
21 | /**
22 | * Controlador da área de transferência do editor de articulação.
23 | */
24 | class ClipboardController {
25 | constructor(editorCtrl, validacaoCtrl) {
26 | this.editorCtrl = editorCtrl;
27 | this.validacaoCtrl = validacaoCtrl;
28 |
29 | editorCtrl.registrarEventListener('paste', (event) => aoColar(event, this));
30 | }
31 |
32 | /**
33 | * Cola um texto de articulação no editor.
34 | *
35 | * @param {String} texto
36 | */
37 | colarTexto(texto) {
38 | let selecao = this.editorCtrl.getSelection();
39 | let range = selecao.getRangeAt(0);
40 |
41 | if (!range.collapsed) {
42 | range.deleteContents();
43 | this.editorCtrl.atualizarContexto();
44 | }
45 |
46 | for (let brs = (range.endContainer.nodeType === Node.TEXT_NODE ? range.endContainer.parentNode : range.endContainer).querySelectorAll('br'), i = 0; i < brs.length; i++) {
47 | brs[i].remove();
48 | }
49 |
50 | let fragmento = transformar(texto, this.editorCtrl.contexto.cursor.tipo, this.editorCtrl.contexto.cursor.continuacao);
51 |
52 | colarFragmento(fragmento, this.editorCtrl, this.validacaoCtrl);
53 | }
54 | }
55 |
56 | /**
57 | * Transforma um texto em um fragmento a ser colado.
58 | *
59 | * @param {*} texto Texto a ser transformado.
60 | * @param {*} tipo Tipo do contexto atual do cursor.
61 | * @param {*} continuacao Se o tipo é uma continuação.
62 | */
63 | function transformar(texto, tipo, continuacao) {
64 | var fragmento;
65 |
66 | if (continuacao) {
67 | fragmento = transformarTextoPuro(texto, 'continuacao');
68 | } else {
69 | let dados = interpretarArticulacao(texto, 'json');
70 |
71 | if (dados.textoAnterior) {
72 | fragmento = transformarTextoPuro(dados.textoAnterior, tipo || 'artigo');
73 | }
74 |
75 | if (dados.articulacao.length > 0) {
76 | let fragmentoArticulacao = transformarArticulacao(dados.articulacao);
77 |
78 | if (fragmento) {
79 | fragmento.appendChild(fragmentoArticulacao);
80 | } else {
81 | fragmento = fragmentoArticulacao;
82 | }
83 | }
84 | }
85 |
86 | return fragmento;
87 | }
88 |
89 | /**
90 | * Trata a colagem da área de transferência.
91 | *
92 | * @param {ClipboardEvent} event
93 | * @param {ClipboardController} clipboardCtrl
94 | */
95 | function aoColar(event, clipboardCtrl) {
96 | var clipboardData = event.clipboardData || window.clipboardData;
97 | var itens = clipboardData.items;
98 |
99 | if (itens) {
100 | for (let i = 0; i < itens.length; i++) {
101 | if (itens[i].type === 'text/plain') {
102 | itens[i].getAsString(clipboardCtrl.colarTexto.bind(clipboardCtrl));
103 | event.preventDefault();
104 | }
105 | }
106 | } else if (clipboardData.getData) {
107 | clipboardCtrl.colarTexto(clipboardData.getData('text/plain'));
108 | event.preventDefault();
109 | }
110 | }
111 |
112 | /**
113 | * Cola um fragmento no DOM.
114 | *
115 | * @param {DocumentFragment} fragmento
116 | * @param {EditorArticulacaoController} editorCtrl
117 | * @param {ValidacaoController} validacaoCtrl
118 | */
119 | function colarFragmento(fragmento, editorCtrl, validacaoCtrl) {
120 | prepararDesfazer(fragmento, editorCtrl);
121 |
122 | let proximaSelecao = fragmento.lastChild;
123 | let selecao = editorCtrl.getSelection();
124 | let range = selecao.getRangeAt(0);
125 | let noInicial = range.startContainer;
126 | let excluirNoAtual = fragmento.firstChild.nodeType !== Node.TEXT_NODE && noInicial !== editorCtrl._elemento && noInicial.textContent.trim().length === 0;
127 | let primeiroElementoAValidar;
128 |
129 | // Se a seleção estiver no container, então devemos inserir elementos filhos...
130 | if (range.collapsed && noInicial === editorCtrl._elemento) {
131 | // Remove as quebras de linha, usada como placeholder pelos contentEditable.
132 | for (let itens = editorCtrl._elemento.querySelectorAll('br'), i = 0; i < itens.length; i++) {
133 | itens[i].remove();
134 | }
135 |
136 | // Remove os elementos vazios.
137 | for (let itens = editorCtrl._elemento.querySelectorAll('*:empty'), i = 0; i < itens.length; i++) {
138 | itens[i].remove();
139 | }
140 |
141 | // Transforma os nós textuais em parágrafos.
142 | for (let item = fragmento.firstChild; item && item.nodeType === Node.TEXT_NODE; item = fragmento.firstChild) {
143 | let p = document.createElement('p');
144 | p.appendChild(item);
145 | p.setAttribute('data-tipo', 'artigo');
146 | fragmento.insertBefore(p, fragmento.firstElementChild);
147 | }
148 |
149 | primeiroElementoAValidar = fragmento.firstElementChild;
150 | range.insertNode(fragmento);
151 | } else {
152 | if (excluirNoAtual) {
153 | primeiroElementoAValidar = fragmento.firstElementChild;
154 | } else {
155 | primeiroElementoAValidar = noInicial.nodeType === Node.ELEMENT_NODE ? noInicial : noInicial.parentElement;
156 | }
157 |
158 | // Insere os primeiros nós textuais na própria seleção.
159 | for (let item = fragmento.firstChild; item && item.nodeType === Node.TEXT_NODE; item = fragmento.firstChild) {
160 | range.insertNode(item);
161 | }
162 |
163 | // Insere os elementos em seguida, no container, pois não podem estar aninhados ao elemento da seleção.
164 | let referencia = range.endContainer;
165 |
166 | while (referencia.parentElement !== editorCtrl._elemento) {
167 | referencia = referencia.parentElement;
168 | }
169 |
170 | referencia.parentElement.insertBefore(fragmento, referencia.nextSibling);
171 | }
172 |
173 | if (excluirNoAtual) {
174 | noInicial.remove();
175 | }
176 |
177 | // Altera a seleção.
178 | selecao.removeAllRanges();
179 | range = document.createRange();
180 |
181 | range.setStartAfter(proximaSelecao);
182 | selecao.addRange(range);
183 |
184 | editorCtrl.atualizarContexto();
185 |
186 | // Realiza validação do fragmento colado.
187 | let ultimoElementoAValidar = proximaSelecao.nodeType === Node.ELEMENT_NODE ? proximaSelecao : proximaSelecao.parentElement;
188 |
189 | for (let noAlterado = primeiroElementoAValidar; noAlterado && noAlterado !== ultimoElementoAValidar; noAlterado = noAlterado.nextElementSibling) {
190 | validacaoCtrl.validar(noAlterado);
191 | }
192 | }
193 |
194 | /**
195 | * Realiza uma cópia do fragmento e monitora ctrl+z para remover fragmentos.
196 | *
197 | * @param {DocumentFragment} fragmento
198 | * @param {EditorArticulacaoController} editorCtrl
199 | */
200 | function prepararDesfazer(fragmento, editorCtrl) {
201 | var copia = [];
202 |
203 | for (let i = 0, l = fragmento.childNodes.length; i < l; i++) {
204 | copia.push(fragmento.childNodes[i]);
205 | }
206 |
207 | let desfazer = function() {
208 | let anterior = copia[0].previousSibling;
209 | let posterior = copia[copia.length - 1].nextSibling;
210 |
211 | // Remove os elementos
212 | for (let i = 0; i < copia.length; i++) {
213 | copia[i].remove();
214 | }
215 |
216 | removerListeners();
217 |
218 | // Restaura o cursor.
219 | let selecao = editorCtrl.getSelection();
220 | let range = document.createRange();
221 |
222 | if (anterior) {
223 | range.setStartAfter(anterior);
224 | selecao.removeAllRanges();
225 | selecao.addRange(range);
226 | } else if (posterior) {
227 | range.setStartBefore(posterior);
228 | selecao.removeAllRanges();
229 | selecao.addRange(range);
230 | }
231 | };
232 |
233 | let keyDownListener = function(keyboardEvent) {
234 | // Desfaz se pressionar o ctrl+z
235 | if (keyboardEvent.ctrlKey && (keyboardEvent.key === 'z' || keyboardEvent.key === 'Z')) {
236 | desfazer();
237 | keyboardEvent.preventDefault();
238 | }
239 | };
240 |
241 | let bakExecCommand = document.execCommand;
242 |
243 | let removerListeners = function() {
244 | editorCtrl._elemento.removeEventListener('keydown', keyDownListener);
245 | editorCtrl._elemento.removeEventListener('keypress', removerListeners);
246 | document.execCommand = bakExecCommand;
247 | };
248 |
249 | editorCtrl._elemento.addEventListener('keydown', keyDownListener);
250 | editorCtrl._elemento.addEventListener('keypress', removerListeners);
251 |
252 | document.execCommand = function(comando) {
253 | if (comando === 'undo') {
254 | desfazer();
255 | } else {
256 | return bakExecCommand.apply(document, arguments);
257 | }
258 | };
259 | }
260 |
261 | /**
262 | * Insere texto simples no contexto atual.
263 | *
264 | * @param {String} texto Texto a ser interpretado.
265 | * @param {String} tipo Tipo atual.
266 | * @returns {DocumentFragment} Fragmento gerado para colagem.
267 | */
268 | function transformarTextoPuro(texto, tipo) {
269 | if (texto.length === 0) {
270 | return;
271 | }
272 |
273 | var fragmento = document.createDocumentFragment();
274 | let primeiraQuebra = texto.indexOf('\n');
275 |
276 | if (primeiraQuebra !== 0) {
277 | /* A primeira linha deve ser inserida no dispositivo atual.
278 | * Para tanto, utiliza-se um TextNode.
279 | */
280 | let textNode = document.createTextNode(primeiraQuebra > 0 ? texto.substr(0, primeiraQuebra) : texto);
281 | fragmento.appendChild(textNode);
282 | }
283 |
284 | if (primeiraQuebra !== -1) {
285 | // Demais linhas devem ser criadas em novos parágrafos.
286 | let novoTexto = transformarQuebrasDeLinhaEmP(texto.substr(primeiraQuebra + 1));
287 | fragmento.appendChild(novoTexto);
288 |
289 | /**
290 | * Define o tipo para cada P, formatando automaticamente com base na terminação de dois pontos (:) ou ponto final(.).
291 | */
292 | let anterior = [];
293 |
294 | if (fragmento.firstChild.nodeType === Node.TEXT_NODE && fragmento.firstChild.textContent.endsWith(':')) {
295 | let enumeracao = novoTipoEnumeracao(tipo);
296 |
297 | if (tipo !== enumeracao) {
298 | anterior.push(tipo);
299 | tipo = enumeracao;
300 | }
301 | }
302 |
303 | for (let item = fragmento.firstElementChild; item; item = item.nextElementSibling) {
304 | item.setAttribute('data-tipo', tipo);
305 |
306 | if (item.textContent.endsWith(':')) {
307 | let enumeracao = novoTipoEnumeracao(tipo);
308 |
309 | if (tipo !== enumeracao) {
310 | anterior.push(tipo);
311 | tipo = enumeracao;
312 | }
313 | } else if (anterior.length > 0 && item.textContent.endsWith('.')) {
314 | tipo = anterior.pop();
315 | }
316 | }
317 | }
318 |
319 | return fragmento;
320 | }
321 |
322 | function novoTipoEnumeracao(tipoAtual) {
323 | switch (tipoAtual) {
324 | case 'artigo':
325 | case 'paragrafo':
326 | return 'inciso';
327 |
328 | case 'inciso':
329 | return 'alinea';
330 |
331 | case 'alinea':
332 | return 'item';
333 |
334 | default:
335 | return null;
336 | }
337 | }
338 |
339 | function transformarArticulacao(articulacao) {
340 | let fragmento = document.createDocumentFragment();
341 |
342 | articulacao.forEach(item => fragmento.appendChild(item.paraEditor()));
343 |
344 | return fragmento;
345 | }
346 |
347 | export { ClipboardController, transformarQuebrasDeLinhaEmP, transformarTextoPuro, transformar };
348 | export default ClipboardController;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://badge.fury.io/js/silegismg-editor-articulacao)
2 | [](https://github.com/silegis-mg/editor-articulacao/actions/workflows/node.js.yml)
3 | [](https://codeclimate.com/github/silegis-mg/editor-articulacao/maintainability)
4 |
5 | # Editor de Articulação
6 |
7 | O editor de articulação é uma biblioteca javascript elaborada pela Assembleia Legislativa de Minas Gerais,
8 | como parte do Sistema de Informação Legislativa de Minas Gerais (Silegis-MG).
9 |
10 | Ele permite a edição de texto articulado, formatando e numerando automaticamente artigos, parágrafos,
11 | incisos, alíneas e itens, bem como as divisões em títulos, capítulos, seções e subseções. O texto articulado
12 | é estruturado em formato XML, conforme elemento `Articulacao` definido pelo schema do [LexML](https://github.com/lexml/lexml-xml-schemas/tree/master/src/main/resources/xsd).
13 |
14 | ## Demonstração
15 |
16 | Acesse https://silegis-mg.github.io/editor-articulacao/ para ver uma simples demonstração do editor de articulação funcionando em seu navegador.
17 |
18 | ## Funcionalidades
19 |
20 | * Criação de **rótulo e numeração automática** para dispositivos (artigo, parágrafo, inciso, alínea e item);
21 |
22 | * Formatação padrão dos dispositivos, conforme regras de redação definidas no art. 12 da [LCP 78/2004](http://www.almg.gov.br/consulte/legislacao/completa/completa.html?tipo=LCP&num=78&comp=&ano=2004).
23 |
24 | * A formatação pode ser **configurada**, para atender ao padrão de redação federal, sem alteração no código.
25 |
26 | * Divisão dos artigos em títulos, capítulos, seções e subseções;
27 | * Validação automática de:
28 | * **caixa alta:** artigos e parágrafos não devem ser escritos usando apenas caixa alta;
29 | * **uso de aspas:** citações devem estar entre aspas e terminar com ponto final (.);
30 | * **enumerações:** enumerações devem possuir mais de um elemento;
31 | * **letra maiúscula:** artigos e parágrafos devem ser iniciados com letra maiúscula;
32 | * **pontuação:** artigos e parágrafos devem ser terminados com ponto final (.) ou dois pontos (:), sem espaço antes da pontuação, e enumerações devem ser terminadas com ponto final (.), ponto e vírgula (;) ou dois pontos (:), sem espaço antes da pontuação.;
33 | * **sentença única:** dispositivos devem conter uma única sentença.
34 | * Auto-formatação:
35 | * **ao abrir aspas**, formata-se automaticamente como um texto dentro do caput, permitindo múltiplas linhas dentro das aspas;
36 | * **ao fechar aspas**, formata-se a próxima linha como um novo artigo;
37 | * **ao finalizar com dois pontos**, inicia-se uma enumeração (de artigo ou parágrafo para inciso, de inciso para alínea e de alínea para item);
38 | * **ao finalizar com ponto final**, finaliza-se a enumeração (de item para alínea, de alínea para inciso, de inciso para artigo ou parágrafo);
39 | * **ao dar enter em uma linha vazia**, troca a formatação da linha vazia para artigo, quando formatado como parágrafo, ou encerra a enumeração, quando formatado como inciso, alínea ou item.
40 | * Articulação pode ser importada/exportada de/para XML, seguindo especificação do **LexML**;
41 | * Interpretação de conteúdo colado, de forma a permitir a formatação e numeração automática em dispositivos estruturados.
42 |
43 | ## Como usar a partir do código fonte
44 |
45 | ### Pré-requisitos para compilação
46 |
47 | * [NodeJS com npm](https://nodejs.org/en/download/)
48 |
49 | ### Baixando o editor
50 |
51 | ```
52 | git clone https://github.com/silegis-mg/editor-articulacao.git
53 | ```
54 |
55 | ### Instalação das dependências
56 |
57 | Após baixar o editor, mude para o diretório onde encontram os fontes e instale as dependências:
58 |
59 | ```
60 | cd editor-articulacao
61 | npm install
62 | ```
63 |
64 | ### Executando exemplo
65 |
66 | Finalizado o passo anterior, execute:
67 |
68 | ```
69 | npm start
70 | ```
71 |
72 | Em seguida, basta abrir o navegador no endereço http://localhost:8080/exemplo.html
73 |
74 | ### Testando
75 |
76 | O editor de articulação possui testes automatizados utilizando karma e protractor.
77 |
78 | ```
79 | npm test
80 | ```
81 |
82 | Se estiver utlizando proxy, defina a variável de ambiente http_proxy para que o teste consiga baixar o webdriver do Chrome mais atual.
83 |
84 | ### Gerando pacote para aplicações finais em ES5
85 |
86 | O javascript minificado é gerado por meio do webpack, a partir de uma tarefa do grunt. Existem dois empacotamentos para uso em aplicações finais:
87 |
88 | #### Plain-JS
89 |
90 | O empacotamento `plain-js` define `silegismgEditorArticulacao` como uma função global para transformar um elemento no DOM em um editor de articulação.
91 | Também define a função `silegismgEditorArticulacaoController` para criar o controller, caso o utilizador queira maior controle
92 | da interface de usuário.
93 |
94 | Também é definida a função global [`silegismgInterpretadorArticulacao.interpretar`](#api-interpretador) para interpretação de texto articulado.
95 |
96 | ##### Gerando pacote
97 |
98 | ```
99 | npx grunt build-plain
100 | ```
101 |
102 | É possível incluir o polyfill do babel também, utilizando:
103 |
104 | ```
105 | npx grunt build-plain-polyfill
106 | ```
107 |
108 | ##### Utilizando plain-js
109 |
110 | Existem duas possibilidades para criar o editor de articulação. Uma que incorpora a barra de ferramentas
111 | e outra que apenas vincula o controlador do editor de articulação, permitindo ao utilizador personalizar por
112 | completo a interface de usuário.
113 |
114 | Para criar o editor de articulação com barra de ferramentas padrão, utilize a sintaxe `silegismgEditorArticulacao(elemento, opcoes)`.
115 | Para criar o editor de articulação personalizando por completa a interface de usuário, utilize a sintaxe `silegismgEditorArticulacaoController(elemento, opcoes)`, que retornará o controlador, cujos métodos estão descritos na [API do controlador](#api-controlador). Para exemplo de como personalizar a interface, veja o [arquivo de teste do protractor](test/protractor/teste.html).
116 |
117 | Veja também a [API do interpretador de articulação](#api-interpretador).
118 |
119 | ##### Exemplo
120 |
121 | ```html
122 |
123 |
124 |
127 | ```
128 |
129 | #### Angular 1
130 |
131 | O empacotamento `angular1` registra a diretiva `silegismgEditorArticulacaoConteudo` no módulo `silegismg-editor-articulacao` para AngularJS 1.x.
132 |
133 | ##### Gerando pacote
134 |
135 | ```
136 | npx grunt build-angular1
137 | ```
138 |
139 | ##### Exemplo
140 |
141 | ```html
142 |
143 |
144 | ```
145 |
146 |
147 |
148 | ### Utilizando como módulo ES6 e webpack
149 |
150 | ```
151 | npm install silegismg-editor-articulacao
152 | ```
153 |
154 | JS:
155 | ```js
156 | import { ComponenteEdicao, EditorArticulacaoController, interpretadorArticulacao } from 'silegismg-editor-articulacao';
157 |
158 | const opcoes = { /* ... */ };
159 | var elemento = document.getElementById('exemplo');
160 | var ctrl = new EditorArticulacaoController(elemento, opcoes);
161 | ```
162 |
163 | HTML:
164 | ```html
165 |
166 | ```
167 |
168 | #### Configuração do webpack
169 |
170 | O editor de articulação importa o conteúdo do CSS e manipula em tempo de execução, a fim de aplicar os parâmetros de configuração. Para tanto, deve-se utilizar o seguinte loader para os arquivos CSS deste módulo:
171 |
172 | ```json
173 | {
174 | test: /\.css$/,
175 | use: {
176 | loader: 'css-loader',
177 | options: {
178 | minimize: true,
179 | sourceMap: true
180 | }
181 | }
182 | }
183 | ```
184 |
185 | ## Opções do editor de articulação
186 |
187 | | Atributo | Tipo | Valor padrão | Descrição |
188 | | -------- | ---- | ------------ | --------- |
189 | | shadowDOM | Boolean | false | **(Experimental)** Determina se deve adotar o Shadow DOM, se suportado pelo navegador. |
190 | | transformacaoAutomatica | Boolean | true | Determina se o editor de articulação deve aplicar transformação automática. |
191 | | escaparXML | Boolean | false | Determina o escapamento de caracteres de código alto unicode durante a exportação para lexmlString. |
192 | | rotulo | [Object](#opcoes.rotulo) | | Determina o sufixo para os rótulos dos dispositivos. |
193 | | validarAoAtribuir | Boolean | true | Determina se deve validar o conteúdo atribuído ao componente. |
194 | | validacao | [Object](#opcoes.validacao) | | Determina as validações que devem ocorrer. |
195 |
196 |
197 |
198 | ### Opções de rótulo
199 |
200 | Todos os atributos de rótulo são do tipo literal (String).
201 |
202 | | Atributo | Valor padrão | Descrição |
203 | | -------- | ------------ | --------- |
204 | | separadorArtigo | – | Separador do rótulo do artigo 1º ao 9º |
205 | | separadorArtigoSemOrdinal | – | Separador do rótulo do artigo 10 em diante |
206 | | separadorParagrafo | – | Separador do rótulo do parágrafo 1º ao 9º |
207 | | separadorParagrafoSemOrdinal | – | Separador do rótulo do parágrafo 10 em diante |
208 | | separadorParagrafoUnico | – | Separador do rótulo parágrafo único |
209 | | separadorInciso | – | Separador do rótulo de inciso |
210 | | separadorAlinea | ) | Separador do rótulo da alínea |
211 | | separadorItem | ) | Separador do rótulo do item |
212 |
213 |
214 |
215 | ### Opções de validação
216 |
217 | Todas as opções de validação são habilitadas (valor true) por padrão.
218 |
219 | | Atributo | Descrição |
220 | | -------- | --------- |
221 | | caixaAlta | Determina se deve validar o uso de caixa alta. |
222 | | citacao | Determina se deve validar o uso de aspas em citações. |
223 | | enumeracaoElementos | Determina se deve validar a presença de múltiplos elementos em uma enumeração. |
224 | | inicialMaiuscula | Determina se deve validar o uso de letra maiúscula no caput do artigo e em parágrafos. |
225 | | pontuacao | Determina se deve validar as pontuações. |
226 | | pontuacaoEnumeracao | Determina se deve validar pontuação de enumeração. |
227 | | sentencaUnica | Determina se deve exigir sentença única no dispositivo. |
228 |
229 |
230 |
231 | ## API do controlador
232 |
233 | | Propriedade/Função | Retorno/Valor | Descrição |
234 | | ------------------ | ------------- | --------- |
235 | | lexml *(propriedade)* | ElementNS | Obtém ou define o XML da articulação no formato LexML. |
236 | | lexmlString *(propriedade)* | String | Obtém ou define o XML da articulação no formato LexML, porém em String. |
237 | | alterado *(propriedade, somente leitura)* | Boolean | Verifica se o editor de articulação sofreu alteração. |
238 | | alterarTipoDispositivoSelecionado(novoTipo) | void | Altera o tipo do dispositivo em que o cursor se encontra, pelo novo tipo (String) fornecido como parâmetro. Os tipos possíveis são: titulo, capitulo, secao, subsecao, artigo, paragrafo, inciso, alinea e continuacao (todos sem acentuação ou cedilha). |
239 | | contexto | object | Obtém o contexto atual do editor |
240 |
241 | ### Eventos do controlador
242 |
243 | | Evento | Disparo | Condição | Classe do evento | Dados do evento |
244 | |--------|---------|----------|------------------|-----------------|
245 | | change | blur | Texto articulado alterado | ArticulacaoChangeEvent | N/A |
246 | | contexto | | Objeto de contexto atualizado | ContextoArticulacaoAtualizadoEvent | ContextoArticulacao |
247 | | transformacao | | Controlador aplicou alguma transformação automática | TransformacaoAutomaticaEvent | Objeto contendo os seguintes atributos: automatica (booleano), tipoAnterior (literal, tipo do elemento antes da alteração), novoTipo (literal, tipo do elemento depois da alteração), transformacao (literal, nome da transformacao), editorArticulacaoCtrl (controller) |
248 |
249 |
250 |
251 |
252 | ## API do interpretador
253 |
254 | Para interpretar um texto puro, transformando em um texto estruturado utilizando LexML, utilize a função interpretar (veja [código-fonte](src/interpretadorArticulacao.js)), com a seguinte sintaxe:
255 |
256 | ```javascript
257 | interpretadorArticulacao.interpretar(texto, formatoDestino, formatoOrigem);
258 | ```
259 |
260 | onde ``texto`` é uma `string`, ``formatoDestino`` é uma das opções "json", "lexml" (padrão) ou "lexmlString" e ``formatoOrigem`` é "texto" (padrão) ou "html".
261 |
262 | Contribuições desejadas
263 | -----------------------
264 | * Identificação de remissões;
265 | * Renumeração automática de remissões, em caso de alterações nos dispositivos;
266 | * Modo de edição de norma, em que alterações a um texto original são consideradas emendas.
267 |
268 | Limitações conhecidas (aceita-se contribuições)
269 | -----------------------------------------------
270 | As limitações conhecidas correspondem a um conjunto de funcionalidade que não funciona como deveria, mas seu comportamento é aceitável para a proposta do editor. Contribuições são bem-vindas.
271 |
272 | * Copiar do editor de articulação e colar em editor externo omite os rótulos;
273 | * Interpretação de artigo com emenda (exemplo: Art. 283-A da Constituição do Estado de Minas Gerais), apesar de haver suporte para importação de LexML com este tipo de dispositivo.
274 |
275 | # Compatibilidade com navegadores
276 |
277 | | Navegador | Compatível | Mantida[1](#rodape1) |
278 | | ---------- |:----------:|:----------:|
279 | | Firefox 52 | ✓ | ✓ |
280 | | Firefox Quantum 57 | ✓ | |
281 | | Chrome 62 | ✓ | ✓ |
282 | | IE 11 | ✓ | |
283 | | Edge | ✓ | |
284 | | Safari | ? | |
285 |
286 | 1: Considera-se compatibilidade com navegador mantida aquela que é constantemente testada pela equipe de desenvolvimento.
287 |
--------------------------------------------------------------------------------
/src/editor-articulacao.d.ts:
--------------------------------------------------------------------------------
1 | import ContextoArticulacao from './ContextoArticulacao';
2 | import interpretadorArticulacao from './interpretadorArticulacao';
3 |
4 | /**
5 | * Transforma um elemento do DOM em um editor de articulação com
6 | * barra de ferramentas.
7 | */
8 | declare class ComponenteEdicao {
9 | /**
10 | * Cria um controlador do Editor de Articulação vinculado a um elemento do DOM.
11 | *
12 | * @param Elemento que receberá o editor de articulação.
13 | * @param opcoes Opções do editor de articulação.
14 | */
15 | constructor(elemento: HTMLElement, opcoes?: IOpcoesEditorArticulacaoController);
16 | }
17 |
18 | declare class EditorArticulacaoController {
19 | /**
20 | * Cria um controlador do Editor de Articulação vinculado a um elemento do DOM.
21 | *
22 | * @param Elemento que receberá o editor de articulação.
23 | * @param opcoes Opções do editor de articulação.
24 | */
25 | constructor(elemento: HTMLElement, opcoes?: IOpcoesEditorArticulacaoController);
26 |
27 | /**
28 | * Obtém o XML da articulação no formato LexML.
29 | */
30 | get lexml(): Element | DocumentFragment;
31 |
32 | /**
33 | * Define o XML da articulação no formato LexML.
34 | */
35 | set lexml(valor: Element | DocumentFragment);
36 |
37 | /**
38 | * Obtém o XML da articulação no formato LexML, porém em String.
39 | */
40 | get lexmlString(): string;
41 |
42 | /**
43 | * Define o XML da articulação no formato LexML, porém em String.
44 | */
45 | set lexmlString(valor: string);
46 |
47 | /**
48 | * Verifica se o editor de articulação sofreu alteração.
49 | */
50 | get alterado(): boolean;
51 |
52 | /**
53 | * Altera o tipo do dispositivo em que o cursor se encontra, pelo novo tipo fornecido como parâmetro.
54 | *
55 | * @param novoTipo Novo tipo do dispositivo.
56 | */
57 | alterarTipoDispositivoSelecionado(novoTipo: TipoDispositivo): void;
58 |
59 | get contexto(): ContextoArticulacao;
60 | }
61 |
62 | declare interface IOpcoesEditorArticulacaoController {
63 | /**
64 | * Determina se deve adotar o Shadow DOM, caso suportado pelo navegador.
65 | * (Padrão: false)
66 | */
67 | shadowDOM?: boolean;
68 |
69 | /**
70 | * Determina se o editor de articulação deve aplicar transformação automática.
71 | * (Padrão: true)
72 | */
73 | transformacaoAutomatica?: boolean;
74 |
75 | /**
76 | * Determina o escapamento de caracteres de código alto unicode durante
77 | * a exportação para lexmlString.
78 | * (Padrão: false)
79 | */
80 | escaparXML?: boolean;
81 |
82 | /**
83 | * Determina o sufixo para os rótulos dos dispositivos.
84 | */
85 | rotulo?: {
86 | /**
87 | * Separador do rótulo do artigo 1º ao 9º
88 | */
89 | separadorArtigo?: string;
90 |
91 | /**
92 | * Separador do rótulo do artigo 10 em diante
93 | */
94 | separadorArtigoSemOrdinal?: string;
95 |
96 | /**
97 | * Separador do rótulo do parágrafo 1º ao 9º
98 | */
99 | separadorParagrafo?: string;
100 |
101 | /**
102 | * Separador do rótulo do parágrafo 10 em diante
103 | */
104 | separadorParagrafoSemOrdinal?: string;
105 |
106 | /**
107 | * Separador do rótulo parágrafo único
108 | */
109 | separadorParagrafoUnico?: string;
110 |
111 | /**
112 | * Separador do rótulo de inciso
113 | */
114 | separadorInciso?: string;
115 |
116 | /**
117 | * Separador do rótulo da alínea
118 | */
119 | separadorAlinea?: string;
120 |
121 | /**
122 | * Separador do rótulo do item
123 | */
124 | separadorItem?: string;
125 | };
126 |
127 | /**
128 | * Determina se deve validar o conteúdo atribuído ao componente.
129 | * (Padrão: true)
130 | */
131 | validarAoAtribuir?: boolean;
132 |
133 | /**
134 | * Determina as validações que devem ocorrer.
135 | */
136 | validacao?: {
137 | /**
138 | * Determina se deve validar o uso de caixa alta.
139 | */
140 | caixaAlta?: boolean;
141 |
142 | /**
143 | * Determina se deve validar o uso de aspas em citações.
144 | */
145 | citacao?: boolean;
146 |
147 | /**
148 | * Determina se deve validar a presença de múltiplos elementos em uma enumeração.
149 | */
150 | enumeracaoElementos?: boolean;
151 |
152 | /**
153 | * Determina se deve validar o uso de letra maiúscula no caput do artigo e em parágrafos.
154 | */
155 | inicialMaiuscula?: boolean;
156 |
157 | /**
158 | * Determina se deve validar as pontuações.
159 | */
160 | pontuacao?: boolean;
161 |
162 | /**
163 | * Determina se deve validar pontuação de enumeração.
164 | */
165 | pontuacaoEnumeracao?: boolean;
166 |
167 | /**
168 | * Determina se deve exigir sentença única no dispositivo.
169 | */
170 | sentencaUnica?: boolean;
171 | }
172 | }
173 |
174 | declare enum TipoDispositivo {
175 | TITULO = 'titulo',
176 | CAPITULO = 'capitulo',
177 | SECAO = 'secao',
178 | SUBSECAO = 'subsecao',
179 | ARTIGO = 'artigo',
180 | PARAGRAFO = 'paragrafo',
181 | INCISO = 'inciso',
182 | ALINEA = 'alinea',
183 | CONTINUACAO = 'continuacao'
184 | }
185 |
186 | declare const interpretadorArticulacao = {
187 | Artigo,
188 | Paragrafo,
189 | Inciso,
190 | Alinea,
191 | Item,
192 | Titulo,
193 | Capitulo,
194 | Secao,
195 | Subsecao,
196 | transformarQuebrasDeLinhaEmP,
197 | interpretar
198 | }
199 |
200 | /**
201 | * Interpreta conteúdo de articulação.
202 | *
203 | * @param texto Texto a ser interpretado
204 | * @param formatoDestino Formato a ser retornado: 'json', 'lexml' (padrão) ou "lexmlString".
205 | * @param formatoOrigem Formatao a ser processado: 'texto' (padrão), 'html'.
206 | */
207 | function interpretar(texto: string, formatoDestino: TFORMATO_DESTINO = 'lexml', formatoOrigem: TFORMATO_ORIGEM = 'texto'): IResultadoInterpretacao | Element | DocumentFragment | string;
208 |
209 | /**
210 | * Interpreta conteúdo de articulação.
211 | *
212 | * @param texto Texto a ser interpretado
213 | */
214 | function interpretar(texto: string): Element | DocumentFragment;
215 |
216 | /**
217 | * Interpreta conteúdo de articulação.
218 | *
219 | * @param texto Texto a ser interpretado
220 | * @param formatoDestino Formato a ser retornado: 'json', 'lexml' (padrão) ou "lexmlString".
221 | * @param formatoOrigem Formatao a ser processado: 'texto' (padrão), 'html'.
222 | */
223 | function interpretar(texto: string, formatoDestino: 'json', formatoOrigem: TFORMATO_ORIGEM = 'texto'): IResultadoInterpretacao;
224 |
225 | /**
226 | * Interpreta conteúdo de articulação.
227 | *
228 | * @param texto Texto a ser interpretado
229 | * @param formatoDestino Formato a ser retornado: 'json', 'lexml' (padrão) ou "lexmlString".
230 | * @param formatoOrigem Formatao a ser processado: 'texto' (padrão), 'html'.
231 | */
232 | function interpretar(texto: string, formatoDestino: 'lexml', formatoOrigem: TFORMATO_ORIGEM = 'texto'): Element | DocumentFragment;
233 |
234 | /**
235 | * Interpreta conteúdo de articulação.
236 | *
237 | * @param texto Texto a ser interpretado
238 | * @param formatoDestino Formato a ser retornado: 'json', 'lexml' (padrão) ou "lexmlString".
239 | * @param formatoOrigem Formatao a ser processado: 'texto' (padrão), 'html'.
240 | */
241 | function interpretar(texto: string, formatoDestino: 'lexmlString', formatoOrigem: TFORMATO_ORIGEM = 'texto'): string;
242 |
243 | declare interface IResultadoInterpretacao {
244 | textoAnterior: string;
245 | articulacao: Dispositivo[];
246 | }
247 |
248 | declare type TFORMATO_DESTINO = 'json' | 'lexml' | 'lexmlString';
249 |
250 | declare type TFORMATO_ORIGEM = 'texto' | 'html';
251 |
252 | declare class Dispositivo {
253 | constructor(tipo: string, numero: string, descricao: string, derivacoes: string[]);
254 |
255 | adicionar(dispositivo: Dispositivo);
256 |
257 | /**
258 | * Transforma o conteúdo na descrição em fragmento do DOM.
259 | */
260 | transformarConteudoEmFragmento(): DocumentFragment;
261 |
262 | /**
263 | * Transforma o dispositivo no formato do editor.
264 | */
265 | paraEditor(): DocumentFragment;
266 | }
267 |
268 | declare class Artigo extends Dispositivo {
269 | constructor(numero: string, caput: string);
270 |
271 | adicionar(incisoOuParagrafo: Inciso | Paragrafo);
272 | }
273 |
274 | declare class Paragrafo extends Dispositivo {
275 | constructor(numero: string, descricao: string);
276 |
277 | adicionar(inciso: Inciso);
278 | }
279 |
280 | declare class Inciso extends Dispositivo {
281 | constructor(numero: string, descricao: string);
282 |
283 | adicionar(alinea: Alinea);
284 | }
285 |
286 | declare class Alinea extends Dispositivo {
287 | constructor(numero: string, descricao: string);
288 |
289 | adicionar(item: Item);
290 | }
291 |
292 | declare class Item extends Dispositivo {
293 | constructor(numero: string, descricao: string);
294 | }
295 |
296 | declare class Divisao extends Dispositivo {
297 | constructor(tipo: string, numero: string, descricao: string);
298 |
299 | adicionar(item);
300 | }
301 |
302 | declare class Titulo extends Divisao {
303 | constructor(numero: string, descricao: string);
304 | }
305 |
306 | declare class Capitulo extends Divisao {
307 | constructor(numero: string, descricao: string);
308 | }
309 |
310 | declare class Secao extends Divisao {
311 | constructor(numero: string, descricao: string);
312 | }
313 |
314 | declare class Subsecao extends Divisao {
315 | constructor(numero: string, descricao: string);
316 | }
317 |
318 | /**
319 | * Representa o contexto do usuário no editor de articulação.
320 | * Possui dois atributos: cursor, contendo o contexto no cursor,
321 | * e as permissões de alteração de dispositivo na seleção.
322 | */
323 | declare class ContextoArticulacao {
324 | constructor(elementoArticulacao: HTMLElement, dispositivo: Dispositivo);
325 |
326 | comparar(obj2: ContextoArticulacao): boolean {
327 | for (let i in this.cursor) {
328 | if (this.cursor[i] !== obj2.cursor[i]) {
329 | return true;
330 | }
331 | }
332 |
333 | for (let i in this.permissoes) {
334 | if (this.permissoes[i] !== obj2.permissoes[i]) {
335 | return true;
336 | }
337 | }
338 |
339 | return false;
340 | }
341 |
342 | /**
343 | * Determina se o contexto atual é válido.
344 | *
345 | * @returns {Boolean} Verdadeiro, se o contexto estiver válido.
346 | */
347 | get valido(): boolean;
348 |
349 | /**
350 | * Verifica quais tipos de dispositivos são permitidos no contexto atual.
351 | */
352 | readonly permissoes: {
353 | readonly titulo: boolean;
354 | readonly capitulo: boolean;
355 | readonly secao: boolean;
356 | readonly subsecao: boolean;
357 | readonly artigo: boolean;
358 | readonly continuacao: boolean;
359 | readonly inciso: boolean;
360 | readonly paragrafo: boolean;
361 | readonly alinea: boolean;
362 | readonly item: boolean;
363 | }
364 |
365 | /**
366 | * Informações sobre o contexto onde está o cursor.
367 | */
368 | readonly cursor: {
369 | /**
370 | * Indica se o cursor está em um trecho com itálico.
371 | */
372 | readonly italico: boolean;
373 |
374 | /**
375 | * Indica se o cursor está em um elemento desconhecido.
376 | */
377 | readonly desconhecido: boolean;
378 |
379 | /**
380 | * Indica se o cursor está em um título.
381 | */
382 | readonly titulo: boolean;
383 |
384 | /**
385 | * Indica se o cursor está em um capítulo.
386 | */
387 | readonly capitulo: boolean;
388 |
389 | /**
390 | * Indica se o cursor está em uma seção.
391 | */
392 | readonly secao: boolean;
393 |
394 | /**
395 | * Indica se o cursor está em uma subseção.
396 | */
397 | readonly subsecao: boolean;
398 |
399 | /**
400 | * Indica se o cursor está em um artigo.
401 | */
402 | readonly artigo: boolean;
403 |
404 | /**
405 | * Indica se o cursor está em uma continuação (outra linha)
406 | * do dispositivo.
407 | */
408 | readonly continuacao: boolean;
409 |
410 | /**
411 | * Indica se o cursor está em um parágrafo.
412 | */
413 | readonly paragrafo: boolean;
414 |
415 | /**
416 | * Indica se o cursor está em uma alínea.
417 | */
418 | readonly inciso: boolean;
419 |
420 | /**
421 | * Indica se o cursor está em uma alínea.
422 | */
423 | readonly alinea: boolean;
424 |
425 | /**
426 | * Indica se o cursor está em um item.
427 | */
428 | readonly item: boolean;
429 |
430 | /**
431 | * Indica se o cursor está na raíz do editor.
432 | */
433 | readonly raiz: boolean;
434 |
435 | /**
436 | * Elemento do DOM do dispositivo atual.
437 | */
438 | readonly elemento: HTMLElement;
439 |
440 | /**
441 | * Tipo do dispositivo atual.
442 | */
443 | readonly tipo: TipoDispositivo;
444 |
445 | /**
446 | * Dispositivo atual.
447 | */
448 | readonly dispositivo: Dispositivo;
449 |
450 | /**
451 | * Instância do dispositivo anterior.
452 | */
453 | readonly dispositivoAnterior: Dispositivo;
454 |
455 | /**
456 | * Tipo do dispositivo anterior.
457 | */
458 | readonly tipoAnterior: TipoDispositivo;
459 | }
460 | }
461 |
462 | export = {
463 | ComponenteEdicao,
464 | EditorArticulacaoController,
465 | interpretadorArticulacao
466 | }
--------------------------------------------------------------------------------
/test/protractor/formatacaoDispositivos.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2017 Assembleia Legislativa de Minas Gerais
2 | *
3 | * This file is part of Editor-Articulacao.
4 | *
5 | * Editor-Articulacao is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, version 3.
8 | *
9 | * Editor-Articulacao is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with Editor-Articulacao. If not, see .
16 | */
17 |
18 | const { $describe, escrever } = require('./utilitariosTeste');
19 |
20 | $describe('Formatação do editor de articulação', function(it) {
21 | var editor = element(by.id('articulacao'));
22 | var resultado = element(by.id('lexml'));
23 |
24 | function finalizar() {
25 | browser.actions()
26 | .mouseMove(element(by.id('atualizar')))
27 | .click()
28 | .perform();
29 | }
30 |
31 | it('Vários artigos', function() {
32 | escrever('Primeiro.' + protractor.Key.ENTER, true);
33 | escrever('Segundo.' + protractor.Key.ENTER, true);
34 | escrever('Terceiro.' + protractor.Key.ENTER, true);
35 | escrever('Quarto.' + protractor.Key.ENTER, true);
36 | escrever('Quinto.' + protractor.Key.ENTER, true);
37 | escrever('Sexto.' + protractor.Key.ENTER, true);
38 | escrever('Sétimo.' + protractor.Key.ENTER, true);
39 | escrever('Oitavo.' + protractor.Key.ENTER, true);
40 | escrever('Nono.' + protractor.Key.ENTER, true);
41 | escrever('Dez.');
42 |
43 | finalizar();
44 |
45 | expect(resultado.getText()).toEqual('Art. 1º –
Primeiro.
Art. 2º –
Segundo.
Art. 3º –
Terceiro.
Art. 4º –
Quarto.
Art. 5º –
Quinto.
Art. 6º –
Sexto.
Art. 7º –
Sétimo.
Art. 8º –
Oitavo.
Art. 9º –
Nono.
Art. 10 –
Dez.
');
46 | });
47 |
48 | it('Ao digitar dois pontos, deve avançar o nível e recuar ao dar enter em linha vazia', function() {
49 | escrever('Este é um artigo:' + protractor.Key.ENTER);
50 | escrever('Este é um inciso:' + protractor.Key.ENTER);
51 | escrever('Esta é uma alínea:' + protractor.Key.ENTER);
52 | escrever('Este é um item.');
53 | escrever(protractor.Key.ENTER);
54 | escrever(protractor.Key.ENTER);
55 | escrever(protractor.Key.ENTER);
56 | escrever('Segundo artigo.');
57 |
58 | finalizar();
59 |
60 | expect(resultado.getText()).toEqual('Art. 1º –
Este é um artigo:
I –
Este é um inciso:
a)
Esta é uma alínea:
1)
Este é um item.
Art. 2º –
Segundo artigo.
');
61 | });
62 |
63 | it('Deve-se sair do inciso automaticamente quando for terminado com ponto final', function() {
64 | escrever('Este é um artigo:' + protractor.Key.ENTER);
65 | escrever('este é um inciso;' + protractor.Key.ENTER);
66 | escrever('este é outro inciso.' + protractor.Key.ENTER);
67 | escrever('Este é outro artigo.');
68 |
69 | finalizar();
70 |
71 | expect(resultado.getText()).toEqual('Art. 1º –
Este é um artigo:
I –
este é um inciso;
II –
este é outro inciso.
Art. 2º –
Este é outro artigo.
');
72 | });
73 |
74 | it('Quando houver apenas um parágrafo, deve formatar como parágrafo único', function() {
75 | escrever('Este é um artigo.' + protractor.Key.ENTER);
76 | escrever('Este é um parágrafo.');
77 |
78 | browser.actions()
79 | .mouseMove(element(by.css('button[data-tipo-destino="paragrafo"]')))
80 | .click()
81 | .perform();
82 |
83 | finalizar();
84 |
85 | expect(resultado.getText()).toEqual('Art. 1º –
Este é um artigo.
Parágrafo único –
Este é um parágrafo.
');
86 | });
87 |
88 | it('Quando houver dois parágrafos, a formatação deve ser numérica', function() {
89 | escrever('Este é um artigo.' + protractor.Key.ENTER);
90 |
91 | browser.actions()
92 | .mouseMove(element(by.css('button[data-tipo-destino="paragrafo"]')))
93 | .click()
94 | .perform();
95 |
96 | browser.sleep(250);
97 |
98 | escrever('Este é um parágrafo.' + protractor.Key.ENTER);
99 | escrever('Este é outro parágrafo.');
100 |
101 | finalizar();
102 |
103 | expect(resultado.getText()).toEqual('Art. 1º –
Este é um artigo.
§ 1º –
Este é um parágrafo.
§ 2º –
Este é outro parágrafo.
');
104 | });
105 |
106 | it('Incisos podem estar no caput e em parágrafos', function() {
107 | escrever('Este é um artigo:' + protractor.Key.ENTER);
108 | escrever('Este é um inciso.' + protractor.Key.ENTER);
109 |
110 | browser.actions()
111 | .mouseMove(element(by.css('button[data-tipo-destino="paragrafo"]')))
112 | .click()
113 | .perform();
114 |
115 | browser.sleep(250);
116 |
117 | escrever('Este é um parágrafo:' + protractor.Key.ENTER);
118 | escrever('Este é o inciso do parágrafo.' + protractor.Key.ENTER);
119 | escrever('Este é outro parágrafo formatado automaticamente.');
120 |
121 | finalizar();
122 |
123 | expect(resultado.getText()).toEqual('Art. 1º –
Este é um artigo:
I –
Este é um inciso.
§ 1º –
Este é um parágrafo:
I –
Este é o inciso do parágrafo.
§ 2º –
Este é outro parágrafo formatado automaticamente.
');
124 | });
125 |
126 | it('Remoção de parágrafo deve mover inciso para caput.', function() {
127 | escrever('Este é um artigo:' + protractor.Key.ENTER);
128 | escrever('este é um inciso;' + protractor.Key.ENTER);
129 |
130 | browser.actions()
131 | .mouseMove(element(by.css('button[data-tipo-destino="paragrafo"]')))
132 | .click()
133 | .perform();
134 |
135 | browser.sleep(250);
136 |
137 | escrever('Este é um parágrafo:' + protractor.Key.ENTER);
138 | escrever('este é o inciso do parágrafo.');
139 |
140 | for (let i = 'este é o inciso do parágrafo.'.length; i >= 0; i--) {
141 | escrever(protractor.Key.LEFT, true);
142 | }
143 |
144 | browser.actions().keyDown(protractor.Key.SHIFT)
145 | .sendKeys(protractor.Key.HOME)
146 | .keyUp(protractor.Key.SHIFT)
147 | .perform();
148 |
149 | browser.sleep(250);
150 |
151 | browser.actions().sendKeys(protractor.Key.BACK_SPACE).sendKeys(protractor.Key.BACK_SPACE).perform();
152 |
153 | browser.sleep(250)
154 |
155 | finalizar();
156 |
157 | expect(resultado.getText()).toEqual('Art. 1º –
Este é um artigo:
I –
este é um inciso;
II –
este é o inciso do parágrafo.
');
158 | });
159 |
160 | it('Deve ser possível escrever citação com única linha', function() {
161 | escrever('Estou testando:' + protractor.Key.ENTER);
162 | escrever('"Esta é a única linha da citação.".' + protractor.Key.ENTER);
163 | escrever('Segundo artigo.');
164 |
165 | finalizar();
166 |
167 | expect(resultado.getText()).toEqual('Art. 1º –
Estou testando:
"Esta é a única linha da citação.".
Art. 2º –
Segundo artigo.
');
168 | });
169 |
170 | it('Deve ser possível escrever citação com dois parágrafos', function() {
171 | escrever('Estou testando:' + protractor.Key.ENTER);
172 | escrever('"Esta é a primeira linha da citação.' + protractor.Key.ENTER);
173 | escrever('Esta é a segunda linha da citação.".' + protractor.Key.ENTER);
174 | escrever('Segundo artigo.');
175 |
176 | finalizar();
177 |
178 | expect(resultado.getText()).toEqual('Art. 1º –