1
|
|
|
/*!
|
2
|
|
|
* MultiformTextEditor - jQuery plugin for rich text editing
|
3
|
|
|
* version: 1.0.0 (30 Nov 2017)
|
4
|
|
|
* @requires jQuery v2.0 or later
|
5
|
|
|
*
|
6
|
|
|
* License: MIT License
|
7
|
|
|
*
|
8
|
|
|
* Copyright (c) 2017 Multi4me - [email protected]
|
9
|
|
|
*
|
10
|
|
|
*/
|
11
|
|
|
|
12
|
|
|
|
13
|
|
|
|
14
|
|
|
/*Focado em programadores com experiência em JavaScript e JQuery, o objetivo do plugin é ser uma forma de editar texto com a flexibilidade que permita adicionar qualquer tipo de formatação e inserção sem a necessidade de alterar a base do plugin e usando qualquer estrutura HTML e estilização que se queira.
|
15
|
|
|
- O protótipo "formatTxt()" aplica os eventos aos itens do menu e caixa de edição para destaque, detecção e aplicação dos formatos e montagens. Usa o objeto "datafrmt_obj" para aplicação das edições com o comando "execCommand(comando, ShowDefaultUI(false), valor(opcional))" e outras possibilidades de formatação.
|
16
|
|
|
- É possível, por exemplo, adicionar uma lista de formas em SVG (retângulo, losango, elipse, etc.) para serem inseridos no texto, algo que não é padrão dos plugins de edição, ou mesmo um item para aplicar um conjunto de estilos css ao texto selecionado por meio de uma classe pré-criada no css da página.
|
17
|
|
|
- O programador deverá seguir os padrões do objeto "datafrmt_obj" para adicionar propriedades e métodos para formatar, manipular e inserir na caixa editável ou usando o que já está setado (negrito, itálico, sublinhado, ...), podendo reescrever os dados padrões, seus rótulos e valores.
|
18
|
|
|
- Como caixa de texto deve-se usar um elemento (não-input) com o atributo "contenteditable" valendo "true" e o menu deve ser montado seguindo apenas alguns padrões:
|
19
|
|
|
- cada item de formatação terá a classe "frmttxt-mn-it" e nele o atributo "data-frmt=*" possuirá o mesmo rótulo da formatação referenciada no objeto "datafrmt_obj", opcionalmente podendo-se usar "data-val=*" para quando se quer passar valor à formatação, embora seja possível passar valor de diferentes formas;
|
20
|
|
|
- elementos que listam um grupo de itens de formatação (como uma paleta de cores, em que cada cor é um elemento "frmttxt-mn-it") terá a classe "frmttxt-mn-gp", que quando clicado exibirá seu filho com a classe "frmttxt-mn-drpdn" que é quem possui o conjunto de itens de formatação;
|
21
|
|
|
- se um elemento em algum lugar no menu for necessário para manipular elementos e dados antes e durante as formatações (como dois elementos para o usuário selecionar se quer aplicar cor ao texto ou fundo), deve-se setar a classe "frmttxt-mn-mp" e o atributo "data-mnpl=*" com o nome do método criado no objeto "datamnpl_obj" (deve ser diferente dos nomes de "datafrmt_obj").
|
22
|
|
|
- Veja a explicação do objeto "datafrmt_obj" e "datamnpl_obj" ao final. Ambos podem ser editados pelo programador para se alterar os padrões de formatação já disponíveis.
|
23
|
|
|
- Exemplo de menu de edição e caixa de texto:
|
24
|
|
|
<div class="menu-edit unselectable">
|
25
|
|
|
<b class="frmttxt-mn-it" data-frmt="ngrto">B</b>
|
26
|
|
|
<i class="frmttxt-mn-it" data-frmt="itlco">i</i>
|
27
|
|
|
<u class="frmttxt-mn-it" data-frmt="sblnhdo" data-val="underline">U</u>
|
28
|
|
|
❘
|
29
|
|
|
<div class="frmttxt-mn-gp texto-cor">
|
30
|
|
|
<span>✽<span>
|
31
|
|
|
<div class="frmttxt-mn-drpdn d-nn">
|
32
|
|
|
<span class="frmttxt-mn-it" data-frmt="txtcor" data-val="rgb(255, 255, 255)" style="background: #FFFFFF;"></span><span class="frmttxt-mn-it" data-frmt="txtcor" data-val="rgb(247, 218, 100)" style="background: #F7DA64;"></span>
|
33
|
|
|
<span class="frmttxt-mn-it" data-frmt="txtcor" data-val="rgb(0, 168, 133)" style="background: #00A885;"></span><span class="frmttxt-mn-it" data-frmt="txtcor" data-val="rgb(128, 110, 128)" style="background: #806E80;"></span>
|
34
|
|
|
<span class="frmttxt-mn-it rmv" data-frmt="txtcor" data-val="#888888" style="background: #F1F1F1;">×</span><!-- Usado como remoção de cor de texto ou fundo -->
|
35
|
|
|
<div class="frmttxt-mn-mp" data-mnpl="txtcortp">
|
36
|
|
|
<input id="tipo-cor1" type="radio" name="tipo-cor" value="1" checked><label for="tipo-cor1">Texto</label>
|
37
|
|
|
<input id="tipo-cor2" type="radio" name="tipo-cor" value="2"><label for="tipo-cor2">Fundo</label>
|
38
|
|
|
</div>
|
39
|
|
|
</div>
|
40
|
|
|
</div>
|
41
|
|
|
❘
|
42
|
|
|
<span class="frmttxt-mn-it limpa-frmt" data-frmt="lmpfrmt"> </span>
|
43
|
|
|
</div>
|
44
|
|
|
<div id="txt-edit" contenteditable="true" placeholder="Digite o texto"></div>
|
45
|
|
|
- O exemplo acima foi montado já usando os rótulos das formatações pré-criadas para negrito, itálico, sublinhado e cor e ao ser instanciado, a seguir, acrescenta-se objeto com uma nova formatação para criação de bloco de citação. Uma função é passada como último parâmetro para executar algo após qualquer alteração na caixa de texto:
|
46
|
|
|
$(".menu-edit").formatTxt($("#txt-edit"), {tagcompleta: {tg:["BLOCKQUOTE"], vltgc: function(mnit, slc){
|
47
|
|
|
return "<blockquote>"+(slc.txt || "...")+"</blockquote>";
|
48
|
|
|
}}}, null, function(){console.log("alterou")});
|
49
|
|
|
- Ainda no exemplo acima o programador poderá antes alterar dados das formatações padrões como a "sblnhdo", por exemplo, mudando a forma como texto será sublinhado (aplicando um estilo com atributo "text-decoration") e colocando 'data-val="underline"' no html do item no menu:
|
50
|
|
|
$.fn.formatTxt.datafrmt_obj.sblnhdo = {tg:[{nm:null, atr:"style", ext:"text-decoration"}], vlatr: function(mnit, slc){ return ["style", "text-decoration"]; }};
|
51
|
|
|
- também poderia-se passar o mesmo objeto na aplicação de "formatTxt()" como parâmetro e usando o mesmo nome da formatação padrão ("sblnhdo") para que seja sobrescrita*/
|
52
|
|
|
|
53
|
|
|
|
54
|
|
|
(function($){
|
55
|
|
|
$.fn.formatTxt = function(elmtcx, dfrmt_obj, dmnpl_obj, cllbck){
|
56
|
|
|
return this.each(function(){//Aplica à um ou mais elementos de uma vez
|
57
|
|
|
var elmt_mn = $(this);//elemento instanciado - menu de edição da caixa
|
58
|
|
|
var elmt_cx = (typeof(elmtcx)!="function" ? elmtcx : elmtcx.apply(elmt_mn));
|
59
|
|
|
var dados_obj = $.extend({}, $.fn.formatTxt.datafrmt_obj, dfrmt_obj, $.fn.formatTxt.datamnpl_obj, dmnpl_obj);//funde os objetos padrões aos objetos passados como parâmetro. O "$.extend" já valida cada objeto não usando caso seja nulo, indefinido, falso, etc.
|
60
|
|
|
elmt_mn.data("frmttxt-cx", elmt_cx);//guarda como dado no menu o elemento da caixa específico
|
61
|
|
|
elmt_cx.data("frmttxt-mn", elmt_mn);//guarda como dado na caixa o elemento do menu específico
|
62
|
|
|
var pos_limit;//variáveis globalizadas neste escopo
|
63
|
|
|
//Prepara clique nos itens de edição do menu para formatação no texto, podendo ser subitem de um grupo. Necessário usar evento "mousedown" evitando problema em "click" no IE
|
64
|
|
|
elmt_mn.on("mousedown.mnedt", ".frmttxt-mn-it", function(event){
|
65
|
|
|
event.preventDefault();//necessário para melhoria no IE
|
66
|
|
|
event.stopPropagation();//evita que os subitens reexecutem eventos do pai
|
67
|
|
|
var elmtcxit = $(event.delegateTarget).data("frmttxt-cx");
|
68
|
|
|
//Aplica formatação conforme o item de objeto a ser aplicado, podendo ser simples (por tipo e opcionalmente usando valor, podendo ser um método de captura) ou montando a tag completa usando método
|
69
|
|
|
var slc_obj = $.fn.formatTxt.GetSetSelectObj();//obtém objeto com os dados de seleção
|
70
|
|
|
var dfrmt = $(this).attr("data-frmt");//nome do tipo de formato
|
71
|
|
|
dados_obj[dfrmt].precall ? dados_obj[dfrmt].precall(this, slc_obj) : null;//chama o método de pre-execução (quando existe), manipulando dados e elementos antes de usá-los na formatação
|
72
|
|
|
var dval = $(this).attr("data-val");//valor do formato ou indefinido (se não existe)
|
73
|
|
|
var tp = dados_obj[dfrmt].tp;//tipo de comando (opcional)
|
74
|
|
|
var css = dados_obj[dfrmt].css;//true/false para tipo estilização (opcional)
|
75
|
|
|
var exec = true;//receberá false caso o comando não seja suportado pelo navegador
|
76
|
|
|
var execss;//receberá true/false caso seja usado e o comando para estilização seja suportado ou não pelo navegador
|
77
|
|
|
if(css){//Se for passado como "true", antes de aplicar formatação, tenta executar o comando "styleWithCSS" (não suportado pelo IE)
|
78
|
|
|
try{
|
79
|
|
|
execss = document.execCommand("styleWithCSS", false, "true");//seta para tipo formatação por estilização, ainda retornando true/false conforme sucesso da aplicação
|
80
|
|
|
}catch(e){ execss = false; }
|
81
|
|
|
}
|
82
|
|
|
if(dados_obj[dfrmt].incall){//Se existe, chama o método de execução interna para realizar a formatação toda personalizada
|
83
|
|
|
exec = dados_obj[dfrmt].incall(this, slc_obj, execss);
|
84
|
|
|
}else{
|
85
|
|
|
if(tp){//Simples - executa o tipo de comando a ser aplicado ao texto - "execCommand(CommandName(tp), ShowDefaultUI(false), ValueArgument(null ou informado))"
|
86
|
|
|
var vl = dados_obj[dfrmt].vl ? dados_obj[dfrmt].vl(this, slc_obj) : dval;//se valor é um método de captura usa-o passando elemento como parâmetro, senão: usa "data-val" do elemento
|
87
|
|
|
try{//Tenta executar comando e caso não seja suportado atribui 'false' à variável "exec"
|
88
|
|
|
document.execCommand(tp, false, (vl!=undefined ? vl : null));//um valor é passado ao comando ou nulo
|
89
|
|
|
}catch(e){ exec = false; }
|
90
|
|
|
}else if(dados_obj[dfrmt].vlatr){//Valores de atributo - aplica ao envólucro da seleção ou cria elemento com o atributo
|
91
|
|
|
var vlatr = dados_obj[dfrmt].vlatr(this, slc_obj);//método usa o item de menu/submenu como parâmetro e retorna uma array (['atributo', 'valor1'(opcional), 'valor2'(opcional)])
|
92
|
|
|
var atr = vlatr[0]; var vl1 = vlatr[1]; var vl2 = vlatr[2];
|
93
|
|
|
var elmt_slc = $(slc_obj.elmt);//elemento/envólucro da seleção
|
94
|
|
|
if(!$(slc_obj.elmt).is("[contenteditable=true]") && $(slc_obj.elmt).text() == slc_obj.txt){//Se o envolucro da seleção não é a própria caixa editável e se o texto selecionado é igual à todo o texto do envólucro detectado aplica o atributo
|
95
|
|
|
if(atr == "style"){//Para aplicar atributo de estilo:
|
96
|
|
|
var ecss = elmt_slc.css(vl1);//valor atual da propriedade css especificado ou indefinido se ainda não foi aplicado
|
97
|
|
|
if(dval == "inherit" || ecss == vl2 || ecss == dval){//Se o dado de valor do item no menu é "inherit" ou se, no elemento selecionado, o valor do estilo específico já foi aplicado (com mesmo valor informado ou dado do item):
|
98
|
|
|
elmt_slc.css(vl1, "");//retira a propriedade css informada (vl1)
|
99
|
|
|
}else{//Se o estilo ainda não foi aplicado ou valor é diferente:
|
100
|
|
|
elmt_slc.css(vl1, (vl2 ? vl2 : dval));//aplica a propriedade "css" (vl1) informada com o valor (vl2, se informado) ou com o dado de valor do item no menu (dval)
|
101
|
|
|
}
|
102
|
|
|
}else if(atr == "class"){//Para aplicar atributo de classe:
|
103
|
|
|
var cls = (vl1 ? vl1 : dval);//usa como classe o valor (vl1, se informado) ou pelo dado do item no menu (dval)
|
104
|
|
|
elmt_slc.toggleClass(cls);//se o elemento já possui a classe remove-a, senão adiciona-a
|
105
|
|
|
}else{//Para qualquer outro atributo:
|
106
|
|
|
var vl = (vl1 ? vl1 : dval);//usa como valor o vl1 (se informado) ou o dado do item no menu (dval)
|
107
|
|
|
elmt_slc.attr(atr) == vl ? elmt_slc.removeAttr(atr) : elmt_slc.attr(atr, vl);//se o elemento já possui o atributo com o mesmo valor especificado remove-o, senão adiciona ou altera com valor informado
|
108
|
|
|
}
|
109
|
|
|
}else if(slc_obj.txt){//Se seleção não possui invólucro próprio cria um "<span>" padrão com o atributo para substituição (se texto não for vazio)
|
110
|
|
|
var elmt_nv = $("<span/>", {"text": slc_obj.txt});//novo elemento padrão com o texto da seleção
|
111
|
|
|
atr == "style" ? elmt_nv.css(vl1, (vl2 ? vl2 : dval)) : elmt_nv.attr(atr, (vl1 ? vl1 : dval));//se o atributo a ser adicionado é de estilo aplica css, senão aplica como atributo comum
|
112
|
|
|
try{//Tenta executar comando e caso não seja suportado atribui 'false' à variável "exec"
|
113
|
|
|
document.execCommand("insertHTML", false, elmt_nv.prop('outerHTML'));//excuta na seleção a substituição pelo elemento novo
|
114
|
|
|
}catch(e){ exec = false; }
|
115
|
|
|
}
|
116
|
|
|
}else if(dados_obj[dfrmt].vltgc){//Tag completa. Insere no lugar do texto selecionado um elemento todo premontado //-->comparar para ver se a seleção já possui elemento com mesma tag e texto e desmontá-lo
|
117
|
|
|
var vltgc = dados_obj[dfrmt].vltgc(this, slc_obj);//passa o item de menu/submenu como parâmetro do método
|
118
|
|
|
try{//Tenta executar comando e caso não seja suportado atribui 'false' à variável "exec"
|
119
|
|
|
document.execCommand("insertHTML", false, vltgc);//usa fixamente o comando "insertHTML" (não suportado pelo IE)
|
120
|
|
|
}catch(e){ exec = false; }
|
121
|
|
|
}
|
122
|
|
|
}
|
123
|
|
|
try{//Tenta executar comando não suportado pelo IE
|
124
|
|
|
document.execCommand("styleWithCSS", false, "false");//re-seta para tipo formatação por tag's (padrão)
|
125
|
|
|
}catch(e){/*faz nada*/}
|
126
|
|
|
elmtcxit.focus();//foca na caixa de texto novamente evitando falhas no desfoque
|
127
|
|
|
//Chama o método de pós-execução (quando existe), manipulando dados e elementos após a formatação
|
128
|
|
|
if(dados_obj[dfrmt].poscall){
|
129
|
|
|
var ret = dados_obj[dfrmt].poscall(this, slc_obj, $.fn.formatTxt.GetSetSelectObj(), exec, execss);//parâmetros: o item clicado no menu, dados da seleção inicial, dados da seleção agora, "exec" e "execss" (true/false informando se a formatação foi suportada)
|
130
|
|
|
typeof ret == "boolean" ? exec = ret : null;//se há um retorno true/false atribui ao "exec"
|
131
|
|
|
}
|
132
|
|
|
//Manipula caixa de conteúdo editável e usa a função a ser chamada após mudanças no texto (opcional)
|
133
|
|
|
cllbck != undefined ? cllbck.apply(this, [elmtcxit, slc_obj, exec]) : null;//em "apply(this, [param1, param2...])" o primeiro parâmetro refere-se ao "this" (escopo) usado na função, que é o item clicado no menu. Parâmetros: a caixa editável, dados da seleção e exec (true/false informando se o commando foi suportado)
|
134
|
|
|
//Executa o clique para ocultar submenu, filtrando pelo evento específico com rótulo ".mnedt"
|
135
|
|
|
$("body").trigger("mousedown.mnedt");
|
136
|
|
|
});
|
137
|
|
|
//Prepara clique nos itens de grupo do menu exibindo o elemento de submenu (dropdown) que agrupa os subitens
|
138
|
|
|
elmt_mn.find(".frmttxt-mn-drpdn").hide();//garante que todos submenus estejam ocultos
|
139
|
|
|
elmt_mn.on("mousedown.mnedt", ".frmttxt-mn-gp", function(event){
|
140
|
|
|
event.preventDefault();//necessário para melhoria no IE
|
141
|
|
|
var wrap_drpdwn = $(this).find(".frmttxt-mn-drpdn");
|
142
|
|
|
if(wrap_drpdwn.is(":hidden")){//Exibe submenu apenas se está oculto evitando repetições
|
143
|
|
|
$("body").trigger("mousedown.mnedt");//se algum outro está visível executa evento que oculta
|
144
|
|
|
wrap_drpdwn.show();//exibe o submenu
|
145
|
|
|
!pos_limit ? pos_limit = $(event.delegateTarget).offset().left + $(event.delegateTarget).width() : null;//guarda a posição (x, à direita) limite do elemento menu
|
146
|
|
|
if(wrap_drpdwn.css("left") != "auto" && (wrap_drpdwn.offset().left + wrap_drpdwn.width()) > pos_limit){//Após ficar visível o submenu é verificado e ajeitado caso passe do limite do menu
|
147
|
|
|
wrap_drpdwn.css({left: "auto", right: "-13px"});//inverte-se, ficando deslocado a esquerda
|
148
|
|
|
}
|
149
|
|
|
var wrap_gp = $(this);//reserva elemento do grupo
|
150
|
|
|
//Aplica evento ao "body", verificando quando o usuário clica fora do grupo, ocultando o submenu
|
151
|
|
|
$("body").off("mousedown.mnedt keydown.mnedt");//remove eventos previamente evitando acumulo dos mesmos eventos
|
152
|
|
|
$("body").on("mousedown.mnedt keydown.mnedt", function(event){//Ao qualquer elemento ser pressionado. É usado rótulo ".mnedt" para ser agora e depois: removido especificamente
|
153
|
|
|
var kcd = event.which;
|
154
|
|
|
//Verifica se o alvo não é o elemento pai do grupo ("wrap_gp") e não é um elemento filho dela
|
155
|
|
|
if(kcd == 27 || (!$(event.target).is(wrap_gp) && wrap_gp.has(event.target).length == 0)){//Filtro de exceções
|
156
|
|
|
wrap_gp.find(".frmttxt-mn-drpdn").hide();//oculta submenu
|
157
|
|
|
$("body").off("mousedown.mnedt keydown.mnedt");//remove evento, filtrando pelo evento específico com rótulo de menu (sem interferir nos outros cliques do "body")
|
158
|
|
|
$.fn.formatTxt.GetSetSelectObj(null, true);//restaura a seleção, quando guardada
|
159
|
|
|
}
|
160
|
|
|
});
|
161
|
|
|
}else if(!$(event.target).is(wrap_drpdwn) && wrap_drpdwn.has(event.target).length == 0){//Se submenu já está visível e o item clicado não foi o submenu e filhos: oculta-o
|
162
|
|
|
wrap_drpdwn.hide();//oculta submenu
|
163
|
|
|
$.fn.formatTxt.GetSetSelectObj(null, true);//restaura a seleção, quando guardada
|
164
|
|
|
}
|
165
|
|
|
});
|
166
|
|
|
//Prepara clique nos itens de manipulação do menu para agregar eventos extras ao fluxo principal. É necessário o vento "click" para ser executado após o usuário soltar mouse
|
167
|
|
|
elmt_mn.on("click.mnedt", ".frmttxt-mn-mp", function(event){
|
168
|
|
|
event.stopPropagation();//evita eventos de elementos pai sejam executados
|
169
|
|
|
var elmtcxit = $(event.delegateTarget).data("frmttxt-cx");
|
170
|
|
|
//Executa métodos conforme o item de objeto "datamnpl_obj"
|
171
|
|
|
var slc_obj = $.fn.formatTxt.GetSetSelectObj();//obtém objeto com os dados de seleção
|
172
|
|
|
var dmnpl = $(this).data("mnpl");//nome do método manipulador
|
173
|
|
|
var ret = dados_obj[dmnpl](this, slc_obj, event);//chama o método manipulando dados e elementos para usá-los nas formatações independentemente
|
174
|
|
|
//Como poderá haver alterações após execução do método: manipula a caixa de texto editável
|
175
|
|
|
ret !== false ? elmtcxit.focus() : null;//foca na caixa de texto novamente evitando falhas no desfoque, mas apenas se não retornar 'true' (booleano)
|
176
|
|
|
elmtcxit.keyup();//executa evento que detecta a formatação para destacar no menu
|
177
|
|
|
});
|
178
|
|
|
//Ao selecionar texto (por mouse ou teclado) detecta a formatação (tags) para destacar no menu. Usa temporizador para só executar num intervalo de tempo
|
179
|
|
|
elmt_cx.on("mouseup.mnedt keyup.mnedt", function(event){
|
180
|
|
|
var elmtmnit = $(event.delegateTarget).data("frmttxt-mn");
|
181
|
|
|
typeof mnslc_tm != "undefined" ? clearTimeout(mnslc_tm) : null;//limpa o temporizador antes de reseta-lo
|
182
|
|
|
mnslc_tm = setTimeout(function(){
|
183
|
|
|
elmtmnit.find(".frmttxt-mn-slcndo").removeClass('frmttxt-mn-slcndo');//remove destaque de todos elementos do menu (filhos, netos...) que possuem a classe
|
184
|
|
|
var slc_obj = $.fn.formatTxt.GetSetSelectObj();//obtém objeto com os dados de seleção
|
185
|
|
|
if(slc_obj && !$(slc_obj.elmt).is("[contenteditable=true]")){//Só entra se há seleção e se o envolucro da seleção não é a própria caixa editável
|
186
|
|
|
//Aplica classe de destaque aos elementos do menu comparando "tag" do texto selecionado com as tags de referência (alguns possuem tags altenativas para diferentes navegadores e/ou formas de formatação)
|
187
|
|
|
var tg_ar, tg_vl;
|
188
|
|
|
for(var prop in dados_obj){//Varre o objeto passando por cada propriedade
|
|
|
|
|
189
|
|
|
tg_ar = dados_obj[prop].tg;//array de tags
|
190
|
|
|
if(tg_ar){//Se o item possui tags de referência
|
191
|
|
|
for(var i=0; i<tg_ar.length; i++){
|
192
|
|
|
if(typeof tg_ar[i]=="string" && tg_ar[i]==slc_obj.tag){//Tag simples - verifica nome somente [ex.: <b>, <i>, <u>, ...]
|
193
|
|
|
elmtmnit.find("[data-frmt="+prop+"]").addClass('frmttxt-mn-slcndo');//aplica o destaque ao item conforme nome da formatação (propriedade)
|
194
|
|
|
break;//bateu com uma tag da propriedade atual - sai do loop filho
|
195
|
|
|
}else if(tg_ar[i].nm == slc_obj.tag || tg_ar[i].nm === null){//Tag com subitens ({nm:"*", atr:"*", ext:"*"}) - verifica na seleção: nome de tag(pode valer 'null'), atributo, podendo ter dado extra. Se o objeto de tag tem o "nm" igual a 'null' aceita qualquer nome de tag
|
196
|
|
|
tg_vl = $(slc_obj.elmt).attr(tg_ar[i].atr);//obtém o valor do atributo informado ou indefinido (se o elemento não o possui).
|
197
|
|
|
if(tg_vl){//Se há um valor (o atributo bateu com o do elemento)
|
198
|
|
|
if(tg_ar[i].atr == "style"){//Se é atributo estilo: captura o valor pelo dado extra
|
199
|
|
|
tg_vl = $(slc_obj.elmt).prop("style")[tg_ar[i].ext];//captura no estilo do elemento selecionado: o valor da propriedade especificada
|
200
|
|
|
if(!tg_vl){//Se for invalido pula para a próxima tag
|
201
|
|
|
continue;
|
202
|
|
|
}
|
203
|
|
|
}else if(tg_ar[i].atr == "class"){//Se é atributo de classe usa a classe informada (extra)
|
204
|
|
|
if(tg_ar[i].ext && $(slc_obj.elmt).hasClass(tg_ar[i].ext)){//Se a classe foi especificada e existe no elemento usa-a como valor
|
205
|
|
|
tg_vl = tg_ar[i].ext;
|
206
|
|
|
}else{//Se classe não existe pula para a próxima tag
|
207
|
|
|
continue;
|
208
|
|
|
}
|
209
|
|
|
}
|
210
|
|
|
elmtmnit.find("[data-frmt='"+prop+"'][data-val='"+tg_vl+"']").addClass('frmttxt-mn-slcndo');//aplica o destaque ao item do menu
|
211
|
|
|
break;//bateu com uma tag da propriedade atual - sai do loop filho
|
212
|
|
|
}
|
213
|
|
|
}
|
214
|
|
|
}
|
215
|
|
|
}
|
216
|
|
|
};
|
217
|
|
|
}
|
218
|
|
|
}, 200);
|
219
|
|
|
});
|
220
|
|
|
//Na caixa, limpa formatação ao colar texto
|
221
|
|
|
elmt_cx.on("paste.mnedt", function(event){
|
222
|
|
|
event.preventDefault();
|
223
|
|
|
var cntd;
|
224
|
|
|
if(event.originalEvent.clipboardData){//Verifica se existe dados de cópia na memória
|
225
|
|
|
cntd = (event.originalEvent || event).clipboardData.getData('text/plain');//captura o conteúdo copiado
|
226
|
|
|
document.execCommand("insertText", false, cntd);//insere o conteúdo
|
227
|
|
|
}else if(window.clipboardData){//Verifica dados de cópia na memória (para IE)
|
228
|
|
|
cntd = window.clipboardData.getData('Text');//captura o conteúdo copiado
|
229
|
|
|
if(window.getSelection){
|
230
|
|
|
window.getSelection().getRangeAt(0).insertNode(document.createTextNode(cntd));//insere o conteúdo
|
231
|
|
|
}else{
|
232
|
|
|
document.selection.createRange().pasteHTML(cntd);//insere o conteúdo
|
233
|
|
|
}
|
234
|
|
|
}
|
235
|
|
|
});
|
236
|
|
|
//Na caixa, ao usuário teclar 'tab' insere código de espaço equivalente no html, sem deixar perder o foco
|
237
|
|
|
elmt_cx.on("keydown.mnedt", function(event){
|
238
|
|
|
var kcd = event.which;//o "which" da jQuery normaliza os códigos de tecla independente do tipo de evento
|
239
|
|
|
if(kcd == 9){//código da tecla "tab"
|
240
|
|
|
event.preventDefault();//impede a aplicação padrão da tecla (foco no próximo elemento)
|
241
|
|
|
try{//Tenta executar commando padrão do "JavaScript" de inserção de texto (não suportado no IE)
|
242
|
|
|
document.execCommand("insertHtml", false, " ");
|
243
|
|
|
}catch(e){//Se não funcionou insere pelo método de seleção
|
244
|
|
|
$.fn.formatTxt.GetSetSelectObj(" ");//insere quatro espaços
|
245
|
|
|
}
|
246
|
|
|
}
|
247
|
|
|
});
|
248
|
|
|
});
|
249
|
|
|
};
|
250
|
|
|
|
251
|
|
|
//Função captura dados do trecho selecionado/posicionado pelo usuário e retorna como objeto. Os parâmetros são para inserção de texto e restauração de seleção de 'range'
|
252
|
|
|
$.fn.formatTxt.range = null;
|
253
|
|
|
$.fn.formatTxt.GetSetSelectObj = function(txt, rng){
|
254
|
|
|
var slc_rng, slc_elmt, slc_tag, slc_txt, vrtxt = (txt && typeof txt == "string");
|
255
|
|
|
if(window.getSelection){
|
256
|
|
|
var slctn = window.getSelection();
|
257
|
|
|
if(slctn.rangeCount){
|
258
|
|
|
slc_rng = slctn.getRangeAt(0);
|
259
|
|
|
var node = slc_rng.commonAncestorContainer;
|
260
|
|
|
slc_txt = slctn.toString();
|
261
|
|
|
slc_elmt = node.nodeType == 1 ? node : node.parentNode;
|
262
|
|
|
if(slc_txt && slc_elmt.textContent != slc_txt){//Se não bateu com texto selecionado. Para navegadores como Firefox que não detectam o envólucro imediato
|
263
|
|
|
var ncntnr = slctn.getRangeAt(0).startContainer.nextSibling;
|
264
|
|
|
slc_elmt = (ncntnr && ncntnr.textContent == slc_txt ? ncntnr : slc_elmt);
|
265
|
|
|
}
|
266
|
|
|
slc_tag = (slc_elmt.nodeType == 1 ? slc_elmt.nodeName : slc_elmt.parentNode.nodeName);
|
267
|
|
|
if(vrtxt){//Se foi passado texto para inserção, insere-o
|
268
|
|
|
slc_rng.deleteContents();
|
269
|
|
|
slc_rng.insertNode(document.createTextNode(txt));
|
270
|
|
|
}
|
271
|
|
|
if(rng == true && $.fn.formatTxt.range){//Se foi requisitado e há "range" (trecho) reservado: restaura seleção
|
272
|
|
|
slctn.removeAllRanges();
|
273
|
|
|
slctn.addRange($.fn.formatTxt.range);
|
274
|
|
|
$.fn.formatTxt.range = null;//anula reserva de "range" novamente
|
275
|
|
|
}
|
276
|
|
|
}else{
|
277
|
|
|
return null;
|
278
|
|
|
}
|
279
|
|
|
}else if(document.selection && document.selection.type != "Control"){//Para IE
|
280
|
|
|
slc_rng = document.selection.createRange();
|
281
|
|
|
slc_elmt = slc_rng.parentElement();
|
282
|
|
|
slc_txt = slc_rng.text;
|
283
|
|
|
slc_tag = slc_rng.parentElement().nodeName;
|
284
|
|
|
vrtxt ? slc_rng.text = txt : null;//se informado texto, insere-o
|
285
|
|
|
if(rng == true && $.fn.formatTxt.range){//Se foi requisitado e há "range" (trecho) reservado: restaura seleção
|
286
|
|
|
$.fn.formatTxt.range.select();
|
287
|
|
|
$.fn.formatTxt.range = null;//anula reserva de "range" novamente
|
288
|
|
|
}
|
289
|
|
|
}else{
|
290
|
|
|
return null;
|
291
|
|
|
}
|
292
|
|
|
return {rng: slc_rng, elmt: slc_elmt, tag: slc_tag, txt: slc_txt};
|
293
|
|
|
}
|
294
|
|
|
//Função para alterar elemento(s) podendo-se mudar a tag/elemento, assim como adicionar e remover atributos, opcionalmente
|
295
|
|
|
$.fn.formatTxt.altrElmnt = function(elmt, nelmt, atr_ad, atr_rm){
|
296
|
|
|
elmt.each(function(){
|
297
|
|
|
if(atr_ad){//Se há array de atributos a serem adicionados:
|
298
|
|
|
for(var i=0; i<atr_ad.length; i++){
|
299
|
|
|
var atr = atr_ad[i].atr;
|
300
|
|
|
var vl1 = (typeof atr_ad[i].vl1 != "function" ? atr_ad[i].vl1 : atr_ad[i].vl1(this));
|
301
|
|
|
if(atr == "style"){//Se é atributo de estilo inclui por "css()" usando dois valores
|
302
|
|
|
var vl2 = (typeof atr_ad[i].vl2 != "function" ? atr_ad[i].vl2 : atr_ad[i].vl2(this));
|
303
|
|
|
$(this).css(vl1, vl2);
|
304
|
|
|
}else if(atr == "class"){//Se é atributo de classe inclui valor por "addClass()"
|
305
|
|
|
$(this).addClass(vl1);
|
306
|
|
|
}else{//Se é atributo comum inclui valor
|
307
|
|
|
$(this).attr(atr, vl1);
|
308
|
|
|
}
|
309
|
|
|
}
|
310
|
|
|
}
|
311
|
|
|
if(atr_rm){//Se há array de atributos a serem removidos:
|
312
|
|
|
for(var i=0; i<atr_rm.length; i++){
|
|
|
|
|
313
|
|
|
$(this).removeAttr(atr_rm[i]);
|
314
|
|
|
}
|
315
|
|
|
}
|
316
|
|
|
if(nelmt){//Se foi passado novo elemento, substitui pela tag informada
|
317
|
|
|
var atrs = {};
|
318
|
|
|
$.each($(this)[0].attributes, function(idx, atr){//Reserva os atributos do elemento
|
319
|
|
|
atrs[atr.nodeName] = atr.nodeValue;
|
320
|
|
|
});
|
321
|
|
|
//Substitui o elemento pelo novo, com todos os atributos e conteúdo
|
322
|
|
|
$(this).replaceWith( $("<"+nelmt+"/>", atrs).append($(this).contents()) );
|
323
|
|
|
}
|
324
|
|
|
});
|
325
|
|
|
}
|
326
|
|
|
//Função para matar aplicações do plugin nos filhos do elemento (ou coleção de elementos) de menu e de cada caixa editável. Remove eventos com o rótulo "mnedt" e outros dados
|
327
|
|
|
$.fn.formatTxt.destroy = function(elmt){//O elemento do parâmetro deve ser um menu que tenha sido instanciado anteriormente
|
328
|
|
|
$("body").trigger("mousedown.mnedt");//oculta submenus
|
329
|
|
|
elmt.off(".mnedt");//remove eventos do(s) menu(s)
|
330
|
|
|
elmt.find("*").off(".mnedt");//remove eventos dos filhos do(s) menu(s)
|
331
|
|
|
elmt.find(".frmttxt-mn-slcndo").removeClass('frmttxt-mn-slcndo');//remove destaques do(s) menu(s)
|
332
|
|
|
elmt.each(function(){//Usa o elemento da caixa editável reservado em cada menu
|
333
|
|
|
var elmtcxit = $(this).data("frmttxt-cx");
|
334
|
|
|
elmtcxit.off(".mnedt");//remove eventos da caixa
|
335
|
|
|
elmtcxit.find("*").off(".mnedt");//remove eventos dos filhos da caixa
|
336
|
|
|
elmtcxit.removeData("frmttxt-mn");//remove o dado com o elemento de menu reservado na caixa
|
337
|
|
|
});
|
338
|
|
|
elmt.removeData("frmttxt-cx");//remove o dado com o elemento de caixa reservado na(s) caixa(s)
|
339
|
|
|
}
|
340
|
|
|
|
341
|
|
|
/*- Cada item (propriedade) é um rótulo para a formatação e o elemento do menu deverá ter o mesmo em "data-frmt='nome'", ex.: "ngrto" para aplicação de negrito.
|
342
|
|
|
- O subitem "precall" (quando necessário) é um método de pre-execução chamado para manipular dados e elementos antes de usá-los na formatação, podendo alterar até mesmo os próprios dados do objeto (ex.: usando formatação de cor, antes de executar, é verificado se foi selecionado aplicação de cor no texto ou no fundo, alterando o subitem de tipo de formatação "tp"), o "incall" executa internamente para quando se quer aplicar formatação de uma maneira totalmente personalizada e o "poscall" faz o mesmo mas ao final das execuções principais e por isso pode ser usado para solucionar execuções não suportadas (nesse caso deve-se retornar true/false conforme funcionamento).
|
343
|
|
|
- O subitem "tp" é o nome do tipo padrão de comando a ser aplicado como formatação. Quando não usado, a formatação usará apenas o subitem "vlatr" ou "vltgc".
|
344
|
|
|
- O subitem "tg" possui array com as tags (em maiúsculo) de referência para a tag que a formatação cria e para, ao usuário selecionar texto, destacar o elemento do menu correspondente (ex.:"B" para <b> procurará elemento com data-frmt="ngrto"), podendo ser várias (navegadores podem criar diferentes tags) e quando a mesma formatação possui valores diferentes (como tamanhos de fonte) e/ou é aplicada usando os métodos de valor ("vlatr" ou "vltgc") "tg" poderá ter objetos com 'nome', 'atributo', podendo possuir ainda propriedade 'extra' para style ou valor específico de classe, ex.: {nm:"SPAN", atr:"style", ext:"font-size"} ou {nm:"DIV", atr:"class", ext:"formato-1"}. O 'extra' para atributo "style" deverá seguir o padrão do método "css()" da jQuery (ex.: "borderTopWidth" para borda superior).
|
345
|
|
|
- Quando se quer aplicar valor o item (elemento) do menu deverá possuir "data-val='valor'" e o valor deverá ser "inherit" para os casos em que se quer remoção de estilo específico. O subitem "vl" é um método para capturar valor dinamicamente (como a cor) sem se usar "data-val='valor'". O subitem "vlatr" retorna uma array com um 'atributo' (índice 0), 'valor1' (2, opcional) e 'valor2' (3, opcional - fixo ou capturado se não for usado o "data-val='valor'") para ser aplicado ao trecho selecionado, ex.: ["style", "color", mnit.css("color")], se "tg" for usado deverá ter o "nm" valendo "null" pois o atributo poderá ser aplicado a qualquer elemento que circunda a seleção. O método "vltgc" é para substituir a seleção por uma tag completa (incluindo o texto), ex.: para uma linha: "<hr>" ou para uma citação: "<blockquote>texto</blockquote>". Nos casos de uso de "vlatr" e "vltgc" o subitem "tp" não será usado.
|
346
|
|
|
- O subitem "css" deverá ser incluído valendo "true" quando se quer que a formatação aconteça por meio de estilização ou como no caso de criação de lista (que ajeita o tamanho dos subelementos) altere filhos por estilização. É útil para tipos de comandos que criam por padrão tag's não suportadas em HTML5, como a criação de "<font...>" (obsoleto) para cor e tamanho. Seu uso limita a comparação de tag com valor (para destaque) em alguns casos, nos quais deverá ser usado o método "vlatr" ou "vltgc" para aplicar a formatação*/
|
347
|
|
|
$.fn.formatTxt.datafrmt_obj = {
|
348
|
|
|
ngrto: {tp:"bold", tg:["B", "STRONG"]}, itlco: {tp:"italic", tg:["I", "EM"]}, sblnhdo: {tp:"underline", tg:["U"]},
|
349
|
|
|
rscdo: {tp:"strikeThrough", tg:["STRIKE"]}, sbrscrto: {tp:"superscript", tg:["SUP"]}, sbscrto: {tp:"subscript", tg:["SUB"]},
|
350
|
|
|
sbrlnhdo: {tg:[{nm:null, atr:"style", ext:"text-decoration"}], vlatr: function(mnit, slc){ return ["style", "text-decoration"]; }},
|
|
|
|
|
351
|
|
|
insrcrctr: {tp:"insertText", vl: function(mnit, slc){ return $(mnit).text(); }, poscall: function(mnit, slc, nslc, exc, excs){
|
|
|
|
|
352
|
|
|
//Uma vez que a inserção de texto pelo comando "document.execCommand("insertText", ...)" não é suportado pelo IE:
|
353
|
|
|
if(exc == false){//Usa a função 'cross-browser' de seleção, para inserir
|
|
|
|
|
354
|
|
|
return ($.fn.formatTxt.GetSetSelectObj($(mnit).text()) ? true : false);//retorna true/false conforme funcionamento
|
355
|
|
|
}
|
356
|
|
|
}},
|
357
|
|
|
tplist1: {tp:"insertUnorderedList", css: true}, tplist2: {tp:"insertOrderedList", css: true},
|
358
|
|
|
fnttmnho: {tg:[{nm:null, atr:"style", ext:"font-size"}], vlatr: function(mnit, slc){ return ["style", "font-size"]; }},
|
|
|
|
|
359
|
|
|
txtcor: {precall: function(mnit, slc){
|
|
|
|
|
360
|
|
|
this.tp = ($(mnit).parents(".frmttxt-mn-drpdn").find(".frmttxt-mn-mp input:checked").val() == "1" ? "foreColor" : "backColor");
|
361
|
|
|
}, tp:"foreColor", tg:[{nm:null, atr:"style", ext:"color"}], css: true, poscall: function(mnit, slc, nslc, exc, excs){
|
362
|
|
|
if(excs == false && nslc.tag == "FONT"){//Se formatação não foi aplicada com estilização e foi criado um elemento "<font>" (para IE):
|
363
|
|
|
/*this.tg[0].nm = "FONT"; this.tg[0].atr = "color"; this.tg[0].ext = null;//altera padrões da tag de comparação
|
364
|
|
|
var mnitval = $(mnit).attr("data-val"); $(mnit).attr("data-val", mnitval.replace(/\s/g, ""));//retira do valor os espaços conforme padrão da tag <font>: 'rgb(235,107,86)'*/
|
365
|
|
|
if(this.tp == "foreColor"){//Se foi aplicado cor de texto substitui por um "<span>" com estilo da mesma cor
|
366
|
|
|
$.fn.formatTxt.altrElmnt($(nslc.elmt), "span", [{atr:"style", vl1:"color", vl2:$(nslc.elmt).attr("color")}], ["color"]);
|
367
|
|
|
}else{//Se foi aplicado cor de fundo substitui por um "<span>" (já foi aplicado com estilo)
|
368
|
|
|
$.fn.formatTxt.altrElmnt($(nslc.elmt), "span");
|
369
|
|
|
}
|
370
|
|
|
}
|
371
|
|
|
}},
|
372
|
|
|
insrlnk: {precall: function(mnit, slc){
|
|
|
|
|
373
|
|
|
var inp_elmt = $(mnit).parents(".frmttxt-mn-drpdn").find("input"); var val_txt = inp_elmt.val();
|
374
|
|
|
inp_elmt.val((/^([a-zA-Z0-9+.-]+):\/\//i).test(val_txt) || val_txt=="" ? val_txt : "http://"+val_txt);//verifica se há protocolo na url e insere "http://" por padrão
|
375
|
|
|
$.fn.formatTxt.GetSetSelectObj(null, true);//restaura a seleção guardada para formatação do mesmo trecho
|
376
|
|
|
}, tp:"createLink", vl: function(mnit, slc){ return $(mnit).parents(".frmttxt-mn-drpdn").find("input").val(); }, poscall: function(mnit, slc, nslc, exc, excs){
|
|
|
|
|
377
|
|
|
var inp_elmt = $(mnit).parents(".frmttxt-mn-drpdn").find("input");
|
378
|
|
|
if(inp_elmt.val()){//Se o valor do input é valido:
|
379
|
|
|
!this.tp ? $(nslc.elmt).attr("href", inp_elmt.val()) : null;//se o "tp" está anulado (quando há link focado mas não selecionado) apenas altera a "url"
|
380
|
|
|
$(nslc.elmt).attr("target", "_blank");//após criar link adiciona "target" para nova aba ao clicar
|
381
|
|
|
$(mnit).parents(".frmttxt-mn-drpdn").find("input").val("");//apenas limpa o texto do "input" após formatação
|
382
|
|
|
}
|
383
|
|
|
}},
|
384
|
|
|
rmvlnk: {precall: function(mnit, slc){
|
385
|
|
|
slc.tag=="A" && slc.txt=="" ? this.tp = null : null;//anula o tipo de formatação quando há link focado mas não selecionado, para pular direto para o "poscall()"
|
386
|
|
|
},tp:"unlink", tg:["A"], poscall: function(mnit, slc, nslc, exc, excs){
|
|
|
|
|
387
|
|
|
if(slc.tag=="A" && !this.tp){//Se o "tp" está anulado (quando há link focado mas não selecionado):
|
388
|
|
|
$(slc.elmt).replaceWith($(slc.elmt).text());//remove link substituindo o elemento por seu texto
|
389
|
|
|
this.tp = "unlink";//retorna o tipo de formatação
|
390
|
|
|
}
|
391
|
|
|
}},
|
392
|
|
|
lmpfrmt: {tp:"removeFormat"}
|
393
|
|
|
/*, simplescaptura: {tp:"insertText", vl: function(mnit){ return $(mnit).text();}},
|
394
|
|
|
atributo-css: {tg:[{nm:null, atr:"style", ext:"text-decoration"}], vlatr: function(mnit){ return ["style", "text-decoration"]; }},
|
395
|
|
|
atributo-class: {tg:[{nm:null, atr:"class", ext:"sobrelinhado"}], vlatr: function(mnit){ return ["class"]; }},
|
396
|
|
|
tagcompleta: {tg:["BLOCKQUOTE"], vltgc: function(mnit, slc){ return "<blockquote>"+(slc.txt || "...")+"</blockquote>"; }}*/
|
397
|
|
|
}
|
398
|
|
|
/*- Para eventos extras do menu pode-se usar o objeto "datamnpl_obj" de métodos para anexar manipulações ao fluxo principal de formatações e independente delas. Ex.: a formatação de cor necessita de um manipulador para alterar o valor de destaque (dstq:{atr:"style", ext:"color"}) entre cor do texto ("color") e cor do fundo ("background-color") verificando qual foi selecionado pelo usuário.
|
399
|
|
|
- Os nomes/rótulos dos métodos devem ser diferentes dos nomes de propriedade em "datafrmt_obj" uma vez que, no instanciamento, eles são unidos num só objeto, permitindo manipulação de todos os dados. O uso de 'this' nos métodos refere-se ao objeto pai "dados_obj" criado no instanciamento.*/
|
400
|
|
|
$.fn.formatTxt.datamnpl_obj = {
|
401
|
|
|
txtcortp: function(mnmp, slc){//Para cores
|
|
|
|
|
402
|
|
|
var chk = $(mnmp).children("input:checked").val();//opção selecionada pelo usuário
|
403
|
|
|
this.txtcor.tg[0].ext = (chk == "1" ? "color" : "background-color");//altera o valor de comparação para destaque
|
404
|
|
|
$(mnmp).parents(".frmttxt-mn-drpdn").find(".frmttxt-mn-it.rmv").attr("data-val", (chk == "1" ? "#888888" : "inherit"));//altera o valor usado no item que simula remoção de cor, usando a cor padrão para cor de texto e "inherit" para cor de fundo
|
405
|
|
|
},
|
406
|
|
|
insrlnkvl: function(mnmp, slc, evnt){//Para inserção de caracteres
|
407
|
|
|
var inp_elmt = $(mnmp).find("input");
|
408
|
|
|
if(inp_elmt.is(":visible")){//Se o "input" estiver visível:
|
409
|
|
|
if(slc && !$(evnt.target).is(inp_elmt)){//Se há seleção e "input" não foi clicado depois de já visível:
|
410
|
|
|
$.fn.formatTxt.range = slc.rng;//reserva o "range" (trecho) selecionado antes de perder foco
|
411
|
|
|
this.insrlnk.tp = (slc.tag=="A" && slc.txt=="" ? null : "createLink");//se uma tag de link foi focado, mas não selecionada, anula o "tp" (tipo de comando) da formatação "insrlnk", obrigando a alteração apenas do "href" pelo método "vlatr"
|
412
|
|
|
var val_txt = (slc.tag=="A" ? $(slc.elmt).attr("href") : "");//verifica se o elemento selecionado já é link, capturando a url ou vazio
|
413
|
|
|
inp_elmt.val(val_txt).focus();//inlui texto e foca no "input"
|
414
|
|
|
}
|
415
|
|
|
if(!$._data(inp_elmt[0], "events")){//Se ainda não foi aplicado o evento de tecla ao input:
|
416
|
|
|
inp_elmt.on("keydown.mnedt", function(event){//Ao pressionar alguma tecla, verifica:
|
417
|
|
|
var kcd = event.which;
|
418
|
|
|
if(kcd == 13){//código da tecla "enter"
|
419
|
|
|
event.preventDefault();//impede a aplicação padrão da tecla (foco no próximo elemento)
|
420
|
|
|
inp_elmt.val() ? $(this).parent().children(".frmttxt-mn-it").mousedown() : null;//se link é valido executa o evento de formatação já aplicado ao botão "ok"
|
421
|
|
|
}
|
422
|
|
|
});
|
423
|
|
|
}
|
424
|
|
|
return false;//retorna 'false' para evitar que a caixa editável seja focada saindo do "input"
|
425
|
|
|
}else{
|
426
|
|
|
inp_elmt.val("");//apenas limpa o "input" ao ocultar
|
|
|
|
|
427
|
|
|
}
|
428
|
|
|
}
|
429
|
|
|
}
|
430
|
|
|
}(jQuery));
|
431
|
|
|
|
When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically: