document.execCommand("BackgroundImageCache", false, true);
}catch(e){}
}
-
+
Roo.apply(Roo, {
/**
* True if the browser is in strict mode
H 15 24-hour format of an hour with leading zeros
i 05 Minutes with leading zeros
s 01 Seconds, with leading zeros
- O -0600 Difference to Greenwich time (GMT) in hours
+ O -0600 Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
+ P -06:00 Difference to Greenwich time (GMT) with colon between hours and minutes
T CST Timezone setting of the machine running the code
Z -21600 Timezone offset in seconds (negative if west of UTC, positive if east)
</pre>
return "String.leftPad(this.getSeconds(), 2, '0') + ";
case "O":
return "this.getGMTOffset() + ";
+ case "P":
+ return "this.getGMTColonOffset() + ";
case "T":
return "this.getTimezone() + ";
case "Z":
"o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
" (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
].join(""),
+ s:"([+\-]\\d{2,4})"};
+
+
+ case "P":
+ return {g:1,
+ c:[
+ "o = results[", currentGroup, "];\n",
+ "var sn = o.substring(0,1);\n",
+ "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
+ "var mn = o.substring(4,6) % 60;\n",
+ "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
+ " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
+ ].join(""),
s:"([+\-]\\d{4})"};
case "T":
return {g:0,
+ String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
};
+/**
+ * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
+ * @return {String} 2-characters representing hours and 2-characters representing minutes
+ * seperated by a colon and prefixed with + or - (e.g. '-06:00')
+ */
+Date.prototype.getGMTColonOffset = function() {
+ return (this.getTimezoneOffset() > 0 ? "-" : "+")
+ + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
+ + ":"
+ + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
+}
+
/**
* Get the numeric day number of the year, adjusted for leap year.
* @return {Number} 0 through 364 (365 in leap years)
break;
}
return d;
-};/*
+};
+/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
* <script type="text/javascript">
*/
+/**
+ * @class Roo.lib.Dom
+ * @static
+ *
+ * Dom utils (from YIU afaik)
+ *
+ **/
Roo.lib.Dom = {
+ /**
+ * Get the view width
+ * @param {Boolean} full True will get the full document, otherwise it's the view width
+ * @return {Number} The width
+ */
+
getViewWidth : function(full) {
return full ? this.getDocumentWidth() : this.getViewportWidth();
},
-
+ /**
+ * Get the view height
+ * @param {Boolean} full True will get the full document, otherwise it's the view height
+ * @return {Number} The height
+ */
getViewHeight : function(full) {
return full ? this.getDocumentHeight() : this.getViewportHeight();
},
*/
(function() {
-
+ /**
+ * @class Roo.lib.Ajax
+ *
+ */
Roo.lib.Ajax = {
+ /**
+ * @static
+ */
request : function(method, uri, cb, data, options) {
if(options){
var hs = options.headers;
* Fork - LGPL
* <script type="text/javascript">
*/
-
+
+
+// nasty IE9 hack - what a pile of crap that is..
+
+ if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
+ Range.prototype.createContextualFragment = function (html) {
+ var doc = window.document;
+ var container = doc.createElement("div");
+ container.innerHTML = html;
+ var frag = doc.createDocumentFragment(), n;
+ while ((n = container.firstChild)) {
+ frag.appendChild(n);
+ }
+ return frag;
+ };
+}
/**
* @class Roo.DomHelper
});
t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
</code></pre>
-* For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>.
+* For more information see this blog post with examples:
+* <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
+ - Create Elements using DOM, HTML fragments and Templates</a>.
* @constructor
* @param {Object} cfg - Configuration object.
*/
// bc
this.html = cfg;
}
-
+ if (this.url) {
+ this.load();
+ }
};
Roo.Template.prototype = {
+ /**
+ * @cfg {String} url The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
+ * it should be fixed so that template is observable...
+ */
+ url : false,
/**
* @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
*/
*/
applyTemplate : function(values){
try {
-
+
if(this.compiled){
return this.compiled(values);
}
},
+ loading : false,
+
+ load : function ()
+ {
+
+ if (this.loading) {
+ return;
+ }
+ var _t = this;
+
+ this.loading = true;
+ this.compiled = false;
+
+ var cx = new Roo.data.Connection();
+ cx.request({
+ url : this.url,
+ method : 'GET',
+ success : function (response) {
+ _t.loading = false;
+ _t.html = response.responseText;
+ _t.url = false;
+ _t.compile();
+ },
+ failure : function(response) {
+ Roo.log("Template failed to load from " + url);
+ _t.loading = false;
+ }
+ });
+ },
+
/**
* Sets the HTML used as the template and optionally compiles it.
* @param {String} html
* @param {String} msgCls (optional) A css class to apply to the msg element
* @return {Element} The mask element
*/
- mask : function(msg, msgCls){
+ mask : function(msg, msgCls)
+ {
if(this.getStyle("position") == "static"){
this.setStyle("position", "relative");
}
}
this.addClass("x-masked");
this._mask.setDisplayed(true);
+
+ // we wander
+ var z = 0;
+ var dom = this.dom
+ while (dom && dom.style) {
+ if (!isNaN(parseInt(dom.style.zIndex))) {
+ z = Math.max(z, parseInt(dom.style.zIndex));
+ }
+ dom = dom.parentNode;
+ }
+ // if we are masking the body - then it hides everything..
+ if (this.dom == document.body) {
+ z = 1000000;
+ this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
+ this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
+ }
+
if(typeof msg == 'string'){
if(!this._maskMsg){
this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
mm.dom.firstChild.innerHTML = msg;
mm.setDisplayed(true);
mm.center(this);
+ mm.setStyle('z-index', z + 102);
}
if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
this._mask.setHeight(this.getHeight());
}
+ this._mask.setStyle('z-index', z + 100);
+
return this._mask;
},
*/
/**
+ * Global Ajax request class.
+ *
* @class Roo.Ajax
* @extends Roo.data.Connection
- * Global Ajax request class.
- *
- * @instanceOf Roo.data.Connection
+ * @static
+ *
+ * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
+ * @cfg {Object} extraParams An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
+ * @cfg {Object} defaultHeaders An object containing request headers which are added to each request made by this object. (defaults to undefined)
+ * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+ * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+ * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
+ * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
*/
Roo.Ajax = new Roo.data.Connection({
// fix up the docs
-
/**
- * fix up scoping
* @scope Roo.Ajax
- */
-
- /**
- * @cfg {String} url @hide
- */
- /**
- * @cfg {Object} extraParams @hide
- */
- /**
- * @cfg {Object} defaultHeaders @hide
- */
- /**
- * @cfg {String} method (Optional) @hide
- */
- /**
- * @cfg {Number} timeout (Optional) @hide
- */
- /**
- * @cfg {Boolean} autoAbort (Optional) @hide
- */
-
- /**
- * @cfg {Boolean} disableCaching (Optional) @hide
- */
-
- /**
- * @property disableCaching
- * True to add a unique cache-buster param to GET requests. (defaults to true)
- * @type Boolean
- */
- /**
- * @property url
- * The default URL to be used for requests to the server. (defaults to undefined)
- * @type String
- */
- /**
- * @property extraParams
- * An object containing properties which are used as
- * extra parameters to each request made by this object. (defaults to undefined)
- * @type Object
- */
- /**
- * @property defaultHeaders
- * An object containing request headers which are added to each request made by this object. (defaults to undefined)
- * @type Object
- */
- /**
- * @property method
- * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
- * @type String
- */
- /**
- * @property timeout
- * The timeout in milliseconds to be used for requests. (defaults to 30000)
- * @type Number
- */
-
- /**
- * @property autoAbort
- * Whether a new request should abort any pending requests. (defaults to false)
- * @type Boolean
+ * @type {Boolear}
*/
autoAbort : false,
/**
* Serialize the passed form into a url encoded string
+ * @scope Roo.Ajax
* @param {String/HTMLElement} form
* @return {String}
*/
this.modified[name] = this.data[name];
}
this.data[name] = value;
- if(!this.editing){
+ if(!this.editing && this.store){
this.store.afterEdit(this);
}
},
"start" : "start",
"limit" : "limit",
"sort" : "sort",
- "dir" : "dir"
+ "dir" : "dir",
+ "multisort" : "_multisort"
};
if(config && config.data){
this.relayEvents(this.proxy, ["loadexception"]);
}
this.sortToggle = {};
+ this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
Roo.data.Store.superclass.constructor.call(this);
* @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
*/
/**
+ * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
+ */
+ multiSort: false,
+ /**
* @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
* version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
*/
p[pn["sort"]] = this.sortInfo.field;
p[pn["dir"]] = this.sortInfo.direction;
}
+ if (this.multiSort) {
+ var pn = this.paramNames;
+ p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
+ }
+
this.proxy.load(p, this.reader, this.loadRecords, this, options);
}
},
}
// if data returned failure - throw an exception.
if (o.success === false) {
+ // show a message if no listener is registered.
+ if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
+ }
+ // loadmask wil be hooked into this..
this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
return;
}
}
},
+
/**
* Loads data from a passed data block. A Reader which understands the format of the data
* must have been configured in the constructor.
sort : function(fieldName, dir){
var f = this.fields.get(fieldName);
if(!dir){
- if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
+ this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
+
+ if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
}else{
dir = f.sortDir;
}
this.fireEvent("update", this, record, Roo.data.Record.EDIT);
},
-
+
// private
afterReject : function(record){
this.modified.remove(record);
}
};
-// private
+/** @private */
Roo.Component.AUTO_ID = 1000;
Roo.extend(Roo.Component, Roo.util.Observable, {
/**
- * @property {Boolean} hidden
+ * @scope Roo.Component.prototype
+ * @type {Boolean}
* true if this component is hidden. Read-only.
*/
hidden : false,
/**
+ * @type {Boolean}
* true if this component is disabled. Read-only.
*/
disabled : false,
/**
+ * @type {Boolean}
* true if this component has been rendered. Read-only.
*/
rendered : false,
*/
hideMode: 'display',
- // private
+ /** @private */
ctype : "Roo.Component",
- /** @cfg {String} actionMode
+ /**
+ * @cfg {String} actionMode
* which property holds the element that used for hide() / show() / disable() / enable()
* default is 'el'
*/
actionMode : "el",
- // private
+ /** @private */
getActionEl : function(){
return this[this.actionMode];
},
return this;
},
- // private
+ /** @private */
// default function is not really useful
onRender : function(ct, position){
if(this.el){
}
},
- // private
+ /** @private */
getAutoCreate : function(){
var cfg = typeof this.autoCreate == "object" ?
this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
return cfg;
},
- // private
+ /** @private */
afterRender : Roo.emptyFn,
/**
}
},
- // private
+ /** @private */
beforeDestroy : function(){
},
- // private
+ /** @private */
onDestroy : function(){
},
return this;
},
- // private
+ /** @private */
blur : function(){
if(this.rendered){
this.el.blur();
/** @private */
this.addEvents({
- /**
- * @event beforeclick
- * Fires before a click is processed. Returns false to cancel the default action.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "beforeclick" : true,
- /**
- * @event click
- * Fires when a template node is clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "click" : true,
- /**
- * @event dblclick
- * Fires when a template node is double clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "dblclick" : true,
- /**
- * @event contextmenu
- * Fires when a template node is right clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "contextmenu" : true,
- /**
- * @event selectionchange
- * Fires when the selected nodes change.
- * @param {Roo.View} this
- * @param {Array} selections Array of the selected nodes
- */
- "selectionchange" : true,
-
- /**
- * @event beforeselect
- * Fires before a selection is made. If any handlers return false, the selection is cancelled.
- * @param {Roo.View} this
- * @param {HTMLElement} node The node to be selected
- * @param {Array} selections Array of currently selected nodes
- */
- "beforeselect" : true
- });
+ /**
+ * @event beforeclick
+ * Fires before a click is processed. Returns false to cancel the default action.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "beforeclick" : true,
+ /**
+ * @event click
+ * Fires when a template node is clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "click" : true,
+ /**
+ * @event dblclick
+ * Fires when a template node is double clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "dblclick" : true,
+ /**
+ * @event contextmenu
+ * Fires when a template node is right clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "contextmenu" : true,
+ /**
+ * @event selectionchange
+ * Fires when the selected nodes change.
+ * @param {Roo.View} this
+ * @param {Array} selections Array of the selected nodes
+ */
+ "selectionchange" : true,
+
+ /**
+ * @event beforeselect
+ * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+ * @param {Roo.View} this
+ * @param {HTMLElement} node The node to be selected
+ * @param {Array} selections Array of currently selected nodes
+ */
+ "beforeselect" : true,
+ /**
+ * @event preparedata
+ * Fires on every row to render, to allow you to change the data.
+ * @param {Roo.View} this
+ * @param {Object} data to be rendered (change this)
+ */
+ "preparedata" : true
+ });
this.el.on({
"click": this.onClick,
* @cfg {String|Roo.Template} tpl The template used by this View
*/
tpl : false,
-
+ /**
+ * @cfg {String} dataName the named area of the template to use as the data area
+ * Works with domtemplates roo-name="name"
+ */
+ dataName: false,
/**
* @cfg {String} selectedClass The css class to add to selected nodes
*/
/**
* @cfg {Boolean} multiSelect Allow multiple selection
*/
-
multiSelect : false,
/**
* @cfg {Boolean} singleSelect Allow single selection
*/
singleSelect: false,
+ /**
+ * @cfg {Boolean} toggleSelect - selecting
+ */
+ toggleSelect : false,
+
/**
* Returns the element this view is bound to.
* @return {Roo.Element}
*/
refresh : function(){
var t = this.tpl;
+
+ // if we are using something like 'domtemplate', then
+ // the what gets used is:
+ // t.applySubtemplate(NAME, data, wrapping data..)
+ // the outer template then get' applied with
+ // the store 'extra data'
+ // and the body get's added to the
+ // roo-name="data" node?
+ // <span class='roo-tpl-{name}'></span> ?????
+
+
+
this.clearSelections();
this.el.update("");
var html = [];
var records = this.store.getRange();
- if(records.length < 1){
+ if(records.length < 1) {
+
+ // is this valid?? = should it render a template??
+
this.el.update(this.emptyText);
return;
}
+ var el = this.el;
+ if (this.dataName) {
+ this.el.update(t.apply(this.store.meta)); //????
+ el = this.el.child('.roo-tpl-' + this.dataName);
+ }
+
for(var i = 0, len = records.length; i < len; i++){
var data = this.prepareData(records[i].data, i, records[i]);
- html[html.length] = t.apply(data);
+ this.fireEvent("preparedata", this, data, i, records[i]);
+ html[html.length] = Roo.util.Format.trim(
+ this.dataName ?
+ t.applySubtemplate(this.dataName, data, this.store.meta) :
+ t.apply(data)
+ );
}
- this.el.update(html.join(""));
- this.nodes = this.el.dom.childNodes;
+
+
+
+ el.update(html.join(""));
+ this.nodes = el.dom.childNodes;
this.updateIndexes(0);
},
/**
* Function to override to reformat the data that is sent to
* the template for each node.
+ * DEPRICATED - use the preparedata event handler.
* @param {Array/Object} data The raw data (array of colData for a data model bound view or
* a JSON object for an UpdateManager bound view).
*/
- prepareData : function(data){
+ prepareData : function(data, index, record)
+ {
+ this.fireEvent("preparedata", this, data, index, record);
return data;
},
this.clearSelections();
var index = this.store.indexOf(record);
var n = this.nodes[index];
- this.tpl.insertBefore(n, this.prepareData(record.data));
+ this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
n.parentNode.removeChild(n);
this.updateIndexes(index, index);
},
- onAdd : function(ds, records, index){
+
+
+// --------- FIXME
+ onAdd : function(ds, records, index)
+ {
this.clearSelections();
if(this.nodes.length == 0){
this.refresh();
}
var n = this.nodes[index];
for(var i = 0, len = records.length; i < len; i++){
- var d = this.prepareData(records[i].data);
+ var d = this.prepareData(records[i].data, i, records[i]);
if(n){
this.tpl.insertBefore(n, d);
}else{
+
this.tpl.append(this.el, d);
}
}
onRemove : function(ds, record, index){
this.clearSelections();
- this.el.dom.removeChild(this.nodes[index]);
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName) :
+ this.el;
+ el.dom.removeChild(this.nodes[index]);
this.updateIndexes(index);
},
* @return {HTMLElement} The template node
*/
findItemFromChild : function(node){
- var el = this.el.dom;
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName,true) :
+ this.el.dom;
+
if(!node || node.parentNode == el){
return node;
}
}
},
- onItemClick : function(item, index, e){
+ onItemClick : function(item, index, e)
+ {
if(this.fireEvent("beforeclick", this, index, item, e) === false){
return false;
}
+ if (this.toggleSelect) {
+ var m = this.isSelected(item) ? 'unselect' : 'select';
+ Roo.log(m);
+ var _t = this;
+ _t[m](item, true, false);
+ return true;
+ }
if(this.multiSelect || this.singleSelect){
if(this.multiSelect && e.shiftKey && this.lastSelection){
this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
for(var i = 0, len = nodeInfo.length; i < len; i++){
this.select(nodeInfo[i], true, true);
}
- } else{
- var node = this.getNode(nodeInfo);
- if(node && !this.isSelected(node)){
- if(!keepExisting){
- this.clearSelections(true);
- }
- if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
- Roo.fly(node).addClass(this.selectedClass);
- this.selections.push(node);
- if(!suppressEvent){
- this.fireEvent("selectionchange", this, this.selections);
- }
- }
+ return;
+ }
+ var node = this.getNode(nodeInfo);
+ if(!node || this.isSelected(node)){
+ return; // already selected.
+ }
+ if(!keepExisting){
+ this.clearSelections(true);
+ }
+ if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
+ Roo.fly(node).addClass(this.selectedClass);
+ this.selections.push(node);
+ if(!suppressEvent){
+ this.fireEvent("selectionchange", this, this.selections);
}
}
+
+
+ },
+ /**
+ * Unselects nodes.
+ * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+ * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ */
+ unselect : function(nodeInfo, keepExisting, suppressEvent)
+ {
+ if(nodeInfo instanceof Array){
+ Roo.each(this.selections, function(s) {
+ this.unselect(s, nodeInfo);
+ }, this);
+ return;
+ }
+ var node = this.getNode(nodeInfo);
+ if(!node || !this.isSelected(node)){
+ Roo.log("not selected");
+ return; // not selected.
+ }
+ // fireevent???
+ var ns = [];
+ Roo.each(this.selections, function(s) {
+ if (s == node ) {
+ Roo.fly(node).removeClass(this.selectedClass);
+
+ return;
+ }
+ ns.push(s);
+ },this);
+
+ this.selections= ns;
+ this.fireEvent("selectionchange", this, this.selections);
},
/**
* @param {DatePicker} this
* @param {Date} date The selected date
*/
- select: true
+ 'select': true,
+ /**
+ * @event monthchange
+ * Fires when the displayed month changes
+ * @param {DatePicker} this
+ * @param {Date} date The selected month
+ */
+ 'monthchange': true
});
if(this.handler){
},
// private
- update : function(date){
+ update : function(date)
+ {
var vd = this.activeDate;
this.activeDate = date;
if(vd && this.el){
return;
}
}
+
var days = date.getDaysInMonth();
var firstOfMonth = date.getFirstDateOfMonth();
var startingPos = firstOfMonth.getDay()-this.startDay;
}
this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
-
+ this.fireEvent('monthchange', this, date);
+
if(!this.internalRender){
var main = this.el.dom.firstChild;
var w = main.offsetWidth;
this.update.defer(10, this, [date]);
}
}
+
+
}
-});/*
+}); /*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
}
if(this.tabPosition != "bottom"){
- /** The body element that contains {@link Roo.TabPanelItem} bodies.
- * @type Roo.Element
- */
- this.bodyEl = Roo.get(this.createBody(this.el.dom));
- this.el.addClass("x-tabs-top");
+ /** The body element that contains {@link Roo.TabPanelItem} bodies. +
+ * @type Roo.Element
+ */
+ this.bodyEl = Roo.get(this.createBody(this.el.dom));
+ this.el.addClass("x-tabs-top");
}
this.items = [];
this.cpad = this.el.getPadding("lr");
this.hiddenCount = 0;
+
+ // toolbar on the tabbar support...
+ if (this.toolbar) {
+ var tcfg = this.toolbar;
+ tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
+ this.toolbar = new Roo.Toolbar(tcfg);
+ if (Roo.isSafari) {
+ var tbl = tcfg.container.child('table', true);
+ tbl.setAttribute('width', '100%');
+ }
+
+ }
+
+
+
Roo.TabPanel.superclass.constructor.call(this);
};
Roo.extend(Roo.TabPanel, Roo.util.Observable, {
- /*
- *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
- */
+ /*
+ *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
+ */
tabPosition : "top",
- /*
- *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
- */
+ /*
+ *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
+ */
currentTabWidth : 0,
- /*
- *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
- */
+ /*
+ *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
+ */
minTabWidth : 40,
- /*
- *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
- */
+ /*
+ *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
+ */
maxTabWidth : 250,
- /*
- *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
- */
+ /*
+ *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
+ */
preferredTabWidth : 175,
- /*
- *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
- */
+ /*
+ *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
+ */
resizeTabs : false,
- /*
- *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
- */
+ /*
+ *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
+ */
monitorResize : true,
+ /*
+ *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
+ */
+ toolbar : false,
/**
* Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
/** @private */
Roo.TabPanel.prototype.createStripList = function(strip){
// div wrapper for retard IE
- strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
+ // returns the "tr" element.
+ strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
+ '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
+ '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
return strip.firstChild.firstChild.firstChild.firstChild;
};
/** @private */
/** @private */
Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
var td = document.createElement("td");
- stripEl.appendChild(td);
+ stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
+ //stripEl.appendChild(td);
if(closable){
td.className = "x-tabs-closable";
if(!this.closeTpl){
* Basic Toolbar class.
* @constructor
* Creates a new Toolbar
- * @param {Object} config The config object
+ * @param {Object} container The config object
*/
Roo.Toolbar = function(container, buttons, config)
{
if (typeof(container) == 'object' && container.xtype) {
config = container;
container = config.container;
- buttons = config.buttons; // not really - use items!!
+ buttons = config.buttons || []; // not really - use items!!
}
var xitems = [];
if (config && config.items) {
if(container){
this.render(container);
}
+ this.xitems = xitems;
Roo.each(xitems, function(b) {
this.add(b);
}, this);
Roo.Toolbar.prototype = {
/**
- * @cfg {Roo.data.Store} items
- * array of button configs or elements to add
+ * @cfg {Array} items
+ * array of button configs or elements to add (will be converted to a MixedCollection)
*/
/**
fields : false,
/**
- * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
- * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
+ * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
+ * Note: the field should not have been rendered yet. For a field that has already been
+ * rendered, use {@link #addElement}.
* @param {Roo.form.Field} field
* @return {Roo.ToolbarItem}
*/
dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
}
msgEl.innerHTML = text || ' ';
- var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth),
- Math.max(opt.minWidth || this.minWidth, bwidth));
+
+ var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
+ //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
+ var w = Math.max(
+ Math.min(opt.width || cw , this.maxWidth),
+ Math.max(opt.minWidth || this.minWidth, bwidth)
+ );
if(opt.prompt){
activeTextEl.setWidth(w);
}
if(dlg.isVisible()){
dlg.fixedcenter = false;
}
+ // to big, make it scroll. = But as usual stupid IE does not support
+ // !important..
+
+ if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
+ bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
+ bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
+ } else {
+ bodyEl.dom.style.height = '';
+ bodyEl.dom.style.overflowY = '';
+ }
+ if (cw > w) {
+ bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
+ } else {
+ bodyEl.dom.style.overflowX = '';
+ }
+
dlg.setContentSize(w, bodyEl.getHeight());
if(dlg.isVisible()){
dlg.fixedcenter = true;
* @param {Object} config Configuration options
* @return {Roo.MessageBox} This message box
*/
- show : function(options){
+ show : function(options)
+ {
+
+ // this causes nightmares if you show one dialog after another
+ // especially on callbacks..
+
if(this.isVisible()){
+
this.hide();
+ Roo.log("[Roo.Messagebox] Show called while message displayed:" );
+ Roo.log("Old Dialog Message:" + msgEl.innerHTML );
+ Roo.log("New Dialog Message:" + options.msg )
+ //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
+ //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
+
}
var d = this.getDialog();
opt = options;
* @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
* @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
* @cfg {Boolean} loader A TreeLoader for use with this TreePanel
+ * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
* @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
- * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
- * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
+ * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
+ * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
*
* @constructor
* @param {String/HTMLElement/Element} el The container element
/**
* Read-only. The id of the container element becomes this TreePanel's id.
*/
- this.id = this.el.id;
- this.addEvents({
+ this.id = this.el.id;
+ this.addEvents({
/**
* @event beforeload
* Fires before a node is loaded, return false to cancel
* @param {Node} node The node
*/
"beforechildrenrendered":true,
+ /**
+ * @event startdrag
+ * Fires when a node starts being dragged
+ * @param {Roo.tree.TreePanel} this
+ * @param {Roo.tree.TreeNode} node
+ * @param {event} e The raw browser event
+ */
+ "startdrag" : true,
/**
- * @event startdrag
- * Fires when a node starts being dragged
- * @param {Roo.tree.TreePanel} this
- * @param {Roo.tree.TreeNode} node
- * @param {event} e The raw browser event
- */
- "startdrag" : true,
- /**
- * @event enddrag
- * Fires when a drag operation is complete
- * @param {Roo.tree.TreePanel} this
- * @param {Roo.tree.TreeNode} node
- * @param {event} e The raw browser event
- */
- "enddrag" : true,
- /**
- * @event dragdrop
- * Fires when a dragged node is dropped on a valid DD target
- * @param {Roo.tree.TreePanel} this
- * @param {Roo.tree.TreeNode} node
- * @param {DD} dd The dd it was dropped on
- * @param {event} e The raw browser event
- */
- "dragdrop" : true,
- /**
- * @event beforenodedrop
- * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
- * passed to handlers has the following properties:<br />
- * <ul style="padding:5px;padding-left:16px;">
- * <li>tree - The TreePanel</li>
- * <li>target - The node being targeted for the drop</li>
- * <li>data - The drag data from the drag source</li>
- * <li>point - The point of the drop - append, above or below</li>
- * <li>source - The drag source</li>
- * <li>rawEvent - Raw mouse event</li>
- * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
- * to be inserted by setting them on this object.</li>
- * <li>cancel - Set this to true to cancel the drop.</li>
- * </ul>
- * @param {Object} dropEvent
- */
- "beforenodedrop" : true,
- /**
- * @event nodedrop
- * Fires after a DD object is dropped on a node in this tree. The dropEvent
- * passed to handlers has the following properties:<br />
- * <ul style="padding:5px;padding-left:16px;">
- * <li>tree - The TreePanel</li>
- * <li>target - The node being targeted for the drop</li>
- * <li>data - The drag data from the drag source</li>
- * <li>point - The point of the drop - append, above or below</li>
- * <li>source - The drag source</li>
- * <li>rawEvent - Raw mouse event</li>
- * <li>dropNode - Dropped node(s).</li>
- * </ul>
- * @param {Object} dropEvent
- */
- "nodedrop" : true,
- /**
- * @event nodedragover
- * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
- * passed to handlers has the following properties:<br />
- * <ul style="padding:5px;padding-left:16px;">
- * <li>tree - The TreePanel</li>
- * <li>target - The node being targeted for the drop</li>
- * <li>data - The drag data from the drag source</li>
- * <li>point - The point of the drop - append, above or below</li>
- * <li>source - The drag source</li>
- * <li>rawEvent - Raw mouse event</li>
- * <li>dropNode - Drop node(s) provided by the source.</li>
- * <li>cancel - Set this to true to signal drop not allowed.</li>
- * </ul>
- * @param {Object} dragOverEvent
- */
- "nodedragover" : true
+ * @event enddrag
+ * Fires when a drag operation is complete
+ * @param {Roo.tree.TreePanel} this
+ * @param {Roo.tree.TreeNode} node
+ * @param {event} e The raw browser event
+ */
+ "enddrag" : true,
+ /**
+ * @event dragdrop
+ * Fires when a dragged node is dropped on a valid DD target
+ * @param {Roo.tree.TreePanel} this
+ * @param {Roo.tree.TreeNode} node
+ * @param {DD} dd The dd it was dropped on
+ * @param {event} e The raw browser event
+ */
+ "dragdrop" : true,
+ /**
+ * @event beforenodedrop
+ * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
+ * passed to handlers has the following properties:<br />
+ * <ul style="padding:5px;padding-left:16px;">
+ * <li>tree - The TreePanel</li>
+ * <li>target - The node being targeted for the drop</li>
+ * <li>data - The drag data from the drag source</li>
+ * <li>point - The point of the drop - append, above or below</li>
+ * <li>source - The drag source</li>
+ * <li>rawEvent - Raw mouse event</li>
+ * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
+ * to be inserted by setting them on this object.</li>
+ * <li>cancel - Set this to true to cancel the drop.</li>
+ * </ul>
+ * @param {Object} dropEvent
+ */
+ "beforenodedrop" : true,
+ /**
+ * @event nodedrop
+ * Fires after a DD object is dropped on a node in this tree. The dropEvent
+ * passed to handlers has the following properties:<br />
+ * <ul style="padding:5px;padding-left:16px;">
+ * <li>tree - The TreePanel</li>
+ * <li>target - The node being targeted for the drop</li>
+ * <li>data - The drag data from the drag source</li>
+ * <li>point - The point of the drop - append, above or below</li>
+ * <li>source - The drag source</li>
+ * <li>rawEvent - Raw mouse event</li>
+ * <li>dropNode - Dropped node(s).</li>
+ * </ul>
+ * @param {Object} dropEvent
+ */
+ "nodedrop" : true,
+ /**
+ * @event nodedragover
+ * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
+ * passed to handlers has the following properties:<br />
+ * <ul style="padding:5px;padding-left:16px;">
+ * <li>tree - The TreePanel</li>
+ * <li>target - The node being targeted for the drop</li>
+ * <li>data - The drag data from the drag source</li>
+ * <li>point - The point of the drop - append, above or below</li>
+ * <li>source - The drag source</li>
+ * <li>rawEvent - Raw mouse event</li>
+ * <li>dropNode - Drop node(s) provided by the source.</li>
+ * <li>cancel - Set this to true to signal drop not allowed.</li>
+ * </ul>
+ * @param {Object} dragOverEvent
+ */
+ "nodedragover" : true
- });
- if(this.singleExpand){
+ });
+ if(this.singleExpand){
this.on("beforeexpand", this.restrictExpand, this);
- }
+ }
+ if (this.editor) {
+ this.editor.tree = this;
+ this.editor = Roo.factory(this.editor, Roo.tree);
+ }
+
+ if (this.selModel) {
+ this.selModel = Roo.factory(this.selModel, Roo.tree);
+ }
+
};
Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
rootVisible : true,
* @class Roo.tree.DefaultSelectionModel
* @extends Roo.util.Observable
* The default single selection for a TreePanel.
+ * @param {Object} cfg Configuration
*/
-Roo.tree.DefaultSelectionModel = function(){
+Roo.tree.DefaultSelectionModel = function(cfg){
this.selNode = null;
+
+
this.addEvents({
/**
* @event selectionchange
*/
"beforeselect" : true
});
+
+ Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
};
Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
* @class Roo.tree.MultiSelectionModel
* @extends Roo.util.Observable
* Multi selection for a TreePanel.
+ * @param {Object} cfg Configuration
*/
Roo.tree.MultiSelectionModel = function(){
this.selNodes = [];
*/
"selectionchange" : true
});
+ Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
+
};
Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
* @type TreeNodeUI
*/
this.ui = new uiClass(this);
+
+ // finally support items[]
+ if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
+ return;
+ }
+
+
+ Roo.each(this.attributes.items, function(c) {
+ this.appendChild(Roo.factory(c,Roo.Tree));
+ }, this);
+ delete this.attributes.items;
+
+
+
};
Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
preventHScroll: true,
// these methods are overridden to provide lazy rendering support
// private override
- appendChild : function(){
+ appendChild : function()
+ {
var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
if(node && this.childrenRendered){
node.render();
}
},
- renderElements : function(n, a, targetNode, bulkRender){
+ renderElements : function(n, a, targetNode, bulkRender)
+ {
// add some indent caching, this helps performance when rendering a large tree
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
var t = n.getOwnerTree();
var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
+ if (typeof(n.attributes.html) != 'undefined') {
+ txt = n.attributes.html;
+ }
var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
var cb = typeof a.checked == 'boolean';
var href = a.href ? a.href : Roo.isGecko ? "" : "#";
this.c1 = c1; this.c2 = c2;
}
}else{
- if(!this.wasLeaf){
+ // this changes non-leafs into leafs if they have no children.
+ // it's not very rational behaviour..
+
+ if(!this.wasLeaf && this.node.leaf){
Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
delete this.c1;
delete this.c2;
* nodes from a specified URL. The response must be a javascript Array definition
* who's elements are node definition objects. eg:
* <pre><code>
- [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
- { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
+{ success : true,
+ data : [
+
+ { 'id': 1, 'text': 'A folder Node', 'leaf': false },
+ { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
+ ]
+}
+
+
</code></pre>
* <br><br>
+ * The old style respose with just an array is still supported, but not recommended.
+ * <br><br>
+ *
* A server request is sent, and child nodes are loaded only when a node is expanded.
* The loading node's id is passed to the server under the parameter name "node" to
* enable the server to produce the correct child nodes.
/**
* @cfg {Object} uiProviders (optional) An object containing properties which
*
- * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
+ * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
* specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
* <i>uiProvider</i> attribute of a returned child node is a string rather
* than a reference to a TreeNodeUI implementation, this that string value
},
// private
- createNode : function(attr){
+ createNode : function(attr)
+ {
// apply baseAttrs, nice idea Corey!
if(this.baseAttrs){
Roo.applyIf(attr, this.baseAttrs);
new Roo.tree.AsyncTreeNode(attr));
},
- processResponse : function(response, node, callback){
+ processResponse : function(response, node, callback)
+ {
var json = response.responseText;
try {
- var o = /** eval:var:zzzzzzzzzz */ eval("("+json+")");
+ var o = Roo.decode(json);
+
+ if (this.root === false && typeof(o.success) != undefined) {
+ this.root = 'data'; // the default behaviour for list like data..
+ }
+
+ if (this.root !== false && !o.success) {
+ // it's a failure condition.
+ var a = response.argument;
+ this.fireEvent("loadexception", this, a.node, response);
+ Roo.log("Load failed - should have a handler really");
+ return;
+ }
+
+
+
if (this.root !== false) {
- o = o[this.root];
+ o = o[this.root];
}
for(var i = 0, len = o.length; i < len; i++){
this.fireEvent("load", this, a.node, response);
},
- handleFailure : function(response){
+ handleFailure : function(response)
+ {
+ // should handle failure better..
this.transId = false;
var a = response.argument;
this.fireEvent("loadexception", this, a.node, response);
* Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
* as the editor field.
* @constructor
- * @param {TreePanel} tree
- * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
+ * @param {Object} config (used to be the tree panel.)
+ * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
+ *
+ * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
+ * @cfg {Roo.form.TextField|Object} field The field configuration
+ *
+ *
*/
-Roo.tree.TreeEditor = function(tree, config){
+Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
+ var tree = config;
+ var field;
+ if (oldconfig) { // old style..
+ field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
+ } else {
+ // new style..
+ tree = config.tree;
+ config.field = config.field || {};
+ config.field.xtype = 'TextField';
+ field = Roo.factory(config.field, Roo.form);
+ }
config = config || {};
- var field = config.events ? config : new Roo.form.TextField(config);
- Roo.tree.TreeEditor.superclass.constructor.call(this, field);
+
+
+ this.addEvents({
+ /**
+ * @event beforenodeedit
+ * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
+ * false from the handler of this event.
+ * @param {Editor} this
+ * @param {Roo.tree.Node} node
+ */
+ "beforenodeedit" : true
+ });
+
+ //Roo.log(config);
+ Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
this.tree = tree;
this.maxWidth,
(td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
this.setSize(w, '');
+
+ return this.fireEvent('beforenodeedit', this, this.editNode);
+
},
// private
this.triggerEdit(node);
return false;
}
+ return true;
},
// private
this.value = v;
if(this.rendered){
this.el.dom.value = (v === null || v === undefined ? '' : v);
- this.validate();
+ this.validate();
}
},
},
setValue : function(v){
+ v = this.fixPrecision(v);
Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
},
beforeBlur : function(){
var v = this.parseValue(this.getRawValue());
if(v){
- this.setValue(this.fixPrecision(v));
+ this.setValue(v);
}
}
});/*
* in order for a value to be mapped.
*/
valueField: undefined,
+
+
/**
* @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
* field's data value (defaults to the underlying DOM element's name)
addicon : false,
editicon: false,
-
+ // element that contains real text value.. (when hidden is used..)
+
// private
onRender : function(ct, position){
Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
// prevent input submission
this.el.dom.removeAttribute('name');
+
+
}
if(Roo.isGecko){
this.el.dom.setAttribute('autocomplete', 'off');
this.store.on('beforeload', this.onBeforeLoad, this);
this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.collapse, this);
+ this.store.on('loadexception', this.onLoadException, this);
if(this.resizable){
this.resizer = new Roo.Resizable(this.list, {
"tab" : function(e){
this.onViewClick(false);
+ this.fireEvent("specialkey", this, e);
return true;
},
if(this.store){
this.store.un('beforeload', this.onBeforeLoad, this);
this.store.un('load', this.onLoad, this);
- this.store.un('loadexception', this.collapse, this);
+ this.store.un('loadexception', this.onLoadException, this);
}
Roo.form.ComboBox.superclass.onDestroy.call(this);
},
}
//this.el.focus();
},
-
+ // private
+ onLoadException : function()
+ {
+ this.collapse();
+ Roo.log(this.store.reader.jsonData);
+ if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ }
+
+
+ },
// private
onTypeAhead : function(){
if(this.store.getCount() > 0){
record = r;
return false;
}
+ return true;
});
}
return record;
},
-
+
+ getName: function()
+ {
+ // returns hidden if it's set..
+ if (!this.rendered) {return ''};
+ return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
+
+ },
// private
onViewMove : function(e, t){
this.inKeyMode = false;
},
// private
- onViewClick : function(doFocus){
+ onViewClick : function(doFocus)
+ {
var index = this.view.getSelectedIndexes()[0];
var r = this.store.getAt(index);
if(r){
* @hide
* @method autoSize
*/
+});/*
+ * Copyright(c) 2010-2012, Roo J Solutions Limited
+ *
+ * Licence LGPL
+ *
+ */
+
+/**
+ * @class Roo.form.ComboBoxArray
+ * @extends Roo.form.ComboBox
+ * A facebook style adder... for lists of email / people / countries etc...
+ * pick multiple items from a combo box, and shows each one.
+ *
+ * Fred [x] Brian [x] [Pick another |v]
+ *
+ *
+ * For this to work: it needs various extra information
+ * - normal combo problay has
+ * name, hiddenName
+ * + displayField, valueField
+ *
+ * For our purpose...
+ *
+ *
+ * If we change from 'extends' to wrapping...
+ *
+ *
+ *
+
+
+ * @constructor
+ * Create a new ComboBoxArray.
+ * @param {Object} config Configuration options
+ */
+
+
+Roo.form.ComboBoxArray = function(config)
+{
+
+ Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
+
+ this.items = new Roo.util.MixedCollection(false);
+
+ // construct the child combo...
+
+
+
+
+
+
+}
+
+
+Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
+{
+ /**
+ * @cfg {Roo.form.Combo} combo The combo box that is wrapped
+ */
+
+ lastData : false,
+
+ // behavies liek a hiddne field
+ inputType: 'hidden',
+ /**
+ * @cfg {Number} width The width of the box that displays the selected element
+ */
+ width: 300,
+
+
+
+ /**
+ * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
+ */
+ name : false,
+ /**
+ * @cfg {String} name The hidden name of the field, often contains an comma seperated list of names
+ */
+ hiddenName : false,
+
+
+ // private the array of items that are displayed..
+ items : false,
+ // private - the hidden field el.
+ hiddenEl : false,
+ // private - the filed el..
+ el : false,
+
+ //validateValue : function() { return true; }, // all values are ok!
+ //onAddClick: function() { },
+
+ onRender : function(ct, position)
+ {
+
+ // create the standard hidden element
+ //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
+
+
+ // give fake names to child combo;
+ this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
+ this.combo.name = this.name? (this.name+'-subcombo') : this.name;
+
+ this.combo = Roo.factory(this.combo, Roo.form);
+ this.combo.onRender(ct, position);
+
+ // assigned so form know we need to do this..
+ this.store = this.combo.store;
+ this.valueField = this.combo.valueField;
+ this.displayField = this.combo.displayField ;
+
+
+ this.combo.wrap.addClass('x-cbarray-grp');
+
+ var cbwrap = this.combo.wrap.createChild(
+ {tag: 'div', cls: 'x-cbarray-cb'},
+ this.combo.el.dom
+ );
+
+
+ this.hiddenEl = this.combo.wrap.createChild({
+ tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
+ });
+ this.el = this.combo.wrap.createChild({
+ tag: 'input', type:'hidden' , name: this.name, value : ''
+ });
+ // this.el.dom.removeAttribute("name");
+
+
+ this.outerWrap = this.combo.wrap;
+ this.wrap = cbwrap;
+
+ this.outerWrap.setWidth(this.width);
+ this.outerWrap.dom.removeChild(this.el.dom);
+
+ this.wrap.dom.appendChild(this.el.dom);
+ this.outerWrap.dom.removeChild(this.combo.trigger.dom);
+ this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
+
+ this.combo.trigger.setStyle('position','relative');
+ this.combo.trigger.setStyle('left', '0px');
+ this.combo.trigger.setStyle('top', '2px');
+
+ this.combo.el.setStyle('vertical-align', 'text-bottom');
+
+ //this.trigger.setStyle('vertical-align', 'top');
+
+ // this should use the code from combo really... on('add' ....)
+ if (this.adder) {
+
+
+ this.adder = this.outerWrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
+ var _t = this;
+ this.adder.on('click', function(e) {
+ _t.fireEvent('adderclick', this, e);
+ }, _t);
+ }
+ //var _t = this;
+ //this.adder.on('click', this.onAddClick, _t);
+
+
+ this.combo.on('select', function(cb, rec, ix) {
+ this.addItem(rec.data);
+
+ cb.setValue('');
+ cb.el.dom.value = '';
+ //cb.lastData = rec.data;
+ // add to list
+
+ }, this);
+
+
+ },
+
+
+ getName: function()
+ {
+ // returns hidden if it's set..
+ if (!this.rendered) {return ''};
+ return this.hiddenName ? this.hiddenName : this.name;
+
+ },
+
+
+ onResize: function(w, h){
+
+ return;
+ // not sure if this is needed..
+ this.combo.onResize(w,h);
+
+ if(typeof w != 'number'){
+ // we do not handle it!?!?
+ return;
+ }
+ var tw = this.combo.trigger.getWidth();
+ tw += this.addicon ? this.addicon.getWidth() : 0;
+ tw += this.editicon ? this.editicon.getWidth() : 0;
+ var x = w - tw;
+ this.combo.el.setWidth( this.combo.adjustWidth('input', x));
+
+ this.combo.trigger.setStyle('left', '0px');
+
+ if(this.list && this.listWidth === undefined){
+ var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
+ this.list.setWidth(lw);
+ this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+ }
+
+
+
+ },
+
+ addItem: function(rec)
+ {
+ var idField = this.combo.valueField;
+ var displayField = this.combo.displayField;
+ if (this.items.indexOfKey(rec[idField]) > -1) {
+ //console.log("GOT " + rec.data.id);
+ return;
+ }
+
+ var x = new Roo.form.ComboBoxArray.Item({
+ //id : rec[this.idField],
+ data : rec,
+ nameField : displayField ,
+ tipField : displayField ,
+ cb : this
+ });
+ // use the
+ this.items.add(rec[idField],x);
+ // add it before the element..
+ this.updateHiddenEl();
+ x.render(this.outerWrap, this.wrap.dom);
+ // add the image handler..
+ },
+
+ updateHiddenEl : function()
+ {
+ this.validate();
+ if (!this.hiddenEl) {
+ return;
+ }
+ var ar = [];
+ var idField = this.combo.valueField;
+
+ this.items.each(function(f) {
+ ar.push(f.data[idField]);
+
+ });
+ this.hiddenEl.dom.value = ar.join(',');
+ this.validate();
+ },
+
+ reset : function()
+ {
+ Roo.form.ComboBoxArray.superclass.reset.call(this);
+ this.items.each(function(f) {
+ f.remove();
+ });
+ if (this.hiddenEl) {
+ this.hiddenEl.dom.value = '';
+ }
+
+ },
+ getValue: function()
+ {
+ return this.hiddenEl ? this.hiddenEl.dom.value : '';
+ },
+ setValue: function(v) // not a valid action - must use addItems..
+ {
+
+
+ if (this.store.isLocal) {
+ // then we can use the store to find the values..
+ // comma seperated at present.. this needs to allow JSON based encoding..
+ this.hiddenEl.value = v;
+ var v_ar = [];
+ Roo.each(v.split(','), function(k) {
+ Roo.log("CHECK " + this.valueField + ',' + k);
+ var li = this.store.query(this.valueField, k);
+ if (!li.length) {
+ return;
+ }
+ add = {};
+ add[this.valueField] = k;
+ add[this.displayField] = li.item(0).data[this.displayField];
+
+ this.addItem(add);
+ }, this)
+
+
+
+ }
+
+
+ },
+ setFromData: function(v)
+ {
+ // this recieves an object, if setValues is called.
+ var keys = v[this.valueField].split(',');
+ var display = v[this.displayField].split(',');
+ for (var i = 0 ; i < keys.length; i++) {
+
+ add = {};
+ add[this.valueField] = keys[i];
+ add[this.displayField] = display[i];
+ this.addItem(add);
+ }
+
+
+ },
+
+
+ validateValue : function(value){
+ return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
+
+ }
+
+});
+
+
+
+/**
+ * @class Roo.form.ComboBoxArray.Item
+ * @extends Roo.BoxComponent
+ * A selected item in the list
+ * Fred [x] Brian [x] [Pick another |v]
+ *
+ * @constructor
+ * Create a new item.
+ * @param {Object} config Configuration options
+ */
+
+Roo.form.ComboBoxArray.Item = function(config) {
+ config.id = Roo.id();
+ Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
+}
+
+Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
+ data : {},
+ cb: false,
+ nameField : false,
+ tipField : false,
+
+
+ defaultAutoCreate : {
+ tag: 'div',
+ cls: 'x-cbarray-item',
+ cn : [
+ { tag: 'div' },
+ {
+ tag: 'img',
+ width:16,
+ height : 16,
+ src : Roo.BLANK_IMAGE_URL ,
+ align: 'center'
+ }
+ ]
+
+ },
+
+
+ onRender : function(ct, position)
+ {
+ Roo.form.Field.superclass.onRender.call(this, ct, position);
+
+ if(!this.el){
+ var cfg = this.getAutoCreate();
+ this.el = ct.createChild(cfg, position);
+ }
+
+ this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
+
+ this.el.child('div').dom.innerHTML = this.cb.renderer ?
+ this.cb.renderer(this.data) :
+ String.format('{0}',this.data[this.nameField]);
+
+
+ this.el.child('div').dom.setAttribute('qtip',
+ String.format('{0}',this.data[this.tipField])
+ );
+
+ this.el.child('img').on('click', this.remove, this);
+
+ },
+
+ remove : function()
+ {
+
+ this.cb.items.remove(this);
+ this.el.child('img').un('click', this.remove, this);
+ this.el.remove();
+ this.cb.updateHiddenEl();
+ }
+
+
});/*
* Based on:
* Ext JS Library 1.1.1
* @class Ext.form.HtmlEditor
* @extends Ext.form.Field
* Provides a lightweight HTML Editor component.
- * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
+ *
+ * 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/>
*/
defaultLinkValue : 'http:/'+'/',
+ /**
+ * @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,
// id of frame..
frameId: false,
onFocus : Roo.emptyFn,
iframePad:3,
hideMode:'offsets',
- defaultAutoCreate : {
+
+ defaultAutoCreate : { // modified by initCompnoent..
tag: "textarea",
style:"width:500px;height:300px;",
autocomplete: "off"
* @param {HtmlEditor} this
*/
editorevent: true
- })
+ });
+ this.defaultAutoCreate = {
+ tag: "textarea",
+ style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
+ autocomplete: "off"
+ };
},
/**
* want to change the initialization markup of the iframe (e.g. to add stylesheets).
*/
getDocMarkup : function(){
- return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
+ // body styles..
+ var st = '';
+ if (this.stylesheets === false) {
+
+ Roo.get(document.head).select('style').each(function(node) {
+ st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+ });
+
+ Roo.get(document.head).select('link').each(function(node) {
+ st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+ });
+
+ } else if (!this.stylesheets.length) {
+ // simple..
+ st = '<style type="text/css">' +
+ 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ '</style>';
+ } else {
+ Roo.each(this.stylesheets, function(s) {
+ st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
+ });
+
+ }
+
+ st += '<style type="text/css">' +
+ 'IMG { cursor: pointer } ' +
+ '</style>';
+
+
+ return '<html><head>' + st +
+ //<style type="text/css">' +
+ //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ //'</style>' +
+ ' </head><body class="roo-htmleditor-body"></body></html>';
},
// private
- onRender : function(ct, position){
+ onRender : function(ct, position)
+ {
+ var _t = this;
Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
this.el.dom.style.border = '0 none';
this.el.dom.setAttribute('tabIndex', -1);
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);
-
-
+ this.createToolbar(this);
name: this.frameId,
frameBorder : 'no',
'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
- });
+ }, this.el
+ );
// console.log(iframe);
//this.wrap.dom.appendChild(iframe);
Roo.TaskMgr.start(task);
if(!this.width){
- this.setSize(this.el.getSize());
+ 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){
+ 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'){
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){
syncValue : function(){
if(this.initialized){
var bd = (this.doc.body || this.doc.documentElement);
+ //this.cleanUpPaste(); -- this is done else where and causes havoc..
var html = bd.innerHTML;
if(Roo.isSafari){
var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
}
}
html = this.cleanHtml(html);
+ // fix up the special chars..
+ html = html.replace(/([\x80-\uffff])/g, function (a, b) {
+ return "&#"+b.charCodeAt()+";"
+ });
if(this.fireEvent('beforesync', this, html) !== false){
this.el.dom.value = html;
this.fireEvent('sync', this, html);
if(v.length < 1){
v = ' ';
}
+
if(this.fireEvent('beforepush', this, v) !== false){
- (this.doc.body || this.doc.documentElement).innerHTML = v;
+ var d = (this.doc.body || this.doc.documentElement);
+ d.innerHTML = v;
+ this.cleanUpPaste();
+ this.el.dom.value = d.innerHTML;
this.fireEvent('push', this, v);
}
}
dbody.bgProperties = 'fixed'; // ie
Roo.DomHelper.applyStyles(dbody, ss);
Roo.EventManager.on(this.doc, {
- 'mousedown': this.onEditorEvent,
+ //'mousedown': this.onEditorEvent,
+ 'mouseup': this.onEditorEvent,
'dblclick': this.onEditorEvent,
'click': this.onEditorEvent,
'keyup': this.onEditorEvent,
onEditorEvent : function(e){
this.fireEvent('editorevent', this, e);
// this.updateToolbar();
- this.syncValue();
+ this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
},
insertTag : function(tg)
this.doc.execCommand(cmd, false, value === undefined ? null : value);
this.syncValue();
},
-
+
+
/**
* Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
* to insert tRoo.
- * @param {String} text
+ * @param {String} text | dom node..
*/
- insertAtCursor : function(text){
+ insertAtCursor : function(text)
+ {
+
+
+
if(!this.activated){
return;
}
+ /*
if(Roo.isIE){
this.win.focus();
var r = this.doc.selection.createRange();
r.pasteHTML(text);
this.syncValue();
this.deferFocus();
+
}
- }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
+ return;
+ }
+ */
+ if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
this.win.focus();
- this.execCmd('InsertHTML', text);
+
+
+ // from jquery ui (MIT licenced)
+ var range, node;
+ var win = this.win;
+
+ if (win.getSelection && win.getSelection().getRangeAt) {
+ range = win.getSelection().getRangeAt(0);
+ node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
+ range.insertNode(node);
+ } else if (win.document.selection && win.document.selection.createRange) {
+ // no firefox support
+ var txt = typeof(text) == 'string' ? text : text.outerHTML;
+ win.document.selection.createRange().pasteHTML(txt);
+ } else {
+ // no firefox support
+ var txt = typeof(text) == 'string' ? text : text.outerHTML;
+ this.execCmd('InsertHTML', txt);
+ }
+
+ this.syncValue();
+
this.deferFocus();
}
},
switch(c){
case 'b':
cmd = 'bold';
- break;
+ break;
case 'i':
cmd = 'italic';
- break;
+ break;
+
case 'u':
cmd = 'underline';
+ break;
+
case 'v':
this.cleanUpPaste.defer(100, this);
return;
- break;
+
}
if(cmd){
this.win.focus();
- var range = this.createRange(this.getSelection());
+ var range = this.createRange(this.getSelection()).cloneRange();
if (Roo.isIE) {
var parent = range.parentElement();
return parent;
}
-
- var ar = range.endContainer.childNodes;
- if (!ar.length) {
- ar = range.commonAncestorContainer.childNodes;
- //alert(ar.length);
+ // is ancestor a text element.
+ var ac = range.commonAncestorContainer;
+ if (ac.nodeType == 3) {
+ ac = ac.parentNode;
}
+
+ var ar = ac.childNodes;
+
var nodes = [];
var other_nodes = [];
var has_other_nodes = false;
other_nodes.push(ar[i]);
continue;
}
+ // outer..
if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
continue;
}
}
},
+ /***
+ *
+ * Range intersection.. the hard stuff...
+ * '-1' = before
+ * '0' = hits..
+ * '1' = after.
+ * [ -- selected range --- ]
+ * [fail] [fail]
+ *
+ * basically..
+ * if end is before start or hits it. fail.
+ * if start is after end or hits it fail.
+ *
+ * if either hits (but other is outside. - then it's not
+ *
+ *
+ **/
-
- // BC Hacks - cause I cant work out what i was trying to do..
+ // @see http://www.thismuchiknow.co.uk/?p=64.
rangeIntersectsNode : function(range, node)
{
var nodeRange = node.ownerDocument.createRange();
try {
nodeRange.selectNode(node);
- }
- catch (e) {
+ } catch (e) {
nodeRange.selectNodeContents(node);
}
-
- return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
- range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
+
+ var rangeStartRange = range.cloneRange();
+ rangeStartRange.collapse(true);
+
+ var rangeEndRange = range.cloneRange();
+ rangeEndRange.collapse(false);
+
+ var nodeStartRange = nodeRange.cloneRange();
+ nodeStartRange.collapse(true);
+
+ var nodeEndRange = nodeRange.cloneRange();
+ nodeEndRange.collapse(false);
+
+ return rangeStartRange.compareBoundaryPoints(
+ Range.START_TO_START, nodeEndRange) == -1 &&
+ rangeEndRange.compareBoundaryPoints(
+ Range.START_TO_START, nodeStartRange) == 1;
+
+
},
- rangeCompareNode : function(range, node) {
+ rangeCompareNode : function(range, node)
+ {
var nodeRange = node.ownerDocument.createRange();
try {
nodeRange.selectNode(node);
} catch (e) {
nodeRange.selectNodeContents(node);
}
- var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
- var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
-
- if (nodeIsBefore && !nodeIsAfter)
- return 0;
- if (!nodeIsBefore && nodeIsAfter)
- return 1;
+
+
+ range.collapse(true);
+
+ nodeRange.collapse(true);
+
+ var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
+ var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
+
+ //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
+
+ var nodeIsBefore = ss == 1;
+ var nodeIsAfter = ee == -1;
+
if (nodeIsBefore && nodeIsAfter)
- return 2;
-
+ return 0; // outer
+ if (!nodeIsBefore && nodeIsAfter)
+ return 1; //right trailed.
+
+ if (nodeIsBefore && !nodeIsAfter)
+ return 2; // left trailed.
+ // fully contined.
return 3;
},
cleanUpPaste : function()
{
// cleans up the whole document..
- // console.log('cleanuppaste');
- this.cleanUpChildren(this.doc.body)
+ Roo.log('cleanuppaste');
+ this.cleanUpChildren(this.doc.body);
+ var clean = this.cleanWordChars(this.doc.body.innerHTML);
+ if (clean != this.doc.body.innerHTML) {
+ this.doc.body.innerHTML = clean;
+ }
+ },
+
+ cleanWordChars : function(input) {
+ var he = Roo.form.HtmlEditor;
+
+ var output = input;
+ Roo.each(he.swapCodes, function(sw) {
+ var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
+ output = output.replace(swapper, sw[1]);
+ });
+ return output;
},
+
+
cleanUpChildren : function (n)
{
if (!n.childNodes.length) {
return;
}
+
+ var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
+
+ // remove <a name=....> as rendering on yahoo mailer is bored with this.
+
+ if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
+ remove_keep_children = true;
+ }
+
+ if (remove_keep_children) {
+ this.cleanUpChildren(node);
+ // inserts everything just before this node...
+ while (node.childNodes.length) {
+ var cn = node.childNodes[0];
+ node.removeChild(cn);
+ node.parentNode.insertBefore(cn, node);
+ }
+ node.parentNode.removeChild(node);
+ return;
+ }
+
if (!node.attributes || !node.attributes.length) {
this.cleanUpChildren(node);
return;
Roo.each(parts, function(p) {
p = p.replace(/\s+/g,'');
if (!p.length) {
- return;
+ return true;
}
var l = p.split(':').shift().replace(/\s+/g,'');
+ // only allow 'c whitelisted system attributes'
if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
node.removeAttribute(n);
return false;
}
+ return true;
});
cleanStyle(a.name,a.value);
}
/// 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 = '';
+ }
}
// style cleanup!?
Roo.form.HtmlEditor.clean = [
'script', 'style', 'title', 'xml'
];
-
+Roo.form.HtmlEditor.remove = [
+ 'font'
+];
// attributes..
Roo.form.HtmlEditor.ablack = [
'http', 'https', 'mailto'
];
+// white listed style attributes.
Roo.form.HtmlEditor.cwhite= [
'text-align',
'font-size'
];
-// <script type="text/javascript">
+
+Roo.form.HtmlEditor.swapCodes =[
+ [ 8211, "--" ],
+ [ 8212, "--" ],
+ [ 8216, "'" ],
+ [ 8217, "'" ],
+ [ 8220, '"' ],
+ [ 8221, '"' ],
+ [ 8226, "*" ],
+ [ 8230, "..." ]
+];
+
+ // <script type="text/javascript">
/*
* Based on
* Ext JS Library 1.1.1
{
Roo.apply(this, config);
+
+ // default disabled, based on 'good practice'..
+ this.disable = this.disable || {};
+ Roo.applyIf(this.disable, {
+ fontSize : true,
+ colors : true,
+ specialElements : true
+ });
+
+
//Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
// dont call parent... till later.
}
// "é" , // e ecute
// "ú" , // u ecute?
],
+
+ specialElements : [
+ {
+ text: "Insert Table",
+ xtype: 'MenuItem',
+ xns : Roo.Menu,
+ ihtml : '<table><tr><td>Cell</td></tr></table>'
+
+ },
+ {
+ text: "Insert Image",
+ xtype: 'MenuItem',
+ xns : Roo.Menu,
+ ihtml : '<img src="about:blank"/>'
+
+ }
+
+
+ ],
+
+
inputElements : [
"form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
"input:submit", "input:button", "select", "textarea", "label" ],
};
- if(this.disable.colors){
+ if(!this.disable.colors){
tb.add(
'-', {
id:editor.frameId +'-forecolor',
smenu = {
text: "©",
cls: 'x-edit-none',
+
menu : {
items : []
- }
+ }
};
for (var i =0; i < this.specialChars.length; i++) {
smenu.menu.items.push({
html: this.specialChars[i],
handler: function(a,b) {
editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
+ //editor.insertAtCursor(a.html);
},
tabIndex:-1
}
+
+ if (!this.disable.specialElements) {
+ var semenu = {
+ text: "Other;",
+ cls: 'x-edit-none',
+ menu : {
+ items : []
+ }
+ };
+ for (var i =0; i < this.specialElements.length; i++) {
+ semenu.menu.items.push(
+ Roo.apply({
+ handler: function(a,b) {
+ editor.insertAtCursor(this.ihtml);
+ }
+ }, this.specialElements[i])
+ );
+
+ }
+
+ tb.add(semenu);
+
+
+ }
+
+
if (this.btns) {
for(var i =0; i< this.btns.length;i++) {
var b = this.btns[i];
new Roo.form.HtmlEditor({
....
toolbars : [
- new Roo.form.HtmlEditor.ToolbarStandard(),
- new Roo.form.HtmlEditor.ToolbarContext()
- })
- }
+ { xtype: 'ToolbarStandard', styles : {} }
+ { xtype: 'ToolbarContext', disable : {} }
+ ]
+})
+
*
* @config : {Object} disable List of elements to disable.. (not done yet.)
- *
+ * @config : {Object} styles Map of styles available.
*
*/
Roo.apply(this, config);
//Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
// dont call parent... till later.
+ this.styles = this.styles || {};
}
Roo.form.HtmlEditor.ToolbarContext.types = {
'IMG' : {
align: {
title: "Align",
opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
- width: 40
+ width: 80
},
valign: {
title: "Valign",
opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
- width: 40
+ width: 80
},
colspan: {
title: "Colspan",
width: 200
}
},
+
+ // should we really allow this??
+ // should this just be
'BODY' : {
title : {
title: "title",
- width: 120,
+ width: 200,
disabled : true
}
+ },
+ '*' : {
+ // empty..
}
};
*/
disable : false,
+ /**
+ * @cfg {Object} styles List of styles
+ * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
+ *
+ * These must be defined in the page, so they get rendered correctly..
+ * .headline { }
+ * TD.underline { }
+ *
+ */
+ styles : false,
}
this.tb = this.toolbars.BODY;
this.tb.el.show();
+ this.buildFooter();
+ this.footer.show();
+ editor.on('hide', function( ) { this.footer.hide() }, this);
+ editor.on('show', function( ) { this.footer.show() }, this);
this.rendered = true;
* Protected method that will not generally be called directly. It triggers
* a toolbar update by reading the markup state of the current selection in the editor.
*/
- updateToolbar: function(){
+ updateToolbar: function(editor,ev,sel){
+ //Roo.log(ev);
+ // capture mouse up - this is handy for selecting images..
+ // perhaps should go somewhere else...
if(!this.editor.activated){
- this.editor.onFirstFocus();
+ this.editor.onFirstFocus();
return;
}
-
+
+ // http://developer.yahoo.com/yui/docs/simple-editor.js.html
+ // selectNode - might want to handle IE?
+ if (ev &&
+ (ev.type == 'mouseup' || ev.type == 'click' ) &&
+ ev.target && ev.target.tagName == 'IMG') {
+ // they have click on an image...
+ // let's see if we can change the selection...
+ sel = ev.target;
+
+ var nodeRange = sel.ownerDocument.createRange();
+ try {
+ nodeRange.selectNode(sel);
+ } catch (e) {
+ nodeRange.selectNodeContents(sel);
+ }
+ //nodeRange.collapse(true);
+ var s = editor.win.getSelection();
+ s.removeAllRanges();
+ s.addRange(nodeRange);
+ }
+
+
+ var updateFooter = sel ? false : true;
+
var ans = this.editor.getAllAncestors();
// pick
var ty= Roo.form.HtmlEditor.ToolbarContext.types;
- var 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;
+
+ 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;
+
+ }
+ // pick a menu that exists..
var tn = sel.tagName.toUpperCase();
- sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
+ //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
+
tn = sel.tagName.toUpperCase();
- if (this.tb.name == tn) {
- return; // no change
+
+ var lastSel = this.tb.selectedNode
+
+ this.tb.selectedNode = sel;
+
+ // if current menu does not match..
+ if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
+
+ this.tb.el.hide();
+ ///console.log("show: " + tn);
+ this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
+ this.tb.el.show();
+ // update name
+ this.tb.items.first().el.innerHTML = tn + ': ';
+
+
+ // update attributes
+ if (this.tb.fields) {
+ this.tb.fields.each(function(e) {
+ e.setValue(sel.getAttribute(e.attrname));
+ });
+ }
+
+ var hasStyles = false;
+ for(var i in this.styles) {
+ hasStyles = true;
+ break;
+ }
+
+ // update styles
+ if (hasStyles) {
+ var st = this.tb.fields.item(0);
+
+ st.store.removeAll();
+
+
+ var cn = sel.className.split(/\s+/);
+
+ var avs = [];
+ if (this.styles['*']) {
+
+ Roo.each(this.styles['*'], function(v) {
+ avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
+ });
+ }
+ if (this.styles[tn]) {
+ Roo.each(this.styles[tn], function(v) {
+ avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
+ });
+ }
+
+ st.store.loadData(avs);
+ st.collapse();
+ st.setValue(cn);
+ }
+ // flag our selected Node.
+ this.tb.selectedNode = sel;
+
+
+ Roo.menu.MenuMgr.hideAll();
+
}
- this.tb.el.hide();
- ///console.log("show: " + tn);
- this.tb = this.toolbars[tn];
- this.tb.el.show();
- this.tb.fields.each(function(e) {
- e.setValue(sel.getAttribute(e.name));
+
+ if (!updateFooter) {
+ return;
+ }
+ // update the footer
+ //
+ var html = '';
+
+ this.footerEls = ans.reverse();
+ Roo.each(this.footerEls, function(a,i) {
+ if (!a) { return; }
+ html += html.length ? ' > ' : '';
+
+ html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
+
});
- this.tb.selectedNode = sel;
+
+ //
+ var sz = this.footDisp.up('td').getSize();
+ this.footDisp.dom.style.width = (sz.width -10) + 'px';
+ this.footDisp.dom.style.marginLeft = '5px';
+ this.footDisp.dom.style.overflow = 'hidden';
- Roo.menu.MenuMgr.hideAll();
-
+ this.footDisp.dom.innerHTML = html;
+
//this.editorsyncValue();
},
var tb = new Roo.Toolbar(wdiv);
+ // add the name..
+
tb.add(nm+ ": ");
+
+ var styles = [];
+ for(var i in this.styles) {
+ styles.push(i);
+ }
+
+ // styles...
+ if (styles && styles.length) {
+
+ // this needs a multi-select checkbox...
+ tb.addField( new Roo.form.ComboBox({
+ store: new Roo.data.SimpleStore({
+ id : 'val',
+ fields: ['val', 'selected'],
+ data : []
+ }),
+ name : '-roo-edit-className',
+ attrname : 'className',
+ displayField:'val',
+ typeAhead: false,
+ mode: 'local',
+ editable : false,
+ triggerAction: 'all',
+ emptyText:'Select Style',
+ selectOnFocus:true,
+ width: 130,
+ listeners : {
+ 'select': function(c, r, i) {
+ // initial support only for on class per el..
+ tb.selectedNode.className = r ? r.get('val') : '';
+ editor.syncValue();
+ }
+ }
+
+ }));
+ }
+
+
+
for (var i in tlist) {
+
var item = tlist[i];
tb.add(item.title + ": ");
+
+
+
+
if (item.opts) {
- // fixme
-
-
+ // opts == pulldown..
tb.addField( new Roo.form.ComboBox({
store: new Roo.data.SimpleStore({
id : 'val',
fields: ['val'],
- data : item.opts // from states.js
+ data : item.opts
}),
- name : i,
+ name : '-roo-edit-' + i,
+ attrname : i,
displayField:'val',
typeAhead: false,
mode: 'local',
width: item.width ? item.width : 130,
listeners : {
'select': function(c, r, i) {
- tb.selectedNode.setAttribute(c.name, r.get('val'));
+ tb.selectedNode.setAttribute(c.attrname, r.get('val'));
}
}
}));
continue;
-
-
-
+
tb.addField( new Roo.form.TextField({
name: i,
continue;
}
tb.addField( new Roo.form.TextField({
- name: i,
+ name: '-roo-edit-' + i,
+ attrname : i,
+
width: item.width,
//allowBlank:true,
value: '',
listeners: {
'change' : function(f, nv, ov) {
- tb.selectedNode.setAttribute(f.name, nv);
+ tb.selectedNode.setAttribute(f.attrname, nv);
}
}
}));
return tb;
+ },
+ buildFooter : function()
+ {
+
+ var fel = this.editor.wrap.createChild();
+ this.footer = new Roo.Toolbar(fel);
+ // toolbar has scrolly on left / right?
+ var footDisp= new Roo.Toolbar.Fill();
+ var _t = this;
+ this.footer.add(
+ {
+ text : '<',
+ xtype: 'Button',
+ handler : function() {
+ _t.footDisp.scrollTo('left',0,true)
+ }
+ }
+ );
+ this.footer.add( footDisp );
+ this.footer.add(
+ {
+ text : '>',
+ xtype: 'Button',
+ handler : function() {
+ // no animation..
+ _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
+ }
+ }
+ );
+ var fel = Roo.get(footDisp.el);
+ fel.addClass('x-editor-context');
+ this.footDispWrap = fel;
+ this.footDispWrap.overflow = 'hidden';
+
+ this.footDisp = fel.createChild();
+ this.footDispWrap.on('click', this.onContextClick, this)
+
+
+ },
+ onContextClick : function (ev,dom)
+ {
+ ev.preventDefault();
+ var cn = dom.className;
+ Roo.log(cn);
+ if (!cn.match(/x-ed-loc-/)) {
+ return;
+ }
+ var n = cn.split('-').pop();
+ var ans = this.footerEls;
+ var sel = ans[n];
+
+ // pick
+ var range = this.editor.createRange();
+
+ range.selectNodeContents(sel);
+ //range.selectNode(sel);
+
+
+ var selection = this.editor.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+
+
+ this.updateToolbar(null, null, sel);
+
+
}
+
});
this.fireEvent('actioncomplete', this, action);
}else{
+
+ // failure condition..
+ // we have a scenario where updates need confirming.
+ // eg. if a locking scenario exists..
+ // we look for { errors : { needs_confirm : true }} in the response.
+ if (
+ (typeof(action.result) != 'undefined') &&
+ (typeof(action.result.errors) != 'undefined') &&
+ (typeof(action.result.errors.needs_confirm) != 'undefined')
+ ){
+ var _t = this;
+ Roo.MessageBox.confirm(
+ "Change requires confirmation",
+ action.result.errorMsg,
+ function(r) {
+ if (r != 'yes') {
+ return;
+ }
+ _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
+ }
+
+ );
+
+
+
+ return;
+ }
+
Roo.callback(o.failure, o.scope, [this, action]);
// show an error message if no failed handler is set..
if (!this.hasListener('actionfailed')) {
- Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
+ Roo.MessageBox.alert("Error",
+ (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
+ action.result.errorMsg :
+ "Saving Failed, please check your entries or try again"
+ );
}
this.fireEvent('actionfailed', this, action);
* This differs from getValues as it calls getValue on each child item, rather than using dom data.
* @return {Object}
*/
- getFieldValues : function()
+ getFieldValues : function(with_hidden)
{
if (this.childForms) {
// copy values from the child forms
+ // should this call getFieldValues - probably not as we do not currently copy
+ // hidden fields when we generate..
Roo.each(this.childForms, function (f) {
this.setValues(f.getValues());
}, this);
return;
}
var v = f.getValue();
+ // not sure if this supported any more..
if ((typeof(v) == 'object') && f.getRawValue) {
v = f.getRawValue() ; // dates..
}
+ // combo boxes where name != hiddenName...
+ if (f.name != f.getName()) {
+ ret[f.name] = f.getRawValue();
+ }
ret[f.getName()] = v;
});
if (!id) {
return ret;
}
- Ext.each(this.allItems, function(f){
+ Roo.each(this.allItems, function(f){
if (f.id == id || f.name == id ){
ret = f;
return false;
Roo.MessageBox.hide();
}
-
this.response = response;
this.failureType = Roo.form.Action.CONNECT_FAILURE;
this.form.afterAction(this, false);
data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
ds.loadData( data);
}
+ // clear selection so it does not get stale.
+ if (this.grid.sm) {
+ this.grid.sm.clearSelections();
+ }
+
Roo.form.GridField.superclass.setValue.call(this, v);
this.refreshValue();
// should load data in the grid really....
//if(this.inputValue !== undefined){
this.wrap = this.el.wrap();
- this.viewEl = this.wrap.createChild({ tag: 'div'});
+ this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
if (this.bodyStyle) {
this.viewEl.applyStyles(this.bodyStyle);
Roo.form.DayPicker.superclass.onDestroy.call(this);
}
+});/*
+ * RooJS Library 1.1.1
+ * Copyright(c) 2008-2011 Alan Knowles
+ *
+ * License - LGPL
+ */
+
+
+/**
+ * @class Roo.form.ComboCheck
+ * @extends Roo.form.ComboBox
+ * A combobox for multiple select items.
+ *
+ * FIXME - could do with a reset button..
+ *
+ * @constructor
+ * Create a new ComboCheck
+ * @param {Object} config Configuration options
+ */
+Roo.form.ComboCheck = function(config){
+ Roo.form.ComboCheck.superclass.constructor.call(this, config);
+ // should verify some data...
+ // like
+ // hiddenName = required..
+ // displayField = required
+ // valudField == required
+ var req= [ 'hiddenName', 'displayField', 'valueField' ];
+ var _t = this;
+ Roo.each(req, function(e) {
+ if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
+ throw "Roo.form.ComboCheck : missing value for: " + e;
+ }
+ });
+
+
+};
+
+Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
+
+
+ editable : false,
+
+ selectedClass: 'x-menu-item-checked',
+
+ // private
+ onRender : function(ct, position){
+ var _t = this;
+
+
+
+ if(!this.tpl){
+ var cls = 'x-combo-list';
+
+
+ this.tpl = new Roo.Template({
+ html : '<div class="'+cls+'-item x-menu-check-item">' +
+ '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
+ '<span>{' + this.displayField + '}</span>' +
+ '</div>'
+
+ });
+ }
+
+
+ Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
+ this.view.singleSelect = false;
+ this.view.multiSelect = true;
+ this.view.toggleSelect = true;
+ this.pageTb.add(new Roo.Toolbar.Fill(), {
+
+ text: 'Done',
+ handler: function()
+ {
+ _t.collapse();
+ }
+ });
+ },
+
+ onViewOver : function(e, t){
+ // do nothing...
+ return;
+
+ },
+
+ onViewClick : function(doFocus,index){
+ return;
+
+ },
+ select: function () {
+ //Roo.log("SELECT CALLED");
+ },
+
+ selectByValue : function(xv, scrollIntoView){
+ var ar = this.getValueArray();
+ var sels = [];
+
+ Roo.each(ar, function(v) {
+ if(v === undefined || v === null){
+ return;
+ }
+ var r = this.findRecord(this.valueField, v);
+ if(r){
+ sels.push(this.store.indexOf(r))
+
+ }
+ },this);
+ this.view.select(sels);
+ return false;
+ },
+
+
+
+ onSelect : function(record, index){
+ // Roo.log("onselect Called");
+ // this is only called by the clear button now..
+ this.view.clearSelections();
+ this.setValue('[]');
+ if (this.value != this.valueBefore) {
+ this.fireEvent('change', this, this.value, this.valueBefore);
+ }
+ },
+ getValueArray : function()
+ {
+ var ar = [] ;
+
+ try {
+ //Roo.log(this.value);
+ if (typeof(this.value) == 'undefined') {
+ return [];
+ }
+ var ar = Roo.decode(this.value);
+ return ar instanceof Array ? ar : []; //?? valid?
+
+ } catch(e) {
+ Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
+ return [];
+ }
+
+ },
+ expand : function ()
+ {
+ Roo.form.ComboCheck.superclass.expand.call(this);
+ this.valueBefore = this.value;
+
+
+ },
+
+ collapse : function(){
+ Roo.form.ComboCheck.superclass.collapse.call(this);
+ var sl = this.view.getSelectedIndexes();
+ var st = this.store;
+ var nv = [];
+ var tv = [];
+ var r;
+ Roo.each(sl, function(i) {
+ r = st.getAt(i);
+ nv.push(r.get(this.valueField));
+ },this);
+ this.setValue(Roo.encode(nv));
+ if (this.value != this.valueBefore) {
+
+ this.fireEvent('change', this, this.value, this.valueBefore);
+ }
+
+ },
+
+ setValue : function(v){
+ // Roo.log(v);
+ this.value = v;
+
+ var vals = this.getValueArray();
+ var tv = [];
+ Roo.each(vals, function(k) {
+ var r = this.findRecord(this.valueField, k);
+ if(r){
+ tv.push(r.data[this.displayField]);
+ }else if(this.valueNotFoundText !== undefined){
+ tv.push( this.valueNotFoundText );
+ }
+ },this);
+ // Roo.log(tv);
+
+ Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
+ this.hiddenField.value = v;
+ this.value = v;
+ }
+
});//<script type="text/javasscript">
{
// basically accepts a pannel...
// can accept a layout region..!?!?
- // console.log('BorderLayout add ' + cfg.xtype)
+ //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
if (!cfg.xtype.match(/Panel$/)) {
return false;
}
var ret = false;
+
+ if (typeof(cfg.region) == 'undefined') {
+ Roo.log("Failed to add Panel, region was not set");
+ Roo.log(cfg);
+ return false;
+ }
var region = cfg.region;
delete cfg.region;
xitems = cfg.items;
delete cfg.items;
}
-
+ var nb = false;
switch(cfg.xtype)
{
ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
//console.log('adding nested layout panel ' + cfg.toSource());
this.add(region, ret);
-
+ nb = {}; /// find first...
break;
case 'GridPanel':
default:
alert("Can not add '" + cfg.xtype + "' to BorderLayout");
- return;
+ return null;
// GridPanel (grid, cfg)
}
this.beginUpdate();
// add children..
+ var region = '';
+ var abn = {};
Roo.each(xitems, function(i) {
- ret.addxtype(i);
+ region = nb && i.region ? i.region : false;
+
+ var add = ret.addxtype(i);
+
+ if (region) {
+ nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
+ if (!i.background) {
+ abn[region] = nb[region] ;
+ }
+ }
+
});
this.endUpdate();
+
+ // make the last non-background panel active..
+ //if (nb) { Roo.log(abn); }
+ if (nb) {
+
+ for(var r in abn) {
+ region = this.getRegion(r);
+ if (region) {
+ // tried using nb[r], but it does not work..
+
+ region.showPanel(abn[r]);
+
+ }
+ }
+ }
return ret;
}
* @class Roo.LayoutRegion
* @extends Roo.BasicLayoutRegion
* This class represents a region in a layout manager.
- * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
- * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
- * @cfg {Boolean} floatable False to disable floating (defaults to true)
- * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
- * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
- * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
- * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
- * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
- * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
- * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
- * @cfg {String} title The title for the region (overrides panel titles)
- * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
- * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
- * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
- * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
- * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
- * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
- * the space available, similar to FireFox 1.5 tabs (defaults to false)
- * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
- * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
- * @cfg {Boolean} showPin True to show a pin button
-* @cfg {Boolean} hidden True to start the region hidden (defaults to false)
-* @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
-* @cfg {Boolean} disableTabTips True to disable tab tooltips
-* @cfg {Number} width For East/West panels
-* @cfg {Number} height For North/South panels
-* @cfg {Boolean} split To show the splitter
+ * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
+ * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
+ * @cfg {Boolean} floatable False to disable floating (defaults to true)
+ * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
+ * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
+ * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
+ * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
+ * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
+ * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
+ * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
+ * @cfg {String} title The title for the region (overrides panel titles)
+ * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
+ * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
+ * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
+ * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
+ * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
+ * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
+ * the space available, similar to FireFox 1.5 tabs (defaults to false)
+ * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
+ * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
+ * @cfg {Boolean} showPin True to show a pin button
+ * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
+ * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
+ * @cfg {Boolean} disableTabTips True to disable tab tooltips
+ * @cfg {Number} width For East/West panels
+ * @cfg {Number} height For North/South panels
+ * @cfg {Boolean} split To show the splitter
+ * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
*/
Roo.LayoutRegion = function(mgr, config, pos){
Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
// overridden
},
- initTabs : function(){
+ initTabs : function()
+ {
this.bodyEl.setStyle("overflow", "hidden");
- var ts = new Roo.TabPanel(this.bodyEl.dom, {
- tabPosition: this.bottomTabs ? 'bottom' : 'top',
- disableTooltips: this.config.disableTabTips
- });
+ var ts = new Roo.TabPanel(
+ this.bodyEl.dom,
+ {
+ tabPosition: this.bottomTabs ? 'bottom' : 'top',
+ disableTooltips: this.config.disableTabTips,
+ toolbar : this.config.toolbar
+ }
+ );
if(this.config.hideTabs){
ts.stripWrap.setDisplayed(false);
}
* @class Roo.ContentPanel
* @extends Roo.util.Observable
* A basic ContentPanel element.
- * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
- * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
+ * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
+ * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
* @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
- * @cfg {Boolean} closable True if the panel can be closed/removed
- * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
+ * @cfg {Boolean} closable True if the panel can be closed/removed
+ * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
* @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
- * @cfg {Toolbar} toolbar A toolbar for this panel
- * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
- * @cfg {String} title The title for this panel
- * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
- * @cfg {String} url Calls {@link #setUrl} with this value
- * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
- * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
- * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
+ * @cfg {Toolbar} toolbar A toolbar for this panel
+ * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
+ * @cfg {String} title The title for this panel
+ * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
+ * @cfg {String} url Calls {@link #setUrl} with this value
+ * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
+ * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
+ * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
+ * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
+
* @constructor
* Create a new ContentPanel.
* @param {String/HTMLElement/Roo.Element} el The container element for this panel
}
if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
- this.wrapEl = this.el.wrap();
- this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
-
+ this.wrapEl = this.el.wrap();
+ this.toolbar.container = this.el.insertSibling(false, 'before');
+ this.toolbar = new Roo.Toolbar(this.toolbar);
}
* @param {Number} width The width after any component adjustments
* @param {Number} height The height after any component adjustments
*/
- "resize" : true
+ "resize" : true,
+
+ /**
+ * @event render
+ * Fires when this tab is created
+ * @param {Roo.ContentPanel} this
+ */
+ "render" : true
+
+
+
});
if(this.autoScroll){
this.resizeEl.setStyle("overflow", "auto");
Roo.ContentPanel.superclass.constructor.call(this);
+
+ this.fireEvent('render', this);
};
Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
this.el = null;
},
+ /**
+ * form - if the content panel contains a form - this is a reference to it.
+ * @type {Roo.form.Form}
+ */
+ form : false,
+ /**
+ * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
+ * This contains a reference to it.
+ * @type {Roo.View}
+ */
+ view : false,
+
/**
* Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
* <pre><code>
if ( this.form.allItems.length) this.form.render(el.dom);
return this.form;
}
- if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
+ // should only have one of theses..
+ if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
// views..
cfg.el = this.el.appendChild(document.createElement("div"));
// factory?
- var ret = new Roo[cfg.xtype](cfg);
- ret.render(false, ''); // render blank..
- return ret;
+ var ret = new Roo.factory(cfg);
+ ret.render && ret.render(false, ''); // render blank..
+ this.view = ret;
+ return ret;
}
return false;
-
}
});
this.dataSource= Roo.factory(this.dataSource, Roo.data);
this.ds = this.dataSource;
this.ds.xmodule = this.xmodule || false;
-
+
}
*/
dropTarget: false,
+
+
// private
rendered : false,
if (this.toolbar && this.toolbar.xtype) {
this.toolbar.container = this.getView().getHeaderPanel(true);
- this.toolbar = new Ext.Toolbar(this.toolbar);
+ this.toolbar = new Roo.Toolbar(this.toolbar);
}
if (this.footer && this.footer.xtype) {
this.footer.dataSource = this.getDataSource();
if(cindex !== false){
return this.view.getHeaderCell(cindex);
}
+ return null;
},
nextVisible : function(h){
Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
- /**
- * Override this function to apply custom css classes to rows during rendering
- * @param {Record} record The record
- * @param {Number} index
- * @method getRowClass
- */
+
rowClass : "x-grid-row",
cellClass : "x-grid-col",
this.rowEl.elements = els;
return this.rowEl;
},
-
+ /**
+ * Gets the 'td' of the cell
+ *
+ * @param {Integer} rowIndex row to select
+ * @param {Integer} colIndex column to select
+ *
+ * @return {Object}
+ */
getCell : function(rowIndex, colIndex){
var locked = this.cm.getLockedCount();
var source;
// buffers
var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
var hasListener = this.grid.hasListener('rowclass');
+
var rowcfg = {};
for(var j = 0, len = rs.length; j < len; j++){
r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
}
+
var markup = ct.apply(p);
if(!c.locked){
cb[cb.length] = markup;
if(this.enableMoveAnim && Roo.enableFx){
this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
}
+ // if multisort - fix sortOrder, and reload..
+ if (this.grid.dataSource.multiSort) {
+ // the we can call sort again..
+ var dm = this.grid.dataSource;
+ var cm = this.grid.colModel;
+ var so = [];
+ for(var i = 0; i < cm.config.length; i++ ) {
+
+ if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
+ continue; // dont' bother, it's not in sort list or being set.
+ }
+
+ so.push(cm.config[i].dataIndex);
+ };
+ dm.sortOrder = so;
+ dm.load(dm.lastOptions);
+
+
+ }
+
},
updateCell : function(dm, rowIndex, dataIndex){
},
updateHeaderSortState : function(){
- var state = this.ds.getSortState();
- if(!state){
- return;
+
+ // sort state can be single { field: xxx, direction : yyy}
+ // or { xxx=>ASC , yyy : DESC ..... }
+
+ var mstate = {};
+ if (!this.ds.multiSort) {
+ var state = this.ds.getSortState();
+ if(!state){
+ return;
+ }
+ mstate[state.field] = state.direction;
+ // FIXME... - this is not used here.. but might be elsewhere..
+ this.sortState = state;
+
+ } else {
+ mstate = this.ds.sortToggle;
}
- this.sortState = state;
- var sortColumn = this.cm.findColumnIndex(state.field);
- if(sortColumn != -1){
- var sortDir = state.direction;
- var sc = this.sortClasses;
- var hds = this.el.select(this.headerSelector).removeClass(sc);
- hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
+ //remove existing sort classes..
+
+ var sc = this.sortClasses;
+ var hds = this.el.select(this.headerSelector).removeClass(sc);
+
+ for(var f in mstate) {
+
+ var sortColumn = this.cm.findColumnIndex(f);
+
+ if(sortColumn != -1){
+ var sortDir = mstate[f];
+ hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
+ }
}
+
+
+
},
+
handleHeaderClick : function(g, index){
if(this.headersDisabled){
return;
return;
}
g.stopEditing();
+
+ if (dm.multiSort) {
+ // update the sortOrder
+ var so = [];
+ for(var i = 0; i < cm.config.length; i++ ) {
+
+ if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
+ continue; // dont' bother, it's not in sort list or being set.
+ }
+
+ so.push(cm.config[i].dataIndex);
+ };
+ dm.sortOrder = so;
+ }
+
+
dm.sort(cm.getDataIndex(index));
},
* This class provides the basic implementation for cell selection in a grid.
* @constructor
* @param {Object} config The object containing the configuration of this model.
+ * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
*/
Roo.grid.CellSelectionModel = function(config){
Roo.apply(this, config);
<li>o.cell: An array of [rowIndex, columnIndex]</li>
</ul>
*/
- "selectionchange" : true
+ "selectionchange" : true,
+ /**
+ * @event tabend
+ * Fires when the tab (or enter) was pressed on the last editable cell
+ * You can use this to trigger add new row.
+ * @param {SelectionModel} this
+ */
+ "tabend" : true,
+ /**
+ * @event beforeeditnext
+ * Fires before the next editable sell is made active
+ * You can use this to skip to another cell or fire the tabend
+ * if you set cell to false
+ * @param {Object} eventdata object : { cell : [ row, col ] }
+ */
+ "beforeeditnext" : true
});
Roo.grid.CellSelectionModel.superclass.constructor.call(this);
};
Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
+
+ enter_is_tab: false,
/** @ignore */
initEvents : function(){
/** @ignore */
handleKeyDown : function(e){
- Roo.log('Cell Sel Model handleKeyDown');
+ //Roo.log('Cell Sel Model handleKeyDown');
if(!e.isNavKeyPress()){
return;
}
var k = e.getKey(), r = s.cell[0], c = s.cell[1];
var newCell;
+
+
switch(k){
case e.TAB:
// handled by onEditorKey
if (g.isEditor && g.editing) {
return;
}
- if(e.shiftKey){
- newCell = walk(r, c-1, -1);
- }else{
- newCell = walk(r, c+1, 1);
+ if(e.shiftKey) {
+ newCell = walk(r, c-1, -1);
+ } else {
+ newCell = walk(r, c+1, 1);
}
- break;
- case e.DOWN:
- newCell = walk(r+1, c, 1);
- break;
- case e.UP:
- newCell = walk(r-1, c, -1);
- break;
- case e.RIGHT:
- newCell = walk(r, c+1, 1);
- break;
- case e.LEFT:
- newCell = walk(r, c-1, -1);
- break;
- case e.ENTER:
- if(g.isEditor && !g.editing){
- g.startEditing(r, c);
- e.stopEvent();
- return;
+ break;
+
+ case e.DOWN:
+ newCell = walk(r+1, c, 1);
+ break;
+
+ case e.UP:
+ newCell = walk(r-1, c, -1);
+ break;
+
+ case e.RIGHT:
+ newCell = walk(r, c+1, 1);
+ break;
+
+ case e.LEFT:
+ newCell = walk(r, c-1, -1);
+ break;
+
+ case e.ENTER:
+
+ if(g.isEditor && !g.editing){
+ g.startEditing(r, c);
+ e.stopEvent();
+ return;
}
+
+
break;
};
if(newCell){
this.select(newCell[0], newCell[1]);
e.stopEvent();
+
}
},
acceptsNav : function(row, col, cm){
return !cm.isHidden(col) && cm.isCellEditable(col, row);
},
-
+ /**
+ * Selects a cell.
+ * @param {Number} field (not used) - as it's normally used as a listener
+ * @param {Number} e - event - fake it by using
+ *
+ * var e = Roo.EventObjectImpl.prototype;
+ * e.keyCode = e.TAB
+ *
+ *
+ */
onEditorKey : function(field, e){
- var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
+ var k = e.getKey(),
+ newCell,
+ g = this.grid,
+ ed = g.activeEditor,
+ forward = false;
///Roo.log('onEditorKey' + k);
+
+ if (this.enter_is_tab && k == e.ENTER) {
+ k = e.TAB;
+ }
+
if(k == e.TAB){
if(e.shiftKey){
newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
}else{
newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
+ forward = true;
}
+
e.stopEvent();
- }else if(k == e.ENTER && !e.ctrlKey){
+
+ } else if(k == e.ENTER && !e.ctrlKey){
ed.completeEdit();
e.stopEvent();
newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
- }else if(k == e.ESC){
+
+ } else if(k == e.ESC){
ed.cancelEdit();
}
-
-
+
+ if (newCell) {
+ var ecall = { cell : newCell, forward : forward };
+ this.fireEvent('beforeeditnext', ecall );
+ newCell = ecall.cell;
+ forward = ecall.forward;
+ }
+
if(newCell){
//Roo.log('next cell after edit');
g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
+ } else if (forward) {
+ // tabbed past last
+ this.fireEvent.defer(100, this, ['tabend',this]);
}
}
});/*
cancel:false,
editor: ed
};
+ var cell = Roo.get(this.view.getCell(ed.row,ed.col))
+ cell.show();
+
if(String(value) !== String(startValue)){
if(this.fireEvent("validateedit", e) !== false && !e.cancel){
this.stopEditing();
if(this.colModel.isCellEditable(col, row)){
this.view.ensureVisible(row, col, true);
+
var r = this.dataSource.getAt(row);
var field = this.colModel.getDataIndex(col);
+ var cell = Roo.get(this.view.getCell(row,col));
var e = {
grid: this,
record: r,
value: r.data[field],
row: row,
column: col,
- cancel:false
+ cancel:false
};
if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
this.editing = true;
ed.render(ed.parentEl || document.body);
}
ed.field.reset();
+
+ cell.hide();
+
(function(){ // complex but required for focus issues in safari, ie and opera
ed.row = row;
ed.col = col;
ed.record = r;
- ed.on("complete", this.onEditComplete, this, {single: true});
- ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
+ ed.on("complete", this.onEditComplete, this, {single: true});
+ ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
this.activeEditor = ed;
var v = r.data[field];
ed.startEdit(this.view.getCell(row, col), v);
if(this.store){
this.store.on('beforeload', this.onBeforeLoad, this);
this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
this.removeMask = false;
}else{
var um = this.el.getUpdateManager();
enable : function(){
this.disabled = false;
},
-
+
+ onLoadException : function()
+ {
+ if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ }
+ this.el.unmask(this.removeMask);
+ },
// private
- onLoad : function(){
+ onLoad : function()
+ {
this.el.unmask(this.removeMask);
},
if(this.store){
this.store.un('beforeload', this.onBeforeLoad, this);
this.store.un('load', this.onLoad, this);
- this.store.un('loadexception', this.onLoad, this);
+ this.store.un('loadexception', this.onLoadException, this);
}else{
var um = this.el.getUpdateManager();
um.un('beforeupdate', this.onBeforeLoad, this);
* Fork - LGPL
* <script type="text/javascript">
*/
-Roo.XTemplate = function(){
+
+
+/**
+ * @class Roo.XTemplate
+ * @extends Roo.Template
+ * Provides a template that can have nested templates for loops or conditionals. The syntax is:
+<pre><code>
+var t = new Roo.XTemplate(
+ '<select name="{name}">',
+ '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
+ '</select>'
+);
+
+// then append, applying the master template values
+ </code></pre>
+ *
+ * Supported features:
+ *
+ * Tags:
+
+<pre><code>
+ {a_variable} - output encoded.
+ {a_variable.format:("Y-m-d")} - call a method on the variable
+ {a_variable:raw} - unencoded output
+ {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
+ {a_variable:this.method_on_template(...)} - call a method on the template object.
+
+</code></pre>
+ * The tpl tag:
+<pre><code>
+ <tpl for="a_variable or condition.."></tpl>
+ <tpl if="a_variable or condition"></tpl>
+ <tpl exec="some javascript"></tpl>
+ <tpl name="named_template"></tpl> (experimental)
+
+ <tpl for="."></tpl> - just iterate the property..
+ <tpl for=".."></tpl> - iterates with the parent (probably the template)
+</code></pre>
+ *
+ */
+Roo.XTemplate = function()
+{
Roo.XTemplate.superclass.constructor.apply(this, arguments);
- var s = this.html;
-
- s = ['<tpl>', s, '</tpl>'].join('');
-
- var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
-
- var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
- var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
- var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
- var m, id = 0;
- var tpls = [];
-
- while(m = s.match(re)){
- var m2 = m[0].match(nameRe);
- var m3 = m[0].match(ifRe);
- var m4 = m[0].match(execRe);
- var exp = null, fn = null, exec = null;
- var name = m2 && m2[1] ? m2[1] : '';
- if(m3){
- exp = m3 && m3[1] ? m3[1] : null;
- if(exp){
- fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
- }
- }
- if(m4){
- exp = m4 && m4[1] ? m4[1] : null;
- if(exp){
- exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
- }
- }
- if(name){
- switch(name){
- case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
- case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
- default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
- }
- }
- tpls.push({
- id: id,
- target: name,
- exec: exec,
- test: fn,
- body: m[1]||''
- });
- s = s.replace(m[0], '{xtpl'+ id + '}');
- ++id;
- }
- for(var i = tpls.length-1; i >= 0; --i){
- this.compileTpl(tpls[i]);
+ if (this.html) {
+ this.compile();
}
- this.master = tpls[tpls.length-1];
- this.tpls = tpls;
};
+
+
Roo.extend(Roo.XTemplate, Roo.Template, {
+ /**
+ * The various sub templates
+ */
+ tpls : false,
+ /**
+ *
+ * basic tag replacing syntax
+ * WORD:WORD()
+ *
+ * // you can fake an object call by doing this
+ * x.t:(test,tesT)
+ *
+ */
re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
- applySubTemplate : function(id, values, parent){
+ /**
+ * compile the template
+ *
+ * This is not recursive, so I'm not sure how nested templates are really going to be handled..
+ *
+ */
+ compile: function()
+ {
+ var s = this.html;
+
+ s = ['<tpl>', s, '</tpl>'].join('');
+
+ var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
+ nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
+ ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
+ execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
+ namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
+ m,
+ id = 0,
+ tpls = [];
+
+ while(true == !!(m = s.match(re))){
+ var forMatch = m[0].match(nameRe),
+ ifMatch = m[0].match(ifRe),
+ execMatch = m[0].match(execRe),
+ namedMatch = m[0].match(namedRe),
+
+ exp = null,
+ fn = null,
+ exec = null,
+ name = forMatch && forMatch[1] ? forMatch[1] : '';
+
+ if (ifMatch) {
+ // if - puts fn into test..
+ exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
+ if(exp){
+ fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
+ }
+ }
+
+ if (execMatch) {
+ // exec - calls a function... returns empty if true is returned.
+ exp = execMatch && execMatch[1] ? execMatch[1] : null;
+ if(exp){
+ exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
+ }
+ }
+
+
+ if (name) {
+ // for =
+ switch(name){
+ case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
+ case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
+ default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
+ }
+ }
+ var uid = namedMatch ? namedMatch[1] : id;
+
+
+ tpls.push({
+ id: namedMatch ? namedMatch[1] : id,
+ target: name,
+ exec: exec,
+ test: fn,
+ body: m[1] || ''
+ });
+ if (namedMatch) {
+ s = s.replace(m[0], '');
+ } else {
+ s = s.replace(m[0], '{xtpl'+ id + '}');
+ }
+ ++id;
+ }
+ this.tpls = [];
+ for(var i = tpls.length-1; i >= 0; --i){
+ this.compileTpl(tpls[i]);
+ this.tpls[tpls[i].id] = tpls[i];
+ }
+ this.master = tpls[tpls.length-1];
+ return this;
+ },
+ /**
+ * same as applyTemplate, except it's done to one of the subTemplates
+ * when using named templates, you can do:
+ *
+ * var str = pl.applySubTemplate('your-name', values);
+ *
+ *
+ * @param {Number} id of the template
+ * @param {Object} values to apply to template
+ * @param {Object} parent (normaly the instance of this object)
+ */
+ applySubTemplate : function(id, values, parent)
+ {
+
+
var t = this.tpls[id];
- if(t.test && !t.test.call(this, values, parent)){
- return '';
+
+
+ try {
+ if(t.test && !t.test.call(this, values, parent)){
+ return '';
+ }
+ } catch(e) {
+ Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
+ Roo.log(e.toString());
+ Roo.log(t.test);
+ return ''
}
- if(t.exec && t.exec.call(this, values, parent)){
- return '';
+ try {
+
+ if(t.exec && t.exec.call(this, values, parent)){
+ return '';
+ }
+ } catch(e) {
+ Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
+ Roo.log(e.toString());
+ Roo.log(t.exec);
+ return ''
}
- var vs = t.target ? t.target.call(this, values, parent) : values;
- parent = t.target ? values : parent;
- if(t.target && vs instanceof Array){
- var buf = [];
- for(var i = 0, len = vs.length; i < len; i++){
- buf[buf.length] = t.compiled.call(this, vs[i], parent);
+ try {
+ var vs = t.target ? t.target.call(this, values, parent) : values;
+ parent = t.target ? values : parent;
+ if(t.target && vs instanceof Array){
+ var buf = [];
+ for(var i = 0, len = vs.length; i < len; i++){
+ buf[buf.length] = t.compiled.call(this, vs[i], parent);
+ }
+ return buf.join('');
}
- return buf.join('');
+ return t.compiled.call(this, vs, parent);
+ } catch (e) {
+ Roo.log("Xtemplate.applySubTemplate : Exception thrown");
+ Roo.log(e.toString());
+ Roo.log(t.compiled);
+ return '';
}
- return t.compiled.call(this, vs, parent);
},
- compileTpl : function(tpl){
+ compileTpl : function(tpl)
+ {
var fm = Roo.util.Format;
var useF = this.disableFormats !== true;
var sep = Roo.isGecko ? "+" : ",";
- var fn = function(m, name, format, args){
+ var undef = function(str) {
+ Roo.log("Property not found :" + str);
+ return '';
+ };
+
+ var fn = function(m, name, format, args)
+ {
+ //Roo.log(arguments);
+ args = args ? args.replace(/\\'/g,"'") : args;
+ //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
+ if (typeof(format) == 'undefined') {
+ format= 'htmlEncode';
+ }
+ if (format == 'raw' ) {
+ format = false;
+ }
+
if(name.substr(0, 4) == 'xtpl'){
return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
}
- var v;
- if(name.indexOf('.') != -1){
- v = name;
- }else{
- v = "values['" + name + "']";
- }
+
+ // build an array of options to determine if value is undefined..
+
+ // basically get 'xxxx.yyyy' then do
+ // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
+ // (function () { Roo.log("Property not found"); return ''; })() :
+ // ......
+
+ var udef_ar = [];
+ var lookfor = '';
+ Roo.each(name.split('.'), function(st) {
+ lookfor += (lookfor.length ? '.': '') + st;
+ udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
+ });
+
+ var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
+
+
if(format && useF){
+
args = args ? ',' + args : "";
+
if(format.substr(0, 5) != "this."){
format = "fm." + format + '(';
}else{
format = 'this.call("'+ format.substr(5) + '", ';
args = ", values";
}
- }else{
- args= ''; format = "("+v+" === undefined ? '' : ";
+
+ return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
}
- return "'"+ sep + format + v + args + ")"+sep+"'";
+
+ if (args.length) {
+ // called with xxyx.yuu:(test,test)
+ // change to ()
+ return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
+ }
+ // raw.. - :raw modifier..
+ return "'"+ sep + udef_st + name + ")"+sep+"'";
+
};
var body;
// branched to use + in gecko and [].join() in others
if(Roo.isGecko){
- body = "tpl.compiled = function(values, parent){ return '" +
+ body = "tpl.compiled = function(values, parent){ with(values) { return '" +
tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
- "';};";
+ "';};};";
}else{
- body = ["tpl.compiled = function(values, parent){ return ['"];
- body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
- body.push("'].join('');};");
+ body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
+ body.push(tpl.body.replace(/(\r\n|\n)/g,
+ '\\n').replace(/'/g, "\\'").replace(this.re, fn));
+ body.push("'].join('');};};");
body = body.join('');
}
- /** eval:var:zzzzzzz */
+
+ Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
+
+ /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
eval(body);
+
return this;
},
applyTemplate : function(values){
return this.master.compiled.call(this, values, {});
- var s = this.subs;
+ //var s = this.subs;
},
apply : function(){
return this.applyTemplate.apply(this, arguments);
- },
+ }
- compile : function(){return this;}
-});
+ });
Roo.XTemplate.from = function(el){
el = Roo.getDom(el);
/**
* @class Roo.XComponent
* A delayed Element creator...
+ * Or a way to group chunks of interface together.
*
* 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 )
+ *
* @extends Roo.util.Observable
* @constructor
* @param cfg {Object} configuration of component
* Fires when this the componnt is built
* @param {Roo.XComponent} c the component
*/
- 'built' : true,
- /**
- * @event buildcomplete
- * Fires on the top level element when all elements have been built
- * @param {Roo.XComponent} c the top level component.
- */
- 'buildcomplete' : true
+ 'built' : true
});
-
+ this.region = this.region || 'center'; // default..
Roo.XComponent.register(this);
this.modules = false;
this.el = false; // where the layout goes..
* String to display while loading.
*/
name : false,
+ /**
+ * @cfg {String} region
+ * Region to render component to (defaults to center)
+ */
+ region : 'center',
+
/**
* @cfg {Array} items
* A single item array - the first element is the root of the tree..
* It's done this way to stay compatible with the Xtype system...
*/
- items : false
-
-
+ items : false,
+
+ /**
+ * @property _tree
+ * The method that retuns the tree of parts that make up this compoennt
+ * @type {function}
+ */
+ _tree : false,
+
+ /**
+ * render
+ * render element to dom or tree
+ * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
+ */
+
+ render : function(el)
+ {
+
+ el = el || false;
+ var hp = this.parent ? 1 : 0;
+
+ 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 = false;
+ el = Roo.get(ename);
+ if (!el) {
+ Roo.log("Warning - element can not be found :#" + ename );
+ return;
+ }
+ }
+
+
+ if (!this.parent) {
+
+ el = el ? Roo.get(el) : false;
+
+ // it's a top level one..
+ this.parent = {
+ el : new Roo.BorderLayout(el || document.body, {
+
+ center: {
+ titlebar: false,
+ autoScroll:false,
+ closeOnTab: true,
+ tabPosition: 'top',
+ //resizeTabs: true,
+ alwaysShowTabs: el && hp? false : true,
+ hideTabs: el || !hp ? true : false,
+ minTabWidth: 140
+ }
+ })
+ }
+ }
+
+
+ // The 'tree' method is '_tree now'
+
+ var tree = this._tree ? this._tree() : this.tree();
+ tree.region = tree.region || this.region;
+ this.el = this.parent.el.addxtype(tree);
+ this.fireEvent('built', this);
+
+ this.panel = this.el;
+ this.layout = this.panel.layout;
+ this.parentLayout = this.parent.layout || false;
+
+ }
});
/**
* @property modules
* array of modules to be created by registration system.
- * @type Roo.XComponent
+ * @type {Array} of Roo.XComponent
*/
modules : [],
-
+ /**
+ * @property elmodules
+ * array of modules to be created by which use #ID
+ * @type {Array} of Roo.XComponent
+ */
+
+ elmodules : [],
+
/**
* Register components to be built later.
* * @param {Object} details about module
*/
register : function(obj) {
+
+ Roo.XComponent.event.fireEvent('register', obj);
+ switch(typeof(obj.disabled) ) {
+
+ case 'undefined':
+ break;
+
+ case 'function':
+ if ( obj.disabled() ) {
+ return;
+ }
+ break;
+ default:
+ if (obj.disabled) {
+ return;
+ }
+ break;
+ }
+
this.modules.push(obj);
},
/**
* convert a string to an object..
- *
+ * eg. 'AAA.BBB' -> finds AAA.BBB
+
*/
toObject : function(str)
if (!str || typeof(str) == 'object') {
return str;
}
+ if (str.substring(0,1) == '#') {
+ return str;
+ }
+
var ar = str.split('.');
var rt, o;
rt = ar.shift();
/** eval:var:o */
- eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
+ try {
+ eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
+ } catch (e) {
+ throw "Module not found : " + str;
+ }
+
if (o === false) {
throw "Module not found : " + str;
}
}
o = o[e];
});
+
return o;
},
*/
preBuild : function ()
{
-
+ var _t = this;
Roo.each(this.modules , function (obj)
{
- obj.parent = this.toObject(obj.parent);
+ var opar = obj.parent;
+ try {
+ obj.parent = this.toObject(opar);
+ } catch(e) {
+ Roo.log("parent:toObject failed: " + e.toString());
+ return;
+ }
if (!obj.parent) {
+ Roo.debug && Roo.log("GOT top level module");
+ Roo.debug && Roo.log(obj);
+ obj.modules = new Roo.util.MixedCollection(false,
+ function(o) { return o.order + '' }
+ );
this.topModule = obj;
return;
}
-
+ // parent is a string (usually a dom element name..)
+ if (typeof(obj.parent) == 'string') {
+ this.elmodules.push(obj);
+ return;
+ }
+ if (obj.parent.constructor != Roo.XComponent) {
+ Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
+ }
if (!obj.parent.modules) {
obj.parent.modules = new Roo.util.MixedCollection(false,
function(o) { return o.order + '' }
var cmp = function(a,b) {
return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
};
-
- if (!this.topModule || !this.topModule.modules) {
+ if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
throw "No top level modules to build";
}
-
- // make a flat list in order of modules to build.
- var mods = [ this.topModule ];
+ // make a flat list in order of modules to build.
+ var mods = this.topModule ? [ this.topModule ] : [];
+
+ // elmodules (is a list of DOM based modules )
+ Roo.each(this.elmodules,function(e) { mods.push(e) });
+
// add modules to their parents..
var addMod = function(m) {
- // Roo.debug && Roo.log(m.modKey);
+ Roo.debug && Roo.log("build Order: add: " + m.name);
mods.push(m);
if (m.modules) {
+ Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
m.modules.keySort('ASC', cmp );
+ Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
+
m.modules.each(addMod);
- }
+ } else {
+ Roo.debug && Roo.log("build Order: no child modules");
+ }
// not sure if this is used any more..
if (m.finalize) {
m.finalize.name = m.name + " (clean up) ";
}
}
- this.topModule.modules.keySort('ASC', cmp );
- this.topModule.modules.each(addMod);
+ if (this.topModule) {
+ this.topModule.modules.keySort('ASC', cmp );
+ this.topModule.modules.each(addMod);
+ }
return mods;
},
}
-
+ var msg = "Building Interface...";
// flash it up as modal - so we store the mask!?
Roo.MessageBox.show({ title: 'loading' });
Roo.MessageBox.show({
title: "Please wait...",
- msg: "Building Interface...",
+ msg: msg,
width:450,
progress:true,
closable:false,
if (!mods.length) {
Roo.debug && Roo.log('hide?');
Roo.MessageBox.hide();
- _this.topModule.fireEvent('buildcomplete', _this.topModule);
- return;
+ Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
+
+ // THE END...
+ return false;
}
var m = mods.shift();
+
+
Roo.debug && Roo.log(m);
- if (typeof(m) == 'function') { // not sure if this is supported any more..
+ // not sure if this is supported any more.. - modules that are are just function
+ if (typeof(m) == 'function') {
m.call(this);
return progressRun.defer(10, _this);
}
- Roo.MessageBox.updateProgress(
- (total - mods.length)/total, "Building Interface " + (total - mods.length) +
+
+ msg = "Building Interface " + (total - mods.length) +
" of " + total +
- (m.name ? (' - ' + m.name) : '')
- );
+ (m.name ? (' - ' + m.name) : '');
+ Roo.debug && Roo.log(msg);
+ Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
-
+ // is the module disabled?
var disabled = (typeof(m.disabled) == 'function') ?
m.disabled.call(m.module.disabled) : m.disabled;
return progressRun(); // we do not update the display!
}
- if (!m.parent) {
- // it's a top level one..
- var layoutbase = new Ext.BorderLayout(document.body, {
-
- center: {
- titlebar: false,
- autoScroll:false,
- closeOnTab: true,
- tabPosition: 'top',
- //resizeTabs: true,
- alwaysShowTabs: true,
- minTabWidth: 140
- }
- });
- var tree = m.tree();
- tree.region = 'center';
- m.el = layoutbase.addxtype(tree);
- m.panel = m.el;
- m.layout = m.panel.layout;
- return progressRun.defer(10, _this);
- }
-
- var tree = m.tree();
- tree.region = tree.region || m.region;
- m.el = m.parent.el.addxtype(tree);
- m.fireEvent('built', m);
- m.panel = m.el;
- m.layout = m.panel.layout;
- progressRun.defer(10, _this);
+ // now build
+
+
+ m.render();
+ // it's 10 on top level, and 1 on others??? why...
+ return progressRun.defer(10, _this);
+
}
progressRun.defer(1, _this);
- }
-
+ },
+
+
+ /**
+ * Event Object.
+ *
+ *
+ */
+ event: false,
+ /**
+ * wrapper for event.on - aliased later..
+ * Typically use to register a event handler for register:
+ *
+ * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
+ *
+ */
+ on : false
});
+
+Roo.XComponent.event = new Roo.util.Observable({
+ events : {
+ /**
+ * @event register
+ * Fires when an Component is registered,
+ * set the disable property on the Component to stop registration.
+ * @param {Roo.XComponent} c the component being registerd.
+ *
+ */
+ 'register' : true,
+ /**
+ * @event buildcomplete
+ * Fires on the top level element when all elements have been built
+ * @param {Roo.XComponent} the top level component.
+ */
+ 'buildcomplete' : true
+
+ }
+});
+
+Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
//<script type="text/javascript">