})();
Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
- "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
+ "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
+ "Roo.app", "Roo.ux",
+ "Roo.bootstrap",
+ "Roo.bootstrap.dash");
/*
* Based on:
* Ext JS Library 1.1.1
var E = Roo.lib.Event;
var D = Roo.lib.Dom;
+
+
var fireDocReady = function(){
if(!docReadyState){
}, o.delay || 10);
};
};
+ var transitionEndVal = false;
+
+ var transitionEnd = function()
+ {
+ if (transitionEndVal) {
+ return transitionEndVal;
+ }
+ var el = document.createElement('div');
+
+ var transEndEventNames = {
+ WebkitTransition : 'webkitTransitionEnd',
+ MozTransition : 'transitionend',
+ OTransition : 'oTransitionEnd otransitionend',
+ transition : 'transitionend'
+ };
+
+ for (var name in transEndEventNames) {
+ if (el.style[name] !== undefined) {
+ transitionEndVal = transEndEventNames[name];
+ return transitionEndVal ;
+ }
+ }
+ }
+
var listen = function(element, ename, opt, fn, scope){
var o = (!opt || typeof opt == "boolean") ? {} : opt;
fn = fn || o.fn; scope = scope || o.scope;
var el = Roo.getDom(element);
+
+
if(!el){
throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
}
+
+ if (ename == 'transitionend') {
+ ename = transitionEnd();
+ }
var h = function(e){
e = Roo.EventObject.setEvent(e);
var t;
h = createBuffered(h, o);
}
fn._handlers = fn._handlers || [];
+
+
fn._handlers.push([Roo.id(el), ename, h]);
-
+
+
+
E.on(el, ename, h);
if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
el.addEventListener("DOMMouseScroll", h, false);
*/
mask : function(msg, msgCls)
{
- if(this.getStyle("position") == "static"){
+ if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
this.setStyle("position", "relative");
}
if(!this._mask){
/**
* Sets or Returns the value the dom attribute value
- * @param {String} name The attribute name
+ * @param {String|Object} name The attribute name (or object to set multiple attributes)
* @param {String} value (optional) The value to set the attribute to
* @return {String} The attribute value
*/
this.dom.setAttribute(name, arguments[1]);
return arguments[1];
}
+ if (typeof(name) == 'object') {
+ for(var i in name) {
+ this.attr(i, name[i]);
+ }
+ return name;
+ }
+
+
if (!this.dom.hasAttribute(name)) {
return undefined;
}
/**
* Filters this composite to only elements that match the passed selector.
* @param {String} selector A string CSS selector
+ * @param {Boolean} inverse return inverse filter (not matches)
* @return {CompositeElement} this
*/
- filter : function(selector){
+ filter : function(selector, inverse){
var els = [];
+ inverse = inverse || false;
this.each(function(el){
- if(el.is(selector)){
+ var match = inverse ? !el.is(selector) : el.is(selector);
+ if(match){
els[els.length] = el.dom;
}
});
* @class Roo.XComponent
* A delayed Element creator...
* Or a way to group chunks of interface together.
+ * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
+ * used in conjunction with XComponent.build() it will create an instance of each element,
+ * then call addxtype() to build the User interface.
*
* Mypart.xyx = new Roo.XComponent({
* It can be used to build a big heiracy, with parent etc.
* or you can just use this to render a single compoent to a dom element
* MYPART.render(Roo.Element | String(id) | dom_element )
+ *
+ *
+ * Usage patterns.
+ *
+ * Classic Roo
+ *
+ * Roo is designed primarily as a single page application, so the UI build for a standard interface will
+ * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
+ *
+ * Each sub module is expected to have a parent pointing to the class name of it's parent module.
+ *
+ * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
+ * - if mulitple topModules exist, the last one is defined as the top module.
+ *
+ * Embeded Roo
+ *
+ * When the top level or multiple modules are to embedded into a existing HTML page,
+ * the parent element can container '#id' of the element where the module will be drawn.
+ *
+ * Bootstrap Roo
+ *
+ * Unlike classic Roo, the bootstrap tends not to be used as a single page.
+ * it relies more on a include mechanism, where sub modules are included into an outer page.
+ * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
+ *
+ * Bootstrap Roo Included elements
+ *
+ * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
+ * hence confusing the component builder as it thinks there are multiple top level elements.
+ *
+ *
*
* @extends Roo.util.Observable
* @constructor
el = el || false;
var hp = this.parent ? 1 : 0;
+ Roo.log(this);
if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
// if parent is a '#.....' string, then let's use that..
- var ename = this.parent.substr(1)
- this.parent = (this.parent == '#bootstrap') ? { el : true} : false; // flags it as a top module...
- el = Roo.get(ename);
+ var ename = this.parent.substr(1);
+ this.parent = false;
+ Roo.log(ename);
+ switch (ename) {
+ case 'bootstrap-body' :
+ if (typeof(Roo.bootstrap.Body) != 'undefined') {
+ this.parent = { el : new Roo.bootstrap.Body() };
+ Roo.log("setting el to doc body");
+
+ } else {
+ throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
+ }
+ break;
+ case 'bootstrap':
+ this.parent = { el : true};
+ // fall through
+ default:
+ el = Roo.get(ename);
+ break;
+ }
+
+
if (!el && !this.parent) {
Roo.log("Warning - element can not be found :#" + ename );
return;
}
}
+ Roo.log("EL:");Roo.log(el);
+ Roo.log("this.parent.el:");Roo.log(this.parent.el);
+
var tree = this._tree ? this._tree() : this.tree();
+ // altertive root elements ??? - we need a better way to indicate these.
+ var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
+ (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
- if (!this.parent && typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) {
+ if (!this.parent && is_alt) {
//el = Roo.get(document.body);
this.parent = { el : true };
}
}
}
- if (!this.parent.el) {
- // probably an old style ctor, which has been disabled.
- return;
-
- }
+ if (!this.parent.el) {
+ // probably an old style ctor, which has been disabled.
+ return;
+
+ }
// The 'tree' method is '_tree now'
tree.region = tree.region || this.region;
this.panel = this.el;
this.layout = this.panel.layout;
- this.parentLayout = this.parent.layout || false;
+ this.parentLayout = this.parent.layout || false;
}
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();
},
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;
+ //this.setSize(this.owner.wrap.getSize());
+ //this.fireEvent('editmodechange', this, this.sourceEditMode);
},
- // 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.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;
}
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
/**
*/
});
-Roo.form.HtmlEditor.white = [
+Roo.HtmlEditorCore.white = [
'area', 'br', 'img', 'input', 'hr', 'wbr',
'address', 'blockquote', 'center', 'dd', 'dir', 'div',
];
-Roo.form.HtmlEditor.black = [
+Roo.HtmlEditorCore.black = [
// 'embed', 'object', // enable - backend responsiblity to clean thiese
'applet', //
'base', 'basefont', 'bgsound', 'blink', 'body',
'iframe', 'layer', 'link', 'meta', 'object',
'script', 'style' ,'title', 'xml' // clean later..
];
-Roo.form.HtmlEditor.clean = [
+Roo.HtmlEditorCore.clean = [
'script', 'style', 'title', 'xml'
];
-Roo.form.HtmlEditor.remove = [
+Roo.HtmlEditorCore.remove = [
'font'
];
// attributes..
-Roo.form.HtmlEditor.ablack = [
+Roo.HtmlEditorCore.ablack = [
'on'
];
-Roo.form.HtmlEditor.aclean = [
+Roo.HtmlEditorCore.aclean = [
'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
];
// protocols..
-Roo.form.HtmlEditor.pwhite= [
+Roo.HtmlEditorCore.pwhite= [
'http', 'https', 'mailto'
];
// white listed style attributes.
-Roo.form.HtmlEditor.cwhite= [
+Roo.HtmlEditorCore.cwhite= [
// 'text-align', /// default is to allow most things..
];
// black listed style attributes.
-Roo.form.HtmlEditor.cblack= [
+Roo.HtmlEditorCore.cblack= [
// 'font-size' -- this can be set by the project
];
-Roo.form.HtmlEditor.swapCodes =[
+Roo.HtmlEditorCore.swapCodes =[
[ 8211, "--" ],
[ 8212, "--" ],
[ 8216, "'" ],
[ 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();
+ }
+ }
+
+
+
+
+ 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');
+
+// 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(){
+
+
+
+ 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
+ /**
+ * @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
+ */
+});
+
// <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);
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();
/**
* @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