this.sortInfo = meta.sortInfo || this.sortInfo;
this.modified = [];
this.fireEvent('metachange', this, this.reader.meta);
+ },
+
+ moveIndex : function(data, type)
+ {
+ var index = this.indexOf(data);
+
+ var newIndex = index + type;
+
+ this.remove(data);
+
+ this.insert(newIndex, data);
+
}
});/*
* Based on:
*/
Roo.View = function(config, depreciated_tpl, depreciated_config){
+ this.parent = false;
+
if (typeof(depreciated_tpl) == 'undefined') {
// new way.. - universal constructor.
Roo.apply(this, config);
this.tpl.compile();
-
-
-
/** @private */
this.addEvents({
/**
*/
toggleSelect : false,
+ /**
+ * @cfg {Boolean} tickable - selecting
+ */
+ tickable : false,
+
/**
* Returns the element this view is bound to.
* @return {Roo.Element}
* Refreshes the view. - called by datachanged on the store. - do not call directly.
*/
refresh : function(){
+ Roo.log('refresh');
var t = this.tpl;
// if we are using something like 'domtemplate', then
for(var i = 0, len = records.length; i < len; i++){
var data = this.prepareData(records[i].data, i, records[i]);
this.fireEvent("preparedata", this, data, i, records[i]);
+
+ var d = Roo.apply({}, data);
+
+ if(this.tickable){
+ Roo.apply(d, {'roo-id' : Roo.id()});
+
+ var _this = this;
+
+ Roo.each(this.parent.item, function(item){
+ if(item[_this.parent.valueField] != data[_this.parent.valueField]){
+ return;
+ }
+ Roo.apply(d, {'roo-data-checked' : 'checked'});
+ });
+ }
+
html[html.length] = Roo.util.Format.trim(
this.dataName ?
- t.applySubtemplate(this.dataName, data, this.store.meta) :
- t.apply(data)
+ t.applySubtemplate(this.dataName, d, this.store.meta) :
+ t.apply(d)
);
}
this.nodes = el.dom.childNodes;
this.updateIndexes(0);
},
+
/**
* Function to override to reformat the data that is sent to
},
onUpdate : function(ds, record){
+ Roo.log('on update');
this.clearSelections();
var index = this.store.indexOf(record);
var n = this.nodes[index];
// --------- FIXME
onAdd : function(ds, records, index)
{
+ Roo.log(['on Add', ds, records, index] );
this.clearSelections();
if(this.nodes.length == 0){
this.refresh();
},
onRemove : function(ds, record, index){
+ Roo.log('onRemove');
this.clearSelections();
var el = this.dataName ?
this.el.child('.roo-tpl-' + this.dataName) :
this.el;
+
el.dom.removeChild(this.nodes[index]);
this.updateIndexes(index);
},
* onbeforeLoad - masks the loading area.
*
*/
- onBeforeLoad : function()
+ onBeforeLoad : function(store,opts)
{
- this.el.update("");
+ Roo.log('onBeforeLoad');
+ if (!opts.add) {
+ this.el.update("");
+ }
this.el.mask(this.mask ? this.mask : "Loading" );
},
onLoad : function ()
this.select(item, this.multiSelect && e.ctrlKey);
this.lastSelection = item;
}
- e.preventDefault();
+
+ if(!this.tickable){
+ e.preventDefault();
+ }
+
}
return true;
},
autoSize : function(){
//this.el.beginMeasure();
this.textEl.setWidth(1);
- this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
+ /*
+ * #2804 [new] Tabs in Roojs
+ * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
+ */
+ this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
//this.el.endMeasure();
},
tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
});
var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
- ul.on("click", this.onClick, this);
+ ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
+
ul.on("mouseover", this.onMouseOver, this);
ul.on("mouseout", this.onMouseOut, this);
this.items.each(function(item){
// private
onClick : function(e){
- var t;
- if(t = this.findTargetItem(e)){
- t.onClick(e);
- this.fireEvent("click", this, t, e);
+ Roo.log("menu.onClick");
+ var t = this.findTargetItem(e);
+ if(!t){
+ return;
+ }
+ Roo.log(e);
+ if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
+ if(t == this.activeItem && t.shouldDeactivate(e)){
+ this.activeItem.deactivate();
+ delete this.activeItem;
+ return;
+ }
+ if(t.canActivate){
+ this.setActiveItem(t, true);
+ }
+ return;
+
+
}
+
+ t.onClick(e);
+ this.fireEvent("click", this, t, e);
},
// private
getValue : function(){
if(this.valueField){
return typeof this.value != 'undefined' ? this.value : '';
- }else{
- return Roo.form.ComboBox.superclass.getValue.call(this);
}
+ return Roo.form.ComboBox.superclass.getValue.call(this);
},
/**
Roo.form.ComboBoxArray = function(config)
{
+ this.addEvents({
+ /**
+ * @event remove
+ * Fires when remove the value from the list
+ * @param {Roo.form.ComboBoxArray} _self This combo box array
+ * @param {Roo.form.ComboBoxArray.Item} item removed item
+ */
+ 'remove' : true
+
+
+ });
Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
}, this)
}
- if (typeof(v) == 'object') {
+ if (typeof(v) == 'object' ) {
// then let's assume it's an array of objects..
Roo.each(v, function(l) {
this.addItem(l);
remove : function()
{
-
this.cb.items.remove(this);
this.el.child('img').un('click', this.remove, this);
this.el.remove();
this.cb.updateHiddenEl();
+
+ this.cb.fireEvent('remove', this.cb, this);
}
});/*
* Based on:
// private
onClick : function(){
+ if (this.disabled) {
+ return;
+ }
this.setChecked(!this.checked);
//if(this.el.dom.checked != this.checked){
});//<script type="text/javascript">
/*
- * Ext JS Library 1.1.1
+ * Based Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
- * licensing@extjs.com
- *
- * http://www.extjs.com/license
+ * LGPL
+ *
*/
- /*
- *
- * Known bugs:
- * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
- * - IE ? - no idea how much works there.
- *
- *
- *
- */
-
-
/**
- * @class Ext.form.HtmlEditor
- * @extends Ext.form.Field
- * Provides a lightweight HTML Editor component.
+ * @class Roo.HtmlEditorCore
+ * @extends Roo.Component
+ * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
*
- * This has been tested on Fireforx / Chrome.. IE may not be so great..
- *
- * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
- * supported by this editor.</b><br/><br/>
- * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
* any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
*/
-Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
- /**
- * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
- */
- toolbars : false,
- /**
- * @cfg {String} createLinkText The default text for the create link prompt
- */
- createLinkText : 'Please enter the URL for the link:',
- /**
- * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
+
+Roo.HtmlEditorCore = function(config){
+
+
+ Roo.HtmlEditorCore.superclass.constructor.call(this, config);
+
+
+ this.addEvents({
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {Roo.HtmlEditorCore} this
+ */
+ initialize: true,
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {Roo.HtmlEditorCore} this
+ */
+ activate: true,
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ beforesync: true,
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ beforepush: true,
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ sync: true,
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ push: true,
+
+ /**
+ * @event editorevent
+ * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+ * @param {Roo.HtmlEditorCore} this
+ */
+ editorevent: true
+ });
+
+ // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
+
+ // defaults : white / black...
+ this.applyBlacklists();
+
+
+
+};
+
+
+Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
+
+
+ /**
+ * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
*/
- defaultLinkValue : 'http:/'+'/',
-
+
+ owner : false,
+
/**
* @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
* Roo.resizable.
iframePad:3,
hideMode:'offsets',
- defaultAutoCreate : { // modified by initCompnoent..
- tag: "textarea",
- style:"width:500px;height:300px;",
- autocomplete: "off"
- },
-
- // private
- initComponent : function(){
- this.addEvents({
- /**
- * @event initialize
- * Fires when the editor is fully initialized (including the iframe)
- * @param {HtmlEditor} this
- */
- initialize: true,
- /**
- * @event activate
- * Fires when the editor is first receives the focus. Any insertion must wait
- * until after this event.
- * @param {HtmlEditor} this
- */
- activate: true,
- /**
- * @event beforesync
- * Fires before the textarea is updated with content from the editor iframe. Return false
- * to cancel the sync.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- beforesync: true,
- /**
- * @event beforepush
- * Fires before the iframe editor is updated with content from the textarea. Return false
- * to cancel the push.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- beforepush: true,
- /**
- * @event sync
- * Fires when the textarea is updated with content from the editor iframe.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- sync: true,
- /**
- * @event push
- * Fires when the iframe editor is updated with content from the textarea.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- push: true,
- /**
- * @event editmodechange
- * Fires when the editor switches edit modes
- * @param {HtmlEditor} this
- * @param {Boolean} sourceEdit True if source edit, false if standard editing.
- */
- editmodechange: true,
- /**
- * @event editorevent
- * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
- * @param {HtmlEditor} this
- */
- editorevent: true
- });
- this.defaultAutoCreate = {
- tag: "textarea",
- style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
- autocomplete: "off"
- };
- },
-
- /**
- * Protected method that will not generally be called directly. It
- * is called when the editor creates its toolbar. Override this method if you need to
- * add custom toolbar buttons.
- * @param {HtmlEditor} editor
- */
- createToolbar : function(editor){
- if (!editor.toolbars || !editor.toolbars.length) {
- editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
- }
-
- for (var i =0 ; i < editor.toolbars.length;i++) {
- editor.toolbars[i] = Roo.factory(
- typeof(editor.toolbars[i]) == 'string' ?
- { xtype: editor.toolbars[i]} : editor.toolbars[i],
- Roo.form.HtmlEditor);
- editor.toolbars[i].init(editor);
- }
-
-
- },
+ clearUp: true,
+
+ // blacklist + whitelisted elements..
+ black: false,
+ white: false,
+
+
/**
* Protected method that will not generally be called directly. It
getDocMarkup : function(){
// body styles..
var st = '';
+ Roo.log(this.stylesheets);
+
+ // inherit styels from page...??
if (this.stylesheets === false) {
Roo.get(document.head).select('style').each(function(node) {
onRender : function(ct, position)
{
var _t = this;
- Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
+ //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
+ this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
+
+
this.el.dom.style.border = '0 none';
this.el.dom.setAttribute('tabIndex', -1);
- this.el.addClass('x-hidden');
+ this.el.addClass('x-hidden hide');
+
+
+
if(Roo.isIE){ // fix IE 1px bogus margin
this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
}
- this.wrap = this.el.wrap({
- cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
- });
+
- if (this.resizable) {
- this.resizeEl = new Roo.Resizable(this.wrap, {
- pinned : true,
- wrap: true,
- dynamic : true,
- minHeight : this.height,
- height: this.height,
- handles : this.resizable,
- width: this.width,
- listeners : {
- resize : function(r, w, h) {
- _t.onResize(w,h); // -something
- }
- }
- });
-
- }
-
this.frameId = Roo.id();
- this.createToolbar(this);
-
-
+
- var iframe = this.wrap.createChild({
+ var iframe = this.owner.wrap.createChild({
tag: 'iframe',
+ cls: 'form-control', // bootstrap..
id: this.frameId,
name: this.frameId,
frameBorder : 'no',
}, this.el
);
- // console.log(iframe);
- //this.wrap.dom.appendChild(iframe);
-
+
this.iframe = iframe.dom;
this.assignDocWin();
}
},
interval : 10,
- duration:10000,
+ duration: 10000,
scope: this
};
Roo.TaskMgr.start(task);
- if(!this.width){
- this.setSize(this.wrap.getSize());
- }
- if (this.resizeEl) {
- this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
- // should trigger onReize..
- }
+
+
},
// private
onResize : function(w, h)
{
- //Roo.log('resize: ' +w + ',' + h );
- Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
- if(this.el && this.iframe){
- if(typeof w == 'number'){
- var aw = w - this.wrap.getFrameWidth('lr');
- this.el.setWidth(this.adjustWidth('textarea', aw));
- this.iframe.style.width = aw + 'px';
- }
- if(typeof h == 'number'){
- var tbh = 0;
- for (var i =0; i < this.toolbars.length;i++) {
- // fixme - ask toolbars for heights?
- tbh += this.toolbars[i].tb.el.getHeight();
- if (this.toolbars[i].footer) {
- tbh += this.toolbars[i].footer.el.getHeight();
- }
- }
-
-
-
-
- var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
- ah -= 5; // knock a few pixes off for look..
- this.el.setHeight(this.adjustWidth('textarea', ah));
- this.iframe.style.height = ah + 'px';
- if(this.doc){
- (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
- }
+ Roo.log('resize: ' +w + ',' + h );
+ //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
+ if(!this.iframe){
+ return;
+ }
+ if(typeof w == 'number'){
+
+ this.iframe.style.width = w + 'px';
+ }
+ if(typeof h == 'number'){
+
+ this.iframe.style.height = h + 'px';
+ if(this.doc){
+ (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
}
}
+
},
/**
this.sourceEditMode = sourceEditMode === true;
if(this.sourceEditMode){
-// Roo.log('in');
-// Roo.log(this.syncValue());
- this.syncValue();
- this.iframe.className = 'x-hidden';
- this.el.removeClass('x-hidden');
- this.el.dom.removeAttribute('tabIndex');
- this.el.focus();
+
+ Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
+
}else{
-// Roo.log('out')
-// Roo.log(this.pushValue());
- this.pushValue();
- this.iframe.className = '';
- this.el.addClass('x-hidden');
- this.el.dom.setAttribute('tabIndex', -1);
+ Roo.get(this.iframe).removeClass(['x-hidden','hide']);
+ //this.iframe.className = '';
this.deferFocus();
}
- this.setSize(this.wrap.getSize());
- this.fireEvent('editmodechange', this, this.sourceEditMode);
- },
-
- // private used internally
- createLink : function(){
- var url = prompt(this.createLinkText, this.defaultLinkValue);
- if(url && url != 'http:/'+'/'){
- this.relayCmd('createlink', url);
- }
- },
-
- // private (for BoxComponent)
- adjustSize : Roo.BoxComponent.prototype.adjustSize,
-
- // private (for BoxComponent)
- getResizeEl : function(){
- return this.wrap;
- },
-
- // private (for BoxComponent)
- getPositionEl : function(){
- return this.wrap;
- },
-
- // private
- initEvents : function(){
- this.originalValue = this.getValue();
+ //this.setSize(this.owner.wrap.getSize());
+ //this.fireEvent('editmodechange', this, this.sourceEditMode);
},
- /**
- * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
- * @method
- */
- markInvalid : Roo.emptyFn,
- /**
- * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
- * @method
- */
- clearInvalid : Roo.emptyFn,
-
- setValue : function(v){
- Roo.form.HtmlEditor.superclass.setValue.call(this, v);
- this.pushValue();
- },
+
+
/**
* Protected method that will not generally be called directly. If you need/want
},
/**
+ * HTML Editor -> Textarea
* Protected method that will not generally be called directly. Syncs the contents
* of the editor iframe with the textarea.
*/
var html = bd.innerHTML;
if(Roo.isSafari){
var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
- var m = bs.match(/text-align:(.*?);/i);
+ var m = bs ? bs.match(/text-align:(.*?);/i) : false;
if(m && m[1]){
html = '<div style="'+m[0]+'">' + html + '</div>';
}
}
return "&#"+cc+";"
});
- if(this.fireEvent('beforesync', this, html) !== false){
+ if(this.owner.fireEvent('beforesync', this, html) !== false){
this.el.dom.value = html;
- this.fireEvent('sync', this, html);
+ this.owner.fireEvent('sync', this, html);
}
}
},
*/
pushValue : function(){
if(this.initialized){
- var v = this.el.dom.value;
+ var v = this.el.dom.value.trim();
- if(v.length < 1){
- v = ' ';
- }
+// if(v.length < 1){
+// v = ' ';
+// }
- if(this.fireEvent('beforepush', this, v) !== false){
+ if(this.owner.fireEvent('beforepush', this, v) !== false){
var d = (this.doc.body || this.doc.documentElement);
d.innerHTML = v;
this.cleanUpPaste();
this.el.dom.value = d.innerHTML;
- this.fireEvent('push', this, v);
+ this.owner.fireEvent('push', this, v);
}
}
},
this.doc = iframe.contentWindow.document;
this.win = iframe.contentWindow;
} else {
- if (!Roo.get(this.frameId)) {
+// if (!Roo.get(this.frameId)) {
+// return;
+// }
+// this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
+// this.win = Roo.get(this.frameId).dom.contentWindow;
+
+ if (!Roo.get(this.frameId) && !iframe.contentDocument) {
return;
}
+
this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
- this.win = Roo.get(this.frameId).dom.contentWindow;
+ this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
}
},
//var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
// this copies styles from the containing element into thsi one..
// not sure why we need all of this..
- var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
- ss['background-attachment'] = 'fixed'; // w3c
+ //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
+
+ //var ss = this.el.getStyles( 'background-image', 'background-repeat');
+ //ss['background-attachment'] = 'fixed'; // w3c
dbody.bgProperties = 'fixed'; // ie
- Roo.DomHelper.applyStyles(dbody, ss);
+ //Roo.DomHelper.applyStyles(dbody, ss);
Roo.EventManager.on(this.doc, {
//'mousedown': this.onEditorEvent,
'mouseup': this.onEditorEvent,
}
this.initialized = true;
- this.fireEvent('initialize', this);
+ this.owner.fireEvent('initialize', this);
this.pushValue();
},
if(this.rendered){
- for (var i =0; i < this.toolbars.length;i++) {
- // fixme - ask toolbars for heights?
- this.toolbars[i].onDestroy();
- }
+ //for (var i =0; i < this.toolbars.length;i++) {
+ // // fixme - ask toolbars for heights?
+ // this.toolbars[i].onDestroy();
+ // }
- this.wrap.dom.innerHTML = '';
- this.wrap.remove();
+ //this.wrap.dom.innerHTML = '';
+ //this.wrap.remove();
}
},
this.activated = true;
- for (var i =0; i < this.toolbars.length;i++) {
- this.toolbars[i].onFirstFocus();
- }
-
+
+
if(Roo.isGecko){ // prevent silly gecko errors
this.win.focus();
var s = this.win.getSelection();
this.execCmd('styleWithCSS', false);
}catch(e){}
}
- this.fireEvent('activate', this);
+ this.owner.fireEvent('activate', this);
},
// private
},
onEditorEvent : function(e){
- this.fireEvent('editorevent', this, e);
+ this.owner.fireEvent('editorevent', this, e);
// this.updateToolbar();
this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
},
range.insertNode(this.doc.createTextNode(txt));
} ,
- // private
- relayBtnCmd : function(btn){
- this.relayCmd(btn.cmd);
- },
+
/**
* Executes a Midas editor command on the editor document and performs necessary focus and
relayCmd : function(cmd, value){
this.win.focus();
this.execCmd(cmd, value);
- this.fireEvent('editorevent', this);
+ this.owner.fireEvent('editorevent', this);
//this.updateToolbar();
- this.deferFocus();
+ this.owner.deferFocus();
},
/**
cleanUpPaste : function()
{
// cleans up the whole document..
- Roo.log('cleanuppaste');
+ Roo.log('cleanuppaste');
+
this.cleanUpChildren(this.doc.body);
var clean = this.cleanWordChars(this.doc.body.innerHTML);
if (clean != this.doc.body.innerHTML) {
},
cleanWordChars : function(input) {// change the chars to hex code
- var he = Roo.form.HtmlEditor;
+ var he = Roo.HtmlEditorCore;
var output = input;
Roo.each(he.swapCodes, function(sw) {
// clean up silly Windows -- stuff?
return;
}
+ var lcname = node.tagName.toLowerCase();
+ // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
+ // whitelist of tags..
- if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
+ if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
// remove node.
node.parentNode.removeChild(node);
return;
}
- var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
+ var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
// remove <a name=....> as rendering on yahoo mailer is borked with this.
// this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
}
+ var cwhite = this.cwhite;
+ var cblack = this.cblack;
+
function cleanStyle(n,v)
{
if (v.match(/expression/)) { //XSS?? should we even bother..
node.removeAttribute(n);
return;
}
- var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
- var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
-
var parts = v.split(/;/);
var clean = [];
var l = p.split(':').shift().replace(/\s+/g,'');
l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
-
- if ( cblack.indexOf(l) > -1) {
+ if ( cwhite.length && cblack.indexOf(l) > -1) {
// Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
//node.removeAttribute(n);
return true;
node.removeAttribute(a.name);
continue;
}
- if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
+ if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
node.removeAttribute(a.name);
continue;
}
- if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
+ if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
cleanAttr(a.name,a.value); // fixme..
continue;
}
cleanStyle(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.className = '';
+ /// clean up MS crap..
+ // tecnically this should be a list of valid class'es..
+
+
+ if (a.name == 'class') {
+ if (a.value.match(/^Mso/)) {
+ node.className = '';
+ }
+
+ if (a.value.match(/body/)) {
+ node.className = '';
+ }
+ continue;
+ }
+
+ // style cleanup!?
+ // class cleanup?
+
+ }
+
+
+ this.cleanUpChildren(node);
+
+
+ },
+ /**
+ * Clean up MS wordisms...
+ */
+ cleanWord : function(node)
+ {
+ var _t = this;
+ var cleanWordChildren = function()
+ {
+ if (!node.childNodes.length) {
+ return;
+ }
+ for (var i = node.childNodes.length-1; i > -1 ; i--) {
+ _t.cleanWord(node.childNodes[i]);
+ }
+ }
+
+
+ if (!node) {
+ this.cleanWord(this.doc.body);
+ return;
+ }
+ if (node.nodeName == "#text") {
+ // clean up silly Windows -- stuff?
+ return;
+ }
+ if (node.nodeName == "#comment") {
+ node.parentNode.removeChild(node);
+ // clean up silly Windows -- stuff?
+ return;
+ }
+
+ if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+ node.parentNode.removeChild(node);
+ return;
+ }
+
+ // remove - but keep children..
+ if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
+ while (node.childNodes.length) {
+ var cn = node.childNodes[0];
+ node.removeChild(cn);
+ node.parentNode.insertBefore(cn, node);
+ }
+ node.parentNode.removeChild(node);
+ cleanWordChildren();
+ return;
+ }
+ // 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');
+ }
+ }
+
+ cleanWordChildren();
+
+
+ },
+ domToHTML : function(currentElement, depth, nopadtext) {
+
+ depth = depth || 0;
+ nopadtext = nopadtext || false;
+
+ if (!currentElement) {
+ return this.domToHTML(this.doc.body);
+ }
+
+ //Roo.log(currentElement);
+ var j;
+ var allText = false;
+ var nodeName = currentElement.nodeName;
+ var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
+
+ if (nodeName == '#text') {
+ return currentElement.nodeValue;
+ }
+
+
+ var ret = '';
+ if (nodeName != 'BODY') {
+
+ var i = 0;
+ // Prints the node tagName, such as <A>, <IMG>, etc
+ if (tagName) {
+ var attr = [];
+ for(i = 0; i < currentElement.attributes.length;i++) {
+ // quoting?
+ var aname = currentElement.attributes.item(i).name;
+ if (!currentElement.attributes.item(i).value.length) {
+ continue;
+ }
+ attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
+ }
+
+ ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
+ }
+ else {
+
+ // eack
+ }
+ } else {
+ tagName = false;
+ }
+ if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
+ return ret;
+ }
+ if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
+ nopadtext = true;
+ }
+
+
+ // Traverse the tree
+ i = 0;
+ var currentElementChild = currentElement.childNodes.item(i);
+ var allText = true;
+ var innerHTML = '';
+ lastnode = '';
+ while (currentElementChild) {
+ // Formatting code (indent the tree so it looks nice on the screen)
+ var nopad = nopadtext;
+ if (lastnode == 'SPAN') {
+ nopad = true;
+ }
+ // text
+ if (currentElementChild.nodeName == '#text') {
+ var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
+ if (!nopad && toadd.length > 80) {
+ innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
+ }
+ innerHTML += toadd;
+
+ i++;
+ currentElementChild = currentElement.childNodes.item(i);
+ lastNode = '';
+ continue;
+ }
+ allText = false;
+
+ innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
+
+ // Recursively traverse the tree structure of the child node
+ innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
+ lastnode = currentElementChild.nodeName;
+ i++;
+ currentElementChild=currentElement.childNodes.item(i);
+ }
+
+ ret += innerHTML;
+
+ if (!allText) {
+ // The remaining code is mostly for formatting the tree
+ ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
+ }
+
+
+ if (tagName) {
+ ret+= "</"+tagName+">";
+ }
+ return ret;
+
+ },
+
+ applyBlacklists : function()
+ {
+ var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
+ var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
+
+ this.white = [];
+ this.black = [];
+ Roo.each(Roo.HtmlEditorCore.white, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ this.white.push(tag);
+
+ }, this);
+
+ Roo.each(w, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.white.indexOf(tag) > -1) {
+ return;
+ }
+ this.white.push(tag);
+
+ }, this);
+
+
+ Roo.each(Roo.HtmlEditorCore.black, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ this.black.push(tag);
+
+ }, this);
+
+ Roo.each(b, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.black.indexOf(tag) > -1) {
+ return;
+ }
+ this.black.push(tag);
+
+ }, this);
+
+
+ w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
+ b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
+
+ this.cwhite = [];
+ this.cblack = [];
+ Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ this.cwhite.push(tag);
+
+ }, this);
+
+ Roo.each(w, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.cwhite.indexOf(tag) > -1) {
+ return;
+ }
+ this.cwhite.push(tag);
+
+ }, this);
+
+
+ Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ this.cblack.push(tag);
+
+ }, this);
+
+ Roo.each(b, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.cblack.indexOf(tag) > -1) {
+ return;
+ }
+ this.cblack.push(tag);
+
+ }, this);
+ }
+
+ // hide stuff that is not compatible
+ /**
+ * @event blur
+ * @hide
+ */
+ /**
+ * @event change
+ * @hide
+ */
+ /**
+ * @event focus
+ * @hide
+ */
+ /**
+ * @event specialkey
+ * @hide
+ */
+ /**
+ * @cfg {String} fieldClass @hide
+ */
+ /**
+ * @cfg {String} focusClass @hide
+ */
+ /**
+ * @cfg {String} autoCreate @hide
+ */
+ /**
+ * @cfg {String} inputType @hide
+ */
+ /**
+ * @cfg {String} invalidClass @hide
+ */
+ /**
+ * @cfg {String} invalidText @hide
+ */
+ /**
+ * @cfg {String} msgFx @hide
+ */
+ /**
+ * @cfg {String} validateOnBlur @hide
+ */
+});
+
+Roo.HtmlEditorCore.white = [
+ 'area', 'br', 'img', 'input', 'hr', 'wbr',
+
+ 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
+ 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
+ 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
+ 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
+ 'table', 'ul', 'xmp',
+
+ 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+ 'thead', 'tr',
+
+ 'dir', 'menu', 'ol', 'ul', 'dl',
+
+ 'embed', 'object'
+];
+
+
+Roo.HtmlEditorCore.black = [
+ // 'embed', 'object', // enable - backend responsiblity to clean thiese
+ 'applet', //
+ 'base', 'basefont', 'bgsound', 'blink', 'body',
+ 'frame', 'frameset', 'head', 'html', 'ilayer',
+ 'iframe', 'layer', 'link', 'meta', 'object',
+ 'script', 'style' ,'title', 'xml' // clean later..
+];
+Roo.HtmlEditorCore.clean = [
+ 'script', 'style', 'title', 'xml'
+];
+Roo.HtmlEditorCore.remove = [
+ 'font'
+];
+// attributes..
+
+Roo.HtmlEditorCore.ablack = [
+ 'on'
+];
+
+Roo.HtmlEditorCore.aclean = [
+ 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
+];
+
+// protocols..
+Roo.HtmlEditorCore.pwhite= [
+ 'http', 'https', 'mailto'
+];
+
+// white listed style attributes.
+Roo.HtmlEditorCore.cwhite= [
+ // 'text-align', /// default is to allow most things..
+
+
+// 'font-size'//??
+];
+
+// black listed style attributes.
+Roo.HtmlEditorCore.cblack= [
+ // 'font-size' -- this can be set by the project
+];
+
+
+Roo.HtmlEditorCore.swapCodes =[
+ [ 8211, "--" ],
+ [ 8212, "--" ],
+ [ 8216, "'" ],
+ [ 8217, "'" ],
+ [ 8220, '"' ],
+ [ 8221, '"' ],
+ [ 8226, "*" ],
+ [ 8230, "..." ]
+];
+
+ //<script type="text/javascript">
+
+/*
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ * Licence LGPL
+ *
+ */
+
+
+Roo.form.HtmlEditor = function(config){
+
+
+
+ Roo.form.HtmlEditor.superclass.constructor.call(this, config);
+
+ if (!this.toolbars) {
+ this.toolbars = [];
+ }
+ this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
+
+
+};
+
+/**
+ * @class Roo.form.HtmlEditor
+ * @extends Roo.form.Field
+ * Provides a lightweight HTML Editor component.
+ *
+ * This has been tested on Fireforx / Chrome.. IE may not be so great..
+ *
+ * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
+ * supported by this editor.</b><br/><br/>
+ * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
+ * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
+ */
+Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
+ /**
+ * @cfg {Boolean} clearUp
+ */
+ clearUp : true,
+ /**
+ * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+ */
+ toolbars : false,
+
+ /**
+ * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
+ * Roo.resizable.
+ */
+ resizable : false,
+ /**
+ * @cfg {Number} height (in pixels)
+ */
+ height: 300,
+ /**
+ * @cfg {Number} width (in pixels)
+ */
+ width: 500,
+
+ /**
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
+ */
+ stylesheets: false,
+
+
+ /**
+ * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
+ *
+ */
+ cblack: false,
+ /**
+ * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
+ *
+ */
+ cwhite: false,
+
+ /**
+ * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
+ *
+ */
+ black: false,
+ /**
+ * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
+ *
+ */
+ white: false,
+
+ // id of frame..
+ frameId: false,
+
+ // private properties
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
+
+ onFocus : Roo.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
+
+ defaultAutoCreate : { // modified by initCompnoent..
+ tag: "textarea",
+ style:"width:500px;height:300px;",
+ autocomplete: "off"
+ },
+
+ // private
+ initComponent : function(){
+ this.addEvents({
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {HtmlEditor} this
+ */
+ initialize: true,
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {HtmlEditor} this
+ */
+ activate: true,
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ beforesync: true,
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ beforepush: true,
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ sync: true,
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ push: true,
+ /**
+ * @event editmodechange
+ * Fires when the editor switches edit modes
+ * @param {HtmlEditor} this
+ * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+ */
+ editmodechange: true,
+ /**
+ * @event editorevent
+ * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+ * @param {HtmlEditor} this
+ */
+ editorevent: true,
+ /**
+ * @event firstfocus
+ * Fires when on first focus - needed by toolbars..
+ * @param {HtmlEditor} this
+ */
+ firstfocus: true,
+ /**
+ * @event autosave
+ * Auto save the htmlEditor value as a file into Events
+ * @param {HtmlEditor} this
+ */
+ autosave: true,
+ /**
+ * @event savedpreview
+ * preview the saved version of htmlEditor
+ * @param {HtmlEditor} this
+ */
+ savedpreview: true
+ });
+ this.defaultAutoCreate = {
+ tag: "textarea",
+ style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
+ autocomplete: "off"
+ };
+ },
+
+ /**
+ * Protected method that will not generally be called directly. It
+ * is called when the editor creates its toolbar. Override this method if you need to
+ * add custom toolbar buttons.
+ * @param {HtmlEditor} editor
+ */
+ createToolbar : function(editor){
+ Roo.log("create toolbars");
+ if (!editor.toolbars || !editor.toolbars.length) {
+ editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
+ }
+
+ for (var i =0 ; i < editor.toolbars.length;i++) {
+ editor.toolbars[i] = Roo.factory(
+ typeof(editor.toolbars[i]) == 'string' ?
+ { xtype: editor.toolbars[i]} : editor.toolbars[i],
+ Roo.form.HtmlEditor);
+ editor.toolbars[i].init(editor);
+ }
+
+
+ },
+
+
+ // private
+ onRender : function(ct, position)
+ {
+ var _t = this;
+ Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
+
+ this.wrap = this.el.wrap({
+ cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
+ });
+
+ this.editorcore.onRender(ct, position);
+
+ if (this.resizable) {
+ this.resizeEl = new Roo.Resizable(this.wrap, {
+ pinned : true,
+ wrap: true,
+ dynamic : true,
+ minHeight : this.height,
+ height: this.height,
+ handles : this.resizable,
+ width: this.width,
+ listeners : {
+ resize : function(r, w, h) {
+ _t.onResize(w,h); // -something
+ }
+ }
+ });
+
+ }
+ this.createToolbar(this);
+
+
+ if(!this.width){
+ this.setSize(this.wrap.getSize());
+ }
+ if (this.resizeEl) {
+ this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
+ // should trigger onReize..
+ }
+
+// if(this.autosave && this.w){
+// this.autoSaveFn = setInterval(this.autosave, 1000);
+// }
+ },
+
+ // private
+ onResize : function(w, h)
+ {
+ //Roo.log('resize: ' +w + ',' + h );
+ Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
+ var ew = false;
+ var eh = false;
+
+ if(this.el ){
+ if(typeof w == 'number'){
+ var aw = w - this.wrap.getFrameWidth('lr');
+ this.el.setWidth(this.adjustWidth('textarea', aw));
+ ew = aw;
+ }
+ if(typeof h == 'number'){
+ var tbh = 0;
+ for (var i =0; i < this.toolbars.length;i++) {
+ // fixme - ask toolbars for heights?
+ tbh += this.toolbars[i].tb.el.getHeight();
+ if (this.toolbars[i].footer) {
+ tbh += this.toolbars[i].footer.el.getHeight();
+ }
}
- if (a.value.match(/body/)) {
- node.className = '';
- }
- continue;
+
+
+
+ var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
+ ah -= 5; // knock a few pixes off for look..
+ this.el.setHeight(this.adjustWidth('textarea', ah));
+ var eh = ah;
}
+ }
+ Roo.log('onResize:' + [w,h,ew,eh].join(',') );
+ this.editorcore.onResize(ew,eh);
+
+ },
+
+ /**
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+ */
+ toggleSourceEdit : function(sourceEditMode)
+ {
+ this.editorcore.toggleSourceEdit(sourceEditMode);
+
+ if(this.editorcore.sourceEditMode){
+ Roo.log('editor - showing textarea');
- // style cleanup!?
- // class cleanup?
+// Roo.log('in');
+// Roo.log(this.syncValue());
+ this.editorcore.syncValue();
+ this.el.removeClass('x-hidden');
+ this.el.dom.removeAttribute('tabIndex');
+ this.el.focus();
+ }else{
+ Roo.log('editor - hiding textarea');
+// Roo.log('out')
+// Roo.log(this.pushValue());
+ this.editorcore.pushValue();
+ this.el.addClass('x-hidden');
+ this.el.dom.setAttribute('tabIndex', -1);
+ //this.deferFocus();
}
+
+ this.setSize(this.wrap.getSize());
+ this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
+ },
+
+ // private (for BoxComponent)
+ adjustSize : Roo.BoxComponent.prototype.adjustSize,
+
+ // private (for BoxComponent)
+ getResizeEl : function(){
+ return this.wrap;
+ },
+
+ // private (for BoxComponent)
+ getPositionEl : function(){
+ return this.wrap;
+ },
+
+ // private
+ initEvents : function(){
+ this.originalValue = this.getValue();
+ },
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ markInvalid : Roo.emptyFn,
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ clearInvalid : Roo.emptyFn,
+
+ setValue : function(v){
+ Roo.form.HtmlEditor.superclass.setValue.call(this, v);
+ this.editorcore.pushValue();
+ },
+
+
+ // private
+ deferFocus : function(){
+ this.focus.defer(10, this);
+ },
+
+ // doc'ed in Field
+ focus : function(){
+ this.editorcore.focus();
+ },
+
+
+ // private
+ onDestroy : function(){
- this.cleanUpChildren(node);
- }
+ if(this.rendered){
+
+ for (var i =0; i < this.toolbars.length;i++) {
+ // fixme - ask toolbars for heights?
+ this.toolbars[i].onDestroy();
+ }
+
+ this.wrap.dom.innerHTML = '';
+ this.wrap.remove();
+ }
+ },
+
+ // private
+ onFirstFocus : function(){
+ //Roo.log("onFirstFocus");
+ this.editorcore.onFirstFocus();
+ for (var i =0; i < this.toolbars.length;i++) {
+ this.toolbars[i].onFirstFocus();
+ }
+
+ },
+
+ // private
+ syncValue : function()
+ {
+ this.editorcore.syncValue();
+ },
+ pushValue : function()
+ {
+ this.editorcore.pushValue();
+ }
+
// hide stuff that is not compatible
/**
* @cfg {String} validateOnBlur @hide
*/
});
-
-Roo.form.HtmlEditor.white = [
- 'area', 'br', 'img', 'input', 'hr', 'wbr',
-
- 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
- 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
- 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
- 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
- 'table', 'ul', 'xmp',
-
- 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr',
-
- 'dir', 'menu', 'ol', 'ul', 'dl',
-
- 'embed', 'object'
-];
-
-
-Roo.form.HtmlEditor.black = [
- // 'embed', 'object', // enable - backend responsiblity to clean thiese
- 'applet', //
- 'base', 'basefont', 'bgsound', 'blink', 'body',
- 'frame', 'frameset', 'head', 'html', 'ilayer',
- 'iframe', 'layer', 'link', 'meta', 'object',
- 'script', 'style' ,'title', 'xml' // clean later..
-];
-Roo.form.HtmlEditor.clean = [
- 'script', 'style', 'title', 'xml'
-];
-Roo.form.HtmlEditor.remove = [
- 'font'
-];
-// attributes..
-
-Roo.form.HtmlEditor.ablack = [
- 'on'
-];
-
-Roo.form.HtmlEditor.aclean = [
- 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
-];
-
-// protocols..
-Roo.form.HtmlEditor.pwhite= [
- 'http', 'https', 'mailto'
-];
-
-// white listed style attributes.
-Roo.form.HtmlEditor.cwhite= [
- // 'text-align', /// default is to allow most things..
-
-
-// 'font-size'//??
-];
-
-// black listed style attributes.
-Roo.form.HtmlEditor.cblack= [
- // 'font-size' -- this can be set by the project
-];
-
-
-Roo.form.HtmlEditor.swapCodes =[
- [ 8211, "--" ],
- [ 8212, "--" ],
- [ 8216, "'" ],
- [ 8217, "'" ],
- [ 8220, '"' ],
- [ 8221, '"' ],
- [ 8226, "*" ],
- [ 8230, "..." ]
-];
-
+
// <script type="text/javascript">
/*
* Based on
rendered: false,
editor : false,
+ editorcore : false,
/**
* @cfg {Object} disable List of toolbar elements to disable
*/
disable : false,
+
+
+ /**
+ * @cfg {String} createLinkText The default text for the create link prompt
+ */
+ createLinkText : 'Please enter the URL for the link:',
+ /**
+ * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
+ */
+ defaultLinkValue : 'http:/'+'/',
+
+
/**
* @cfg {Array} fontFamilies An array of available font families
*/
init : function(editor)
{
this.editor = editor;
+ this.editorcore = editor.editorcore ? editor.editorcore : editor;
+ var editorcore = this.editorcore;
+ var _t = this;
- var fid = editor.frameId;
+ var fid = editorcore.frameId;
var etb = this;
function btn(id, toggle, handler){
var xid = fid + '-'+ id ;
cmd : id,
cls : 'x-btn-icon x-edit-'+id,
enableToggle:toggle !== false,
- scope: editor, // was editor...
- handler:handler||editor.relayBtnCmd,
+ scope: _t, // was editor...
+ handler:handler||_t.relayBtnCmd,
clickEvent:'mousedown',
tooltip: etb.buttonTips[id] || undefined, ///tips ???
tabIndex:-1
width:135,
listeners : {
'select': function(c, r, i) {
- editor.insertTag(r.get('tag'));
+ editorcore.insertTag(r.get('tag'));
editor.focus();
}
}
'-',
- btn('increasefontsize', false, editor.adjustFont),
- btn('decreasefontsize', false, editor.adjustFont)
+ btn('increasefontsize', false, editorcore.adjustFont),
+ btn('decreasefontsize', false, editorcore.adjustFont)
);
};
if(!this.disable.colors){
tb.add(
'-', {
- id:editor.frameId +'-forecolor',
+ id:editorcore.frameId +'-forecolor',
cls:'x-btn-icon x-edit-forecolor',
clickEvent:'mousedown',
tooltip: this.buttonTips['forecolor'] || undefined,
value:'000000',
plain:true,
selectHandler: function(cp, color){
- editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
+ editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
editor.deferFocus();
},
- scope: editor,
+ scope: editorcore,
clickEvent:'mousedown'
})
}, {
- id:editor.frameId +'backcolor',
+ id:editorcore.frameId +'backcolor',
cls:'x-btn-icon x-edit-backcolor',
clickEvent:'mousedown',
tooltip: this.buttonTips['backcolor'] || undefined,
allowReselect: true,
selectHandler: function(cp, color){
if(Roo.isGecko){
- editor.execCmd('useCSS', false);
- editor.execCmd('hilitecolor', color);
- editor.execCmd('useCSS', true);
+ editorcore.execCmd('useCSS', false);
+ editorcore.execCmd('hilitecolor', color);
+ editorcore.execCmd('useCSS', true);
editor.deferFocus();
}else{
- editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
+ editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
Roo.isSafari || Roo.isIE ? '#'+color : color);
editor.deferFocus();
}
},
- scope:editor,
+ scope:editorcore,
clickEvent:'mousedown'
})
}
if(!this.disable.links){
tb.add(
'-',
- btn('createlink', false, editor.createLink) /// MOVE TO HERE?!!?!?!?!
+ btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
);
};
tb.add(
'-',
btn('sourceedit', true, function(btn){
+ Roo.log(this);
this.toggleSourceEdit(btn.pressed);
})
);
html: this.specialChars[i],
handler: function(a,b) {
- editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
+ editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
//editor.insertAtCursor(a.html);
},
handler: function(a,b) {
Roo.log(a);
Roo.log(b);
- var c = Roo.get(editor.doc.body);
+ var c = Roo.get(editorcore.doc.body);
c.select('[style]').each(function(s) {
s.dom.style.removeProperty(a.actiontype);
});
-
+ editorcore.syncValue();
},
tabIndex:-1
});
}
+ cmenu.menu.items.push({
+ actiontype : 'word',
+ html: 'Remove MS Word Formating',
+ handler: function(a,b) {
+ editorcore.cleanWord();
+ editorcore.syncValue();
+ },
+ tabIndex:-1
+ });
+
+ cmenu.menu.items.push({
+ actiontype : 'all',
+ html: 'Remove All Styles',
+ handler: function(a,b) {
+
+ var c = Roo.get(editorcore.doc.body);
+ c.select('[style]').each(function(s) {
+ s.dom.removeAttribute('style');
+ });
+ editorcore.syncValue();
+ },
+ tabIndex:-1
+ });
+ cmenu.menu.items.push({
+ actiontype : 'word',
+ html: 'Tidy HTML Source',
+ handler: function(a,b) {
+ editorcore.doc.body.innerHTML = editorcore.domToHTML();
+ editorcore.syncValue();
+ },
+ tabIndex:-1
+ });
+
tb.add(cmenu);
}
for(var i =0; i< this.btns.length;i++) {
var b = Roo.factory(this.btns[i],Roo.form);
b.cls = 'x-edit-none';
- b.scope = editor;
+ b.scope = editorcore;
tb.add(b);
}
// disable everything...
this.tb.items.each(function(item){
- if(item.id != editor.frameId+ '-sourceedit'){
+ if(item.id != editorcore.frameId+ '-sourceedit'){
item.disable();
}
});
},
+ relayBtnCmd : function(btn) {
+ this.editorcore.relayCmd(btn.cmd);
+ },
+ // private used internally
+ createLink : function(){
+ Roo.log("create link?");
+ var url = prompt(this.createLinkText, this.defaultLinkValue);
+ if(url && url != 'http:/'+'/'){
+ this.editorcore.relayCmd('createlink', url);
+ }
+ },
+
/**
* Protected method that will not generally be called directly. It triggers
*/
updateToolbar: function(){
- if(!this.editor.activated){
+ if(!this.editorcore.activated){
this.editor.onFirstFocus();
return;
}
var btns = this.tb.items.map,
- doc = this.editor.doc,
- frameId = this.editor.frameId;
+ doc = this.editorcore.doc,
+ frameId = this.editorcore.frameId;
if(!this.disable.font && !Roo.isSafari){
/*
btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
}
- var ans = this.editor.getAllAncestors();
+ var ans = this.editorcore.getAllAncestors();
if (this.formatCombo) {
},
toggleSourceEdit : function(sourceEditMode){
+
+ Roo.log("toolbar toogle");
if(sourceEditMode === undefined){
sourceEditMode = !this.sourceEditMode;
}
this.sourceEditMode = sourceEditMode === true;
- var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
+ var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
// just toggle the button?
- if(btn.pressed !== this.editor.sourceEditMode){
- btn.toggle(this.editor.sourceEditMode);
+ if(btn.pressed !== this.sourceEditMode){
+ btn.toggle(this.sourceEditMode);
return;
}
- if(this.sourceEditMode){
+ if(sourceEditMode){
+ Roo.log("disabling buttons");
this.tb.items.each(function(item){
if(item.cmd != 'sourceedit'){
item.disable();
});
}else{
- if(this.initialized){
+ Roo.log("enabling buttons");
+ if(this.editorcore.initialized){
this.tb.items.each(function(item){
item.enable();
});
}
}
+ Roo.log("calling toggole on editor");
// tell the editor that it's been pressed..
this.editor.toggleSourceEdit(sourceEditMode);
rendered: false,
editor : false,
+ editorcore : false,
/**
* @cfg {Object} disable List of toolbar elements to disable
init : function(editor)
{
this.editor = editor;
+ this.editorcore = editor.editorcore ? editor.editorcore : editor;
+ var editorcore = this.editorcore;
-
- var fid = editor.frameId;
+ var fid = editorcore.frameId;
var etb = this;
function btn(id, toggle, handler){
var xid = fid + '-'+ id ;
cmd : id,
cls : 'x-btn-icon x-edit-'+id,
enableToggle:toggle !== false,
- scope: editor, // was editor...
- handler:handler||editor.relayBtnCmd,
+ scope: editorcore, // was editor...
+ handler:handler||editorcore.relayBtnCmd,
clickEvent:'mousedown',
tooltip: etb.buttonTips[id] || undefined, ///tips ???
tabIndex:-1
//Roo.log(ev);
// capture mouse up - this is handy for selecting images..
// perhaps should go somewhere else...
- if(!this.editor.activated){
+ if(!this.editorcore.activated){
this.editor.onFirstFocus();
return;
}
nodeRange.selectNodeContents(sel);
}
//nodeRange.collapse(true);
- var s = editor.win.getSelection();
+ var s = this.editorcore.win.getSelection();
s.removeAllRanges();
s.addRange(nodeRange);
}
var updateFooter = sel ? false : true;
- var ans = this.editor.getAllAncestors();
+ var ans = this.editorcore.getAllAncestors();
// pick
var ty= Roo.form.HtmlEditor.ToolbarContext.types;
if (!sel) {
- sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editor.doc.body;
- sel = sel ? sel : this.editor.doc.body;
- sel = sel.tagName.length ? sel : this.editor.doc.body;
+ sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
+ sel = sel ? sel : this.editorcore.doc.body;
+ sel = sel.tagName.length ? sel : this.editorcore.doc.body;
}
// pick a menu that exists..
buildToolbar: function(tlist, nm)
{
var editor = this.editor;
+ var editorcore = this.editorcore;
// create a new element.
var wdiv = editor.wrap.createChild({
tag: 'div'
'select': function(c, r, i) {
// initial support only for on class per el..
tb.selectedNode.className = r ? r.get('val') : '';
- editor.syncValue();
+ editorcore.syncValue();
}
}
}
pn.removeChild(sn);
- var range = editor.createRange();
+ var range = editorcore.createRange();
range.setStart(stn,0);
range.setEnd(en,0); //????
//range.selectNode(sel);
- var selection = editor.getSelection();
+ var selection = editorcore.getSelection();
selection.removeAllRanges();
selection.addRange(range);
var sel = ans[n];
// pick
- var range = this.editor.createRange();
+ var range = this.editorcore.createRange();
range.selectNodeContents(sel);
//range.selectNode(sel);
- var selection = this.editor.getSelection();
+ var selection = this.editorcore.getSelection();
selection.removeAllRanges();
selection.addRange(range);
var v = this.view;
var header = v.findHeaderIndex(t);
if(header !== false){
- this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
+ var ename = name == 'touchstart' ? 'click' : name;
+
+ this.fireEvent("header" + ename, this, header, e);
}else{
var row = v.findRowIndex(t);
var cell = v.findCellIndex(t);
hdClass : "x-grid-hd",
splitClass : "x-grid-hd-split",
- init: function(grid){
+ init: function(grid){
this.grid = grid;
var cid = this.grid.getGridEl().id;
this.colSelector = "#" + cid + " ." + this.cellClass + "-";
this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
},
- getColumnRenderers : function(){
+ getColumnRenderers : function(){
var renderers = [];
var cm = this.grid.colModel;
var colCount = cm.getColumnCount();
},
- handleHeaderClick : function(g, index){
+ handleHeaderClick : function(g, index,e){
+
+ Roo.log("header click");
+
+ if (Roo.isTouch) {
+ // touch events on header are handled by context
+ this.handleHdCtx(g,index,e);
+ return;
+ }
+
+
if(this.headersDisabled){
return;
}
cm.setLocked(index, false);
}
break;
+ case 'wider': // used to expand cols on touch..
+ case 'narrow':
+ var cw = cm.getColumnWidth(index);
+ cw += (item.id == 'wider' ? 1 : -1) * 50;
+ cw = Math.max(0, cw);
+ cw = Math.min(cw,4000);
+ cm.setColumnWidth(index, cw);
+ break;
+
default:
index = cm.getIndexById(item.id.substr(4));
if(index != -1){
{id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
);
}
+ if (Roo.isTouch) {
+ this.hmenu.add('-',
+ {id:"wider", text: this.columnsWiderText},
+ {id:"narrow", text: this.columnsNarrowText }
+ );
+
+
+ }
+
if(this.grid.enableColumnHide !== false){
this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
sortDescText : "Sort Descending",
lockText : "Lock Column",
unlockText : "Unlock Column",
- columnsText : "Columns"
+ columnsText : "Columns",
+
+ columnsWiderText : "Wider",
+ columnsNarrowText : "Thinner"
});
/**
* @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
* given the cell's data value. See {@link #setRenderer}. If not specified, the
- * default renderer uses the raw data value.
+ * default renderer uses the raw data value. If an object is returned (bootstrap only)
+ * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
*/
/**
* @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor