+ this.fireEvent('select', this, this.date);
+
+
+ },
+
+ onMousedown: function(e){
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ onIncrementHours: function()
+ {
+ Roo.log('onIncrementHours');
+ this.time = this.time.add(Date.HOUR, 1);
+ this.update();
+
+ },
+
+ onDecrementHours: function()
+ {
+ Roo.log('onDecrementHours');
+ this.time = this.time.add(Date.HOUR, -1);
+ this.update();
+ },
+
+ onIncrementMinutes: function()
+ {
+ Roo.log('onIncrementMinutes');
+ this.time = this.time.add(Date.MINUTE, 1);
+ this.update();
+ },
+
+ onDecrementMinutes: function()
+ {
+ Roo.log('onDecrementMinutes');
+ this.time = this.time.add(Date.MINUTE, -1);
+ this.update();
+ },
+
+ onTogglePeriod: function()
+ {
+ Roo.log('onTogglePeriod');
+ this.time = this.time.add(Date.HOUR, 12);
+ this.update();
+ }
+
+
+});
+
+
+Roo.apply(Roo.bootstrap.form.TimeField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-time',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'tfoot',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ colspan: '7',
+ cls: '',
+ cn: [
+ {
+ tag: 'button',
+ cls: 'btn btn-info ok',
+ html: 'OK'
+ }
+ ]
+ }
+
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * MonthField
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.MonthField
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap MonthField class
+ *
+ * @cfg {String} language default en
+ *
+ * @constructor
+ * Create a new MonthField
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.MonthField = function(config){
+ Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ /**
+ * @event show
+ * Fires when this field show.
+ * @param {Roo.bootstrap.form.MonthField} this
+ * @param {Mixed} date The date value
+ */
+ show : true,
+ /**
+ * @event show
+ * Fires when this field hide.
+ * @param {Roo.bootstrap.form.MonthField} this
+ * @param {Mixed} date The date value
+ */
+ hide : true,
+ /**
+ * @event select
+ * Fires when select a date.
+ * @param {Roo.bootstrap.form.MonthField} this
+ * @param {String} oldvalue The old value
+ * @param {String} newvalue The new value
+ */
+ select : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
+
+ onRender: function(ct, position)
+ {
+
+ Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
+
+ this.language = this.language || 'en';
+ this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
+ this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
+
+ this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
+ this.isInline = false;
+ this.isInput = true;
+ this.component = this.el.select('.add-on', true).first() || false;
+ this.component = (this.component && this.component.length === 0) ? false : this.component;
+ this.hasInput = this.component && this.inputEL().length;
+
+ this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
+
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+
+ this.picker().addClass('datepicker-dropdown');
+
+ Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
+ v.setStyle('width', '189px');
+ });
+
+ this.fillMonths();
+
+ this.update();
+
+ if(this.isInline) {
+ this.show();
+ }
+
+ },
+
+ setValue: function(v, suppressEvent)
+ {
+ var o = this.getValue();
+
+ Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
+
+ this.update();
+
+ if(suppressEvent !== true){
+ this.fireEvent('select', this, o, v);
+ }
+
+ },
+
+ getValue: function()
+ {
+ return this.value;
+ },
+
+ onClick: function(e)
+ {
+ e.stopPropagation();
+ e.preventDefault();
+
+ var target = e.getTarget();
+
+ if(target.nodeName.toLowerCase() === 'i'){
+ target = Roo.get(target).dom.parentNode;
+ }
+
+ var nodeName = target.nodeName;
+ var className = target.className;
+ var html = target.innerHTML;
+
+ if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
+ return;
+ }
+
+ this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
+
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+
+ this.hide();
+
+ },
+
+ picker : function()
+ {
+ return this.pickerEl;
+ },
+
+ fillMonths: function()
+ {
+ var i = 0;
+ var months = this.picker().select('>.datepicker-months td', true).first();
+
+ months.dom.innerHTML = '';
+
+ while (i < 12) {
+ var month = {
+ tag: 'span',
+ cls: 'month',
+ html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
+ };
+
+ months.createChild(month);
+ }
+
+ },
+
+ update: function()
+ {
+ var _this = this;
+
+ if(typeof(this.vIndex) == 'undefined' && this.value.length){
+ this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
+ }
+
+ Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
+ e.removeClass('active');
+
+ if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
+ e.addClass('active');
+ }
+ })
+ },
+
+ place: function()
+ {
+ if(this.isInline) {
+ return;
+ }
+
+ this.picker().removeClass(['bottom', 'top']);
+
+ if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
+ /*
+ * place to the top of element!
+ *
+ */
+
+ this.picker().addClass('top');
+ this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
+
+ return;
+ }
+
+ this.picker().addClass('bottom');
+
+ this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
+ },
+
+ onFocus : function()
+ {
+ Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
+ this.show();
+ },
+
+ onBlur : function()
+ {
+ Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
+
+ var d = this.inputEl().getValue();
+
+ this.setValue(d);
+
+ this.hide();
+ },
+
+ show : function()
+ {
+ this.picker().show();
+ this.picker().select('>.datepicker-months', true).first().show();
+ this.update();
+ this.place();
+
+ this.fireEvent('show', this, this.date);
+ },
+
+ hide : function()
+ {
+ if(this.isInline) {
+ return;
+ }
+ this.picker().hide();
+ this.fireEvent('hide', this, this.date);
+
+ },
+
+ onMousedown: function(e)
+ {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ keyup: function(e)
+ {
+ Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
+ this.update();
+ },
+
+ fireKey: function(e)
+ {
+ if (!this.picker().isVisible()){
+ if (e.keyCode == 27) {// allow escape to hide and re-show picker
+ this.show();
+ }
+ return;
+ }
+
+ var dir;
+
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ dir = e.keyCode == 37 ? -1 : 1;
+
+ this.vIndex = this.vIndex + dir;
+
+ if(this.vIndex < 0){
+ this.vIndex = 0;
+ }
+
+ if(this.vIndex > 11){
+ this.vIndex = 11;
+ }
+
+ if(isNaN(this.vIndex)){
+ this.vIndex = 0;
+ }
+
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+
+ break;
+ case 38: // up
+ case 40: // down
+
+ dir = e.keyCode == 38 ? -1 : 1;
+
+ this.vIndex = this.vIndex + dir * 4;
+
+ if(this.vIndex < 0){
+ this.vIndex = 0;
+ }
+
+ if(this.vIndex > 11){
+ this.vIndex = 11;
+ }
+
+ if(isNaN(this.vIndex)){
+ this.vIndex = 0;
+ }
+
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+ break;
+
+ case 13: // enter
+
+ if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+ }
+
+ this.hide();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+ }
+ this.hide();
+ break;
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ break;
+ default :
+ this.hide();
+
+ }
+ },
+
+ remove: function()
+ {
+ this.picker().remove();
+ }
+
+});
+
+Roo.apply(Roo.bootstrap.form.MonthField, {
+
+ content : {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
+ }
+ ]
+ }
+ ]
+ },
+
+ dates:{
+ en: {
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ }
+ }
+});
+
+Roo.apply(Roo.bootstrap.form.MonthField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu roo-dynamic',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-months',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.form.DateField.content
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});
+
+
+
+
+ /*
+ * - LGPL
+ *
+ * CheckBox
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.CheckBox
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap CheckBox class
+ *
+ * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
+ * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
+ * @cfg {String} boxLabel The text that appears beside the checkbox
+ * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
+ * @cfg {Boolean} checked initnal the element
+ * @cfg {Boolean} inline inline the element (default false)
+ * @cfg {String} groupId the checkbox group id // normal just use for checkbox
+ * @cfg {String} tooltip label tooltip
+ *
+ * @constructor
+ * Create a new CheckBox
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.CheckBox = function(config){
+ Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ /**
+ * @event check
+ * Fires when the element is checked or unchecked.
+ * @param {Roo.bootstrap.form.CheckBox} this This input
+ * @param {Boolean} checked The new checked value
+ */
+ check : true,
+ /**
+ * @event click
+ * Fires when the element is click.
+ * @param {Roo.bootstrap.form.CheckBox} this This input
+ */
+ click : true
+ });
+
+};
+
+Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
+
+ inputType: 'checkbox',
+ inputValue: 1,
+ valueOff: 0,
+ boxLabel: false,
+ checked: false,
+ weight : false,
+ inline: false,
+ tooltip : '',
+
+ // checkbox success does not make any sense really..
+ invalidClass : "",
+ validClass : "",
+
+
+ getAutoCreate : function()
+ {
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+
+ var id = Roo.id();
+
+ var cfg = {};
+
+ cfg.cls = 'form-group form-check ' + this.inputType; //input-group
+
+ if(this.inline){
+ cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
+ }
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ value : this.inputValue,
+ cls : 'roo-' + this.inputType, //'form-box',
+ placeholder : this.placeholder || ''
+
+ };
+
+ if(this.inputType != 'radio'){
+ var hidden = {
+ tag: 'input',
+ type : 'hidden',
+ cls : 'roo-hidden-value',
+ value : this.checked ? this.inputValue : this.valueOff
+ };
+ }
+
+
+ if (this.weight) { // Validity check?
+ cfg.cls += " " + this.inputType + "-" + this.weight;
+ }
+
+ if (this.disabled) {
+ input.disabled=true;
+ }
+
+ if(this.checked){
+ input.checked = this.checked;
+ }
+
+ if (this.name) {
+
+ input.name = this.name;
+
+ if(this.inputType != 'radio'){
+ hidden.name = this.name;
+ input.name = '_hidden_' + this.name;
+ }
+ }
+
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
+
+ var settings=this;
+
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ var inputblock = input;
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.before
+ });
+ }
+
+ inputblock.cn.push(input);
+
+ if(this.inputType != 'radio'){
+ inputblock.cn.push(hidden);
+ }
+
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+
+ }
+ var boxLabelCfg = false;
+
+ if(this.boxLabel){
+
+ boxLabelCfg = {
+ tag: 'label',
+ //'for': id, // box label is handled by onclick - so no for...
+ cls: 'box-label',
+ html: this.boxLabel
+ };
+ if(this.tooltip){
+ boxLabelCfg.tooltip = this.tooltip;
+ }
+
+ }
+
+
+ if (align ==='left' && this.fieldLabel.length) {
+// Roo.log("left and has label");
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label',
+ html : this.fieldLabel
+ },
+ {
+ cls : "",
+ cn: [
+ inputblock
+ ]
+ }
+ ];
+
+ if (boxLabelCfg) {
+ cfg.cn[1].cn.push(boxLabelCfg);
+ }
+
+ if(this.labelWidth > 12){
+ cfg.cn[0].style = "width: " + this.labelWidth + 'px';
+ }
+
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ cfg.cn[0].cls += ' col-lg-' + this.labellg;
+ cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ cfg.cn[0].cls += ' col-md-' + this.labelmd;
+ cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ cfg.cn[0].cls += ' col-sm-' + this.labelsm;
+ cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ cfg.cn[0].cls += ' col-xs-' + this.labelxs;
+ cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+ } else if ( this.fieldLabel.length) {
+// Roo.log(" label");
+ cfg.cn = [
+
+ {
+ tag: this.boxLabel ? 'span' : 'label',
+ 'for': id,
+ cls: 'control-label box-input-label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+ },
+
+ inputblock
+
+ ];
+ if (boxLabelCfg) {
+ cfg.cn.push(boxLabelCfg);
+ }
+
+ } else {
+
+// Roo.log(" no label && no align");
+ cfg.cn = [ inputblock ] ;
+ if (boxLabelCfg) {
+ cfg.cn.push(boxLabelCfg);
+ }
+
+
+ }
+
+
+
+ if(this.inputType != 'radio'){
+ cfg.cn.push(hidden);
+ }
+
+ return cfg;
+
+ },
+
+ /**
+ * return the real input element.
+ */
+ inputEl: function ()
+ {
+ return this.el.select('input.roo-' + this.inputType,true).first();
+ },
+ hiddenEl: function ()
+ {
+ return this.el.select('input.roo-hidden-value',true).first();
+ },
+
+ labelEl: function()
+ {
+ return this.el.select('label.control-label',true).first();
+ },
+ /* depricated... */
+
+ label: function()
+ {
+ return this.labelEl();
+ },
+
+ boxLabelEl: function()
+ {
+ return this.el.select('label.box-label',true).first();
+ },
+
+ initEvents : function()
+ {
+// Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
+
+ this.inputEl().on('click', this.onClick, this);
+
+ if (this.boxLabel) {
+ this.el.select('label.box-label',true).first().on('click', this.onClick, this);
+ }
+
+ this.startValue = this.getValue();
+
+ if(this.groupId){
+ Roo.bootstrap.form.CheckBox.register(this);
+ }
+ },
+
+ onClick : function(e)
+ {
+ if(this.fireEvent('click', this, e) !== false){
+ this.setChecked(!this.checked);
+ }
+
+ },
+
+ setChecked : function(state,suppressEvent)
+ {
+ this.startValue = this.getValue();
+
+ if(this.inputType == 'radio'){
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ e.dom.checked = false;
+ });
+
+ this.inputEl().dom.checked = true;
+
+ this.inputEl().dom.value = this.inputValue;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, true);
+ }
+
+ this.validate();
+
+ return;
+ }
+
+ this.checked = state;
+
+ this.inputEl().dom.checked = state;
+
+
+ this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, state);
+ }
+
+ this.validate();
+ },
+
+ getValue : function()
+ {
+ if(this.inputType == 'radio'){
+ return this.getGroupValue();
+ }
+
+ return this.hiddenEl().dom.value;
+
+ },
+
+ getGroupValue : function()
+ {
+ if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
+ return '';
+ }
+
+ return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
+ },
+
+ setValue : function(v,suppressEvent)
+ {
+ if(this.inputType == 'radio'){
+ this.setGroupValue(v, suppressEvent);
+ return;
+ }
+
+ this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
+
+ this.validate();
+ },
+
+ setGroupValue : function(v, suppressEvent)
+ {
+ this.startValue = this.getValue();
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ e.dom.checked = false;
+
+ if(e.dom.value == v){
+ e.dom.checked = true;
+ }
+ });
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, true);
+ }
+
+ this.validate();
+
+ return;
+ },
+
+ validate : function()
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
+ }
+
+ if(
+ this.disabled ||
+ (this.inputType == 'radio' && this.validateRadio()) ||
+ (this.inputType == 'checkbox' && this.validateCheckbox())
+ ){
+ this.markValid();
+ return true;
+ }
+
+ this.markInvalid();
+ return false;
+ },
+
+ validateRadio : function()
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
+ }
+
+ if(this.allowBlank){
+ return true;
+ }
+
+ var valid = false;
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ if(!e.dom.checked){
+ return;
+ }
+
+ valid = true;
+
+ return false;
+ });
+
+ return valid;
+ },
+
+ validateCheckbox : function()
+ {
+ if(!this.groupId){
+ return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
+ //return (this.getValue() == this.inputValue) ? true : false;
+ }
+
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
+ return false;
+ }
+
+ var r = false;
+
+ for(var i in group){
+ if(group[i].el.isVisible(true)){
+ r = false;
+ break;
+ }
+
+ r = true;
+ }
+
+ for(var i in group){
+ if(r){
+ break;
+ }
+
+ r = (group[i].getValue() == group[i].inputValue) ? true : false;
+ }
+
+ return r;
+ },
+
+ /**
+ * Mark this field as valid
+ */
+ markValid : function()
+ {
+ var _this = this;
+
+ this.fireEvent('valid', this);
+
+ var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
+
+ if(this.groupId){
+ label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
+ }
+
+ if(label){
+ label.markValid();
+ }
+
+ if(this.inputType == 'radio'){
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ var fg = e.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.validClass);
+ } else {
+ fg.removeClass(['is-valid', 'is-invalid']);
+ fg.addClass('is-valid');
+ }
+ });
+
+ return;
+ }
+
+ if(!this.groupId){
+ var fg = this.el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([this.invalidClass, this.validClass]);
+ fg.addClass(this.validClass);
+ } else {
+ fg.removeClass(['is-valid', 'is-invalid']);
+ fg.addClass('is-valid');
+ }
+ return;
+ }
+
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
+ return;
+ }
+
+ for(var i in group){
+ var fg = group[i].el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([this.invalidClass, this.validClass]);
+ fg.addClass(this.validClass);
+ } else {
+ fg.removeClass(['is-valid', 'is-invalid']);
+ fg.addClass('is-valid');
+ }
+ }
+ },
+
+ /**
+ * Mark this field as invalid
+ * @param {String} msg The validation message
+ */
+ markInvalid : function(msg)
+ {
+ if(this.allowBlank){
+ return;
+ }
+
+ var _this = this;
+
+ this.fireEvent('invalid', this, msg);
+
+ var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
+
+ if(this.groupId){
+ label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
+ }
+
+ if(label){
+ label.markInvalid();
+ }
+
+ if(this.inputType == 'radio'){
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ var fg = e.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.invalidClass);
+ } else {
+ fg.removeClass(['is-invalid', 'is-valid']);
+ fg.addClass('is-invalid');
+ }
+ });
+
+ return;
+ }
+
+ if(!this.groupId){
+ var fg = this.el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.invalidClass);
+ } else {
+ fg.removeClass(['is-invalid', 'is-valid']);
+ fg.addClass('is-invalid');
+ }
+ return;
+ }
+
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
+ return;
+ }
+
+ for(var i in group){
+ var fg = group[i].el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.invalidClass);
+ } else {
+ fg.removeClass(['is-invalid', 'is-valid']);
+ fg.addClass('is-invalid');
+ }
+ }
+
+ },
+
+ clearInvalid : function()
+ {
+ Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
+
+ // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
+
+ var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
+
+ if (label && label.iconEl) {
+ label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
+ label.iconEl.removeClass(['is-invalid', 'is-valid']);
+ }
+ },
+
+ disable : function()
+ {
+ if(this.inputType != 'radio'){
+ Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
+ return;
+ }
+
+ var _this = this;
+
+ if(this.rendered){
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ _this.getActionEl().addClass(this.disabledClass);
+ e.dom.disabled = true;
+ });
+ }
+
+ this.disabled = true;
+ this.fireEvent("disable", this);
+ return this;
+ },
+
+ enable : function()
+ {
+ if(this.inputType != 'radio'){
+ Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
+ return;
+ }
+
+ var _this = this;
+
+ if(this.rendered){
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ _this.getActionEl().removeClass(this.disabledClass);
+ e.dom.disabled = false;
+ });
+ }
+
+ this.disabled = false;
+ this.fireEvent("enable", this);
+ return this;
+ },
+
+ setBoxLabel : function(v)
+ {
+ this.boxLabel = v;
+
+ if(this.rendered){
+ this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ }
+ }
+
+});
+
+Roo.apply(Roo.bootstrap.form.CheckBox, {
+
+ groups: {},
+
+ /**
+ * register a CheckBox Group
+ * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
+ */
+ register : function(checkbox)
+ {
+ if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
+ this.groups[checkbox.groupId] = {};
+ }
+
+ if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
+ return;
+ }
+
+ this.groups[checkbox.groupId][checkbox.name] = checkbox;
+
+ },
+ /**
+ * fetch a CheckBox Group based on the group ID
+ * @param {string} the group ID
+ * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
+ */
+ get: function(groupId) {
+ if (typeof(this.groups[groupId]) == 'undefined') {
+ return false;
+ }
+
+ return this.groups[groupId] ;
+ }
+
+
+});
+/*
+ * - LGPL
+ *
+ * RadioItem
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.Radio
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Radio class
+ * @cfg {String} boxLabel - the label associated
+ * @cfg {String} value - the value of radio
+ *
+ * @constructor
+ * Create a new Radio
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.form.Radio = function(config){
+ Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
+
+};
+
+Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
+
+ boxLabel : '',
+
+ value : '',
+
+ getAutoCreate : function()
+ {
+ var cfg = {
+ tag : 'div',
+ cls : 'form-group radio',
+ cn : [
+ {
+ tag : 'label',
+ cls : 'box-label',
+ html : this.boxLabel
+ }
+ ]
+ };
+
+ return cfg;
+ },
+
+ initEvents : function()
+ {
+ this.parent().register(this);
+
+ this.el.on('click', this.onClick, this);
+
+ },
+
+ onClick : function(e)
+ {
+ if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
+ this.setChecked(true);
+ }
+ },
+
+ setChecked : function(state, suppressEvent)
+ {
+ this.parent().setValue(this.value, suppressEvent);
+
+ },
+
+ setBoxLabel : function(v)
+ {
+ this.boxLabel = v;
+
+ if(this.rendered){
+ this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ }
+ }
+
+});
+
+
+ /*
+ * - LGPL
+ *
+ * Input
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.SecurePass
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap SecurePass class
+ *
+ *
+ * @constructor
+ * Create a new SecurePass
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.SecurePass = function (config) {
+ // these go here, so the translation tool can replace them..
+ this.errors = {
+ PwdEmpty: "Please type a password, and then retype it to confirm.",
+ PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
+ PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
+ PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
+ IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
+ FNInPwd: "Your password can't contain your first name. Please type a different password.",
+ LNInPwd: "Your password can't contain your last name. Please type a different password.",
+ TooWeak: "Your password is Too Weak."
+ },
+ this.meterLabel = "Password strength:";
+ this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
+ this.meterClass = [
+ "roo-password-meter-tooweak",
+ "roo-password-meter-weak",
+ "roo-password-meter-medium",
+ "roo-password-meter-strong",
+ "roo-password-meter-grey"
+ ];
+
+ this.errors = {};
+
+ Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
+}
+
+Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
+ /**
+ * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
+ * {
+ * PwdEmpty: "Please type a password, and then retype it to confirm.",
+ * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
+ * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
+ * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
+ * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
+ * FNInPwd: "Your password can't contain your first name. Please type a different password.",
+ * LNInPwd: "Your password can't contain your last name. Please type a different password."
+ * })
+ */
+ // private
+
+ meterWidth: 300,
+ errorMsg :'',
+ errors: false,
+ imageRoot: '/',
+ /**
+ * @cfg {String/Object} Label for the strength meter (defaults to
+ * 'Password strength:')
+ */
+ // private
+ meterLabel: '',
+ /**
+ * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
+ * ['Weak', 'Medium', 'Strong'])
+ */
+ // private
+ pwdStrengths: false,
+ // private
+ strength: 0,
+ // private
+ _lastPwd: null,
+ // private
+ kCapitalLetter: 0,
+ kSmallLetter: 1,
+ kDigit: 2,
+ kPunctuation: 3,
+
+ insecure: false,
+ // private
+ initEvents: function ()
+ {
+ Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
+
+ if (this.el.is('input[type=password]') && Roo.isSafari) {
+ this.el.on('keydown', this.SafariOnKeyDown, this);
+ }
+
+ this.el.on('keyup', this.checkStrength, this, {buffer: 50});
+ },
+ // private
+ onRender: function (ct, position)
+ {
+ Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
+ this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
+ this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
+
+ this.trigger.createChild({
+ cn: [
+ {
+ //id: 'PwdMeter',
+ tag: 'div',
+ cls: 'roo-password-meter-grey col-xs-12',
+ style: {
+ //width: 0,
+ //width: this.meterWidth + 'px'
+ }
+ },
+ {
+ cls: 'roo-password-meter-text'
+ }
+ ]
+ });
+
+
+ if (this.hideTrigger) {
+ this.trigger.setDisplayed(false);
+ }
+ this.setSize(this.width || '', this.height || '');
+ },
+ // private
+ onDestroy: function ()
+ {
+ if (this.trigger) {
+ this.trigger.removeAllListeners();
+ this.trigger.remove();
+ }
+ if (this.wrap) {
+ this.wrap.remove();
+ }
+ Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
+ },
+ // private
+ checkStrength: function ()
+ {
+ var pwd = this.inputEl().getValue();
+ if (pwd == this._lastPwd) {
+ return;
+ }
+
+ var strength;
+ if (this.ClientSideStrongPassword(pwd)) {
+ strength = 3;
+ } else if (this.ClientSideMediumPassword(pwd)) {
+ strength = 2;
+ } else if (this.ClientSideWeakPassword(pwd)) {
+ strength = 1;
+ } else {
+ strength = 0;
+ }
+
+ Roo.log('strength1: ' + strength);
+
+ //var pm = this.trigger.child('div/div/div').dom;
+ var pm = this.trigger.child('div/div');
+ pm.removeClass(this.meterClass);
+ pm.addClass(this.meterClass[strength]);
+
+
+ var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+
+ pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
+
+ this._lastPwd = pwd;
+ },
+ reset: function ()
+ {
+ Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
+
+ this._lastPwd = '';
+
+ var pm = this.trigger.child('div/div');
+ pm.removeClass(this.meterClass);
+ pm.addClass('roo-password-meter-grey');
+
+
+ var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+
+ pt.innerHTML = '';
+ this.inputEl().dom.type='password';
+ },
+ // private
+ validateValue: function (value)
+ {
+ if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
+ return false;
+ }
+ if (value.length == 0) {
+ if (this.allowBlank) {
+ this.clearInvalid();
+ return true;
+ }
+
+ this.markInvalid(this.errors.PwdEmpty);
+ this.errorMsg = this.errors.PwdEmpty;
+ return false;
+ }
+
+ if(this.insecure){
+ return true;
+ }
+
+ if (!value.match(/[\x21-\x7e]+/)) {
+ this.markInvalid(this.errors.PwdBadChar);
+ this.errorMsg = this.errors.PwdBadChar;
+ return false;
+ }
+ if (value.length < 6) {
+ this.markInvalid(this.errors.PwdShort);
+ this.errorMsg = this.errors.PwdShort;
+ return false;
+ }
+ if (value.length > 16) {
+ this.markInvalid(this.errors.PwdLong);
+ this.errorMsg = this.errors.PwdLong;
+ return false;
+ }
+ var strength;
+ if (this.ClientSideStrongPassword(value)) {
+ strength = 3;
+ } else if (this.ClientSideMediumPassword(value)) {
+ strength = 2;
+ } else if (this.ClientSideWeakPassword(value)) {
+ strength = 1;
+ } else {
+ strength = 0;
+ }
+
+
+ if (strength < 2) {
+ //this.markInvalid(this.errors.TooWeak);
+ this.errorMsg = this.errors.TooWeak;
+ //return false;
+ }
+
+
+ console.log('strength2: ' + strength);
+
+ //var pm = this.trigger.child('div/div/div').dom;
+
+ var pm = this.trigger.child('div/div');
+ pm.removeClass(this.meterClass);
+ pm.addClass(this.meterClass[strength]);
+
+ var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+
+ pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
+
+ this.errorMsg = '';
+ return true;
+ },
+ // private
+ CharacterSetChecks: function (type)
+ {
+ this.type = type;
+ this.fResult = false;
+ },
+ // private
+ isctype: function (character, type)
+ {
+ switch (type) {
+ case this.kCapitalLetter:
+ if (character >= 'A' && character <= 'Z') {
+ return true;
+ }
+ break;
+
+ case this.kSmallLetter:
+ if (character >= 'a' && character <= 'z') {
+ return true;
+ }
+ break;
+
+ case this.kDigit:
+ if (character >= '0' && character <= '9') {
+ return true;
+ }
+ break;
+
+ case this.kPunctuation:
+ if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
+ return true;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ },
+ // private
+ IsLongEnough: function (pwd, size)
+ {
+ return !(pwd == null || isNaN(size) || pwd.length < size);
+ },
+ // private
+ SpansEnoughCharacterSets: function (word, nb)
+ {
+ if (!this.IsLongEnough(word, nb))
+ {
+ return false;
+ }
+
+ var characterSetChecks = new Array(
+ new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
+ new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
+ );
+
+ for (var index = 0; index < word.length; ++index) {
+ for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
+ if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
+ characterSetChecks[nCharSet].fResult = true;
+ break;
+ }
+ }
+ }
+
+ var nCharSets = 0;
+ for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
+ if (characterSetChecks[nCharSet].fResult) {
+ ++nCharSets;
+ }
+ }
+
+ if (nCharSets < nb) {
+ return false;
+ }
+ return true;
+ },
+ // private
+ ClientSideStrongPassword: function (pwd)
+ {
+ return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
+ },
+ // private
+ ClientSideMediumPassword: function (pwd)
+ {
+ return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
+ },
+ // private
+ ClientSideWeakPassword: function (pwd)
+ {
+ return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
+ }
+
+});
+Roo.htmleditor = {};
+
+/**
+ * @class Roo.htmleditor.Filter
+ * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
+ * @cfg {DomElement} node The node to iterate and filter
+ * @cfg {boolean|String|Array} tag Tags to replace
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+
+
+Roo.htmleditor.Filter = function(cfg) {
+ Roo.apply(this.cfg);
+ // this does not actually call walk as it's really just a abstract class
+}
+
+
+Roo.htmleditor.Filter.prototype = {
+
+ node: false,
+
+ tag: false,
+
+ // overrride to do replace comments.
+ replaceComment : false,
+
+ // overrride to do replace or do stuff with tags..
+ replaceTag : false,
+
+ walk : function(dom)
+ {
+ Roo.each( Array.from(dom.childNodes), function( e ) {
+ switch(true) {
+
+ case e.nodeType == 8 && this.replaceComment !== false: // comment
+ this.replaceComment(e);
+ return;
+
+ case e.nodeType != 1: //not a node.
+ return;
+
+ case this.tag === true: // everything
+ case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
+ case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
+ if (this.replaceTag && false === this.replaceTag(e)) {
+ return;
+ }
+ if (e.hasChildNodes()) {
+ this.walk(e);
+ }
+ return;
+
+ default: // tags .. that do not match.
+ if (e.hasChildNodes()) {
+ this.walk(e);
+ }
+ }
+
+ }, this);
+
+ }
+};
+
+/**
+ * @class Roo.htmleditor.FilterAttributes
+ * clean attributes and styles including http:// etc.. in attribute
+ * @constructor
+* Run a new Attribute Filter
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterAttributes = function(cfg)
+{
+ Roo.apply(this, cfg);
+ this.attrib_black = this.attrib_black || [];
+ this.attrib_white = this.attrib_white || [];
+
+ this.attrib_clean = this.attrib_clean || [];
+ this.style_white = this.style_white || [];
+ this.style_black = this.style_black || [];
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
+{
+ tag: true, // all tags
+
+ attrib_black : false, // array
+ attrib_clean : false,
+ attrib_white : false,
+
+ style_white : false,
+ style_black : false,
+
+
+ replaceTag : function(node)
+ {
+ if (!node.attributes || !node.attributes.length) {
+ return true;
+ }
+
+ for (var i = node.attributes.length-1; i > -1 ; i--) {
+ var a = node.attributes[i];
+ //console.log(a);
+ if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
+ node.removeAttribute(a.name);
+ continue;
+ }
+
+
+
+ if (a.name.toLowerCase().substr(0,2)=='on') {
+ node.removeAttribute(a.name);
+ continue;
+ }
+
+
+ if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
+ node.removeAttribute(a.name);
+ continue;
+ }
+ if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
+ this.cleanAttr(node,a.name,a.value); // fixme..
+ continue;
+ }
+ if (a.name == 'style') {
+ this.cleanStyle(node,a.name,a.value);
+ continue;
+ }
+ /// clean up MS crap..
+ // tecnically this should be a list of valid class'es..
+
+
+ if (a.name == 'class') {
+ if (a.value.match(/^Mso/)) {
+ node.removeAttribute('class');
+ }
+
+ if (a.value.match(/^body$/)) {
+ node.removeAttribute('class');
+ }
+ continue;
+ }
+
+
+ // style cleanup!?
+ // class cleanup?
+
+ }
+ return true; // clean children
+ },
+
+ cleanAttr: function(node, n,v)
+ {
+
+ if (v.match(/^\./) || v.match(/^\//)) {
+ return;
+ }
+ if (v.match(/^(http|https):\/\//)
+ || v.match(/^mailto:/)
+ || v.match(/^ftp:/)
+ || v.match(/^data:/)
+ ) {
+ return;
+ }
+ if (v.match(/^#/)) {
+ return;
+ }
+ if (v.match(/^\{/)) { // allow template editing.
+ return;
+ }
+// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
+ node.removeAttribute(n);
+
+ },
+ cleanStyle : function(node, n,v)
+ {
+ if (v.match(/expression/)) { //XSS?? should we even bother..
+ node.removeAttribute(n);
+ return;
+ }
+
+ var parts = v.split(/;/);
+ var clean = [];
+
+ Roo.each(parts, function(p) {
+ p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
+ if (!p.length) {
+ return true;
+ }
+ var l = p.split(':').shift().replace(/\s+/g,'');
+ l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
+
+ if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
+ return true;
+ }
+ //Roo.log()
+ // only allow 'c whitelisted system attributes'
+ if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
+ return true;
+ }
+
+
+ clean.push(p);
+ return true;
+ },this);
+ if (clean.length) {
+ node.setAttribute(n, clean.join(';'));
+ } else {
+ node.removeAttribute(n);
+ }
+
+ }
+
+
+
+
+});/**
+ * @class Roo.htmleditor.FilterBlack
+ * remove blacklisted elements.
+ * @constructor
+ * Run a new Blacklisted Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterBlack = function(cfg)
+{
+ Roo.apply(this, cfg);
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
+{
+ tag : true, // all elements.
+
+ replaceTag : function(n)
+ {
+ n.parentNode.removeChild(n);
+ }
+});
+/**
+ * @class Roo.htmleditor.FilterComment
+ * remove comments.
+ * @constructor
+* Run a new Comments Filter
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterComment = function(cfg)
+{
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
+{
+
+ replaceComment : function(n)
+ {
+ n.parentNode.removeChild(n);
+ }
+});/**
+ * @class Roo.htmleditor.FilterKeepChildren
+ * remove tags but keep children
+ * @constructor
+ * Run a new Keep Children Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterKeepChildren = function(cfg)
+{
+ Roo.apply(this, cfg);
+ if (this.tag === false) {
+ return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
+ }
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
+{
+
+
+ replaceTag : function(node)
+ {
+ // walk children...
+ //Roo.log(node);
+ var ar = Array.from(node.childNodes);
+ //remove first..
+ for (var i = 0; i < ar.length; i++) {
+ if (ar[i].nodeType == 1) {
+ if (
+ (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
+ || // array and it matches
+ (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
+ ) {
+ this.replaceTag(ar[i]); // child is blacklisted as well...
+ continue;
+ }
+ }
+ }
+ ar = Array.from(node.childNodes);
+ for (var i = 0; i < ar.length; i++) {
+
+ node.removeChild(ar[i]);
+ // what if we need to walk these???
+ node.parentNode.insertBefore(ar[i], node);
+ if (this.tag !== false) {
+ this.walk(ar[i]);
+
+ }
+ }
+ node.parentNode.removeChild(node);
+ return false; // don't walk children
+
+
+ }
+});/**
+ * @class Roo.htmleditor.FilterParagraph
+ * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
+ * like on 'push' to remove the <p> tags and replace them with line breaks.
+ * @constructor
+ * Run a new Paragraph Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterParagraph = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
+{
+
+
+ tag : 'P',
+
+
+ replaceTag : function(node)
+ {
+
+ if (node.childNodes.length == 1 &&
+ node.childNodes[0].nodeType == 3 &&
+ node.childNodes[0].textContent.trim().length < 1
+ ) {
+ // remove and replace with '<BR>';
+ node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
+ return false; // no need to walk..
+ }
+ var ar = Array.from(node.childNodes);
+ for (var i = 0; i < ar.length; i++) {
+ node.removeChild(ar[i]);
+ // what if we need to walk these???
+ node.parentNode.insertBefore(ar[i], node);
+ }
+ // now what about this?
+ // <p> </p>
+
+ // double BR.
+ node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+ node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+ node.parentNode.removeChild(node);
+
+ return false;
+
+ }
+
+});/**
+ * @class Roo.htmleditor.FilterSpan
+ * filter span's with no attributes out..
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterSpan = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
+{
+
+ tag : 'SPAN',
+
+
+ replaceTag : function(node)
+ {
+ if (node.attributes && node.attributes.length > 0) {
+ return true; // walk if there are any.
+ }
+ Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
+ return false;
+
+ }
+
+});/**
+ * @class Roo.htmleditor.FilterTableWidth
+ try and remove table width data - as that frequently messes up other stuff.
+ *
+ * was cleanTableWidths.
+ *
+ * Quite often pasting from word etc.. results in tables with column and widths.
+ * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
+ *
+ * @constructor
+ * Run a new Table Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterTableWidth = function(cfg)
+{
+ // no need to apply config.
+ this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
+{
+
+
+
+ replaceTag: function(node) {
+
+
+
+ if (node.hasAttribute('width')) {
+ node.removeAttribute('width');
+ }
+
+
+ if (node.hasAttribute("style")) {
+ // pretty basic...
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+ if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
+ return;
+ }
+ // what ever is left... we allow.
+ nstyle.push(s);
+ });
+ node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+ if (!nstyle.length) {
+ node.removeAttribute('style');
+ }
+ }
+
+ return true; // continue doing children..
+ }
+});/**
+ * @class Roo.htmleditor.FilterWord
+ * try and clean up all the mess that Word generates.
+ *
+ * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
+
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterWord = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
+{
+ tag: true,
+
+
+ /**
+ * Clean up MS wordisms...
+ */
+ replaceTag : function(node)
+ {
+
+ // no idea what this does - span with text, replaceds with just text.
+ if(
+ node.nodeName == 'SPAN' &&
+ !node.hasAttributes() &&
+ node.childNodes.length == 1 &&
+ node.firstChild.nodeName == "#text"
+ ) {
+ var textNode = node.firstChild;
+ node.removeChild(textNode);
+ if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
+ node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
+ }
+ node.parentNode.insertBefore(textNode, node);
+ if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
+ node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
+ }
+
+ node.parentNode.removeChild(node);
+ return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
+ }
+
+
+
+ if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+ node.parentNode.removeChild(node);
+ return false; // dont do chidlren
+ }
+ //Roo.log(node.tagName);
+ // remove - but keep children..
+ if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
+ //Roo.log('-- removed');
+ while (node.childNodes.length) {
+ var cn = node.childNodes[0];
+ node.removeChild(cn);
+ node.parentNode.insertBefore(cn, node);
+ // move node to parent - and clean it..
+ this.replaceTag(cn);
+ }
+ node.parentNode.removeChild(node);
+ /// no need to iterate chidlren = it's got none..
+ //this.iterateChildren(node, this.cleanWord);
+ return false; // no need to iterate children.
+ }
+ // clean styles
+ if (node.className.length) {
+
+ var cn = node.className.split(/\W+/);
+ var cna = [];
+ Roo.each(cn, function(cls) {
+ if (cls.match(/Mso[a-zA-Z]+/)) {
+ return;
+ }
+ cna.push(cls);
+ });
+ node.className = cna.length ? cna.join(' ') : '';
+ if (!cna.length) {
+ node.removeAttribute("class");
+ }
+ }
+
+ if (node.hasAttribute("lang")) {
+ node.removeAttribute("lang");
+ }
+
+ if (node.hasAttribute("style")) {
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+ if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
+ return;
+ }
+ // what ever is left... we allow.
+ nstyle.push(s);
+ });
+ node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+ if (!nstyle.length) {
+ node.removeAttribute('style');
+ }
+ }
+ return true; // do children
+