document.execCommand("BackgroundImageCache", false, true);
}catch(e){}
}
-
+
Roo.apply(Roo, {
/**
* True if the browser is in strict mode
BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
emptyFn : function(){},
-
+
/**
* Copies all the properties of config to obj if they don't already exist.
* @param {Object} obj The receiver of the properties
}
var buf = [];
for(var key in o){
- var ov = o[key], k = encodeURIComponent(key);
+ var ov = o[key], k = Roo.encodeURIComponent(key);
var type = typeof ov;
if(type == 'undefined'){
buf.push(k, "=&");
}else if(type != "function" && type != "object"){
- buf.push(k, "=", encodeURIComponent(ov), "&");
+ buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
}else if(ov instanceof Array){
if (ov.length) {
for(var i = 0, len = ov.length; i < len; i++) {
- buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
+ buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
}
} else {
buf.push(k, "=&");
}
buf.pop();
return buf.join("");
+ },
+ /**
+ * Safe version of encodeURIComponent
+ * @param {String} data
+ * @return {String}
+ */
+
+ encodeURIComponent : function (data)
+ {
+ try {
+ return encodeURIComponent(data);
+ } catch(e) {} // should be an uri encode error.
+
+ if (data == '' || data == null){
+ return '';
+ }
+ // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
+ function nibble_to_hex(nibble){
+ var chars = '0123456789ABCDEF';
+ return chars.charAt(nibble);
+ }
+ data = data.toString();
+ var buffer = '';
+ for(var i=0; i<data.length; i++){
+ var c = data.charCodeAt(i);
+ var bs = new Array();
+ if (c > 0x10000){
+ // 4 bytes
+ bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
+ bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
+ bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
+ bs[3] = 0x80 | (c & 0x3F);
+ }else if (c > 0x800){
+ // 3 bytes
+ bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
+ bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
+ bs[2] = 0x80 | (c & 0x3F);
+ }else if (c > 0x80){
+ // 2 bytes
+ bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
+ bs[1] = 0x80 | (c & 0x3F);
+ }else{
+ // 1 byte
+ bs[0] = c;
+ }
+ for(var j=0; j<bs.length; j++){
+ var b = bs[j];
+ var hex = nibble_to_hex((b & 0xF0) >>> 4)
+ + nibble_to_hex(b &0x0F);
+ buffer += '%'+hex;
+ }
+ }
+ return buffer;
+
},
/**
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;
for (var j = 0; j < el.options.length; j++) {
if (el.options[j].selected) {
if (Roo.isIE) {
- data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
+ data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
}
else {
- data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
+ data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
}
}
}
case 'radio':
case 'checkbox':
if (el.checked) {
- data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
+ data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
}
break;
case 'file':
break;
case 'submit':
if(hasSubmit == false) {
- data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
+ data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
hasSubmit = true;
}
break;
default:
- data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
+ data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
break;
}
}
var oConn = this;
if (callback && callback.timeout) {
+
this.timeout[o.tId] = window.setTimeout(function() {
oConn.abort(o, callback, true);
}, callback.timeout);
* 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
* Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
- * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
+ * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
* @singleton
*/
Roo.DomHelper = function(){
});
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 " + _t.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;
},
} : function(ns, name){
var d = this.dom;
return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
+ },
+
+
+ /**
+ * Sets or Returns the value the dom attribute value
+ * @param {String} name The attribute name
+ * @param {String} value (optional) The value to set the attribute to
+ * @return {String} The attribute value
+ */
+ attr : function(name){
+ if (arguments.length > 1) {
+ this.dom.setAttribute(name, arguments[1]);
+ return arguments[1];
+ }
+ if (!this.dom.hasAttribute(name)) {
+ return undefined;
+ }
+ return this.dom.getAttribute(name);
}
+
+
+
};
var ep = El.prototype;
failure: this.handleFailure,
scope: this,
argument: {options: o},
- timeout : this.timeout
+ timeout : o.timeout || this.timeout
};
var method = o.method||this.method||(p ? "POST" : "GET");
}
}
});
-
-/**
- * @class Roo.Ajax
- * @extends Roo.data.Connection
- * Global Ajax request class.
- *
- * @singleton
- */
-Roo.Ajax = new Roo.data.Connection({
- // fix up the docs
- /**
- * @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
- */
- autoAbort : false,
-
- /**
- * Serialize the passed form into a url encoded string
- * @param {String/HTMLElement} form
- * @return {String}
- */
- serializeForm : function(form){
- return Roo.lib.Ajax.serializeForm(form);
- }
-});/*
+/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
*/
/**
+ * 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}
*/
*/
update : function(url, params, callback, discardUrl){
if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
- var method = this.method, cfg;
+ var method = this.method,
+ cfg;
if(typeof url == "object"){ // must be config object
cfg = url;
url = cfg.url;
timeout: (this.timeout*1000),
argument: {"url": url, "form": null, "callback": callback, "params": params}
});
-
+ Roo.log("updated manager called with timeout of " + o.timeout);
this.transaction = Roo.Ajax.request(o);
}
},
};
/*
* Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
+ * Roo JS
+ * (c)) Alan Knowles
+ * Licence : LGPL
*/
+
/**
- * @class Roo.util.DelayedTask
- * Provides a convenient method of performing setTimeout where a new
- * timeout cancels the old timeout. An example would be performing validation on a keypress.
- * You can use this class to buffer
- * the keypress events for a certain number of milliseconds, and perform only if they stop
- * for that amount of time.
- * @constructor The parameters to this constructor serve as defaults and are not required.
- * @param {Function} fn (optional) The default function to timeout
- * @param {Object} scope (optional) The default scope of that timeout
- * @param {Array} args (optional) The default Array of arguments
+ * @class Roo.DomTemplate
+ * @extends Roo.Template
+ * An effort at a dom based template engine..
+ *
+ * Similar to XTemplate, except it uses dom parsing to create the template..
+ *
+ * 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>
+ <div roo-for="a_variable or condition.."></div>
+ <div roo-if="a_variable or condition"></div>
+ <div roo-exec="some javascript"></div>
+ <div roo-name="named_template"></div>
+
+</code></pre>
+ *
*/
-Roo.util.DelayedTask = function(fn, scope, args){
- var id = null, d, t;
+Roo.DomTemplate = function()
+{
+ Roo.DomTemplate.superclass.constructor.apply(this, arguments);
+ if (this.html) {
+ this.compile();
+ }
+};
- var call = function(){
- var now = new Date().getTime();
- if(now - t >= d){
- clearInterval(id);
- id = null;
- fn.apply(scope, args || []);
- }
- };
+
+Roo.extend(Roo.DomTemplate, Roo.Template, {
/**
- * Cancels any pending timeout and queues a new one
- * @param {Number} delay The milliseconds to delay
- * @param {Function} newFn (optional) Overrides function passed to constructor
- * @param {Object} newScope (optional) Overrides scope passed to constructor
- * @param {Array} newArgs (optional) Overrides args passed to constructor
+ * id counter for sub templates.
*/
- this.delay = function(delay, newFn, newScope, newArgs){
- if(id && delay != d){
- this.cancel();
+ id : 0,
+ /**
+ * flag to indicate if dom parser is inside a pre,
+ * it will strip whitespace if not.
+ */
+ inPre : false,
+
+ /**
+ * 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 : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
+ //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
+
+ iterChild : function (node, method) {
+
+ var oldPre = this.inPre;
+ if (node.tagName == 'PRE') {
+ this.inPre = true;
}
- d = delay;
- t = new Date().getTime();
- fn = newFn || fn;
- scope = newScope || scope;
- args = newArgs || args;
- if(!id){
- id = setInterval(call, d);
+ for( var i = 0; i < node.childNodes.length; i++) {
+ method.call(this, node.childNodes[i]);
}
- };
-
+ this.inPre = oldPre;
+ },
+
+
+
/**
- * Cancel the last queued timeout
+ * compile the template
+ *
+ * This is not recursive, so I'm not sure how nested templates are really going to be handled..
+ *
*/
- this.cancel = function(){
- if(id){
+ compile: function()
+ {
+ var s = this.html;
+
+ // covert the html into DOM...
+ var doc = false;
+ var div =false;
+ try {
+ doc = document.implementation.createHTMLDocument("");
+ doc.documentElement.innerHTML = this.html ;
+ div = doc.documentElement;
+ } catch (e) {
+ // old IE... - nasty -- it causes all sorts of issues.. with
+ // images getting pulled from server..
+ div = document.createElement('div');
+ div.innerHTML = this.html;
+ }
+ //doc.documentElement.innerHTML = htmlBody
+
+
+
+ this.tpls = [];
+ var _t = this;
+ this.iterChild(div, function(n) {_t.compileNode(n, true); });
+
+ var tpls = this.tpls;
+
+ // create a top level template from the snippet..
+
+ //Roo.log(div.innerHTML);
+
+ var tpl = {
+ uid : 'master',
+ id : this.id++,
+ attr : false,
+ value : false,
+ body : div.innerHTML,
+
+ forCall : false,
+ execCall : false,
+ dom : div,
+ isTop : true
+
+ };
+ tpls.unshift(tpl);
+
+
+ // compile them...
+ this.tpls = [];
+ Roo.each(tpls, function(tp){
+ this.compileTpl(tp);
+ this.tpls[tp.id] = tp;
+ }, this);
+
+ this.master = tpls[0];
+ return this;
+
+
+ },
+
+ compileNode : function(node, istop) {
+ // test for
+ //Roo.log(node);
+
+
+ // skip anything not a tag..
+ if (node.nodeType != 1) {
+ if (node.nodeType == 3 && !this.inPre) {
+ // reduce white space..
+ node.nodeValue = node.nodeValue.replace(/\s+/g, ' ');
+
+ }
+ return;
+ }
+
+ var tpl = {
+ uid : false,
+ id : false,
+ attr : false,
+ value : false,
+ body : '',
+
+ forCall : false,
+ execCall : false,
+ dom : false,
+ isTop : istop
+
+
+ };
+
+
+ switch(true) {
+ case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
+ case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
+ case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
+ case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
+ // no default..
+ }
+
+
+ if (!tpl.attr) {
+ // just itterate children..
+ this.iterChild(node,this.compileNode);
+ return;
+ }
+ tpl.uid = this.id++;
+ tpl.value = node.getAttribute('roo-' + tpl.attr);
+ node.removeAttribute('roo-'+ tpl.attr);
+ if (tpl.attr != 'name') {
+ var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
+ node.parentNode.replaceChild(placeholder, node);
+ } else {
+
+ var placeholder = document.createElement('span');
+ placeholder.className = 'roo-tpl-' + tpl.value;
+ node.parentNode.replaceChild(placeholder, node);
+ }
+
+ // parent now sees '{domtplXXXX}
+ this.iterChild(node,this.compileNode);
+
+ // we should now have node body...
+ var div = document.createElement('div');
+ div.appendChild(node);
+ tpl.dom = node;
+ // this has the unfortunate side effect of converting tagged attributes
+ // eg. href="{...}" into %7C...%7D
+ // this has been fixed by searching for those combo's although it's a bit hacky..
+
+
+ tpl.body = div.innerHTML;
+
+
+
+ tpl.id = tpl.uid;
+ switch(tpl.attr) {
+ case 'for' :
+ switch (tpl.value) {
+ case '.': tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
+ case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
+ default: tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
+ }
+ break;
+
+ case 'exec':
+ tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
+ break;
+
+ case 'if':
+ tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
+ break;
+
+ case 'name':
+ tpl.id = tpl.value; // replace non characters???
+ break;
+
+ }
+
+
+ this.tpls.push(tpl);
+
+
+
+ },
+
+
+
+
+ /**
+ * Compile a segment of the template into a 'sub-template'
+ *
+ *
+ *
+ *
+ */
+ compileTpl : function(tpl)
+ {
+ var fm = Roo.util.Format;
+ var useF = this.disableFormats !== true;
+
+ var sep = Roo.isGecko ? "+\n" : ",\n";
+
+ var undef = function(str) {
+ Roo.debug && Roo.log("Property not found :" + str);
+ return '';
+ };
+
+ //Roo.log(tpl.body);
+
+
+
+ var fn = function(m, lbrace, name, format, args)
+ {
+ //Roo.log("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, 6) == 'domtpl'){
+ return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
+ }
+
+ // 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";
+ }
+
+ return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
+ }
+
+ if (args && 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){ with(values) { return '" +
+ tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
+ "';};};";
+ }else{
+ 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('');
+ }
+
+ Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
+
+ /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
+ eval(body);
+
+ 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];
+
+
+ try {
+ if(t.ifCall && !t.ifCall.call(this, values, parent)){
+ Roo.debug && Roo.log('if call on ' + t.value + ' return false');
+ return '';
+ }
+ } catch(e) {
+ Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
+ Roo.log(values);
+
+ return '';
+ }
+ try {
+
+ if(t.execCall && t.execCall.call(this, values, parent)){
+ return '';
+ }
+ } catch(e) {
+ Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
+ Roo.log(values);
+ return '';
+ }
+
+ try {
+ var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
+ parent = t.target ? values : parent;
+ if(t.forCall && vs instanceof Array){
+ var buf = [];
+ for(var i = 0, len = vs.length; i < len; i++){
+ try {
+ buf[buf.length] = t.compiled.call(this, vs[i], parent);
+ } catch (e) {
+ Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
+ Roo.log(e.body);
+ //Roo.log(t.compiled);
+ Roo.log(vs[i]);
+ }
+ }
+ return buf.join('');
+ }
+ } catch (e) {
+ Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
+ Roo.log(values);
+ return '';
+ }
+ try {
+ return t.compiled.call(this, vs, parent);
+ } catch (e) {
+ Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
+ Roo.log(e.body);
+ //Roo.log(t.compiled);
+ Roo.log(values);
+ return '';
+ }
+ },
+
+
+
+ applyTemplate : function(values){
+ return this.master.compiled.call(this, values, {});
+ //var s = this.subs;
+ },
+
+ apply : function(){
+ return this.applyTemplate.apply(this, arguments);
+ }
+
+ });
+
+Roo.DomTemplate.from = function(el){
+ el = Roo.getDom(el);
+ return new Roo.Domtemplate(el.value || el.innerHTML);
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.util.DelayedTask
+ * Provides a convenient method of performing setTimeout where a new
+ * timeout cancels the old timeout. An example would be performing validation on a keypress.
+ * You can use this class to buffer
+ * the keypress events for a certain number of milliseconds, and perform only if they stop
+ * for that amount of time.
+ * @constructor The parameters to this constructor serve as defaults and are not required.
+ * @param {Function} fn (optional) The default function to timeout
+ * @param {Object} scope (optional) The default scope of that timeout
+ * @param {Array} args (optional) The default Array of arguments
+ */
+Roo.util.DelayedTask = function(fn, scope, args){
+ var id = null, d, t;
+
+ var call = function(){
+ var now = new Date().getTime();
+ if(now - t >= d){
+ clearInterval(id);
+ id = null;
+ fn.apply(scope, args || []);
+ }
+ };
+ /**
+ * Cancels any pending timeout and queues a new one
+ * @param {Number} delay The milliseconds to delay
+ * @param {Function} newFn (optional) Overrides function passed to constructor
+ * @param {Object} newScope (optional) Overrides scope passed to constructor
+ * @param {Array} newArgs (optional) Overrides args passed to constructor
+ */
+ this.delay = function(delay, newFn, newScope, newArgs){
+ if(id && delay != d){
+ this.cancel();
+ }
+ d = delay;
+ t = new Date().getTime();
+ fn = newFn || fn;
+ scope = newScope || scope;
+ args = newArgs || args;
+ if(!id){
+ id = setInterval(call, d);
+ }
+ };
+
+ /**
+ * Cancel the last queued timeout
+ */
+ this.cancel = function(){
+ if(id){
clearInterval(id);
id = null;
}
* @return {String} The formatted currency string
*/
usMoney : function(v){
- v = (Math.round((v-0)*100))/100;
- v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
+ return '$' + Roo.util.Format.number(v);
+ },
+
+ /**
+ * Format a number
+ * eventually this should probably emulate php's number_format
+ * @param {Number/String} value The numeric value to format
+ * @param {Number} decimals number of decimal places
+ * @return {String} The formatted currency string
+ */
+ number : function(v,decimals)
+ {
+ // multiply and round.
+ decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
+ var mul = Math.pow(10, decimals);
+ var zero = String(mul).substring(1);
+ v = (Math.round((v-0)*mul))/mul;
+
+ // if it's '0' number.. then
+
+ //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
v = String(v);
var ps = v.split('.');
var whole = ps[0];
- var sub = ps[1] ? '.'+ ps[1] : '.00';
+
+
var r = /(\d+)(\d{3})/;
+ // add comma's
while (r.test(whole)) {
whole = whole.replace(r, '$1' + ',' + '$2');
}
- return "$" + whole + sub ;
+
+
+ var sub = ps[1] ?
+ // has decimals..
+ (decimals ? ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
+ // does not have decimals
+ (decimals ? ('.' + zero) : '');
+
+
+ return whole + sub ;
},
/**
return false;
}
};
-}();
\ No newline at end of file
+}();/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+
+/**
+ * @class Roo.util.ClickRepeater
+ * @extends Roo.util.Observable
+ *
+ * A wrapper class which can be applied to any element. Fires a "click" event while the
+ * mouse is pressed. The interval between firings may be specified in the config but
+ * defaults to 10 milliseconds.
+ *
+ * Optionally, a CSS class may be applied to the element during the time it is pressed.
+ *
+ * @cfg {String/HTMLElement/Element} el The element to act as a button.
+ * @cfg {Number} delay The initial delay before the repeating event begins firing.
+ * Similar to an autorepeat key delay.
+ * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
+ * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
+ * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
+ * "interval" and "delay" are ignored. "immediate" is honored.
+ * @cfg {Boolean} preventDefault True to prevent the default click event
+ * @cfg {Boolean} stopDefault True to stop the default click event
+ *
+ * @history
+ * 2007-02-02 jvs Original code contributed by Nige "Animal" White
+ * 2007-02-02 jvs Renamed to ClickRepeater
+ * 2007-02-03 jvs Modifications for FF Mac and Safari
+ *
+ * @constructor
+ * @param {String/HTMLElement/Element} el The element to listen on
+ * @param {Object} config
+ **/
+Roo.util.ClickRepeater = function(el, config)
+{
+ this.el = Roo.get(el);
+ this.el.unselectable();
+
+ Roo.apply(this, config);
+
+ this.addEvents({
+ /**
+ * @event mousedown
+ * Fires when the mouse button is depressed.
+ * @param {Roo.util.ClickRepeater} this
+ */
+ "mousedown" : true,
+ /**
+ * @event click
+ * Fires on a specified interval during the time the element is pressed.
+ * @param {Roo.util.ClickRepeater} this
+ */
+ "click" : true,
+ /**
+ * @event mouseup
+ * Fires when the mouse key is released.
+ * @param {Roo.util.ClickRepeater} this
+ */
+ "mouseup" : true
+ });
+
+ this.el.on("mousedown", this.handleMouseDown, this);
+ if(this.preventDefault || this.stopDefault){
+ this.el.on("click", function(e){
+ if(this.preventDefault){
+ e.preventDefault();
+ }
+ if(this.stopDefault){
+ e.stopEvent();
+ }
+ }, this);
+ }
+
+ // allow inline handler
+ if(this.handler){
+ this.on("click", this.handler, this.scope || this);
+ }
+
+ Roo.util.ClickRepeater.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
+ interval : 20,
+ delay: 250,
+ preventDefault : true,
+ stopDefault : false,
+ timer : 0,
+
+ // private
+ handleMouseDown : function(){
+ clearTimeout(this.timer);
+ this.el.blur();
+ if(this.pressClass){
+ this.el.addClass(this.pressClass);
+ }
+ this.mousedownTime = new Date();
+
+ Roo.get(document).on("mouseup", this.handleMouseUp, this);
+ this.el.on("mouseout", this.handleMouseOut, this);
+
+ this.fireEvent("mousedown", this);
+ this.fireEvent("click", this);
+
+ this.timer = this.click.defer(this.delay || this.interval, this);
+ },
+
+ // private
+ click : function(){
+ this.fireEvent("click", this);
+ this.timer = this.click.defer(this.getInterval(), this);
+ },
+
+ // private
+ getInterval: function(){
+ if(!this.accelerate){
+ return this.interval;
+ }
+ var pressTime = this.mousedownTime.getElapsed();
+ if(pressTime < 500){
+ return 400;
+ }else if(pressTime < 1700){
+ return 320;
+ }else if(pressTime < 2600){
+ return 250;
+ }else if(pressTime < 3500){
+ return 180;
+ }else if(pressTime < 4400){
+ return 140;
+ }else if(pressTime < 5300){
+ return 80;
+ }else if(pressTime < 6200){
+ return 50;
+ }else{
+ return 10;
+ }
+ },
+
+ // private
+ handleMouseOut : function(){
+ clearTimeout(this.timer);
+ if(this.pressClass){
+ this.el.removeClass(this.pressClass);
+ }
+ this.el.on("mouseover", this.handleMouseReturn, this);
+ },
+
+ // private
+ handleMouseReturn : function(){
+ this.el.un("mouseover", this.handleMouseReturn);
+ if(this.pressClass){
+ this.el.addClass(this.pressClass);
+ }
+ this.click();
+ },
+
+ // private
+ handleMouseUp : function(){
+ clearTimeout(this.timer);
+ this.el.un("mouseover", this.handleMouseReturn);
+ this.el.un("mouseout", this.handleMouseOut);
+ Roo.get(document).un("mouseup", this.handleMouseUp);
+ this.el.removeClass(this.pressClass);
+ this.fireEvent("mouseup", this);
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+/**
+ * @class Roo.KeyNav
+ * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
+ * navigation keys to function calls that will get called when the keys are pressed, providing an easy
+ * way to implement custom navigation schemes for any UI component.</p>
+ * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
+ * pageUp, pageDown, del, home, end. Usage:</p>
+ <pre><code>
+var nav = new Roo.KeyNav("my-element", {
+ "left" : function(e){
+ this.moveLeft(e.ctrlKey);
+ },
+ "right" : function(e){
+ this.moveRight(e.ctrlKey);
+ },
+ "enter" : function(e){
+ this.save();
+ },
+ scope : this
+});
+</code></pre>
+ * @constructor
+ * @param {String/HTMLElement/Roo.Element} el The element to bind to
+ * @param {Object} config The config
+ */
+Roo.KeyNav = function(el, config){
+ this.el = Roo.get(el);
+ Roo.apply(this, config);
+ if(!this.disabled){
+ this.disabled = true;
+ this.enable();
+ }
+};
+
+Roo.KeyNav.prototype = {
+ /**
+ * @cfg {Boolean} disabled
+ * True to disable this KeyNav instance (defaults to false)
+ */
+ disabled : false,
+ /**
+ * @cfg {String} defaultEventAction
+ * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key. Valid values are
+ * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
+ * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
+ */
+ defaultEventAction: "stopEvent",
+ /**
+ * @cfg {Boolean} forceKeyDown
+ * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
+ * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
+ * handle keydown instead of keypress.
+ */
+ forceKeyDown : false,
+
+ // private
+ prepareEvent : function(e){
+ var k = e.getKey();
+ var h = this.keyToHandler[k];
+ //if(h && this[h]){
+ // e.stopPropagation();
+ //}
+ if(Roo.isSafari && h && k >= 37 && k <= 40){
+ e.stopEvent();
+ }
+ },
+
+ // private
+ relay : function(e){
+ var k = e.getKey();
+ var h = this.keyToHandler[k];
+ if(h && this[h]){
+ if(this.doRelay(e, this[h], h) !== true){
+ e[this.defaultEventAction]();
+ }
+ }
+ },
+
+ // private
+ doRelay : function(e, h, hname){
+ return h.call(this.scope || this, e);
+ },
+
+ // possible handlers
+ enter : false,
+ left : false,
+ right : false,
+ up : false,
+ down : false,
+ tab : false,
+ esc : false,
+ pageUp : false,
+ pageDown : false,
+ del : false,
+ home : false,
+ end : false,
+
+ // quick lookup hash
+ keyToHandler : {
+ 37 : "left",
+ 39 : "right",
+ 38 : "up",
+ 40 : "down",
+ 33 : "pageUp",
+ 34 : "pageDown",
+ 46 : "del",
+ 36 : "home",
+ 35 : "end",
+ 13 : "enter",
+ 27 : "esc",
+ 9 : "tab"
+ },
+
+ /**
+ * Enable this KeyNav
+ */
+ enable: function(){
+ if(this.disabled){
+ // ie won't do special keys on keypress, no one else will repeat keys with keydown
+ // the EventObject will normalize Safari automatically
+ if(this.forceKeyDown || Roo.isIE || Roo.isAir){
+ this.el.on("keydown", this.relay, this);
+ }else{
+ this.el.on("keydown", this.prepareEvent, this);
+ this.el.on("keypress", this.relay, this);
+ }
+ this.disabled = false;
+ }
+ },
+
+ /**
+ * Disable this KeyNav
+ */
+ disable: function(){
+ if(!this.disabled){
+ if(this.forceKeyDown || Roo.isIE || Roo.isAir){
+ this.el.un("keydown", this.relay);
+ }else{
+ this.el.un("keydown", this.prepareEvent);
+ this.el.un("keypress", this.relay);
+ }
+ this.disabled = true;
+ }
+ }
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+/**
+ * @class Roo.KeyMap
+ * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
+ * The constructor accepts the same config object as defined by {@link #addBinding}.
+ * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
+ * combination it will call the function with this signature (if the match is a multi-key
+ * combination the callback will still be called only once): (String key, Roo.EventObject e)
+ * A KeyMap can also handle a string representation of keys.<br />
+ * Usage:
+ <pre><code>
+// map one key by key code
+var map = new Roo.KeyMap("my-element", {
+ key: 13, // or Roo.EventObject.ENTER
+ fn: myHandler,
+ scope: myObject
+});
+
+// map multiple keys to one action by string
+var map = new Roo.KeyMap("my-element", {
+ key: "a\r\n\t",
+ fn: myHandler,
+ scope: myObject
+});
+
+// map multiple keys to multiple actions by strings and array of codes
+var map = new Roo.KeyMap("my-element", [
+ {
+ key: [10,13],
+ fn: function(){ alert("Return was pressed"); }
+ }, {
+ key: "abc",
+ fn: function(){ alert('a, b or c was pressed'); }
+ }, {
+ key: "\t",
+ ctrl:true,
+ shift:true,
+ fn: function(){ alert('Control + shift + tab was pressed.'); }
+ }
+]);
+</code></pre>
+ * <b>Note: A KeyMap starts enabled</b>
+ * @constructor
+ * @param {String/HTMLElement/Roo.Element} el The element to bind to
+ * @param {Object} config The config (see {@link #addBinding})
+ * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
+ */
+Roo.KeyMap = function(el, config, eventName){
+ this.el = Roo.get(el);
+ this.eventName = eventName || "keydown";
+ this.bindings = [];
+ if(config){
+ this.addBinding(config);
+ }
+ this.enable();
+};
+
+Roo.KeyMap.prototype = {
+ /**
+ * True to stop the event from bubbling and prevent the default browser action if the
+ * key was handled by the KeyMap (defaults to false)
+ * @type Boolean
+ */
+ stopEvent : false,
+
+ /**
+ * Add a new binding to this KeyMap. The following config object properties are supported:
+ * <pre>
+Property Type Description
+---------- --------------- ----------------------------------------------------------------------
+key String/Array A single keycode or an array of keycodes to handle
+shift Boolean True to handle key only when shift is pressed (defaults to false)
+ctrl Boolean True to handle key only when ctrl is pressed (defaults to false)
+alt Boolean True to handle key only when alt is pressed (defaults to false)
+fn Function The function to call when KeyMap finds the expected key combination
+scope Object The scope of the callback function
+</pre>
+ *
+ * Usage:
+ * <pre><code>
+// Create a KeyMap
+var map = new Roo.KeyMap(document, {
+ key: Roo.EventObject.ENTER,
+ fn: handleKey,
+ scope: this
+});
+
+//Add a new binding to the existing KeyMap later
+map.addBinding({
+ key: 'abc',
+ shift: true,
+ fn: handleKey,
+ scope: this
+});
+</code></pre>
+ * @param {Object/Array} config A single KeyMap config or an array of configs
+ */
+ addBinding : function(config){
+ if(config instanceof Array){
+ for(var i = 0, len = config.length; i < len; i++){
+ this.addBinding(config[i]);
+ }
+ return;
+ }
+ var keyCode = config.key,
+ shift = config.shift,
+ ctrl = config.ctrl,
+ alt = config.alt,
+ fn = config.fn,
+ scope = config.scope;
+ if(typeof keyCode == "string"){
+ var ks = [];
+ var keyString = keyCode.toUpperCase();
+ for(var j = 0, len = keyString.length; j < len; j++){
+ ks.push(keyString.charCodeAt(j));
+ }
+ keyCode = ks;
+ }
+ var keyArray = keyCode instanceof Array;
+ var handler = function(e){
+ if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
+ var k = e.getKey();
+ if(keyArray){
+ for(var i = 0, len = keyCode.length; i < len; i++){
+ if(keyCode[i] == k){
+ if(this.stopEvent){
+ e.stopEvent();
+ }
+ fn.call(scope || window, k, e);
+ return;
+ }
+ }
+ }else{
+ if(k == keyCode){
+ if(this.stopEvent){
+ e.stopEvent();
+ }
+ fn.call(scope || window, k, e);
+ }
+ }
+ }
+ };
+ this.bindings.push(handler);
+ },
+
+ /**
+ * Shorthand for adding a single key listener
+ * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
+ * following options:
+ * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
+ * @param {Function} fn The function to call
+ * @param {Object} scope (optional) The scope of the function
+ */
+ on : function(key, fn, scope){
+ var keyCode, shift, ctrl, alt;
+ if(typeof key == "object" && !(key instanceof Array)){
+ keyCode = key.key;
+ shift = key.shift;
+ ctrl = key.ctrl;
+ alt = key.alt;
+ }else{
+ keyCode = key;
+ }
+ this.addBinding({
+ key: keyCode,
+ shift: shift,
+ ctrl: ctrl,
+ alt: alt,
+ fn: fn,
+ scope: scope
+ })
+ },
+
+ // private
+ handleKeyDown : function(e){
+ if(this.enabled){ //just in case
+ var b = this.bindings;
+ for(var i = 0, len = b.length; i < len; i++){
+ b[i].call(this, e);
+ }
+ }
+ },
+
+ /**
+ * Returns true if this KeyMap is enabled
+ * @return {Boolean}
+ */
+ isEnabled : function(){
+ return this.enabled;
+ },
+
+ /**
+ * Enables this KeyMap
+ */
+ enable: function(){
+ if(!this.enabled){
+ this.el.on(this.eventName, this.handleKeyDown, this);
+ this.enabled = true;
+ }
+ },
+
+ /**
+ * Disable this KeyMap
+ */
+ disable: function(){
+ if(this.enabled){
+ this.el.removeListener(this.eventName, this.handleKeyDown, this);
+ this.enabled = false;
+ }
+ }
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+/**
+ * @class Roo.util.TextMetrics
+ * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
+ * wide, in pixels, a given block of text will be.
+ * @singleton
+ */
+Roo.util.TextMetrics = function(){
+ var shared;
+ return {
+ /**
+ * Measures the size of the specified text
+ * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
+ * that can affect the size of the rendered text
+ * @param {String} text The text to measure
+ * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
+ * in order to accurately measure the text height
+ * @return {Object} An object containing the text's size {width: (width), height: (height)}
+ */
+ measure : function(el, text, fixedWidth){
+ if(!shared){
+ shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
+ }
+ shared.bind(el);
+ shared.setFixedWidth(fixedWidth || 'auto');
+ return shared.getSize(text);
+ },
+
+ /**
+ * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
+ * the overhead of multiple calls to initialize the style properties on each measurement.
+ * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
+ * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
+ * in order to accurately measure the text height
+ * @return {Roo.util.TextMetrics.Instance} instance The new instance
+ */
+ createInstance : function(el, fixedWidth){
+ return Roo.util.TextMetrics.Instance(el, fixedWidth);
+ }
+ };
+}();
+
+
+
+Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
+ var ml = new Roo.Element(document.createElement('div'));
+ document.body.appendChild(ml.dom);
+ ml.position('absolute');
+ ml.setLeftTop(-1000, -1000);
+ ml.hide();
+
+ if(fixedWidth){
+ ml.setWidth(fixedWidth);
+ }
+
+ var instance = {
+ /**
+ * Returns the size of the specified text based on the internal element's style and width properties
+ * @memberOf Roo.util.TextMetrics.Instance#
+ * @param {String} text The text to measure
+ * @return {Object} An object containing the text's size {width: (width), height: (height)}
+ */
+ getSize : function(text){
+ ml.update(text);
+ var s = ml.getSize();
+ ml.update('');
+ return s;
+ },
+
+ /**
+ * Binds this TextMetrics instance to an element from which to copy existing CSS styles
+ * that can affect the size of the rendered text
+ * @memberOf Roo.util.TextMetrics.Instance#
+ * @param {String/HTMLElement} el The element, dom node or id
+ */
+ bind : function(el){
+ ml.setStyle(
+ Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
+ );
+ },
+
+ /**
+ * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
+ * to set a fixed width in order to accurately measure the text height.
+ * @memberOf Roo.util.TextMetrics.Instance#
+ * @param {Number} width The width to set on the element
+ */
+ setFixedWidth : function(width){
+ ml.setWidth(width);
+ },
+
+ /**
+ * Returns the measured width of the specified text
+ * @memberOf Roo.util.TextMetrics.Instance#
+ * @param {String} text The text to measure
+ * @return {Number} width The width in pixels
+ */
+ getWidth : function(text){
+ ml.dom.style.width = 'auto';
+ return this.getSize(text).width;
+ },
+
+ /**
+ * Returns the measured height of the specified text. For multiline text, be sure to call
+ * {@link #setFixedWidth} if necessary.
+ * @memberOf Roo.util.TextMetrics.Instance#
+ * @param {String} text The text to measure
+ * @return {Number} height The height in pixels
+ */
+ getHeight : function(text){
+ return this.getSize(text).height;
+ }
+ };
+
+ instance.bind(bindTo);
+
+ return instance;
+};
+
+// backwards compat
+Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.state.Provider
+ * Abstract base class for state provider implementations. This class provides methods
+ * for encoding and decoding <b>typed</b> variables including dates and defines the
+ * Provider interface.
+ */
+Roo.state.Provider = function(){
+ /**
+ * @event statechange
+ * Fires when a state change occurs.
+ * @param {Provider} this This state provider
+ * @param {String} key The state key which was changed
+ * @param {String} value The encoded value for the state
+ */
+ this.addEvents({
+ "statechange": true
+ });
+ this.state = {};
+ Roo.state.Provider.superclass.constructor.call(this);
+};
+Roo.extend(Roo.state.Provider, Roo.util.Observable, {
+ /**
+ * Returns the current value for a key
+ * @param {String} name The key name
+ * @param {Mixed} defaultValue A default value to return if the key's value is not found
+ * @return {Mixed} The state data
+ */
+ get : function(name, defaultValue){
+ return typeof this.state[name] == "undefined" ?
+ defaultValue : this.state[name];
+ },
+
+ /**
+ * Clears a value from the state
+ * @param {String} name The key name
+ */
+ clear : function(name){
+ delete this.state[name];
+ this.fireEvent("statechange", this, name, null);
+ },
+
+ /**
+ * Sets the value for a key
+ * @param {String} name The key name
+ * @param {Mixed} value The value to set
+ */
+ set : function(name, value){
+ this.state[name] = value;
+ this.fireEvent("statechange", this, name, value);
+ },
+
+ /**
+ * Decodes a string previously encoded with {@link #encodeValue}.
+ * @param {String} value The value to decode
+ * @return {Mixed} The decoded value
+ */
+ decodeValue : function(cookie){
+ var re = /^(a|n|d|b|s|o)\:(.*)$/;
+ var matches = re.exec(unescape(cookie));
+ if(!matches || !matches[1]) return; // non state cookie
+ var type = matches[1];
+ var v = matches[2];
+ switch(type){
+ case "n":
+ return parseFloat(v);
+ case "d":
+ return new Date(Date.parse(v));
+ case "b":
+ return (v == "1");
+ case "a":
+ var all = [];
+ var values = v.split("^");
+ for(var i = 0, len = values.length; i < len; i++){
+ all.push(this.decodeValue(values[i]));
+ }
+ return all;
+ case "o":
+ var all = {};
+ var values = v.split("^");
+ for(var i = 0, len = values.length; i < len; i++){
+ var kv = values[i].split("=");
+ all[kv[0]] = this.decodeValue(kv[1]);
+ }
+ return all;
+ default:
+ return v;
+ }
+ },
+
+ /**
+ * Encodes a value including type information. Decode with {@link #decodeValue}.
+ * @param {Mixed} value The value to encode
+ * @return {String} The encoded value
+ */
+ encodeValue : function(v){
+ var enc;
+ if(typeof v == "number"){
+ enc = "n:" + v;
+ }else if(typeof v == "boolean"){
+ enc = "b:" + (v ? "1" : "0");
+ }else if(v instanceof Date){
+ enc = "d:" + v.toGMTString();
+ }else if(v instanceof Array){
+ var flat = "";
+ for(var i = 0, len = v.length; i < len; i++){
+ flat += this.encodeValue(v[i]);
+ if(i != len-1) flat += "^";
+ }
+ enc = "a:" + flat;
+ }else if(typeof v == "object"){
+ var flat = "";
+ for(var key in v){
+ if(typeof v[key] != "function"){
+ flat += key + "=" + this.encodeValue(v[key]) + "^";
+ }
+ }
+ enc = "o:" + flat.substring(0, flat.length-1);
+ }else{
+ enc = "s:" + v;
+ }
+ return escape(enc);
+ }
+});
+
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.state.Manager
+ * This is the global state manager. By default all components that are "state aware" check this class
+ * for state information if you don't pass them a custom state provider. In order for this class
+ * to be useful, it must be initialized with a provider when your application initializes.
+ <pre><code>
+// in your initialization function
+init : function(){
+ Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
+ ...
+ // supposed you have a {@link Roo.BorderLayout}
+ var layout = new Roo.BorderLayout(...);
+ layout.restoreState();
+ // or a {Roo.BasicDialog}
+ var dialog = new Roo.BasicDialog(...);
+ dialog.restoreState();
+ </code></pre>
+ * @singleton
+ */
+Roo.state.Manager = function(){
+ var provider = new Roo.state.Provider();
+
+ return {
+ /**
+ * Configures the default state provider for your application
+ * @param {Provider} stateProvider The state provider to set
+ */
+ setProvider : function(stateProvider){
+ provider = stateProvider;
+ },
+
+ /**
+ * Returns the current value for a key
+ * @param {String} name The key name
+ * @param {Mixed} defaultValue The default value to return if the key lookup does not match
+ * @return {Mixed} The state data
+ */
+ get : function(key, defaultValue){
+ return provider.get(key, defaultValue);
+ },
+
+ /**
+ * Sets the value for a key
+ * @param {String} name The key name
+ * @param {Mixed} value The state data
+ */
+ set : function(key, value){
+ provider.set(key, value);
+ },
+
+ /**
+ * Clears a value from the state
+ * @param {String} name The key name
+ */
+ clear : function(key){
+ provider.clear(key);
+ },
+
+ /**
+ * Gets the currently configured state provider
+ * @return {Provider} The state provider
+ */
+ getProvider : function(){
+ return provider;
+ }
+ };
+}();
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.state.CookieProvider
+ * @extends Roo.state.Provider
+ * The default Provider implementation which saves state via cookies.
+ * <br />Usage:
+ <pre><code>
+ var cp = new Roo.state.CookieProvider({
+ path: "/cgi-bin/",
+ expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
+ domain: "roojs.com"
+ })
+ Roo.state.Manager.setProvider(cp);
+ </code></pre>
+ * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
+ * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
+ * @cfg {String} domain The domain to save the cookie for. Note that you cannot specify a different domain than
+ * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
+ * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
+ * domain the page is running on including the 'www' like 'www.roojs.com')
+ * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
+ * @constructor
+ * Create a new CookieProvider
+ * @param {Object} config The configuration object
+ */
+Roo.state.CookieProvider = function(config){
+ Roo.state.CookieProvider.superclass.constructor.call(this);
+ this.path = "/";
+ this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
+ this.domain = null;
+ this.secure = false;
+ Roo.apply(this, config);
+ this.state = this.readCookies();
+};
+
+Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
+ // private
+ set : function(name, value){
+ if(typeof value == "undefined" || value === null){
+ this.clear(name);
+ return;
+ }
+ this.setCookie(name, value);
+ Roo.state.CookieProvider.superclass.set.call(this, name, value);
+ },
+
+ // private
+ clear : function(name){
+ this.clearCookie(name);
+ Roo.state.CookieProvider.superclass.clear.call(this, name);
+ },
+
+ // private
+ readCookies : function(){
+ var cookies = {};
+ var c = document.cookie + ";";
+ var re = /\s?(.*?)=(.*?);/g;
+ var matches;
+ while((matches = re.exec(c)) != null){
+ var name = matches[1];
+ var value = matches[2];
+ if(name && name.substring(0,3) == "ys-"){
+ cookies[name.substr(3)] = this.decodeValue(value);
+ }
+ }
+ return cookies;
+ },
+
+ // private
+ setCookie : function(name, value){
+ document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
+ ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
+ ((this.path == null) ? "" : ("; path=" + this.path)) +
+ ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
+ ((this.secure == true) ? "; secure" : "");
+ },
+
+ // private
+ clearCookie : function(name){
+ document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
+ ((this.path == null) ? "" : ("; path=" + this.path)) +
+ ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
+ ((this.secure == true) ? "; secure" : "");
+ }
+});
\ No newline at end of file