4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
17 window["undefined"] = window["undefined"];
21 * Roo core utilities and functions.
26 * Copies all the properties of config to obj.
27 * @param {Object} obj The receiver of the properties
28 * @param {Object} config The source of the properties
29 * @param {Object} defaults A different object that will also be applied for default values
30 * @return {Object} returns obj
35 Roo.apply = function(o, c, defaults){
37 // no "this" reference for friendly out of scope calls
38 Roo.apply(o, defaults);
40 if(o && c && typeof c == 'object'){
51 var ua = navigator.userAgent.toLowerCase();
53 var isStrict = document.compatMode == "CSS1Compat",
54 isOpera = ua.indexOf("opera") > -1,
55 isSafari = (/webkit|khtml/).test(ua),
56 isFirefox = ua.indexOf("firefox") > -1,
57 isIE = ua.indexOf("msie") > -1,
58 isIE7 = ua.indexOf("msie 7") > -1,
59 isIE11 = /trident.*rv\:11\./.test(ua),
60 isEdge = ua.indexOf("edge") > -1,
61 isGecko = !isSafari && ua.indexOf("gecko") > -1,
62 isBorderBox = isIE && !isStrict,
63 isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64 isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65 isLinux = (ua.indexOf("linux") != -1),
66 isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67 isIOS = /iphone|ipad/.test(ua),
68 isAndroid = /android/.test(ua),
69 isTouch = (function() {
71 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72 window.addEventListener('touchstart', function __set_has_touch__ () {
74 window.removeEventListener('touchstart', __set_has_touch__);
76 return false; // no touch on chrome!?
78 document.createEvent("TouchEvent");
85 // remove css image flicker
88 document.execCommand("BackgroundImageCache", false, true);
94 * True if the browser is in strict mode
99 * True if the page is running over SSL
104 * True when the document is fully initialized and ready for action
109 * Turn on debugging output (currently only the factory uses this)
116 * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
119 enableGarbageCollector : true,
122 * True to automatically purge event listeners after uncaching an element (defaults to false).
123 * Note: this only happens if enableGarbageCollector is true.
126 enableListenerCollection:false,
129 * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130 * the IE insecure content warning (defaults to javascript:false).
133 SSL_SECURE_URL : "javascript:false",
136 * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137 * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
140 BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
142 emptyFn : function(){},
145 * Copies all the properties of config to obj if they don't already exist.
146 * @param {Object} obj The receiver of the properties
147 * @param {Object} config The source of the properties
148 * @return {Object} returns obj
150 applyIf : function(o, c){
153 if(typeof o[p] == "undefined"){ o[p] = c[p]; }
160 * Applies event listeners to elements by selectors when the document is ready.
161 * The event name is specified with an @ suffix.
164 // add a listener for click on all anchors in element with id foo
165 '#foo a@click' : function(e, t){
169 // add the same listener to multiple selectors (separated by comma BEFORE the @)
170 '#foo a, #bar span.some-class@mouseover' : function(){
175 * @param {Object} obj The list of behaviors to apply
177 addBehaviors : function(o){
179 Roo.onReady(function(){
184 var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
186 var parts = b.split('@');
187 if(parts[1]){ // for Object prototype breakers
190 cache[s] = Roo.select(s);
192 cache[s].on(parts[1], o[b]);
199 * Generates unique ids. If the element already has an id, it is unchanged
200 * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201 * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202 * @return {String} The generated Id.
204 id : function(el, prefix){
205 prefix = prefix || "roo-gen";
207 var id = prefix + (++idSeed);
208 return el ? (el.id ? el.id : (el.id = id)) : id;
213 * Extends one class with another class and optionally overrides members with the passed literal. This class
214 * also adds the function "override()" to the class that can be used to override
215 * members on an instance.
216 * @param {Object} subclass The class inheriting the functionality
217 * @param {Object} superclass The class being extended
218 * @param {Object} overrides (optional) A literal with members
223 var io = function(o){
228 return function(sb, sp, overrides){
229 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
232 sb = function(){sp.apply(this, arguments);};
234 var F = function(){}, sbp, spp = sp.prototype;
236 sbp = sb.prototype = new F();
240 if(spp.constructor == Object.prototype.constructor){
245 sb.override = function(o){
249 Roo.override(sb, overrides);
255 * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
257 Roo.override(MyClass, {
258 newMethod1: function(){
261 newMethod2: function(foo){
266 * @param {Object} origclass The class to override
267 * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
268 * containing one or more methods.
271 override : function(origclass, overrides){
273 var p = origclass.prototype;
274 for(var method in overrides){
275 p[method] = overrides[method];
280 * Creates namespaces to be used for scoping variables and classes so that they are not global. Usage:
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
286 * @param {String} namespace1
287 * @param {String} namespace2
288 * @param {String} etc
291 namespace : function(){
292 var a=arguments, o=null, i, j, d, rt;
293 for (i=0; i<a.length; ++i) {
297 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298 for (j=1; j<d.length; ++j) {
299 o[d[j]]=o[d[j]] || {};
305 * Creates namespaces to be used for scoping variables and classes so that they are not global. Usage:
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
310 * @param {String} classname
311 * @param {String} namespace (optional)
315 factory : function(c, ns)
317 // no xtype, no ns or c.xns - or forced off by c.xns
318 if (!c.xtype || (!ns && !c.xns) || (c.xns === false)) { // not enough info...
321 ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322 if (c.constructor == ns[c.xtype]) {// already created...
326 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327 var ret = new ns[c.xtype](c);
331 c.xns = false; // prevent recursion..
335 * Logs to console if it can.
337 * @param {String|Object} string
342 if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
349 * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2". Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
353 urlEncode : function(o){
359 var ov = o[key], k = Roo.encodeURIComponent(key);
360 var type = typeof ov;
361 if(type == 'undefined'){
363 }else if(type != "function" && type != "object"){
364 buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365 }else if(ov instanceof Array){
367 for(var i = 0, len = ov.length; i < len; i++) {
368 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
379 * Safe version of encodeURIComponent
380 * @param {String} data
384 encodeURIComponent : function (data)
387 return encodeURIComponent(data);
388 } catch(e) {} // should be an uri encode error.
390 if (data == '' || data == null){
393 // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394 function nibble_to_hex(nibble){
395 var chars = '0123456789ABCDEF';
396 return chars.charAt(nibble);
398 data = data.toString();
400 for(var i=0; i<data.length; i++){
401 var c = data.charCodeAt(i);
402 var bs = new Array();
405 bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406 bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407 bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408 bs[3] = 0x80 | (c & 0x3F);
409 }else if (c > 0x800){
411 bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412 bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413 bs[2] = 0x80 | (c & 0x3F);
416 bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417 bs[1] = 0x80 | (c & 0x3F);
422 for(var j=0; j<bs.length; j++){
424 var hex = nibble_to_hex((b & 0xF0) >>> 4)
425 + nibble_to_hex(b &0x0F);
434 * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435 * @param {String} string
436 * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437 * @return {Object} A literal with members
439 urlDecode : function(string, overwrite){
440 if(!string || !string.length){
444 var pairs = string.split('&');
445 var pair, name, value;
446 for(var i = 0, len = pairs.length; i < len; i++){
447 pair = pairs[i].split('=');
448 name = decodeURIComponent(pair[0]);
449 value = decodeURIComponent(pair[1]);
450 if(overwrite !== true){
451 if(typeof obj[name] == "undefined"){
453 }else if(typeof obj[name] == "string"){
454 obj[name] = [obj[name]];
455 obj[name].push(value);
457 obj[name].push(value);
467 * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468 * passed array is not really an array, your function is called once with it.
469 * The supplied function is called with (Object item, Number index, Array allItems).
470 * @param {Array/NodeList/Mixed} array
471 * @param {Function} fn
472 * @param {Object} scope
474 each : function(array, fn, scope){
475 if(typeof array.length == "undefined" || typeof array == "string"){
478 for(var i = 0, len = array.length; i < len; i++){
479 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
484 combine : function(){
485 var as = arguments, l = as.length, r = [];
486 for(var i = 0; i < l; i++){
488 if(a instanceof Array){
490 }else if(a.length !== undefined && !a.substr){
491 r = r.concat(Array.prototype.slice.call(a, 0));
500 * Escapes the passed string for use in a regular expression
501 * @param {String} str
504 escapeRe : function(s) {
505 return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
509 callback : function(cb, scope, args, delay){
510 if(typeof cb == "function"){
512 cb.defer(delay, scope, args || []);
514 cb.apply(scope, args || []);
520 * Return the dom node for the passed string (id), dom node, or Roo.Element
521 * @param {String/HTMLElement/Roo.Element} el
522 * @return HTMLElement
524 getDom : function(el){
528 return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
532 * Shorthand for {@link Roo.ComponentMgr#get}
534 * @return Roo.Component
536 getCmp : function(id){
537 return Roo.ComponentMgr.get(id);
540 num : function(v, defaultValue){
541 if(typeof v != 'number'){
547 destroy : function(){
548 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
552 as.removeAllListeners();
556 if(typeof as.purgeListeners == 'function'){
559 if(typeof as.destroy == 'function'){
566 // inpired by a similar function in mootools library
568 * Returns the type of object that is passed in. If the object passed in is null or undefined it
569 * return false otherwise it returns one of the following values:<ul>
570 * <li><b>string</b>: If the object passed is a string</li>
571 * <li><b>number</b>: If the object passed is a number</li>
572 * <li><b>boolean</b>: If the object passed is a boolean value</li>
573 * <li><b>function</b>: If the object passed is a function reference</li>
574 * <li><b>object</b>: If the object passed is an object</li>
575 * <li><b>array</b>: If the object passed is an array</li>
576 * <li><b>regexp</b>: If the object passed is a regular expression</li>
577 * <li><b>element</b>: If the object passed is a DOM Element</li>
578 * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579 * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580 * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581 * @param {Mixed} object
585 if(o === undefined || o === null){
592 if(t == 'object' && o.nodeName) {
594 case 1: return 'element';
595 case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
598 if(t == 'object' || t == 'function') {
599 switch(o.constructor) {
600 case Array: return 'array';
601 case RegExp: return 'regexp';
603 if(typeof o.length == 'number' && typeof o.item == 'function') {
611 * Returns true if the passed value is null, undefined or an empty string (optional).
612 * @param {Mixed} value The value to test
613 * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
616 isEmpty : function(v, allowBlank){
617 return v === null || v === undefined || (!allowBlank ? v === '' : false);
625 isFirefox : isFirefox,
637 isBorderBox : isBorderBox,
639 isWindows : isWindows,
647 isAndroid : isAndroid,
652 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653 * you may want to set this to true.
656 useShims : ((isIE && !isIE7) || (isGecko && isMac)),
661 * Selects a single element as a Roo Element
662 * This is about as close as you can get to jQuery's $('do crazy stuff')
663 * @param {String} selector The selector/xpath query
664 * @param {Node} root (optional) The start of the query (defaults to document).
665 * @return {Roo.Element}
667 selectNode : function(selector, root)
669 var node = Roo.DomQuery.selectNode(selector,root);
670 return node ? Roo.get(node) : new Roo.Element(false);
673 * Find the current bootstrap width Grid size
674 * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675 * @returns {String} (xs|sm|md|lg|xl)
678 getGridSize : function()
680 var w = Roo.lib.Dom.getViewWidth();
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
707 * Ext JS Library 1.1.1
708 * Copyright(c) 2006-2007, Ext JS, LLC.
710 * Originally Released Under LGPL - original licence link has changed is not relivant.
713 * <script type="text/javascript">
717 // wrappedn so fnCleanup is not in global scope...
719 function fnCleanUp() {
720 var p = Function.prototype;
721 delete p.createSequence;
723 delete p.createDelegate;
724 delete p.createCallback;
725 delete p.createInterceptor;
727 window.detachEvent("onunload", fnCleanUp);
729 window.attachEvent("onunload", fnCleanUp);
736 * These functions are available on every Function object (any JavaScript function).
738 Roo.apply(Function.prototype, {
740 * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741 * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742 * Will create a function that is bound to those 2 args.
743 * @return {Function} The new function
745 createCallback : function(/*args...*/){
746 // make args available, in function below
747 var args = arguments;
750 return method.apply(window, args);
755 * Creates a delegate (callback) that sets the scope to obj.
756 * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757 * Will create a function that is automatically scoped to this.
758 * @param {Object} obj (optional) The object for which the scope is set
759 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761 * if a number the args are inserted at the specified position
762 * @return {Function} The new function
764 createDelegate : function(obj, args, appendArgs){
767 var callArgs = args || arguments;
768 if(appendArgs === true){
769 callArgs = Array.prototype.slice.call(arguments, 0);
770 callArgs = callArgs.concat(args);
771 }else if(typeof appendArgs == "number"){
772 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776 return method.apply(obj || window, callArgs);
781 * Calls this function after the number of millseconds specified.
782 * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783 * @param {Object} obj (optional) The object for which the scope is set
784 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786 * if a number the args are inserted at the specified position
787 * @return {Number} The timeout id that can be used with clearTimeout
789 defer : function(millis, obj, args, appendArgs){
790 var fn = this.createDelegate(obj, args, appendArgs);
792 return setTimeout(fn, millis);
798 * Create a combined function call sequence of the original function + the passed function.
799 * The resulting function returns the results of the original function.
800 * The passed fcn is called with the parameters of the original function
801 * @param {Function} fcn The function to sequence
802 * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803 * @return {Function} The new function
805 createSequence : function(fcn, scope){
806 if(typeof fcn != "function"){
811 var retval = method.apply(this || window, arguments);
812 fcn.apply(scope || this || window, arguments);
818 * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819 * The resulting function returns the results of the original function.
820 * The passed fcn is called with the parameters of the original function.
822 * @param {Function} fcn The function to call before the original
823 * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824 * @return {Function} The new function
826 createInterceptor : function(fcn, scope){
827 if(typeof fcn != "function"){
834 if(fcn.apply(scope || this || window, arguments) === false){
837 return method.apply(this || window, arguments);
843 * Ext JS Library 1.1.1
844 * Copyright(c) 2006-2007, Ext JS, LLC.
846 * Originally Released Under LGPL - original licence link has changed is not relivant.
849 * <script type="text/javascript">
852 Roo.applyIf(String, {
857 * Escapes the passed string for ' and \
858 * @param {String} string The string to escape
859 * @return {String} The escaped string
862 escape : function(string) {
863 return string.replace(/('|\\)/g, "\\$1");
867 * Pads the left side of a string with a specified character. This is especially useful
868 * for normalizing number and date strings. Example usage:
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
873 * @param {String} string The original string
874 * @param {Number} size The total length of the output string
875 * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876 * @return {String} The padded string
879 leftPad : function (val, size, ch) {
880 var result = new String(val);
881 if(ch === null || ch === undefined || ch === '') {
884 while (result.length < size) {
885 result = ch + result;
891 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
892 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
898 * @param {String} string The tokenized string to be formatted
899 * @param {String} value1 The value to replace token {0}
900 * @param {String} value2 Etc...
901 * @return {String} The formatted string
904 format : function(format){
905 var args = Array.prototype.slice.call(arguments, 1);
906 return format.replace(/\{(\d+)\}/g, function(m, i){
907 return Roo.util.Format.htmlEncode(args[i]);
915 * Utility function that allows you to easily switch a string between two alternating values. The passed value
916 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
917 * they are already different, the first value passed in is returned. Note that this method returns the new value
918 * but does not change the current string.
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 * @param {String} value The value to compare to the current string
927 * @param {String} other The new value to use if the string already equals the first value passed in
928 * @return {String} The new value
931 String.prototype.toggle = function(value, other){
932 return this == value ? other : value;
937 * Remove invalid unicode characters from a string
939 * @return {String} The clean string
941 String.prototype.unicodeClean = function () {
942 return this.replace(/[\s\S]/g,
943 function(character) {
944 if (character.charCodeAt()< 256) {
948 encodeURIComponent(character);
959 * Ext JS Library 1.1.1
960 * Copyright(c) 2006-2007, Ext JS, LLC.
962 * Originally Released Under LGPL - original licence link has changed is not relivant.
965 * <script type="text/javascript">
971 Roo.applyIf(Number.prototype, {
973 * Checks whether or not the current number is within a desired range. If the number is already within the
974 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975 * exceeded. Note that this method returns the constrained value but does not change the current number.
976 * @param {Number} min The minimum number in the range
977 * @param {Number} max The maximum number in the range
978 * @return {Number} The constrained value if outside the range, otherwise the current value
980 constrain : function(min, max){
981 return Math.min(Math.max(this, min), max);
985 * Ext JS Library 1.1.1
986 * Copyright(c) 2006-2007, Ext JS, LLC.
988 * Originally Released Under LGPL - original licence link has changed is not relivant.
991 * <script type="text/javascript">
996 Roo.applyIf(Array.prototype, {
999 * Checks whether or not the specified object exists in the array.
1000 * @param {Object} o The object to check for
1001 * @return {Number} The index of o in the array (or -1 if it is not found)
1003 indexOf : function(o){
1004 for (var i = 0, len = this.length; i < len; i++){
1005 if(this[i] == o) { return i; }
1011 * Removes the specified object from the array. If the object is not found nothing happens.
1012 * @param {Object} o The object to remove
1014 remove : function(o){
1015 var index = this.indexOf(o);
1017 this.splice(index, 1);
1021 * Map (JS 1.6 compatibility)
1022 * @param {Function} function to call
1024 map : function(fun )
1026 var len = this.length >>> 0;
1027 if (typeof fun != "function") {
1028 throw new TypeError();
1030 var res = new Array(len);
1031 var thisp = arguments[1];
1032 for (var i = 0; i < len; i++)
1035 res[i] = fun.call(thisp, this[i], i, this);
1043 * @param {Array} o The array to compare to
1044 * @returns {Boolean} true if the same
1046 equals : function(b)
1048 // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1055 if (this.length !== b.length) {
1059 // sort?? a.sort().equals(b.sort());
1061 for (var i = 0; i < this.length; ++i) {
1062 if (this[i] !== b[i]) {
1074 Roo.applyIf(Array, {
1078 * @param {Array} o Or Array like object (eg. nodelist)
1085 for (var i =0; i < o.length; i++) {
1094 * Ext JS Library 1.1.1
1095 * Copyright(c) 2006-2007, Ext JS, LLC.
1097 * Originally Released Under LGPL - original licence link has changed is not relivant.
1100 * <script type="text/javascript">
1106 * The date parsing and format syntax is a subset of
1107 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108 * supported will provide results equivalent to their PHP versions.
1110 * Following is the list of all currently supported formats:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1115 Format Output Description
1116 ------ ---------- --------------------------------------------------------------
1117 d 10 Day of the month, 2 digits with leading zeros
1118 D Wed A textual representation of a day, three letters
1119 j 10 Day of the month without leading zeros
1120 l Wednesday A full textual representation of the day of the week
1121 S th English ordinal day of month suffix, 2 chars (use with j)
1122 w 3 Numeric representation of the day of the week
1123 z 9 The julian date, or day of the year (0-365)
1124 W 01 ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125 F January A full textual representation of the month
1126 m 01 Numeric representation of a month, with leading zeros
1127 M Jan Month name abbreviation, three letters
1128 n 1 Numeric representation of a month, without leading zeros
1129 t 31 Number of days in the given month
1130 L 0 Whether it's a leap year (1 if it is a leap year, else 0)
1131 Y 2007 A full numeric representation of a year, 4 digits
1132 y 07 A two digit representation of a year
1133 a pm Lowercase Ante meridiem and Post meridiem
1134 A PM Uppercase Ante meridiem and Post meridiem
1135 g 3 12-hour format of an hour without leading zeros
1136 G 15 24-hour format of an hour without leading zeros
1137 h 03 12-hour format of an hour with leading zeros
1138 H 15 24-hour format of an hour with leading zeros
1139 i 05 Minutes with leading zeros
1140 s 01 Seconds, with leading zeros
1141 O -0600 Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142 P -06:00 Difference to Greenwich time (GMT) with colon between hours and minutes
1143 T CST Timezone setting of the machine running the code
1144 Z -21600 Timezone offset in seconds (negative if west of UTC, positive if east)
1147 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d')); //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a')); //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A')); //Wednesday, the 10th of January 2007 03:05:01 PM
1155 * Here are some standard date/time patterns that you might find helpful. They
1156 * are not part of the source of Date.js, but to use them you can simply copy this
1157 * block of code into any script that is included after Date.js and they will also become
1158 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
1161 ISO8601Long:"Y-m-d H:i:s",
1162 ISO8601Short:"Y-m-d",
1164 LongDate: "l, F d, Y",
1165 FullDateTime: "l, F d, Y g:i:s A",
1168 LongTime: "g:i:s A",
1169 SortableDateTime: "Y-m-d\\TH:i:s",
1170 UniversalSortableDateTime: "Y-m-d H:i:sO",
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1183 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184 * They generate precompiled functions from date formats instead of parsing and
1185 * processing the pattern every time you format a date. These functions are available
1186 * on every Date object (any javascript function).
1188 * The original article and download are here:
1189 * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1196 Returns the number of milliseconds between this date and date
1197 @param {Date} date (optional) Defaults to now
1198 @return {Number} The diff in milliseconds
1199 @member Date getElapsed
1201 Date.prototype.getElapsed = function(date) {
1202 return Math.abs((date || new Date()).getTime()-this.getTime());
1204 // was in date file..
1208 Date.parseFunctions = {count:0};
1210 Date.parseRegexes = [];
1212 Date.formatFunctions = {count:0};
1215 Date.prototype.dateFormat = function(format) {
1216 if (Date.formatFunctions[format] == null) {
1217 Date.createNewFormat(format);
1219 var func = Date.formatFunctions[format];
1220 return this[func]();
1225 * Formats a date given the supplied format string
1226 * @param {String} format The format string
1227 * @return {String} The formatted date
1230 Date.prototype.format = Date.prototype.dateFormat;
1233 Date.createNewFormat = function(format) {
1234 var funcName = "format" + Date.formatFunctions.count++;
1235 Date.formatFunctions[format] = funcName;
1236 var code = "Date.prototype." + funcName + " = function(){return ";
1237 var special = false;
1239 for (var i = 0; i < format.length; ++i) {
1240 ch = format.charAt(i);
1241 if (!special && ch == "\\") {
1246 code += "'" + String.escape(ch) + "' + ";
1249 code += Date.getFormatCode(ch);
1252 /** eval:var:zzzzzzzzzzzzz */
1253 eval(code.substring(0, code.length - 3) + ";}");
1257 Date.getFormatCode = function(character) {
1258 switch (character) {
1260 return "String.leftPad(this.getDate(), 2, '0') + ";
1262 return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1264 return "this.getDate() + ";
1266 return "Date.dayNames[this.getDay()] + ";
1268 return "this.getSuffix() + ";
1270 return "this.getDay() + ";
1272 return "this.getDayOfYear() + ";
1274 return "this.getWeekOfYear() + ";
1276 return "Date.monthNames[this.getMonth()] + ";
1278 return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1280 return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1282 return "(this.getMonth() + 1) + ";
1284 return "this.getDaysInMonth() + ";
1286 return "(this.isLeapYear() ? 1 : 0) + ";
1288 return "this.getFullYear() + ";
1290 return "('' + this.getFullYear()).substring(2, 4) + ";
1292 return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1294 return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1296 return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1298 return "this.getHours() + ";
1300 return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1302 return "String.leftPad(this.getHours(), 2, '0') + ";
1304 return "String.leftPad(this.getMinutes(), 2, '0') + ";
1306 return "String.leftPad(this.getSeconds(), 2, '0') + ";
1308 return "this.getGMTOffset() + ";
1310 return "this.getGMTColonOffset() + ";
1312 return "this.getTimezone() + ";
1314 return "(this.getTimezoneOffset() * -60) + ";
1316 return "'" + String.escape(character) + "' + ";
1321 * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322 * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates. Any part of
1323 * the date format that is not specified will default to the current date value for that part. Time parts can also
1324 * be specified, but default to 0. Keep in mind that the input date string must precisely match the specified format
1325 * string or the parse operation will fail.
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1340 * @param {String} input The unparsed date as a string
1341 * @param {String} format The format the date is in
1342 * @return {Date} The parsed date
1345 Date.parseDate = function(input, format) {
1346 if (Date.parseFunctions[format] == null) {
1347 Date.createParser(format);
1349 var func = Date.parseFunctions[format];
1350 return Date[func](input);
1356 Date.createParser = function(format) {
1357 var funcName = "parse" + Date.parseFunctions.count++;
1358 var regexNum = Date.parseRegexes.length;
1359 var currentGroup = 1;
1360 Date.parseFunctions[format] = funcName;
1362 var code = "Date." + funcName + " = function(input){\n"
1363 + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364 + "var d = new Date();\n"
1365 + "y = d.getFullYear();\n"
1366 + "m = d.getMonth();\n"
1367 + "d = d.getDate();\n"
1368 + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369 + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370 + "if (results && results.length > 0) {";
1373 var special = false;
1375 for (var i = 0; i < format.length; ++i) {
1376 ch = format.charAt(i);
1377 if (!special && ch == "\\") {
1382 regex += String.escape(ch);
1385 var obj = Date.formatCodeToRegex(ch, currentGroup);
1386 currentGroup += obj.g;
1388 if (obj.g && obj.c) {
1394 code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395 + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397 + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399 + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400 + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401 + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402 + "else if (y >= 0 && m >= 0)\n"
1403 + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404 + "else if (y >= 0)\n"
1405 + "{v = new Date(y); v.setFullYear(y);}\n"
1406 + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407 + " ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408 + " v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1411 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412 /** eval:var:zzzzzzzzzzzzz */
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418 switch (character) {
1422 s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1425 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426 s:"(\\d{1,2})"}; // day of month without leading zeroes
1429 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430 s:"(\\d{2})"}; // day of month with leading zeroes
1434 s:"(?:" + Date.dayNames.join("|") + ")"};
1438 s:"(?:st|nd|rd|th)"};
1453 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454 s:"(" + Date.monthNames.join("|") + ")"};
1457 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458 s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1461 c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462 s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1465 c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466 s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1477 c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1481 c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1486 c:"if (results[" + currentGroup + "] == 'am') {\n"
1487 + "if (h == 12) { h = 0; }\n"
1488 + "} else { if (h < 12) { h += 12; }}",
1492 c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493 + "if (h == 12) { h = 0; }\n"
1494 + "} else { if (h < 12) { h += 12; }}",
1499 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500 s:"(\\d{1,2})"}; // 12/24-hr format format of an hour without leading zeroes
1504 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505 s:"(\\d{2})"}; // 12/24-hr format format of an hour with leading zeroes
1508 c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1512 c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1517 "o = results[", currentGroup, "];\n",
1518 "var sn = o.substring(0,1);\n", // get + / - sign
1519 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1524 s:"([+\-]\\d{2,4})"};
1530 "o = results[", currentGroup, "];\n",
1531 "var sn = o.substring(0,1);\n",
1532 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533 "var mn = o.substring(4,6) % 60;\n",
1534 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1541 s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1544 c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545 + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546 s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1550 s:String.escape(character)};
1555 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556 * @return {String} The abbreviated timezone name (e.g. 'CST')
1558 Date.prototype.getTimezone = function() {
1559 return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1563 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1566 Date.prototype.getGMTOffset = function() {
1567 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569 + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1573 * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574 * @return {String} 2-characters representing hours and 2-characters representing minutes
1575 * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1577 Date.prototype.getGMTColonOffset = function() {
1578 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1581 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1585 * Get the numeric day number of the year, adjusted for leap year.
1586 * @return {Number} 0 through 364 (365 in leap years)
1588 Date.prototype.getDayOfYear = function() {
1590 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591 for (var i = 0; i < this.getMonth(); ++i) {
1592 num += Date.daysInMonth[i];
1594 return num + this.getDate() - 1;
1598 * Get the string representation of the numeric week number of the year
1599 * (equivalent to the format specifier 'W').
1600 * @return {String} '00' through '52'
1602 Date.prototype.getWeekOfYear = function() {
1603 // Skip to Thursday of this week
1604 var now = this.getDayOfYear() + (4 - this.getDay());
1605 // Find the first Thursday of the year
1606 var jan1 = new Date(this.getFullYear(), 0, 1);
1607 var then = (7 - jan1.getDay() + 4);
1608 return String.leftPad(((now - then) / 7) + 1, 2, "0");
1612 * Whether or not the current date is in a leap year.
1613 * @return {Boolean} True if the current date is in a leap year, else false
1615 Date.prototype.isLeapYear = function() {
1616 var year = this.getFullYear();
1617 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1621 * Get the first day of the current month, adjusted for leap year. The returned value
1622 * is the numeric day index within the week (0-6) which can be used in conjunction with
1623 * the {@link #monthNames} array to retrieve the textual day name.
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1629 * @return {Number} The day number (0-6)
1631 Date.prototype.getFirstDayOfMonth = function() {
1632 var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633 return (day < 0) ? (day + 7) : day;
1637 * Get the last day of the current month, adjusted for leap year. The returned value
1638 * is the numeric day index within the week (0-6) which can be used in conjunction with
1639 * the {@link #monthNames} array to retrieve the textual day name.
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1645 * @return {Number} The day number (0-6)
1647 Date.prototype.getLastDayOfMonth = function() {
1648 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649 return (day < 0) ? (day + 7) : day;
1654 * Get the first date of this date's month
1657 Date.prototype.getFirstDateOfMonth = function() {
1658 return new Date(this.getFullYear(), this.getMonth(), 1);
1662 * Get the last date of this date's month
1665 Date.prototype.getLastDateOfMonth = function() {
1666 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1669 * Get the number of days in the current month, adjusted for leap year.
1670 * @return {Number} The number of days in the month
1672 Date.prototype.getDaysInMonth = function() {
1673 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674 return Date.daysInMonth[this.getMonth()];
1678 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679 * @return {String} 'st, 'nd', 'rd' or 'th'
1681 Date.prototype.getSuffix = function() {
1682 switch (this.getDate()) {
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1702 * An array of textual month names.
1703 * Override these values for international dates, for example...
1704 * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1723 * An array of textual day names.
1724 * Override these values for international dates, for example...
1725 * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1741 Date.monthNumbers = {
1756 * Creates and returns a new Date instance with the exact same date value as the called instance.
1757 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758 * variable will also be changed. When the intention is to create a new variable that will not
1759 * modify the original instance, you should create a clone.
1761 * Example of correctly cloning a date:
1764 var orig = new Date('10/1/2006');
1767 document.write(orig); //returns 'Thu Oct 05 2006'!
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1773 document.write(orig); //returns 'Thu Oct 01 2006'
1775 * @return {Date} The new Date instance
1777 Date.prototype.clone = function() {
1778 return new Date(this.getTime());
1782 * Clears any time information from this date
1783 @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784 @return {Date} this or the clone
1786 Date.prototype.clearTime = function(clone){
1788 return this.clone().clearTime();
1793 this.setMilliseconds(0);
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800 Date.brokenSetMonth = Date.prototype.setMonth;
1801 Date.prototype.setMonth = function(num){
1803 var n = Math.ceil(-num);
1804 var back_year = Math.ceil(n/12);
1805 var month = (n % 12) ? 12 - n % 12 : 0 ;
1806 this.setFullYear(this.getFullYear() - back_year);
1807 return Date.brokenSetMonth.call(this, month);
1809 return Date.brokenSetMonth.apply(this, arguments);
1814 /** Date interval constant
1818 /** Date interval constant
1822 /** Date interval constant
1826 /** Date interval constant
1830 /** Date interval constant
1834 /** Date interval constant
1838 /** Date interval constant
1844 * Provides a convenient method of performing basic date arithmetic. This method
1845 * does not modify the Date instance being called - it creates and returns
1846 * a new Date instance containing the resulting date value.
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1863 * @param {String} interval A valid date interval enum value
1864 * @param {Number} value The amount to add to the current date
1865 * @return {Date} The new Date instance
1867 Date.prototype.add = function(interval, value){
1868 var d = this.clone();
1869 if (!interval || value === 0) { return d; }
1870 switch(interval.toLowerCase()){
1872 d.setMilliseconds(this.getMilliseconds() + value);
1875 d.setSeconds(this.getSeconds() + value);
1878 d.setMinutes(this.getMinutes() + value);
1881 d.setHours(this.getHours() + value);
1884 d.setDate(this.getDate() + value);
1887 var day = this.getDate();
1889 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1892 d.setMonth(this.getMonth() + value);
1895 d.setFullYear(this.getFullYear() + value);
1901 * @class Roo.lib.Dom
1905 * Dom utils (from YIU afaik)
1911 * Get the view width
1912 * @param {Boolean} full True will get the full document, otherwise it's the view width
1913 * @return {Number} The width
1916 getViewWidth : function(full) {
1917 return full ? this.getDocumentWidth() : this.getViewportWidth();
1920 * Get the view height
1921 * @param {Boolean} full True will get the full document, otherwise it's the view height
1922 * @return {Number} The height
1924 getViewHeight : function(full) {
1925 return full ? this.getDocumentHeight() : this.getViewportHeight();
1928 * Get the Full Document height
1929 * @return {Number} The height
1931 getDocumentHeight: function() {
1932 var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933 return Math.max(scrollHeight, this.getViewportHeight());
1936 * Get the Full Document width
1937 * @return {Number} The width
1939 getDocumentWidth: function() {
1940 var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941 return Math.max(scrollWidth, this.getViewportWidth());
1944 * Get the Window Viewport height
1945 * @return {Number} The height
1947 getViewportHeight: function() {
1948 var height = self.innerHeight;
1949 var mode = document.compatMode;
1951 if ((mode || Roo.isIE) && !Roo.isOpera) {
1952 height = (mode == "CSS1Compat") ?
1953 document.documentElement.clientHeight :
1954 document.body.clientHeight;
1960 * Get the Window Viewport width
1961 * @return {Number} The width
1963 getViewportWidth: function() {
1964 var width = self.innerWidth;
1965 var mode = document.compatMode;
1967 if (mode || Roo.isIE) {
1968 width = (mode == "CSS1Compat") ?
1969 document.documentElement.clientWidth :
1970 document.body.clientWidth;
1975 isAncestor : function(p, c) {
1982 if (p.contains && !Roo.isSafari) {
1983 return p.contains(c);
1984 } else if (p.compareDocumentPosition) {
1985 return !!(p.compareDocumentPosition(c) & 16);
1987 var parent = c.parentNode;
1992 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1995 parent = parent.parentNode;
2001 getRegion : function(el) {
2002 return Roo.lib.Region.getRegion(el);
2005 getY : function(el) {
2006 return this.getXY(el)[1];
2009 getX : function(el) {
2010 return this.getXY(el)[0];
2013 getXY : function(el) {
2014 var p, pe, b, scroll, bd = document.body;
2015 el = Roo.getDom(el);
2016 var fly = Roo.lib.AnimBase.fly;
2017 if (el.getBoundingClientRect) {
2018 b = el.getBoundingClientRect();
2019 scroll = fly(document).getScroll();
2020 return [b.left + scroll.left, b.top + scroll.top];
2026 var hasAbsolute = fly(el).getStyle("position") == "absolute";
2033 if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2040 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2048 if (p != el && pe.getStyle('overflow') != 'visible') {
2056 if (Roo.isSafari && hasAbsolute) {
2061 if (Roo.isGecko && !hasAbsolute) {
2063 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2068 while (p && p != bd) {
2069 if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2081 setXY : function(el, xy) {
2082 el = Roo.fly(el, '_setXY');
2084 var pts = el.translatePoints(xy);
2085 if (xy[0] !== false) {
2086 el.dom.style.left = pts.left + "px";
2088 if (xy[1] !== false) {
2089 el.dom.style.top = pts.top + "px";
2093 setX : function(el, x) {
2094 this.setXY(el, [x, false]);
2097 setY : function(el, y) {
2098 this.setXY(el, [false, y]);
2102 * Portions of this file are based on pieces of Yahoo User Interface Library
2103 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104 * YUI licensed under the BSD License:
2105 * http://developer.yahoo.net/yui/license.txt
2106 * <script type="text/javascript">
2110 Roo.lib.Event = function() {
2111 var loadComplete = false;
2113 var unloadListeners = [];
2115 var onAvailStack = [];
2117 var lastError = null;
2130 startInterval: function() {
2131 if (!this._interval) {
2133 var callback = function() {
2134 self._tryPreloadAttach();
2136 this._interval = setInterval(callback, this.POLL_INTERVAL);
2141 onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142 onAvailStack.push({ id: p_id,
2145 override: p_override,
2146 checkReady: false });
2148 retryCount = this.POLL_RETRYS;
2149 this.startInterval();
2153 addListener: function(el, eventName, fn) {
2154 el = Roo.getDom(el);
2159 if ("unload" == eventName) {
2160 unloadListeners[unloadListeners.length] =
2161 [el, eventName, fn];
2165 var wrappedFn = function(e) {
2166 return fn(Roo.lib.Event.getEvent(e));
2169 var li = [el, eventName, fn, wrappedFn];
2171 var index = listeners.length;
2172 listeners[index] = li;
2174 this.doAdd(el, eventName, wrappedFn, false);
2180 removeListener: function(el, eventName, fn) {
2183 el = Roo.getDom(el);
2186 return this.purgeElement(el, false, eventName);
2190 if ("unload" == eventName) {
2192 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193 var li = unloadListeners[i];
2196 li[1] == eventName &&
2198 unloadListeners.splice(i, 1);
2206 var cacheItem = null;
2209 var index = arguments[3];
2211 if ("undefined" == typeof index) {
2212 index = this._getCacheIndex(el, eventName, fn);
2216 cacheItem = listeners[index];
2219 if (!el || !cacheItem) {
2223 this.doRemove(el, eventName, cacheItem[this.WFN], false);
2225 delete listeners[index][this.WFN];
2226 delete listeners[index][this.FN];
2227 listeners.splice(index, 1);
2234 getTarget: function(ev, resolveTextNode) {
2235 ev = ev.browserEvent || ev;
2236 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2237 var t = ev.target || ev.srcElement;
2238 return this.resolveTextNode(t);
2242 resolveTextNode: function(node) {
2243 if (Roo.isSafari && node && 3 == node.nodeType) {
2244 return node.parentNode;
2251 getPageX: function(ev) {
2252 ev = ev.browserEvent || ev;
2253 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2255 if (!x && 0 !== x) {
2256 x = ev.clientX || 0;
2259 x += this.getScroll()[1];
2267 getPageY: function(ev) {
2268 ev = ev.browserEvent || ev;
2269 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2271 if (!y && 0 !== y) {
2272 y = ev.clientY || 0;
2275 y += this.getScroll()[0];
2284 getXY: function(ev) {
2285 ev = ev.browserEvent || ev;
2286 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2287 return [this.getPageX(ev), this.getPageY(ev)];
2291 getRelatedTarget: function(ev) {
2292 ev = ev.browserEvent || ev;
2293 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2294 var t = ev.relatedTarget;
2296 if (ev.type == "mouseout") {
2298 } else if (ev.type == "mouseover") {
2303 return this.resolveTextNode(t);
2307 getTime: function(ev) {
2308 ev = ev.browserEvent || ev;
2309 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2311 var t = new Date().getTime();
2315 this.lastError = ex;
2324 stopEvent: function(ev) {
2325 this.stopPropagation(ev);
2326 this.preventDefault(ev);
2330 stopPropagation: function(ev) {
2331 ev = ev.browserEvent || ev;
2332 if (ev.stopPropagation) {
2333 ev.stopPropagation();
2335 ev.cancelBubble = true;
2340 preventDefault: function(ev) {
2341 ev = ev.browserEvent || ev;
2342 if(ev.preventDefault) {
2343 ev.preventDefault();
2345 ev.returnValue = false;
2350 getEvent: function(e) {
2351 var ev = e || window.event;
2353 var c = this.getEvent.caller;
2355 ev = c.arguments[0];
2356 if (ev && Event == ev.constructor) {
2366 getCharCode: function(ev) {
2367 ev = ev.browserEvent || ev;
2368 return ev.charCode || ev.keyCode || 0;
2372 _getCacheIndex: function(el, eventName, fn) {
2373 for (var i = 0,len = listeners.length; i < len; ++i) {
2374 var li = listeners[i];
2376 li[this.FN] == fn &&
2377 li[this.EL] == el &&
2378 li[this.TYPE] == eventName) {
2390 getEl: function(id) {
2391 return document.getElementById(id);
2395 clearCache: function() {
2399 _load: function(e) {
2400 loadComplete = true;
2401 var EU = Roo.lib.Event;
2405 EU.doRemove(window, "load", EU._load);
2410 _tryPreloadAttach: function() {
2419 var tryAgain = !loadComplete;
2421 tryAgain = (retryCount > 0);
2426 for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427 var item = onAvailStack[i];
2429 var el = this.getEl(item.id);
2432 if (!item.checkReady ||
2435 (document && document.body)) {
2438 if (item.override) {
2439 if (item.override === true) {
2442 scope = item.override;
2445 item.fn.call(scope, item.obj);
2446 onAvailStack[i] = null;
2449 notAvail.push(item);
2454 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2458 this.startInterval();
2460 clearInterval(this._interval);
2461 this._interval = null;
2464 this.locked = false;
2471 purgeElement: function(el, recurse, eventName) {
2472 var elListeners = this.getListeners(el, eventName);
2474 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475 var l = elListeners[i];
2476 this.removeListener(el, l.type, l.fn);
2480 if (recurse && el && el.childNodes) {
2481 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482 this.purgeElement(el.childNodes[i], recurse, eventName);
2488 getListeners: function(el, eventName) {
2489 var results = [], searchLists;
2491 searchLists = [listeners, unloadListeners];
2492 } else if (eventName == "unload") {
2493 searchLists = [unloadListeners];
2495 searchLists = [listeners];
2498 for (var j = 0; j < searchLists.length; ++j) {
2499 var searchList = searchLists[j];
2500 if (searchList && searchList.length > 0) {
2501 for (var i = 0,len = searchList.length; i < len; ++i) {
2502 var l = searchList[i];
2503 if (l && l[this.EL] === el &&
2504 (!eventName || eventName === l[this.TYPE])) {
2509 adjust: l[this.ADJ_SCOPE],
2517 return (results.length) ? results : null;
2521 _unload: function(e) {
2523 var EU = Roo.lib.Event, i, j, l, len, index;
2525 for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526 l = unloadListeners[i];
2529 if (l[EU.ADJ_SCOPE]) {
2530 if (l[EU.ADJ_SCOPE] === true) {
2533 scope = l[EU.ADJ_SCOPE];
2536 l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537 unloadListeners[i] = null;
2543 unloadListeners = null;
2545 if (listeners && listeners.length > 0) {
2546 j = listeners.length;
2549 l = listeners[index];
2551 EU.removeListener(l[EU.EL], l[EU.TYPE],
2561 EU.doRemove(window, "unload", EU._unload);
2566 getScroll: function() {
2567 var dd = document.documentElement, db = document.body;
2568 if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569 return [dd.scrollTop, dd.scrollLeft];
2571 return [db.scrollTop, db.scrollLeft];
2578 doAdd: function () {
2579 if (window.addEventListener) {
2580 return function(el, eventName, fn, capture) {
2581 el.addEventListener(eventName, fn, (capture));
2583 } else if (window.attachEvent) {
2584 return function(el, eventName, fn, capture) {
2585 el.attachEvent("on" + eventName, fn);
2594 doRemove: function() {
2595 if (window.removeEventListener) {
2596 return function (el, eventName, fn, capture) {
2597 el.removeEventListener(eventName, fn, (capture));
2599 } else if (window.detachEvent) {
2600 return function (el, eventName, fn) {
2601 el.detachEvent("on" + eventName, fn);
2613 var E = Roo.lib.Event;
2614 E.on = E.addListener;
2615 E.un = E.removeListener;
2617 if (document && document.body) {
2620 E.doAdd(window, "load", E._load);
2622 E.doAdd(window, "unload", E._unload);
2623 E._tryPreloadAttach();
2630 * @class Roo.lib.Ajax
2632 * provide a simple Ajax request utility functions
2634 * Portions of this file are based on pieces of Yahoo User Interface Library
2635 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636 * YUI licensed under the BSD License:
2637 * http://developer.yahoo.net/yui/license.txt
2638 * <script type="text/javascript">
2646 request : function(method, uri, cb, data, options) {
2648 var hs = options.headers;
2651 if(hs.hasOwnProperty(h)){
2652 this.initHeader(h, hs[h], false);
2656 if(options.xmlData){
2657 this.initHeader('Content-Type', 'text/xml', false);
2659 data = options.xmlData;
2663 return this.asyncRequest(method, uri, cb, data);
2669 * @param {DomForm} form element
2670 * @return {String} urlencode form output.
2672 serializeForm : function(form) {
2673 if(typeof form == 'string') {
2674 form = (document.getElementById(form) || document.forms[form]);
2677 var el, name, val, disabled, data = '', hasSubmit = false;
2678 for (var i = 0; i < form.elements.length; i++) {
2679 el = form.elements[i];
2680 disabled = form.elements[i].disabled;
2681 name = form.elements[i].name;
2682 val = form.elements[i].value;
2684 if (!disabled && name){
2688 case 'select-multiple':
2689 for (var j = 0; j < el.options.length; j++) {
2690 if (el.options[j].selected) {
2692 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2695 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2703 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2716 if(hasSubmit == false) {
2717 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2722 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2727 data = data.substr(0, data.length - 1);
2735 useDefaultHeader:true,
2737 defaultPostHeader:'application/x-www-form-urlencoded',
2739 useDefaultXhrHeader:true,
2741 defaultXhrHeader:'XMLHttpRequest',
2743 hasDefaultHeaders:true,
2755 setProgId:function(id)
2757 this.activeX.unshift(id);
2760 setDefaultPostHeader:function(b)
2762 this.useDefaultHeader = b;
2765 setDefaultXhrHeader:function(b)
2767 this.useDefaultXhrHeader = b;
2770 setPollingInterval:function(i)
2772 if (typeof i == 'number' && isFinite(i)) {
2773 this.pollInterval = i;
2777 createXhrObject:function(transactionId)
2783 http = new XMLHttpRequest();
2785 obj = { conn:http, tId:transactionId };
2789 for (var i = 0; i < this.activeX.length; ++i) {
2793 http = new ActiveXObject(this.activeX[i]);
2795 obj = { conn:http, tId:transactionId };
2808 getConnectionObject:function()
2811 var tId = this.transactionId;
2815 o = this.createXhrObject(tId);
2817 this.transactionId++;
2828 asyncRequest:function(method, uri, callback, postData)
2830 var o = this.getConnectionObject();
2836 o.conn.open(method, uri, true);
2838 if (this.useDefaultXhrHeader) {
2839 if (!this.defaultHeaders['X-Requested-With']) {
2840 this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2844 if(postData && this.useDefaultHeader){
2845 this.initHeader('Content-Type', this.defaultPostHeader);
2848 if (this.hasDefaultHeaders || this.hasHeaders) {
2852 this.handleReadyState(o, callback);
2853 o.conn.send(postData || null);
2859 handleReadyState:function(o, callback)
2863 if (callback && callback.timeout) {
2865 this.timeout[o.tId] = window.setTimeout(function() {
2866 oConn.abort(o, callback, true);
2867 }, callback.timeout);
2870 this.poll[o.tId] = window.setInterval(
2872 if (o.conn && o.conn.readyState == 4) {
2873 window.clearInterval(oConn.poll[o.tId]);
2874 delete oConn.poll[o.tId];
2876 if(callback && callback.timeout) {
2877 window.clearTimeout(oConn.timeout[o.tId]);
2878 delete oConn.timeout[o.tId];
2881 oConn.handleTransactionResponse(o, callback);
2884 , this.pollInterval);
2887 handleTransactionResponse:function(o, callback, isAbort)
2891 this.releaseObject(o);
2895 var httpStatus, responseObject;
2899 if (o.conn.status !== undefined && o.conn.status != 0) {
2900 httpStatus = o.conn.status;
2912 if (httpStatus >= 200 && httpStatus < 300) {
2913 responseObject = this.createResponseObject(o, callback.argument);
2914 if (callback.success) {
2915 if (!callback.scope) {
2916 callback.success(responseObject);
2921 callback.success.apply(callback.scope, [responseObject]);
2926 switch (httpStatus) {
2934 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935 if (callback.failure) {
2936 if (!callback.scope) {
2937 callback.failure(responseObject);
2940 callback.failure.apply(callback.scope, [responseObject]);
2945 responseObject = this.createResponseObject(o, callback.argument);
2946 if (callback.failure) {
2947 if (!callback.scope) {
2948 callback.failure(responseObject);
2951 callback.failure.apply(callback.scope, [responseObject]);
2957 this.releaseObject(o);
2958 responseObject = null;
2961 createResponseObject:function(o, callbackArg)
2968 var headerStr = o.conn.getAllResponseHeaders();
2969 var header = headerStr.split('\n');
2970 for (var i = 0; i < header.length; i++) {
2971 var delimitPos = header[i].indexOf(':');
2972 if (delimitPos != -1) {
2973 headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2981 obj.status = o.conn.status;
2982 obj.statusText = o.conn.statusText;
2983 obj.getResponseHeader = headerObj;
2984 obj.getAllResponseHeaders = headerStr;
2985 obj.responseText = o.conn.responseText;
2986 obj.responseXML = o.conn.responseXML;
2988 if (typeof callbackArg !== undefined) {
2989 obj.argument = callbackArg;
2995 createExceptionObject:function(tId, callbackArg, isAbort)
2998 var COMM_ERROR = 'communication failure';
2999 var ABORT_CODE = -1;
3000 var ABORT_ERROR = 'transaction aborted';
3006 obj.status = ABORT_CODE;
3007 obj.statusText = ABORT_ERROR;
3010 obj.status = COMM_CODE;
3011 obj.statusText = COMM_ERROR;
3015 obj.argument = callbackArg;
3021 initHeader:function(label, value, isDefault)
3023 var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3025 if (headerObj[label] === undefined) {
3026 headerObj[label] = value;
3031 headerObj[label] = value + "," + headerObj[label];
3035 this.hasDefaultHeaders = true;
3038 this.hasHeaders = true;
3043 setHeader:function(o)
3045 if (this.hasDefaultHeaders) {
3046 for (var prop in this.defaultHeaders) {
3047 if (this.defaultHeaders.hasOwnProperty(prop)) {
3048 o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3053 if (this.hasHeaders) {
3054 for (var prop in this.headers) {
3055 if (this.headers.hasOwnProperty(prop)) {
3056 o.conn.setRequestHeader(prop, this.headers[prop]);
3060 this.hasHeaders = false;
3064 resetDefaultHeaders:function() {
3065 delete this.defaultHeaders;
3066 this.defaultHeaders = {};
3067 this.hasDefaultHeaders = false;
3070 abort:function(o, callback, isTimeout)
3072 if(this.isCallInProgress(o)) {
3074 window.clearInterval(this.poll[o.tId]);
3075 delete this.poll[o.tId];
3077 delete this.timeout[o.tId];
3080 this.handleTransactionResponse(o, callback, true);
3090 isCallInProgress:function(o)
3093 return o.conn.readyState != 4 && o.conn.readyState != 0;
3102 releaseObject:function(o)
3111 'MSXML2.XMLHTTP.3.0',
3119 * Portions of this file are based on pieces of Yahoo User Interface Library
3120 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121 * YUI licensed under the BSD License:
3122 * http://developer.yahoo.net/yui/license.txt
3123 * <script type="text/javascript">
3127 Roo.lib.Region = function(t, r, b, l) {
3137 Roo.lib.Region.prototype = {
3138 contains : function(region) {
3139 return ( region.left >= this.left &&
3140 region.right <= this.right &&
3141 region.top >= this.top &&
3142 region.bottom <= this.bottom );
3146 getArea : function() {
3147 return ( (this.bottom - this.top) * (this.right - this.left) );
3150 intersect : function(region) {
3151 var t = Math.max(this.top, region.top);
3152 var r = Math.min(this.right, region.right);
3153 var b = Math.min(this.bottom, region.bottom);
3154 var l = Math.max(this.left, region.left);
3156 if (b >= t && r >= l) {
3157 return new Roo.lib.Region(t, r, b, l);
3162 union : function(region) {
3163 var t = Math.min(this.top, region.top);
3164 var r = Math.max(this.right, region.right);
3165 var b = Math.max(this.bottom, region.bottom);
3166 var l = Math.min(this.left, region.left);
3168 return new Roo.lib.Region(t, r, b, l);
3171 adjust : function(t, l, b, r) {
3180 Roo.lib.Region.getRegion = function(el) {
3181 var p = Roo.lib.Dom.getXY(el);
3184 var r = p[0] + el.offsetWidth;
3185 var b = p[1] + el.offsetHeight;
3188 return new Roo.lib.Region(t, r, b, l);
3191 * Portions of this file are based on pieces of Yahoo User Interface Library
3192 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193 * YUI licensed under the BSD License:
3194 * http://developer.yahoo.net/yui/license.txt
3195 * <script type="text/javascript">
3198 //@@dep Roo.lib.Region
3201 Roo.lib.Point = function(x, y) {
3202 if (x instanceof Array) {
3206 this.x = this.right = this.left = this[0] = x;
3207 this.y = this.top = this.bottom = this[1] = y;
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3212 * Portions of this file are based on pieces of Yahoo User Interface Library
3213 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214 * YUI licensed under the BSD License:
3215 * http://developer.yahoo.net/yui/license.txt
3216 * <script type="text/javascript">
3223 scroll : function(el, args, duration, easing, cb, scope) {
3224 this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3227 motion : function(el, args, duration, easing, cb, scope) {
3228 this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3231 color : function(el, args, duration, easing, cb, scope) {
3232 this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3235 run : function(el, args, duration, easing, cb, scope, type) {
3236 type = type || Roo.lib.AnimBase;
3237 if (typeof easing == "string") {
3238 easing = Roo.lib.Easing[easing];
3240 var anim = new type(el, args, duration, easing);
3241 anim.animateX(function() {
3242 Roo.callback(cb, scope);
3248 * Portions of this file are based on pieces of Yahoo User Interface Library
3249 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250 * YUI licensed under the BSD License:
3251 * http://developer.yahoo.net/yui/license.txt
3252 * <script type="text/javascript">
3260 if (!libFlyweight) {
3261 libFlyweight = new Roo.Element.Flyweight();
3263 libFlyweight.dom = el;
3264 return libFlyweight;
3267 // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3271 Roo.lib.AnimBase = function(el, attributes, duration, method) {
3273 this.init(el, attributes, duration, method);
3277 Roo.lib.AnimBase.fly = fly;
3281 Roo.lib.AnimBase.prototype = {
3283 toString: function() {
3284 var el = this.getEl();
3285 var id = el.id || el.tagName;
3286 return ("Anim " + id);
3290 noNegatives: /width|height|opacity|padding/i,
3291 offsetAttribute: /^((width|height)|(top|left))$/,
3292 defaultUnit: /width|height|top$|bottom$|left$|right$/i,
3293 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3297 doMethod: function(attr, start, end) {
3298 return this.method(this.currentFrame, start, end - start, this.totalFrames);
3302 setAttribute: function(attr, val, unit) {
3303 if (this.patterns.noNegatives.test(attr)) {
3304 val = (val > 0) ? val : 0;
3307 Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3311 getAttribute: function(attr) {
3312 var el = this.getEl();
3313 var val = fly(el).getStyle(attr);
3315 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316 return parseFloat(val);
3319 var a = this.patterns.offsetAttribute.exec(attr) || [];
3320 var pos = !!( a[3] );
3321 var box = !!( a[2] );
3324 if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3334 getDefaultUnit: function(attr) {
3335 if (this.patterns.defaultUnit.test(attr)) {
3342 animateX : function(callback, scope) {
3343 var f = function() {
3344 this.onComplete.removeListener(f);
3345 if (typeof callback == "function") {
3346 callback.call(scope || this, this);
3349 this.onComplete.addListener(f, this);
3354 setRuntimeAttribute: function(attr) {
3357 var attributes = this.attributes;
3359 this.runtimeAttributes[attr] = {};
3361 var isset = function(prop) {
3362 return (typeof prop !== 'undefined');
3365 if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3369 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3372 if (isset(attributes[attr]['to'])) {
3373 end = attributes[attr]['to'];
3374 } else if (isset(attributes[attr]['by'])) {
3375 if (start.constructor == Array) {
3377 for (var i = 0, len = start.length; i < len; ++i) {
3378 end[i] = start[i] + attributes[attr]['by'][i];
3381 end = start + attributes[attr]['by'];
3385 this.runtimeAttributes[attr].start = start;
3386 this.runtimeAttributes[attr].end = end;
3389 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3393 init: function(el, attributes, duration, method) {
3395 var isAnimated = false;
3398 var startTime = null;
3401 var actualFrames = 0;
3404 el = Roo.getDom(el);
3407 this.attributes = attributes || {};
3410 this.duration = duration || 1;
3413 this.method = method || Roo.lib.Easing.easeNone;
3416 this.useSeconds = true;
3419 this.currentFrame = 0;
3422 this.totalFrames = Roo.lib.AnimMgr.fps;
3425 this.getEl = function() {
3430 this.isAnimated = function() {
3435 this.getStartTime = function() {
3439 this.runtimeAttributes = {};
3442 this.animate = function() {
3443 if (this.isAnimated()) {
3447 this.currentFrame = 0;
3449 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3451 Roo.lib.AnimMgr.registerElement(this);
3455 this.stop = function(finish) {
3457 this.currentFrame = this.totalFrames;
3458 this._onTween.fire();
3460 Roo.lib.AnimMgr.stop(this);
3463 var onStart = function() {
3464 this.onStart.fire();
3466 this.runtimeAttributes = {};
3467 for (var attr in this.attributes) {
3468 this.setRuntimeAttribute(attr);
3473 startTime = new Date();
3477 var onTween = function() {
3479 duration: new Date() - this.getStartTime(),
3480 currentFrame: this.currentFrame
3483 data.toString = function() {
3485 'duration: ' + data.duration +
3486 ', currentFrame: ' + data.currentFrame
3490 this.onTween.fire(data);
3492 var runtimeAttributes = this.runtimeAttributes;
3494 for (var attr in runtimeAttributes) {
3495 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3501 var onComplete = function() {
3502 var actual_duration = (new Date() - startTime) / 1000 ;
3505 duration: actual_duration,
3506 frames: actualFrames,
3507 fps: actualFrames / actual_duration
3510 data.toString = function() {
3512 'duration: ' + data.duration +
3513 ', frames: ' + data.frames +
3514 ', fps: ' + data.fps
3520 this.onComplete.fire(data);
3524 this._onStart = new Roo.util.Event(this);
3525 this.onStart = new Roo.util.Event(this);
3526 this.onTween = new Roo.util.Event(this);
3527 this._onTween = new Roo.util.Event(this);
3528 this.onComplete = new Roo.util.Event(this);
3529 this._onComplete = new Roo.util.Event(this);
3530 this._onStart.addListener(onStart);
3531 this._onTween.addListener(onTween);
3532 this._onComplete.addListener(onComplete);
3537 * Portions of this file are based on pieces of Yahoo User Interface Library
3538 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539 * YUI licensed under the BSD License:
3540 * http://developer.yahoo.net/yui/license.txt
3541 * <script type="text/javascript">
3545 Roo.lib.AnimMgr = new function() {
3562 this.registerElement = function(tween) {
3563 queue[queue.length] = tween;
3565 tween._onStart.fire();
3570 this.unRegister = function(tween, index) {
3571 tween._onComplete.fire();
3572 index = index || getIndex(tween);
3574 queue.splice(index, 1);
3578 if (tweenCount <= 0) {
3584 this.start = function() {
3585 if (thread === null) {
3586 thread = setInterval(this.run, this.delay);
3591 this.stop = function(tween) {
3593 clearInterval(thread);
3595 for (var i = 0, len = queue.length; i < len; ++i) {
3596 if (queue[0].isAnimated()) {
3597 this.unRegister(queue[0], 0);
3606 this.unRegister(tween);
3611 this.run = function() {
3612 for (var i = 0, len = queue.length; i < len; ++i) {
3613 var tween = queue[i];
3614 if (!tween || !tween.isAnimated()) {
3618 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3620 tween.currentFrame += 1;
3622 if (tween.useSeconds) {
3623 correctFrame(tween);
3625 tween._onTween.fire();
3628 Roo.lib.AnimMgr.stop(tween, i);
3633 var getIndex = function(anim) {
3634 for (var i = 0, len = queue.length; i < len; ++i) {
3635 if (queue[i] == anim) {
3643 var correctFrame = function(tween) {
3644 var frames = tween.totalFrames;
3645 var frame = tween.currentFrame;
3646 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647 var elapsed = (new Date() - tween.getStartTime());
3650 if (elapsed < tween.duration * 1000) {
3651 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3653 tweak = frames - (frame + 1);
3655 if (tweak > 0 && isFinite(tweak)) {
3656 if (tween.currentFrame + tweak >= frames) {
3657 tweak = frames - (frame + 1);
3660 tween.currentFrame += tweak;
3666 * Portions of this file are based on pieces of Yahoo User Interface Library
3667 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668 * YUI licensed under the BSD License:
3669 * http://developer.yahoo.net/yui/license.txt
3670 * <script type="text/javascript">
3673 Roo.lib.Bezier = new function() {
3675 this.getPosition = function(points, t) {
3676 var n = points.length;
3679 for (var i = 0; i < n; ++i) {
3680 tmp[i] = [points[i][0], points[i][1]];
3683 for (var j = 1; j < n; ++j) {
3684 for (i = 0; i < n - j; ++i) {
3685 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3690 return [ tmp[0][0], tmp[0][1] ];
3696 * @class Roo.lib.Color
3698 * An abstract Color implementation. Concrete Color implementations should use
3699 * an instance of this function as their prototype, and implement the getRGB and
3700 * getHSL functions. getRGB should return an object representing the RGB
3701 * components of this Color, with the red, green, and blue components in the
3702 * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703 * return an object representing the HSL components of this Color, with the hue
3704 * component in the range [0,360), the saturation and lightness components in
3705 * the range [0,100], and the alpha component in the range [0,1].
3710 * Functions for Color handling and processing.
3712 * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3714 * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715 * rights to this program, with the intention of it becoming part of the public
3716 * domain. Because this program is released into the public domain, it comes with
3717 * no warranty either expressed or implied, to the extent permitted by law.
3719 * For more free and public domain JavaScript code by the same author, visit:
3720 * http://www.safalra.com/web-design/javascript/
3723 Roo.lib.Color = function() { }
3726 Roo.apply(Roo.lib.Color.prototype, {
3734 * @return {Object} an object representing the RGBA components of this Color. The red,
3735 * green, and blue components are converted to integers in the range [0,255].
3736 * The alpha is a value in the range [0,1].
3738 getIntegerRGB : function(){
3740 // get the RGB components of this Color
3741 var rgb = this.getRGB();
3743 // return the integer components
3745 'r' : Math.round(rgb.r),
3746 'g' : Math.round(rgb.g),
3747 'b' : Math.round(rgb.b),
3755 * @return {Object} an object representing the RGBA components of this Color. The red,
3756 * green, and blue components are converted to numbers in the range [0,100].
3757 * The alpha is a value in the range [0,1].
3759 getPercentageRGB : function(){
3761 // get the RGB components of this Color
3762 var rgb = this.getRGB();
3764 // return the percentage components
3766 'r' : 100 * rgb.r / 255,
3767 'g' : 100 * rgb.g / 255,
3768 'b' : 100 * rgb.b / 255,
3775 * getCSSHexadecimalRGB
3776 * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777 * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778 * are two-digit hexadecimal numbers.
3780 getCSSHexadecimalRGB : function()
3783 // get the integer RGB components
3784 var rgb = this.getIntegerRGB();
3786 // determine the hexadecimal equivalents
3787 var r16 = rgb.r.toString(16);
3788 var g16 = rgb.g.toString(16);
3789 var b16 = rgb.b.toString(16);
3791 // return the CSS RGB Color value
3793 + (r16.length == 2 ? r16 : '0' + r16)
3794 + (g16.length == 2 ? g16 : '0' + g16)
3795 + (b16.length == 2 ? b16 : '0' + b16);
3801 * @return {String} a string representing this Color as a CSS integer RGB Color
3802 * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803 * are integers in the range [0,255].
3805 getCSSIntegerRGB : function(){
3807 // get the integer RGB components
3808 var rgb = this.getIntegerRGB();
3810 // return the CSS RGB Color value
3811 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3817 * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818 * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819 * b are integers in the range [0,255] and a is in the range [0,1].
3821 getCSSIntegerRGBA : function(){
3823 // get the integer RGB components
3824 var rgb = this.getIntegerRGB();
3826 // return the CSS integer RGBA Color value
3827 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3832 * getCSSPercentageRGB
3833 * @return {String} a string representing this Color as a CSS percentage RGB Color
3834 * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835 * b are in the range [0,100].
3837 getCSSPercentageRGB : function(){
3839 // get the percentage RGB components
3840 var rgb = this.getPercentageRGB();
3842 // return the CSS RGB Color value
3843 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3848 * getCSSPercentageRGBA
3849 * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850 * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851 * and b are in the range [0,100] and a is in the range [0,1].
3853 getCSSPercentageRGBA : function(){
3855 // get the percentage RGB components
3856 var rgb = this.getPercentageRGB();
3858 // return the CSS percentage RGBA Color value
3859 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3865 * @return {String} a string representing this Color as a CSS HSL Color value - that
3866 * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867 * s and l are in the range [0,100].
3869 getCSSHSL : function(){
3871 // get the HSL components
3872 var hsl = this.getHSL();
3874 // return the CSS HSL Color value
3875 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3881 * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882 * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883 * s and l are in the range [0,100], and a is in the range [0,1].
3885 getCSSHSLA : function(){
3887 // get the HSL components
3888 var hsl = this.getHSL();
3890 // return the CSS HSL Color value
3891 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3896 * Sets the Color of the specified node to this Color. This functions sets
3897 * the CSS 'color' property for the node. The parameter is:
3899 * @param {DomElement} node - the node whose Color should be set
3901 setNodeColor : function(node){
3903 // set the Color of the node
3904 node.style.color = this.getCSSHexadecimalRGB();
3909 * Sets the background Color of the specified node to this Color. This
3910 * functions sets the CSS 'background-color' property for the node. The
3913 * @param {DomElement} node - the node whose background Color should be set
3915 setNodeBackgroundColor : function(node){
3917 // set the background Color of the node
3918 node.style.backgroundColor = this.getCSSHexadecimalRGB();
3921 // convert between formats..
3924 var r = this.getIntegerRGB();
3925 return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3930 var hsl = this.getHSL();
3931 // return the CSS HSL Color value
3932 return new Roo.lib.HSLColor(hsl.h, hsl.s, hsl.l , hsl.a );
3938 var rgb = this.toRGB();
3939 var hsv = rgb.getHSV();
3940 // return the CSS HSL Color value
3941 return new Roo.lib.HSVColor(hsv.h, hsv.s, hsv.v , hsv.a );
3945 // modify v = 0 ... 1 (eg. 0.5)
3946 saturate : function(v)
3948 var rgb = this.toRGB();
3949 var hsv = rgb.getHSV();
3950 return new Roo.lib.HSVColor(hsv.h, hsv.s * v, hsv.v , hsv.a );
3958 * @return {Object} the RGB and alpha components of this Color as an object with r,
3959 * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3964 // return the RGB components
3976 * @return {Object} the HSV and alpha components of this Color as an object with h,
3977 * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978 * [0,100], and a is in the range [0,1].
3983 // calculate the HSV components if necessary
3984 if (this.hsv == null) {
3985 this.calculateHSV();
3988 // return the HSV components
4000 * @return {Object} the HSL and alpha components of this Color as an object with h,
4001 * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002 * [0,100], and a is in the range [0,1].
4004 getHSL : function(){
4007 // calculate the HSV components if necessary
4008 if (this.hsl == null) { this.calculateHSL(); }
4010 // return the HSL components
4025 * @class Roo.lib.RGBColor
4026 * @extends Roo.lib.Color
4027 * Creates a Color specified in the RGB Color space, with an optional alpha
4028 * component. The parameters are:
4032 * @param {Number} r - the red component, clipped to the range [0,255]
4033 * @param {Number} g - the green component, clipped to the range [0,255]
4034 * @param {Number} b - the blue component, clipped to the range [0,255]
4035 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036 * optional and defaults to 1
4038 Roo.lib.RGBColor = function (r, g, b, a){
4040 // store the alpha component after clipping it if necessary
4041 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4043 // store the RGB components after clipping them if necessary
4046 'r' : Math.max(0, Math.min(255, r)),
4047 'g' : Math.max(0, Math.min(255, g)),
4048 'b' : Math.max(0, Math.min(255, b))
4051 // initialise the HSV and HSL components to null
4055 * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056 * range [0,360). The parameters are:
4058 * maximum - the maximum of the RGB component values
4059 * range - the range of the RGB component values
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4068 getHue : function(maximum, range)
4072 // check whether the range is zero
4075 // set the hue to zero (any hue is acceptable as the Color is grey)
4080 // determine which of the components has the highest value and set the hue
4083 // red has the highest value
4085 var hue = (rgb.g - rgb.b) / range * 60;
4086 if (hue < 0) { hue += 360; }
4089 // green has the highest value
4091 var hue = (rgb.b - rgb.r) / range * 60 + 120;
4094 // blue has the highest value
4096 var hue = (rgb.r - rgb.g) / range * 60 + 240;
4108 /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109 * be returned be the getHSV function.
4111 calculateHSV : function(){
4113 // get the maximum and range of the RGB component values
4114 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4117 // store the HSV components
4120 'h' : this.getHue(maximum, range),
4121 's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122 'v' : maximum / 2.55
4127 /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128 * be returned be the getHSL function.
4130 calculateHSL : function(){
4132 // get the maximum and range of the RGB component values
4133 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4136 // determine the lightness in the range [0,1]
4137 var l = maximum / 255 - range / 510;
4139 // store the HSL components
4142 'h' : this.getHue(maximum, range),
4143 's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4152 * @class Roo.lib.HSVColor
4153 * @extends Roo.lib.Color
4154 * Creates a Color specified in the HSV Color space, with an optional alpha
4155 * component. The parameters are:
4158 * @param {Number} h - the hue component, wrapped to the range [0,360)
4159 * @param {Number} s - the saturation component, clipped to the range [0,100]
4160 * @param {Number} v - the value component, clipped to the range [0,100]
4161 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162 * optional and defaults to 1
4164 Roo.lib.HSVColor = function (h, s, v, a){
4166 // store the alpha component after clipping it if necessary
4167 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4169 // store the HSV components after clipping or wrapping them if necessary
4172 'h' : (h % 360 + 360) % 360,
4173 's' : Math.max(0, Math.min(100, s)),
4174 'v' : Math.max(0, Math.min(100, v))
4177 // initialise the RGB and HSL components to null
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183 /* Calculates and stores the RGB components of this HSVColor so that they can
4184 * be returned be the getRGB function.
4186 calculateRGB: function ()
4189 // check whether the saturation is zero
4192 // set the Color to the appropriate shade of grey
4199 // set some temporary values
4200 var f = hsv.h / 60 - Math.floor(hsv.h / 60);
4201 var p = hsv.v * (1 - hsv.s / 100);
4202 var q = hsv.v * (1 - hsv.s / 100 * f);
4203 var t = hsv.v * (1 - hsv.s / 100 * (1 - f));
4205 // set the RGB Color components to their temporary values
4206 switch (Math.floor(hsv.h / 60)){
4207 case 0: var r = hsv.v; var g = t; var b = p; break;
4208 case 1: var r = q; var g = hsv.v; var b = p; break;
4209 case 2: var r = p; var g = hsv.v; var b = t; break;
4210 case 3: var r = p; var g = q; var b = hsv.v; break;
4211 case 4: var r = t; var g = p; var b = hsv.v; break;
4212 case 5: var r = hsv.v; var g = p; var b = q; break;
4217 // store the RGB components
4227 /* Calculates and stores the HSL components of this HSVColor so that they can
4228 * be returned be the getHSL function.
4230 calculateHSL : function (){
4233 // determine the lightness in the range [0,100]
4234 var l = (2 - hsv.s / 100) * hsv.v / 2;
4236 // store the HSL components
4240 's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4244 // correct a division-by-zero error
4245 if (isNaN(hsl.s)) { hsl.s = 0; }
4254 * @class Roo.lib.HSLColor
4255 * @extends Roo.lib.Color
4258 * Creates a Color specified in the HSL Color space, with an optional alpha
4259 * component. The parameters are:
4261 * @param {Number} h - the hue component, wrapped to the range [0,360)
4262 * @param {Number} s - the saturation component, clipped to the range [0,100]
4263 * @param {Number} l - the lightness component, clipped to the range [0,100]
4264 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265 * optional and defaults to 1
4268 Roo.lib.HSLColor = function(h, s, l, a){
4270 // store the alpha component after clipping it if necessary
4271 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4273 // store the HSL components after clipping or wrapping them if necessary
4276 'h' : (h % 360 + 360) % 360,
4277 's' : Math.max(0, Math.min(100, s)),
4278 'l' : Math.max(0, Math.min(100, l))
4281 // initialise the RGB and HSV components to null
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4286 /* Calculates and stores the RGB components of this HSLColor so that they can
4287 * be returned be the getRGB function.
4289 calculateRGB: function (){
4291 // check whether the saturation is zero
4292 if (this.hsl.s == 0){
4294 // store the RGB components representing the appropriate shade of grey
4297 'r' : this.hsl.l * 2.55,
4298 'g' : this.hsl.l * 2.55,
4299 'b' : this.hsl.l * 2.55
4304 // set some temporary values
4305 var p = this.hsl.l < 50
4306 ? this.hsl.l * (1 + hsl.s / 100)
4307 : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308 var q = 2 * hsl.l - p;
4310 // initialise the RGB components
4313 'r' : (h + 120) / 60 % 6,
4315 'b' : (h + 240) / 60 % 6
4318 // loop over the RGB components
4319 for (var key in this.rgb){
4321 // ensure that the property is not inherited from the root object
4322 if (this.rgb.hasOwnProperty(key)){
4324 // set the component to its value in the range [0,100]
4325 if (this.rgb[key] < 1){
4326 this.rgb[key] = q + (p - q) * this.rgb[key];
4327 }else if (this.rgb[key] < 3){
4329 }else if (this.rgb[key] < 4){
4330 this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4335 // set the component to its value in the range [0,255]
4336 this.rgb[key] *= 2.55;
4346 /* Calculates and stores the HSV components of this HSLColor so that they can
4347 * be returned be the getHSL function.
4349 calculateHSV : function(){
4351 // set a temporary value
4352 var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4354 // store the HSV components
4358 's' : 200 * t / (this.hsl.l + t),
4359 'v' : t + this.hsl.l
4362 // correct a division-by-zero error
4363 if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4370 * Portions of this file are based on pieces of Yahoo User Interface Library
4371 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372 * YUI licensed under the BSD License:
4373 * http://developer.yahoo.net/yui/license.txt
4374 * <script type="text/javascript">
4379 Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380 Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4383 Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4385 var fly = Roo.lib.AnimBase.fly;
4387 var superclass = Y.ColorAnim.superclass;
4388 var proto = Y.ColorAnim.prototype;
4390 proto.toString = function() {
4391 var el = this.getEl();
4392 var id = el.id || el.tagName;
4393 return ("ColorAnim " + id);
4396 proto.patterns.color = /color$/i;
4397 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4403 proto.parseColor = function(s) {
4404 if (s.length == 3) {
4408 var c = this.patterns.hex.exec(s);
4409 if (c && c.length == 4) {
4410 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4413 c = this.patterns.rgb.exec(s);
4414 if (c && c.length == 4) {
4415 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4418 c = this.patterns.hex3.exec(s);
4419 if (c && c.length == 4) {
4420 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4425 // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426 proto.getAttribute = function(attr) {
4427 var el = this.getEl();
4428 if (this.patterns.color.test(attr)) {
4429 var val = fly(el).getStyle(attr);
4431 if (this.patterns.transparent.test(val)) {
4432 var parent = el.parentNode;
4433 val = fly(parent).getStyle(attr);
4435 while (parent && this.patterns.transparent.test(val)) {
4436 parent = parent.parentNode;
4437 val = fly(parent).getStyle(attr);
4438 if (parent.tagName.toUpperCase() == 'HTML') {
4444 val = superclass.getAttribute.call(this, attr);
4449 proto.getAttribute = function(attr) {
4450 var el = this.getEl();
4451 if (this.patterns.color.test(attr)) {
4452 var val = fly(el).getStyle(attr);
4454 if (this.patterns.transparent.test(val)) {
4455 var parent = el.parentNode;
4456 val = fly(parent).getStyle(attr);
4458 while (parent && this.patterns.transparent.test(val)) {
4459 parent = parent.parentNode;
4460 val = fly(parent).getStyle(attr);
4461 if (parent.tagName.toUpperCase() == 'HTML') {
4467 val = superclass.getAttribute.call(this, attr);
4473 proto.doMethod = function(attr, start, end) {
4476 if (this.patterns.color.test(attr)) {
4478 for (var i = 0, len = start.length; i < len; ++i) {
4479 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4482 val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4485 val = superclass.doMethod.call(this, attr, start, end);
4491 proto.setRuntimeAttribute = function(attr) {
4492 superclass.setRuntimeAttribute.call(this, attr);
4494 if (this.patterns.color.test(attr)) {
4495 var attributes = this.attributes;
4496 var start = this.parseColor(this.runtimeAttributes[attr].start);
4497 var end = this.parseColor(this.runtimeAttributes[attr].end);
4499 if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500 end = this.parseColor(attributes[attr].by);
4502 for (var i = 0, len = start.length; i < len; ++i) {
4503 end[i] = start[i] + end[i];
4507 this.runtimeAttributes[attr].start = start;
4508 this.runtimeAttributes[attr].end = end;
4514 * Portions of this file are based on pieces of Yahoo User Interface Library
4515 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516 * YUI licensed under the BSD License:
4517 * http://developer.yahoo.net/yui/license.txt
4518 * <script type="text/javascript">
4524 easeNone: function (t, b, c, d) {
4525 return c * t / d + b;
4529 easeIn: function (t, b, c, d) {
4530 return c * (t /= d) * t + b;
4534 easeOut: function (t, b, c, d) {
4535 return -c * (t /= d) * (t - 2) + b;
4539 easeBoth: function (t, b, c, d) {
4540 if ((t /= d / 2) < 1) {
4541 return c / 2 * t * t + b;
4544 return -c / 2 * ((--t) * (t - 2) - 1) + b;
4548 easeInStrong: function (t, b, c, d) {
4549 return c * (t /= d) * t * t * t + b;
4553 easeOutStrong: function (t, b, c, d) {
4554 return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4558 easeBothStrong: function (t, b, c, d) {
4559 if ((t /= d / 2) < 1) {
4560 return c / 2 * t * t * t * t + b;
4563 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4568 elasticIn: function (t, b, c, d, a, p) {
4572 if ((t /= d) == 1) {
4579 if (!a || a < Math.abs(c)) {
4584 var s = p / (2 * Math.PI) * Math.asin(c / a);
4587 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4591 elasticOut: function (t, b, c, d, a, p) {
4595 if ((t /= d) == 1) {
4602 if (!a || a < Math.abs(c)) {
4607 var s = p / (2 * Math.PI) * Math.asin(c / a);
4610 return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4614 elasticBoth: function (t, b, c, d, a, p) {
4619 if ((t /= d / 2) == 2) {
4627 if (!a || a < Math.abs(c)) {
4632 var s = p / (2 * Math.PI) * Math.asin(c / a);
4636 return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637 Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4639 return a * Math.pow(2, -10 * (t -= 1)) *
4640 Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4645 backIn: function (t, b, c, d, s) {
4646 if (typeof s == 'undefined') {
4649 return c * (t /= d) * t * ((s + 1) * t - s) + b;
4653 backOut: function (t, b, c, d, s) {
4654 if (typeof s == 'undefined') {
4657 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4661 backBoth: function (t, b, c, d, s) {
4662 if (typeof s == 'undefined') {
4666 if ((t /= d / 2 ) < 1) {
4667 return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4669 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4673 bounceIn: function (t, b, c, d) {
4674 return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4678 bounceOut: function (t, b, c, d) {
4679 if ((t /= d) < (1 / 2.75)) {
4680 return c * (7.5625 * t * t) + b;
4681 } else if (t < (2 / 2.75)) {
4682 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683 } else if (t < (2.5 / 2.75)) {
4684 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4686 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4690 bounceBoth: function (t, b, c, d) {
4692 return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4694 return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4697 * Portions of this file are based on pieces of Yahoo User Interface Library
4698 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699 * YUI licensed under the BSD License:
4700 * http://developer.yahoo.net/yui/license.txt
4701 * <script type="text/javascript">
4705 Roo.lib.Motion = function(el, attributes, duration, method) {
4707 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4711 Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4715 var superclass = Y.Motion.superclass;
4716 var proto = Y.Motion.prototype;
4718 proto.toString = function() {
4719 var el = this.getEl();
4720 var id = el.id || el.tagName;
4721 return ("Motion " + id);
4724 proto.patterns.points = /^points$/i;
4726 proto.setAttribute = function(attr, val, unit) {
4727 if (this.patterns.points.test(attr)) {
4728 unit = unit || 'px';
4729 superclass.setAttribute.call(this, 'left', val[0], unit);
4730 superclass.setAttribute.call(this, 'top', val[1], unit);
4732 superclass.setAttribute.call(this, attr, val, unit);
4736 proto.getAttribute = function(attr) {
4737 if (this.patterns.points.test(attr)) {
4739 superclass.getAttribute.call(this, 'left'),
4740 superclass.getAttribute.call(this, 'top')
4743 val = superclass.getAttribute.call(this, attr);
4749 proto.doMethod = function(attr, start, end) {
4752 if (this.patterns.points.test(attr)) {
4753 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4756 val = superclass.doMethod.call(this, attr, start, end);
4761 proto.setRuntimeAttribute = function(attr) {
4762 if (this.patterns.points.test(attr)) {
4763 var el = this.getEl();
4764 var attributes = this.attributes;
4766 var control = attributes['points']['control'] || [];
4770 if (control.length > 0 && !(control[0] instanceof Array)) {
4771 control = [control];
4774 for (i = 0,len = control.length; i < len; ++i) {
4775 tmp[i] = control[i];
4780 Roo.fly(el).position();
4782 if (isset(attributes['points']['from'])) {
4783 Roo.lib.Dom.setXY(el, attributes['points']['from']);
4786 Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4789 start = this.getAttribute('points');
4792 if (isset(attributes['points']['to'])) {
4793 end = translateValues.call(this, attributes['points']['to'], start);
4795 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796 for (i = 0,len = control.length; i < len; ++i) {
4797 control[i] = translateValues.call(this, control[i], start);
4801 } else if (isset(attributes['points']['by'])) {
4802 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4804 for (i = 0,len = control.length; i < len; ++i) {
4805 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4809 this.runtimeAttributes[attr] = [start];
4811 if (control.length > 0) {
4812 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4815 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4818 superclass.setRuntimeAttribute.call(this, attr);
4822 var translateValues = function(val, start) {
4823 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4829 var isset = function(prop) {
4830 return (typeof prop !== 'undefined');
4834 * Portions of this file are based on pieces of Yahoo User Interface Library
4835 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836 * YUI licensed under the BSD License:
4837 * http://developer.yahoo.net/yui/license.txt
4838 * <script type="text/javascript">
4842 Roo.lib.Scroll = function(el, attributes, duration, method) {
4844 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4848 Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4852 var superclass = Y.Scroll.superclass;
4853 var proto = Y.Scroll.prototype;
4855 proto.toString = function() {
4856 var el = this.getEl();
4857 var id = el.id || el.tagName;
4858 return ("Scroll " + id);
4861 proto.doMethod = function(attr, start, end) {
4864 if (attr == 'scroll') {
4866 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4871 val = superclass.doMethod.call(this, attr, start, end);
4876 proto.getAttribute = function(attr) {
4878 var el = this.getEl();
4880 if (attr == 'scroll') {
4881 val = [ el.scrollLeft, el.scrollTop ];
4883 val = superclass.getAttribute.call(this, attr);
4889 proto.setAttribute = function(attr, val, unit) {
4890 var el = this.getEl();
4892 if (attr == 'scroll') {
4893 el.scrollLeft = val[0];
4894 el.scrollTop = val[1];
4896 superclass.setAttribute.call(this, attr, val, unit);
4901 * Originally based of this code... - refactored for Roo...
4902 * https://github.com/aaalsaleh/undo-manager
4905 * @author Abdulrahman Alsaleh
4906 * @copyright 2015 Abdulrahman Alsaleh
4907 * @license MIT License (c)
4909 * Hackily modifyed by alan@roojs.com
4914 * TOTALLY UNTESTED...
4916 * Documentation to be done....
4921 * @class Roo.lib.UndoManager
4922 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4923 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4929 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4933 * For more information see this blog post with examples:
4934 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4935 - Create Elements using DOM, HTML fragments and Templates</a>.
4937 * @param {Number} limit how far back to go ... use 1000?
4938 * @param {Object} scope usually use document..
4941 Roo.lib.UndoManager = function (limit, undoScopeHost)
4945 this.scope = undoScopeHost;
4946 this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4947 if (this.fireEvent) {
4953 Roo.lib.UndoManager.prototype = {
4964 * To push and execute a transaction, the method undoManager.transact
4965 * must be called by passing a transaction object as the first argument, and a merge
4966 * flag as the second argument. A transaction object has the following properties:
4970 undoManager.transact({
4972 execute: function() { ... },
4973 undo: function() { ... },
4974 // redo same as execute
4975 redo: function() { this.execute(); }
4978 // merge transaction
4979 undoManager.transact({
4981 execute: function() { ... }, // this will be run...
4982 undo: function() { ... }, // what to do when undo is run.
4983 // redo same as execute
4984 redo: function() { this.execute(); }
4989 * @param {Object} transaction The transaction to add to the stack.
4990 * @return {String} The HTML fragment
4994 transact : function (transaction, merge)
4996 if (arguments.length < 2) {
4997 throw new TypeError('Not enough arguments to UndoManager.transact.');
5000 transaction.execute();
5002 this.stack.splice(0, this.position);
5003 if (merge && this.length) {
5004 this.stack[0].push(transaction);
5006 this.stack.unshift([transaction]);
5011 if (this.limit && this.stack.length > this.limit) {
5012 this.length = this.stack.length = this.limit;
5014 this.length = this.stack.length;
5017 if (this.fireEvent) {
5018 this.scope.dispatchEvent(
5019 new CustomEvent('DOMTransaction', {
5021 transactions: this.stack[0].slice()
5032 if (this.position < this.length) {
5033 for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5034 this.stack[this.position][i].undo();
5038 if (this.fireEvent) {
5039 this.scope.dispatchEvent(
5040 new CustomEvent('undo', {
5042 transactions: this.stack[this.position - 1].slice()
5054 if (this.position > 0) {
5055 for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5056 this.stack[this.position - 1][i].redo();
5060 if (this.fireEvent) {
5061 this.scope.dispatchEvent(
5062 new CustomEvent('redo', {
5064 transactions: this.stack[this.position].slice()
5074 item : function (index)
5076 if (index >= 0 && index < this.length) {
5077 return this.stack[index].slice();
5082 clearUndo : function () {
5083 this.stack.length = this.length = this.position;
5086 clearRedo : function () {
5087 this.stack.splice(0, this.position);
5089 this.length = this.stack.length;
5092 * Reset the undo - probaly done on load to clear all history.
5099 this.current_html = this.scope.innerHTML;
5100 if (this.timer !== false) {
5101 clearTimeout(this.timer);
5112 // this will handle the undo/redo on the element.?
5113 bindEvents : function()
5115 var el = this.scope;
5116 el.undoManager = this;
5119 this.scope.addEventListener('keydown', function(e) {
5120 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5122 el.undoManager.redo(); // Ctrl/Command + Shift + Z
5124 el.undoManager.undo(); // Ctrl/Command + Z
5134 el.addEventListener('input', function(e) {
5135 if(el.innerHTML == t.current_html) {
5138 // only record events every second.
5139 if (t.timer !== false) {
5140 clearTimeout(t.timer);
5143 t.timer = setTimeout(function() { t.merge = false; }, 1000);
5145 t.addEvent(t.merge);
5146 t.merge = true; // ignore changes happening every second..
5150 * Manually add an event.
5151 * Normall called without arguements - and it will just get added to the stack.
5155 addEvent : function(merge)
5157 // not sure if this should clear the timer
5158 merge = typeof(merge) == 'undefined' ? false : merge;
5160 el.undoManager.transact({
5161 oldHTML: this.current_html,
5162 newHTML: this.scope.innerHTML,
5163 // nothing to execute (content already changed when input is fired)
5164 execute: function() { },
5166 this.scope.innerHTML = this.current_html = this.oldHTML;
5169 this.scope.innerHTML = this.current_html = this.newHTML;
5175 this.current_html = el.innerHTML;
5186 * Ext JS Library 1.1.1
5187 * Copyright(c) 2006-2007, Ext JS, LLC.
5189 * Originally Released Under LGPL - original licence link has changed is not relivant.
5192 * <script type="text/javascript">
5196 // nasty IE9 hack - what a pile of crap that is..
5198 if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5199 Range.prototype.createContextualFragment = function (html) {
5200 var doc = window.document;
5201 var container = doc.createElement("div");
5202 container.innerHTML = html;
5203 var frag = doc.createDocumentFragment(), n;
5204 while ((n = container.firstChild)) {
5205 frag.appendChild(n);
5212 * @class Roo.DomHelper
5213 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5214 * 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>.
5217 Roo.DomHelper = function(){
5218 var tempTableEl = null;
5219 var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5220 var tableRe = /^table|tbody|tr|td$/i;
5222 // build as innerHTML where available
5224 var createHtml = function(o){
5225 if(typeof o == 'string'){
5234 if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5235 if(attr == "style"){
5237 if(typeof s == "function"){
5240 if(typeof s == "string"){
5241 b += ' style="' + s + '"';
5242 }else if(typeof s == "object"){
5245 if(typeof s[key] != "function"){
5246 b += key + ":" + s[key] + ";";
5253 b += ' class="' + o["cls"] + '"';
5254 }else if(attr == "htmlFor"){
5255 b += ' for="' + o["htmlFor"] + '"';
5257 b += " " + attr + '="' + o[attr] + '"';
5261 if(emptyTags.test(o.tag)){
5265 var cn = o.children || o.cn;
5267 //http://bugs.kde.org/show_bug.cgi?id=71506
5268 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5269 for(var i = 0, len = cn.length; i < len; i++) {
5270 b += createHtml(cn[i], b);
5273 b += createHtml(cn, b);
5279 b += "</" + o.tag + ">";
5286 var createDom = function(o, parentNode){
5288 // defininition craeted..
5290 if (o.ns && o.ns != 'html') {
5292 if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5293 xmlns[o.ns] = o.xmlns;
5296 if (typeof(xmlns[o.ns]) == 'undefined') {
5297 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5303 if (typeof(o) == 'string') {
5304 return parentNode.appendChild(document.createTextNode(o));
5306 o.tag = o.tag || div;
5307 if (o.ns && Roo.isIE) {
5309 o.tag = o.ns + ':' + o.tag;
5312 var el = ns ? document.createElementNS( ns, o.tag||'div') : document.createElement(o.tag||'div');
5313 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5316 if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" ||
5317 attr == "style" || typeof o[attr] == "function") { continue; }
5319 if(attr=="cls" && Roo.isIE){
5320 el.className = o["cls"];
5322 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5328 Roo.DomHelper.applyStyles(el, o.style);
5329 var cn = o.children || o.cn;
5331 //http://bugs.kde.org/show_bug.cgi?id=71506
5332 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5333 for(var i = 0, len = cn.length; i < len; i++) {
5334 createDom(cn[i], el);
5341 el.innerHTML = o.html;
5344 parentNode.appendChild(el);
5349 var ieTable = function(depth, s, h, e){
5350 tempTableEl.innerHTML = [s, h, e].join('');
5351 var i = -1, el = tempTableEl;
5352 while(++i < depth && el.firstChild){
5358 // kill repeat to save bytes
5362 tbe = '</tbody>'+te,
5368 * Nasty code for IE's broken table implementation
5370 var insertIntoTable = function(tag, where, el, html){
5372 tempTableEl = document.createElement('div');
5377 if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5380 if(where == 'beforebegin'){
5384 before = el.nextSibling;
5387 node = ieTable(4, trs, html, tre);
5389 else if(tag == 'tr'){
5390 if(where == 'beforebegin'){
5393 node = ieTable(3, tbs, html, tbe);
5394 } else if(where == 'afterend'){
5395 before = el.nextSibling;
5397 node = ieTable(3, tbs, html, tbe);
5398 } else{ // INTO a TR
5399 if(where == 'afterbegin'){
5400 before = el.firstChild;
5402 node = ieTable(4, trs, html, tre);
5404 } else if(tag == 'tbody'){
5405 if(where == 'beforebegin'){
5408 node = ieTable(2, ts, html, te);
5409 } else if(where == 'afterend'){
5410 before = el.nextSibling;
5412 node = ieTable(2, ts, html, te);
5414 if(where == 'afterbegin'){
5415 before = el.firstChild;
5417 node = ieTable(3, tbs, html, tbe);
5420 if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5423 if(where == 'afterbegin'){
5424 before = el.firstChild;
5426 node = ieTable(2, ts, html, te);
5428 el.insertBefore(node, before);
5432 // this is a bit like the react update code...
5435 var updateNode = function(from, to)
5437 // should we handle non-standard elements?
5438 Roo.log(["UpdateNode" , from, to]);
5439 if (from.nodeType != to.nodeType) {
5440 Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5441 from.parentNode.replaceChild(to, from);
5444 if (from.nodeType == 3) {
5445 // assume it's text?!
5446 if (from.data == to.data) {
5449 from.data = to.data;
5453 // assume 'to' doesnt have '1/3 nodetypes!
5454 if (from.nodeType !=1 || from.tagName != to.tagName) {
5455 Roo.log(["ReplaceChild" , from, to ]);
5456 from.parentNode.replaceChild(to, from);
5459 // compare attributes
5460 var ar = Array.from(from.attributes);
5461 for(var i = 0; i< ar.length;i++) {
5462 if (to.hasAttribute(ar[i].name)) {
5465 if (ar[i].name == 'id') { // always keep ids?
5468 from.removeAttribute(ar[i].name);
5471 for(var i = 0; i< ar.length;i++) {
5472 if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5475 from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5478 var far = Array.from(from.childNodes);
5479 var tar = Array.from(to.childNodes);
5480 // if the lengths are different.. then it's probably a editable content change, rather than
5481 // a change of the block definition..
5483 // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5484 /*if (from.innerHTML == to.innerHTML) {
5487 if (far.length != tar.length) {
5488 from.innerHTML = to.innerHTML;
5493 for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5494 if (i >= far.length) {
5495 from.appendChild(tar[i]);
5496 Roo.log(["add", tar[i]]);
5498 } else if ( i >= tar.length) {
5499 from.removeChild(far[i]);
5500 Roo.log(["remove", far[i]]);
5503 updateNode(far[i], tar[i]);
5515 /** True to force the use of DOM instead of html fragments @type Boolean */
5519 * Returns the markup for the passed Element(s) config
5520 * @param {Object} o The Dom object spec (and children)
5523 markup : function(o){
5524 return createHtml(o);
5528 * Applies a style specification to an element
5529 * @param {String/HTMLElement} el The element to apply styles to
5530 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5531 * a function which returns such a specification.
5533 applyStyles : function(el, styles){
5536 if(typeof styles == "string"){
5537 var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5539 while ((matches = re.exec(styles)) != null){
5540 el.setStyle(matches[1], matches[2]);
5542 }else if (typeof styles == "object"){
5543 for (var style in styles){
5544 el.setStyle(style, styles[style]);
5546 }else if (typeof styles == "function"){
5547 Roo.DomHelper.applyStyles(el, styles.call());
5553 * Inserts an HTML fragment into the Dom
5554 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5555 * @param {HTMLElement} el The context element
5556 * @param {String} html The HTML fragmenet
5557 * @return {HTMLElement} The new node
5559 insertHtml : function(where, el, html){
5560 where = where.toLowerCase();
5561 if(el.insertAdjacentHTML){
5562 if(tableRe.test(el.tagName)){
5564 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5570 el.insertAdjacentHTML('BeforeBegin', html);
5571 return el.previousSibling;
5573 el.insertAdjacentHTML('AfterBegin', html);
5574 return el.firstChild;
5576 el.insertAdjacentHTML('BeforeEnd', html);
5577 return el.lastChild;
5579 el.insertAdjacentHTML('AfterEnd', html);
5580 return el.nextSibling;
5582 throw 'Illegal insertion point -> "' + where + '"';
5584 var range = el.ownerDocument.createRange();
5588 range.setStartBefore(el);
5589 frag = range.createContextualFragment(html);
5590 el.parentNode.insertBefore(frag, el);
5591 return el.previousSibling;
5594 range.setStartBefore(el.firstChild);
5595 frag = range.createContextualFragment(html);
5596 el.insertBefore(frag, el.firstChild);
5597 return el.firstChild;
5599 el.innerHTML = html;
5600 return el.firstChild;
5604 range.setStartAfter(el.lastChild);
5605 frag = range.createContextualFragment(html);
5606 el.appendChild(frag);
5607 return el.lastChild;
5609 el.innerHTML = html;
5610 return el.lastChild;
5613 range.setStartAfter(el);
5614 frag = range.createContextualFragment(html);
5615 el.parentNode.insertBefore(frag, el.nextSibling);
5616 return el.nextSibling;
5618 throw 'Illegal insertion point -> "' + where + '"';
5622 * Creates new Dom element(s) and inserts them before el
5623 * @param {String/HTMLElement/Element} el The context element
5624 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5625 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5626 * @return {HTMLElement/Roo.Element} The new node
5628 insertBefore : function(el, o, returnElement){
5629 return this.doInsert(el, o, returnElement, "beforeBegin");
5633 * Creates new Dom element(s) and inserts them after el
5634 * @param {String/HTMLElement/Element} el The context element
5635 * @param {Object} o The Dom object spec (and children)
5636 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5637 * @return {HTMLElement/Roo.Element} The new node
5639 insertAfter : function(el, o, returnElement){
5640 return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5644 * Creates new Dom element(s) and inserts them as the first child of el
5645 * @param {String/HTMLElement/Element} el The context element
5646 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5647 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5648 * @return {HTMLElement/Roo.Element} The new node
5650 insertFirst : function(el, o, returnElement){
5651 return this.doInsert(el, o, returnElement, "afterBegin");
5655 doInsert : function(el, o, returnElement, pos, sibling){
5656 el = Roo.getDom(el);
5658 if(this.useDom || o.ns){
5659 newNode = createDom(o, null);
5660 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5662 var html = createHtml(o);
5663 newNode = this.insertHtml(pos, el, html);
5665 return returnElement ? Roo.get(newNode, true) : newNode;
5669 * Creates new Dom element(s) and appends them to el
5670 * @param {String/HTMLElement/Element} el The context element
5671 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5672 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5673 * @return {HTMLElement/Roo.Element} The new node
5675 append : function(el, o, returnElement){
5676 el = Roo.getDom(el);
5678 if(this.useDom || o.ns){
5679 newNode = createDom(o, null);
5680 el.appendChild(newNode);
5682 var html = createHtml(o);
5683 newNode = this.insertHtml("beforeEnd", el, html);
5685 return returnElement ? Roo.get(newNode, true) : newNode;
5689 * Creates new Dom element(s) and overwrites the contents of el with them
5690 * @param {String/HTMLElement/Element} el The context element
5691 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5692 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5693 * @return {HTMLElement/Roo.Element} The new node
5695 overwrite : function(el, o, returnElement)
5697 el = Roo.getDom(el);
5700 while (el.childNodes.length) {
5701 el.removeChild(el.firstChild);
5705 el.innerHTML = createHtml(o);
5708 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5712 * Creates a new Roo.DomHelper.Template from the Dom object spec
5713 * @param {Object} o The Dom object spec (and children)
5714 * @return {Roo.DomHelper.Template} The new template
5716 createTemplate : function(o){
5717 var html = createHtml(o);
5718 return new Roo.Template(html);
5721 * Updates the first element with the spec from the o (replacing if necessary)
5722 * This iterates through the children, and updates attributes / children etc..
5723 * @param {String/HTMLElement/Element} el The context element
5724 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5727 update : function(el, o)
5729 updateNode(Roo.getDom(el), createDom(o));
5738 * Ext JS Library 1.1.1
5739 * Copyright(c) 2006-2007, Ext JS, LLC.
5741 * Originally Released Under LGPL - original licence link has changed is not relivant.
5744 * <script type="text/javascript">
5748 * @class Roo.Template
5749 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5750 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5753 var t = new Roo.Template({
5754 html : '<div name="{id}">' +
5755 '<span class="{cls}">{name:trim} {someval:this.myformat}{value:ellipsis(10)}</span>' +
5757 myformat: function (value, allValues) {
5758 return 'XX' + value;
5761 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5763 * For more information see this blog post with examples:
5764 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5765 - Create Elements using DOM, HTML fragments and Templates</a>.
5767 * @param {Object} cfg - Configuration object.
5769 Roo.Template = function(cfg){
5771 if(cfg instanceof Array){
5773 }else if(arguments.length > 1){
5774 cfg = Array.prototype.join.call(arguments, "");
5778 if (typeof(cfg) == 'object') {
5789 Roo.Template.prototype = {
5792 * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5798 * @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..
5799 * it should be fixed so that template is observable...
5803 * @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5811 * Returns an HTML fragment of this template with the specified values applied.
5812 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5813 * @return {String} The HTML fragment
5818 applyTemplate : function(values){
5819 //Roo.log(["applyTemplate", values]);
5823 return this.compiled(values);
5825 var useF = this.disableFormats !== true;
5826 var fm = Roo.util.Format, tpl = this;
5827 var fn = function(m, name, format, args){
5829 if(format.substr(0, 5) == "this."){
5830 return tpl.call(format.substr(5), values[name], values);
5833 // quoted values are required for strings in compiled templates,
5834 // but for non compiled we need to strip them
5835 // quoted reversed for jsmin
5836 var re = /^\s*['"](.*)["']\s*$/;
5837 args = args.split(',');
5838 for(var i = 0, len = args.length; i < len; i++){
5839 args[i] = args[i].replace(re, "$1");
5841 args = [values[name]].concat(args);
5843 args = [values[name]];
5845 return fm[format].apply(fm, args);
5848 return values[name] !== undefined ? values[name] : "";
5851 return this.html.replace(this.re, fn);
5869 this.loading = true;
5870 this.compiled = false;
5872 var cx = new Roo.data.Connection();
5876 success : function (response) {
5880 _t.set(response.responseText,true);
5886 failure : function(response) {
5887 Roo.log("Template failed to load from " + _t.url);
5894 * Sets the HTML used as the template and optionally compiles it.
5895 * @param {String} html
5896 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5897 * @return {Roo.Template} this
5899 set : function(html, compile){
5901 this.compiled = false;
5909 * True to disable format functions (defaults to false)
5912 disableFormats : false,
5915 * The regular expression used to match template variables
5919 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5922 * Compiles the template into an internal function, eliminating the RegEx overhead.
5923 * @return {Roo.Template} this
5925 compile : function(){
5926 var fm = Roo.util.Format;
5927 var useF = this.disableFormats !== true;
5928 var sep = Roo.isGecko ? "+" : ",";
5929 var fn = function(m, name, format, args){
5931 args = args ? ',' + args : "";
5932 if(format.substr(0, 5) != "this."){
5933 format = "fm." + format + '(';
5935 format = 'this.call("'+ format.substr(5) + '", ';
5939 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5941 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5944 // branched to use + in gecko and [].join() in others
5946 body = "this.compiled = function(values){ return '" +
5947 this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5950 body = ["this.compiled = function(values){ return ['"];
5951 body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5952 body.push("'].join('');};");
5953 body = body.join('');
5963 // private function used to call members
5964 call : function(fnName, value, allValues){
5965 return this[fnName](value, allValues);
5969 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5970 * @param {String/HTMLElement/Roo.Element} el The context element
5971 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5972 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5973 * @return {HTMLElement/Roo.Element} The new node or Element
5975 insertFirst: function(el, values, returnElement){
5976 return this.doInsert('afterBegin', el, values, returnElement);
5980 * Applies the supplied values to the template and inserts the new node(s) before el.
5981 * @param {String/HTMLElement/Roo.Element} el The context element
5982 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5983 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5984 * @return {HTMLElement/Roo.Element} The new node or Element
5986 insertBefore: function(el, values, returnElement){
5987 return this.doInsert('beforeBegin', el, values, returnElement);
5991 * Applies the supplied values to the template and inserts the new node(s) after el.
5992 * @param {String/HTMLElement/Roo.Element} el The context element
5993 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5994 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5995 * @return {HTMLElement/Roo.Element} The new node or Element
5997 insertAfter : function(el, values, returnElement){
5998 return this.doInsert('afterEnd', el, values, returnElement);
6002 * Applies the supplied values to the template and appends the new node(s) to el.
6003 * @param {String/HTMLElement/Roo.Element} el The context element
6004 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6005 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6006 * @return {HTMLElement/Roo.Element} The new node or Element
6008 append : function(el, values, returnElement){
6009 return this.doInsert('beforeEnd', el, values, returnElement);
6012 doInsert : function(where, el, values, returnEl){
6013 el = Roo.getDom(el);
6014 var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6015 return returnEl ? Roo.get(newNode, true) : newNode;
6019 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6020 * @param {String/HTMLElement/Roo.Element} el The context element
6021 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6022 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6023 * @return {HTMLElement/Roo.Element} The new node or Element
6025 overwrite : function(el, values, returnElement){
6026 el = Roo.getDom(el);
6027 el.innerHTML = this.applyTemplate(values);
6028 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6032 * Alias for {@link #applyTemplate}
6035 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6038 Roo.DomHelper.Template = Roo.Template;
6041 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6042 * @param {String/HTMLElement} el A DOM element or its id
6043 * @returns {Roo.Template} The created template
6046 Roo.Template.from = function(el){
6047 el = Roo.getDom(el);
6048 return new Roo.Template(el.value || el.innerHTML);
6051 * Ext JS Library 1.1.1
6052 * Copyright(c) 2006-2007, Ext JS, LLC.
6054 * Originally Released Under LGPL - original licence link has changed is not relivant.
6057 * <script type="text/javascript">
6062 * This is code is also distributed under MIT license for use
6063 * with jQuery and prototype JavaScript libraries.
6066 * @class Roo.DomQuery
6067 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
6069 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
6072 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
6074 <h4>Element Selectors:</h4>
6076 <li> <b>*</b> any element</li>
6077 <li> <b>E</b> an element with the tag E</li>
6078 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6079 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6080 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6081 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6083 <h4>Attribute Selectors:</h4>
6084 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6086 <li> <b>E[foo]</b> has an attribute "foo"</li>
6087 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6088 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6089 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6090 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6091 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6092 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6094 <h4>Pseudo Classes:</h4>
6096 <li> <b>E:first-child</b> E is the first child of its parent</li>
6097 <li> <b>E:last-child</b> E is the last child of its parent</li>
6098 <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
6099 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6100 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6101 <li> <b>E:only-child</b> E is the only child of its parent</li>
6102 <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
6103 <li> <b>E:first</b> the first E in the resultset</li>
6104 <li> <b>E:last</b> the last E in the resultset</li>
6105 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6106 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6107 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6108 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6109 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6110 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6111 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6112 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6113 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6115 <h4>CSS Value Selectors:</h4>
6117 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6118 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6119 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6120 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6121 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6122 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6126 Roo.DomQuery = function(){
6127 var cache = {}, simpleCache = {}, valueCache = {};
6128 var nonSpace = /\S/;
6129 var trimRe = /^\s+|\s+$/g;
6130 var tplRe = /\{(\d+)\}/g;
6131 var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6132 var tagTokenRe = /^(#)?([\w-\*]+)/;
6133 var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6135 function child(p, index){
6137 var n = p.firstChild;
6139 if(n.nodeType == 1){
6150 while((n = n.nextSibling) && n.nodeType != 1);
6155 while((n = n.previousSibling) && n.nodeType != 1);
6159 function children(d){
6160 var n = d.firstChild, ni = -1;
6162 var nx = n.nextSibling;
6163 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6173 function byClassName(c, a, v){
6177 var r = [], ri = -1, cn;
6178 for(var i = 0, ci; ci = c[i]; i++){
6182 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6183 +' ').indexOf(v) != -1){
6190 function attrValue(n, attr){
6191 if(!n.tagName && typeof n.length != "undefined"){
6200 if(attr == "class" || attr == "className"){
6201 return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6203 return n.getAttribute(attr) || n[attr];
6207 function getNodes(ns, mode, tagName){
6208 var result = [], ri = -1, cs;
6212 tagName = tagName || "*";
6213 if(typeof ns.getElementsByTagName != "undefined"){
6217 for(var i = 0, ni; ni = ns[i]; i++){
6218 cs = ni.getElementsByTagName(tagName);
6219 for(var j = 0, ci; ci = cs[j]; j++){
6223 }else if(mode == "/" || mode == ">"){
6224 var utag = tagName.toUpperCase();
6225 for(var i = 0, ni, cn; ni = ns[i]; i++){
6226 cn = ni.children || ni.childNodes;
6227 for(var j = 0, cj; cj = cn[j]; j++){
6228 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
6233 }else if(mode == "+"){
6234 var utag = tagName.toUpperCase();
6235 for(var i = 0, n; n = ns[i]; i++){
6236 while((n = n.nextSibling) && n.nodeType != 1);
6237 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6241 }else if(mode == "~"){
6242 for(var i = 0, n; n = ns[i]; i++){
6243 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6252 function concat(a, b){
6256 for(var i = 0, l = b.length; i < l; i++){
6262 function byTag(cs, tagName){
6263 if(cs.tagName || cs == document){
6269 var r = [], ri = -1;
6270 tagName = tagName.toLowerCase();
6271 for(var i = 0, ci; ci = cs[i]; i++){
6272 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6279 function byId(cs, attr, id){
6280 if(cs.tagName || cs == document){
6286 var r = [], ri = -1;
6287 for(var i = 0,ci; ci = cs[i]; i++){
6288 if(ci && ci.id == id){
6296 function byAttribute(cs, attr, value, op, custom){
6297 var r = [], ri = -1, st = custom=="{";
6298 var f = Roo.DomQuery.operators[op];
6299 for(var i = 0, ci; ci = cs[i]; i++){
6302 a = Roo.DomQuery.getStyle(ci, attr);
6304 else if(attr == "class" || attr == "className"){
6305 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6306 }else if(attr == "for"){
6308 }else if(attr == "href"){
6309 a = ci.getAttribute("href", 2);
6311 a = ci.getAttribute(attr);
6313 if((f && f(a, value)) || (!f && a)){
6320 function byPseudo(cs, name, value){
6321 return Roo.DomQuery.pseudos[name](cs, value);
6324 // This is for IE MSXML which does not support expandos.
6325 // IE runs the same speed using setAttribute, however FF slows way down
6326 // and Safari completely fails so they need to continue to use expandos.
6327 var isIE = window.ActiveXObject ? true : false;
6329 // this eval is stop the compressor from
6330 // renaming the variable to something shorter
6332 /** eval:var:batch */
6337 function nodupIEXml(cs){
6339 cs[0].setAttribute("_nodup", d);
6341 for(var i = 1, len = cs.length; i < len; i++){
6343 if(!c.getAttribute("_nodup") != d){
6344 c.setAttribute("_nodup", d);
6348 for(var i = 0, len = cs.length; i < len; i++){
6349 cs[i].removeAttribute("_nodup");
6358 var len = cs.length, c, i, r = cs, cj, ri = -1;
6359 if(!len || typeof cs.nodeType != "undefined" || len == 1){
6362 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6363 return nodupIEXml(cs);
6367 for(i = 1; c = cs[i]; i++){
6372 for(var j = 0; j < i; j++){
6375 for(j = i+1; cj = cs[j]; j++){
6387 function quickDiffIEXml(c1, c2){
6389 for(var i = 0, len = c1.length; i < len; i++){
6390 c1[i].setAttribute("_qdiff", d);
6393 for(var i = 0, len = c2.length; i < len; i++){
6394 if(c2[i].getAttribute("_qdiff") != d){
6395 r[r.length] = c2[i];
6398 for(var i = 0, len = c1.length; i < len; i++){
6399 c1[i].removeAttribute("_qdiff");
6404 function quickDiff(c1, c2){
6405 var len1 = c1.length;
6409 if(isIE && c1[0].selectSingleNode){
6410 return quickDiffIEXml(c1, c2);
6413 for(var i = 0; i < len1; i++){
6417 for(var i = 0, len = c2.length; i < len; i++){
6418 if(c2[i]._qdiff != d){
6419 r[r.length] = c2[i];
6425 function quickId(ns, mode, root, id){
6427 var d = root.ownerDocument || root;
6428 return d.getElementById(id);
6430 ns = getNodes(ns, mode, "*");
6431 return byId(ns, null, id);
6435 getStyle : function(el, name){
6436 return Roo.fly(el).getStyle(name);
6439 * Compiles a selector/xpath query into a reusable function. The returned function
6440 * takes one parameter "root" (optional), which is the context node from where the query should start.
6441 * @param {String} selector The selector/xpath query
6442 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6443 * @return {Function}
6445 compile : function(path, type){
6446 type = type || "select";
6448 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6449 var q = path, mode, lq;
6450 var tk = Roo.DomQuery.matchers;
6451 var tklen = tk.length;
6454 // accept leading mode switch
6455 var lmode = q.match(modeRe);
6456 if(lmode && lmode[1]){
6457 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6458 q = q.replace(lmode[1], "");
6460 // strip leading slashes
6461 while(path.substr(0, 1)=="/"){
6462 path = path.substr(1);
6465 while(q && lq != q){
6467 var tm = q.match(tagTokenRe);
6468 if(type == "select"){
6471 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6473 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6475 q = q.replace(tm[0], "");
6476 }else if(q.substr(0, 1) != '@'){
6477 fn[fn.length] = 'n = getNodes(n, mode, "*");';
6482 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6484 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6486 q = q.replace(tm[0], "");
6489 while(!(mm = q.match(modeRe))){
6490 var matched = false;
6491 for(var j = 0; j < tklen; j++){
6493 var m = q.match(t.re);
6495 fn[fn.length] = t.select.replace(tplRe, function(x, i){
6498 q = q.replace(m[0], "");
6503 // prevent infinite loop on bad selector
6505 throw 'Error parsing selector, parsing failed at "' + q + '"';
6509 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6510 q = q.replace(mm[1], "");
6513 fn[fn.length] = "return nodup(n);\n}";
6516 * list of variables that need from compression as they are used by eval.
6526 * eval:var:byClassName
6528 * eval:var:byAttribute
6529 * eval:var:attrValue
6537 * Selects a group of elements.
6538 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6539 * @param {Node} root (optional) The start of the query (defaults to document).
6542 select : function(path, root, type){
6543 if(!root || root == document){
6546 if(typeof root == "string"){
6547 root = document.getElementById(root);
6549 var paths = path.split(",");
6551 for(var i = 0, len = paths.length; i < len; i++){
6552 var p = paths[i].replace(trimRe, "");
6554 cache[p] = Roo.DomQuery.compile(p);
6556 throw p + " is not a valid selector";
6559 var result = cache[p](root);
6560 if(result && result != document){
6561 results = results.concat(result);
6564 if(paths.length > 1){
6565 return nodup(results);
6571 * Selects a single element.
6572 * @param {String} selector The selector/xpath query
6573 * @param {Node} root (optional) The start of the query (defaults to document).
6576 selectNode : function(path, root){
6577 return Roo.DomQuery.select(path, root)[0];
6581 * Selects the value of a node, optionally replacing null with the defaultValue.
6582 * @param {String} selector The selector/xpath query
6583 * @param {Node} root (optional) The start of the query (defaults to document).
6584 * @param {String} defaultValue
6586 selectValue : function(path, root, defaultValue){
6587 path = path.replace(trimRe, "");
6588 if(!valueCache[path]){
6589 valueCache[path] = Roo.DomQuery.compile(path, "select");
6591 var n = valueCache[path](root);
6592 n = n[0] ? n[0] : n;
6593 var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6594 return ((v === null||v === undefined||v==='') ? defaultValue : v);
6598 * Selects the value of a node, parsing integers and floats.
6599 * @param {String} selector The selector/xpath query
6600 * @param {Node} root (optional) The start of the query (defaults to document).
6601 * @param {Number} defaultValue
6604 selectNumber : function(path, root, defaultValue){
6605 var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6606 return parseFloat(v);
6610 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6611 * @param {String/HTMLElement/Array} el An element id, element or array of elements
6612 * @param {String} selector The simple selector to test
6615 is : function(el, ss){
6616 if(typeof el == "string"){
6617 el = document.getElementById(el);
6619 var isArray = (el instanceof Array);
6620 var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6621 return isArray ? (result.length == el.length) : (result.length > 0);
6625 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6626 * @param {Array} el An array of elements to filter
6627 * @param {String} selector The simple selector to test
6628 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6629 * the selector instead of the ones that match
6632 filter : function(els, ss, nonMatches){
6633 ss = ss.replace(trimRe, "");
6634 if(!simpleCache[ss]){
6635 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6637 var result = simpleCache[ss](els);
6638 return nonMatches ? quickDiff(result, els) : result;
6642 * Collection of matching regular expressions and code snippets.
6646 select: 'n = byClassName(n, null, " {1} ");'
6648 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6649 select: 'n = byPseudo(n, "{1}", "{2}");'
6651 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6652 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6655 select: 'n = byId(n, null, "{1}");'
6658 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6663 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6664 * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, > <.
6667 "=" : function(a, v){
6670 "!=" : function(a, v){
6673 "^=" : function(a, v){
6674 return a && a.substr(0, v.length) == v;
6676 "$=" : function(a, v){
6677 return a && a.substr(a.length-v.length) == v;
6679 "*=" : function(a, v){
6680 return a && a.indexOf(v) !== -1;
6682 "%=" : function(a, v){
6683 return (a % v) == 0;
6685 "|=" : function(a, v){
6686 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6688 "~=" : function(a, v){
6689 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6694 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6695 * and the argument (if any) supplied in the selector.
6698 "first-child" : function(c){
6699 var r = [], ri = -1, n;
6700 for(var i = 0, ci; ci = n = c[i]; i++){
6701 while((n = n.previousSibling) && n.nodeType != 1);
6709 "last-child" : function(c){
6710 var r = [], ri = -1, n;
6711 for(var i = 0, ci; ci = n = c[i]; i++){
6712 while((n = n.nextSibling) && n.nodeType != 1);
6720 "nth-child" : function(c, a) {
6721 var r = [], ri = -1;
6722 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6723 var f = (m[1] || 1) - 0, l = m[2] - 0;
6724 for(var i = 0, n; n = c[i]; i++){
6725 var pn = n.parentNode;
6726 if (batch != pn._batch) {
6728 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6729 if(cn.nodeType == 1){
6736 if (l == 0 || n.nodeIndex == l){
6739 } else if ((n.nodeIndex + l) % f == 0){
6747 "only-child" : function(c){
6748 var r = [], ri = -1;;
6749 for(var i = 0, ci; ci = c[i]; i++){
6750 if(!prev(ci) && !next(ci)){
6757 "empty" : function(c){
6758 var r = [], ri = -1;
6759 for(var i = 0, ci; ci = c[i]; i++){
6760 var cns = ci.childNodes, j = 0, cn, empty = true;
6763 if(cn.nodeType == 1 || cn.nodeType == 3){
6775 "contains" : function(c, v){
6776 var r = [], ri = -1;
6777 for(var i = 0, ci; ci = c[i]; i++){
6778 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6785 "nodeValue" : function(c, v){
6786 var r = [], ri = -1;
6787 for(var i = 0, ci; ci = c[i]; i++){
6788 if(ci.firstChild && ci.firstChild.nodeValue == v){
6795 "checked" : function(c){
6796 var r = [], ri = -1;
6797 for(var i = 0, ci; ci = c[i]; i++){
6798 if(ci.checked == true){
6805 "not" : function(c, ss){
6806 return Roo.DomQuery.filter(c, ss, true);
6809 "odd" : function(c){
6810 return this["nth-child"](c, "odd");
6813 "even" : function(c){
6814 return this["nth-child"](c, "even");
6817 "nth" : function(c, a){
6818 return c[a-1] || [];
6821 "first" : function(c){
6825 "last" : function(c){
6826 return c[c.length-1] || [];
6829 "has" : function(c, ss){
6830 var s = Roo.DomQuery.select;
6831 var r = [], ri = -1;
6832 for(var i = 0, ci; ci = c[i]; i++){
6833 if(s(ss, ci).length > 0){
6840 "next" : function(c, ss){
6841 var is = Roo.DomQuery.is;
6842 var r = [], ri = -1;
6843 for(var i = 0, ci; ci = c[i]; i++){
6852 "prev" : function(c, ss){
6853 var is = Roo.DomQuery.is;
6854 var r = [], ri = -1;
6855 for(var i = 0, ci; ci = c[i]; i++){
6868 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6869 * @param {String} path The selector/xpath query
6870 * @param {Node} root (optional) The start of the query (defaults to document).
6875 Roo.query = Roo.DomQuery.select;
6878 * Ext JS Library 1.1.1
6879 * Copyright(c) 2006-2007, Ext JS, LLC.
6881 * Originally Released Under LGPL - original licence link has changed is not relivant.
6884 * <script type="text/javascript">
6888 * @class Roo.util.Observable
6889 * Base class that provides a common interface for publishing events. Subclasses are expected to
6890 * to have a property "events" with all the events defined.<br>
6893 Employee = function(name){
6900 Roo.extend(Employee, Roo.util.Observable);
6902 * @param {Object} config properties to use (incuding events / listeners)
6905 Roo.util.Observable = function(cfg){
6908 this.addEvents(cfg.events || {});
6910 delete cfg.events; // make sure
6913 Roo.apply(this, cfg);
6916 this.on(this.listeners);
6917 delete this.listeners;
6920 Roo.util.Observable.prototype = {
6922 * @cfg {Object} listeners list of events and functions to call for this object,
6926 'click' : function(e) {
6936 * Fires the specified event with the passed parameters (minus the event name).
6937 * @param {String} eventName
6938 * @param {Object...} args Variable number of parameters are passed to handlers
6939 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6941 fireEvent : function(){
6942 var ce = this.events[arguments[0].toLowerCase()];
6943 if(typeof ce == "object"){
6944 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6951 filterOptRe : /^(?:scope|delay|buffer|single)$/,
6954 * Appends an event handler to this component
6955 * @param {String} eventName The type of event to listen for
6956 * @param {Function} handler The method the event invokes
6957 * @param {Object} scope (optional) The scope in which to execute the handler
6958 * function. The handler function's "this" context.
6959 * @param {Object} options (optional) An object containing handler configuration
6960 * properties. This may contain any of the following properties:<ul>
6961 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6962 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6963 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6964 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6965 * by the specified number of milliseconds. If the event fires again within that time, the original
6966 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6969 * <b>Combining Options</b><br>
6970 * Using the options argument, it is possible to combine different types of listeners:<br>
6972 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6974 el.on('click', this.onClick, this, {
6981 * <b>Attaching multiple handlers in 1 call</b><br>
6982 * The method also allows for a single argument to be passed which is a config object containing properties
6983 * which specify multiple handlers.
6992 fn: this.onMouseOver,
6996 fn: this.onMouseOut,
7002 * Or a shorthand syntax which passes the same scope object to all handlers:
7005 'click': this.onClick,
7006 'mouseover': this.onMouseOver,
7007 'mouseout': this.onMouseOut,
7012 addListener : function(eventName, fn, scope, o){
7013 if(typeof eventName == "object"){
7016 if(this.filterOptRe.test(e)){
7019 if(typeof o[e] == "function"){
7021 this.addListener(e, o[e], o.scope, o);
7023 // individual options
7024 this.addListener(e, o[e].fn, o[e].scope, o[e]);
7029 o = (!o || typeof o == "boolean") ? {} : o;
7030 eventName = eventName.toLowerCase();
7031 var ce = this.events[eventName] || true;
7032 if(typeof ce == "boolean"){
7033 ce = new Roo.util.Event(this, eventName);
7034 this.events[eventName] = ce;
7036 ce.addListener(fn, scope, o);
7040 * Removes a listener
7041 * @param {String} eventName The type of event to listen for
7042 * @param {Function} handler The handler to remove
7043 * @param {Object} scope (optional) The scope (this object) for the handler
7045 removeListener : function(eventName, fn, scope){
7046 var ce = this.events[eventName.toLowerCase()];
7047 if(typeof ce == "object"){
7048 ce.removeListener(fn, scope);
7053 * Removes all listeners for this object
7055 purgeListeners : function(){
7056 for(var evt in this.events){
7057 if(typeof this.events[evt] == "object"){
7058 this.events[evt].clearListeners();
7063 relayEvents : function(o, events){
7064 var createHandler = function(ename){
7067 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7070 for(var i = 0, len = events.length; i < len; i++){
7071 var ename = events[i];
7072 if(!this.events[ename]){
7073 this.events[ename] = true;
7075 o.on(ename, createHandler(ename), this);
7080 * Used to define events on this Observable
7081 * @param {Object} object The object with the events defined
7083 addEvents : function(o){
7087 Roo.applyIf(this.events, o);
7091 * Checks to see if this object has any listeners for a specified event
7092 * @param {String} eventName The name of the event to check for
7093 * @return {Boolean} True if the event is being listened for, else false
7095 hasListener : function(eventName){
7096 var e = this.events[eventName];
7097 return typeof e == "object" && e.listeners.length > 0;
7101 * Appends an event handler to this element (shorthand for addListener)
7102 * @param {String} eventName The type of event to listen for
7103 * @param {Function} handler The method the event invokes
7104 * @param {Object} scope (optional) The scope in which to execute the handler
7105 * function. The handler function's "this" context.
7106 * @param {Object} options (optional)
7109 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7111 * Removes a listener (shorthand for removeListener)
7112 * @param {String} eventName The type of event to listen for
7113 * @param {Function} handler The handler to remove
7114 * @param {Object} scope (optional) The scope (this object) for the handler
7117 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7120 * Starts capture on the specified Observable. All events will be passed
7121 * to the supplied function with the event name + standard signature of the event
7122 * <b>before</b> the event is fired. If the supplied function returns false,
7123 * the event will not fire.
7124 * @param {Observable} o The Observable to capture
7125 * @param {Function} fn The function to call
7126 * @param {Object} scope (optional) The scope (this object) for the fn
7129 Roo.util.Observable.capture = function(o, fn, scope){
7130 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7134 * Removes <b>all</b> added captures from the Observable.
7135 * @param {Observable} o The Observable to release
7138 Roo.util.Observable.releaseCapture = function(o){
7139 o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7144 var createBuffered = function(h, o, scope){
7145 var task = new Roo.util.DelayedTask();
7147 task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7151 var createSingle = function(h, e, fn, scope){
7153 e.removeListener(fn, scope);
7154 return h.apply(scope, arguments);
7158 var createDelayed = function(h, o, scope){
7160 var args = Array.prototype.slice.call(arguments, 0);
7161 setTimeout(function(){
7162 h.apply(scope, args);
7167 Roo.util.Event = function(obj, name){
7170 this.listeners = [];
7173 Roo.util.Event.prototype = {
7174 addListener : function(fn, scope, options){
7175 var o = options || {};
7176 scope = scope || this.obj;
7177 if(!this.isListening(fn, scope)){
7178 var l = {fn: fn, scope: scope, options: o};
7181 h = createDelayed(h, o, scope);
7184 h = createSingle(h, this, fn, scope);
7187 h = createBuffered(h, o, scope);
7190 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7191 this.listeners.push(l);
7193 this.listeners = this.listeners.slice(0);
7194 this.listeners.push(l);
7199 findListener : function(fn, scope){
7200 scope = scope || this.obj;
7201 var ls = this.listeners;
7202 for(var i = 0, len = ls.length; i < len; i++){
7204 if(l.fn == fn && l.scope == scope){
7211 isListening : function(fn, scope){
7212 return this.findListener(fn, scope) != -1;
7215 removeListener : function(fn, scope){
7217 if((index = this.findListener(fn, scope)) != -1){
7219 this.listeners.splice(index, 1);
7221 this.listeners = this.listeners.slice(0);
7222 this.listeners.splice(index, 1);
7229 clearListeners : function(){
7230 this.listeners = [];
7234 var ls = this.listeners, scope, len = ls.length;
7237 var args = Array.prototype.slice.call(arguments, 0);
7238 for(var i = 0; i < len; i++){
7240 if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7241 this.firing = false;
7245 this.firing = false;
7252 * Copyright(c) 2007-2017, Roo J Solutions Ltd
7259 * @class Roo.Document
7260 * @extends Roo.util.Observable
7261 * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7263 * @param {Object} config the methods and properties of the 'base' class for the application.
7265 * Generic Page handler - implement this to start your app..
7268 * MyProject = new Roo.Document({
7270 'load' : true // your events..
7273 'ready' : function() {
7274 // fired on Roo.onReady()
7279 Roo.Document = function(cfg) {
7284 Roo.util.Observable.call(this,cfg);
7288 Roo.onReady(function() {
7289 _this.fireEvent('ready');
7295 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7297 * Ext JS Library 1.1.1
7298 * Copyright(c) 2006-2007, Ext JS, LLC.
7300 * Originally Released Under LGPL - original licence link has changed is not relivant.
7303 * <script type="text/javascript">
7307 * @class Roo.EventManager
7308 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
7309 * several useful events directly.
7310 * See {@link Roo.EventObject} for more details on normalized event objects.
7313 Roo.EventManager = function(){
7314 var docReadyEvent, docReadyProcId, docReadyState = false;
7315 var resizeEvent, resizeTask, textEvent, textSize;
7316 var E = Roo.lib.Event;
7317 var D = Roo.lib.Dom;
7322 var fireDocReady = function(){
7324 docReadyState = true;
7327 clearInterval(docReadyProcId);
7329 if(Roo.isGecko || Roo.isOpera) {
7330 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7333 var defer = document.getElementById("ie-deferred-loader");
7335 defer.onreadystatechange = null;
7336 defer.parentNode.removeChild(defer);
7340 docReadyEvent.fire();
7341 docReadyEvent.clearListeners();
7346 var initDocReady = function(){
7347 docReadyEvent = new Roo.util.Event();
7348 if(Roo.isGecko || Roo.isOpera) {
7349 document.addEventListener("DOMContentLoaded", fireDocReady, false);
7351 document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7352 var defer = document.getElementById("ie-deferred-loader");
7353 defer.onreadystatechange = function(){
7354 if(this.readyState == "complete"){
7358 }else if(Roo.isSafari){
7359 docReadyProcId = setInterval(function(){
7360 var rs = document.readyState;
7361 if(rs == "complete") {
7366 // no matter what, make sure it fires on load
7367 E.on(window, "load", fireDocReady);
7370 var createBuffered = function(h, o){
7371 var task = new Roo.util.DelayedTask(h);
7373 // create new event object impl so new events don't wipe out properties
7374 e = new Roo.EventObjectImpl(e);
7375 task.delay(o.buffer, h, null, [e]);
7379 var createSingle = function(h, el, ename, fn){
7381 Roo.EventManager.removeListener(el, ename, fn);
7386 var createDelayed = function(h, o){
7388 // create new event object impl so new events don't wipe out properties
7389 e = new Roo.EventObjectImpl(e);
7390 setTimeout(function(){
7395 var transitionEndVal = false;
7397 var transitionEnd = function()
7399 if (transitionEndVal) {
7400 return transitionEndVal;
7402 var el = document.createElement('div');
7404 var transEndEventNames = {
7405 WebkitTransition : 'webkitTransitionEnd',
7406 MozTransition : 'transitionend',
7407 OTransition : 'oTransitionEnd otransitionend',
7408 transition : 'transitionend'
7411 for (var name in transEndEventNames) {
7412 if (el.style[name] !== undefined) {
7413 transitionEndVal = transEndEventNames[name];
7414 return transitionEndVal ;
7421 var listen = function(element, ename, opt, fn, scope)
7423 var o = (!opt || typeof opt == "boolean") ? {} : opt;
7424 fn = fn || o.fn; scope = scope || o.scope;
7425 var el = Roo.getDom(element);
7429 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7432 if (ename == 'transitionend') {
7433 ename = transitionEnd();
7435 var h = function(e){
7436 e = Roo.EventObject.setEvent(e);
7439 t = e.getTarget(o.delegate, el);
7446 if(o.stopEvent === true){
7449 if(o.preventDefault === true){
7452 if(o.stopPropagation === true){
7453 e.stopPropagation();
7456 if(o.normalized === false){
7460 fn.call(scope || el, e, t, o);
7463 h = createDelayed(h, o);
7466 h = createSingle(h, el, ename, fn);
7469 h = createBuffered(h, o);
7472 fn._handlers = fn._handlers || [];
7475 fn._handlers.push([Roo.id(el), ename, h]);
7479 E.on(el, ename, h); // this adds the actuall listener to the object..
7482 if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7483 el.addEventListener("DOMMouseScroll", h, false);
7484 E.on(window, 'unload', function(){
7485 el.removeEventListener("DOMMouseScroll", h, false);
7488 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7489 Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7494 var stopListening = function(el, ename, fn){
7495 var id = Roo.id(el), hds = fn._handlers, hd = fn;
7497 for(var i = 0, len = hds.length; i < len; i++){
7499 if(h[0] == id && h[1] == ename){
7506 E.un(el, ename, hd);
7507 el = Roo.getDom(el);
7508 if(ename == "mousewheel" && el.addEventListener){
7509 el.removeEventListener("DOMMouseScroll", hd, false);
7511 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7512 Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7516 var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7523 * @scope Roo.EventManager
7528 * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7529 * object with a Roo.EventObject
7530 * @param {Function} fn The method the event invokes
7531 * @param {Object} scope An object that becomes the scope of the handler
7532 * @param {boolean} override If true, the obj passed in becomes
7533 * the execution scope of the listener
7534 * @return {Function} The wrapped function
7537 wrap : function(fn, scope, override){
7539 Roo.EventObject.setEvent(e);
7540 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7545 * Appends an event handler to an element (shorthand for addListener)
7546 * @param {String/HTMLElement} element The html element or id to assign the
7547 * @param {String} eventName The type of event to listen for
7548 * @param {Function} handler The method the event invokes
7549 * @param {Object} scope (optional) The scope in which to execute the handler
7550 * function. The handler function's "this" context.
7551 * @param {Object} options (optional) An object containing handler configuration
7552 * properties. This may contain any of the following properties:<ul>
7553 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7554 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7555 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7556 * <li>preventDefault {Boolean} True to prevent the default action</li>
7557 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7558 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7559 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7560 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7561 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7562 * by the specified number of milliseconds. If the event fires again within that time, the original
7563 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7566 * <b>Combining Options</b><br>
7567 * Using the options argument, it is possible to combine different types of listeners:<br>
7569 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7571 el.on('click', this.onClick, this, {
7578 * <b>Attaching multiple handlers in 1 call</b><br>
7579 * The method also allows for a single argument to be passed which is a config object containing properties
7580 * which specify multiple handlers.
7590 fn: this.onMouseOver
7599 * Or a shorthand syntax:<br>
7602 'click' : this.onClick,
7603 'mouseover' : this.onMouseOver,
7604 'mouseout' : this.onMouseOut
7608 addListener : function(element, eventName, fn, scope, options){
7609 if(typeof eventName == "object"){
7615 if(typeof o[e] == "function"){
7617 listen(element, e, o, o[e], o.scope);
7619 // individual options
7620 listen(element, e, o[e]);
7625 return listen(element, eventName, options, fn, scope);
7629 * Removes an event handler
7631 * @param {String/HTMLElement} element The id or html element to remove the
7633 * @param {String} eventName The type of event
7634 * @param {Function} fn
7635 * @return {Boolean} True if a listener was actually removed
7637 removeListener : function(element, eventName, fn){
7638 return stopListening(element, eventName, fn);
7642 * Fires when the document is ready (before onload and before images are loaded). Can be
7643 * accessed shorthanded Roo.onReady().
7644 * @param {Function} fn The method the event invokes
7645 * @param {Object} scope An object that becomes the scope of the handler
7646 * @param {boolean} options
7648 onDocumentReady : function(fn, scope, options){
7649 if(docReadyState){ // if it already fired
7650 docReadyEvent.addListener(fn, scope, options);
7651 docReadyEvent.fire();
7652 docReadyEvent.clearListeners();
7658 docReadyEvent.addListener(fn, scope, options);
7662 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7663 * @param {Function} fn The method the event invokes
7664 * @param {Object} scope An object that becomes the scope of the handler
7665 * @param {boolean} options
7667 onWindowResize : function(fn, scope, options)
7670 resizeEvent = new Roo.util.Event();
7671 resizeTask = new Roo.util.DelayedTask(function(){
7672 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7674 E.on(window, "resize", function()
7677 resizeTask.delay(50);
7679 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7683 resizeEvent.addListener(fn, scope, options);
7687 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7688 * @param {Function} fn The method the event invokes
7689 * @param {Object} scope An object that becomes the scope of the handler
7690 * @param {boolean} options
7692 onTextResize : function(fn, scope, options){
7694 textEvent = new Roo.util.Event();
7695 var textEl = new Roo.Element(document.createElement('div'));
7696 textEl.dom.className = 'x-text-resize';
7697 textEl.dom.innerHTML = 'X';
7698 textEl.appendTo(document.body);
7699 textSize = textEl.dom.offsetHeight;
7700 setInterval(function(){
7701 if(textEl.dom.offsetHeight != textSize){
7702 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7704 }, this.textResizeInterval);
7706 textEvent.addListener(fn, scope, options);
7710 * Removes the passed window resize listener.
7711 * @param {Function} fn The method the event invokes
7712 * @param {Object} scope The scope of handler
7714 removeResizeListener : function(fn, scope){
7716 resizeEvent.removeListener(fn, scope);
7721 fireResize : function(){
7723 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7727 * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7731 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7733 textResizeInterval : 50
7738 * @scopeAlias pub=Roo.EventManager
7742 * Appends an event handler to an element (shorthand for addListener)
7743 * @param {String/HTMLElement} element The html element or id to assign the
7744 * @param {String} eventName The type of event to listen for
7745 * @param {Function} handler The method the event invokes
7746 * @param {Object} scope (optional) The scope in which to execute the handler
7747 * function. The handler function's "this" context.
7748 * @param {Object} options (optional) An object containing handler configuration
7749 * properties. This may contain any of the following properties:<ul>
7750 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7751 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7752 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7753 * <li>preventDefault {Boolean} True to prevent the default action</li>
7754 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7755 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7756 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7757 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7758 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7759 * by the specified number of milliseconds. If the event fires again within that time, the original
7760 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7763 * <b>Combining Options</b><br>
7764 * Using the options argument, it is possible to combine different types of listeners:<br>
7766 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7768 el.on('click', this.onClick, this, {
7775 * <b>Attaching multiple handlers in 1 call</b><br>
7776 * The method also allows for a single argument to be passed which is a config object containing properties
7777 * which specify multiple handlers.
7787 fn: this.onMouseOver
7796 * Or a shorthand syntax:<br>
7799 'click' : this.onClick,
7800 'mouseover' : this.onMouseOver,
7801 'mouseout' : this.onMouseOut
7805 pub.on = pub.addListener;
7806 pub.un = pub.removeListener;
7808 pub.stoppedMouseDownEvent = new Roo.util.Event();
7812 * Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Roo.EventManager#onDocumentReady}.
7813 * @param {Function} fn The method the event invokes
7814 * @param {Object} scope An object that becomes the scope of the handler
7815 * @param {boolean} override If true, the obj passed in becomes
7816 * the execution scope of the listener
7820 Roo.onReady = Roo.EventManager.onDocumentReady;
7822 Roo.onReady(function(){
7823 var bd = Roo.get(document.body);
7828 : Roo.isIE11 ? "roo-ie11"
7829 : Roo.isEdge ? "roo-edge"
7830 : Roo.isGecko ? "roo-gecko"
7831 : Roo.isOpera ? "roo-opera"
7832 : Roo.isSafari ? "roo-safari" : ""];
7835 cls.push("roo-mac");
7838 cls.push("roo-linux");
7841 cls.push("roo-ios");
7844 cls.push("roo-touch");
7846 if(Roo.isBorderBox){
7847 cls.push('roo-border-box');
7849 if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7850 var p = bd.dom.parentNode;
7852 p.className += ' roo-strict';
7855 bd.addClass(cls.join(' '));
7859 * @class Roo.EventObject
7860 * EventObject exposes the Yahoo! UI Event functionality directly on the object
7861 * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
7864 function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7866 var target = e.getTarget();
7869 var myDiv = Roo.get("myDiv");
7870 myDiv.on("click", handleClick);
7872 Roo.EventManager.on("myDiv", 'click', handleClick);
7873 Roo.EventManager.addListener("myDiv", 'click', handleClick);
7877 Roo.EventObject = function(){
7879 var E = Roo.lib.Event;
7881 // safari keypress events for special keys return bad keycodes
7884 63235 : 39, // right
7887 63276 : 33, // page up
7888 63277 : 34, // page down
7889 63272 : 46, // delete
7894 // normalize button clicks
7895 var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7896 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7898 Roo.EventObjectImpl = function(e){
7900 this.setEvent(e.browserEvent || e);
7903 Roo.EventObjectImpl.prototype = {
7905 * Used to fix doc tools.
7906 * @scope Roo.EventObject.prototype
7912 /** The normal browser event */
7913 browserEvent : null,
7914 /** The button pressed in a mouse event */
7916 /** True if the shift key was down during the event */
7918 /** True if the control key was down during the event */
7920 /** True if the alt key was down during the event */
7979 setEvent : function(e){
7980 if(e == this || (e && e.browserEvent)){ // already wrapped
7983 this.browserEvent = e;
7985 // normalize buttons
7986 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7987 if(e.type == 'click' && this.button == -1){
7991 this.shiftKey = e.shiftKey;
7992 // mac metaKey behaves like ctrlKey
7993 this.ctrlKey = e.ctrlKey || e.metaKey;
7994 this.altKey = e.altKey;
7995 // in getKey these will be normalized for the mac
7996 this.keyCode = e.keyCode;
7997 // keyup warnings on firefox.
7998 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7999 // cache the target for the delayed and or buffered events
8000 this.target = E.getTarget(e);
8002 this.xy = E.getXY(e);
8005 this.shiftKey = false;
8006 this.ctrlKey = false;
8007 this.altKey = false;
8017 * Stop the event (preventDefault and stopPropagation)
8019 stopEvent : function(){
8020 if(this.browserEvent){
8021 if(this.browserEvent.type == 'mousedown'){
8022 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8024 E.stopEvent(this.browserEvent);
8029 * Prevents the browsers default handling of the event.
8031 preventDefault : function(){
8032 if(this.browserEvent){
8033 E.preventDefault(this.browserEvent);
8038 isNavKeyPress : function(){
8039 var k = this.keyCode;
8040 k = Roo.isSafari ? (safariKeys[k] || k) : k;
8041 return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8044 isSpecialKey : function(){
8045 var k = this.keyCode;
8046 return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13 || k == 40 || k == 27 ||
8047 (k == 16) || (k == 17) ||
8048 (k >= 18 && k <= 20) ||
8049 (k >= 33 && k <= 35) ||
8050 (k >= 36 && k <= 39) ||
8051 (k >= 44 && k <= 45);
8054 * Cancels bubbling of the event.
8056 stopPropagation : function(){
8057 if(this.browserEvent){
8058 if(this.type == 'mousedown'){
8059 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8061 E.stopPropagation(this.browserEvent);
8066 * Gets the key code for the event.
8069 getCharCode : function(){
8070 return this.charCode || this.keyCode;
8074 * Returns a normalized keyCode for the event.
8075 * @return {Number} The key code
8077 getKey : function(){
8078 var k = this.keyCode || this.charCode;
8079 return Roo.isSafari ? (safariKeys[k] || k) : k;
8083 * Gets the x coordinate of the event.
8086 getPageX : function(){
8091 * Gets the y coordinate of the event.
8094 getPageY : function(){
8099 * Gets the time of the event.
8102 getTime : function(){
8103 if(this.browserEvent){
8104 return E.getTime(this.browserEvent);
8110 * Gets the page coordinates of the event.
8111 * @return {Array} The xy values like [x, y]
8118 * Gets the target for the event.
8119 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8120 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8121 search as a number or element (defaults to 10 || document.body)
8122 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8123 * @return {HTMLelement}
8125 getTarget : function(selector, maxDepth, returnEl){
8126 return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8129 * Gets the related target.
8130 * @return {HTMLElement}
8132 getRelatedTarget : function(){
8133 if(this.browserEvent){
8134 return E.getRelatedTarget(this.browserEvent);
8140 * Normalizes mouse wheel delta across browsers
8141 * @return {Number} The delta
8143 getWheelDelta : function(){
8144 var e = this.browserEvent;
8146 if(e.wheelDelta){ /* IE/Opera. */
8147 delta = e.wheelDelta/120;
8148 }else if(e.detail){ /* Mozilla case. */
8149 delta = -e.detail/3;
8155 * Returns true if the control, meta, shift or alt key was pressed during this event.
8158 hasModifier : function(){
8159 return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8163 * Returns true if the target of this event equals el or is a child of el
8164 * @param {String/HTMLElement/Element} el
8165 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8168 within : function(el, related){
8169 var t = this[related ? "getRelatedTarget" : "getTarget"]();
8170 return t && Roo.fly(el).contains(t);
8173 getPoint : function(){
8174 return new Roo.lib.Point(this.xy[0], this.xy[1]);
8178 return new Roo.EventObjectImpl();
8183 * Ext JS Library 1.1.1
8184 * Copyright(c) 2006-2007, Ext JS, LLC.
8186 * Originally Released Under LGPL - original licence link has changed is not relivant.
8189 * <script type="text/javascript">
8193 // was in Composite Element!??!?!
8196 var D = Roo.lib.Dom;
8197 var E = Roo.lib.Event;
8198 var A = Roo.lib.Anim;
8200 // local style camelizing for speed
8202 var camelRe = /(-[a-z])/gi;
8203 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8204 var view = document.defaultView;
8207 * @class Roo.Element
8208 * Represents an Element in the DOM.<br><br>
8211 var el = Roo.get("my-div");
8214 var el = getEl("my-div");
8216 // or with a DOM element
8217 var el = Roo.get(myDivElement);
8219 * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8220 * each call instead of constructing a new one.<br><br>
8221 * <b>Animations</b><br />
8222 * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8223 * should either be a boolean (true) or an object literal with animation options. The animation options are:
8225 Option Default Description
8226 --------- -------- ---------------------------------------------
8227 duration .35 The duration of the animation in seconds
8228 easing easeOut The YUI easing method
8229 callback none A function to execute when the anim completes
8230 scope this The scope (this) of the callback function
8232 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8233 * manipulate the animation. Here's an example:
8235 var el = Roo.get("my-div");
8240 // default animation
8241 el.setWidth(100, true);
8243 // animation with some options set
8250 // using the "anim" property to get the Anim object
8256 el.setWidth(100, opt);
8258 if(opt.anim.isAnimated()){
8262 * <b> Composite (Collections of) Elements</b><br />
8263 * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8264 * @constructor Create a new Element directly.
8265 * @param {String/HTMLElement} element
8266 * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
8268 Roo.Element = function(element, forceNew)
8270 var dom = typeof element == "string" ?
8271 document.getElementById(element) : element;
8273 this.listeners = {};
8275 if(!dom){ // invalid id/element
8279 if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8280 return Roo.Element.cache[id];
8290 * The DOM element ID
8293 this.id = id || Roo.id(dom);
8295 return this; // assumed for cctor?
8298 var El = Roo.Element;
8302 * The element's default display mode (defaults to "")
8305 originalDisplay : "",
8308 // note this is overridden in BS version..
8311 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8317 * Sets the element's visibility mode. When setVisible() is called it
8318 * will use this to determine whether to set the visibility or the display property.
8319 * @param visMode Element.VISIBILITY or Element.DISPLAY
8320 * @return {Roo.Element} this
8322 setVisibilityMode : function(visMode){
8323 this.visibilityMode = visMode;
8327 * Convenience method for setVisibilityMode(Element.DISPLAY)
8328 * @param {String} display (optional) What to set display to when visible
8329 * @return {Roo.Element} this
8331 enableDisplayMode : function(display){
8332 this.setVisibilityMode(El.DISPLAY);
8333 if(typeof display != "undefined") { this.originalDisplay = display; }
8338 * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8339 * @param {String} selector The simple selector to test
8340 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8341 search as a number or element (defaults to 10 || document.body)
8342 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8343 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8345 findParent : function(simpleSelector, maxDepth, returnEl){
8346 var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8347 maxDepth = maxDepth || 50;
8348 if(typeof maxDepth != "number"){
8349 stopEl = Roo.getDom(maxDepth);
8352 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8353 if(dq.is(p, simpleSelector)){
8354 return returnEl ? Roo.get(p) : p;
8364 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8365 * @param {String} selector The simple selector to test
8366 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8367 search as a number or element (defaults to 10 || document.body)
8368 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8369 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8371 findParentNode : function(simpleSelector, maxDepth, returnEl){
8372 var p = Roo.fly(this.dom.parentNode, '_internal');
8373 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8377 * Looks at the scrollable parent element
8379 findScrollableParent : function()
8381 var overflowRegex = /(auto|scroll)/;
8383 if(this.getStyle('position') === 'fixed'){
8384 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8387 var excludeStaticParent = this.getStyle('position') === "absolute";
8389 for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8391 if (excludeStaticParent && parent.getStyle('position') === "static") {
8395 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8399 if(parent.dom.nodeName.toLowerCase() == 'body'){
8400 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8404 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8408 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8409 * This is a shortcut for findParentNode() that always returns an Roo.Element.
8410 * @param {String} selector The simple selector to test
8411 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8412 search as a number or element (defaults to 10 || document.body)
8413 * @return {Roo.Element} The matching DOM node (or null if no match was found)
8415 up : function(simpleSelector, maxDepth){
8416 return this.findParentNode(simpleSelector, maxDepth, true);
8422 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8423 * @param {String} selector The simple selector to test
8424 * @return {Boolean} True if this element matches the selector, else false
8426 is : function(simpleSelector){
8427 return Roo.DomQuery.is(this.dom, simpleSelector);
8431 * Perform animation on this element.
8432 * @param {Object} args The YUI animation control args
8433 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8434 * @param {Function} onComplete (optional) Function to call when animation completes
8435 * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8436 * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8437 * @return {Roo.Element} this
8439 animate : function(args, duration, onComplete, easing, animType){
8440 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8445 * @private Internal animation call
8447 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8448 animType = animType || 'run';
8450 var anim = Roo.lib.Anim[animType](
8452 (opt.duration || defaultDur) || .35,
8453 (opt.easing || defaultEase) || 'easeOut',
8455 Roo.callback(cb, this);
8456 Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8464 // private legacy anim prep
8465 preanim : function(a, i){
8466 return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8470 * Removes worthless text nodes
8471 * @param {Boolean} forceReclean (optional) By default the element
8472 * keeps track if it has been cleaned already so
8473 * you can call this over and over. However, if you update the element and
8474 * need to force a reclean, you can pass true.
8476 clean : function(forceReclean){
8477 if(this.isCleaned && forceReclean !== true){
8481 var d = this.dom, n = d.firstChild, ni = -1;
8483 var nx = n.nextSibling;
8484 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8491 this.isCleaned = true;
8496 calcOffsetsTo : function(el){
8499 var restorePos = false;
8500 if(el.getStyle('position') == 'static'){
8501 el.position('relative');
8506 while(op && op != d && op.tagName != 'HTML'){
8509 op = op.offsetParent;
8512 el.position('static');
8518 * Scrolls this element into view within the passed container.
8519 * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8520 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8521 * @return {Roo.Element} this
8523 scrollIntoView : function(container, hscroll){
8524 var c = Roo.getDom(container) || document.body;
8527 var o = this.calcOffsetsTo(c),
8530 b = t+el.offsetHeight,
8531 r = l+el.offsetWidth;
8533 var ch = c.clientHeight;
8534 var ct = parseInt(c.scrollTop, 10);
8535 var cl = parseInt(c.scrollLeft, 10);
8537 var cr = cl + c.clientWidth;
8545 if(hscroll !== false){
8549 c.scrollLeft = r-c.clientWidth;
8556 scrollChildIntoView : function(child, hscroll){
8557 Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8561 * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8562 * the new height may not be available immediately.
8563 * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8564 * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8565 * @param {Function} onComplete (optional) Function to call when animation completes
8566 * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8567 * @return {Roo.Element} this
8569 autoHeight : function(animate, duration, onComplete, easing){
8570 var oldHeight = this.getHeight();
8572 this.setHeight(1); // force clipping
8573 setTimeout(function(){
8574 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8576 this.setHeight(height);
8578 if(typeof onComplete == "function"){
8582 this.setHeight(oldHeight); // restore original height
8583 this.setHeight(height, animate, duration, function(){
8585 if(typeof onComplete == "function") { onComplete(); }
8586 }.createDelegate(this), easing);
8588 }.createDelegate(this), 0);
8593 * Returns true if this element is an ancestor of the passed element
8594 * @param {HTMLElement/String} el The element to check
8595 * @return {Boolean} True if this element is an ancestor of el, else false
8597 contains : function(el){
8598 if(!el){return false;}
8599 return D.isAncestor(this.dom, el.dom ? el.dom : el);
8603 * Checks whether the element is currently visible using both visibility and display properties.
8604 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8605 * @return {Boolean} True if the element is currently visible, else false
8607 isVisible : function(deep) {
8608 var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8609 if(deep !== true || !vis){
8612 var p = this.dom.parentNode;
8613 while(p && p.tagName.toLowerCase() != "body"){
8614 if(!Roo.fly(p, '_isVisible').isVisible()){
8623 * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8624 * @param {String} selector The CSS selector
8625 * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8626 * @return {CompositeElement/CompositeElementLite} The composite element
8628 select : function(selector, unique){
8629 return El.select(selector, unique, this.dom);
8633 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8634 * @param {String} selector The CSS selector
8635 * @return {Array} An array of the matched nodes
8637 query : function(selector, unique){
8638 return Roo.DomQuery.select(selector, this.dom);
8642 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8643 * @param {String} selector The CSS selector
8644 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8645 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8647 child : function(selector, returnDom){
8648 var n = Roo.DomQuery.selectNode(selector, this.dom);
8649 return returnDom ? n : Roo.get(n);
8653 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8654 * @param {String} selector The CSS selector
8655 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8656 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8658 down : function(selector, returnDom){
8659 var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8660 return returnDom ? n : Roo.get(n);
8664 * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8665 * @param {String} group The group the DD object is member of
8666 * @param {Object} config The DD config object
8667 * @param {Object} overrides An object containing methods to override/implement on the DD object
8668 * @return {Roo.dd.DD} The DD object
8670 initDD : function(group, config, overrides){
8671 var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8672 return Roo.apply(dd, overrides);
8676 * Initializes a {@link Roo.dd.DDProxy} object for this element.
8677 * @param {String} group The group the DDProxy object is member of
8678 * @param {Object} config The DDProxy config object
8679 * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8680 * @return {Roo.dd.DDProxy} The DDProxy object
8682 initDDProxy : function(group, config, overrides){
8683 var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8684 return Roo.apply(dd, overrides);
8688 * Initializes a {@link Roo.dd.DDTarget} object for this element.
8689 * @param {String} group The group the DDTarget object is member of
8690 * @param {Object} config The DDTarget config object
8691 * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8692 * @return {Roo.dd.DDTarget} The DDTarget object
8694 initDDTarget : function(group, config, overrides){
8695 var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8696 return Roo.apply(dd, overrides);
8700 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8701 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8702 * @param {Boolean} visible Whether the element is visible
8703 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8704 * @return {Roo.Element} this
8706 setVisible : function(visible, animate){
8708 if(this.visibilityMode == El.DISPLAY){
8709 this.setDisplayed(visible);
8712 this.dom.style.visibility = visible ? "visible" : "hidden";
8715 // closure for composites
8717 var visMode = this.visibilityMode;
8719 this.setOpacity(.01);
8720 this.setVisible(true);
8722 this.anim({opacity: { to: (visible?1:0) }},
8723 this.preanim(arguments, 1),
8724 null, .35, 'easeIn', function(){
8726 if(visMode == El.DISPLAY){
8727 dom.style.display = "none";
8729 dom.style.visibility = "hidden";
8731 Roo.get(dom).setOpacity(1);
8739 * Returns true if display is not "none"
8742 isDisplayed : function() {
8743 return this.getStyle("display") != "none";
8747 * Toggles the element's visibility or display, depending on visibility mode.
8748 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8749 * @return {Roo.Element} this
8751 toggle : function(animate){
8752 this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8757 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8758 * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8759 * @return {Roo.Element} this
8761 setDisplayed : function(value) {
8762 if(typeof value == "boolean"){
8763 value = value ? this.originalDisplay : "none";
8765 this.setStyle("display", value);
8770 * Tries to focus the element. Any exceptions are caught and ignored.
8771 * @return {Roo.Element} this
8773 focus : function() {
8781 * Tries to blur the element. Any exceptions are caught and ignored.
8782 * @return {Roo.Element} this
8792 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8793 * @param {String/Array} className The CSS class to add, or an array of classes
8794 * @return {Roo.Element} this
8796 addClass : function(className){
8797 if(className instanceof Array){
8798 for(var i = 0, len = className.length; i < len; i++) {
8799 this.addClass(className[i]);
8802 if(className && !this.hasClass(className)){
8803 if (this.dom instanceof SVGElement) {
8804 this.dom.className.baseVal =this.dom.className.baseVal + " " + className;
8806 this.dom.className = this.dom.className + " " + className;
8814 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8815 * @param {String/Array} className The CSS class to add, or an array of classes
8816 * @return {Roo.Element} this
8818 radioClass : function(className){
8819 var siblings = this.dom.parentNode.childNodes;
8820 for(var i = 0; i < siblings.length; i++) {
8821 var s = siblings[i];
8822 if(s.nodeType == 1){
8823 Roo.get(s).removeClass(className);
8826 this.addClass(className);
8831 * Removes one or more CSS classes from the element.
8832 * @param {String/Array} className The CSS class to remove, or an array of classes
8833 * @return {Roo.Element} this
8835 removeClass : function(className){
8837 var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8838 if(!className || !cn){
8841 if(className instanceof Array){
8842 for(var i = 0, len = className.length; i < len; i++) {
8843 this.removeClass(className[i]);
8846 if(this.hasClass(className)){
8847 var re = this.classReCache[className];
8849 re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8850 this.classReCache[className] = re;
8852 if (this.dom instanceof SVGElement) {
8853 this.dom.className.baseVal = cn.replace(re, " ");
8855 this.dom.className = cn.replace(re, " ");
8866 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8867 * @param {String} className The CSS class to toggle
8868 * @return {Roo.Element} this
8870 toggleClass : function(className){
8871 if(this.hasClass(className)){
8872 this.removeClass(className);
8874 this.addClass(className);
8880 * Checks if the specified CSS class exists on this element's DOM node.
8881 * @param {String} className The CSS class to check for
8882 * @return {Boolean} True if the class exists, else false
8884 hasClass : function(className){
8885 if (this.dom instanceof SVGElement) {
8886 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1;
8888 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8892 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
8893 * @param {String} oldClassName The CSS class to replace
8894 * @param {String} newClassName The replacement CSS class
8895 * @return {Roo.Element} this
8897 replaceClass : function(oldClassName, newClassName){
8898 this.removeClass(oldClassName);
8899 this.addClass(newClassName);
8904 * Returns an object with properties matching the styles requested.
8905 * For example, el.getStyles('color', 'font-size', 'width') might return
8906 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8907 * @param {String} style1 A style name
8908 * @param {String} style2 A style name
8909 * @param {String} etc.
8910 * @return {Object} The style object
8912 getStyles : function(){
8913 var a = arguments, len = a.length, r = {};
8914 for(var i = 0; i < len; i++){
8915 r[a[i]] = this.getStyle(a[i]);
8921 * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8922 * @param {String} property The style property whose value is returned.
8923 * @return {String} The current value of the style property for this element.
8925 getStyle : function(){
8926 return view && view.getComputedStyle ?
8928 var el = this.dom, v, cs, camel;
8929 if(prop == 'float'){
8932 if(el.style && (v = el.style[prop])){
8935 if(cs = view.getComputedStyle(el, "")){
8936 if(!(camel = propCache[prop])){
8937 camel = propCache[prop] = prop.replace(camelRe, camelFn);
8944 var el = this.dom, v, cs, camel;
8945 if(prop == 'opacity'){
8946 if(typeof el.style.filter == 'string'){
8947 var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8949 var fv = parseFloat(m[1]);
8951 return fv ? fv / 100 : 0;
8956 }else if(prop == 'float'){
8957 prop = "styleFloat";
8959 if(!(camel = propCache[prop])){
8960 camel = propCache[prop] = prop.replace(camelRe, camelFn);
8962 if(v = el.style[camel]){
8965 if(cs = el.currentStyle){
8973 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8974 * @param {String/Object} property The style property to be set, or an object of multiple styles.
8975 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8976 * @return {Roo.Element} this
8978 setStyle : function(prop, value){
8979 if(typeof prop == "string"){
8981 if (prop == 'float') {
8982 this.setStyle(Roo.isIE ? 'styleFloat' : 'cssFloat', value);
8987 if(!(camel = propCache[prop])){
8988 camel = propCache[prop] = prop.replace(camelRe, camelFn);
8991 if(camel == 'opacity') {
8992 this.setOpacity(value);
8994 this.dom.style[camel] = value;
8997 for(var style in prop){
8998 if(typeof prop[style] != "function"){
8999 this.setStyle(style, prop[style]);
9007 * More flexible version of {@link #setStyle} for setting style properties.
9008 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9009 * a function which returns such a specification.
9010 * @return {Roo.Element} this
9012 applyStyles : function(style){
9013 Roo.DomHelper.applyStyles(this.dom, style);
9018 * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9019 * @return {Number} The X position of the element
9022 return D.getX(this.dom);
9026 * Gets the current Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9027 * @return {Number} The Y position of the element
9030 return D.getY(this.dom);
9034 * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9035 * @return {Array} The XY position of the element
9038 return D.getXY(this.dom);
9042 * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9043 * @param {Number} The X position of the element
9044 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9045 * @return {Roo.Element} this
9047 setX : function(x, animate){
9049 D.setX(this.dom, x);
9051 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9057 * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9058 * @param {Number} The Y position of the element
9059 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9060 * @return {Roo.Element} this
9062 setY : function(y, animate){
9064 D.setY(this.dom, y);
9066 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9072 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9073 * @param {String} left The left CSS property value
9074 * @return {Roo.Element} this
9076 setLeft : function(left){
9077 this.setStyle("left", this.addUnits(left));
9082 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9083 * @param {String} top The top CSS property value
9084 * @return {Roo.Element} this
9086 setTop : function(top){
9087 this.setStyle("top", this.addUnits(top));
9092 * Sets the element's CSS right style.
9093 * @param {String} right The right CSS property value
9094 * @return {Roo.Element} this
9096 setRight : function(right){
9097 this.setStyle("right", this.addUnits(right));
9102 * Sets the element's CSS bottom style.
9103 * @param {String} bottom The bottom CSS property value
9104 * @return {Roo.Element} this
9106 setBottom : function(bottom){
9107 this.setStyle("bottom", this.addUnits(bottom));
9112 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9113 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9114 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9115 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9116 * @return {Roo.Element} this
9118 setXY : function(pos, animate){
9120 D.setXY(this.dom, pos);
9122 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9128 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9129 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9130 * @param {Number} x X value for new position (coordinates are page-based)
9131 * @param {Number} y Y value for new position (coordinates are page-based)
9132 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9133 * @return {Roo.Element} this
9135 setLocation : function(x, y, animate){
9136 this.setXY([x, y], this.preanim(arguments, 2));
9141 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9142 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9143 * @param {Number} x X value for new position (coordinates are page-based)
9144 * @param {Number} y Y value for new position (coordinates are page-based)
9145 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9146 * @return {Roo.Element} this
9148 moveTo : function(x, y, animate){
9149 this.setXY([x, y], this.preanim(arguments, 2));
9154 * Returns the region of the given element.
9155 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9156 * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9158 getRegion : function(){
9159 return D.getRegion(this.dom);
9163 * Returns the offset height of the element
9164 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9165 * @return {Number} The element's height
9167 getHeight : function(contentHeight){
9168 var h = this.dom.offsetHeight || 0;
9169 return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9173 * Returns the offset width of the element
9174 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9175 * @return {Number} The element's width
9177 getWidth : function(contentWidth){
9178 var w = this.dom.offsetWidth || 0;
9179 return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9183 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9184 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9185 * if a height has not been set using CSS.
9188 getComputedHeight : function(){
9189 var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9191 h = parseInt(this.getStyle('height'), 10) || 0;
9192 if(!this.isBorderBox()){
9193 h += this.getFrameWidth('tb');
9200 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9201 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9202 * if a width has not been set using CSS.
9205 getComputedWidth : function(){
9206 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9208 w = parseInt(this.getStyle('width'), 10) || 0;
9209 if(!this.isBorderBox()){
9210 w += this.getFrameWidth('lr');
9217 * Returns the size of the element.
9218 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9219 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9221 getSize : function(contentSize){
9222 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9226 * Returns the width and height of the viewport.
9227 * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9229 getViewSize : function(){
9230 var d = this.dom, doc = document, aw = 0, ah = 0;
9231 if(d == doc || d == doc.body){
9232 return {width : D.getViewWidth(), height: D.getViewHeight()};
9235 width : d.clientWidth,
9236 height: d.clientHeight
9242 * Returns the value of the "value" attribute
9243 * @param {Boolean} asNumber true to parse the value as a number
9244 * @return {String/Number}
9246 getValue : function(asNumber){
9247 return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9251 adjustWidth : function(width){
9252 if(typeof width == "number"){
9253 if(this.autoBoxAdjust && !this.isBorderBox()){
9254 width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9264 adjustHeight : function(height){
9265 if(typeof height == "number"){
9266 if(this.autoBoxAdjust && !this.isBorderBox()){
9267 height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9277 * Set the width of the element
9278 * @param {Number} width The new width
9279 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9280 * @return {Roo.Element} this
9282 setWidth : function(width, animate){
9283 width = this.adjustWidth(width);
9285 this.dom.style.width = this.addUnits(width);
9287 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9293 * Set the height of the element
9294 * @param {Number} height The new height
9295 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9296 * @return {Roo.Element} this
9298 setHeight : function(height, animate){
9299 height = this.adjustHeight(height);
9301 this.dom.style.height = this.addUnits(height);
9303 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9309 * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9310 * @param {Number} width The new width
9311 * @param {Number} height The new height
9312 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9313 * @return {Roo.Element} this
9315 setSize : function(width, height, animate){
9316 if(typeof width == "object"){ // in case of object from getSize()
9317 height = width.height; width = width.width;
9319 width = this.adjustWidth(width); height = this.adjustHeight(height);
9321 this.dom.style.width = this.addUnits(width);
9322 this.dom.style.height = this.addUnits(height);
9324 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9330 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9331 * @param {Number} x X value for new position (coordinates are page-based)
9332 * @param {Number} y Y value for new position (coordinates are page-based)
9333 * @param {Number} width The new width
9334 * @param {Number} height The new height
9335 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9336 * @return {Roo.Element} this
9338 setBounds : function(x, y, width, height, animate){
9340 this.setSize(width, height);
9341 this.setLocation(x, y);
9343 width = this.adjustWidth(width); height = this.adjustHeight(height);
9344 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9345 this.preanim(arguments, 4), 'motion');
9351 * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
9352 * @param {Roo.lib.Region} region The region to fill
9353 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9354 * @return {Roo.Element} this
9356 setRegion : function(region, animate){
9357 this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9362 * Appends an event handler
9364 * @param {String} eventName The type of event to append
9365 * @param {Function} fn The method the event invokes
9366 * @param {Object} scope (optional) The scope (this object) of the fn
9367 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
9369 addListener : function(eventName, fn, scope, options)
9371 if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9372 this.addListener('touchstart', this.onTapHandler, this);
9375 // we need to handle a special case where dom element is a svg element.
9376 // in this case we do not actua
9381 if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9382 if (typeof(this.listeners[eventName]) == 'undefined') {
9383 this.listeners[eventName] = new Roo.util.Event(this, eventName);
9385 this.listeners[eventName].addListener(fn, scope, options);
9390 Roo.EventManager.on(this.dom, eventName, fn, scope || this, options);
9395 onTapHandler : function(event)
9397 if(!this.tapedTwice) {
9398 this.tapedTwice = true;
9400 setTimeout( function() {
9401 s.tapedTwice = false;
9405 event.preventDefault();
9406 var revent = new MouseEvent('dblclick', {
9412 this.dom.dispatchEvent(revent);
9413 //action on double tap goes below
9418 * Removes an event handler from this element
9419 * @param {String} eventName the type of event to remove
9420 * @param {Function} fn the method the event invokes
9421 * @param {Function} scope (needed for svg fake listeners)
9422 * @return {Roo.Element} this
9424 removeListener : function(eventName, fn, scope){
9425 Roo.EventManager.removeListener(this.dom, eventName, fn);
9426 if (typeof(this.listeners) == 'undefined' || typeof(this.listeners[eventName]) == 'undefined') {
9429 this.listeners[eventName].removeListener(fn, scope);
9434 * Removes all previous added listeners from this element
9435 * @return {Roo.Element} this
9437 removeAllListeners : function(){
9438 E.purgeElement(this.dom);
9439 this.listeners = {};
9443 relayEvent : function(eventName, observable){
9444 this.on(eventName, function(e){
9445 observable.fireEvent(eventName, e);
9451 * Set the opacity of the element
9452 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9453 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454 * @return {Roo.Element} this
9456 setOpacity : function(opacity, animate){
9458 var s = this.dom.style;
9461 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9462 (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9464 s.opacity = opacity;
9467 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9473 * Gets the left X coordinate
9474 * @param {Boolean} local True to get the local css position instead of page coordinate
9477 getLeft : function(local){
9481 return parseInt(this.getStyle("left"), 10) || 0;
9486 * Gets the right X coordinate of the element (element X position + element width)
9487 * @param {Boolean} local True to get the local css position instead of page coordinate
9490 getRight : function(local){
9492 return this.getX() + this.getWidth();
9494 return (this.getLeft(true) + this.getWidth()) || 0;
9499 * Gets the top Y coordinate
9500 * @param {Boolean} local True to get the local css position instead of page coordinate
9503 getTop : function(local) {
9507 return parseInt(this.getStyle("top"), 10) || 0;
9512 * Gets the bottom Y coordinate of the element (element Y position + element height)
9513 * @param {Boolean} local True to get the local css position instead of page coordinate
9516 getBottom : function(local){
9518 return this.getY() + this.getHeight();
9520 return (this.getTop(true) + this.getHeight()) || 0;
9525 * Initializes positioning on this element. If a desired position is not passed, it will make the
9526 * the element positioned relative IF it is not already positioned.
9527 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9528 * @param {Number} zIndex (optional) The zIndex to apply
9529 * @param {Number} x (optional) Set the page X position
9530 * @param {Number} y (optional) Set the page Y position
9532 position : function(pos, zIndex, x, y){
9534 if(this.getStyle('position') == 'static'){
9535 this.setStyle('position', 'relative');
9538 this.setStyle("position", pos);
9541 this.setStyle("z-index", zIndex);
9543 if(x !== undefined && y !== undefined){
9545 }else if(x !== undefined){
9547 }else if(y !== undefined){
9553 * Clear positioning back to the default when the document was loaded
9554 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9555 * @return {Roo.Element} this
9557 clearPositioning : function(value){
9565 "position" : "static"
9571 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9572 * snapshot before performing an update and then restoring the element.
9575 getPositioning : function(){
9576 var l = this.getStyle("left");
9577 var t = this.getStyle("top");
9579 "position" : this.getStyle("position"),
9581 "right" : l ? "" : this.getStyle("right"),
9583 "bottom" : t ? "" : this.getStyle("bottom"),
9584 "z-index" : this.getStyle("z-index")
9589 * Gets the width of the border(s) for the specified side(s)
9590 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9591 * passing lr would get the border (l)eft width + the border (r)ight width.
9592 * @return {Number} The width of the sides passed added together
9594 getBorderWidth : function(side){
9595 return this.addStyles(side, El.borders);
9599 * Gets the width of the padding(s) for the specified side(s)
9600 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9601 * passing lr would get the padding (l)eft + the padding (r)ight.
9602 * @return {Number} The padding of the sides passed added together
9604 getPadding : function(side){
9605 return this.addStyles(side, El.paddings);
9609 * Set positioning with an object returned by getPositioning().
9610 * @param {Object} posCfg
9611 * @return {Roo.Element} this
9613 setPositioning : function(pc){
9614 this.applyStyles(pc);
9615 if(pc.right == "auto"){
9616 this.dom.style.right = "";
9618 if(pc.bottom == "auto"){
9619 this.dom.style.bottom = "";
9625 fixDisplay : function(){
9626 if(this.getStyle("display") == "none"){
9627 this.setStyle("visibility", "hidden");
9628 this.setStyle("display", this.originalDisplay); // first try reverting to default
9629 if(this.getStyle("display") == "none"){ // if that fails, default to block
9630 this.setStyle("display", "block");
9636 * Quick set left and top adding default units
9637 * @param {String} left The left CSS property value
9638 * @param {String} top The top CSS property value
9639 * @return {Roo.Element} this
9641 setLeftTop : function(left, top){
9642 this.dom.style.left = this.addUnits(left);
9643 this.dom.style.top = this.addUnits(top);
9648 * Move this element relative to its current position.
9649 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9650 * @param {Number} distance How far to move the element in pixels
9651 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652 * @return {Roo.Element} this
9654 move : function(direction, distance, animate){
9655 var xy = this.getXY();
9656 direction = direction.toLowerCase();
9660 this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9664 this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9669 this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9674 this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9681 * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9682 * @return {Roo.Element} this
9685 if(!this.isClipped){
9686 this.isClipped = true;
9687 this.originalClip = {
9688 "o": this.getStyle("overflow"),
9689 "x": this.getStyle("overflow-x"),
9690 "y": this.getStyle("overflow-y")
9692 this.setStyle("overflow", "hidden");
9693 this.setStyle("overflow-x", "hidden");
9694 this.setStyle("overflow-y", "hidden");
9700 * Return clipping (overflow) to original clipping before clip() was called
9701 * @return {Roo.Element} this
9703 unclip : function(){
9705 this.isClipped = false;
9706 var o = this.originalClip;
9707 if(o.o){this.setStyle("overflow", o.o);}
9708 if(o.x){this.setStyle("overflow-x", o.x);}
9709 if(o.y){this.setStyle("overflow-y", o.y);}
9716 * Gets the x,y coordinates specified by the anchor position on the element.
9717 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo} for details on supported anchor positions.
9718 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9719 * {width: (target width), height: (target height)} (defaults to the element's current size)
9720 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9721 * @return {Array} [x, y] An array containing the element's x and y coordinates
9723 getAnchorXY : function(anchor, local, s){
9724 //Passing a different size is useful for pre-calculating anchors,
9725 //especially for anchored animations that change the el size.
9727 var w, h, vp = false;
9730 if(d == document.body || d == document){
9732 w = D.getViewWidth(); h = D.getViewHeight();
9734 w = this.getWidth(); h = this.getHeight();
9737 w = s.width; h = s.height;
9739 var x = 0, y = 0, r = Math.round;
9740 switch((anchor || "tl").toLowerCase()){
9782 var sc = this.getScroll();
9783 return [x + sc.left, y + sc.top];
9785 //Add the element's offset xy
9786 var o = this.getXY();
9787 return [x+o[0], y+o[1]];
9791 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9792 * supported position values.
9793 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9794 * @param {String} position The position to align to.
9795 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9796 * @return {Array} [x, y]
9798 getAlignToXY : function(el, p, o)
9803 throw "Element.alignTo with an element that doesn't exist";
9805 var c = false; //constrain to viewport
9806 var p1 = "", p2 = "";
9813 }else if(p.indexOf("-") == -1){
9816 p = p.toLowerCase();
9817 var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9819 throw "Element.alignTo with an invalid alignment " + p;
9821 p1 = m[1]; p2 = m[2]; c = !!m[3];
9823 //Subtract the aligned el's internal xy from the target's offset xy
9824 //plus custom offset to get the aligned el's new offset xy
9825 var a1 = this.getAnchorXY(p1, true);
9826 var a2 = el.getAnchorXY(p2, false);
9827 var x = a2[0] - a1[0] + o[0];
9828 var y = a2[1] - a1[1] + o[1];
9830 //constrain the aligned el to viewport if necessary
9831 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9832 // 5px of margin for ie
9833 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9835 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9836 //perpendicular to the vp border, allow the aligned el to slide on that border,
9837 //otherwise swap the aligned el to the opposite border of the target.
9838 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9839 var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9840 var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t") );
9841 var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9844 var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9845 var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9847 if((x+w) > dw + scrollX){
9848 x = swapX ? r.left-w : dw+scrollX-w;
9851 x = swapX ? r.right : scrollX;
9853 if((y+h) > dh + scrollY){
9854 y = swapY ? r.top-h : dh+scrollY-h;
9857 y = swapY ? r.bottom : scrollY;
9864 getConstrainToXY : function(){
9865 var os = {top:0, left:0, bottom:0, right: 0};
9867 return function(el, local, offsets, proposedXY){
9869 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9871 var vw, vh, vx = 0, vy = 0;
9872 if(el.dom == document.body || el.dom == document){
9873 vw = Roo.lib.Dom.getViewWidth();
9874 vh = Roo.lib.Dom.getViewHeight();
9876 vw = el.dom.clientWidth;
9877 vh = el.dom.clientHeight;
9879 var vxy = el.getXY();
9885 var s = el.getScroll();
9887 vx += offsets.left + s.left;
9888 vy += offsets.top + s.top;
9890 vw -= offsets.right;
9891 vh -= offsets.bottom;
9896 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9897 var x = xy[0], y = xy[1];
9898 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9900 // only move it if it needs it
9903 // first validate right/bottom
9912 // then make sure top/left isn't negative
9921 return moved ? [x, y] : false;
9926 adjustForConstraints : function(xy, parent, offsets){
9927 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
9931 * Aligns this element with another element relative to the specified anchor points. If the other element is the
9932 * document it aligns it to the viewport.
9933 * The position parameter is optional, and can be specified in any one of the following formats:
9935 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9936 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9937 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
9938 * deprecated in favor of the newer two anchor syntax below</i>.</li>
9939 * <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
9940 * element's anchor point, and the second value is used as the target's anchor point.</li>
9942 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
9943 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9944 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
9945 * that specified in order to enforce the viewport constraints.
9946 * Following are all of the supported anchor positions:
9949 ----- -----------------------------
9950 tl The top left corner (default)
9951 t The center of the top edge
9952 tr The top right corner
9953 l The center of the left edge
9954 c In the center of the element
9955 r The center of the right edge
9956 bl The bottom left corner
9957 b The center of the bottom edge
9958 br The bottom right corner
9962 // align el to other-el using the default positioning ("tl-bl", non-constrained)
9963 el.alignTo("other-el");
9965 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9966 el.alignTo("other-el", "tr?");
9968 // align the bottom right corner of el with the center left edge of other-el
9969 el.alignTo("other-el", "br-l?");
9971 // align the center of el with the bottom left corner of other-el and
9972 // adjust the x position by -6 pixels (and the y position by 0)
9973 el.alignTo("other-el", "c-bl", [-6, 0]);
9975 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9976 * @param {String} position The position to align to.
9977 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9978 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9979 * @return {Roo.Element} this
9981 alignTo : function(element, position, offsets, animate){
9982 var xy = this.getAlignToXY(element, position, offsets);
9983 this.setXY(xy, this.preanim(arguments, 3));
9988 * Anchors an element to another element and realigns it when the window is resized.
9989 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9990 * @param {String} position The position to align to.
9991 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9992 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9993 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9994 * is a number, it is used as the buffer delay (defaults to 50ms).
9995 * @param {Function} callback The function to call after the animation finishes
9996 * @return {Roo.Element} this
9998 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9999 var action = function(){
10000 this.alignTo(el, alignment, offsets, animate);
10001 Roo.callback(callback, this);
10003 Roo.EventManager.onWindowResize(action, this);
10004 var tm = typeof monitorScroll;
10005 if(tm != 'undefined'){
10006 Roo.EventManager.on(window, 'scroll', action, this,
10007 {buffer: tm == 'number' ? monitorScroll : 50});
10009 action.call(this); // align immediately
10013 * Clears any opacity settings from this element. Required in some cases for IE.
10014 * @return {Roo.Element} this
10016 clearOpacity : function(){
10017 if (window.ActiveXObject) {
10018 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10019 this.dom.style.filter = "";
10022 this.dom.style.opacity = "";
10023 this.dom.style["-moz-opacity"] = "";
10024 this.dom.style["-khtml-opacity"] = "";
10030 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10031 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10032 * @return {Roo.Element} this
10034 hide : function(animate){
10035 this.setVisible(false, this.preanim(arguments, 0));
10040 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10041 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10042 * @return {Roo.Element} this
10044 show : function(animate){
10045 this.setVisible(true, this.preanim(arguments, 0));
10050 * @private Test if size has a unit, otherwise appends the default
10052 addUnits : function(size){
10053 return Roo.Element.addUnits(size, this.defaultUnit);
10057 * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10058 * @return {Roo.Element} this
10060 beginMeasure : function(){
10062 if(el.offsetWidth || el.offsetHeight){
10063 return this; // offsets work already
10066 var p = this.dom, b = document.body; // start with this element
10067 while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10068 var pe = Roo.get(p);
10069 if(pe.getStyle('display') == 'none'){
10070 changed.push({el: p, visibility: pe.getStyle("visibility")});
10071 p.style.visibility = "hidden";
10072 p.style.display = "block";
10076 this._measureChanged = changed;
10082 * Restores displays to before beginMeasure was called
10083 * @return {Roo.Element} this
10085 endMeasure : function(){
10086 var changed = this._measureChanged;
10088 for(var i = 0, len = changed.length; i < len; i++) {
10089 var r = changed[i];
10090 r.el.style.visibility = r.visibility;
10091 r.el.style.display = "none";
10093 this._measureChanged = null;
10099 * Update the innerHTML of this element, optionally searching for and processing scripts
10100 * @param {String} html The new HTML
10101 * @param {Boolean} loadScripts (optional) true to look for and process scripts
10102 * @param {Function} callback For async script loading you can be noticed when the update completes
10103 * @return {Roo.Element} this
10105 update : function(html, loadScripts, callback){
10106 if(typeof html == "undefined"){
10109 if(loadScripts !== true){
10110 this.dom.innerHTML = html;
10111 if(typeof callback == "function"){
10117 var dom = this.dom;
10119 html += '<span id="' + id + '"></span>';
10121 E.onAvailable(id, function(){
10122 var hd = document.getElementsByTagName("head")[0];
10123 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10124 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10125 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10128 while(match = re.exec(html)){
10129 var attrs = match[1];
10130 var srcMatch = attrs ? attrs.match(srcRe) : false;
10131 if(srcMatch && srcMatch[2]){
10132 var s = document.createElement("script");
10133 s.src = srcMatch[2];
10134 var typeMatch = attrs.match(typeRe);
10135 if(typeMatch && typeMatch[2]){
10136 s.type = typeMatch[2];
10139 }else if(match[2] && match[2].length > 0){
10140 if(window.execScript) {
10141 window.execScript(match[2]);
10149 window.eval(match[2]);
10153 var el = document.getElementById(id);
10154 if(el){el.parentNode.removeChild(el);}
10155 if(typeof callback == "function"){
10159 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10164 * Direct access to the UpdateManager update() method (takes the same parameters).
10165 * @param {String/Function} url The url for this request or a function to call to get the url
10166 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
10167 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10168 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
10169 * @return {Roo.Element} this
10172 var um = this.getUpdateManager();
10173 um.update.apply(um, arguments);
10178 * Gets this element's UpdateManager
10179 * @return {Roo.UpdateManager} The UpdateManager
10181 getUpdateManager : function(){
10182 if(!this.updateManager){
10183 this.updateManager = new Roo.UpdateManager(this);
10185 return this.updateManager;
10189 * Disables text selection for this element (normalized across browsers)
10190 * @return {Roo.Element} this
10192 unselectable : function(){
10193 this.dom.unselectable = "on";
10194 this.swallowEvent("selectstart", true);
10195 this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10196 this.addClass("x-unselectable");
10201 * Calculates the x, y to center this element on the screen
10202 * @return {Array} The x, y values [x, y]
10204 getCenterXY : function(){
10205 return this.getAlignToXY(document, 'c-c');
10209 * Centers the Element in either the viewport, or another Element.
10210 * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10212 center : function(centerIn){
10213 this.alignTo(centerIn || document, 'c-c');
10218 * Tests various css rules/browsers to determine if this element uses a border box
10219 * @return {Boolean}
10221 isBorderBox : function(){
10222 return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10226 * Return a box {x, y, width, height} that can be used to set another elements
10227 * size/location to match this element.
10228 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10229 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10230 * @return {Object} box An object in the format {x, y, width, height}
10232 getBox : function(contentBox, local){
10237 var left = parseInt(this.getStyle("left"), 10) || 0;
10238 var top = parseInt(this.getStyle("top"), 10) || 0;
10241 var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10243 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10245 var l = this.getBorderWidth("l")+this.getPadding("l");
10246 var r = this.getBorderWidth("r")+this.getPadding("r");
10247 var t = this.getBorderWidth("t")+this.getPadding("t");
10248 var b = this.getBorderWidth("b")+this.getPadding("b");
10249 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
10251 bx.right = bx.x + bx.width;
10252 bx.bottom = bx.y + bx.height;
10257 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10258 for more information about the sides.
10259 * @param {String} sides
10262 getFrameWidth : function(sides, onlyContentBox){
10263 return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10267 * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
10268 * @param {Object} box The box to fill {x, y, width, height}
10269 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10270 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10271 * @return {Roo.Element} this
10273 setBox : function(box, adjust, animate){
10274 var w = box.width, h = box.height;
10275 if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10276 w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10277 h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10279 this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10284 * Forces the browser to repaint this element
10285 * @return {Roo.Element} this
10287 repaint : function(){
10288 var dom = this.dom;
10289 this.addClass("x-repaint");
10290 setTimeout(function(){
10291 Roo.get(dom).removeClass("x-repaint");
10297 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10298 * then it returns the calculated width of the sides (see getPadding)
10299 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10300 * @return {Object/Number}
10302 getMargins : function(side){
10305 top: parseInt(this.getStyle("margin-top"), 10) || 0,
10306 left: parseInt(this.getStyle("margin-left"), 10) || 0,
10307 bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10308 right: parseInt(this.getStyle("margin-right"), 10) || 0
10311 return this.addStyles(side, El.margins);
10316 addStyles : function(sides, styles){
10318 for(var i = 0, len = sides.length; i < len; i++){
10319 v = this.getStyle(styles[sides.charAt(i)]);
10321 w = parseInt(v, 10);
10329 * Creates a proxy element of this element
10330 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10331 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10332 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10333 * @return {Roo.Element} The new proxy element
10335 createProxy : function(config, renderTo, matchBox){
10337 renderTo = Roo.getDom(renderTo);
10339 renderTo = document.body;
10341 config = typeof config == "object" ?
10342 config : {tag : "div", cls: config};
10343 var proxy = Roo.DomHelper.append(renderTo, config, true);
10345 proxy.setBox(this.getBox());
10351 * Puts a mask over this element to disable user interaction. Requires core.css.
10352 * This method can only be applied to elements which accept child nodes.
10353 * @param {String} msg (optional) A message to display in the mask
10354 * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10355 * @return {Element} The mask element
10357 mask : function(msg, msgCls)
10359 if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10360 this.setStyle("position", "relative");
10363 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10366 this.addClass("x-masked");
10367 this._mask.setDisplayed(true);
10371 var dom = this.dom;
10372 while (dom && dom.style) {
10373 if (!isNaN(parseInt(dom.style.zIndex))) {
10374 z = Math.max(z, parseInt(dom.style.zIndex));
10376 dom = dom.parentNode;
10378 // if we are masking the body - then it hides everything..
10379 if (this.dom == document.body) {
10381 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10382 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10385 if(typeof msg == 'string'){
10386 if(!this._maskMsg){
10387 this._maskMsg = Roo.DomHelper.append(this.dom, {
10388 cls: "roo-el-mask-msg",
10392 cls: 'fa fa-spinner fa-spin'
10400 var mm = this._maskMsg;
10401 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10402 if (mm.dom.lastChild) { // weird IE issue?
10403 mm.dom.lastChild.innerHTML = msg;
10405 mm.setDisplayed(true);
10407 mm.setStyle('z-index', z + 102);
10409 if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10410 this._mask.setHeight(this.getHeight());
10412 this._mask.setStyle('z-index', z + 100);
10418 * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10419 * it is cached for reuse.
10421 unmask : function(removeEl){
10423 if(removeEl === true){
10424 this._mask.remove();
10427 this._maskMsg.remove();
10428 delete this._maskMsg;
10431 this._mask.setDisplayed(false);
10433 this._maskMsg.setDisplayed(false);
10437 this.removeClass("x-masked");
10441 * Returns true if this element is masked
10442 * @return {Boolean}
10444 isMasked : function(){
10445 return this._mask && this._mask.isVisible();
10449 * Creates an iframe shim for this element to keep selects and other windowed objects from
10451 * @return {Roo.Element} The new shim element
10453 createShim : function(){
10454 var el = document.createElement('iframe');
10455 el.frameBorder = 'no';
10456 el.className = 'roo-shim';
10457 if(Roo.isIE && Roo.isSecure){
10458 el.src = Roo.SSL_SECURE_URL;
10460 var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10461 shim.autoBoxAdjust = false;
10466 * Removes this element from the DOM and deletes it from the cache
10468 remove : function(){
10469 if(this.dom.parentNode){
10470 this.dom.parentNode.removeChild(this.dom);
10472 delete El.cache[this.dom.id];
10476 * Sets up event handlers to add and remove a css class when the mouse is over this element
10477 * @param {String} className
10478 * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10479 * mouseout events for children elements
10480 * @return {Roo.Element} this
10482 addClassOnOver : function(className, preventFlicker){
10483 this.on("mouseover", function(){
10484 Roo.fly(this, '_internal').addClass(className);
10486 var removeFn = function(e){
10487 if(preventFlicker !== true || !e.within(this, true)){
10488 Roo.fly(this, '_internal').removeClass(className);
10491 this.on("mouseout", removeFn, this.dom);
10496 * Sets up event handlers to add and remove a css class when this element has the focus
10497 * @param {String} className
10498 * @return {Roo.Element} this
10500 addClassOnFocus : function(className){
10501 this.on("focus", function(){
10502 Roo.fly(this, '_internal').addClass(className);
10504 this.on("blur", function(){
10505 Roo.fly(this, '_internal').removeClass(className);
10510 * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
10511 * @param {String} className
10512 * @return {Roo.Element} this
10514 addClassOnClick : function(className){
10515 var dom = this.dom;
10516 this.on("mousedown", function(){
10517 Roo.fly(dom, '_internal').addClass(className);
10518 var d = Roo.get(document);
10519 var fn = function(){
10520 Roo.fly(dom, '_internal').removeClass(className);
10521 d.removeListener("mouseup", fn);
10523 d.on("mouseup", fn);
10529 * Stops the specified event from bubbling and optionally prevents the default action
10530 * @param {String} eventName
10531 * @param {Boolean} preventDefault (optional) true to prevent the default action too
10532 * @return {Roo.Element} this
10534 swallowEvent : function(eventName, preventDefault){
10535 var fn = function(e){
10536 e.stopPropagation();
10537 if(preventDefault){
10538 e.preventDefault();
10541 if(eventName instanceof Array){
10542 for(var i = 0, len = eventName.length; i < len; i++){
10543 this.on(eventName[i], fn);
10547 this.on(eventName, fn);
10554 fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10557 * Sizes this element to its parent element's dimensions performing
10558 * neccessary box adjustments.
10559 * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10560 * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10561 * @return {Roo.Element} this
10563 fitToParent : function(monitorResize, targetParent) {
10564 Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10565 this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10566 if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10569 var p = Roo.get(targetParent || this.dom.parentNode);
10570 this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10571 if (monitorResize === true) {
10572 this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10573 Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10579 * Gets the next sibling, skipping text nodes
10580 * @return {HTMLElement} The next sibling or null
10582 getNextSibling : function(){
10583 var n = this.dom.nextSibling;
10584 while(n && n.nodeType != 1){
10591 * Gets the previous sibling, skipping text nodes
10592 * @return {HTMLElement} The previous sibling or null
10594 getPrevSibling : function(){
10595 var n = this.dom.previousSibling;
10596 while(n && n.nodeType != 1){
10597 n = n.previousSibling;
10604 * Appends the passed element(s) to this element
10605 * @param {String/HTMLElement/Array/Element/CompositeElement} el
10606 * @return {Roo.Element} this
10608 appendChild: function(el){
10615 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10616 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
10617 * automatically generated with the specified attributes.
10618 * @param {HTMLElement} insertBefore (optional) a child element of this element
10619 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10620 * @return {Roo.Element} The new child element
10622 createChild: function(config, insertBefore, returnDom){
10623 config = config || {tag:'div'};
10625 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10627 return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
10631 * Appends this element to the passed element
10632 * @param {String/HTMLElement/Element} el The new parent element
10633 * @return {Roo.Element} this
10635 appendTo: function(el){
10636 el = Roo.getDom(el);
10637 el.appendChild(this.dom);
10642 * Inserts this element before the passed element in the DOM
10643 * @param {String/HTMLElement/Element} el The element to insert before
10644 * @return {Roo.Element} this
10646 insertBefore: function(el){
10647 el = Roo.getDom(el);
10648 el.parentNode.insertBefore(this.dom, el);
10653 * Inserts this element after the passed element in the DOM
10654 * @param {String/HTMLElement/Element} el The element to insert after
10655 * @return {Roo.Element} this
10657 insertAfter: function(el){
10658 el = Roo.getDom(el);
10659 el.parentNode.insertBefore(this.dom, el.nextSibling);
10664 * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10665 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10666 * @return {Roo.Element} The new child
10668 insertFirst: function(el, returnDom){
10670 if(typeof el == 'object' && !el.nodeType){ // dh config
10671 return this.createChild(el, this.dom.firstChild, returnDom);
10673 el = Roo.getDom(el);
10674 this.dom.insertBefore(el, this.dom.firstChild);
10675 return !returnDom ? Roo.get(el) : el;
10680 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10681 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10682 * @param {String} where (optional) 'before' or 'after' defaults to before
10683 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10684 * @return {Roo.Element} the inserted Element
10686 insertSibling: function(el, where, returnDom){
10687 where = where ? where.toLowerCase() : 'before';
10689 var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10691 if(typeof el == 'object' && !el.nodeType){ // dh config
10692 if(where == 'after' && !this.dom.nextSibling){
10693 rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10695 rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10699 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10700 where == 'before' ? this.dom : this.dom.nextSibling);
10709 * Creates and wraps this element with another element
10710 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10711 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10712 * @return {HTMLElement/Element} The newly created wrapper element
10714 wrap: function(config, returnDom){
10716 config = {tag: "div"};
10718 var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10719 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10724 * Replaces the passed element with this element
10725 * @param {String/HTMLElement/Element} el The element to replace
10726 * @return {Roo.Element} this
10728 replace: function(el){
10730 this.insertBefore(el);
10736 * Inserts an html fragment into this element
10737 * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10738 * @param {String} html The HTML fragment
10739 * @param {Boolean} returnEl True to return an Roo.Element
10740 * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10742 insertHtml : function(where, html, returnEl){
10743 var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10744 return returnEl ? Roo.get(el) : el;
10748 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10749 * @param {Object} o The object with the attributes
10750 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10751 * @return {Roo.Element} this
10753 set : function(o, useSet){
10755 useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10756 for(var attr in o){
10757 if(attr == "style" || typeof o[attr] == "function") { continue; }
10759 el.className = o["cls"];
10762 el.setAttribute(attr, o[attr]);
10764 el[attr] = o[attr];
10769 Roo.DomHelper.applyStyles(el, o.style);
10775 * Convenience method for constructing a KeyMap
10776 * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
10777 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10778 * @param {Function} fn The function to call
10779 * @param {Object} scope (optional) The scope of the function
10780 * @return {Roo.KeyMap} The KeyMap created
10782 addKeyListener : function(key, fn, scope){
10784 if(typeof key != "object" || key instanceof Array){
10800 return new Roo.KeyMap(this, config);
10804 * Creates a KeyMap for this element
10805 * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10806 * @return {Roo.KeyMap} The KeyMap created
10808 addKeyMap : function(config){
10809 return new Roo.KeyMap(this, config);
10813 * Returns true if this element is scrollable.
10814 * @return {Boolean}
10816 isScrollable : function(){
10817 var dom = this.dom;
10818 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10822 * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
10823 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10824 * @param {Number} value The new scroll value
10825 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10826 * @return {Element} this
10829 scrollTo : function(side, value, animate){
10830 var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10831 if(!animate || !A){
10832 this.dom[prop] = value;
10834 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10835 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10841 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10842 * within this element's scrollable range.
10843 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10844 * @param {Number} distance How far to scroll the element in pixels
10845 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10846 * @return {Boolean} Returns true if a scroll was triggered or false if the element
10847 * was scrolled as far as it could go.
10849 scroll : function(direction, distance, animate){
10850 if(!this.isScrollable()){
10854 var l = el.scrollLeft, t = el.scrollTop;
10855 var w = el.scrollWidth, h = el.scrollHeight;
10856 var cw = el.clientWidth, ch = el.clientHeight;
10857 direction = direction.toLowerCase();
10858 var scrolled = false;
10859 var a = this.preanim(arguments, 2);
10864 var v = Math.min(l + distance, w-cw);
10865 this.scrollTo("left", v, a);
10872 var v = Math.max(l - distance, 0);
10873 this.scrollTo("left", v, a);
10881 var v = Math.max(t - distance, 0);
10882 this.scrollTo("top", v, a);
10890 var v = Math.min(t + distance, h-ch);
10891 this.scrollTo("top", v, a);
10900 * Translates the passed page coordinates into left/top css values for this element
10901 * @param {Number/Array} x The page x or an array containing [x, y]
10902 * @param {Number} y The page y
10903 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10905 translatePoints : function(x, y){
10906 if(typeof x == 'object' || x instanceof Array){
10907 y = x[1]; x = x[0];
10909 var p = this.getStyle('position');
10910 var o = this.getXY();
10912 var l = parseInt(this.getStyle('left'), 10);
10913 var t = parseInt(this.getStyle('top'), 10);
10916 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10919 t = (p == "relative") ? 0 : this.dom.offsetTop;
10922 return {left: (x - o[0] + l), top: (y - o[1] + t)};
10926 * Returns the current scroll position of the element.
10927 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10929 getScroll : function(){
10930 var d = this.dom, doc = document;
10931 if(d == doc || d == doc.body){
10932 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10933 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10934 return {left: l, top: t};
10936 return {left: d.scrollLeft, top: d.scrollTop};
10941 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10942 * are convert to standard 6 digit hex color.
10943 * @param {String} attr The css attribute
10944 * @param {String} defaultValue The default value to use when a valid color isn't found
10945 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10948 getColor : function(attr, defaultValue, prefix){
10949 var v = this.getStyle(attr);
10950 if(!v || v == "transparent" || v == "inherit") {
10951 return defaultValue;
10953 var color = typeof prefix == "undefined" ? "#" : prefix;
10954 if(v.substr(0, 4) == "rgb("){
10955 var rvs = v.slice(4, v.length -1).split(",");
10956 for(var i = 0; i < 3; i++){
10957 var h = parseInt(rvs[i]).toString(16);
10964 if(v.substr(0, 1) == "#"){
10965 if(v.length == 4) {
10966 for(var i = 1; i < 4; i++){
10967 var c = v.charAt(i);
10970 }else if(v.length == 7){
10971 color += v.substr(1);
10975 return(color.length > 5 ? color.toLowerCase() : defaultValue);
10979 * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10980 * gradient background, rounded corners and a 4-way shadow.
10981 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10982 * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10983 * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10984 * @return {Roo.Element} this
10986 boxWrap : function(cls){
10987 cls = cls || 'x-box';
10988 var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10989 el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10994 * Returns the value of a namespaced attribute from the element's underlying DOM node.
10995 * @param {String} namespace The namespace in which to look for the attribute
10996 * @param {String} name The attribute name
10997 * @return {String} The attribute value
10999 getAttributeNS : Roo.isIE ? function(ns, name){
11001 var type = typeof d[ns+":"+name];
11002 if(type != 'undefined' && type != 'unknown'){
11003 return d[ns+":"+name];
11006 } : function(ns, name){
11008 return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11013 * Sets or Returns the value the dom attribute value
11014 * @param {String|Object} name The attribute name (or object to set multiple attributes)
11015 * @param {String} value (optional) The value to set the attribute to
11016 * @return {String} The attribute value
11018 attr : function(name){
11019 if (arguments.length > 1) {
11020 this.dom.setAttribute(name, arguments[1]);
11021 return arguments[1];
11023 if (typeof(name) == 'object') {
11024 for(var i in name) {
11025 this.attr(i, name[i]);
11031 if (!this.dom.hasAttribute(name)) {
11034 return this.dom.getAttribute(name);
11041 var ep = El.prototype;
11044 * Appends an event handler (Shorthand for addListener)
11045 * @param {String} eventName The type of event to append
11046 * @param {Function} fn The method the event invokes
11047 * @param {Object} scope (optional) The scope (this object) of the fn
11048 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
11051 ep.on = ep.addListener;
11052 // backwards compat
11053 ep.mon = ep.addListener;
11056 * Removes an event handler from this element (shorthand for removeListener)
11057 * @param {String} eventName the type of event to remove
11058 * @param {Function} fn the method the event invokes
11059 * @return {Roo.Element} this
11062 ep.un = ep.removeListener;
11065 * true to automatically adjust width and height settings for box-model issues (default to true)
11067 ep.autoBoxAdjust = true;
11070 El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11073 El.addUnits = function(v, defaultUnit){
11074 if(v === "" || v == "auto"){
11077 if(v === undefined){
11080 if(typeof v == "number" || !El.unitPattern.test(v)){
11081 return v + (defaultUnit || 'px');
11086 // special markup used throughout Roo when box wrapping elements
11087 El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
11089 * Visibility mode constant - Use visibility to hide element
11095 * Visibility mode constant - Use display to hide element
11101 El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11102 El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11103 El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11115 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11116 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11117 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11118 * @return {Element} The Element object
11121 El.get = function(el){
11123 if(!el){ return null; }
11124 if(typeof el == "string"){ // element id
11125 if(!(elm = document.getElementById(el))){
11128 if(ex = El.cache[el]){
11131 ex = El.cache[el] = new El(elm);
11134 }else if(el.tagName){ // dom element
11138 if(ex = El.cache[id]){
11141 ex = El.cache[id] = new El(el);
11144 }else if(el instanceof El){
11146 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11147 // catch case where it hasn't been appended
11148 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11151 }else if(el.isComposite){
11153 }else if(el instanceof Array){
11154 return El.select(el);
11155 }else if(el == document){
11156 // create a bogus element object representing the document object
11158 var f = function(){};
11159 f.prototype = El.prototype;
11161 docEl.dom = document;
11169 El.uncache = function(el){
11170 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11172 delete El.cache[a[i].id || a[i]];
11178 // Garbage collection - uncache elements/purge listeners on orphaned elements
11179 // so we don't hold a reference and cause the browser to retain them
11180 El.garbageCollect = function(){
11181 if(!Roo.enableGarbageCollector){
11182 clearInterval(El.collectorThread);
11185 for(var eid in El.cache){
11186 var el = El.cache[eid], d = el.dom;
11187 // -------------------------------------------------------
11188 // Determining what is garbage:
11189 // -------------------------------------------------------
11191 // dom node is null, definitely garbage
11192 // -------------------------------------------------------
11194 // no parentNode == direct orphan, definitely garbage
11195 // -------------------------------------------------------
11196 // !d.offsetParent && !document.getElementById(eid)
11197 // display none elements have no offsetParent so we will
11198 // also try to look it up by it's id. However, check
11199 // offsetParent first so we don't do unneeded lookups.
11200 // This enables collection of elements that are not orphans
11201 // directly, but somewhere up the line they have an orphan
11203 // -------------------------------------------------------
11204 if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11205 delete El.cache[eid];
11206 if(d && Roo.enableListenerCollection){
11212 El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11216 El.Flyweight = function(dom){
11219 El.Flyweight.prototype = El.prototype;
11221 El._flyweights = {};
11223 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11224 * the dom node can be overwritten by other code.
11225 * @param {String/HTMLElement} el The dom node or id
11226 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11227 * prevent conflicts (e.g. internally Roo uses "_internal")
11229 * @return {Element} The shared Element object
11231 El.fly = function(el, named){
11232 named = named || '_global';
11233 el = Roo.getDom(el);
11237 if(!El._flyweights[named]){
11238 El._flyweights[named] = new El.Flyweight();
11240 El._flyweights[named].dom = el;
11241 return El._flyweights[named];
11245 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11246 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11247 * Shorthand of {@link Roo.Element#get}
11248 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11249 * @return {Element} The Element object
11255 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11256 * the dom node can be overwritten by other code.
11257 * Shorthand of {@link Roo.Element#fly}
11258 * @param {String/HTMLElement} el The dom node or id
11259 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11260 * prevent conflicts (e.g. internally Roo uses "_internal")
11262 * @return {Element} The shared Element object
11268 // speedy lookup for elements never to box adjust
11269 var noBoxAdjust = Roo.isStrict ? {
11272 input:1, select:1, textarea:1
11274 if(Roo.isIE || Roo.isGecko){
11275 noBoxAdjust['button'] = 1;
11279 Roo.EventManager.on(window, 'unload', function(){
11281 delete El._flyweights;
11289 Roo.Element.selectorFunction = Roo.DomQuery.select;
11292 Roo.Element.select = function(selector, unique, root){
11294 if(typeof selector == "string"){
11295 els = Roo.Element.selectorFunction(selector, root);
11296 }else if(selector.length !== undefined){
11299 throw "Invalid selector";
11301 if(unique === true){
11302 return new Roo.CompositeElement(els);
11304 return new Roo.CompositeElementLite(els);
11308 * Selects elements based on the passed CSS selector to enable working on them as 1.
11309 * @param {String/Array} selector The CSS selector or an array of elements
11310 * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11311 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11312 * @return {CompositeElementLite/CompositeElement}
11316 Roo.select = Roo.Element.select;
11333 * Ext JS Library 1.1.1
11334 * Copyright(c) 2006-2007, Ext JS, LLC.
11336 * Originally Released Under LGPL - original licence link has changed is not relivant.
11339 * <script type="text/javascript">
11344 //Notifies Element that fx methods are available
11345 Roo.enableFx = true;
11349 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
11350 * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11351 * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the
11352 * Element effects to work.</p><br/>
11354 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11355 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11356 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11357 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
11358 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11359 * expected results and should be done with care.</p><br/>
11361 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11362 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
11365 ----- -----------------------------
11366 tl The top left corner
11367 t The center of the top edge
11368 tr The top right corner
11369 l The center of the left edge
11370 r The center of the right edge
11371 bl The bottom left corner
11372 b The center of the bottom edge
11373 br The bottom right corner
11375 * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11376 * below are common options that can be passed to any Fx method.</b>
11377 * @cfg {Function} callback A function called when the effect is finished
11378 * @cfg {Object} scope The scope of the effect function
11379 * @cfg {String} easing A valid Easing value for the effect
11380 * @cfg {String} afterCls A css class to apply after the effect
11381 * @cfg {Number} duration The length of time (in seconds) that the effect should last
11382 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11383 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
11384 * effects that end with the element being visually hidden, ignored otherwise)
11385 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11386 * a function which returns such a specification that will be applied to the Element after the effect finishes
11387 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11388 * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
11389 * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11393 * Slides the element into view. An anchor point can be optionally passed to set the point of
11394 * origin for the slide effect. This function automatically handles wrapping the element with
11395 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11398 // default: slide the element in from the top
11401 // custom: slide the element in from the right with a 2-second duration
11402 el.slideIn('r', { duration: 2 });
11404 // common config options shown with default values
11410 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11411 * @param {Object} options (optional) Object literal with any of the Fx config options
11412 * @return {Roo.Element} The Element
11414 slideIn : function(anchor, o){
11415 var el = this.getFxEl();
11418 el.queueFx(o, function(){
11420 anchor = anchor || "t";
11422 // fix display to visibility
11425 // restore values after effect
11426 var r = this.getFxRestore();
11427 var b = this.getBox();
11428 // fixed size for slide
11432 var wrap = this.fxWrap(r.pos, o, "hidden");
11434 var st = this.dom.style;
11435 st.visibility = "visible";
11436 st.position = "absolute";
11438 // clear out temp styles after slide and unwrap
11439 var after = function(){
11440 el.fxUnwrap(wrap, r.pos, o);
11441 st.width = r.width;
11442 st.height = r.height;
11445 // time to calc the positions
11446 var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11448 switch(anchor.toLowerCase()){
11450 wrap.setSize(b.width, 0);
11451 st.left = st.bottom = "0";
11455 wrap.setSize(0, b.height);
11456 st.right = st.top = "0";
11460 wrap.setSize(0, b.height);
11461 wrap.setX(b.right);
11462 st.left = st.top = "0";
11463 a = {width: bw, points: pt};
11466 wrap.setSize(b.width, 0);
11467 wrap.setY(b.bottom);
11468 st.left = st.top = "0";
11469 a = {height: bh, points: pt};
11472 wrap.setSize(0, 0);
11473 st.right = st.bottom = "0";
11474 a = {width: bw, height: bh};
11477 wrap.setSize(0, 0);
11478 wrap.setY(b.y+b.height);
11479 st.right = st.top = "0";
11480 a = {width: bw, height: bh, points: pt};
11483 wrap.setSize(0, 0);
11484 wrap.setXY([b.right, b.bottom]);
11485 st.left = st.top = "0";
11486 a = {width: bw, height: bh, points: pt};
11489 wrap.setSize(0, 0);
11490 wrap.setX(b.x+b.width);
11491 st.left = st.bottom = "0";
11492 a = {width: bw, height: bh, points: pt};
11495 this.dom.style.visibility = "visible";
11498 arguments.callee.anim = wrap.fxanim(a,
11508 * Slides the element out of view. An anchor point can be optionally passed to set the end point
11509 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
11510 * 'hidden') but block elements will still take up space in the document. The element must be removed
11511 * from the DOM using the 'remove' config option if desired. This function automatically handles
11512 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11515 // default: slide the element out to the top
11518 // custom: slide the element out to the right with a 2-second duration
11519 el.slideOut('r', { duration: 2 });
11521 // common config options shown with default values
11529 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11530 * @param {Object} options (optional) Object literal with any of the Fx config options
11531 * @return {Roo.Element} The Element
11533 slideOut : function(anchor, o){
11534 var el = this.getFxEl();
11537 el.queueFx(o, function(){
11539 anchor = anchor || "t";
11541 // restore values after effect
11542 var r = this.getFxRestore();
11544 var b = this.getBox();
11545 // fixed size for slide
11549 var wrap = this.fxWrap(r.pos, o, "visible");
11551 var st = this.dom.style;
11552 st.visibility = "visible";
11553 st.position = "absolute";
11557 var after = function(){
11559 el.setDisplayed(false);
11564 el.fxUnwrap(wrap, r.pos, o);
11566 st.width = r.width;
11567 st.height = r.height;
11572 var a, zero = {to: 0};
11573 switch(anchor.toLowerCase()){
11575 st.left = st.bottom = "0";
11576 a = {height: zero};
11579 st.right = st.top = "0";
11583 st.left = st.top = "0";
11584 a = {width: zero, points: {to:[b.right, b.y]}};
11587 st.left = st.top = "0";
11588 a = {height: zero, points: {to:[b.x, b.bottom]}};
11591 st.right = st.bottom = "0";
11592 a = {width: zero, height: zero};
11595 st.right = st.top = "0";
11596 a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11599 st.left = st.top = "0";
11600 a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11603 st.left = st.bottom = "0";
11604 a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11608 arguments.callee.anim = wrap.fxanim(a,
11618 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
11619 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
11620 * The element must be removed from the DOM using the 'remove' config option if desired.
11626 // common config options shown with default values
11634 * @param {Object} options (optional) Object literal with any of the Fx config options
11635 * @return {Roo.Element} The Element
11637 puff : function(o){
11638 var el = this.getFxEl();
11641 el.queueFx(o, function(){
11642 this.clearOpacity();
11645 // restore values after effect
11646 var r = this.getFxRestore();
11647 var st = this.dom.style;
11649 var after = function(){
11651 el.setDisplayed(false);
11658 el.setPositioning(r.pos);
11659 st.width = r.width;
11660 st.height = r.height;
11665 var width = this.getWidth();
11666 var height = this.getHeight();
11668 arguments.callee.anim = this.fxanim({
11669 width : {to: this.adjustWidth(width * 2)},
11670 height : {to: this.adjustHeight(height * 2)},
11671 points : {by: [-(width * .5), -(height * .5)]},
11673 fontSize: {to:200, unit: "%"}
11684 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11685 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
11686 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11692 // all config options shown with default values
11700 * @param {Object} options (optional) Object literal with any of the Fx config options
11701 * @return {Roo.Element} The Element
11703 switchOff : function(o){
11704 var el = this.getFxEl();
11707 el.queueFx(o, function(){
11708 this.clearOpacity();
11711 // restore values after effect
11712 var r = this.getFxRestore();
11713 var st = this.dom.style;
11715 var after = function(){
11717 el.setDisplayed(false);
11723 el.setPositioning(r.pos);
11724 st.width = r.width;
11725 st.height = r.height;
11730 this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11731 this.clearOpacity();
11735 points:{by:[0, this.getHeight() * .5]}
11736 }, o, 'motion', 0.3, 'easeIn', after);
11737 }).defer(100, this);
11744 * Highlights the Element by setting a color (applies to the background-color by default, but can be
11745 * changed using the "attr" config option) and then fading back to the original color. If no original
11746 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11749 // default: highlight background to yellow
11752 // custom: highlight foreground text to blue for 2 seconds
11753 el.highlight("0000ff", { attr: 'color', duration: 2 });
11755 // common config options shown with default values
11756 el.highlight("ffff9c", {
11757 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11758 endColor: (current color) or "ffffff",
11763 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11764 * @param {Object} options (optional) Object literal with any of the Fx config options
11765 * @return {Roo.Element} The Element
11767 highlight : function(color, o){
11768 var el = this.getFxEl();
11771 el.queueFx(o, function(){
11772 color = color || "ffff9c";
11773 attr = o.attr || "backgroundColor";
11775 this.clearOpacity();
11778 var origColor = this.getColor(attr);
11779 var restoreColor = this.dom.style[attr];
11780 endColor = (o.endColor || origColor) || "ffffff";
11782 var after = function(){
11783 el.dom.style[attr] = restoreColor;
11788 a[attr] = {from: color, to: endColor};
11789 arguments.callee.anim = this.fxanim(a,
11799 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11802 // default: a single light blue ripple
11805 // custom: 3 red ripples lasting 3 seconds total
11806 el.frame("ff0000", 3, { duration: 3 });
11808 // common config options shown with default values
11809 el.frame("C3DAF9", 1, {
11810 duration: 1 //duration of entire animation (not each individual ripple)
11811 // Note: Easing is not configurable and will be ignored if included
11814 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11815 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11816 * @param {Object} options (optional) Object literal with any of the Fx config options
11817 * @return {Roo.Element} The Element
11819 frame : function(color, count, o){
11820 var el = this.getFxEl();
11823 el.queueFx(o, function(){
11824 color = color || "#C3DAF9";
11825 if(color.length == 6){
11826 color = "#" + color;
11828 count = count || 1;
11829 duration = o.duration || 1;
11832 var b = this.getBox();
11833 var animFn = function(){
11834 var proxy = this.createProxy({
11837 visbility:"hidden",
11838 position:"absolute",
11839 "z-index":"35000", // yee haw
11840 border:"0px solid " + color
11843 var scale = Roo.isBorderBox ? 2 : 1;
11845 top:{from:b.y, to:b.y - 20},
11846 left:{from:b.x, to:b.x - 20},
11847 borderWidth:{from:0, to:10},
11848 opacity:{from:1, to:0},
11849 height:{from:b.height, to:(b.height + (20*scale))},
11850 width:{from:b.width, to:(b.width + (20*scale))}
11851 }, duration, function(){
11855 animFn.defer((duration/2)*1000, this);
11866 * Creates a pause before any subsequent queued effects begin. If there are
11867 * no effects queued after the pause it will have no effect.
11872 * @param {Number} seconds The length of time to pause (in seconds)
11873 * @return {Roo.Element} The Element
11875 pause : function(seconds){
11876 var el = this.getFxEl();
11879 el.queueFx(o, function(){
11880 setTimeout(function(){
11882 }, seconds * 1000);
11888 * Fade an element in (from transparent to opaque). The ending opacity can be specified
11889 * using the "endOpacity" config option.
11892 // default: fade in from opacity 0 to 100%
11895 // custom: fade in from opacity 0 to 75% over 2 seconds
11896 el.fadeIn({ endOpacity: .75, duration: 2});
11898 // common config options shown with default values
11900 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11905 * @param {Object} options (optional) Object literal with any of the Fx config options
11906 * @return {Roo.Element} The Element
11908 fadeIn : function(o){
11909 var el = this.getFxEl();
11911 el.queueFx(o, function(){
11912 this.setOpacity(0);
11914 this.dom.style.visibility = 'visible';
11915 var to = o.endOpacity || 1;
11916 arguments.callee.anim = this.fxanim({opacity:{to:to}},
11917 o, null, .5, "easeOut", function(){
11919 this.clearOpacity();
11928 * Fade an element out (from opaque to transparent). The ending opacity can be specified
11929 * using the "endOpacity" config option.
11932 // default: fade out from the element's current opacity to 0
11935 // custom: fade out from the element's current opacity to 25% over 2 seconds
11936 el.fadeOut({ endOpacity: .25, duration: 2});
11938 // common config options shown with default values
11940 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11947 * @param {Object} options (optional) Object literal with any of the Fx config options
11948 * @return {Roo.Element} The Element
11950 fadeOut : function(o){
11951 var el = this.getFxEl();
11953 el.queueFx(o, function(){
11954 arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11955 o, null, .5, "easeOut", function(){
11956 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11957 this.dom.style.display = "none";
11959 this.dom.style.visibility = "hidden";
11961 this.clearOpacity();
11969 * Animates the transition of an element's dimensions from a starting height/width
11970 * to an ending height/width.
11973 // change height and width to 100x100 pixels
11974 el.scale(100, 100);
11976 // common config options shown with default values. The height and width will default to
11977 // the element's existing values if passed as null.
11980 [element's height], {
11985 * @param {Number} width The new width (pass undefined to keep the original width)
11986 * @param {Number} height The new height (pass undefined to keep the original height)
11987 * @param {Object} options (optional) Object literal with any of the Fx config options
11988 * @return {Roo.Element} The Element
11990 scale : function(w, h, o){
11991 this.shift(Roo.apply({}, o, {
11999 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12000 * Any of these properties not specified in the config object will not be changed. This effect
12001 * requires that at least one new dimension, position or opacity setting must be passed in on
12002 * the config object in order for the function to have any effect.
12005 // slide the element horizontally to x position 200 while changing the height and opacity
12006 el.shift({ x: 200, height: 50, opacity: .8 });
12008 // common config options shown with default values.
12010 width: [element's width],
12011 height: [element's height],
12012 x: [element's x position],
12013 y: [element's y position],
12014 opacity: [element's opacity],
12019 * @param {Object} options Object literal with any of the Fx config options
12020 * @return {Roo.Element} The Element
12022 shift : function(o){
12023 var el = this.getFxEl();
12025 el.queueFx(o, function(){
12026 var a = {}, w = o.width, h = o.height, x = o.x, y = o.y, op = o.opacity;
12027 if(w !== undefined){
12028 a.width = {to: this.adjustWidth(w)};
12030 if(h !== undefined){
12031 a.height = {to: this.adjustHeight(h)};
12033 if(x !== undefined || y !== undefined){
12035 x !== undefined ? x : this.getX(),
12036 y !== undefined ? y : this.getY()
12039 if(op !== undefined){
12040 a.opacity = {to: op};
12042 if(o.xy !== undefined){
12043 a.points = {to: o.xy};
12045 arguments.callee.anim = this.fxanim(a,
12046 o, 'motion', .35, "easeOut", function(){
12054 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
12055 * ending point of the effect.
12058 // default: slide the element downward while fading out
12061 // custom: slide the element out to the right with a 2-second duration
12062 el.ghost('r', { duration: 2 });
12064 // common config options shown with default values
12072 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12073 * @param {Object} options (optional) Object literal with any of the Fx config options
12074 * @return {Roo.Element} The Element
12076 ghost : function(anchor, o){
12077 var el = this.getFxEl();
12080 el.queueFx(o, function(){
12081 anchor = anchor || "b";
12083 // restore values after effect
12084 var r = this.getFxRestore();
12085 var w = this.getWidth(),
12086 h = this.getHeight();
12088 var st = this.dom.style;
12090 var after = function(){
12092 el.setDisplayed(false);
12098 el.setPositioning(r.pos);
12099 st.width = r.width;
12100 st.height = r.height;
12105 var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12106 switch(anchor.toLowerCase()){
12133 arguments.callee.anim = this.fxanim(a,
12143 * Ensures that all effects queued after syncFx is called on the element are
12144 * run concurrently. This is the opposite of {@link #sequenceFx}.
12145 * @return {Roo.Element} The Element
12147 syncFx : function(){
12148 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12157 * Ensures that all effects queued after sequenceFx is called on the element are
12158 * run in sequence. This is the opposite of {@link #syncFx}.
12159 * @return {Roo.Element} The Element
12161 sequenceFx : function(){
12162 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12164 concurrent : false,
12171 nextFx : function(){
12172 var ef = this.fxQueue[0];
12179 * Returns true if the element has any effects actively running or queued, else returns false.
12180 * @return {Boolean} True if element has active effects, else false
12182 hasActiveFx : function(){
12183 return this.fxQueue && this.fxQueue[0];
12187 * Stops any running effects and clears the element's internal effects queue if it contains
12188 * any additional effects that haven't started yet.
12189 * @return {Roo.Element} The Element
12191 stopFx : function(){
12192 if(this.hasActiveFx()){
12193 var cur = this.fxQueue[0];
12194 if(cur && cur.anim && cur.anim.isAnimated()){
12195 this.fxQueue = [cur]; // clear out others
12196 cur.anim.stop(true);
12203 beforeFx : function(o){
12204 if(this.hasActiveFx() && !o.concurrent){
12215 * Returns true if the element is currently blocking so that no other effect can be queued
12216 * until this effect is finished, else returns false if blocking is not set. This is commonly
12217 * used to ensure that an effect initiated by a user action runs to completion prior to the
12218 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12219 * @return {Boolean} True if blocking, else false
12221 hasFxBlock : function(){
12222 var q = this.fxQueue;
12223 return q && q[0] && q[0].block;
12227 queueFx : function(o, fn){
12231 if(!this.hasFxBlock()){
12232 Roo.applyIf(o, this.fxDefaults);
12234 var run = this.beforeFx(o);
12235 fn.block = o.block;
12236 this.fxQueue.push(fn);
12248 fxWrap : function(pos, o, vis){
12250 if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12253 wrapXY = this.getXY();
12255 var div = document.createElement("div");
12256 div.style.visibility = vis;
12257 wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12258 wrap.setPositioning(pos);
12259 if(wrap.getStyle("position") == "static"){
12260 wrap.position("relative");
12262 this.clearPositioning('auto');
12264 wrap.dom.appendChild(this.dom);
12266 wrap.setXY(wrapXY);
12273 fxUnwrap : function(wrap, pos, o){
12274 this.clearPositioning();
12275 this.setPositioning(pos);
12277 wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12283 getFxRestore : function(){
12284 var st = this.dom.style;
12285 return {pos: this.getPositioning(), width: st.width, height : st.height};
12289 afterFx : function(o){
12291 this.applyStyles(o.afterStyle);
12294 this.addClass(o.afterCls);
12296 if(o.remove === true){
12299 Roo.callback(o.callback, o.scope, [this]);
12301 this.fxQueue.shift();
12307 getFxEl : function(){ // support for composite element fx
12308 return Roo.get(this.dom);
12312 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12313 animType = animType || 'run';
12315 var anim = Roo.lib.Anim[animType](
12317 (opt.duration || defaultDur) || .35,
12318 (opt.easing || defaultEase) || 'easeOut',
12320 Roo.callback(cb, this);
12329 // backwords compat
12330 Roo.Fx.resize = Roo.Fx.scale;
12332 //When included, Roo.Fx is automatically applied to Element so that all basic
12333 //effects are available directly via the Element API
12334 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12336 * Ext JS Library 1.1.1
12337 * Copyright(c) 2006-2007, Ext JS, LLC.
12339 * Originally Released Under LGPL - original licence link has changed is not relivant.
12342 * <script type="text/javascript">
12347 * @class Roo.CompositeElement
12348 * Standard composite class. Creates a Roo.Element for every element in the collection.
12350 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12351 * actions will be performed on all the elements in this collection.</b>
12353 * All methods return <i>this</i> and can be chained.
12355 var els = Roo.select("#some-el div.some-class", true);
12356 // or select directly from an existing element
12357 var el = Roo.get('some-el');
12358 el.select('div.some-class', true);
12360 els.setWidth(100); // all elements become 100 width
12361 els.hide(true); // all elements fade out and hide
12363 els.setWidth(100).hide(true);
12366 Roo.CompositeElement = function(els){
12367 this.elements = [];
12368 this.addElements(els);
12370 Roo.CompositeElement.prototype = {
12372 addElements : function(els){
12376 if(typeof els == "string"){
12377 els = Roo.Element.selectorFunction(els);
12379 var yels = this.elements;
12380 var index = yels.length-1;
12381 for(var i = 0, len = els.length; i < len; i++) {
12382 yels[++index] = Roo.get(els[i]);
12388 * Clears this composite and adds the elements returned by the passed selector.
12389 * @param {String/Array} els A string CSS selector, an array of elements or an element
12390 * @return {CompositeElement} this
12392 fill : function(els){
12393 this.elements = [];
12399 * Filters this composite to only elements that match the passed selector.
12400 * @param {String} selector A string CSS selector
12401 * @param {Boolean} inverse return inverse filter (not matches)
12402 * @return {CompositeElement} this
12404 filter : function(selector, inverse){
12406 inverse = inverse || false;
12407 this.each(function(el){
12408 var match = inverse ? !el.is(selector) : el.is(selector);
12410 els[els.length] = el.dom;
12417 invoke : function(fn, args){
12418 var els = this.elements;
12419 for(var i = 0, len = els.length; i < len; i++) {
12420 Roo.Element.prototype[fn].apply(els[i], args);
12425 * Adds elements to this composite.
12426 * @param {String/Array} els A string CSS selector, an array of elements or an element
12427 * @return {CompositeElement} this
12429 add : function(els){
12430 if(typeof els == "string"){
12431 this.addElements(Roo.Element.selectorFunction(els));
12432 }else if(els.length !== undefined){
12433 this.addElements(els);
12435 this.addElements([els]);
12440 * Calls the passed function passing (el, this, index) for each element in this composite.
12441 * @param {Function} fn The function to call
12442 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12443 * @return {CompositeElement} this
12445 each : function(fn, scope){
12446 var els = this.elements;
12447 for(var i = 0, len = els.length; i < len; i++){
12448 if(fn.call(scope || els[i], els[i], this, i) === false) {
12456 * Returns the Element object at the specified index
12457 * @param {Number} index
12458 * @return {Roo.Element}
12460 item : function(index){
12461 return this.elements[index] || null;
12465 * Returns the first Element
12466 * @return {Roo.Element}
12468 first : function(){
12469 return this.item(0);
12473 * Returns the last Element
12474 * @return {Roo.Element}
12477 return this.item(this.elements.length-1);
12481 * Returns the number of elements in this composite
12484 getCount : function(){
12485 return this.elements.length;
12489 * Returns true if this composite contains the passed element
12492 contains : function(el){
12493 return this.indexOf(el) !== -1;
12497 * Returns true if this composite contains the passed element
12500 indexOf : function(el){
12501 return this.elements.indexOf(Roo.get(el));
12506 * Removes the specified element(s).
12507 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12508 * or an array of any of those.
12509 * @param {Boolean} removeDom (optional) True to also remove the element from the document
12510 * @return {CompositeElement} this
12512 removeElement : function(el, removeDom){
12513 if(el instanceof Array){
12514 for(var i = 0, len = el.length; i < len; i++){
12515 this.removeElement(el[i]);
12519 var index = typeof el == 'number' ? el : this.indexOf(el);
12522 var d = this.elements[index];
12526 d.parentNode.removeChild(d);
12529 this.elements.splice(index, 1);
12535 * Replaces the specified element with the passed element.
12536 * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12538 * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12539 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12540 * @return {CompositeElement} this
12542 replaceElement : function(el, replacement, domReplace){
12543 var index = typeof el == 'number' ? el : this.indexOf(el);
12546 this.elements[index].replaceWith(replacement);
12548 this.elements.splice(index, 1, Roo.get(replacement))
12555 * Removes all elements.
12557 clear : function(){
12558 this.elements = [];
12562 Roo.CompositeElement.createCall = function(proto, fnName){
12563 if(!proto[fnName]){
12564 proto[fnName] = function(){
12565 return this.invoke(fnName, arguments);
12569 for(var fnName in Roo.Element.prototype){
12570 if(typeof Roo.Element.prototype[fnName] == "function"){
12571 Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12577 * Ext JS Library 1.1.1
12578 * Copyright(c) 2006-2007, Ext JS, LLC.
12580 * Originally Released Under LGPL - original licence link has changed is not relivant.
12583 * <script type="text/javascript">
12587 * @class Roo.CompositeElementLite
12588 * @extends Roo.CompositeElement
12589 * Flyweight composite class. Reuses the same Roo.Element for element operations.
12591 var els = Roo.select("#some-el div.some-class");
12592 // or select directly from an existing element
12593 var el = Roo.get('some-el');
12594 el.select('div.some-class');
12596 els.setWidth(100); // all elements become 100 width
12597 els.hide(true); // all elements fade out and hide
12599 els.setWidth(100).hide(true);
12600 </code></pre><br><br>
12601 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12602 * actions will be performed on all the elements in this collection.</b>
12604 Roo.CompositeElementLite = function(els){
12605 Roo.CompositeElementLite.superclass.constructor.call(this, els);
12606 this.el = new Roo.Element.Flyweight();
12608 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12609 addElements : function(els){
12611 if(els instanceof Array){
12612 this.elements = this.elements.concat(els);
12614 var yels = this.elements;
12615 var index = yels.length-1;
12616 for(var i = 0, len = els.length; i < len; i++) {
12617 yels[++index] = els[i];
12623 invoke : function(fn, args){
12624 var els = this.elements;
12626 for(var i = 0, len = els.length; i < len; i++) {
12628 Roo.Element.prototype[fn].apply(el, args);
12633 * Returns a flyweight Element of the dom element object at the specified index
12634 * @param {Number} index
12635 * @return {Roo.Element}
12637 item : function(index){
12638 if(!this.elements[index]){
12641 this.el.dom = this.elements[index];
12645 // fixes scope with flyweight
12646 addListener : function(eventName, handler, scope, opt){
12647 var els = this.elements;
12648 for(var i = 0, len = els.length; i < len; i++) {
12649 Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12655 * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12656 * passed is the flyweight (shared) Roo.Element instance, so if you require a
12657 * a reference to the dom node, use el.dom.</b>
12658 * @param {Function} fn The function to call
12659 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12660 * @return {CompositeElement} this
12662 each : function(fn, scope){
12663 var els = this.elements;
12665 for(var i = 0, len = els.length; i < len; i++){
12667 if(fn.call(scope || el, el, this, i) === false){
12674 indexOf : function(el){
12675 return this.elements.indexOf(Roo.getDom(el));
12678 replaceElement : function(el, replacement, domReplace){
12679 var index = typeof el == 'number' ? el : this.indexOf(el);
12681 replacement = Roo.getDom(replacement);
12683 var d = this.elements[index];
12684 d.parentNode.insertBefore(replacement, d);
12685 d.parentNode.removeChild(d);
12687 this.elements.splice(index, 1, replacement);
12692 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12696 * Ext JS Library 1.1.1
12697 * Copyright(c) 2006-2007, Ext JS, LLC.
12699 * Originally Released Under LGPL - original licence link has changed is not relivant.
12702 * <script type="text/javascript">
12708 * @class Roo.data.Connection
12709 * @extends Roo.util.Observable
12710 * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12711 * either to a configured URL, or to a URL specified at request time.
12713 * Requests made by this class are asynchronous, and will return immediately. No data from
12714 * the server will be available to the statement immediately following the {@link #request} call.
12715 * To process returned data, use a callback in the request options object, or an event listener.
12717 * Note: If you are doing a file upload, you will not get a normal response object sent back to
12718 * your callback or event handler. Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12719 * The response object is created using the innerHTML of the IFRAME's document as the responseText
12720 * property and, if present, the IFRAME's XML document as the responseXML property.
12722 * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12723 * that it be placed either inside a <textarea> in an HTML document and retrieved from the responseText
12724 * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12725 * standard DOM methods.
12727 * @param {Object} config a configuration object.
12729 Roo.data.Connection = function(config){
12730 Roo.apply(this, config);
12733 * @event beforerequest
12734 * Fires before a network request is made to retrieve a data object.
12735 * @param {Connection} conn This Connection object.
12736 * @param {Object} options The options config object passed to the {@link #request} method.
12738 "beforerequest" : true,
12740 * @event requestcomplete
12741 * Fires if the request was successfully completed.
12742 * @param {Connection} conn This Connection object.
12743 * @param {Object} response The XHR object containing the response data.
12744 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12745 * @param {Object} options The options config object passed to the {@link #request} method.
12747 "requestcomplete" : true,
12749 * @event requestexception
12750 * Fires if an error HTTP status was returned from the server.
12751 * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12752 * @param {Connection} conn This Connection object.
12753 * @param {Object} response The XHR object containing the response data.
12754 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12755 * @param {Object} options The options config object passed to the {@link #request} method.
12757 "requestexception" : true
12759 Roo.data.Connection.superclass.constructor.call(this);
12762 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12764 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12767 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12768 * extra parameters to each request made by this object. (defaults to undefined)
12771 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12772 * to each request made by this object. (defaults to undefined)
12775 * @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)
12778 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12782 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12788 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12791 disableCaching: true,
12794 * Sends an HTTP request to a remote server.
12795 * @param {Object} options An object which may contain the following properties:<ul>
12796 * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12797 * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12798 * request, a url encoded string or a function to call to get either.</li>
12799 * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12800 * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12801 * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12802 * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12803 * <li>options {Object} The parameter to the request call.</li>
12804 * <li>success {Boolean} True if the request succeeded.</li>
12805 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12807 * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12808 * The callback is passed the following parameters:<ul>
12809 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12810 * <li>options {Object} The parameter to the request call.</li>
12812 * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12813 * The callback is passed the following parameters:<ul>
12814 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12815 * <li>options {Object} The parameter to the request call.</li>
12817 * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12818 * for the callback function. Defaults to the browser window.</li>
12819 * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12820 * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12821 * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12822 * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12823 * params for the post data. Any params will be appended to the URL.</li>
12824 * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12826 * @return {Number} transactionId
12828 request : function(o){
12829 if(this.fireEvent("beforerequest", this, o) !== false){
12832 if(typeof p == "function"){
12833 p = p.call(o.scope||window, o);
12835 if(typeof p == "object"){
12836 p = Roo.urlEncode(o.params);
12838 if(this.extraParams){
12839 var extras = Roo.urlEncode(this.extraParams);
12840 p = p ? (p + '&' + extras) : extras;
12843 var url = o.url || this.url;
12844 if(typeof url == 'function'){
12845 url = url.call(o.scope||window, o);
12849 var form = Roo.getDom(o.form);
12850 url = url || form.action;
12852 var enctype = form.getAttribute("enctype");
12855 return this.doFormDataUpload(o, url);
12858 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12859 return this.doFormUpload(o, p, url);
12861 var f = Roo.lib.Ajax.serializeForm(form);
12862 p = p ? (p + '&' + f) : f;
12865 if (!o.form && o.formData) {
12866 o.formData = o.formData === true ? new FormData() : o.formData;
12867 for (var k in o.params) {
12868 o.formData.append(k,o.params[k]);
12871 return this.doFormDataUpload(o, url);
12875 var hs = o.headers;
12876 if(this.defaultHeaders){
12877 hs = Roo.apply(hs || {}, this.defaultHeaders);
12884 success: this.handleResponse,
12885 failure: this.handleFailure,
12887 argument: {options: o},
12888 timeout : o.timeout || this.timeout
12891 var method = o.method||this.method||(p ? "POST" : "GET");
12893 if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12894 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12897 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12901 }else if(this.autoAbort !== false){
12905 if((method == 'GET' && p) || o.xmlData){
12906 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12909 Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12910 this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12911 Roo.lib.Ajax.useDefaultHeader == true;
12912 return this.transId;
12914 Roo.callback(o.callback, o.scope, [o, null, null]);
12920 * Determine whether this object has a request outstanding.
12921 * @param {Number} transactionId (Optional) defaults to the last transaction
12922 * @return {Boolean} True if there is an outstanding request.
12924 isLoading : function(transId){
12926 return Roo.lib.Ajax.isCallInProgress(transId);
12928 return this.transId ? true : false;
12933 * Aborts any outstanding request.
12934 * @param {Number} transactionId (Optional) defaults to the last transaction
12936 abort : function(transId){
12937 if(transId || this.isLoading()){
12938 Roo.lib.Ajax.abort(transId || this.transId);
12943 handleResponse : function(response){
12944 this.transId = false;
12945 var options = response.argument.options;
12946 response.argument = options ? options.argument : null;
12947 this.fireEvent("requestcomplete", this, response, options);
12948 Roo.callback(options.success, options.scope, [response, options]);
12949 Roo.callback(options.callback, options.scope, [options, true, response]);
12953 handleFailure : function(response, e){
12954 this.transId = false;
12955 var options = response.argument.options;
12956 response.argument = options ? options.argument : null;
12957 this.fireEvent("requestexception", this, response, options, e);
12958 Roo.callback(options.failure, options.scope, [response, options]);
12959 Roo.callback(options.callback, options.scope, [options, false, response]);
12963 doFormUpload : function(o, ps, url){
12965 var frame = document.createElement('iframe');
12968 frame.className = 'x-hidden';
12970 frame.src = Roo.SSL_SECURE_URL;
12972 document.body.appendChild(frame);
12975 document.frames[id].name = id;
12978 var form = Roo.getDom(o.form);
12980 form.method = 'POST';
12981 form.enctype = form.encoding = 'multipart/form-data';
12987 if(ps){ // add dynamic params
12989 ps = Roo.urlDecode(ps, false);
12991 if(ps.hasOwnProperty(k)){
12992 hd = document.createElement('input');
12993 hd.type = 'hidden';
12996 form.appendChild(hd);
13003 var r = { // bogus response object
13008 r.argument = o ? o.argument : null;
13013 doc = frame.contentWindow.document;
13015 doc = (frame.contentDocument || window.frames[id].document);
13017 if(doc && doc.body){
13018 r.responseText = doc.body.innerHTML;
13020 if(doc && doc.XMLDocument){
13021 r.responseXML = doc.XMLDocument;
13023 r.responseXML = doc;
13030 Roo.EventManager.removeListener(frame, 'load', cb, this);
13032 this.fireEvent("requestcomplete", this, r, o);
13033 Roo.callback(o.success, o.scope, [r, o]);
13034 Roo.callback(o.callback, o.scope, [o, true, r]);
13036 setTimeout(function(){document.body.removeChild(frame);}, 100);
13039 Roo.EventManager.on(frame, 'load', cb, this);
13042 if(hiddens){ // remove dynamic params
13043 for(var i = 0, len = hiddens.length; i < len; i++){
13044 form.removeChild(hiddens[i]);
13048 // this is a 'formdata version???'
13051 doFormDataUpload : function(o, url)
13055 var form = Roo.getDom(o.form);
13056 form.enctype = form.encoding = 'multipart/form-data';
13057 formData = o.formData === true ? new FormData(form) : o.formData;
13059 formData = o.formData === true ? new FormData() : o.formData;
13064 success: this.handleResponse,
13065 failure: this.handleFailure,
13067 argument: {options: o},
13068 timeout : o.timeout || this.timeout
13071 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13075 }else if(this.autoAbort !== false){
13079 //Roo.lib.Ajax.defaultPostHeader = null;
13080 Roo.lib.Ajax.useDefaultHeader = false;
13081 this.transId = Roo.lib.Ajax.request( "POST", url, cb, formData, o);
13082 Roo.lib.Ajax.useDefaultHeader = true;
13090 * Ext JS Library 1.1.1
13091 * Copyright(c) 2006-2007, Ext JS, LLC.
13093 * Originally Released Under LGPL - original licence link has changed is not relivant.
13096 * <script type="text/javascript">
13100 * Global Ajax request class.
13103 * @extends Roo.data.Connection
13106 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
13107 * @cfg {Object} extraParams An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13108 * @cfg {Object} defaultHeaders An object containing request headers which are added to each request made by this object. (defaults to undefined)
13109 * @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)
13110 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13111 * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13112 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13114 Roo.Ajax = new Roo.data.Connection({
13123 * Serialize the passed form into a url encoded string
13125 * @param {String/HTMLElement} form
13128 serializeForm : function(form){
13129 return Roo.lib.Ajax.serializeForm(form);
13133 * Ext JS Library 1.1.1
13134 * Copyright(c) 2006-2007, Ext JS, LLC.
13136 * Originally Released Under LGPL - original licence link has changed is not relivant.
13139 * <script type="text/javascript">
13144 * @class Roo.UpdateManager
13145 * @extends Roo.util.Observable
13146 * Provides AJAX-style update for Element object.<br><br>
13149 * // Get it from a Roo.Element object
13150 * var el = Roo.get("foo");
13151 * var mgr = el.getUpdateManager();
13152 * mgr.update("http://myserver.com/index.php", "param1=1&param2=2");
13154 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13156 * // or directly (returns the same UpdateManager instance)
13157 * var mgr = new Roo.UpdateManager("myElementId");
13158 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13159 * mgr.on("update", myFcnNeedsToKnow);
13161 // short handed call directly from the element object
13162 Roo.get("foo").load({
13166 text: "Loading Foo..."
13170 * Create new UpdateManager directly.
13171 * @param {String/HTMLElement/Roo.Element} el The element to update
13172 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
13174 Roo.UpdateManager = function(el, forceNew){
13176 if(!forceNew && el.updateManager){
13177 return el.updateManager;
13180 * The Element object
13181 * @type Roo.Element
13185 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13188 this.defaultUrl = null;
13192 * @event beforeupdate
13193 * Fired before an update is made, return false from your handler and the update is cancelled.
13194 * @param {Roo.Element} el
13195 * @param {String/Object/Function} url
13196 * @param {String/Object} params
13198 "beforeupdate": true,
13201 * Fired after successful update is made.
13202 * @param {Roo.Element} el
13203 * @param {Object} oResponseObject The response Object
13208 * Fired on update failure.
13209 * @param {Roo.Element} el
13210 * @param {Object} oResponseObject The response Object
13214 var d = Roo.UpdateManager.defaults;
13216 * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13219 this.sslBlankUrl = d.sslBlankUrl;
13221 * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13224 this.disableCaching = d.disableCaching;
13226 * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '<div class="loading-indicator">Loading...</div>').
13229 this.indicatorText = d.indicatorText;
13231 * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13234 this.showLoadIndicator = d.showLoadIndicator;
13236 * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13239 this.timeout = d.timeout;
13242 * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13245 this.loadScripts = d.loadScripts;
13248 * Transaction object of current executing transaction
13250 this.transaction = null;
13255 this.autoRefreshProcId = null;
13257 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13260 this.refreshDelegate = this.refresh.createDelegate(this);
13262 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13265 this.updateDelegate = this.update.createDelegate(this);
13267 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13270 this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13274 this.successDelegate = this.processSuccess.createDelegate(this);
13278 this.failureDelegate = this.processFailure.createDelegate(this);
13280 if(!this.renderer){
13282 * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13284 this.renderer = new Roo.UpdateManager.BasicRenderer();
13287 Roo.UpdateManager.superclass.constructor.call(this);
13290 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13292 * Get the Element this UpdateManager is bound to
13293 * @return {Roo.Element} The element
13295 getEl : function(){
13299 * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13300 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
13303 url: "your-url.php",<br/>
13304 params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13305 callback: yourFunction,<br/>
13306 scope: yourObject, //(optional scope) <br/>
13307 discardUrl: false, <br/>
13308 nocache: false,<br/>
13309 text: "Loading...",<br/>
13311 scripts: false<br/>
13314 * The only required property is url. The optional properties nocache, text and scripts
13315 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13316 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
13317 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13318 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
13320 update : function(url, params, callback, discardUrl){
13321 if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13322 var method = this.method,
13324 if(typeof url == "object"){ // must be config object
13327 params = params || cfg.params;
13328 callback = callback || cfg.callback;
13329 discardUrl = discardUrl || cfg.discardUrl;
13330 if(callback && cfg.scope){
13331 callback = callback.createDelegate(cfg.scope);
13333 if(typeof cfg.method != "undefined"){method = cfg.method;};
13334 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13335 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13336 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13337 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13339 this.showLoading();
13341 this.defaultUrl = url;
13343 if(typeof url == "function"){
13344 url = url.call(this);
13347 method = method || (params ? "POST" : "GET");
13348 if(method == "GET"){
13349 url = this.prepareUrl(url);
13352 var o = Roo.apply(cfg ||{}, {
13355 success: this.successDelegate,
13356 failure: this.failureDelegate,
13357 callback: undefined,
13358 timeout: (this.timeout*1000),
13359 argument: {"url": url, "form": null, "callback": callback, "params": params}
13361 Roo.log("updated manager called with timeout of " + o.timeout);
13362 this.transaction = Roo.Ajax.request(o);
13367 * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
13368 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13369 * @param {String/HTMLElement} form The form Id or form element
13370 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13371 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13372 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13374 formUpdate : function(form, url, reset, callback){
13375 if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13376 if(typeof url == "function"){
13377 url = url.call(this);
13379 form = Roo.getDom(form);
13380 this.transaction = Roo.Ajax.request({
13383 success: this.successDelegate,
13384 failure: this.failureDelegate,
13385 timeout: (this.timeout*1000),
13386 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13388 this.showLoading.defer(1, this);
13393 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13394 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13396 refresh : function(callback){
13397 if(this.defaultUrl == null){
13400 this.update(this.defaultUrl, null, callback, true);
13404 * Set this element to auto refresh.
13405 * @param {Number} interval How often to update (in seconds).
13406 * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
13407 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
13408 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13409 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13411 startAutoRefresh : function(interval, url, params, callback, refreshNow){
13413 this.update(url || this.defaultUrl, params, callback, true);
13415 if(this.autoRefreshProcId){
13416 clearInterval(this.autoRefreshProcId);
13418 this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13422 * Stop auto refresh on this element.
13424 stopAutoRefresh : function(){
13425 if(this.autoRefreshProcId){
13426 clearInterval(this.autoRefreshProcId);
13427 delete this.autoRefreshProcId;
13431 isAutoRefreshing : function(){
13432 return this.autoRefreshProcId ? true : false;
13435 * Called to update the element to "Loading" state. Override to perform custom action.
13437 showLoading : function(){
13438 if(this.showLoadIndicator){
13439 this.el.update(this.indicatorText);
13444 * Adds unique parameter to query string if disableCaching = true
13447 prepareUrl : function(url){
13448 if(this.disableCaching){
13449 var append = "_dc=" + (new Date().getTime());
13450 if(url.indexOf("?") !== -1){
13451 url += "&" + append;
13453 url += "?" + append;
13462 processSuccess : function(response){
13463 this.transaction = null;
13464 if(response.argument.form && response.argument.reset){
13465 try{ // put in try/catch since some older FF releases had problems with this
13466 response.argument.form.reset();
13469 if(this.loadScripts){
13470 this.renderer.render(this.el, response, this,
13471 this.updateComplete.createDelegate(this, [response]));
13473 this.renderer.render(this.el, response, this);
13474 this.updateComplete(response);
13478 updateComplete : function(response){
13479 this.fireEvent("update", this.el, response);
13480 if(typeof response.argument.callback == "function"){
13481 response.argument.callback(this.el, true, response);
13488 processFailure : function(response){
13489 this.transaction = null;
13490 this.fireEvent("failure", this.el, response);
13491 if(typeof response.argument.callback == "function"){
13492 response.argument.callback(this.el, false, response);
13497 * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13498 * @param {Object} renderer The object implementing the render() method
13500 setRenderer : function(renderer){
13501 this.renderer = renderer;
13504 getRenderer : function(){
13505 return this.renderer;
13509 * Set the defaultUrl used for updates
13510 * @param {String/Function} defaultUrl The url or a function to call to get the url
13512 setDefaultUrl : function(defaultUrl){
13513 this.defaultUrl = defaultUrl;
13517 * Aborts the executing transaction
13519 abort : function(){
13520 if(this.transaction){
13521 Roo.Ajax.abort(this.transaction);
13526 * Returns true if an update is in progress
13527 * @return {Boolean}
13529 isUpdating : function(){
13530 if(this.transaction){
13531 return Roo.Ajax.isLoading(this.transaction);
13538 * @class Roo.UpdateManager.defaults
13539 * @static (not really - but it helps the doc tool)
13540 * The defaults collection enables customizing the default properties of UpdateManager
13542 Roo.UpdateManager.defaults = {
13544 * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13550 * True to process scripts by default (Defaults to false).
13553 loadScripts : false,
13556 * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13559 sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13561 * Whether to append unique parameter on get request to disable caching (Defaults to false).
13564 disableCaching : false,
13566 * Whether to show indicatorText when loading (Defaults to true).
13569 showLoadIndicator : true,
13571 * Text for loading indicator (Defaults to '<div class="loading-indicator">Loading...</div>').
13574 indicatorText : '<div class="loading-indicator">Loading...</div>'
13578 * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13580 * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13581 * @param {String/HTMLElement/Roo.Element} el The element to update
13582 * @param {String} url The url
13583 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13584 * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13587 * @member Roo.UpdateManager
13589 Roo.UpdateManager.updateElement = function(el, url, params, options){
13590 var um = Roo.get(el, true).getUpdateManager();
13591 Roo.apply(um, options);
13592 um.update(url, params, options ? options.callback : null);
13594 // alias for backwards compat
13595 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13597 * @class Roo.UpdateManager.BasicRenderer
13598 * Default Content renderer. Updates the elements innerHTML with the responseText.
13600 Roo.UpdateManager.BasicRenderer = function(){};
13602 Roo.UpdateManager.BasicRenderer.prototype = {
13604 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13605 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13606 * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13607 * @param {Roo.Element} el The element being rendered
13608 * @param {Object} response The YUI Connect response object
13609 * @param {UpdateManager} updateManager The calling update manager
13610 * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13612 render : function(el, response, updateManager, callback){
13613 el.update(response.responseText, updateManager.loadScripts, callback);
13619 * (c)) Alan Knowles
13625 * @class Roo.DomTemplate
13626 * @extends Roo.Template
13627 * An effort at a dom based template engine..
13629 * Similar to XTemplate, except it uses dom parsing to create the template..
13631 * Supported features:
13636 {a_variable} - output encoded.
13637 {a_variable.format:("Y-m-d")} - call a method on the variable
13638 {a_variable:raw} - unencoded output
13639 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13640 {a_variable:this.method_on_template(...)} - call a method on the template object.
13645 <div roo-for="a_variable or condition.."></div>
13646 <div roo-if="a_variable or condition"></div>
13647 <div roo-exec="some javascript"></div>
13648 <div roo-name="named_template"></div>
13653 Roo.DomTemplate = function()
13655 Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13662 Roo.extend(Roo.DomTemplate, Roo.Template, {
13664 * id counter for sub templates.
13668 * flag to indicate if dom parser is inside a pre,
13669 * it will strip whitespace if not.
13674 * The various sub templates
13682 * basic tag replacing syntax
13685 * // you can fake an object call by doing this
13689 re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13690 //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13692 iterChild : function (node, method) {
13694 var oldPre = this.inPre;
13695 if (node.tagName == 'PRE') {
13698 for( var i = 0; i < node.childNodes.length; i++) {
13699 method.call(this, node.childNodes[i]);
13701 this.inPre = oldPre;
13707 * compile the template
13709 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13712 compile: function()
13716 // covert the html into DOM...
13720 doc = document.implementation.createHTMLDocument("");
13721 doc.documentElement.innerHTML = this.html ;
13722 div = doc.documentElement;
13724 // old IE... - nasty -- it causes all sorts of issues.. with
13725 // images getting pulled from server..
13726 div = document.createElement('div');
13727 div.innerHTML = this.html;
13729 //doc.documentElement.innerHTML = htmlBody
13735 this.iterChild(div, function(n) {_t.compileNode(n, true); });
13737 var tpls = this.tpls;
13739 // create a top level template from the snippet..
13741 //Roo.log(div.innerHTML);
13748 body : div.innerHTML,
13761 Roo.each(tpls, function(tp){
13762 this.compileTpl(tp);
13763 this.tpls[tp.id] = tp;
13766 this.master = tpls[0];
13772 compileNode : function(node, istop) {
13777 // skip anything not a tag..
13778 if (node.nodeType != 1) {
13779 if (node.nodeType == 3 && !this.inPre) {
13780 // reduce white space..
13781 node.nodeValue = node.nodeValue.replace(/\s+/g, ' ');
13804 case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13805 case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13806 case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13807 case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13813 // just itterate children..
13814 this.iterChild(node,this.compileNode);
13817 tpl.uid = this.id++;
13818 tpl.value = node.getAttribute('roo-' + tpl.attr);
13819 node.removeAttribute('roo-'+ tpl.attr);
13820 if (tpl.attr != 'name') {
13821 var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13822 node.parentNode.replaceChild(placeholder, node);
13825 var placeholder = document.createElement('span');
13826 placeholder.className = 'roo-tpl-' + tpl.value;
13827 node.parentNode.replaceChild(placeholder, node);
13830 // parent now sees '{domtplXXXX}
13831 this.iterChild(node,this.compileNode);
13833 // we should now have node body...
13834 var div = document.createElement('div');
13835 div.appendChild(node);
13837 // this has the unfortunate side effect of converting tagged attributes
13838 // eg. href="{...}" into %7C...%7D
13839 // this has been fixed by searching for those combo's although it's a bit hacky..
13842 tpl.body = div.innerHTML;
13849 switch (tpl.value) {
13850 case '.': tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13851 case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13852 default: tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13857 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13861 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13865 tpl.id = tpl.value; // replace non characters???
13871 this.tpls.push(tpl);
13881 * Compile a segment of the template into a 'sub-template'
13887 compileTpl : function(tpl)
13889 var fm = Roo.util.Format;
13890 var useF = this.disableFormats !== true;
13892 var sep = Roo.isGecko ? "+\n" : ",\n";
13894 var undef = function(str) {
13895 Roo.debug && Roo.log("Property not found :" + str);
13899 //Roo.log(tpl.body);
13903 var fn = function(m, lbrace, name, format, args)
13906 //Roo.log(arguments);
13907 args = args ? args.replace(/\\'/g,"'") : args;
13908 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13909 if (typeof(format) == 'undefined') {
13910 format = 'htmlEncode';
13912 if (format == 'raw' ) {
13916 if(name.substr(0, 6) == 'domtpl'){
13917 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13920 // build an array of options to determine if value is undefined..
13922 // basically get 'xxxx.yyyy' then do
13923 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13924 // (function () { Roo.log("Property not found"); return ''; })() :
13929 Roo.each(name.split('.'), function(st) {
13930 lookfor += (lookfor.length ? '.': '') + st;
13931 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
13934 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13937 if(format && useF){
13939 args = args ? ',' + args : "";
13941 if(format.substr(0, 5) != "this."){
13942 format = "fm." + format + '(';
13944 format = 'this.call("'+ format.substr(5) + '", ';
13948 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
13951 if (args && args.length) {
13952 // called with xxyx.yuu:(test,test)
13954 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
13956 // raw.. - :raw modifier..
13957 return "'"+ sep + udef_st + name + ")"+sep+"'";
13961 // branched to use + in gecko and [].join() in others
13963 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
13964 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13967 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
13968 body.push(tpl.body.replace(/(\r\n|\n)/g,
13969 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13970 body.push("'].join('');};};");
13971 body = body.join('');
13974 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13976 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
13983 * same as applyTemplate, except it's done to one of the subTemplates
13984 * when using named templates, you can do:
13986 * var str = pl.applySubTemplate('your-name', values);
13989 * @param {Number} id of the template
13990 * @param {Object} values to apply to template
13991 * @param {Object} parent (normaly the instance of this object)
13993 applySubTemplate : function(id, values, parent)
13997 var t = this.tpls[id];
14001 if(t.ifCall && !t.ifCall.call(this, values, parent)){
14002 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14006 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14013 if(t.execCall && t.execCall.call(this, values, parent)){
14017 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14023 var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14024 parent = t.target ? values : parent;
14025 if(t.forCall && vs instanceof Array){
14027 for(var i = 0, len = vs.length; i < len; i++){
14029 buf[buf.length] = t.compiled.call(this, vs[i], parent);
14031 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14033 //Roo.log(t.compiled);
14037 return buf.join('');
14040 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14045 return t.compiled.call(this, vs, parent);
14047 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14049 //Roo.log(t.compiled);
14057 applyTemplate : function(values){
14058 return this.master.compiled.call(this, values, {});
14059 //var s = this.subs;
14062 apply : function(){
14063 return this.applyTemplate.apply(this, arguments);
14068 Roo.DomTemplate.from = function(el){
14069 el = Roo.getDom(el);
14070 return new Roo.Domtemplate(el.value || el.innerHTML);
14073 * Ext JS Library 1.1.1
14074 * Copyright(c) 2006-2007, Ext JS, LLC.
14076 * Originally Released Under LGPL - original licence link has changed is not relivant.
14079 * <script type="text/javascript">
14083 * @class Roo.util.DelayedTask
14084 * Provides a convenient method of performing setTimeout where a new
14085 * timeout cancels the old timeout. An example would be performing validation on a keypress.
14086 * You can use this class to buffer
14087 * the keypress events for a certain number of milliseconds, and perform only if they stop
14088 * for that amount of time.
14089 * @constructor The parameters to this constructor serve as defaults and are not required.
14090 * @param {Function} fn (optional) The default function to timeout
14091 * @param {Object} scope (optional) The default scope of that timeout
14092 * @param {Array} args (optional) The default Array of arguments
14094 Roo.util.DelayedTask = function(fn, scope, args){
14095 var id = null, d, t;
14097 var call = function(){
14098 var now = new Date().getTime();
14102 fn.apply(scope, args || []);
14106 * Cancels any pending timeout and queues a new one
14107 * @param {Number} delay The milliseconds to delay
14108 * @param {Function} newFn (optional) Overrides function passed to constructor
14109 * @param {Object} newScope (optional) Overrides scope passed to constructor
14110 * @param {Array} newArgs (optional) Overrides args passed to constructor
14112 this.delay = function(delay, newFn, newScope, newArgs){
14113 if(id && delay != d){
14117 t = new Date().getTime();
14119 scope = newScope || scope;
14120 args = newArgs || args;
14122 id = setInterval(call, d);
14127 * Cancel the last queued timeout
14129 this.cancel = function(){
14137 * Ext JS Library 1.1.1
14138 * Copyright(c) 2006-2007, Ext JS, LLC.
14140 * Originally Released Under LGPL - original licence link has changed is not relivant.
14143 * <script type="text/javascript">
14146 * @class Roo.util.TaskRunner
14147 * Manage background tasks - not sure why this is better that setInterval?
14152 Roo.util.TaskRunner = function(interval){
14153 interval = interval || 10;
14154 var tasks = [], removeQueue = [];
14156 var running = false;
14158 var stopThread = function(){
14164 var startThread = function(){
14167 id = setInterval(runTasks, interval);
14171 var removeTask = function(task){
14172 removeQueue.push(task);
14178 var runTasks = function(){
14179 if(removeQueue.length > 0){
14180 for(var i = 0, len = removeQueue.length; i < len; i++){
14181 tasks.remove(removeQueue[i]);
14184 if(tasks.length < 1){
14189 var now = new Date().getTime();
14190 for(var i = 0, len = tasks.length; i < len; ++i){
14192 var itime = now - t.taskRunTime;
14193 if(t.interval <= itime){
14194 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14195 t.taskRunTime = now;
14196 if(rt === false || t.taskRunCount === t.repeat){
14201 if(t.duration && t.duration <= (now - t.taskStartTime)){
14208 * Queues a new task.
14209 * @param {Object} task
14211 * Task property : interval = how frequent to run.
14212 * Task object should implement
14214 * Task object may implement
14215 * function onStop()
14217 this.start = function(task){
14219 task.taskStartTime = new Date().getTime();
14220 task.taskRunTime = 0;
14221 task.taskRunCount = 0;
14227 * @param {Object} task
14229 this.stop = function(task){
14236 this.stopAll = function(){
14238 for(var i = 0, len = tasks.length; i < len; i++){
14239 if(tasks[i].onStop){
14248 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14250 * Ext JS Library 1.1.1
14251 * Copyright(c) 2006-2007, Ext JS, LLC.
14253 * Originally Released Under LGPL - original licence link has changed is not relivant.
14256 * <script type="text/javascript">
14261 * @class Roo.util.MixedCollection
14262 * @extends Roo.util.Observable
14263 * A Collection class that maintains both numeric indexes and keys and exposes events.
14265 * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14266 * collection (defaults to false)
14267 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14268 * and return the key value for that item. This is used when available to look up the key on items that
14269 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
14270 * equivalent to providing an implementation for the {@link #getKey} method.
14272 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14280 * Fires when the collection is cleared.
14285 * Fires when an item is added to the collection.
14286 * @param {Number} index The index at which the item was added.
14287 * @param {Object} o The item added.
14288 * @param {String} key The key associated with the added item.
14293 * Fires when an item is replaced in the collection.
14294 * @param {String} key he key associated with the new added.
14295 * @param {Object} old The item being replaced.
14296 * @param {Object} new The new item.
14301 * Fires when an item is removed from the collection.
14302 * @param {Object} o The item being removed.
14303 * @param {String} key (optional) The key associated with the removed item.
14308 this.allowFunctions = allowFunctions === true;
14310 this.getKey = keyFn;
14312 Roo.util.MixedCollection.superclass.constructor.call(this);
14315 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14316 allowFunctions : false,
14319 * Adds an item to the collection.
14320 * @param {String} key The key to associate with the item
14321 * @param {Object} o The item to add.
14322 * @return {Object} The item added.
14324 add : function(key, o){
14325 if(arguments.length == 1){
14327 key = this.getKey(o);
14329 if(typeof key == "undefined" || key === null){
14331 this.items.push(o);
14332 this.keys.push(null);
14334 var old = this.map[key];
14336 return this.replace(key, o);
14339 this.items.push(o);
14341 this.keys.push(key);
14343 this.fireEvent("add", this.length-1, o, key);
14348 * MixedCollection has a generic way to fetch keys if you implement getKey.
14351 var mc = new Roo.util.MixedCollection();
14352 mc.add(someEl.dom.id, someEl);
14353 mc.add(otherEl.dom.id, otherEl);
14357 var mc = new Roo.util.MixedCollection();
14358 mc.getKey = function(el){
14364 // or via the constructor
14365 var mc = new Roo.util.MixedCollection(false, function(el){
14371 * @param o {Object} The item for which to find the key.
14372 * @return {Object} The key for the passed item.
14374 getKey : function(o){
14379 * Replaces an item in the collection.
14380 * @param {String} key The key associated with the item to replace, or the item to replace.
14381 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14382 * @return {Object} The new item.
14384 replace : function(key, o){
14385 if(arguments.length == 1){
14387 key = this.getKey(o);
14389 var old = this.item(key);
14390 if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14391 return this.add(key, o);
14393 var index = this.indexOfKey(key);
14394 this.items[index] = o;
14396 this.fireEvent("replace", key, old, o);
14401 * Adds all elements of an Array or an Object to the collection.
14402 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14403 * an Array of values, each of which are added to the collection.
14405 addAll : function(objs){
14406 if(arguments.length > 1 || objs instanceof Array){
14407 var args = arguments.length > 1 ? arguments : objs;
14408 for(var i = 0, len = args.length; i < len; i++){
14412 for(var key in objs){
14413 if(this.allowFunctions || typeof objs[key] != "function"){
14414 this.add(key, objs[key]);
14421 * Executes the specified function once for every item in the collection, passing each
14422 * item as the first and only parameter. returning false from the function will stop the iteration.
14423 * @param {Function} fn The function to execute for each item.
14424 * @param {Object} scope (optional) The scope in which to execute the function.
14426 each : function(fn, scope){
14427 var items = [].concat(this.items); // each safe for removal
14428 for(var i = 0, len = items.length; i < len; i++){
14429 if(fn.call(scope || items[i], items[i], i, len) === false){
14436 * Executes the specified function once for every key in the collection, passing each
14437 * key, and its associated item as the first two parameters.
14438 * @param {Function} fn The function to execute for each item.
14439 * @param {Object} scope (optional) The scope in which to execute the function.
14441 eachKey : function(fn, scope){
14442 for(var i = 0, len = this.keys.length; i < len; i++){
14443 fn.call(scope || window, this.keys[i], this.items[i], i, len);
14448 * Returns the first item in the collection which elicits a true return value from the
14449 * passed selection function.
14450 * @param {Function} fn The selection function to execute for each item.
14451 * @param {Object} scope (optional) The scope in which to execute the function.
14452 * @return {Object} The first item in the collection which returned true from the selection function.
14454 find : function(fn, scope){
14455 for(var i = 0, len = this.items.length; i < len; i++){
14456 if(fn.call(scope || window, this.items[i], this.keys[i])){
14457 return this.items[i];
14464 * Inserts an item at the specified index in the collection.
14465 * @param {Number} index The index to insert the item at.
14466 * @param {String} key The key to associate with the new item, or the item itself.
14467 * @param {Object} o (optional) If the second parameter was a key, the new item.
14468 * @return {Object} The item inserted.
14470 insert : function(index, key, o){
14471 if(arguments.length == 2){
14473 key = this.getKey(o);
14475 if(index >= this.length){
14476 return this.add(key, o);
14479 this.items.splice(index, 0, o);
14480 if(typeof key != "undefined" && key != null){
14483 this.keys.splice(index, 0, key);
14484 this.fireEvent("add", index, o, key);
14489 * Removed an item from the collection.
14490 * @param {Object} o The item to remove.
14491 * @return {Object} The item removed.
14493 remove : function(o){
14494 return this.removeAt(this.indexOf(o));
14498 * Remove an item from a specified index in the collection.
14499 * @param {Number} index The index within the collection of the item to remove.
14501 removeAt : function(index){
14502 if(index < this.length && index >= 0){
14504 var o = this.items[index];
14505 this.items.splice(index, 1);
14506 var key = this.keys[index];
14507 if(typeof key != "undefined"){
14508 delete this.map[key];
14510 this.keys.splice(index, 1);
14511 this.fireEvent("remove", o, key);
14516 * Removed an item associated with the passed key fom the collection.
14517 * @param {String} key The key of the item to remove.
14519 removeKey : function(key){
14520 return this.removeAt(this.indexOfKey(key));
14524 * Returns the number of items in the collection.
14525 * @return {Number} the number of items in the collection.
14527 getCount : function(){
14528 return this.length;
14532 * Returns index within the collection of the passed Object.
14533 * @param {Object} o The item to find the index of.
14534 * @return {Number} index of the item.
14536 indexOf : function(o){
14537 if(!this.items.indexOf){
14538 for(var i = 0, len = this.items.length; i < len; i++){
14539 if(this.items[i] == o) {
14545 return this.items.indexOf(o);
14550 * Returns index within the collection of the passed key.
14551 * @param {String} key The key to find the index of.
14552 * @return {Number} index of the key.
14554 indexOfKey : function(key){
14555 if(!this.keys.indexOf){
14556 for(var i = 0, len = this.keys.length; i < len; i++){
14557 if(this.keys[i] == key) {
14563 return this.keys.indexOf(key);
14568 * Returns the item associated with the passed key OR index. Key has priority over index.
14569 * @param {String/Number} key The key or index of the item.
14570 * @return {Object} The item associated with the passed key.
14572 item : function(key){
14573 if (key === 'length') {
14576 var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14577 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14581 * Returns the item at the specified index.
14582 * @param {Number} index The index of the item.
14585 itemAt : function(index){
14586 return this.items[index];
14590 * Returns the item associated with the passed key.
14591 * @param {String/Number} key The key of the item.
14592 * @return {Object} The item associated with the passed key.
14594 key : function(key){
14595 return this.map[key];
14599 * Returns true if the collection contains the passed Object as an item.
14600 * @param {Object} o The Object to look for in the collection.
14601 * @return {Boolean} True if the collection contains the Object as an item.
14603 contains : function(o){
14604 return this.indexOf(o) != -1;
14608 * Returns true if the collection contains the passed Object as a key.
14609 * @param {String} key The key to look for in the collection.
14610 * @return {Boolean} True if the collection contains the Object as a key.
14612 containsKey : function(key){
14613 return typeof this.map[key] != "undefined";
14617 * Removes all items from the collection.
14619 clear : function(){
14624 this.fireEvent("clear");
14628 * Returns the first item in the collection.
14629 * @return {Object} the first item in the collection..
14631 first : function(){
14632 return this.items[0];
14636 * Returns the last item in the collection.
14637 * @return {Object} the last item in the collection..
14640 return this.items[this.length-1];
14643 _sort : function(property, dir, fn){
14644 var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14645 fn = fn || function(a, b){
14648 var c = [], k = this.keys, items = this.items;
14649 for(var i = 0, len = items.length; i < len; i++){
14650 c[c.length] = {key: k[i], value: items[i], index: i};
14652 c.sort(function(a, b){
14653 var v = fn(a[property], b[property]) * dsc;
14655 v = (a.index < b.index ? -1 : 1);
14659 for(var i = 0, len = c.length; i < len; i++){
14660 items[i] = c[i].value;
14663 this.fireEvent("sort", this);
14667 * Sorts this collection with the passed comparison function
14668 * @param {String} direction (optional) "ASC" or "DESC"
14669 * @param {Function} fn (optional) comparison function
14671 sort : function(dir, fn){
14672 this._sort("value", dir, fn);
14676 * Sorts this collection by keys
14677 * @param {String} direction (optional) "ASC" or "DESC"
14678 * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14680 keySort : function(dir, fn){
14681 this._sort("key", dir, fn || function(a, b){
14682 return String(a).toUpperCase()-String(b).toUpperCase();
14687 * Returns a range of items in this collection
14688 * @param {Number} startIndex (optional) defaults to 0
14689 * @param {Number} endIndex (optional) default to the last item
14690 * @return {Array} An array of items
14692 getRange : function(start, end){
14693 var items = this.items;
14694 if(items.length < 1){
14697 start = start || 0;
14698 end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14701 for(var i = start; i <= end; i++) {
14702 r[r.length] = items[i];
14705 for(var i = start; i >= end; i--) {
14706 r[r.length] = items[i];
14713 * Filter the <i>objects</i> in this collection by a specific property.
14714 * Returns a new collection that has been filtered.
14715 * @param {String} property A property on your objects
14716 * @param {String/RegExp} value Either string that the property values
14717 * should start with or a RegExp to test against the property
14718 * @return {MixedCollection} The new filtered collection
14720 filter : function(property, value){
14721 if(!value.exec){ // not a regex
14722 value = String(value);
14723 if(value.length == 0){
14724 return this.clone();
14726 value = new RegExp("^" + Roo.escapeRe(value), "i");
14728 return this.filterBy(function(o){
14729 return o && value.test(o[property]);
14734 * Filter by a function. * Returns a new collection that has been filtered.
14735 * The passed function will be called with each
14736 * object in the collection. If the function returns true, the value is included
14737 * otherwise it is filtered.
14738 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14739 * @param {Object} scope (optional) The scope of the function (defaults to this)
14740 * @return {MixedCollection} The new filtered collection
14742 filterBy : function(fn, scope){
14743 var r = new Roo.util.MixedCollection();
14744 r.getKey = this.getKey;
14745 var k = this.keys, it = this.items;
14746 for(var i = 0, len = it.length; i < len; i++){
14747 if(fn.call(scope||this, it[i], k[i])){
14748 r.add(k[i], it[i]);
14755 * Creates a duplicate of this collection
14756 * @return {MixedCollection}
14758 clone : function(){
14759 var r = new Roo.util.MixedCollection();
14760 var k = this.keys, it = this.items;
14761 for(var i = 0, len = it.length; i < len; i++){
14762 r.add(k[i], it[i]);
14764 r.getKey = this.getKey;
14769 * Returns the item associated with the passed key or index.
14771 * @param {String/Number} key The key or index of the item.
14772 * @return {Object} The item associated with the passed key.
14774 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14776 * Ext JS Library 1.1.1
14777 * Copyright(c) 2006-2007, Ext JS, LLC.
14779 * Originally Released Under LGPL - original licence link has changed is not relivant.
14782 * <script type="text/javascript">
14785 * @class Roo.util.JSON
14786 * Modified version of Douglas Crockford"s json.js that doesn"t
14787 * mess with the Object prototype
14788 * http://www.json.org/js.html
14791 Roo.util.JSON = new (function(){
14792 var useHasOwn = {}.hasOwnProperty ? true : false;
14794 // crashes Safari in some instances
14795 //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14797 var pad = function(n) {
14798 return n < 10 ? "0" + n : n;
14811 var encodeString = function(s){
14812 if (/["\\\x00-\x1f]/.test(s)) {
14813 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14818 c = b.charCodeAt();
14820 Math.floor(c / 16).toString(16) +
14821 (c % 16).toString(16);
14824 return '"' + s + '"';
14827 var encodeArray = function(o){
14828 var a = ["["], b, i, l = o.length, v;
14829 for (i = 0; i < l; i += 1) {
14831 switch (typeof v) {
14840 a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14848 var encodeDate = function(o){
14849 return '"' + o.getFullYear() + "-" +
14850 pad(o.getMonth() + 1) + "-" +
14851 pad(o.getDate()) + "T" +
14852 pad(o.getHours()) + ":" +
14853 pad(o.getMinutes()) + ":" +
14854 pad(o.getSeconds()) + '"';
14858 * Encodes an Object, Array or other value
14859 * @param {Mixed} o The variable to encode
14860 * @return {String} The JSON string
14862 this.encode = function(o)
14864 // should this be extended to fully wrap stringify..
14866 if(typeof o == "undefined" || o === null){
14868 }else if(o instanceof Array){
14869 return encodeArray(o);
14870 }else if(o instanceof Date){
14871 return encodeDate(o);
14872 }else if(typeof o == "string"){
14873 return encodeString(o);
14874 }else if(typeof o == "number"){
14875 return isFinite(o) ? String(o) : "null";
14876 }else if(typeof o == "boolean"){
14879 var a = ["{"], b, i, v;
14881 if(!useHasOwn || o.hasOwnProperty(i)) {
14883 switch (typeof v) {
14892 a.push(this.encode(i), ":",
14893 v === null ? "null" : this.encode(v));
14904 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14905 * @param {String} json The JSON string
14906 * @return {Object} The resulting object
14908 this.decode = function(json){
14910 return /** eval:var:json */ eval("(" + json + ')');
14914 * Shorthand for {@link Roo.util.JSON#encode}
14915 * @member Roo encode
14917 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14919 * Shorthand for {@link Roo.util.JSON#decode}
14920 * @member Roo decode
14922 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14925 * Ext JS Library 1.1.1
14926 * Copyright(c) 2006-2007, Ext JS, LLC.
14928 * Originally Released Under LGPL - original licence link has changed is not relivant.
14931 * <script type="text/javascript">
14935 * @class Roo.util.Format
14936 * Reusable data formatting functions
14939 Roo.util.Format = function(){
14940 var trimRe = /^\s+|\s+$/g;
14943 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14944 * @param {String} value The string to truncate
14945 * @param {Number} length The maximum length to allow before truncating
14946 * @return {String} The converted text
14948 ellipsis : function(value, len){
14949 if(value && value.length > len){
14950 return value.substr(0, len-3)+"...";
14956 * Checks a reference and converts it to empty string if it is undefined
14957 * @param {Mixed} value Reference to check
14958 * @return {Mixed} Empty string if converted, otherwise the original value
14960 undef : function(value){
14961 return typeof value != "undefined" ? value : "";
14965 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14966 * @param {String} value The string to encode
14967 * @return {String} The encoded text
14969 htmlEncode : function(value){
14970 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
14974 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14975 * @param {String} value The string to decode
14976 * @return {String} The decoded text
14978 htmlDecode : function(value){
14979 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"');
14983 * Trims any whitespace from either side of a string
14984 * @param {String} value The text to trim
14985 * @return {String} The trimmed text
14987 trim : function(value){
14988 return String(value).replace(trimRe, "");
14992 * Returns a substring from within an original string
14993 * @param {String} value The original text
14994 * @param {Number} start The start index of the substring
14995 * @param {Number} length The length of the substring
14996 * @return {String} The substring
14998 substr : function(value, start, length){
14999 return String(value).substr(start, length);
15003 * Converts a string to all lower case letters
15004 * @param {String} value The text to convert
15005 * @return {String} The converted text
15007 lowercase : function(value){
15008 return String(value).toLowerCase();
15012 * Converts a string to all upper case letters
15013 * @param {String} value The text to convert
15014 * @return {String} The converted text
15016 uppercase : function(value){
15017 return String(value).toUpperCase();
15021 * Converts the first character only of a string to upper case
15022 * @param {String} value The text to convert
15023 * @return {String} The converted text
15025 capitalize : function(value){
15026 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15030 call : function(value, fn){
15031 if(arguments.length > 2){
15032 var args = Array.prototype.slice.call(arguments, 2);
15033 args.unshift(value);
15035 return /** eval:var:value */ eval(fn).apply(window, args);
15037 /** eval:var:value */
15038 return /** eval:var:value */ eval(fn).call(window, value);
15044 * safer version of Math.toFixed..??/
15045 * @param {Number/String} value The numeric value to format
15046 * @param {Number/String} value Decimal places
15047 * @return {String} The formatted currency string
15049 toFixed : function(v, n)
15051 // why not use to fixed - precision is buggered???
15053 return Math.round(v-0);
15055 var fact = Math.pow(10,n+1);
15056 v = (Math.round((v-0)*fact))/fact;
15057 var z = (''+fact).substring(2);
15058 if (v == Math.floor(v)) {
15059 return Math.floor(v) + '.' + z;
15062 // now just padd decimals..
15063 var ps = String(v).split('.');
15064 var fd = (ps[1] + z);
15065 var r = fd.substring(0,n);
15066 var rm = fd.substring(n);
15068 return ps[0] + '.' + r;
15070 r*=1; // turn it into a number;
15072 if (String(r).length != n) {
15075 r = String(r).substring(1); // chop the end off.
15078 return ps[0] + '.' + r;
15083 * Format a number as US currency
15084 * @param {Number/String} value The numeric value to format
15085 * @return {String} The formatted currency string
15087 usMoney : function(v){
15088 return '$' + Roo.util.Format.number(v);
15093 * eventually this should probably emulate php's number_format
15094 * @param {Number/String} value The numeric value to format
15095 * @param {Number} decimals number of decimal places
15096 * @param {String} delimiter for thousands (default comma)
15097 * @return {String} The formatted currency string
15099 number : function(v, decimals, thousandsDelimiter)
15101 // multiply and round.
15102 decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15103 thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15105 var mul = Math.pow(10, decimals);
15106 var zero = String(mul).substring(1);
15107 v = (Math.round((v-0)*mul))/mul;
15109 // if it's '0' number.. then
15111 //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15113 var ps = v.split('.');
15116 var r = /(\d+)(\d{3})/;
15119 if(thousandsDelimiter.length != 0) {
15120 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15125 (decimals ? ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15126 // does not have decimals
15127 (decimals ? ('.' + zero) : '');
15130 return whole + sub ;
15134 * Parse a value into a formatted date using the specified format pattern.
15135 * @param {Mixed} value The value to format
15136 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15137 * @return {String} The formatted date string
15139 date : function(v, format){
15143 if(!(v instanceof Date)){
15144 v = new Date(Date.parse(v));
15146 return v.dateFormat(format || Roo.util.Format.defaults.date);
15150 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15151 * @param {String} format Any valid date format string
15152 * @return {Function} The date formatting function
15154 dateRenderer : function(format){
15155 return function(v){
15156 return Roo.util.Format.date(v, format);
15161 stripTagsRE : /<\/?[^>]+>/gi,
15164 * Strips all HTML tags
15165 * @param {Mixed} value The text from which to strip tags
15166 * @return {String} The stripped text
15168 stripTags : function(v){
15169 return !v ? v : String(v).replace(this.stripTagsRE, "");
15173 * Size in Mb,Gb etc.
15174 * @param {Number} value The number to be formated
15175 * @param {number} decimals how many decimal places
15176 * @return {String} the formated string
15178 size : function(value, decimals)
15180 var sizes = ['b', 'k', 'M', 'G', 'T'];
15184 var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15185 return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals) + sizes[i];
15192 Roo.util.Format.defaults = {
15196 * Ext JS Library 1.1.1
15197 * Copyright(c) 2006-2007, Ext JS, LLC.
15199 * Originally Released Under LGPL - original licence link has changed is not relivant.
15202 * <script type="text/javascript">
15209 * @class Roo.MasterTemplate
15210 * @extends Roo.Template
15211 * Provides a template that can have child templates. The syntax is:
15213 var t = new Roo.MasterTemplate(
15214 '<select name="{name}">',
15215 '<tpl name="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
15218 t.add('options', {value: 'foo', text: 'bar'});
15219 // or you can add multiple child elements in one shot
15220 t.addAll('options', [
15221 {value: 'foo', text: 'bar'},
15222 {value: 'foo2', text: 'bar2'},
15223 {value: 'foo3', text: 'bar3'}
15225 // then append, applying the master template values
15226 t.append('my-form', {name: 'my-select'});
15228 * A name attribute for the child template is not required if you have only one child
15229 * template or you want to refer to them by index.
15231 Roo.MasterTemplate = function(){
15232 Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15233 this.originalHtml = this.html;
15235 var m, re = this.subTemplateRe;
15238 while(m = re.exec(this.html)){
15239 var name = m[1], content = m[2];
15244 tpl : new Roo.Template(content)
15247 st[name] = st[subIndex];
15249 st[subIndex].tpl.compile();
15250 st[subIndex].tpl.call = this.call.createDelegate(this);
15253 this.subCount = subIndex;
15256 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15258 * The regular expression used to match sub templates
15262 subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15265 * Applies the passed values to a child template.
15266 * @param {String/Number} name (optional) The name or index of the child template
15267 * @param {Array/Object} values The values to be applied to the template
15268 * @return {MasterTemplate} this
15270 add : function(name, values){
15271 if(arguments.length == 1){
15272 values = arguments[0];
15275 var s = this.subs[name];
15276 s.buffer[s.buffer.length] = s.tpl.apply(values);
15281 * Applies all the passed values to a child template.
15282 * @param {String/Number} name (optional) The name or index of the child template
15283 * @param {Array} values The values to be applied to the template, this should be an array of objects.
15284 * @param {Boolean} reset (optional) True to reset the template first
15285 * @return {MasterTemplate} this
15287 fill : function(name, values, reset){
15289 if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15297 for(var i = 0, len = values.length; i < len; i++){
15298 this.add(name, values[i]);
15304 * Resets the template for reuse
15305 * @return {MasterTemplate} this
15307 reset : function(){
15309 for(var i = 0; i < this.subCount; i++){
15315 applyTemplate : function(values){
15317 var replaceIndex = -1;
15318 this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15319 return s[++replaceIndex].buffer.join("");
15321 return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15324 apply : function(){
15325 return this.applyTemplate.apply(this, arguments);
15328 compile : function(){return this;}
15332 * Alias for fill().
15335 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15337 * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15338 * var tpl = Roo.MasterTemplate.from('element-id');
15339 * @param {String/HTMLElement} el
15340 * @param {Object} config
15343 Roo.MasterTemplate.from = function(el, config){
15344 el = Roo.getDom(el);
15345 return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15348 * Ext JS Library 1.1.1
15349 * Copyright(c) 2006-2007, Ext JS, LLC.
15351 * Originally Released Under LGPL - original licence link has changed is not relivant.
15354 * <script type="text/javascript">
15359 * @class Roo.util.CSS
15360 * Utility class for manipulating CSS rules
15364 Roo.util.CSS = function(){
15366 var doc = document;
15368 var camelRe = /(-[a-z])/gi;
15369 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15373 * Very simple dynamic creation of stylesheets from a text blob of rules. The text will wrapped in a style
15374 * tag and appended to the HEAD of the document.
15375 * @param {String|Object} cssText The text containing the css rules
15376 * @param {String} id An id to add to the stylesheet for later removal
15377 * @return {StyleSheet}
15379 createStyleSheet : function(cssText, id){
15381 var head = doc.getElementsByTagName("head")[0];
15382 var nrules = doc.createElement("style");
15383 nrules.setAttribute("type", "text/css");
15385 nrules.setAttribute("id", id);
15387 if (typeof(cssText) != 'string') {
15388 // support object maps..
15389 // not sure if this a good idea..
15390 // perhaps it should be merged with the general css handling
15391 // and handle js style props.
15392 var cssTextNew = [];
15393 for(var n in cssText) {
15395 for(var k in cssText[n]) {
15396 citems.push( k + ' : ' +cssText[n][k] + ';' );
15398 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15401 cssText = cssTextNew.join("\n");
15407 head.appendChild(nrules);
15408 ss = nrules.styleSheet;
15409 ss.cssText = cssText;
15412 nrules.appendChild(doc.createTextNode(cssText));
15414 nrules.cssText = cssText;
15416 head.appendChild(nrules);
15417 ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15419 this.cacheStyleSheet(ss);
15424 * Removes a style or link tag by id
15425 * @param {String} id The id of the tag
15427 removeStyleSheet : function(id){
15428 var existing = doc.getElementById(id);
15430 existing.parentNode.removeChild(existing);
15435 * Dynamically swaps an existing stylesheet reference for a new one
15436 * @param {String} id The id of an existing link tag to remove
15437 * @param {String} url The href of the new stylesheet to include
15439 swapStyleSheet : function(id, url){
15440 this.removeStyleSheet(id);
15441 var ss = doc.createElement("link");
15442 ss.setAttribute("rel", "stylesheet");
15443 ss.setAttribute("type", "text/css");
15444 ss.setAttribute("id", id);
15445 ss.setAttribute("href", url);
15446 doc.getElementsByTagName("head")[0].appendChild(ss);
15450 * Refresh the rule cache if you have dynamically added stylesheets
15451 * @return {Object} An object (hash) of rules indexed by selector
15453 refreshCache : function(){
15454 return this.getRules(true);
15458 cacheStyleSheet : function(stylesheet){
15462 try{// try catch for cross domain access issue
15463 var ssRules = stylesheet.cssRules || stylesheet.rules;
15464 for(var j = ssRules.length-1; j >= 0; --j){
15465 rules[ssRules[j].selectorText] = ssRules[j];
15471 * Gets all css rules for the document
15472 * @param {Boolean} refreshCache true to refresh the internal cache
15473 * @return {Object} An object (hash) of rules indexed by selector
15475 getRules : function(refreshCache){
15476 if(rules == null || refreshCache){
15478 var ds = doc.styleSheets;
15479 for(var i =0, len = ds.length; i < len; i++){
15481 this.cacheStyleSheet(ds[i]);
15489 * Gets an an individual CSS rule by selector(s)
15490 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15491 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15492 * @return {CSSRule} The CSS rule or null if one is not found
15494 getRule : function(selector, refreshCache){
15495 var rs = this.getRules(refreshCache);
15496 if(!(selector instanceof Array)){
15497 return rs[selector];
15499 for(var i = 0; i < selector.length; i++){
15500 if(rs[selector[i]]){
15501 return rs[selector[i]];
15509 * Updates a rule property
15510 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15511 * @param {String} property The css property
15512 * @param {String} value The new value for the property
15513 * @return {Boolean} true If a rule was found and updated
15515 updateRule : function(selector, property, value){
15516 if(!(selector instanceof Array)){
15517 var rule = this.getRule(selector);
15519 rule.style[property.replace(camelRe, camelFn)] = value;
15523 for(var i = 0; i < selector.length; i++){
15524 if(this.updateRule(selector[i], property, value)){
15534 * Ext JS Library 1.1.1
15535 * Copyright(c) 2006-2007, Ext JS, LLC.
15537 * Originally Released Under LGPL - original licence link has changed is not relivant.
15540 * <script type="text/javascript">
15546 * @class Roo.util.ClickRepeater
15547 * @extends Roo.util.Observable
15549 * A wrapper class which can be applied to any element. Fires a "click" event while the
15550 * mouse is pressed. The interval between firings may be specified in the config but
15551 * defaults to 10 milliseconds.
15553 * Optionally, a CSS class may be applied to the element during the time it is pressed.
15555 * @cfg {String/HTMLElement/Element} el The element to act as a button.
15556 * @cfg {Number} delay The initial delay before the repeating event begins firing.
15557 * Similar to an autorepeat key delay.
15558 * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15559 * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15560 * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15561 * "interval" and "delay" are ignored. "immediate" is honored.
15562 * @cfg {Boolean} preventDefault True to prevent the default click event
15563 * @cfg {Boolean} stopDefault True to stop the default click event
15566 * 2007-02-02 jvs Original code contributed by Nige "Animal" White
15567 * 2007-02-02 jvs Renamed to ClickRepeater
15568 * 2007-02-03 jvs Modifications for FF Mac and Safari
15571 * @param {String/HTMLElement/Element} el The element to listen on
15572 * @param {Object} config
15574 Roo.util.ClickRepeater = function(el, config)
15576 this.el = Roo.get(el);
15577 this.el.unselectable();
15579 Roo.apply(this, config);
15584 * Fires when the mouse button is depressed.
15585 * @param {Roo.util.ClickRepeater} this
15587 "mousedown" : true,
15590 * Fires on a specified interval during the time the element is pressed.
15591 * @param {Roo.util.ClickRepeater} this
15596 * Fires when the mouse key is released.
15597 * @param {Roo.util.ClickRepeater} this
15602 this.el.on("mousedown", this.handleMouseDown, this);
15603 if(this.preventDefault || this.stopDefault){
15604 this.el.on("click", function(e){
15605 if(this.preventDefault){
15606 e.preventDefault();
15608 if(this.stopDefault){
15614 // allow inline handler
15616 this.on("click", this.handler, this.scope || this);
15619 Roo.util.ClickRepeater.superclass.constructor.call(this);
15622 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15625 preventDefault : true,
15626 stopDefault : false,
15630 handleMouseDown : function(){
15631 clearTimeout(this.timer);
15633 if(this.pressClass){
15634 this.el.addClass(this.pressClass);
15636 this.mousedownTime = new Date();
15638 Roo.get(document).on("mouseup", this.handleMouseUp, this);
15639 this.el.on("mouseout", this.handleMouseOut, this);
15641 this.fireEvent("mousedown", this);
15642 this.fireEvent("click", this);
15644 this.timer = this.click.defer(this.delay || this.interval, this);
15648 click : function(){
15649 this.fireEvent("click", this);
15650 this.timer = this.click.defer(this.getInterval(), this);
15654 getInterval: function(){
15655 if(!this.accelerate){
15656 return this.interval;
15658 var pressTime = this.mousedownTime.getElapsed();
15659 if(pressTime < 500){
15661 }else if(pressTime < 1700){
15663 }else if(pressTime < 2600){
15665 }else if(pressTime < 3500){
15667 }else if(pressTime < 4400){
15669 }else if(pressTime < 5300){
15671 }else if(pressTime < 6200){
15679 handleMouseOut : function(){
15680 clearTimeout(this.timer);
15681 if(this.pressClass){
15682 this.el.removeClass(this.pressClass);
15684 this.el.on("mouseover", this.handleMouseReturn, this);
15688 handleMouseReturn : function(){
15689 this.el.un("mouseover", this.handleMouseReturn);
15690 if(this.pressClass){
15691 this.el.addClass(this.pressClass);
15697 handleMouseUp : function(){
15698 clearTimeout(this.timer);
15699 this.el.un("mouseover", this.handleMouseReturn);
15700 this.el.un("mouseout", this.handleMouseOut);
15701 Roo.get(document).un("mouseup", this.handleMouseUp);
15702 this.el.removeClass(this.pressClass);
15703 this.fireEvent("mouseup", this);
15706 * @class Roo.util.Clipboard
15712 Roo.util.Clipboard = {
15714 * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15715 * @param {String} text to copy to clipboard
15717 write : function(text) {
15718 // navigator clipboard api needs a secure context (https)
15719 if (navigator.clipboard && window.isSecureContext) {
15720 // navigator clipboard api method'
15721 navigator.clipboard.writeText(text);
15724 // text area method
15725 var ta = document.createElement("textarea");
15727 // make the textarea out of viewport
15728 ta.style.position = "fixed";
15729 ta.style.left = "-999999px";
15730 ta.style.top = "-999999px";
15731 document.body.appendChild(ta);
15734 document.execCommand('copy');
15744 * Ext JS Library 1.1.1
15745 * Copyright(c) 2006-2007, Ext JS, LLC.
15747 * Originally Released Under LGPL - original licence link has changed is not relivant.
15750 * <script type="text/javascript">
15755 * @class Roo.KeyNav
15756 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
15757 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15758 * way to implement custom navigation schemes for any UI component.</p>
15759 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15760 * pageUp, pageDown, del, home, end. Usage:</p>
15762 var nav = new Roo.KeyNav("my-element", {
15763 "left" : function(e){
15764 this.moveLeft(e.ctrlKey);
15766 "right" : function(e){
15767 this.moveRight(e.ctrlKey);
15769 "enter" : function(e){
15776 * @param {String/HTMLElement/Roo.Element} el The element to bind to
15777 * @param {Object} config The config
15779 Roo.KeyNav = function(el, config){
15780 this.el = Roo.get(el);
15781 Roo.apply(this, config);
15782 if(!this.disabled){
15783 this.disabled = true;
15788 Roo.KeyNav.prototype = {
15790 * @cfg {Boolean} disabled
15791 * True to disable this KeyNav instance (defaults to false)
15795 * @cfg {String} defaultEventAction
15796 * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key. Valid values are
15797 * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15798 * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15800 defaultEventAction: "stopEvent",
15802 * @cfg {Boolean} forceKeyDown
15803 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
15804 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15805 * handle keydown instead of keypress.
15807 forceKeyDown : false,
15810 prepareEvent : function(e){
15811 var k = e.getKey();
15812 var h = this.keyToHandler[k];
15813 //if(h && this[h]){
15814 // e.stopPropagation();
15816 if(Roo.isSafari && h && k >= 37 && k <= 40){
15822 relay : function(e){
15823 var k = e.getKey();
15824 var h = this.keyToHandler[k];
15826 if(this.doRelay(e, this[h], h) !== true){
15827 e[this.defaultEventAction]();
15833 doRelay : function(e, h, hname){
15834 return h.call(this.scope || this, e);
15837 // possible handlers
15851 // quick lookup hash
15868 * Enable this KeyNav
15870 enable: function(){
15872 // ie won't do special keys on keypress, no one else will repeat keys with keydown
15873 // the EventObject will normalize Safari automatically
15874 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15875 this.el.on("keydown", this.relay, this);
15877 this.el.on("keydown", this.prepareEvent, this);
15878 this.el.on("keypress", this.relay, this);
15880 this.disabled = false;
15885 * Disable this KeyNav
15887 disable: function(){
15888 if(!this.disabled){
15889 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15890 this.el.un("keydown", this.relay);
15892 this.el.un("keydown", this.prepareEvent);
15893 this.el.un("keypress", this.relay);
15895 this.disabled = true;
15900 * Ext JS Library 1.1.1
15901 * Copyright(c) 2006-2007, Ext JS, LLC.
15903 * Originally Released Under LGPL - original licence link has changed is not relivant.
15906 * <script type="text/javascript">
15911 * @class Roo.KeyMap
15912 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15913 * The constructor accepts the same config object as defined by {@link #addBinding}.
15914 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15915 * combination it will call the function with this signature (if the match is a multi-key
15916 * combination the callback will still be called only once): (String key, Roo.EventObject e)
15917 * A KeyMap can also handle a string representation of keys.<br />
15920 // map one key by key code
15921 var map = new Roo.KeyMap("my-element", {
15922 key: 13, // or Roo.EventObject.ENTER
15927 // map multiple keys to one action by string
15928 var map = new Roo.KeyMap("my-element", {
15934 // map multiple keys to multiple actions by strings and array of codes
15935 var map = new Roo.KeyMap("my-element", [
15938 fn: function(){ alert("Return was pressed"); }
15941 fn: function(){ alert('a, b or c was pressed'); }
15946 fn: function(){ alert('Control + shift + tab was pressed.'); }
15950 * <b>Note: A KeyMap starts enabled</b>
15952 * @param {String/HTMLElement/Roo.Element} el The element to bind to
15953 * @param {Object} config The config (see {@link #addBinding})
15954 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15956 Roo.KeyMap = function(el, config, eventName){
15957 this.el = Roo.get(el);
15958 this.eventName = eventName || "keydown";
15959 this.bindings = [];
15961 this.addBinding(config);
15966 Roo.KeyMap.prototype = {
15968 * True to stop the event from bubbling and prevent the default browser action if the
15969 * key was handled by the KeyMap (defaults to false)
15975 * Add a new binding to this KeyMap. The following config object properties are supported:
15977 Property Type Description
15978 ---------- --------------- ----------------------------------------------------------------------
15979 key String/Array A single keycode or an array of keycodes to handle
15980 shift Boolean True to handle key only when shift is pressed (defaults to false)
15981 ctrl Boolean True to handle key only when ctrl is pressed (defaults to false)
15982 alt Boolean True to handle key only when alt is pressed (defaults to false)
15983 fn Function The function to call when KeyMap finds the expected key combination
15984 scope Object The scope of the callback function
15990 var map = new Roo.KeyMap(document, {
15991 key: Roo.EventObject.ENTER,
15996 //Add a new binding to the existing KeyMap later
16004 * @param {Object/Array} config A single KeyMap config or an array of configs
16006 addBinding : function(config){
16007 if(config instanceof Array){
16008 for(var i = 0, len = config.length; i < len; i++){
16009 this.addBinding(config[i]);
16013 var keyCode = config.key,
16014 shift = config.shift,
16015 ctrl = config.ctrl,
16018 scope = config.scope;
16019 if(typeof keyCode == "string"){
16021 var keyString = keyCode.toUpperCase();
16022 for(var j = 0, len = keyString.length; j < len; j++){
16023 ks.push(keyString.charCodeAt(j));
16027 var keyArray = keyCode instanceof Array;
16028 var handler = function(e){
16029 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
16030 var k = e.getKey();
16032 for(var i = 0, len = keyCode.length; i < len; i++){
16033 if(keyCode[i] == k){
16034 if(this.stopEvent){
16037 fn.call(scope || window, k, e);
16043 if(this.stopEvent){
16046 fn.call(scope || window, k, e);
16051 this.bindings.push(handler);
16055 * Shorthand for adding a single key listener
16056 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16057 * following options:
16058 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16059 * @param {Function} fn The function to call
16060 * @param {Object} scope (optional) The scope of the function
16062 on : function(key, fn, scope){
16063 var keyCode, shift, ctrl, alt;
16064 if(typeof key == "object" && !(key instanceof Array)){
16083 handleKeyDown : function(e){
16084 if(this.enabled){ //just in case
16085 var b = this.bindings;
16086 for(var i = 0, len = b.length; i < len; i++){
16087 b[i].call(this, e);
16093 * Returns true if this KeyMap is enabled
16094 * @return {Boolean}
16096 isEnabled : function(){
16097 return this.enabled;
16101 * Enables this KeyMap
16103 enable: function(){
16105 this.el.on(this.eventName, this.handleKeyDown, this);
16106 this.enabled = true;
16111 * Disable this KeyMap
16113 disable: function(){
16115 this.el.removeListener(this.eventName, this.handleKeyDown, this);
16116 this.enabled = false;
16121 * Ext JS Library 1.1.1
16122 * Copyright(c) 2006-2007, Ext JS, LLC.
16124 * Originally Released Under LGPL - original licence link has changed is not relivant.
16127 * <script type="text/javascript">
16132 * @class Roo.util.TextMetrics
16133 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16134 * wide, in pixels, a given block of text will be.
16137 Roo.util.TextMetrics = function(){
16141 * Measures the size of the specified text
16142 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16143 * that can affect the size of the rendered text
16144 * @param {String} text The text to measure
16145 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16146 * in order to accurately measure the text height
16147 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16149 measure : function(el, text, fixedWidth){
16151 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16154 shared.setFixedWidth(fixedWidth || 'auto');
16155 return shared.getSize(text);
16159 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
16160 * the overhead of multiple calls to initialize the style properties on each measurement.
16161 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16162 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16163 * in order to accurately measure the text height
16164 * @return {Roo.util.TextMetrics.Instance} instance The new instance
16166 createInstance : function(el, fixedWidth){
16167 return Roo.util.TextMetrics.Instance(el, fixedWidth);
16173 * @class Roo.util.TextMetrics.Instance
16174 * Instance of TextMetrics Calcuation
16176 * Create a new TextMetrics Instance
16177 * @param {Object} bindto
16178 * @param {Boolean} fixedWidth
16181 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16183 var ml = new Roo.Element(document.createElement('div'));
16184 document.body.appendChild(ml.dom);
16185 ml.position('absolute');
16186 ml.setLeftTop(-1000, -1000);
16190 ml.setWidth(fixedWidth);
16195 * Returns the size of the specified text based on the internal element's style and width properties
16196 * @param {String} text The text to measure
16197 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16199 getSize : function(text){
16201 var s = ml.getSize();
16207 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16208 * that can affect the size of the rendered text
16209 * @param {String/HTMLElement} el The element, dom node or id
16211 bind : function(el){
16213 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16218 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
16219 * to set a fixed width in order to accurately measure the text height.
16220 * @param {Number} width The width to set on the element
16222 setFixedWidth : function(width){
16223 ml.setWidth(width);
16227 * Returns the measured width of the specified text
16228 * @param {String} text The text to measure
16229 * @return {Number} width The width in pixels
16231 getWidth : function(text){
16232 ml.dom.style.width = 'auto';
16233 return this.getSize(text).width;
16237 * Returns the measured height of the specified text. For multiline text, be sure to call
16238 * {@link #setFixedWidth} if necessary.
16239 * @param {String} text The text to measure
16240 * @return {Number} height The height in pixels
16242 getHeight : function(text){
16243 return this.getSize(text).height;
16247 instance.bind(bindTo);
16252 // backwards compat
16253 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16255 * Ext JS Library 1.1.1
16256 * Copyright(c) 2006-2007, Ext JS, LLC.
16258 * Originally Released Under LGPL - original licence link has changed is not relivant.
16261 * <script type="text/javascript">
16265 * @class Roo.state.Provider
16266 * Abstract base class for state provider implementations. This class provides methods
16267 * for encoding and decoding <b>typed</b> variables including dates and defines the
16268 * Provider interface.
16270 Roo.state.Provider = function(){
16272 * @event statechange
16273 * Fires when a state change occurs.
16274 * @param {Provider} this This state provider
16275 * @param {String} key The state key which was changed
16276 * @param {String} value The encoded value for the state
16279 "statechange": true
16282 Roo.state.Provider.superclass.constructor.call(this);
16284 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16286 * Returns the current value for a key
16287 * @param {String} name The key name
16288 * @param {Mixed} defaultValue A default value to return if the key's value is not found
16289 * @return {Mixed} The state data
16291 get : function(name, defaultValue){
16292 return typeof this.state[name] == "undefined" ?
16293 defaultValue : this.state[name];
16297 * Clears a value from the state
16298 * @param {String} name The key name
16300 clear : function(name){
16301 delete this.state[name];
16302 this.fireEvent("statechange", this, name, null);
16306 * Sets the value for a key
16307 * @param {String} name The key name
16308 * @param {Mixed} value The value to set
16310 set : function(name, value){
16311 this.state[name] = value;
16312 this.fireEvent("statechange", this, name, value);
16316 * Decodes a string previously encoded with {@link #encodeValue}.
16317 * @param {String} value The value to decode
16318 * @return {Mixed} The decoded value
16320 decodeValue : function(cookie){
16321 var re = /^(a|n|d|b|s|o)\:(.*)$/;
16322 var matches = re.exec(unescape(cookie));
16323 if(!matches || !matches[1]) {
16324 return; // non state cookie
16326 var type = matches[1];
16327 var v = matches[2];
16330 return parseFloat(v);
16332 return new Date(Date.parse(v));
16337 var values = v.split("^");
16338 for(var i = 0, len = values.length; i < len; i++){
16339 all.push(this.decodeValue(values[i]));
16344 var values = v.split("^");
16345 for(var i = 0, len = values.length; i < len; i++){
16346 var kv = values[i].split("=");
16347 all[kv[0]] = this.decodeValue(kv[1]);
16356 * Encodes a value including type information. Decode with {@link #decodeValue}.
16357 * @param {Mixed} value The value to encode
16358 * @return {String} The encoded value
16360 encodeValue : function(v){
16362 if(typeof v == "number"){
16364 }else if(typeof v == "boolean"){
16365 enc = "b:" + (v ? "1" : "0");
16366 }else if(v instanceof Date){
16367 enc = "d:" + v.toGMTString();
16368 }else if(v instanceof Array){
16370 for(var i = 0, len = v.length; i < len; i++){
16371 flat += this.encodeValue(v[i]);
16377 }else if(typeof v == "object"){
16380 if(typeof v[key] != "function"){
16381 flat += key + "=" + this.encodeValue(v[key]) + "^";
16384 enc = "o:" + flat.substring(0, flat.length-1);
16388 return escape(enc);
16394 * Ext JS Library 1.1.1
16395 * Copyright(c) 2006-2007, Ext JS, LLC.
16397 * Originally Released Under LGPL - original licence link has changed is not relivant.
16400 * <script type="text/javascript">
16403 * @class Roo.state.Manager
16404 * This is the global state manager. By default all components that are "state aware" check this class
16405 * for state information if you don't pass them a custom state provider. In order for this class
16406 * to be useful, it must be initialized with a provider when your application initializes.
16408 // in your initialization function
16410 Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16412 // supposed you have a {@link Roo.BorderLayout}
16413 var layout = new Roo.BorderLayout(...);
16414 layout.restoreState();
16415 // or a {Roo.BasicDialog}
16416 var dialog = new Roo.BasicDialog(...);
16417 dialog.restoreState();
16421 Roo.state.Manager = function(){
16422 var provider = new Roo.state.Provider();
16426 * Configures the default state provider for your application
16427 * @param {Provider} stateProvider The state provider to set
16429 setProvider : function(stateProvider){
16430 provider = stateProvider;
16434 * Returns the current value for a key
16435 * @param {String} name The key name
16436 * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16437 * @return {Mixed} The state data
16439 get : function(key, defaultValue){
16440 return provider.get(key, defaultValue);
16444 * Sets the value for a key
16445 * @param {String} name The key name
16446 * @param {Mixed} value The state data
16448 set : function(key, value){
16449 provider.set(key, value);
16453 * Clears a value from the state
16454 * @param {String} name The key name
16456 clear : function(key){
16457 provider.clear(key);
16461 * Gets the currently configured state provider
16462 * @return {Provider} The state provider
16464 getProvider : function(){
16471 * Ext JS Library 1.1.1
16472 * Copyright(c) 2006-2007, Ext JS, LLC.
16474 * Originally Released Under LGPL - original licence link has changed is not relivant.
16477 * <script type="text/javascript">
16480 * @class Roo.state.CookieProvider
16481 * @extends Roo.state.Provider
16482 * The default Provider implementation which saves state via cookies.
16485 var cp = new Roo.state.CookieProvider({
16487 expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16488 domain: "roojs.com"
16490 Roo.state.Manager.setProvider(cp);
16492 * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16493 * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16494 * @cfg {String} domain The domain to save the cookie for. Note that you cannot specify a different domain than
16495 * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16496 * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16497 * domain the page is running on including the 'www' like 'www.roojs.com')
16498 * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16500 * Create a new CookieProvider
16501 * @param {Object} config The configuration object
16503 Roo.state.CookieProvider = function(config){
16504 Roo.state.CookieProvider.superclass.constructor.call(this);
16506 this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16507 this.domain = null;
16508 this.secure = false;
16509 Roo.apply(this, config);
16510 this.state = this.readCookies();
16513 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16515 set : function(name, value){
16516 if(typeof value == "undefined" || value === null){
16520 this.setCookie(name, value);
16521 Roo.state.CookieProvider.superclass.set.call(this, name, value);
16525 clear : function(name){
16526 this.clearCookie(name);
16527 Roo.state.CookieProvider.superclass.clear.call(this, name);
16531 readCookies : function(){
16533 var c = document.cookie + ";";
16534 var re = /\s?(.*?)=(.*?);/g;
16536 while((matches = re.exec(c)) != null){
16537 var name = matches[1];
16538 var value = matches[2];
16539 if(name && name.substring(0,3) == "ys-"){
16540 cookies[name.substr(3)] = this.decodeValue(value);
16547 setCookie : function(name, value){
16548 document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16549 ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16550 ((this.path == null) ? "" : ("; path=" + this.path)) +
16551 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16552 ((this.secure == true) ? "; secure" : "");
16556 clearCookie : function(name){
16557 document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16558 ((this.path == null) ? "" : ("; path=" + this.path)) +
16559 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16560 ((this.secure == true) ? "; secure" : "");
16564 * Ext JS Library 1.1.1
16565 * Copyright(c) 2006-2007, Ext JS, LLC.
16567 * Originally Released Under LGPL - original licence link has changed is not relivant.
16570 * <script type="text/javascript">
16575 * @class Roo.ComponentMgr
16576 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16579 Roo.ComponentMgr = function(){
16580 var all = new Roo.util.MixedCollection();
16584 * Registers a component.
16585 * @param {Roo.Component} c The component
16587 register : function(c){
16592 * Unregisters a component.
16593 * @param {Roo.Component} c The component
16595 unregister : function(c){
16600 * Returns a component by id
16601 * @param {String} id The component id
16603 get : function(id){
16604 return all.get(id);
16608 * Registers a function that will be called when a specified component is added to ComponentMgr
16609 * @param {String} id The component id
16610 * @param {Funtction} fn The callback function
16611 * @param {Object} scope The scope of the callback
16613 onAvailable : function(id, fn, scope){
16614 all.on("add", function(index, o){
16616 fn.call(scope || o, o);
16617 all.un("add", fn, scope);
16624 * Ext JS Library 1.1.1
16625 * Copyright(c) 2006-2007, Ext JS, LLC.
16627 * Originally Released Under LGPL - original licence link has changed is not relivant.
16630 * <script type="text/javascript">
16634 * @class Roo.Component
16635 * @extends Roo.util.Observable
16636 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
16637 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
16638 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16639 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16640 * All visual components (widgets) that require rendering into a layout should subclass Component.
16642 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
16643 * element and its id used as the component id. If a string is passed, it is assumed to be the id of an existing element
16644 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
16646 Roo.Component = function(config){
16647 config = config || {};
16648 if(config.tagName || config.dom || typeof config == "string"){ // element object
16649 config = {el: config, id: config.id || config};
16651 this.initialConfig = config;
16653 Roo.apply(this, config);
16657 * Fires after the component is disabled.
16658 * @param {Roo.Component} this
16663 * Fires after the component is enabled.
16664 * @param {Roo.Component} this
16668 * @event beforeshow
16669 * Fires before the component is shown. Return false to stop the show.
16670 * @param {Roo.Component} this
16675 * Fires after the component is shown.
16676 * @param {Roo.Component} this
16680 * @event beforehide
16681 * Fires before the component is hidden. Return false to stop the hide.
16682 * @param {Roo.Component} this
16687 * Fires after the component is hidden.
16688 * @param {Roo.Component} this
16692 * @event beforerender
16693 * Fires before the component is rendered. Return false to stop the render.
16694 * @param {Roo.Component} this
16696 beforerender : true,
16699 * Fires after the component is rendered.
16700 * @param {Roo.Component} this
16704 * @event beforedestroy
16705 * Fires before the component is destroyed. Return false to stop the destroy.
16706 * @param {Roo.Component} this
16708 beforedestroy : true,
16711 * Fires after the component is destroyed.
16712 * @param {Roo.Component} this
16717 this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16719 Roo.ComponentMgr.register(this);
16720 Roo.Component.superclass.constructor.call(this);
16721 this.initComponent();
16722 if(this.renderTo){ // not supported by all components yet. use at your own risk!
16723 this.render(this.renderTo);
16724 delete this.renderTo;
16729 Roo.Component.AUTO_ID = 1000;
16731 Roo.extend(Roo.Component, Roo.util.Observable, {
16733 * @scope Roo.Component.prototype
16735 * true if this component is hidden. Read-only.
16740 * true if this component is disabled. Read-only.
16745 * true if this component has been rendered. Read-only.
16749 /** @cfg {String} disableClass
16750 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16752 disabledClass : "x-item-disabled",
16753 /** @cfg {Boolean} allowDomMove
16754 * Whether the component can move the Dom node when rendering (defaults to true).
16756 allowDomMove : true,
16757 /** @cfg {String} hideMode (display|visibility)
16758 * How this component should hidden. Supported values are
16759 * "visibility" (css visibility), "offsets" (negative offset position) and
16760 * "display" (css display) - defaults to "display".
16762 hideMode: 'display',
16765 ctype : "Roo.Component",
16768 * @cfg {String} actionMode
16769 * which property holds the element that used for hide() / show() / disable() / enable()
16770 * default is 'el' for forms you probably want to set this to fieldEl
16775 getActionEl : function(){
16776 return this[this.actionMode];
16779 initComponent : Roo.emptyFn,
16781 * If this is a lazy rendering component, render it to its container element.
16782 * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
16784 render : function(container, position){
16790 if(this.fireEvent("beforerender", this) === false){
16794 if(!container && this.el){
16795 this.el = Roo.get(this.el);
16796 container = this.el.dom.parentNode;
16797 this.allowDomMove = false;
16799 this.container = Roo.get(container);
16800 this.rendered = true;
16801 if(position !== undefined){
16802 if(typeof position == 'number'){
16803 position = this.container.dom.childNodes[position];
16805 position = Roo.getDom(position);
16808 this.onRender(this.container, position || null);
16810 this.el.addClass(this.cls);
16814 this.el.applyStyles(this.style);
16817 this.fireEvent("render", this);
16818 this.afterRender(this.container);
16831 // default function is not really useful
16832 onRender : function(ct, position){
16834 this.el = Roo.get(this.el);
16835 if(this.allowDomMove !== false){
16836 ct.dom.insertBefore(this.el.dom, position);
16842 getAutoCreate : function(){
16843 var cfg = typeof this.autoCreate == "object" ?
16844 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16845 if(this.id && !cfg.id){
16852 afterRender : Roo.emptyFn,
16855 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16856 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16858 destroy : function(){
16859 if(this.fireEvent("beforedestroy", this) !== false){
16860 this.purgeListeners();
16861 this.beforeDestroy();
16863 this.el.removeAllListeners();
16865 if(this.actionMode == "container"){
16866 this.container.remove();
16870 Roo.ComponentMgr.unregister(this);
16871 this.fireEvent("destroy", this);
16876 beforeDestroy : function(){
16881 onDestroy : function(){
16886 * Returns the underlying {@link Roo.Element}.
16887 * @return {Roo.Element} The element
16889 getEl : function(){
16894 * Returns the id of this component.
16897 getId : function(){
16902 * Try to focus this component.
16903 * @param {Boolean} selectText True to also select the text in this component (if applicable)
16904 * @return {Roo.Component} this
16906 focus : function(selectText){
16909 if(selectText === true){
16910 this.el.dom.select();
16925 * Disable this component.
16926 * @return {Roo.Component} this
16928 disable : function(){
16932 this.disabled = true;
16933 this.fireEvent("disable", this);
16938 onDisable : function(){
16939 this.getActionEl().addClass(this.disabledClass);
16940 this.el.dom.disabled = true;
16944 * Enable this component.
16945 * @return {Roo.Component} this
16947 enable : function(){
16951 this.disabled = false;
16952 this.fireEvent("enable", this);
16957 onEnable : function(){
16958 this.getActionEl().removeClass(this.disabledClass);
16959 this.el.dom.disabled = false;
16963 * Convenience function for setting disabled/enabled by boolean.
16964 * @param {Boolean} disabled
16966 setDisabled : function(disabled){
16967 this[disabled ? "disable" : "enable"]();
16971 * Show this component.
16972 * @return {Roo.Component} this
16975 if(this.fireEvent("beforeshow", this) !== false){
16976 this.hidden = false;
16980 this.fireEvent("show", this);
16986 onShow : function(){
16987 var ae = this.getActionEl();
16988 if(this.hideMode == 'visibility'){
16989 ae.dom.style.visibility = "visible";
16990 }else if(this.hideMode == 'offsets'){
16991 ae.removeClass('x-hidden');
16993 ae.dom.style.display = "";
16998 * Hide this component.
16999 * @return {Roo.Component} this
17002 if(this.fireEvent("beforehide", this) !== false){
17003 this.hidden = true;
17007 this.fireEvent("hide", this);
17013 onHide : function(){
17014 var ae = this.getActionEl();
17015 if(this.hideMode == 'visibility'){
17016 ae.dom.style.visibility = "hidden";
17017 }else if(this.hideMode == 'offsets'){
17018 ae.addClass('x-hidden');
17020 ae.dom.style.display = "none";
17025 * Convenience function to hide or show this component by boolean.
17026 * @param {Boolean} visible True to show, false to hide
17027 * @return {Roo.Component} this
17029 setVisible: function(visible){
17039 * Returns true if this component is visible.
17041 isVisible : function(){
17042 return this.getActionEl().isVisible();
17045 cloneConfig : function(overrides){
17046 overrides = overrides || {};
17047 var id = overrides.id || Roo.id();
17048 var cfg = Roo.applyIf(overrides, this.initialConfig);
17049 cfg.id = id; // prevent dup id
17050 return new this.constructor(cfg);
17054 * Ext JS Library 1.1.1
17055 * Copyright(c) 2006-2007, Ext JS, LLC.
17057 * Originally Released Under LGPL - original licence link has changed is not relivant.
17060 * <script type="text/javascript">
17064 * @class Roo.BoxComponent
17065 * @extends Roo.Component
17066 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
17067 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
17068 * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17069 * layout containers.
17071 * @param {Roo.Element/String/Object} config The configuration options.
17073 Roo.BoxComponent = function(config){
17074 Roo.Component.call(this, config);
17078 * Fires after the component is resized.
17079 * @param {Roo.Component} this
17080 * @param {Number} adjWidth The box-adjusted width that was set
17081 * @param {Number} adjHeight The box-adjusted height that was set
17082 * @param {Number} rawWidth The width that was originally specified
17083 * @param {Number} rawHeight The height that was originally specified
17088 * Fires after the component is moved.
17089 * @param {Roo.Component} this
17090 * @param {Number} x The new x position
17091 * @param {Number} y The new y position
17097 Roo.extend(Roo.BoxComponent, Roo.Component, {
17098 // private, set in afterRender to signify that the component has been rendered
17100 // private, used to defer height settings to subclasses
17101 deferHeight: false,
17102 /** @cfg {Number} width
17103 * width (optional) size of component
17105 /** @cfg {Number} height
17106 * height (optional) size of component
17110 * Sets the width and height of the component. This method fires the resize event. This method can accept
17111 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17112 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17113 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17114 * @return {Roo.BoxComponent} this
17116 setSize : function(w, h){
17117 // support for standard size objects
17118 if(typeof w == 'object'){
17123 if(!this.boxReady){
17129 // prevent recalcs when not needed
17130 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17133 this.lastSize = {width: w, height: h};
17135 var adj = this.adjustSize(w, h);
17136 var aw = adj.width, ah = adj.height;
17137 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17138 var rz = this.getResizeEl();
17139 if(!this.deferHeight && aw !== undefined && ah !== undefined){
17140 rz.setSize(aw, ah);
17141 }else if(!this.deferHeight && ah !== undefined){
17143 }else if(aw !== undefined){
17146 this.onResize(aw, ah, w, h);
17147 this.fireEvent('resize', this, aw, ah, w, h);
17153 * Gets the current size of the component's underlying element.
17154 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17156 getSize : function(){
17157 return this.el.getSize();
17161 * Gets the current XY position of the component's underlying element.
17162 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17163 * @return {Array} The XY position of the element (e.g., [100, 200])
17165 getPosition : function(local){
17166 if(local === true){
17167 return [this.el.getLeft(true), this.el.getTop(true)];
17169 return this.xy || this.el.getXY();
17173 * Gets the current box measurements of the component's underlying element.
17174 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17175 * @returns {Object} box An object in the format {x, y, width, height}
17177 getBox : function(local){
17178 var s = this.el.getSize();
17180 s.x = this.el.getLeft(true);
17181 s.y = this.el.getTop(true);
17183 var xy = this.xy || this.el.getXY();
17191 * Sets the current box measurements of the component's underlying element.
17192 * @param {Object} box An object in the format {x, y, width, height}
17193 * @returns {Roo.BoxComponent} this
17195 updateBox : function(box){
17196 this.setSize(box.width, box.height);
17197 this.setPagePosition(box.x, box.y);
17202 getResizeEl : function(){
17203 return this.resizeEl || this.el;
17207 getPositionEl : function(){
17208 return this.positionEl || this.el;
17212 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
17213 * This method fires the move event.
17214 * @param {Number} left The new left
17215 * @param {Number} top The new top
17216 * @returns {Roo.BoxComponent} this
17218 setPosition : function(x, y){
17221 if(!this.boxReady){
17224 var adj = this.adjustPosition(x, y);
17225 var ax = adj.x, ay = adj.y;
17227 var el = this.getPositionEl();
17228 if(ax !== undefined || ay !== undefined){
17229 if(ax !== undefined && ay !== undefined){
17230 el.setLeftTop(ax, ay);
17231 }else if(ax !== undefined){
17233 }else if(ay !== undefined){
17236 this.onPosition(ax, ay);
17237 this.fireEvent('move', this, ax, ay);
17243 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
17244 * This method fires the move event.
17245 * @param {Number} x The new x position
17246 * @param {Number} y The new y position
17247 * @returns {Roo.BoxComponent} this
17249 setPagePosition : function(x, y){
17252 if(!this.boxReady){
17255 if(x === undefined || y === undefined){ // cannot translate undefined points
17258 var p = this.el.translatePoints(x, y);
17259 this.setPosition(p.left, p.top);
17264 onRender : function(ct, position){
17265 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17267 this.resizeEl = Roo.get(this.resizeEl);
17269 if(this.positionEl){
17270 this.positionEl = Roo.get(this.positionEl);
17275 afterRender : function(){
17276 Roo.BoxComponent.superclass.afterRender.call(this);
17277 this.boxReady = true;
17278 this.setSize(this.width, this.height);
17279 if(this.x || this.y){
17280 this.setPosition(this.x, this.y);
17282 if(this.pageX || this.pageY){
17283 this.setPagePosition(this.pageX, this.pageY);
17288 * Force the component's size to recalculate based on the underlying element's current height and width.
17289 * @returns {Roo.BoxComponent} this
17291 syncSize : function(){
17292 delete this.lastSize;
17293 this.setSize(this.el.getWidth(), this.el.getHeight());
17298 * Called after the component is resized, this method is empty by default but can be implemented by any
17299 * subclass that needs to perform custom logic after a resize occurs.
17300 * @param {Number} adjWidth The box-adjusted width that was set
17301 * @param {Number} adjHeight The box-adjusted height that was set
17302 * @param {Number} rawWidth The width that was originally specified
17303 * @param {Number} rawHeight The height that was originally specified
17305 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17310 * Called after the component is moved, this method is empty by default but can be implemented by any
17311 * subclass that needs to perform custom logic after a move occurs.
17312 * @param {Number} x The new x position
17313 * @param {Number} y The new y position
17315 onPosition : function(x, y){
17320 adjustSize : function(w, h){
17321 if(this.autoWidth){
17324 if(this.autoHeight){
17327 return {width : w, height: h};
17331 adjustPosition : function(x, y){
17332 return {x : x, y: y};
17336 * Ext JS Library 1.1.1
17337 * Copyright(c) 2006-2007, Ext JS, LLC.
17339 * Originally Released Under LGPL - original licence link has changed is not relivant.
17342 * <script type="text/javascript">
17347 * @extends Roo.Element
17348 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17349 * automatic maintaining of shadow/shim positions.
17350 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17351 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17352 * you can pass a string with a CSS class name. False turns off the shadow.
17353 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17354 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17355 * @cfg {String} cls CSS class to add to the element
17356 * @cfg {Number} zindex Starting z-index (defaults to 11000)
17357 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17359 * @param {Object} config An object with config options.
17360 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17363 Roo.Layer = function(config, existingEl){
17364 config = config || {};
17365 var dh = Roo.DomHelper;
17366 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17368 this.dom = Roo.getDom(existingEl);
17371 var o = config.dh || {tag: "div", cls: "x-layer"};
17372 this.dom = dh.append(pel, o);
17375 this.addClass(config.cls);
17377 this.constrain = config.constrain !== false;
17378 this.visibilityMode = Roo.Element.VISIBILITY;
17380 this.id = this.dom.id = config.id;
17382 this.id = Roo.id(this.dom);
17384 this.zindex = config.zindex || this.getZIndex();
17385 this.position("absolute", this.zindex);
17387 this.shadowOffset = config.shadowOffset || 4;
17388 this.shadow = new Roo.Shadow({
17389 offset : this.shadowOffset,
17390 mode : config.shadow
17393 this.shadowOffset = 0;
17395 this.useShim = config.shim !== false && Roo.useShims;
17396 this.useDisplay = config.useDisplay;
17400 var supr = Roo.Element.prototype;
17402 // shims are shared among layer to keep from having 100 iframes
17405 Roo.extend(Roo.Layer, Roo.Element, {
17407 getZIndex : function(){
17408 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17411 getShim : function(){
17418 var shim = shims.shift();
17420 shim = this.createShim();
17421 shim.enableDisplayMode('block');
17422 shim.dom.style.display = 'none';
17423 shim.dom.style.visibility = 'visible';
17425 var pn = this.dom.parentNode;
17426 if(shim.dom.parentNode != pn){
17427 pn.insertBefore(shim.dom, this.dom);
17429 shim.setStyle('z-index', this.getZIndex()-2);
17434 hideShim : function(){
17436 this.shim.setDisplayed(false);
17437 shims.push(this.shim);
17442 disableShadow : function(){
17444 this.shadowDisabled = true;
17445 this.shadow.hide();
17446 this.lastShadowOffset = this.shadowOffset;
17447 this.shadowOffset = 0;
17451 enableShadow : function(show){
17453 this.shadowDisabled = false;
17454 this.shadowOffset = this.lastShadowOffset;
17455 delete this.lastShadowOffset;
17463 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17464 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17465 sync : function(doShow){
17466 var sw = this.shadow;
17467 if(!this.updating && this.isVisible() && (sw || this.useShim)){
17468 var sh = this.getShim();
17470 var w = this.getWidth(),
17471 h = this.getHeight();
17473 var l = this.getLeft(true),
17474 t = this.getTop(true);
17476 if(sw && !this.shadowDisabled){
17477 if(doShow && !sw.isVisible()){
17480 sw.realign(l, t, w, h);
17486 // fit the shim behind the shadow, so it is shimmed too
17487 var a = sw.adjusts, s = sh.dom.style;
17488 s.left = (Math.min(l, l+a.l))+"px";
17489 s.top = (Math.min(t, t+a.t))+"px";
17490 s.width = (w+a.w)+"px";
17491 s.height = (h+a.h)+"px";
17498 sh.setLeftTop(l, t);
17505 destroy : function(){
17508 this.shadow.hide();
17510 this.removeAllListeners();
17511 var pn = this.dom.parentNode;
17513 pn.removeChild(this.dom);
17515 Roo.Element.uncache(this.id);
17518 remove : function(){
17523 beginUpdate : function(){
17524 this.updating = true;
17528 endUpdate : function(){
17529 this.updating = false;
17534 hideUnders : function(negOffset){
17536 this.shadow.hide();
17542 constrainXY : function(){
17543 if(this.constrain){
17544 var vw = Roo.lib.Dom.getViewWidth(),
17545 vh = Roo.lib.Dom.getViewHeight();
17546 var s = Roo.get(document).getScroll();
17548 var xy = this.getXY();
17549 var x = xy[0], y = xy[1];
17550 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17551 // only move it if it needs it
17553 // first validate right/bottom
17554 if((x + w) > vw+s.left){
17555 x = vw - w - this.shadowOffset;
17558 if((y + h) > vh+s.top){
17559 y = vh - h - this.shadowOffset;
17562 // then make sure top/left isn't negative
17573 var ay = this.avoidY;
17574 if(y <= ay && (y+h) >= ay){
17580 supr.setXY.call(this, xy);
17586 isVisible : function(){
17587 return this.visible;
17591 showAction : function(){
17592 this.visible = true; // track visibility to prevent getStyle calls
17593 if(this.useDisplay === true){
17594 this.setDisplayed("");
17595 }else if(this.lastXY){
17596 supr.setXY.call(this, this.lastXY);
17597 }else if(this.lastLT){
17598 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17603 hideAction : function(){
17604 this.visible = false;
17605 if(this.useDisplay === true){
17606 this.setDisplayed(false);
17608 this.setLeftTop(-10000,-10000);
17612 // overridden Element method
17613 setVisible : function(v, a, d, c, e){
17618 var cb = function(){
17623 }.createDelegate(this);
17624 supr.setVisible.call(this, true, true, d, cb, e);
17627 this.hideUnders(true);
17636 }.createDelegate(this);
17638 supr.setVisible.call(this, v, a, d, cb, e);
17647 storeXY : function(xy){
17648 delete this.lastLT;
17652 storeLeftTop : function(left, top){
17653 delete this.lastXY;
17654 this.lastLT = [left, top];
17658 beforeFx : function(){
17659 this.beforeAction();
17660 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17664 afterFx : function(){
17665 Roo.Layer.superclass.afterFx.apply(this, arguments);
17666 this.sync(this.isVisible());
17670 beforeAction : function(){
17671 if(!this.updating && this.shadow){
17672 this.shadow.hide();
17676 // overridden Element method
17677 setLeft : function(left){
17678 this.storeLeftTop(left, this.getTop(true));
17679 supr.setLeft.apply(this, arguments);
17683 setTop : function(top){
17684 this.storeLeftTop(this.getLeft(true), top);
17685 supr.setTop.apply(this, arguments);
17689 setLeftTop : function(left, top){
17690 this.storeLeftTop(left, top);
17691 supr.setLeftTop.apply(this, arguments);
17695 setXY : function(xy, a, d, c, e){
17697 this.beforeAction();
17699 var cb = this.createCB(c);
17700 supr.setXY.call(this, xy, a, d, cb, e);
17707 createCB : function(c){
17718 // overridden Element method
17719 setX : function(x, a, d, c, e){
17720 this.setXY([x, this.getY()], a, d, c, e);
17723 // overridden Element method
17724 setY : function(y, a, d, c, e){
17725 this.setXY([this.getX(), y], a, d, c, e);
17728 // overridden Element method
17729 setSize : function(w, h, a, d, c, e){
17730 this.beforeAction();
17731 var cb = this.createCB(c);
17732 supr.setSize.call(this, w, h, a, d, cb, e);
17738 // overridden Element method
17739 setWidth : function(w, a, d, c, e){
17740 this.beforeAction();
17741 var cb = this.createCB(c);
17742 supr.setWidth.call(this, w, a, d, cb, e);
17748 // overridden Element method
17749 setHeight : function(h, a, d, c, e){
17750 this.beforeAction();
17751 var cb = this.createCB(c);
17752 supr.setHeight.call(this, h, a, d, cb, e);
17758 // overridden Element method
17759 setBounds : function(x, y, w, h, a, d, c, e){
17760 this.beforeAction();
17761 var cb = this.createCB(c);
17763 this.storeXY([x, y]);
17764 supr.setXY.call(this, [x, y]);
17765 supr.setSize.call(this, w, h, a, d, cb, e);
17768 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17774 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17775 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17776 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17777 * @param {Number} zindex The new z-index to set
17778 * @return {this} The Layer
17780 setZIndex : function(zindex){
17781 this.zindex = zindex;
17782 this.setStyle("z-index", zindex + 2);
17784 this.shadow.setZIndex(zindex + 1);
17787 this.shim.setStyle("z-index", zindex);
17792 * Original code for Roojs - LGPL
17793 * <script type="text/javascript">
17797 * @class Roo.XComponent
17798 * A delayed Element creator...
17799 * Or a way to group chunks of interface together.
17800 * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17801 * used in conjunction with XComponent.build() it will create an instance of each element,
17802 * then call addxtype() to build the User interface.
17804 * Mypart.xyx = new Roo.XComponent({
17806 parent : 'Mypart.xyz', // empty == document.element.!!
17810 disabled : function() {}
17812 tree : function() { // return an tree of xtype declared components
17816 xtype : 'NestedLayoutPanel',
17823 * It can be used to build a big heiracy, with parent etc.
17824 * or you can just use this to render a single compoent to a dom element
17825 * MYPART.render(Roo.Element | String(id) | dom_element )
17832 * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17833 * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17835 * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17837 * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17838 * - if mulitple topModules exist, the last one is defined as the top module.
17842 * When the top level or multiple modules are to embedded into a existing HTML page,
17843 * the parent element can container '#id' of the element where the module will be drawn.
17847 * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17848 * it relies more on a include mechanism, where sub modules are included into an outer page.
17849 * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17851 * Bootstrap Roo Included elements
17853 * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17854 * hence confusing the component builder as it thinks there are multiple top level elements.
17856 * String Over-ride & Translations
17858 * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17859 * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17860 * are needed. @see Roo.XComponent.overlayString
17864 * @extends Roo.util.Observable
17866 * @param cfg {Object} configuration of component
17869 Roo.XComponent = function(cfg) {
17870 Roo.apply(this, cfg);
17874 * Fires when this the componnt is built
17875 * @param {Roo.XComponent} c the component
17880 this.region = this.region || 'center'; // default..
17881 Roo.XComponent.register(this);
17882 this.modules = false;
17883 this.el = false; // where the layout goes..
17887 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17890 * The created element (with Roo.factory())
17891 * @type {Roo.Layout}
17897 * for BC - use el in new code
17898 * @type {Roo.Layout}
17904 * for BC - use el in new code
17905 * @type {Roo.Layout}
17910 * @cfg {Function|boolean} disabled
17911 * If this module is disabled by some rule, return true from the funtion
17916 * @cfg {String} parent
17917 * Name of parent element which it get xtype added to..
17922 * @cfg {String} order
17923 * Used to set the order in which elements are created (usefull for multiple tabs)
17928 * @cfg {String} name
17929 * String to display while loading.
17933 * @cfg {String} region
17934 * Region to render component to (defaults to center)
17939 * @cfg {Array} items
17940 * A single item array - the first element is the root of the tree..
17941 * It's done this way to stay compatible with the Xtype system...
17947 * The method that retuns the tree of parts that make up this compoennt
17954 * render element to dom or tree
17955 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17958 render : function(el)
17962 var hp = this.parent ? 1 : 0;
17963 Roo.debug && Roo.log(this);
17965 var tree = this._tree ? this._tree() : this.tree();
17968 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17969 // if parent is a '#.....' string, then let's use that..
17970 var ename = this.parent.substr(1);
17971 this.parent = false;
17972 Roo.debug && Roo.log(ename);
17974 case 'bootstrap-body':
17975 if (typeof(tree.el) != 'undefined' && tree.el == document.body) {
17976 // this is the BorderLayout standard?
17977 this.parent = { el : true };
17980 if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype) > -1) {
17981 // need to insert stuff...
17983 el : new Roo.bootstrap.layout.Border({
17984 el : document.body,
17990 tabPosition: 'top',
17991 //resizeTabs: true,
17992 alwaysShowTabs: true,
18002 if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18003 this.parent = { el : new Roo.bootstrap.Body() };
18004 Roo.debug && Roo.log("setting el to doc body");
18007 throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18011 this.parent = { el : true};
18014 el = Roo.get(ename);
18015 if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18016 this.parent = { el : true};
18023 if (!el && !this.parent) {
18024 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18029 Roo.debug && Roo.log("EL:");
18030 Roo.debug && Roo.log(el);
18031 Roo.debug && Roo.log("this.parent.el:");
18032 Roo.debug && Roo.log(this.parent.el);
18035 // altertive root elements ??? - we need a better way to indicate these.
18036 var is_alt = Roo.XComponent.is_alt ||
18037 (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18038 (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18039 (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18043 if (!this.parent && is_alt) {
18044 //el = Roo.get(document.body);
18045 this.parent = { el : true };
18050 if (!this.parent) {
18052 Roo.debug && Roo.log("no parent - creating one");
18054 el = el ? Roo.get(el) : false;
18056 if (typeof(Roo.BorderLayout) == 'undefined' ) {
18059 el : new Roo.bootstrap.layout.Border({
18060 el: el || document.body,
18066 tabPosition: 'top',
18067 //resizeTabs: true,
18068 alwaysShowTabs: false,
18071 overflow: 'visible'
18077 // it's a top level one..
18079 el : new Roo.BorderLayout(el || document.body, {
18084 tabPosition: 'top',
18085 //resizeTabs: true,
18086 alwaysShowTabs: el && hp? false : true,
18087 hideTabs: el || !hp ? true : false,
18095 if (!this.parent.el) {
18096 // probably an old style ctor, which has been disabled.
18100 // The 'tree' method is '_tree now'
18102 tree.region = tree.region || this.region;
18103 var is_body = false;
18104 if (this.parent.el === true) {
18105 // bootstrap... - body..
18109 this.parent.el = Roo.factory(tree);
18113 this.el = this.parent.el.addxtype(tree, undefined, is_body);
18114 this.fireEvent('built', this);
18116 this.panel = this.el;
18117 this.layout = this.panel.layout;
18118 this.parentLayout = this.parent.layout || false;
18124 Roo.apply(Roo.XComponent, {
18126 * @property hideProgress
18127 * true to disable the building progress bar.. usefull on single page renders.
18130 hideProgress : false,
18132 * @property buildCompleted
18133 * True when the builder has completed building the interface.
18136 buildCompleted : false,
18139 * @property topModule
18140 * the upper most module - uses document.element as it's constructor.
18147 * @property modules
18148 * array of modules to be created by registration system.
18149 * @type {Array} of Roo.XComponent
18154 * @property elmodules
18155 * array of modules to be created by which use #ID
18156 * @type {Array} of Roo.XComponent
18163 * Is an alternative Root - normally used by bootstrap or other systems,
18164 * where the top element in the tree can wrap 'body'
18165 * @type {boolean} (default false)
18170 * @property build_from_html
18171 * Build elements from html - used by bootstrap HTML stuff
18172 * - this is cleared after build is completed
18173 * @type {boolean} (default false)
18176 build_from_html : false,
18178 * Register components to be built later.
18180 * This solves the following issues
18181 * - Building is not done on page load, but after an authentication process has occured.
18182 * - Interface elements are registered on page load
18183 * - Parent Interface elements may not be loaded before child, so this handles that..
18190 module : 'Pman.Tab.projectMgr',
18192 parent : 'Pman.layout',
18193 disabled : false, // or use a function..
18196 * * @param {Object} details about module
18198 register : function(obj) {
18200 Roo.XComponent.event.fireEvent('register', obj);
18201 switch(typeof(obj.disabled) ) {
18207 if ( obj.disabled() ) {
18213 if (obj.disabled || obj.region == '#disabled') {
18219 this.modules.push(obj);
18223 * convert a string to an object..
18224 * eg. 'AAA.BBB' -> finds AAA.BBB
18228 toObject : function(str)
18230 if (!str || typeof(str) == 'object') {
18233 if (str.substring(0,1) == '#') {
18237 var ar = str.split('.');
18242 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18244 throw "Module not found : " + str;
18248 throw "Module not found : " + str;
18250 Roo.each(ar, function(e) {
18251 if (typeof(o[e]) == 'undefined') {
18252 throw "Module not found : " + str;
18263 * move modules into their correct place in the tree..
18266 preBuild : function ()
18269 Roo.each(this.modules , function (obj)
18271 Roo.XComponent.event.fireEvent('beforebuild', obj);
18273 var opar = obj.parent;
18275 obj.parent = this.toObject(opar);
18277 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18282 Roo.debug && Roo.log("GOT top level module");
18283 Roo.debug && Roo.log(obj);
18284 obj.modules = new Roo.util.MixedCollection(false,
18285 function(o) { return o.order + '' }
18287 this.topModule = obj;
18290 // parent is a string (usually a dom element name..)
18291 if (typeof(obj.parent) == 'string') {
18292 this.elmodules.push(obj);
18295 if (obj.parent.constructor != Roo.XComponent) {
18296 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18298 if (!obj.parent.modules) {
18299 obj.parent.modules = new Roo.util.MixedCollection(false,
18300 function(o) { return o.order + '' }
18303 if (obj.parent.disabled) {
18304 obj.disabled = true;
18306 obj.parent.modules.add(obj);
18311 * make a list of modules to build.
18312 * @return {Array} list of modules.
18315 buildOrder : function()
18318 var cmp = function(a,b) {
18319 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18321 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18322 throw "No top level modules to build";
18325 // make a flat list in order of modules to build.
18326 var mods = this.topModule ? [ this.topModule ] : [];
18329 // elmodules (is a list of DOM based modules )
18330 Roo.each(this.elmodules, function(e) {
18332 if (!this.topModule &&
18333 typeof(e.parent) == 'string' &&
18334 e.parent.substring(0,1) == '#' &&
18335 Roo.get(e.parent.substr(1))
18338 _this.topModule = e;
18344 // add modules to their parents..
18345 var addMod = function(m) {
18346 Roo.debug && Roo.log("build Order: add: " + m.name);
18349 if (m.modules && !m.disabled) {
18350 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18351 m.modules.keySort('ASC', cmp );
18352 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18354 m.modules.each(addMod);
18356 Roo.debug && Roo.log("build Order: no child modules");
18358 // not sure if this is used any more..
18360 m.finalize.name = m.name + " (clean up) ";
18361 mods.push(m.finalize);
18365 if (this.topModule && this.topModule.modules) {
18366 this.topModule.modules.keySort('ASC', cmp );
18367 this.topModule.modules.each(addMod);
18373 * Build the registered modules.
18374 * @param {Object} parent element.
18375 * @param {Function} optional method to call after module has been added.
18379 build : function(opts)
18382 if (typeof(opts) != 'undefined') {
18383 Roo.apply(this,opts);
18387 var mods = this.buildOrder();
18389 //this.allmods = mods;
18390 //Roo.debug && Roo.log(mods);
18392 if (!mods.length) { // should not happen
18393 throw "NO modules!!!";
18397 var msg = "Building Interface...";
18398 // flash it up as modal - so we store the mask!?
18399 if (!this.hideProgress && Roo.MessageBox) {
18400 Roo.MessageBox.show({ title: 'loading' });
18401 Roo.MessageBox.show({
18402 title: "Please wait...",
18412 var total = mods.length;
18415 var progressRun = function() {
18416 if (!mods.length) {
18417 Roo.debug && Roo.log('hide?');
18418 if (!this.hideProgress && Roo.MessageBox) {
18419 Roo.MessageBox.hide();
18421 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18423 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18429 var m = mods.shift();
18432 Roo.debug && Roo.log(m);
18433 // not sure if this is supported any more.. - modules that are are just function
18434 if (typeof(m) == 'function') {
18436 return progressRun.defer(10, _this);
18440 msg = "Building Interface " + (total - mods.length) +
18442 (m.name ? (' - ' + m.name) : '');
18443 Roo.debug && Roo.log(msg);
18444 if (!_this.hideProgress && Roo.MessageBox) {
18445 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
18449 // is the module disabled?
18450 var disabled = (typeof(m.disabled) == 'function') ?
18451 m.disabled.call(m.module.disabled) : m.disabled;
18455 return progressRun(); // we do not update the display!
18463 // it's 10 on top level, and 1 on others??? why...
18464 return progressRun.defer(10, _this);
18467 progressRun.defer(1, _this);
18473 * Overlay a set of modified strings onto a component
18474 * This is dependant on our builder exporting the strings and 'named strings' elements.
18476 * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18477 * @param {Object} associative array of 'named' string and it's new value.
18480 overlayStrings : function( component, strings )
18482 if (typeof(component['_named_strings']) == 'undefined') {
18483 throw "ERROR: component does not have _named_strings";
18485 for ( var k in strings ) {
18486 var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18487 if (md !== false) {
18488 component['_strings'][md] = strings[k];
18490 Roo.log('could not find named string: ' + k + ' in');
18491 Roo.log(component);
18506 * wrapper for event.on - aliased later..
18507 * Typically use to register a event handler for register:
18509 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18518 Roo.XComponent.event = new Roo.util.Observable({
18522 * Fires when an Component is registered,
18523 * set the disable property on the Component to stop registration.
18524 * @param {Roo.XComponent} c the component being registerd.
18529 * @event beforebuild
18530 * Fires before each Component is built
18531 * can be used to apply permissions.
18532 * @param {Roo.XComponent} c the component being registerd.
18535 'beforebuild' : true,
18537 * @event buildcomplete
18538 * Fires on the top level element when all elements have been built
18539 * @param {Roo.XComponent} the top level component.
18541 'buildcomplete' : true
18546 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
18549 * marked - a markdown parser
18550 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18551 * https://github.com/chjj/marked
18557 * Roo.Markdown - is a very crude wrapper around marked..
18561 * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18563 * Note: move the sample code to the bottom of this
18564 * file before uncommenting it.
18569 Roo.Markdown.toHtml = function(text) {
18571 var c = new Roo.Markdown.marked.setOptions({
18572 renderer: new Roo.Markdown.marked.Renderer(),
18583 text = text.replace(/\\\n/g,' ');
18584 return Roo.Markdown.marked(text);
18589 // Wraps all "globals" so that the only thing
18590 // exposed is makeHtml().
18596 * eval:var:unescape
18604 var escape = function (html, encode) {
18606 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18607 .replace(/</g, '<')
18608 .replace(/>/g, '>')
18609 .replace(/"/g, '"')
18610 .replace(/'/g, ''');
18613 var unescape = function (html) {
18614 // explicitly match decimal, hex, and named HTML entities
18615 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18616 n = n.toLowerCase();
18617 if (n === 'colon') { return ':'; }
18618 if (n.charAt(0) === '#') {
18619 return n.charAt(1) === 'x'
18620 ? String.fromCharCode(parseInt(n.substring(2), 16))
18621 : String.fromCharCode(+n.substring(1));
18627 var replace = function (regex, opt) {
18628 regex = regex.source;
18630 return function self(name, val) {
18631 if (!name) { return new RegExp(regex, opt); }
18632 val = val.source || val;
18633 val = val.replace(/(^|[^\[])\^/g, '$1');
18634 regex = regex.replace(name, val);
18643 var noop = function () {}
18649 var merge = function (obj) {
18654 for (; i < arguments.length; i++) {
18655 target = arguments[i];
18656 for (key in target) {
18657 if (Object.prototype.hasOwnProperty.call(target, key)) {
18658 obj[key] = target[key];
18668 * Block-Level Grammar
18676 code: /^( {4}[^\n]+\n*)+/,
18678 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18679 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18681 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18682 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18683 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18684 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18685 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18687 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18691 block.bullet = /(?:[*+-]|\d+\.)/;
18692 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18693 block.item = replace(block.item, 'gm')
18694 (/bull/g, block.bullet)
18697 block.list = replace(block.list)
18698 (/bull/g, block.bullet)
18699 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18700 ('def', '\\n+(?=' + block.def.source + ')')
18703 block.blockquote = replace(block.blockquote)
18707 block._tag = '(?!(?:'
18708 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18709 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18710 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18712 block.html = replace(block.html)
18713 ('comment', /<!--[\s\S]*?-->/)
18714 ('closed', /<(tag)[\s\S]+?<\/\1>/)
18715 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18716 (/tag/g, block._tag)
18719 block.paragraph = replace(block.paragraph)
18721 ('heading', block.heading)
18722 ('lheading', block.lheading)
18723 ('blockquote', block.blockquote)
18724 ('tag', '<' + block._tag)
18729 * Normal Block Grammar
18732 block.normal = merge({}, block);
18735 * GFM Block Grammar
18738 block.gfm = merge({}, block.normal, {
18739 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18741 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18744 block.gfm.paragraph = replace(block.paragraph)
18746 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18747 + block.list.source.replace('\\1', '\\3') + '|')
18751 * GFM + Tables Block Grammar
18754 block.tables = merge({}, block.gfm, {
18755 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18756 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18763 var Lexer = function (options) {
18765 this.tokens.links = {};
18766 this.options = options || marked.defaults;
18767 this.rules = block.normal;
18769 if (this.options.gfm) {
18770 if (this.options.tables) {
18771 this.rules = block.tables;
18773 this.rules = block.gfm;
18779 * Expose Block Rules
18782 Lexer.rules = block;
18785 * Static Lex Method
18788 Lexer.lex = function(src, options) {
18789 var lexer = new Lexer(options);
18790 return lexer.lex(src);
18797 Lexer.prototype.lex = function(src) {
18799 .replace(/\r\n|\r/g, '\n')
18800 .replace(/\t/g, ' ')
18801 .replace(/\u00a0/g, ' ')
18802 .replace(/\u2424/g, '\n');
18804 return this.token(src, true);
18811 Lexer.prototype.token = function(src, top, bq) {
18812 var src = src.replace(/^ +$/gm, '')
18825 if (cap = this.rules.newline.exec(src)) {
18826 src = src.substring(cap[0].length);
18827 if (cap[0].length > 1) {
18835 if (cap = this.rules.code.exec(src)) {
18836 src = src.substring(cap[0].length);
18837 cap = cap[0].replace(/^ {4}/gm, '');
18840 text: !this.options.pedantic
18841 ? cap.replace(/\n+$/, '')
18848 if (cap = this.rules.fences.exec(src)) {
18849 src = src.substring(cap[0].length);
18859 if (cap = this.rules.heading.exec(src)) {
18860 src = src.substring(cap[0].length);
18863 depth: cap[1].length,
18869 // table no leading pipe (gfm)
18870 if (top && (cap = this.rules.nptable.exec(src))) {
18871 src = src.substring(cap[0].length);
18875 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18876 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18877 cells: cap[3].replace(/\n$/, '').split('\n')
18880 for (i = 0; i < item.align.length; i++) {
18881 if (/^ *-+: *$/.test(item.align[i])) {
18882 item.align[i] = 'right';
18883 } else if (/^ *:-+: *$/.test(item.align[i])) {
18884 item.align[i] = 'center';
18885 } else if (/^ *:-+ *$/.test(item.align[i])) {
18886 item.align[i] = 'left';
18888 item.align[i] = null;
18892 for (i = 0; i < item.cells.length; i++) {
18893 item.cells[i] = item.cells[i].split(/ *\| */);
18896 this.tokens.push(item);
18902 if (cap = this.rules.lheading.exec(src)) {
18903 src = src.substring(cap[0].length);
18906 depth: cap[2] === '=' ? 1 : 2,
18913 if (cap = this.rules.hr.exec(src)) {
18914 src = src.substring(cap[0].length);
18922 if (cap = this.rules.blockquote.exec(src)) {
18923 src = src.substring(cap[0].length);
18926 type: 'blockquote_start'
18929 cap = cap[0].replace(/^ *> ?/gm, '');
18931 // Pass `top` to keep the current
18932 // "toplevel" state. This is exactly
18933 // how markdown.pl works.
18934 this.token(cap, top, true);
18937 type: 'blockquote_end'
18944 if (cap = this.rules.list.exec(src)) {
18945 src = src.substring(cap[0].length);
18949 type: 'list_start',
18950 ordered: bull.length > 1
18953 // Get each top-level item.
18954 cap = cap[0].match(this.rules.item);
18960 for (; i < l; i++) {
18963 // Remove the list item's bullet
18964 // so it is seen as the next token.
18965 space = item.length;
18966 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18968 // Outdent whatever the
18969 // list item contains. Hacky.
18970 if (~item.indexOf('\n ')) {
18971 space -= item.length;
18972 item = !this.options.pedantic
18973 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18974 : item.replace(/^ {1,4}/gm, '');
18977 // Determine whether the next list item belongs here.
18978 // Backpedal if it does not belong in this list.
18979 if (this.options.smartLists && i !== l - 1) {
18980 b = block.bullet.exec(cap[i + 1])[0];
18981 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18982 src = cap.slice(i + 1).join('\n') + src;
18987 // Determine whether item is loose or not.
18988 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18989 // for discount behavior.
18990 loose = next || /\n\n(?!\s*$)/.test(item);
18992 next = item.charAt(item.length - 1) === '\n';
18993 if (!loose) { loose = next; }
18998 ? 'loose_item_start'
18999 : 'list_item_start'
19003 this.token(item, false, bq);
19006 type: 'list_item_end'
19018 if (cap = this.rules.html.exec(src)) {
19019 src = src.substring(cap[0].length);
19021 type: this.options.sanitize
19024 pre: !this.options.sanitizer
19025 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19032 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19033 src = src.substring(cap[0].length);
19034 this.tokens.links[cap[1].toLowerCase()] = {
19042 if (top && (cap = this.rules.table.exec(src))) {
19043 src = src.substring(cap[0].length);
19047 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19048 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19049 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19052 for (i = 0; i < item.align.length; i++) {
19053 if (/^ *-+: *$/.test(item.align[i])) {
19054 item.align[i] = 'right';
19055 } else if (/^ *:-+: *$/.test(item.align[i])) {
19056 item.align[i] = 'center';
19057 } else if (/^ *:-+ *$/.test(item.align[i])) {
19058 item.align[i] = 'left';
19060 item.align[i] = null;
19064 for (i = 0; i < item.cells.length; i++) {
19065 item.cells[i] = item.cells[i]
19066 .replace(/^ *\| *| *\| *$/g, '')
19070 this.tokens.push(item);
19075 // top-level paragraph
19076 if (top && (cap = this.rules.paragraph.exec(src))) {
19077 src = src.substring(cap[0].length);
19080 text: cap[1].charAt(cap[1].length - 1) === '\n'
19081 ? cap[1].slice(0, -1)
19088 if (cap = this.rules.text.exec(src)) {
19089 // Top-level should never reach here.
19090 src = src.substring(cap[0].length);
19100 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19104 return this.tokens;
19108 * Inline-Level Grammar
19112 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19113 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19115 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19116 link: /^!?\[(inside)\]\(href\)/,
19117 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19118 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19119 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19120 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19121 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19122 br: /^ {2,}\n(?!\s*$)/,
19124 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19127 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19128 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19130 inline.link = replace(inline.link)
19131 ('inside', inline._inside)
19132 ('href', inline._href)
19135 inline.reflink = replace(inline.reflink)
19136 ('inside', inline._inside)
19140 * Normal Inline Grammar
19143 inline.normal = merge({}, inline);
19146 * Pedantic Inline Grammar
19149 inline.pedantic = merge({}, inline.normal, {
19150 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19151 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19155 * GFM Inline Grammar
19158 inline.gfm = merge({}, inline.normal, {
19159 escape: replace(inline.escape)('])', '~|])')(),
19160 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19161 del: /^~~(?=\S)([\s\S]*?\S)~~/,
19162 text: replace(inline.text)
19164 ('|', '|https?://|')
19169 * GFM + Line Breaks Inline Grammar
19172 inline.breaks = merge({}, inline.gfm, {
19173 br: replace(inline.br)('{2,}', '*')(),
19174 text: replace(inline.gfm.text)('{2,}', '*')()
19178 * Inline Lexer & Compiler
19181 var InlineLexer = function (links, options) {
19182 this.options = options || marked.defaults;
19183 this.links = links;
19184 this.rules = inline.normal;
19185 this.renderer = this.options.renderer || new Renderer;
19186 this.renderer.options = this.options;
19190 Error('Tokens array requires a `links` property.');
19193 if (this.options.gfm) {
19194 if (this.options.breaks) {
19195 this.rules = inline.breaks;
19197 this.rules = inline.gfm;
19199 } else if (this.options.pedantic) {
19200 this.rules = inline.pedantic;
19205 * Expose Inline Rules
19208 InlineLexer.rules = inline;
19211 * Static Lexing/Compiling Method
19214 InlineLexer.output = function(src, links, options) {
19215 var inline = new InlineLexer(links, options);
19216 return inline.output(src);
19223 InlineLexer.prototype.output = function(src) {
19232 if (cap = this.rules.escape.exec(src)) {
19233 src = src.substring(cap[0].length);
19239 if (cap = this.rules.autolink.exec(src)) {
19240 src = src.substring(cap[0].length);
19241 if (cap[2] === '@') {
19242 text = cap[1].charAt(6) === ':'
19243 ? this.mangle(cap[1].substring(7))
19244 : this.mangle(cap[1]);
19245 href = this.mangle('mailto:') + text;
19247 text = escape(cap[1]);
19250 out += this.renderer.link(href, null, text);
19255 if (!this.inLink && (cap = this.rules.url.exec(src))) {
19256 src = src.substring(cap[0].length);
19257 text = escape(cap[1]);
19259 out += this.renderer.link(href, null, text);
19264 if (cap = this.rules.tag.exec(src)) {
19265 if (!this.inLink && /^<a /i.test(cap[0])) {
19266 this.inLink = true;
19267 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19268 this.inLink = false;
19270 src = src.substring(cap[0].length);
19271 out += this.options.sanitize
19272 ? this.options.sanitizer
19273 ? this.options.sanitizer(cap[0])
19280 if (cap = this.rules.link.exec(src)) {
19281 src = src.substring(cap[0].length);
19282 this.inLink = true;
19283 out += this.outputLink(cap, {
19287 this.inLink = false;
19292 if ((cap = this.rules.reflink.exec(src))
19293 || (cap = this.rules.nolink.exec(src))) {
19294 src = src.substring(cap[0].length);
19295 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19296 link = this.links[link.toLowerCase()];
19297 if (!link || !link.href) {
19298 out += cap[0].charAt(0);
19299 src = cap[0].substring(1) + src;
19302 this.inLink = true;
19303 out += this.outputLink(cap, link);
19304 this.inLink = false;
19309 if (cap = this.rules.strong.exec(src)) {
19310 src = src.substring(cap[0].length);
19311 out += this.renderer.strong(this.output(cap[2] || cap[1]));
19316 if (cap = this.rules.em.exec(src)) {
19317 src = src.substring(cap[0].length);
19318 out += this.renderer.em(this.output(cap[2] || cap[1]));
19323 if (cap = this.rules.code.exec(src)) {
19324 src = src.substring(cap[0].length);
19325 out += this.renderer.codespan(escape(cap[2], true));
19330 if (cap = this.rules.br.exec(src)) {
19331 src = src.substring(cap[0].length);
19332 out += this.renderer.br();
19337 if (cap = this.rules.del.exec(src)) {
19338 src = src.substring(cap[0].length);
19339 out += this.renderer.del(this.output(cap[1]));
19344 if (cap = this.rules.text.exec(src)) {
19345 src = src.substring(cap[0].length);
19346 out += this.renderer.text(escape(this.smartypants(cap[0])));
19352 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19363 InlineLexer.prototype.outputLink = function(cap, link) {
19364 var href = escape(link.href)
19365 , title = link.title ? escape(link.title) : null;
19367 return cap[0].charAt(0) !== '!'
19368 ? this.renderer.link(href, title, this.output(cap[1]))
19369 : this.renderer.image(href, title, escape(cap[1]));
19373 * Smartypants Transformations
19376 InlineLexer.prototype.smartypants = function(text) {
19377 if (!this.options.smartypants) { return text; }
19380 .replace(/---/g, '\u2014')
19382 .replace(/--/g, '\u2013')
19384 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19385 // closing singles & apostrophes
19386 .replace(/'/g, '\u2019')
19388 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19390 .replace(/"/g, '\u201d')
19392 .replace(/\.{3}/g, '\u2026');
19399 InlineLexer.prototype.mangle = function(text) {
19400 if (!this.options.mangle) { return text; }
19406 for (; i < l; i++) {
19407 ch = text.charCodeAt(i);
19408 if (Math.random() > 0.5) {
19409 ch = 'x' + ch.toString(16);
19411 out += '&#' + ch + ';';
19422 * eval:var:Renderer
19425 var Renderer = function (options) {
19426 this.options = options || {};
19429 Renderer.prototype.code = function(code, lang, escaped) {
19430 if (this.options.highlight) {
19431 var out = this.options.highlight(code, lang);
19432 if (out != null && out !== code) {
19437 // hack!!! - it's already escapeD?
19442 return '<pre><code>'
19443 + (escaped ? code : escape(code, true))
19444 + '\n</code></pre>';
19447 return '<pre><code class="'
19448 + this.options.langPrefix
19449 + escape(lang, true)
19451 + (escaped ? code : escape(code, true))
19452 + '\n</code></pre>\n';
19455 Renderer.prototype.blockquote = function(quote) {
19456 return '<blockquote>\n' + quote + '</blockquote>\n';
19459 Renderer.prototype.html = function(html) {
19463 Renderer.prototype.heading = function(text, level, raw) {
19467 + this.options.headerPrefix
19468 + raw.toLowerCase().replace(/[^\w]+/g, '-')
19476 Renderer.prototype.hr = function() {
19477 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19480 Renderer.prototype.list = function(body, ordered) {
19481 var type = ordered ? 'ol' : 'ul';
19482 return '<' + type + '>\n' + body + '</' + type + '>\n';
19485 Renderer.prototype.listitem = function(text) {
19486 return '<li>' + text + '</li>\n';
19489 Renderer.prototype.paragraph = function(text) {
19490 return '<p>' + text + '</p>\n';
19493 Renderer.prototype.table = function(header, body) {
19494 return '<table class="table table-striped">\n'
19504 Renderer.prototype.tablerow = function(content) {
19505 return '<tr>\n' + content + '</tr>\n';
19508 Renderer.prototype.tablecell = function(content, flags) {
19509 var type = flags.header ? 'th' : 'td';
19510 var tag = flags.align
19511 ? '<' + type + ' style="text-align:' + flags.align + '">'
19512 : '<' + type + '>';
19513 return tag + content + '</' + type + '>\n';
19516 // span level renderer
19517 Renderer.prototype.strong = function(text) {
19518 return '<strong>' + text + '</strong>';
19521 Renderer.prototype.em = function(text) {
19522 return '<em>' + text + '</em>';
19525 Renderer.prototype.codespan = function(text) {
19526 return '<code>' + text + '</code>';
19529 Renderer.prototype.br = function() {
19530 return this.options.xhtml ? '<br/>' : '<br>';
19533 Renderer.prototype.del = function(text) {
19534 return '<del>' + text + '</del>';
19537 Renderer.prototype.link = function(href, title, text) {
19538 if (this.options.sanitize) {
19540 var prot = decodeURIComponent(unescape(href))
19541 .replace(/[^\w:]/g, '')
19546 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19550 var out = '<a href="' + href + '"';
19552 out += ' title="' + title + '"';
19554 out += '>' + text + '</a>';
19558 Renderer.prototype.image = function(href, title, text) {
19559 var out = '<img src="' + href + '" alt="' + text + '"';
19561 out += ' title="' + title + '"';
19563 out += this.options.xhtml ? '/>' : '>';
19567 Renderer.prototype.text = function(text) {
19572 * Parsing & Compiling
19578 var Parser= function (options) {
19581 this.options = options || marked.defaults;
19582 this.options.renderer = this.options.renderer || new Renderer;
19583 this.renderer = this.options.renderer;
19584 this.renderer.options = this.options;
19588 * Static Parse Method
19591 Parser.parse = function(src, options, renderer) {
19592 var parser = new Parser(options, renderer);
19593 return parser.parse(src);
19600 Parser.prototype.parse = function(src) {
19601 this.inline = new InlineLexer(src.links, this.options, this.renderer);
19602 this.tokens = src.reverse();
19605 while (this.next()) {
19616 Parser.prototype.next = function() {
19617 return this.token = this.tokens.pop();
19621 * Preview Next Token
19624 Parser.prototype.peek = function() {
19625 return this.tokens[this.tokens.length - 1] || 0;
19629 * Parse Text Tokens
19632 Parser.prototype.parseText = function() {
19633 var body = this.token.text;
19635 while (this.peek().type === 'text') {
19636 body += '\n' + this.next().text;
19639 return this.inline.output(body);
19643 * Parse Current Token
19646 Parser.prototype.tok = function() {
19647 switch (this.token.type) {
19652 return this.renderer.hr();
19655 return this.renderer.heading(
19656 this.inline.output(this.token.text),
19661 return this.renderer.code(this.token.text,
19663 this.token.escaped);
19676 for (i = 0; i < this.token.header.length; i++) {
19677 flags = { header: true, align: this.token.align[i] };
19678 cell += this.renderer.tablecell(
19679 this.inline.output(this.token.header[i]),
19680 { header: true, align: this.token.align[i] }
19683 header += this.renderer.tablerow(cell);
19685 for (i = 0; i < this.token.cells.length; i++) {
19686 row = this.token.cells[i];
19689 for (j = 0; j < row.length; j++) {
19690 cell += this.renderer.tablecell(
19691 this.inline.output(row[j]),
19692 { header: false, align: this.token.align[j] }
19696 body += this.renderer.tablerow(cell);
19698 return this.renderer.table(header, body);
19700 case 'blockquote_start': {
19703 while (this.next().type !== 'blockquote_end') {
19704 body += this.tok();
19707 return this.renderer.blockquote(body);
19709 case 'list_start': {
19711 , ordered = this.token.ordered;
19713 while (this.next().type !== 'list_end') {
19714 body += this.tok();
19717 return this.renderer.list(body, ordered);
19719 case 'list_item_start': {
19722 while (this.next().type !== 'list_item_end') {
19723 body += this.token.type === 'text'
19728 return this.renderer.listitem(body);
19730 case 'loose_item_start': {
19733 while (this.next().type !== 'list_item_end') {
19734 body += this.tok();
19737 return this.renderer.listitem(body);
19740 var html = !this.token.pre && !this.options.pedantic
19741 ? this.inline.output(this.token.text)
19743 return this.renderer.html(html);
19745 case 'paragraph': {
19746 return this.renderer.paragraph(this.inline.output(this.token.text));
19749 return this.renderer.paragraph(this.parseText());
19761 var marked = function (src, opt, callback) {
19762 if (callback || typeof opt === 'function') {
19768 opt = merge({}, marked.defaults, opt || {});
19770 var highlight = opt.highlight
19776 tokens = Lexer.lex(src, opt)
19778 return callback(e);
19781 pending = tokens.length;
19785 var done = function(err) {
19787 opt.highlight = highlight;
19788 return callback(err);
19794 out = Parser.parse(tokens, opt);
19799 opt.highlight = highlight;
19803 : callback(null, out);
19806 if (!highlight || highlight.length < 3) {
19810 delete opt.highlight;
19812 if (!pending) { return done(); }
19814 for (; i < tokens.length; i++) {
19816 if (token.type !== 'code') {
19817 return --pending || done();
19819 return highlight(token.text, token.lang, function(err, code) {
19820 if (err) { return done(err); }
19821 if (code == null || code === token.text) {
19822 return --pending || done();
19825 token.escaped = true;
19826 --pending || done();
19834 if (opt) { opt = merge({}, marked.defaults, opt); }
19835 return Parser.parse(Lexer.lex(src, opt), opt);
19837 e.message += '\nPlease report this to https://github.com/chjj/marked.';
19838 if ((opt || marked.defaults).silent) {
19839 return '<p>An error occured:</p><pre>'
19840 + escape(e.message + '', true)
19852 marked.setOptions = function(opt) {
19853 merge(marked.defaults, opt);
19857 marked.defaults = {
19868 langPrefix: 'lang-',
19869 smartypants: false,
19871 renderer: new Renderer,
19879 marked.Parser = Parser;
19880 marked.parser = Parser.parse;
19882 marked.Renderer = Renderer;
19884 marked.Lexer = Lexer;
19885 marked.lexer = Lexer.lex;
19887 marked.InlineLexer = InlineLexer;
19888 marked.inlineLexer = InlineLexer.output;
19890 marked.parse = marked;
19892 Roo.Markdown.marked = marked;
19896 * Ext JS Library 1.1.1
19897 * Copyright(c) 2006-2007, Ext JS, LLC.
19899 * Originally Released Under LGPL - original licence link has changed is not relivant.
19902 * <script type="text/javascript">
19908 * These classes are derivatives of the similarly named classes in the YUI Library.
19909 * The original license:
19910 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19911 * Code licensed under the BSD License:
19912 * http://developer.yahoo.net/yui/license.txt
19917 var Event=Roo.EventManager;
19918 var Dom=Roo.lib.Dom;
19921 * @class Roo.dd.DragDrop
19922 * @extends Roo.util.Observable
19923 * Defines the interface and base operation of items that that can be
19924 * dragged or can be drop targets. It was designed to be extended, overriding
19925 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19926 * Up to three html elements can be associated with a DragDrop instance:
19928 * <li>linked element: the element that is passed into the constructor.
19929 * This is the element which defines the boundaries for interaction with
19930 * other DragDrop objects.</li>
19931 * <li>handle element(s): The drag operation only occurs if the element that
19932 * was clicked matches a handle element. By default this is the linked
19933 * element, but there are times that you will want only a portion of the
19934 * linked element to initiate the drag operation, and the setHandleElId()
19935 * method provides a way to define this.</li>
19936 * <li>drag element: this represents the element that would be moved along
19937 * with the cursor during a drag operation. By default, this is the linked
19938 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
19939 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19942 * This class should not be instantiated until the onload event to ensure that
19943 * the associated elements are available.
19944 * The following would define a DragDrop obj that would interact with any
19945 * other DragDrop obj in the "group1" group:
19947 * dd = new Roo.dd.DragDrop("div1", "group1");
19949 * Since none of the event handlers have been implemented, nothing would
19950 * actually happen if you were to run the code above. Normally you would
19951 * override this class or one of the default implementations, but you can
19952 * also override the methods you want on an instance of the class...
19954 * dd.onDragDrop = function(e, id) {
19955 * alert("dd was dropped on " + id);
19959 * @param {String} id of the element that is linked to this instance
19960 * @param {String} sGroup the group of related DragDrop objects
19961 * @param {object} config an object containing configurable attributes
19962 * Valid properties for DragDrop:
19963 * padding, isTarget, maintainOffset, primaryButtonOnly
19965 Roo.dd.DragDrop = function(id, sGroup, config) {
19967 this.init(id, sGroup, config);
19972 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19975 * The id of the element associated with this object. This is what we
19976 * refer to as the "linked element" because the size and position of
19977 * this element is used to determine when the drag and drop objects have
19985 * Configuration attributes passed into the constructor
19992 * The id of the element that will be dragged. By default this is same
19993 * as the linked element , but could be changed to another element. Ex:
19995 * @property dragElId
20002 * the id of the element that initiates the drag operation. By default
20003 * this is the linked element, but could be changed to be a child of this
20004 * element. This lets us do things like only starting the drag when the
20005 * header element within the linked html element is clicked.
20006 * @property handleElId
20013 * An associative array of HTML tags that will be ignored if clicked.
20014 * @property invalidHandleTypes
20015 * @type {string: string}
20017 invalidHandleTypes: null,
20020 * An associative array of ids for elements that will be ignored if clicked
20021 * @property invalidHandleIds
20022 * @type {string: string}
20024 invalidHandleIds: null,
20027 * An indexted array of css class names for elements that will be ignored
20029 * @property invalidHandleClasses
20032 invalidHandleClasses: null,
20035 * The linked element's absolute X position at the time the drag was
20037 * @property startPageX
20044 * The linked element's absolute X position at the time the drag was
20046 * @property startPageY
20053 * The group defines a logical collection of DragDrop objects that are
20054 * related. Instances only get events when interacting with other
20055 * DragDrop object in the same group. This lets us define multiple
20056 * groups using a single DragDrop subclass if we want.
20058 * @type {string: string}
20063 * Individual drag/drop instances can be locked. This will prevent
20064 * onmousedown start drag.
20072 * Lock this instance
20075 lock: function() { this.locked = true; },
20078 * Unlock this instace
20081 unlock: function() { this.locked = false; },
20084 * By default, all insances can be a drop target. This can be disabled by
20085 * setting isTarget to false.
20092 * The padding configured for this drag and drop object for calculating
20093 * the drop zone intersection with this object.
20100 * Cached reference to the linked element
20101 * @property _domRef
20107 * Internal typeof flag
20108 * @property __ygDragDrop
20111 __ygDragDrop: true,
20114 * Set to true when horizontal contraints are applied
20115 * @property constrainX
20122 * Set to true when vertical contraints are applied
20123 * @property constrainY
20130 * The left constraint
20138 * The right constraint
20146 * The up constraint
20155 * The down constraint
20163 * Maintain offsets when we resetconstraints. Set to true when you want
20164 * the position of the element relative to its parent to stay the same
20165 * when the page changes
20167 * @property maintainOffset
20170 maintainOffset: false,
20173 * Array of pixel locations the element will snap to if we specified a
20174 * horizontal graduation/interval. This array is generated automatically
20175 * when you define a tick interval.
20182 * Array of pixel locations the element will snap to if we specified a
20183 * vertical graduation/interval. This array is generated automatically
20184 * when you define a tick interval.
20191 * By default the drag and drop instance will only respond to the primary
20192 * button click (left button for a right-handed mouse). Set to true to
20193 * allow drag and drop to start with any mouse click that is propogated
20195 * @property primaryButtonOnly
20198 primaryButtonOnly: true,
20201 * The availabe property is false until the linked dom element is accessible.
20202 * @property available
20208 * By default, drags can only be initiated if the mousedown occurs in the
20209 * region the linked element is. This is done in part to work around a
20210 * bug in some browsers that mis-report the mousedown if the previous
20211 * mouseup happened outside of the window. This property is set to true
20212 * if outer handles are defined.
20214 * @property hasOuterHandles
20218 hasOuterHandles: false,
20221 * Code that executes immediately before the startDrag event
20222 * @method b4StartDrag
20225 b4StartDrag: function(x, y) { },
20228 * Abstract method called after a drag/drop object is clicked
20229 * and the drag or mousedown time thresholds have beeen met.
20230 * @method startDrag
20231 * @param {int} X click location
20232 * @param {int} Y click location
20234 startDrag: function(x, y) { /* override this */ },
20237 * Code that executes immediately before the onDrag event
20241 b4Drag: function(e) { },
20244 * Abstract method called during the onMouseMove event while dragging an
20247 * @param {Event} e the mousemove event
20249 onDrag: function(e) { /* override this */ },
20252 * Abstract method called when this element fist begins hovering over
20253 * another DragDrop obj
20254 * @method onDragEnter
20255 * @param {Event} e the mousemove event
20256 * @param {String|DragDrop[]} id In POINT mode, the element
20257 * id this is hovering over. In INTERSECT mode, an array of one or more
20258 * dragdrop items being hovered over.
20260 onDragEnter: function(e, id) { /* override this */ },
20263 * Code that executes immediately before the onDragOver event
20264 * @method b4DragOver
20267 b4DragOver: function(e) { },
20270 * Abstract method called when this element is hovering over another
20272 * @method onDragOver
20273 * @param {Event} e the mousemove event
20274 * @param {String|DragDrop[]} id In POINT mode, the element
20275 * id this is hovering over. In INTERSECT mode, an array of dd items
20276 * being hovered over.
20278 onDragOver: function(e, id) { /* override this */ },
20281 * Code that executes immediately before the onDragOut event
20282 * @method b4DragOut
20285 b4DragOut: function(e) { },
20288 * Abstract method called when we are no longer hovering over an element
20289 * @method onDragOut
20290 * @param {Event} e the mousemove event
20291 * @param {String|DragDrop[]} id In POINT mode, the element
20292 * id this was hovering over. In INTERSECT mode, an array of dd items
20293 * that the mouse is no longer over.
20295 onDragOut: function(e, id) { /* override this */ },
20298 * Code that executes immediately before the onDragDrop event
20299 * @method b4DragDrop
20302 b4DragDrop: function(e) { },
20305 * Abstract method called when this item is dropped on another DragDrop
20307 * @method onDragDrop
20308 * @param {Event} e the mouseup event
20309 * @param {String|DragDrop[]} id In POINT mode, the element
20310 * id this was dropped on. In INTERSECT mode, an array of dd items this
20313 onDragDrop: function(e, id) { /* override this */ },
20316 * Abstract method called when this item is dropped on an area with no
20318 * @method onInvalidDrop
20319 * @param {Event} e the mouseup event
20321 onInvalidDrop: function(e) { /* override this */ },
20324 * Code that executes immediately before the endDrag event
20325 * @method b4EndDrag
20328 b4EndDrag: function(e) { },
20331 * Fired when we are done dragging the object
20333 * @param {Event} e the mouseup event
20335 endDrag: function(e) { /* override this */ },
20338 * Code executed immediately before the onMouseDown event
20339 * @method b4MouseDown
20340 * @param {Event} e the mousedown event
20343 b4MouseDown: function(e) { },
20346 * Event handler that fires when a drag/drop obj gets a mousedown
20347 * @method onMouseDown
20348 * @param {Event} e the mousedown event
20350 onMouseDown: function(e) { /* override this */ },
20353 * Event handler that fires when a drag/drop obj gets a mouseup
20354 * @method onMouseUp
20355 * @param {Event} e the mouseup event
20357 onMouseUp: function(e) { /* override this */ },
20360 * Override the onAvailable method to do what is needed after the initial
20361 * position was determined.
20362 * @method onAvailable
20364 onAvailable: function () {
20368 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20371 defaultPadding : {left:0, right:0, top:0, bottom:0},
20374 * Initializes the drag drop object's constraints to restrict movement to a certain element.
20378 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20379 { dragElId: "existingProxyDiv" });
20380 dd.startDrag = function(){
20381 this.constrainTo("parent-id");
20384 * Or you can initalize it using the {@link Roo.Element} object:
20386 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20387 startDrag : function(){
20388 this.constrainTo("parent-id");
20392 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20393 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20394 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20395 * an object containing the sides to pad. For example: {right:10, bottom:10}
20396 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20398 constrainTo : function(constrainTo, pad, inContent){
20399 if(typeof pad == "number"){
20400 pad = {left: pad, right:pad, top:pad, bottom:pad};
20402 pad = pad || this.defaultPadding;
20403 var b = Roo.get(this.getEl()).getBox();
20404 var ce = Roo.get(constrainTo);
20405 var s = ce.getScroll();
20406 var c, cd = ce.dom;
20407 if(cd == document.body){
20408 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20411 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20415 var topSpace = b.y - c.y;
20416 var leftSpace = b.x - c.x;
20418 this.resetConstraints();
20419 this.setXConstraint(leftSpace - (pad.left||0), // left
20420 c.width - leftSpace - b.width - (pad.right||0) //right
20422 this.setYConstraint(topSpace - (pad.top||0), //top
20423 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20428 * Returns a reference to the linked element
20430 * @return {HTMLElement} the html element
20432 getEl: function() {
20433 if (!this._domRef) {
20434 this._domRef = Roo.getDom(this.id);
20437 return this._domRef;
20441 * Returns a reference to the actual element to drag. By default this is
20442 * the same as the html element, but it can be assigned to another
20443 * element. An example of this can be found in Roo.dd.DDProxy
20444 * @method getDragEl
20445 * @return {HTMLElement} the html element
20447 getDragEl: function() {
20448 return Roo.getDom(this.dragElId);
20452 * Sets up the DragDrop object. Must be called in the constructor of any
20453 * Roo.dd.DragDrop subclass
20455 * @param id the id of the linked element
20456 * @param {String} sGroup the group of related items
20457 * @param {object} config configuration attributes
20459 init: function(id, sGroup, config) {
20460 this.initTarget(id, sGroup, config);
20461 if (!Roo.isTouch) {
20462 Event.on(this.id, "mousedown", this.handleMouseDown, this);
20464 Event.on(this.id, "touchstart", this.handleMouseDown, this);
20465 // Event.on(this.id, "selectstart", Event.preventDefault);
20469 * Initializes Targeting functionality only... the object does not
20470 * get a mousedown handler.
20471 * @method initTarget
20472 * @param id the id of the linked element
20473 * @param {String} sGroup the group of related items
20474 * @param {object} config configuration attributes
20476 initTarget: function(id, sGroup, config) {
20478 // configuration attributes
20479 this.config = config || {};
20481 // create a local reference to the drag and drop manager
20482 this.DDM = Roo.dd.DDM;
20483 // initialize the groups array
20486 // assume that we have an element reference instead of an id if the
20487 // parameter is not a string
20488 if (typeof id !== "string") {
20495 // add to an interaction group
20496 this.addToGroup((sGroup) ? sGroup : "default");
20498 // We don't want to register this as the handle with the manager
20499 // so we just set the id rather than calling the setter.
20500 this.handleElId = id;
20502 // the linked element is the element that gets dragged by default
20503 this.setDragElId(id);
20505 // by default, clicked anchors will not start drag operations.
20506 this.invalidHandleTypes = { A: "A" };
20507 this.invalidHandleIds = {};
20508 this.invalidHandleClasses = [];
20510 this.applyConfig();
20512 this.handleOnAvailable();
20516 * Applies the configuration parameters that were passed into the constructor.
20517 * This is supposed to happen at each level through the inheritance chain. So
20518 * a DDProxy implentation will execute apply config on DDProxy, DD, and
20519 * DragDrop in order to get all of the parameters that are available in
20521 * @method applyConfig
20523 applyConfig: function() {
20525 // configurable properties:
20526 // padding, isTarget, maintainOffset, primaryButtonOnly
20527 this.padding = this.config.padding || [0, 0, 0, 0];
20528 this.isTarget = (this.config.isTarget !== false);
20529 this.maintainOffset = (this.config.maintainOffset);
20530 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20535 * Executed when the linked element is available
20536 * @method handleOnAvailable
20539 handleOnAvailable: function() {
20540 this.available = true;
20541 this.resetConstraints();
20542 this.onAvailable();
20546 * Configures the padding for the target zone in px. Effectively expands
20547 * (or reduces) the virtual object size for targeting calculations.
20548 * Supports css-style shorthand; if only one parameter is passed, all sides
20549 * will have that padding, and if only two are passed, the top and bottom
20550 * will have the first param, the left and right the second.
20551 * @method setPadding
20552 * @param {int} iTop Top pad
20553 * @param {int} iRight Right pad
20554 * @param {int} iBot Bot pad
20555 * @param {int} iLeft Left pad
20557 setPadding: function(iTop, iRight, iBot, iLeft) {
20558 // this.padding = [iLeft, iRight, iTop, iBot];
20559 if (!iRight && 0 !== iRight) {
20560 this.padding = [iTop, iTop, iTop, iTop];
20561 } else if (!iBot && 0 !== iBot) {
20562 this.padding = [iTop, iRight, iTop, iRight];
20564 this.padding = [iTop, iRight, iBot, iLeft];
20569 * Stores the initial placement of the linked element.
20570 * @method setInitialPosition
20571 * @param {int} diffX the X offset, default 0
20572 * @param {int} diffY the Y offset, default 0
20574 setInitPosition: function(diffX, diffY) {
20575 var el = this.getEl();
20577 if (!this.DDM.verifyEl(el)) {
20581 var dx = diffX || 0;
20582 var dy = diffY || 0;
20584 var p = Dom.getXY( el );
20586 this.initPageX = p[0] - dx;
20587 this.initPageY = p[1] - dy;
20589 this.lastPageX = p[0];
20590 this.lastPageY = p[1];
20593 this.setStartPosition(p);
20597 * Sets the start position of the element. This is set when the obj
20598 * is initialized, the reset when a drag is started.
20599 * @method setStartPosition
20600 * @param pos current position (from previous lookup)
20603 setStartPosition: function(pos) {
20604 var p = pos || Dom.getXY( this.getEl() );
20605 this.deltaSetXY = null;
20607 this.startPageX = p[0];
20608 this.startPageY = p[1];
20612 * Add this instance to a group of related drag/drop objects. All
20613 * instances belong to at least one group, and can belong to as many
20614 * groups as needed.
20615 * @method addToGroup
20616 * @param sGroup {string} the name of the group
20618 addToGroup: function(sGroup) {
20619 this.groups[sGroup] = true;
20620 this.DDM.regDragDrop(this, sGroup);
20624 * Remove's this instance from the supplied interaction group
20625 * @method removeFromGroup
20626 * @param {string} sGroup The group to drop
20628 removeFromGroup: function(sGroup) {
20629 if (this.groups[sGroup]) {
20630 delete this.groups[sGroup];
20633 this.DDM.removeDDFromGroup(this, sGroup);
20637 * Allows you to specify that an element other than the linked element
20638 * will be moved with the cursor during a drag
20639 * @method setDragElId
20640 * @param id {string} the id of the element that will be used to initiate the drag
20642 setDragElId: function(id) {
20643 this.dragElId = id;
20647 * Allows you to specify a child of the linked element that should be
20648 * used to initiate the drag operation. An example of this would be if
20649 * you have a content div with text and links. Clicking anywhere in the
20650 * content area would normally start the drag operation. Use this method
20651 * to specify that an element inside of the content div is the element
20652 * that starts the drag operation.
20653 * @method setHandleElId
20654 * @param id {string} the id of the element that will be used to
20655 * initiate the drag.
20657 setHandleElId: function(id) {
20658 if (typeof id !== "string") {
20661 this.handleElId = id;
20662 this.DDM.regHandle(this.id, id);
20666 * Allows you to set an element outside of the linked element as a drag
20668 * @method setOuterHandleElId
20669 * @param id the id of the element that will be used to initiate the drag
20671 setOuterHandleElId: function(id) {
20672 if (typeof id !== "string") {
20675 Event.on(id, "mousedown",
20676 this.handleMouseDown, this);
20677 this.setHandleElId(id);
20679 this.hasOuterHandles = true;
20683 * Remove all drag and drop hooks for this element
20686 unreg: function() {
20687 Event.un(this.id, "mousedown",
20688 this.handleMouseDown);
20689 Event.un(this.id, "touchstart",
20690 this.handleMouseDown);
20691 this._domRef = null;
20692 this.DDM._remove(this);
20695 destroy : function(){
20700 * Returns true if this instance is locked, or the drag drop mgr is locked
20701 * (meaning that all drag/drop is disabled on the page.)
20703 * @return {boolean} true if this obj or all drag/drop is locked, else
20706 isLocked: function() {
20707 return (this.DDM.isLocked() || this.locked);
20711 * Fired when this object is clicked
20712 * @method handleMouseDown
20714 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20717 handleMouseDown: function(e, oDD){
20719 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20720 //Roo.log('not touch/ button !=0');
20723 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20724 return; // double touch..
20728 if (this.isLocked()) {
20729 //Roo.log('locked');
20733 this.DDM.refreshCache(this.groups);
20734 // Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20735 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20736 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
20737 //Roo.log('no outer handes or not over target');
20740 // Roo.log('check validator');
20741 if (this.clickValidator(e)) {
20742 // Roo.log('validate success');
20743 // set the initial element position
20744 this.setStartPosition();
20747 this.b4MouseDown(e);
20748 this.onMouseDown(e);
20750 this.DDM.handleMouseDown(e, this);
20752 this.DDM.stopEvent(e);
20760 clickValidator: function(e) {
20761 var target = e.getTarget();
20762 return ( this.isValidHandleChild(target) &&
20763 (this.id == this.handleElId ||
20764 this.DDM.handleWasClicked(target, this.id)) );
20768 * Allows you to specify a tag name that should not start a drag operation
20769 * when clicked. This is designed to facilitate embedding links within a
20770 * drag handle that do something other than start the drag.
20771 * @method addInvalidHandleType
20772 * @param {string} tagName the type of element to exclude
20774 addInvalidHandleType: function(tagName) {
20775 var type = tagName.toUpperCase();
20776 this.invalidHandleTypes[type] = type;
20780 * Lets you to specify an element id for a child of a drag handle
20781 * that should not initiate a drag
20782 * @method addInvalidHandleId
20783 * @param {string} id the element id of the element you wish to ignore
20785 addInvalidHandleId: function(id) {
20786 if (typeof id !== "string") {
20789 this.invalidHandleIds[id] = id;
20793 * Lets you specify a css class of elements that will not initiate a drag
20794 * @method addInvalidHandleClass
20795 * @param {string} cssClass the class of the elements you wish to ignore
20797 addInvalidHandleClass: function(cssClass) {
20798 this.invalidHandleClasses.push(cssClass);
20802 * Unsets an excluded tag name set by addInvalidHandleType
20803 * @method removeInvalidHandleType
20804 * @param {string} tagName the type of element to unexclude
20806 removeInvalidHandleType: function(tagName) {
20807 var type = tagName.toUpperCase();
20808 // this.invalidHandleTypes[type] = null;
20809 delete this.invalidHandleTypes[type];
20813 * Unsets an invalid handle id
20814 * @method removeInvalidHandleId
20815 * @param {string} id the id of the element to re-enable
20817 removeInvalidHandleId: function(id) {
20818 if (typeof id !== "string") {
20821 delete this.invalidHandleIds[id];
20825 * Unsets an invalid css class
20826 * @method removeInvalidHandleClass
20827 * @param {string} cssClass the class of the element(s) you wish to
20830 removeInvalidHandleClass: function(cssClass) {
20831 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20832 if (this.invalidHandleClasses[i] == cssClass) {
20833 delete this.invalidHandleClasses[i];
20839 * Checks the tag exclusion list to see if this click should be ignored
20840 * @method isValidHandleChild
20841 * @param {HTMLElement} node the HTMLElement to evaluate
20842 * @return {boolean} true if this is a valid tag type, false if not
20844 isValidHandleChild: function(node) {
20847 // var n = (node.nodeName == "#text") ? node.parentNode : node;
20850 nodeName = node.nodeName.toUpperCase();
20852 nodeName = node.nodeName;
20854 valid = valid && !this.invalidHandleTypes[nodeName];
20855 valid = valid && !this.invalidHandleIds[node.id];
20857 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20858 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20867 * Create the array of horizontal tick marks if an interval was specified
20868 * in setXConstraint().
20869 * @method setXTicks
20872 setXTicks: function(iStartX, iTickSize) {
20874 this.xTickSize = iTickSize;
20878 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20880 this.xTicks[this.xTicks.length] = i;
20885 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20887 this.xTicks[this.xTicks.length] = i;
20892 this.xTicks.sort(this.DDM.numericSort) ;
20896 * Create the array of vertical tick marks if an interval was specified in
20897 * setYConstraint().
20898 * @method setYTicks
20901 setYTicks: function(iStartY, iTickSize) {
20903 this.yTickSize = iTickSize;
20907 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20909 this.yTicks[this.yTicks.length] = i;
20914 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20916 this.yTicks[this.yTicks.length] = i;
20921 this.yTicks.sort(this.DDM.numericSort) ;
20925 * By default, the element can be dragged any place on the screen. Use
20926 * this method to limit the horizontal travel of the element. Pass in
20927 * 0,0 for the parameters if you want to lock the drag to the y axis.
20928 * @method setXConstraint
20929 * @param {int} iLeft the number of pixels the element can move to the left
20930 * @param {int} iRight the number of pixels the element can move to the
20932 * @param {int} iTickSize optional parameter for specifying that the
20934 * should move iTickSize pixels at a time.
20936 setXConstraint: function(iLeft, iRight, iTickSize) {
20937 this.leftConstraint = iLeft;
20938 this.rightConstraint = iRight;
20940 this.minX = this.initPageX - iLeft;
20941 this.maxX = this.initPageX + iRight;
20942 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20944 this.constrainX = true;
20948 * Clears any constraints applied to this instance. Also clears ticks
20949 * since they can't exist independent of a constraint at this time.
20950 * @method clearConstraints
20952 clearConstraints: function() {
20953 this.constrainX = false;
20954 this.constrainY = false;
20959 * Clears any tick interval defined for this instance
20960 * @method clearTicks
20962 clearTicks: function() {
20963 this.xTicks = null;
20964 this.yTicks = null;
20965 this.xTickSize = 0;
20966 this.yTickSize = 0;
20970 * By default, the element can be dragged any place on the screen. Set
20971 * this to limit the vertical travel of the element. Pass in 0,0 for the
20972 * parameters if you want to lock the drag to the x axis.
20973 * @method setYConstraint
20974 * @param {int} iUp the number of pixels the element can move up
20975 * @param {int} iDown the number of pixels the element can move down
20976 * @param {int} iTickSize optional parameter for specifying that the
20977 * element should move iTickSize pixels at a time.
20979 setYConstraint: function(iUp, iDown, iTickSize) {
20980 this.topConstraint = iUp;
20981 this.bottomConstraint = iDown;
20983 this.minY = this.initPageY - iUp;
20984 this.maxY = this.initPageY + iDown;
20985 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20987 this.constrainY = true;
20992 * resetConstraints must be called if you manually reposition a dd element.
20993 * @method resetConstraints
20994 * @param {boolean} maintainOffset
20996 resetConstraints: function() {
20999 // Maintain offsets if necessary
21000 if (this.initPageX || this.initPageX === 0) {
21001 // figure out how much this thing has moved
21002 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21003 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21005 this.setInitPosition(dx, dy);
21007 // This is the first time we have detected the element's position
21009 this.setInitPosition();
21012 if (this.constrainX) {
21013 this.setXConstraint( this.leftConstraint,
21014 this.rightConstraint,
21018 if (this.constrainY) {
21019 this.setYConstraint( this.topConstraint,
21020 this.bottomConstraint,
21026 * Normally the drag element is moved pixel by pixel, but we can specify
21027 * that it move a number of pixels at a time. This method resolves the
21028 * location when we have it set up like this.
21030 * @param {int} val where we want to place the object
21031 * @param {int[]} tickArray sorted array of valid points
21032 * @return {int} the closest tick
21035 getTick: function(val, tickArray) {
21038 // If tick interval is not defined, it is effectively 1 pixel,
21039 // so we return the value passed to us.
21041 } else if (tickArray[0] >= val) {
21042 // The value is lower than the first tick, so we return the first
21044 return tickArray[0];
21046 for (var i=0, len=tickArray.length; i<len; ++i) {
21048 if (tickArray[next] && tickArray[next] >= val) {
21049 var diff1 = val - tickArray[i];
21050 var diff2 = tickArray[next] - val;
21051 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21055 // The value is larger than the last tick, so we return the last
21057 return tickArray[tickArray.length - 1];
21064 * @return {string} string representation of the dd obj
21066 toString: function() {
21067 return ("DragDrop " + this.id);
21075 * Ext JS Library 1.1.1
21076 * Copyright(c) 2006-2007, Ext JS, LLC.
21078 * Originally Released Under LGPL - original licence link has changed is not relivant.
21081 * <script type="text/javascript">
21086 * The drag and drop utility provides a framework for building drag and drop
21087 * applications. In addition to enabling drag and drop for specific elements,
21088 * the drag and drop elements are tracked by the manager class, and the
21089 * interactions between the various elements are tracked during the drag and
21090 * the implementing code is notified about these important moments.
21093 // Only load the library once. Rewriting the manager class would orphan
21094 // existing drag and drop instances.
21095 if (!Roo.dd.DragDropMgr) {
21098 * @class Roo.dd.DragDropMgr
21099 * DragDropMgr is a singleton that tracks the element interaction for
21100 * all DragDrop items in the window. Generally, you will not call
21101 * this class directly, but it does have helper methods that could
21102 * be useful in your DragDrop implementations.
21105 Roo.dd.DragDropMgr = function() {
21107 var Event = Roo.EventManager;
21112 * Two dimensional Array of registered DragDrop objects. The first
21113 * dimension is the DragDrop item group, the second the DragDrop
21116 * @type {string: string}
21123 * Array of element ids defined as drag handles. Used to determine
21124 * if the element that generated the mousedown event is actually the
21125 * handle and not the html element itself.
21126 * @property handleIds
21127 * @type {string: string}
21134 * the DragDrop object that is currently being dragged
21135 * @property dragCurrent
21143 * the DragDrop object(s) that are being hovered over
21144 * @property dragOvers
21152 * the X distance between the cursor and the object being dragged
21161 * the Y distance between the cursor and the object being dragged
21170 * Flag to determine if we should prevent the default behavior of the
21171 * events we define. By default this is true, but this can be set to
21172 * false if you need the default behavior (not recommended)
21173 * @property preventDefault
21177 preventDefault: true,
21180 * Flag to determine if we should stop the propagation of the events
21181 * we generate. This is true by default but you may want to set it to
21182 * false if the html element contains other features that require the
21184 * @property stopPropagation
21188 stopPropagation: true,
21191 * Internal flag that is set to true when drag and drop has been
21193 * @property initialized
21200 * All drag and drop can be disabled.
21208 * Called the first time an element is registered.
21214 this.initialized = true;
21218 * In point mode, drag and drop interaction is defined by the
21219 * location of the cursor during the drag/drop
21227 * In intersect mode, drag and drop interactio nis defined by the
21228 * overlap of two or more drag and drop objects.
21229 * @property INTERSECT
21236 * The current drag and drop mode. Default: POINT
21244 * Runs method on all drag and drop objects
21245 * @method _execOnAll
21249 _execOnAll: function(sMethod, args) {
21250 for (var i in this.ids) {
21251 for (var j in this.ids[i]) {
21252 var oDD = this.ids[i][j];
21253 if (! this.isTypeOfDD(oDD)) {
21256 oDD[sMethod].apply(oDD, args);
21262 * Drag and drop initialization. Sets up the global event handlers
21267 _onLoad: function() {
21271 if (!Roo.isTouch) {
21272 Event.on(document, "mouseup", this.handleMouseUp, this, true);
21273 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21275 Event.on(document, "touchend", this.handleMouseUp, this, true);
21276 Event.on(document, "touchmove", this.handleMouseMove, this, true);
21278 Event.on(window, "unload", this._onUnload, this, true);
21279 Event.on(window, "resize", this._onResize, this, true);
21280 // Event.on(window, "mouseout", this._test);
21285 * Reset constraints on all drag and drop objs
21286 * @method _onResize
21290 _onResize: function(e) {
21291 this._execOnAll("resetConstraints", []);
21295 * Lock all drag and drop functionality
21299 lock: function() { this.locked = true; },
21302 * Unlock all drag and drop functionality
21306 unlock: function() { this.locked = false; },
21309 * Is drag and drop locked?
21311 * @return {boolean} True if drag and drop is locked, false otherwise.
21314 isLocked: function() { return this.locked; },
21317 * Location cache that is set for all drag drop objects when a drag is
21318 * initiated, cleared when the drag is finished.
21319 * @property locationCache
21326 * Set useCache to false if you want to force object the lookup of each
21327 * drag and drop linked element constantly during a drag.
21328 * @property useCache
21335 * The number of pixels that the mouse needs to move after the
21336 * mousedown before the drag is initiated. Default=3;
21337 * @property clickPixelThresh
21341 clickPixelThresh: 3,
21344 * The number of milliseconds after the mousedown event to initiate the
21345 * drag if we don't get a mouseup event. Default=1000
21346 * @property clickTimeThresh
21350 clickTimeThresh: 350,
21353 * Flag that indicates that either the drag pixel threshold or the
21354 * mousdown time threshold has been met
21355 * @property dragThreshMet
21360 dragThreshMet: false,
21363 * Timeout used for the click time threshold
21364 * @property clickTimeout
21369 clickTimeout: null,
21372 * The X position of the mousedown event stored for later use when a
21373 * drag threshold is met.
21382 * The Y position of the mousedown event stored for later use when a
21383 * drag threshold is met.
21392 * Each DragDrop instance must be registered with the DragDropMgr.
21393 * This is executed in DragDrop.init()
21394 * @method regDragDrop
21395 * @param {DragDrop} oDD the DragDrop object to register
21396 * @param {String} sGroup the name of the group this element belongs to
21399 regDragDrop: function(oDD, sGroup) {
21400 if (!this.initialized) { this.init(); }
21402 if (!this.ids[sGroup]) {
21403 this.ids[sGroup] = {};
21405 this.ids[sGroup][oDD.id] = oDD;
21409 * Removes the supplied dd instance from the supplied group. Executed
21410 * by DragDrop.removeFromGroup, so don't call this function directly.
21411 * @method removeDDFromGroup
21415 removeDDFromGroup: function(oDD, sGroup) {
21416 if (!this.ids[sGroup]) {
21417 this.ids[sGroup] = {};
21420 var obj = this.ids[sGroup];
21421 if (obj && obj[oDD.id]) {
21422 delete obj[oDD.id];
21427 * Unregisters a drag and drop item. This is executed in
21428 * DragDrop.unreg, use that method instead of calling this directly.
21433 _remove: function(oDD) {
21434 for (var g in oDD.groups) {
21435 if (g && this.ids[g][oDD.id]) {
21436 delete this.ids[g][oDD.id];
21439 delete this.handleIds[oDD.id];
21443 * Each DragDrop handle element must be registered. This is done
21444 * automatically when executing DragDrop.setHandleElId()
21445 * @method regHandle
21446 * @param {String} sDDId the DragDrop id this element is a handle for
21447 * @param {String} sHandleId the id of the element that is the drag
21451 regHandle: function(sDDId, sHandleId) {
21452 if (!this.handleIds[sDDId]) {
21453 this.handleIds[sDDId] = {};
21455 this.handleIds[sDDId][sHandleId] = sHandleId;
21459 * Utility function to determine if a given element has been
21460 * registered as a drag drop item.
21461 * @method isDragDrop
21462 * @param {String} id the element id to check
21463 * @return {boolean} true if this element is a DragDrop item,
21467 isDragDrop: function(id) {
21468 return ( this.getDDById(id) ) ? true : false;
21472 * Returns the drag and drop instances that are in all groups the
21473 * passed in instance belongs to.
21474 * @method getRelated
21475 * @param {DragDrop} p_oDD the obj to get related data for
21476 * @param {boolean} bTargetsOnly if true, only return targetable objs
21477 * @return {DragDrop[]} the related instances
21480 getRelated: function(p_oDD, bTargetsOnly) {
21482 for (var i in p_oDD.groups) {
21483 for (j in this.ids[i]) {
21484 var dd = this.ids[i][j];
21485 if (! this.isTypeOfDD(dd)) {
21488 if (!bTargetsOnly || dd.isTarget) {
21489 oDDs[oDDs.length] = dd;
21498 * Returns true if the specified dd target is a legal target for
21499 * the specifice drag obj
21500 * @method isLegalTarget
21501 * @param {DragDrop} the drag obj
21502 * @param {DragDrop} the target
21503 * @return {boolean} true if the target is a legal target for the
21507 isLegalTarget: function (oDD, oTargetDD) {
21508 var targets = this.getRelated(oDD, true);
21509 for (var i=0, len=targets.length;i<len;++i) {
21510 if (targets[i].id == oTargetDD.id) {
21519 * My goal is to be able to transparently determine if an object is
21520 * typeof DragDrop, and the exact subclass of DragDrop. typeof
21521 * returns "object", oDD.constructor.toString() always returns
21522 * "DragDrop" and not the name of the subclass. So for now it just
21523 * evaluates a well-known variable in DragDrop.
21524 * @method isTypeOfDD
21525 * @param {Object} the object to evaluate
21526 * @return {boolean} true if typeof oDD = DragDrop
21529 isTypeOfDD: function (oDD) {
21530 return (oDD && oDD.__ygDragDrop);
21534 * Utility function to determine if a given element has been
21535 * registered as a drag drop handle for the given Drag Drop object.
21537 * @param {String} id the element id to check
21538 * @return {boolean} true if this element is a DragDrop handle, false
21542 isHandle: function(sDDId, sHandleId) {
21543 return ( this.handleIds[sDDId] &&
21544 this.handleIds[sDDId][sHandleId] );
21548 * Returns the DragDrop instance for a given id
21549 * @method getDDById
21550 * @param {String} id the id of the DragDrop object
21551 * @return {DragDrop} the drag drop object, null if it is not found
21554 getDDById: function(id) {
21555 for (var i in this.ids) {
21556 if (this.ids[i][id]) {
21557 return this.ids[i][id];
21564 * Fired after a registered DragDrop object gets the mousedown event.
21565 * Sets up the events required to track the object being dragged
21566 * @method handleMouseDown
21567 * @param {Event} e the event
21568 * @param oDD the DragDrop object being dragged
21572 handleMouseDown: function(e, oDD) {
21574 Roo.QuickTips.disable();
21576 this.currentTarget = e.getTarget();
21578 this.dragCurrent = oDD;
21580 var el = oDD.getEl();
21582 // track start position
21583 this.startX = e.getPageX();
21584 this.startY = e.getPageY();
21586 this.deltaX = this.startX - el.offsetLeft;
21587 this.deltaY = this.startY - el.offsetTop;
21589 this.dragThreshMet = false;
21591 this.clickTimeout = setTimeout(
21593 var DDM = Roo.dd.DDM;
21594 DDM.startDrag(DDM.startX, DDM.startY);
21596 this.clickTimeThresh );
21600 * Fired when either the drag pixel threshol or the mousedown hold
21601 * time threshold has been met.
21602 * @method startDrag
21603 * @param x {int} the X position of the original mousedown
21604 * @param y {int} the Y position of the original mousedown
21607 startDrag: function(x, y) {
21608 clearTimeout(this.clickTimeout);
21609 if (this.dragCurrent) {
21610 this.dragCurrent.b4StartDrag(x, y);
21611 this.dragCurrent.startDrag(x, y);
21613 this.dragThreshMet = true;
21617 * Internal function to handle the mouseup event. Will be invoked
21618 * from the context of the document.
21619 * @method handleMouseUp
21620 * @param {Event} e the event
21624 handleMouseUp: function(e) {
21627 Roo.QuickTips.enable();
21629 if (! this.dragCurrent) {
21633 clearTimeout(this.clickTimeout);
21635 if (this.dragThreshMet) {
21636 this.fireEvents(e, true);
21646 * Utility to stop event propagation and event default, if these
21647 * features are turned on.
21648 * @method stopEvent
21649 * @param {Event} e the event as returned by this.getEvent()
21652 stopEvent: function(e){
21653 if(this.stopPropagation) {
21654 e.stopPropagation();
21657 if (this.preventDefault) {
21658 e.preventDefault();
21663 * Internal function to clean up event handlers after the drag
21664 * operation is complete
21666 * @param {Event} e the event
21670 stopDrag: function(e) {
21671 // Fire the drag end event for the item that was dragged
21672 if (this.dragCurrent) {
21673 if (this.dragThreshMet) {
21674 this.dragCurrent.b4EndDrag(e);
21675 this.dragCurrent.endDrag(e);
21678 this.dragCurrent.onMouseUp(e);
21681 this.dragCurrent = null;
21682 this.dragOvers = {};
21686 * Internal function to handle the mousemove event. Will be invoked
21687 * from the context of the html element.
21689 * @TODO figure out what we can do about mouse events lost when the
21690 * user drags objects beyond the window boundary. Currently we can
21691 * detect this in internet explorer by verifying that the mouse is
21692 * down during the mousemove event. Firefox doesn't give us the
21693 * button state on the mousemove event.
21694 * @method handleMouseMove
21695 * @param {Event} e the event
21699 handleMouseMove: function(e) {
21700 if (! this.dragCurrent) {
21704 // var button = e.which || e.button;
21706 // check for IE mouseup outside of page boundary
21707 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21709 return this.handleMouseUp(e);
21712 if (!this.dragThreshMet) {
21713 var diffX = Math.abs(this.startX - e.getPageX());
21714 var diffY = Math.abs(this.startY - e.getPageY());
21715 if (diffX > this.clickPixelThresh ||
21716 diffY > this.clickPixelThresh) {
21717 this.startDrag(this.startX, this.startY);
21721 if (this.dragThreshMet) {
21722 this.dragCurrent.b4Drag(e);
21723 this.dragCurrent.onDrag(e);
21724 if(!this.dragCurrent.moveOnly){
21725 this.fireEvents(e, false);
21735 * Iterates over all of the DragDrop elements to find ones we are
21736 * hovering over or dropping on
21737 * @method fireEvents
21738 * @param {Event} e the event
21739 * @param {boolean} isDrop is this a drop op or a mouseover op?
21743 fireEvents: function(e, isDrop) {
21744 var dc = this.dragCurrent;
21746 // If the user did the mouse up outside of the window, we could
21747 // get here even though we have ended the drag.
21748 if (!dc || dc.isLocked()) {
21752 var pt = e.getPoint();
21754 // cache the previous dragOver array
21760 var enterEvts = [];
21762 // Check to see if the object(s) we were hovering over is no longer
21763 // being hovered over so we can fire the onDragOut event
21764 for (var i in this.dragOvers) {
21766 var ddo = this.dragOvers[i];
21768 if (! this.isTypeOfDD(ddo)) {
21772 if (! this.isOverTarget(pt, ddo, this.mode)) {
21773 outEvts.push( ddo );
21776 oldOvers[i] = true;
21777 delete this.dragOvers[i];
21780 for (var sGroup in dc.groups) {
21782 if ("string" != typeof sGroup) {
21786 for (i in this.ids[sGroup]) {
21787 var oDD = this.ids[sGroup][i];
21788 if (! this.isTypeOfDD(oDD)) {
21792 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21793 if (this.isOverTarget(pt, oDD, this.mode)) {
21794 // look for drop interactions
21796 dropEvts.push( oDD );
21797 // look for drag enter and drag over interactions
21800 // initial drag over: dragEnter fires
21801 if (!oldOvers[oDD.id]) {
21802 enterEvts.push( oDD );
21803 // subsequent drag overs: dragOver fires
21805 overEvts.push( oDD );
21808 this.dragOvers[oDD.id] = oDD;
21816 if (outEvts.length) {
21817 dc.b4DragOut(e, outEvts);
21818 dc.onDragOut(e, outEvts);
21821 if (enterEvts.length) {
21822 dc.onDragEnter(e, enterEvts);
21825 if (overEvts.length) {
21826 dc.b4DragOver(e, overEvts);
21827 dc.onDragOver(e, overEvts);
21830 if (dropEvts.length) {
21831 dc.b4DragDrop(e, dropEvts);
21832 dc.onDragDrop(e, dropEvts);
21836 // fire dragout events
21838 for (i=0, len=outEvts.length; i<len; ++i) {
21839 dc.b4DragOut(e, outEvts[i].id);
21840 dc.onDragOut(e, outEvts[i].id);
21843 // fire enter events
21844 for (i=0,len=enterEvts.length; i<len; ++i) {
21845 // dc.b4DragEnter(e, oDD.id);
21846 dc.onDragEnter(e, enterEvts[i].id);
21849 // fire over events
21850 for (i=0,len=overEvts.length; i<len; ++i) {
21851 dc.b4DragOver(e, overEvts[i].id);
21852 dc.onDragOver(e, overEvts[i].id);
21855 // fire drop events
21856 for (i=0, len=dropEvts.length; i<len; ++i) {
21857 dc.b4DragDrop(e, dropEvts[i].id);
21858 dc.onDragDrop(e, dropEvts[i].id);
21863 // notify about a drop that did not find a target
21864 if (isDrop && !dropEvts.length) {
21865 dc.onInvalidDrop(e);
21871 * Helper function for getting the best match from the list of drag
21872 * and drop objects returned by the drag and drop events when we are
21873 * in INTERSECT mode. It returns either the first object that the
21874 * cursor is over, or the object that has the greatest overlap with
21875 * the dragged element.
21876 * @method getBestMatch
21877 * @param {DragDrop[]} dds The array of drag and drop objects
21879 * @return {DragDrop} The best single match
21882 getBestMatch: function(dds) {
21884 // Return null if the input is not what we expect
21885 //if (!dds || !dds.length || dds.length == 0) {
21887 // If there is only one item, it wins
21888 //} else if (dds.length == 1) {
21890 var len = dds.length;
21895 // Loop through the targeted items
21896 for (var i=0; i<len; ++i) {
21898 // If the cursor is over the object, it wins. If the
21899 // cursor is over multiple matches, the first one we come
21901 if (dd.cursorIsOver) {
21904 // Otherwise the object with the most overlap wins
21907 winner.overlap.getArea() < dd.overlap.getArea()) {
21918 * Refreshes the cache of the top-left and bottom-right points of the
21919 * drag and drop objects in the specified group(s). This is in the
21920 * format that is stored in the drag and drop instance, so typical
21923 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21927 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21929 * @TODO this really should be an indexed array. Alternatively this
21930 * method could accept both.
21931 * @method refreshCache
21932 * @param {Object} groups an associative array of groups to refresh
21935 refreshCache: function(groups) {
21936 for (var sGroup in groups) {
21937 if ("string" != typeof sGroup) {
21940 for (var i in this.ids[sGroup]) {
21941 var oDD = this.ids[sGroup][i];
21943 if (this.isTypeOfDD(oDD)) {
21944 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21945 var loc = this.getLocation(oDD);
21947 this.locationCache[oDD.id] = loc;
21949 delete this.locationCache[oDD.id];
21950 // this will unregister the drag and drop object if
21951 // the element is not in a usable state
21960 * This checks to make sure an element exists and is in the DOM. The
21961 * main purpose is to handle cases where innerHTML is used to remove
21962 * drag and drop objects from the DOM. IE provides an 'unspecified
21963 * error' when trying to access the offsetParent of such an element
21965 * @param {HTMLElement} el the element to check
21966 * @return {boolean} true if the element looks usable
21969 verifyEl: function(el) {
21974 parent = el.offsetParent;
21977 parent = el.offsetParent;
21988 * Returns a Region object containing the drag and drop element's position
21989 * and size, including the padding configured for it
21990 * @method getLocation
21991 * @param {DragDrop} oDD the drag and drop object to get the
21993 * @return {Roo.lib.Region} a Region object representing the total area
21994 * the element occupies, including any padding
21995 * the instance is configured for.
21998 getLocation: function(oDD) {
21999 if (! this.isTypeOfDD(oDD)) {
22003 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22006 pos= Roo.lib.Dom.getXY(el);
22014 x2 = x1 + el.offsetWidth;
22016 y2 = y1 + el.offsetHeight;
22018 t = y1 - oDD.padding[0];
22019 r = x2 + oDD.padding[1];
22020 b = y2 + oDD.padding[2];
22021 l = x1 - oDD.padding[3];
22023 return new Roo.lib.Region( t, r, b, l );
22027 * Checks the cursor location to see if it over the target
22028 * @method isOverTarget
22029 * @param {Roo.lib.Point} pt The point to evaluate
22030 * @param {DragDrop} oTarget the DragDrop object we are inspecting
22031 * @return {boolean} true if the mouse is over the target
22035 isOverTarget: function(pt, oTarget, intersect) {
22036 // use cache if available
22037 var loc = this.locationCache[oTarget.id];
22038 if (!loc || !this.useCache) {
22039 loc = this.getLocation(oTarget);
22040 this.locationCache[oTarget.id] = loc;
22048 oTarget.cursorIsOver = loc.contains( pt );
22050 // DragDrop is using this as a sanity check for the initial mousedown
22051 // in this case we are done. In POINT mode, if the drag obj has no
22052 // contraints, we are also done. Otherwise we need to evaluate the
22053 // location of the target as related to the actual location of the
22054 // dragged element.
22055 var dc = this.dragCurrent;
22056 if (!dc || !dc.getTargetCoord ||
22057 (!intersect && !dc.constrainX && !dc.constrainY)) {
22058 return oTarget.cursorIsOver;
22061 oTarget.overlap = null;
22063 // Get the current location of the drag element, this is the
22064 // location of the mouse event less the delta that represents
22065 // where the original mousedown happened on the element. We
22066 // need to consider constraints and ticks as well.
22067 var pos = dc.getTargetCoord(pt.x, pt.y);
22069 var el = dc.getDragEl();
22070 var curRegion = new Roo.lib.Region( pos.y,
22071 pos.x + el.offsetWidth,
22072 pos.y + el.offsetHeight,
22075 var overlap = curRegion.intersect(loc);
22078 oTarget.overlap = overlap;
22079 return (intersect) ? true : oTarget.cursorIsOver;
22086 * unload event handler
22087 * @method _onUnload
22091 _onUnload: function(e, me) {
22092 Roo.dd.DragDropMgr.unregAll();
22096 * Cleans up the drag and drop events and objects.
22101 unregAll: function() {
22103 if (this.dragCurrent) {
22105 this.dragCurrent = null;
22108 this._execOnAll("unreg", []);
22110 for (i in this.elementCache) {
22111 delete this.elementCache[i];
22114 this.elementCache = {};
22119 * A cache of DOM elements
22120 * @property elementCache
22127 * Get the wrapper for the DOM element specified
22128 * @method getElWrapper
22129 * @param {String} id the id of the element to get
22130 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22132 * @deprecated This wrapper isn't that useful
22135 getElWrapper: function(id) {
22136 var oWrapper = this.elementCache[id];
22137 if (!oWrapper || !oWrapper.el) {
22138 oWrapper = this.elementCache[id] =
22139 new this.ElementWrapper(Roo.getDom(id));
22145 * Returns the actual DOM element
22146 * @method getElement
22147 * @param {String} id the id of the elment to get
22148 * @return {Object} The element
22149 * @deprecated use Roo.getDom instead
22152 getElement: function(id) {
22153 return Roo.getDom(id);
22157 * Returns the style property for the DOM element (i.e.,
22158 * document.getElById(id).style)
22160 * @param {String} id the id of the elment to get
22161 * @return {Object} The style property of the element
22162 * @deprecated use Roo.getDom instead
22165 getCss: function(id) {
22166 var el = Roo.getDom(id);
22167 return (el) ? el.style : null;
22171 * Inner class for cached elements
22172 * @class DragDropMgr.ElementWrapper
22177 ElementWrapper: function(el) {
22182 this.el = el || null;
22187 this.id = this.el && el.id;
22189 * A reference to the style property
22192 this.css = this.el && el.style;
22196 * Returns the X position of an html element
22198 * @param el the element for which to get the position
22199 * @return {int} the X coordinate
22201 * @deprecated use Roo.lib.Dom.getX instead
22204 getPosX: function(el) {
22205 return Roo.lib.Dom.getX(el);
22209 * Returns the Y position of an html element
22211 * @param el the element for which to get the position
22212 * @return {int} the Y coordinate
22213 * @deprecated use Roo.lib.Dom.getY instead
22216 getPosY: function(el) {
22217 return Roo.lib.Dom.getY(el);
22221 * Swap two nodes. In IE, we use the native method, for others we
22222 * emulate the IE behavior
22224 * @param n1 the first node to swap
22225 * @param n2 the other node to swap
22228 swapNode: function(n1, n2) {
22232 var p = n2.parentNode;
22233 var s = n2.nextSibling;
22236 p.insertBefore(n1, n2);
22237 } else if (n2 == n1.nextSibling) {
22238 p.insertBefore(n2, n1);
22240 n1.parentNode.replaceChild(n2, n1);
22241 p.insertBefore(n1, s);
22247 * Returns the current scroll position
22248 * @method getScroll
22252 getScroll: function () {
22253 var t, l, dde=document.documentElement, db=document.body;
22254 if (dde && (dde.scrollTop || dde.scrollLeft)) {
22256 l = dde.scrollLeft;
22263 return { top: t, left: l };
22267 * Returns the specified element style property
22269 * @param {HTMLElement} el the element
22270 * @param {string} styleProp the style property
22271 * @return {string} The value of the style property
22272 * @deprecated use Roo.lib.Dom.getStyle
22275 getStyle: function(el, styleProp) {
22276 return Roo.fly(el).getStyle(styleProp);
22280 * Gets the scrollTop
22281 * @method getScrollTop
22282 * @return {int} the document's scrollTop
22285 getScrollTop: function () { return this.getScroll().top; },
22288 * Gets the scrollLeft
22289 * @method getScrollLeft
22290 * @return {int} the document's scrollTop
22293 getScrollLeft: function () { return this.getScroll().left; },
22296 * Sets the x/y position of an element to the location of the
22299 * @param {HTMLElement} moveEl The element to move
22300 * @param {HTMLElement} targetEl The position reference element
22303 moveToEl: function (moveEl, targetEl) {
22304 var aCoord = Roo.lib.Dom.getXY(targetEl);
22305 Roo.lib.Dom.setXY(moveEl, aCoord);
22309 * Numeric array sort function
22310 * @method numericSort
22313 numericSort: function(a, b) { return (a - b); },
22317 * @property _timeoutCount
22324 * Trying to make the load order less important. Without this we get
22325 * an error if this file is loaded before the Event Utility.
22326 * @method _addListeners
22330 _addListeners: function() {
22331 var DDM = Roo.dd.DDM;
22332 if ( Roo.lib.Event && document ) {
22335 if (DDM._timeoutCount > 2000) {
22337 setTimeout(DDM._addListeners, 10);
22338 if (document && document.body) {
22339 DDM._timeoutCount += 1;
22346 * Recursively searches the immediate parent and all child nodes for
22347 * the handle element in order to determine wheter or not it was
22349 * @method handleWasClicked
22350 * @param node the html element to inspect
22353 handleWasClicked: function(node, id) {
22354 if (this.isHandle(id, node.id)) {
22357 // check to see if this is a text node child of the one we want
22358 var p = node.parentNode;
22361 if (this.isHandle(id, p.id)) {
22376 // shorter alias, save a few bytes
22377 Roo.dd.DDM = Roo.dd.DragDropMgr;
22378 Roo.dd.DDM._addListeners();
22382 * Ext JS Library 1.1.1
22383 * Copyright(c) 2006-2007, Ext JS, LLC.
22385 * Originally Released Under LGPL - original licence link has changed is not relivant.
22388 * <script type="text/javascript">
22393 * A DragDrop implementation where the linked element follows the
22394 * mouse cursor during a drag.
22395 * @extends Roo.dd.DragDrop
22397 * @param {String} id the id of the linked element
22398 * @param {String} sGroup the group of related DragDrop items
22399 * @param {object} config an object containing configurable attributes
22400 * Valid properties for DD:
22403 Roo.dd.DD = function(id, sGroup, config) {
22405 this.init(id, sGroup, config);
22409 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22412 * When set to true, the utility automatically tries to scroll the browser
22413 * window wehn a drag and drop element is dragged near the viewport boundary.
22414 * Defaults to true.
22421 * Sets the pointer offset to the distance between the linked element's top
22422 * left corner and the location the element was clicked
22423 * @method autoOffset
22424 * @param {int} iPageX the X coordinate of the click
22425 * @param {int} iPageY the Y coordinate of the click
22427 autoOffset: function(iPageX, iPageY) {
22428 var x = iPageX - this.startPageX;
22429 var y = iPageY - this.startPageY;
22430 this.setDelta(x, y);
22434 * Sets the pointer offset. You can call this directly to force the
22435 * offset to be in a particular location (e.g., pass in 0,0 to set it
22436 * to the center of the object)
22438 * @param {int} iDeltaX the distance from the left
22439 * @param {int} iDeltaY the distance from the top
22441 setDelta: function(iDeltaX, iDeltaY) {
22442 this.deltaX = iDeltaX;
22443 this.deltaY = iDeltaY;
22447 * Sets the drag element to the location of the mousedown or click event,
22448 * maintaining the cursor location relative to the location on the element
22449 * that was clicked. Override this if you want to place the element in a
22450 * location other than where the cursor is.
22451 * @method setDragElPos
22452 * @param {int} iPageX the X coordinate of the mousedown or drag event
22453 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22455 setDragElPos: function(iPageX, iPageY) {
22456 // the first time we do this, we are going to check to make sure
22457 // the element has css positioning
22459 var el = this.getDragEl();
22460 this.alignElWithMouse(el, iPageX, iPageY);
22464 * Sets the element to the location of the mousedown or click event,
22465 * maintaining the cursor location relative to the location on the element
22466 * that was clicked. Override this if you want to place the element in a
22467 * location other than where the cursor is.
22468 * @method alignElWithMouse
22469 * @param {HTMLElement} el the element to move
22470 * @param {int} iPageX the X coordinate of the mousedown or drag event
22471 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22473 alignElWithMouse: function(el, iPageX, iPageY) {
22474 var oCoord = this.getTargetCoord(iPageX, iPageY);
22475 var fly = el.dom ? el : Roo.fly(el);
22476 if (!this.deltaSetXY) {
22477 var aCoord = [oCoord.x, oCoord.y];
22479 var newLeft = fly.getLeft(true);
22480 var newTop = fly.getTop(true);
22481 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22483 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22486 this.cachePosition(oCoord.x, oCoord.y);
22487 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22492 * Saves the most recent position so that we can reset the constraints and
22493 * tick marks on-demand. We need to know this so that we can calculate the
22494 * number of pixels the element is offset from its original position.
22495 * @method cachePosition
22496 * @param iPageX the current x position (optional, this just makes it so we
22497 * don't have to look it up again)
22498 * @param iPageY the current y position (optional, this just makes it so we
22499 * don't have to look it up again)
22501 cachePosition: function(iPageX, iPageY) {
22503 this.lastPageX = iPageX;
22504 this.lastPageY = iPageY;
22506 var aCoord = Roo.lib.Dom.getXY(this.getEl());
22507 this.lastPageX = aCoord[0];
22508 this.lastPageY = aCoord[1];
22513 * Auto-scroll the window if the dragged object has been moved beyond the
22514 * visible window boundary.
22515 * @method autoScroll
22516 * @param {int} x the drag element's x position
22517 * @param {int} y the drag element's y position
22518 * @param {int} h the height of the drag element
22519 * @param {int} w the width of the drag element
22522 autoScroll: function(x, y, h, w) {
22525 // The client height
22526 var clientH = Roo.lib.Dom.getViewWidth();
22528 // The client width
22529 var clientW = Roo.lib.Dom.getViewHeight();
22531 // The amt scrolled down
22532 var st = this.DDM.getScrollTop();
22534 // The amt scrolled right
22535 var sl = this.DDM.getScrollLeft();
22537 // Location of the bottom of the element
22540 // Location of the right of the element
22543 // The distance from the cursor to the bottom of the visible area,
22544 // adjusted so that we don't scroll if the cursor is beyond the
22545 // element drag constraints
22546 var toBot = (clientH + st - y - this.deltaY);
22548 // The distance from the cursor to the right of the visible area
22549 var toRight = (clientW + sl - x - this.deltaX);
22552 // How close to the edge the cursor must be before we scroll
22553 // var thresh = (document.all) ? 100 : 40;
22556 // How many pixels to scroll per autoscroll op. This helps to reduce
22557 // clunky scrolling. IE is more sensitive about this ... it needs this
22558 // value to be higher.
22559 var scrAmt = (document.all) ? 80 : 30;
22561 // Scroll down if we are near the bottom of the visible page and the
22562 // obj extends below the crease
22563 if ( bot > clientH && toBot < thresh ) {
22564 window.scrollTo(sl, st + scrAmt);
22567 // Scroll up if the window is scrolled down and the top of the object
22568 // goes above the top border
22569 if ( y < st && st > 0 && y - st < thresh ) {
22570 window.scrollTo(sl, st - scrAmt);
22573 // Scroll right if the obj is beyond the right border and the cursor is
22574 // near the border.
22575 if ( right > clientW && toRight < thresh ) {
22576 window.scrollTo(sl + scrAmt, st);
22579 // Scroll left if the window has been scrolled to the right and the obj
22580 // extends past the left border
22581 if ( x < sl && sl > 0 && x - sl < thresh ) {
22582 window.scrollTo(sl - scrAmt, st);
22588 * Finds the location the element should be placed if we want to move
22589 * it to where the mouse location less the click offset would place us.
22590 * @method getTargetCoord
22591 * @param {int} iPageX the X coordinate of the click
22592 * @param {int} iPageY the Y coordinate of the click
22593 * @return an object that contains the coordinates (Object.x and Object.y)
22596 getTargetCoord: function(iPageX, iPageY) {
22599 var x = iPageX - this.deltaX;
22600 var y = iPageY - this.deltaY;
22602 if (this.constrainX) {
22603 if (x < this.minX) { x = this.minX; }
22604 if (x > this.maxX) { x = this.maxX; }
22607 if (this.constrainY) {
22608 if (y < this.minY) { y = this.minY; }
22609 if (y > this.maxY) { y = this.maxY; }
22612 x = this.getTick(x, this.xTicks);
22613 y = this.getTick(y, this.yTicks);
22620 * Sets up config options specific to this class. Overrides
22621 * Roo.dd.DragDrop, but all versions of this method through the
22622 * inheritance chain are called
22624 applyConfig: function() {
22625 Roo.dd.DD.superclass.applyConfig.call(this);
22626 this.scroll = (this.config.scroll !== false);
22630 * Event that fires prior to the onMouseDown event. Overrides
22633 b4MouseDown: function(e) {
22634 // this.resetConstraints();
22635 this.autoOffset(e.getPageX(),
22640 * Event that fires prior to the onDrag event. Overrides
22643 b4Drag: function(e) {
22644 this.setDragElPos(e.getPageX(),
22648 toString: function() {
22649 return ("DD " + this.id);
22652 //////////////////////////////////////////////////////////////////////////
22653 // Debugging ygDragDrop events that can be overridden
22654 //////////////////////////////////////////////////////////////////////////
22656 startDrag: function(x, y) {
22659 onDrag: function(e) {
22662 onDragEnter: function(e, id) {
22665 onDragOver: function(e, id) {
22668 onDragOut: function(e, id) {
22671 onDragDrop: function(e, id) {
22674 endDrag: function(e) {
22681 * Ext JS Library 1.1.1
22682 * Copyright(c) 2006-2007, Ext JS, LLC.
22684 * Originally Released Under LGPL - original licence link has changed is not relivant.
22687 * <script type="text/javascript">
22691 * @class Roo.dd.DDProxy
22692 * A DragDrop implementation that inserts an empty, bordered div into
22693 * the document that follows the cursor during drag operations. At the time of
22694 * the click, the frame div is resized to the dimensions of the linked html
22695 * element, and moved to the exact location of the linked element.
22697 * References to the "frame" element refer to the single proxy element that
22698 * was created to be dragged in place of all DDProxy elements on the
22701 * @extends Roo.dd.DD
22703 * @param {String} id the id of the linked html element
22704 * @param {String} sGroup the group of related DragDrop objects
22705 * @param {object} config an object containing configurable attributes
22706 * Valid properties for DDProxy in addition to those in DragDrop:
22707 * resizeFrame, centerFrame, dragElId
22709 Roo.dd.DDProxy = function(id, sGroup, config) {
22711 this.init(id, sGroup, config);
22717 * The default drag frame div id
22718 * @property Roo.dd.DDProxy.dragElId
22722 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22724 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22727 * By default we resize the drag frame to be the same size as the element
22728 * we want to drag (this is to get the frame effect). We can turn it off
22729 * if we want a different behavior.
22730 * @property resizeFrame
22736 * By default the frame is positioned exactly where the drag element is, so
22737 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
22738 * you do not have constraints on the obj is to have the drag frame centered
22739 * around the cursor. Set centerFrame to true for this effect.
22740 * @property centerFrame
22743 centerFrame: false,
22746 * Creates the proxy element if it does not yet exist
22747 * @method createFrame
22749 createFrame: function() {
22751 var body = document.body;
22753 if (!body || !body.firstChild) {
22754 setTimeout( function() { self.createFrame(); }, 50 );
22758 var div = this.getDragEl();
22761 div = document.createElement("div");
22762 div.id = this.dragElId;
22765 s.position = "absolute";
22766 s.visibility = "hidden";
22768 s.border = "2px solid #aaa";
22771 // appendChild can blow up IE if invoked prior to the window load event
22772 // while rendering a table. It is possible there are other scenarios
22773 // that would cause this to happen as well.
22774 body.insertBefore(div, body.firstChild);
22779 * Initialization for the drag frame element. Must be called in the
22780 * constructor of all subclasses
22781 * @method initFrame
22783 initFrame: function() {
22784 this.createFrame();
22787 applyConfig: function() {
22788 Roo.dd.DDProxy.superclass.applyConfig.call(this);
22790 this.resizeFrame = (this.config.resizeFrame !== false);
22791 this.centerFrame = (this.config.centerFrame);
22792 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22796 * Resizes the drag frame to the dimensions of the clicked object, positions
22797 * it over the object, and finally displays it
22798 * @method showFrame
22799 * @param {int} iPageX X click position
22800 * @param {int} iPageY Y click position
22803 showFrame: function(iPageX, iPageY) {
22804 var el = this.getEl();
22805 var dragEl = this.getDragEl();
22806 var s = dragEl.style;
22808 this._resizeProxy();
22810 if (this.centerFrame) {
22811 this.setDelta( Math.round(parseInt(s.width, 10)/2),
22812 Math.round(parseInt(s.height, 10)/2) );
22815 this.setDragElPos(iPageX, iPageY);
22817 Roo.fly(dragEl).show();
22821 * The proxy is automatically resized to the dimensions of the linked
22822 * element when a drag is initiated, unless resizeFrame is set to false
22823 * @method _resizeProxy
22826 _resizeProxy: function() {
22827 if (this.resizeFrame) {
22828 var el = this.getEl();
22829 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22833 // overrides Roo.dd.DragDrop
22834 b4MouseDown: function(e) {
22835 var x = e.getPageX();
22836 var y = e.getPageY();
22837 this.autoOffset(x, y);
22838 this.setDragElPos(x, y);
22841 // overrides Roo.dd.DragDrop
22842 b4StartDrag: function(x, y) {
22843 // show the drag frame
22844 this.showFrame(x, y);
22847 // overrides Roo.dd.DragDrop
22848 b4EndDrag: function(e) {
22849 Roo.fly(this.getDragEl()).hide();
22852 // overrides Roo.dd.DragDrop
22853 // By default we try to move the element to the last location of the frame.
22854 // This is so that the default behavior mirrors that of Roo.dd.DD.
22855 endDrag: function(e) {
22857 var lel = this.getEl();
22858 var del = this.getDragEl();
22860 // Show the drag frame briefly so we can get its position
22861 del.style.visibility = "";
22864 // Hide the linked element before the move to get around a Safari
22866 lel.style.visibility = "hidden";
22867 Roo.dd.DDM.moveToEl(lel, del);
22868 del.style.visibility = "hidden";
22869 lel.style.visibility = "";
22874 beforeMove : function(){
22878 afterDrag : function(){
22882 toString: function() {
22883 return ("DDProxy " + this.id);
22889 * Ext JS Library 1.1.1
22890 * Copyright(c) 2006-2007, Ext JS, LLC.
22892 * Originally Released Under LGPL - original licence link has changed is not relivant.
22895 * <script type="text/javascript">
22899 * @class Roo.dd.DDTarget
22900 * A DragDrop implementation that does not move, but can be a drop
22901 * target. You would get the same result by simply omitting implementation
22902 * for the event callbacks, but this way we reduce the processing cost of the
22903 * event listener and the callbacks.
22904 * @extends Roo.dd.DragDrop
22906 * @param {String} id the id of the element that is a drop target
22907 * @param {String} sGroup the group of related DragDrop objects
22908 * @param {object} config an object containing configurable attributes
22909 * Valid properties for DDTarget in addition to those in
22913 Roo.dd.DDTarget = function(id, sGroup, config) {
22915 this.initTarget(id, sGroup, config);
22917 if (config && (config.listeners || config.events)) {
22918 Roo.dd.DragDrop.superclass.constructor.call(this, {
22919 listeners : config.listeners || {},
22920 events : config.events || {}
22925 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22926 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22927 toString: function() {
22928 return ("DDTarget " + this.id);
22933 * Ext JS Library 1.1.1
22934 * Copyright(c) 2006-2007, Ext JS, LLC.
22936 * Originally Released Under LGPL - original licence link has changed is not relivant.
22939 * <script type="text/javascript">
22944 * @class Roo.dd.ScrollManager
22945 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22946 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22949 Roo.dd.ScrollManager = function(){
22950 var ddm = Roo.dd.DragDropMgr;
22957 var onStop = function(e){
22962 var triggerRefresh = function(){
22963 if(ddm.dragCurrent){
22964 ddm.refreshCache(ddm.dragCurrent.groups);
22968 var doScroll = function(){
22969 if(ddm.dragCurrent){
22970 var dds = Roo.dd.ScrollManager;
22972 if(proc.el.scroll(proc.dir, dds.increment)){
22976 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22981 var clearProc = function(){
22983 clearInterval(proc.id);
22990 var startProc = function(el, dir){
22991 Roo.log('scroll startproc');
22995 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22998 var onFire = function(e, isDrop){
23000 if(isDrop || !ddm.dragCurrent){ return; }
23001 var dds = Roo.dd.ScrollManager;
23002 if(!dragEl || dragEl != ddm.dragCurrent){
23003 dragEl = ddm.dragCurrent;
23004 // refresh regions on drag start
23005 dds.refreshCache();
23008 var xy = Roo.lib.Event.getXY(e);
23009 var pt = new Roo.lib.Point(xy[0], xy[1]);
23010 for(var id in els){
23011 var el = els[id], r = el._region;
23012 if(r && r.contains(pt) && el.isScrollable()){
23013 if(r.bottom - pt.y <= dds.thresh){
23015 startProc(el, "down");
23018 }else if(r.right - pt.x <= dds.thresh){
23020 startProc(el, "left");
23023 }else if(pt.y - r.top <= dds.thresh){
23025 startProc(el, "up");
23028 }else if(pt.x - r.left <= dds.thresh){
23030 startProc(el, "right");
23039 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23040 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23044 * Registers new overflow element(s) to auto scroll
23045 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23047 register : function(el){
23048 if(el instanceof Array){
23049 for(var i = 0, len = el.length; i < len; i++) {
23050 this.register(el[i]);
23056 Roo.dd.ScrollManager.els = els;
23060 * Unregisters overflow element(s) so they are no longer scrolled
23061 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23063 unregister : function(el){
23064 if(el instanceof Array){
23065 for(var i = 0, len = el.length; i < len; i++) {
23066 this.unregister(el[i]);
23075 * The number of pixels from the edge of a container the pointer needs to be to
23076 * trigger scrolling (defaults to 25)
23082 * The number of pixels to scroll in each scroll increment (defaults to 50)
23088 * The frequency of scrolls in milliseconds (defaults to 500)
23094 * True to animate the scroll (defaults to true)
23100 * The animation duration in seconds -
23101 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23107 * Manually trigger a cache refresh.
23109 refreshCache : function(){
23110 for(var id in els){
23111 if(typeof els[id] == 'object'){ // for people extending the object prototype
23112 els[id]._region = els[id].getRegion();
23119 * Ext JS Library 1.1.1
23120 * Copyright(c) 2006-2007, Ext JS, LLC.
23122 * Originally Released Under LGPL - original licence link has changed is not relivant.
23125 * <script type="text/javascript">
23130 * @class Roo.dd.Registry
23131 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
23132 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23135 Roo.dd.Registry = function(){
23138 var autoIdSeed = 0;
23140 var getId = function(el, autogen){
23141 if(typeof el == "string"){
23145 if(!id && autogen !== false){
23146 id = "roodd-" + (++autoIdSeed);
23154 * Register a drag drop element
23155 * @param {String|HTMLElement} element The id or DOM node to register
23156 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23157 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
23158 * knows how to interpret, plus there are some specific properties known to the Registry that should be
23159 * populated in the data object (if applicable):
23161 Value Description<br />
23162 --------- ------------------------------------------<br />
23163 handles Array of DOM nodes that trigger dragging<br />
23164 for the element being registered<br />
23165 isHandle True if the element passed in triggers<br />
23166 dragging itself, else false
23169 register : function(el, data){
23171 if(typeof el == "string"){
23172 el = document.getElementById(el);
23175 elements[getId(el)] = data;
23176 if(data.isHandle !== false){
23177 handles[data.ddel.id] = data;
23180 var hs = data.handles;
23181 for(var i = 0, len = hs.length; i < len; i++){
23182 handles[getId(hs[i])] = data;
23188 * Unregister a drag drop element
23189 * @param {String|HTMLElement} element The id or DOM node to unregister
23191 unregister : function(el){
23192 var id = getId(el, false);
23193 var data = elements[id];
23195 delete elements[id];
23197 var hs = data.handles;
23198 for(var i = 0, len = hs.length; i < len; i++){
23199 delete handles[getId(hs[i], false)];
23206 * Returns the handle registered for a DOM Node by id
23207 * @param {String|HTMLElement} id The DOM node or id to look up
23208 * @return {Object} handle The custom handle data
23210 getHandle : function(id){
23211 if(typeof id != "string"){ // must be element?
23214 return handles[id];
23218 * Returns the handle that is registered for the DOM node that is the target of the event
23219 * @param {Event} e The event
23220 * @return {Object} handle The custom handle data
23222 getHandleFromEvent : function(e){
23223 var t = Roo.lib.Event.getTarget(e);
23224 return t ? handles[t.id] : null;
23228 * Returns a custom data object that is registered for a DOM node by id
23229 * @param {String|HTMLElement} id The DOM node or id to look up
23230 * @return {Object} data The custom data
23232 getTarget : function(id){
23233 if(typeof id != "string"){ // must be element?
23236 return elements[id];
23240 * Returns a custom data object that is registered for the DOM node that is the target of the event
23241 * @param {Event} e The event
23242 * @return {Object} data The custom data
23244 getTargetFromEvent : function(e){
23245 var t = Roo.lib.Event.getTarget(e);
23246 return t ? elements[t.id] || handles[t.id] : null;
23251 * Ext JS Library 1.1.1
23252 * Copyright(c) 2006-2007, Ext JS, LLC.
23254 * Originally Released Under LGPL - original licence link has changed is not relivant.
23257 * <script type="text/javascript">
23262 * @class Roo.dd.StatusProxy
23263 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
23264 * default drag proxy used by all Roo.dd components.
23266 * @param {Object} config
23268 Roo.dd.StatusProxy = function(config){
23269 Roo.apply(this, config);
23270 this.id = this.id || Roo.id();
23271 this.el = new Roo.Layer({
23273 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23274 {tag: "div", cls: "x-dd-drop-icon"},
23275 {tag: "div", cls: "x-dd-drag-ghost"}
23278 shadow: !config || config.shadow !== false
23280 this.ghost = Roo.get(this.el.dom.childNodes[1]);
23281 this.dropStatus = this.dropNotAllowed;
23284 Roo.dd.StatusProxy.prototype = {
23286 * @cfg {String} dropAllowed
23287 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23289 dropAllowed : "x-dd-drop-ok",
23291 * @cfg {String} dropNotAllowed
23292 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23294 dropNotAllowed : "x-dd-drop-nodrop",
23297 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23298 * over the current target element.
23299 * @param {String} cssClass The css class for the new drop status indicator image
23301 setStatus : function(cssClass){
23302 cssClass = cssClass || this.dropNotAllowed;
23303 if(this.dropStatus != cssClass){
23304 this.el.replaceClass(this.dropStatus, cssClass);
23305 this.dropStatus = cssClass;
23310 * Resets the status indicator to the default dropNotAllowed value
23311 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23313 reset : function(clearGhost){
23314 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23315 this.dropStatus = this.dropNotAllowed;
23317 this.ghost.update("");
23322 * Updates the contents of the ghost element
23323 * @param {String} html The html that will replace the current innerHTML of the ghost element
23325 update : function(html){
23326 if(typeof html == "string"){
23327 this.ghost.update(html);
23329 this.ghost.update("");
23330 html.style.margin = "0";
23331 this.ghost.dom.appendChild(html);
23333 // ensure float = none set?? cant remember why though.
23334 var el = this.ghost.dom.firstChild;
23336 Roo.fly(el).setStyle('float', 'none');
23341 * Returns the underlying proxy {@link Roo.Layer}
23342 * @return {Roo.Layer} el
23344 getEl : function(){
23349 * Returns the ghost element
23350 * @return {Roo.Element} el
23352 getGhost : function(){
23358 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23360 hide : function(clear){
23368 * Stops the repair animation if it's currently running
23371 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23377 * Displays this proxy
23384 * Force the Layer to sync its shadow and shim positions to the element
23391 * Causes the proxy to return to its position of origin via an animation. Should be called after an
23392 * invalid drop operation by the item being dragged.
23393 * @param {Array} xy The XY position of the element ([x, y])
23394 * @param {Function} callback The function to call after the repair is complete
23395 * @param {Object} scope The scope in which to execute the callback
23397 repair : function(xy, callback, scope){
23398 this.callback = callback;
23399 this.scope = scope;
23400 if(xy && this.animRepair !== false){
23401 this.el.addClass("x-dd-drag-repair");
23402 this.el.hideUnders(true);
23403 this.anim = this.el.shift({
23404 duration: this.repairDuration || .5,
23408 callback: this.afterRepair,
23412 this.afterRepair();
23417 afterRepair : function(){
23419 if(typeof this.callback == "function"){
23420 this.callback.call(this.scope || this);
23422 this.callback = null;
23427 * Ext JS Library 1.1.1
23428 * Copyright(c) 2006-2007, Ext JS, LLC.
23430 * Originally Released Under LGPL - original licence link has changed is not relivant.
23433 * <script type="text/javascript">
23437 * @class Roo.dd.DragSource
23438 * @extends Roo.dd.DDProxy
23439 * A simple class that provides the basic implementation needed to make any element draggable.
23441 * @param {String/HTMLElement/Element} el The container element
23442 * @param {Object} config
23444 Roo.dd.DragSource = function(el, config){
23445 this.el = Roo.get(el);
23446 this.dragData = {};
23448 Roo.apply(this, config);
23451 this.proxy = new Roo.dd.StatusProxy();
23454 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23455 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23457 this.dragging = false;
23460 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23462 * @cfg {String} dropAllowed
23463 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23465 dropAllowed : "x-dd-drop-ok",
23467 * @cfg {String} dropNotAllowed
23468 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23470 dropNotAllowed : "x-dd-drop-nodrop",
23473 * Returns the data object associated with this drag source
23474 * @return {Object} data An object containing arbitrary data
23476 getDragData : function(e){
23477 return this.dragData;
23481 onDragEnter : function(e, id){
23482 var target = Roo.dd.DragDropMgr.getDDById(id);
23483 this.cachedTarget = target;
23484 if(this.beforeDragEnter(target, e, id) !== false){
23485 if(target.isNotifyTarget){
23486 var status = target.notifyEnter(this, e, this.dragData);
23487 this.proxy.setStatus(status);
23489 this.proxy.setStatus(this.dropAllowed);
23492 if(this.afterDragEnter){
23494 * An empty function by default, but provided so that you can perform a custom action
23495 * when the dragged item enters the drop target by providing an implementation.
23496 * @param {Roo.dd.DragDrop} target The drop target
23497 * @param {Event} e The event object
23498 * @param {String} id The id of the dragged element
23499 * @method afterDragEnter
23501 this.afterDragEnter(target, e, id);
23507 * An empty function by default, but provided so that you can perform a custom action
23508 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23509 * @param {Roo.dd.DragDrop} target The drop target
23510 * @param {Event} e The event object
23511 * @param {String} id The id of the dragged element
23512 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23514 beforeDragEnter : function(target, e, id){
23519 alignElWithMouse: function() {
23520 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23525 onDragOver : function(e, id){
23526 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23527 if(this.beforeDragOver(target, e, id) !== false){
23528 if(target.isNotifyTarget){
23529 var status = target.notifyOver(this, e, this.dragData);
23530 this.proxy.setStatus(status);
23533 if(this.afterDragOver){
23535 * An empty function by default, but provided so that you can perform a custom action
23536 * while the dragged item is over the drop target by providing an implementation.
23537 * @param {Roo.dd.DragDrop} target The drop target
23538 * @param {Event} e The event object
23539 * @param {String} id The id of the dragged element
23540 * @method afterDragOver
23542 this.afterDragOver(target, e, id);
23548 * An empty function by default, but provided so that you can perform a custom action
23549 * while the dragged item is over the drop target and optionally cancel the onDragOver.
23550 * @param {Roo.dd.DragDrop} target The drop target
23551 * @param {Event} e The event object
23552 * @param {String} id The id of the dragged element
23553 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23555 beforeDragOver : function(target, e, id){
23560 onDragOut : function(e, id){
23561 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23562 if(this.beforeDragOut(target, e, id) !== false){
23563 if(target.isNotifyTarget){
23564 target.notifyOut(this, e, this.dragData);
23566 this.proxy.reset();
23567 if(this.afterDragOut){
23569 * An empty function by default, but provided so that you can perform a custom action
23570 * after the dragged item is dragged out of the target without dropping.
23571 * @param {Roo.dd.DragDrop} target The drop target
23572 * @param {Event} e The event object
23573 * @param {String} id The id of the dragged element
23574 * @method afterDragOut
23576 this.afterDragOut(target, e, id);
23579 this.cachedTarget = null;
23583 * An empty function by default, but provided so that you can perform a custom action before the dragged
23584 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23585 * @param {Roo.dd.DragDrop} target The drop target
23586 * @param {Event} e The event object
23587 * @param {String} id The id of the dragged element
23588 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23590 beforeDragOut : function(target, e, id){
23595 onDragDrop : function(e, id){
23596 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23597 if(this.beforeDragDrop(target, e, id) !== false){
23598 if(target.isNotifyTarget){
23599 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23600 this.onValidDrop(target, e, id);
23602 this.onInvalidDrop(target, e, id);
23605 this.onValidDrop(target, e, id);
23608 if(this.afterDragDrop){
23610 * An empty function by default, but provided so that you can perform a custom action
23611 * after a valid drag drop has occurred by providing an implementation.
23612 * @param {Roo.dd.DragDrop} target The drop target
23613 * @param {Event} e The event object
23614 * @param {String} id The id of the dropped element
23615 * @method afterDragDrop
23617 this.afterDragDrop(target, e, id);
23620 delete this.cachedTarget;
23624 * An empty function by default, but provided so that you can perform a custom action before the dragged
23625 * item is dropped onto the target and optionally cancel the onDragDrop.
23626 * @param {Roo.dd.DragDrop} target The drop target
23627 * @param {Event} e The event object
23628 * @param {String} id The id of the dragged element
23629 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23631 beforeDragDrop : function(target, e, id){
23636 onValidDrop : function(target, e, id){
23638 if(this.afterValidDrop){
23640 * An empty function by default, but provided so that you can perform a custom action
23641 * after a valid drop has occurred by providing an implementation.
23642 * @param {Object} target The target DD
23643 * @param {Event} e The event object
23644 * @param {String} id The id of the dropped element
23645 * @method afterInvalidDrop
23647 this.afterValidDrop(target, e, id);
23652 getRepairXY : function(e, data){
23653 return this.el.getXY();
23657 onInvalidDrop : function(target, e, id){
23658 this.beforeInvalidDrop(target, e, id);
23659 if(this.cachedTarget){
23660 if(this.cachedTarget.isNotifyTarget){
23661 this.cachedTarget.notifyOut(this, e, this.dragData);
23663 this.cacheTarget = null;
23665 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23667 if(this.afterInvalidDrop){
23669 * An empty function by default, but provided so that you can perform a custom action
23670 * after an invalid drop has occurred by providing an implementation.
23671 * @param {Event} e The event object
23672 * @param {String} id The id of the dropped element
23673 * @method afterInvalidDrop
23675 this.afterInvalidDrop(e, id);
23680 afterRepair : function(){
23682 this.el.highlight(this.hlColor || "c3daf9");
23684 this.dragging = false;
23688 * An empty function by default, but provided so that you can perform a custom action after an invalid
23689 * drop has occurred.
23690 * @param {Roo.dd.DragDrop} target The drop target
23691 * @param {Event} e The event object
23692 * @param {String} id The id of the dragged element
23693 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23695 beforeInvalidDrop : function(target, e, id){
23700 handleMouseDown : function(e){
23701 if(this.dragging) {
23704 var data = this.getDragData(e);
23705 if(data && this.onBeforeDrag(data, e) !== false){
23706 this.dragData = data;
23708 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23713 * An empty function by default, but provided so that you can perform a custom action before the initial
23714 * drag event begins and optionally cancel it.
23715 * @param {Object} data An object containing arbitrary data to be shared with drop targets
23716 * @param {Event} e The event object
23717 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23719 onBeforeDrag : function(data, e){
23724 * An empty function by default, but provided so that you can perform a custom action once the initial
23725 * drag event has begun. The drag cannot be canceled from this function.
23726 * @param {Number} x The x position of the click on the dragged object
23727 * @param {Number} y The y position of the click on the dragged object
23729 onStartDrag : Roo.emptyFn,
23731 // private - YUI override
23732 startDrag : function(x, y){
23733 this.proxy.reset();
23734 this.dragging = true;
23735 this.proxy.update("");
23736 this.onInitDrag(x, y);
23741 onInitDrag : function(x, y){
23742 var clone = this.el.dom.cloneNode(true);
23743 clone.id = Roo.id(); // prevent duplicate ids
23744 this.proxy.update(clone);
23745 this.onStartDrag(x, y);
23750 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23751 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23753 getProxy : function(){
23758 * Hides the drag source's {@link Roo.dd.StatusProxy}
23760 hideProxy : function(){
23762 this.proxy.reset(true);
23763 this.dragging = false;
23767 triggerCacheRefresh : function(){
23768 Roo.dd.DDM.refreshCache(this.groups);
23771 // private - override to prevent hiding
23772 b4EndDrag: function(e) {
23775 // private - override to prevent moving
23776 endDrag : function(e){
23777 this.onEndDrag(this.dragData, e);
23781 onEndDrag : function(data, e){
23784 // private - pin to cursor
23785 autoOffset : function(x, y) {
23786 this.setDelta(-12, -20);
23790 * Ext JS Library 1.1.1
23791 * Copyright(c) 2006-2007, Ext JS, LLC.
23793 * Originally Released Under LGPL - original licence link has changed is not relivant.
23796 * <script type="text/javascript">
23801 * @class Roo.dd.DropTarget
23802 * @extends Roo.dd.DDTarget
23803 * A simple class that provides the basic implementation needed to make any element a drop target that can have
23804 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
23806 * @param {String/HTMLElement/Element} el The container element
23807 * @param {Object} config
23809 Roo.dd.DropTarget = function(el, config){
23810 this.el = Roo.get(el);
23812 var listeners = false; ;
23813 if (config && config.listeners) {
23814 listeners= config.listeners;
23815 delete config.listeners;
23817 Roo.apply(this, config);
23819 if(this.containerScroll){
23820 Roo.dd.ScrollManager.register(this.el);
23824 * @scope Roo.dd.DropTarget
23829 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23830 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
23831 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
23833 * IMPORTANT : it should set this.valid to true|false
23835 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23836 * @param {Event} e The event
23837 * @param {Object} data An object containing arbitrary data supplied by the drag source
23843 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23844 * This method will be called on every mouse movement while the drag source is over the drop target.
23845 * This default implementation simply returns the dropAllowed config value.
23847 * IMPORTANT : it should set this.valid to true|false
23849 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23850 * @param {Event} e The event
23851 * @param {Object} data An object containing arbitrary data supplied by the drag source
23857 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23858 * out of the target without dropping. This default implementation simply removes the CSS class specified by
23859 * overClass (if any) from the drop element.
23862 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23863 * @param {Event} e The event
23864 * @param {Object} data An object containing arbitrary data supplied by the drag source
23870 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23871 * been dropped on it. This method has no default implementation and returns false, so you must provide an
23872 * implementation that does something to process the drop event and returns true so that the drag source's
23873 * repair action does not run.
23875 * IMPORTANT : it should set this.success
23877 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23878 * @param {Event} e The event
23879 * @param {Object} data An object containing arbitrary data supplied by the drag source
23885 Roo.dd.DropTarget.superclass.constructor.call( this,
23887 this.ddGroup || this.group,
23890 listeners : listeners || {}
23898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23900 * @cfg {String} overClass
23901 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23904 * @cfg {String} ddGroup
23905 * The drag drop group to handle drop events for
23909 * @cfg {String} dropAllowed
23910 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23912 dropAllowed : "x-dd-drop-ok",
23914 * @cfg {String} dropNotAllowed
23915 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23917 dropNotAllowed : "x-dd-drop-nodrop",
23919 * @cfg {boolean} success
23920 * set this after drop listener..
23924 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23925 * if the drop point is valid for over/enter..
23932 isNotifyTarget : true,
23937 notifyEnter : function(dd, e, data)
23940 this.fireEvent('enter', dd, e, data);
23941 if(this.overClass){
23942 this.el.addClass(this.overClass);
23944 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23945 this.valid ? this.dropAllowed : this.dropNotAllowed
23952 notifyOver : function(dd, e, data)
23955 this.fireEvent('over', dd, e, data);
23956 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23957 this.valid ? this.dropAllowed : this.dropNotAllowed
23964 notifyOut : function(dd, e, data)
23966 this.fireEvent('out', dd, e, data);
23967 if(this.overClass){
23968 this.el.removeClass(this.overClass);
23975 notifyDrop : function(dd, e, data)
23977 this.success = false;
23978 this.fireEvent('drop', dd, e, data);
23979 return this.success;
23983 * Ext JS Library 1.1.1
23984 * Copyright(c) 2006-2007, Ext JS, LLC.
23986 * Originally Released Under LGPL - original licence link has changed is not relivant.
23989 * <script type="text/javascript">
23994 * @class Roo.dd.DragZone
23995 * @extends Roo.dd.DragSource
23996 * This class provides a container DD instance that proxies for multiple child node sources.<br />
23997 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23999 * @param {String/HTMLElement/Element} el The container element
24000 * @param {Object} config
24002 Roo.dd.DragZone = function(el, config){
24003 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24004 if(this.containerScroll){
24005 Roo.dd.ScrollManager.register(this.el);
24009 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24011 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24012 * for auto scrolling during drag operations.
24015 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24016 * method after a failed drop (defaults to "c3daf9" - light blue)
24020 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24021 * for a valid target to drag based on the mouse down. Override this method
24022 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24023 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24024 * @param {EventObject} e The mouse down event
24025 * @return {Object} The dragData
24027 getDragData : function(e){
24028 return Roo.dd.Registry.getHandleFromEvent(e);
24032 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24033 * this.dragData.ddel
24034 * @param {Number} x The x position of the click on the dragged object
24035 * @param {Number} y The y position of the click on the dragged object
24036 * @return {Boolean} true to continue the drag, false to cancel
24038 onInitDrag : function(x, y){
24039 this.proxy.update(this.dragData.ddel.cloneNode(true));
24040 this.onStartDrag(x, y);
24045 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
24047 afterRepair : function(){
24049 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24051 this.dragging = false;
24055 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24056 * the XY of this.dragData.ddel
24057 * @param {EventObject} e The mouse up event
24058 * @return {Array} The xy location (e.g. [100, 200])
24060 getRepairXY : function(e){
24061 return Roo.Element.fly(this.dragData.ddel).getXY();
24065 * Ext JS Library 1.1.1
24066 * Copyright(c) 2006-2007, Ext JS, LLC.
24068 * Originally Released Under LGPL - original licence link has changed is not relivant.
24071 * <script type="text/javascript">
24074 * @class Roo.dd.DropZone
24075 * @extends Roo.dd.DropTarget
24076 * This class provides a container DD instance that proxies for multiple child node targets.<br />
24077 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24079 * @param {String/HTMLElement/Element} el The container element
24080 * @param {Object} config
24082 Roo.dd.DropZone = function(el, config){
24083 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24086 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24088 * Returns a custom data object associated with the DOM node that is the target of the event. By default
24089 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24090 * provide your own custom lookup.
24091 * @param {Event} e The event
24092 * @return {Object} data The custom data
24094 getTargetFromEvent : function(e){
24095 return Roo.dd.Registry.getTargetFromEvent(e);
24099 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24100 * that it has registered. This method has no default implementation and should be overridden to provide
24101 * node-specific processing if necessary.
24102 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24103 * {@link #getTargetFromEvent} for this node)
24104 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24105 * @param {Event} e The event
24106 * @param {Object} data An object containing arbitrary data supplied by the drag source
24108 onNodeEnter : function(n, dd, e, data){
24113 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24114 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
24115 * overridden to provide the proper feedback.
24116 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24117 * {@link #getTargetFromEvent} for this node)
24118 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24119 * @param {Event} e The event
24120 * @param {Object} data An object containing arbitrary data supplied by the drag source
24121 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24122 * underlying {@link Roo.dd.StatusProxy} can be updated
24124 onNodeOver : function(n, dd, e, data){
24125 return this.dropAllowed;
24129 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24130 * the drop node without dropping. This method has no default implementation and should be overridden to provide
24131 * node-specific processing if necessary.
24132 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24133 * {@link #getTargetFromEvent} for this node)
24134 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24135 * @param {Event} e The event
24136 * @param {Object} data An object containing arbitrary data supplied by the drag source
24138 onNodeOut : function(n, dd, e, data){
24143 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24144 * the drop node. The default implementation returns false, so it should be overridden to provide the
24145 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24146 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24147 * {@link #getTargetFromEvent} for this node)
24148 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24149 * @param {Event} e The event
24150 * @param {Object} data An object containing arbitrary data supplied by the drag source
24151 * @return {Boolean} True if the drop was valid, else false
24153 onNodeDrop : function(n, dd, e, data){
24158 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24159 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
24160 * it should be overridden to provide the proper feedback if necessary.
24161 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24162 * @param {Event} e The event
24163 * @param {Object} data An object containing arbitrary data supplied by the drag source
24164 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24165 * underlying {@link Roo.dd.StatusProxy} can be updated
24167 onContainerOver : function(dd, e, data){
24168 return this.dropNotAllowed;
24172 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24173 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
24174 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24175 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
24176 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24177 * @param {Event} e The event
24178 * @param {Object} data An object containing arbitrary data supplied by the drag source
24179 * @return {Boolean} True if the drop was valid, else false
24181 onContainerDrop : function(dd, e, data){
24186 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24187 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
24188 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24189 * you should override this method and provide a custom implementation.
24190 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24191 * @param {Event} e The event
24192 * @param {Object} data An object containing arbitrary data supplied by the drag source
24193 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24194 * underlying {@link Roo.dd.StatusProxy} can be updated
24196 notifyEnter : function(dd, e, data){
24197 return this.dropNotAllowed;
24201 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24202 * This method will be called on every mouse movement while the drag source is over the drop zone.
24203 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24204 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24205 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24206 * registered node, it will call {@link #onContainerOver}.
24207 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24208 * @param {Event} e The event
24209 * @param {Object} data An object containing arbitrary data supplied by the drag source
24210 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24211 * underlying {@link Roo.dd.StatusProxy} can be updated
24213 notifyOver : function(dd, e, data){
24214 var n = this.getTargetFromEvent(e);
24215 if(!n){ // not over valid drop target
24216 if(this.lastOverNode){
24217 this.onNodeOut(this.lastOverNode, dd, e, data);
24218 this.lastOverNode = null;
24220 return this.onContainerOver(dd, e, data);
24222 if(this.lastOverNode != n){
24223 if(this.lastOverNode){
24224 this.onNodeOut(this.lastOverNode, dd, e, data);
24226 this.onNodeEnter(n, dd, e, data);
24227 this.lastOverNode = n;
24229 return this.onNodeOver(n, dd, e, data);
24233 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24234 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
24235 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24236 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24237 * @param {Event} e The event
24238 * @param {Object} data An object containing arbitrary data supplied by the drag zone
24240 notifyOut : function(dd, e, data){
24241 if(this.lastOverNode){
24242 this.onNodeOut(this.lastOverNode, dd, e, data);
24243 this.lastOverNode = null;
24248 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24249 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
24250 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24251 * otherwise it will call {@link #onContainerDrop}.
24252 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24253 * @param {Event} e The event
24254 * @param {Object} data An object containing arbitrary data supplied by the drag source
24255 * @return {Boolean} True if the drop was valid, else false
24257 notifyDrop : function(dd, e, data){
24258 if(this.lastOverNode){
24259 this.onNodeOut(this.lastOverNode, dd, e, data);
24260 this.lastOverNode = null;
24262 var n = this.getTargetFromEvent(e);
24264 this.onNodeDrop(n, dd, e, data) :
24265 this.onContainerDrop(dd, e, data);
24269 triggerCacheRefresh : function(){
24270 Roo.dd.DDM.refreshCache(this.groups);
24274 * Ext JS Library 1.1.1
24275 * Copyright(c) 2006-2007, Ext JS, LLC.
24277 * Originally Released Under LGPL - original licence link has changed is not relivant.
24280 * <script type="text/javascript">
24285 * @class Roo.data.SortTypes
24287 * Defines the default sorting (casting?) comparison functions used when sorting data.
24289 Roo.data.SortTypes = {
24291 * Default sort that does nothing
24292 * @param {Mixed} s The value being converted
24293 * @return {Mixed} The comparison value
24295 none : function(s){
24300 * The regular expression used to strip tags
24304 stripTagsRE : /<\/?[^>]+>/gi,
24307 * Strips all HTML tags to sort on text only
24308 * @param {Mixed} s The value being converted
24309 * @return {String} The comparison value
24311 asText : function(s){
24312 return String(s).replace(this.stripTagsRE, "");
24316 * Strips all HTML tags to sort on text only - Case insensitive
24317 * @param {Mixed} s The value being converted
24318 * @return {String} The comparison value
24320 asUCText : function(s){
24321 return String(s).toUpperCase().replace(this.stripTagsRE, "");
24325 * Case insensitive string
24326 * @param {Mixed} s The value being converted
24327 * @return {String} The comparison value
24329 asUCString : function(s) {
24330 return String(s).toUpperCase();
24335 * @param {Mixed} s The value being converted
24336 * @return {Number} The comparison value
24338 asDate : function(s) {
24342 if(s instanceof Date){
24343 return s.getTime();
24345 return Date.parse(String(s));
24350 * @param {Mixed} s The value being converted
24351 * @return {Float} The comparison value
24353 asFloat : function(s) {
24354 var val = parseFloat(String(s).replace(/,/g, ""));
24363 * @param {Mixed} s The value being converted
24364 * @return {Number} The comparison value
24366 asInt : function(s) {
24367 var val = parseInt(String(s).replace(/,/g, ""));
24375 * Ext JS Library 1.1.1
24376 * Copyright(c) 2006-2007, Ext JS, LLC.
24378 * Originally Released Under LGPL - original licence link has changed is not relivant.
24381 * <script type="text/javascript">
24385 * @class Roo.data.Record
24386 * Instances of this class encapsulate both record <em>definition</em> information, and record
24387 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24388 * to access Records cached in an {@link Roo.data.Store} object.<br>
24390 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24391 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24394 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24396 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24397 * {@link #create}. The parameters are the same.
24398 * @param {Array} data An associative Array of data values keyed by the field name.
24399 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24400 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24401 * not specified an integer id is generated.
24403 Roo.data.Record = function(data, id){
24404 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24409 * Generate a constructor for a specific record layout.
24410 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24411 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24412 * Each field definition object may contain the following properties: <ul>
24413 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
24414 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24415 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24416 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24417 * is being used, then this is a string containing the javascript expression to reference the data relative to
24418 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24419 * to the data item relative to the record element. If the mapping expression is the same as the field name,
24420 * this may be omitted.</p></li>
24421 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24422 * <ul><li>auto (Default, implies no conversion)</li>
24427 * <li>date</li></ul></p></li>
24428 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24429 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24430 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24431 * by the Reader into an object that will be stored in the Record. It is passed the
24432 * following parameters:<ul>
24433 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24435 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24437 * <br>usage:<br><pre><code>
24438 var TopicRecord = Roo.data.Record.create(
24439 {name: 'title', mapping: 'topic_title'},
24440 {name: 'author', mapping: 'username'},
24441 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24442 {name: 'lastPost', mapping: 'post_time', type: 'date'},
24443 {name: 'lastPoster', mapping: 'user2'},
24444 {name: 'excerpt', mapping: 'post_text'}
24447 var myNewRecord = new TopicRecord({
24448 title: 'Do my job please',
24451 lastPost: new Date(),
24452 lastPoster: 'Animal',
24453 excerpt: 'No way dude!'
24455 myStore.add(myNewRecord);
24460 Roo.data.Record.create = function(o){
24461 var f = function(){
24462 f.superclass.constructor.apply(this, arguments);
24464 Roo.extend(f, Roo.data.Record);
24465 var p = f.prototype;
24466 p.fields = new Roo.util.MixedCollection(false, function(field){
24469 for(var i = 0, len = o.length; i < len; i++){
24470 p.fields.add(new Roo.data.Field(o[i]));
24472 f.getField = function(name){
24473 return p.fields.get(name);
24478 Roo.data.Record.AUTO_ID = 1000;
24479 Roo.data.Record.EDIT = 'edit';
24480 Roo.data.Record.REJECT = 'reject';
24481 Roo.data.Record.COMMIT = 'commit';
24483 Roo.data.Record.prototype = {
24485 * Readonly flag - true if this record has been modified.
24494 join : function(store){
24495 this.store = store;
24499 * Set the named field to the specified value.
24500 * @param {String} name The name of the field to set.
24501 * @param {Object} value The value to set the field to.
24503 set : function(name, value){
24504 if(this.data[name] == value){
24508 if(!this.modified){
24509 this.modified = {};
24511 if(typeof this.modified[name] == 'undefined'){
24512 this.modified[name] = this.data[name];
24514 this.data[name] = value;
24515 if(!this.editing && this.store){
24516 this.store.afterEdit(this);
24521 * Get the value of the named field.
24522 * @param {String} name The name of the field to get the value of.
24523 * @return {Object} The value of the field.
24525 get : function(name){
24526 return this.data[name];
24530 beginEdit : function(){
24531 this.editing = true;
24532 this.modified = {};
24536 cancelEdit : function(){
24537 this.editing = false;
24538 delete this.modified;
24542 endEdit : function(){
24543 this.editing = false;
24544 if(this.dirty && this.store){
24545 this.store.afterEdit(this);
24550 * Usually called by the {@link Roo.data.Store} which owns the Record.
24551 * Rejects all changes made to the Record since either creation, or the last commit operation.
24552 * Modified fields are reverted to their original values.
24554 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24555 * of reject operations.
24557 reject : function(){
24558 var m = this.modified;
24560 if(typeof m[n] != "function"){
24561 this.data[n] = m[n];
24564 this.dirty = false;
24565 delete this.modified;
24566 this.editing = false;
24568 this.store.afterReject(this);
24573 * Usually called by the {@link Roo.data.Store} which owns the Record.
24574 * Commits all changes made to the Record since either creation, or the last commit operation.
24576 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24577 * of commit operations.
24579 commit : function(){
24580 this.dirty = false;
24581 delete this.modified;
24582 this.editing = false;
24584 this.store.afterCommit(this);
24589 hasError : function(){
24590 return this.error != null;
24594 clearError : function(){
24599 * Creates a copy of this record.
24600 * @param {String} id (optional) A new record id if you don't want to use this record's id
24603 copy : function(newId) {
24604 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24608 * Ext JS Library 1.1.1
24609 * Copyright(c) 2006-2007, Ext JS, LLC.
24611 * Originally Released Under LGPL - original licence link has changed is not relivant.
24614 * <script type="text/javascript">
24620 * @class Roo.data.Store
24621 * @extends Roo.util.Observable
24622 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24623 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24625 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
24626 * has no knowledge of the format of the data returned by the Proxy.<br>
24628 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24629 * instances from the data object. These records are cached and made available through accessor functions.
24631 * Creates a new Store.
24632 * @param {Object} config A config object containing the objects needed for the Store to access data,
24633 * and read the data into Records.
24635 Roo.data.Store = function(config){
24636 this.data = new Roo.util.MixedCollection(false);
24637 this.data.getKey = function(o){
24640 this.baseParams = {};
24642 this.paramNames = {
24647 "multisort" : "_multisort"
24650 if(config && config.data){
24651 this.inlineData = config.data;
24652 delete config.data;
24655 Roo.apply(this, config);
24657 if(this.reader){ // reader passed
24658 this.reader = Roo.factory(this.reader, Roo.data);
24659 this.reader.xmodule = this.xmodule || false;
24660 if(!this.recordType){
24661 this.recordType = this.reader.recordType;
24663 if(this.reader.onMetaChange){
24664 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24668 if(this.recordType){
24669 this.fields = this.recordType.prototype.fields;
24671 this.modified = [];
24675 * @event datachanged
24676 * Fires when the data cache has changed, and a widget which is using this Store
24677 * as a Record cache should refresh its view.
24678 * @param {Store} this
24680 datachanged : true,
24682 * @event metachange
24683 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24684 * @param {Store} this
24685 * @param {Object} meta The JSON metadata
24690 * Fires when Records have been added to the Store
24691 * @param {Store} this
24692 * @param {Roo.data.Record[]} records The array of Records added
24693 * @param {Number} index The index at which the record(s) were added
24698 * Fires when a Record has been removed from the Store
24699 * @param {Store} this
24700 * @param {Roo.data.Record} record The Record that was removed
24701 * @param {Number} index The index at which the record was removed
24706 * Fires when a Record has been updated
24707 * @param {Store} this
24708 * @param {Roo.data.Record} record The Record that was updated
24709 * @param {String} operation The update operation being performed. Value may be one of:
24711 Roo.data.Record.EDIT
24712 Roo.data.Record.REJECT
24713 Roo.data.Record.COMMIT
24719 * Fires when the data cache has been cleared.
24720 * @param {Store} this
24724 * @event beforeload
24725 * Fires before a request is made for a new data object. If the beforeload handler returns false
24726 * the load action will be canceled.
24727 * @param {Store} this
24728 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24732 * @event beforeloadadd
24733 * Fires after a new set of Records has been loaded.
24734 * @param {Store} this
24735 * @param {Roo.data.Record[]} records The Records that were loaded
24736 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24738 beforeloadadd : true,
24741 * Fires after a new set of Records has been loaded, before they are added to the store.
24742 * @param {Store} this
24743 * @param {Roo.data.Record[]} records The Records that were loaded
24744 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24745 * @params {Object} return from reader
24749 * @event loadexception
24750 * Fires if an exception occurs in the Proxy during loading.
24751 * Called with the signature of the Proxy's "loadexception" event.
24752 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24755 * @param {Object} return from JsonData.reader() - success, totalRecords, records
24756 * @param {Object} load options
24757 * @param {Object} jsonData from your request (normally this contains the Exception)
24759 loadexception : true
24763 this.proxy = Roo.factory(this.proxy, Roo.data);
24764 this.proxy.xmodule = this.xmodule || false;
24765 this.relayEvents(this.proxy, ["loadexception"]);
24767 this.sortToggle = {};
24768 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24770 Roo.data.Store.superclass.constructor.call(this);
24772 if(this.inlineData){
24773 this.loadData(this.inlineData);
24774 delete this.inlineData;
24778 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24780 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
24781 * without a remote query - used by combo/forms at present.
24785 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24788 * @cfg {Array} data Inline data to be loaded when the store is initialized.
24791 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
24792 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24795 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24796 * on any HTTP request
24799 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24802 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24806 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24807 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24809 remoteSort : false,
24812 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24813 * loaded or when a record is removed. (defaults to false).
24815 pruneModifiedRecords : false,
24818 lastOptions : null,
24821 * Add Records to the Store and fires the add event.
24822 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24824 add : function(records){
24825 records = [].concat(records);
24826 for(var i = 0, len = records.length; i < len; i++){
24827 records[i].join(this);
24829 var index = this.data.length;
24830 this.data.addAll(records);
24831 this.fireEvent("add", this, records, index);
24835 * Remove a Record from the Store and fires the remove event.
24836 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24838 remove : function(record){
24839 var index = this.data.indexOf(record);
24840 this.data.removeAt(index);
24842 if(this.pruneModifiedRecords){
24843 this.modified.remove(record);
24845 this.fireEvent("remove", this, record, index);
24849 * Remove all Records from the Store and fires the clear event.
24851 removeAll : function(){
24853 if(this.pruneModifiedRecords){
24854 this.modified = [];
24856 this.fireEvent("clear", this);
24860 * Inserts Records to the Store at the given index and fires the add event.
24861 * @param {Number} index The start index at which to insert the passed Records.
24862 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24864 insert : function(index, records){
24865 records = [].concat(records);
24866 for(var i = 0, len = records.length; i < len; i++){
24867 this.data.insert(index, records[i]);
24868 records[i].join(this);
24870 this.fireEvent("add", this, records, index);
24874 * Get the index within the cache of the passed Record.
24875 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24876 * @return {Number} The index of the passed Record. Returns -1 if not found.
24878 indexOf : function(record){
24879 return this.data.indexOf(record);
24883 * Get the index within the cache of the Record with the passed id.
24884 * @param {String} id The id of the Record to find.
24885 * @return {Number} The index of the Record. Returns -1 if not found.
24887 indexOfId : function(id){
24888 return this.data.indexOfKey(id);
24892 * Get the Record with the specified id.
24893 * @param {String} id The id of the Record to find.
24894 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24896 getById : function(id){
24897 return this.data.key(id);
24901 * Get the Record at the specified index.
24902 * @param {Number} index The index of the Record to find.
24903 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24905 getAt : function(index){
24906 return this.data.itemAt(index);
24910 * Returns a range of Records between specified indices.
24911 * @param {Number} startIndex (optional) The starting index (defaults to 0)
24912 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24913 * @return {Roo.data.Record[]} An array of Records
24915 getRange : function(start, end){
24916 return this.data.getRange(start, end);
24920 storeOptions : function(o){
24921 o = Roo.apply({}, o);
24924 this.lastOptions = o;
24928 * Loads the Record cache from the configured Proxy using the configured Reader.
24930 * If using remote paging, then the first load call must specify the <em>start</em>
24931 * and <em>limit</em> properties in the options.params property to establish the initial
24932 * position within the dataset, and the number of Records to cache on each read from the Proxy.
24934 * <strong>It is important to note that for remote data sources, loading is asynchronous,
24935 * and this call will return before the new data has been loaded. Perform any post-processing
24936 * in a callback function, or in a "load" event handler.</strong>
24938 * @param {Object} options An object containing properties which control loading options:<ul>
24939 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24940 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24943 data : data, // array of key=>value data like JsonReader
24944 total : data.length,
24950 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24951 * passed the following arguments:<ul>
24952 * <li>r : Roo.data.Record[]</li>
24953 * <li>options: Options object from the load call</li>
24954 * <li>success: Boolean success indicator</li></ul></li>
24955 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24956 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24959 load : function(options){
24960 options = options || {};
24961 if(this.fireEvent("beforeload", this, options) !== false){
24962 this.storeOptions(options);
24963 var p = Roo.apply(options.params || {}, this.baseParams);
24964 // if meta was not loaded from remote source.. try requesting it.
24965 if (!this.reader.metaFromRemote) {
24966 p._requestMeta = 1;
24968 if(this.sortInfo && this.remoteSort){
24969 var pn = this.paramNames;
24970 p[pn["sort"]] = this.sortInfo.field;
24971 p[pn["dir"]] = this.sortInfo.direction;
24973 if (this.multiSort) {
24974 var pn = this.paramNames;
24975 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24978 this.proxy.load(p, this.reader, this.loadRecords, this, options);
24983 * Reloads the Record cache from the configured Proxy using the configured Reader and
24984 * the options from the last load operation performed.
24985 * @param {Object} options (optional) An object containing properties which may override the options
24986 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24987 * the most recently used options are reused).
24989 reload : function(options){
24990 this.load(Roo.applyIf(options||{}, this.lastOptions));
24994 // Called as a callback by the Reader during a load operation.
24995 loadRecords : function(o, options, success){
24998 if(success !== false){
24999 this.fireEvent("load", this, [], options, o);
25001 if(options.callback){
25002 options.callback.call(options.scope || this, [], options, false);
25006 // if data returned failure - throw an exception.
25007 if (o.success === false) {
25008 // show a message if no listener is registered.
25009 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25010 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25012 // loadmask wil be hooked into this..
25013 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25016 var r = o.records, t = o.totalRecords || r.length;
25018 this.fireEvent("beforeloadadd", this, r, options, o);
25020 if(!options || options.add !== true){
25021 if(this.pruneModifiedRecords){
25022 this.modified = [];
25024 for(var i = 0, len = r.length; i < len; i++){
25028 this.data = this.snapshot;
25029 delete this.snapshot;
25032 this.data.addAll(r);
25033 this.totalLength = t;
25035 this.fireEvent("datachanged", this);
25037 this.totalLength = Math.max(t, this.data.length+r.length);
25041 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25043 var e = new Roo.data.Record({});
25045 e.set(this.parent.displayField, this.parent.emptyTitle);
25046 e.set(this.parent.valueField, '');
25051 this.fireEvent("load", this, r, options, o);
25052 if(options.callback){
25053 options.callback.call(options.scope || this, r, options, true);
25059 * Loads data from a passed data block. A Reader which understands the format of the data
25060 * must have been configured in the constructor.
25061 * @param {Object} data The data block from which to read the Records. The format of the data expected
25062 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25063 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25065 loadData : function(o, append){
25066 var r = this.reader.readRecords(o);
25067 this.loadRecords(r, {add: append}, true);
25071 * using 'cn' the nested child reader read the child array into it's child stores.
25072 * @param {Object} rec The record with a 'children array
25074 loadDataFromChildren : function(rec)
25076 this.loadData(this.reader.toLoadData(rec));
25081 * Gets the number of cached records.
25083 * <em>If using paging, this may not be the total size of the dataset. If the data object
25084 * used by the Reader contains the dataset size, then the getTotalCount() function returns
25085 * the data set size</em>
25087 getCount : function(){
25088 return this.data.length || 0;
25092 * Gets the total number of records in the dataset as returned by the server.
25094 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25095 * the dataset size</em>
25097 getTotalCount : function(){
25098 return this.totalLength || 0;
25102 * Returns the sort state of the Store as an object with two properties:
25104 field {String} The name of the field by which the Records are sorted
25105 direction {String} The sort order, "ASC" or "DESC"
25108 getSortState : function(){
25109 return this.sortInfo;
25113 applySort : function(){
25114 if(this.sortInfo && !this.remoteSort){
25115 var s = this.sortInfo, f = s.field;
25116 var st = this.fields.get(f).sortType;
25117 var fn = function(r1, r2){
25118 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25119 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25121 this.data.sort(s.direction, fn);
25122 if(this.snapshot && this.snapshot != this.data){
25123 this.snapshot.sort(s.direction, fn);
25129 * Sets the default sort column and order to be used by the next load operation.
25130 * @param {String} fieldName The name of the field to sort by.
25131 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25133 setDefaultSort : function(field, dir){
25134 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25138 * Sort the Records.
25139 * If remote sorting is used, the sort is performed on the server, and the cache is
25140 * reloaded. If local sorting is used, the cache is sorted internally.
25141 * @param {String} fieldName The name of the field to sort by.
25142 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25144 sort : function(fieldName, dir){
25145 var f = this.fields.get(fieldName);
25147 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25149 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25150 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25155 this.sortToggle[f.name] = dir;
25156 this.sortInfo = {field: f.name, direction: dir};
25157 if(!this.remoteSort){
25159 this.fireEvent("datachanged", this);
25161 this.load(this.lastOptions);
25166 * Calls the specified function for each of the Records in the cache.
25167 * @param {Function} fn The function to call. The Record is passed as the first parameter.
25168 * Returning <em>false</em> aborts and exits the iteration.
25169 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25171 each : function(fn, scope){
25172 this.data.each(fn, scope);
25176 * Gets all records modified since the last commit. Modified records are persisted across load operations
25177 * (e.g., during paging).
25178 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25180 getModifiedRecords : function(){
25181 return this.modified;
25185 createFilterFn : function(property, value, anyMatch){
25186 if(!value.exec){ // not a regex
25187 value = String(value);
25188 if(value.length == 0){
25191 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25193 return function(r){
25194 return value.test(r.data[property]);
25199 * Sums the value of <i>property</i> for each record between start and end and returns the result.
25200 * @param {String} property A field on your records
25201 * @param {Number} start The record index to start at (defaults to 0)
25202 * @param {Number} end The last record index to include (defaults to length - 1)
25203 * @return {Number} The sum
25205 sum : function(property, start, end){
25206 var rs = this.data.items, v = 0;
25207 start = start || 0;
25208 end = (end || end === 0) ? end : rs.length-1;
25210 for(var i = start; i <= end; i++){
25211 v += (rs[i].data[property] || 0);
25217 * Filter the records by a specified property.
25218 * @param {String} field A field on your records
25219 * @param {String/RegExp} value Either a string that the field
25220 * should start with or a RegExp to test against the field
25221 * @param {Boolean} anyMatch True to match any part not just the beginning
25223 filter : function(property, value, anyMatch){
25224 var fn = this.createFilterFn(property, value, anyMatch);
25225 return fn ? this.filterBy(fn) : this.clearFilter();
25229 * Filter by a function. The specified function will be called with each
25230 * record in this data source. If the function returns true the record is included,
25231 * otherwise it is filtered.
25232 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25233 * @param {Object} scope (optional) The scope of the function (defaults to this)
25235 filterBy : function(fn, scope){
25236 this.snapshot = this.snapshot || this.data;
25237 this.data = this.queryBy(fn, scope||this);
25238 this.fireEvent("datachanged", this);
25242 * Query the records by a specified property.
25243 * @param {String} field A field on your records
25244 * @param {String/RegExp} value Either a string that the field
25245 * should start with or a RegExp to test against the field
25246 * @param {Boolean} anyMatch True to match any part not just the beginning
25247 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25249 query : function(property, value, anyMatch){
25250 var fn = this.createFilterFn(property, value, anyMatch);
25251 return fn ? this.queryBy(fn) : this.data.clone();
25255 * Query by a function. The specified function will be called with each
25256 * record in this data source. If the function returns true the record is included
25258 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25259 * @param {Object} scope (optional) The scope of the function (defaults to this)
25260 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25262 queryBy : function(fn, scope){
25263 var data = this.snapshot || this.data;
25264 return data.filterBy(fn, scope||this);
25268 * Collects unique values for a particular dataIndex from this store.
25269 * @param {String} dataIndex The property to collect
25270 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25271 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25272 * @return {Array} An array of the unique values
25274 collect : function(dataIndex, allowNull, bypassFilter){
25275 var d = (bypassFilter === true && this.snapshot) ?
25276 this.snapshot.items : this.data.items;
25277 var v, sv, r = [], l = {};
25278 for(var i = 0, len = d.length; i < len; i++){
25279 v = d[i].data[dataIndex];
25281 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25290 * Revert to a view of the Record cache with no filtering applied.
25291 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25293 clearFilter : function(suppressEvent){
25294 if(this.snapshot && this.snapshot != this.data){
25295 this.data = this.snapshot;
25296 delete this.snapshot;
25297 if(suppressEvent !== true){
25298 this.fireEvent("datachanged", this);
25304 afterEdit : function(record){
25305 if(this.modified.indexOf(record) == -1){
25306 this.modified.push(record);
25308 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25312 afterReject : function(record){
25313 this.modified.remove(record);
25314 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25318 afterCommit : function(record){
25319 this.modified.remove(record);
25320 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25324 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25325 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25327 commitChanges : function(){
25328 var m = this.modified.slice(0);
25329 this.modified = [];
25330 for(var i = 0, len = m.length; i < len; i++){
25336 * Cancel outstanding changes on all changed records.
25338 rejectChanges : function(){
25339 var m = this.modified.slice(0);
25340 this.modified = [];
25341 for(var i = 0, len = m.length; i < len; i++){
25346 onMetaChange : function(meta, rtype, o){
25347 this.recordType = rtype;
25348 this.fields = rtype.prototype.fields;
25349 delete this.snapshot;
25350 this.sortInfo = meta.sortInfo || this.sortInfo;
25351 this.modified = [];
25352 this.fireEvent('metachange', this, this.reader.meta);
25355 moveIndex : function(data, type)
25357 var index = this.indexOf(data);
25359 var newIndex = index + type;
25363 this.insert(newIndex, data);
25368 * Ext JS Library 1.1.1
25369 * Copyright(c) 2006-2007, Ext JS, LLC.
25371 * Originally Released Under LGPL - original licence link has changed is not relivant.
25374 * <script type="text/javascript">
25378 * @class Roo.data.SimpleStore
25379 * @extends Roo.data.Store
25380 * Small helper class to make creating Stores from Array data easier.
25381 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25382 * @cfg {Array} fields An array of field definition objects, or field name strings.
25383 * @cfg {Object} an existing reader (eg. copied from another store)
25384 * @cfg {Array} data The multi-dimensional array of data
25385 * @cfg {Roo.data.DataProxy} proxy [not-required]
25386 * @cfg {Roo.data.Reader} reader [not-required]
25388 * @param {Object} config
25390 Roo.data.SimpleStore = function(config)
25392 Roo.data.SimpleStore.superclass.constructor.call(this, {
25394 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25397 Roo.data.Record.create(config.fields)
25399 proxy : new Roo.data.MemoryProxy(config.data)
25403 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25405 * Ext JS Library 1.1.1
25406 * Copyright(c) 2006-2007, Ext JS, LLC.
25408 * Originally Released Under LGPL - original licence link has changed is not relivant.
25411 * <script type="text/javascript">
25416 * @extends Roo.data.Store
25417 * @class Roo.data.JsonStore
25418 * Small helper class to make creating Stores for JSON data easier. <br/>
25420 var store = new Roo.data.JsonStore({
25421 url: 'get-images.php',
25423 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25426 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25427 * JsonReader and HttpProxy (unless inline data is provided).</b>
25428 * @cfg {Array} fields An array of field definition objects, or field name strings.
25430 * @param {Object} config
25432 Roo.data.JsonStore = function(c){
25433 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25434 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25435 reader: new Roo.data.JsonReader(c, c.fields)
25438 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25440 * Ext JS Library 1.1.1
25441 * Copyright(c) 2006-2007, Ext JS, LLC.
25443 * Originally Released Under LGPL - original licence link has changed is not relivant.
25446 * <script type="text/javascript">
25450 Roo.data.Field = function(config){
25451 if(typeof config == "string"){
25452 config = {name: config};
25454 Roo.apply(this, config);
25457 this.type = "auto";
25460 var st = Roo.data.SortTypes;
25461 // named sortTypes are supported, here we look them up
25462 if(typeof this.sortType == "string"){
25463 this.sortType = st[this.sortType];
25466 // set default sortType for strings and dates
25467 if(!this.sortType){
25470 this.sortType = st.asUCString;
25473 this.sortType = st.asDate;
25476 this.sortType = st.none;
25481 var stripRe = /[\$,%]/g;
25483 // prebuilt conversion function for this field, instead of
25484 // switching every time we're reading a value
25486 var cv, dateFormat = this.dateFormat;
25491 cv = function(v){ return v; };
25494 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25498 return v !== undefined && v !== null && v !== '' ?
25499 parseInt(String(v).replace(stripRe, ""), 10) : '';
25504 return v !== undefined && v !== null && v !== '' ?
25505 parseFloat(String(v).replace(stripRe, ""), 10) : '';
25510 cv = function(v){ return v === true || v === "true" || v == 1; };
25517 if(v instanceof Date){
25521 if(dateFormat == "timestamp"){
25522 return new Date(v*1000);
25524 return Date.parseDate(v, dateFormat);
25526 var parsed = Date.parse(v);
25527 return parsed ? new Date(parsed) : null;
25536 Roo.data.Field.prototype = {
25544 * Ext JS Library 1.1.1
25545 * Copyright(c) 2006-2007, Ext JS, LLC.
25547 * Originally Released Under LGPL - original licence link has changed is not relivant.
25550 * <script type="text/javascript">
25553 // Base class for reading structured data from a data source. This class is intended to be
25554 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25557 * @class Roo.data.DataReader
25559 * Base class for reading structured data from a data source. This class is intended to be
25560 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25563 Roo.data.DataReader = function(meta, recordType){
25567 this.recordType = recordType instanceof Array ?
25568 Roo.data.Record.create(recordType) : recordType;
25571 Roo.data.DataReader.prototype = {
25574 readerType : 'Data',
25576 * Create an empty record
25577 * @param {Object} data (optional) - overlay some values
25578 * @return {Roo.data.Record} record created.
25580 newRow : function(d) {
25582 this.recordType.prototype.fields.each(function(c) {
25584 case 'int' : da[c.name] = 0; break;
25585 case 'date' : da[c.name] = new Date(); break;
25586 case 'float' : da[c.name] = 0.0; break;
25587 case 'boolean' : da[c.name] = false; break;
25588 default : da[c.name] = ""; break;
25592 return new this.recordType(Roo.apply(da, d));
25598 * Ext JS Library 1.1.1
25599 * Copyright(c) 2006-2007, Ext JS, LLC.
25601 * Originally Released Under LGPL - original licence link has changed is not relivant.
25604 * <script type="text/javascript">
25608 * @class Roo.data.DataProxy
25609 * @extends Roo.util.Observable
25611 * This class is an abstract base class for implementations which provide retrieval of
25612 * unformatted data objects.<br>
25614 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25615 * (of the appropriate type which knows how to parse the data object) to provide a block of
25616 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25618 * Custom implementations must implement the load method as described in
25619 * {@link Roo.data.HttpProxy#load}.
25621 Roo.data.DataProxy = function(){
25624 * @event beforeload
25625 * Fires before a network request is made to retrieve a data object.
25626 * @param {Object} This DataProxy object.
25627 * @param {Object} params The params parameter to the load function.
25632 * Fires before the load method's callback is called.
25633 * @param {Object} This DataProxy object.
25634 * @param {Object} o The data object.
25635 * @param {Object} arg The callback argument object passed to the load function.
25639 * @event loadexception
25640 * Fires if an Exception occurs during data retrieval.
25641 * @param {Object} This DataProxy object.
25642 * @param {Object} o The data object.
25643 * @param {Object} arg The callback argument object passed to the load function.
25644 * @param {Object} e The Exception.
25646 loadexception : true
25648 Roo.data.DataProxy.superclass.constructor.call(this);
25651 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25654 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25658 * Ext JS Library 1.1.1
25659 * Copyright(c) 2006-2007, Ext JS, LLC.
25661 * Originally Released Under LGPL - original licence link has changed is not relivant.
25664 * <script type="text/javascript">
25667 * @class Roo.data.MemoryProxy
25668 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25669 * to the Reader when its load method is called.
25671 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25673 Roo.data.MemoryProxy = function(data){
25677 Roo.data.MemoryProxy.superclass.constructor.call(this);
25681 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25684 * Load data from the requested source (in this case an in-memory
25685 * data object passed to the constructor), read the data object into
25686 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25687 * process that block using the passed callback.
25688 * @param {Object} params This parameter is not used by the MemoryProxy class.
25689 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25690 * object into a block of Roo.data.Records.
25691 * @param {Function} callback The function into which to pass the block of Roo.data.records.
25692 * The function must be passed <ul>
25693 * <li>The Record block object</li>
25694 * <li>The "arg" argument from the load function</li>
25695 * <li>A boolean success indicator</li>
25697 * @param {Object} scope The scope in which to call the callback
25698 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25700 load : function(params, reader, callback, scope, arg){
25701 params = params || {};
25704 result = reader.readRecords(params.data ? params.data :this.data);
25706 this.fireEvent("loadexception", this, arg, null, e);
25707 callback.call(scope, null, arg, false);
25710 callback.call(scope, result, arg, true);
25714 update : function(params, records){
25719 * Ext JS Library 1.1.1
25720 * Copyright(c) 2006-2007, Ext JS, LLC.
25722 * Originally Released Under LGPL - original licence link has changed is not relivant.
25725 * <script type="text/javascript">
25728 * @class Roo.data.HttpProxy
25729 * @extends Roo.data.DataProxy
25730 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25731 * configured to reference a certain URL.<br><br>
25733 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25734 * from which the running page was served.<br><br>
25736 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25738 * Be aware that to enable the browser to parse an XML document, the server must set
25739 * the Content-Type header in the HTTP response to "text/xml".
25741 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25742 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
25743 * will be used to make the request.
25745 Roo.data.HttpProxy = function(conn){
25746 Roo.data.HttpProxy.superclass.constructor.call(this);
25747 // is conn a conn config or a real conn?
25749 this.useAjax = !conn || !conn.events;
25753 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25754 // thse are take from connection...
25757 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25760 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25761 * extra parameters to each request made by this object. (defaults to undefined)
25764 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25765 * to each request made by this object. (defaults to undefined)
25768 * @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)
25771 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25774 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25780 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25784 * Return the {@link Roo.data.Connection} object being used by this Proxy.
25785 * @return {Connection} The Connection object. This object may be used to subscribe to events on
25786 * a finer-grained basis than the DataProxy events.
25788 getConnection : function(){
25789 return this.useAjax ? Roo.Ajax : this.conn;
25793 * Load data from the configured {@link Roo.data.Connection}, read the data object into
25794 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25795 * process that block using the passed callback.
25796 * @param {Object} params An object containing properties which are to be used as HTTP parameters
25797 * for the request to the remote server.
25798 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25799 * object into a block of Roo.data.Records.
25800 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25801 * The function must be passed <ul>
25802 * <li>The Record block object</li>
25803 * <li>The "arg" argument from the load function</li>
25804 * <li>A boolean success indicator</li>
25806 * @param {Object} scope The scope in which to call the callback
25807 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25809 load : function(params, reader, callback, scope, arg){
25810 if(this.fireEvent("beforeload", this, params) !== false){
25812 params : params || {},
25814 callback : callback,
25819 callback : this.loadResponse,
25823 Roo.applyIf(o, this.conn);
25824 if(this.activeRequest){
25825 Roo.Ajax.abort(this.activeRequest);
25827 this.activeRequest = Roo.Ajax.request(o);
25829 this.conn.request(o);
25832 callback.call(scope||this, null, arg, false);
25837 loadResponse : function(o, success, response){
25838 delete this.activeRequest;
25840 this.fireEvent("loadexception", this, o, response);
25841 o.request.callback.call(o.request.scope, null, o.request.arg, false);
25846 result = o.reader.read(response);
25849 o.raw = { errorMsg : response.responseText };
25850 this.fireEvent("loadexception", this, o, response, e);
25851 o.request.callback.call(o.request.scope, o, o.request.arg, false);
25855 this.fireEvent("load", this, o, o.request.arg);
25856 o.request.callback.call(o.request.scope, result, o.request.arg, true);
25860 update : function(dataSet){
25865 updateResponse : function(dataSet){
25870 * Ext JS Library 1.1.1
25871 * Copyright(c) 2006-2007, Ext JS, LLC.
25873 * Originally Released Under LGPL - original licence link has changed is not relivant.
25876 * <script type="text/javascript">
25880 * @class Roo.data.ScriptTagProxy
25881 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25882 * other than the originating domain of the running page.<br><br>
25884 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
25885 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25887 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25888 * source code that is used as the source inside a <script> tag.<br><br>
25890 * In order for the browser to process the returned data, the server must wrap the data object
25891 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25892 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25893 * depending on whether the callback name was passed:
25896 boolean scriptTag = false;
25897 String cb = request.getParameter("callback");
25900 response.setContentType("text/javascript");
25902 response.setContentType("application/x-json");
25904 Writer out = response.getWriter();
25906 out.write(cb + "(");
25908 out.print(dataBlock.toJsonString());
25915 * @param {Object} config A configuration object.
25917 Roo.data.ScriptTagProxy = function(config){
25918 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25919 Roo.apply(this, config);
25920 this.head = document.getElementsByTagName("head")[0];
25923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25927 * @cfg {String} url The URL from which to request the data object.
25930 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25934 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25935 * the server the name of the callback function set up by the load call to process the returned data object.
25936 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25937 * javascript output which calls this named function passing the data object as its only parameter.
25939 callbackParam : "callback",
25941 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25942 * name to the request.
25947 * Load data from the configured URL, read the data object into
25948 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25949 * process that block using the passed callback.
25950 * @param {Object} params An object containing properties which are to be used as HTTP parameters
25951 * for the request to the remote server.
25952 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25953 * object into a block of Roo.data.Records.
25954 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25955 * The function must be passed <ul>
25956 * <li>The Record block object</li>
25957 * <li>The "arg" argument from the load function</li>
25958 * <li>A boolean success indicator</li>
25960 * @param {Object} scope The scope in which to call the callback
25961 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25963 load : function(params, reader, callback, scope, arg){
25964 if(this.fireEvent("beforeload", this, params) !== false){
25966 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25968 var url = this.url;
25969 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25971 url += "&_dc=" + (new Date().getTime());
25973 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25976 cb : "stcCallback"+transId,
25977 scriptId : "stcScript"+transId,
25981 callback : callback,
25987 window[trans.cb] = function(o){
25988 conn.handleResponse(o, trans);
25991 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25993 if(this.autoAbort !== false){
25997 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25999 var script = document.createElement("script");
26000 script.setAttribute("src", url);
26001 script.setAttribute("type", "text/javascript");
26002 script.setAttribute("id", trans.scriptId);
26003 this.head.appendChild(script);
26005 this.trans = trans;
26007 callback.call(scope||this, null, arg, false);
26012 isLoading : function(){
26013 return this.trans ? true : false;
26017 * Abort the current server request.
26019 abort : function(){
26020 if(this.isLoading()){
26021 this.destroyTrans(this.trans);
26026 destroyTrans : function(trans, isLoaded){
26027 this.head.removeChild(document.getElementById(trans.scriptId));
26028 clearTimeout(trans.timeoutId);
26030 window[trans.cb] = undefined;
26032 delete window[trans.cb];
26035 // if hasn't been loaded, wait for load to remove it to prevent script error
26036 window[trans.cb] = function(){
26037 window[trans.cb] = undefined;
26039 delete window[trans.cb];
26046 handleResponse : function(o, trans){
26047 this.trans = false;
26048 this.destroyTrans(trans, true);
26051 result = trans.reader.readRecords(o);
26053 this.fireEvent("loadexception", this, o, trans.arg, e);
26054 trans.callback.call(trans.scope||window, null, trans.arg, false);
26057 this.fireEvent("load", this, o, trans.arg);
26058 trans.callback.call(trans.scope||window, result, trans.arg, true);
26062 handleFailure : function(trans){
26063 this.trans = false;
26064 this.destroyTrans(trans, false);
26065 this.fireEvent("loadexception", this, null, trans.arg);
26066 trans.callback.call(trans.scope||window, null, trans.arg, false);
26070 * Ext JS Library 1.1.1
26071 * Copyright(c) 2006-2007, Ext JS, LLC.
26073 * Originally Released Under LGPL - original licence link has changed is not relivant.
26076 * <script type="text/javascript">
26080 * @class Roo.data.JsonReader
26081 * @extends Roo.data.DataReader
26082 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26083 * based on mappings in a provided Roo.data.Record constructor.
26085 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26086 * in the reply previously.
26091 var RecordDef = Roo.data.Record.create([
26092 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26093 {name: 'occupation'} // This field will use "occupation" as the mapping.
26095 var myReader = new Roo.data.JsonReader({
26096 totalProperty: "results", // The property which contains the total dataset size (optional)
26097 root: "rows", // The property which contains an Array of row objects
26098 id: "id" // The property within each row object that provides an ID for the record (optional)
26102 * This would consume a JSON file like this:
26104 { 'results': 2, 'rows': [
26105 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26106 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26109 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26110 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26111 * paged from the remote server.
26112 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26113 * @cfg {String} root name of the property which contains the Array of row objects.
26114 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26115 * @cfg {Array} fields Array of field definition objects
26117 * Create a new JsonReader
26118 * @param {Object} meta Metadata configuration options
26119 * @param {Object} recordType Either an Array of field definition objects,
26120 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26122 Roo.data.JsonReader = function(meta, recordType){
26125 // set some defaults:
26126 Roo.applyIf(meta, {
26127 totalProperty: 'total',
26128 successProperty : 'success',
26133 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26135 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26137 readerType : 'Json',
26140 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
26141 * Used by Store query builder to append _requestMeta to params.
26144 metaFromRemote : false,
26146 * This method is only used by a DataProxy which has retrieved data from a remote server.
26147 * @param {Object} response The XHR object which contains the JSON data in its responseText.
26148 * @return {Object} data A data block which is used by an Roo.data.Store object as
26149 * a cache of Roo.data.Records.
26151 read : function(response){
26152 var json = response.responseText;
26154 var o = /* eval:var:o */ eval("("+json+")");
26156 throw {message: "JsonReader.read: Json object not found"};
26162 this.metaFromRemote = true;
26163 this.meta = o.metaData;
26164 this.recordType = Roo.data.Record.create(o.metaData.fields);
26165 this.onMetaChange(this.meta, this.recordType, o);
26167 return this.readRecords(o);
26170 // private function a store will implement
26171 onMetaChange : function(meta, recordType, o){
26178 simpleAccess: function(obj, subsc) {
26185 getJsonAccessor: function(){
26187 return function(expr) {
26189 return(re.test(expr))
26190 ? new Function("obj", "return obj." + expr)
26195 return Roo.emptyFn;
26200 * Create a data block containing Roo.data.Records from an XML document.
26201 * @param {Object} o An object which contains an Array of row objects in the property specified
26202 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26203 * which contains the total size of the dataset.
26204 * @return {Object} data A data block which is used by an Roo.data.Store object as
26205 * a cache of Roo.data.Records.
26207 readRecords : function(o){
26209 * After any data loads, the raw JSON data is available for further custom processing.
26213 var s = this.meta, Record = this.recordType,
26214 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26216 // Generate extraction functions for the totalProperty, the root, the id, and for each field
26218 if(s.totalProperty) {
26219 this.getTotal = this.getJsonAccessor(s.totalProperty);
26221 if(s.successProperty) {
26222 this.getSuccess = this.getJsonAccessor(s.successProperty);
26224 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26226 var g = this.getJsonAccessor(s.id);
26227 this.getId = function(rec) {
26229 return (r === undefined || r === "") ? null : r;
26232 this.getId = function(){return null;};
26235 for(var jj = 0; jj < fl; jj++){
26237 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26238 this.ef[jj] = this.getJsonAccessor(map);
26242 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26243 if(s.totalProperty){
26244 var vt = parseInt(this.getTotal(o), 10);
26249 if(s.successProperty){
26250 var vs = this.getSuccess(o);
26251 if(vs === false || vs === 'false'){
26256 for(var i = 0; i < c; i++){
26259 var id = this.getId(n);
26260 for(var j = 0; j < fl; j++){
26262 var v = this.ef[j](n);
26264 Roo.log('missing convert for ' + f.name);
26268 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26272 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26278 var record = new Record(values, id);
26280 records[i] = record;
26286 totalRecords : totalRecords
26289 // used when loading children.. @see loadDataFromChildren
26290 toLoadData: function(rec)
26292 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26293 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26294 return { data : data, total : data.length };
26299 * Ext JS Library 1.1.1
26300 * Copyright(c) 2006-2007, Ext JS, LLC.
26302 * Originally Released Under LGPL - original licence link has changed is not relivant.
26305 * <script type="text/javascript">
26309 * @class Roo.data.XmlReader
26310 * @extends Roo.data.DataReader
26311 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26312 * based on mappings in a provided Roo.data.Record constructor.<br><br>
26314 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26315 * header in the HTTP response must be set to "text/xml".</em>
26319 var RecordDef = Roo.data.Record.create([
26320 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26321 {name: 'occupation'} // This field will use "occupation" as the mapping.
26323 var myReader = new Roo.data.XmlReader({
26324 totalRecords: "results", // The element which contains the total dataset size (optional)
26325 record: "row", // The repeated element which contains row information
26326 id: "id" // The element within the row that provides an ID for the record (optional)
26330 * This would consume an XML file like this:
26334 <results>2</results>
26337 <name>Bill</name>
26338 <occupation>Gardener</occupation>
26342 <name>Ben</name>
26343 <occupation>Horticulturalist</occupation>
26347 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26348 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26349 * paged from the remote server.
26350 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26351 * @cfg {String} success The DomQuery path to the success attribute used by forms.
26352 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26353 * a record identifier value.
26355 * Create a new XmlReader
26356 * @param {Object} meta Metadata configuration options
26357 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
26358 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26359 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
26361 Roo.data.XmlReader = function(meta, recordType){
26363 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26365 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26367 readerType : 'Xml',
26370 * This method is only used by a DataProxy which has retrieved data from a remote server.
26371 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
26372 * to contain a method called 'responseXML' that returns an XML document object.
26373 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26374 * a cache of Roo.data.Records.
26376 read : function(response){
26377 var doc = response.responseXML;
26379 throw {message: "XmlReader.read: XML Document not available"};
26381 return this.readRecords(doc);
26385 * Create a data block containing Roo.data.Records from an XML document.
26386 * @param {Object} doc A parsed XML document.
26387 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26388 * a cache of Roo.data.Records.
26390 readRecords : function(doc){
26392 * After any data loads/reads, the raw XML Document is available for further custom processing.
26393 * @type XMLDocument
26395 this.xmlData = doc;
26396 var root = doc.documentElement || doc;
26397 var q = Roo.DomQuery;
26398 var recordType = this.recordType, fields = recordType.prototype.fields;
26399 var sid = this.meta.id;
26400 var totalRecords = 0, success = true;
26401 if(this.meta.totalRecords){
26402 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26405 if(this.meta.success){
26406 var sv = q.selectValue(this.meta.success, root, true);
26407 success = sv !== false && sv !== 'false';
26410 var ns = q.select(this.meta.record, root);
26411 for(var i = 0, len = ns.length; i < len; i++) {
26414 var id = sid ? q.selectValue(sid, n) : undefined;
26415 for(var j = 0, jlen = fields.length; j < jlen; j++){
26416 var f = fields.items[j];
26417 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26419 values[f.name] = v;
26421 var record = new recordType(values, id);
26423 records[records.length] = record;
26429 totalRecords : totalRecords || records.length
26434 * Ext JS Library 1.1.1
26435 * Copyright(c) 2006-2007, Ext JS, LLC.
26437 * Originally Released Under LGPL - original licence link has changed is not relivant.
26440 * <script type="text/javascript">
26444 * @class Roo.data.ArrayReader
26445 * @extends Roo.data.DataReader
26446 * Data reader class to create an Array of Roo.data.Record objects from an Array.
26447 * Each element of that Array represents a row of data fields. The
26448 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26449 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26453 var RecordDef = Roo.data.Record.create([
26454 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
26455 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
26457 var myReader = new Roo.data.ArrayReader({
26458 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
26462 * This would consume an Array like this:
26464 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26468 * Create a new JsonReader
26469 * @param {Object} meta Metadata configuration options.
26470 * @param {Object|Array} recordType Either an Array of field definition objects
26472 * @cfg {Array} fields Array of field definition objects
26473 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26474 * as specified to {@link Roo.data.Record#create},
26475 * or an {@link Roo.data.Record} object
26478 * created using {@link Roo.data.Record#create}.
26480 Roo.data.ArrayReader = function(meta, recordType)
26482 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26485 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26488 * Create a data block containing Roo.data.Records from an XML document.
26489 * @param {Object} o An Array of row objects which represents the dataset.
26490 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26491 * a cache of Roo.data.Records.
26493 readRecords : function(o)
26495 var sid = this.meta ? this.meta.id : null;
26496 var recordType = this.recordType, fields = recordType.prototype.fields;
26499 for(var i = 0; i < root.length; i++){
26502 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26503 for(var j = 0, jlen = fields.length; j < jlen; j++){
26504 var f = fields.items[j];
26505 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26506 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26508 values[f.name] = v;
26510 var record = new recordType(values, id);
26512 records[records.length] = record;
26516 totalRecords : records.length
26519 // used when loading children.. @see loadDataFromChildren
26520 toLoadData: function(rec)
26522 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26523 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26530 * Ext JS Library 1.1.1
26531 * Copyright(c) 2006-2007, Ext JS, LLC.
26533 * Originally Released Under LGPL - original licence link has changed is not relivant.
26536 * <script type="text/javascript">
26541 * @class Roo.data.Tree
26542 * @extends Roo.util.Observable
26543 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26544 * in the tree have most standard DOM functionality.
26546 * @param {Node} root (optional) The root node
26548 Roo.data.Tree = function(root){
26549 this.nodeHash = {};
26551 * The root node for this tree
26556 this.setRootNode(root);
26561 * Fires when a new child node is appended to a node in this tree.
26562 * @param {Tree} tree The owner tree
26563 * @param {Node} parent The parent node
26564 * @param {Node} node The newly appended node
26565 * @param {Number} index The index of the newly appended node
26570 * Fires when a child node is removed from a node in this tree.
26571 * @param {Tree} tree The owner tree
26572 * @param {Node} parent The parent node
26573 * @param {Node} node The child node removed
26578 * Fires when a node is moved to a new location in the tree
26579 * @param {Tree} tree The owner tree
26580 * @param {Node} node The node moved
26581 * @param {Node} oldParent The old parent of this node
26582 * @param {Node} newParent The new parent of this node
26583 * @param {Number} index The index it was moved to
26588 * Fires when a new child node is inserted in a node in this tree.
26589 * @param {Tree} tree The owner tree
26590 * @param {Node} parent The parent node
26591 * @param {Node} node The child node inserted
26592 * @param {Node} refNode The child node the node was inserted before
26596 * @event beforeappend
26597 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26598 * @param {Tree} tree The owner tree
26599 * @param {Node} parent The parent node
26600 * @param {Node} node The child node to be appended
26602 "beforeappend" : true,
26604 * @event beforeremove
26605 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26606 * @param {Tree} tree The owner tree
26607 * @param {Node} parent The parent node
26608 * @param {Node} node The child node to be removed
26610 "beforeremove" : true,
26612 * @event beforemove
26613 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26614 * @param {Tree} tree The owner tree
26615 * @param {Node} node The node being moved
26616 * @param {Node} oldParent The parent of the node
26617 * @param {Node} newParent The new parent the node is moving to
26618 * @param {Number} index The index it is being moved to
26620 "beforemove" : true,
26622 * @event beforeinsert
26623 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26624 * @param {Tree} tree The owner tree
26625 * @param {Node} parent The parent node
26626 * @param {Node} node The child node to be inserted
26627 * @param {Node} refNode The child node the node is being inserted before
26629 "beforeinsert" : true
26632 Roo.data.Tree.superclass.constructor.call(this);
26635 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26636 pathSeparator: "/",
26638 proxyNodeEvent : function(){
26639 return this.fireEvent.apply(this, arguments);
26643 * Returns the root node for this tree.
26646 getRootNode : function(){
26651 * Sets the root node for this tree.
26652 * @param {Node} node
26655 setRootNode : function(node){
26657 node.ownerTree = this;
26658 node.isRoot = true;
26659 this.registerNode(node);
26664 * Gets a node in this tree by its id.
26665 * @param {String} id
26668 getNodeById : function(id){
26669 return this.nodeHash[id];
26672 registerNode : function(node){
26673 this.nodeHash[node.id] = node;
26676 unregisterNode : function(node){
26677 delete this.nodeHash[node.id];
26680 toString : function(){
26681 return "[Tree"+(this.id?" "+this.id:"")+"]";
26686 * @class Roo.data.Node
26687 * @extends Roo.util.Observable
26688 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26689 * @cfg {String} id The id for this node. If one is not specified, one is generated.
26691 * @param {Object} attributes The attributes/config for the node
26693 Roo.data.Node = function(attributes){
26695 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26698 this.attributes = attributes || {};
26699 this.leaf = this.attributes.leaf;
26701 * The node id. @type String
26703 this.id = this.attributes.id;
26705 this.id = Roo.id(null, "ynode-");
26706 this.attributes.id = this.id;
26711 * All child nodes of this node. @type Array
26713 this.childNodes = [];
26714 if(!this.childNodes.indexOf){ // indexOf is a must
26715 this.childNodes.indexOf = function(o){
26716 for(var i = 0, len = this.length; i < len; i++){
26725 * The parent node for this node. @type Node
26727 this.parentNode = null;
26729 * The first direct child node of this node, or null if this node has no child nodes. @type Node
26731 this.firstChild = null;
26733 * The last direct child node of this node, or null if this node has no child nodes. @type Node
26735 this.lastChild = null;
26737 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26739 this.previousSibling = null;
26741 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26743 this.nextSibling = null;
26748 * Fires when a new child node is appended
26749 * @param {Tree} tree The owner tree
26750 * @param {Node} this This node
26751 * @param {Node} node The newly appended node
26752 * @param {Number} index The index of the newly appended node
26757 * Fires when a child node is removed
26758 * @param {Tree} tree The owner tree
26759 * @param {Node} this This node
26760 * @param {Node} node The removed node
26765 * Fires when this node is moved to a new location in the tree
26766 * @param {Tree} tree The owner tree
26767 * @param {Node} this This node
26768 * @param {Node} oldParent The old parent of this node
26769 * @param {Node} newParent The new parent of this node
26770 * @param {Number} index The index it was moved to
26775 * Fires when a new child node is inserted.
26776 * @param {Tree} tree The owner tree
26777 * @param {Node} this This node
26778 * @param {Node} node The child node inserted
26779 * @param {Node} refNode The child node the node was inserted before
26783 * @event beforeappend
26784 * Fires before a new child is appended, return false to cancel the append.
26785 * @param {Tree} tree The owner tree
26786 * @param {Node} this This node
26787 * @param {Node} node The child node to be appended
26789 "beforeappend" : true,
26791 * @event beforeremove
26792 * Fires before a child is removed, return false to cancel the remove.
26793 * @param {Tree} tree The owner tree
26794 * @param {Node} this This node
26795 * @param {Node} node The child node to be removed
26797 "beforeremove" : true,
26799 * @event beforemove
26800 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26801 * @param {Tree} tree The owner tree
26802 * @param {Node} this This node
26803 * @param {Node} oldParent The parent of this node
26804 * @param {Node} newParent The new parent this node is moving to
26805 * @param {Number} index The index it is being moved to
26807 "beforemove" : true,
26809 * @event beforeinsert
26810 * Fires before a new child is inserted, return false to cancel the insert.
26811 * @param {Tree} tree The owner tree
26812 * @param {Node} this This node
26813 * @param {Node} node The child node to be inserted
26814 * @param {Node} refNode The child node the node is being inserted before
26816 "beforeinsert" : true
26818 this.listeners = this.attributes.listeners;
26819 Roo.data.Node.superclass.constructor.call(this);
26822 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26823 fireEvent : function(evtName){
26824 // first do standard event for this node
26825 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26828 // then bubble it up to the tree if the event wasn't cancelled
26829 var ot = this.getOwnerTree();
26831 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26839 * Returns true if this node is a leaf
26840 * @return {Boolean}
26842 isLeaf : function(){
26843 return this.leaf === true;
26847 setFirstChild : function(node){
26848 this.firstChild = node;
26852 setLastChild : function(node){
26853 this.lastChild = node;
26858 * Returns true if this node is the last child of its parent
26859 * @return {Boolean}
26861 isLast : function(){
26862 return (!this.parentNode ? true : this.parentNode.lastChild == this);
26866 * Returns true if this node is the first child of its parent
26867 * @return {Boolean}
26869 isFirst : function(){
26870 return (!this.parentNode ? true : this.parentNode.firstChild == this);
26873 hasChildNodes : function(){
26874 return !this.isLeaf() && this.childNodes.length > 0;
26878 * Insert node(s) as the last child node of this node.
26879 * @param {Node/Array} node The node or Array of nodes to append
26880 * @return {Node} The appended node if single append, or null if an array was passed
26882 appendChild : function(node){
26884 if(node instanceof Array){
26886 }else if(arguments.length > 1){
26890 // if passed an array or multiple args do them one by one
26892 for(var i = 0, len = multi.length; i < len; i++) {
26893 this.appendChild(multi[i]);
26896 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26899 var index = this.childNodes.length;
26900 var oldParent = node.parentNode;
26901 // it's a move, make sure we move it cleanly
26903 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26906 oldParent.removeChild(node);
26909 index = this.childNodes.length;
26911 this.setFirstChild(node);
26913 this.childNodes.push(node);
26914 node.parentNode = this;
26915 var ps = this.childNodes[index-1];
26917 node.previousSibling = ps;
26918 ps.nextSibling = node;
26920 node.previousSibling = null;
26922 node.nextSibling = null;
26923 this.setLastChild(node);
26924 node.setOwnerTree(this.getOwnerTree());
26925 this.fireEvent("append", this.ownerTree, this, node, index);
26926 if(this.ownerTree) {
26927 this.ownerTree.fireEvent("appendnode", this, node, index);
26930 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26937 * Removes a child node from this node.
26938 * @param {Node} node The node to remove
26939 * @return {Node} The removed node
26941 removeChild : function(node){
26942 var index = this.childNodes.indexOf(node);
26946 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26950 // remove it from childNodes collection
26951 this.childNodes.splice(index, 1);
26954 if(node.previousSibling){
26955 node.previousSibling.nextSibling = node.nextSibling;
26957 if(node.nextSibling){
26958 node.nextSibling.previousSibling = node.previousSibling;
26961 // update child refs
26962 if(this.firstChild == node){
26963 this.setFirstChild(node.nextSibling);
26965 if(this.lastChild == node){
26966 this.setLastChild(node.previousSibling);
26969 node.setOwnerTree(null);
26970 // clear any references from the node
26971 node.parentNode = null;
26972 node.previousSibling = null;
26973 node.nextSibling = null;
26974 this.fireEvent("remove", this.ownerTree, this, node);
26979 * Inserts the first node before the second node in this nodes childNodes collection.
26980 * @param {Node} node The node to insert
26981 * @param {Node} refNode The node to insert before (if null the node is appended)
26982 * @return {Node} The inserted node
26984 insertBefore : function(node, refNode){
26985 if(!refNode){ // like standard Dom, refNode can be null for append
26986 return this.appendChild(node);
26989 if(node == refNode){
26993 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26996 var index = this.childNodes.indexOf(refNode);
26997 var oldParent = node.parentNode;
26998 var refIndex = index;
27000 // when moving internally, indexes will change after remove
27001 if(oldParent == this && this.childNodes.indexOf(node) < index){
27005 // it's a move, make sure we move it cleanly
27007 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27010 oldParent.removeChild(node);
27013 this.setFirstChild(node);
27015 this.childNodes.splice(refIndex, 0, node);
27016 node.parentNode = this;
27017 var ps = this.childNodes[refIndex-1];
27019 node.previousSibling = ps;
27020 ps.nextSibling = node;
27022 node.previousSibling = null;
27024 node.nextSibling = refNode;
27025 refNode.previousSibling = node;
27026 node.setOwnerTree(this.getOwnerTree());
27027 this.fireEvent("insert", this.ownerTree, this, node, refNode);
27029 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27035 * Returns the child node at the specified index.
27036 * @param {Number} index
27039 item : function(index){
27040 return this.childNodes[index];
27044 * Replaces one child node in this node with another.
27045 * @param {Node} newChild The replacement node
27046 * @param {Node} oldChild The node to replace
27047 * @return {Node} The replaced node
27049 replaceChild : function(newChild, oldChild){
27050 this.insertBefore(newChild, oldChild);
27051 this.removeChild(oldChild);
27056 * Returns the index of a child node
27057 * @param {Node} node
27058 * @return {Number} The index of the node or -1 if it was not found
27060 indexOf : function(child){
27061 return this.childNodes.indexOf(child);
27065 * Returns the tree this node is in.
27068 getOwnerTree : function(){
27069 // if it doesn't have one, look for one
27070 if(!this.ownerTree){
27074 this.ownerTree = p.ownerTree;
27080 return this.ownerTree;
27084 * Returns depth of this node (the root node has a depth of 0)
27087 getDepth : function(){
27090 while(p.parentNode){
27098 setOwnerTree : function(tree){
27099 // if it's move, we need to update everyone
27100 if(tree != this.ownerTree){
27101 if(this.ownerTree){
27102 this.ownerTree.unregisterNode(this);
27104 this.ownerTree = tree;
27105 var cs = this.childNodes;
27106 for(var i = 0, len = cs.length; i < len; i++) {
27107 cs[i].setOwnerTree(tree);
27110 tree.registerNode(this);
27116 * Returns the path for this node. The path can be used to expand or select this node programmatically.
27117 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27118 * @return {String} The path
27120 getPath : function(attr){
27121 attr = attr || "id";
27122 var p = this.parentNode;
27123 var b = [this.attributes[attr]];
27125 b.unshift(p.attributes[attr]);
27128 var sep = this.getOwnerTree().pathSeparator;
27129 return sep + b.join(sep);
27133 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27134 * function call will be the scope provided or the current node. The arguments to the function
27135 * will be the args provided or the current node. If the function returns false at any point,
27136 * the bubble is stopped.
27137 * @param {Function} fn The function to call
27138 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27139 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27141 bubble : function(fn, scope, args){
27144 if(fn.call(scope || p, args || p) === false){
27152 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27153 * function call will be the scope provided or the current node. The arguments to the function
27154 * will be the args provided or the current node. If the function returns false at any point,
27155 * the cascade is stopped on that branch.
27156 * @param {Function} fn The function to call
27157 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27158 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27160 cascade : function(fn, scope, args){
27161 if(fn.call(scope || this, args || this) !== false){
27162 var cs = this.childNodes;
27163 for(var i = 0, len = cs.length; i < len; i++) {
27164 cs[i].cascade(fn, scope, args);
27170 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27171 * function call will be the scope provided or the current node. The arguments to the function
27172 * will be the args provided or the current node. If the function returns false at any point,
27173 * the iteration stops.
27174 * @param {Function} fn The function to call
27175 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27176 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27178 eachChild : function(fn, scope, args){
27179 var cs = this.childNodes;
27180 for(var i = 0, len = cs.length; i < len; i++) {
27181 if(fn.call(scope || this, args || cs[i]) === false){
27188 * Finds the first child that has the attribute with the specified value.
27189 * @param {String} attribute The attribute name
27190 * @param {Mixed} value The value to search for
27191 * @return {Node} The found child or null if none was found
27193 findChild : function(attribute, value){
27194 var cs = this.childNodes;
27195 for(var i = 0, len = cs.length; i < len; i++) {
27196 if(cs[i].attributes[attribute] == value){
27204 * Finds the first child by a custom function. The child matches if the function passed
27206 * @param {Function} fn
27207 * @param {Object} scope (optional)
27208 * @return {Node} The found child or null if none was found
27210 findChildBy : function(fn, scope){
27211 var cs = this.childNodes;
27212 for(var i = 0, len = cs.length; i < len; i++) {
27213 if(fn.call(scope||cs[i], cs[i]) === true){
27221 * Sorts this nodes children using the supplied sort function
27222 * @param {Function} fn
27223 * @param {Object} scope (optional)
27225 sort : function(fn, scope){
27226 var cs = this.childNodes;
27227 var len = cs.length;
27229 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27231 for(var i = 0; i < len; i++){
27233 n.previousSibling = cs[i-1];
27234 n.nextSibling = cs[i+1];
27236 this.setFirstChild(n);
27239 this.setLastChild(n);
27246 * Returns true if this node is an ancestor (at any point) of the passed node.
27247 * @param {Node} node
27248 * @return {Boolean}
27250 contains : function(node){
27251 return node.isAncestor(this);
27255 * Returns true if the passed node is an ancestor (at any point) of this node.
27256 * @param {Node} node
27257 * @return {Boolean}
27259 isAncestor : function(node){
27260 var p = this.parentNode;
27270 toString : function(){
27271 return "[Node"+(this.id?" "+this.id:"")+"]";
27275 * Ext JS Library 1.1.1
27276 * Copyright(c) 2006-2007, Ext JS, LLC.
27278 * Originally Released Under LGPL - original licence link has changed is not relivant.
27281 * <script type="text/javascript">
27286 * @class Roo.Shadow
27287 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
27288 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
27289 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27291 * Create a new Shadow
27292 * @param {Object} config The config object
27294 Roo.Shadow = function(config){
27295 Roo.apply(this, config);
27296 if(typeof this.mode != "string"){
27297 this.mode = this.defaultMode;
27299 var o = this.offset, a = {h: 0};
27300 var rad = Math.floor(this.offset/2);
27301 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27307 a.l -= this.offset + rad;
27308 a.t -= this.offset + rad;
27319 a.l -= (this.offset - rad);
27320 a.t -= this.offset + rad;
27322 a.w -= (this.offset - rad)*2;
27333 a.l -= (this.offset - rad);
27334 a.t -= (this.offset - rad);
27336 a.w -= (this.offset + rad + 1);
27337 a.h -= (this.offset + rad);
27346 Roo.Shadow.prototype = {
27348 * @cfg {String} mode
27349 * The shadow display mode. Supports the following options:<br />
27350 * sides: Shadow displays on both sides and bottom only<br />
27351 * frame: Shadow displays equally on all four sides<br />
27352 * drop: Traditional bottom-right drop shadow (default)
27356 * @cfg {String} offset
27357 * The number of pixels to offset the shadow from the element (defaults to 4)
27362 defaultMode: "drop",
27365 * Displays the shadow under the target element
27366 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27368 show : function(target){
27369 target = Roo.get(target);
27371 this.el = Roo.Shadow.Pool.pull();
27372 if(this.el.dom.nextSibling != target.dom){
27373 this.el.insertBefore(target);
27376 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27378 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27381 target.getLeft(true),
27382 target.getTop(true),
27386 this.el.dom.style.display = "block";
27390 * Returns true if the shadow is visible, else false
27392 isVisible : function(){
27393 return this.el ? true : false;
27397 * Direct alignment when values are already available. Show must be called at least once before
27398 * calling this method to ensure it is initialized.
27399 * @param {Number} left The target element left position
27400 * @param {Number} top The target element top position
27401 * @param {Number} width The target element width
27402 * @param {Number} height The target element height
27404 realign : function(l, t, w, h){
27408 var a = this.adjusts, d = this.el.dom, s = d.style;
27410 s.left = (l+a.l)+"px";
27411 s.top = (t+a.t)+"px";
27412 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27414 if(s.width != sws || s.height != shs){
27418 var cn = d.childNodes;
27419 var sww = Math.max(0, (sw-12))+"px";
27420 cn[0].childNodes[1].style.width = sww;
27421 cn[1].childNodes[1].style.width = sww;
27422 cn[2].childNodes[1].style.width = sww;
27423 cn[1].style.height = Math.max(0, (sh-12))+"px";
27429 * Hides this shadow
27433 this.el.dom.style.display = "none";
27434 Roo.Shadow.Pool.push(this.el);
27440 * Adjust the z-index of this shadow
27441 * @param {Number} zindex The new z-index
27443 setZIndex : function(z){
27446 this.el.setStyle("z-index", z);
27451 // Private utility class that manages the internal Shadow cache
27452 Roo.Shadow.Pool = function(){
27454 var markup = Roo.isIE ?
27455 '<div class="x-ie-shadow"></div>' :
27456 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
27459 var sh = p.shift();
27461 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27462 sh.autoBoxAdjust = false;
27467 push : function(sh){
27473 * Ext JS Library 1.1.1
27474 * Copyright(c) 2006-2007, Ext JS, LLC.
27476 * Originally Released Under LGPL - original licence link has changed is not relivant.
27479 * <script type="text/javascript">
27484 * @class Roo.SplitBar
27485 * @extends Roo.util.Observable
27486 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27490 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27491 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27492 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27493 split.minSize = 100;
27494 split.maxSize = 600;
27495 split.animate = true;
27496 split.on('moved', splitterMoved);
27499 * Create a new SplitBar
27500 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
27501 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
27502 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27503 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
27504 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27505 position of the SplitBar).
27507 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27510 this.el = Roo.get(dragElement, true);
27511 this.el.dom.unselectable = "on";
27513 this.resizingEl = Roo.get(resizingElement, true);
27517 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27518 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27521 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27524 * The minimum size of the resizing element. (Defaults to 0)
27530 * The maximum size of the resizing element. (Defaults to 2000)
27533 this.maxSize = 2000;
27536 * Whether to animate the transition to the new size
27539 this.animate = false;
27542 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27545 this.useShim = false;
27550 if(!existingProxy){
27552 this.proxy = Roo.SplitBar.createProxy(this.orientation);
27554 this.proxy = Roo.get(existingProxy).dom;
27557 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27560 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27563 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27566 this.dragSpecs = {};
27569 * @private The adapter to use to positon and resize elements
27571 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27572 this.adapter.init(this);
27574 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27576 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27577 this.el.addClass("x-splitbar-h");
27580 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27581 this.el.addClass("x-splitbar-v");
27587 * Fires when the splitter is moved (alias for {@link #event-moved})
27588 * @param {Roo.SplitBar} this
27589 * @param {Number} newSize the new width or height
27594 * Fires when the splitter is moved
27595 * @param {Roo.SplitBar} this
27596 * @param {Number} newSize the new width or height
27600 * @event beforeresize
27601 * Fires before the splitter is dragged
27602 * @param {Roo.SplitBar} this
27604 "beforeresize" : true,
27606 "beforeapply" : true
27609 Roo.util.Observable.call(this);
27612 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27613 onStartProxyDrag : function(x, y){
27614 this.fireEvent("beforeresize", this);
27616 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
27618 o.enableDisplayMode("block");
27619 // all splitbars share the same overlay
27620 Roo.SplitBar.prototype.overlay = o;
27622 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27623 this.overlay.show();
27624 Roo.get(this.proxy).setDisplayed("block");
27625 var size = this.adapter.getElementSize(this);
27626 this.activeMinSize = this.getMinimumSize();;
27627 this.activeMaxSize = this.getMaximumSize();;
27628 var c1 = size - this.activeMinSize;
27629 var c2 = Math.max(this.activeMaxSize - size, 0);
27630 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27631 this.dd.resetConstraints();
27632 this.dd.setXConstraint(
27633 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
27634 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27636 this.dd.setYConstraint(0, 0);
27638 this.dd.resetConstraints();
27639 this.dd.setXConstraint(0, 0);
27640 this.dd.setYConstraint(
27641 this.placement == Roo.SplitBar.TOP ? c1 : c2,
27642 this.placement == Roo.SplitBar.TOP ? c2 : c1
27645 this.dragSpecs.startSize = size;
27646 this.dragSpecs.startPoint = [x, y];
27647 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27651 * @private Called after the drag operation by the DDProxy
27653 onEndProxyDrag : function(e){
27654 Roo.get(this.proxy).setDisplayed(false);
27655 var endPoint = Roo.lib.Event.getXY(e);
27657 this.overlay.hide();
27660 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27661 newSize = this.dragSpecs.startSize +
27662 (this.placement == Roo.SplitBar.LEFT ?
27663 endPoint[0] - this.dragSpecs.startPoint[0] :
27664 this.dragSpecs.startPoint[0] - endPoint[0]
27667 newSize = this.dragSpecs.startSize +
27668 (this.placement == Roo.SplitBar.TOP ?
27669 endPoint[1] - this.dragSpecs.startPoint[1] :
27670 this.dragSpecs.startPoint[1] - endPoint[1]
27673 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27674 if(newSize != this.dragSpecs.startSize){
27675 if(this.fireEvent('beforeapply', this, newSize) !== false){
27676 this.adapter.setElementSize(this, newSize);
27677 this.fireEvent("moved", this, newSize);
27678 this.fireEvent("resize", this, newSize);
27684 * Get the adapter this SplitBar uses
27685 * @return The adapter object
27687 getAdapter : function(){
27688 return this.adapter;
27692 * Set the adapter this SplitBar uses
27693 * @param {Object} adapter A SplitBar adapter object
27695 setAdapter : function(adapter){
27696 this.adapter = adapter;
27697 this.adapter.init(this);
27701 * Gets the minimum size for the resizing element
27702 * @return {Number} The minimum size
27704 getMinimumSize : function(){
27705 return this.minSize;
27709 * Sets the minimum size for the resizing element
27710 * @param {Number} minSize The minimum size
27712 setMinimumSize : function(minSize){
27713 this.minSize = minSize;
27717 * Gets the maximum size for the resizing element
27718 * @return {Number} The maximum size
27720 getMaximumSize : function(){
27721 return this.maxSize;
27725 * Sets the maximum size for the resizing element
27726 * @param {Number} maxSize The maximum size
27728 setMaximumSize : function(maxSize){
27729 this.maxSize = maxSize;
27733 * Sets the initialize size for the resizing element
27734 * @param {Number} size The initial size
27736 setCurrentSize : function(size){
27737 var oldAnimate = this.animate;
27738 this.animate = false;
27739 this.adapter.setElementSize(this, size);
27740 this.animate = oldAnimate;
27744 * Destroy this splitbar.
27745 * @param {Boolean} removeEl True to remove the element
27747 destroy : function(removeEl){
27749 this.shim.remove();
27752 this.proxy.parentNode.removeChild(this.proxy);
27760 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
27762 Roo.SplitBar.createProxy = function(dir){
27763 var proxy = new Roo.Element(document.createElement("div"));
27764 proxy.unselectable();
27765 var cls = 'x-splitbar-proxy';
27766 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27767 document.body.appendChild(proxy.dom);
27772 * @class Roo.SplitBar.BasicLayoutAdapter
27773 * Default Adapter. It assumes the splitter and resizing element are not positioned
27774 * elements and only gets/sets the width of the element. Generally used for table based layouts.
27776 Roo.SplitBar.BasicLayoutAdapter = function(){
27779 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27780 // do nothing for now
27781 init : function(s){
27785 * Called before drag operations to get the current size of the resizing element.
27786 * @param {Roo.SplitBar} s The SplitBar using this adapter
27788 getElementSize : function(s){
27789 if(s.orientation == Roo.SplitBar.HORIZONTAL){
27790 return s.resizingEl.getWidth();
27792 return s.resizingEl.getHeight();
27797 * Called after drag operations to set the size of the resizing element.
27798 * @param {Roo.SplitBar} s The SplitBar using this adapter
27799 * @param {Number} newSize The new size to set
27800 * @param {Function} onComplete A function to be invoked when resizing is complete
27802 setElementSize : function(s, newSize, onComplete){
27803 if(s.orientation == Roo.SplitBar.HORIZONTAL){
27805 s.resizingEl.setWidth(newSize);
27807 onComplete(s, newSize);
27810 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27815 s.resizingEl.setHeight(newSize);
27817 onComplete(s, newSize);
27820 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27827 *@class Roo.SplitBar.AbsoluteLayoutAdapter
27828 * @extends Roo.SplitBar.BasicLayoutAdapter
27829 * Adapter that moves the splitter element to align with the resized sizing element.
27830 * Used with an absolute positioned SplitBar.
27831 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27832 * document.body, make sure you assign an id to the body element.
27834 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27835 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27836 this.container = Roo.get(container);
27839 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27840 init : function(s){
27841 this.basic.init(s);
27844 getElementSize : function(s){
27845 return this.basic.getElementSize(s);
27848 setElementSize : function(s, newSize, onComplete){
27849 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27852 moveSplitter : function(s){
27853 var yes = Roo.SplitBar;
27854 switch(s.placement){
27856 s.el.setX(s.resizingEl.getRight());
27859 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27862 s.el.setY(s.resizingEl.getBottom());
27865 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27872 * Orientation constant - Create a vertical SplitBar
27876 Roo.SplitBar.VERTICAL = 1;
27879 * Orientation constant - Create a horizontal SplitBar
27883 Roo.SplitBar.HORIZONTAL = 2;
27886 * Placement constant - The resizing element is to the left of the splitter element
27890 Roo.SplitBar.LEFT = 1;
27893 * Placement constant - The resizing element is to the right of the splitter element
27897 Roo.SplitBar.RIGHT = 2;
27900 * Placement constant - The resizing element is positioned above the splitter element
27904 Roo.SplitBar.TOP = 3;
27907 * Placement constant - The resizing element is positioned under splitter element
27911 Roo.SplitBar.BOTTOM = 4;
27914 * Ext JS Library 1.1.1
27915 * Copyright(c) 2006-2007, Ext JS, LLC.
27917 * Originally Released Under LGPL - original licence link has changed is not relivant.
27920 * <script type="text/javascript">
27925 * @extends Roo.util.Observable
27926 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
27927 * This class also supports single and multi selection modes. <br>
27928 * Create a data model bound view:
27930 var store = new Roo.data.Store(...);
27932 var view = new Roo.View({
27934 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
27936 singleSelect: true,
27937 selectedClass: "ydataview-selected",
27941 // listen for node click?
27942 view.on("click", function(vw, index, node, e){
27943 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27947 dataModel.load("foobar.xml");
27949 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27951 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27952 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27954 * Note: old style constructor is still suported (container, template, config)
27957 * Create a new View
27958 * @param {Object} config The config object
27961 Roo.View = function(config, depreciated_tpl, depreciated_config){
27963 this.parent = false;
27965 if (typeof(depreciated_tpl) == 'undefined') {
27966 // new way.. - universal constructor.
27967 Roo.apply(this, config);
27968 this.el = Roo.get(this.el);
27971 this.el = Roo.get(config);
27972 this.tpl = depreciated_tpl;
27973 Roo.apply(this, depreciated_config);
27975 this.wrapEl = this.el.wrap().wrap();
27976 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27979 if(typeof(this.tpl) == "string"){
27980 this.tpl = new Roo.Template(this.tpl);
27982 // support xtype ctors..
27983 this.tpl = new Roo.factory(this.tpl, Roo);
27987 this.tpl.compile();
27992 * @event beforeclick
27993 * Fires before a click is processed. Returns false to cancel the default action.
27994 * @param {Roo.View} this
27995 * @param {Number} index The index of the target node
27996 * @param {HTMLElement} node The target node
27997 * @param {Roo.EventObject} e The raw event object
27999 "beforeclick" : true,
28002 * Fires when a template node is clicked.
28003 * @param {Roo.View} this
28004 * @param {Number} index The index of the target node
28005 * @param {HTMLElement} node The target node
28006 * @param {Roo.EventObject} e The raw event object
28011 * Fires when a template node is double clicked.
28012 * @param {Roo.View} this
28013 * @param {Number} index The index of the target node
28014 * @param {HTMLElement} node The target node
28015 * @param {Roo.EventObject} e The raw event object
28019 * @event contextmenu
28020 * Fires when a template node is right clicked.
28021 * @param {Roo.View} this
28022 * @param {Number} index The index of the target node
28023 * @param {HTMLElement} node The target node
28024 * @param {Roo.EventObject} e The raw event object
28026 "contextmenu" : true,
28028 * @event selectionchange
28029 * Fires when the selected nodes change.
28030 * @param {Roo.View} this
28031 * @param {Array} selections Array of the selected nodes
28033 "selectionchange" : true,
28036 * @event beforeselect
28037 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28038 * @param {Roo.View} this
28039 * @param {HTMLElement} node The node to be selected
28040 * @param {Array} selections Array of currently selected nodes
28042 "beforeselect" : true,
28044 * @event preparedata
28045 * Fires on every row to render, to allow you to change the data.
28046 * @param {Roo.View} this
28047 * @param {Object} data to be rendered (change this)
28049 "preparedata" : true
28057 "click": this.onClick,
28058 "dblclick": this.onDblClick,
28059 "contextmenu": this.onContextMenu,
28063 this.selections = [];
28065 this.cmp = new Roo.CompositeElementLite([]);
28067 this.store = Roo.factory(this.store, Roo.data);
28068 this.setStore(this.store, true);
28071 if ( this.footer && this.footer.xtype) {
28073 var fctr = this.wrapEl.appendChild(document.createElement("div"));
28075 this.footer.dataSource = this.store;
28076 this.footer.container = fctr;
28077 this.footer = Roo.factory(this.footer, Roo);
28078 fctr.insertFirst(this.el);
28080 // this is a bit insane - as the paging toolbar seems to detach the el..
28081 // dom.parentNode.parentNode.parentNode
28082 // they get detached?
28086 Roo.View.superclass.constructor.call(this);
28091 Roo.extend(Roo.View, Roo.util.Observable, {
28094 * @cfg {Roo.data.Store} store Data store to load data from.
28099 * @cfg {String|Roo.Element} el The container element.
28104 * @cfg {String|Roo.Template} tpl The template used by this View
28108 * @cfg {String} dataName the named area of the template to use as the data area
28109 * Works with domtemplates roo-name="name"
28113 * @cfg {String} selectedClass The css class to add to selected nodes
28115 selectedClass : "x-view-selected",
28117 * @cfg {String} emptyText The empty text to show when nothing is loaded.
28122 * @cfg {String} text to display on mask (default Loading)
28126 * @cfg {Boolean} multiSelect Allow multiple selection
28128 multiSelect : false,
28130 * @cfg {Boolean} singleSelect Allow single selection
28132 singleSelect: false,
28135 * @cfg {Boolean} toggleSelect - selecting
28137 toggleSelect : false,
28140 * @cfg {Boolean} tickable - selecting
28145 * Returns the element this view is bound to.
28146 * @return {Roo.Element}
28148 getEl : function(){
28149 return this.wrapEl;
28155 * Refreshes the view. - called by datachanged on the store. - do not call directly.
28157 refresh : function(){
28158 //Roo.log('refresh');
28161 // if we are using something like 'domtemplate', then
28162 // the what gets used is:
28163 // t.applySubtemplate(NAME, data, wrapping data..)
28164 // the outer template then get' applied with
28165 // the store 'extra data'
28166 // and the body get's added to the
28167 // roo-name="data" node?
28168 // <span class='roo-tpl-{name}'></span> ?????
28172 this.clearSelections();
28173 this.el.update("");
28175 var records = this.store.getRange();
28176 if(records.length < 1) {
28178 // is this valid?? = should it render a template??
28180 this.el.update(this.emptyText);
28184 if (this.dataName) {
28185 this.el.update(t.apply(this.store.meta)); //????
28186 el = this.el.child('.roo-tpl-' + this.dataName);
28189 for(var i = 0, len = records.length; i < len; i++){
28190 var data = this.prepareData(records[i].data, i, records[i]);
28191 this.fireEvent("preparedata", this, data, i, records[i]);
28193 var d = Roo.apply({}, data);
28196 Roo.apply(d, {'roo-id' : Roo.id()});
28200 Roo.each(this.parent.item, function(item){
28201 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28204 Roo.apply(d, {'roo-data-checked' : 'checked'});
28208 html[html.length] = Roo.util.Format.trim(
28210 t.applySubtemplate(this.dataName, d, this.store.meta) :
28217 el.update(html.join(""));
28218 this.nodes = el.dom.childNodes;
28219 this.updateIndexes(0);
28224 * Function to override to reformat the data that is sent to
28225 * the template for each node.
28226 * DEPRICATED - use the preparedata event handler.
28227 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28228 * a JSON object for an UpdateManager bound view).
28230 prepareData : function(data, index, record)
28232 this.fireEvent("preparedata", this, data, index, record);
28236 onUpdate : function(ds, record){
28237 // Roo.log('on update');
28238 this.clearSelections();
28239 var index = this.store.indexOf(record);
28240 var n = this.nodes[index];
28241 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28242 n.parentNode.removeChild(n);
28243 this.updateIndexes(index, index);
28249 onAdd : function(ds, records, index)
28251 //Roo.log(['on Add', ds, records, index] );
28252 this.clearSelections();
28253 if(this.nodes.length == 0){
28257 var n = this.nodes[index];
28258 for(var i = 0, len = records.length; i < len; i++){
28259 var d = this.prepareData(records[i].data, i, records[i]);
28261 this.tpl.insertBefore(n, d);
28264 this.tpl.append(this.el, d);
28267 this.updateIndexes(index);
28270 onRemove : function(ds, record, index){
28271 // Roo.log('onRemove');
28272 this.clearSelections();
28273 var el = this.dataName ?
28274 this.el.child('.roo-tpl-' + this.dataName) :
28277 el.dom.removeChild(this.nodes[index]);
28278 this.updateIndexes(index);
28282 * Refresh an individual node.
28283 * @param {Number} index
28285 refreshNode : function(index){
28286 this.onUpdate(this.store, this.store.getAt(index));
28289 updateIndexes : function(startIndex, endIndex){
28290 var ns = this.nodes;
28291 startIndex = startIndex || 0;
28292 endIndex = endIndex || ns.length - 1;
28293 for(var i = startIndex; i <= endIndex; i++){
28294 ns[i].nodeIndex = i;
28299 * Changes the data store this view uses and refresh the view.
28300 * @param {Store} store
28302 setStore : function(store, initial){
28303 if(!initial && this.store){
28304 this.store.un("datachanged", this.refresh);
28305 this.store.un("add", this.onAdd);
28306 this.store.un("remove", this.onRemove);
28307 this.store.un("update", this.onUpdate);
28308 this.store.un("clear", this.refresh);
28309 this.store.un("beforeload", this.onBeforeLoad);
28310 this.store.un("load", this.onLoad);
28311 this.store.un("loadexception", this.onLoad);
28315 store.on("datachanged", this.refresh, this);
28316 store.on("add", this.onAdd, this);
28317 store.on("remove", this.onRemove, this);
28318 store.on("update", this.onUpdate, this);
28319 store.on("clear", this.refresh, this);
28320 store.on("beforeload", this.onBeforeLoad, this);
28321 store.on("load", this.onLoad, this);
28322 store.on("loadexception", this.onLoad, this);
28330 * onbeforeLoad - masks the loading area.
28333 onBeforeLoad : function(store,opts)
28335 //Roo.log('onBeforeLoad');
28337 this.el.update("");
28339 this.el.mask(this.mask ? this.mask : "Loading" );
28341 onLoad : function ()
28348 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28349 * @param {HTMLElement} node
28350 * @return {HTMLElement} The template node
28352 findItemFromChild : function(node){
28353 var el = this.dataName ?
28354 this.el.child('.roo-tpl-' + this.dataName,true) :
28357 if(!node || node.parentNode == el){
28360 var p = node.parentNode;
28361 while(p && p != el){
28362 if(p.parentNode == el){
28371 onClick : function(e){
28372 var item = this.findItemFromChild(e.getTarget());
28374 var index = this.indexOf(item);
28375 if(this.onItemClick(item, index, e) !== false){
28376 this.fireEvent("click", this, index, item, e);
28379 this.clearSelections();
28384 onContextMenu : function(e){
28385 var item = this.findItemFromChild(e.getTarget());
28387 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28392 onDblClick : function(e){
28393 var item = this.findItemFromChild(e.getTarget());
28395 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28399 onItemClick : function(item, index, e)
28401 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28404 if (this.toggleSelect) {
28405 var m = this.isSelected(item) ? 'unselect' : 'select';
28408 _t[m](item, true, false);
28411 if(this.multiSelect || this.singleSelect){
28412 if(this.multiSelect && e.shiftKey && this.lastSelection){
28413 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28415 this.select(item, this.multiSelect && e.ctrlKey);
28416 this.lastSelection = item;
28419 if(!this.tickable){
28420 e.preventDefault();
28428 * Get the number of selected nodes.
28431 getSelectionCount : function(){
28432 return this.selections.length;
28436 * Get the currently selected nodes.
28437 * @return {Array} An array of HTMLElements
28439 getSelectedNodes : function(){
28440 return this.selections;
28444 * Get the indexes of the selected nodes.
28447 getSelectedIndexes : function(){
28448 var indexes = [], s = this.selections;
28449 for(var i = 0, len = s.length; i < len; i++){
28450 indexes.push(s[i].nodeIndex);
28456 * Clear all selections
28457 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28459 clearSelections : function(suppressEvent){
28460 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28461 this.cmp.elements = this.selections;
28462 this.cmp.removeClass(this.selectedClass);
28463 this.selections = [];
28464 if(!suppressEvent){
28465 this.fireEvent("selectionchange", this, this.selections);
28471 * Returns true if the passed node is selected
28472 * @param {HTMLElement/Number} node The node or node index
28473 * @return {Boolean}
28475 isSelected : function(node){
28476 var s = this.selections;
28480 node = this.getNode(node);
28481 return s.indexOf(node) !== -1;
28486 * @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
28487 * @param {Boolean} keepExisting (optional) true to keep existing selections
28488 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28490 select : function(nodeInfo, keepExisting, suppressEvent){
28491 if(nodeInfo instanceof Array){
28493 this.clearSelections(true);
28495 for(var i = 0, len = nodeInfo.length; i < len; i++){
28496 this.select(nodeInfo[i], true, true);
28500 var node = this.getNode(nodeInfo);
28501 if(!node || this.isSelected(node)){
28502 return; // already selected.
28505 this.clearSelections(true);
28508 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28509 Roo.fly(node).addClass(this.selectedClass);
28510 this.selections.push(node);
28511 if(!suppressEvent){
28512 this.fireEvent("selectionchange", this, this.selections);
28520 * @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
28521 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28522 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28524 unselect : function(nodeInfo, keepExisting, suppressEvent)
28526 if(nodeInfo instanceof Array){
28527 Roo.each(this.selections, function(s) {
28528 this.unselect(s, nodeInfo);
28532 var node = this.getNode(nodeInfo);
28533 if(!node || !this.isSelected(node)){
28534 //Roo.log("not selected");
28535 return; // not selected.
28539 Roo.each(this.selections, function(s) {
28541 Roo.fly(node).removeClass(this.selectedClass);
28548 this.selections= ns;
28549 this.fireEvent("selectionchange", this, this.selections);
28553 * Gets a template node.
28554 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28555 * @return {HTMLElement} The node or null if it wasn't found
28557 getNode : function(nodeInfo){
28558 if(typeof nodeInfo == "string"){
28559 return document.getElementById(nodeInfo);
28560 }else if(typeof nodeInfo == "number"){
28561 return this.nodes[nodeInfo];
28567 * Gets a range template nodes.
28568 * @param {Number} startIndex
28569 * @param {Number} endIndex
28570 * @return {Array} An array of nodes
28572 getNodes : function(start, end){
28573 var ns = this.nodes;
28574 start = start || 0;
28575 end = typeof end == "undefined" ? ns.length - 1 : end;
28578 for(var i = start; i <= end; i++){
28582 for(var i = start; i >= end; i--){
28590 * Finds the index of the passed node
28591 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28592 * @return {Number} The index of the node or -1
28594 indexOf : function(node){
28595 node = this.getNode(node);
28596 if(typeof node.nodeIndex == "number"){
28597 return node.nodeIndex;
28599 var ns = this.nodes;
28600 for(var i = 0, len = ns.length; i < len; i++){
28610 * Ext JS Library 1.1.1
28611 * Copyright(c) 2006-2007, Ext JS, LLC.
28613 * Originally Released Under LGPL - original licence link has changed is not relivant.
28616 * <script type="text/javascript">
28620 * @class Roo.JsonView
28621 * @extends Roo.View
28622 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28624 var view = new Roo.JsonView({
28625 container: "my-element",
28626 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
28631 // listen for node click?
28632 view.on("click", function(vw, index, node, e){
28633 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28636 // direct load of JSON data
28637 view.load("foobar.php");
28639 // Example from my blog list
28640 var tpl = new Roo.Template(
28641 '<div class="entry">' +
28642 '<a class="entry-title" href="{link}">{title}</a>' +
28643 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
28644 "</div><hr />"
28647 var moreView = new Roo.JsonView({
28648 container : "entry-list",
28652 moreView.on("beforerender", this.sortEntries, this);
28654 url: "/blog/get-posts.php",
28655 params: "allposts=true",
28656 text: "Loading Blog Entries..."
28660 * Note: old code is supported with arguments : (container, template, config)
28664 * Create a new JsonView
28666 * @param {Object} config The config object
28669 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28672 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28674 var um = this.el.getUpdateManager();
28675 um.setRenderer(this);
28676 um.on("update", this.onLoad, this);
28677 um.on("failure", this.onLoadException, this);
28680 * @event beforerender
28681 * Fires before rendering of the downloaded JSON data.
28682 * @param {Roo.JsonView} this
28683 * @param {Object} data The JSON data loaded
28687 * Fires when data is loaded.
28688 * @param {Roo.JsonView} this
28689 * @param {Object} data The JSON data loaded
28690 * @param {Object} response The raw Connect response object
28693 * @event loadexception
28694 * Fires when loading fails.
28695 * @param {Roo.JsonView} this
28696 * @param {Object} response The raw Connect response object
28699 'beforerender' : true,
28701 'loadexception' : true
28704 Roo.extend(Roo.JsonView, Roo.View, {
28706 * @type {String} The root property in the loaded JSON object that contains the data
28711 * Refreshes the view.
28713 refresh : function(){
28714 this.clearSelections();
28715 this.el.update("");
28717 var o = this.jsonData;
28718 if(o && o.length > 0){
28719 for(var i = 0, len = o.length; i < len; i++){
28720 var data = this.prepareData(o[i], i, o);
28721 html[html.length] = this.tpl.apply(data);
28724 html.push(this.emptyText);
28726 this.el.update(html.join(""));
28727 this.nodes = this.el.dom.childNodes;
28728 this.updateIndexes(0);
28732 * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
28733 * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
28736 url: "your-url.php",
28737 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28738 callback: yourFunction,
28739 scope: yourObject, //(optional scope)
28742 text: "Loading...",
28747 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28748 * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
28749 * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
28750 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28751 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
28754 var um = this.el.getUpdateManager();
28755 um.update.apply(um, arguments);
28758 // note - render is a standard framework call...
28759 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28760 render : function(el, response){
28762 this.clearSelections();
28763 this.el.update("");
28766 if (response != '') {
28767 o = Roo.util.JSON.decode(response.responseText);
28770 o = o[this.jsonRoot];
28776 * The current JSON data or null
28779 this.beforeRender();
28784 * Get the number of records in the current JSON dataset
28787 getCount : function(){
28788 return this.jsonData ? this.jsonData.length : 0;
28792 * Returns the JSON object for the specified node(s)
28793 * @param {HTMLElement/Array} node The node or an array of nodes
28794 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28795 * you get the JSON object for the node
28797 getNodeData : function(node){
28798 if(node instanceof Array){
28800 for(var i = 0, len = node.length; i < len; i++){
28801 data.push(this.getNodeData(node[i]));
28805 return this.jsonData[this.indexOf(node)] || null;
28808 beforeRender : function(){
28809 this.snapshot = this.jsonData;
28811 this.sort.apply(this, this.sortInfo);
28813 this.fireEvent("beforerender", this, this.jsonData);
28816 onLoad : function(el, o){
28817 this.fireEvent("load", this, this.jsonData, o);
28820 onLoadException : function(el, o){
28821 this.fireEvent("loadexception", this, o);
28825 * Filter the data by a specific property.
28826 * @param {String} property A property on your JSON objects
28827 * @param {String/RegExp} value Either string that the property values
28828 * should start with, or a RegExp to test against the property
28830 filter : function(property, value){
28833 var ss = this.snapshot;
28834 if(typeof value == "string"){
28835 var vlen = value.length;
28837 this.clearFilter();
28840 value = value.toLowerCase();
28841 for(var i = 0, len = ss.length; i < len; i++){
28843 if(o[property].substr(0, vlen).toLowerCase() == value){
28847 } else if(value.exec){ // regex?
28848 for(var i = 0, len = ss.length; i < len; i++){
28850 if(value.test(o[property])){
28857 this.jsonData = data;
28863 * Filter by a function. The passed function will be called with each
28864 * object in the current dataset. If the function returns true the value is kept,
28865 * otherwise it is filtered.
28866 * @param {Function} fn
28867 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28869 filterBy : function(fn, scope){
28872 var ss = this.snapshot;
28873 for(var i = 0, len = ss.length; i < len; i++){
28875 if(fn.call(scope || this, o)){
28879 this.jsonData = data;
28885 * Clears the current filter.
28887 clearFilter : function(){
28888 if(this.snapshot && this.jsonData != this.snapshot){
28889 this.jsonData = this.snapshot;
28896 * Sorts the data for this view and refreshes it.
28897 * @param {String} property A property on your JSON objects to sort on
28898 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28899 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28901 sort : function(property, dir, sortType){
28902 this.sortInfo = Array.prototype.slice.call(arguments, 0);
28905 var dsc = dir && dir.toLowerCase() == "desc";
28906 var f = function(o1, o2){
28907 var v1 = sortType ? sortType(o1[p]) : o1[p];
28908 var v2 = sortType ? sortType(o2[p]) : o2[p];
28911 return dsc ? +1 : -1;
28912 } else if(v1 > v2){
28913 return dsc ? -1 : +1;
28918 this.jsonData.sort(f);
28920 if(this.jsonData != this.snapshot){
28921 this.snapshot.sort(f);
28927 * Ext JS Library 1.1.1
28928 * Copyright(c) 2006-2007, Ext JS, LLC.
28930 * Originally Released Under LGPL - original licence link has changed is not relivant.
28933 * <script type="text/javascript">
28938 * @class Roo.ColorPalette
28939 * @extends Roo.Component
28940 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
28941 * Here's an example of typical usage:
28943 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
28944 cp.render('my-div');
28946 cp.on('select', function(palette, selColor){
28947 // do something with selColor
28951 * Create a new ColorPalette
28952 * @param {Object} config The config object
28954 Roo.ColorPalette = function(config){
28955 Roo.ColorPalette.superclass.constructor.call(this, config);
28959 * Fires when a color is selected
28960 * @param {ColorPalette} this
28961 * @param {String} color The 6-digit color hex code (without the # symbol)
28967 this.on("select", this.handler, this.scope, true);
28970 Roo.extend(Roo.ColorPalette, Roo.Component, {
28972 * @cfg {String} itemCls
28973 * The CSS class to apply to the containing element (defaults to "x-color-palette")
28975 itemCls : "x-color-palette",
28977 * @cfg {String} value
28978 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
28979 * the hex codes are case-sensitive.
28982 clickEvent:'click',
28984 ctype: "Roo.ColorPalette",
28987 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28989 allowReselect : false,
28992 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
28993 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
28994 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28995 * of colors with the width setting until the box is symmetrical.</p>
28996 * <p>You can override individual colors if needed:</p>
28998 var cp = new Roo.ColorPalette();
28999 cp.colors[0] = "FF0000"; // change the first box to red
29002 Or you can provide a custom array of your own for complete control:
29004 var cp = new Roo.ColorPalette();
29005 cp.colors = ["000000", "993300", "333300"];
29010 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29011 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29012 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29013 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29014 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29018 onRender : function(container, position){
29019 var t = new Roo.MasterTemplate(
29020 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
29022 var c = this.colors;
29023 for(var i = 0, len = c.length; i < len; i++){
29026 var el = document.createElement("div");
29027 el.className = this.itemCls;
29029 container.dom.insertBefore(el, position);
29030 this.el = Roo.get(el);
29031 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
29032 if(this.clickEvent != 'click'){
29033 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
29038 afterRender : function(){
29039 Roo.ColorPalette.superclass.afterRender.call(this);
29041 var s = this.value;
29048 handleClick : function(e, t){
29049 e.preventDefault();
29050 if(!this.disabled){
29051 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29052 this.select(c.toUpperCase());
29057 * Selects the specified color in the palette (fires the select event)
29058 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29060 select : function(color){
29061 color = color.replace("#", "");
29062 if(color != this.value || this.allowReselect){
29065 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29067 el.child("a.color-"+color).addClass("x-color-palette-sel");
29068 this.value = color;
29069 this.fireEvent("select", this, color);
29074 * Ext JS Library 1.1.1
29075 * Copyright(c) 2006-2007, Ext JS, LLC.
29077 * Originally Released Under LGPL - original licence link has changed is not relivant.
29080 * <script type="text/javascript">
29084 * @class Roo.DatePicker
29085 * @extends Roo.Component
29086 * Simple date picker class.
29088 * Create a new DatePicker
29089 * @param {Object} config The config object
29091 Roo.DatePicker = function(config){
29092 Roo.DatePicker.superclass.constructor.call(this, config);
29094 this.value = config && config.value ?
29095 config.value.clearTime() : new Date().clearTime();
29100 * Fires when a date is selected
29101 * @param {DatePicker} this
29102 * @param {Date} date The selected date
29106 * @event monthchange
29107 * Fires when the displayed month changes
29108 * @param {DatePicker} this
29109 * @param {Date} date The selected month
29111 'monthchange': true
29115 this.on("select", this.handler, this.scope || this);
29117 // build the disabledDatesRE
29118 if(!this.disabledDatesRE && this.disabledDates){
29119 var dd = this.disabledDates;
29121 for(var i = 0; i < dd.length; i++){
29123 if(i != dd.length-1) {
29127 this.disabledDatesRE = new RegExp(re + ")");
29131 Roo.extend(Roo.DatePicker, Roo.Component, {
29133 * @cfg {String} todayText
29134 * The text to display on the button that selects the current date (defaults to "Today")
29136 todayText : "Today",
29138 * @cfg {String} okText
29139 * The text to display on the ok button
29141 okText : " OK ", //   to give the user extra clicking room
29143 * @cfg {String} cancelText
29144 * The text to display on the cancel button
29146 cancelText : "Cancel",
29148 * @cfg {String} todayTip
29149 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29151 todayTip : "{0} (Spacebar)",
29153 * @cfg {Date} minDate
29154 * Minimum allowable date (JavaScript date object, defaults to null)
29158 * @cfg {Date} maxDate
29159 * Maximum allowable date (JavaScript date object, defaults to null)
29163 * @cfg {String} minText
29164 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29166 minText : "This date is before the minimum date",
29168 * @cfg {String} maxText
29169 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29171 maxText : "This date is after the maximum date",
29173 * @cfg {String} format
29174 * The default date format string which can be overriden for localization support. The format must be
29175 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29179 * @cfg {Array} disabledDays
29180 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29182 disabledDays : null,
29184 * @cfg {String} disabledDaysText
29185 * The tooltip to display when the date falls on a disabled day (defaults to "")
29187 disabledDaysText : "",
29189 * @cfg {RegExp} disabledDatesRE
29190 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29192 disabledDatesRE : null,
29194 * @cfg {String} disabledDatesText
29195 * The tooltip text to display when the date falls on a disabled date (defaults to "")
29197 disabledDatesText : "",
29199 * @cfg {Boolean} constrainToViewport
29200 * True to constrain the date picker to the viewport (defaults to true)
29202 constrainToViewport : true,
29204 * @cfg {Array} monthNames
29205 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29207 monthNames : Date.monthNames,
29209 * @cfg {Array} dayNames
29210 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29212 dayNames : Date.dayNames,
29214 * @cfg {String} nextText
29215 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29217 nextText: 'Next Month (Control+Right)',
29219 * @cfg {String} prevText
29220 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29222 prevText: 'Previous Month (Control+Left)',
29224 * @cfg {String} monthYearText
29225 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29227 monthYearText: 'Choose a month (Control+Up/Down to move years)',
29229 * @cfg {Number} startDay
29230 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29234 * @cfg {Bool} showClear
29235 * Show a clear button (usefull for date form elements that can be blank.)
29241 * Sets the value of the date field
29242 * @param {Date} value The date to set
29244 setValue : function(value){
29245 var old = this.value;
29247 if (typeof(value) == 'string') {
29249 value = Date.parseDate(value, this.format);
29252 value = new Date();
29255 this.value = value.clearTime(true);
29257 this.update(this.value);
29262 * Gets the current selected value of the date field
29263 * @return {Date} The selected date
29265 getValue : function(){
29270 focus : function(){
29272 this.update(this.activeDate);
29277 onRender : function(container, position){
29280 '<table cellspacing="0">',
29281 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',
29282 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29283 var dn = this.dayNames;
29284 for(var i = 0; i < 7; i++){
29285 var d = this.startDay+i;
29289 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29291 m[m.length] = "</tr></thead><tbody><tr>";
29292 for(var i = 0; i < 42; i++) {
29293 if(i % 7 == 0 && i != 0){
29294 m[m.length] = "</tr><tr>";
29296 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29298 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29299 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29301 var el = document.createElement("div");
29302 el.className = "x-date-picker";
29303 el.innerHTML = m.join("");
29305 container.dom.insertBefore(el, position);
29307 this.el = Roo.get(el);
29308 this.eventEl = Roo.get(el.firstChild);
29310 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29311 handler: this.showPrevMonth,
29313 preventDefault:true,
29317 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29318 handler: this.showNextMonth,
29320 preventDefault:true,
29324 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
29326 this.monthPicker = this.el.down('div.x-date-mp');
29327 this.monthPicker.enableDisplayMode('block');
29329 var kn = new Roo.KeyNav(this.eventEl, {
29330 "left" : function(e){
29332 this.showPrevMonth() :
29333 this.update(this.activeDate.add("d", -1));
29336 "right" : function(e){
29338 this.showNextMonth() :
29339 this.update(this.activeDate.add("d", 1));
29342 "up" : function(e){
29344 this.showNextYear() :
29345 this.update(this.activeDate.add("d", -7));
29348 "down" : function(e){
29350 this.showPrevYear() :
29351 this.update(this.activeDate.add("d", 7));
29354 "pageUp" : function(e){
29355 this.showNextMonth();
29358 "pageDown" : function(e){
29359 this.showPrevMonth();
29362 "enter" : function(e){
29363 e.stopPropagation();
29370 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
29372 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
29374 this.el.unselectable();
29376 this.cells = this.el.select("table.x-date-inner tbody td");
29377 this.textNodes = this.el.query("table.x-date-inner tbody span");
29379 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29381 tooltip: this.monthYearText
29384 this.mbtn.on('click', this.showMonthPicker, this);
29385 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29388 var today = (new Date()).dateFormat(this.format);
29390 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29391 if (this.showClear) {
29392 baseTb.add( new Roo.Toolbar.Fill());
29395 text: String.format(this.todayText, today),
29396 tooltip: String.format(this.todayTip, today),
29397 handler: this.selectToday,
29401 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29404 if (this.showClear) {
29406 baseTb.add( new Roo.Toolbar.Fill());
29409 cls: 'x-btn-icon x-btn-clear',
29410 handler: function() {
29412 this.fireEvent("select", this, '');
29422 this.update(this.value);
29425 createMonthPicker : function(){
29426 if(!this.monthPicker.dom.firstChild){
29427 var buf = ['<table border="0" cellspacing="0">'];
29428 for(var i = 0; i < 6; i++){
29430 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29431 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29433 '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
29434 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29438 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29440 '</button><button type="button" class="x-date-mp-cancel">',
29442 '</button></td></tr>',
29445 this.monthPicker.update(buf.join(''));
29446 this.monthPicker.on('click', this.onMonthClick, this);
29447 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29449 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29450 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29452 this.mpMonths.each(function(m, a, i){
29455 m.dom.xmonth = 5 + Math.round(i * .5);
29457 m.dom.xmonth = Math.round((i-1) * .5);
29463 showMonthPicker : function(){
29464 this.createMonthPicker();
29465 var size = this.el.getSize();
29466 this.monthPicker.setSize(size);
29467 this.monthPicker.child('table').setSize(size);
29469 this.mpSelMonth = (this.activeDate || this.value).getMonth();
29470 this.updateMPMonth(this.mpSelMonth);
29471 this.mpSelYear = (this.activeDate || this.value).getFullYear();
29472 this.updateMPYear(this.mpSelYear);
29474 this.monthPicker.slideIn('t', {duration:.2});
29477 updateMPYear : function(y){
29479 var ys = this.mpYears.elements;
29480 for(var i = 1; i <= 10; i++){
29481 var td = ys[i-1], y2;
29483 y2 = y + Math.round(i * .5);
29484 td.firstChild.innerHTML = y2;
29487 y2 = y - (5-Math.round(i * .5));
29488 td.firstChild.innerHTML = y2;
29491 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29495 updateMPMonth : function(sm){
29496 this.mpMonths.each(function(m, a, i){
29497 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29501 selectMPMonth: function(m){
29505 onMonthClick : function(e, t){
29507 var el = new Roo.Element(t), pn;
29508 if(el.is('button.x-date-mp-cancel')){
29509 this.hideMonthPicker();
29511 else if(el.is('button.x-date-mp-ok')){
29512 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29513 this.hideMonthPicker();
29515 else if(pn = el.up('td.x-date-mp-month', 2)){
29516 this.mpMonths.removeClass('x-date-mp-sel');
29517 pn.addClass('x-date-mp-sel');
29518 this.mpSelMonth = pn.dom.xmonth;
29520 else if(pn = el.up('td.x-date-mp-year', 2)){
29521 this.mpYears.removeClass('x-date-mp-sel');
29522 pn.addClass('x-date-mp-sel');
29523 this.mpSelYear = pn.dom.xyear;
29525 else if(el.is('a.x-date-mp-prev')){
29526 this.updateMPYear(this.mpyear-10);
29528 else if(el.is('a.x-date-mp-next')){
29529 this.updateMPYear(this.mpyear+10);
29533 onMonthDblClick : function(e, t){
29535 var el = new Roo.Element(t), pn;
29536 if(pn = el.up('td.x-date-mp-month', 2)){
29537 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29538 this.hideMonthPicker();
29540 else if(pn = el.up('td.x-date-mp-year', 2)){
29541 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29542 this.hideMonthPicker();
29546 hideMonthPicker : function(disableAnim){
29547 if(this.monthPicker){
29548 if(disableAnim === true){
29549 this.monthPicker.hide();
29551 this.monthPicker.slideOut('t', {duration:.2});
29557 showPrevMonth : function(e){
29558 this.update(this.activeDate.add("mo", -1));
29562 showNextMonth : function(e){
29563 this.update(this.activeDate.add("mo", 1));
29567 showPrevYear : function(){
29568 this.update(this.activeDate.add("y", -1));
29572 showNextYear : function(){
29573 this.update(this.activeDate.add("y", 1));
29577 handleMouseWheel : function(e){
29578 var delta = e.getWheelDelta();
29580 this.showPrevMonth();
29582 } else if(delta < 0){
29583 this.showNextMonth();
29589 handleDateClick : function(e, t){
29591 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29592 this.setValue(new Date(t.dateValue));
29593 this.fireEvent("select", this, this.value);
29598 selectToday : function(){
29599 this.setValue(new Date().clearTime());
29600 this.fireEvent("select", this, this.value);
29604 update : function(date)
29606 var vd = this.activeDate;
29607 this.activeDate = date;
29609 var t = date.getTime();
29610 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29611 this.cells.removeClass("x-date-selected");
29612 this.cells.each(function(c){
29613 if(c.dom.firstChild.dateValue == t){
29614 c.addClass("x-date-selected");
29615 setTimeout(function(){
29616 try{c.dom.firstChild.focus();}catch(e){}
29625 var days = date.getDaysInMonth();
29626 var firstOfMonth = date.getFirstDateOfMonth();
29627 var startingPos = firstOfMonth.getDay()-this.startDay;
29629 if(startingPos <= this.startDay){
29633 var pm = date.add("mo", -1);
29634 var prevStart = pm.getDaysInMonth()-startingPos;
29636 var cells = this.cells.elements;
29637 var textEls = this.textNodes;
29638 days += startingPos;
29640 // convert everything to numbers so it's fast
29641 var day = 86400000;
29642 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29643 var today = new Date().clearTime().getTime();
29644 var sel = date.clearTime().getTime();
29645 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29646 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29647 var ddMatch = this.disabledDatesRE;
29648 var ddText = this.disabledDatesText;
29649 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29650 var ddaysText = this.disabledDaysText;
29651 var format = this.format;
29653 var setCellClass = function(cal, cell){
29655 var t = d.getTime();
29656 cell.firstChild.dateValue = t;
29658 cell.className += " x-date-today";
29659 cell.title = cal.todayText;
29662 cell.className += " x-date-selected";
29663 setTimeout(function(){
29664 try{cell.firstChild.focus();}catch(e){}
29669 cell.className = " x-date-disabled";
29670 cell.title = cal.minText;
29674 cell.className = " x-date-disabled";
29675 cell.title = cal.maxText;
29679 if(ddays.indexOf(d.getDay()) != -1){
29680 cell.title = ddaysText;
29681 cell.className = " x-date-disabled";
29684 if(ddMatch && format){
29685 var fvalue = d.dateFormat(format);
29686 if(ddMatch.test(fvalue)){
29687 cell.title = ddText.replace("%0", fvalue);
29688 cell.className = " x-date-disabled";
29694 for(; i < startingPos; i++) {
29695 textEls[i].innerHTML = (++prevStart);
29696 d.setDate(d.getDate()+1);
29697 cells[i].className = "x-date-prevday";
29698 setCellClass(this, cells[i]);
29700 for(; i < days; i++){
29701 intDay = i - startingPos + 1;
29702 textEls[i].innerHTML = (intDay);
29703 d.setDate(d.getDate()+1);
29704 cells[i].className = "x-date-active";
29705 setCellClass(this, cells[i]);
29708 for(; i < 42; i++) {
29709 textEls[i].innerHTML = (++extraDays);
29710 d.setDate(d.getDate()+1);
29711 cells[i].className = "x-date-nextday";
29712 setCellClass(this, cells[i]);
29715 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29716 this.fireEvent('monthchange', this, date);
29718 if(!this.internalRender){
29719 var main = this.el.dom.firstChild;
29720 var w = main.offsetWidth;
29721 this.el.setWidth(w + this.el.getBorderWidth("lr"));
29722 Roo.fly(main).setWidth(w);
29723 this.internalRender = true;
29724 // opera does not respect the auto grow header center column
29725 // then, after it gets a width opera refuses to recalculate
29726 // without a second pass
29727 if(Roo.isOpera && !this.secondPass){
29728 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29729 this.secondPass = true;
29730 this.update.defer(10, this, [date]);
29738 * Ext JS Library 1.1.1
29739 * Copyright(c) 2006-2007, Ext JS, LLC.
29741 * Originally Released Under LGPL - original licence link has changed is not relivant.
29744 * <script type="text/javascript">
29747 * @class Roo.TabPanel
29748 * @extends Roo.util.Observable
29749 * A lightweight tab container.
29753 // basic tabs 1, built from existing content
29754 var tabs = new Roo.TabPanel("tabs1");
29755 tabs.addTab("script", "View Script");
29756 tabs.addTab("markup", "View Markup");
29757 tabs.activate("script");
29759 // more advanced tabs, built from javascript
29760 var jtabs = new Roo.TabPanel("jtabs");
29761 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29763 // set up the UpdateManager
29764 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29765 var updater = tab2.getUpdateManager();
29766 updater.setDefaultUrl("ajax1.htm");
29767 tab2.on('activate', updater.refresh, updater, true);
29769 // Use setUrl for Ajax loading
29770 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29771 tab3.setUrl("ajax2.htm", null, true);
29774 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29777 jtabs.activate("jtabs-1");
29780 * Create a new TabPanel.
29781 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29782 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29784 Roo.TabPanel = function(container, config){
29786 * The container element for this TabPanel.
29787 * @type Roo.Element
29789 this.el = Roo.get(container, true);
29791 if(typeof config == "boolean"){
29792 this.tabPosition = config ? "bottom" : "top";
29794 Roo.apply(this, config);
29797 if(this.tabPosition == "bottom"){
29798 this.bodyEl = Roo.get(this.createBody(this.el.dom));
29799 this.el.addClass("x-tabs-bottom");
29801 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29802 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29803 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29805 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29807 if(this.tabPosition != "bottom"){
29808 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29809 * @type Roo.Element
29811 this.bodyEl = Roo.get(this.createBody(this.el.dom));
29812 this.el.addClass("x-tabs-top");
29816 this.bodyEl.setStyle("position", "relative");
29818 this.active = null;
29819 this.activateDelegate = this.activate.createDelegate(this);
29824 * Fires when the active tab changes
29825 * @param {Roo.TabPanel} this
29826 * @param {Roo.TabPanelItem} activePanel The new active tab
29830 * @event beforetabchange
29831 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29832 * @param {Roo.TabPanel} this
29833 * @param {Object} e Set cancel to true on this object to cancel the tab change
29834 * @param {Roo.TabPanelItem} tab The tab being changed to
29836 "beforetabchange" : true
29839 Roo.EventManager.onWindowResize(this.onResize, this);
29840 this.cpad = this.el.getPadding("lr");
29841 this.hiddenCount = 0;
29844 // toolbar on the tabbar support...
29845 if (this.toolbar) {
29846 var tcfg = this.toolbar;
29847 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
29848 this.toolbar = new Roo.Toolbar(tcfg);
29849 if (Roo.isSafari) {
29850 var tbl = tcfg.container.child('table', true);
29851 tbl.setAttribute('width', '100%');
29858 Roo.TabPanel.superclass.constructor.call(this);
29861 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29863 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29865 tabPosition : "top",
29867 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29869 currentTabWidth : 0,
29871 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29875 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29879 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29881 preferredTabWidth : 175,
29883 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29885 resizeTabs : false,
29887 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29889 monitorResize : true,
29891 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
29896 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29897 * @param {String} id The id of the div to use <b>or create</b>
29898 * @param {String} text The text for the tab
29899 * @param {String} content (optional) Content to put in the TabPanelItem body
29900 * @param {Boolean} closable (optional) True to create a close icon on the tab
29901 * @return {Roo.TabPanelItem} The created TabPanelItem
29903 addTab : function(id, text, content, closable){
29904 var item = new Roo.TabPanelItem(this, id, text, closable);
29905 this.addTabItem(item);
29907 item.setContent(content);
29913 * Returns the {@link Roo.TabPanelItem} with the specified id/index
29914 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29915 * @return {Roo.TabPanelItem}
29917 getTab : function(id){
29918 return this.items[id];
29922 * Hides the {@link Roo.TabPanelItem} with the specified id/index
29923 * @param {String/Number} id The id or index of the TabPanelItem to hide.
29925 hideTab : function(id){
29926 var t = this.items[id];
29929 this.hiddenCount++;
29930 this.autoSizeTabs();
29935 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29936 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29938 unhideTab : function(id){
29939 var t = this.items[id];
29941 t.setHidden(false);
29942 this.hiddenCount--;
29943 this.autoSizeTabs();
29948 * Adds an existing {@link Roo.TabPanelItem}.
29949 * @param {Roo.TabPanelItem} item The TabPanelItem to add
29951 addTabItem : function(item){
29952 this.items[item.id] = item;
29953 this.items.push(item);
29954 if(this.resizeTabs){
29955 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29956 this.autoSizeTabs();
29963 * Removes a {@link Roo.TabPanelItem}.
29964 * @param {String/Number} id The id or index of the TabPanelItem to remove.
29966 removeTab : function(id){
29967 var items = this.items;
29968 var tab = items[id];
29969 if(!tab) { return; }
29970 var index = items.indexOf(tab);
29971 if(this.active == tab && items.length > 1){
29972 var newTab = this.getNextAvailable(index);
29977 this.stripEl.dom.removeChild(tab.pnode.dom);
29978 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29979 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29981 items.splice(index, 1);
29982 delete this.items[tab.id];
29983 tab.fireEvent("close", tab);
29984 tab.purgeListeners();
29985 this.autoSizeTabs();
29988 getNextAvailable : function(start){
29989 var items = this.items;
29991 // look for a next tab that will slide over to
29992 // replace the one being removed
29993 while(index < items.length){
29994 var item = items[++index];
29995 if(item && !item.isHidden()){
29999 // if one isn't found select the previous tab (on the left)
30002 var item = items[--index];
30003 if(item && !item.isHidden()){
30011 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30012 * @param {String/Number} id The id or index of the TabPanelItem to disable.
30014 disableTab : function(id){
30015 var tab = this.items[id];
30016 if(tab && this.active != tab){
30022 * Enables a {@link Roo.TabPanelItem} that is disabled.
30023 * @param {String/Number} id The id or index of the TabPanelItem to enable.
30025 enableTab : function(id){
30026 var tab = this.items[id];
30031 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30032 * @param {String/Number} id The id or index of the TabPanelItem to activate.
30033 * @return {Roo.TabPanelItem} The TabPanelItem.
30035 activate : function(id){
30036 var tab = this.items[id];
30040 if(tab == this.active || tab.disabled){
30044 this.fireEvent("beforetabchange", this, e, tab);
30045 if(e.cancel !== true && !tab.disabled){
30047 this.active.hide();
30049 this.active = this.items[id];
30050 this.active.show();
30051 this.fireEvent("tabchange", this, this.active);
30057 * Gets the active {@link Roo.TabPanelItem}.
30058 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30060 getActiveTab : function(){
30061 return this.active;
30065 * Updates the tab body element to fit the height of the container element
30066 * for overflow scrolling
30067 * @param {Number} targetHeight (optional) Override the starting height from the elements height
30069 syncHeight : function(targetHeight){
30070 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30071 var bm = this.bodyEl.getMargins();
30072 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30073 this.bodyEl.setHeight(newHeight);
30077 onResize : function(){
30078 if(this.monitorResize){
30079 this.autoSizeTabs();
30084 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30086 beginUpdate : function(){
30087 this.updating = true;
30091 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30093 endUpdate : function(){
30094 this.updating = false;
30095 this.autoSizeTabs();
30099 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30101 autoSizeTabs : function(){
30102 var count = this.items.length;
30103 var vcount = count - this.hiddenCount;
30104 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30107 var w = Math.max(this.el.getWidth() - this.cpad, 10);
30108 var availWidth = Math.floor(w / vcount);
30109 var b = this.stripBody;
30110 if(b.getWidth() > w){
30111 var tabs = this.items;
30112 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30113 if(availWidth < this.minTabWidth){
30114 /*if(!this.sleft){ // incomplete scrolling code
30115 this.createScrollButtons();
30118 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30121 if(this.currentTabWidth < this.preferredTabWidth){
30122 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30128 * Returns the number of tabs in this TabPanel.
30131 getCount : function(){
30132 return this.items.length;
30136 * Resizes all the tabs to the passed width
30137 * @param {Number} The new width
30139 setTabWidth : function(width){
30140 this.currentTabWidth = width;
30141 for(var i = 0, len = this.items.length; i < len; i++) {
30142 if(!this.items[i].isHidden()) {
30143 this.items[i].setWidth(width);
30149 * Destroys this TabPanel
30150 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30152 destroy : function(removeEl){
30153 Roo.EventManager.removeResizeListener(this.onResize, this);
30154 for(var i = 0, len = this.items.length; i < len; i++){
30155 this.items[i].purgeListeners();
30157 if(removeEl === true){
30158 this.el.update("");
30165 * @class Roo.TabPanelItem
30166 * @extends Roo.util.Observable
30167 * Represents an individual item (tab plus body) in a TabPanel.
30168 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30169 * @param {String} id The id of this TabPanelItem
30170 * @param {String} text The text for the tab of this TabPanelItem
30171 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30173 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30175 * The {@link Roo.TabPanel} this TabPanelItem belongs to
30176 * @type Roo.TabPanel
30178 this.tabPanel = tabPanel;
30180 * The id for this TabPanelItem
30185 this.disabled = false;
30189 this.loaded = false;
30190 this.closable = closable;
30193 * The body element for this TabPanelItem.
30194 * @type Roo.Element
30196 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30197 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30198 this.bodyEl.setStyle("display", "block");
30199 this.bodyEl.setStyle("zoom", "1");
30202 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30204 this.el = Roo.get(els.el, true);
30205 this.inner = Roo.get(els.inner, true);
30206 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30207 this.pnode = Roo.get(els.el.parentNode, true);
30208 this.el.on("mousedown", this.onTabMouseDown, this);
30209 this.el.on("click", this.onTabClick, this);
30212 var c = Roo.get(els.close, true);
30213 c.dom.title = this.closeText;
30214 c.addClassOnOver("close-over");
30215 c.on("click", this.closeClick, this);
30221 * Fires when this tab becomes the active tab.
30222 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30223 * @param {Roo.TabPanelItem} this
30227 * @event beforeclose
30228 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30229 * @param {Roo.TabPanelItem} this
30230 * @param {Object} e Set cancel to true on this object to cancel the close.
30232 "beforeclose": true,
30235 * Fires when this tab is closed.
30236 * @param {Roo.TabPanelItem} this
30240 * @event deactivate
30241 * Fires when this tab is no longer the active tab.
30242 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30243 * @param {Roo.TabPanelItem} this
30245 "deactivate" : true
30247 this.hidden = false;
30249 Roo.TabPanelItem.superclass.constructor.call(this);
30252 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30253 purgeListeners : function(){
30254 Roo.util.Observable.prototype.purgeListeners.call(this);
30255 this.el.removeAllListeners();
30258 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30261 this.pnode.addClass("on");
30264 this.tabPanel.stripWrap.repaint();
30266 this.fireEvent("activate", this.tabPanel, this);
30270 * Returns true if this tab is the active tab.
30271 * @return {Boolean}
30273 isActive : function(){
30274 return this.tabPanel.getActiveTab() == this;
30278 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30281 this.pnode.removeClass("on");
30283 this.fireEvent("deactivate", this.tabPanel, this);
30286 hideAction : function(){
30287 this.bodyEl.hide();
30288 this.bodyEl.setStyle("position", "absolute");
30289 this.bodyEl.setLeft("-20000px");
30290 this.bodyEl.setTop("-20000px");
30293 showAction : function(){
30294 this.bodyEl.setStyle("position", "relative");
30295 this.bodyEl.setTop("");
30296 this.bodyEl.setLeft("");
30297 this.bodyEl.show();
30301 * Set the tooltip for the tab.
30302 * @param {String} tooltip The tab's tooltip
30304 setTooltip : function(text){
30305 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30306 this.textEl.dom.qtip = text;
30307 this.textEl.dom.removeAttribute('title');
30309 this.textEl.dom.title = text;
30313 onTabClick : function(e){
30314 e.preventDefault();
30315 this.tabPanel.activate(this.id);
30318 onTabMouseDown : function(e){
30319 e.preventDefault();
30320 this.tabPanel.activate(this.id);
30323 getWidth : function(){
30324 return this.inner.getWidth();
30327 setWidth : function(width){
30328 var iwidth = width - this.pnode.getPadding("lr");
30329 this.inner.setWidth(iwidth);
30330 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30331 this.pnode.setWidth(width);
30335 * Show or hide the tab
30336 * @param {Boolean} hidden True to hide or false to show.
30338 setHidden : function(hidden){
30339 this.hidden = hidden;
30340 this.pnode.setStyle("display", hidden ? "none" : "");
30344 * Returns true if this tab is "hidden"
30345 * @return {Boolean}
30347 isHidden : function(){
30348 return this.hidden;
30352 * Returns the text for this tab
30355 getText : function(){
30359 autoSize : function(){
30360 //this.el.beginMeasure();
30361 this.textEl.setWidth(1);
30363 * #2804 [new] Tabs in Roojs
30364 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30366 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30367 //this.el.endMeasure();
30371 * Sets the text for the tab (Note: this also sets the tooltip text)
30372 * @param {String} text The tab's text and tooltip
30374 setText : function(text){
30376 this.textEl.update(text);
30377 this.setTooltip(text);
30378 if(!this.tabPanel.resizeTabs){
30383 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30385 activate : function(){
30386 this.tabPanel.activate(this.id);
30390 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30392 disable : function(){
30393 if(this.tabPanel.active != this){
30394 this.disabled = true;
30395 this.pnode.addClass("disabled");
30400 * Enables this TabPanelItem if it was previously disabled.
30402 enable : function(){
30403 this.disabled = false;
30404 this.pnode.removeClass("disabled");
30408 * Sets the content for this TabPanelItem.
30409 * @param {String} content The content
30410 * @param {Boolean} loadScripts true to look for and load scripts
30412 setContent : function(content, loadScripts){
30413 this.bodyEl.update(content, loadScripts);
30417 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30418 * @return {Roo.UpdateManager} The UpdateManager
30420 getUpdateManager : function(){
30421 return this.bodyEl.getUpdateManager();
30425 * Set a URL to be used to load the content for this TabPanelItem.
30426 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30427 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
30428 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
30429 * @return {Roo.UpdateManager} The UpdateManager
30431 setUrl : function(url, params, loadOnce){
30432 if(this.refreshDelegate){
30433 this.un('activate', this.refreshDelegate);
30435 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30436 this.on("activate", this.refreshDelegate);
30437 return this.bodyEl.getUpdateManager();
30441 _handleRefresh : function(url, params, loadOnce){
30442 if(!loadOnce || !this.loaded){
30443 var updater = this.bodyEl.getUpdateManager();
30444 updater.update(url, params, this._setLoaded.createDelegate(this));
30449 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
30450 * Will fail silently if the setUrl method has not been called.
30451 * This does not activate the panel, just updates its content.
30453 refresh : function(){
30454 if(this.refreshDelegate){
30455 this.loaded = false;
30456 this.refreshDelegate();
30461 _setLoaded : function(){
30462 this.loaded = true;
30466 closeClick : function(e){
30469 this.fireEvent("beforeclose", this, o);
30470 if(o.cancel !== true){
30471 this.tabPanel.removeTab(this.id);
30475 * The text displayed in the tooltip for the close icon.
30478 closeText : "Close this tab"
30482 Roo.TabPanel.prototype.createStrip = function(container){
30483 var strip = document.createElement("div");
30484 strip.className = "x-tabs-wrap";
30485 container.appendChild(strip);
30489 Roo.TabPanel.prototype.createStripList = function(strip){
30490 // div wrapper for retard IE
30491 // returns the "tr" element.
30492 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30493 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30494 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30495 return strip.firstChild.firstChild.firstChild.firstChild;
30498 Roo.TabPanel.prototype.createBody = function(container){
30499 var body = document.createElement("div");
30500 Roo.id(body, "tab-body");
30501 Roo.fly(body).addClass("x-tabs-body");
30502 container.appendChild(body);
30506 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30507 var body = Roo.getDom(id);
30509 body = document.createElement("div");
30512 Roo.fly(body).addClass("x-tabs-item-body");
30513 bodyEl.insertBefore(body, bodyEl.firstChild);
30517 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30518 var td = document.createElement("td");
30519 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30520 //stripEl.appendChild(td);
30522 td.className = "x-tabs-closable";
30523 if(!this.closeTpl){
30524 this.closeTpl = new Roo.Template(
30525 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30526 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30527 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
30530 var el = this.closeTpl.overwrite(td, {"text": text});
30531 var close = el.getElementsByTagName("div")[0];
30532 var inner = el.getElementsByTagName("em")[0];
30533 return {"el": el, "close": close, "inner": inner};
30536 this.tabTpl = new Roo.Template(
30537 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30538 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30541 var el = this.tabTpl.overwrite(td, {"text": text});
30542 var inner = el.getElementsByTagName("em")[0];
30543 return {"el": el, "inner": inner};
30547 * Ext JS Library 1.1.1
30548 * Copyright(c) 2006-2007, Ext JS, LLC.
30550 * Originally Released Under LGPL - original licence link has changed is not relivant.
30553 * <script type="text/javascript">
30557 * @class Roo.Button
30558 * @extends Roo.util.Observable
30559 * Simple Button class
30560 * @cfg {String} text The button text
30561 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30562 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30563 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30564 * @cfg {Object} scope The scope of the handler
30565 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30566 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30567 * @cfg {Boolean} hidden True to start hidden (defaults to false)
30568 * @cfg {Boolean} disabled True to start disabled (defaults to false)
30569 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30570 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30571 applies if enableToggle = true)
30572 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30573 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30574 an {@link Roo.util.ClickRepeater} config object (defaults to false).
30576 * Create a new button
30577 * @param {Object} config The config object
30579 Roo.Button = function(renderTo, config)
30583 renderTo = config.renderTo || false;
30586 Roo.apply(this, config);
30590 * Fires when this button is clicked
30591 * @param {Button} this
30592 * @param {EventObject} e The click event
30597 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30598 * @param {Button} this
30599 * @param {Boolean} pressed
30604 * Fires when the mouse hovers over the button
30605 * @param {Button} this
30606 * @param {Event} e The event object
30608 'mouseover' : true,
30611 * Fires when the mouse exits the button
30612 * @param {Button} this
30613 * @param {Event} e The event object
30618 * Fires when the button is rendered
30619 * @param {Button} this
30624 this.menu = Roo.menu.MenuMgr.get(this.menu);
30626 // register listeners first!! - so render can be captured..
30627 Roo.util.Observable.call(this);
30629 this.render(renderTo);
30635 Roo.extend(Roo.Button, Roo.util.Observable, {
30641 * Read-only. True if this button is hidden
30646 * Read-only. True if this button is disabled
30651 * Read-only. True if this button is pressed (only if enableToggle = true)
30657 * @cfg {Number} tabIndex
30658 * The DOM tabIndex for this button (defaults to undefined)
30660 tabIndex : undefined,
30663 * @cfg {Boolean} enableToggle
30664 * True to enable pressed/not pressed toggling (defaults to false)
30666 enableToggle: false,
30668 * @cfg {Roo.menu.Menu} menu
30669 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30673 * @cfg {String} menuAlign
30674 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30676 menuAlign : "tl-bl?",
30679 * @cfg {String} iconCls
30680 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30682 iconCls : undefined,
30684 * @cfg {String} type
30685 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
30690 menuClassTarget: 'tr',
30693 * @cfg {String} clickEvent
30694 * The type of event to map to the button's event handler (defaults to 'click')
30696 clickEvent : 'click',
30699 * @cfg {Boolean} handleMouseEvents
30700 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30702 handleMouseEvents : true,
30705 * @cfg {String} tooltipType
30706 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30708 tooltipType : 'qtip',
30711 * @cfg {String} cls
30712 * A CSS class to apply to the button's main element.
30716 * @cfg {Roo.Template} template (Optional)
30717 * An {@link Roo.Template} with which to create the Button's main element. This Template must
30718 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30719 * require code modifications if required elements (e.g. a button) aren't present.
30723 render : function(renderTo){
30725 if(this.hideParent){
30726 this.parentEl = Roo.get(renderTo);
30728 if(!this.dhconfig){
30729 if(!this.template){
30730 if(!Roo.Button.buttonTemplate){
30731 // hideous table template
30732 Roo.Button.buttonTemplate = new Roo.Template(
30733 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30734 '<td class="x-btn-left"><i> </i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i> </i></td>',
30735 "</tr></tbody></table>");
30737 this.template = Roo.Button.buttonTemplate;
30739 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
30740 var btnEl = btn.child("button:first");
30741 btnEl.on('focus', this.onFocus, this);
30742 btnEl.on('blur', this.onBlur, this);
30744 btn.addClass(this.cls);
30747 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30750 btnEl.addClass(this.iconCls);
30752 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30755 if(this.tabIndex !== undefined){
30756 btnEl.dom.tabIndex = this.tabIndex;
30759 if(typeof this.tooltip == 'object'){
30760 Roo.QuickTips.tips(Roo.apply({
30764 btnEl.dom[this.tooltipType] = this.tooltip;
30768 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30772 this.el.dom.id = this.el.id = this.id;
30775 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30776 this.menu.on("show", this.onMenuShow, this);
30777 this.menu.on("hide", this.onMenuHide, this);
30779 btn.addClass("x-btn");
30780 if(Roo.isIE && !Roo.isIE7){
30781 this.autoWidth.defer(1, this);
30785 if(this.handleMouseEvents){
30786 btn.on("mouseover", this.onMouseOver, this);
30787 btn.on("mouseout", this.onMouseOut, this);
30788 btn.on("mousedown", this.onMouseDown, this);
30790 btn.on(this.clickEvent, this.onClick, this);
30791 //btn.on("mouseup", this.onMouseUp, this);
30798 Roo.ButtonToggleMgr.register(this);
30800 this.el.addClass("x-btn-pressed");
30803 var repeater = new Roo.util.ClickRepeater(btn,
30804 typeof this.repeat == "object" ? this.repeat : {}
30806 repeater.on("click", this.onClick, this);
30809 this.fireEvent('render', this);
30813 * Returns the button's underlying element
30814 * @return {Roo.Element} The element
30816 getEl : function(){
30821 * Destroys this Button and removes any listeners.
30823 destroy : function(){
30824 Roo.ButtonToggleMgr.unregister(this);
30825 this.el.removeAllListeners();
30826 this.purgeListeners();
30831 autoWidth : function(){
30833 this.el.setWidth("auto");
30834 if(Roo.isIE7 && Roo.isStrict){
30835 var ib = this.el.child('button');
30836 if(ib && ib.getWidth() > 20){
30838 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30843 this.el.beginMeasure();
30845 if(this.el.getWidth() < this.minWidth){
30846 this.el.setWidth(this.minWidth);
30849 this.el.endMeasure();
30856 * Assigns this button's click handler
30857 * @param {Function} handler The function to call when the button is clicked
30858 * @param {Object} scope (optional) Scope for the function passed in
30860 setHandler : function(handler, scope){
30861 this.handler = handler;
30862 this.scope = scope;
30866 * Sets this button's text
30867 * @param {String} text The button text
30869 setText : function(text){
30872 this.el.child("td.x-btn-center button.x-btn-text").update(text);
30878 * Gets the text for this button
30879 * @return {String} The button text
30881 getText : function(){
30889 this.hidden = false;
30891 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30899 this.hidden = true;
30901 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30906 * Convenience function for boolean show/hide
30907 * @param {Boolean} visible True to show, false to hide
30909 setVisible: function(visible){
30918 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30919 * @param {Boolean} state (optional) Force a particular state
30921 toggle : function(state){
30922 state = state === undefined ? !this.pressed : state;
30923 if(state != this.pressed){
30925 this.el.addClass("x-btn-pressed");
30926 this.pressed = true;
30927 this.fireEvent("toggle", this, true);
30929 this.el.removeClass("x-btn-pressed");
30930 this.pressed = false;
30931 this.fireEvent("toggle", this, false);
30933 if(this.toggleHandler){
30934 this.toggleHandler.call(this.scope || this, this, state);
30942 focus : function(){
30943 this.el.child('button:first').focus();
30947 * Disable this button
30949 disable : function(){
30951 this.el.addClass("x-btn-disabled");
30953 this.disabled = true;
30957 * Enable this button
30959 enable : function(){
30961 this.el.removeClass("x-btn-disabled");
30963 this.disabled = false;
30967 * Convenience function for boolean enable/disable
30968 * @param {Boolean} enabled True to enable, false to disable
30970 setDisabled : function(v){
30971 this[v !== true ? "enable" : "disable"]();
30975 onClick : function(e)
30978 e.preventDefault();
30983 if(!this.disabled){
30984 if(this.enableToggle){
30987 if(this.menu && !this.menu.isVisible()){
30988 this.menu.show(this.el, this.menuAlign);
30990 this.fireEvent("click", this, e);
30992 this.el.removeClass("x-btn-over");
30993 this.handler.call(this.scope || this, this, e);
30998 onMouseOver : function(e){
30999 if(!this.disabled){
31000 this.el.addClass("x-btn-over");
31001 this.fireEvent('mouseover', this, e);
31005 onMouseOut : function(e){
31006 if(!e.within(this.el, true)){
31007 this.el.removeClass("x-btn-over");
31008 this.fireEvent('mouseout', this, e);
31012 onFocus : function(e){
31013 if(!this.disabled){
31014 this.el.addClass("x-btn-focus");
31018 onBlur : function(e){
31019 this.el.removeClass("x-btn-focus");
31022 onMouseDown : function(e){
31023 if(!this.disabled && e.button == 0){
31024 this.el.addClass("x-btn-click");
31025 Roo.get(document).on('mouseup', this.onMouseUp, this);
31029 onMouseUp : function(e){
31031 this.el.removeClass("x-btn-click");
31032 Roo.get(document).un('mouseup', this.onMouseUp, this);
31036 onMenuShow : function(e){
31037 this.el.addClass("x-btn-menu-active");
31040 onMenuHide : function(e){
31041 this.el.removeClass("x-btn-menu-active");
31045 // Private utility class used by Button
31046 Roo.ButtonToggleMgr = function(){
31049 function toggleGroup(btn, state){
31051 var g = groups[btn.toggleGroup];
31052 for(var i = 0, l = g.length; i < l; i++){
31054 g[i].toggle(false);
31061 register : function(btn){
31062 if(!btn.toggleGroup){
31065 var g = groups[btn.toggleGroup];
31067 g = groups[btn.toggleGroup] = [];
31070 btn.on("toggle", toggleGroup);
31073 unregister : function(btn){
31074 if(!btn.toggleGroup){
31077 var g = groups[btn.toggleGroup];
31080 btn.un("toggle", toggleGroup);
31086 * Ext JS Library 1.1.1
31087 * Copyright(c) 2006-2007, Ext JS, LLC.
31089 * Originally Released Under LGPL - original licence link has changed is not relivant.
31092 * <script type="text/javascript">
31096 * @class Roo.SplitButton
31097 * @extends Roo.Button
31098 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31099 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
31100 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31101 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31102 * @cfg {String} arrowTooltip The title attribute of the arrow
31104 * Create a new menu button
31105 * @param {String/HTMLElement/Element} renderTo The element to append the button to
31106 * @param {Object} config The config object
31108 Roo.SplitButton = function(renderTo, config){
31109 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31111 * @event arrowclick
31112 * Fires when this button's arrow is clicked
31113 * @param {SplitButton} this
31114 * @param {EventObject} e The click event
31116 this.addEvents({"arrowclick":true});
31119 Roo.extend(Roo.SplitButton, Roo.Button, {
31120 render : function(renderTo){
31121 // this is one sweet looking template!
31122 var tpl = new Roo.Template(
31123 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31124 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31125 '<tr><td class="x-btn-left"><i> </i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
31126 "</tbody></table></td><td>",
31127 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31128 '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button"> </button></td><td class="x-btn-right"><i> </i></td></tr>',
31129 "</tbody></table></td></tr></table>"
31131 var btn = tpl.append(renderTo, [this.text, this.type], true);
31132 var btnEl = btn.child("button");
31134 btn.addClass(this.cls);
31137 btnEl.setStyle('background-image', 'url(' +this.icon +')');
31140 btnEl.addClass(this.iconCls);
31142 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31146 if(this.handleMouseEvents){
31147 btn.on("mouseover", this.onMouseOver, this);
31148 btn.on("mouseout", this.onMouseOut, this);
31149 btn.on("mousedown", this.onMouseDown, this);
31150 btn.on("mouseup", this.onMouseUp, this);
31152 btn.on(this.clickEvent, this.onClick, this);
31154 if(typeof this.tooltip == 'object'){
31155 Roo.QuickTips.tips(Roo.apply({
31159 btnEl.dom[this.tooltipType] = this.tooltip;
31162 if(this.arrowTooltip){
31163 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31172 this.el.addClass("x-btn-pressed");
31174 if(Roo.isIE && !Roo.isIE7){
31175 this.autoWidth.defer(1, this);
31180 this.menu.on("show", this.onMenuShow, this);
31181 this.menu.on("hide", this.onMenuHide, this);
31183 this.fireEvent('render', this);
31187 autoWidth : function(){
31189 var tbl = this.el.child("table:first");
31190 var tbl2 = this.el.child("table:last");
31191 this.el.setWidth("auto");
31192 tbl.setWidth("auto");
31193 if(Roo.isIE7 && Roo.isStrict){
31194 var ib = this.el.child('button:first');
31195 if(ib && ib.getWidth() > 20){
31197 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31202 this.el.beginMeasure();
31204 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31205 tbl.setWidth(this.minWidth-tbl2.getWidth());
31208 this.el.endMeasure();
31211 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31215 * Sets this button's click handler
31216 * @param {Function} handler The function to call when the button is clicked
31217 * @param {Object} scope (optional) Scope for the function passed above
31219 setHandler : function(handler, scope){
31220 this.handler = handler;
31221 this.scope = scope;
31225 * Sets this button's arrow click handler
31226 * @param {Function} handler The function to call when the arrow is clicked
31227 * @param {Object} scope (optional) Scope for the function passed above
31229 setArrowHandler : function(handler, scope){
31230 this.arrowHandler = handler;
31231 this.scope = scope;
31237 focus : function(){
31239 this.el.child("button:first").focus();
31244 onClick : function(e){
31245 e.preventDefault();
31246 if(!this.disabled){
31247 if(e.getTarget(".x-btn-menu-arrow-wrap")){
31248 if(this.menu && !this.menu.isVisible()){
31249 this.menu.show(this.el, this.menuAlign);
31251 this.fireEvent("arrowclick", this, e);
31252 if(this.arrowHandler){
31253 this.arrowHandler.call(this.scope || this, this, e);
31256 this.fireEvent("click", this, e);
31258 this.handler.call(this.scope || this, this, e);
31264 onMouseDown : function(e){
31265 if(!this.disabled){
31266 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31270 onMouseUp : function(e){
31271 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31276 // backwards compat
31277 Roo.MenuButton = Roo.SplitButton;/*
31279 * Ext JS Library 1.1.1
31280 * Copyright(c) 2006-2007, Ext JS, LLC.
31282 * Originally Released Under LGPL - original licence link has changed is not relivant.
31285 * <script type="text/javascript">
31289 * @class Roo.Toolbar
31290 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31291 * Basic Toolbar class.
31293 * Creates a new Toolbar
31294 * @param {Object} container The config object
31296 Roo.Toolbar = function(container, buttons, config)
31298 /// old consturctor format still supported..
31299 if(container instanceof Array){ // omit the container for later rendering
31300 buttons = container;
31304 if (typeof(container) == 'object' && container.xtype) {
31305 config = container;
31306 container = config.container;
31307 buttons = config.buttons || []; // not really - use items!!
31310 if (config && config.items) {
31311 xitems = config.items;
31312 delete config.items;
31314 Roo.apply(this, config);
31315 this.buttons = buttons;
31318 this.render(container);
31320 this.xitems = xitems;
31321 Roo.each(xitems, function(b) {
31327 Roo.Toolbar.prototype = {
31329 * @cfg {Array} items
31330 * array of button configs or elements to add (will be converted to a MixedCollection)
31334 * @cfg {String/HTMLElement/Element} container
31335 * The id or element that will contain the toolbar
31338 render : function(ct){
31339 this.el = Roo.get(ct);
31341 this.el.addClass(this.cls);
31343 // using a table allows for vertical alignment
31344 // 100% width is needed by Safari...
31345 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31346 this.tr = this.el.child("tr", true);
31348 this.items = new Roo.util.MixedCollection(false, function(o){
31349 return o.id || ("item" + (++autoId));
31352 this.add.apply(this, this.buttons);
31353 delete this.buttons;
31358 * Adds element(s) to the toolbar -- this function takes a variable number of
31359 * arguments of mixed type and adds them to the toolbar.
31360 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31362 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31363 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31364 * <li>Field: Any form field (equivalent to {@link #addField})</li>
31365 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31366 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31367 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31368 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31369 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31370 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31372 * @param {Mixed} arg2
31373 * @param {Mixed} etc.
31376 var a = arguments, l = a.length;
31377 for(var i = 0; i < l; i++){
31382 _add : function(el) {
31385 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31388 if (el.applyTo){ // some kind of form field
31389 return this.addField(el);
31391 if (el.render){ // some kind of Toolbar.Item
31392 return this.addItem(el);
31394 if (typeof el == "string"){ // string
31395 if(el == "separator" || el == "-"){
31396 return this.addSeparator();
31399 return this.addSpacer();
31402 return this.addFill();
31404 return this.addText(el);
31407 if(el.tagName){ // element
31408 return this.addElement(el);
31410 if(typeof el == "object"){ // must be button config?
31411 return this.addButton(el);
31413 // and now what?!?!
31419 * Add an Xtype element
31420 * @param {Object} xtype Xtype Object
31421 * @return {Object} created Object
31423 addxtype : function(e){
31424 return this.add(e);
31428 * Returns the Element for this toolbar.
31429 * @return {Roo.Element}
31431 getEl : function(){
31437 * @return {Roo.Toolbar.Item} The separator item
31439 addSeparator : function(){
31440 return this.addItem(new Roo.Toolbar.Separator());
31444 * Adds a spacer element
31445 * @return {Roo.Toolbar.Spacer} The spacer item
31447 addSpacer : function(){
31448 return this.addItem(new Roo.Toolbar.Spacer());
31452 * Adds a fill element that forces subsequent additions to the right side of the toolbar
31453 * @return {Roo.Toolbar.Fill} The fill item
31455 addFill : function(){
31456 return this.addItem(new Roo.Toolbar.Fill());
31460 * Adds any standard HTML element to the toolbar
31461 * @param {String/HTMLElement/Element} el The element or id of the element to add
31462 * @return {Roo.Toolbar.Item} The element's item
31464 addElement : function(el){
31465 return this.addItem(new Roo.Toolbar.Item(el));
31468 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31469 * @type Roo.util.MixedCollection
31474 * Adds any Toolbar.Item or subclass
31475 * @param {Roo.Toolbar.Item} item
31476 * @return {Roo.Toolbar.Item} The item
31478 addItem : function(item){
31479 var td = this.nextBlock();
31481 this.items.add(item);
31486 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31487 * @param {Object/Array} config A button config or array of configs
31488 * @return {Roo.Toolbar.Button/Array}
31490 addButton : function(config){
31491 if(config instanceof Array){
31493 for(var i = 0, len = config.length; i < len; i++) {
31494 buttons.push(this.addButton(config[i]));
31499 if(!(config instanceof Roo.Toolbar.Button)){
31501 new Roo.Toolbar.SplitButton(config) :
31502 new Roo.Toolbar.Button(config);
31504 var td = this.nextBlock();
31511 * Adds text to the toolbar
31512 * @param {String} text The text to add
31513 * @return {Roo.Toolbar.Item} The element's item
31515 addText : function(text){
31516 return this.addItem(new Roo.Toolbar.TextItem(text));
31520 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31521 * @param {Number} index The index where the item is to be inserted
31522 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31523 * @return {Roo.Toolbar.Button/Item}
31525 insertButton : function(index, item){
31526 if(item instanceof Array){
31528 for(var i = 0, len = item.length; i < len; i++) {
31529 buttons.push(this.insertButton(index + i, item[i]));
31533 if (!(item instanceof Roo.Toolbar.Button)){
31534 item = new Roo.Toolbar.Button(item);
31536 var td = document.createElement("td");
31537 this.tr.insertBefore(td, this.tr.childNodes[index]);
31539 this.items.insert(index, item);
31544 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31545 * @param {Object} config
31546 * @return {Roo.Toolbar.Item} The element's item
31548 addDom : function(config, returnEl){
31549 var td = this.nextBlock();
31550 Roo.DomHelper.overwrite(td, config);
31551 var ti = new Roo.Toolbar.Item(td.firstChild);
31553 this.items.add(ti);
31558 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31559 * @type Roo.util.MixedCollection
31564 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31565 * Note: the field should not have been rendered yet. For a field that has already been
31566 * rendered, use {@link #addElement}.
31567 * @param {Roo.form.Field} field
31568 * @return {Roo.ToolbarItem}
31572 addField : function(field) {
31573 if (!this.fields) {
31575 this.fields = new Roo.util.MixedCollection(false, function(o){
31576 return o.id || ("item" + (++autoId));
31581 var td = this.nextBlock();
31583 var ti = new Roo.Toolbar.Item(td.firstChild);
31585 this.items.add(ti);
31586 this.fields.add(field);
31597 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31598 this.el.child('div').hide();
31606 this.el.child('div').show();
31610 nextBlock : function(){
31611 var td = document.createElement("td");
31612 this.tr.appendChild(td);
31617 destroy : function(){
31618 if(this.items){ // rendered?
31619 Roo.destroy.apply(Roo, this.items.items);
31621 if(this.fields){ // rendered?
31622 Roo.destroy.apply(Roo, this.fields.items);
31624 Roo.Element.uncache(this.el, this.tr);
31629 * @class Roo.Toolbar.Item
31630 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31632 * Creates a new Item
31633 * @param {HTMLElement} el
31635 Roo.Toolbar.Item = function(el){
31637 if (typeof (el.xtype) != 'undefined') {
31642 this.el = Roo.getDom(el);
31643 this.id = Roo.id(this.el);
31644 this.hidden = false;
31649 * Fires when the button is rendered
31650 * @param {Button} this
31654 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31656 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31657 //Roo.Toolbar.Item.prototype = {
31660 * Get this item's HTML Element
31661 * @return {HTMLElement}
31663 getEl : function(){
31668 render : function(td){
31671 td.appendChild(this.el);
31673 this.fireEvent('render', this);
31677 * Removes and destroys this item.
31679 destroy : function(){
31680 this.td.parentNode.removeChild(this.td);
31687 this.hidden = false;
31688 this.td.style.display = "";
31695 this.hidden = true;
31696 this.td.style.display = "none";
31700 * Convenience function for boolean show/hide.
31701 * @param {Boolean} visible true to show/false to hide
31703 setVisible: function(visible){
31712 * Try to focus this item.
31714 focus : function(){
31715 Roo.fly(this.el).focus();
31719 * Disables this item.
31721 disable : function(){
31722 Roo.fly(this.td).addClass("x-item-disabled");
31723 this.disabled = true;
31724 this.el.disabled = true;
31728 * Enables this item.
31730 enable : function(){
31731 Roo.fly(this.td).removeClass("x-item-disabled");
31732 this.disabled = false;
31733 this.el.disabled = false;
31739 * @class Roo.Toolbar.Separator
31740 * @extends Roo.Toolbar.Item
31741 * A simple toolbar separator class
31743 * Creates a new Separator
31745 Roo.Toolbar.Separator = function(cfg){
31747 var s = document.createElement("span");
31748 s.className = "ytb-sep";
31753 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31755 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31756 enable:Roo.emptyFn,
31757 disable:Roo.emptyFn,
31762 * @class Roo.Toolbar.Spacer
31763 * @extends Roo.Toolbar.Item
31764 * A simple element that adds extra horizontal space to a toolbar.
31766 * Creates a new Spacer
31768 Roo.Toolbar.Spacer = function(cfg){
31769 var s = document.createElement("div");
31770 s.className = "ytb-spacer";
31774 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31776 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31777 enable:Roo.emptyFn,
31778 disable:Roo.emptyFn,
31783 * @class Roo.Toolbar.Fill
31784 * @extends Roo.Toolbar.Spacer
31785 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31787 * Creates a new Spacer
31789 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31791 render : function(td){
31792 td.style.width = '100%';
31793 Roo.Toolbar.Fill.superclass.render.call(this, td);
31798 * @class Roo.Toolbar.TextItem
31799 * @extends Roo.Toolbar.Item
31800 * A simple class that renders text directly into a toolbar.
31802 * Creates a new TextItem
31803 * @cfg {string} text
31805 Roo.Toolbar.TextItem = function(cfg){
31806 var text = cfg || "";
31807 if (typeof(cfg) == 'object') {
31808 text = cfg.text || "";
31812 var s = document.createElement("span");
31813 s.className = "ytb-text";
31814 s.innerHTML = text;
31819 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
31821 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31824 enable:Roo.emptyFn,
31825 disable:Roo.emptyFn,
31828 * Shows this button
31831 this.hidden = false;
31832 this.el.style.display = "";
31836 * Hides this button
31839 this.hidden = true;
31840 this.el.style.display = "none";
31846 * @class Roo.Toolbar.Button
31847 * @extends Roo.Button
31848 * A button that renders into a toolbar.
31850 * Creates a new Button
31851 * @param {Object} config A standard {@link Roo.Button} config object
31853 Roo.Toolbar.Button = function(config){
31854 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31856 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31860 render : function(td){
31862 Roo.Toolbar.Button.superclass.render.call(this, td);
31866 * Removes and destroys this button
31868 destroy : function(){
31869 Roo.Toolbar.Button.superclass.destroy.call(this);
31870 this.td.parentNode.removeChild(this.td);
31874 * Shows this button
31877 this.hidden = false;
31878 this.td.style.display = "";
31882 * Hides this button
31885 this.hidden = true;
31886 this.td.style.display = "none";
31890 * Disables this item
31892 disable : function(){
31893 Roo.fly(this.td).addClass("x-item-disabled");
31894 this.disabled = true;
31898 * Enables this item
31900 enable : function(){
31901 Roo.fly(this.td).removeClass("x-item-disabled");
31902 this.disabled = false;
31905 // backwards compat
31906 Roo.ToolbarButton = Roo.Toolbar.Button;
31909 * @class Roo.Toolbar.SplitButton
31910 * @extends Roo.SplitButton
31911 * A menu button that renders into a toolbar.
31913 * Creates a new SplitButton
31914 * @param {Object} config A standard {@link Roo.SplitButton} config object
31916 Roo.Toolbar.SplitButton = function(config){
31917 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31919 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31920 render : function(td){
31922 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31926 * Removes and destroys this button
31928 destroy : function(){
31929 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31930 this.td.parentNode.removeChild(this.td);
31934 * Shows this button
31937 this.hidden = false;
31938 this.td.style.display = "";
31942 * Hides this button
31945 this.hidden = true;
31946 this.td.style.display = "none";
31950 // backwards compat
31951 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31953 * Ext JS Library 1.1.1
31954 * Copyright(c) 2006-2007, Ext JS, LLC.
31956 * Originally Released Under LGPL - original licence link has changed is not relivant.
31959 * <script type="text/javascript">
31963 * @class Roo.PagingToolbar
31964 * @extends Roo.Toolbar
31965 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31966 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31968 * Create a new PagingToolbar
31969 * @param {Object} config The config object
31971 Roo.PagingToolbar = function(el, ds, config)
31973 // old args format still supported... - xtype is prefered..
31974 if (typeof(el) == 'object' && el.xtype) {
31975 // created from xtype...
31977 ds = el.dataSource;
31978 el = config.container;
31981 if (config.items) {
31982 items = config.items;
31986 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31989 this.renderButtons(this.el);
31992 // supprot items array.
31994 Roo.each(items, function(e) {
31995 this.add(Roo.factory(e));
32000 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32003 * @cfg {String/HTMLElement/Element} container
32004 * container The id or element that will contain the toolbar
32007 * @cfg {Boolean} displayInfo
32008 * True to display the displayMsg (defaults to false)
32013 * @cfg {Number} pageSize
32014 * The number of records to display per page (defaults to 20)
32018 * @cfg {String} displayMsg
32019 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32021 displayMsg : 'Displaying {0} - {1} of {2}',
32023 * @cfg {String} emptyMsg
32024 * The message to display when no records are found (defaults to "No data to display")
32026 emptyMsg : 'No data to display',
32028 * Customizable piece of the default paging text (defaults to "Page")
32031 beforePageText : "Page",
32033 * Customizable piece of the default paging text (defaults to "of %0")
32036 afterPageText : "of {0}",
32038 * Customizable piece of the default paging text (defaults to "First Page")
32041 firstText : "First Page",
32043 * Customizable piece of the default paging text (defaults to "Previous Page")
32046 prevText : "Previous Page",
32048 * Customizable piece of the default paging text (defaults to "Next Page")
32051 nextText : "Next Page",
32053 * Customizable piece of the default paging text (defaults to "Last Page")
32056 lastText : "Last Page",
32058 * Customizable piece of the default paging text (defaults to "Refresh")
32061 refreshText : "Refresh",
32064 renderButtons : function(el){
32065 Roo.PagingToolbar.superclass.render.call(this, el);
32066 this.first = this.addButton({
32067 tooltip: this.firstText,
32068 cls: "x-btn-icon x-grid-page-first",
32070 handler: this.onClick.createDelegate(this, ["first"])
32072 this.prev = this.addButton({
32073 tooltip: this.prevText,
32074 cls: "x-btn-icon x-grid-page-prev",
32076 handler: this.onClick.createDelegate(this, ["prev"])
32078 //this.addSeparator();
32079 this.add(this.beforePageText);
32080 this.field = Roo.get(this.addDom({
32085 cls: "x-grid-page-number"
32087 this.field.on("keydown", this.onPagingKeydown, this);
32088 this.field.on("focus", function(){this.dom.select();});
32089 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32090 this.field.setHeight(18);
32091 //this.addSeparator();
32092 this.next = this.addButton({
32093 tooltip: this.nextText,
32094 cls: "x-btn-icon x-grid-page-next",
32096 handler: this.onClick.createDelegate(this, ["next"])
32098 this.last = this.addButton({
32099 tooltip: this.lastText,
32100 cls: "x-btn-icon x-grid-page-last",
32102 handler: this.onClick.createDelegate(this, ["last"])
32104 //this.addSeparator();
32105 this.loading = this.addButton({
32106 tooltip: this.refreshText,
32107 cls: "x-btn-icon x-grid-loading",
32108 handler: this.onClick.createDelegate(this, ["refresh"])
32111 if(this.displayInfo){
32112 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32117 updateInfo : function(){
32118 if(this.displayEl){
32119 var count = this.ds.getCount();
32120 var msg = count == 0 ?
32124 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32126 this.displayEl.update(msg);
32131 onLoad : function(ds, r, o){
32132 this.cursor = o.params ? o.params.start : 0;
32133 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32135 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32136 this.field.dom.value = ap;
32137 this.first.setDisabled(ap == 1);
32138 this.prev.setDisabled(ap == 1);
32139 this.next.setDisabled(ap == ps);
32140 this.last.setDisabled(ap == ps);
32141 this.loading.enable();
32146 getPageData : function(){
32147 var total = this.ds.getTotalCount();
32150 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32151 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32156 onLoadError : function(){
32157 this.loading.enable();
32161 onPagingKeydown : function(e){
32162 var k = e.getKey();
32163 var d = this.getPageData();
32165 var v = this.field.dom.value, pageNum;
32166 if(!v || isNaN(pageNum = parseInt(v, 10))){
32167 this.field.dom.value = d.activePage;
32170 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32171 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32174 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
32176 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32177 this.field.dom.value = pageNum;
32178 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32181 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32183 var v = this.field.dom.value, pageNum;
32184 var increment = (e.shiftKey) ? 10 : 1;
32185 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32188 if(!v || isNaN(pageNum = parseInt(v, 10))) {
32189 this.field.dom.value = d.activePage;
32192 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32194 this.field.dom.value = parseInt(v, 10) + increment;
32195 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32196 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32203 beforeLoad : function(){
32205 this.loading.disable();
32210 onClick : function(which){
32214 ds.load({params:{start: 0, limit: this.pageSize}});
32217 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32220 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32223 var total = ds.getTotalCount();
32224 var extra = total % this.pageSize;
32225 var lastStart = extra ? (total - extra) : total-this.pageSize;
32226 ds.load({params:{start: lastStart, limit: this.pageSize}});
32229 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32235 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32236 * @param {Roo.data.Store} store The data store to unbind
32238 unbind : function(ds){
32239 ds.un("beforeload", this.beforeLoad, this);
32240 ds.un("load", this.onLoad, this);
32241 ds.un("loadexception", this.onLoadError, this);
32242 ds.un("remove", this.updateInfo, this);
32243 ds.un("add", this.updateInfo, this);
32244 this.ds = undefined;
32248 * Binds the paging toolbar to the specified {@link Roo.data.Store}
32249 * @param {Roo.data.Store} store The data store to bind
32251 bind : function(ds){
32252 ds.on("beforeload", this.beforeLoad, this);
32253 ds.on("load", this.onLoad, this);
32254 ds.on("loadexception", this.onLoadError, this);
32255 ds.on("remove", this.updateInfo, this);
32256 ds.on("add", this.updateInfo, this);
32261 * Ext JS Library 1.1.1
32262 * Copyright(c) 2006-2007, Ext JS, LLC.
32264 * Originally Released Under LGPL - original licence link has changed is not relivant.
32267 * <script type="text/javascript">
32271 * @class Roo.Resizable
32272 * @extends Roo.util.Observable
32273 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32274 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32275 * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
32276 * the element will be wrapped for you automatically.</p>
32277 * <p>Here is the list of valid resize handles:</p>
32280 ------ -------------------
32289 'hd' horizontal drag
32292 * <p>Here's an example showing the creation of a typical Resizable:</p>
32294 var resizer = new Roo.Resizable("element-id", {
32302 resizer.on("resize", myHandler);
32304 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32305 * resizer.east.setDisplayed(false);</p>
32306 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32307 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32308 * resize operation's new size (defaults to [0, 0])
32309 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32310 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32311 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32312 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32313 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32314 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32315 * @cfg {Number} width The width of the element in pixels (defaults to null)
32316 * @cfg {Number} height The height of the element in pixels (defaults to null)
32317 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32318 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32319 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32320 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32321 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
32322 * in favor of the handles config option (defaults to false)
32323 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32324 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32325 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32326 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32327 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32328 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32329 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32330 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32331 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32332 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32333 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32335 * Create a new resizable component
32336 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32337 * @param {Object} config configuration options
32339 Roo.Resizable = function(el, config)
32341 this.el = Roo.get(el);
32343 if(config && config.wrap){
32344 config.resizeChild = this.el;
32345 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32346 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32347 this.el.setStyle("overflow", "hidden");
32348 this.el.setPositioning(config.resizeChild.getPositioning());
32349 config.resizeChild.clearPositioning();
32350 if(!config.width || !config.height){
32351 var csize = config.resizeChild.getSize();
32352 this.el.setSize(csize.width, csize.height);
32354 if(config.pinned && !config.adjustments){
32355 config.adjustments = "auto";
32359 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32360 this.proxy.unselectable();
32361 this.proxy.enableDisplayMode('block');
32363 Roo.apply(this, config);
32366 this.disableTrackOver = true;
32367 this.el.addClass("x-resizable-pinned");
32369 // if the element isn't positioned, make it relative
32370 var position = this.el.getStyle("position");
32371 if(position != "absolute" && position != "fixed"){
32372 this.el.setStyle("position", "relative");
32374 if(!this.handles){ // no handles passed, must be legacy style
32375 this.handles = 's,e,se';
32376 if(this.multiDirectional){
32377 this.handles += ',n,w';
32380 if(this.handles == "all"){
32381 this.handles = "n s e w ne nw se sw";
32383 var hs = this.handles.split(/\s*?[,;]\s*?| /);
32384 var ps = Roo.Resizable.positions;
32385 for(var i = 0, len = hs.length; i < len; i++){
32386 if(hs[i] && ps[hs[i]]){
32387 var pos = ps[hs[i]];
32388 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32392 this.corner = this.southeast;
32394 // updateBox = the box can move..
32395 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32396 this.updateBox = true;
32399 this.activeHandle = null;
32401 if(this.resizeChild){
32402 if(typeof this.resizeChild == "boolean"){
32403 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32405 this.resizeChild = Roo.get(this.resizeChild, true);
32409 if(this.adjustments == "auto"){
32410 var rc = this.resizeChild;
32411 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32412 if(rc && (hw || hn)){
32413 rc.position("relative");
32414 rc.setLeft(hw ? hw.el.getWidth() : 0);
32415 rc.setTop(hn ? hn.el.getHeight() : 0);
32417 this.adjustments = [
32418 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32419 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32423 if(this.draggable){
32424 this.dd = this.dynamic ?
32425 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32426 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32432 * @event beforeresize
32433 * Fired before resize is allowed. Set enabled to false to cancel resize.
32434 * @param {Roo.Resizable} this
32435 * @param {Roo.EventObject} e The mousedown event
32437 "beforeresize" : true,
32440 * Fired a resizing.
32441 * @param {Roo.Resizable} this
32442 * @param {Number} x The new x position
32443 * @param {Number} y The new y position
32444 * @param {Number} w The new w width
32445 * @param {Number} h The new h hight
32446 * @param {Roo.EventObject} e The mouseup event
32451 * Fired after a resize.
32452 * @param {Roo.Resizable} this
32453 * @param {Number} width The new width
32454 * @param {Number} height The new height
32455 * @param {Roo.EventObject} e The mouseup event
32460 if(this.width !== null && this.height !== null){
32461 this.resizeTo(this.width, this.height);
32463 this.updateChildSize();
32466 this.el.dom.style.zoom = 1;
32468 Roo.Resizable.superclass.constructor.call(this);
32471 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32472 resizeChild : false,
32473 adjustments : [0, 0],
32483 multiDirectional : false,
32484 disableTrackOver : false,
32485 easing : 'easeOutStrong',
32486 widthIncrement : 0,
32487 heightIncrement : 0,
32491 preserveRatio : false,
32492 transparent: false,
32498 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32500 constrainTo: undefined,
32502 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32504 resizeRegion: undefined,
32508 * Perform a manual resize
32509 * @param {Number} width
32510 * @param {Number} height
32512 resizeTo : function(width, height){
32513 this.el.setSize(width, height);
32514 this.updateChildSize();
32515 this.fireEvent("resize", this, width, height, null);
32519 startSizing : function(e, handle){
32520 this.fireEvent("beforeresize", this, e);
32521 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32524 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
32525 this.overlay.unselectable();
32526 this.overlay.enableDisplayMode("block");
32527 this.overlay.on("mousemove", this.onMouseMove, this);
32528 this.overlay.on("mouseup", this.onMouseUp, this);
32530 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32532 this.resizing = true;
32533 this.startBox = this.el.getBox();
32534 this.startPoint = e.getXY();
32535 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32536 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32538 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32539 this.overlay.show();
32541 if(this.constrainTo) {
32542 var ct = Roo.get(this.constrainTo);
32543 this.resizeRegion = ct.getRegion().adjust(
32544 ct.getFrameWidth('t'),
32545 ct.getFrameWidth('l'),
32546 -ct.getFrameWidth('b'),
32547 -ct.getFrameWidth('r')
32551 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32553 this.proxy.setBox(this.startBox);
32555 this.proxy.setStyle('visibility', 'visible');
32561 onMouseDown : function(handle, e){
32564 this.activeHandle = handle;
32565 this.startSizing(e, handle);
32570 onMouseUp : function(e){
32571 var size = this.resizeElement();
32572 this.resizing = false;
32574 this.overlay.hide();
32576 this.fireEvent("resize", this, size.width, size.height, e);
32580 updateChildSize : function(){
32582 if(this.resizeChild){
32584 var child = this.resizeChild;
32585 var adj = this.adjustments;
32586 if(el.dom.offsetWidth){
32587 var b = el.getSize(true);
32588 child.setSize(b.width+adj[0], b.height+adj[1]);
32590 // Second call here for IE
32591 // The first call enables instant resizing and
32592 // the second call corrects scroll bars if they
32595 setTimeout(function(){
32596 if(el.dom.offsetWidth){
32597 var b = el.getSize(true);
32598 child.setSize(b.width+adj[0], b.height+adj[1]);
32606 snap : function(value, inc, min){
32607 if(!inc || !value) {
32610 var newValue = value;
32611 var m = value % inc;
32614 newValue = value + (inc-m);
32616 newValue = value - m;
32619 return Math.max(min, newValue);
32623 resizeElement : function(){
32624 var box = this.proxy.getBox();
32625 if(this.updateBox){
32626 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32628 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32630 this.updateChildSize();
32638 constrain : function(v, diff, m, mx){
32641 }else if(v - diff > mx){
32648 onMouseMove : function(e){
32651 try{// try catch so if something goes wrong the user doesn't get hung
32653 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32657 //var curXY = this.startPoint;
32658 var curSize = this.curSize || this.startBox;
32659 var x = this.startBox.x, y = this.startBox.y;
32660 var ox = x, oy = y;
32661 var w = curSize.width, h = curSize.height;
32662 var ow = w, oh = h;
32663 var mw = this.minWidth, mh = this.minHeight;
32664 var mxw = this.maxWidth, mxh = this.maxHeight;
32665 var wi = this.widthIncrement;
32666 var hi = this.heightIncrement;
32668 var eventXY = e.getXY();
32669 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32670 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32672 var pos = this.activeHandle.position;
32677 w = Math.min(Math.max(mw, w), mxw);
32682 h = Math.min(Math.max(mh, h), mxh);
32687 w = Math.min(Math.max(mw, w), mxw);
32688 h = Math.min(Math.max(mh, h), mxh);
32691 diffY = this.constrain(h, diffY, mh, mxh);
32698 var adiffX = Math.abs(diffX);
32699 var sub = (adiffX % wi); // how much
32700 if (sub > (wi/2)) { // far enough to snap
32701 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32703 // remove difference..
32704 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32708 x = Math.max(this.minX, x);
32711 diffX = this.constrain(w, diffX, mw, mxw);
32717 w = Math.min(Math.max(mw, w), mxw);
32718 diffY = this.constrain(h, diffY, mh, mxh);
32723 diffX = this.constrain(w, diffX, mw, mxw);
32724 diffY = this.constrain(h, diffY, mh, mxh);
32731 diffX = this.constrain(w, diffX, mw, mxw);
32733 h = Math.min(Math.max(mh, h), mxh);
32739 var sw = this.snap(w, wi, mw);
32740 var sh = this.snap(h, hi, mh);
32741 if(sw != w || sh != h){
32764 if(this.preserveRatio){
32769 h = Math.min(Math.max(mh, h), mxh);
32774 w = Math.min(Math.max(mw, w), mxw);
32779 w = Math.min(Math.max(mw, w), mxw);
32785 w = Math.min(Math.max(mw, w), mxw);
32791 h = Math.min(Math.max(mh, h), mxh);
32799 h = Math.min(Math.max(mh, h), mxh);
32809 h = Math.min(Math.max(mh, h), mxh);
32817 if (pos == 'hdrag') {
32820 this.proxy.setBounds(x, y, w, h);
32822 this.resizeElement();
32826 this.fireEvent("resizing", this, x, y, w, h, e);
32830 handleOver : function(){
32832 this.el.addClass("x-resizable-over");
32837 handleOut : function(){
32838 if(!this.resizing){
32839 this.el.removeClass("x-resizable-over");
32844 * Returns the element this component is bound to.
32845 * @return {Roo.Element}
32847 getEl : function(){
32852 * Returns the resizeChild element (or null).
32853 * @return {Roo.Element}
32855 getResizeChild : function(){
32856 return this.resizeChild;
32858 groupHandler : function()
32863 * Destroys this resizable. If the element was wrapped and
32864 * removeEl is not true then the element remains.
32865 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32867 destroy : function(removeEl){
32868 this.proxy.remove();
32870 this.overlay.removeAllListeners();
32871 this.overlay.remove();
32873 var ps = Roo.Resizable.positions;
32875 if(typeof ps[k] != "function" && this[ps[k]]){
32876 var h = this[ps[k]];
32877 h.el.removeAllListeners();
32882 this.el.update("");
32889 // hash to map config positions to true positions
32890 Roo.Resizable.positions = {
32891 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
32896 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32898 // only initialize the template if resizable is used
32899 var tpl = Roo.DomHelper.createTemplate(
32900 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32903 Roo.Resizable.Handle.prototype.tpl = tpl;
32905 this.position = pos;
32907 // show north drag fro topdra
32908 var handlepos = pos == 'hdrag' ? 'north' : pos;
32910 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32911 if (pos == 'hdrag') {
32912 this.el.setStyle('cursor', 'pointer');
32914 this.el.unselectable();
32916 this.el.setOpacity(0);
32918 this.el.on("mousedown", this.onMouseDown, this);
32919 if(!disableTrackOver){
32920 this.el.on("mouseover", this.onMouseOver, this);
32921 this.el.on("mouseout", this.onMouseOut, this);
32926 Roo.Resizable.Handle.prototype = {
32927 afterResize : function(rz){
32932 onMouseDown : function(e){
32933 this.rz.onMouseDown(this, e);
32936 onMouseOver : function(e){
32937 this.rz.handleOver(this, e);
32940 onMouseOut : function(e){
32941 this.rz.handleOut(this, e);
32945 * Ext JS Library 1.1.1
32946 * Copyright(c) 2006-2007, Ext JS, LLC.
32948 * Originally Released Under LGPL - original licence link has changed is not relivant.
32951 * <script type="text/javascript">
32955 * @class Roo.Editor
32956 * @extends Roo.Component
32957 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32959 * Create a new Editor
32960 * @param {Roo.form.Field} field The Field object (or descendant)
32961 * @param {Object} config The config object
32963 Roo.Editor = function(field, config){
32964 Roo.Editor.superclass.constructor.call(this, config);
32965 this.field = field;
32968 * @event beforestartedit
32969 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
32970 * false from the handler of this event.
32971 * @param {Editor} this
32972 * @param {Roo.Element} boundEl The underlying element bound to this editor
32973 * @param {Mixed} value The field value being set
32975 "beforestartedit" : true,
32978 * Fires when this editor is displayed
32979 * @param {Roo.Element} boundEl The underlying element bound to this editor
32980 * @param {Mixed} value The starting field value
32982 "startedit" : true,
32984 * @event beforecomplete
32985 * Fires after a change has been made to the field, but before the change is reflected in the underlying
32986 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
32987 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32988 * event will not fire since no edit actually occurred.
32989 * @param {Editor} this
32990 * @param {Mixed} value The current field value
32991 * @param {Mixed} startValue The original field value
32993 "beforecomplete" : true,
32996 * Fires after editing is complete and any changed value has been written to the underlying field.
32997 * @param {Editor} this
32998 * @param {Mixed} value The current field value
32999 * @param {Mixed} startValue The original field value
33003 * @event specialkey
33004 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
33005 * {@link Roo.EventObject#getKey} to determine which key was pressed.
33006 * @param {Roo.form.Field} this
33007 * @param {Roo.EventObject} e The event object
33009 "specialkey" : true
33013 Roo.extend(Roo.Editor, Roo.Component, {
33015 * @cfg {Boolean/String} autosize
33016 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33017 * or "height" to adopt the height only (defaults to false)
33020 * @cfg {Boolean} revertInvalid
33021 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33022 * validation fails (defaults to true)
33025 * @cfg {Boolean} ignoreNoChange
33026 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33027 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
33028 * will never be ignored.
33031 * @cfg {Boolean} hideEl
33032 * False to keep the bound element visible while the editor is displayed (defaults to true)
33035 * @cfg {Mixed} value
33036 * The data value of the underlying field (defaults to "")
33040 * @cfg {String} alignment
33041 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33045 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33046 * for bottom-right shadow (defaults to "frame")
33050 * @cfg {Boolean} constrain True to constrain the editor to the viewport
33054 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33056 completeOnEnter : false,
33058 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33060 cancelOnEsc : false,
33062 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33067 onRender : function(ct, position){
33068 this.el = new Roo.Layer({
33069 shadow: this.shadow,
33075 constrain: this.constrain
33077 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33078 if(this.field.msgTarget != 'title'){
33079 this.field.msgTarget = 'qtip';
33081 this.field.render(this.el);
33083 this.field.el.dom.setAttribute('autocomplete', 'off');
33085 this.field.on("specialkey", this.onSpecialKey, this);
33086 if(this.swallowKeys){
33087 this.field.el.swallowEvent(['keydown','keypress']);
33090 this.field.on("blur", this.onBlur, this);
33091 if(this.field.grow){
33092 this.field.on("autosize", this.el.sync, this.el, {delay:1});
33096 onSpecialKey : function(field, e)
33098 //Roo.log('editor onSpecialKey');
33099 if(this.completeOnEnter && e.getKey() == e.ENTER){
33101 this.completeEdit();
33104 // do not fire special key otherwise it might hide close the editor...
33105 if(e.getKey() == e.ENTER){
33108 if(this.cancelOnEsc && e.getKey() == e.ESC){
33112 this.fireEvent('specialkey', field, e);
33117 * Starts the editing process and shows the editor.
33118 * @param {String/HTMLElement/Element} el The element to edit
33119 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33120 * to the innerHTML of el.
33122 startEdit : function(el, value){
33124 this.completeEdit();
33126 this.boundEl = Roo.get(el);
33127 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33128 if(!this.rendered){
33129 this.render(this.parentEl || document.body);
33131 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33134 this.startValue = v;
33135 this.field.setValue(v);
33137 var sz = this.boundEl.getSize();
33138 switch(this.autoSize){
33140 this.setSize(sz.width, "");
33143 this.setSize("", sz.height);
33146 this.setSize(sz.width, sz.height);
33149 this.el.alignTo(this.boundEl, this.alignment);
33150 this.editing = true;
33152 Roo.QuickTips.disable();
33158 * Sets the height and width of this editor.
33159 * @param {Number} width The new width
33160 * @param {Number} height The new height
33162 setSize : function(w, h){
33163 this.field.setSize(w, h);
33170 * Realigns the editor to the bound field based on the current alignment config value.
33172 realign : function(){
33173 this.el.alignTo(this.boundEl, this.alignment);
33177 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33178 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33180 completeEdit : function(remainVisible){
33184 var v = this.getValue();
33185 if(this.revertInvalid !== false && !this.field.isValid()){
33186 v = this.startValue;
33187 this.cancelEdit(true);
33189 if(String(v) === String(this.startValue) && this.ignoreNoChange){
33190 this.editing = false;
33194 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33195 this.editing = false;
33196 if(this.updateEl && this.boundEl){
33197 this.boundEl.update(v);
33199 if(remainVisible !== true){
33202 this.fireEvent("complete", this, v, this.startValue);
33207 onShow : function(){
33209 if(this.hideEl !== false){
33210 this.boundEl.hide();
33213 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33214 this.fixIEFocus = true;
33215 this.deferredFocus.defer(50, this);
33217 this.field.focus();
33219 this.fireEvent("startedit", this.boundEl, this.startValue);
33222 deferredFocus : function(){
33224 this.field.focus();
33229 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
33230 * reverted to the original starting value.
33231 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33232 * cancel (defaults to false)
33234 cancelEdit : function(remainVisible){
33236 this.setValue(this.startValue);
33237 if(remainVisible !== true){
33244 onBlur : function(){
33245 if(this.allowBlur !== true && this.editing){
33246 this.completeEdit();
33251 onHide : function(){
33253 this.completeEdit();
33257 if(this.field.collapse){
33258 this.field.collapse();
33261 if(this.hideEl !== false){
33262 this.boundEl.show();
33265 Roo.QuickTips.enable();
33270 * Sets the data value of the editor
33271 * @param {Mixed} value Any valid value supported by the underlying field
33273 setValue : function(v){
33274 this.field.setValue(v);
33278 * Gets the data value of the editor
33279 * @return {Mixed} The data value
33281 getValue : function(){
33282 return this.field.getValue();
33286 * Ext JS Library 1.1.1
33287 * Copyright(c) 2006-2007, Ext JS, LLC.
33289 * Originally Released Under LGPL - original licence link has changed is not relivant.
33292 * <script type="text/javascript">
33296 * @class Roo.BasicDialog
33297 * @extends Roo.util.Observable
33298 * @parent none builder
33299 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
33301 var dlg = new Roo.BasicDialog("my-dlg", {
33310 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33311 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
33312 dlg.addButton('Cancel', dlg.hide, dlg);
33315 <b>A Dialog should always be a direct child of the body element.</b>
33316 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33317 * @cfg {String} title Default text to display in the title bar (defaults to null)
33318 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33319 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33320 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33321 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33322 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33323 * (defaults to null with no animation)
33324 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33325 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33326 * property for valid values (defaults to 'all')
33327 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33328 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33329 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33330 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33331 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33332 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33333 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33334 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33335 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33336 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33337 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33338 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33339 * draggable = true (defaults to false)
33340 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33341 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33342 * shadow (defaults to false)
33343 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33344 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33345 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33346 * @cfg {Array} buttons Array of buttons
33347 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33349 * Create a new BasicDialog.
33350 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33351 * @param {Object} config Configuration options
33353 Roo.BasicDialog = function(el, config){
33354 this.el = Roo.get(el);
33355 var dh = Roo.DomHelper;
33356 if(!this.el && config && config.autoCreate){
33357 if(typeof config.autoCreate == "object"){
33358 if(!config.autoCreate.id){
33359 config.autoCreate.id = el;
33361 this.el = dh.append(document.body,
33362 config.autoCreate, true);
33364 this.el = dh.append(document.body,
33365 {tag: "div", id: el, style:'visibility:hidden;'}, true);
33369 el.setDisplayed(true);
33370 el.hide = this.hideAction;
33372 el.addClass("x-dlg");
33374 Roo.apply(this, config);
33376 this.proxy = el.createProxy("x-dlg-proxy");
33377 this.proxy.hide = this.hideAction;
33378 this.proxy.setOpacity(.5);
33382 el.setWidth(config.width);
33385 el.setHeight(config.height);
33387 this.size = el.getSize();
33388 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33389 this.xy = [config.x,config.y];
33391 this.xy = el.getCenterXY(true);
33393 /** The header element @type Roo.Element */
33394 this.header = el.child("> .x-dlg-hd");
33395 /** The body element @type Roo.Element */
33396 this.body = el.child("> .x-dlg-bd");
33397 /** The footer element @type Roo.Element */
33398 this.footer = el.child("> .x-dlg-ft");
33401 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
33404 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33407 this.header.unselectable();
33409 this.header.update(this.title);
33411 // this element allows the dialog to be focused for keyboard event
33412 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33413 this.focusEl.swallowEvent("click", true);
33415 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33417 // wrap the body and footer for special rendering
33418 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33420 this.bwrap.dom.appendChild(this.footer.dom);
33423 this.bg = this.el.createChild({
33424 tag: "div", cls:"x-dlg-bg",
33425 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
33427 this.centerBg = this.bg.child("div.x-dlg-bg-center");
33430 if(this.autoScroll !== false && !this.autoTabs){
33431 this.body.setStyle("overflow", "auto");
33434 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33436 if(this.closable !== false){
33437 this.el.addClass("x-dlg-closable");
33438 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33439 this.close.on("click", this.closeClick, this);
33440 this.close.addClassOnOver("x-dlg-close-over");
33442 if(this.collapsible !== false){
33443 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33444 this.collapseBtn.on("click", this.collapseClick, this);
33445 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33446 this.header.on("dblclick", this.collapseClick, this);
33448 if(this.resizable !== false){
33449 this.el.addClass("x-dlg-resizable");
33450 this.resizer = new Roo.Resizable(el, {
33451 minWidth: this.minWidth || 80,
33452 minHeight:this.minHeight || 80,
33453 handles: this.resizeHandles || "all",
33456 this.resizer.on("beforeresize", this.beforeResize, this);
33457 this.resizer.on("resize", this.onResize, this);
33459 if(this.draggable !== false){
33460 el.addClass("x-dlg-draggable");
33461 if (!this.proxyDrag) {
33462 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33465 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33467 dd.setHandleElId(this.header.id);
33468 dd.endDrag = this.endMove.createDelegate(this);
33469 dd.startDrag = this.startMove.createDelegate(this);
33470 dd.onDrag = this.onDrag.createDelegate(this);
33475 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33476 this.mask.enableDisplayMode("block");
33478 this.el.addClass("x-dlg-modal");
33481 this.shadow = new Roo.Shadow({
33482 mode : typeof this.shadow == "string" ? this.shadow : "sides",
33483 offset : this.shadowOffset
33486 this.shadowOffset = 0;
33488 if(Roo.useShims && this.shim !== false){
33489 this.shim = this.el.createShim();
33490 this.shim.hide = this.hideAction;
33498 if (this.buttons) {
33499 var bts= this.buttons;
33501 Roo.each(bts, function(b) {
33510 * Fires when a key is pressed
33511 * @param {Roo.BasicDialog} this
33512 * @param {Roo.EventObject} e
33517 * Fires when this dialog is moved by the user.
33518 * @param {Roo.BasicDialog} this
33519 * @param {Number} x The new page X
33520 * @param {Number} y The new page Y
33525 * Fires when this dialog is resized by the user.
33526 * @param {Roo.BasicDialog} this
33527 * @param {Number} width The new width
33528 * @param {Number} height The new height
33532 * @event beforehide
33533 * Fires before this dialog is hidden.
33534 * @param {Roo.BasicDialog} this
33536 "beforehide" : true,
33539 * Fires when this dialog is hidden.
33540 * @param {Roo.BasicDialog} this
33544 * @event beforeshow
33545 * Fires before this dialog is shown.
33546 * @param {Roo.BasicDialog} this
33548 "beforeshow" : true,
33551 * Fires when this dialog is shown.
33552 * @param {Roo.BasicDialog} this
33556 el.on("keydown", this.onKeyDown, this);
33557 el.on("mousedown", this.toFront, this);
33558 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33560 Roo.DialogManager.register(this);
33561 Roo.BasicDialog.superclass.constructor.call(this);
33564 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33565 shadowOffset: Roo.isIE ? 6 : 5,
33568 minButtonWidth: 75,
33569 defaultButton: null,
33570 buttonAlign: "right",
33575 * Sets the dialog title text
33576 * @param {String} text The title text to display
33577 * @return {Roo.BasicDialog} this
33579 setTitle : function(text){
33580 this.header.update(text);
33585 closeClick : function(){
33590 collapseClick : function(){
33591 this[this.collapsed ? "expand" : "collapse"]();
33595 * Collapses the dialog to its minimized state (only the title bar is visible).
33596 * Equivalent to the user clicking the collapse dialog button.
33598 collapse : function(){
33599 if(!this.collapsed){
33600 this.collapsed = true;
33601 this.el.addClass("x-dlg-collapsed");
33602 this.restoreHeight = this.el.getHeight();
33603 this.resizeTo(this.el.getWidth(), this.header.getHeight());
33608 * Expands a collapsed dialog back to its normal state. Equivalent to the user
33609 * clicking the expand dialog button.
33611 expand : function(){
33612 if(this.collapsed){
33613 this.collapsed = false;
33614 this.el.removeClass("x-dlg-collapsed");
33615 this.resizeTo(this.el.getWidth(), this.restoreHeight);
33620 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33621 * @return {Roo.TabPanel} The tabs component
33623 initTabs : function(){
33624 var tabs = this.getTabs();
33625 while(tabs.getTab(0)){
33628 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33630 tabs.addTab(Roo.id(dom), dom.title);
33638 beforeResize : function(){
33639 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33643 onResize : function(){
33644 this.refreshSize();
33645 this.syncBodyHeight();
33646 this.adjustAssets();
33648 this.fireEvent("resize", this, this.size.width, this.size.height);
33652 onKeyDown : function(e){
33653 if(this.isVisible()){
33654 this.fireEvent("keydown", this, e);
33659 * Resizes the dialog.
33660 * @param {Number} width
33661 * @param {Number} height
33662 * @return {Roo.BasicDialog} this
33664 resizeTo : function(width, height){
33665 this.el.setSize(width, height);
33666 this.size = {width: width, height: height};
33667 this.syncBodyHeight();
33668 if(this.fixedcenter){
33671 if(this.isVisible()){
33672 this.constrainXY();
33673 this.adjustAssets();
33675 this.fireEvent("resize", this, width, height);
33681 * Resizes the dialog to fit the specified content size.
33682 * @param {Number} width
33683 * @param {Number} height
33684 * @return {Roo.BasicDialog} this
33686 setContentSize : function(w, h){
33687 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33688 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33689 //if(!this.el.isBorderBox()){
33690 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33691 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33694 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33695 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33697 this.resizeTo(w, h);
33702 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
33703 * executed in response to a particular key being pressed while the dialog is active.
33704 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33705 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33706 * @param {Function} fn The function to call
33707 * @param {Object} scope (optional) The scope of the function
33708 * @return {Roo.BasicDialog} this
33710 addKeyListener : function(key, fn, scope){
33711 var keyCode, shift, ctrl, alt;
33712 if(typeof key == "object" && !(key instanceof Array)){
33713 keyCode = key["key"];
33714 shift = key["shift"];
33715 ctrl = key["ctrl"];
33720 var handler = function(dlg, e){
33721 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
33722 var k = e.getKey();
33723 if(keyCode instanceof Array){
33724 for(var i = 0, len = keyCode.length; i < len; i++){
33725 if(keyCode[i] == k){
33726 fn.call(scope || window, dlg, k, e);
33732 fn.call(scope || window, dlg, k, e);
33737 this.on("keydown", handler);
33742 * Returns the TabPanel component (creates it if it doesn't exist).
33743 * Note: If you wish to simply check for the existence of tabs without creating them,
33744 * check for a null 'tabs' property.
33745 * @return {Roo.TabPanel} The tabs component
33747 getTabs : function(){
33749 this.el.addClass("x-dlg-auto-tabs");
33750 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33751 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33757 * Adds a button to the footer section of the dialog.
33758 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33759 * object or a valid Roo.DomHelper element config
33760 * @param {Function} handler The function called when the button is clicked
33761 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33762 * @return {Roo.Button} The new button
33764 addButton : function(config, handler, scope){
33765 var dh = Roo.DomHelper;
33767 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33769 if(!this.btnContainer){
33770 var tb = this.footer.createChild({
33772 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33773 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33775 this.btnContainer = tb.firstChild.firstChild.firstChild;
33780 minWidth: this.minButtonWidth,
33783 if(typeof config == "string"){
33784 bconfig.text = config;
33787 bconfig.dhconfig = config;
33789 Roo.apply(bconfig, config);
33793 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33794 bconfig.position = Math.max(0, bconfig.position);
33795 fc = this.btnContainer.childNodes[bconfig.position];
33798 var btn = new Roo.Button(
33800 this.btnContainer.insertBefore(document.createElement("td"),fc)
33801 : this.btnContainer.appendChild(document.createElement("td")),
33802 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
33805 this.syncBodyHeight();
33808 * Array of all the buttons that have been added to this dialog via addButton
33813 this.buttons.push(btn);
33818 * Sets the default button to be focused when the dialog is displayed.
33819 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33820 * @return {Roo.BasicDialog} this
33822 setDefaultButton : function(btn){
33823 this.defaultButton = btn;
33828 getHeaderFooterHeight : function(safe){
33831 height += this.header.getHeight();
33834 var fm = this.footer.getMargins();
33835 height += (this.footer.getHeight()+fm.top+fm.bottom);
33837 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33838 height += this.centerBg.getPadding("tb");
33843 syncBodyHeight : function()
33845 var bd = this.body, // the text
33846 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33848 var height = this.size.height - this.getHeaderFooterHeight(false);
33849 bd.setHeight(height-bd.getMargins("tb"));
33850 var hh = this.header.getHeight();
33851 var h = this.size.height-hh;
33854 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33855 bw.setHeight(h-cb.getPadding("tb"));
33857 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33858 bd.setWidth(bw.getWidth(true));
33860 this.tabs.syncHeight();
33862 this.tabs.el.repaint();
33868 * Restores the previous state of the dialog if Roo.state is configured.
33869 * @return {Roo.BasicDialog} this
33871 restoreState : function(){
33872 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33873 if(box && box.width){
33874 this.xy = [box.x, box.y];
33875 this.resizeTo(box.width, box.height);
33881 beforeShow : function(){
33883 if(this.fixedcenter){
33884 this.xy = this.el.getCenterXY(true);
33887 Roo.get(document.body).addClass("x-body-masked");
33888 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33891 this.constrainXY();
33895 animShow : function(){
33896 var b = Roo.get(this.animateTarget).getBox();
33897 this.proxy.setSize(b.width, b.height);
33898 this.proxy.setLocation(b.x, b.y);
33900 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33901 true, .35, this.showEl.createDelegate(this));
33905 * Shows the dialog.
33906 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33907 * @return {Roo.BasicDialog} this
33909 show : function(animateTarget){
33910 if (this.fireEvent("beforeshow", this) === false){
33913 if(this.syncHeightBeforeShow){
33914 this.syncBodyHeight();
33915 }else if(this.firstShow){
33916 this.firstShow = false;
33917 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33919 this.animateTarget = animateTarget || this.animateTarget;
33920 if(!this.el.isVisible()){
33922 if(this.animateTarget && Roo.get(this.animateTarget)){
33932 showEl : function(){
33934 this.el.setXY(this.xy);
33936 this.adjustAssets(true);
33939 // IE peekaboo bug - fix found by Dave Fenwick
33943 this.fireEvent("show", this);
33947 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
33948 * dialog itself will receive focus.
33950 focus : function(){
33951 if(this.defaultButton){
33952 this.defaultButton.focus();
33954 this.focusEl.focus();
33959 constrainXY : function(){
33960 if(this.constraintoviewport !== false){
33961 if(!this.viewSize){
33962 if(this.container){
33963 var s = this.container.getSize();
33964 this.viewSize = [s.width, s.height];
33966 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33969 var s = Roo.get(this.container||document).getScroll();
33971 var x = this.xy[0], y = this.xy[1];
33972 var w = this.size.width, h = this.size.height;
33973 var vw = this.viewSize[0], vh = this.viewSize[1];
33974 // only move it if it needs it
33976 // first validate right/bottom
33977 if(x + w > vw+s.left){
33981 if(y + h > vh+s.top){
33985 // then make sure top/left isn't negative
33997 if(this.isVisible()){
33998 this.el.setLocation(x, y);
33999 this.adjustAssets();
34006 onDrag : function(){
34007 if(!this.proxyDrag){
34008 this.xy = this.el.getXY();
34009 this.adjustAssets();
34014 adjustAssets : function(doShow){
34015 var x = this.xy[0], y = this.xy[1];
34016 var w = this.size.width, h = this.size.height;
34017 if(doShow === true){
34019 this.shadow.show(this.el);
34025 if(this.shadow && this.shadow.isVisible()){
34026 this.shadow.show(this.el);
34028 if(this.shim && this.shim.isVisible()){
34029 this.shim.setBounds(x, y, w, h);
34034 adjustViewport : function(w, h){
34036 w = Roo.lib.Dom.getViewWidth();
34037 h = Roo.lib.Dom.getViewHeight();
34040 this.viewSize = [w, h];
34041 if(this.modal && this.mask.isVisible()){
34042 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34043 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34045 if(this.isVisible()){
34046 this.constrainXY();
34051 * Destroys this dialog and all its supporting elements (including any tabs, shim,
34052 * shadow, proxy, mask, etc.) Also removes all event listeners.
34053 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34055 destroy : function(removeEl){
34056 if(this.isVisible()){
34057 this.animateTarget = null;
34060 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34062 this.tabs.destroy(removeEl);
34075 for(var i = 0, len = this.buttons.length; i < len; i++){
34076 this.buttons[i].destroy();
34079 this.el.removeAllListeners();
34080 if(removeEl === true){
34081 this.el.update("");
34084 Roo.DialogManager.unregister(this);
34088 startMove : function(){
34089 if(this.proxyDrag){
34092 if(this.constraintoviewport !== false){
34093 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34098 endMove : function(){
34099 if(!this.proxyDrag){
34100 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34102 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34105 this.refreshSize();
34106 this.adjustAssets();
34108 this.fireEvent("move", this, this.xy[0], this.xy[1]);
34112 * Brings this dialog to the front of any other visible dialogs
34113 * @return {Roo.BasicDialog} this
34115 toFront : function(){
34116 Roo.DialogManager.bringToFront(this);
34121 * Sends this dialog to the back (under) of any other visible dialogs
34122 * @return {Roo.BasicDialog} this
34124 toBack : function(){
34125 Roo.DialogManager.sendToBack(this);
34130 * Centers this dialog in the viewport
34131 * @return {Roo.BasicDialog} this
34133 center : function(){
34134 var xy = this.el.getCenterXY(true);
34135 this.moveTo(xy[0], xy[1]);
34140 * Moves the dialog's top-left corner to the specified point
34141 * @param {Number} x
34142 * @param {Number} y
34143 * @return {Roo.BasicDialog} this
34145 moveTo : function(x, y){
34147 if(this.isVisible()){
34148 this.el.setXY(this.xy);
34149 this.adjustAssets();
34155 * Aligns the dialog to the specified element
34156 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34157 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34158 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34159 * @return {Roo.BasicDialog} this
34161 alignTo : function(element, position, offsets){
34162 this.xy = this.el.getAlignToXY(element, position, offsets);
34163 if(this.isVisible()){
34164 this.el.setXY(this.xy);
34165 this.adjustAssets();
34171 * Anchors an element to another element and realigns it when the window is resized.
34172 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34173 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34174 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34175 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34176 * is a number, it is used as the buffer delay (defaults to 50ms).
34177 * @return {Roo.BasicDialog} this
34179 anchorTo : function(el, alignment, offsets, monitorScroll){
34180 var action = function(){
34181 this.alignTo(el, alignment, offsets);
34183 Roo.EventManager.onWindowResize(action, this);
34184 var tm = typeof monitorScroll;
34185 if(tm != 'undefined'){
34186 Roo.EventManager.on(window, 'scroll', action, this,
34187 {buffer: tm == 'number' ? monitorScroll : 50});
34194 * Returns true if the dialog is visible
34195 * @return {Boolean}
34197 isVisible : function(){
34198 return this.el.isVisible();
34202 animHide : function(callback){
34203 var b = Roo.get(this.animateTarget).getBox();
34205 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34207 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34208 this.hideEl.createDelegate(this, [callback]));
34212 * Hides the dialog.
34213 * @param {Function} callback (optional) Function to call when the dialog is hidden
34214 * @return {Roo.BasicDialog} this
34216 hide : function(callback){
34217 if (this.fireEvent("beforehide", this) === false){
34221 this.shadow.hide();
34226 // sometimes animateTarget seems to get set.. causing problems...
34227 // this just double checks..
34228 if(this.animateTarget && Roo.get(this.animateTarget)) {
34229 this.animHide(callback);
34232 this.hideEl(callback);
34238 hideEl : function(callback){
34242 Roo.get(document.body).removeClass("x-body-masked");
34244 this.fireEvent("hide", this);
34245 if(typeof callback == "function"){
34251 hideAction : function(){
34252 this.setLeft("-10000px");
34253 this.setTop("-10000px");
34254 this.setStyle("visibility", "hidden");
34258 refreshSize : function(){
34259 this.size = this.el.getSize();
34260 this.xy = this.el.getXY();
34261 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34265 // z-index is managed by the DialogManager and may be overwritten at any time
34266 setZIndex : function(index){
34268 this.mask.setStyle("z-index", index);
34271 this.shim.setStyle("z-index", ++index);
34274 this.shadow.setZIndex(++index);
34276 this.el.setStyle("z-index", ++index);
34278 this.proxy.setStyle("z-index", ++index);
34281 this.resizer.proxy.setStyle("z-index", ++index);
34284 this.lastZIndex = index;
34288 * Returns the element for this dialog
34289 * @return {Roo.Element} The underlying dialog Element
34291 getEl : function(){
34297 * @class Roo.DialogManager
34298 * Provides global access to BasicDialogs that have been created and
34299 * support for z-indexing (layering) multiple open dialogs.
34301 Roo.DialogManager = function(){
34303 var accessList = [];
34307 var sortDialogs = function(d1, d2){
34308 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34312 var orderDialogs = function(){
34313 accessList.sort(sortDialogs);
34314 var seed = Roo.DialogManager.zseed;
34315 for(var i = 0, len = accessList.length; i < len; i++){
34316 var dlg = accessList[i];
34318 dlg.setZIndex(seed + (i*10));
34325 * The starting z-index for BasicDialogs (defaults to 9000)
34326 * @type Number The z-index value
34331 register : function(dlg){
34332 list[dlg.id] = dlg;
34333 accessList.push(dlg);
34337 unregister : function(dlg){
34338 delete list[dlg.id];
34341 if(!accessList.indexOf){
34342 for( i = 0, len = accessList.length; i < len; i++){
34343 if(accessList[i] == dlg){
34344 accessList.splice(i, 1);
34349 i = accessList.indexOf(dlg);
34351 accessList.splice(i, 1);
34357 * Gets a registered dialog by id
34358 * @param {String/Object} id The id of the dialog or a dialog
34359 * @return {Roo.BasicDialog} this
34361 get : function(id){
34362 return typeof id == "object" ? id : list[id];
34366 * Brings the specified dialog to the front
34367 * @param {String/Object} dlg The id of the dialog or a dialog
34368 * @return {Roo.BasicDialog} this
34370 bringToFront : function(dlg){
34371 dlg = this.get(dlg);
34374 dlg._lastAccess = new Date().getTime();
34381 * Sends the specified dialog to the back
34382 * @param {String/Object} dlg The id of the dialog or a dialog
34383 * @return {Roo.BasicDialog} this
34385 sendToBack : function(dlg){
34386 dlg = this.get(dlg);
34387 dlg._lastAccess = -(new Date().getTime());
34393 * Hides all dialogs
34395 hideAll : function(){
34396 for(var id in list){
34397 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34406 * @class Roo.LayoutDialog
34407 * @extends Roo.BasicDialog
34408 * @children Roo.ContentPanel
34409 * @parent builder none
34410 * Dialog which provides adjustments for working with a layout in a Dialog.
34411 * Add your necessary layout config options to the dialog's config.<br>
34412 * Example usage (including a nested layout):
34415 dialog = new Roo.LayoutDialog("download-dlg", {
34424 // layout config merges with the dialog config
34426 tabPosition: "top",
34427 alwaysShowTabs: true
34430 dialog.addKeyListener(27, dialog.hide, dialog);
34431 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34432 dialog.addButton("Build It!", this.getDownload, this);
34434 // we can even add nested layouts
34435 var innerLayout = new Roo.BorderLayout("dl-inner", {
34445 innerLayout.beginUpdate();
34446 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34447 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34448 innerLayout.endUpdate(true);
34450 var layout = dialog.getLayout();
34451 layout.beginUpdate();
34452 layout.add("center", new Roo.ContentPanel("standard-panel",
34453 {title: "Download the Source", fitToFrame:true}));
34454 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34455 {title: "Build your own roo.js"}));
34456 layout.getRegion("center").showPanel(sp);
34457 layout.endUpdate();
34461 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34462 * @param {Object} config configuration options
34464 Roo.LayoutDialog = function(el, cfg){
34467 if (typeof(cfg) == 'undefined') {
34468 config = Roo.apply({}, el);
34469 // not sure why we use documentElement here.. - it should always be body.
34470 // IE7 borks horribly if we use documentElement.
34471 // webkit also does not like documentElement - it creates a body element...
34472 el = Roo.get( document.body || document.documentElement ).createChild();
34473 //config.autoCreate = true;
34477 config.autoTabs = false;
34478 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34479 this.body.setStyle({overflow:"hidden", position:"relative"});
34480 this.layout = new Roo.BorderLayout(this.body.dom, config);
34481 this.layout.monitorWindowResize = false;
34482 this.el.addClass("x-dlg-auto-layout");
34483 // fix case when center region overwrites center function
34484 this.center = Roo.BasicDialog.prototype.center;
34485 this.on("show", this.layout.layout, this.layout, true);
34486 if (config.items) {
34487 var xitems = config.items;
34488 delete config.items;
34489 Roo.each(xitems, this.addxtype, this);
34494 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34498 * @cfg {Roo.LayoutRegion} east
34501 * @cfg {Roo.LayoutRegion} west
34504 * @cfg {Roo.LayoutRegion} south
34507 * @cfg {Roo.LayoutRegion} north
34510 * @cfg {Roo.LayoutRegion} center
34513 * @cfg {Roo.Button} buttons[] Bottom buttons..
34518 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34521 endUpdate : function(){
34522 this.layout.endUpdate();
34526 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34529 beginUpdate : function(){
34530 this.layout.beginUpdate();
34534 * Get the BorderLayout for this dialog
34535 * @return {Roo.BorderLayout}
34537 getLayout : function(){
34538 return this.layout;
34541 showEl : function(){
34542 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34544 this.layout.layout();
34549 // Use the syncHeightBeforeShow config option to control this automatically
34550 syncBodyHeight : function(){
34551 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34552 if(this.layout){this.layout.layout();}
34556 * Add an xtype element (actually adds to the layout.)
34557 * @return {Object} xdata xtype object data.
34560 addxtype : function(c) {
34561 return this.layout.addxtype(c);
34565 * Ext JS Library 1.1.1
34566 * Copyright(c) 2006-2007, Ext JS, LLC.
34568 * Originally Released Under LGPL - original licence link has changed is not relivant.
34571 * <script type="text/javascript">
34575 * @class Roo.MessageBox
34576 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
34580 Roo.Msg.alert('Status', 'Changes saved successfully.');
34582 // Prompt for user data:
34583 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34585 // process text value...
34589 // Show a dialog using config options:
34591 title:'Save Changes?',
34592 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34593 buttons: Roo.Msg.YESNOCANCEL,
34600 Roo.MessageBox = function(){
34601 var dlg, opt, mask, waitTimer;
34602 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34603 var buttons, activeTextEl, bwidth;
34606 var handleButton = function(button){
34608 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34612 var handleHide = function(){
34613 if(opt && opt.cls){
34614 dlg.el.removeClass(opt.cls);
34617 Roo.TaskMgr.stop(waitTimer);
34623 var updateButtons = function(b){
34626 buttons["ok"].hide();
34627 buttons["cancel"].hide();
34628 buttons["yes"].hide();
34629 buttons["no"].hide();
34630 dlg.footer.dom.style.display = 'none';
34633 dlg.footer.dom.style.display = '';
34634 for(var k in buttons){
34635 if(typeof buttons[k] != "function"){
34638 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34639 width += buttons[k].el.getWidth()+15;
34649 var handleEsc = function(d, k, e){
34650 if(opt && opt.closable !== false){
34660 * Returns a reference to the underlying {@link Roo.BasicDialog} element
34661 * @return {Roo.BasicDialog} The BasicDialog element
34663 getDialog : function(){
34665 dlg = new Roo.BasicDialog("x-msg-box", {
34670 constraintoviewport:false,
34672 collapsible : false,
34675 width:400, height:100,
34676 buttonAlign:"center",
34677 closeClick : function(){
34678 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34679 handleButton("no");
34681 handleButton("cancel");
34685 dlg.on("hide", handleHide);
34687 dlg.addKeyListener(27, handleEsc);
34689 var bt = this.buttonText;
34690 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34691 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34692 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34693 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34694 bodyEl = dlg.body.createChild({
34696 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
34698 msgEl = bodyEl.dom.firstChild;
34699 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34700 textboxEl.enableDisplayMode();
34701 textboxEl.addKeyListener([10,13], function(){
34702 if(dlg.isVisible() && opt && opt.buttons){
34703 if(opt.buttons.ok){
34704 handleButton("ok");
34705 }else if(opt.buttons.yes){
34706 handleButton("yes");
34710 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34711 textareaEl.enableDisplayMode();
34712 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34713 progressEl.enableDisplayMode();
34714 var pf = progressEl.dom.firstChild;
34716 pp = Roo.get(pf.firstChild);
34717 pp.setHeight(pf.offsetHeight);
34725 * Updates the message box body text
34726 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34727 * the XHTML-compliant non-breaking space character '&#160;')
34728 * @return {Roo.MessageBox} This message box
34730 updateText : function(text){
34731 if(!dlg.isVisible() && !opt.width){
34732 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34734 msgEl.innerHTML = text || ' ';
34736 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34737 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34739 Math.min(opt.width || cw , this.maxWidth),
34740 Math.max(opt.minWidth || this.minWidth, bwidth)
34743 activeTextEl.setWidth(w);
34745 if(dlg.isVisible()){
34746 dlg.fixedcenter = false;
34748 // to big, make it scroll. = But as usual stupid IE does not support
34751 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34752 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34753 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34755 bodyEl.dom.style.height = '';
34756 bodyEl.dom.style.overflowY = '';
34759 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34761 bodyEl.dom.style.overflowX = '';
34764 dlg.setContentSize(w, bodyEl.getHeight());
34765 if(dlg.isVisible()){
34766 dlg.fixedcenter = true;
34772 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
34773 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34774 * @param {Number} value Any number between 0 and 1 (e.g., .5)
34775 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34776 * @return {Roo.MessageBox} This message box
34778 updateProgress : function(value, text){
34780 this.updateText(text);
34782 if (pp) { // weird bug on my firefox - for some reason this is not defined
34783 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34789 * Returns true if the message box is currently displayed
34790 * @return {Boolean} True if the message box is visible, else false
34792 isVisible : function(){
34793 return dlg && dlg.isVisible();
34797 * Hides the message box if it is displayed
34800 if(this.isVisible()){
34806 * Displays a new message box, or reinitializes an existing message box, based on the config options
34807 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34808 * The following config object properties are supported:
34810 Property Type Description
34811 ---------- --------------- ------------------------------------------------------------------------------------
34812 animEl String/Element An id or Element from which the message box should animate as it opens and
34813 closes (defaults to undefined)
34814 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34815 cancel:'Bar'}), or false to not show any buttons (defaults to false)
34816 closable Boolean False to hide the top-right close button (defaults to true). Note that
34817 progress and wait dialogs will ignore this property and always hide the
34818 close button as they can only be closed programmatically.
34819 cls String A custom CSS class to apply to the message box element
34820 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
34821 displayed (defaults to 75)
34822 fn Function A callback function to execute after closing the dialog. The arguments to the
34823 function will be btn (the name of the button that was clicked, if applicable,
34824 e.g. "ok"), and text (the value of the active text field, if applicable).
34825 Progress and wait dialogs will ignore this option since they do not respond to
34826 user actions and can only be closed programmatically, so any required function
34827 should be called by the same code after it closes the dialog.
34828 icon String A CSS class that provides a background image to be used as an icon for
34829 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34830 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
34831 minWidth Number The minimum width in pixels of the message box (defaults to 100)
34832 modal Boolean False to allow user interaction with the page while the message box is
34833 displayed (defaults to true)
34834 msg String A string that will replace the existing message box body text (defaults
34835 to the XHTML-compliant non-breaking space character ' ')
34836 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
34837 progress Boolean True to display a progress bar (defaults to false)
34838 progressText String The text to display inside the progress bar if progress = true (defaults to '')
34839 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
34840 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
34841 title String The title text
34842 value String The string value to set into the active textbox element if displayed
34843 wait Boolean True to display a progress bar (defaults to false)
34844 width Number The width of the dialog in pixels
34851 msg: 'Please enter your address:',
34853 buttons: Roo.MessageBox.OKCANCEL,
34856 animEl: 'addAddressBtn'
34859 * @param {Object} config Configuration options
34860 * @return {Roo.MessageBox} This message box
34862 show : function(options)
34865 // this causes nightmares if you show one dialog after another
34866 // especially on callbacks..
34868 if(this.isVisible()){
34871 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34872 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
34873 Roo.log("New Dialog Message:" + options.msg )
34874 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34875 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34878 var d = this.getDialog();
34880 d.setTitle(opt.title || " ");
34881 d.close.setDisplayed(opt.closable !== false);
34882 activeTextEl = textboxEl;
34883 opt.prompt = opt.prompt || (opt.multiline ? true : false);
34888 textareaEl.setHeight(typeof opt.multiline == "number" ?
34889 opt.multiline : this.defaultTextHeight);
34890 activeTextEl = textareaEl;
34899 progressEl.setDisplayed(opt.progress === true);
34900 this.updateProgress(0);
34901 activeTextEl.dom.value = opt.value || "";
34903 dlg.setDefaultButton(activeTextEl);
34905 var bs = opt.buttons;
34908 db = buttons["ok"];
34909 }else if(bs && bs.yes){
34910 db = buttons["yes"];
34912 dlg.setDefaultButton(db);
34914 bwidth = updateButtons(opt.buttons);
34915 this.updateText(opt.msg);
34917 d.el.addClass(opt.cls);
34919 d.proxyDrag = opt.proxyDrag === true;
34920 d.modal = opt.modal !== false;
34921 d.mask = opt.modal !== false ? mask : false;
34922 if(!d.isVisible()){
34923 // force it to the end of the z-index stack so it gets a cursor in FF
34924 document.body.appendChild(dlg.el.dom);
34925 d.animateTarget = null;
34926 d.show(options.animEl);
34932 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
34933 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34934 * and closing the message box when the process is complete.
34935 * @param {String} title The title bar text
34936 * @param {String} msg The message box body text
34937 * @return {Roo.MessageBox} This message box
34939 progress : function(title, msg){
34946 minWidth: this.minProgressWidth,
34953 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34954 * If a callback function is passed it will be called after the user clicks the button, and the
34955 * id of the button that was clicked will be passed as the only parameter to the callback
34956 * (could also be the top-right close button).
34957 * @param {String} title The title bar text
34958 * @param {String} msg The message box body text
34959 * @param {Function} fn (optional) The callback function invoked after the message box is closed
34960 * @param {Object} scope (optional) The scope of the callback function
34961 * @return {Roo.MessageBox} This message box
34963 alert : function(title, msg, fn, scope){
34976 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
34977 * interaction while waiting for a long-running process to complete that does not have defined intervals.
34978 * You are responsible for closing the message box when the process is complete.
34979 * @param {String} msg The message box body text
34980 * @param {String} title (optional) The title bar text
34981 * @return {Roo.MessageBox} This message box
34983 wait : function(msg, title){
34994 waitTimer = Roo.TaskMgr.start({
34996 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35004 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35005 * If a callback function is passed it will be called after the user clicks either button, and the id of the
35006 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35007 * @param {String} title The title bar text
35008 * @param {String} msg The message box body text
35009 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35010 * @param {Object} scope (optional) The scope of the callback function
35011 * @return {Roo.MessageBox} This message box
35013 confirm : function(title, msg, fn, scope){
35017 buttons: this.YESNO,
35026 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35027 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
35028 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35029 * (could also be the top-right close button) and the text that was entered will be passed as the two
35030 * parameters to the callback.
35031 * @param {String} title The title bar text
35032 * @param {String} msg The message box body text
35033 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35034 * @param {Object} scope (optional) The scope of the callback function
35035 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35036 * property, or the height in pixels to create the textbox (defaults to false / single-line)
35037 * @return {Roo.MessageBox} This message box
35039 prompt : function(title, msg, fn, scope, multiline){
35043 buttons: this.OKCANCEL,
35048 multiline: multiline,
35055 * Button config that displays a single OK button
35060 * Button config that displays Yes and No buttons
35063 YESNO : {yes:true, no:true},
35065 * Button config that displays OK and Cancel buttons
35068 OKCANCEL : {ok:true, cancel:true},
35070 * Button config that displays Yes, No and Cancel buttons
35073 YESNOCANCEL : {yes:true, no:true, cancel:true},
35076 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35079 defaultTextHeight : 75,
35081 * The maximum width in pixels of the message box (defaults to 600)
35086 * The minimum width in pixels of the message box (defaults to 100)
35091 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
35092 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35095 minProgressWidth : 250,
35097 * An object containing the default button text strings that can be overriden for localized language support.
35098 * Supported properties are: ok, cancel, yes and no.
35099 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35112 * Shorthand for {@link Roo.MessageBox}
35114 Roo.Msg = Roo.MessageBox;/*
35116 * Ext JS Library 1.1.1
35117 * Copyright(c) 2006-2007, Ext JS, LLC.
35119 * Originally Released Under LGPL - original licence link has changed is not relivant.
35122 * <script type="text/javascript">
35125 * @class Roo.QuickTips
35126 * Provides attractive and customizable tooltips for any element.
35129 Roo.QuickTips = function(){
35130 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35131 var ce, bd, xy, dd;
35132 var visible = false, disabled = true, inited = false;
35133 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35135 var onOver = function(e){
35139 var t = e.getTarget();
35140 if(!t || t.nodeType !== 1 || t == document || t == document.body){
35143 if(ce && t == ce.el){
35144 clearTimeout(hideProc);
35147 if(t && tagEls[t.id]){
35148 tagEls[t.id].el = t;
35149 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35152 var ttp, et = Roo.fly(t);
35153 var ns = cfg.namespace;
35154 if(tm.interceptTitles && t.title){
35157 t.removeAttribute("title");
35158 e.preventDefault();
35160 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35163 showProc = show.defer(tm.showDelay, tm, [{
35165 text: ttp.replace(/\\n/g,'<br/>'),
35166 width: et.getAttributeNS(ns, cfg.width),
35167 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35168 title: et.getAttributeNS(ns, cfg.title),
35169 cls: et.getAttributeNS(ns, cfg.cls)
35174 var onOut = function(e){
35175 clearTimeout(showProc);
35176 var t = e.getTarget();
35177 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35178 hideProc = setTimeout(hide, tm.hideDelay);
35182 var onMove = function(e){
35188 if(tm.trackMouse && ce){
35193 var onDown = function(e){
35194 clearTimeout(showProc);
35195 clearTimeout(hideProc);
35197 if(tm.hideOnClick){
35200 tm.enable.defer(100, tm);
35205 var getPad = function(){
35206 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35209 var show = function(o){
35213 clearTimeout(dismissProc);
35215 if(removeCls){ // in case manually hidden
35216 el.removeClass(removeCls);
35220 el.addClass(ce.cls);
35221 removeCls = ce.cls;
35224 tipTitle.update(ce.title);
35227 tipTitle.update('');
35230 el.dom.style.width = tm.maxWidth+'px';
35231 //tipBody.dom.style.width = '';
35232 tipBodyText.update(o.text);
35233 var p = getPad(), w = ce.width;
35235 var td = tipBodyText.dom;
35236 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35237 if(aw > tm.maxWidth){
35239 }else if(aw < tm.minWidth){
35245 //tipBody.setWidth(w);
35246 el.setWidth(parseInt(w, 10) + p);
35247 if(ce.autoHide === false){
35248 close.setDisplayed(true);
35253 close.setDisplayed(false);
35259 el.avoidY = xy[1]-18;
35264 el.setStyle("visibility", "visible");
35265 el.fadeIn({callback: afterShow});
35271 var afterShow = function(){
35275 if(tm.autoDismiss && ce.autoHide !== false){
35276 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35281 var hide = function(noanim){
35282 clearTimeout(dismissProc);
35283 clearTimeout(hideProc);
35285 if(el.isVisible()){
35287 if(noanim !== true && tm.animate){
35288 el.fadeOut({callback: afterHide});
35295 var afterHide = function(){
35298 el.removeClass(removeCls);
35305 * @cfg {Number} minWidth
35306 * The minimum width of the quick tip (defaults to 40)
35310 * @cfg {Number} maxWidth
35311 * The maximum width of the quick tip (defaults to 300)
35315 * @cfg {Boolean} interceptTitles
35316 * True to automatically use the element's DOM title value if available (defaults to false)
35318 interceptTitles : false,
35320 * @cfg {Boolean} trackMouse
35321 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35323 trackMouse : false,
35325 * @cfg {Boolean} hideOnClick
35326 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35328 hideOnClick : true,
35330 * @cfg {Number} showDelay
35331 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35335 * @cfg {Number} hideDelay
35336 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35340 * @cfg {Boolean} autoHide
35341 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35342 * Used in conjunction with hideDelay.
35347 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35348 * (defaults to true). Used in conjunction with autoDismissDelay.
35350 autoDismiss : true,
35353 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35355 autoDismissDelay : 5000,
35357 * @cfg {Boolean} animate
35358 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35363 * @cfg {String} title
35364 * Title text to display (defaults to ''). This can be any valid HTML markup.
35368 * @cfg {String} text
35369 * Body text to display (defaults to ''). This can be any valid HTML markup.
35373 * @cfg {String} cls
35374 * A CSS class to apply to the base quick tip element (defaults to '').
35378 * @cfg {Number} width
35379 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
35380 * minWidth or maxWidth.
35385 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
35386 * or display QuickTips in a page.
35389 tm = Roo.QuickTips;
35390 cfg = tm.tagConfig;
35392 if(!Roo.isReady){ // allow calling of init() before onReady
35393 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35396 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35397 el.fxDefaults = {stopFx: true};
35398 // maximum custom styling
35399 //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
35400 el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');
35401 tipTitle = el.child('h3');
35402 tipTitle.enableDisplayMode("block");
35403 tipBody = el.child('div.x-tip-bd');
35404 tipBodyText = el.child('div.x-tip-bd-inner');
35405 //bdLeft = el.child('div.x-tip-bd-left');
35406 //bdRight = el.child('div.x-tip-bd-right');
35407 close = el.child('div.x-tip-close');
35408 close.enableDisplayMode("block");
35409 close.on("click", hide);
35410 var d = Roo.get(document);
35411 d.on("mousedown", onDown);
35412 d.on("mouseover", onOver);
35413 d.on("mouseout", onOut);
35414 d.on("mousemove", onMove);
35415 esc = d.addKeyListener(27, hide);
35418 dd = el.initDD("default", null, {
35419 onDrag : function(){
35423 dd.setHandleElId(tipTitle.id);
35432 * Configures a new quick tip instance and assigns it to a target element. The following config options
35435 Property Type Description
35436 ---------- --------------------- ------------------------------------------------------------------------
35437 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
35439 * @param {Object} config The config object
35441 register : function(config){
35442 var cs = config instanceof Array ? config : arguments;
35443 for(var i = 0, len = cs.length; i < len; i++) {
35445 var target = c.target;
35447 if(target instanceof Array){
35448 for(var j = 0, jlen = target.length; j < jlen; j++){
35449 tagEls[target[j]] = c;
35452 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35459 * Removes this quick tip from its element and destroys it.
35460 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35462 unregister : function(el){
35463 delete tagEls[Roo.id(el)];
35467 * Enable this quick tip.
35469 enable : function(){
35470 if(inited && disabled){
35472 if(locks.length < 1){
35479 * Disable this quick tip.
35481 disable : function(){
35483 clearTimeout(showProc);
35484 clearTimeout(hideProc);
35485 clearTimeout(dismissProc);
35493 * Returns true if the quick tip is enabled, else false.
35495 isEnabled : function(){
35501 namespace : "roo", // was ext?? this may break..
35502 alt_namespace : "ext",
35503 attribute : "qtip",
35513 // backwards compat
35514 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35516 * Ext JS Library 1.1.1
35517 * Copyright(c) 2006-2007, Ext JS, LLC.
35519 * Originally Released Under LGPL - original licence link has changed is not relivant.
35522 * <script type="text/javascript">
35527 * @class Roo.tree.TreePanel
35528 * @extends Roo.data.Tree
35529 * @cfg {Roo.tree.TreeNode} root The root node
35530 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35531 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35532 * @cfg {Boolean} enableDD true to enable drag and drop
35533 * @cfg {Boolean} enableDrag true to enable just drag
35534 * @cfg {Boolean} enableDrop true to enable just drop
35535 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35536 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35537 * @cfg {String} ddGroup The DD group this TreePanel belongs to
35538 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35539 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35540 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35541 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35542 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35543 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35544 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35545 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35546 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35547 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35548 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35549 * @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>
35550 * @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>
35553 * @param {String/HTMLElement/Element} el The container element
35554 * @param {Object} config
35556 Roo.tree.TreePanel = function(el, config){
35558 var loader = false;
35560 root = config.root;
35561 delete config.root;
35563 if (config.loader) {
35564 loader = config.loader;
35565 delete config.loader;
35568 Roo.apply(this, config);
35569 Roo.tree.TreePanel.superclass.constructor.call(this);
35570 this.el = Roo.get(el);
35571 this.el.addClass('x-tree');
35572 //console.log(root);
35574 this.setRootNode( Roo.factory(root, Roo.tree));
35577 this.loader = Roo.factory(loader, Roo.tree);
35580 * Read-only. The id of the container element becomes this TreePanel's id.
35582 this.id = this.el.id;
35585 * @event beforeload
35586 * Fires before a node is loaded, return false to cancel
35587 * @param {Node} node The node being loaded
35589 "beforeload" : true,
35592 * Fires when a node is loaded
35593 * @param {Node} node The node that was loaded
35597 * @event textchange
35598 * Fires when the text for a node is changed
35599 * @param {Node} node The node
35600 * @param {String} text The new text
35601 * @param {String} oldText The old text
35603 "textchange" : true,
35605 * @event beforeexpand
35606 * Fires before a node is expanded, return false to cancel.
35607 * @param {Node} node The node
35608 * @param {Boolean} deep
35609 * @param {Boolean} anim
35611 "beforeexpand" : true,
35613 * @event beforecollapse
35614 * Fires before a node is collapsed, return false to cancel.
35615 * @param {Node} node The node
35616 * @param {Boolean} deep
35617 * @param {Boolean} anim
35619 "beforecollapse" : true,
35622 * Fires when a node is expanded
35623 * @param {Node} node The node
35627 * @event disabledchange
35628 * Fires when the disabled status of a node changes
35629 * @param {Node} node The node
35630 * @param {Boolean} disabled
35632 "disabledchange" : true,
35635 * Fires when a node is collapsed
35636 * @param {Node} node The node
35640 * @event beforeclick
35641 * Fires before click processing on a node. Return false to cancel the default action.
35642 * @param {Node} node The node
35643 * @param {Roo.EventObject} e The event object
35645 "beforeclick":true,
35647 * @event checkchange
35648 * Fires when a node with a checkbox's checked property changes
35649 * @param {Node} this This node
35650 * @param {Boolean} checked
35652 "checkchange":true,
35655 * Fires when a node is clicked
35656 * @param {Node} node The node
35657 * @param {Roo.EventObject} e The event object
35662 * Fires when a node is double clicked
35663 * @param {Node} node The node
35664 * @param {Roo.EventObject} e The event object
35668 * @event contextmenu
35669 * Fires when a node is right clicked
35670 * @param {Node} node The node
35671 * @param {Roo.EventObject} e The event object
35673 "contextmenu":true,
35675 * @event beforechildrenrendered
35676 * Fires right before the child nodes for a node are rendered
35677 * @param {Node} node The node
35679 "beforechildrenrendered":true,
35682 * Fires when a node starts being dragged
35683 * @param {Roo.tree.TreePanel} this
35684 * @param {Roo.tree.TreeNode} node
35685 * @param {event} e The raw browser event
35687 "startdrag" : true,
35690 * Fires when a drag operation is complete
35691 * @param {Roo.tree.TreePanel} this
35692 * @param {Roo.tree.TreeNode} node
35693 * @param {event} e The raw browser event
35698 * Fires when a dragged node is dropped on a valid DD target
35699 * @param {Roo.tree.TreePanel} this
35700 * @param {Roo.tree.TreeNode} node
35701 * @param {DD} dd The dd it was dropped on
35702 * @param {event} e The raw browser event
35706 * @event beforenodedrop
35707 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35708 * passed to handlers has the following properties:<br />
35709 * <ul style="padding:5px;padding-left:16px;">
35710 * <li>tree - The TreePanel</li>
35711 * <li>target - The node being targeted for the drop</li>
35712 * <li>data - The drag data from the drag source</li>
35713 * <li>point - The point of the drop - append, above or below</li>
35714 * <li>source - The drag source</li>
35715 * <li>rawEvent - Raw mouse event</li>
35716 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35717 * to be inserted by setting them on this object.</li>
35718 * <li>cancel - Set this to true to cancel the drop.</li>
35720 * @param {Object} dropEvent
35722 "beforenodedrop" : true,
35725 * Fires after a DD object is dropped on a node in this tree. The dropEvent
35726 * passed to handlers has the following properties:<br />
35727 * <ul style="padding:5px;padding-left:16px;">
35728 * <li>tree - The TreePanel</li>
35729 * <li>target - The node being targeted for the drop</li>
35730 * <li>data - The drag data from the drag source</li>
35731 * <li>point - The point of the drop - append, above or below</li>
35732 * <li>source - The drag source</li>
35733 * <li>rawEvent - Raw mouse event</li>
35734 * <li>dropNode - Dropped node(s).</li>
35736 * @param {Object} dropEvent
35740 * @event nodedragover
35741 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35742 * passed to handlers has the following properties:<br />
35743 * <ul style="padding:5px;padding-left:16px;">
35744 * <li>tree - The TreePanel</li>
35745 * <li>target - The node being targeted for the drop</li>
35746 * <li>data - The drag data from the drag source</li>
35747 * <li>point - The point of the drop - append, above or below</li>
35748 * <li>source - The drag source</li>
35749 * <li>rawEvent - Raw mouse event</li>
35750 * <li>dropNode - Drop node(s) provided by the source.</li>
35751 * <li>cancel - Set this to true to signal drop not allowed.</li>
35753 * @param {Object} dragOverEvent
35755 "nodedragover" : true,
35757 * @event appendnode
35758 * Fires when append node to the tree
35759 * @param {Roo.tree.TreePanel} this
35760 * @param {Roo.tree.TreeNode} node
35761 * @param {Number} index The index of the newly appended node
35763 "appendnode" : true
35766 if(this.singleExpand){
35767 this.on("beforeexpand", this.restrictExpand, this);
35770 this.editor.tree = this;
35771 this.editor = Roo.factory(this.editor, Roo.tree);
35774 if (this.selModel) {
35775 this.selModel = Roo.factory(this.selModel, Roo.tree);
35779 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35780 rootVisible : true,
35781 animate: Roo.enableFx,
35784 hlDrop : Roo.enableFx,
35788 rendererTip: false,
35790 restrictExpand : function(node){
35791 var p = node.parentNode;
35793 if(p.expandedChild && p.expandedChild.parentNode == p){
35794 p.expandedChild.collapse();
35796 p.expandedChild = node;
35800 // private override
35801 setRootNode : function(node){
35802 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35803 if(!this.rootVisible){
35804 node.ui = new Roo.tree.RootTreeNodeUI(node);
35810 * Returns the container element for this TreePanel
35812 getEl : function(){
35817 * Returns the default TreeLoader for this TreePanel
35819 getLoader : function(){
35820 return this.loader;
35826 expandAll : function(){
35827 this.root.expand(true);
35831 * Collapse all nodes
35833 collapseAll : function(){
35834 this.root.collapse(true);
35838 * Returns the selection model used by this TreePanel
35840 getSelectionModel : function(){
35841 if(!this.selModel){
35842 this.selModel = new Roo.tree.DefaultSelectionModel();
35844 return this.selModel;
35848 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35849 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35850 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35853 getChecked : function(a, startNode){
35854 startNode = startNode || this.root;
35856 var f = function(){
35857 if(this.attributes.checked){
35858 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35861 startNode.cascade(f);
35866 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35867 * @param {String} path
35868 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35869 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35870 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35872 expandPath : function(path, attr, callback){
35873 attr = attr || "id";
35874 var keys = path.split(this.pathSeparator);
35875 var curNode = this.root;
35876 if(curNode.attributes[attr] != keys[1]){ // invalid root
35878 callback(false, null);
35883 var f = function(){
35884 if(++index == keys.length){
35886 callback(true, curNode);
35890 var c = curNode.findChild(attr, keys[index]);
35893 callback(false, curNode);
35898 c.expand(false, false, f);
35900 curNode.expand(false, false, f);
35904 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35905 * @param {String} path
35906 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35907 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35908 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35910 selectPath : function(path, attr, callback){
35911 attr = attr || "id";
35912 var keys = path.split(this.pathSeparator);
35913 var v = keys.pop();
35914 if(keys.length > 0){
35915 var f = function(success, node){
35916 if(success && node){
35917 var n = node.findChild(attr, v);
35923 }else if(callback){
35924 callback(false, n);
35928 callback(false, n);
35932 this.expandPath(keys.join(this.pathSeparator), attr, f);
35934 this.root.select();
35936 callback(true, this.root);
35941 getTreeEl : function(){
35946 * Trigger rendering of this TreePanel
35948 render : function(){
35949 if (this.innerCt) {
35950 return this; // stop it rendering more than once!!
35953 this.innerCt = this.el.createChild({tag:"ul",
35954 cls:"x-tree-root-ct " +
35955 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35957 if(this.containerScroll){
35958 Roo.dd.ScrollManager.register(this.el);
35960 if((this.enableDD || this.enableDrop) && !this.dropZone){
35962 * The dropZone used by this tree if drop is enabled
35963 * @type Roo.tree.TreeDropZone
35965 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35966 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35969 if((this.enableDD || this.enableDrag) && !this.dragZone){
35971 * The dragZone used by this tree if drag is enabled
35972 * @type Roo.tree.TreeDragZone
35974 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35975 ddGroup: this.ddGroup || "TreeDD",
35976 scroll: this.ddScroll
35979 this.getSelectionModel().init(this);
35981 Roo.log("ROOT not set in tree");
35984 this.root.render();
35985 if(!this.rootVisible){
35986 this.root.renderChildren();
35992 * Ext JS Library 1.1.1
35993 * Copyright(c) 2006-2007, Ext JS, LLC.
35995 * Originally Released Under LGPL - original licence link has changed is not relivant.
35998 * <script type="text/javascript">
36003 * @class Roo.tree.DefaultSelectionModel
36004 * @extends Roo.util.Observable
36005 * The default single selection for a TreePanel.
36006 * @param {Object} cfg Configuration
36008 Roo.tree.DefaultSelectionModel = function(cfg){
36009 this.selNode = null;
36015 * @event selectionchange
36016 * Fires when the selected node changes
36017 * @param {DefaultSelectionModel} this
36018 * @param {TreeNode} node the new selection
36020 "selectionchange" : true,
36023 * @event beforeselect
36024 * Fires before the selected node changes, return false to cancel the change
36025 * @param {DefaultSelectionModel} this
36026 * @param {TreeNode} node the new selection
36027 * @param {TreeNode} node the old selection
36029 "beforeselect" : true
36032 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36035 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36036 init : function(tree){
36038 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36039 tree.on("click", this.onNodeClick, this);
36042 onNodeClick : function(node, e){
36043 if (e.ctrlKey && this.selNode == node) {
36044 this.unselect(node);
36052 * @param {TreeNode} node The node to select
36053 * @return {TreeNode} The selected node
36055 select : function(node){
36056 var last = this.selNode;
36057 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36059 last.ui.onSelectedChange(false);
36061 this.selNode = node;
36062 node.ui.onSelectedChange(true);
36063 this.fireEvent("selectionchange", this, node, last);
36070 * @param {TreeNode} node The node to unselect
36072 unselect : function(node){
36073 if(this.selNode == node){
36074 this.clearSelections();
36079 * Clear all selections
36081 clearSelections : function(){
36082 var n = this.selNode;
36084 n.ui.onSelectedChange(false);
36085 this.selNode = null;
36086 this.fireEvent("selectionchange", this, null);
36092 * Get the selected node
36093 * @return {TreeNode} The selected node
36095 getSelectedNode : function(){
36096 return this.selNode;
36100 * Returns true if the node is selected
36101 * @param {TreeNode} node The node to check
36102 * @return {Boolean}
36104 isSelected : function(node){
36105 return this.selNode == node;
36109 * Selects the node above the selected node in the tree, intelligently walking the nodes
36110 * @return TreeNode The new selection
36112 selectPrevious : function(){
36113 var s = this.selNode || this.lastSelNode;
36117 var ps = s.previousSibling;
36119 if(!ps.isExpanded() || ps.childNodes.length < 1){
36120 return this.select(ps);
36122 var lc = ps.lastChild;
36123 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36126 return this.select(lc);
36128 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36129 return this.select(s.parentNode);
36135 * Selects the node above the selected node in the tree, intelligently walking the nodes
36136 * @return TreeNode The new selection
36138 selectNext : function(){
36139 var s = this.selNode || this.lastSelNode;
36143 if(s.firstChild && s.isExpanded()){
36144 return this.select(s.firstChild);
36145 }else if(s.nextSibling){
36146 return this.select(s.nextSibling);
36147 }else if(s.parentNode){
36149 s.parentNode.bubble(function(){
36150 if(this.nextSibling){
36151 newS = this.getOwnerTree().selModel.select(this.nextSibling);
36160 onKeyDown : function(e){
36161 var s = this.selNode || this.lastSelNode;
36162 // undesirable, but required
36167 var k = e.getKey();
36175 this.selectPrevious();
36178 e.preventDefault();
36179 if(s.hasChildNodes()){
36180 if(!s.isExpanded()){
36182 }else if(s.firstChild){
36183 this.select(s.firstChild, e);
36188 e.preventDefault();
36189 if(s.hasChildNodes() && s.isExpanded()){
36191 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36192 this.select(s.parentNode, e);
36200 * @class Roo.tree.MultiSelectionModel
36201 * @extends Roo.util.Observable
36202 * Multi selection for a TreePanel.
36203 * @param {Object} cfg Configuration
36205 Roo.tree.MultiSelectionModel = function(){
36206 this.selNodes = [];
36210 * @event selectionchange
36211 * Fires when the selected nodes change
36212 * @param {MultiSelectionModel} this
36213 * @param {Array} nodes Array of the selected nodes
36215 "selectionchange" : true
36217 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36221 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36222 init : function(tree){
36224 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36225 tree.on("click", this.onNodeClick, this);
36228 onNodeClick : function(node, e){
36229 this.select(node, e, e.ctrlKey);
36234 * @param {TreeNode} node The node to select
36235 * @param {EventObject} e (optional) An event associated with the selection
36236 * @param {Boolean} keepExisting True to retain existing selections
36237 * @return {TreeNode} The selected node
36239 select : function(node, e, keepExisting){
36240 if(keepExisting !== true){
36241 this.clearSelections(true);
36243 if(this.isSelected(node)){
36244 this.lastSelNode = node;
36247 this.selNodes.push(node);
36248 this.selMap[node.id] = node;
36249 this.lastSelNode = node;
36250 node.ui.onSelectedChange(true);
36251 this.fireEvent("selectionchange", this, this.selNodes);
36257 * @param {TreeNode} node The node to unselect
36259 unselect : function(node){
36260 if(this.selMap[node.id]){
36261 node.ui.onSelectedChange(false);
36262 var sn = this.selNodes;
36265 index = sn.indexOf(node);
36267 for(var i = 0, len = sn.length; i < len; i++){
36275 this.selNodes.splice(index, 1);
36277 delete this.selMap[node.id];
36278 this.fireEvent("selectionchange", this, this.selNodes);
36283 * Clear all selections
36285 clearSelections : function(suppressEvent){
36286 var sn = this.selNodes;
36288 for(var i = 0, len = sn.length; i < len; i++){
36289 sn[i].ui.onSelectedChange(false);
36291 this.selNodes = [];
36293 if(suppressEvent !== true){
36294 this.fireEvent("selectionchange", this, this.selNodes);
36300 * Returns true if the node is selected
36301 * @param {TreeNode} node The node to check
36302 * @return {Boolean}
36304 isSelected : function(node){
36305 return this.selMap[node.id] ? true : false;
36309 * Returns an array of the selected nodes
36312 getSelectedNodes : function(){
36313 return this.selNodes;
36316 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36318 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36320 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36323 * Ext JS Library 1.1.1
36324 * Copyright(c) 2006-2007, Ext JS, LLC.
36326 * Originally Released Under LGPL - original licence link has changed is not relivant.
36329 * <script type="text/javascript">
36333 * @class Roo.tree.TreeNode
36334 * @extends Roo.data.Node
36335 * @cfg {String} text The text for this node
36336 * @cfg {Boolean} expanded true to start the node expanded
36337 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36338 * @cfg {Boolean} allowDrop false if this node cannot be drop on
36339 * @cfg {Boolean} disabled true to start the node disabled
36340 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36341 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
36342 * @cfg {String} cls A css class to be added to the node
36343 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36344 * @cfg {String} href URL of the link used for the node (defaults to #)
36345 * @cfg {String} hrefTarget target frame for the link
36346 * @cfg {String} qtip An Ext QuickTip for the node
36347 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36348 * @cfg {Boolean} singleClickExpand True for single click expand on this node
36349 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36350 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36351 * (defaults to undefined with no checkbox rendered)
36353 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36355 Roo.tree.TreeNode = function(attributes){
36356 attributes = attributes || {};
36357 if(typeof attributes == "string"){
36358 attributes = {text: attributes};
36360 this.childrenRendered = false;
36361 this.rendered = false;
36362 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36363 this.expanded = attributes.expanded === true;
36364 this.isTarget = attributes.isTarget !== false;
36365 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36366 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36369 * Read-only. The text for this node. To change it use setText().
36372 this.text = attributes.text;
36374 * True if this node is disabled.
36377 this.disabled = attributes.disabled === true;
36381 * @event textchange
36382 * Fires when the text for this node is changed
36383 * @param {Node} this This node
36384 * @param {String} text The new text
36385 * @param {String} oldText The old text
36387 "textchange" : true,
36389 * @event beforeexpand
36390 * Fires before this node is expanded, return false to cancel.
36391 * @param {Node} this This node
36392 * @param {Boolean} deep
36393 * @param {Boolean} anim
36395 "beforeexpand" : true,
36397 * @event beforecollapse
36398 * Fires before this node is collapsed, return false to cancel.
36399 * @param {Node} this This node
36400 * @param {Boolean} deep
36401 * @param {Boolean} anim
36403 "beforecollapse" : true,
36406 * Fires when this node is expanded
36407 * @param {Node} this This node
36411 * @event disabledchange
36412 * Fires when the disabled status of this node changes
36413 * @param {Node} this This node
36414 * @param {Boolean} disabled
36416 "disabledchange" : true,
36419 * Fires when this node is collapsed
36420 * @param {Node} this This node
36424 * @event beforeclick
36425 * Fires before click processing. Return false to cancel the default action.
36426 * @param {Node} this This node
36427 * @param {Roo.EventObject} e The event object
36429 "beforeclick":true,
36431 * @event checkchange
36432 * Fires when a node with a checkbox's checked property changes
36433 * @param {Node} this This node
36434 * @param {Boolean} checked
36436 "checkchange":true,
36439 * Fires when this node is clicked
36440 * @param {Node} this This node
36441 * @param {Roo.EventObject} e The event object
36446 * Fires when this node is double clicked
36447 * @param {Node} this This node
36448 * @param {Roo.EventObject} e The event object
36452 * @event contextmenu
36453 * Fires when this node is right clicked
36454 * @param {Node} this This node
36455 * @param {Roo.EventObject} e The event object
36457 "contextmenu":true,
36459 * @event beforechildrenrendered
36460 * Fires right before the child nodes for this node are rendered
36461 * @param {Node} this This node
36463 "beforechildrenrendered":true
36466 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36469 * Read-only. The UI for this node
36472 this.ui = new uiClass(this);
36474 // finally support items[]
36475 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36480 Roo.each(this.attributes.items, function(c) {
36481 this.appendChild(Roo.factory(c,Roo.Tree));
36483 delete this.attributes.items;
36488 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36489 preventHScroll: true,
36491 * Returns true if this node is expanded
36492 * @return {Boolean}
36494 isExpanded : function(){
36495 return this.expanded;
36499 * Returns the UI object for this node
36500 * @return {TreeNodeUI}
36502 getUI : function(){
36506 // private override
36507 setFirstChild : function(node){
36508 var of = this.firstChild;
36509 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36510 if(this.childrenRendered && of && node != of){
36511 of.renderIndent(true, true);
36514 this.renderIndent(true, true);
36518 // private override
36519 setLastChild : function(node){
36520 var ol = this.lastChild;
36521 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36522 if(this.childrenRendered && ol && node != ol){
36523 ol.renderIndent(true, true);
36526 this.renderIndent(true, true);
36530 // these methods are overridden to provide lazy rendering support
36531 // private override
36532 appendChild : function()
36534 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36535 if(node && this.childrenRendered){
36538 this.ui.updateExpandIcon();
36542 // private override
36543 removeChild : function(node){
36544 this.ownerTree.getSelectionModel().unselect(node);
36545 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36546 // if it's been rendered remove dom node
36547 if(this.childrenRendered){
36550 if(this.childNodes.length < 1){
36551 this.collapse(false, false);
36553 this.ui.updateExpandIcon();
36555 if(!this.firstChild) {
36556 this.childrenRendered = false;
36561 // private override
36562 insertBefore : function(node, refNode){
36563 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36564 if(newNode && refNode && this.childrenRendered){
36567 this.ui.updateExpandIcon();
36572 * Sets the text for this node
36573 * @param {String} text
36575 setText : function(text){
36576 var oldText = this.text;
36578 this.attributes.text = text;
36579 if(this.rendered){ // event without subscribing
36580 this.ui.onTextChange(this, text, oldText);
36582 this.fireEvent("textchange", this, text, oldText);
36586 * Triggers selection of this node
36588 select : function(){
36589 this.getOwnerTree().getSelectionModel().select(this);
36593 * Triggers deselection of this node
36595 unselect : function(){
36596 this.getOwnerTree().getSelectionModel().unselect(this);
36600 * Returns true if this node is selected
36601 * @return {Boolean}
36603 isSelected : function(){
36604 return this.getOwnerTree().getSelectionModel().isSelected(this);
36608 * Expand this node.
36609 * @param {Boolean} deep (optional) True to expand all children as well
36610 * @param {Boolean} anim (optional) false to cancel the default animation
36611 * @param {Function} callback (optional) A callback to be called when
36612 * expanding this node completes (does not wait for deep expand to complete).
36613 * Called with 1 parameter, this node.
36615 expand : function(deep, anim, callback){
36616 if(!this.expanded){
36617 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36620 if(!this.childrenRendered){
36621 this.renderChildren();
36623 this.expanded = true;
36625 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36626 this.ui.animExpand(function(){
36627 this.fireEvent("expand", this);
36628 if(typeof callback == "function"){
36632 this.expandChildNodes(true);
36634 }.createDelegate(this));
36638 this.fireEvent("expand", this);
36639 if(typeof callback == "function"){
36644 if(typeof callback == "function"){
36649 this.expandChildNodes(true);
36653 isHiddenRoot : function(){
36654 return this.isRoot && !this.getOwnerTree().rootVisible;
36658 * Collapse this node.
36659 * @param {Boolean} deep (optional) True to collapse all children as well
36660 * @param {Boolean} anim (optional) false to cancel the default animation
36662 collapse : function(deep, anim){
36663 if(this.expanded && !this.isHiddenRoot()){
36664 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36667 this.expanded = false;
36668 if((this.getOwnerTree().animate && anim !== false) || anim){
36669 this.ui.animCollapse(function(){
36670 this.fireEvent("collapse", this);
36672 this.collapseChildNodes(true);
36674 }.createDelegate(this));
36677 this.ui.collapse();
36678 this.fireEvent("collapse", this);
36682 var cs = this.childNodes;
36683 for(var i = 0, len = cs.length; i < len; i++) {
36684 cs[i].collapse(true, false);
36690 delayedExpand : function(delay){
36691 if(!this.expandProcId){
36692 this.expandProcId = this.expand.defer(delay, this);
36697 cancelExpand : function(){
36698 if(this.expandProcId){
36699 clearTimeout(this.expandProcId);
36701 this.expandProcId = false;
36705 * Toggles expanded/collapsed state of the node
36707 toggle : function(){
36716 * Ensures all parent nodes are expanded
36718 ensureVisible : function(callback){
36719 var tree = this.getOwnerTree();
36720 tree.expandPath(this.parentNode.getPath(), false, function(){
36721 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36722 Roo.callback(callback);
36723 }.createDelegate(this));
36727 * Expand all child nodes
36728 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36730 expandChildNodes : function(deep){
36731 var cs = this.childNodes;
36732 for(var i = 0, len = cs.length; i < len; i++) {
36733 cs[i].expand(deep);
36738 * Collapse all child nodes
36739 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36741 collapseChildNodes : function(deep){
36742 var cs = this.childNodes;
36743 for(var i = 0, len = cs.length; i < len; i++) {
36744 cs[i].collapse(deep);
36749 * Disables this node
36751 disable : function(){
36752 this.disabled = true;
36754 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36755 this.ui.onDisableChange(this, true);
36757 this.fireEvent("disabledchange", this, true);
36761 * Enables this node
36763 enable : function(){
36764 this.disabled = false;
36765 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36766 this.ui.onDisableChange(this, false);
36768 this.fireEvent("disabledchange", this, false);
36772 renderChildren : function(suppressEvent){
36773 if(suppressEvent !== false){
36774 this.fireEvent("beforechildrenrendered", this);
36776 var cs = this.childNodes;
36777 for(var i = 0, len = cs.length; i < len; i++){
36778 cs[i].render(true);
36780 this.childrenRendered = true;
36784 sort : function(fn, scope){
36785 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36786 if(this.childrenRendered){
36787 var cs = this.childNodes;
36788 for(var i = 0, len = cs.length; i < len; i++){
36789 cs[i].render(true);
36795 render : function(bulkRender){
36796 this.ui.render(bulkRender);
36797 if(!this.rendered){
36798 this.rendered = true;
36800 this.expanded = false;
36801 this.expand(false, false);
36807 renderIndent : function(deep, refresh){
36809 this.ui.childIndent = null;
36811 this.ui.renderIndent();
36812 if(deep === true && this.childrenRendered){
36813 var cs = this.childNodes;
36814 for(var i = 0, len = cs.length; i < len; i++){
36815 cs[i].renderIndent(true, refresh);
36821 * Ext JS Library 1.1.1
36822 * Copyright(c) 2006-2007, Ext JS, LLC.
36824 * Originally Released Under LGPL - original licence link has changed is not relivant.
36827 * <script type="text/javascript">
36831 * @class Roo.tree.AsyncTreeNode
36832 * @extends Roo.tree.TreeNode
36833 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36835 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36837 Roo.tree.AsyncTreeNode = function(config){
36838 this.loaded = false;
36839 this.loading = false;
36840 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36842 * @event beforeload
36843 * Fires before this node is loaded, return false to cancel
36844 * @param {Node} this This node
36846 this.addEvents({'beforeload':true, 'load': true});
36849 * Fires when this node is loaded
36850 * @param {Node} this This node
36853 * The loader used by this node (defaults to using the tree's defined loader)
36858 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36859 expand : function(deep, anim, callback){
36860 if(this.loading){ // if an async load is already running, waiting til it's done
36862 var f = function(){
36863 if(!this.loading){ // done loading
36864 clearInterval(timer);
36865 this.expand(deep, anim, callback);
36867 }.createDelegate(this);
36868 timer = setInterval(f, 200);
36872 if(this.fireEvent("beforeload", this) === false){
36875 this.loading = true;
36876 this.ui.beforeLoad(this);
36877 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36879 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36883 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36887 * Returns true if this node is currently loading
36888 * @return {Boolean}
36890 isLoading : function(){
36891 return this.loading;
36894 loadComplete : function(deep, anim, callback){
36895 this.loading = false;
36896 this.loaded = true;
36897 this.ui.afterLoad(this);
36898 this.fireEvent("load", this);
36899 this.expand(deep, anim, callback);
36903 * Returns true if this node has been loaded
36904 * @return {Boolean}
36906 isLoaded : function(){
36907 return this.loaded;
36910 hasChildNodes : function(){
36911 if(!this.isLeaf() && !this.loaded){
36914 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36919 * Trigger a reload for this node
36920 * @param {Function} callback
36922 reload : function(callback){
36923 this.collapse(false, false);
36924 while(this.firstChild){
36925 this.removeChild(this.firstChild);
36927 this.childrenRendered = false;
36928 this.loaded = false;
36929 if(this.isHiddenRoot()){
36930 this.expanded = false;
36932 this.expand(false, false, callback);
36936 * Ext JS Library 1.1.1
36937 * Copyright(c) 2006-2007, Ext JS, LLC.
36939 * Originally Released Under LGPL - original licence link has changed is not relivant.
36942 * <script type="text/javascript">
36946 * @class Roo.tree.TreeNodeUI
36948 * @param {Object} node The node to render
36949 * The TreeNode UI implementation is separate from the
36950 * tree implementation. Unless you are customizing the tree UI,
36951 * you should never have to use this directly.
36953 Roo.tree.TreeNodeUI = function(node){
36955 this.rendered = false;
36956 this.animating = false;
36957 this.emptyIcon = Roo.BLANK_IMAGE_URL;
36960 Roo.tree.TreeNodeUI.prototype = {
36961 removeChild : function(node){
36963 this.ctNode.removeChild(node.ui.getEl());
36967 beforeLoad : function(){
36968 this.addClass("x-tree-node-loading");
36971 afterLoad : function(){
36972 this.removeClass("x-tree-node-loading");
36975 onTextChange : function(node, text, oldText){
36977 this.textNode.innerHTML = text;
36981 onDisableChange : function(node, state){
36982 this.disabled = state;
36984 this.addClass("x-tree-node-disabled");
36986 this.removeClass("x-tree-node-disabled");
36990 onSelectedChange : function(state){
36993 this.addClass("x-tree-selected");
36996 this.removeClass("x-tree-selected");
37000 onMove : function(tree, node, oldParent, newParent, index, refNode){
37001 this.childIndent = null;
37003 var targetNode = newParent.ui.getContainer();
37004 if(!targetNode){//target not rendered
37005 this.holder = document.createElement("div");
37006 this.holder.appendChild(this.wrap);
37009 var insertBefore = refNode ? refNode.ui.getEl() : null;
37011 targetNode.insertBefore(this.wrap, insertBefore);
37013 targetNode.appendChild(this.wrap);
37015 this.node.renderIndent(true);
37019 addClass : function(cls){
37021 Roo.fly(this.elNode).addClass(cls);
37025 removeClass : function(cls){
37027 Roo.fly(this.elNode).removeClass(cls);
37031 remove : function(){
37033 this.holder = document.createElement("div");
37034 this.holder.appendChild(this.wrap);
37038 fireEvent : function(){
37039 return this.node.fireEvent.apply(this.node, arguments);
37042 initEvents : function(){
37043 this.node.on("move", this.onMove, this);
37044 var E = Roo.EventManager;
37045 var a = this.anchor;
37047 var el = Roo.fly(a, '_treeui');
37049 if(Roo.isOpera){ // opera render bug ignores the CSS
37050 el.setStyle("text-decoration", "none");
37053 el.on("click", this.onClick, this);
37054 el.on("dblclick", this.onDblClick, this);
37057 Roo.EventManager.on(this.checkbox,
37058 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37061 el.on("contextmenu", this.onContextMenu, this);
37063 var icon = Roo.fly(this.iconNode);
37064 icon.on("click", this.onClick, this);
37065 icon.on("dblclick", this.onDblClick, this);
37066 icon.on("contextmenu", this.onContextMenu, this);
37067 E.on(this.ecNode, "click", this.ecClick, this, true);
37069 if(this.node.disabled){
37070 this.addClass("x-tree-node-disabled");
37072 if(this.node.hidden){
37073 this.addClass("x-tree-node-disabled");
37075 var ot = this.node.getOwnerTree();
37076 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37077 if(dd && (!this.node.isRoot || ot.rootVisible)){
37078 Roo.dd.Registry.register(this.elNode, {
37080 handles: this.getDDHandles(),
37086 getDDHandles : function(){
37087 return [this.iconNode, this.textNode];
37092 this.wrap.style.display = "none";
37098 this.wrap.style.display = "";
37102 onContextMenu : function(e){
37103 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37104 e.preventDefault();
37106 this.fireEvent("contextmenu", this.node, e);
37110 onClick : function(e){
37115 if(this.fireEvent("beforeclick", this.node, e) !== false){
37116 if(!this.disabled && this.node.attributes.href){
37117 this.fireEvent("click", this.node, e);
37120 e.preventDefault();
37125 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37126 this.node.toggle();
37129 this.fireEvent("click", this.node, e);
37135 onDblClick : function(e){
37136 e.preventDefault();
37141 this.toggleCheck();
37143 if(!this.animating && this.node.hasChildNodes()){
37144 this.node.toggle();
37146 this.fireEvent("dblclick", this.node, e);
37149 onCheckChange : function(){
37150 var checked = this.checkbox.checked;
37151 this.node.attributes.checked = checked;
37152 this.fireEvent('checkchange', this.node, checked);
37155 ecClick : function(e){
37156 if(!this.animating && this.node.hasChildNodes()){
37157 this.node.toggle();
37161 startDrop : function(){
37162 this.dropping = true;
37165 // delayed drop so the click event doesn't get fired on a drop
37166 endDrop : function(){
37167 setTimeout(function(){
37168 this.dropping = false;
37169 }.createDelegate(this), 50);
37172 expand : function(){
37173 this.updateExpandIcon();
37174 this.ctNode.style.display = "";
37177 focus : function(){
37178 if(!this.node.preventHScroll){
37179 try{this.anchor.focus();
37181 }else if(!Roo.isIE){
37183 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37184 var l = noscroll.scrollLeft;
37185 this.anchor.focus();
37186 noscroll.scrollLeft = l;
37191 toggleCheck : function(value){
37192 var cb = this.checkbox;
37194 cb.checked = (value === undefined ? !cb.checked : value);
37200 this.anchor.blur();
37204 animExpand : function(callback){
37205 var ct = Roo.get(this.ctNode);
37207 if(!this.node.hasChildNodes()){
37208 this.updateExpandIcon();
37209 this.ctNode.style.display = "";
37210 Roo.callback(callback);
37213 this.animating = true;
37214 this.updateExpandIcon();
37217 callback : function(){
37218 this.animating = false;
37219 Roo.callback(callback);
37222 duration: this.node.ownerTree.duration || .25
37226 highlight : function(){
37227 var tree = this.node.getOwnerTree();
37228 Roo.fly(this.wrap).highlight(
37229 tree.hlColor || "C3DAF9",
37230 {endColor: tree.hlBaseColor}
37234 collapse : function(){
37235 this.updateExpandIcon();
37236 this.ctNode.style.display = "none";
37239 animCollapse : function(callback){
37240 var ct = Roo.get(this.ctNode);
37241 ct.enableDisplayMode('block');
37244 this.animating = true;
37245 this.updateExpandIcon();
37248 callback : function(){
37249 this.animating = false;
37250 Roo.callback(callback);
37253 duration: this.node.ownerTree.duration || .25
37257 getContainer : function(){
37258 return this.ctNode;
37261 getEl : function(){
37265 appendDDGhost : function(ghostNode){
37266 ghostNode.appendChild(this.elNode.cloneNode(true));
37269 getDDRepairXY : function(){
37270 return Roo.lib.Dom.getXY(this.iconNode);
37273 onRender : function(){
37277 render : function(bulkRender){
37278 var n = this.node, a = n.attributes;
37279 var targetNode = n.parentNode ?
37280 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37282 if(!this.rendered){
37283 this.rendered = true;
37285 this.renderElements(n, a, targetNode, bulkRender);
37288 if(this.textNode.setAttributeNS){
37289 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37291 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37294 this.textNode.setAttribute("ext:qtip", a.qtip);
37296 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37299 }else if(a.qtipCfg){
37300 a.qtipCfg.target = Roo.id(this.textNode);
37301 Roo.QuickTips.register(a.qtipCfg);
37304 if(!this.node.expanded){
37305 this.updateExpandIcon();
37308 if(bulkRender === true) {
37309 targetNode.appendChild(this.wrap);
37314 renderElements : function(n, a, targetNode, bulkRender)
37316 // add some indent caching, this helps performance when rendering a large tree
37317 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37318 var t = n.getOwnerTree();
37319 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37320 if (typeof(n.attributes.html) != 'undefined') {
37321 txt = n.attributes.html;
37323 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37324 var cb = typeof a.checked == 'boolean';
37325 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37326 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37327 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37328 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37329 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37330 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37331 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37332 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
37333 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37334 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37337 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37338 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37339 n.nextSibling.ui.getEl(), buf.join(""));
37341 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37344 this.elNode = this.wrap.childNodes[0];
37345 this.ctNode = this.wrap.childNodes[1];
37346 var cs = this.elNode.childNodes;
37347 this.indentNode = cs[0];
37348 this.ecNode = cs[1];
37349 this.iconNode = cs[2];
37352 this.checkbox = cs[3];
37355 this.anchor = cs[index];
37356 this.textNode = cs[index].firstChild;
37359 getAnchor : function(){
37360 return this.anchor;
37363 getTextEl : function(){
37364 return this.textNode;
37367 getIconEl : function(){
37368 return this.iconNode;
37371 isChecked : function(){
37372 return this.checkbox ? this.checkbox.checked : false;
37375 updateExpandIcon : function(){
37377 var n = this.node, c1, c2;
37378 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37379 var hasChild = n.hasChildNodes();
37383 c1 = "x-tree-node-collapsed";
37384 c2 = "x-tree-node-expanded";
37387 c1 = "x-tree-node-expanded";
37388 c2 = "x-tree-node-collapsed";
37391 this.removeClass("x-tree-node-leaf");
37392 this.wasLeaf = false;
37394 if(this.c1 != c1 || this.c2 != c2){
37395 Roo.fly(this.elNode).replaceClass(c1, c2);
37396 this.c1 = c1; this.c2 = c2;
37399 // this changes non-leafs into leafs if they have no children.
37400 // it's not very rational behaviour..
37402 if(!this.wasLeaf && this.node.leaf){
37403 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37406 this.wasLeaf = true;
37409 var ecc = "x-tree-ec-icon "+cls;
37410 if(this.ecc != ecc){
37411 this.ecNode.className = ecc;
37417 getChildIndent : function(){
37418 if(!this.childIndent){
37422 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37424 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37426 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37431 this.childIndent = buf.join("");
37433 return this.childIndent;
37436 renderIndent : function(){
37439 var p = this.node.parentNode;
37441 indent = p.ui.getChildIndent();
37443 if(this.indentMarkup != indent){ // don't rerender if not required
37444 this.indentNode.innerHTML = indent;
37445 this.indentMarkup = indent;
37447 this.updateExpandIcon();
37452 Roo.tree.RootTreeNodeUI = function(){
37453 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37455 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37456 render : function(){
37457 if(!this.rendered){
37458 var targetNode = this.node.ownerTree.innerCt.dom;
37459 this.node.expanded = true;
37460 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37461 this.wrap = this.ctNode = targetNode.firstChild;
37464 collapse : function(){
37466 expand : function(){
37470 * Ext JS Library 1.1.1
37471 * Copyright(c) 2006-2007, Ext JS, LLC.
37473 * Originally Released Under LGPL - original licence link has changed is not relivant.
37476 * <script type="text/javascript">
37479 * @class Roo.tree.TreeLoader
37480 * @extends Roo.util.Observable
37481 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37482 * nodes from a specified URL. The response must be a javascript Array definition
37483 * who's elements are node definition objects. eg:
37488 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37489 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37496 * The old style respose with just an array is still supported, but not recommended.
37499 * A server request is sent, and child nodes are loaded only when a node is expanded.
37500 * The loading node's id is passed to the server under the parameter name "node" to
37501 * enable the server to produce the correct child nodes.
37503 * To pass extra parameters, an event handler may be attached to the "beforeload"
37504 * event, and the parameters specified in the TreeLoader's baseParams property:
37506 myTreeLoader.on("beforeload", function(treeLoader, node) {
37507 this.baseParams.category = node.attributes.category;
37512 * This would pass an HTTP parameter called "category" to the server containing
37513 * the value of the Node's "category" attribute.
37515 * Creates a new Treeloader.
37516 * @param {Object} config A config object containing config properties.
37518 Roo.tree.TreeLoader = function(config){
37519 this.baseParams = {};
37520 this.requestMethod = "POST";
37521 Roo.apply(this, config);
37526 * @event beforeload
37527 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37528 * @param {Object} This TreeLoader object.
37529 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37530 * @param {Object} callback The callback function specified in the {@link #load} call.
37535 * Fires when the node has been successfuly loaded.
37536 * @param {Object} This TreeLoader object.
37537 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37538 * @param {Object} response The response object containing the data from the server.
37542 * @event loadexception
37543 * Fires if the network request failed.
37544 * @param {Object} This TreeLoader object.
37545 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37546 * @param {Object} response The response object containing the data from the server.
37548 loadexception : true,
37551 * Fires before a node is created, enabling you to return custom Node types
37552 * @param {Object} This TreeLoader object.
37553 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37558 Roo.tree.TreeLoader.superclass.constructor.call(this);
37561 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37563 * @cfg {String} dataUrl The URL from which to request a Json string which
37564 * specifies an array of node definition object representing the child nodes
37568 * @cfg {String} requestMethod either GET or POST
37569 * defaults to POST (due to BC)
37573 * @cfg {Object} baseParams (optional) An object containing properties which
37574 * specify HTTP parameters to be passed to each request for child nodes.
37577 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37578 * created by this loader. If the attributes sent by the server have an attribute in this object,
37579 * they take priority.
37582 * @cfg {Object} uiProviders (optional) An object containing properties which
37584 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37585 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37586 * <i>uiProvider</i> attribute of a returned child node is a string rather
37587 * than a reference to a TreeNodeUI implementation, this that string value
37588 * is used as a property name in the uiProviders object. You can define the provider named
37589 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37594 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37595 * child nodes before loading.
37597 clearOnLoad : true,
37600 * @cfg {String} root (optional) Default to false. Use this to read data from an object
37601 * property on loading, rather than expecting an array. (eg. more compatible to a standard
37602 * Grid query { data : [ .....] }
37607 * @cfg {String} queryParam (optional)
37608 * Name of the query as it will be passed on the querystring (defaults to 'node')
37609 * eg. the request will be ?node=[id]
37616 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37617 * This is called automatically when a node is expanded, but may be used to reload
37618 * a node (or append new children if the {@link #clearOnLoad} option is false.)
37619 * @param {Roo.tree.TreeNode} node
37620 * @param {Function} callback
37622 load : function(node, callback){
37623 if(this.clearOnLoad){
37624 while(node.firstChild){
37625 node.removeChild(node.firstChild);
37628 if(node.attributes.children){ // preloaded json children
37629 var cs = node.attributes.children;
37630 for(var i = 0, len = cs.length; i < len; i++){
37631 node.appendChild(this.createNode(cs[i]));
37633 if(typeof callback == "function"){
37636 }else if(this.dataUrl){
37637 this.requestData(node, callback);
37641 getParams: function(node){
37642 var buf = [], bp = this.baseParams;
37643 for(var key in bp){
37644 if(typeof bp[key] != "function"){
37645 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37648 var n = this.queryParam === false ? 'node' : this.queryParam;
37649 buf.push(n + "=", encodeURIComponent(node.id));
37650 return buf.join("");
37653 requestData : function(node, callback){
37654 if(this.fireEvent("beforeload", this, node, callback) !== false){
37655 this.transId = Roo.Ajax.request({
37656 method:this.requestMethod,
37657 url: this.dataUrl||this.url,
37658 success: this.handleResponse,
37659 failure: this.handleFailure,
37661 argument: {callback: callback, node: node},
37662 params: this.getParams(node)
37665 // if the load is cancelled, make sure we notify
37666 // the node that we are done
37667 if(typeof callback == "function"){
37673 isLoading : function(){
37674 return this.transId ? true : false;
37677 abort : function(){
37678 if(this.isLoading()){
37679 Roo.Ajax.abort(this.transId);
37684 createNode : function(attr)
37686 // apply baseAttrs, nice idea Corey!
37687 if(this.baseAttrs){
37688 Roo.applyIf(attr, this.baseAttrs);
37690 if(this.applyLoader !== false){
37691 attr.loader = this;
37693 // uiProvider = depreciated..
37695 if(typeof(attr.uiProvider) == 'string'){
37696 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
37697 /** eval:var:attr */ eval(attr.uiProvider);
37699 if(typeof(this.uiProviders['default']) != 'undefined') {
37700 attr.uiProvider = this.uiProviders['default'];
37703 this.fireEvent('create', this, attr);
37705 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37707 new Roo.tree.TreeNode(attr) :
37708 new Roo.tree.AsyncTreeNode(attr));
37711 processResponse : function(response, node, callback)
37713 var json = response.responseText;
37716 var o = Roo.decode(json);
37718 if (this.root === false && typeof(o.success) != undefined) {
37719 this.root = 'data'; // the default behaviour for list like data..
37722 if (this.root !== false && !o.success) {
37723 // it's a failure condition.
37724 var a = response.argument;
37725 this.fireEvent("loadexception", this, a.node, response);
37726 Roo.log("Load failed - should have a handler really");
37732 if (this.root !== false) {
37736 for(var i = 0, len = o.length; i < len; i++){
37737 var n = this.createNode(o[i]);
37739 node.appendChild(n);
37742 if(typeof callback == "function"){
37743 callback(this, node);
37746 this.handleFailure(response);
37750 handleResponse : function(response){
37751 this.transId = false;
37752 var a = response.argument;
37753 this.processResponse(response, a.node, a.callback);
37754 this.fireEvent("load", this, a.node, response);
37757 handleFailure : function(response)
37759 // should handle failure better..
37760 this.transId = false;
37761 var a = response.argument;
37762 this.fireEvent("loadexception", this, a.node, response);
37763 if(typeof a.callback == "function"){
37764 a.callback(this, a.node);
37769 * Ext JS Library 1.1.1
37770 * Copyright(c) 2006-2007, Ext JS, LLC.
37772 * Originally Released Under LGPL - original licence link has changed is not relivant.
37775 * <script type="text/javascript">
37779 * @class Roo.tree.TreeFilter
37780 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37781 * @param {TreePanel} tree
37782 * @param {Object} config (optional)
37784 Roo.tree.TreeFilter = function(tree, config){
37786 this.filtered = {};
37787 Roo.apply(this, config);
37790 Roo.tree.TreeFilter.prototype = {
37797 * Filter the data by a specific attribute.
37798 * @param {String/RegExp} value Either string that the attribute value
37799 * should start with or a RegExp to test against the attribute
37800 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37801 * @param {TreeNode} startNode (optional) The node to start the filter at.
37803 filter : function(value, attr, startNode){
37804 attr = attr || "text";
37806 if(typeof value == "string"){
37807 var vlen = value.length;
37808 // auto clear empty filter
37809 if(vlen == 0 && this.clearBlank){
37813 value = value.toLowerCase();
37815 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37817 }else if(value.exec){ // regex?
37819 return value.test(n.attributes[attr]);
37822 throw 'Illegal filter type, must be string or regex';
37824 this.filterBy(f, null, startNode);
37828 * Filter by a function. The passed function will be called with each
37829 * node in the tree (or from the startNode). If the function returns true, the node is kept
37830 * otherwise it is filtered. If a node is filtered, its children are also filtered.
37831 * @param {Function} fn The filter function
37832 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37834 filterBy : function(fn, scope, startNode){
37835 startNode = startNode || this.tree.root;
37836 if(this.autoClear){
37839 var af = this.filtered, rv = this.reverse;
37840 var f = function(n){
37841 if(n == startNode){
37847 var m = fn.call(scope || n, n);
37855 startNode.cascade(f);
37858 if(typeof id != "function"){
37860 if(n && n.parentNode){
37861 n.parentNode.removeChild(n);
37869 * Clears the current filter. Note: with the "remove" option
37870 * set a filter cannot be cleared.
37872 clear : function(){
37874 var af = this.filtered;
37876 if(typeof id != "function"){
37883 this.filtered = {};
37888 * Ext JS Library 1.1.1
37889 * Copyright(c) 2006-2007, Ext JS, LLC.
37891 * Originally Released Under LGPL - original licence link has changed is not relivant.
37894 * <script type="text/javascript">
37899 * @class Roo.tree.TreeSorter
37900 * Provides sorting of nodes in a TreePanel
37902 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37903 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37904 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37905 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37906 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37907 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37909 * @param {TreePanel} tree
37910 * @param {Object} config
37912 Roo.tree.TreeSorter = function(tree, config){
37913 Roo.apply(this, config);
37914 tree.on("beforechildrenrendered", this.doSort, this);
37915 tree.on("append", this.updateSort, this);
37916 tree.on("insert", this.updateSort, this);
37918 var dsc = this.dir && this.dir.toLowerCase() == "desc";
37919 var p = this.property || "text";
37920 var sortType = this.sortType;
37921 var fs = this.folderSort;
37922 var cs = this.caseSensitive === true;
37923 var leafAttr = this.leafAttr || 'leaf';
37925 this.sortFn = function(n1, n2){
37927 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37930 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37934 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37935 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37937 return dsc ? +1 : -1;
37939 return dsc ? -1 : +1;
37946 Roo.tree.TreeSorter.prototype = {
37947 doSort : function(node){
37948 node.sort(this.sortFn);
37951 compareNodes : function(n1, n2){
37952 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37955 updateSort : function(tree, node){
37956 if(node.childrenRendered){
37957 this.doSort.defer(1, this, [node]);
37962 * Ext JS Library 1.1.1
37963 * Copyright(c) 2006-2007, Ext JS, LLC.
37965 * Originally Released Under LGPL - original licence link has changed is not relivant.
37968 * <script type="text/javascript">
37971 if(Roo.dd.DropZone){
37973 Roo.tree.TreeDropZone = function(tree, config){
37974 this.allowParentInsert = false;
37975 this.allowContainerDrop = false;
37976 this.appendOnly = false;
37977 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37979 this.lastInsertClass = "x-tree-no-status";
37980 this.dragOverData = {};
37983 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37984 ddGroup : "TreeDD",
37987 expandDelay : 1000,
37989 expandNode : function(node){
37990 if(node.hasChildNodes() && !node.isExpanded()){
37991 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37995 queueExpand : function(node){
37996 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37999 cancelExpand : function(){
38000 if(this.expandProcId){
38001 clearTimeout(this.expandProcId);
38002 this.expandProcId = false;
38006 isValidDropPoint : function(n, pt, dd, e, data){
38007 if(!n || !data){ return false; }
38008 var targetNode = n.node;
38009 var dropNode = data.node;
38010 // default drop rules
38011 if(!(targetNode && targetNode.isTarget && pt)){
38014 if(pt == "append" && targetNode.allowChildren === false){
38017 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38020 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38023 // reuse the object
38024 var overEvent = this.dragOverData;
38025 overEvent.tree = this.tree;
38026 overEvent.target = targetNode;
38027 overEvent.data = data;
38028 overEvent.point = pt;
38029 overEvent.source = dd;
38030 overEvent.rawEvent = e;
38031 overEvent.dropNode = dropNode;
38032 overEvent.cancel = false;
38033 var result = this.tree.fireEvent("nodedragover", overEvent);
38034 return overEvent.cancel === false && result !== false;
38037 getDropPoint : function(e, n, dd)
38041 return tn.allowChildren !== false ? "append" : false; // always append for root
38043 var dragEl = n.ddel;
38044 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38045 var y = Roo.lib.Event.getPageY(e);
38046 //var noAppend = tn.allowChildren === false || tn.isLeaf();
38048 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38049 var noAppend = tn.allowChildren === false;
38050 if(this.appendOnly || tn.parentNode.allowChildren === false){
38051 return noAppend ? false : "append";
38053 var noBelow = false;
38054 if(!this.allowParentInsert){
38055 noBelow = tn.hasChildNodes() && tn.isExpanded();
38057 var q = (b - t) / (noAppend ? 2 : 3);
38058 if(y >= t && y < (t + q)){
38060 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38067 onNodeEnter : function(n, dd, e, data)
38069 this.cancelExpand();
38072 onNodeOver : function(n, dd, e, data)
38075 var pt = this.getDropPoint(e, n, dd);
38078 // auto node expand check
38079 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38080 this.queueExpand(node);
38081 }else if(pt != "append"){
38082 this.cancelExpand();
38085 // set the insert point style on the target node
38086 var returnCls = this.dropNotAllowed;
38087 if(this.isValidDropPoint(n, pt, dd, e, data)){
38092 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38093 cls = "x-tree-drag-insert-above";
38094 }else if(pt == "below"){
38095 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38096 cls = "x-tree-drag-insert-below";
38098 returnCls = "x-tree-drop-ok-append";
38099 cls = "x-tree-drag-append";
38101 if(this.lastInsertClass != cls){
38102 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38103 this.lastInsertClass = cls;
38110 onNodeOut : function(n, dd, e, data){
38112 this.cancelExpand();
38113 this.removeDropIndicators(n);
38116 onNodeDrop : function(n, dd, e, data){
38117 var point = this.getDropPoint(e, n, dd);
38118 var targetNode = n.node;
38119 targetNode.ui.startDrop();
38120 if(!this.isValidDropPoint(n, point, dd, e, data)){
38121 targetNode.ui.endDrop();
38124 // first try to find the drop node
38125 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38128 target: targetNode,
38133 dropNode: dropNode,
38136 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38137 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38138 targetNode.ui.endDrop();
38141 // allow target changing
38142 targetNode = dropEvent.target;
38143 if(point == "append" && !targetNode.isExpanded()){
38144 targetNode.expand(false, null, function(){
38145 this.completeDrop(dropEvent);
38146 }.createDelegate(this));
38148 this.completeDrop(dropEvent);
38153 completeDrop : function(de){
38154 var ns = de.dropNode, p = de.point, t = de.target;
38155 if(!(ns instanceof Array)){
38159 for(var i = 0, len = ns.length; i < len; i++){
38162 t.parentNode.insertBefore(n, t);
38163 }else if(p == "below"){
38164 t.parentNode.insertBefore(n, t.nextSibling);
38170 if(this.tree.hlDrop){
38174 this.tree.fireEvent("nodedrop", de);
38177 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38178 if(this.tree.hlDrop){
38179 dropNode.ui.focus();
38180 dropNode.ui.highlight();
38182 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38185 getTree : function(){
38189 removeDropIndicators : function(n){
38192 Roo.fly(el).removeClass([
38193 "x-tree-drag-insert-above",
38194 "x-tree-drag-insert-below",
38195 "x-tree-drag-append"]);
38196 this.lastInsertClass = "_noclass";
38200 beforeDragDrop : function(target, e, id){
38201 this.cancelExpand();
38205 afterRepair : function(data){
38206 if(data && Roo.enableFx){
38207 data.node.ui.highlight();
38217 * Ext JS Library 1.1.1
38218 * Copyright(c) 2006-2007, Ext JS, LLC.
38220 * Originally Released Under LGPL - original licence link has changed is not relivant.
38223 * <script type="text/javascript">
38227 if(Roo.dd.DragZone){
38228 Roo.tree.TreeDragZone = function(tree, config){
38229 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38233 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38234 ddGroup : "TreeDD",
38236 onBeforeDrag : function(data, e){
38238 return n && n.draggable && !n.disabled;
38242 onInitDrag : function(e){
38243 var data = this.dragData;
38244 this.tree.getSelectionModel().select(data.node);
38245 this.proxy.update("");
38246 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38247 this.tree.fireEvent("startdrag", this.tree, data.node, e);
38250 getRepairXY : function(e, data){
38251 return data.node.ui.getDDRepairXY();
38254 onEndDrag : function(data, e){
38255 this.tree.fireEvent("enddrag", this.tree, data.node, e);
38260 onValidDrop : function(dd, e, id){
38261 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38265 beforeInvalidDrop : function(e, id){
38266 // this scrolls the original position back into view
38267 var sm = this.tree.getSelectionModel();
38268 sm.clearSelections();
38269 sm.select(this.dragData.node);
38274 * Ext JS Library 1.1.1
38275 * Copyright(c) 2006-2007, Ext JS, LLC.
38277 * Originally Released Under LGPL - original licence link has changed is not relivant.
38280 * <script type="text/javascript">
38283 * @class Roo.tree.TreeEditor
38284 * @extends Roo.Editor
38285 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
38286 * as the editor field.
38288 * @param {Object} config (used to be the tree panel.)
38289 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38291 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38292 * @cfg {Roo.form.TextField} field [required] The field configuration
38296 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38299 if (oldconfig) { // old style..
38300 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38303 tree = config.tree;
38304 config.field = config.field || {};
38305 config.field.xtype = 'TextField';
38306 field = Roo.factory(config.field, Roo.form);
38308 config = config || {};
38313 * @event beforenodeedit
38314 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
38315 * false from the handler of this event.
38316 * @param {Editor} this
38317 * @param {Roo.tree.Node} node
38319 "beforenodeedit" : true
38323 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38327 tree.on('beforeclick', this.beforeNodeClick, this);
38328 tree.getTreeEl().on('mousedown', this.hide, this);
38329 this.on('complete', this.updateNode, this);
38330 this.on('beforestartedit', this.fitToTree, this);
38331 this.on('startedit', this.bindScroll, this, {delay:10});
38332 this.on('specialkey', this.onSpecialKey, this);
38335 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38337 * @cfg {String} alignment
38338 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38344 * @cfg {Boolean} hideEl
38345 * True to hide the bound element while the editor is displayed (defaults to false)
38349 * @cfg {String} cls
38350 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38352 cls: "x-small-editor x-tree-editor",
38354 * @cfg {Boolean} shim
38355 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38361 * @cfg {Number} maxWidth
38362 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
38363 * the containing tree element's size, it will be automatically limited for you to the container width, taking
38364 * scroll and client offsets into account prior to each edit.
38371 fitToTree : function(ed, el){
38372 var td = this.tree.getTreeEl().dom, nd = el.dom;
38373 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
38374 td.scrollLeft = nd.offsetLeft;
38378 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38379 this.setSize(w, '');
38381 return this.fireEvent('beforenodeedit', this, this.editNode);
38386 triggerEdit : function(node){
38387 this.completeEdit();
38388 this.editNode = node;
38389 this.startEdit(node.ui.textNode, node.text);
38393 bindScroll : function(){
38394 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38398 beforeNodeClick : function(node, e){
38399 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38400 this.lastClick = new Date();
38401 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38403 this.triggerEdit(node);
38410 updateNode : function(ed, value){
38411 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38412 this.editNode.setText(value);
38416 onHide : function(){
38417 Roo.tree.TreeEditor.superclass.onHide.call(this);
38419 this.editNode.ui.focus();
38424 onSpecialKey : function(field, e){
38425 var k = e.getKey();
38429 }else if(k == e.ENTER && !e.hasModifier()){
38431 this.completeEdit();
38434 });//<Script type="text/javascript">
38437 * Ext JS Library 1.1.1
38438 * Copyright(c) 2006-2007, Ext JS, LLC.
38440 * Originally Released Under LGPL - original licence link has changed is not relivant.
38443 * <script type="text/javascript">
38447 * Not documented??? - probably should be...
38450 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38451 //focus: Roo.emptyFn, // prevent odd scrolling behavior
38453 renderElements : function(n, a, targetNode, bulkRender){
38454 //consel.log("renderElements?");
38455 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38457 var t = n.getOwnerTree();
38458 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38460 var cols = t.columns;
38461 var bw = t.borderWidth;
38463 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38464 var cb = typeof a.checked == "boolean";
38465 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38466 var colcls = 'x-t-' + tid + '-c0';
38468 '<li class="x-tree-node">',
38471 '<div class="x-tree-node-el ', a.cls,'">',
38473 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38476 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38477 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
38478 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38479 (a.icon ? ' x-tree-node-inline-icon' : ''),
38480 (a.iconCls ? ' '+a.iconCls : ''),
38481 '" unselectable="on" />',
38482 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
38483 (a.checked ? 'checked="checked" />' : ' />')) : ''),
38485 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38486 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38487 '<span unselectable="on" qtip="' + tx + '">',
38491 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38492 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38494 for(var i = 1, len = cols.length; i < len; i++){
38496 colcls = 'x-t-' + tid + '-c' +i;
38497 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38498 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38499 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38505 '<div class="x-clear"></div></div>',
38506 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38509 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38510 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38511 n.nextSibling.ui.getEl(), buf.join(""));
38513 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38515 var el = this.wrap.firstChild;
38517 this.elNode = el.firstChild;
38518 this.ranchor = el.childNodes[1];
38519 this.ctNode = this.wrap.childNodes[1];
38520 var cs = el.firstChild.childNodes;
38521 this.indentNode = cs[0];
38522 this.ecNode = cs[1];
38523 this.iconNode = cs[2];
38526 this.checkbox = cs[3];
38529 this.anchor = cs[index];
38531 this.textNode = cs[index].firstChild;
38533 //el.on("click", this.onClick, this);
38534 //el.on("dblclick", this.onDblClick, this);
38537 // console.log(this);
38539 initEvents : function(){
38540 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38543 var a = this.ranchor;
38545 var el = Roo.get(a);
38547 if(Roo.isOpera){ // opera render bug ignores the CSS
38548 el.setStyle("text-decoration", "none");
38551 el.on("click", this.onClick, this);
38552 el.on("dblclick", this.onDblClick, this);
38553 el.on("contextmenu", this.onContextMenu, this);
38557 /*onSelectedChange : function(state){
38560 this.addClass("x-tree-selected");
38563 this.removeClass("x-tree-selected");
38566 addClass : function(cls){
38568 Roo.fly(this.elRow).addClass(cls);
38574 removeClass : function(cls){
38576 Roo.fly(this.elRow).removeClass(cls);
38582 });//<Script type="text/javascript">
38586 * Ext JS Library 1.1.1
38587 * Copyright(c) 2006-2007, Ext JS, LLC.
38589 * Originally Released Under LGPL - original licence link has changed is not relivant.
38592 * <script type="text/javascript">
38597 * @class Roo.tree.ColumnTree
38598 * @extends Roo.tree.TreePanel
38599 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
38600 * @cfg {int} borderWidth compined right/left border allowance
38602 * @param {String/HTMLElement/Element} el The container element
38603 * @param {Object} config
38605 Roo.tree.ColumnTree = function(el, config)
38607 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38611 * Fire this event on a container when it resizes
38612 * @param {int} w Width
38613 * @param {int} h Height
38617 this.on('resize', this.onResize, this);
38620 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38624 borderWidth: Roo.isBorderBox ? 0 : 2,
38627 render : function(){
38628 // add the header.....
38630 Roo.tree.ColumnTree.superclass.render.apply(this);
38632 this.el.addClass('x-column-tree');
38634 this.headers = this.el.createChild(
38635 {cls:'x-tree-headers'},this.innerCt.dom);
38637 var cols = this.columns, c;
38638 var totalWidth = 0;
38640 var len = cols.length;
38641 for(var i = 0; i < len; i++){
38643 totalWidth += c.width;
38644 this.headEls.push(this.headers.createChild({
38645 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38647 cls:'x-tree-hd-text',
38650 style:'width:'+(c.width-this.borderWidth)+'px;'
38653 this.headers.createChild({cls:'x-clear'});
38654 // prevent floats from wrapping when clipped
38655 this.headers.setWidth(totalWidth);
38656 //this.innerCt.setWidth(totalWidth);
38657 this.innerCt.setStyle({ overflow: 'auto' });
38658 this.onResize(this.width, this.height);
38662 onResize : function(w,h)
38667 this.innerCt.setWidth(this.width);
38668 this.innerCt.setHeight(this.height-20);
38671 var cols = this.columns, c;
38672 var totalWidth = 0;
38674 var len = cols.length;
38675 for(var i = 0; i < len; i++){
38677 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38678 // it's the expander..
38679 expEl = this.headEls[i];
38682 totalWidth += c.width;
38686 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
38688 this.headers.setWidth(w-20);
38697 * Ext JS Library 1.1.1
38698 * Copyright(c) 2006-2007, Ext JS, LLC.
38700 * Originally Released Under LGPL - original licence link has changed is not relivant.
38703 * <script type="text/javascript">
38707 * @class Roo.menu.Menu
38708 * @extends Roo.util.Observable
38709 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38710 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
38711 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38713 * Creates a new Menu
38714 * @param {Object} config Configuration options
38716 Roo.menu.Menu = function(config){
38718 Roo.menu.Menu.superclass.constructor.call(this, config);
38720 this.id = this.id || Roo.id();
38723 * @event beforeshow
38724 * Fires before this menu is displayed
38725 * @param {Roo.menu.Menu} this
38729 * @event beforehide
38730 * Fires before this menu is hidden
38731 * @param {Roo.menu.Menu} this
38736 * Fires after this menu is displayed
38737 * @param {Roo.menu.Menu} this
38742 * Fires after this menu is hidden
38743 * @param {Roo.menu.Menu} this
38748 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38749 * @param {Roo.menu.Menu} this
38750 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38751 * @param {Roo.EventObject} e
38756 * Fires when the mouse is hovering over this menu
38757 * @param {Roo.menu.Menu} this
38758 * @param {Roo.EventObject} e
38759 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38764 * Fires when the mouse exits this menu
38765 * @param {Roo.menu.Menu} this
38766 * @param {Roo.EventObject} e
38767 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38772 * Fires when a menu item contained in this menu is clicked
38773 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38774 * @param {Roo.EventObject} e
38778 if (this.registerMenu) {
38779 Roo.menu.MenuMgr.register(this);
38782 var mis = this.items;
38783 this.items = new Roo.util.MixedCollection();
38785 this.add.apply(this, mis);
38789 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38791 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38795 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38796 * for bottom-right shadow (defaults to "sides")
38800 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38801 * this menu (defaults to "tl-tr?")
38803 subMenuAlign : "tl-tr?",
38805 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38806 * relative to its element of origin (defaults to "tl-bl?")
38808 defaultAlign : "tl-bl?",
38810 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38812 allowOtherMenus : false,
38814 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38816 registerMenu : true,
38821 render : function(){
38825 var el = this.el = new Roo.Layer({
38827 shadow:this.shadow,
38829 parentEl: this.parentEl || document.body,
38833 this.keyNav = new Roo.menu.MenuNav(this);
38836 el.addClass("x-menu-plain");
38839 el.addClass(this.cls);
38841 // generic focus element
38842 this.focusEl = el.createChild({
38843 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38845 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38846 //disabling touch- as it's causing issues ..
38847 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
38848 ul.on('click' , this.onClick, this);
38851 ul.on("mouseover", this.onMouseOver, this);
38852 ul.on("mouseout", this.onMouseOut, this);
38853 this.items.each(function(item){
38858 var li = document.createElement("li");
38859 li.className = "x-menu-list-item";
38860 ul.dom.appendChild(li);
38861 item.render(li, this);
38868 autoWidth : function(){
38869 var el = this.el, ul = this.ul;
38873 var w = this.width;
38876 }else if(Roo.isIE){
38877 el.setWidth(this.minWidth);
38878 var t = el.dom.offsetWidth; // force recalc
38879 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38884 delayAutoWidth : function(){
38887 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38889 this.awTask.delay(20);
38894 findTargetItem : function(e){
38895 var t = e.getTarget(".x-menu-list-item", this.ul, true);
38896 if(t && t.menuItemId){
38897 return this.items.get(t.menuItemId);
38902 onClick : function(e){
38903 Roo.log("menu.onClick");
38904 var t = this.findTargetItem(e);
38909 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
38910 if(t == this.activeItem && t.shouldDeactivate(e)){
38911 this.activeItem.deactivate();
38912 delete this.activeItem;
38916 this.setActiveItem(t, true);
38924 this.fireEvent("click", this, t, e);
38928 setActiveItem : function(item, autoExpand){
38929 if(item != this.activeItem){
38930 if(this.activeItem){
38931 this.activeItem.deactivate();
38933 this.activeItem = item;
38934 item.activate(autoExpand);
38935 }else if(autoExpand){
38941 tryActivate : function(start, step){
38942 var items = this.items;
38943 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38944 var item = items.get(i);
38945 if(!item.disabled && item.canActivate){
38946 this.setActiveItem(item, false);
38954 onMouseOver : function(e){
38956 if(t = this.findTargetItem(e)){
38957 if(t.canActivate && !t.disabled){
38958 this.setActiveItem(t, true);
38961 this.fireEvent("mouseover", this, e, t);
38965 onMouseOut : function(e){
38967 if(t = this.findTargetItem(e)){
38968 if(t == this.activeItem && t.shouldDeactivate(e)){
38969 this.activeItem.deactivate();
38970 delete this.activeItem;
38973 this.fireEvent("mouseout", this, e, t);
38977 * Read-only. Returns true if the menu is currently displayed, else false.
38980 isVisible : function(){
38981 return this.el && !this.hidden;
38985 * Displays this menu relative to another element
38986 * @param {String/HTMLElement/Roo.Element} element The element to align to
38987 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38988 * the element (defaults to this.defaultAlign)
38989 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38991 show : function(el, pos, parentMenu){
38992 this.parentMenu = parentMenu;
38996 this.fireEvent("beforeshow", this);
38997 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39001 * Displays this menu at a specific xy position
39002 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39003 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39005 showAt : function(xy, parentMenu, /* private: */_e){
39006 this.parentMenu = parentMenu;
39011 this.fireEvent("beforeshow", this);
39012 xy = this.el.adjustForConstraints(xy);
39016 this.hidden = false;
39018 this.fireEvent("show", this);
39021 focus : function(){
39023 this.doFocus.defer(50, this);
39027 doFocus : function(){
39029 this.focusEl.focus();
39034 * Hides this menu and optionally all parent menus
39035 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39037 hide : function(deep){
39038 if(this.el && this.isVisible()){
39039 this.fireEvent("beforehide", this);
39040 if(this.activeItem){
39041 this.activeItem.deactivate();
39042 this.activeItem = null;
39045 this.hidden = true;
39046 this.fireEvent("hide", this);
39048 if(deep === true && this.parentMenu){
39049 this.parentMenu.hide(true);
39054 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39055 * Any of the following are valid:
39057 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39058 * <li>An HTMLElement object which will be converted to a menu item</li>
39059 * <li>A menu item config object that will be created as a new menu item</li>
39060 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39061 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39066 var menu = new Roo.menu.Menu();
39068 // Create a menu item to add by reference
39069 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39071 // Add a bunch of items at once using different methods.
39072 // Only the last item added will be returned.
39073 var item = menu.add(
39074 menuItem, // add existing item by ref
39075 'Dynamic Item', // new TextItem
39076 '-', // new separator
39077 { text: 'Config Item' } // new item by config
39080 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39081 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39084 var a = arguments, l = a.length, item;
39085 for(var i = 0; i < l; i++){
39087 if ((typeof(el) == "object") && el.xtype && el.xns) {
39088 el = Roo.factory(el, Roo.menu);
39091 if(el.render){ // some kind of Item
39092 item = this.addItem(el);
39093 }else if(typeof el == "string"){ // string
39094 if(el == "separator" || el == "-"){
39095 item = this.addSeparator();
39097 item = this.addText(el);
39099 }else if(el.tagName || el.el){ // element
39100 item = this.addElement(el);
39101 }else if(typeof el == "object"){ // must be menu item config?
39102 item = this.addMenuItem(el);
39109 * Returns this menu's underlying {@link Roo.Element} object
39110 * @return {Roo.Element} The element
39112 getEl : function(){
39120 * Adds a separator bar to the menu
39121 * @return {Roo.menu.Item} The menu item that was added
39123 addSeparator : function(){
39124 return this.addItem(new Roo.menu.Separator());
39128 * Adds an {@link Roo.Element} object to the menu
39129 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39130 * @return {Roo.menu.Item} The menu item that was added
39132 addElement : function(el){
39133 return this.addItem(new Roo.menu.BaseItem(el));
39137 * Adds an existing object based on {@link Roo.menu.Item} to the menu
39138 * @param {Roo.menu.Item} item The menu item to add
39139 * @return {Roo.menu.Item} The menu item that was added
39141 addItem : function(item){
39142 this.items.add(item);
39144 var li = document.createElement("li");
39145 li.className = "x-menu-list-item";
39146 this.ul.dom.appendChild(li);
39147 item.render(li, this);
39148 this.delayAutoWidth();
39154 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39155 * @param {Object} config A MenuItem config object
39156 * @return {Roo.menu.Item} The menu item that was added
39158 addMenuItem : function(config){
39159 if(!(config instanceof Roo.menu.Item)){
39160 if(typeof config.checked == "boolean"){ // must be check menu item config?
39161 config = new Roo.menu.CheckItem(config);
39163 config = new Roo.menu.Item(config);
39166 return this.addItem(config);
39170 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39171 * @param {String} text The text to display in the menu item
39172 * @return {Roo.menu.Item} The menu item that was added
39174 addText : function(text){
39175 return this.addItem(new Roo.menu.TextItem({ text : text }));
39179 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39180 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39181 * @param {Roo.menu.Item} item The menu item to add
39182 * @return {Roo.menu.Item} The menu item that was added
39184 insert : function(index, item){
39185 this.items.insert(index, item);
39187 var li = document.createElement("li");
39188 li.className = "x-menu-list-item";
39189 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39190 item.render(li, this);
39191 this.delayAutoWidth();
39197 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39198 * @param {Roo.menu.Item} item The menu item to remove
39200 remove : function(item){
39201 this.items.removeKey(item.id);
39206 * Removes and destroys all items in the menu
39208 removeAll : function(){
39210 while(f = this.items.first()){
39216 // MenuNav is a private utility class used internally by the Menu
39217 Roo.menu.MenuNav = function(menu){
39218 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39219 this.scope = this.menu = menu;
39222 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39223 doRelay : function(e, h){
39224 var k = e.getKey();
39225 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39226 this.menu.tryActivate(0, 1);
39229 return h.call(this.scope || this, e, this.menu);
39232 up : function(e, m){
39233 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39234 m.tryActivate(m.items.length-1, -1);
39238 down : function(e, m){
39239 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39240 m.tryActivate(0, 1);
39244 right : function(e, m){
39246 m.activeItem.expandMenu(true);
39250 left : function(e, m){
39252 if(m.parentMenu && m.parentMenu.activeItem){
39253 m.parentMenu.activeItem.activate();
39257 enter : function(e, m){
39259 e.stopPropagation();
39260 m.activeItem.onClick(e);
39261 m.fireEvent("click", this, m.activeItem);
39267 * Ext JS Library 1.1.1
39268 * Copyright(c) 2006-2007, Ext JS, LLC.
39270 * Originally Released Under LGPL - original licence link has changed is not relivant.
39273 * <script type="text/javascript">
39277 * @class Roo.menu.MenuMgr
39278 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39281 Roo.menu.MenuMgr = function(){
39282 var menus, active, groups = {}, attached = false, lastShow = new Date();
39284 // private - called when first menu is created
39287 active = new Roo.util.MixedCollection();
39288 Roo.get(document).addKeyListener(27, function(){
39289 if(active.length > 0){
39296 function hideAll(){
39297 if(active && active.length > 0){
39298 var c = active.clone();
39299 c.each(function(m){
39306 function onHide(m){
39308 if(active.length < 1){
39309 Roo.get(document).un("mousedown", onMouseDown);
39315 function onShow(m){
39316 var last = active.last();
39317 lastShow = new Date();
39320 Roo.get(document).on("mousedown", onMouseDown);
39324 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39325 m.parentMenu.activeChild = m;
39326 }else if(last && last.isVisible()){
39327 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39332 function onBeforeHide(m){
39334 m.activeChild.hide();
39336 if(m.autoHideTimer){
39337 clearTimeout(m.autoHideTimer);
39338 delete m.autoHideTimer;
39343 function onBeforeShow(m){
39344 var pm = m.parentMenu;
39345 if(!pm && !m.allowOtherMenus){
39347 }else if(pm && pm.activeChild && active != m){
39348 pm.activeChild.hide();
39353 function onMouseDown(e){
39354 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39360 function onBeforeCheck(mi, state){
39362 var g = groups[mi.group];
39363 for(var i = 0, l = g.length; i < l; i++){
39365 g[i].setChecked(false);
39374 * Hides all menus that are currently visible
39376 hideAll : function(){
39381 register : function(menu){
39385 menus[menu.id] = menu;
39386 menu.on("beforehide", onBeforeHide);
39387 menu.on("hide", onHide);
39388 menu.on("beforeshow", onBeforeShow);
39389 menu.on("show", onShow);
39390 var g = menu.group;
39391 if(g && menu.events["checkchange"]){
39395 groups[g].push(menu);
39396 menu.on("checkchange", onCheck);
39401 * Returns a {@link Roo.menu.Menu} object
39402 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39403 * be used to generate and return a new Menu instance.
39405 get : function(menu){
39406 if(typeof menu == "string"){ // menu id
39407 return menus[menu];
39408 }else if(menu.events){ // menu instance
39410 }else if(typeof menu.length == 'number'){ // array of menu items?
39411 return new Roo.menu.Menu({items:menu});
39412 }else{ // otherwise, must be a config
39413 return new Roo.menu.Menu(menu);
39418 unregister : function(menu){
39419 delete menus[menu.id];
39420 menu.un("beforehide", onBeforeHide);
39421 menu.un("hide", onHide);
39422 menu.un("beforeshow", onBeforeShow);
39423 menu.un("show", onShow);
39424 var g = menu.group;
39425 if(g && menu.events["checkchange"]){
39426 groups[g].remove(menu);
39427 menu.un("checkchange", onCheck);
39432 registerCheckable : function(menuItem){
39433 var g = menuItem.group;
39438 groups[g].push(menuItem);
39439 menuItem.on("beforecheckchange", onBeforeCheck);
39444 unregisterCheckable : function(menuItem){
39445 var g = menuItem.group;
39447 groups[g].remove(menuItem);
39448 menuItem.un("beforecheckchange", onBeforeCheck);
39454 * Ext JS Library 1.1.1
39455 * Copyright(c) 2006-2007, Ext JS, LLC.
39457 * Originally Released Under LGPL - original licence link has changed is not relivant.
39460 * <script type="text/javascript">
39465 * @class Roo.menu.BaseItem
39466 * @extends Roo.Component
39468 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
39469 * management and base configuration options shared by all menu components.
39471 * Creates a new BaseItem
39472 * @param {Object} config Configuration options
39474 Roo.menu.BaseItem = function(config){
39475 Roo.menu.BaseItem.superclass.constructor.call(this, config);
39480 * Fires when this item is clicked
39481 * @param {Roo.menu.BaseItem} this
39482 * @param {Roo.EventObject} e
39487 * Fires when this item is activated
39488 * @param {Roo.menu.BaseItem} this
39492 * @event deactivate
39493 * Fires when this item is deactivated
39494 * @param {Roo.menu.BaseItem} this
39500 this.on("click", this.handler, this.scope, true);
39504 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39506 * @cfg {Function} handler
39507 * A function that will handle the click event of this menu item (defaults to undefined)
39510 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39512 canActivate : false,
39515 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39520 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39522 activeClass : "x-menu-item-active",
39524 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39526 hideOnClick : true,
39528 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39533 ctype: "Roo.menu.BaseItem",
39536 actionMode : "container",
39539 render : function(container, parentMenu){
39540 this.parentMenu = parentMenu;
39541 Roo.menu.BaseItem.superclass.render.call(this, container);
39542 this.container.menuItemId = this.id;
39546 onRender : function(container, position){
39547 this.el = Roo.get(this.el);
39548 container.dom.appendChild(this.el.dom);
39552 onClick : function(e){
39553 if(!this.disabled && this.fireEvent("click", this, e) !== false
39554 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39555 this.handleClick(e);
39562 activate : function(){
39566 var li = this.container;
39567 li.addClass(this.activeClass);
39568 this.region = li.getRegion().adjust(2, 2, -2, -2);
39569 this.fireEvent("activate", this);
39574 deactivate : function(){
39575 this.container.removeClass(this.activeClass);
39576 this.fireEvent("deactivate", this);
39580 shouldDeactivate : function(e){
39581 return !this.region || !this.region.contains(e.getPoint());
39585 handleClick : function(e){
39586 if(this.hideOnClick){
39587 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39592 expandMenu : function(autoActivate){
39597 hideMenu : function(){
39602 * Ext JS Library 1.1.1
39603 * Copyright(c) 2006-2007, Ext JS, LLC.
39605 * Originally Released Under LGPL - original licence link has changed is not relivant.
39608 * <script type="text/javascript">
39612 * @class Roo.menu.Adapter
39613 * @extends Roo.menu.BaseItem
39615 * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
39616 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39618 * Creates a new Adapter
39619 * @param {Object} config Configuration options
39621 Roo.menu.Adapter = function(component, config){
39622 Roo.menu.Adapter.superclass.constructor.call(this, config);
39623 this.component = component;
39625 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39627 canActivate : true,
39630 onRender : function(container, position){
39631 this.component.render(container);
39632 this.el = this.component.getEl();
39636 activate : function(){
39640 this.component.focus();
39641 this.fireEvent("activate", this);
39646 deactivate : function(){
39647 this.fireEvent("deactivate", this);
39651 disable : function(){
39652 this.component.disable();
39653 Roo.menu.Adapter.superclass.disable.call(this);
39657 enable : function(){
39658 this.component.enable();
39659 Roo.menu.Adapter.superclass.enable.call(this);
39663 * Ext JS Library 1.1.1
39664 * Copyright(c) 2006-2007, Ext JS, LLC.
39666 * Originally Released Under LGPL - original licence link has changed is not relivant.
39669 * <script type="text/javascript">
39673 * @class Roo.menu.TextItem
39674 * @extends Roo.menu.BaseItem
39675 * Adds a static text string to a menu, usually used as either a heading or group separator.
39676 * Note: old style constructor with text is still supported.
39679 * Creates a new TextItem
39680 * @param {Object} cfg Configuration
39682 Roo.menu.TextItem = function(cfg){
39683 if (typeof(cfg) == 'string') {
39686 Roo.apply(this,cfg);
39689 Roo.menu.TextItem.superclass.constructor.call(this);
39692 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39694 * @cfg {String} text Text to show on item.
39699 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39701 hideOnClick : false,
39703 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39705 itemCls : "x-menu-text",
39708 onRender : function(){
39709 var s = document.createElement("span");
39710 s.className = this.itemCls;
39711 s.innerHTML = this.text;
39713 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39717 * Ext JS Library 1.1.1
39718 * Copyright(c) 2006-2007, Ext JS, LLC.
39720 * Originally Released Under LGPL - original licence link has changed is not relivant.
39723 * <script type="text/javascript">
39727 * @class Roo.menu.Separator
39728 * @extends Roo.menu.BaseItem
39729 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39730 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39732 * @param {Object} config Configuration options
39734 Roo.menu.Separator = function(config){
39735 Roo.menu.Separator.superclass.constructor.call(this, config);
39738 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39740 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39742 itemCls : "x-menu-sep",
39744 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39746 hideOnClick : false,
39749 onRender : function(li){
39750 var s = document.createElement("span");
39751 s.className = this.itemCls;
39752 s.innerHTML = " ";
39754 li.addClass("x-menu-sep-li");
39755 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39759 * Ext JS Library 1.1.1
39760 * Copyright(c) 2006-2007, Ext JS, LLC.
39762 * Originally Released Under LGPL - original licence link has changed is not relivant.
39765 * <script type="text/javascript">
39768 * @class Roo.menu.Item
39769 * @extends Roo.menu.BaseItem
39770 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39771 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39772 * activation and click handling.
39774 * Creates a new Item
39775 * @param {Object} config Configuration options
39777 Roo.menu.Item = function(config){
39778 Roo.menu.Item.superclass.constructor.call(this, config);
39780 this.menu = Roo.menu.MenuMgr.get(this.menu);
39783 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39785 * @cfg {Roo.menu.Menu} menu
39789 * @cfg {String} text
39790 * The text to show on the menu item.
39794 * @cfg {String} HTML to render in menu
39795 * The text to show on the menu item (HTML version).
39799 * @cfg {String} icon
39800 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39804 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39806 itemCls : "x-menu-item",
39808 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39810 canActivate : true,
39812 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39815 // doc'd in BaseItem
39819 ctype: "Roo.menu.Item",
39822 onRender : function(container, position){
39823 var el = document.createElement("a");
39824 el.hideFocus = true;
39825 el.unselectable = "on";
39826 el.href = this.href || "#";
39827 if(this.hrefTarget){
39828 el.target = this.hrefTarget;
39830 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
39832 var html = this.html.length ? this.html : String.format('{0}',this.text);
39834 el.innerHTML = String.format(
39835 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39836 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39838 Roo.menu.Item.superclass.onRender.call(this, container, position);
39842 * Sets the text to display in this menu item
39843 * @param {String} text The text to display
39844 * @param {Boolean} isHTML true to indicate text is pure html.
39846 setText : function(text, isHTML){
39854 var html = this.html.length ? this.html : String.format('{0}',this.text);
39856 this.el.update(String.format(
39857 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39858 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39859 this.parentMenu.autoWidth();
39864 handleClick : function(e){
39865 if(!this.href){ // if no link defined, stop the event automatically
39868 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39872 activate : function(autoExpand){
39873 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39883 shouldDeactivate : function(e){
39884 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39885 if(this.menu && this.menu.isVisible()){
39886 return !this.menu.getEl().getRegion().contains(e.getPoint());
39894 deactivate : function(){
39895 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39900 expandMenu : function(autoActivate){
39901 if(!this.disabled && this.menu){
39902 clearTimeout(this.hideTimer);
39903 delete this.hideTimer;
39904 if(!this.menu.isVisible() && !this.showTimer){
39905 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39906 }else if (this.menu.isVisible() && autoActivate){
39907 this.menu.tryActivate(0, 1);
39913 deferExpand : function(autoActivate){
39914 delete this.showTimer;
39915 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39917 this.menu.tryActivate(0, 1);
39922 hideMenu : function(){
39923 clearTimeout(this.showTimer);
39924 delete this.showTimer;
39925 if(!this.hideTimer && this.menu && this.menu.isVisible()){
39926 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39931 deferHide : function(){
39932 delete this.hideTimer;
39937 * Ext JS Library 1.1.1
39938 * Copyright(c) 2006-2007, Ext JS, LLC.
39940 * Originally Released Under LGPL - original licence link has changed is not relivant.
39943 * <script type="text/javascript">
39947 * @class Roo.menu.CheckItem
39948 * @extends Roo.menu.Item
39949 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39951 * Creates a new CheckItem
39952 * @param {Object} config Configuration options
39954 Roo.menu.CheckItem = function(config){
39955 Roo.menu.CheckItem.superclass.constructor.call(this, config);
39958 * @event beforecheckchange
39959 * Fires before the checked value is set, providing an opportunity to cancel if needed
39960 * @param {Roo.menu.CheckItem} this
39961 * @param {Boolean} checked The new checked value that will be set
39963 "beforecheckchange" : true,
39965 * @event checkchange
39966 * Fires after the checked value has been set
39967 * @param {Roo.menu.CheckItem} this
39968 * @param {Boolean} checked The checked value that was set
39970 "checkchange" : true
39972 if(this.checkHandler){
39973 this.on('checkchange', this.checkHandler, this.scope);
39976 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39978 * @cfg {String} group
39979 * All check items with the same group name will automatically be grouped into a single-select
39980 * radio button group (defaults to '')
39983 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39985 itemCls : "x-menu-item x-menu-check-item",
39987 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39989 groupClass : "x-menu-group-item",
39992 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
39993 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39994 * initialized with checked = true will be rendered as checked.
39999 ctype: "Roo.menu.CheckItem",
40002 onRender : function(c){
40003 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40005 this.el.addClass(this.groupClass);
40007 Roo.menu.MenuMgr.registerCheckable(this);
40009 this.checked = false;
40010 this.setChecked(true, true);
40015 destroy : function(){
40017 Roo.menu.MenuMgr.unregisterCheckable(this);
40019 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40023 * Set the checked state of this item
40024 * @param {Boolean} checked The new checked value
40025 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40027 setChecked : function(state, suppressEvent){
40028 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40029 if(this.container){
40030 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40032 this.checked = state;
40033 if(suppressEvent !== true){
40034 this.fireEvent("checkchange", this, state);
40040 handleClick : function(e){
40041 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40042 this.setChecked(!this.checked);
40044 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40048 * Ext JS Library 1.1.1
40049 * Copyright(c) 2006-2007, Ext JS, LLC.
40051 * Originally Released Under LGPL - original licence link has changed is not relivant.
40054 * <script type="text/javascript">
40058 * @class Roo.menu.DateItem
40059 * @extends Roo.menu.Adapter
40060 * A menu item that wraps the {@link Roo.DatPicker} component.
40062 * Creates a new DateItem
40063 * @param {Object} config Configuration options
40065 Roo.menu.DateItem = function(config){
40066 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40067 /** The Roo.DatePicker object @type Roo.DatePicker */
40068 this.picker = this.component;
40069 this.addEvents({select: true});
40071 this.picker.on("render", function(picker){
40072 picker.getEl().swallowEvent("click");
40073 picker.container.addClass("x-menu-date-item");
40076 this.picker.on("select", this.onSelect, this);
40079 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40081 onSelect : function(picker, date){
40082 this.fireEvent("select", this, date, picker);
40083 Roo.menu.DateItem.superclass.handleClick.call(this);
40087 * Ext JS Library 1.1.1
40088 * Copyright(c) 2006-2007, Ext JS, LLC.
40090 * Originally Released Under LGPL - original licence link has changed is not relivant.
40093 * <script type="text/javascript">
40097 * @class Roo.menu.ColorItem
40098 * @extends Roo.menu.Adapter
40099 * A menu item that wraps the {@link Roo.ColorPalette} component.
40101 * Creates a new ColorItem
40102 * @param {Object} config Configuration options
40104 Roo.menu.ColorItem = function(config){
40105 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40106 /** The Roo.ColorPalette object @type Roo.ColorPalette */
40107 this.palette = this.component;
40108 this.relayEvents(this.palette, ["select"]);
40109 if(this.selectHandler){
40110 this.on('select', this.selectHandler, this.scope);
40113 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40115 * Ext JS Library 1.1.1
40116 * Copyright(c) 2006-2007, Ext JS, LLC.
40118 * Originally Released Under LGPL - original licence link has changed is not relivant.
40121 * <script type="text/javascript">
40126 * @class Roo.menu.DateMenu
40127 * @extends Roo.menu.Menu
40128 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40130 * Creates a new DateMenu
40131 * @param {Object} config Configuration options
40133 Roo.menu.DateMenu = function(config){
40134 Roo.menu.DateMenu.superclass.constructor.call(this, config);
40136 var di = new Roo.menu.DateItem(config);
40139 * The {@link Roo.DatePicker} instance for this DateMenu
40142 this.picker = di.picker;
40145 * @param {DatePicker} picker
40146 * @param {Date} date
40148 this.relayEvents(di, ["select"]);
40149 this.on('beforeshow', function(){
40151 this.picker.hideMonthPicker(false);
40155 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40159 * Ext JS Library 1.1.1
40160 * Copyright(c) 2006-2007, Ext JS, LLC.
40162 * Originally Released Under LGPL - original licence link has changed is not relivant.
40165 * <script type="text/javascript">
40170 * @class Roo.menu.ColorMenu
40171 * @extends Roo.menu.Menu
40172 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40174 * Creates a new ColorMenu
40175 * @param {Object} config Configuration options
40177 Roo.menu.ColorMenu = function(config){
40178 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40180 var ci = new Roo.menu.ColorItem(config);
40183 * The {@link Roo.ColorPalette} instance for this ColorMenu
40184 * @type ColorPalette
40186 this.palette = ci.palette;
40189 * @param {ColorPalette} palette
40190 * @param {String} color
40192 this.relayEvents(ci, ["select"]);
40194 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40196 * Ext JS Library 1.1.1
40197 * Copyright(c) 2006-2007, Ext JS, LLC.
40199 * Originally Released Under LGPL - original licence link has changed is not relivant.
40202 * <script type="text/javascript">
40206 * @class Roo.form.TextItem
40207 * @extends Roo.BoxComponent
40208 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40210 * Creates a new TextItem
40211 * @param {Object} config Configuration options
40213 Roo.form.TextItem = function(config){
40214 Roo.form.TextItem.superclass.constructor.call(this, config);
40217 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
40220 * @cfg {String} tag the tag for this item (default div)
40224 * @cfg {String} html the content for this item
40228 getAutoCreate : function()
40241 onRender : function(ct, position)
40243 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40246 var cfg = this.getAutoCreate();
40248 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40250 if (!cfg.name.length) {
40253 this.el = ct.createChild(cfg, position);
40258 * @param {String} html update the Contents of the element.
40260 setHTML : function(html)
40262 this.fieldEl.dom.innerHTML = html;
40267 * Ext JS Library 1.1.1
40268 * Copyright(c) 2006-2007, Ext JS, LLC.
40270 * Originally Released Under LGPL - original licence link has changed is not relivant.
40273 * <script type="text/javascript">
40277 * @class Roo.form.Field
40278 * @extends Roo.BoxComponent
40279 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40281 * Creates a new Field
40282 * @param {Object} config Configuration options
40284 Roo.form.Field = function(config){
40285 Roo.form.Field.superclass.constructor.call(this, config);
40288 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
40290 * @cfg {String} fieldLabel Label to use when rendering a form.
40293 * @cfg {String} qtip Mouse over tip
40297 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40299 invalidClass : "x-form-invalid",
40301 * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
40303 invalidText : "The value in this field is invalid",
40305 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40307 focusClass : "x-form-focus",
40309 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40310 automatic validation (defaults to "keyup").
40312 validationEvent : "keyup",
40314 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40316 validateOnBlur : true,
40318 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40320 validationDelay : 250,
40322 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40323 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40325 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40327 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40329 fieldClass : "x-form-field",
40331 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
40334 ----------- ----------------------------------------------------------------------
40335 qtip Display a quick tip when the user hovers over the field
40336 title Display a default browser title attribute popup
40337 under Add a block div beneath the field containing the error text
40338 side Add an error icon to the right of the field with a popup on hover
40339 [element id] Add the error text directly to the innerHTML of the specified element
40342 msgTarget : 'qtip',
40344 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40349 * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
40354 * @cfg {Boolean} disabled True to disable the field (defaults to false).
40359 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40361 inputType : undefined,
40364 * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
40366 tabIndex : undefined,
40369 isFormField : true,
40374 * @property {Roo.Element} fieldEl
40375 * Element Containing the rendered Field (with label etc.)
40378 * @cfg {Mixed} value A value to initialize this field with.
40383 * @cfg {String} name The field's HTML name attribute.
40386 * @cfg {String} cls A CSS class to apply to the field's underlying element.
40389 loadedValue : false,
40393 initComponent : function(){
40394 Roo.form.Field.superclass.initComponent.call(this);
40398 * Fires when this field receives input focus.
40399 * @param {Roo.form.Field} this
40404 * Fires when this field loses input focus.
40405 * @param {Roo.form.Field} this
40409 * @event specialkey
40410 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
40411 * {@link Roo.EventObject#getKey} to determine which key was pressed.
40412 * @param {Roo.form.Field} this
40413 * @param {Roo.EventObject} e The event object
40418 * Fires just before the field blurs if the field value has changed.
40419 * @param {Roo.form.Field} this
40420 * @param {Mixed} newValue The new value
40421 * @param {Mixed} oldValue The original value
40426 * Fires after the field has been marked as invalid.
40427 * @param {Roo.form.Field} this
40428 * @param {String} msg The validation message
40433 * Fires after the field has been validated with no errors.
40434 * @param {Roo.form.Field} this
40439 * Fires after the key up
40440 * @param {Roo.form.Field} this
40441 * @param {Roo.EventObject} e The event Object
40448 * Returns the name attribute of the field if available
40449 * @return {String} name The field name
40451 getName: function(){
40452 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40456 onRender : function(ct, position){
40457 Roo.form.Field.superclass.onRender.call(this, ct, position);
40459 var cfg = this.getAutoCreate();
40461 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40463 if (!cfg.name.length) {
40466 if(this.inputType){
40467 cfg.type = this.inputType;
40469 this.el = ct.createChild(cfg, position);
40471 var type = this.el.dom.type;
40473 if(type == 'password'){
40476 this.el.addClass('x-form-'+type);
40479 this.el.dom.readOnly = true;
40481 if(this.tabIndex !== undefined){
40482 this.el.dom.setAttribute('tabIndex', this.tabIndex);
40485 this.el.addClass([this.fieldClass, this.cls]);
40490 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40491 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40492 * @return {Roo.form.Field} this
40494 applyTo : function(target){
40495 this.allowDomMove = false;
40496 this.el = Roo.get(target);
40497 this.render(this.el.dom.parentNode);
40502 initValue : function(){
40503 if(this.value !== undefined){
40504 this.setValue(this.value);
40505 }else if(this.el.dom.value.length > 0){
40506 this.setValue(this.el.dom.value);
40511 * Returns true if this field has been changed since it was originally loaded and is not disabled.
40512 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
40514 isDirty : function() {
40515 if(this.disabled) {
40518 return String(this.getValue()) !== String(this.originalValue);
40522 * stores the current value in loadedValue
40524 resetHasChanged : function()
40526 this.loadedValue = String(this.getValue());
40529 * checks the current value against the 'loaded' value.
40530 * Note - will return false if 'resetHasChanged' has not been called first.
40532 hasChanged : function()
40534 if(this.disabled || this.readOnly) {
40537 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40543 afterRender : function(){
40544 Roo.form.Field.superclass.afterRender.call(this);
40549 fireKey : function(e){
40550 //Roo.log('field ' + e.getKey());
40551 if(e.isNavKeyPress()){
40552 this.fireEvent("specialkey", this, e);
40557 * Resets the current field value to the originally loaded value and clears any validation messages
40559 reset : function(){
40560 this.setValue(this.resetValue);
40561 this.originalValue = this.getValue();
40562 this.clearInvalid();
40566 initEvents : function(){
40567 // safari killled keypress - so keydown is now used..
40568 this.el.on("keydown" , this.fireKey, this);
40569 this.el.on("focus", this.onFocus, this);
40570 this.el.on("blur", this.onBlur, this);
40571 this.el.relayEvent('keyup', this);
40573 // reference to original value for reset
40574 this.originalValue = this.getValue();
40575 this.resetValue = this.getValue();
40579 onFocus : function(){
40580 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40581 this.el.addClass(this.focusClass);
40583 if(!this.hasFocus){
40584 this.hasFocus = true;
40585 this.startValue = this.getValue();
40586 this.fireEvent("focus", this);
40590 beforeBlur : Roo.emptyFn,
40593 onBlur : function(){
40595 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40596 this.el.removeClass(this.focusClass);
40598 this.hasFocus = false;
40599 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40602 var v = this.getValue();
40603 if(String(v) !== String(this.startValue)){
40604 this.fireEvent('change', this, v, this.startValue);
40606 this.fireEvent("blur", this);
40610 * Returns whether or not the field value is currently valid
40611 * @param {Boolean} preventMark True to disable marking the field invalid
40612 * @return {Boolean} True if the value is valid, else false
40614 isValid : function(preventMark){
40618 var restore = this.preventMark;
40619 this.preventMark = preventMark === true;
40620 var v = this.validateValue(this.processValue(this.getRawValue()));
40621 this.preventMark = restore;
40626 * Validates the field value
40627 * @return {Boolean} True if the value is valid, else false
40629 validate : function(){
40630 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40631 this.clearInvalid();
40637 processValue : function(value){
40642 // Subclasses should provide the validation implementation by overriding this
40643 validateValue : function(value){
40648 * Mark this field as invalid
40649 * @param {String} msg The validation message
40651 markInvalid : function(msg){
40652 if(!this.rendered || this.preventMark){ // not rendered
40656 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40658 obj.el.addClass(this.invalidClass);
40659 msg = msg || this.invalidText;
40660 switch(this.msgTarget){
40662 obj.el.dom.qtip = msg;
40663 obj.el.dom.qclass = 'x-form-invalid-tip';
40664 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40665 Roo.QuickTips.enable();
40669 this.el.dom.title = msg;
40673 var elp = this.el.findParent('.x-form-element', 5, true);
40674 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40675 this.errorEl.setWidth(elp.getWidth(true)-20);
40677 this.errorEl.update(msg);
40678 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40681 if(!this.errorIcon){
40682 var elp = this.el.findParent('.x-form-element', 5, true);
40683 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40685 this.alignErrorIcon();
40686 this.errorIcon.dom.qtip = msg;
40687 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40688 this.errorIcon.show();
40689 this.on('resize', this.alignErrorIcon, this);
40692 var t = Roo.getDom(this.msgTarget);
40694 t.style.display = this.msgDisplay;
40697 this.fireEvent('invalid', this, msg);
40701 alignErrorIcon : function(){
40702 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40706 * Clear any invalid styles/messages for this field
40708 clearInvalid : function(){
40709 if(!this.rendered || this.preventMark){ // not rendered
40712 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40714 obj.el.removeClass(this.invalidClass);
40715 switch(this.msgTarget){
40717 obj.el.dom.qtip = '';
40720 this.el.dom.title = '';
40724 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40728 if(this.errorIcon){
40729 this.errorIcon.dom.qtip = '';
40730 this.errorIcon.hide();
40731 this.un('resize', this.alignErrorIcon, this);
40735 var t = Roo.getDom(this.msgTarget);
40737 t.style.display = 'none';
40740 this.fireEvent('valid', this);
40744 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
40745 * @return {Mixed} value The field value
40747 getRawValue : function(){
40748 var v = this.el.getValue();
40754 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
40755 * @return {Mixed} value The field value
40757 getValue : function(){
40758 var v = this.el.getValue();
40764 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
40765 * @param {Mixed} value The value to set
40767 setRawValue : function(v){
40768 return this.el.dom.value = (v === null || v === undefined ? '' : v);
40772 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
40773 * @param {Mixed} value The value to set
40775 setValue : function(v){
40778 this.el.dom.value = (v === null || v === undefined ? '' : v);
40783 adjustSize : function(w, h){
40784 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40785 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40789 adjustWidth : function(tag, w){
40790 tag = tag.toLowerCase();
40791 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40792 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40793 if(tag == 'input'){
40796 if(tag == 'textarea'){
40799 }else if(Roo.isOpera){
40800 if(tag == 'input'){
40803 if(tag == 'textarea'){
40813 // anything other than normal should be considered experimental
40814 Roo.form.Field.msgFx = {
40816 show: function(msgEl, f){
40817 msgEl.setDisplayed('block');
40820 hide : function(msgEl, f){
40821 msgEl.setDisplayed(false).update('');
40826 show: function(msgEl, f){
40827 msgEl.slideIn('t', {stopFx:true});
40830 hide : function(msgEl, f){
40831 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40836 show: function(msgEl, f){
40837 msgEl.fixDisplay();
40838 msgEl.alignTo(f.el, 'tl-tr');
40839 msgEl.slideIn('l', {stopFx:true});
40842 hide : function(msgEl, f){
40843 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40848 * Ext JS Library 1.1.1
40849 * Copyright(c) 2006-2007, Ext JS, LLC.
40851 * Originally Released Under LGPL - original licence link has changed is not relivant.
40854 * <script type="text/javascript">
40859 * @class Roo.form.TextField
40860 * @extends Roo.form.Field
40861 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
40862 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40864 * Creates a new TextField
40865 * @param {Object} config Configuration options
40867 Roo.form.TextField = function(config){
40868 Roo.form.TextField.superclass.constructor.call(this, config);
40872 * Fires when the autosize function is triggered. The field may or may not have actually changed size
40873 * according to the default logic, but this event provides a hook for the developer to apply additional
40874 * logic at runtime to resize the field if needed.
40875 * @param {Roo.form.Field} this This text field
40876 * @param {Number} width The new field width
40882 Roo.extend(Roo.form.TextField, Roo.form.Field, {
40884 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40888 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40892 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40896 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40900 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40904 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40906 disableKeyFilter : false,
40908 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40912 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40916 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40918 maxLength : Number.MAX_VALUE,
40920 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40922 minLengthText : "The minimum length for this field is {0}",
40924 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40926 maxLengthText : "The maximum length for this field is {0}",
40928 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40930 selectOnFocus : false,
40932 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
40934 allowLeadingSpace : false,
40936 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40938 blankText : "This field is required",
40940 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40941 * If available, this function will be called only after the basic validators all return true, and will be passed the
40942 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40946 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40947 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40948 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
40952 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40956 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40962 initEvents : function()
40964 if (this.emptyText) {
40965 this.el.attr('placeholder', this.emptyText);
40968 Roo.form.TextField.superclass.initEvents.call(this);
40969 if(this.validationEvent == 'keyup'){
40970 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40971 this.el.on('keyup', this.filterValidation, this);
40973 else if(this.validationEvent !== false){
40974 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40977 if(this.selectOnFocus){
40978 this.on("focus", this.preFocus, this);
40980 if (!this.allowLeadingSpace) {
40981 this.on('blur', this.cleanLeadingSpace, this);
40984 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40985 this.el.on("keypress", this.filterKeys, this);
40988 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
40989 this.el.on("click", this.autoSize, this);
40991 if(this.el.is('input[type=password]') && Roo.isSafari){
40992 this.el.on('keydown', this.SafariOnKeyDown, this);
40996 processValue : function(value){
40997 if(this.stripCharsRe){
40998 var newValue = value.replace(this.stripCharsRe, '');
40999 if(newValue !== value){
41000 this.setRawValue(newValue);
41007 filterValidation : function(e){
41008 if(!e.isNavKeyPress()){
41009 this.validationTask.delay(this.validationDelay);
41014 onKeyUp : function(e){
41015 if(!e.isNavKeyPress()){
41019 // private - clean the leading white space
41020 cleanLeadingSpace : function(e)
41022 if ( this.inputType == 'file') {
41026 this.setValue((this.getValue() + '').replace(/^\s+/,''));
41029 * Resets the current field value to the originally-loaded value and clears any validation messages.
41032 reset : function(){
41033 Roo.form.TextField.superclass.reset.call(this);
41037 preFocus : function(){
41039 if(this.selectOnFocus){
41040 this.el.dom.select();
41046 filterKeys : function(e){
41047 var k = e.getKey();
41048 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41051 var c = e.getCharCode(), cc = String.fromCharCode(c);
41052 if(Roo.isIE && (e.isSpecialKey() || !cc)){
41055 if(!this.maskRe.test(cc)){
41060 setValue : function(v){
41062 Roo.form.TextField.superclass.setValue.apply(this, arguments);
41068 * Validates a value according to the field's validation rules and marks the field as invalid
41069 * if the validation fails
41070 * @param {Mixed} value The value to validate
41071 * @return {Boolean} True if the value is valid, else false
41073 validateValue : function(value){
41074 if(value.length < 1) { // if it's blank
41075 if(this.allowBlank){
41076 this.clearInvalid();
41079 this.markInvalid(this.blankText);
41083 if(value.length < this.minLength){
41084 this.markInvalid(String.format(this.minLengthText, this.minLength));
41087 if(value.length > this.maxLength){
41088 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41092 var vt = Roo.form.VTypes;
41093 if(!vt[this.vtype](value, this)){
41094 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41098 if(typeof this.validator == "function"){
41099 var msg = this.validator(value);
41101 this.markInvalid(msg);
41105 if(this.regex && !this.regex.test(value)){
41106 this.markInvalid(this.regexText);
41113 * Selects text in this field
41114 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41115 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41117 selectText : function(start, end){
41118 var v = this.getRawValue();
41120 start = start === undefined ? 0 : start;
41121 end = end === undefined ? v.length : end;
41122 var d = this.el.dom;
41123 if(d.setSelectionRange){
41124 d.setSelectionRange(start, end);
41125 }else if(d.createTextRange){
41126 var range = d.createTextRange();
41127 range.moveStart("character", start);
41128 range.moveEnd("character", v.length-end);
41135 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41136 * This only takes effect if grow = true, and fires the autosize event.
41138 autoSize : function(){
41139 if(!this.grow || !this.rendered){
41143 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41146 var v = el.dom.value;
41147 var d = document.createElement('div');
41148 d.appendChild(document.createTextNode(v));
41152 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41153 this.el.setWidth(w);
41154 this.fireEvent("autosize", this, w);
41158 SafariOnKeyDown : function(event)
41160 // this is a workaround for a password hang bug on chrome/ webkit.
41162 var isSelectAll = false;
41164 if(this.el.dom.selectionEnd > 0){
41165 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41167 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41168 event.preventDefault();
41173 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41175 event.preventDefault();
41176 // this is very hacky as keydown always get's upper case.
41178 var cc = String.fromCharCode(event.getCharCode());
41181 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
41189 * Ext JS Library 1.1.1
41190 * Copyright(c) 2006-2007, Ext JS, LLC.
41192 * Originally Released Under LGPL - original licence link has changed is not relivant.
41195 * <script type="text/javascript">
41199 * @class Roo.form.Hidden
41200 * @extends Roo.form.TextField
41201 * Simple Hidden element used on forms
41203 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41206 * Creates a new Hidden form element.
41207 * @param {Object} config Configuration options
41212 // easy hidden field...
41213 Roo.form.Hidden = function(config){
41214 Roo.form.Hidden.superclass.constructor.call(this, config);
41217 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41219 inputType: 'hidden',
41222 labelSeparator: '',
41224 itemCls : 'x-form-item-display-none'
41232 * Ext JS Library 1.1.1
41233 * Copyright(c) 2006-2007, Ext JS, LLC.
41235 * Originally Released Under LGPL - original licence link has changed is not relivant.
41238 * <script type="text/javascript">
41242 * @class Roo.form.TriggerField
41243 * @extends Roo.form.TextField
41244 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41245 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41246 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41247 * for which you can provide a custom implementation. For example:
41249 var trigger = new Roo.form.TriggerField();
41250 trigger.onTriggerClick = myTriggerFn;
41251 trigger.applyTo('my-field');
41254 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41255 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41256 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
41257 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41259 * Create a new TriggerField.
41260 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41261 * to the base TextField)
41263 Roo.form.TriggerField = function(config){
41264 this.mimicing = false;
41265 Roo.form.TriggerField.superclass.constructor.call(this, config);
41268 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
41270 * @cfg {String} triggerClass A CSS class to apply to the trigger
41273 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41274 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41276 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41278 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41282 /** @cfg {Boolean} grow @hide */
41283 /** @cfg {Number} growMin @hide */
41284 /** @cfg {Number} growMax @hide */
41290 autoSize: Roo.emptyFn,
41294 deferHeight : true,
41297 actionMode : 'wrap',
41299 onResize : function(w, h){
41300 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41301 if(typeof w == 'number'){
41302 var x = w - this.trigger.getWidth();
41303 this.el.setWidth(this.adjustWidth('input', x));
41304 this.trigger.setStyle('left', x+'px');
41309 adjustSize : Roo.BoxComponent.prototype.adjustSize,
41312 getResizeEl : function(){
41317 getPositionEl : function(){
41322 alignErrorIcon : function(){
41323 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41327 onRender : function(ct, position){
41328 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41329 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41330 this.trigger = this.wrap.createChild(this.triggerConfig ||
41331 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41332 if(this.hideTrigger){
41333 this.trigger.setDisplayed(false);
41335 this.initTrigger();
41337 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41342 initTrigger : function(){
41343 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41344 this.trigger.addClassOnOver('x-form-trigger-over');
41345 this.trigger.addClassOnClick('x-form-trigger-click');
41349 onDestroy : function(){
41351 this.trigger.removeAllListeners();
41352 this.trigger.remove();
41355 this.wrap.remove();
41357 Roo.form.TriggerField.superclass.onDestroy.call(this);
41361 onFocus : function(){
41362 Roo.form.TriggerField.superclass.onFocus.call(this);
41363 if(!this.mimicing){
41364 this.wrap.addClass('x-trigger-wrap-focus');
41365 this.mimicing = true;
41366 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41367 if(this.monitorTab){
41368 this.el.on("keydown", this.checkTab, this);
41374 checkTab : function(e){
41375 if(e.getKey() == e.TAB){
41376 this.triggerBlur();
41381 onBlur : function(){
41386 mimicBlur : function(e, t){
41387 if(!this.wrap.contains(t) && this.validateBlur()){
41388 this.triggerBlur();
41393 triggerBlur : function(){
41394 this.mimicing = false;
41395 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41396 if(this.monitorTab){
41397 this.el.un("keydown", this.checkTab, this);
41399 this.wrap.removeClass('x-trigger-wrap-focus');
41400 Roo.form.TriggerField.superclass.onBlur.call(this);
41404 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41405 validateBlur : function(e, t){
41410 onDisable : function(){
41411 Roo.form.TriggerField.superclass.onDisable.call(this);
41413 this.wrap.addClass('x-item-disabled');
41418 onEnable : function(){
41419 Roo.form.TriggerField.superclass.onEnable.call(this);
41421 this.wrap.removeClass('x-item-disabled');
41426 onShow : function(){
41427 var ae = this.getActionEl();
41430 ae.dom.style.display = '';
41431 ae.dom.style.visibility = 'visible';
41437 onHide : function(){
41438 var ae = this.getActionEl();
41439 ae.dom.style.display = 'none';
41443 * The function that should handle the trigger's click event. This method does nothing by default until overridden
41444 * by an implementing function.
41446 * @param {EventObject} e
41448 onTriggerClick : Roo.emptyFn
41451 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
41452 // to be extended by an implementing class. For an example of implementing this class, see the custom
41453 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41454 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41455 initComponent : function(){
41456 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41458 this.triggerConfig = {
41459 tag:'span', cls:'x-form-twin-triggers', cn:[
41460 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41461 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41465 getTrigger : function(index){
41466 return this.triggers[index];
41469 initTrigger : function(){
41470 var ts = this.trigger.select('.x-form-trigger', true);
41471 this.wrap.setStyle('overflow', 'hidden');
41472 var triggerField = this;
41473 ts.each(function(t, all, index){
41474 t.hide = function(){
41475 var w = triggerField.wrap.getWidth();
41476 this.dom.style.display = 'none';
41477 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41479 t.show = function(){
41480 var w = triggerField.wrap.getWidth();
41481 this.dom.style.display = '';
41482 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41484 var triggerIndex = 'Trigger'+(index+1);
41486 if(this['hide'+triggerIndex]){
41487 t.dom.style.display = 'none';
41489 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41490 t.addClassOnOver('x-form-trigger-over');
41491 t.addClassOnClick('x-form-trigger-click');
41493 this.triggers = ts.elements;
41496 onTrigger1Click : Roo.emptyFn,
41497 onTrigger2Click : Roo.emptyFn
41500 * Ext JS Library 1.1.1
41501 * Copyright(c) 2006-2007, Ext JS, LLC.
41503 * Originally Released Under LGPL - original licence link has changed is not relivant.
41506 * <script type="text/javascript">
41510 * @class Roo.form.TextArea
41511 * @extends Roo.form.TextField
41512 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
41513 * support for auto-sizing.
41515 * Creates a new TextArea
41516 * @param {Object} config Configuration options
41518 Roo.form.TextArea = function(config){
41519 Roo.form.TextArea.superclass.constructor.call(this, config);
41520 // these are provided exchanges for backwards compat
41521 // minHeight/maxHeight were replaced by growMin/growMax to be
41522 // compatible with TextField growing config values
41523 if(this.minHeight !== undefined){
41524 this.growMin = this.minHeight;
41526 if(this.maxHeight !== undefined){
41527 this.growMax = this.maxHeight;
41531 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
41533 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41537 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41541 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41542 * in the field (equivalent to setting overflow: hidden, defaults to false)
41544 preventScrollbars: false,
41546 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41547 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41551 onRender : function(ct, position){
41553 this.defaultAutoCreate = {
41555 style:"width:300px;height:60px;",
41556 autocomplete: "new-password"
41559 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41561 this.textSizeEl = Roo.DomHelper.append(document.body, {
41562 tag: "pre", cls: "x-form-grow-sizer"
41564 if(this.preventScrollbars){
41565 this.el.setStyle("overflow", "hidden");
41567 this.el.setHeight(this.growMin);
41571 onDestroy : function(){
41572 if(this.textSizeEl){
41573 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41575 Roo.form.TextArea.superclass.onDestroy.call(this);
41579 onKeyUp : function(e){
41580 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41586 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41587 * This only takes effect if grow = true, and fires the autosize event if the height changes.
41589 autoSize : function(){
41590 if(!this.grow || !this.textSizeEl){
41594 var v = el.dom.value;
41595 var ts = this.textSizeEl;
41598 ts.appendChild(document.createTextNode(v));
41601 Roo.fly(ts).setWidth(this.el.getWidth());
41603 v = "  ";
41606 v = v.replace(/\n/g, '<p> </p>');
41608 v += " \n ";
41611 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41612 if(h != this.lastHeight){
41613 this.lastHeight = h;
41614 this.el.setHeight(h);
41615 this.fireEvent("autosize", this, h);
41620 * Ext JS Library 1.1.1
41621 * Copyright(c) 2006-2007, Ext JS, LLC.
41623 * Originally Released Under LGPL - original licence link has changed is not relivant.
41626 * <script type="text/javascript">
41631 * @class Roo.form.NumberField
41632 * @extends Roo.form.TextField
41633 * Numeric text field that provides automatic keystroke filtering and numeric validation.
41635 * Creates a new NumberField
41636 * @param {Object} config Configuration options
41638 Roo.form.NumberField = function(config){
41639 Roo.form.NumberField.superclass.constructor.call(this, config);
41642 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
41644 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41646 fieldClass: "x-form-field x-form-num-field",
41648 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41650 allowDecimals : true,
41652 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41654 decimalSeparator : ".",
41656 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41658 decimalPrecision : 2,
41660 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41662 allowNegative : true,
41664 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41666 minValue : Number.NEGATIVE_INFINITY,
41668 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41670 maxValue : Number.MAX_VALUE,
41672 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41674 minText : "The minimum value for this field is {0}",
41676 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41678 maxText : "The maximum value for this field is {0}",
41680 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41681 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41683 nanText : "{0} is not a valid number",
41686 initEvents : function(){
41687 Roo.form.NumberField.superclass.initEvents.call(this);
41688 var allowed = "0123456789";
41689 if(this.allowDecimals){
41690 allowed += this.decimalSeparator;
41692 if(this.allowNegative){
41695 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41696 var keyPress = function(e){
41697 var k = e.getKey();
41698 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41701 var c = e.getCharCode();
41702 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41706 this.el.on("keypress", keyPress, this);
41710 validateValue : function(value){
41711 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41714 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41717 var num = this.parseValue(value);
41719 this.markInvalid(String.format(this.nanText, value));
41722 if(num < this.minValue){
41723 this.markInvalid(String.format(this.minText, this.minValue));
41726 if(num > this.maxValue){
41727 this.markInvalid(String.format(this.maxText, this.maxValue));
41733 getValue : function(){
41734 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41738 parseValue : function(value){
41739 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41740 return isNaN(value) ? '' : value;
41744 fixPrecision : function(value){
41745 var nan = isNaN(value);
41746 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41747 return nan ? '' : value;
41749 return parseFloat(value).toFixed(this.decimalPrecision);
41752 setValue : function(v){
41753 v = this.fixPrecision(v);
41754 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41758 decimalPrecisionFcn : function(v){
41759 return Math.floor(v);
41762 beforeBlur : function(){
41763 var v = this.parseValue(this.getRawValue());
41770 * Ext JS Library 1.1.1
41771 * Copyright(c) 2006-2007, Ext JS, LLC.
41773 * Originally Released Under LGPL - original licence link has changed is not relivant.
41776 * <script type="text/javascript">
41780 * @class Roo.form.DateField
41781 * @extends Roo.form.TriggerField
41782 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41784 * Create a new DateField
41785 * @param {Object} config
41787 Roo.form.DateField = function(config)
41789 Roo.form.DateField.superclass.constructor.call(this, config);
41795 * Fires when a date is selected
41796 * @param {Roo.form.DateField} combo This combo box
41797 * @param {Date} date The date selected
41804 if(typeof this.minValue == "string") {
41805 this.minValue = this.parseDate(this.minValue);
41807 if(typeof this.maxValue == "string") {
41808 this.maxValue = this.parseDate(this.maxValue);
41810 this.ddMatch = null;
41811 if(this.disabledDates){
41812 var dd = this.disabledDates;
41814 for(var i = 0; i < dd.length; i++){
41816 if(i != dd.length-1) {
41820 this.ddMatch = new RegExp(re + ")");
41824 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
41826 * @cfg {String} format
41827 * The default date format string which can be overriden for localization support. The format must be
41828 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41832 * @cfg {String} altFormats
41833 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41834 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41836 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41838 * @cfg {Array} disabledDays
41839 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41841 disabledDays : null,
41843 * @cfg {String} disabledDaysText
41844 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41846 disabledDaysText : "Disabled",
41848 * @cfg {Array} disabledDates
41849 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41850 * expression so they are very powerful. Some examples:
41852 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41853 * <li>["03/08", "09/16"] would disable those days for every year</li>
41854 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41855 * <li>["03/../2006"] would disable every day in March 2006</li>
41856 * <li>["^03"] would disable every day in every March</li>
41858 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41859 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41861 disabledDates : null,
41863 * @cfg {String} disabledDatesText
41864 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41866 disabledDatesText : "Disabled",
41870 * @cfg {Date/String} zeroValue
41871 * if the date is less that this number, then the field is rendered as empty
41874 zeroValue : '1800-01-01',
41878 * @cfg {Date/String} minValue
41879 * The minimum allowed date. Can be either a Javascript date object or a string date in a
41880 * valid format (defaults to null).
41884 * @cfg {Date/String} maxValue
41885 * The maximum allowed date. Can be either a Javascript date object or a string date in a
41886 * valid format (defaults to null).
41890 * @cfg {String} minText
41891 * The error text to display when the date in the cell is before minValue (defaults to
41892 * 'The date in this field must be after {minValue}').
41894 minText : "The date in this field must be equal to or after {0}",
41896 * @cfg {String} maxText
41897 * The error text to display when the date in the cell is after maxValue (defaults to
41898 * 'The date in this field must be before {maxValue}').
41900 maxText : "The date in this field must be equal to or before {0}",
41902 * @cfg {String} invalidText
41903 * The error text to display when the date in the field is invalid (defaults to
41904 * '{value} is not a valid date - it must be in the format {format}').
41906 invalidText : "{0} is not a valid date - it must be in the format {1}",
41908 * @cfg {String} triggerClass
41909 * An additional CSS class used to style the trigger button. The trigger will always get the
41910 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41911 * which displays a calendar icon).
41913 triggerClass : 'x-form-date-trigger',
41917 * @cfg {Boolean} useIso
41918 * if enabled, then the date field will use a hidden field to store the
41919 * real value as iso formated date. default (false)
41923 * @cfg {String/Object} autoCreate
41924 * A DomHelper element spec, or true for a default element spec (defaults to
41925 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41928 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41931 hiddenField: false,
41933 onRender : function(ct, position)
41935 Roo.form.DateField.superclass.onRender.call(this, ct, position);
41937 //this.el.dom.removeAttribute('name');
41938 Roo.log("Changing name?");
41939 this.el.dom.setAttribute('name', this.name + '____hidden___' );
41940 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41942 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41943 // prevent input submission
41944 this.hiddenName = this.name;
41951 validateValue : function(value)
41953 value = this.formatDate(value);
41954 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41955 Roo.log('super failed');
41958 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41961 var svalue = value;
41962 value = this.parseDate(value);
41964 Roo.log('parse date failed' + svalue);
41965 this.markInvalid(String.format(this.invalidText, svalue, this.format));
41968 var time = value.getTime();
41969 if(this.minValue && time < this.minValue.getTime()){
41970 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41973 if(this.maxValue && time > this.maxValue.getTime()){
41974 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41977 if(this.disabledDays){
41978 var day = value.getDay();
41979 for(var i = 0; i < this.disabledDays.length; i++) {
41980 if(day === this.disabledDays[i]){
41981 this.markInvalid(this.disabledDaysText);
41986 var fvalue = this.formatDate(value);
41987 if(this.ddMatch && this.ddMatch.test(fvalue)){
41988 this.markInvalid(String.format(this.disabledDatesText, fvalue));
41995 // Provides logic to override the default TriggerField.validateBlur which just returns true
41996 validateBlur : function(){
41997 return !this.menu || !this.menu.isVisible();
42000 getName: function()
42002 // returns hidden if it's set..
42003 if (!this.rendered) {return ''};
42004 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42009 * Returns the current date value of the date field.
42010 * @return {Date} The date value
42012 getValue : function(){
42014 return this.hiddenField ?
42015 this.hiddenField.value :
42016 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42020 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42021 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42022 * (the default format used is "m/d/y").
42025 //All of these calls set the same date value (May 4, 2006)
42027 //Pass a date object:
42028 var dt = new Date('5/4/06');
42029 dateField.setValue(dt);
42031 //Pass a date string (default format):
42032 dateField.setValue('5/4/06');
42034 //Pass a date string (custom format):
42035 dateField.format = 'Y-m-d';
42036 dateField.setValue('2006-5-4');
42038 * @param {String/Date} date The date or valid date string
42040 setValue : function(date){
42041 if (this.hiddenField) {
42042 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42044 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42045 // make sure the value field is always stored as a date..
42046 this.value = this.parseDate(date);
42052 parseDate : function(value){
42054 if (value instanceof Date) {
42055 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42062 if(!value || value instanceof Date){
42065 var v = Date.parseDate(value, this.format);
42066 if (!v && this.useIso) {
42067 v = Date.parseDate(value, 'Y-m-d');
42069 if(!v && this.altFormats){
42070 if(!this.altFormatsArray){
42071 this.altFormatsArray = this.altFormats.split("|");
42073 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42074 v = Date.parseDate(value, this.altFormatsArray[i]);
42077 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42084 formatDate : function(date, fmt){
42085 return (!date || !(date instanceof Date)) ?
42086 date : date.dateFormat(fmt || this.format);
42091 select: function(m, d){
42094 this.fireEvent('select', this, d);
42096 show : function(){ // retain focus styling
42100 this.focus.defer(10, this);
42101 var ml = this.menuListeners;
42102 this.menu.un("select", ml.select, this);
42103 this.menu.un("show", ml.show, this);
42104 this.menu.un("hide", ml.hide, this);
42109 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42110 onTriggerClick : function(){
42114 if(this.menu == null){
42115 this.menu = new Roo.menu.DateMenu();
42117 Roo.apply(this.menu.picker, {
42118 showClear: this.allowBlank,
42119 minDate : this.minValue,
42120 maxDate : this.maxValue,
42121 disabledDatesRE : this.ddMatch,
42122 disabledDatesText : this.disabledDatesText,
42123 disabledDays : this.disabledDays,
42124 disabledDaysText : this.disabledDaysText,
42125 format : this.useIso ? 'Y-m-d' : this.format,
42126 minText : String.format(this.minText, this.formatDate(this.minValue)),
42127 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42129 this.menu.on(Roo.apply({}, this.menuListeners, {
42132 this.menu.picker.setValue(this.getValue() || new Date());
42133 this.menu.show(this.el, "tl-bl?");
42136 beforeBlur : function(){
42137 var v = this.parseDate(this.getRawValue());
42147 isDirty : function() {
42148 if(this.disabled) {
42152 if(typeof(this.startValue) === 'undefined'){
42156 return String(this.getValue()) !== String(this.startValue);
42160 cleanLeadingSpace : function(e)
42167 * Ext JS Library 1.1.1
42168 * Copyright(c) 2006-2007, Ext JS, LLC.
42170 * Originally Released Under LGPL - original licence link has changed is not relivant.
42173 * <script type="text/javascript">
42177 * @class Roo.form.MonthField
42178 * @extends Roo.form.TriggerField
42179 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42181 * Create a new MonthField
42182 * @param {Object} config
42184 Roo.form.MonthField = function(config){
42186 Roo.form.MonthField.superclass.constructor.call(this, config);
42192 * Fires when a date is selected
42193 * @param {Roo.form.MonthFieeld} combo This combo box
42194 * @param {Date} date The date selected
42201 if(typeof this.minValue == "string") {
42202 this.minValue = this.parseDate(this.minValue);
42204 if(typeof this.maxValue == "string") {
42205 this.maxValue = this.parseDate(this.maxValue);
42207 this.ddMatch = null;
42208 if(this.disabledDates){
42209 var dd = this.disabledDates;
42211 for(var i = 0; i < dd.length; i++){
42213 if(i != dd.length-1) {
42217 this.ddMatch = new RegExp(re + ")");
42221 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
42223 * @cfg {String} format
42224 * The default date format string which can be overriden for localization support. The format must be
42225 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42229 * @cfg {String} altFormats
42230 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42231 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42233 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42235 * @cfg {Array} disabledDays
42236 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42238 disabledDays : [0,1,2,3,4,5,6],
42240 * @cfg {String} disabledDaysText
42241 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42243 disabledDaysText : "Disabled",
42245 * @cfg {Array} disabledDates
42246 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42247 * expression so they are very powerful. Some examples:
42249 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42250 * <li>["03/08", "09/16"] would disable those days for every year</li>
42251 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42252 * <li>["03/../2006"] would disable every day in March 2006</li>
42253 * <li>["^03"] would disable every day in every March</li>
42255 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42256 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42258 disabledDates : null,
42260 * @cfg {String} disabledDatesText
42261 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42263 disabledDatesText : "Disabled",
42265 * @cfg {Date/String} minValue
42266 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42267 * valid format (defaults to null).
42271 * @cfg {Date/String} maxValue
42272 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42273 * valid format (defaults to null).
42277 * @cfg {String} minText
42278 * The error text to display when the date in the cell is before minValue (defaults to
42279 * 'The date in this field must be after {minValue}').
42281 minText : "The date in this field must be equal to or after {0}",
42283 * @cfg {String} maxTextf
42284 * The error text to display when the date in the cell is after maxValue (defaults to
42285 * 'The date in this field must be before {maxValue}').
42287 maxText : "The date in this field must be equal to or before {0}",
42289 * @cfg {String} invalidText
42290 * The error text to display when the date in the field is invalid (defaults to
42291 * '{value} is not a valid date - it must be in the format {format}').
42293 invalidText : "{0} is not a valid date - it must be in the format {1}",
42295 * @cfg {String} triggerClass
42296 * An additional CSS class used to style the trigger button. The trigger will always get the
42297 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42298 * which displays a calendar icon).
42300 triggerClass : 'x-form-date-trigger',
42304 * @cfg {Boolean} useIso
42305 * if enabled, then the date field will use a hidden field to store the
42306 * real value as iso formated date. default (true)
42310 * @cfg {String/Object} autoCreate
42311 * A DomHelper element spec, or true for a default element spec (defaults to
42312 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42315 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42318 hiddenField: false,
42320 hideMonthPicker : false,
42322 onRender : function(ct, position)
42324 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42326 this.el.dom.removeAttribute('name');
42327 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42329 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42330 // prevent input submission
42331 this.hiddenName = this.name;
42338 validateValue : function(value)
42340 value = this.formatDate(value);
42341 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42344 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42347 var svalue = value;
42348 value = this.parseDate(value);
42350 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42353 var time = value.getTime();
42354 if(this.minValue && time < this.minValue.getTime()){
42355 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42358 if(this.maxValue && time > this.maxValue.getTime()){
42359 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42362 /*if(this.disabledDays){
42363 var day = value.getDay();
42364 for(var i = 0; i < this.disabledDays.length; i++) {
42365 if(day === this.disabledDays[i]){
42366 this.markInvalid(this.disabledDaysText);
42372 var fvalue = this.formatDate(value);
42373 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42374 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42382 // Provides logic to override the default TriggerField.validateBlur which just returns true
42383 validateBlur : function(){
42384 return !this.menu || !this.menu.isVisible();
42388 * Returns the current date value of the date field.
42389 * @return {Date} The date value
42391 getValue : function(){
42395 return this.hiddenField ?
42396 this.hiddenField.value :
42397 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42401 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42402 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42403 * (the default format used is "m/d/y").
42406 //All of these calls set the same date value (May 4, 2006)
42408 //Pass a date object:
42409 var dt = new Date('5/4/06');
42410 monthField.setValue(dt);
42412 //Pass a date string (default format):
42413 monthField.setValue('5/4/06');
42415 //Pass a date string (custom format):
42416 monthField.format = 'Y-m-d';
42417 monthField.setValue('2006-5-4');
42419 * @param {String/Date} date The date or valid date string
42421 setValue : function(date){
42422 Roo.log('month setValue' + date);
42423 // can only be first of month..
42425 var val = this.parseDate(date);
42427 if (this.hiddenField) {
42428 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42430 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42431 this.value = this.parseDate(date);
42435 parseDate : function(value){
42436 if(!value || value instanceof Date){
42437 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42440 var v = Date.parseDate(value, this.format);
42441 if (!v && this.useIso) {
42442 v = Date.parseDate(value, 'Y-m-d');
42446 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42450 if(!v && this.altFormats){
42451 if(!this.altFormatsArray){
42452 this.altFormatsArray = this.altFormats.split("|");
42454 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42455 v = Date.parseDate(value, this.altFormatsArray[i]);
42462 formatDate : function(date, fmt){
42463 return (!date || !(date instanceof Date)) ?
42464 date : date.dateFormat(fmt || this.format);
42469 select: function(m, d){
42471 this.fireEvent('select', this, d);
42473 show : function(){ // retain focus styling
42477 this.focus.defer(10, this);
42478 var ml = this.menuListeners;
42479 this.menu.un("select", ml.select, this);
42480 this.menu.un("show", ml.show, this);
42481 this.menu.un("hide", ml.hide, this);
42485 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42486 onTriggerClick : function(){
42490 if(this.menu == null){
42491 this.menu = new Roo.menu.DateMenu();
42495 Roo.apply(this.menu.picker, {
42497 showClear: this.allowBlank,
42498 minDate : this.minValue,
42499 maxDate : this.maxValue,
42500 disabledDatesRE : this.ddMatch,
42501 disabledDatesText : this.disabledDatesText,
42503 format : this.useIso ? 'Y-m-d' : this.format,
42504 minText : String.format(this.minText, this.formatDate(this.minValue)),
42505 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42508 this.menu.on(Roo.apply({}, this.menuListeners, {
42516 // hide month picker get's called when we called by 'before hide';
42518 var ignorehide = true;
42519 p.hideMonthPicker = function(disableAnim){
42523 if(this.monthPicker){
42524 Roo.log("hideMonthPicker called");
42525 if(disableAnim === true){
42526 this.monthPicker.hide();
42528 this.monthPicker.slideOut('t', {duration:.2});
42529 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42530 p.fireEvent("select", this, this.value);
42536 Roo.log('picker set value');
42537 Roo.log(this.getValue());
42538 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42539 m.show(this.el, 'tl-bl?');
42540 ignorehide = false;
42541 // this will trigger hideMonthPicker..
42544 // hidden the day picker
42545 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42551 p.showMonthPicker.defer(100, p);
42557 beforeBlur : function(){
42558 var v = this.parseDate(this.getRawValue());
42564 /** @cfg {Boolean} grow @hide */
42565 /** @cfg {Number} growMin @hide */
42566 /** @cfg {Number} growMax @hide */
42573 * Ext JS Library 1.1.1
42574 * Copyright(c) 2006-2007, Ext JS, LLC.
42576 * Originally Released Under LGPL - original licence link has changed is not relivant.
42579 * <script type="text/javascript">
42584 * @class Roo.form.ComboBox
42585 * @extends Roo.form.TriggerField
42586 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42588 * Create a new ComboBox.
42589 * @param {Object} config Configuration options
42591 Roo.form.ComboBox = function(config){
42592 Roo.form.ComboBox.superclass.constructor.call(this, config);
42596 * Fires when the dropdown list is expanded
42597 * @param {Roo.form.ComboBox} combo This combo box
42602 * Fires when the dropdown list is collapsed
42603 * @param {Roo.form.ComboBox} combo This combo box
42607 * @event beforeselect
42608 * Fires before a list item is selected. Return false to cancel the selection.
42609 * @param {Roo.form.ComboBox} combo This combo box
42610 * @param {Roo.data.Record} record The data record returned from the underlying store
42611 * @param {Number} index The index of the selected item in the dropdown list
42613 'beforeselect' : true,
42616 * Fires when a list item is selected
42617 * @param {Roo.form.ComboBox} combo This combo box
42618 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42619 * @param {Number} index The index of the selected item in the dropdown list
42623 * @event beforequery
42624 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42625 * The event object passed has these properties:
42626 * @param {Roo.form.ComboBox} combo This combo box
42627 * @param {String} query The query
42628 * @param {Boolean} forceAll true to force "all" query
42629 * @param {Boolean} cancel true to cancel the query
42630 * @param {Object} e The query event object
42632 'beforequery': true,
42635 * Fires when the 'add' icon is pressed (add a listener to enable add button)
42636 * @param {Roo.form.ComboBox} combo This combo box
42641 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42642 * @param {Roo.form.ComboBox} combo This combo box
42643 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42649 if(this.transform){
42650 this.allowDomMove = false;
42651 var s = Roo.getDom(this.transform);
42652 if(!this.hiddenName){
42653 this.hiddenName = s.name;
42656 this.mode = 'local';
42657 var d = [], opts = s.options;
42658 for(var i = 0, len = opts.length;i < len; i++){
42660 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42662 this.value = value;
42664 d.push([value, o.text]);
42666 this.store = new Roo.data.SimpleStore({
42668 fields: ['value', 'text'],
42671 this.valueField = 'value';
42672 this.displayField = 'text';
42674 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42675 if(!this.lazyRender){
42676 this.target = true;
42677 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42678 s.parentNode.removeChild(s); // remove it
42679 this.render(this.el.parentNode);
42681 s.parentNode.removeChild(s); // remove it
42686 this.store = Roo.factory(this.store, Roo.data);
42689 this.selectedIndex = -1;
42690 if(this.mode == 'local'){
42691 if(config.queryDelay === undefined){
42692 this.queryDelay = 10;
42694 if(config.minChars === undefined){
42700 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42702 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42705 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42706 * rendering into an Roo.Editor, defaults to false)
42709 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42710 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42713 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42716 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42717 * the dropdown list (defaults to undefined, with no header element)
42721 * @cfg {String/Roo.Template} tpl The template to use to render the output
42725 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42727 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42729 listWidth: undefined,
42731 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42732 * mode = 'remote' or 'text' if mode = 'local')
42734 displayField: undefined,
42736 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42737 * mode = 'remote' or 'value' if mode = 'local').
42738 * Note: use of a valueField requires the user make a selection
42739 * in order for a value to be mapped.
42741 valueField: undefined,
42745 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42746 * field's data value (defaults to the underlying DOM element's name)
42748 hiddenName: undefined,
42750 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42754 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42756 selectedClass: 'x-combo-selected',
42758 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
42759 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42760 * which displays a downward arrow icon).
42762 triggerClass : 'x-form-arrow-trigger',
42764 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42768 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42769 * anchor positions (defaults to 'tl-bl')
42771 listAlign: 'tl-bl?',
42773 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42777 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
42778 * query specified by the allQuery config option (defaults to 'query')
42780 triggerAction: 'query',
42782 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42783 * (defaults to 4, does not apply if editable = false)
42787 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42788 * delay (typeAheadDelay) if it matches a known value (defaults to false)
42792 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42793 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42797 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42798 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
42802 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
42803 * when editable = true (defaults to false)
42805 selectOnFocus:false,
42807 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42809 queryParam: 'query',
42811 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
42812 * when mode = 'remote' (defaults to 'Loading...')
42814 loadingText: 'Loading...',
42816 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42820 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42824 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42825 * traditional select (defaults to true)
42829 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42833 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42837 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42838 * listWidth has a higher value)
42842 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42843 * allow the user to set arbitrary text into the field (defaults to false)
42845 forceSelection:false,
42847 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42848 * if typeAhead = true (defaults to 250)
42850 typeAheadDelay : 250,
42852 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42853 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42855 valueNotFoundText : undefined,
42857 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42859 blockFocus : false,
42862 * @cfg {Boolean} disableClear Disable showing of clear button.
42864 disableClear : false,
42866 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
42868 alwaysQuery : false,
42874 // element that contains real text value.. (when hidden is used..)
42877 onRender : function(ct, position)
42879 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42881 if(this.hiddenName){
42882 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
42884 this.hiddenField.value =
42885 this.hiddenValue !== undefined ? this.hiddenValue :
42886 this.value !== undefined ? this.value : '';
42888 // prevent input submission
42889 this.el.dom.removeAttribute('name');
42895 this.el.dom.setAttribute('autocomplete', 'off');
42898 var cls = 'x-combo-list';
42900 this.list = new Roo.Layer({
42901 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42904 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42905 this.list.setWidth(lw);
42906 this.list.swallowEvent('mousewheel');
42907 this.assetHeight = 0;
42910 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42911 this.assetHeight += this.header.getHeight();
42914 this.innerList = this.list.createChild({cls:cls+'-inner'});
42915 this.innerList.on('mouseover', this.onViewOver, this);
42916 this.innerList.on('mousemove', this.onViewMove, this);
42917 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42919 if(this.allowBlank && !this.pageSize && !this.disableClear){
42920 this.footer = this.list.createChild({cls:cls+'-ft'});
42921 this.pageTb = new Roo.Toolbar(this.footer);
42925 this.footer = this.list.createChild({cls:cls+'-ft'});
42926 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42927 {pageSize: this.pageSize});
42931 if (this.pageTb && this.allowBlank && !this.disableClear) {
42933 this.pageTb.add(new Roo.Toolbar.Fill(), {
42934 cls: 'x-btn-icon x-btn-clear',
42936 handler: function()
42939 _this.clearValue();
42940 _this.onSelect(false, -1);
42945 this.assetHeight += this.footer.getHeight();
42950 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42953 this.view = new Roo.View(this.innerList, this.tpl, {
42956 selectedClass: this.selectedClass
42959 this.view.on('click', this.onViewClick, this);
42961 this.store.on('beforeload', this.onBeforeLoad, this);
42962 this.store.on('load', this.onLoad, this);
42963 this.store.on('loadexception', this.onLoadException, this);
42965 if(this.resizable){
42966 this.resizer = new Roo.Resizable(this.list, {
42967 pinned:true, handles:'se'
42969 this.resizer.on('resize', function(r, w, h){
42970 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42971 this.listWidth = w;
42972 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42973 this.restrictHeight();
42975 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42977 if(!this.editable){
42978 this.editable = true;
42979 this.setEditable(false);
42983 if (typeof(this.events.add.listeners) != 'undefined') {
42985 this.addicon = this.wrap.createChild(
42986 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
42988 this.addicon.on('click', function(e) {
42989 this.fireEvent('add', this);
42992 if (typeof(this.events.edit.listeners) != 'undefined') {
42994 this.editicon = this.wrap.createChild(
42995 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
42996 if (this.addicon) {
42997 this.editicon.setStyle('margin-left', '40px');
42999 this.editicon.on('click', function(e) {
43001 // we fire even if inothing is selected..
43002 this.fireEvent('edit', this, this.lastData );
43012 initEvents : function(){
43013 Roo.form.ComboBox.superclass.initEvents.call(this);
43015 this.keyNav = new Roo.KeyNav(this.el, {
43016 "up" : function(e){
43017 this.inKeyMode = true;
43021 "down" : function(e){
43022 if(!this.isExpanded()){
43023 this.onTriggerClick();
43025 this.inKeyMode = true;
43030 "enter" : function(e){
43031 this.onViewClick();
43035 "esc" : function(e){
43039 "tab" : function(e){
43040 this.onViewClick(false);
43041 this.fireEvent("specialkey", this, e);
43047 doRelay : function(foo, bar, hname){
43048 if(hname == 'down' || this.scope.isExpanded()){
43049 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43056 this.queryDelay = Math.max(this.queryDelay || 10,
43057 this.mode == 'local' ? 10 : 250);
43058 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43059 if(this.typeAhead){
43060 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43062 if(this.editable !== false){
43063 this.el.on("keyup", this.onKeyUp, this);
43065 if(this.forceSelection){
43066 this.on('blur', this.doForce, this);
43070 onDestroy : function(){
43072 this.view.setStore(null);
43073 this.view.el.removeAllListeners();
43074 this.view.el.remove();
43075 this.view.purgeListeners();
43078 this.list.destroy();
43081 this.store.un('beforeload', this.onBeforeLoad, this);
43082 this.store.un('load', this.onLoad, this);
43083 this.store.un('loadexception', this.onLoadException, this);
43085 Roo.form.ComboBox.superclass.onDestroy.call(this);
43089 fireKey : function(e){
43090 if(e.isNavKeyPress() && !this.list.isVisible()){
43091 this.fireEvent("specialkey", this, e);
43096 onResize: function(w, h){
43097 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43099 if(typeof w != 'number'){
43100 // we do not handle it!?!?
43103 var tw = this.trigger.getWidth();
43104 tw += this.addicon ? this.addicon.getWidth() : 0;
43105 tw += this.editicon ? this.editicon.getWidth() : 0;
43107 this.el.setWidth( this.adjustWidth('input', x));
43109 this.trigger.setStyle('left', x+'px');
43111 if(this.list && this.listWidth === undefined){
43112 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43113 this.list.setWidth(lw);
43114 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43122 * Allow or prevent the user from directly editing the field text. If false is passed,
43123 * the user will only be able to select from the items defined in the dropdown list. This method
43124 * is the runtime equivalent of setting the 'editable' config option at config time.
43125 * @param {Boolean} value True to allow the user to directly edit the field text
43127 setEditable : function(value){
43128 if(value == this.editable){
43131 this.editable = value;
43133 this.el.dom.setAttribute('readOnly', true);
43134 this.el.on('mousedown', this.onTriggerClick, this);
43135 this.el.addClass('x-combo-noedit');
43137 this.el.dom.setAttribute('readOnly', false);
43138 this.el.un('mousedown', this.onTriggerClick, this);
43139 this.el.removeClass('x-combo-noedit');
43144 onBeforeLoad : function(){
43145 if(!this.hasFocus){
43148 this.innerList.update(this.loadingText ?
43149 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43150 this.restrictHeight();
43151 this.selectedIndex = -1;
43155 onLoad : function(){
43156 if(!this.hasFocus){
43159 if(this.store.getCount() > 0){
43161 this.restrictHeight();
43162 if(this.lastQuery == this.allQuery){
43164 this.el.dom.select();
43166 if(!this.selectByValue(this.value, true)){
43167 this.select(0, true);
43171 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43172 this.taTask.delay(this.typeAheadDelay);
43176 this.onEmptyResults();
43181 onLoadException : function()
43184 Roo.log(this.store.reader.jsonData);
43185 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43186 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43192 onTypeAhead : function(){
43193 if(this.store.getCount() > 0){
43194 var r = this.store.getAt(0);
43195 var newValue = r.data[this.displayField];
43196 var len = newValue.length;
43197 var selStart = this.getRawValue().length;
43198 if(selStart != len){
43199 this.setRawValue(newValue);
43200 this.selectText(selStart, newValue.length);
43206 onSelect : function(record, index){
43207 if(this.fireEvent('beforeselect', this, record, index) !== false){
43208 this.setFromData(index > -1 ? record.data : false);
43210 this.fireEvent('select', this, record, index);
43215 * Returns the currently selected field value or empty string if no value is set.
43216 * @return {String} value The selected value
43218 getValue : function(){
43219 if(this.valueField){
43220 return typeof this.value != 'undefined' ? this.value : '';
43222 return Roo.form.ComboBox.superclass.getValue.call(this);
43226 * Clears any text/value currently set in the field
43228 clearValue : function(){
43229 if(this.hiddenField){
43230 this.hiddenField.value = '';
43233 this.setRawValue('');
43234 this.lastSelectionText = '';
43239 * Sets the specified value into the field. If the value finds a match, the corresponding record text
43240 * will be displayed in the field. If the value does not match the data value of an existing item,
43241 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43242 * Otherwise the field will be blank (although the value will still be set).
43243 * @param {String} value The value to match
43245 setValue : function(v){
43247 if(this.valueField){
43248 var r = this.findRecord(this.valueField, v);
43250 text = r.data[this.displayField];
43251 }else if(this.valueNotFoundText !== undefined){
43252 text = this.valueNotFoundText;
43255 this.lastSelectionText = text;
43256 if(this.hiddenField){
43257 this.hiddenField.value = v;
43259 Roo.form.ComboBox.superclass.setValue.call(this, text);
43263 * @property {Object} the last set data for the element
43268 * Sets the value of the field based on a object which is related to the record format for the store.
43269 * @param {Object} value the value to set as. or false on reset?
43271 setFromData : function(o){
43272 var dv = ''; // display value
43273 var vv = ''; // value value..
43275 if (this.displayField) {
43276 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43278 // this is an error condition!!!
43279 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
43282 if(this.valueField){
43283 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43285 if(this.hiddenField){
43286 this.hiddenField.value = vv;
43288 this.lastSelectionText = dv;
43289 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43293 // no hidden field.. - we store the value in 'value', but still display
43294 // display field!!!!
43295 this.lastSelectionText = dv;
43296 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43302 reset : function(){
43303 // overridden so that last data is reset..
43304 this.setValue(this.resetValue);
43305 this.originalValue = this.getValue();
43306 this.clearInvalid();
43307 this.lastData = false;
43309 this.view.clearSelections();
43313 findRecord : function(prop, value){
43315 if(this.store.getCount() > 0){
43316 this.store.each(function(r){
43317 if(r.data[prop] == value){
43327 getName: function()
43329 // returns hidden if it's set..
43330 if (!this.rendered) {return ''};
43331 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
43335 onViewMove : function(e, t){
43336 this.inKeyMode = false;
43340 onViewOver : function(e, t){
43341 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43344 var item = this.view.findItemFromChild(t);
43346 var index = this.view.indexOf(item);
43347 this.select(index, false);
43352 onViewClick : function(doFocus)
43354 var index = this.view.getSelectedIndexes()[0];
43355 var r = this.store.getAt(index);
43357 this.onSelect(r, index);
43359 if(doFocus !== false && !this.blockFocus){
43365 restrictHeight : function(){
43366 this.innerList.dom.style.height = '';
43367 var inner = this.innerList.dom;
43368 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43369 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43370 this.list.beginUpdate();
43371 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43372 this.list.alignTo(this.el, this.listAlign);
43373 this.list.endUpdate();
43377 onEmptyResults : function(){
43382 * Returns true if the dropdown list is expanded, else false.
43384 isExpanded : function(){
43385 return this.list.isVisible();
43389 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43390 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43391 * @param {String} value The data value of the item to select
43392 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43393 * selected item if it is not currently in view (defaults to true)
43394 * @return {Boolean} True if the value matched an item in the list, else false
43396 selectByValue : function(v, scrollIntoView){
43397 if(v !== undefined && v !== null){
43398 var r = this.findRecord(this.valueField || this.displayField, v);
43400 this.select(this.store.indexOf(r), scrollIntoView);
43408 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43409 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43410 * @param {Number} index The zero-based index of the list item to select
43411 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43412 * selected item if it is not currently in view (defaults to true)
43414 select : function(index, scrollIntoView){
43415 this.selectedIndex = index;
43416 this.view.select(index);
43417 if(scrollIntoView !== false){
43418 var el = this.view.getNode(index);
43420 this.innerList.scrollChildIntoView(el, false);
43426 selectNext : function(){
43427 var ct = this.store.getCount();
43429 if(this.selectedIndex == -1){
43431 }else if(this.selectedIndex < ct-1){
43432 this.select(this.selectedIndex+1);
43438 selectPrev : function(){
43439 var ct = this.store.getCount();
43441 if(this.selectedIndex == -1){
43443 }else if(this.selectedIndex != 0){
43444 this.select(this.selectedIndex-1);
43450 onKeyUp : function(e){
43451 if(this.editable !== false && !e.isSpecialKey()){
43452 this.lastKey = e.getKey();
43453 this.dqTask.delay(this.queryDelay);
43458 validateBlur : function(){
43459 return !this.list || !this.list.isVisible();
43463 initQuery : function(){
43464 this.doQuery(this.getRawValue());
43468 doForce : function(){
43469 if(this.el.dom.value.length > 0){
43470 this.el.dom.value =
43471 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43477 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
43478 * query allowing the query action to be canceled if needed.
43479 * @param {String} query The SQL query to execute
43480 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43481 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
43482 * saved in the current store (defaults to false)
43484 doQuery : function(q, forceAll){
43485 if(q === undefined || q === null){
43490 forceAll: forceAll,
43494 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43498 forceAll = qe.forceAll;
43499 if(forceAll === true || (q.length >= this.minChars)){
43500 if(this.lastQuery != q || this.alwaysQuery){
43501 this.lastQuery = q;
43502 if(this.mode == 'local'){
43503 this.selectedIndex = -1;
43505 this.store.clearFilter();
43507 this.store.filter(this.displayField, q);
43511 this.store.baseParams[this.queryParam] = q;
43513 params: this.getParams(q)
43518 this.selectedIndex = -1;
43525 getParams : function(q){
43527 //p[this.queryParam] = q;
43530 p.limit = this.pageSize;
43536 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43538 collapse : function(){
43539 if(!this.isExpanded()){
43543 Roo.get(document).un('mousedown', this.collapseIf, this);
43544 Roo.get(document).un('mousewheel', this.collapseIf, this);
43545 if (!this.editable) {
43546 Roo.get(document).un('keydown', this.listKeyPress, this);
43548 this.fireEvent('collapse', this);
43552 collapseIf : function(e){
43553 if(!e.within(this.wrap) && !e.within(this.list)){
43559 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43561 expand : function(){
43562 if(this.isExpanded() || !this.hasFocus){
43565 this.list.alignTo(this.el, this.listAlign);
43567 Roo.get(document).on('mousedown', this.collapseIf, this);
43568 Roo.get(document).on('mousewheel', this.collapseIf, this);
43569 if (!this.editable) {
43570 Roo.get(document).on('keydown', this.listKeyPress, this);
43573 this.fireEvent('expand', this);
43577 // Implements the default empty TriggerField.onTriggerClick function
43578 onTriggerClick : function(){
43582 if(this.isExpanded()){
43584 if (!this.blockFocus) {
43589 this.hasFocus = true;
43590 if(this.triggerAction == 'all') {
43591 this.doQuery(this.allQuery, true);
43593 this.doQuery(this.getRawValue());
43595 if (!this.blockFocus) {
43600 listKeyPress : function(e)
43602 //Roo.log('listkeypress');
43603 // scroll to first matching element based on key pres..
43604 if (e.isSpecialKey()) {
43607 var k = String.fromCharCode(e.getKey()).toUpperCase();
43610 var csel = this.view.getSelectedNodes();
43611 var cselitem = false;
43613 var ix = this.view.indexOf(csel[0]);
43614 cselitem = this.store.getAt(ix);
43615 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43621 this.store.each(function(v) {
43623 // start at existing selection.
43624 if (cselitem.id == v.id) {
43630 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43631 match = this.store.indexOf(v);
43636 if (match === false) {
43637 return true; // no more action?
43640 this.view.select(match);
43641 var sn = Roo.get(this.view.getSelectedNodes()[0]);
43642 sn.scrollIntoView(sn.dom.parentNode, false);
43646 * @cfg {Boolean} grow
43650 * @cfg {Number} growMin
43654 * @cfg {Number} growMax
43662 * Copyright(c) 2010-2012, Roo J Solutions Limited
43669 * @class Roo.form.ComboBoxArray
43670 * @extends Roo.form.TextField
43671 * A facebook style adder... for lists of email / people / countries etc...
43672 * pick multiple items from a combo box, and shows each one.
43674 * Fred [x] Brian [x] [Pick another |v]
43677 * For this to work: it needs various extra information
43678 * - normal combo problay has
43680 * + displayField, valueField
43682 * For our purpose...
43685 * If we change from 'extends' to wrapping...
43692 * Create a new ComboBoxArray.
43693 * @param {Object} config Configuration options
43697 Roo.form.ComboBoxArray = function(config)
43701 * @event beforeremove
43702 * Fires before remove the value from the list
43703 * @param {Roo.form.ComboBoxArray} _self This combo box array
43704 * @param {Roo.form.ComboBoxArray.Item} item removed item
43706 'beforeremove' : true,
43709 * Fires when remove the value from the list
43710 * @param {Roo.form.ComboBoxArray} _self This combo box array
43711 * @param {Roo.form.ComboBoxArray.Item} item removed item
43718 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43720 this.items = new Roo.util.MixedCollection(false);
43722 // construct the child combo...
43732 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43735 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43740 // behavies liek a hiddne field
43741 inputType: 'hidden',
43743 * @cfg {Number} width The width of the box that displays the selected element
43750 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
43754 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
43756 hiddenName : false,
43758 * @cfg {String} seperator The value seperator normally ','
43762 // private the array of items that are displayed..
43764 // private - the hidden field el.
43766 // private - the filed el..
43769 //validateValue : function() { return true; }, // all values are ok!
43770 //onAddClick: function() { },
43772 onRender : function(ct, position)
43775 // create the standard hidden element
43776 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43779 // give fake names to child combo;
43780 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43781 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43783 this.combo = Roo.factory(this.combo, Roo.form);
43784 this.combo.onRender(ct, position);
43785 if (typeof(this.combo.width) != 'undefined') {
43786 this.combo.onResize(this.combo.width,0);
43789 this.combo.initEvents();
43791 // assigned so form know we need to do this..
43792 this.store = this.combo.store;
43793 this.valueField = this.combo.valueField;
43794 this.displayField = this.combo.displayField ;
43797 this.combo.wrap.addClass('x-cbarray-grp');
43799 var cbwrap = this.combo.wrap.createChild(
43800 {tag: 'div', cls: 'x-cbarray-cb'},
43805 this.hiddenEl = this.combo.wrap.createChild({
43806 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
43808 this.el = this.combo.wrap.createChild({
43809 tag: 'input', type:'hidden' , name: this.name, value : ''
43811 // this.el.dom.removeAttribute("name");
43814 this.outerWrap = this.combo.wrap;
43815 this.wrap = cbwrap;
43817 this.outerWrap.setWidth(this.width);
43818 this.outerWrap.dom.removeChild(this.el.dom);
43820 this.wrap.dom.appendChild(this.el.dom);
43821 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43822 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43824 this.combo.trigger.setStyle('position','relative');
43825 this.combo.trigger.setStyle('left', '0px');
43826 this.combo.trigger.setStyle('top', '2px');
43828 this.combo.el.setStyle('vertical-align', 'text-bottom');
43830 //this.trigger.setStyle('vertical-align', 'top');
43832 // this should use the code from combo really... on('add' ....)
43836 this.adder = this.outerWrap.createChild(
43837 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
43839 this.adder.on('click', function(e) {
43840 _t.fireEvent('adderclick', this, e);
43844 //this.adder.on('click', this.onAddClick, _t);
43847 this.combo.on('select', function(cb, rec, ix) {
43848 this.addItem(rec.data);
43851 cb.el.dom.value = '';
43852 //cb.lastData = rec.data;
43861 getName: function()
43863 // returns hidden if it's set..
43864 if (!this.rendered) {return ''};
43865 return this.hiddenName ? this.hiddenName : this.name;
43870 onResize: function(w, h){
43873 // not sure if this is needed..
43874 //this.combo.onResize(w,h);
43876 if(typeof w != 'number'){
43877 // we do not handle it!?!?
43880 var tw = this.combo.trigger.getWidth();
43881 tw += this.addicon ? this.addicon.getWidth() : 0;
43882 tw += this.editicon ? this.editicon.getWidth() : 0;
43884 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43886 this.combo.trigger.setStyle('left', '0px');
43888 if(this.list && this.listWidth === undefined){
43889 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43890 this.list.setWidth(lw);
43891 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43898 addItem: function(rec)
43900 var valueField = this.combo.valueField;
43901 var displayField = this.combo.displayField;
43903 if (this.items.indexOfKey(rec[valueField]) > -1) {
43904 //console.log("GOT " + rec.data.id);
43908 var x = new Roo.form.ComboBoxArray.Item({
43909 //id : rec[this.idField],
43911 displayField : displayField ,
43912 tipField : displayField ,
43916 this.items.add(rec[valueField],x);
43917 // add it before the element..
43918 this.updateHiddenEl();
43919 x.render(this.outerWrap, this.wrap.dom);
43920 // add the image handler..
43923 updateHiddenEl : function()
43926 if (!this.hiddenEl) {
43930 var idField = this.combo.valueField;
43932 this.items.each(function(f) {
43933 ar.push(f.data[idField]);
43935 this.hiddenEl.dom.value = ar.join(this.seperator);
43941 this.items.clear();
43943 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43947 this.el.dom.value = '';
43948 if (this.hiddenEl) {
43949 this.hiddenEl.dom.value = '';
43953 getValue: function()
43955 return this.hiddenEl ? this.hiddenEl.dom.value : '';
43957 setValue: function(v) // not a valid action - must use addItems..
43962 if (this.store.isLocal && (typeof(v) == 'string')) {
43963 // then we can use the store to find the values..
43964 // comma seperated at present.. this needs to allow JSON based encoding..
43965 this.hiddenEl.value = v;
43967 Roo.each(v.split(this.seperator), function(k) {
43968 Roo.log("CHECK " + this.valueField + ',' + k);
43969 var li = this.store.query(this.valueField, k);
43974 add[this.valueField] = k;
43975 add[this.displayField] = li.item(0).data[this.displayField];
43981 if (typeof(v) == 'object' ) {
43982 // then let's assume it's an array of objects..
43983 Roo.each(v, function(l) {
43985 if (typeof(l) == 'string') {
43987 add[this.valueField] = l;
43988 add[this.displayField] = l
43997 setFromData: function(v)
43999 // this recieves an object, if setValues is called.
44001 this.el.dom.value = v[this.displayField];
44002 this.hiddenEl.dom.value = v[this.valueField];
44003 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44006 var kv = v[this.valueField];
44007 var dv = v[this.displayField];
44008 kv = typeof(kv) != 'string' ? '' : kv;
44009 dv = typeof(dv) != 'string' ? '' : dv;
44012 var keys = kv.split(this.seperator);
44013 var display = dv.split(this.seperator);
44014 for (var i = 0 ; i < keys.length; i++) {
44016 add[this.valueField] = keys[i];
44017 add[this.displayField] = display[i];
44025 * Validates the combox array value
44026 * @return {Boolean} True if the value is valid, else false
44028 validate : function(){
44029 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44030 this.clearInvalid();
44036 validateValue : function(value){
44037 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44045 isDirty : function() {
44046 if(this.disabled) {
44051 var d = Roo.decode(String(this.originalValue));
44053 return String(this.getValue()) !== String(this.originalValue);
44056 var originalValue = [];
44058 for (var i = 0; i < d.length; i++){
44059 originalValue.push(d[i][this.valueField]);
44062 return String(this.getValue()) !== String(originalValue.join(this.seperator));
44071 * @class Roo.form.ComboBoxArray.Item
44072 * @extends Roo.BoxComponent
44073 * A selected item in the list
44074 * Fred [x] Brian [x] [Pick another |v]
44077 * Create a new item.
44078 * @param {Object} config Configuration options
44081 Roo.form.ComboBoxArray.Item = function(config) {
44082 config.id = Roo.id();
44083 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44086 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44089 displayField : false,
44093 defaultAutoCreate : {
44095 cls: 'x-cbarray-item',
44102 src : Roo.BLANK_IMAGE_URL ,
44110 onRender : function(ct, position)
44112 Roo.form.Field.superclass.onRender.call(this, ct, position);
44115 var cfg = this.getAutoCreate();
44116 this.el = ct.createChild(cfg, position);
44119 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44121 this.el.child('div').dom.innerHTML = this.cb.renderer ?
44122 this.cb.renderer(this.data) :
44123 String.format('{0}',this.data[this.displayField]);
44126 this.el.child('div').dom.setAttribute('qtip',
44127 String.format('{0}',this.data[this.tipField])
44130 this.el.child('img').on('click', this.remove, this);
44134 remove : function()
44136 if(this.cb.disabled){
44140 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44141 this.cb.items.remove(this);
44142 this.el.child('img').un('click', this.remove, this);
44144 this.cb.updateHiddenEl();
44146 this.cb.fireEvent('remove', this.cb, this);
44151 * RooJS Library 1.1.1
44152 * Copyright(c) 2008-2011 Alan Knowles
44159 * @class Roo.form.ComboNested
44160 * @extends Roo.form.ComboBox
44161 * A combobox for that allows selection of nested items in a list,
44176 * Create a new ComboNested
44177 * @param {Object} config Configuration options
44179 Roo.form.ComboNested = function(config){
44180 Roo.form.ComboCheck.superclass.constructor.call(this, config);
44181 // should verify some data...
44183 // hiddenName = required..
44184 // displayField = required
44185 // valudField == required
44186 var req= [ 'hiddenName', 'displayField', 'valueField' ];
44188 Roo.each(req, function(e) {
44189 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44190 throw "Roo.form.ComboNested : missing value for: " + e;
44197 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44200 * @config {Number} max Number of columns to show
44205 list : null, // the outermost div..
44206 innerLists : null, // the
44210 loadingChildren : false,
44212 onRender : function(ct, position)
44214 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44216 if(this.hiddenName){
44217 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
44219 this.hiddenField.value =
44220 this.hiddenValue !== undefined ? this.hiddenValue :
44221 this.value !== undefined ? this.value : '';
44223 // prevent input submission
44224 this.el.dom.removeAttribute('name');
44230 this.el.dom.setAttribute('autocomplete', 'off');
44233 var cls = 'x-combo-list';
44235 this.list = new Roo.Layer({
44236 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44239 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44240 this.list.setWidth(lw);
44241 this.list.swallowEvent('mousewheel');
44242 this.assetHeight = 0;
44245 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44246 this.assetHeight += this.header.getHeight();
44248 this.innerLists = [];
44251 for (var i =0 ; i < this.maxColumns; i++) {
44252 this.onRenderList( cls, i);
44255 // always needs footer, as we are going to have an 'OK' button.
44256 this.footer = this.list.createChild({cls:cls+'-ft'});
44257 this.pageTb = new Roo.Toolbar(this.footer);
44262 handler: function()
44268 if ( this.allowBlank && !this.disableClear) {
44270 this.pageTb.add(new Roo.Toolbar.Fill(), {
44271 cls: 'x-btn-icon x-btn-clear',
44273 handler: function()
44276 _this.clearValue();
44277 _this.onSelect(false, -1);
44282 this.assetHeight += this.footer.getHeight();
44286 onRenderList : function ( cls, i)
44289 var lw = Math.floor(
44290 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44293 this.list.setWidth(lw); // default to '1'
44295 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44296 //il.on('mouseover', this.onViewOver, this, { list: i });
44297 //il.on('mousemove', this.onViewMove, this, { list: i });
44299 il.setStyle({ 'overflow-x' : 'hidden'});
44302 this.tpl = new Roo.Template({
44303 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44304 isEmpty: function (value, allValues) {
44306 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44307 return dl ? 'has-children' : 'no-children'
44312 var store = this.store;
44314 store = new Roo.data.SimpleStore({
44315 //fields : this.store.reader.meta.fields,
44316 reader : this.store.reader,
44320 this.stores[i] = store;
44322 var view = this.views[i] = new Roo.View(
44328 selectedClass: this.selectedClass
44331 view.getEl().setWidth(lw);
44332 view.getEl().setStyle({
44333 position: i < 1 ? 'relative' : 'absolute',
44335 left: (i * lw ) + 'px',
44336 display : i > 0 ? 'none' : 'block'
44338 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44339 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44340 //view.on('click', this.onViewClick, this, { list : i });
44342 store.on('beforeload', this.onBeforeLoad, this);
44343 store.on('load', this.onLoad, this, { list : i});
44344 store.on('loadexception', this.onLoadException, this);
44346 // hide the other vies..
44352 restrictHeight : function()
44355 Roo.each(this.innerLists, function(il,i) {
44356 var el = this.views[i].getEl();
44357 el.dom.style.height = '';
44358 var inner = el.dom;
44359 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44360 // only adjust heights on other ones..
44361 mh = Math.max(h, mh);
44364 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44365 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44372 this.list.beginUpdate();
44373 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44374 this.list.alignTo(this.el, this.listAlign);
44375 this.list.endUpdate();
44380 // -- store handlers..
44382 onBeforeLoad : function()
44384 if(!this.hasFocus){
44387 this.innerLists[0].update(this.loadingText ?
44388 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44389 this.restrictHeight();
44390 this.selectedIndex = -1;
44393 onLoad : function(a,b,c,d)
44395 if (!this.loadingChildren) {
44396 // then we are loading the top level. - hide the children
44397 for (var i = 1;i < this.views.length; i++) {
44398 this.views[i].getEl().setStyle({ display : 'none' });
44400 var lw = Math.floor(
44401 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44404 this.list.setWidth(lw); // default to '1'
44408 if(!this.hasFocus){
44412 if(this.store.getCount() > 0) {
44414 this.restrictHeight();
44416 this.onEmptyResults();
44419 if (!this.loadingChildren) {
44420 this.selectActive();
44423 this.stores[1].loadData([]);
44424 this.stores[2].loadData([]);
44433 onLoadException : function()
44436 Roo.log(this.store.reader.jsonData);
44437 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44438 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44443 // no cleaning of leading spaces on blur here.
44444 cleanLeadingSpace : function(e) { },
44447 onSelectChange : function (view, sels, opts )
44449 var ix = view.getSelectedIndexes();
44451 if (opts.list > this.maxColumns - 2) {
44452 if (view.store.getCount()< 1) {
44453 this.views[opts.list ].getEl().setStyle({ display : 'none' });
44457 // used to clear ?? but if we are loading unselected
44458 this.setFromData(view.store.getAt(ix[0]).data);
44467 // this get's fired when trigger opens..
44468 // this.setFromData({});
44469 var str = this.stores[opts.list+1];
44470 str.data.clear(); // removeall wihtout the fire events..
44474 var rec = view.store.getAt(ix[0]);
44476 this.setFromData(rec.data);
44477 this.fireEvent('select', this, rec, ix[0]);
44479 var lw = Math.floor(
44481 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44482 ) / this.maxColumns
44484 this.loadingChildren = true;
44485 this.stores[opts.list+1].loadDataFromChildren( rec );
44486 this.loadingChildren = false;
44487 var dl = this.stores[opts.list+1]. getTotalCount();
44489 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44491 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44492 for (var i = opts.list+2; i < this.views.length;i++) {
44493 this.views[i].getEl().setStyle({ display : 'none' });
44496 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44497 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44499 if (this.isLoading) {
44500 // this.selectActive(opts.list);
44508 onDoubleClick : function()
44510 this.collapse(); //??
44518 recordToStack : function(store, prop, value, stack)
44520 var cstore = new Roo.data.SimpleStore({
44521 //fields : this.store.reader.meta.fields, // we need array reader.. for
44522 reader : this.store.reader,
44526 var record = false;
44528 if(store.getCount() < 1){
44531 store.each(function(r){
44532 if(r.data[prop] == value){
44537 if (r.data.cn && r.data.cn.length) {
44538 cstore.loadDataFromChildren( r);
44539 var cret = _this.recordToStack(cstore, prop, value, stack);
44540 if (cret !== false) {
44549 if (record == false) {
44552 stack.unshift(srec);
44557 * find the stack of stores that match our value.
44562 selectActive : function ()
44564 // if store is not loaded, then we will need to wait for that to happen first.
44566 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44567 for (var i = 0; i < stack.length; i++ ) {
44568 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44580 * Ext JS Library 1.1.1
44581 * Copyright(c) 2006-2007, Ext JS, LLC.
44583 * Originally Released Under LGPL - original licence link has changed is not relivant.
44586 * <script type="text/javascript">
44589 * @class Roo.form.Checkbox
44590 * @extends Roo.form.Field
44591 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
44593 * Creates a new Checkbox
44594 * @param {Object} config Configuration options
44596 Roo.form.Checkbox = function(config){
44597 Roo.form.Checkbox.superclass.constructor.call(this, config);
44601 * Fires when the checkbox is checked or unchecked.
44602 * @param {Roo.form.Checkbox} this This checkbox
44603 * @param {Boolean} checked The new checked value
44609 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
44611 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44613 focusClass : undefined,
44615 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44617 fieldClass: "x-form-field",
44619 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44623 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44624 * {tag: "input", type: "checkbox", autocomplete: "off"})
44626 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44628 * @cfg {String} boxLabel The text that appears beside the checkbox
44632 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44636 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44638 valueOff: '0', // value when not checked..
44640 actionMode : 'viewEl',
44643 itemCls : 'x-menu-check-item x-form-item',
44644 groupClass : 'x-menu-group-item',
44645 inputType : 'hidden',
44648 inSetChecked: false, // check that we are not calling self...
44650 inputElement: false, // real input element?
44651 basedOn: false, // ????
44653 isFormField: true, // not sure where this is needed!!!!
44655 onResize : function(){
44656 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44657 if(!this.boxLabel){
44658 this.el.alignTo(this.wrap, 'c-c');
44662 initEvents : function(){
44663 Roo.form.Checkbox.superclass.initEvents.call(this);
44664 this.el.on("click", this.onClick, this);
44665 this.el.on("change", this.onClick, this);
44669 getResizeEl : function(){
44673 getPositionEl : function(){
44678 onRender : function(ct, position){
44679 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44681 if(this.inputValue !== undefined){
44682 this.el.dom.value = this.inputValue;
44685 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44686 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44687 var viewEl = this.wrap.createChild({
44688 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44689 this.viewEl = viewEl;
44690 this.wrap.on('click', this.onClick, this);
44692 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44693 this.el.on('propertychange', this.setFromHidden, this); //ie
44698 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44699 // viewEl.on('click', this.onClick, this);
44701 //if(this.checked){
44702 this.setChecked(this.checked);
44704 //this.checked = this.el.dom;
44710 initValue : Roo.emptyFn,
44713 * Returns the checked state of the checkbox.
44714 * @return {Boolean} True if checked, else false
44716 getValue : function(){
44718 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44720 return this.valueOff;
44725 onClick : function(){
44726 if (this.disabled) {
44729 this.setChecked(!this.checked);
44731 //if(this.el.dom.checked != this.checked){
44732 // this.setValue(this.el.dom.checked);
44737 * Sets the checked state of the checkbox.
44738 * On is always based on a string comparison between inputValue and the param.
44739 * @param {Boolean/String} value - the value to set
44740 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44742 setValue : function(v,suppressEvent){
44745 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44746 //if(this.el && this.el.dom){
44747 // this.el.dom.checked = this.checked;
44748 // this.el.dom.defaultChecked = this.checked;
44750 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44751 //this.fireEvent("check", this, this.checked);
44754 setChecked : function(state,suppressEvent)
44756 if (this.inSetChecked) {
44757 this.checked = state;
44763 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44765 this.checked = state;
44766 if(suppressEvent !== true){
44767 this.fireEvent('check', this, state);
44769 this.inSetChecked = true;
44770 this.el.dom.value = state ? this.inputValue : this.valueOff;
44771 this.inSetChecked = false;
44774 // handle setting of hidden value by some other method!!?!?
44775 setFromHidden: function()
44780 //console.log("SET FROM HIDDEN");
44781 //alert('setFrom hidden');
44782 this.setValue(this.el.dom.value);
44785 onDestroy : function()
44788 Roo.get(this.viewEl).remove();
44791 Roo.form.Checkbox.superclass.onDestroy.call(this);
44794 setBoxLabel : function(str)
44796 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44801 * Ext JS Library 1.1.1
44802 * Copyright(c) 2006-2007, Ext JS, LLC.
44804 * Originally Released Under LGPL - original licence link has changed is not relivant.
44807 * <script type="text/javascript">
44811 * @class Roo.form.Radio
44812 * @extends Roo.form.Checkbox
44813 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
44814 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44816 * Creates a new Radio
44817 * @param {Object} config Configuration options
44819 Roo.form.Radio = function(){
44820 Roo.form.Radio.superclass.constructor.apply(this, arguments);
44822 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44823 inputType: 'radio',
44826 * If this radio is part of a group, it will return the selected value
44829 getGroupValue : function(){
44830 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44834 onRender : function(ct, position){
44835 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44837 if(this.inputValue !== undefined){
44838 this.el.dom.value = this.inputValue;
44841 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44842 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44843 //var viewEl = this.wrap.createChild({
44844 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44845 //this.viewEl = viewEl;
44846 //this.wrap.on('click', this.onClick, this);
44848 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44849 //this.el.on('propertychange', this.setFromHidden, this); //ie
44854 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44855 // viewEl.on('click', this.onClick, this);
44858 this.el.dom.checked = 'checked' ;
44864 });Roo.rtf = {}; // namespace
44865 Roo.rtf.Hex = function(hex)
44869 Roo.rtf.Paragraph = function(opts)
44871 this.content = []; ///??? is that used?
44872 };Roo.rtf.Span = function(opts)
44874 this.value = opts.value;
44877 Roo.rtf.Group = function(parent)
44879 // we dont want to acutally store parent - it will make debug a nightmare..
44887 Roo.rtf.Group.prototype = {
44891 addContent : function(node) {
44892 // could set styles...
44893 this.content.push(node);
44895 addChild : function(cn)
44899 // only for images really...
44900 toDataURL : function()
44902 var mimetype = false;
44904 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
44905 mimetype = "image/png";
44907 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44908 mimetype = "image/jpeg";
44911 return 'about:blank'; // ?? error?
44915 var hexstring = this.content[this.content.length-1].value;
44917 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44918 return String.fromCharCode(parseInt(a, 16));
44923 // this looks like it's normally the {rtf{ .... }}
44924 Roo.rtf.Document = function()
44926 // we dont want to acutally store parent - it will make debug a nightmare..
44932 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
44933 addChild : function(cn)
44937 case 'rtlch': // most content seems to be inside this??
44940 this.rtlch.push(cn);
44943 this[cn.type] = cn;
44948 getElementsByType : function(type)
44951 this._getElementsByType(type, ret, this.cn, 'rtf');
44954 _getElementsByType : function (type, ret, search_array, path)
44956 search_array.forEach(function(n,i) {
44957 if (n.type == type) {
44958 n.path = path + '/' + n.type + ':' + i;
44961 if (n.cn.length > 0) {
44962 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44969 Roo.rtf.Ctrl = function(opts)
44971 this.value = opts.value;
44972 this.param = opts.param;
44977 * based on this https://github.com/iarna/rtf-parser
44978 * it's really only designed to extract pict from pasted RTF
44982 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44991 Roo.rtf.Parser = function(text) {
44992 //super({objectMode: true})
44994 this.parserState = this.parseText;
44996 // these are for interpeter...
44998 ///this.parserState = this.parseTop
44999 this.groupStack = [];
45000 this.hexStore = [];
45003 this.groups = []; // where we put the return.
45005 for (var ii = 0; ii < text.length; ++ii) {
45008 if (text[ii] === '\n') {
45014 this.parserState(text[ii]);
45020 Roo.rtf.Parser.prototype = {
45021 text : '', // string being parsed..
45023 controlWordParam : '',
45027 groupStack : false,
45032 row : 1, // reportin?
45036 push : function (el)
45038 var m = 'cmd'+ el.type;
45039 if (typeof(this[m]) == 'undefined') {
45040 Roo.log('invalid cmd:' + el.type);
45046 flushHexStore : function()
45048 if (this.hexStore.length < 1) {
45051 var hexstr = this.hexStore.map(
45056 this.group.addContent( new Roo.rtf.Hex( hexstr ));
45059 this.hexStore.splice(0)
45063 cmdgroupstart : function()
45065 this.flushHexStore();
45067 this.groupStack.push(this.group);
45070 if (this.doc === false) {
45071 this.group = this.doc = new Roo.rtf.Document();
45075 this.group = new Roo.rtf.Group(this.group);
45077 cmdignorable : function()
45079 this.flushHexStore();
45080 this.group.ignorable = true;
45082 cmdendparagraph : function()
45084 this.flushHexStore();
45085 this.group.addContent(new Roo.rtf.Paragraph());
45087 cmdgroupend : function ()
45089 this.flushHexStore();
45090 var endingGroup = this.group;
45093 this.group = this.groupStack.pop();
45095 this.group.addChild(endingGroup);
45100 var doc = this.group || this.doc;
45101 //if (endingGroup instanceof FontTable) {
45102 // doc.fonts = endingGroup.table
45103 //} else if (endingGroup instanceof ColorTable) {
45104 // doc.colors = endingGroup.table
45105 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45106 if (endingGroup.ignorable === false) {
45108 this.groups.push(endingGroup);
45109 // Roo.log( endingGroup );
45111 //Roo.each(endingGroup.content, function(item)) {
45112 // doc.addContent(item);
45114 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45117 cmdtext : function (cmd)
45119 this.flushHexStore();
45120 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45121 //this.group = this.doc
45123 this.group.addContent(new Roo.rtf.Span(cmd));
45125 cmdcontrolword : function (cmd)
45127 this.flushHexStore();
45128 if (!this.group.type) {
45129 this.group.type = cmd.value;
45132 this.group.addContent(new Roo.rtf.Ctrl(cmd));
45133 // we actually don't care about ctrl words...
45136 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45137 if (this[method]) {
45138 this[method](cmd.param)
45140 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45144 cmdhexchar : function(cmd) {
45145 this.hexStore.push(cmd);
45147 cmderror : function(cmd) {
45148 throw new Exception (cmd.value);
45153 if (this.text !== '\u0000') this.emitText()
45159 parseText : function(c)
45162 this.parserState = this.parseEscapes;
45163 } else if (c === '{') {
45164 this.emitStartGroup();
45165 } else if (c === '}') {
45166 this.emitEndGroup();
45167 } else if (c === '\x0A' || c === '\x0D') {
45168 // cr/lf are noise chars
45174 parseEscapes: function (c)
45176 if (c === '\\' || c === '{' || c === '}') {
45178 this.parserState = this.parseText;
45180 this.parserState = this.parseControlSymbol;
45181 this.parseControlSymbol(c);
45184 parseControlSymbol: function(c)
45187 this.text += '\u00a0'; // nbsp
45188 this.parserState = this.parseText
45189 } else if (c === '-') {
45190 this.text += '\u00ad'; // soft hyphen
45191 } else if (c === '_') {
45192 this.text += '\u2011'; // non-breaking hyphen
45193 } else if (c === '*') {
45194 this.emitIgnorable();
45195 this.parserState = this.parseText;
45196 } else if (c === "'") {
45197 this.parserState = this.parseHexChar;
45198 } else if (c === '|') { // formula cacter
45199 this.emitFormula();
45200 this.parserState = this.parseText;
45201 } else if (c === ':') { // subentry in an index entry
45202 this.emitIndexSubEntry();
45203 this.parserState = this.parseText;
45204 } else if (c === '\x0a') {
45205 this.emitEndParagraph();
45206 this.parserState = this.parseText;
45207 } else if (c === '\x0d') {
45208 this.emitEndParagraph();
45209 this.parserState = this.parseText;
45211 this.parserState = this.parseControlWord;
45212 this.parseControlWord(c);
45215 parseHexChar: function (c)
45217 if (/^[A-Fa-f0-9]$/.test(c)) {
45219 if (this.hexChar.length >= 2) {
45220 this.emitHexChar();
45221 this.parserState = this.parseText;
45225 this.emitError("Invalid character \"" + c + "\" in hex literal.");
45226 this.parserState = this.parseText;
45229 parseControlWord : function(c)
45232 this.emitControlWord();
45233 this.parserState = this.parseText;
45234 } else if (/^[-\d]$/.test(c)) {
45235 this.parserState = this.parseControlWordParam;
45236 this.controlWordParam += c;
45237 } else if (/^[A-Za-z]$/.test(c)) {
45238 this.controlWord += c;
45240 this.emitControlWord();
45241 this.parserState = this.parseText;
45245 parseControlWordParam : function (c) {
45246 if (/^\d$/.test(c)) {
45247 this.controlWordParam += c;
45248 } else if (c === ' ') {
45249 this.emitControlWord();
45250 this.parserState = this.parseText;
45252 this.emitControlWord();
45253 this.parserState = this.parseText;
45261 emitText : function () {
45262 if (this.text === '') {
45274 emitControlWord : function ()
45277 if (this.controlWord === '') {
45278 this.emitError('empty control word');
45281 type: 'controlword',
45282 value: this.controlWord,
45283 param: this.controlWordParam !== '' && Number(this.controlWordParam),
45289 this.controlWord = '';
45290 this.controlWordParam = '';
45292 emitStartGroup : function ()
45296 type: 'groupstart',
45302 emitEndGroup : function ()
45312 emitIgnorable : function ()
45322 emitHexChar : function ()
45327 value: this.hexChar,
45334 emitError : function (message)
45342 char: this.cpos //,
45343 //stack: new Error().stack
45346 emitEndParagraph : function () {
45349 type: 'endparagraph',
45357 Roo.htmleditor = {};
45360 * @class Roo.htmleditor.Filter
45361 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45362 * @cfg {DomElement} node The node to iterate and filter
45363 * @cfg {boolean|String|Array} tag Tags to replace
45365 * Create a new Filter.
45366 * @param {Object} config Configuration options
45371 Roo.htmleditor.Filter = function(cfg) {
45372 Roo.apply(this.cfg);
45373 // this does not actually call walk as it's really just a abstract class
45377 Roo.htmleditor.Filter.prototype = {
45383 // overrride to do replace comments.
45384 replaceComment : false,
45386 // overrride to do replace or do stuff with tags..
45387 replaceTag : false,
45389 walk : function(dom)
45391 Roo.each( Array.from(dom.childNodes), function( e ) {
45394 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45395 this.replaceComment(e);
45398 case e.nodeType != 1: //not a node.
45401 case this.tag === true: // everything
45402 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45403 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45404 if (this.replaceTag && false === this.replaceTag(e)) {
45407 if (e.hasChildNodes()) {
45412 default: // tags .. that do not match.
45413 if (e.hasChildNodes()) {
45424 * @class Roo.htmleditor.FilterAttributes
45425 * clean attributes and styles including http:// etc.. in attribute
45427 * Run a new Attribute Filter
45428 * @param {Object} config Configuration options
45430 Roo.htmleditor.FilterAttributes = function(cfg)
45432 Roo.apply(this, cfg);
45433 this.attrib_black = this.attrib_black || [];
45434 this.attrib_white = this.attrib_white || [];
45436 this.attrib_clean = this.attrib_clean || [];
45437 this.style_white = this.style_white || [];
45438 this.style_black = this.style_black || [];
45439 this.walk(cfg.node);
45442 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45444 tag: true, // all tags
45446 attrib_black : false, // array
45447 attrib_clean : false,
45448 attrib_white : false,
45450 style_white : false,
45451 style_black : false,
45454 replaceTag : function(node)
45456 if (!node.attributes || !node.attributes.length) {
45460 for (var i = node.attributes.length-1; i > -1 ; i--) {
45461 var a = node.attributes[i];
45463 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45464 node.removeAttribute(a.name);
45470 if (a.name.toLowerCase().substr(0,2)=='on') {
45471 node.removeAttribute(a.name);
45476 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45477 node.removeAttribute(a.name);
45480 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45481 this.cleanAttr(node,a.name,a.value); // fixme..
45484 if (a.name == 'style') {
45485 this.cleanStyle(node,a.name,a.value);
45488 /// clean up MS crap..
45489 // tecnically this should be a list of valid class'es..
45492 if (a.name == 'class') {
45493 if (a.value.match(/^Mso/)) {
45494 node.removeAttribute('class');
45497 if (a.value.match(/^body$/)) {
45498 node.removeAttribute('class');
45508 return true; // clean children
45511 cleanAttr: function(node, n,v)
45514 if (v.match(/^\./) || v.match(/^\//)) {
45517 if (v.match(/^(http|https):\/\//)
45518 || v.match(/^mailto:/)
45519 || v.match(/^ftp:/)
45520 || v.match(/^data:/)
45524 if (v.match(/^#/)) {
45527 if (v.match(/^\{/)) { // allow template editing.
45530 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45531 node.removeAttribute(n);
45534 cleanStyle : function(node, n,v)
45536 if (v.match(/expression/)) { //XSS?? should we even bother..
45537 node.removeAttribute(n);
45541 var parts = v.split(/;/);
45544 Roo.each(parts, function(p) {
45545 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45549 var l = p.split(':').shift().replace(/\s+/g,'');
45550 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45552 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45556 // only allow 'c whitelisted system attributes'
45557 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45565 if (clean.length) {
45566 node.setAttribute(n, clean.join(';'));
45568 node.removeAttribute(n);
45577 * @class Roo.htmleditor.FilterBlack
45578 * remove blacklisted elements.
45580 * Run a new Blacklisted Filter
45581 * @param {Object} config Configuration options
45584 Roo.htmleditor.FilterBlack = function(cfg)
45586 Roo.apply(this, cfg);
45587 this.walk(cfg.node);
45590 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45592 tag : true, // all elements.
45594 replace : function(n)
45596 n.parentNode.removeChild(n);
45600 * @class Roo.htmleditor.FilterComment
45603 * Run a new Comments Filter
45604 * @param {Object} config Configuration options
45606 Roo.htmleditor.FilterComment = function(cfg)
45608 this.walk(cfg.node);
45611 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45614 replaceComment : function(n)
45616 n.parentNode.removeChild(n);
45619 * @class Roo.htmleditor.FilterKeepChildren
45620 * remove tags but keep children
45622 * Run a new Keep Children Filter
45623 * @param {Object} config Configuration options
45626 Roo.htmleditor.FilterKeepChildren = function(cfg)
45628 Roo.apply(this, cfg);
45629 if (this.tag === false) {
45630 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45632 this.walk(cfg.node);
45635 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45639 replaceTag : function(node)
45641 // walk children...
45643 var ar = Array.from(node.childNodes);
45645 for (var i = 0; i < ar.length; i++) {
45646 if (ar[i].nodeType == 1) {
45648 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45649 || // array and it matches
45650 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45652 this.replaceTag(ar[i]); // child is blacklisted as well...
45657 ar = Array.from(node.childNodes);
45658 for (var i = 0; i < ar.length; i++) {
45660 node.removeChild(ar[i]);
45661 // what if we need to walk these???
45662 node.parentNode.insertBefore(ar[i], node);
45663 if (this.tag !== false) {
45668 node.parentNode.removeChild(node);
45669 return false; // don't walk children
45674 * @class Roo.htmleditor.FilterParagraph
45675 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45676 * like on 'push' to remove the <p> tags and replace them with line breaks.
45678 * Run a new Paragraph Filter
45679 * @param {Object} config Configuration options
45682 Roo.htmleditor.FilterParagraph = function(cfg)
45684 // no need to apply config.
45685 this.walk(cfg.node);
45688 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45695 replaceTag : function(node)
45698 if (node.childNodes.length == 1 &&
45699 node.childNodes[0].nodeType == 3 &&
45700 node.childNodes[0].textContent.trim().length < 1
45702 // remove and replace with '<BR>';
45703 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45704 return false; // no need to walk..
45706 var ar = Array.from(node.childNodes);
45707 for (var i = 0; i < ar.length; i++) {
45708 node.removeChild(ar[i]);
45709 // what if we need to walk these???
45710 node.parentNode.insertBefore(ar[i], node);
45712 // now what about this?
45716 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45717 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45718 node.parentNode.removeChild(node);
45725 * @class Roo.htmleditor.FilterSpan
45726 * filter span's with no attributes out..
45728 * Run a new Span Filter
45729 * @param {Object} config Configuration options
45732 Roo.htmleditor.FilterSpan = function(cfg)
45734 // no need to apply config.
45735 this.walk(cfg.node);
45738 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45744 replaceTag : function(node)
45746 if (node.attributes && node.attributes.length > 0) {
45747 return true; // walk if there are any.
45749 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45755 * @class Roo.htmleditor.FilterTableWidth
45756 try and remove table width data - as that frequently messes up other stuff.
45758 * was cleanTableWidths.
45760 * Quite often pasting from word etc.. results in tables with column and widths.
45761 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45764 * Run a new Table Filter
45765 * @param {Object} config Configuration options
45768 Roo.htmleditor.FilterTableWidth = function(cfg)
45770 // no need to apply config.
45771 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45772 this.walk(cfg.node);
45775 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45780 replaceTag: function(node) {
45784 if (node.hasAttribute('width')) {
45785 node.removeAttribute('width');
45789 if (node.hasAttribute("style")) {
45792 var styles = node.getAttribute("style").split(";");
45794 Roo.each(styles, function(s) {
45795 if (!s.match(/:/)) {
45798 var kv = s.split(":");
45799 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45802 // what ever is left... we allow.
45805 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45806 if (!nstyle.length) {
45807 node.removeAttribute('style');
45811 return true; // continue doing children..
45814 * @class Roo.htmleditor.FilterWord
45815 * try and clean up all the mess that Word generates.
45817 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
45820 * Run a new Span Filter
45821 * @param {Object} config Configuration options
45824 Roo.htmleditor.FilterWord = function(cfg)
45826 // no need to apply config.
45827 this.walk(cfg.node);
45830 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45836 * Clean up MS wordisms...
45838 replaceTag : function(node)
45841 // no idea what this does - span with text, replaceds with just text.
45843 node.nodeName == 'SPAN' &&
45844 !node.hasAttributes() &&
45845 node.childNodes.length == 1 &&
45846 node.firstChild.nodeName == "#text"
45848 var textNode = node.firstChild;
45849 node.removeChild(textNode);
45850 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
45851 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45853 node.parentNode.insertBefore(textNode, node);
45854 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
45855 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45858 node.parentNode.removeChild(node);
45859 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45864 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45865 node.parentNode.removeChild(node);
45866 return false; // dont do chidlren
45868 //Roo.log(node.tagName);
45869 // remove - but keep children..
45870 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45871 //Roo.log('-- removed');
45872 while (node.childNodes.length) {
45873 var cn = node.childNodes[0];
45874 node.removeChild(cn);
45875 node.parentNode.insertBefore(cn, node);
45876 // move node to parent - and clean it..
45877 this.replaceTag(cn);
45879 node.parentNode.removeChild(node);
45880 /// no need to iterate chidlren = it's got none..
45881 //this.iterateChildren(node, this.cleanWord);
45882 return false; // no need to iterate children.
45885 if (node.className.length) {
45887 var cn = node.className.split(/\W+/);
45889 Roo.each(cn, function(cls) {
45890 if (cls.match(/Mso[a-zA-Z]+/)) {
45895 node.className = cna.length ? cna.join(' ') : '';
45897 node.removeAttribute("class");
45901 if (node.hasAttribute("lang")) {
45902 node.removeAttribute("lang");
45905 if (node.hasAttribute("style")) {
45907 var styles = node.getAttribute("style").split(";");
45909 Roo.each(styles, function(s) {
45910 if (!s.match(/:/)) {
45913 var kv = s.split(":");
45914 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45917 // what ever is left... we allow.
45920 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45921 if (!nstyle.length) {
45922 node.removeAttribute('style');
45925 return true; // do children
45932 * @class Roo.htmleditor.FilterStyleToTag
45933 * part of the word stuff... - certain 'styles' should be converted to tags.
45935 * font-weight: bold -> bold
45936 * ?? super / subscrit etc..
45939 * Run a new style to tag filter.
45940 * @param {Object} config Configuration options
45942 Roo.htmleditor.FilterStyleToTag = function(cfg)
45946 B : [ 'fontWeight' , 'bold'],
45947 I : [ 'fontStyle' , 'italic'],
45948 //pre : [ 'font-style' , 'italic'],
45949 // h1.. h6 ?? font-size?
45950 SUP : [ 'verticalAlign' , 'super' ],
45951 SUB : [ 'verticalAlign' , 'sub' ]
45956 Roo.apply(this, cfg);
45959 this.walk(cfg.node);
45966 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45968 tag: true, // all tags
45973 replaceTag : function(node)
45977 if (node.getAttribute("style") === null) {
45981 for (var k in this.tags) {
45982 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45984 node.style.removeProperty(this.tags[k][0]);
45987 if (!inject.length) {
45990 var cn = Array.from(node.childNodes);
45992 Roo.each(inject, function(t) {
45993 var nc = node.ownerDocument.createelement(t);
45994 nn.appendChild(nc);
45997 for(var i = 0;i < cn.length;cn++) {
45998 node.removeChild(cn[i]);
45999 nn.appendChild(cn[i]);
46001 return true /// iterate thru
46005 * @class Roo.htmleditor.FilterLongBr
46006 * BR/BR/BR - keep a maximum of 2...
46008 * Run a new Long BR Filter
46009 * @param {Object} config Configuration options
46012 Roo.htmleditor.FilterLongBr = function(cfg)
46014 // no need to apply config.
46015 this.walk(cfg.node);
46018 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46025 replaceTag : function(node)
46028 var ps = node.nextSibling;
46029 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46030 ps = ps.nextSibling;
46033 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
46034 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46038 if (!ps || ps.nodeType != 1) {
46042 if (!ps || ps.tagName != 'BR') {
46051 if (!node.previousSibling) {
46054 var ps = node.previousSibling;
46056 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46057 ps = ps.previousSibling;
46059 if (!ps || ps.nodeType != 1) {
46062 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46063 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46067 node.parentNode.removeChild(node); // remove me...
46069 return false; // no need to do children
46075 * @class Roo.htmleditor.Tidy
46077 * @cfg {Roo.HtmlEditorCore} core the editor.
46079 * Create a new Filter.
46080 * @param {Object} config Configuration options
46084 Roo.htmleditor.Tidy = function(cfg) {
46085 Roo.apply(this, cfg);
46087 this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46091 Roo.htmleditor.Tidy.toString = function(node)
46093 return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46096 Roo.htmleditor.Tidy.prototype = {
46099 wrap : function(s) {
46100 return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46104 tidy : function(node, indent) {
46106 if (node.nodeType == 3) {
46110 return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46115 if (node.nodeType != 1) {
46121 if (node.tagName == 'BODY') {
46123 return this.cn(node, '');
46126 // Prints the node tagName, such as <A>, <IMG>, etc
46127 var ret = "<" + node.tagName + this.attr(node) ;
46129 // elements with no children..
46130 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46136 var cindent = indent === false ? '' : (indent + ' ');
46137 // tags where we will not pad the children.. (inline text tags etc..)
46138 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46144 var cn = this.cn(node, cindent );
46146 return ret + cn + '</' + node.tagName + '>';
46149 cn: function(node, indent)
46153 var ar = Array.from(node.childNodes);
46154 for (var i = 0 ; i < ar.length ; i++) {
46158 if (indent !== false // indent==false preservies everything
46160 && ar[i].nodeType == 3
46161 && ar[i].nodeValue.length > 0
46162 && ar[i].nodeValue.match(/^\s+/)
46164 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46165 ret.pop(); // remove line break from last?
46168 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46170 if (indent !== false
46171 && ar[i].nodeType == 1 // element - and indent is not set...
46173 ret.push("\n" + indent);
46176 ret.push(this.tidy(ar[i], indent));
46177 // text + trailing indent
46178 if (indent !== false
46179 && ar[i].nodeType == 3
46180 && ar[i].nodeValue.length > 0
46181 && ar[i].nodeValue.match(/\s+$/)
46183 ret.push("\n" + indent);
46190 // what if all text?
46193 return ret.join('');
46198 attr : function(node)
46201 for(i = 0; i < node.attributes.length;i++) {
46203 // skip empty values?
46204 if (!node.attributes.item(i).value.length) {
46207 attr.push( node.attributes.item(i).name + '="' +
46208 Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46211 return attr.length ? (' ' + attr.join(' ') ) : '';
46219 * @class Roo.htmleditor.KeyEnter
46220 * Handle Enter press..
46221 * @cfg {Roo.HtmlEditorCore} core the editor.
46223 * Create a new Filter.
46224 * @param {Object} config Configuration options
46229 Roo.htmleditor.KeyEnter = function(cfg) {
46230 Roo.apply(this, cfg);
46231 // this does not actually call walk as it's really just a abstract class
46233 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46237 Roo.htmleditor.KeyEnter.prototype = {
46241 keypress : function(e) {
46242 if (e.charCode != 13) {
46245 e.preventDefault();
46246 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46247 var doc = this.core.doc;
46249 var docFragment = doc.createDocumentFragment();
46252 var newEle = doc.createTextNode('\n');
46253 docFragment.appendChild(newEle);
46256 var range = this.core.win.getSelection().getRangeAt(0);
46257 var n = range.commonAncestorContainer ;
46258 while (n && n.nodeType != 1) {
46262 if (n && n.tagName == 'UL') {
46263 li = doc.createElement('LI');
46267 if (n && n.tagName == 'LI') {
46268 li = doc.createElement('LI');
46269 if (n.nextSibling) {
46270 n.parentNode.insertBefore(li, n.firstSibling);
46273 n.parentNode.appendChild(li);
46277 range = doc.createRange();
46278 range.setStartAfter(li);
46279 range.collapse(true);
46281 //make the cursor there
46282 var sel = this.core.win.getSelection();
46283 sel.removeAllRanges();
46284 sel.addRange(range);
46289 //add the br, or p, or something else
46290 newEle = doc.createElement('br');
46291 docFragment.appendChild(newEle);
46293 //make the br replace selection
46295 range.deleteContents();
46297 range.insertNode(docFragment);
46299 //create a new range
46300 range = doc.createRange();
46301 range.setStartAfter(newEle);
46302 range.collapse(true);
46304 //make the cursor there
46305 var sel = this.core.win.getSelection();
46306 sel.removeAllRanges();
46307 sel.addRange(range);
46315 * @class Roo.htmleditor.Block
46316 * Base class for html editor blocks - do not use it directly .. extend it..
46317 * @cfg {DomElement} node The node to apply stuff to.
46318 * @cfg {String} friendly_name the name that appears in the context bar about this block
46319 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46322 * Create a new Filter.
46323 * @param {Object} config Configuration options
46326 Roo.htmleditor.Block = function(cfg)
46328 // do nothing .. should not be called really.
46331 Roo.htmleditor.Block.factory = function(node)
46334 var id = Roo.get(node).id;
46335 if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46336 Roo.htmleditor.Block.cache[id].readElement();
46337 return Roo.htmleditor.Block.cache[id];
46340 var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46341 if (typeof(cls) == 'undefined') {
46342 Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46345 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46346 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
46348 // question goes here... do we need to clear out this cache sometimes?
46349 // or show we make it relivant to the htmleditor.
46350 Roo.htmleditor.Block.cache = {};
46352 Roo.htmleditor.Block.prototype = {
46356 // used by context menu
46357 friendly_name : 'Image with caption',
46361 * Update a node with values from this object
46362 * @param {DomElement} node
46364 updateElement : function(node)
46366 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46369 * convert to plain HTML for calling insertAtCursor..
46371 toHTML : function()
46373 return Roo.DomHelper.markup(this.toObject());
46376 * used by readEleemnt to extract data from a node
46377 * may need improving as it's pretty basic
46379 * @param {DomElement} node
46380 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46381 * @param {String} attribute (use html - for contents, or style for using next param as style)
46382 * @param {String} style the style property - eg. text-align
46384 getVal : function(node, tag, attr, style)
46387 if (tag !== true && n.tagName != tag.toUpperCase()) {
46388 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46389 // but kiss for now.
46390 n = node.getElementsByTagName(tag).item(0);
46392 if (attr == 'html') {
46393 return n.innerHTML;
46395 if (attr == 'style') {
46396 return Roo.get(n).getStyle(style);
46399 return Roo.get(n).attr(attr);
46403 * create a DomHelper friendly object - for use with
46404 * Roo.DomHelper.markup / overwrite / etc..
46407 toObject : function()
46412 * Read a node that has a 'data-block' property - and extract the values from it.
46413 * @param {DomElement} node - the node
46415 readElement : function(node)
46426 * @class Roo.htmleditor.BlockFigure
46427 * Block that has an image and a figcaption
46428 * @cfg {String} image_src the url for the image
46429 * @cfg {String} align (left|right) alignment for the block default left
46430 * @cfg {String} text_align (left|right) alignment for the text caption default left.
46431 * @cfg {String} caption the text to appear below (and in the alt tag)
46432 * @cfg {String|number} image_width the width of the image number or %?
46433 * @cfg {String|number} image_height the height of the image number or %?
46436 * Create a new Filter.
46437 * @param {Object} config Configuration options
46440 Roo.htmleditor.BlockFigure = function(cfg)
46443 this.readElement(cfg.node);
46444 this.updateElement(cfg.node);
46446 Roo.apply(this, cfg);
46448 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46456 text_align: 'left',
46461 // used by context menu
46462 friendly_name : 'Image with caption',
46464 context : { // ?? static really
46477 opts : [[ "left"],[ "right"]],
46482 title: "Caption Align",
46483 opts : [ [ "left"],[ "right"],[ "center"]],
46494 * create a DomHelper friendly object - for use with
46495 * Roo.DomHelper.markup / overwrite / etc..
46497 toObject : function()
46499 var d = document.createElement('div');
46500 d.innerHTML = this.caption;
46504 'data-block' : 'Figure',
46505 contenteditable : 'false',
46508 float : this.align ,
46509 width : this.width,
46510 margin: this.margin
46515 src : this.image_src,
46516 alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46523 contenteditable : true,
46525 'text-align': this.text_align
46527 html : this.caption
46534 readElement : function(node)
46536 this.image_src = this.getVal(node, 'img', 'src');
46537 this.align = this.getVal(node, 'figure', 'style', 'float');
46538 this.caption = this.getVal(node, 'figcaption', 'html');
46539 this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46540 this.width = this.getVal(node, 'figure', 'style', 'width');
46541 this.margin = this.getVal(node, 'figure', 'style', 'margin');
46554 //<script type="text/javascript">
46557 * Based Ext JS Library 1.1.1
46558 * Copyright(c) 2006-2007, Ext JS, LLC.
46564 * @class Roo.HtmlEditorCore
46565 * @extends Roo.Component
46566 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46568 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46571 Roo.HtmlEditorCore = function(config){
46574 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46579 * @event initialize
46580 * Fires when the editor is fully initialized (including the iframe)
46581 * @param {Roo.HtmlEditorCore} this
46586 * Fires when the editor is first receives the focus. Any insertion must wait
46587 * until after this event.
46588 * @param {Roo.HtmlEditorCore} this
46592 * @event beforesync
46593 * Fires before the textarea is updated with content from the editor iframe. Return false
46594 * to cancel the sync.
46595 * @param {Roo.HtmlEditorCore} this
46596 * @param {String} html
46600 * @event beforepush
46601 * Fires before the iframe editor is updated with content from the textarea. Return false
46602 * to cancel the push.
46603 * @param {Roo.HtmlEditorCore} this
46604 * @param {String} html
46609 * Fires when the textarea is updated with content from the editor iframe.
46610 * @param {Roo.HtmlEditorCore} this
46611 * @param {String} html
46616 * Fires when the iframe editor is updated with content from the textarea.
46617 * @param {Roo.HtmlEditorCore} this
46618 * @param {String} html
46623 * @event editorevent
46624 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46625 * @param {Roo.HtmlEditorCore} this
46631 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46633 // defaults : white / black...
46634 this.applyBlacklists();
46641 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
46645 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
46651 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
46656 * @cfg {Number} height (in pixels)
46660 * @cfg {Number} width (in pixels)
46665 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46668 stylesheets: false,
46671 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46673 allowComments: false,
46677 // private properties
46678 validationEvent : false,
46680 initialized : false,
46682 sourceEditMode : false,
46683 onFocus : Roo.emptyFn,
46685 hideMode:'offsets',
46689 // blacklist + whitelisted elements..
46696 undoManager : false,
46698 * Protected method that will not generally be called directly. It
46699 * is called when the editor initializes the iframe with HTML contents. Override this method if you
46700 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46702 getDocMarkup : function(){
46706 // inherit styels from page...??
46707 if (this.stylesheets === false) {
46709 Roo.get(document.head).select('style').each(function(node) {
46710 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46713 Roo.get(document.head).select('link').each(function(node) {
46714 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46717 } else if (!this.stylesheets.length) {
46719 st = '<style type="text/css">' +
46720 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46723 for (var i in this.stylesheets) {
46724 if (typeof(this.stylesheets[i]) != 'string') {
46727 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46732 st += '<style type="text/css">' +
46733 'IMG { cursor: pointer } ' +
46736 var cls = 'roo-htmleditor-body';
46738 if(this.bodyCls.length){
46739 cls += ' ' + this.bodyCls;
46742 return '<html><head>' + st +
46743 //<style type="text/css">' +
46744 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46746 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
46750 onRender : function(ct, position)
46753 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46754 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46757 this.el.dom.style.border = '0 none';
46758 this.el.dom.setAttribute('tabIndex', -1);
46759 this.el.addClass('x-hidden hide');
46763 if(Roo.isIE){ // fix IE 1px bogus margin
46764 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46768 this.frameId = Roo.id();
46772 var iframe = this.owner.wrap.createChild({
46774 cls: 'form-control', // bootstrap..
46776 name: this.frameId,
46777 frameBorder : 'no',
46778 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
46783 this.iframe = iframe.dom;
46785 this.assignDocWin();
46787 this.doc.designMode = 'on';
46790 this.doc.write(this.getDocMarkup());
46794 var task = { // must defer to wait for browser to be ready
46796 //console.log("run task?" + this.doc.readyState);
46797 this.assignDocWin();
46798 if(this.doc.body || this.doc.readyState == 'complete'){
46800 this.doc.designMode="on";
46805 Roo.TaskMgr.stop(task);
46806 this.initEditor.defer(10, this);
46813 Roo.TaskMgr.start(task);
46818 onResize : function(w, h)
46820 Roo.log('resize: ' +w + ',' + h );
46821 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46825 if(typeof w == 'number'){
46827 this.iframe.style.width = w + 'px';
46829 if(typeof h == 'number'){
46831 this.iframe.style.height = h + 'px';
46833 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46840 * Toggles the editor between standard and source edit mode.
46841 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46843 toggleSourceEdit : function(sourceEditMode){
46845 this.sourceEditMode = sourceEditMode === true;
46847 if(this.sourceEditMode){
46849 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
46852 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46853 //this.iframe.className = '';
46856 //this.setSize(this.owner.wrap.getSize());
46857 //this.fireEvent('editmodechange', this, this.sourceEditMode);
46864 * Protected method that will not generally be called directly. If you need/want
46865 * custom HTML cleanup, this is the method you should override.
46866 * @param {String} html The HTML to be cleaned
46867 * return {String} The cleaned HTML
46869 cleanHtml : function(html){
46870 html = String(html);
46871 if(html.length > 5){
46872 if(Roo.isSafari){ // strip safari nonsense
46873 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46876 if(html == ' '){
46883 * HTML Editor -> Textarea
46884 * Protected method that will not generally be called directly. Syncs the contents
46885 * of the editor iframe with the textarea.
46887 syncValue : function()
46889 Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46890 if(this.initialized){
46892 this.undoManager.addEvent();
46895 var bd = (this.doc.body || this.doc.documentElement);
46896 //this.cleanUpPaste(); -- this is done else where and causes havoc..
46898 // not sure if this is really the place for this
46899 // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46900 // this has to update attributes that get duped.. like alt and caption..
46903 //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46904 // Roo.htmleditor.Block.factory(e);
46908 var div = document.createElement('div');
46909 div.innerHTML = bd.innerHTML;
46910 // remove content editable. (blocks)
46913 new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46915 var html = div.innerHTML;
46917 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46918 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46920 html = '<div style="'+m[0]+'">' + html + '</div>';
46923 html = this.cleanHtml(html);
46924 // fix up the special chars.. normaly like back quotes in word...
46925 // however we do not want to do this with chinese..
46926 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46928 var cc = match.charCodeAt();
46930 // Get the character value, handling surrogate pairs
46931 if (match.length == 2) {
46932 // It's a surrogate pair, calculate the Unicode code point
46933 var high = match.charCodeAt(0) - 0xD800;
46934 var low = match.charCodeAt(1) - 0xDC00;
46935 cc = (high * 0x400) + low + 0x10000;
46937 (cc >= 0x4E00 && cc < 0xA000 ) ||
46938 (cc >= 0x3400 && cc < 0x4E00 ) ||
46939 (cc >= 0xf900 && cc < 0xfb00 )
46944 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46945 return "&#" + cc + ";";
46952 if(this.owner.fireEvent('beforesync', this, html) !== false){
46953 this.el.dom.value = html;
46954 this.owner.fireEvent('sync', this, html);
46960 * TEXTAREA -> EDITABLE
46961 * Protected method that will not generally be called directly. Pushes the value of the textarea
46962 * into the iframe editor.
46964 pushValue : function()
46966 Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46967 if(this.initialized){
46968 var v = this.el.dom.value.trim();
46971 if(this.owner.fireEvent('beforepush', this, v) !== false){
46972 var d = (this.doc.body || this.doc.documentElement);
46975 this.el.dom.value = d.innerHTML;
46976 this.owner.fireEvent('push', this, v);
46979 Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46981 Roo.htmleditor.Block.factory(e);
46984 var lc = this.doc.body.lastChild;
46985 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46986 // add an extra line at the end.
46987 this.doc.body.appendChild(this.doc.createElement('br'));
46995 deferFocus : function(){
46996 this.focus.defer(10, this);
47000 focus : function(){
47001 if(this.win && !this.sourceEditMode){
47008 assignDocWin: function()
47010 var iframe = this.iframe;
47013 this.doc = iframe.contentWindow.document;
47014 this.win = iframe.contentWindow;
47016 // if (!Roo.get(this.frameId)) {
47019 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47020 // this.win = Roo.get(this.frameId).dom.contentWindow;
47022 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47026 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47027 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47032 initEditor : function(){
47033 //console.log("INIT EDITOR");
47034 this.assignDocWin();
47038 this.doc.designMode="on";
47040 this.doc.write(this.getDocMarkup());
47043 var dbody = (this.doc.body || this.doc.documentElement);
47044 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47045 // this copies styles from the containing element into thsi one..
47046 // not sure why we need all of this..
47047 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47049 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47050 //ss['background-attachment'] = 'fixed'; // w3c
47051 dbody.bgProperties = 'fixed'; // ie
47052 //Roo.DomHelper.applyStyles(dbody, ss);
47053 Roo.EventManager.on(this.doc, {
47054 //'mousedown': this.onEditorEvent,
47055 'mouseup': this.onEditorEvent,
47056 'dblclick': this.onEditorEvent,
47057 'click': this.onEditorEvent,
47058 'keyup': this.onEditorEvent,
47063 Roo.EventManager.on(this.doc, {
47064 'paste': this.onPasteEvent,
47068 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47070 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47071 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47073 this.initialized = true;
47076 // initialize special key events - enter
47077 new Roo.htmleditor.KeyEnter({core : this});
47081 this.owner.fireEvent('initialize', this);
47085 onPasteEvent : function(e,v)
47087 // I think we better assume paste is going to be a dirty load of rubish from word..
47089 // even pasting into a 'email version' of this widget will have to clean up that mess.
47090 var cd = (e.browserEvent.clipboardData || window.clipboardData);
47092 // check what type of paste - if it's an image, then handle it differently.
47093 if (cd.files.length > 0) {
47095 var urlAPI = (window.createObjectURL && window) ||
47096 (window.URL && URL.revokeObjectURL && URL) ||
47097 (window.webkitURL && webkitURL);
47099 var url = urlAPI.createObjectURL( cd.files[0]);
47100 this.insertAtCursor('<img src=" + url + ">');
47104 var html = cd.getData('text/html'); // clipboard event
47105 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47106 var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47110 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47111 .map(function(g) { return g.toDataURL(); });
47114 html = this.cleanWordChars(html);
47116 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47118 if (images.length > 0) {
47119 Roo.each(d.getElementsByTagName('img'), function(img, i) {
47120 img.setAttribute('src', images[i]);
47125 new Roo.htmleditor.FilterStyleToTag({ node : d });
47126 new Roo.htmleditor.FilterAttributes({
47128 attrib_white : ['href', 'src', 'name', 'align'],
47129 attrib_clean : ['href', 'src' ]
47131 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47132 // should be fonts..
47133 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47134 new Roo.htmleditor.FilterParagraph({ node : d });
47135 new Roo.htmleditor.FilterSpan({ node : d });
47136 new Roo.htmleditor.FilterLongBr({ node : d });
47140 this.insertAtCursor(d.innerHTML);
47142 e.preventDefault();
47144 // default behaveiour should be our local cleanup paste? (optional?)
47145 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47146 //this.owner.fireEvent('paste', e, v);
47149 onDestroy : function(){
47155 //for (var i =0; i < this.toolbars.length;i++) {
47156 // // fixme - ask toolbars for heights?
47157 // this.toolbars[i].onDestroy();
47160 //this.wrap.dom.innerHTML = '';
47161 //this.wrap.remove();
47166 onFirstFocus : function(){
47168 this.assignDocWin();
47169 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47171 this.activated = true;
47174 if(Roo.isGecko){ // prevent silly gecko errors
47176 var s = this.win.getSelection();
47177 if(!s.focusNode || s.focusNode.nodeType != 3){
47178 var r = s.getRangeAt(0);
47179 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47184 this.execCmd('useCSS', true);
47185 this.execCmd('styleWithCSS', false);
47188 this.owner.fireEvent('activate', this);
47192 adjustFont: function(btn){
47193 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47194 //if(Roo.isSafari){ // safari
47197 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47198 if(Roo.isSafari){ // safari
47199 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47200 v = (v < 10) ? 10 : v;
47201 v = (v > 48) ? 48 : v;
47202 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47207 v = Math.max(1, v+adjust);
47209 this.execCmd('FontSize', v );
47212 onEditorEvent : function(e)
47214 this.owner.fireEvent('editorevent', this, e);
47215 // this.updateToolbar();
47216 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47219 insertTag : function(tg)
47221 // could be a bit smarter... -> wrap the current selected tRoo..
47222 if (tg.toLowerCase() == 'span' ||
47223 tg.toLowerCase() == 'code' ||
47224 tg.toLowerCase() == 'sup' ||
47225 tg.toLowerCase() == 'sub'
47228 range = this.createRange(this.getSelection());
47229 var wrappingNode = this.doc.createElement(tg.toLowerCase());
47230 wrappingNode.appendChild(range.extractContents());
47231 range.insertNode(wrappingNode);
47238 this.execCmd("formatblock", tg);
47239 this.undoManager.addEvent();
47242 insertText : function(txt)
47246 var range = this.createRange();
47247 range.deleteContents();
47248 //alert(Sender.getAttribute('label'));
47250 range.insertNode(this.doc.createTextNode(txt));
47251 this.undoManager.addEvent();
47257 * Executes a Midas editor command on the editor document and performs necessary focus and
47258 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47259 * @param {String} cmd The Midas command
47260 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47262 relayCmd : function(cmd, value){
47264 this.execCmd(cmd, value);
47265 this.owner.fireEvent('editorevent', this);
47266 //this.updateToolbar();
47267 this.owner.deferFocus();
47271 * Executes a Midas editor command directly on the editor document.
47272 * For visual commands, you should use {@link #relayCmd} instead.
47273 * <b>This should only be called after the editor is initialized.</b>
47274 * @param {String} cmd The Midas command
47275 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47277 execCmd : function(cmd, value){
47278 this.doc.execCommand(cmd, false, value === undefined ? null : value);
47285 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47287 * @param {String} text | dom node..
47289 insertAtCursor : function(text)
47292 if(!this.activated){
47296 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47300 // from jquery ui (MIT licenced)
47302 var win = this.win;
47304 if (win.getSelection && win.getSelection().getRangeAt) {
47306 // delete the existing?
47308 this.createRange(this.getSelection()).deleteContents();
47309 range = win.getSelection().getRangeAt(0);
47310 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47311 range.insertNode(node);
47312 range = range.cloneRange();
47313 range.collapse(false);
47315 win.getSelection().removeAllRanges();
47316 win.getSelection().addRange(range);
47320 } else if (win.document.selection && win.document.selection.createRange) {
47321 // no firefox support
47322 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47323 win.document.selection.createRange().pasteHTML(txt);
47326 // no firefox support
47327 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47328 this.execCmd('InsertHTML', txt);
47336 mozKeyPress : function(e){
47338 var c = e.getCharCode(), cmd;
47341 c = String.fromCharCode(c).toLowerCase();
47355 // this.cleanUpPaste.defer(100, this);
47363 e.preventDefault();
47371 fixKeys : function(){ // load time branching for fastest keydown performance
47373 return function(e){
47374 var k = e.getKey(), r;
47377 r = this.doc.selection.createRange();
47380 r.pasteHTML('    ');
47387 r = this.doc.selection.createRange();
47389 var target = r.parentElement();
47390 if(!target || target.tagName.toLowerCase() != 'li'){
47392 r.pasteHTML('<br/>');
47398 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47399 // this.cleanUpPaste.defer(100, this);
47405 }else if(Roo.isOpera){
47406 return function(e){
47407 var k = e.getKey();
47411 this.execCmd('InsertHTML','    ');
47414 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47415 // this.cleanUpPaste.defer(100, this);
47420 }else if(Roo.isSafari){
47421 return function(e){
47422 var k = e.getKey();
47426 this.execCmd('InsertText','\t');
47430 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47431 // this.cleanUpPaste.defer(100, this);
47439 getAllAncestors: function()
47441 var p = this.getSelectedNode();
47444 a.push(p); // push blank onto stack..
47445 p = this.getParentElement();
47449 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47453 a.push(this.doc.body);
47457 lastSelNode : false,
47460 getSelection : function()
47462 this.assignDocWin();
47463 return Roo.isIE ? this.doc.selection : this.win.getSelection();
47466 * Select a dom node
47467 * @param {DomElement} node the node to select
47469 selectNode : function(node)
47471 Roo.select('.roo-ed-selection', false, this.doc).removeClass('roo-ed-selection');
47472 Roo.get(node).addClass('roo-ed-selection');
47473 var nodeRange = node.ownerDocument.createRange();
47475 nodeRange.selectNode(node);
47477 nodeRange.selectNodeContents(node);
47479 //nodeRange.collapse(true);
47480 var s = this.win.getSelection();
47481 s.removeAllRanges();
47482 s.addRange(nodeRange);
47485 getSelectedNode: function()
47487 // this may only work on Gecko!!!
47489 // should we cache this!!!!
47494 var range = this.createRange(this.getSelection()).cloneRange();
47497 var parent = range.parentElement();
47499 var testRange = range.duplicate();
47500 testRange.moveToElementText(parent);
47501 if (testRange.inRange(range)) {
47504 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47507 parent = parent.parentElement;
47512 // is ancestor a text element.
47513 var ac = range.commonAncestorContainer;
47514 if (ac.nodeType == 3) {
47515 ac = ac.parentNode;
47518 var ar = ac.childNodes;
47521 var other_nodes = [];
47522 var has_other_nodes = false;
47523 for (var i=0;i<ar.length;i++) {
47524 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
47527 // fullly contained node.
47529 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47534 // probably selected..
47535 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47536 other_nodes.push(ar[i]);
47540 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
47545 has_other_nodes = true;
47547 if (!nodes.length && other_nodes.length) {
47548 nodes= other_nodes;
47550 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47556 createRange: function(sel)
47558 // this has strange effects when using with
47559 // top toolbar - not sure if it's a great idea.
47560 //this.editor.contentWindow.focus();
47561 if (typeof sel != "undefined") {
47563 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47565 return this.doc.createRange();
47568 return this.doc.createRange();
47571 getParentElement: function()
47574 this.assignDocWin();
47575 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47577 var range = this.createRange(sel);
47580 var p = range.commonAncestorContainer;
47581 while (p.nodeType == 3) { // text node
47592 * Range intersection.. the hard stuff...
47596 * [ -- selected range --- ]
47600 * if end is before start or hits it. fail.
47601 * if start is after end or hits it fail.
47603 * if either hits (but other is outside. - then it's not
47609 // @see http://www.thismuchiknow.co.uk/?p=64.
47610 rangeIntersectsNode : function(range, node)
47612 var nodeRange = node.ownerDocument.createRange();
47614 nodeRange.selectNode(node);
47616 nodeRange.selectNodeContents(node);
47619 var rangeStartRange = range.cloneRange();
47620 rangeStartRange.collapse(true);
47622 var rangeEndRange = range.cloneRange();
47623 rangeEndRange.collapse(false);
47625 var nodeStartRange = nodeRange.cloneRange();
47626 nodeStartRange.collapse(true);
47628 var nodeEndRange = nodeRange.cloneRange();
47629 nodeEndRange.collapse(false);
47631 return rangeStartRange.compareBoundaryPoints(
47632 Range.START_TO_START, nodeEndRange) == -1 &&
47633 rangeEndRange.compareBoundaryPoints(
47634 Range.START_TO_START, nodeStartRange) == 1;
47638 rangeCompareNode : function(range, node)
47640 var nodeRange = node.ownerDocument.createRange();
47642 nodeRange.selectNode(node);
47644 nodeRange.selectNodeContents(node);
47648 range.collapse(true);
47650 nodeRange.collapse(true);
47652 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47653 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
47655 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47657 var nodeIsBefore = ss == 1;
47658 var nodeIsAfter = ee == -1;
47660 if (nodeIsBefore && nodeIsAfter) {
47663 if (!nodeIsBefore && nodeIsAfter) {
47664 return 1; //right trailed.
47667 if (nodeIsBefore && !nodeIsAfter) {
47668 return 2; // left trailed.
47674 cleanWordChars : function(input) {// change the chars to hex code
47677 [ 8211, "–" ],
47678 [ 8212, "—" ],
47686 var output = input;
47687 Roo.each(swapCodes, function(sw) {
47688 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47690 output = output.replace(swapper, sw[1]);
47700 cleanUpChild : function (node)
47703 new Roo.htmleditor.FilterComment({node : node});
47704 new Roo.htmleditor.FilterAttributes({
47706 attrib_black : this.ablack,
47707 attrib_clean : this.aclean,
47708 style_white : this.cwhite,
47709 style_black : this.cblack
47711 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47712 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47718 * Clean up MS wordisms...
47719 * @deprecated - use filter directly
47721 cleanWord : function(node)
47723 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47730 * @deprecated - use filters
47732 cleanTableWidths : function(node)
47734 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47741 applyBlacklists : function()
47743 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
47744 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
47746 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
47747 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
47748 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
47752 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47753 if (b.indexOf(tag) > -1) {
47756 this.white.push(tag);
47760 Roo.each(w, function(tag) {
47761 if (b.indexOf(tag) > -1) {
47764 if (this.white.indexOf(tag) > -1) {
47767 this.white.push(tag);
47772 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47773 if (w.indexOf(tag) > -1) {
47776 this.black.push(tag);
47780 Roo.each(b, function(tag) {
47781 if (w.indexOf(tag) > -1) {
47784 if (this.black.indexOf(tag) > -1) {
47787 this.black.push(tag);
47792 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
47793 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
47797 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47798 if (b.indexOf(tag) > -1) {
47801 this.cwhite.push(tag);
47805 Roo.each(w, function(tag) {
47806 if (b.indexOf(tag) > -1) {
47809 if (this.cwhite.indexOf(tag) > -1) {
47812 this.cwhite.push(tag);
47817 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47818 if (w.indexOf(tag) > -1) {
47821 this.cblack.push(tag);
47825 Roo.each(b, function(tag) {
47826 if (w.indexOf(tag) > -1) {
47829 if (this.cblack.indexOf(tag) > -1) {
47832 this.cblack.push(tag);
47837 setStylesheets : function(stylesheets)
47839 if(typeof(stylesheets) == 'string'){
47840 Roo.get(this.iframe.contentDocument.head).createChild({
47842 rel : 'stylesheet',
47851 Roo.each(stylesheets, function(s) {
47856 Roo.get(_this.iframe.contentDocument.head).createChild({
47858 rel : 'stylesheet',
47867 removeStylesheets : function()
47871 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47876 setStyle : function(style)
47878 Roo.get(this.iframe.contentDocument.head).createChild({
47887 // hide stuff that is not compatible
47901 * @event specialkey
47905 * @cfg {String} fieldClass @hide
47908 * @cfg {String} focusClass @hide
47911 * @cfg {String} autoCreate @hide
47914 * @cfg {String} inputType @hide
47917 * @cfg {String} invalidClass @hide
47920 * @cfg {String} invalidText @hide
47923 * @cfg {String} msgFx @hide
47926 * @cfg {String} validateOnBlur @hide
47930 Roo.HtmlEditorCore.white = [
47931 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47933 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
47934 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
47935 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
47936 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
47937 'TABLE', 'UL', 'XMP',
47939 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
47942 'DIR', 'MENU', 'OL', 'UL', 'DL',
47948 Roo.HtmlEditorCore.black = [
47949 // 'embed', 'object', // enable - backend responsiblity to clean thiese
47951 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
47952 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
47953 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
47954 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
47955 //'FONT' // CLEAN LATER..
47956 'COLGROUP', 'COL' // messy tables.
47959 Roo.HtmlEditorCore.clean = [ // ?? needed???
47960 'SCRIPT', 'STYLE', 'TITLE', 'XML'
47962 Roo.HtmlEditorCore.tag_remove = [
47967 Roo.HtmlEditorCore.ablack = [
47971 Roo.HtmlEditorCore.aclean = [
47972 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
47976 Roo.HtmlEditorCore.pwhite= [
47977 'http', 'https', 'mailto'
47980 // white listed style attributes.
47981 Roo.HtmlEditorCore.cwhite= [
47982 // 'text-align', /// default is to allow most things..
47988 // black listed style attributes.
47989 Roo.HtmlEditorCore.cblack= [
47990 // 'font-size' -- this can be set by the project
47996 //<script type="text/javascript">
47999 * Ext JS Library 1.1.1
48000 * Copyright(c) 2006-2007, Ext JS, LLC.
48006 Roo.form.HtmlEditor = function(config){
48010 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48012 if (!this.toolbars) {
48013 this.toolbars = [];
48015 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48021 * @class Roo.form.HtmlEditor
48022 * @extends Roo.form.Field
48023 * Provides a lightweight HTML Editor component.
48025 * This has been tested on Fireforx / Chrome.. IE may not be so great..
48027 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48028 * supported by this editor.</b><br/><br/>
48029 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48030 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48032 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48034 * @cfg {Boolean} clearUp
48038 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48043 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
48048 * @cfg {Number} height (in pixels)
48052 * @cfg {Number} width (in pixels)
48057 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
48060 stylesheets: false,
48064 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48069 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48075 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48080 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48085 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48087 allowComments: false,
48089 * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48098 // private properties
48099 validationEvent : false,
48101 initialized : false,
48104 onFocus : Roo.emptyFn,
48106 hideMode:'offsets',
48108 actionMode : 'container', // defaults to hiding it...
48110 defaultAutoCreate : { // modified by initCompnoent..
48112 style:"width:500px;height:300px;",
48113 autocomplete: "new-password"
48117 initComponent : function(){
48120 * @event initialize
48121 * Fires when the editor is fully initialized (including the iframe)
48122 * @param {HtmlEditor} this
48127 * Fires when the editor is first receives the focus. Any insertion must wait
48128 * until after this event.
48129 * @param {HtmlEditor} this
48133 * @event beforesync
48134 * Fires before the textarea is updated with content from the editor iframe. Return false
48135 * to cancel the sync.
48136 * @param {HtmlEditor} this
48137 * @param {String} html
48141 * @event beforepush
48142 * Fires before the iframe editor is updated with content from the textarea. Return false
48143 * to cancel the push.
48144 * @param {HtmlEditor} this
48145 * @param {String} html
48150 * Fires when the textarea is updated with content from the editor iframe.
48151 * @param {HtmlEditor} this
48152 * @param {String} html
48157 * Fires when the iframe editor is updated with content from the textarea.
48158 * @param {HtmlEditor} this
48159 * @param {String} html
48163 * @event editmodechange
48164 * Fires when the editor switches edit modes
48165 * @param {HtmlEditor} this
48166 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48168 editmodechange: true,
48170 * @event editorevent
48171 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48172 * @param {HtmlEditor} this
48176 * @event firstfocus
48177 * Fires when on first focus - needed by toolbars..
48178 * @param {HtmlEditor} this
48183 * Auto save the htmlEditor value as a file into Events
48184 * @param {HtmlEditor} this
48188 * @event savedpreview
48189 * preview the saved version of htmlEditor
48190 * @param {HtmlEditor} this
48192 savedpreview: true,
48195 * @event stylesheetsclick
48196 * Fires when press the Sytlesheets button
48197 * @param {Roo.HtmlEditorCore} this
48199 stylesheetsclick: true,
48202 * Fires when press user pastes into the editor
48203 * @param {Roo.HtmlEditorCore} this
48207 this.defaultAutoCreate = {
48209 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48210 autocomplete: "new-password"
48215 * Protected method that will not generally be called directly. It
48216 * is called when the editor creates its toolbar. Override this method if you need to
48217 * add custom toolbar buttons.
48218 * @param {HtmlEditor} editor
48220 createToolbar : function(editor){
48221 Roo.log("create toolbars");
48222 if (!editor.toolbars || !editor.toolbars.length) {
48223 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48226 for (var i =0 ; i < editor.toolbars.length;i++) {
48227 editor.toolbars[i] = Roo.factory(
48228 typeof(editor.toolbars[i]) == 'string' ?
48229 { xtype: editor.toolbars[i]} : editor.toolbars[i],
48230 Roo.form.HtmlEditor);
48231 editor.toolbars[i].init(editor);
48239 onRender : function(ct, position)
48242 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48244 this.wrap = this.el.wrap({
48245 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48248 this.editorcore.onRender(ct, position);
48250 if (this.resizable) {
48251 this.resizeEl = new Roo.Resizable(this.wrap, {
48255 minHeight : this.height,
48256 height: this.height,
48257 handles : this.resizable,
48260 resize : function(r, w, h) {
48261 _t.onResize(w,h); // -something
48267 this.createToolbar(this);
48271 this.setSize(this.wrap.getSize());
48273 if (this.resizeEl) {
48274 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48275 // should trigger onReize..
48278 this.keyNav = new Roo.KeyNav(this.el, {
48280 "tab" : function(e){
48281 e.preventDefault();
48283 var value = this.getValue();
48285 var start = this.el.dom.selectionStart;
48286 var end = this.el.dom.selectionEnd;
48290 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48291 this.el.dom.setSelectionRange(end + 1, end + 1);
48295 var f = value.substring(0, start).split("\t");
48297 if(f.pop().length != 0){
48301 this.setValue(f.join("\t") + value.substring(end));
48302 this.el.dom.setSelectionRange(start - 1, start - 1);
48306 "home" : function(e){
48307 e.preventDefault();
48309 var curr = this.el.dom.selectionStart;
48310 var lines = this.getValue().split("\n");
48317 this.el.dom.setSelectionRange(0, 0);
48323 for (var i = 0; i < lines.length;i++) {
48324 pos += lines[i].length;
48334 pos -= lines[i].length;
48340 this.el.dom.setSelectionRange(pos, pos);
48344 this.el.dom.selectionStart = pos;
48345 this.el.dom.selectionEnd = curr;
48348 "end" : function(e){
48349 e.preventDefault();
48351 var curr = this.el.dom.selectionStart;
48352 var lines = this.getValue().split("\n");
48359 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48365 for (var i = 0; i < lines.length;i++) {
48367 pos += lines[i].length;
48381 this.el.dom.setSelectionRange(pos, pos);
48385 this.el.dom.selectionStart = curr;
48386 this.el.dom.selectionEnd = pos;
48391 doRelay : function(foo, bar, hname){
48392 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48398 // if(this.autosave && this.w){
48399 // this.autoSaveFn = setInterval(this.autosave, 1000);
48404 onResize : function(w, h)
48406 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48411 if(typeof w == 'number'){
48412 var aw = w - this.wrap.getFrameWidth('lr');
48413 this.el.setWidth(this.adjustWidth('textarea', aw));
48416 if(typeof h == 'number'){
48418 for (var i =0; i < this.toolbars.length;i++) {
48419 // fixme - ask toolbars for heights?
48420 tbh += this.toolbars[i].tb.el.getHeight();
48421 if (this.toolbars[i].footer) {
48422 tbh += this.toolbars[i].footer.el.getHeight();
48429 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48430 ah -= 5; // knock a few pixes off for look..
48432 this.el.setHeight(this.adjustWidth('textarea', ah));
48436 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48437 this.editorcore.onResize(ew,eh);
48442 * Toggles the editor between standard and source edit mode.
48443 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48445 toggleSourceEdit : function(sourceEditMode)
48447 this.editorcore.toggleSourceEdit(sourceEditMode);
48449 if(this.editorcore.sourceEditMode){
48450 Roo.log('editor - showing textarea');
48453 // Roo.log(this.syncValue());
48454 this.editorcore.syncValue();
48455 this.el.removeClass('x-hidden');
48456 this.el.dom.removeAttribute('tabIndex');
48458 this.el.dom.scrollTop = 0;
48461 for (var i = 0; i < this.toolbars.length; i++) {
48462 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48463 this.toolbars[i].tb.hide();
48464 this.toolbars[i].footer.hide();
48469 Roo.log('editor - hiding textarea');
48471 // Roo.log(this.pushValue());
48472 this.editorcore.pushValue();
48474 this.el.addClass('x-hidden');
48475 this.el.dom.setAttribute('tabIndex', -1);
48477 for (var i = 0; i < this.toolbars.length; i++) {
48478 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48479 this.toolbars[i].tb.show();
48480 this.toolbars[i].footer.show();
48484 //this.deferFocus();
48487 this.setSize(this.wrap.getSize());
48488 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48490 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48493 // private (for BoxComponent)
48494 adjustSize : Roo.BoxComponent.prototype.adjustSize,
48496 // private (for BoxComponent)
48497 getResizeEl : function(){
48501 // private (for BoxComponent)
48502 getPositionEl : function(){
48507 initEvents : function(){
48508 this.originalValue = this.getValue();
48512 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48515 markInvalid : Roo.emptyFn,
48517 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48520 clearInvalid : Roo.emptyFn,
48522 setValue : function(v){
48523 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48524 this.editorcore.pushValue();
48529 deferFocus : function(){
48530 this.focus.defer(10, this);
48534 focus : function(){
48535 this.editorcore.focus();
48541 onDestroy : function(){
48547 for (var i =0; i < this.toolbars.length;i++) {
48548 // fixme - ask toolbars for heights?
48549 this.toolbars[i].onDestroy();
48552 this.wrap.dom.innerHTML = '';
48553 this.wrap.remove();
48558 onFirstFocus : function(){
48559 //Roo.log("onFirstFocus");
48560 this.editorcore.onFirstFocus();
48561 for (var i =0; i < this.toolbars.length;i++) {
48562 this.toolbars[i].onFirstFocus();
48568 syncValue : function()
48570 this.editorcore.syncValue();
48573 pushValue : function()
48575 this.editorcore.pushValue();
48578 setStylesheets : function(stylesheets)
48580 this.editorcore.setStylesheets(stylesheets);
48583 removeStylesheets : function()
48585 this.editorcore.removeStylesheets();
48589 // hide stuff that is not compatible
48603 * @event specialkey
48607 * @cfg {String} fieldClass @hide
48610 * @cfg {String} focusClass @hide
48613 * @cfg {String} autoCreate @hide
48616 * @cfg {String} inputType @hide
48619 * @cfg {String} invalidClass @hide
48622 * @cfg {String} invalidText @hide
48625 * @cfg {String} msgFx @hide
48628 * @cfg {String} validateOnBlur @hide
48632 // <script type="text/javascript">
48635 * Ext JS Library 1.1.1
48636 * Copyright(c) 2006-2007, Ext JS, LLC.
48642 * @class Roo.form.HtmlEditorToolbar1
48647 new Roo.form.HtmlEditor({
48650 new Roo.form.HtmlEditorToolbar1({
48651 disable : { fonts: 1 , format: 1, ..., ... , ...],
48657 * @cfg {Object} disable List of elements to disable..
48658 * @cfg {Array} btns List of additional buttons.
48662 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48665 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48668 Roo.apply(this, config);
48670 // default disabled, based on 'good practice'..
48671 this.disable = this.disable || {};
48672 Roo.applyIf(this.disable, {
48675 specialElements : true
48679 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48680 // dont call parent... till later.
48683 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
48690 editorcore : false,
48692 * @cfg {Object} disable List of toolbar elements to disable
48699 * @cfg {String} createLinkText The default text for the create link prompt
48701 createLinkText : 'Please enter the URL for the link:',
48703 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48705 defaultLinkValue : 'http:/'+'/',
48709 * @cfg {Array} fontFamilies An array of available font families
48727 // "á" , ?? a acute?
48732 "°" // , // degrees
48734 // "é" , // e ecute
48735 // "ú" , // u ecute?
48738 specialElements : [
48740 text: "Insert Table",
48743 ihtml : '<table><tr><td>Cell</td></tr></table>'
48747 text: "Insert Image",
48750 ihtml : '<img src="about:blank"/>'
48759 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
48760 "input:submit", "input:button", "select", "textarea", "label" ],
48763 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
48765 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48774 * @cfg {String} defaultFont default font to use.
48776 defaultFont: 'tahoma',
48778 fontSelect : false,
48781 formatCombo : false,
48783 init : function(editor)
48785 this.editor = editor;
48786 this.editorcore = editor.editorcore ? editor.editorcore : editor;
48787 var editorcore = this.editorcore;
48791 var fid = editorcore.frameId;
48793 function btn(id, toggle, handler){
48794 var xid = fid + '-'+ id ;
48798 cls : 'x-btn-icon x-edit-'+id,
48799 enableToggle:toggle !== false,
48800 scope: _t, // was editor...
48801 handler:handler||_t.relayBtnCmd,
48802 clickEvent:'mousedown',
48803 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48810 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48812 // stop form submits
48813 tb.el.on('click', function(e){
48814 e.preventDefault(); // what does this do?
48817 if(!this.disable.font) { // && !Roo.isSafari){
48818 /* why no safari for fonts
48819 editor.fontSelect = tb.el.createChild({
48822 cls:'x-font-select',
48823 html: this.createFontOptions()
48826 editor.fontSelect.on('change', function(){
48827 var font = editor.fontSelect.dom.value;
48828 editor.relayCmd('fontname', font);
48829 editor.deferFocus();
48833 editor.fontSelect.dom,
48839 if(!this.disable.formats){
48840 this.formatCombo = new Roo.form.ComboBox({
48841 store: new Roo.data.SimpleStore({
48844 data : this.formats // from states.js
48848 //autoCreate : {tag: "div", size: "20"},
48849 displayField:'tag',
48853 triggerAction: 'all',
48854 emptyText:'Add tag',
48855 selectOnFocus:true,
48858 'select': function(c, r, i) {
48859 editorcore.insertTag(r.get('tag'));
48865 tb.addField(this.formatCombo);
48869 if(!this.disable.format){
48874 btn('strikethrough')
48877 if(!this.disable.fontSize){
48882 btn('increasefontsize', false, editorcore.adjustFont),
48883 btn('decreasefontsize', false, editorcore.adjustFont)
48888 if(!this.disable.colors){
48891 id:editorcore.frameId +'-forecolor',
48892 cls:'x-btn-icon x-edit-forecolor',
48893 clickEvent:'mousedown',
48894 tooltip: this.buttonTips['forecolor'] || undefined,
48896 menu : new Roo.menu.ColorMenu({
48897 allowReselect: true,
48898 focus: Roo.emptyFn,
48901 selectHandler: function(cp, color){
48902 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48903 editor.deferFocus();
48906 clickEvent:'mousedown'
48909 id:editorcore.frameId +'backcolor',
48910 cls:'x-btn-icon x-edit-backcolor',
48911 clickEvent:'mousedown',
48912 tooltip: this.buttonTips['backcolor'] || undefined,
48914 menu : new Roo.menu.ColorMenu({
48915 focus: Roo.emptyFn,
48918 allowReselect: true,
48919 selectHandler: function(cp, color){
48921 editorcore.execCmd('useCSS', false);
48922 editorcore.execCmd('hilitecolor', color);
48923 editorcore.execCmd('useCSS', true);
48924 editor.deferFocus();
48926 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
48927 Roo.isSafari || Roo.isIE ? '#'+color : color);
48928 editor.deferFocus();
48932 clickEvent:'mousedown'
48937 // now add all the items...
48940 if(!this.disable.alignments){
48943 btn('justifyleft'),
48944 btn('justifycenter'),
48945 btn('justifyright')
48949 //if(!Roo.isSafari){
48950 if(!this.disable.links){
48953 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
48957 if(!this.disable.lists){
48960 btn('insertorderedlist'),
48961 btn('insertunorderedlist')
48964 if(!this.disable.sourceEdit){
48967 btn('sourceedit', true, function(btn){
48968 this.toggleSourceEdit(btn.pressed);
48975 // special menu.. - needs to be tidied up..
48976 if (!this.disable.special) {
48979 cls: 'x-edit-none',
48985 for (var i =0; i < this.specialChars.length; i++) {
48986 smenu.menu.items.push({
48988 html: this.specialChars[i],
48989 handler: function(a,b) {
48990 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48991 //editor.insertAtCursor(a.html);
49005 if (!this.disable.cleanStyles) {
49007 cls: 'x-btn-icon x-btn-clear',
49013 for (var i =0; i < this.cleanStyles.length; i++) {
49014 cmenu.menu.items.push({
49015 actiontype : this.cleanStyles[i],
49016 html: 'Remove ' + this.cleanStyles[i],
49017 handler: function(a,b) {
49020 var c = Roo.get(editorcore.doc.body);
49021 c.select('[style]').each(function(s) {
49022 s.dom.style.removeProperty(a.actiontype);
49024 editorcore.syncValue();
49029 cmenu.menu.items.push({
49030 actiontype : 'tablewidths',
49031 html: 'Remove Table Widths',
49032 handler: function(a,b) {
49033 editorcore.cleanTableWidths();
49034 editorcore.syncValue();
49038 cmenu.menu.items.push({
49039 actiontype : 'word',
49040 html: 'Remove MS Word Formating',
49041 handler: function(a,b) {
49042 editorcore.cleanWord();
49043 editorcore.syncValue();
49048 cmenu.menu.items.push({
49049 actiontype : 'all',
49050 html: 'Remove All Styles',
49051 handler: function(a,b) {
49053 var c = Roo.get(editorcore.doc.body);
49054 c.select('[style]').each(function(s) {
49055 s.dom.removeAttribute('style');
49057 editorcore.syncValue();
49062 cmenu.menu.items.push({
49063 actiontype : 'all',
49064 html: 'Remove All CSS Classes',
49065 handler: function(a,b) {
49067 var c = Roo.get(editorcore.doc.body);
49068 c.select('[class]').each(function(s) {
49069 s.dom.removeAttribute('class');
49071 editorcore.cleanWord();
49072 editorcore.syncValue();
49077 cmenu.menu.items.push({
49078 actiontype : 'tidy',
49079 html: 'Tidy HTML Source',
49080 handler: function(a,b) {
49081 new Roo.htmleditor.Tidy(editorcore.doc.body);
49082 editorcore.syncValue();
49091 if (!this.disable.specialElements) {
49094 cls: 'x-edit-none',
49099 for (var i =0; i < this.specialElements.length; i++) {
49100 semenu.menu.items.push(
49102 handler: function(a,b) {
49103 editor.insertAtCursor(this.ihtml);
49105 }, this.specialElements[i])
49117 for(var i =0; i< this.btns.length;i++) {
49118 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49119 b.cls = 'x-edit-none';
49121 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49122 b.cls += ' x-init-enable';
49125 b.scope = editorcore;
49133 // disable everything...
49135 this.tb.items.each(function(item){
49138 item.id != editorcore.frameId+ '-sourceedit' &&
49139 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49145 this.rendered = true;
49147 // the all the btns;
49148 editor.on('editorevent', this.updateToolbar, this);
49149 // other toolbars need to implement this..
49150 //editor.on('editmodechange', this.updateToolbar, this);
49154 relayBtnCmd : function(btn) {
49155 this.editorcore.relayCmd(btn.cmd);
49157 // private used internally
49158 createLink : function(){
49159 Roo.log("create link?");
49160 var url = prompt(this.createLinkText, this.defaultLinkValue);
49161 if(url && url != 'http:/'+'/'){
49162 this.editorcore.relayCmd('createlink', url);
49168 * Protected method that will not generally be called directly. It triggers
49169 * a toolbar update by reading the markup state of the current selection in the editor.
49171 updateToolbar: function(){
49173 if(!this.editorcore.activated){
49174 this.editor.onFirstFocus();
49178 var btns = this.tb.items.map,
49179 doc = this.editorcore.doc,
49180 frameId = this.editorcore.frameId;
49182 if(!this.disable.font && !Roo.isSafari){
49184 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49185 if(name != this.fontSelect.dom.value){
49186 this.fontSelect.dom.value = name;
49190 if(!this.disable.format){
49191 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49192 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49193 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49194 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49196 if(!this.disable.alignments){
49197 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49198 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49199 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49201 if(!Roo.isSafari && !this.disable.lists){
49202 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49203 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49206 var ans = this.editorcore.getAllAncestors();
49207 if (this.formatCombo) {
49210 var store = this.formatCombo.store;
49211 this.formatCombo.setValue("");
49212 for (var i =0; i < ans.length;i++) {
49213 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49215 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49223 // hides menus... - so this cant be on a menu...
49224 Roo.menu.MenuMgr.hideAll();
49226 //this.editorsyncValue();
49230 createFontOptions : function(){
49231 var buf = [], fs = this.fontFamilies, ff, lc;
49235 for(var i = 0, len = fs.length; i< len; i++){
49237 lc = ff.toLowerCase();
49239 '<option value="',lc,'" style="font-family:',ff,';"',
49240 (this.defaultFont == lc ? ' selected="true">' : '>'),
49245 return buf.join('');
49248 toggleSourceEdit : function(sourceEditMode){
49250 Roo.log("toolbar toogle");
49251 if(sourceEditMode === undefined){
49252 sourceEditMode = !this.sourceEditMode;
49254 this.sourceEditMode = sourceEditMode === true;
49255 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49256 // just toggle the button?
49257 if(btn.pressed !== this.sourceEditMode){
49258 btn.toggle(this.sourceEditMode);
49262 if(sourceEditMode){
49263 Roo.log("disabling buttons");
49264 this.tb.items.each(function(item){
49265 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49271 Roo.log("enabling buttons");
49272 if(this.editorcore.initialized){
49273 this.tb.items.each(function(item){
49279 Roo.log("calling toggole on editor");
49280 // tell the editor that it's been pressed..
49281 this.editor.toggleSourceEdit(sourceEditMode);
49285 * Object collection of toolbar tooltips for the buttons in the editor. The key
49286 * is the command id associated with that button and the value is a valid QuickTips object.
49291 title: 'Bold (Ctrl+B)',
49292 text: 'Make the selected text bold.',
49293 cls: 'x-html-editor-tip'
49296 title: 'Italic (Ctrl+I)',
49297 text: 'Make the selected text italic.',
49298 cls: 'x-html-editor-tip'
49306 title: 'Bold (Ctrl+B)',
49307 text: 'Make the selected text bold.',
49308 cls: 'x-html-editor-tip'
49311 title: 'Italic (Ctrl+I)',
49312 text: 'Make the selected text italic.',
49313 cls: 'x-html-editor-tip'
49316 title: 'Underline (Ctrl+U)',
49317 text: 'Underline the selected text.',
49318 cls: 'x-html-editor-tip'
49321 title: 'Strikethrough',
49322 text: 'Strikethrough the selected text.',
49323 cls: 'x-html-editor-tip'
49325 increasefontsize : {
49326 title: 'Grow Text',
49327 text: 'Increase the font size.',
49328 cls: 'x-html-editor-tip'
49330 decreasefontsize : {
49331 title: 'Shrink Text',
49332 text: 'Decrease the font size.',
49333 cls: 'x-html-editor-tip'
49336 title: 'Text Highlight Color',
49337 text: 'Change the background color of the selected text.',
49338 cls: 'x-html-editor-tip'
49341 title: 'Font Color',
49342 text: 'Change the color of the selected text.',
49343 cls: 'x-html-editor-tip'
49346 title: 'Align Text Left',
49347 text: 'Align text to the left.',
49348 cls: 'x-html-editor-tip'
49351 title: 'Center Text',
49352 text: 'Center text in the editor.',
49353 cls: 'x-html-editor-tip'
49356 title: 'Align Text Right',
49357 text: 'Align text to the right.',
49358 cls: 'x-html-editor-tip'
49360 insertunorderedlist : {
49361 title: 'Bullet List',
49362 text: 'Start a bulleted list.',
49363 cls: 'x-html-editor-tip'
49365 insertorderedlist : {
49366 title: 'Numbered List',
49367 text: 'Start a numbered list.',
49368 cls: 'x-html-editor-tip'
49371 title: 'Hyperlink',
49372 text: 'Make the selected text a hyperlink.',
49373 cls: 'x-html-editor-tip'
49376 title: 'Source Edit',
49377 text: 'Switch to source editing mode.',
49378 cls: 'x-html-editor-tip'
49382 onDestroy : function(){
49385 this.tb.items.each(function(item){
49387 item.menu.removeAll();
49389 item.menu.el.destroy();
49397 onFirstFocus: function() {
49398 this.tb.items.each(function(item){
49407 // <script type="text/javascript">
49410 * Ext JS Library 1.1.1
49411 * Copyright(c) 2006-2007, Ext JS, LLC.
49418 * @class Roo.form.HtmlEditor.ToolbarContext
49423 new Roo.form.HtmlEditor({
49426 { xtype: 'ToolbarStandard', styles : {} }
49427 { xtype: 'ToolbarContext', disable : {} }
49433 * @config : {Object} disable List of elements to disable.. (not done yet.)
49434 * @config : {Object} styles Map of styles available.
49438 Roo.form.HtmlEditor.ToolbarContext = function(config)
49441 Roo.apply(this, config);
49442 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49443 // dont call parent... till later.
49444 this.styles = this.styles || {};
49449 Roo.form.HtmlEditor.ToolbarContext.types = {
49464 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49490 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49561 name : 'selectoptions',
49567 // should we really allow this??
49568 // should this just be
49585 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49586 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49588 Roo.form.HtmlEditor.ToolbarContext.options = {
49590 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49591 [ 'Courier New', 'Courier New'],
49592 [ 'Tahoma', 'Tahoma'],
49593 [ 'Times New Roman,serif', 'Times'],
49594 [ 'Verdana','Verdana' ]
49598 // fixme - these need to be configurable..
49601 //Roo.form.HtmlEditor.ToolbarContext.types
49604 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
49611 editorcore : false,
49613 * @cfg {Object} disable List of toolbar elements to disable
49618 * @cfg {Object} styles List of styles
49619 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
49621 * These must be defined in the page, so they get rendered correctly..
49632 init : function(editor)
49634 this.editor = editor;
49635 this.editorcore = editor.editorcore ? editor.editorcore : editor;
49636 var editorcore = this.editorcore;
49638 var fid = editorcore.frameId;
49640 function btn(id, toggle, handler){
49641 var xid = fid + '-'+ id ;
49645 cls : 'x-btn-icon x-edit-'+id,
49646 enableToggle:toggle !== false,
49647 scope: editorcore, // was editor...
49648 handler:handler||editorcore.relayBtnCmd,
49649 clickEvent:'mousedown',
49650 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49654 // create a new element.
49655 var wdiv = editor.wrap.createChild({
49657 }, editor.wrap.dom.firstChild.nextSibling, true);
49659 // can we do this more than once??
49661 // stop form submits
49664 // disable everything...
49665 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49666 this.toolbars = {};
49668 for (var i in ty) {
49670 this.toolbars[i] = this.buildToolbar(ty[i],i);
49672 this.tb = this.toolbars.BODY;
49674 this.buildFooter();
49675 this.footer.show();
49676 editor.on('hide', function( ) { this.footer.hide() }, this);
49677 editor.on('show', function( ) { this.footer.show() }, this);
49680 this.rendered = true;
49682 // the all the btns;
49683 editor.on('editorevent', this.updateToolbar, this);
49684 // other toolbars need to implement this..
49685 //editor.on('editmodechange', this.updateToolbar, this);
49691 * Protected method that will not generally be called directly. It triggers
49692 * a toolbar update by reading the markup state of the current selection in the editor.
49694 * Note you can force an update by calling on('editorevent', scope, false)
49696 updateToolbar: function(editor ,ev, sel)
49700 ev.stopEvent(); // se if we can stop this looping with mutiple events.
49704 // capture mouse up - this is handy for selecting images..
49705 // perhaps should go somewhere else...
49706 if(!this.editorcore.activated){
49707 this.editor.onFirstFocus();
49710 //Roo.log(ev ? ev.target : 'NOTARGET');
49713 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49714 // selectNode - might want to handle IE?
49719 (ev.type == 'mouseup' || ev.type == 'click' ) &&
49720 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49721 // they have click on an image...
49722 // let's see if we can change the selection...
49725 // this triggers looping?
49726 //this.editorcore.selectNode(sel);
49731 //var updateFooter = sel ? false : true;
49734 var ans = this.editorcore.getAllAncestors();
49737 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49740 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
49741 sel = sel ? sel : this.editorcore.doc.body;
49742 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49746 var tn = sel.tagName.toUpperCase();
49747 var lastSel = this.tb.selectedNode;
49748 this.tb.selectedNode = sel;
49749 var left_label = tn;
49751 // ok see if we are editing a block?
49752 var sel_el = Roo.get(sel);
49754 // you are not actually selecting the block.
49755 if (sel && sel.hasAttribute('data-block')) {
49757 } else if (sel && !sel.hasAttribute('contenteditable')) {
49758 db = sel_el.findParent('[data-block]');
49759 var cepar = sel_el.findParent('[contenteditable=true]');
49760 if (db && cepar && cepar.tagName != 'BODY') {
49761 db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49767 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49769 block = Roo.htmleditor.Block.factory(db);
49771 tn = 'BLOCK.' + db.getAttribute('data-block');
49773 //this.editorcore.selectNode(db);
49774 if (typeof(this.toolbars[tn]) == 'undefined') {
49775 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
49777 this.toolbars[tn].selectedNode = db;
49778 left_label = block.friendly_name;
49779 ans = this.editorcore.getAllAncestors();
49787 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49788 return; // no change?
49794 ///console.log("show: " + tn);
49795 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49799 this.tb.items.first().el.innerHTML = left_label + ': ';
49802 // update attributes
49805 this.tb.fields.each(function(e) {
49806 e.setValue(block[e.name]);
49810 } else if (this.tb.fields && this.tb.selectedNode) {
49811 this.tb.fields.each( function(e) {
49813 e.setValue(this.tb.selectedNode.style[e.stylename]);
49816 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49818 this.updateToolbarStyles(this.tb.selectedNode);
49823 Roo.menu.MenuMgr.hideAll();
49828 // update the footer
49830 this.updateFooter(ans);
49834 updateToolbarStyles : function(sel)
49836 var hasStyles = false;
49837 for(var i in this.styles) {
49843 if (hasStyles && this.tb.hasStyles) {
49844 var st = this.tb.fields.item(0);
49846 st.store.removeAll();
49847 var cn = sel.className.split(/\s+/);
49850 if (this.styles['*']) {
49852 Roo.each(this.styles['*'], function(v) {
49853 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
49856 if (this.styles[tn]) {
49857 Roo.each(this.styles[tn], function(v) {
49858 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
49862 st.store.loadData(avs);
49869 updateFooter : function(ans)
49872 if (ans === false) {
49873 this.footDisp.dom.innerHTML = '';
49877 this.footerEls = ans.reverse();
49878 Roo.each(this.footerEls, function(a,i) {
49879 if (!a) { return; }
49880 html += html.length ? ' > ' : '';
49882 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49887 var sz = this.footDisp.up('td').getSize();
49888 this.footDisp.dom.style.width = (sz.width -10) + 'px';
49889 this.footDisp.dom.style.marginLeft = '5px';
49891 this.footDisp.dom.style.overflow = 'hidden';
49893 this.footDisp.dom.innerHTML = html;
49900 onDestroy : function(){
49903 this.tb.items.each(function(item){
49905 item.menu.removeAll();
49907 item.menu.el.destroy();
49915 onFirstFocus: function() {
49916 // need to do this for all the toolbars..
49917 this.tb.items.each(function(item){
49921 buildToolbar: function(tlist, nm, friendly_name, block)
49923 var editor = this.editor;
49924 var editorcore = this.editorcore;
49925 // create a new element.
49926 var wdiv = editor.wrap.createChild({
49928 }, editor.wrap.dom.firstChild.nextSibling, true);
49931 var tb = new Roo.Toolbar(wdiv);
49932 ///this.tb = tb; // << this sets the active toolbar..
49933 if (tlist === false && block) {
49934 tlist = block.contextMenu(this);
49937 tb.hasStyles = false;
49940 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
49942 var styles = Array.from(this.styles);
49946 if (styles && styles.length) {
49947 tb.hasStyles = true;
49948 // this needs a multi-select checkbox...
49949 tb.addField( new Roo.form.ComboBox({
49950 store: new Roo.data.SimpleStore({
49952 fields: ['val', 'selected'],
49955 name : '-roo-edit-className',
49956 attrname : 'className',
49957 displayField: 'val',
49961 triggerAction: 'all',
49962 emptyText:'Select Style',
49963 selectOnFocus:true,
49966 'select': function(c, r, i) {
49967 // initial support only for on class per el..
49968 tb.selectedNode.className = r ? r.get('val') : '';
49969 editorcore.syncValue();
49976 var tbc = Roo.form.HtmlEditor.ToolbarContext;
49979 for (var i = 0; i < tlist.length; i++) {
49981 // newer versions will use xtype cfg to create menus.
49982 if (typeof(tlist[i].xtype) != 'undefined') {
49984 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49990 var item = tlist[i];
49991 tb.add(item.title + ": ");
49994 //optname == used so you can configure the options available..
49995 var opts = item.opts ? item.opts : false;
49996 if (item.optname) { // use the b
49997 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50002 // opts == pulldown..
50003 tb.addField( new Roo.form.ComboBox({
50004 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50006 fields: ['val', 'display'],
50009 name : '-roo-edit-' + tlist[i].name,
50011 attrname : tlist[i].name,
50012 stylename : item.style ? item.style : false,
50014 displayField: item.displayField ? item.displayField : 'val',
50015 valueField : 'val',
50017 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
50019 triggerAction: 'all',
50020 emptyText:'Select',
50021 selectOnFocus:true,
50022 width: item.width ? item.width : 130,
50024 'select': function(c, r, i) {
50025 if (tb.selectedNode.hasAttribute('data-block')) {
50026 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50027 b[c.attrname] = r.get('val');
50028 b.updateElement(tb.selectedNode);
50029 editorcore.syncValue();
50034 tb.selectedNode.style[c.stylename] = r.get('val');
50035 editorcore.syncValue();
50039 tb.selectedNode.removeAttribute(c.attrname);
50040 editorcore.syncValue();
50043 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50044 editorcore.syncValue();
50053 tb.addField( new Roo.form.TextField({
50056 //allowBlank:false,
50062 tb.addField( new Roo.form.TextField({
50063 name: '-roo-edit-' + tlist[i].name,
50064 attrname : tlist[i].name,
50070 'change' : function(f, nv, ov) {
50072 if (tb.selectedNode.hasAttribute('data-block')) {
50073 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50074 b[f.attrname] = nv;
50075 b.updateElement(tb.selectedNode);
50076 editorcore.syncValue();
50080 tb.selectedNode.setAttribute(f.attrname, nv);
50081 editorcore.syncValue();
50094 text: 'Stylesheets',
50097 click : function ()
50099 _this.editor.fireEvent('stylesheetsclick', _this.editor);
50107 text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50110 click : function ()
50112 var sn = tb.selectedNode;
50114 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50120 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50121 if (sn.hasAttribute('data-block')) {
50122 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
50123 sn.parentNode.removeChild(sn);
50125 } else if (sn && sn.tagName != 'BODY') {
50126 // remove and keep parents.
50127 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50132 var range = editorcore.createRange();
50134 range.setStart(stn,0);
50135 range.setEnd(stn,0);
50136 var selection = editorcore.getSelection();
50137 selection.removeAllRanges();
50138 selection.addRange(range);
50141 //_this.updateToolbar(null, null, pn);
50142 _this.updateToolbar(null, null, null);
50143 _this.updateFooter(false);
50154 tb.el.on('click', function(e){
50155 e.preventDefault(); // what does this do?
50157 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50160 // dont need to disable them... as they will get hidden
50165 buildFooter : function()
50168 var fel = this.editor.wrap.createChild();
50169 this.footer = new Roo.Toolbar(fel);
50170 // toolbar has scrolly on left / right?
50171 var footDisp= new Roo.Toolbar.Fill();
50177 handler : function() {
50178 _t.footDisp.scrollTo('left',0,true)
50182 this.footer.add( footDisp );
50187 handler : function() {
50189 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50193 var fel = Roo.get(footDisp.el);
50194 fel.addClass('x-editor-context');
50195 this.footDispWrap = fel;
50196 this.footDispWrap.overflow = 'hidden';
50198 this.footDisp = fel.createChild();
50199 this.footDispWrap.on('click', this.onContextClick, this)
50203 // when the footer contect changes
50204 onContextClick : function (ev,dom)
50206 ev.preventDefault();
50207 var cn = dom.className;
50209 if (!cn.match(/x-ed-loc-/)) {
50212 var n = cn.split('-').pop();
50213 var ans = this.footerEls;
50216 this.editorcore.selectNode(sel);
50219 this.updateToolbar(null, null, sel);
50236 * Ext JS Library 1.1.1
50237 * Copyright(c) 2006-2007, Ext JS, LLC.
50239 * Originally Released Under LGPL - original licence link has changed is not relivant.
50242 * <script type="text/javascript">
50246 * @class Roo.form.BasicForm
50247 * @extends Roo.util.Observable
50248 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50250 * @param {String/HTMLElement/Roo.Element} el The form element or its id
50251 * @param {Object} config Configuration options
50253 Roo.form.BasicForm = function(el, config){
50254 this.allItems = [];
50255 this.childForms = [];
50256 Roo.apply(this, config);
50258 * The Roo.form.Field items in this form.
50259 * @type MixedCollection
50263 this.items = new Roo.util.MixedCollection(false, function(o){
50264 return o.id || (o.id = Roo.id());
50268 * @event beforeaction
50269 * Fires before any action is performed. Return false to cancel the action.
50270 * @param {Form} this
50271 * @param {Action} action The action to be performed
50273 beforeaction: true,
50275 * @event actionfailed
50276 * Fires when an action fails.
50277 * @param {Form} this
50278 * @param {Action} action The action that failed
50280 actionfailed : true,
50282 * @event actioncomplete
50283 * Fires when an action is completed.
50284 * @param {Form} this
50285 * @param {Action} action The action that completed
50287 actioncomplete : true
50292 Roo.form.BasicForm.superclass.constructor.call(this);
50294 Roo.form.BasicForm.popover.apply();
50297 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50299 * @cfg {String} method
50300 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50303 * @cfg {DataReader} reader
50304 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50305 * This is optional as there is built-in support for processing JSON.
50308 * @cfg {DataReader} errorReader
50309 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50310 * This is completely optional as there is built-in support for processing JSON.
50313 * @cfg {String} url
50314 * The URL to use for form actions if one isn't supplied in the action options.
50317 * @cfg {Boolean} fileUpload
50318 * Set to true if this form is a file upload.
50322 * @cfg {Object} baseParams
50323 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50328 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50333 activeAction : null,
50336 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50337 * or setValues() data instead of when the form was first created.
50339 trackResetOnLoad : false,
50343 * childForms - used for multi-tab forms
50346 childForms : false,
50349 * allItems - full list of fields.
50355 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50356 * element by passing it or its id or mask the form itself by passing in true.
50359 waitMsgTarget : false,
50364 disableMask : false,
50367 * @cfg {Boolean} errorMask (true|false) default false
50372 * @cfg {Number} maskOffset Default 100
50377 initEl : function(el){
50378 this.el = Roo.get(el);
50379 this.id = this.el.id || Roo.id();
50380 this.el.on('submit', this.onSubmit, this);
50381 this.el.addClass('x-form');
50385 onSubmit : function(e){
50390 * Returns true if client-side validation on the form is successful.
50393 isValid : function(){
50395 var target = false;
50396 this.items.each(function(f){
50403 if(!target && f.el.isVisible(true)){
50408 if(this.errorMask && !valid){
50409 Roo.form.BasicForm.popover.mask(this, target);
50415 * Returns array of invalid form fields.
50419 invalidFields : function()
50422 this.items.each(function(f){
50435 * DEPRICATED Returns true if any fields in this form have changed since their original load.
50438 isDirty : function(){
50440 this.items.each(function(f){
50450 * Returns true if any fields in this form have changed since their original load. (New version)
50454 hasChanged : function()
50457 this.items.each(function(f){
50458 if(f.hasChanged()){
50467 * Resets all hasChanged to 'false' -
50468 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50469 * So hasChanged storage is only to be used for this purpose
50472 resetHasChanged : function()
50474 this.items.each(function(f){
50475 f.resetHasChanged();
50482 * Performs a predefined action (submit or load) or custom actions you define on this form.
50483 * @param {String} actionName The name of the action type
50484 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
50485 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50486 * accept other config options):
50488 Property Type Description
50489 ---------------- --------------- ----------------------------------------------------------------------------------
50490 url String The url for the action (defaults to the form's url)
50491 method String The form method to use (defaults to the form's method, or POST if not defined)
50492 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
50493 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
50494 validate the form on the client (defaults to false)
50496 * @return {BasicForm} this
50498 doAction : function(action, options){
50499 if(typeof action == 'string'){
50500 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50502 if(this.fireEvent('beforeaction', this, action) !== false){
50503 this.beforeAction(action);
50504 action.run.defer(100, action);
50510 * Shortcut to do a submit action.
50511 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50512 * @return {BasicForm} this
50514 submit : function(options){
50515 this.doAction('submit', options);
50520 * Shortcut to do a load action.
50521 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50522 * @return {BasicForm} this
50524 load : function(options){
50525 this.doAction('load', options);
50530 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50531 * @param {Record} record The record to edit
50532 * @return {BasicForm} this
50534 updateRecord : function(record){
50535 record.beginEdit();
50536 var fs = record.fields;
50537 fs.each(function(f){
50538 var field = this.findField(f.name);
50540 record.set(f.name, field.getValue());
50548 * Loads an Roo.data.Record into this form.
50549 * @param {Record} record The record to load
50550 * @return {BasicForm} this
50552 loadRecord : function(record){
50553 this.setValues(record.data);
50558 beforeAction : function(action){
50559 var o = action.options;
50561 if(!this.disableMask) {
50562 if(this.waitMsgTarget === true){
50563 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50564 }else if(this.waitMsgTarget){
50565 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50566 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50568 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50576 afterAction : function(action, success){
50577 this.activeAction = null;
50578 var o = action.options;
50580 if(!this.disableMask) {
50581 if(this.waitMsgTarget === true){
50583 }else if(this.waitMsgTarget){
50584 this.waitMsgTarget.unmask();
50586 Roo.MessageBox.updateProgress(1);
50587 Roo.MessageBox.hide();
50595 Roo.callback(o.success, o.scope, [this, action]);
50596 this.fireEvent('actioncomplete', this, action);
50600 // failure condition..
50601 // we have a scenario where updates need confirming.
50602 // eg. if a locking scenario exists..
50603 // we look for { errors : { needs_confirm : true }} in the response.
50605 (typeof(action.result) != 'undefined') &&
50606 (typeof(action.result.errors) != 'undefined') &&
50607 (typeof(action.result.errors.needs_confirm) != 'undefined')
50610 Roo.MessageBox.confirm(
50611 "Change requires confirmation",
50612 action.result.errorMsg,
50617 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
50627 Roo.callback(o.failure, o.scope, [this, action]);
50628 // show an error message if no failed handler is set..
50629 if (!this.hasListener('actionfailed')) {
50630 Roo.MessageBox.alert("Error",
50631 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50632 action.result.errorMsg :
50633 "Saving Failed, please check your entries or try again"
50637 this.fireEvent('actionfailed', this, action);
50643 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50644 * @param {String} id The value to search for
50647 findField : function(id){
50648 var field = this.items.get(id);
50650 this.items.each(function(f){
50651 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50657 return field || null;
50661 * Add a secondary form to this one,
50662 * Used to provide tabbed forms. One form is primary, with hidden values
50663 * which mirror the elements from the other forms.
50665 * @param {Roo.form.Form} form to add.
50668 addForm : function(form)
50671 if (this.childForms.indexOf(form) > -1) {
50675 this.childForms.push(form);
50677 Roo.each(form.allItems, function (fe) {
50679 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50680 if (this.findField(n)) { // already added..
50683 var add = new Roo.form.Hidden({
50686 add.render(this.el);
50693 * Mark fields in this form invalid in bulk.
50694 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50695 * @return {BasicForm} this
50697 markInvalid : function(errors){
50698 if(errors instanceof Array){
50699 for(var i = 0, len = errors.length; i < len; i++){
50700 var fieldError = errors[i];
50701 var f = this.findField(fieldError.id);
50703 f.markInvalid(fieldError.msg);
50709 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50710 field.markInvalid(errors[id]);
50714 Roo.each(this.childForms || [], function (f) {
50715 f.markInvalid(errors);
50722 * Set values for fields in this form in bulk.
50723 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50724 * @return {BasicForm} this
50726 setValues : function(values){
50727 if(values instanceof Array){ // array of objects
50728 for(var i = 0, len = values.length; i < len; i++){
50730 var f = this.findField(v.id);
50732 f.setValue(v.value);
50733 if(this.trackResetOnLoad){
50734 f.originalValue = f.getValue();
50738 }else{ // object hash
50741 if(typeof values[id] != 'function' && (field = this.findField(id))){
50743 if (field.setFromData &&
50744 field.valueField &&
50745 field.displayField &&
50746 // combos' with local stores can
50747 // be queried via setValue()
50748 // to set their value..
50749 (field.store && !field.store.isLocal)
50753 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50754 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50755 field.setFromData(sd);
50758 field.setValue(values[id]);
50762 if(this.trackResetOnLoad){
50763 field.originalValue = field.getValue();
50768 this.resetHasChanged();
50771 Roo.each(this.childForms || [], function (f) {
50772 f.setValues(values);
50773 f.resetHasChanged();
50780 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50781 * they are returned as an array.
50782 * @param {Boolean} asString
50785 getValues : function(asString)
50787 if (this.childForms) {
50788 // copy values from the child forms
50789 Roo.each(this.childForms, function (f) {
50790 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50795 if (typeof(FormData) != 'undefined' && asString !== true) {
50796 // this relies on a 'recent' version of chrome apparently...
50798 var fd = (new FormData(this.el.dom)).entries();
50800 var ent = fd.next();
50801 while (!ent.done) {
50802 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50813 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50814 if(asString === true){
50817 return Roo.urlDecode(fs);
50821 * Returns the fields in this form as an object with key/value pairs.
50822 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50825 getFieldValues : function(with_hidden)
50827 if (this.childForms) {
50828 // copy values from the child forms
50829 // should this call getFieldValues - probably not as we do not currently copy
50830 // hidden fields when we generate..
50831 Roo.each(this.childForms, function (f) {
50832 this.setValues(f.getFieldValues());
50837 this.items.each(function(f){
50840 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50841 // if a subform contains a copy of them.
50842 // if you have subforms with the same editable data, you will need to copy the data back
50846 if (!f.getName()) {
50849 var v = f.getValue();
50850 if (f.inputType =='radio') {
50851 if (typeof(ret[f.getName()]) == 'undefined') {
50852 ret[f.getName()] = ''; // empty..
50855 if (!f.el.dom.checked) {
50859 v = f.el.dom.value;
50863 // not sure if this supported any more..
50864 if ((typeof(v) == 'object') && f.getRawValue) {
50865 v = f.getRawValue() ; // dates..
50867 // combo boxes where name != hiddenName...
50868 if (f.name != f.getName()) {
50869 ret[f.name] = f.getRawValue();
50871 ret[f.getName()] = v;
50878 * Clears all invalid messages in this form.
50879 * @return {BasicForm} this
50881 clearInvalid : function(){
50882 this.items.each(function(f){
50886 Roo.each(this.childForms || [], function (f) {
50895 * Resets this form.
50896 * @return {BasicForm} this
50898 reset : function(){
50899 this.items.each(function(f){
50903 Roo.each(this.childForms || [], function (f) {
50906 this.resetHasChanged();
50912 * Add Roo.form components to this form.
50913 * @param {Field} field1
50914 * @param {Field} field2 (optional)
50915 * @param {Field} etc (optional)
50916 * @return {BasicForm} this
50919 this.items.addAll(Array.prototype.slice.call(arguments, 0));
50925 * Removes a field from the items collection (does NOT remove its markup).
50926 * @param {Field} field
50927 * @return {BasicForm} this
50929 remove : function(field){
50930 this.items.remove(field);
50935 * Looks at the fields in this form, checks them for an id attribute,
50936 * and calls applyTo on the existing dom element with that id.
50937 * @return {BasicForm} this
50939 render : function(){
50940 this.items.each(function(f){
50941 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50949 * Calls {@link Ext#apply} for all fields in this form with the passed object.
50950 * @param {Object} values
50951 * @return {BasicForm} this
50953 applyToFields : function(o){
50954 this.items.each(function(f){
50961 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50962 * @param {Object} values
50963 * @return {BasicForm} this
50965 applyIfToFields : function(o){
50966 this.items.each(function(f){
50974 Roo.BasicForm = Roo.form.BasicForm;
50976 Roo.apply(Roo.form.BasicForm, {
50990 intervalID : false,
50996 if(this.isApplied){
51001 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51002 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51003 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51004 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51007 this.maskEl.top.enableDisplayMode("block");
51008 this.maskEl.left.enableDisplayMode("block");
51009 this.maskEl.bottom.enableDisplayMode("block");
51010 this.maskEl.right.enableDisplayMode("block");
51012 Roo.get(document.body).on('click', function(){
51016 Roo.get(document.body).on('touchstart', function(){
51020 this.isApplied = true
51023 mask : function(form, target)
51027 this.target = target;
51029 if(!this.form.errorMask || !target.el){
51033 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51035 var ot = this.target.el.calcOffsetsTo(scrollable);
51037 var scrollTo = ot[1] - this.form.maskOffset;
51039 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51041 scrollable.scrollTo('top', scrollTo);
51043 var el = this.target.wrap || this.target.el;
51045 var box = el.getBox();
51047 this.maskEl.top.setStyle('position', 'absolute');
51048 this.maskEl.top.setStyle('z-index', 10000);
51049 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51050 this.maskEl.top.setLeft(0);
51051 this.maskEl.top.setTop(0);
51052 this.maskEl.top.show();
51054 this.maskEl.left.setStyle('position', 'absolute');
51055 this.maskEl.left.setStyle('z-index', 10000);
51056 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51057 this.maskEl.left.setLeft(0);
51058 this.maskEl.left.setTop(box.y - this.padding);
51059 this.maskEl.left.show();
51061 this.maskEl.bottom.setStyle('position', 'absolute');
51062 this.maskEl.bottom.setStyle('z-index', 10000);
51063 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51064 this.maskEl.bottom.setLeft(0);
51065 this.maskEl.bottom.setTop(box.bottom + this.padding);
51066 this.maskEl.bottom.show();
51068 this.maskEl.right.setStyle('position', 'absolute');
51069 this.maskEl.right.setStyle('z-index', 10000);
51070 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51071 this.maskEl.right.setLeft(box.right + this.padding);
51072 this.maskEl.right.setTop(box.y - this.padding);
51073 this.maskEl.right.show();
51075 this.intervalID = window.setInterval(function() {
51076 Roo.form.BasicForm.popover.unmask();
51079 window.onwheel = function(){ return false;};
51081 (function(){ this.isMasked = true; }).defer(500, this);
51085 unmask : function()
51087 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51091 this.maskEl.top.setStyle('position', 'absolute');
51092 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51093 this.maskEl.top.hide();
51095 this.maskEl.left.setStyle('position', 'absolute');
51096 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51097 this.maskEl.left.hide();
51099 this.maskEl.bottom.setStyle('position', 'absolute');
51100 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51101 this.maskEl.bottom.hide();
51103 this.maskEl.right.setStyle('position', 'absolute');
51104 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51105 this.maskEl.right.hide();
51107 window.onwheel = function(){ return true;};
51109 if(this.intervalID){
51110 window.clearInterval(this.intervalID);
51111 this.intervalID = false;
51114 this.isMasked = false;
51122 * Ext JS Library 1.1.1
51123 * Copyright(c) 2006-2007, Ext JS, LLC.
51125 * Originally Released Under LGPL - original licence link has changed is not relivant.
51128 * <script type="text/javascript">
51132 * @class Roo.form.Form
51133 * @extends Roo.form.BasicForm
51134 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51135 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51137 * @param {Object} config Configuration options
51139 Roo.form.Form = function(config){
51141 if (config.items) {
51142 xitems = config.items;
51143 delete config.items;
51147 Roo.form.Form.superclass.constructor.call(this, null, config);
51148 this.url = this.url || this.action;
51150 this.root = new Roo.form.Layout(Roo.applyIf({
51154 this.active = this.root;
51156 * Array of all the buttons that have been added to this form via {@link addButton}
51160 this.allItems = [];
51163 * @event clientvalidation
51164 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51165 * @param {Form} this
51166 * @param {Boolean} valid true if the form has passed client-side validation
51168 clientvalidation: true,
51171 * Fires when the form is rendered
51172 * @param {Roo.form.Form} form
51177 if (this.progressUrl) {
51178 // push a hidden field onto the list of fields..
51182 name : 'UPLOAD_IDENTIFIER'
51187 Roo.each(xitems, this.addxtype, this);
51191 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51193 * @cfg {Roo.Button} buttons[] buttons at bottom of form
51197 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51200 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51203 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51205 buttonAlign:'center',
51208 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51213 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51214 * This property cascades to child containers if not set.
51219 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51220 * fires a looping event with that state. This is required to bind buttons to the valid
51221 * state using the config value formBind:true on the button.
51223 monitorValid : false,
51226 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51231 * @cfg {String} progressUrl - Url to return progress data
51234 progressUrl : false,
51236 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51237 * sending a formdata with extra parameters - eg uploaded elements.
51243 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51244 * fields are added and the column is closed. If no fields are passed the column remains open
51245 * until end() is called.
51246 * @param {Object} config The config to pass to the column
51247 * @param {Field} field1 (optional)
51248 * @param {Field} field2 (optional)
51249 * @param {Field} etc (optional)
51250 * @return Column The column container object
51252 column : function(c){
51253 var col = new Roo.form.Column(c);
51255 if(arguments.length > 1){ // duplicate code required because of Opera
51256 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51263 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51264 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51265 * until end() is called.
51266 * @param {Object} config The config to pass to the fieldset
51267 * @param {Field} field1 (optional)
51268 * @param {Field} field2 (optional)
51269 * @param {Field} etc (optional)
51270 * @return FieldSet The fieldset container object
51272 fieldset : function(c){
51273 var fs = new Roo.form.FieldSet(c);
51275 if(arguments.length > 1){ // duplicate code required because of Opera
51276 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51283 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51284 * fields are added and the container is closed. If no fields are passed the container remains open
51285 * until end() is called.
51286 * @param {Object} config The config to pass to the Layout
51287 * @param {Field} field1 (optional)
51288 * @param {Field} field2 (optional)
51289 * @param {Field} etc (optional)
51290 * @return Layout The container object
51292 container : function(c){
51293 var l = new Roo.form.Layout(c);
51295 if(arguments.length > 1){ // duplicate code required because of Opera
51296 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51303 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51304 * @param {Object} container A Roo.form.Layout or subclass of Layout
51305 * @return {Form} this
51307 start : function(c){
51308 // cascade label info
51309 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51310 this.active.stack.push(c);
51311 c.ownerCt = this.active;
51317 * Closes the current open container
51318 * @return {Form} this
51321 if(this.active == this.root){
51324 this.active = this.active.ownerCt;
51329 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
51330 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51331 * as the label of the field.
51332 * @param {Field} field1
51333 * @param {Field} field2 (optional)
51334 * @param {Field} etc. (optional)
51335 * @return {Form} this
51338 this.active.stack.push.apply(this.active.stack, arguments);
51339 this.allItems.push.apply(this.allItems,arguments);
51341 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51342 if(a[i].isFormField){
51347 Roo.form.Form.superclass.add.apply(this, r);
51357 * Find any element that has been added to a form, using it's ID or name
51358 * This can include framesets, columns etc. along with regular fields..
51359 * @param {String} id - id or name to find.
51361 * @return {Element} e - or false if nothing found.
51363 findbyId : function(id)
51369 Roo.each(this.allItems, function(f){
51370 if (f.id == id || f.name == id ){
51381 * Render this form into the passed container. This should only be called once!
51382 * @param {String/HTMLElement/Element} container The element this component should be rendered into
51383 * @return {Form} this
51385 render : function(ct)
51391 var o = this.autoCreate || {
51393 method : this.method || 'POST',
51394 id : this.id || Roo.id()
51396 this.initEl(ct.createChild(o));
51398 this.root.render(this.el);
51402 this.items.each(function(f){
51403 f.render('x-form-el-'+f.id);
51406 if(this.buttons.length > 0){
51407 // tables are required to maintain order and for correct IE layout
51408 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51409 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51410 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51412 var tr = tb.getElementsByTagName('tr')[0];
51413 for(var i = 0, len = this.buttons.length; i < len; i++) {
51414 var b = this.buttons[i];
51415 var td = document.createElement('td');
51416 td.className = 'x-form-btn-td';
51417 b.render(tr.appendChild(td));
51420 if(this.monitorValid){ // initialize after render
51421 this.startMonitoring();
51423 this.fireEvent('rendered', this);
51428 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51429 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51430 * object or a valid Roo.DomHelper element config
51431 * @param {Function} handler The function called when the button is clicked
51432 * @param {Object} scope (optional) The scope of the handler function
51433 * @return {Roo.Button}
51435 addButton : function(config, handler, scope){
51439 minWidth: this.minButtonWidth,
51442 if(typeof config == "string"){
51445 Roo.apply(bc, config);
51447 var btn = new Roo.Button(null, bc);
51448 this.buttons.push(btn);
51453 * Adds a series of form elements (using the xtype property as the factory method.
51454 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51455 * @param {Object} config
51458 addxtype : function()
51460 var ar = Array.prototype.slice.call(arguments, 0);
51462 for(var i = 0; i < ar.length; i++) {
51464 continue; // skip -- if this happends something invalid got sent, we
51465 // should ignore it, as basically that interface element will not show up
51466 // and that should be pretty obvious!!
51469 if (Roo.form[ar[i].xtype]) {
51471 var fe = Roo.factory(ar[i], Roo.form);
51477 fe.store.form = this;
51482 this.allItems.push(fe);
51483 if (fe.items && fe.addxtype) {
51484 fe.addxtype.apply(fe, fe.items);
51494 // console.log('adding ' + ar[i].xtype);
51496 if (ar[i].xtype == 'Button') {
51497 //console.log('adding button');
51498 //console.log(ar[i]);
51499 this.addButton(ar[i]);
51500 this.allItems.push(fe);
51504 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51505 alert('end is not supported on xtype any more, use items');
51507 // //console.log('adding end');
51515 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51516 * option "monitorValid"
51518 startMonitoring : function(){
51521 Roo.TaskMgr.start({
51522 run : this.bindHandler,
51523 interval : this.monitorPoll || 200,
51530 * Stops monitoring of the valid state of this form
51532 stopMonitoring : function(){
51533 this.bound = false;
51537 bindHandler : function(){
51539 return false; // stops binding
51542 this.items.each(function(f){
51543 if(!f.isValid(true)){
51548 for(var i = 0, len = this.buttons.length; i < len; i++){
51549 var btn = this.buttons[i];
51550 if(btn.formBind === true && btn.disabled === valid){
51551 btn.setDisabled(!valid);
51554 this.fireEvent('clientvalidation', this, valid);
51568 Roo.Form = Roo.form.Form;
51571 * Ext JS Library 1.1.1
51572 * Copyright(c) 2006-2007, Ext JS, LLC.
51574 * Originally Released Under LGPL - original licence link has changed is not relivant.
51577 * <script type="text/javascript">
51580 // as we use this in bootstrap.
51581 Roo.namespace('Roo.form');
51583 * @class Roo.form.Action
51584 * Internal Class used to handle form actions
51586 * @param {Roo.form.BasicForm} el The form element or its id
51587 * @param {Object} config Configuration options
51592 // define the action interface
51593 Roo.form.Action = function(form, options){
51595 this.options = options || {};
51598 * Client Validation Failed
51601 Roo.form.Action.CLIENT_INVALID = 'client';
51603 * Server Validation Failed
51606 Roo.form.Action.SERVER_INVALID = 'server';
51608 * Connect to Server Failed
51611 Roo.form.Action.CONNECT_FAILURE = 'connect';
51613 * Reading Data from Server Failed
51616 Roo.form.Action.LOAD_FAILURE = 'load';
51618 Roo.form.Action.prototype = {
51620 failureType : undefined,
51621 response : undefined,
51622 result : undefined,
51624 // interface method
51625 run : function(options){
51629 // interface method
51630 success : function(response){
51634 // interface method
51635 handleResponse : function(response){
51639 // default connection failure
51640 failure : function(response){
51642 this.response = response;
51643 this.failureType = Roo.form.Action.CONNECT_FAILURE;
51644 this.form.afterAction(this, false);
51647 processResponse : function(response){
51648 this.response = response;
51649 if(!response.responseText){
51652 this.result = this.handleResponse(response);
51653 return this.result;
51656 // utility functions used internally
51657 getUrl : function(appendParams){
51658 var url = this.options.url || this.form.url || this.form.el.dom.action;
51660 var p = this.getParams();
51662 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51668 getMethod : function(){
51669 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51672 getParams : function(){
51673 var bp = this.form.baseParams;
51674 var p = this.options.params;
51676 if(typeof p == "object"){
51677 p = Roo.urlEncode(Roo.applyIf(p, bp));
51678 }else if(typeof p == 'string' && bp){
51679 p += '&' + Roo.urlEncode(bp);
51682 p = Roo.urlEncode(bp);
51687 createCallback : function(){
51689 success: this.success,
51690 failure: this.failure,
51692 timeout: (this.form.timeout*1000),
51693 upload: this.form.fileUpload ? this.success : undefined
51698 Roo.form.Action.Submit = function(form, options){
51699 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51702 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51705 haveProgress : false,
51706 uploadComplete : false,
51708 // uploadProgress indicator.
51709 uploadProgress : function()
51711 if (!this.form.progressUrl) {
51715 if (!this.haveProgress) {
51716 Roo.MessageBox.progress("Uploading", "Uploading");
51718 if (this.uploadComplete) {
51719 Roo.MessageBox.hide();
51723 this.haveProgress = true;
51725 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51727 var c = new Roo.data.Connection();
51729 url : this.form.progressUrl,
51734 success : function(req){
51735 //console.log(data);
51739 rdata = Roo.decode(req.responseText)
51741 Roo.log("Invalid data from server..");
51745 if (!rdata || !rdata.success) {
51747 Roo.MessageBox.alert(Roo.encode(rdata));
51750 var data = rdata.data;
51752 if (this.uploadComplete) {
51753 Roo.MessageBox.hide();
51758 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51759 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51762 this.uploadProgress.defer(2000,this);
51765 failure: function(data) {
51766 Roo.log('progress url failed ');
51777 // run get Values on the form, so it syncs any secondary forms.
51778 this.form.getValues();
51780 var o = this.options;
51781 var method = this.getMethod();
51782 var isPost = method == 'POST';
51783 if(o.clientValidation === false || this.form.isValid()){
51785 if (this.form.progressUrl) {
51786 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51787 (new Date() * 1) + '' + Math.random());
51792 Roo.Ajax.request(Roo.apply(this.createCallback(), {
51793 form:this.form.el.dom,
51794 url:this.getUrl(!isPost),
51796 params:isPost ? this.getParams() : null,
51797 isUpload: this.form.fileUpload,
51798 formData : this.form.formData
51801 this.uploadProgress();
51803 }else if (o.clientValidation !== false){ // client validation failed
51804 this.failureType = Roo.form.Action.CLIENT_INVALID;
51805 this.form.afterAction(this, false);
51809 success : function(response)
51811 this.uploadComplete= true;
51812 if (this.haveProgress) {
51813 Roo.MessageBox.hide();
51817 var result = this.processResponse(response);
51818 if(result === true || result.success){
51819 this.form.afterAction(this, true);
51823 this.form.markInvalid(result.errors);
51824 this.failureType = Roo.form.Action.SERVER_INVALID;
51826 this.form.afterAction(this, false);
51828 failure : function(response)
51830 this.uploadComplete= true;
51831 if (this.haveProgress) {
51832 Roo.MessageBox.hide();
51835 this.response = response;
51836 this.failureType = Roo.form.Action.CONNECT_FAILURE;
51837 this.form.afterAction(this, false);
51840 handleResponse : function(response){
51841 if(this.form.errorReader){
51842 var rs = this.form.errorReader.read(response);
51845 for(var i = 0, len = rs.records.length; i < len; i++) {
51846 var r = rs.records[i];
51847 errors[i] = r.data;
51850 if(errors.length < 1){
51854 success : rs.success,
51860 ret = Roo.decode(response.responseText);
51864 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51874 Roo.form.Action.Load = function(form, options){
51875 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51876 this.reader = this.form.reader;
51879 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51884 Roo.Ajax.request(Roo.apply(
51885 this.createCallback(), {
51886 method:this.getMethod(),
51887 url:this.getUrl(false),
51888 params:this.getParams()
51892 success : function(response){
51894 var result = this.processResponse(response);
51895 if(result === true || !result.success || !result.data){
51896 this.failureType = Roo.form.Action.LOAD_FAILURE;
51897 this.form.afterAction(this, false);
51900 this.form.clearInvalid();
51901 this.form.setValues(result.data);
51902 this.form.afterAction(this, true);
51905 handleResponse : function(response){
51906 if(this.form.reader){
51907 var rs = this.form.reader.read(response);
51908 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51910 success : rs.success,
51914 return Roo.decode(response.responseText);
51918 Roo.form.Action.ACTION_TYPES = {
51919 'load' : Roo.form.Action.Load,
51920 'submit' : Roo.form.Action.Submit
51923 * Ext JS Library 1.1.1
51924 * Copyright(c) 2006-2007, Ext JS, LLC.
51926 * Originally Released Under LGPL - original licence link has changed is not relivant.
51929 * <script type="text/javascript">
51933 * @class Roo.form.Layout
51934 * @extends Roo.Component
51935 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51936 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51938 * @param {Object} config Configuration options
51940 Roo.form.Layout = function(config){
51942 if (config.items) {
51943 xitems = config.items;
51944 delete config.items;
51946 Roo.form.Layout.superclass.constructor.call(this, config);
51948 Roo.each(xitems, this.addxtype, this);
51952 Roo.extend(Roo.form.Layout, Roo.Component, {
51954 * @cfg {String/Object} autoCreate
51955 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51958 * @cfg {String/Object/Function} style
51959 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51960 * a function which returns such a specification.
51963 * @cfg {String} labelAlign
51964 * Valid values are "left," "top" and "right" (defaults to "left")
51967 * @cfg {Number} labelWidth
51968 * Fixed width in pixels of all field labels (defaults to undefined)
51971 * @cfg {Boolean} clear
51972 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51976 * @cfg {String} labelSeparator
51977 * The separator to use after field labels (defaults to ':')
51979 labelSeparator : ':',
51981 * @cfg {Boolean} hideLabels
51982 * True to suppress the display of field labels in this layout (defaults to false)
51984 hideLabels : false,
51987 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51992 onRender : function(ct, position){
51993 if(this.el){ // from markup
51994 this.el = Roo.get(this.el);
51995 }else { // generate
51996 var cfg = this.getAutoCreate();
51997 this.el = ct.createChild(cfg, position);
52000 this.el.applyStyles(this.style);
52002 if(this.labelAlign){
52003 this.el.addClass('x-form-label-'+this.labelAlign);
52005 if(this.hideLabels){
52006 this.labelStyle = "display:none";
52007 this.elementStyle = "padding-left:0;";
52009 if(typeof this.labelWidth == 'number'){
52010 this.labelStyle = "width:"+this.labelWidth+"px;";
52011 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52013 if(this.labelAlign == 'top'){
52014 this.labelStyle = "width:auto;";
52015 this.elementStyle = "padding-left:0;";
52018 var stack = this.stack;
52019 var slen = stack.length;
52021 if(!this.fieldTpl){
52022 var t = new Roo.Template(
52023 '<div class="x-form-item {5}">',
52024 '<label for="{0}" style="{2}">{1}{4}</label>',
52025 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52027 '</div><div class="x-form-clear-left"></div>'
52029 t.disableFormats = true;
52031 Roo.form.Layout.prototype.fieldTpl = t;
52033 for(var i = 0; i < slen; i++) {
52034 if(stack[i].isFormField){
52035 this.renderField(stack[i]);
52037 this.renderComponent(stack[i]);
52042 this.el.createChild({cls:'x-form-clear'});
52047 renderField : function(f){
52048 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52051 f.labelStyle||this.labelStyle||'', //2
52052 this.elementStyle||'', //3
52053 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52054 f.itemCls||this.itemCls||'' //5
52055 ], true).getPrevSibling());
52059 renderComponent : function(c){
52060 c.render(c.isLayout ? this.el : this.el.createChild());
52063 * Adds a object form elements (using the xtype property as the factory method.)
52064 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
52065 * @param {Object} config
52067 addxtype : function(o)
52069 // create the lement.
52070 o.form = this.form;
52071 var fe = Roo.factory(o, Roo.form);
52072 this.form.allItems.push(fe);
52073 this.stack.push(fe);
52075 if (fe.isFormField) {
52076 this.form.items.add(fe);
52084 * @class Roo.form.Column
52085 * @extends Roo.form.Layout
52086 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52087 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52089 * @param {Object} config Configuration options
52091 Roo.form.Column = function(config){
52092 Roo.form.Column.superclass.constructor.call(this, config);
52095 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52097 * @cfg {Number/String} width
52098 * The fixed width of the column in pixels or CSS value (defaults to "auto")
52101 * @cfg {String/Object} autoCreate
52102 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52106 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52109 onRender : function(ct, position){
52110 Roo.form.Column.superclass.onRender.call(this, ct, position);
52112 this.el.setWidth(this.width);
52119 * @class Roo.form.Row
52120 * @extends Roo.form.Layout
52121 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52122 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52124 * @param {Object} config Configuration options
52128 Roo.form.Row = function(config){
52129 Roo.form.Row.superclass.constructor.call(this, config);
52132 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52134 * @cfg {Number/String} width
52135 * The fixed width of the column in pixels or CSS value (defaults to "auto")
52138 * @cfg {Number/String} height
52139 * The fixed height of the column in pixels or CSS value (defaults to "auto")
52141 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52145 onRender : function(ct, position){
52146 //console.log('row render');
52148 var t = new Roo.Template(
52149 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52150 '<label for="{0}" style="{2}">{1}{4}</label>',
52151 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52155 t.disableFormats = true;
52157 Roo.form.Layout.prototype.rowTpl = t;
52159 this.fieldTpl = this.rowTpl;
52161 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52162 var labelWidth = 100;
52164 if ((this.labelAlign != 'top')) {
52165 if (typeof this.labelWidth == 'number') {
52166 labelWidth = this.labelWidth
52168 this.padWidth = 20 + labelWidth;
52172 Roo.form.Column.superclass.onRender.call(this, ct, position);
52174 this.el.setWidth(this.width);
52177 this.el.setHeight(this.height);
52182 renderField : function(f){
52183 f.fieldEl = this.fieldTpl.append(this.el, [
52184 f.id, f.fieldLabel,
52185 f.labelStyle||this.labelStyle||'',
52186 this.elementStyle||'',
52187 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52188 f.itemCls||this.itemCls||'',
52189 f.width ? f.width + this.padWidth : 160 + this.padWidth
52196 * @class Roo.form.FieldSet
52197 * @extends Roo.form.Layout
52198 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52199 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52201 * @param {Object} config Configuration options
52203 Roo.form.FieldSet = function(config){
52204 Roo.form.FieldSet.superclass.constructor.call(this, config);
52207 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52209 * @cfg {String} legend
52210 * The text to display as the legend for the FieldSet (defaults to '')
52213 * @cfg {String/Object} autoCreate
52214 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52218 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52221 onRender : function(ct, position){
52222 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52224 this.setLegend(this.legend);
52229 setLegend : function(text){
52231 this.el.child('legend').update(text);
52236 * Ext JS Library 1.1.1
52237 * Copyright(c) 2006-2007, Ext JS, LLC.
52239 * Originally Released Under LGPL - original licence link has changed is not relivant.
52242 * <script type="text/javascript">
52245 * @class Roo.form.VTypes
52246 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52249 Roo.form.VTypes = function(){
52250 // closure these in so they are only created once.
52251 var alpha = /^[a-zA-Z_]+$/;
52252 var alphanum = /^[a-zA-Z0-9_]+$/;
52253 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52254 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52256 // All these messages and functions are configurable
52259 * The function used to validate email addresses
52260 * @param {String} value The email address
52262 'email' : function(v){
52263 return email.test(v);
52266 * The error text to display when the email validation function returns false
52269 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52271 * The keystroke filter mask to be applied on email input
52274 'emailMask' : /[a-z0-9_\.\-@]/i,
52277 * The function used to validate URLs
52278 * @param {String} value The URL
52280 'url' : function(v){
52281 return url.test(v);
52284 * The error text to display when the url validation function returns false
52287 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52290 * The function used to validate alpha values
52291 * @param {String} value The value
52293 'alpha' : function(v){
52294 return alpha.test(v);
52297 * The error text to display when the alpha validation function returns false
52300 'alphaText' : 'This field should only contain letters and _',
52302 * The keystroke filter mask to be applied on alpha input
52305 'alphaMask' : /[a-z_]/i,
52308 * The function used to validate alphanumeric values
52309 * @param {String} value The value
52311 'alphanum' : function(v){
52312 return alphanum.test(v);
52315 * The error text to display when the alphanumeric validation function returns false
52318 'alphanumText' : 'This field should only contain letters, numbers and _',
52320 * The keystroke filter mask to be applied on alphanumeric input
52323 'alphanumMask' : /[a-z0-9_]/i
52325 }();//<script type="text/javascript">
52328 * @class Roo.form.FCKeditor
52329 * @extends Roo.form.TextArea
52330 * Wrapper around the FCKEditor http://www.fckeditor.net
52332 * Creates a new FCKeditor
52333 * @param {Object} config Configuration options
52335 Roo.form.FCKeditor = function(config){
52336 Roo.form.FCKeditor.superclass.constructor.call(this, config);
52339 * @event editorinit
52340 * Fired when the editor is initialized - you can add extra handlers here..
52341 * @param {FCKeditor} this
52342 * @param {Object} the FCK object.
52349 Roo.form.FCKeditor.editors = { };
52350 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52352 //defaultAutoCreate : {
52353 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
52357 * @cfg {Object} fck options - see fck manual for details.
52362 * @cfg {Object} fck toolbar set (Basic or Default)
52364 toolbarSet : 'Basic',
52366 * @cfg {Object} fck BasePath
52368 basePath : '/fckeditor/',
52376 onRender : function(ct, position)
52379 this.defaultAutoCreate = {
52381 style:"width:300px;height:60px;",
52382 autocomplete: "new-password"
52385 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52388 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52389 if(this.preventScrollbars){
52390 this.el.setStyle("overflow", "hidden");
52392 this.el.setHeight(this.growMin);
52395 //console.log('onrender' + this.getId() );
52396 Roo.form.FCKeditor.editors[this.getId()] = this;
52399 this.replaceTextarea() ;
52403 getEditor : function() {
52404 return this.fckEditor;
52407 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
52408 * @param {Mixed} value The value to set
52412 setValue : function(value)
52414 //console.log('setValue: ' + value);
52416 if(typeof(value) == 'undefined') { // not sure why this is happending...
52419 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52421 //if(!this.el || !this.getEditor()) {
52422 // this.value = value;
52423 //this.setValue.defer(100,this,[value]);
52427 if(!this.getEditor()) {
52431 this.getEditor().SetData(value);
52438 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
52439 * @return {Mixed} value The field value
52441 getValue : function()
52444 if (this.frame && this.frame.dom.style.display == 'none') {
52445 return Roo.form.FCKeditor.superclass.getValue.call(this);
52448 if(!this.el || !this.getEditor()) {
52450 // this.getValue.defer(100,this);
52455 var value=this.getEditor().GetData();
52456 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52457 return Roo.form.FCKeditor.superclass.getValue.call(this);
52463 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
52464 * @return {Mixed} value The field value
52466 getRawValue : function()
52468 if (this.frame && this.frame.dom.style.display == 'none') {
52469 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52472 if(!this.el || !this.getEditor()) {
52473 //this.getRawValue.defer(100,this);
52480 var value=this.getEditor().GetData();
52481 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52482 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52486 setSize : function(w,h) {
52490 //if (this.frame && this.frame.dom.style.display == 'none') {
52491 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52494 //if(!this.el || !this.getEditor()) {
52495 // this.setSize.defer(100,this, [w,h]);
52501 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52503 this.frame.dom.setAttribute('width', w);
52504 this.frame.dom.setAttribute('height', h);
52505 this.frame.setSize(w,h);
52509 toggleSourceEdit : function(value) {
52513 this.el.dom.style.display = value ? '' : 'none';
52514 this.frame.dom.style.display = value ? 'none' : '';
52519 focus: function(tag)
52521 if (this.frame.dom.style.display == 'none') {
52522 return Roo.form.FCKeditor.superclass.focus.call(this);
52524 if(!this.el || !this.getEditor()) {
52525 this.focus.defer(100,this, [tag]);
52532 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52533 this.getEditor().Focus();
52535 if (!this.getEditor().Selection.GetSelection()) {
52536 this.focus.defer(100,this, [tag]);
52541 var r = this.getEditor().EditorDocument.createRange();
52542 r.setStart(tgs[0],0);
52543 r.setEnd(tgs[0],0);
52544 this.getEditor().Selection.GetSelection().removeAllRanges();
52545 this.getEditor().Selection.GetSelection().addRange(r);
52546 this.getEditor().Focus();
52553 replaceTextarea : function()
52555 if ( document.getElementById( this.getId() + '___Frame' ) ) {
52558 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52560 // We must check the elements firstly using the Id and then the name.
52561 var oTextarea = document.getElementById( this.getId() );
52563 var colElementsByName = document.getElementsByName( this.getId() ) ;
52565 oTextarea.style.display = 'none' ;
52567 if ( oTextarea.tabIndex ) {
52568 this.TabIndex = oTextarea.tabIndex ;
52571 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52572 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52573 this.frame = Roo.get(this.getId() + '___Frame')
52576 _getConfigHtml : function()
52580 for ( var o in this.fckconfig ) {
52581 sConfig += sConfig.length > 0 ? '&' : '';
52582 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52585 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52589 _getIFrameHtml : function()
52591 var sFile = 'fckeditor.html' ;
52592 /* no idea what this is about..
52595 if ( (/fcksource=true/i).test( window.top.location.search ) )
52596 sFile = 'fckeditor.original.html' ;
52601 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52602 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
52605 var html = '<iframe id="' + this.getId() +
52606 '___Frame" src="' + sLink +
52607 '" width="' + this.width +
52608 '" height="' + this.height + '"' +
52609 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
52610 ' frameborder="0" scrolling="no"></iframe>' ;
52615 _insertHtmlBefore : function( html, element )
52617 if ( element.insertAdjacentHTML ) {
52619 element.insertAdjacentHTML( 'beforeBegin', html ) ;
52621 var oRange = document.createRange() ;
52622 oRange.setStartBefore( element ) ;
52623 var oFragment = oRange.createContextualFragment( html );
52624 element.parentNode.insertBefore( oFragment, element ) ;
52637 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52639 function FCKeditor_OnComplete(editorInstance){
52640 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52641 f.fckEditor = editorInstance;
52642 //console.log("loaded");
52643 f.fireEvent('editorinit', f, editorInstance);
52663 //<script type="text/javascript">
52665 * @class Roo.form.GridField
52666 * @extends Roo.form.Field
52667 * Embed a grid (or editable grid into a form)
52670 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52672 * xgrid.store = Roo.data.Store
52673 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52674 * xgrid.store.reader = Roo.data.JsonReader
52678 * Creates a new GridField
52679 * @param {Object} config Configuration options
52681 Roo.form.GridField = function(config){
52682 Roo.form.GridField.superclass.constructor.call(this, config);
52686 Roo.extend(Roo.form.GridField, Roo.form.Field, {
52688 * @cfg {Number} width - used to restrict width of grid..
52692 * @cfg {Number} height - used to restrict height of grid..
52696 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52702 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52703 * {tag: "input", type: "checkbox", autocomplete: "off"})
52705 // defaultAutoCreate : { tag: 'div' },
52706 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52708 * @cfg {String} addTitle Text to include for adding a title.
52712 onResize : function(){
52713 Roo.form.Field.superclass.onResize.apply(this, arguments);
52716 initEvents : function(){
52717 // Roo.form.Checkbox.superclass.initEvents.call(this);
52718 // has no events...
52723 getResizeEl : function(){
52727 getPositionEl : function(){
52732 onRender : function(ct, position){
52734 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52735 var style = this.style;
52738 Roo.form.GridField.superclass.onRender.call(this, ct, position);
52739 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52740 this.viewEl = this.wrap.createChild({ tag: 'div' });
52742 this.viewEl.applyStyles(style);
52745 this.viewEl.setWidth(this.width);
52748 this.viewEl.setHeight(this.height);
52750 //if(this.inputValue !== undefined){
52751 //this.setValue(this.value);
52754 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52757 this.grid.render();
52758 this.grid.getDataSource().on('remove', this.refreshValue, this);
52759 this.grid.getDataSource().on('update', this.refreshValue, this);
52760 this.grid.on('afteredit', this.refreshValue, this);
52766 * Sets the value of the item.
52767 * @param {String} either an object or a string..
52769 setValue : function(v){
52771 v = v || []; // empty set..
52772 // this does not seem smart - it really only affects memoryproxy grids..
52773 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52774 var ds = this.grid.getDataSource();
52775 // assumes a json reader..
52777 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
52778 ds.loadData( data);
52780 // clear selection so it does not get stale.
52781 if (this.grid.sm) {
52782 this.grid.sm.clearSelections();
52785 Roo.form.GridField.superclass.setValue.call(this, v);
52786 this.refreshValue();
52787 // should load data in the grid really....
52791 refreshValue: function() {
52793 this.grid.getDataSource().each(function(r) {
52796 this.el.dom.value = Roo.encode(val);
52804 * Ext JS Library 1.1.1
52805 * Copyright(c) 2006-2007, Ext JS, LLC.
52807 * Originally Released Under LGPL - original licence link has changed is not relivant.
52810 * <script type="text/javascript">
52813 * @class Roo.form.DisplayField
52814 * @extends Roo.form.Field
52815 * A generic Field to display non-editable data.
52816 * @cfg {Boolean} closable (true|false) default false
52818 * Creates a new Display Field item.
52819 * @param {Object} config Configuration options
52821 Roo.form.DisplayField = function(config){
52822 Roo.form.DisplayField.superclass.constructor.call(this, config);
52827 * Fires after the click the close btn
52828 * @param {Roo.form.DisplayField} this
52834 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
52835 inputType: 'hidden',
52841 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52843 focusClass : undefined,
52845 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52847 fieldClass: 'x-form-field',
52850 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52852 valueRenderer: undefined,
52856 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52857 * {tag: "input", type: "checkbox", autocomplete: "off"})
52860 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52864 onResize : function(){
52865 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52869 initEvents : function(){
52870 // Roo.form.Checkbox.superclass.initEvents.call(this);
52871 // has no events...
52874 this.closeEl.on('click', this.onClose, this);
52880 getResizeEl : function(){
52884 getPositionEl : function(){
52889 onRender : function(ct, position){
52891 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52892 //if(this.inputValue !== undefined){
52893 this.wrap = this.el.wrap();
52895 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52898 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52901 if (this.bodyStyle) {
52902 this.viewEl.applyStyles(this.bodyStyle);
52904 //this.viewEl.setStyle('padding', '2px');
52906 this.setValue(this.value);
52911 initValue : Roo.emptyFn,
52916 onClick : function(){
52921 * Sets the checked state of the checkbox.
52922 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52924 setValue : function(v){
52926 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
52927 // this might be called before we have a dom element..
52928 if (!this.viewEl) {
52931 this.viewEl.dom.innerHTML = html;
52932 Roo.form.DisplayField.superclass.setValue.call(this, v);
52936 onClose : function(e)
52938 e.preventDefault();
52940 this.fireEvent('close', this);
52949 * @class Roo.form.DayPicker
52950 * @extends Roo.form.Field
52951 * A Day picker show [M] [T] [W] ....
52953 * Creates a new Day Picker
52954 * @param {Object} config Configuration options
52956 Roo.form.DayPicker= function(config){
52957 Roo.form.DayPicker.superclass.constructor.call(this, config);
52961 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
52963 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52965 focusClass : undefined,
52967 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52969 fieldClass: "x-form-field",
52972 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52973 * {tag: "input", type: "checkbox", autocomplete: "off"})
52975 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52978 actionMode : 'viewEl',
52982 inputType : 'hidden',
52985 inputElement: false, // real input element?
52986 basedOn: false, // ????
52988 isFormField: true, // not sure where this is needed!!!!
52990 onResize : function(){
52991 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52992 if(!this.boxLabel){
52993 this.el.alignTo(this.wrap, 'c-c');
52997 initEvents : function(){
52998 Roo.form.Checkbox.superclass.initEvents.call(this);
52999 this.el.on("click", this.onClick, this);
53000 this.el.on("change", this.onClick, this);
53004 getResizeEl : function(){
53008 getPositionEl : function(){
53014 onRender : function(ct, position){
53015 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53017 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53019 var r1 = '<table><tr>';
53020 var r2 = '<tr class="x-form-daypick-icons">';
53021 for (var i=0; i < 7; i++) {
53022 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53023 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
53026 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53027 viewEl.select('img').on('click', this.onClick, this);
53028 this.viewEl = viewEl;
53031 // this will not work on Chrome!!!
53032 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
53033 this.el.on('propertychange', this.setFromHidden, this); //ie
53041 initValue : Roo.emptyFn,
53044 * Returns the checked state of the checkbox.
53045 * @return {Boolean} True if checked, else false
53047 getValue : function(){
53048 return this.el.dom.value;
53053 onClick : function(e){
53054 //this.setChecked(!this.checked);
53055 Roo.get(e.target).toggleClass('x-menu-item-checked');
53056 this.refreshValue();
53057 //if(this.el.dom.checked != this.checked){
53058 // this.setValue(this.el.dom.checked);
53063 refreshValue : function()
53066 this.viewEl.select('img',true).each(function(e,i,n) {
53067 val += e.is(".x-menu-item-checked") ? String(n) : '';
53069 this.setValue(val, true);
53073 * Sets the checked state of the checkbox.
53074 * On is always based on a string comparison between inputValue and the param.
53075 * @param {Boolean/String} value - the value to set
53076 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53078 setValue : function(v,suppressEvent){
53079 if (!this.el.dom) {
53082 var old = this.el.dom.value ;
53083 this.el.dom.value = v;
53084 if (suppressEvent) {
53088 // update display..
53089 this.viewEl.select('img',true).each(function(e,i,n) {
53091 var on = e.is(".x-menu-item-checked");
53092 var newv = v.indexOf(String(n)) > -1;
53094 e.toggleClass('x-menu-item-checked');
53100 this.fireEvent('change', this, v, old);
53105 // handle setting of hidden value by some other method!!?!?
53106 setFromHidden: function()
53111 //console.log("SET FROM HIDDEN");
53112 //alert('setFrom hidden');
53113 this.setValue(this.el.dom.value);
53116 onDestroy : function()
53119 Roo.get(this.viewEl).remove();
53122 Roo.form.DayPicker.superclass.onDestroy.call(this);
53126 * RooJS Library 1.1.1
53127 * Copyright(c) 2008-2011 Alan Knowles
53134 * @class Roo.form.ComboCheck
53135 * @extends Roo.form.ComboBox
53136 * A combobox for multiple select items.
53138 * FIXME - could do with a reset button..
53141 * Create a new ComboCheck
53142 * @param {Object} config Configuration options
53144 Roo.form.ComboCheck = function(config){
53145 Roo.form.ComboCheck.superclass.constructor.call(this, config);
53146 // should verify some data...
53148 // hiddenName = required..
53149 // displayField = required
53150 // valudField == required
53151 var req= [ 'hiddenName', 'displayField', 'valueField' ];
53153 Roo.each(req, function(e) {
53154 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53155 throw "Roo.form.ComboCheck : missing value for: " + e;
53162 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53167 selectedClass: 'x-menu-item-checked',
53170 onRender : function(ct, position){
53176 var cls = 'x-combo-list';
53179 this.tpl = new Roo.Template({
53180 html : '<div class="'+cls+'-item x-menu-check-item">' +
53181 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
53182 '<span>{' + this.displayField + '}</span>' +
53189 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53190 this.view.singleSelect = false;
53191 this.view.multiSelect = true;
53192 this.view.toggleSelect = true;
53193 this.pageTb.add(new Roo.Toolbar.Fill(), {
53196 handler: function()
53203 onViewOver : function(e, t){
53209 onViewClick : function(doFocus,index){
53213 select: function () {
53214 //Roo.log("SELECT CALLED");
53217 selectByValue : function(xv, scrollIntoView){
53218 var ar = this.getValueArray();
53221 Roo.each(ar, function(v) {
53222 if(v === undefined || v === null){
53225 var r = this.findRecord(this.valueField, v);
53227 sels.push(this.store.indexOf(r))
53231 this.view.select(sels);
53237 onSelect : function(record, index){
53238 // Roo.log("onselect Called");
53239 // this is only called by the clear button now..
53240 this.view.clearSelections();
53241 this.setValue('[]');
53242 if (this.value != this.valueBefore) {
53243 this.fireEvent('change', this, this.value, this.valueBefore);
53244 this.valueBefore = this.value;
53247 getValueArray : function()
53252 //Roo.log(this.value);
53253 if (typeof(this.value) == 'undefined') {
53256 var ar = Roo.decode(this.value);
53257 return ar instanceof Array ? ar : []; //?? valid?
53260 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
53265 expand : function ()
53268 Roo.form.ComboCheck.superclass.expand.call(this);
53269 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53270 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53275 collapse : function(){
53276 Roo.form.ComboCheck.superclass.collapse.call(this);
53277 var sl = this.view.getSelectedIndexes();
53278 var st = this.store;
53282 Roo.each(sl, function(i) {
53284 nv.push(r.get(this.valueField));
53286 this.setValue(Roo.encode(nv));
53287 if (this.value != this.valueBefore) {
53289 this.fireEvent('change', this, this.value, this.valueBefore);
53290 this.valueBefore = this.value;
53295 setValue : function(v){
53299 var vals = this.getValueArray();
53301 Roo.each(vals, function(k) {
53302 var r = this.findRecord(this.valueField, k);
53304 tv.push(r.data[this.displayField]);
53305 }else if(this.valueNotFoundText !== undefined){
53306 tv.push( this.valueNotFoundText );
53311 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53312 this.hiddenField.value = v;
53318 * Ext JS Library 1.1.1
53319 * Copyright(c) 2006-2007, Ext JS, LLC.
53321 * Originally Released Under LGPL - original licence link has changed is not relivant.
53324 * <script type="text/javascript">
53328 * @class Roo.form.Signature
53329 * @extends Roo.form.Field
53333 * @param {Object} config Configuration options
53336 Roo.form.Signature = function(config){
53337 Roo.form.Signature.superclass.constructor.call(this, config);
53339 this.addEvents({// not in used??
53342 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53343 * @param {Roo.form.Signature} combo This combo box
53348 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53349 * @param {Roo.form.ComboBox} combo This combo box
53350 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53356 Roo.extend(Roo.form.Signature, Roo.form.Field, {
53358 * @cfg {Object} labels Label to use when rendering a form.
53362 * confirm : "Confirm"
53367 confirm : "Confirm"
53370 * @cfg {Number} width The signature panel width (defaults to 300)
53374 * @cfg {Number} height The signature panel height (defaults to 100)
53378 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53380 allowBlank : false,
53383 // {Object} signPanel The signature SVG panel element (defaults to {})
53385 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53386 isMouseDown : false,
53387 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53388 isConfirmed : false,
53389 // {String} signatureTmp SVG mapping string (defaults to empty string)
53393 defaultAutoCreate : { // modified by initCompnoent..
53399 onRender : function(ct, position){
53401 Roo.form.Signature.superclass.onRender.call(this, ct, position);
53403 this.wrap = this.el.wrap({
53404 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53407 this.createToolbar(this);
53408 this.signPanel = this.wrap.createChild({
53410 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53414 this.svgID = Roo.id();
53415 this.svgEl = this.signPanel.createChild({
53416 xmlns : 'http://www.w3.org/2000/svg',
53418 id : this.svgID + "-svg",
53420 height: this.height,
53421 viewBox: '0 0 '+this.width+' '+this.height,
53425 id: this.svgID + "-svg-r",
53427 height: this.height,
53432 id: this.svgID + "-svg-l",
53434 y1: (this.height*0.8), // start set the line in 80% of height
53435 x2: this.width, // end
53436 y2: (this.height*0.8), // end set the line in 80% of height
53438 'stroke-width': "1",
53439 'stroke-dasharray': "3",
53440 'shape-rendering': "crispEdges",
53441 'pointer-events': "none"
53445 id: this.svgID + "-svg-p",
53447 'stroke-width': "3",
53449 'pointer-events': 'none'
53454 this.svgBox = this.svgEl.dom.getScreenCTM();
53456 createSVG : function(){
53457 var svg = this.signPanel;
53458 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53461 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53462 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53463 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53464 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53465 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53466 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53467 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53470 isTouchEvent : function(e){
53471 return e.type.match(/^touch/);
53473 getCoords : function (e) {
53474 var pt = this.svgEl.dom.createSVGPoint();
53477 if (this.isTouchEvent(e)) {
53478 pt.x = e.targetTouches[0].clientX;
53479 pt.y = e.targetTouches[0].clientY;
53481 var a = this.svgEl.dom.getScreenCTM();
53482 var b = a.inverse();
53483 var mx = pt.matrixTransform(b);
53484 return mx.x + ',' + mx.y;
53486 //mouse event headler
53487 down : function (e) {
53488 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53489 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53491 this.isMouseDown = true;
53493 e.preventDefault();
53495 move : function (e) {
53496 if (this.isMouseDown) {
53497 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53498 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53501 e.preventDefault();
53503 up : function (e) {
53504 this.isMouseDown = false;
53505 var sp = this.signatureTmp.split(' ');
53508 if(!sp[sp.length-2].match(/^L/)){
53512 this.signatureTmp = sp.join(" ");
53515 if(this.getValue() != this.signatureTmp){
53516 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53517 this.isConfirmed = false;
53519 e.preventDefault();
53523 * Protected method that will not generally be called directly. It
53524 * is called when the editor creates its toolbar. Override this method if you need to
53525 * add custom toolbar buttons.
53526 * @param {HtmlEditor} editor
53528 createToolbar : function(editor){
53529 function btn(id, toggle, handler){
53530 var xid = fid + '-'+ id ;
53534 cls : 'x-btn-icon x-edit-'+id,
53535 enableToggle:toggle !== false,
53536 scope: editor, // was editor...
53537 handler:handler||editor.relayBtnCmd,
53538 clickEvent:'mousedown',
53539 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53545 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53549 cls : ' x-signature-btn x-signature-'+id,
53550 scope: editor, // was editor...
53551 handler: this.reset,
53552 clickEvent:'mousedown',
53553 text: this.labels.clear
53560 cls : ' x-signature-btn x-signature-'+id,
53561 scope: editor, // was editor...
53562 handler: this.confirmHandler,
53563 clickEvent:'mousedown',
53564 text: this.labels.confirm
53571 * when user is clicked confirm then show this image.....
53573 * @return {String} Image Data URI
53575 getImageDataURI : function(){
53576 var svg = this.svgEl.dom.parentNode.innerHTML;
53577 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53582 * @return {Boolean} this.isConfirmed
53584 getConfirmed : function(){
53585 return this.isConfirmed;
53589 * @return {Number} this.width
53591 getWidth : function(){
53596 * @return {Number} this.height
53598 getHeight : function(){
53599 return this.height;
53602 getSignature : function(){
53603 return this.signatureTmp;
53606 reset : function(){
53607 this.signatureTmp = '';
53608 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53609 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53610 this.isConfirmed = false;
53611 Roo.form.Signature.superclass.reset.call(this);
53613 setSignature : function(s){
53614 this.signatureTmp = s;
53615 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53616 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53618 this.isConfirmed = false;
53619 Roo.form.Signature.superclass.reset.call(this);
53622 // Roo.log(this.signPanel.dom.contentWindow.up())
53625 setConfirmed : function(){
53629 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53632 confirmHandler : function(){
53633 if(!this.getSignature()){
53637 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53638 this.setValue(this.getSignature());
53639 this.isConfirmed = true;
53641 this.fireEvent('confirm', this);
53644 // Subclasses should provide the validation implementation by overriding this
53645 validateValue : function(value){
53646 if(this.allowBlank){
53650 if(this.isConfirmed){
53657 * Ext JS Library 1.1.1
53658 * Copyright(c) 2006-2007, Ext JS, LLC.
53660 * Originally Released Under LGPL - original licence link has changed is not relivant.
53663 * <script type="text/javascript">
53668 * @class Roo.form.ComboBox
53669 * @extends Roo.form.TriggerField
53670 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53672 * Create a new ComboBox.
53673 * @param {Object} config Configuration options
53675 Roo.form.Select = function(config){
53676 Roo.form.Select.superclass.constructor.call(this, config);
53680 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53682 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53685 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53686 * rendering into an Roo.Editor, defaults to false)
53689 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53690 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53693 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53696 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53697 * the dropdown list (defaults to undefined, with no header element)
53701 * @cfg {String/Roo.Template} tpl The template to use to render the output
53705 defaultAutoCreate : {tag: "select" },
53707 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53709 listWidth: undefined,
53711 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53712 * mode = 'remote' or 'text' if mode = 'local')
53714 displayField: undefined,
53716 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53717 * mode = 'remote' or 'value' if mode = 'local').
53718 * Note: use of a valueField requires the user make a selection
53719 * in order for a value to be mapped.
53721 valueField: undefined,
53725 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53726 * field's data value (defaults to the underlying DOM element's name)
53728 hiddenName: undefined,
53730 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53734 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53736 selectedClass: 'x-combo-selected',
53738 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
53739 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53740 * which displays a downward arrow icon).
53742 triggerClass : 'x-form-arrow-trigger',
53744 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53748 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53749 * anchor positions (defaults to 'tl-bl')
53751 listAlign: 'tl-bl?',
53753 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53757 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
53758 * query specified by the allQuery config option (defaults to 'query')
53760 triggerAction: 'query',
53762 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53763 * (defaults to 4, does not apply if editable = false)
53767 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53768 * delay (typeAheadDelay) if it matches a known value (defaults to false)
53772 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53773 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53777 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53778 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
53782 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
53783 * when editable = true (defaults to false)
53785 selectOnFocus:false,
53787 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53789 queryParam: 'query',
53791 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
53792 * when mode = 'remote' (defaults to 'Loading...')
53794 loadingText: 'Loading...',
53796 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53800 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53804 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53805 * traditional select (defaults to true)
53809 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53813 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53817 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53818 * listWidth has a higher value)
53822 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53823 * allow the user to set arbitrary text into the field (defaults to false)
53825 forceSelection:false,
53827 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53828 * if typeAhead = true (defaults to 250)
53830 typeAheadDelay : 250,
53832 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53833 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53835 valueNotFoundText : undefined,
53838 * @cfg {String} defaultValue The value displayed after loading the store.
53843 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53845 blockFocus : false,
53848 * @cfg {Boolean} disableClear Disable showing of clear button.
53850 disableClear : false,
53852 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
53854 alwaysQuery : false,
53860 // element that contains real text value.. (when hidden is used..)
53863 onRender : function(ct, position){
53864 Roo.form.Field.prototype.onRender.call(this, ct, position);
53867 this.store.on('beforeload', this.onBeforeLoad, this);
53868 this.store.on('load', this.onLoad, this);
53869 this.store.on('loadexception', this.onLoadException, this);
53870 this.store.load({});
53878 initEvents : function(){
53879 //Roo.form.ComboBox.superclass.initEvents.call(this);
53883 onDestroy : function(){
53886 this.store.un('beforeload', this.onBeforeLoad, this);
53887 this.store.un('load', this.onLoad, this);
53888 this.store.un('loadexception', this.onLoadException, this);
53890 //Roo.form.ComboBox.superclass.onDestroy.call(this);
53894 fireKey : function(e){
53895 if(e.isNavKeyPress() && !this.list.isVisible()){
53896 this.fireEvent("specialkey", this, e);
53901 onResize: function(w, h){
53909 * Allow or prevent the user from directly editing the field text. If false is passed,
53910 * the user will only be able to select from the items defined in the dropdown list. This method
53911 * is the runtime equivalent of setting the 'editable' config option at config time.
53912 * @param {Boolean} value True to allow the user to directly edit the field text
53914 setEditable : function(value){
53919 onBeforeLoad : function(){
53921 Roo.log("Select before load");
53924 this.innerList.update(this.loadingText ?
53925 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53926 //this.restrictHeight();
53927 this.selectedIndex = -1;
53931 onLoad : function(){
53934 var dom = this.el.dom;
53935 dom.innerHTML = '';
53936 var od = dom.ownerDocument;
53938 if (this.emptyText) {
53939 var op = od.createElement('option');
53940 op.setAttribute('value', '');
53941 op.innerHTML = String.format('{0}', this.emptyText);
53942 dom.appendChild(op);
53944 if(this.store.getCount() > 0){
53946 var vf = this.valueField;
53947 var df = this.displayField;
53948 this.store.data.each(function(r) {
53949 // which colmsn to use... testing - cdoe / title..
53950 var op = od.createElement('option');
53951 op.setAttribute('value', r.data[vf]);
53952 op.innerHTML = String.format('{0}', r.data[df]);
53953 dom.appendChild(op);
53955 if (typeof(this.defaultValue != 'undefined')) {
53956 this.setValue(this.defaultValue);
53961 //this.onEmptyResults();
53966 onLoadException : function()
53968 dom.innerHTML = '';
53970 Roo.log("Select on load exception");
53974 Roo.log(this.store.reader.jsonData);
53975 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53976 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53982 onTypeAhead : function(){
53987 onSelect : function(record, index){
53988 Roo.log('on select?');
53990 if(this.fireEvent('beforeselect', this, record, index) !== false){
53991 this.setFromData(index > -1 ? record.data : false);
53993 this.fireEvent('select', this, record, index);
53998 * Returns the currently selected field value or empty string if no value is set.
53999 * @return {String} value The selected value
54001 getValue : function(){
54002 var dom = this.el.dom;
54003 this.value = dom.options[dom.selectedIndex].value;
54009 * Clears any text/value currently set in the field
54011 clearValue : function(){
54013 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54018 * Sets the specified value into the field. If the value finds a match, the corresponding record text
54019 * will be displayed in the field. If the value does not match the data value of an existing item,
54020 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54021 * Otherwise the field will be blank (although the value will still be set).
54022 * @param {String} value The value to match
54024 setValue : function(v){
54025 var d = this.el.dom;
54026 for (var i =0; i < d.options.length;i++) {
54027 if (v == d.options[i].value) {
54028 d.selectedIndex = i;
54036 * @property {Object} the last set data for the element
54041 * Sets the value of the field based on a object which is related to the record format for the store.
54042 * @param {Object} value the value to set as. or false on reset?
54044 setFromData : function(o){
54045 Roo.log('setfrom data?');
54051 reset : function(){
54055 findRecord : function(prop, value){
54060 if(this.store.getCount() > 0){
54061 this.store.each(function(r){
54062 if(r.data[prop] == value){
54072 getName: function()
54074 // returns hidden if it's set..
54075 if (!this.rendered) {return ''};
54076 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
54084 onEmptyResults : function(){
54085 Roo.log('empty results');
54090 * Returns true if the dropdown list is expanded, else false.
54092 isExpanded : function(){
54097 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54098 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54099 * @param {String} value The data value of the item to select
54100 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54101 * selected item if it is not currently in view (defaults to true)
54102 * @return {Boolean} True if the value matched an item in the list, else false
54104 selectByValue : function(v, scrollIntoView){
54105 Roo.log('select By Value');
54108 if(v !== undefined && v !== null){
54109 var r = this.findRecord(this.valueField || this.displayField, v);
54111 this.select(this.store.indexOf(r), scrollIntoView);
54119 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54120 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54121 * @param {Number} index The zero-based index of the list item to select
54122 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54123 * selected item if it is not currently in view (defaults to true)
54125 select : function(index, scrollIntoView){
54126 Roo.log('select ');
54129 this.selectedIndex = index;
54130 this.view.select(index);
54131 if(scrollIntoView !== false){
54132 var el = this.view.getNode(index);
54134 this.innerList.scrollChildIntoView(el, false);
54142 validateBlur : function(){
54149 initQuery : function(){
54150 this.doQuery(this.getRawValue());
54154 doForce : function(){
54155 if(this.el.dom.value.length > 0){
54156 this.el.dom.value =
54157 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54163 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
54164 * query allowing the query action to be canceled if needed.
54165 * @param {String} query The SQL query to execute
54166 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54167 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
54168 * saved in the current store (defaults to false)
54170 doQuery : function(q, forceAll){
54172 Roo.log('doQuery?');
54173 if(q === undefined || q === null){
54178 forceAll: forceAll,
54182 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54186 forceAll = qe.forceAll;
54187 if(forceAll === true || (q.length >= this.minChars)){
54188 if(this.lastQuery != q || this.alwaysQuery){
54189 this.lastQuery = q;
54190 if(this.mode == 'local'){
54191 this.selectedIndex = -1;
54193 this.store.clearFilter();
54195 this.store.filter(this.displayField, q);
54199 this.store.baseParams[this.queryParam] = q;
54201 params: this.getParams(q)
54206 this.selectedIndex = -1;
54213 getParams : function(q){
54215 //p[this.queryParam] = q;
54218 p.limit = this.pageSize;
54224 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54226 collapse : function(){
54231 collapseIf : function(e){
54236 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54238 expand : function(){
54246 * @cfg {Boolean} grow
54250 * @cfg {Number} growMin
54254 * @cfg {Number} growMax
54262 setWidth : function()
54266 getResizeEl : function(){
54269 });//<script type="text/javasscript">
54273 * @class Roo.DDView
54274 * A DnD enabled version of Roo.View.
54275 * @param {Element/String} container The Element in which to create the View.
54276 * @param {String} tpl The template string used to create the markup for each element of the View
54277 * @param {Object} config The configuration properties. These include all the config options of
54278 * {@link Roo.View} plus some specific to this class.<br>
54280 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54281 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54283 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54284 .x-view-drag-insert-above {
54285 border-top:1px dotted #3366cc;
54287 .x-view-drag-insert-below {
54288 border-bottom:1px dotted #3366cc;
54294 Roo.DDView = function(container, tpl, config) {
54295 Roo.DDView.superclass.constructor.apply(this, arguments);
54296 this.getEl().setStyle("outline", "0px none");
54297 this.getEl().unselectable();
54298 if (this.dragGroup) {
54299 this.setDraggable(this.dragGroup.split(","));
54301 if (this.dropGroup) {
54302 this.setDroppable(this.dropGroup.split(","));
54304 if (this.deletable) {
54305 this.setDeletable();
54307 this.isDirtyFlag = false;
54313 Roo.extend(Roo.DDView, Roo.View, {
54314 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54315 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54316 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54317 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54321 reset: Roo.emptyFn,
54323 clearInvalid: Roo.form.Field.prototype.clearInvalid,
54325 validate: function() {
54329 destroy: function() {
54330 this.purgeListeners();
54331 this.getEl.removeAllListeners();
54332 this.getEl().remove();
54333 if (this.dragZone) {
54334 if (this.dragZone.destroy) {
54335 this.dragZone.destroy();
54338 if (this.dropZone) {
54339 if (this.dropZone.destroy) {
54340 this.dropZone.destroy();
54345 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54346 getName: function() {
54350 /** Loads the View from a JSON string representing the Records to put into the Store. */
54351 setValue: function(v) {
54353 throw "DDView.setValue(). DDView must be constructed with a valid Store";
54356 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54357 this.store.proxy = new Roo.data.MemoryProxy(data);
54361 /** @return {String} a parenthesised list of the ids of the Records in the View. */
54362 getValue: function() {
54364 this.store.each(function(rec) {
54365 result += rec.id + ',';
54367 return result.substr(0, result.length - 1) + ')';
54370 getIds: function() {
54371 var i = 0, result = new Array(this.store.getCount());
54372 this.store.each(function(rec) {
54373 result[i++] = rec.id;
54378 isDirty: function() {
54379 return this.isDirtyFlag;
54383 * Part of the Roo.dd.DropZone interface. If no target node is found, the
54384 * whole Element becomes the target, and this causes the drop gesture to append.
54386 getTargetFromEvent : function(e) {
54387 var target = e.getTarget();
54388 while ((target !== null) && (target.parentNode != this.el.dom)) {
54389 target = target.parentNode;
54392 target = this.el.dom.lastChild || this.el.dom;
54398 * Create the drag data which consists of an object which has the property "ddel" as
54399 * the drag proxy element.
54401 getDragData : function(e) {
54402 var target = this.findItemFromChild(e.getTarget());
54404 this.handleSelection(e);
54405 var selNodes = this.getSelectedNodes();
54408 copy: this.copy || (this.allowCopy && e.ctrlKey),
54412 var selectedIndices = this.getSelectedIndexes();
54413 for (var i = 0; i < selectedIndices.length; i++) {
54414 dragData.records.push(this.store.getAt(selectedIndices[i]));
54416 if (selNodes.length == 1) {
54417 dragData.ddel = target.cloneNode(true); // the div element
54419 var div = document.createElement('div'); // create the multi element drag "ghost"
54420 div.className = 'multi-proxy';
54421 for (var i = 0, len = selNodes.length; i < len; i++) {
54422 div.appendChild(selNodes[i].cloneNode(true));
54424 dragData.ddel = div;
54426 //console.log(dragData)
54427 //console.log(dragData.ddel.innerHTML)
54430 //console.log('nodragData')
54434 /** Specify to which ddGroup items in this DDView may be dragged. */
54435 setDraggable: function(ddGroup) {
54436 if (ddGroup instanceof Array) {
54437 Roo.each(ddGroup, this.setDraggable, this);
54440 if (this.dragZone) {
54441 this.dragZone.addToGroup(ddGroup);
54443 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54444 containerScroll: true,
54448 // Draggability implies selection. DragZone's mousedown selects the element.
54449 if (!this.multiSelect) { this.singleSelect = true; }
54451 // Wire the DragZone's handlers up to methods in *this*
54452 this.dragZone.getDragData = this.getDragData.createDelegate(this);
54456 /** Specify from which ddGroup this DDView accepts drops. */
54457 setDroppable: function(ddGroup) {
54458 if (ddGroup instanceof Array) {
54459 Roo.each(ddGroup, this.setDroppable, this);
54462 if (this.dropZone) {
54463 this.dropZone.addToGroup(ddGroup);
54465 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54466 containerScroll: true,
54470 // Wire the DropZone's handlers up to methods in *this*
54471 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54472 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54473 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54474 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54475 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54479 /** Decide whether to drop above or below a View node. */
54480 getDropPoint : function(e, n, dd){
54481 if (n == this.el.dom) { return "above"; }
54482 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54483 var c = t + (b - t) / 2;
54484 var y = Roo.lib.Event.getPageY(e);
54492 onNodeEnter : function(n, dd, e, data){
54496 onNodeOver : function(n, dd, e, data){
54497 var pt = this.getDropPoint(e, n, dd);
54498 // set the insert point style on the target node
54499 var dragElClass = this.dropNotAllowed;
54502 if (pt == "above"){
54503 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54504 targetElClass = "x-view-drag-insert-above";
54506 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54507 targetElClass = "x-view-drag-insert-below";
54509 if (this.lastInsertClass != targetElClass){
54510 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54511 this.lastInsertClass = targetElClass;
54514 return dragElClass;
54517 onNodeOut : function(n, dd, e, data){
54518 this.removeDropIndicators(n);
54521 onNodeDrop : function(n, dd, e, data){
54522 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54525 var pt = this.getDropPoint(e, n, dd);
54526 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54527 if (pt == "below") { insertAt++; }
54528 for (var i = 0; i < data.records.length; i++) {
54529 var r = data.records[i];
54530 var dup = this.store.getById(r.id);
54531 if (dup && (dd != this.dragZone)) {
54532 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54535 this.store.insert(insertAt++, r.copy());
54537 data.source.isDirtyFlag = true;
54539 this.store.insert(insertAt++, r);
54541 this.isDirtyFlag = true;
54544 this.dragZone.cachedTarget = null;
54548 removeDropIndicators : function(n){
54550 Roo.fly(n).removeClass([
54551 "x-view-drag-insert-above",
54552 "x-view-drag-insert-below"]);
54553 this.lastInsertClass = "_noclass";
54558 * Utility method. Add a delete option to the DDView's context menu.
54559 * @param {String} imageUrl The URL of the "delete" icon image.
54561 setDeletable: function(imageUrl) {
54562 if (!this.singleSelect && !this.multiSelect) {
54563 this.singleSelect = true;
54565 var c = this.getContextMenu();
54566 this.contextMenu.on("itemclick", function(item) {
54569 this.remove(this.getSelectedIndexes());
54573 this.contextMenu.add({
54580 /** Return the context menu for this DDView. */
54581 getContextMenu: function() {
54582 if (!this.contextMenu) {
54583 // Create the View's context menu
54584 this.contextMenu = new Roo.menu.Menu({
54585 id: this.id + "-contextmenu"
54587 this.el.on("contextmenu", this.showContextMenu, this);
54589 return this.contextMenu;
54592 disableContextMenu: function() {
54593 if (this.contextMenu) {
54594 this.el.un("contextmenu", this.showContextMenu, this);
54598 showContextMenu: function(e, item) {
54599 item = this.findItemFromChild(e.getTarget());
54602 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54603 this.contextMenu.showAt(e.getXY());
54608 * Remove {@link Roo.data.Record}s at the specified indices.
54609 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54611 remove: function(selectedIndices) {
54612 selectedIndices = [].concat(selectedIndices);
54613 for (var i = 0; i < selectedIndices.length; i++) {
54614 var rec = this.store.getAt(selectedIndices[i]);
54615 this.store.remove(rec);
54620 * Double click fires the event, but also, if this is draggable, and there is only one other
54621 * related DropZone, it transfers the selected node.
54623 onDblClick : function(e){
54624 var item = this.findItemFromChild(e.getTarget());
54626 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54629 if (this.dragGroup) {
54630 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54631 while (targets.indexOf(this.dropZone) > -1) {
54632 targets.remove(this.dropZone);
54634 if (targets.length == 1) {
54635 this.dragZone.cachedTarget = null;
54636 var el = Roo.get(targets[0].getEl());
54637 var box = el.getBox(true);
54638 targets[0].onNodeDrop(el.dom, {
54640 xy: [box.x, box.y + box.height - 1]
54641 }, null, this.getDragData(e));
54647 handleSelection: function(e) {
54648 this.dragZone.cachedTarget = null;
54649 var item = this.findItemFromChild(e.getTarget());
54651 this.clearSelections(true);
54654 if (item && (this.multiSelect || this.singleSelect)){
54655 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54656 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54657 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54658 this.unselect(item);
54660 this.select(item, this.multiSelect && e.ctrlKey);
54661 this.lastSelection = item;
54666 onItemClick : function(item, index, e){
54667 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54673 unselect : function(nodeInfo, suppressEvent){
54674 var node = this.getNode(nodeInfo);
54675 if(node && this.isSelected(node)){
54676 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54677 Roo.fly(node).removeClass(this.selectedClass);
54678 this.selections.remove(node);
54679 if(!suppressEvent){
54680 this.fireEvent("selectionchange", this, this.selections);
54688 * Ext JS Library 1.1.1
54689 * Copyright(c) 2006-2007, Ext JS, LLC.
54691 * Originally Released Under LGPL - original licence link has changed is not relivant.
54694 * <script type="text/javascript">
54698 * @class Roo.LayoutManager
54699 * @extends Roo.util.Observable
54700 * Base class for layout managers.
54702 Roo.LayoutManager = function(container, config){
54703 Roo.LayoutManager.superclass.constructor.call(this);
54704 this.el = Roo.get(container);
54705 // ie scrollbar fix
54706 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54707 document.body.scroll = "no";
54708 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54709 this.el.position('relative');
54711 this.id = this.el.id;
54712 this.el.addClass("x-layout-container");
54713 /** false to disable window resize monitoring @type Boolean */
54714 this.monitorWindowResize = true;
54719 * Fires when a layout is performed.
54720 * @param {Roo.LayoutManager} this
54724 * @event regionresized
54725 * Fires when the user resizes a region.
54726 * @param {Roo.LayoutRegion} region The resized region
54727 * @param {Number} newSize The new size (width for east/west, height for north/south)
54729 "regionresized" : true,
54731 * @event regioncollapsed
54732 * Fires when a region is collapsed.
54733 * @param {Roo.LayoutRegion} region The collapsed region
54735 "regioncollapsed" : true,
54737 * @event regionexpanded
54738 * Fires when a region is expanded.
54739 * @param {Roo.LayoutRegion} region The expanded region
54741 "regionexpanded" : true
54743 this.updating = false;
54744 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54747 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54749 * Returns true if this layout is currently being updated
54750 * @return {Boolean}
54752 isUpdating : function(){
54753 return this.updating;
54757 * Suspend the LayoutManager from doing auto-layouts while
54758 * making multiple add or remove calls
54760 beginUpdate : function(){
54761 this.updating = true;
54765 * Restore auto-layouts and optionally disable the manager from performing a layout
54766 * @param {Boolean} noLayout true to disable a layout update
54768 endUpdate : function(noLayout){
54769 this.updating = false;
54775 layout: function(){
54779 onRegionResized : function(region, newSize){
54780 this.fireEvent("regionresized", region, newSize);
54784 onRegionCollapsed : function(region){
54785 this.fireEvent("regioncollapsed", region);
54788 onRegionExpanded : function(region){
54789 this.fireEvent("regionexpanded", region);
54793 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54794 * performs box-model adjustments.
54795 * @return {Object} The size as an object {width: (the width), height: (the height)}
54797 getViewSize : function(){
54799 if(this.el.dom != document.body){
54800 size = this.el.getSize();
54802 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54804 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54805 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54810 * Returns the Element this layout is bound to.
54811 * @return {Roo.Element}
54813 getEl : function(){
54818 * Returns the specified region.
54819 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54820 * @return {Roo.LayoutRegion}
54822 getRegion : function(target){
54823 return this.regions[target.toLowerCase()];
54826 onWindowResize : function(){
54827 if(this.monitorWindowResize){
54833 * Ext JS Library 1.1.1
54834 * Copyright(c) 2006-2007, Ext JS, LLC.
54836 * Originally Released Under LGPL - original licence link has changed is not relivant.
54839 * <script type="text/javascript">
54842 * @class Roo.BorderLayout
54843 * @extends Roo.LayoutManager
54844 * @children Roo.ContentPanel
54845 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54846 * please see: <br><br>
54847 * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
54848 * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
54851 var layout = new Roo.BorderLayout(document.body, {
54885 preferredTabWidth: 150
54890 var CP = Roo.ContentPanel;
54892 layout.beginUpdate();
54893 layout.add("north", new CP("north", "North"));
54894 layout.add("south", new CP("south", {title: "South", closable: true}));
54895 layout.add("west", new CP("west", {title: "West"}));
54896 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54897 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54898 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54899 layout.getRegion("center").showPanel("center1");
54900 layout.endUpdate();
54903 <b>The container the layout is rendered into can be either the body element or any other element.
54904 If it is not the body element, the container needs to either be an absolute positioned element,
54905 or you will need to add "position:relative" to the css of the container. You will also need to specify
54906 the container size if it is not the body element.</b>
54909 * Create a new BorderLayout
54910 * @param {String/HTMLElement/Element} container The container this layout is bound to
54911 * @param {Object} config Configuration options
54913 Roo.BorderLayout = function(container, config){
54914 config = config || {};
54915 Roo.BorderLayout.superclass.constructor.call(this, container, config);
54916 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54917 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54918 var target = this.factory.validRegions[i];
54919 if(config[target]){
54920 this.addRegion(target, config[target]);
54925 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54928 * @cfg {Roo.LayoutRegion} east
54931 * @cfg {Roo.LayoutRegion} west
54934 * @cfg {Roo.LayoutRegion} north
54937 * @cfg {Roo.LayoutRegion} south
54940 * @cfg {Roo.LayoutRegion} center
54943 * Creates and adds a new region if it doesn't already exist.
54944 * @param {String} target The target region key (north, south, east, west or center).
54945 * @param {Object} config The regions config object
54946 * @return {BorderLayoutRegion} The new region
54948 addRegion : function(target, config){
54949 if(!this.regions[target]){
54950 var r = this.factory.create(target, this, config);
54951 this.bindRegion(target, r);
54953 return this.regions[target];
54957 bindRegion : function(name, r){
54958 this.regions[name] = r;
54959 r.on("visibilitychange", this.layout, this);
54960 r.on("paneladded", this.layout, this);
54961 r.on("panelremoved", this.layout, this);
54962 r.on("invalidated", this.layout, this);
54963 r.on("resized", this.onRegionResized, this);
54964 r.on("collapsed", this.onRegionCollapsed, this);
54965 r.on("expanded", this.onRegionExpanded, this);
54969 * Performs a layout update.
54971 layout : function(){
54972 if(this.updating) {
54975 var size = this.getViewSize();
54976 var w = size.width;
54977 var h = size.height;
54982 //var x = 0, y = 0;
54984 var rs = this.regions;
54985 var north = rs["north"];
54986 var south = rs["south"];
54987 var west = rs["west"];
54988 var east = rs["east"];
54989 var center = rs["center"];
54990 //if(this.hideOnLayout){ // not supported anymore
54991 //c.el.setStyle("display", "none");
54993 if(north && north.isVisible()){
54994 var b = north.getBox();
54995 var m = north.getMargins();
54996 b.width = w - (m.left+m.right);
54999 centerY = b.height + b.y + m.bottom;
55000 centerH -= centerY;
55001 north.updateBox(this.safeBox(b));
55003 if(south && south.isVisible()){
55004 var b = south.getBox();
55005 var m = south.getMargins();
55006 b.width = w - (m.left+m.right);
55008 var totalHeight = (b.height + m.top + m.bottom);
55009 b.y = h - totalHeight + m.top;
55010 centerH -= totalHeight;
55011 south.updateBox(this.safeBox(b));
55013 if(west && west.isVisible()){
55014 var b = west.getBox();
55015 var m = west.getMargins();
55016 b.height = centerH - (m.top+m.bottom);
55018 b.y = centerY + m.top;
55019 var totalWidth = (b.width + m.left + m.right);
55020 centerX += totalWidth;
55021 centerW -= totalWidth;
55022 west.updateBox(this.safeBox(b));
55024 if(east && east.isVisible()){
55025 var b = east.getBox();
55026 var m = east.getMargins();
55027 b.height = centerH - (m.top+m.bottom);
55028 var totalWidth = (b.width + m.left + m.right);
55029 b.x = w - totalWidth + m.left;
55030 b.y = centerY + m.top;
55031 centerW -= totalWidth;
55032 east.updateBox(this.safeBox(b));
55035 var m = center.getMargins();
55037 x: centerX + m.left,
55038 y: centerY + m.top,
55039 width: centerW - (m.left+m.right),
55040 height: centerH - (m.top+m.bottom)
55042 //if(this.hideOnLayout){
55043 //center.el.setStyle("display", "block");
55045 center.updateBox(this.safeBox(centerBox));
55048 this.fireEvent("layout", this);
55052 safeBox : function(box){
55053 box.width = Math.max(0, box.width);
55054 box.height = Math.max(0, box.height);
55059 * Adds a ContentPanel (or subclass) to this layout.
55060 * @param {String} target The target region key (north, south, east, west or center).
55061 * @param {Roo.ContentPanel} panel The panel to add
55062 * @return {Roo.ContentPanel} The added panel
55064 add : function(target, panel){
55066 target = target.toLowerCase();
55067 return this.regions[target].add(panel);
55071 * Remove a ContentPanel (or subclass) to this layout.
55072 * @param {String} target The target region key (north, south, east, west or center).
55073 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55074 * @return {Roo.ContentPanel} The removed panel
55076 remove : function(target, panel){
55077 target = target.toLowerCase();
55078 return this.regions[target].remove(panel);
55082 * Searches all regions for a panel with the specified id
55083 * @param {String} panelId
55084 * @return {Roo.ContentPanel} The panel or null if it wasn't found
55086 findPanel : function(panelId){
55087 var rs = this.regions;
55088 for(var target in rs){
55089 if(typeof rs[target] != "function"){
55090 var p = rs[target].getPanel(panelId);
55100 * Searches all regions for a panel with the specified id and activates (shows) it.
55101 * @param {String/ContentPanel} panelId The panels id or the panel itself
55102 * @return {Roo.ContentPanel} The shown panel or null
55104 showPanel : function(panelId) {
55105 var rs = this.regions;
55106 for(var target in rs){
55107 var r = rs[target];
55108 if(typeof r != "function"){
55109 if(r.hasPanel(panelId)){
55110 return r.showPanel(panelId);
55118 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55119 * @param {Roo.state.Provider} provider (optional) An alternate state provider
55121 restoreState : function(provider){
55123 provider = Roo.state.Manager;
55125 var sm = new Roo.LayoutStateManager();
55126 sm.init(this, provider);
55130 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
55131 * object should contain properties for each region to add ContentPanels to, and each property's value should be
55132 * a valid ContentPanel config object. Example:
55134 // Create the main layout
55135 var layout = new Roo.BorderLayout('main-ct', {
55146 // Create and add multiple ContentPanels at once via configs
55149 id: 'source-files',
55151 title:'Ext Source Files',
55164 * @param {Object} regions An object containing ContentPanel configs by region name
55166 batchAdd : function(regions){
55167 this.beginUpdate();
55168 for(var rname in regions){
55169 var lr = this.regions[rname];
55171 this.addTypedPanels(lr, regions[rname]);
55178 addTypedPanels : function(lr, ps){
55179 if(typeof ps == 'string'){
55180 lr.add(new Roo.ContentPanel(ps));
55182 else if(ps instanceof Array){
55183 for(var i =0, len = ps.length; i < len; i++){
55184 this.addTypedPanels(lr, ps[i]);
55187 else if(!ps.events){ // raw config?
55189 delete ps.el; // prevent conflict
55190 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55192 else { // panel object assumed!
55197 * Adds a xtype elements to the layout.
55201 xtype : 'ContentPanel',
55208 xtype : 'NestedLayoutPanel',
55214 items : [ ... list of content panels or nested layout panels.. ]
55218 * @param {Object} cfg Xtype definition of item to add.
55220 addxtype : function(cfg)
55222 // basically accepts a pannel...
55223 // can accept a layout region..!?!?
55224 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55226 if (!cfg.xtype.match(/Panel$/)) {
55231 if (typeof(cfg.region) == 'undefined') {
55232 Roo.log("Failed to add Panel, region was not set");
55236 var region = cfg.region;
55242 xitems = cfg.items;
55249 case 'ContentPanel': // ContentPanel (el, cfg)
55250 case 'ScrollPanel': // ContentPanel (el, cfg)
55252 if(cfg.autoCreate) {
55253 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55255 var el = this.el.createChild();
55256 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55259 this.add(region, ret);
55263 case 'TreePanel': // our new panel!
55264 cfg.el = this.el.createChild();
55265 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55266 this.add(region, ret);
55269 case 'NestedLayoutPanel':
55270 // create a new Layout (which is a Border Layout...
55271 var el = this.el.createChild();
55272 var clayout = cfg.layout;
55274 clayout.items = clayout.items || [];
55275 // replace this exitems with the clayout ones..
55276 xitems = clayout.items;
55279 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55280 cfg.background = false;
55282 var layout = new Roo.BorderLayout(el, clayout);
55284 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55285 //console.log('adding nested layout panel ' + cfg.toSource());
55286 this.add(region, ret);
55287 nb = {}; /// find first...
55292 // needs grid and region
55294 //var el = this.getRegion(region).el.createChild();
55295 var el = this.el.createChild();
55296 // create the grid first...
55298 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55300 if (region == 'center' && this.active ) {
55301 cfg.background = false;
55303 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55305 this.add(region, ret);
55306 if (cfg.background) {
55307 ret.on('activate', function(gp) {
55308 if (!gp.grid.rendered) {
55323 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55325 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55326 this.add(region, ret);
55329 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55333 // GridPanel (grid, cfg)
55336 this.beginUpdate();
55340 Roo.each(xitems, function(i) {
55341 region = nb && i.region ? i.region : false;
55343 var add = ret.addxtype(i);
55346 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55347 if (!i.background) {
55348 abn[region] = nb[region] ;
55355 // make the last non-background panel active..
55356 //if (nb) { Roo.log(abn); }
55359 for(var r in abn) {
55360 region = this.getRegion(r);
55362 // tried using nb[r], but it does not work..
55364 region.showPanel(abn[r]);
55375 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55376 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
55377 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55378 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
55381 var CP = Roo.ContentPanel;
55383 var layout = Roo.BorderLayout.create({
55387 panels: [new CP("north", "North")]
55396 panels: [new CP("west", {title: "West"})]
55405 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55414 panels: [new CP("south", {title: "South", closable: true})]
55421 preferredTabWidth: 150,
55423 new CP("center1", {title: "Close Me", closable: true}),
55424 new CP("center2", {title: "Center Panel", closable: false})
55429 layout.getRegion("center").showPanel("center1");
55434 Roo.BorderLayout.create = function(config, targetEl){
55435 var layout = new Roo.BorderLayout(targetEl || document.body, config);
55436 layout.beginUpdate();
55437 var regions = Roo.BorderLayout.RegionFactory.validRegions;
55438 for(var j = 0, jlen = regions.length; j < jlen; j++){
55439 var lr = regions[j];
55440 if(layout.regions[lr] && config[lr].panels){
55441 var r = layout.regions[lr];
55442 var ps = config[lr].panels;
55443 layout.addTypedPanels(r, ps);
55446 layout.endUpdate();
55451 Roo.BorderLayout.RegionFactory = {
55453 validRegions : ["north","south","east","west","center"],
55456 create : function(target, mgr, config){
55457 target = target.toLowerCase();
55458 if(config.lightweight || config.basic){
55459 return new Roo.BasicLayoutRegion(mgr, config, target);
55463 return new Roo.NorthLayoutRegion(mgr, config);
55465 return new Roo.SouthLayoutRegion(mgr, config);
55467 return new Roo.EastLayoutRegion(mgr, config);
55469 return new Roo.WestLayoutRegion(mgr, config);
55471 return new Roo.CenterLayoutRegion(mgr, config);
55473 throw 'Layout region "'+target+'" not supported.';
55477 * Ext JS Library 1.1.1
55478 * Copyright(c) 2006-2007, Ext JS, LLC.
55480 * Originally Released Under LGPL - original licence link has changed is not relivant.
55483 * <script type="text/javascript">
55487 * @class Roo.BasicLayoutRegion
55488 * @extends Roo.util.Observable
55489 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55490 * and does not have a titlebar, tabs or any other features. All it does is size and position
55491 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55493 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55495 this.position = pos;
55498 * @scope Roo.BasicLayoutRegion
55502 * @event beforeremove
55503 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55504 * @param {Roo.LayoutRegion} this
55505 * @param {Roo.ContentPanel} panel The panel
55506 * @param {Object} e The cancel event object
55508 "beforeremove" : true,
55510 * @event invalidated
55511 * Fires when the layout for this region is changed.
55512 * @param {Roo.LayoutRegion} this
55514 "invalidated" : true,
55516 * @event visibilitychange
55517 * Fires when this region is shown or hidden
55518 * @param {Roo.LayoutRegion} this
55519 * @param {Boolean} visibility true or false
55521 "visibilitychange" : true,
55523 * @event paneladded
55524 * Fires when a panel is added.
55525 * @param {Roo.LayoutRegion} this
55526 * @param {Roo.ContentPanel} panel The panel
55528 "paneladded" : true,
55530 * @event panelremoved
55531 * Fires when a panel is removed.
55532 * @param {Roo.LayoutRegion} this
55533 * @param {Roo.ContentPanel} panel The panel
55535 "panelremoved" : true,
55537 * @event beforecollapse
55538 * Fires when this region before collapse.
55539 * @param {Roo.LayoutRegion} this
55541 "beforecollapse" : true,
55544 * Fires when this region is collapsed.
55545 * @param {Roo.LayoutRegion} this
55547 "collapsed" : true,
55550 * Fires when this region is expanded.
55551 * @param {Roo.LayoutRegion} this
55556 * Fires when this region is slid into view.
55557 * @param {Roo.LayoutRegion} this
55559 "slideshow" : true,
55562 * Fires when this region slides out of view.
55563 * @param {Roo.LayoutRegion} this
55565 "slidehide" : true,
55567 * @event panelactivated
55568 * Fires when a panel is activated.
55569 * @param {Roo.LayoutRegion} this
55570 * @param {Roo.ContentPanel} panel The activated panel
55572 "panelactivated" : true,
55575 * Fires when the user resizes this region.
55576 * @param {Roo.LayoutRegion} this
55577 * @param {Number} newSize The new size (width for east/west, height for north/south)
55581 /** A collection of panels in this region. @type Roo.util.MixedCollection */
55582 this.panels = new Roo.util.MixedCollection();
55583 this.panels.getKey = this.getPanelId.createDelegate(this);
55585 this.activePanel = null;
55586 // ensure listeners are added...
55588 if (config.listeners || config.events) {
55589 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55590 listeners : config.listeners || {},
55591 events : config.events || {}
55595 if(skipConfig !== true){
55596 this.applyConfig(config);
55600 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55601 getPanelId : function(p){
55605 applyConfig : function(config){
55606 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55607 this.config = config;
55612 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
55613 * the width, for horizontal (north, south) the height.
55614 * @param {Number} newSize The new width or height
55616 resizeTo : function(newSize){
55617 var el = this.el ? this.el :
55618 (this.activePanel ? this.activePanel.getEl() : null);
55620 switch(this.position){
55623 el.setWidth(newSize);
55624 this.fireEvent("resized", this, newSize);
55628 el.setHeight(newSize);
55629 this.fireEvent("resized", this, newSize);
55635 getBox : function(){
55636 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55639 getMargins : function(){
55640 return this.margins;
55643 updateBox : function(box){
55645 var el = this.activePanel.getEl();
55646 el.dom.style.left = box.x + "px";
55647 el.dom.style.top = box.y + "px";
55648 this.activePanel.setSize(box.width, box.height);
55652 * Returns the container element for this region.
55653 * @return {Roo.Element}
55655 getEl : function(){
55656 return this.activePanel;
55660 * Returns true if this region is currently visible.
55661 * @return {Boolean}
55663 isVisible : function(){
55664 return this.activePanel ? true : false;
55667 setActivePanel : function(panel){
55668 panel = this.getPanel(panel);
55669 if(this.activePanel && this.activePanel != panel){
55670 this.activePanel.setActiveState(false);
55671 this.activePanel.getEl().setLeftTop(-10000,-10000);
55673 this.activePanel = panel;
55674 panel.setActiveState(true);
55676 panel.setSize(this.box.width, this.box.height);
55678 this.fireEvent("panelactivated", this, panel);
55679 this.fireEvent("invalidated");
55683 * Show the specified panel.
55684 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55685 * @return {Roo.ContentPanel} The shown panel or null
55687 showPanel : function(panel){
55688 if(panel = this.getPanel(panel)){
55689 this.setActivePanel(panel);
55695 * Get the active panel for this region.
55696 * @return {Roo.ContentPanel} The active panel or null
55698 getActivePanel : function(){
55699 return this.activePanel;
55703 * Add the passed ContentPanel(s)
55704 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55705 * @return {Roo.ContentPanel} The panel added (if only one was added)
55707 add : function(panel){
55708 if(arguments.length > 1){
55709 for(var i = 0, len = arguments.length; i < len; i++) {
55710 this.add(arguments[i]);
55714 if(this.hasPanel(panel)){
55715 this.showPanel(panel);
55718 var el = panel.getEl();
55719 if(el.dom.parentNode != this.mgr.el.dom){
55720 this.mgr.el.dom.appendChild(el.dom);
55722 if(panel.setRegion){
55723 panel.setRegion(this);
55725 this.panels.add(panel);
55726 el.setStyle("position", "absolute");
55727 if(!panel.background){
55728 this.setActivePanel(panel);
55729 if(this.config.initialSize && this.panels.getCount()==1){
55730 this.resizeTo(this.config.initialSize);
55733 this.fireEvent("paneladded", this, panel);
55738 * Returns true if the panel is in this region.
55739 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55740 * @return {Boolean}
55742 hasPanel : function(panel){
55743 if(typeof panel == "object"){ // must be panel obj
55744 panel = panel.getId();
55746 return this.getPanel(panel) ? true : false;
55750 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55751 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55752 * @param {Boolean} preservePanel Overrides the config preservePanel option
55753 * @return {Roo.ContentPanel} The panel that was removed
55755 remove : function(panel, preservePanel){
55756 panel = this.getPanel(panel);
55761 this.fireEvent("beforeremove", this, panel, e);
55762 if(e.cancel === true){
55765 var panelId = panel.getId();
55766 this.panels.removeKey(panelId);
55771 * Returns the panel specified or null if it's not in this region.
55772 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55773 * @return {Roo.ContentPanel}
55775 getPanel : function(id){
55776 if(typeof id == "object"){ // must be panel obj
55779 return this.panels.get(id);
55783 * Returns this regions position (north/south/east/west/center).
55786 getPosition: function(){
55787 return this.position;
55791 * Ext JS Library 1.1.1
55792 * Copyright(c) 2006-2007, Ext JS, LLC.
55794 * Originally Released Under LGPL - original licence link has changed is not relivant.
55797 * <script type="text/javascript">
55801 * @class Roo.LayoutRegion
55802 * @extends Roo.BasicLayoutRegion
55803 * This class represents a region in a layout manager.
55804 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
55805 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
55806 * @cfg {Boolean} floatable False to disable floating (defaults to true)
55807 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55808 * @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})
55809 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
55810 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
55811 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
55812 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
55813 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
55814 * @cfg {String} title The title for the region (overrides panel titles)
55815 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
55816 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55817 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
55818 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55819 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
55820 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55821 * the space available, similar to FireFox 1.5 tabs (defaults to false)
55822 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
55823 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
55824 * @cfg {Boolean} showPin True to show a pin button
55825 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
55826 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
55827 * @cfg {Boolean} disableTabTips True to disable tab tooltips
55828 * @cfg {Number} width For East/West panels
55829 * @cfg {Number} height For North/South panels
55830 * @cfg {Boolean} split To show the splitter
55831 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
55833 Roo.LayoutRegion = function(mgr, config, pos){
55834 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55835 var dh = Roo.DomHelper;
55836 /** This region's container element
55837 * @type Roo.Element */
55838 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55839 /** This region's title element
55840 * @type Roo.Element */
55842 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55843 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
55844 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55846 this.titleEl.enableDisplayMode();
55847 /** This region's title text element
55848 * @type HTMLElement */
55849 this.titleTextEl = this.titleEl.dom.firstChild;
55850 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55851 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55852 this.closeBtn.enableDisplayMode();
55853 this.closeBtn.on("click", this.closeClicked, this);
55854 this.closeBtn.hide();
55856 this.createBody(config);
55857 this.visible = true;
55858 this.collapsed = false;
55860 if(config.hideWhenEmpty){
55862 this.on("paneladded", this.validateVisibility, this);
55863 this.on("panelremoved", this.validateVisibility, this);
55865 this.applyConfig(config);
55868 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55870 createBody : function(){
55871 /** This region's body element
55872 * @type Roo.Element */
55873 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55876 applyConfig : function(c){
55877 if(c.collapsible && this.position != "center" && !this.collapsedEl){
55878 var dh = Roo.DomHelper;
55879 if(c.titlebar !== false){
55880 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55881 this.collapseBtn.on("click", this.collapse, this);
55882 this.collapseBtn.enableDisplayMode();
55884 if(c.showPin === true || this.showPin){
55885 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55886 this.stickBtn.enableDisplayMode();
55887 this.stickBtn.on("click", this.expand, this);
55888 this.stickBtn.hide();
55891 /** This region's collapsed element
55892 * @type Roo.Element */
55893 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55894 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55896 if(c.floatable !== false){
55897 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55898 this.collapsedEl.on("click", this.collapseClick, this);
55901 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55902 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55903 id: "message", unselectable: "on", style:{"float":"left"}});
55904 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55906 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55907 this.expandBtn.on("click", this.expand, this);
55909 if(this.collapseBtn){
55910 this.collapseBtn.setVisible(c.collapsible == true);
55912 this.cmargins = c.cmargins || this.cmargins ||
55913 (this.position == "west" || this.position == "east" ?
55914 {top: 0, left: 2, right:2, bottom: 0} :
55915 {top: 2, left: 0, right:0, bottom: 2});
55916 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55917 this.bottomTabs = c.tabPosition != "top";
55918 this.autoScroll = c.autoScroll || false;
55919 if(this.autoScroll){
55920 this.bodyEl.setStyle("overflow", "auto");
55922 this.bodyEl.setStyle("overflow", "hidden");
55924 //if(c.titlebar !== false){
55925 if((!c.titlebar && !c.title) || c.titlebar === false){
55926 this.titleEl.hide();
55928 this.titleEl.show();
55930 this.titleTextEl.innerHTML = c.title;
55934 this.duration = c.duration || .30;
55935 this.slideDuration = c.slideDuration || .45;
55938 this.collapse(true);
55945 * Returns true if this region is currently visible.
55946 * @return {Boolean}
55948 isVisible : function(){
55949 return this.visible;
55953 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55954 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
55956 setCollapsedTitle : function(title){
55957 title = title || " ";
55958 if(this.collapsedTitleTextEl){
55959 this.collapsedTitleTextEl.innerHTML = title;
55963 getBox : function(){
55965 if(!this.collapsed){
55966 b = this.el.getBox(false, true);
55968 b = this.collapsedEl.getBox(false, true);
55973 getMargins : function(){
55974 return this.collapsed ? this.cmargins : this.margins;
55977 highlight : function(){
55978 this.el.addClass("x-layout-panel-dragover");
55981 unhighlight : function(){
55982 this.el.removeClass("x-layout-panel-dragover");
55985 updateBox : function(box){
55987 if(!this.collapsed){
55988 this.el.dom.style.left = box.x + "px";
55989 this.el.dom.style.top = box.y + "px";
55990 this.updateBody(box.width, box.height);
55992 this.collapsedEl.dom.style.left = box.x + "px";
55993 this.collapsedEl.dom.style.top = box.y + "px";
55994 this.collapsedEl.setSize(box.width, box.height);
55997 this.tabs.autoSizeTabs();
56001 updateBody : function(w, h){
56003 this.el.setWidth(w);
56004 w -= this.el.getBorderWidth("rl");
56005 if(this.config.adjustments){
56006 w += this.config.adjustments[0];
56010 this.el.setHeight(h);
56011 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56012 h -= this.el.getBorderWidth("tb");
56013 if(this.config.adjustments){
56014 h += this.config.adjustments[1];
56016 this.bodyEl.setHeight(h);
56018 h = this.tabs.syncHeight(h);
56021 if(this.panelSize){
56022 w = w !== null ? w : this.panelSize.width;
56023 h = h !== null ? h : this.panelSize.height;
56025 if(this.activePanel){
56026 var el = this.activePanel.getEl();
56027 w = w !== null ? w : el.getWidth();
56028 h = h !== null ? h : el.getHeight();
56029 this.panelSize = {width: w, height: h};
56030 this.activePanel.setSize(w, h);
56032 if(Roo.isIE && this.tabs){
56033 this.tabs.el.repaint();
56038 * Returns the container element for this region.
56039 * @return {Roo.Element}
56041 getEl : function(){
56046 * Hides this region.
56049 if(!this.collapsed){
56050 this.el.dom.style.left = "-2000px";
56053 this.collapsedEl.dom.style.left = "-2000px";
56054 this.collapsedEl.hide();
56056 this.visible = false;
56057 this.fireEvent("visibilitychange", this, false);
56061 * Shows this region if it was previously hidden.
56064 if(!this.collapsed){
56067 this.collapsedEl.show();
56069 this.visible = true;
56070 this.fireEvent("visibilitychange", this, true);
56073 closeClicked : function(){
56074 if(this.activePanel){
56075 this.remove(this.activePanel);
56079 collapseClick : function(e){
56081 e.stopPropagation();
56084 e.stopPropagation();
56090 * Collapses this region.
56091 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56093 collapse : function(skipAnim, skipCheck){
56094 if(this.collapsed) {
56098 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56100 this.collapsed = true;
56102 this.split.el.hide();
56104 if(this.config.animate && skipAnim !== true){
56105 this.fireEvent("invalidated", this);
56106 this.animateCollapse();
56108 this.el.setLocation(-20000,-20000);
56110 this.collapsedEl.show();
56111 this.fireEvent("collapsed", this);
56112 this.fireEvent("invalidated", this);
56118 animateCollapse : function(){
56123 * Expands this region if it was previously collapsed.
56124 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56125 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56127 expand : function(e, skipAnim){
56129 e.stopPropagation();
56131 if(!this.collapsed || this.el.hasActiveFx()) {
56135 this.afterSlideIn();
56138 this.collapsed = false;
56139 if(this.config.animate && skipAnim !== true){
56140 this.animateExpand();
56144 this.split.el.show();
56146 this.collapsedEl.setLocation(-2000,-2000);
56147 this.collapsedEl.hide();
56148 this.fireEvent("invalidated", this);
56149 this.fireEvent("expanded", this);
56153 animateExpand : function(){
56157 initTabs : function()
56159 this.bodyEl.setStyle("overflow", "hidden");
56160 var ts = new Roo.TabPanel(
56163 tabPosition: this.bottomTabs ? 'bottom' : 'top',
56164 disableTooltips: this.config.disableTabTips,
56165 toolbar : this.config.toolbar
56168 if(this.config.hideTabs){
56169 ts.stripWrap.setDisplayed(false);
56172 ts.resizeTabs = this.config.resizeTabs === true;
56173 ts.minTabWidth = this.config.minTabWidth || 40;
56174 ts.maxTabWidth = this.config.maxTabWidth || 250;
56175 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56176 ts.monitorResize = false;
56177 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56178 ts.bodyEl.addClass('x-layout-tabs-body');
56179 this.panels.each(this.initPanelAsTab, this);
56182 initPanelAsTab : function(panel){
56183 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56184 this.config.closeOnTab && panel.isClosable());
56185 if(panel.tabTip !== undefined){
56186 ti.setTooltip(panel.tabTip);
56188 ti.on("activate", function(){
56189 this.setActivePanel(panel);
56191 if(this.config.closeOnTab){
56192 ti.on("beforeclose", function(t, e){
56194 this.remove(panel);
56200 updatePanelTitle : function(panel, title){
56201 if(this.activePanel == panel){
56202 this.updateTitle(title);
56205 var ti = this.tabs.getTab(panel.getEl().id);
56207 if(panel.tabTip !== undefined){
56208 ti.setTooltip(panel.tabTip);
56213 updateTitle : function(title){
56214 if(this.titleTextEl && !this.config.title){
56215 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
56219 setActivePanel : function(panel){
56220 panel = this.getPanel(panel);
56221 if(this.activePanel && this.activePanel != panel){
56222 this.activePanel.setActiveState(false);
56224 this.activePanel = panel;
56225 panel.setActiveState(true);
56226 if(this.panelSize){
56227 panel.setSize(this.panelSize.width, this.panelSize.height);
56230 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56232 this.updateTitle(panel.getTitle());
56234 this.fireEvent("invalidated", this);
56236 this.fireEvent("panelactivated", this, panel);
56240 * Shows the specified panel.
56241 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56242 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56244 showPanel : function(panel)
56246 panel = this.getPanel(panel);
56249 var tab = this.tabs.getTab(panel.getEl().id);
56250 if(tab.isHidden()){
56251 this.tabs.unhideTab(tab.id);
56255 this.setActivePanel(panel);
56262 * Get the active panel for this region.
56263 * @return {Roo.ContentPanel} The active panel or null
56265 getActivePanel : function(){
56266 return this.activePanel;
56269 validateVisibility : function(){
56270 if(this.panels.getCount() < 1){
56271 this.updateTitle(" ");
56272 this.closeBtn.hide();
56275 if(!this.isVisible()){
56282 * Adds the passed ContentPanel(s) to this region.
56283 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56284 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56286 add : function(panel){
56287 if(arguments.length > 1){
56288 for(var i = 0, len = arguments.length; i < len; i++) {
56289 this.add(arguments[i]);
56293 if(this.hasPanel(panel)){
56294 this.showPanel(panel);
56297 panel.setRegion(this);
56298 this.panels.add(panel);
56299 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56300 this.bodyEl.dom.appendChild(panel.getEl().dom);
56301 if(panel.background !== true){
56302 this.setActivePanel(panel);
56304 this.fireEvent("paneladded", this, panel);
56310 this.initPanelAsTab(panel);
56312 if(panel.background !== true){
56313 this.tabs.activate(panel.getEl().id);
56315 this.fireEvent("paneladded", this, panel);
56320 * Hides the tab for the specified panel.
56321 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56323 hidePanel : function(panel){
56324 if(this.tabs && (panel = this.getPanel(panel))){
56325 this.tabs.hideTab(panel.getEl().id);
56330 * Unhides the tab for a previously hidden panel.
56331 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56333 unhidePanel : function(panel){
56334 if(this.tabs && (panel = this.getPanel(panel))){
56335 this.tabs.unhideTab(panel.getEl().id);
56339 clearPanels : function(){
56340 while(this.panels.getCount() > 0){
56341 this.remove(this.panels.first());
56346 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56347 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56348 * @param {Boolean} preservePanel Overrides the config preservePanel option
56349 * @return {Roo.ContentPanel} The panel that was removed
56351 remove : function(panel, preservePanel){
56352 panel = this.getPanel(panel);
56357 this.fireEvent("beforeremove", this, panel, e);
56358 if(e.cancel === true){
56361 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56362 var panelId = panel.getId();
56363 this.panels.removeKey(panelId);
56365 document.body.appendChild(panel.getEl().dom);
56368 this.tabs.removeTab(panel.getEl().id);
56369 }else if (!preservePanel){
56370 this.bodyEl.dom.removeChild(panel.getEl().dom);
56372 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56373 var p = this.panels.first();
56374 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56375 tempEl.appendChild(p.getEl().dom);
56376 this.bodyEl.update("");
56377 this.bodyEl.dom.appendChild(p.getEl().dom);
56379 this.updateTitle(p.getTitle());
56381 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56382 this.setActivePanel(p);
56384 panel.setRegion(null);
56385 if(this.activePanel == panel){
56386 this.activePanel = null;
56388 if(this.config.autoDestroy !== false && preservePanel !== true){
56389 try{panel.destroy();}catch(e){}
56391 this.fireEvent("panelremoved", this, panel);
56396 * Returns the TabPanel component used by this region
56397 * @return {Roo.TabPanel}
56399 getTabs : function(){
56403 createTool : function(parentEl, className){
56404 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56405 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
56406 btn.addClassOnOver("x-layout-tools-button-over");
56411 * Ext JS Library 1.1.1
56412 * Copyright(c) 2006-2007, Ext JS, LLC.
56414 * Originally Released Under LGPL - original licence link has changed is not relivant.
56417 * <script type="text/javascript">
56423 * @class Roo.SplitLayoutRegion
56424 * @extends Roo.LayoutRegion
56425 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56427 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56428 this.cursor = cursor;
56429 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56432 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56433 splitTip : "Drag to resize.",
56434 collapsibleSplitTip : "Drag to resize. Double click to hide.",
56435 useSplitTips : false,
56437 applyConfig : function(config){
56438 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56441 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
56442 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
56443 /** The SplitBar for this region
56444 * @type Roo.SplitBar */
56445 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56446 this.split.on("moved", this.onSplitMove, this);
56447 this.split.useShim = config.useShim === true;
56448 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56449 if(this.useSplitTips){
56450 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56452 if(config.collapsible){
56453 this.split.el.on("dblclick", this.collapse, this);
56456 if(typeof config.minSize != "undefined"){
56457 this.split.minSize = config.minSize;
56459 if(typeof config.maxSize != "undefined"){
56460 this.split.maxSize = config.maxSize;
56462 if(config.hideWhenEmpty || config.hidden || config.collapsed){
56463 this.hideSplitter();
56468 getHMaxSize : function(){
56469 var cmax = this.config.maxSize || 10000;
56470 var center = this.mgr.getRegion("center");
56471 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56474 getVMaxSize : function(){
56475 var cmax = this.config.maxSize || 10000;
56476 var center = this.mgr.getRegion("center");
56477 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56480 onSplitMove : function(split, newSize){
56481 this.fireEvent("resized", this, newSize);
56485 * Returns the {@link Roo.SplitBar} for this region.
56486 * @return {Roo.SplitBar}
56488 getSplitBar : function(){
56493 this.hideSplitter();
56494 Roo.SplitLayoutRegion.superclass.hide.call(this);
56497 hideSplitter : function(){
56499 this.split.el.setLocation(-2000,-2000);
56500 this.split.el.hide();
56506 this.split.el.show();
56508 Roo.SplitLayoutRegion.superclass.show.call(this);
56511 beforeSlide: function(){
56512 if(Roo.isGecko){// firefox overflow auto bug workaround
56513 this.bodyEl.clip();
56515 this.tabs.bodyEl.clip();
56517 if(this.activePanel){
56518 this.activePanel.getEl().clip();
56520 if(this.activePanel.beforeSlide){
56521 this.activePanel.beforeSlide();
56527 afterSlide : function(){
56528 if(Roo.isGecko){// firefox overflow auto bug workaround
56529 this.bodyEl.unclip();
56531 this.tabs.bodyEl.unclip();
56533 if(this.activePanel){
56534 this.activePanel.getEl().unclip();
56535 if(this.activePanel.afterSlide){
56536 this.activePanel.afterSlide();
56542 initAutoHide : function(){
56543 if(this.autoHide !== false){
56544 if(!this.autoHideHd){
56545 var st = new Roo.util.DelayedTask(this.slideIn, this);
56546 this.autoHideHd = {
56547 "mouseout": function(e){
56548 if(!e.within(this.el, true)){
56552 "mouseover" : function(e){
56558 this.el.on(this.autoHideHd);
56562 clearAutoHide : function(){
56563 if(this.autoHide !== false){
56564 this.el.un("mouseout", this.autoHideHd.mouseout);
56565 this.el.un("mouseover", this.autoHideHd.mouseover);
56569 clearMonitor : function(){
56570 Roo.get(document).un("click", this.slideInIf, this);
56573 // these names are backwards but not changed for compat
56574 slideOut : function(){
56575 if(this.isSlid || this.el.hasActiveFx()){
56578 this.isSlid = true;
56579 if(this.collapseBtn){
56580 this.collapseBtn.hide();
56582 this.closeBtnState = this.closeBtn.getStyle('display');
56583 this.closeBtn.hide();
56585 this.stickBtn.show();
56588 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56589 this.beforeSlide();
56590 this.el.setStyle("z-index", 10001);
56591 this.el.slideIn(this.getSlideAnchor(), {
56592 callback: function(){
56594 this.initAutoHide();
56595 Roo.get(document).on("click", this.slideInIf, this);
56596 this.fireEvent("slideshow", this);
56603 afterSlideIn : function(){
56604 this.clearAutoHide();
56605 this.isSlid = false;
56606 this.clearMonitor();
56607 this.el.setStyle("z-index", "");
56608 if(this.collapseBtn){
56609 this.collapseBtn.show();
56611 this.closeBtn.setStyle('display', this.closeBtnState);
56613 this.stickBtn.hide();
56615 this.fireEvent("slidehide", this);
56618 slideIn : function(cb){
56619 if(!this.isSlid || this.el.hasActiveFx()){
56623 this.isSlid = false;
56624 this.beforeSlide();
56625 this.el.slideOut(this.getSlideAnchor(), {
56626 callback: function(){
56627 this.el.setLeftTop(-10000, -10000);
56629 this.afterSlideIn();
56637 slideInIf : function(e){
56638 if(!e.within(this.el)){
56643 animateCollapse : function(){
56644 this.beforeSlide();
56645 this.el.setStyle("z-index", 20000);
56646 var anchor = this.getSlideAnchor();
56647 this.el.slideOut(anchor, {
56648 callback : function(){
56649 this.el.setStyle("z-index", "");
56650 this.collapsedEl.slideIn(anchor, {duration:.3});
56652 this.el.setLocation(-10000,-10000);
56654 this.fireEvent("collapsed", this);
56661 animateExpand : function(){
56662 this.beforeSlide();
56663 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56664 this.el.setStyle("z-index", 20000);
56665 this.collapsedEl.hide({
56668 this.el.slideIn(this.getSlideAnchor(), {
56669 callback : function(){
56670 this.el.setStyle("z-index", "");
56673 this.split.el.show();
56675 this.fireEvent("invalidated", this);
56676 this.fireEvent("expanded", this);
56704 getAnchor : function(){
56705 return this.anchors[this.position];
56708 getCollapseAnchor : function(){
56709 return this.canchors[this.position];
56712 getSlideAnchor : function(){
56713 return this.sanchors[this.position];
56716 getAlignAdj : function(){
56717 var cm = this.cmargins;
56718 switch(this.position){
56734 getExpandAdj : function(){
56735 var c = this.collapsedEl, cm = this.cmargins;
56736 switch(this.position){
56738 return [-(cm.right+c.getWidth()+cm.left), 0];
56741 return [cm.right+c.getWidth()+cm.left, 0];
56744 return [0, -(cm.top+cm.bottom+c.getHeight())];
56747 return [0, cm.top+cm.bottom+c.getHeight()];
56753 * Ext JS Library 1.1.1
56754 * Copyright(c) 2006-2007, Ext JS, LLC.
56756 * Originally Released Under LGPL - original licence link has changed is not relivant.
56759 * <script type="text/javascript">
56762 * These classes are private internal classes
56764 Roo.CenterLayoutRegion = function(mgr, config){
56765 Roo.LayoutRegion.call(this, mgr, config, "center");
56766 this.visible = true;
56767 this.minWidth = config.minWidth || 20;
56768 this.minHeight = config.minHeight || 20;
56771 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56773 // center panel can't be hidden
56777 // center panel can't be hidden
56780 getMinWidth: function(){
56781 return this.minWidth;
56784 getMinHeight: function(){
56785 return this.minHeight;
56790 Roo.NorthLayoutRegion = function(mgr, config){
56791 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56793 this.split.placement = Roo.SplitBar.TOP;
56794 this.split.orientation = Roo.SplitBar.VERTICAL;
56795 this.split.el.addClass("x-layout-split-v");
56797 var size = config.initialSize || config.height;
56798 if(typeof size != "undefined"){
56799 this.el.setHeight(size);
56802 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56803 orientation: Roo.SplitBar.VERTICAL,
56804 getBox : function(){
56805 if(this.collapsed){
56806 return this.collapsedEl.getBox();
56808 var box = this.el.getBox();
56810 box.height += this.split.el.getHeight();
56815 updateBox : function(box){
56816 if(this.split && !this.collapsed){
56817 box.height -= this.split.el.getHeight();
56818 this.split.el.setLeft(box.x);
56819 this.split.el.setTop(box.y+box.height);
56820 this.split.el.setWidth(box.width);
56822 if(this.collapsed){
56823 this.updateBody(box.width, null);
56825 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56829 Roo.SouthLayoutRegion = function(mgr, config){
56830 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56832 this.split.placement = Roo.SplitBar.BOTTOM;
56833 this.split.orientation = Roo.SplitBar.VERTICAL;
56834 this.split.el.addClass("x-layout-split-v");
56836 var size = config.initialSize || config.height;
56837 if(typeof size != "undefined"){
56838 this.el.setHeight(size);
56841 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56842 orientation: Roo.SplitBar.VERTICAL,
56843 getBox : function(){
56844 if(this.collapsed){
56845 return this.collapsedEl.getBox();
56847 var box = this.el.getBox();
56849 var sh = this.split.el.getHeight();
56856 updateBox : function(box){
56857 if(this.split && !this.collapsed){
56858 var sh = this.split.el.getHeight();
56861 this.split.el.setLeft(box.x);
56862 this.split.el.setTop(box.y-sh);
56863 this.split.el.setWidth(box.width);
56865 if(this.collapsed){
56866 this.updateBody(box.width, null);
56868 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56872 Roo.EastLayoutRegion = function(mgr, config){
56873 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56875 this.split.placement = Roo.SplitBar.RIGHT;
56876 this.split.orientation = Roo.SplitBar.HORIZONTAL;
56877 this.split.el.addClass("x-layout-split-h");
56879 var size = config.initialSize || config.width;
56880 if(typeof size != "undefined"){
56881 this.el.setWidth(size);
56884 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56885 orientation: Roo.SplitBar.HORIZONTAL,
56886 getBox : function(){
56887 if(this.collapsed){
56888 return this.collapsedEl.getBox();
56890 var box = this.el.getBox();
56892 var sw = this.split.el.getWidth();
56899 updateBox : function(box){
56900 if(this.split && !this.collapsed){
56901 var sw = this.split.el.getWidth();
56903 this.split.el.setLeft(box.x);
56904 this.split.el.setTop(box.y);
56905 this.split.el.setHeight(box.height);
56908 if(this.collapsed){
56909 this.updateBody(null, box.height);
56911 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56915 Roo.WestLayoutRegion = function(mgr, config){
56916 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56918 this.split.placement = Roo.SplitBar.LEFT;
56919 this.split.orientation = Roo.SplitBar.HORIZONTAL;
56920 this.split.el.addClass("x-layout-split-h");
56922 var size = config.initialSize || config.width;
56923 if(typeof size != "undefined"){
56924 this.el.setWidth(size);
56927 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56928 orientation: Roo.SplitBar.HORIZONTAL,
56929 getBox : function(){
56930 if(this.collapsed){
56931 return this.collapsedEl.getBox();
56933 var box = this.el.getBox();
56935 box.width += this.split.el.getWidth();
56940 updateBox : function(box){
56941 if(this.split && !this.collapsed){
56942 var sw = this.split.el.getWidth();
56944 this.split.el.setLeft(box.x+box.width);
56945 this.split.el.setTop(box.y);
56946 this.split.el.setHeight(box.height);
56948 if(this.collapsed){
56949 this.updateBody(null, box.height);
56951 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56956 * Ext JS Library 1.1.1
56957 * Copyright(c) 2006-2007, Ext JS, LLC.
56959 * Originally Released Under LGPL - original licence link has changed is not relivant.
56962 * <script type="text/javascript">
56967 * Private internal class for reading and applying state
56969 Roo.LayoutStateManager = function(layout){
56970 // default empty state
56979 Roo.LayoutStateManager.prototype = {
56980 init : function(layout, provider){
56981 this.provider = provider;
56982 var state = provider.get(layout.id+"-layout-state");
56984 var wasUpdating = layout.isUpdating();
56986 layout.beginUpdate();
56988 for(var key in state){
56989 if(typeof state[key] != "function"){
56990 var rstate = state[key];
56991 var r = layout.getRegion(key);
56994 r.resizeTo(rstate.size);
56996 if(rstate.collapsed == true){
56999 r.expand(null, true);
57005 layout.endUpdate();
57007 this.state = state;
57009 this.layout = layout;
57010 layout.on("regionresized", this.onRegionResized, this);
57011 layout.on("regioncollapsed", this.onRegionCollapsed, this);
57012 layout.on("regionexpanded", this.onRegionExpanded, this);
57015 storeState : function(){
57016 this.provider.set(this.layout.id+"-layout-state", this.state);
57019 onRegionResized : function(region, newSize){
57020 this.state[region.getPosition()].size = newSize;
57024 onRegionCollapsed : function(region){
57025 this.state[region.getPosition()].collapsed = true;
57029 onRegionExpanded : function(region){
57030 this.state[region.getPosition()].collapsed = false;
57035 * Ext JS Library 1.1.1
57036 * Copyright(c) 2006-2007, Ext JS, LLC.
57038 * Originally Released Under LGPL - original licence link has changed is not relivant.
57041 * <script type="text/javascript">
57044 * @class Roo.ContentPanel
57045 * @extends Roo.util.Observable
57046 * @children Roo.form.Form Roo.JsonView Roo.View
57047 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57048 * A basic ContentPanel element.
57049 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
57050 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
57051 * @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
57052 * @cfg {Boolean} closable True if the panel can be closed/removed
57053 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
57054 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57055 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
57056 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
57057 * @cfg {String} title The title for this panel
57058 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57059 * @cfg {String} url Calls {@link #setUrl} with this value
57060 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57061 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
57062 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
57063 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
57064 * @cfg {String} style Extra style to add to the content panel
57065 * @cfg {Roo.menu.Menu} menu popup menu
57068 * Create a new ContentPanel.
57069 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57070 * @param {String/Object} config A string to set only the title or a config object
57071 * @param {String} content (optional) Set the HTML content for this panel
57072 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57074 Roo.ContentPanel = function(el, config, content){
57078 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57082 if (config && config.parentLayout) {
57083 el = config.parentLayout.el.createChild();
57086 if(el.autoCreate){ // xtype is available if this is called from factory
57090 this.el = Roo.get(el);
57091 if(!this.el && config && config.autoCreate){
57092 if(typeof config.autoCreate == "object"){
57093 if(!config.autoCreate.id){
57094 config.autoCreate.id = config.id||el;
57096 this.el = Roo.DomHelper.append(document.body,
57097 config.autoCreate, true);
57099 this.el = Roo.DomHelper.append(document.body,
57100 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57105 this.closable = false;
57106 this.loaded = false;
57107 this.active = false;
57108 if(typeof config == "string"){
57109 this.title = config;
57111 Roo.apply(this, config);
57114 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57115 this.wrapEl = this.el.wrap();
57116 this.toolbar.container = this.el.insertSibling(false, 'before');
57117 this.toolbar = new Roo.Toolbar(this.toolbar);
57120 // xtype created footer. - not sure if will work as we normally have to render first..
57121 if (this.footer && !this.footer.el && this.footer.xtype) {
57122 if (!this.wrapEl) {
57123 this.wrapEl = this.el.wrap();
57126 this.footer.container = this.wrapEl.createChild();
57128 this.footer = Roo.factory(this.footer, Roo);
57133 this.resizeEl = Roo.get(this.resizeEl, true);
57135 this.resizeEl = this.el;
57137 // handle view.xtype
57145 * Fires when this panel is activated.
57146 * @param {Roo.ContentPanel} this
57150 * @event deactivate
57151 * Fires when this panel is activated.
57152 * @param {Roo.ContentPanel} this
57154 "deactivate" : true,
57158 * Fires when this panel is resized if fitToFrame is true.
57159 * @param {Roo.ContentPanel} this
57160 * @param {Number} width The width after any component adjustments
57161 * @param {Number} height The height after any component adjustments
57167 * Fires when this tab is created
57168 * @param {Roo.ContentPanel} this
57178 if(this.autoScroll){
57179 this.resizeEl.setStyle("overflow", "auto");
57181 // fix randome scrolling
57182 this.el.on('scroll', function() {
57183 Roo.log('fix random scolling');
57184 this.scrollTo('top',0);
57187 content = content || this.content;
57189 this.setContent(content);
57191 if(config && config.url){
57192 this.setUrl(this.url, this.params, this.loadOnce);
57197 Roo.ContentPanel.superclass.constructor.call(this);
57199 if (this.view && typeof(this.view.xtype) != 'undefined') {
57200 this.view.el = this.el.appendChild(document.createElement("div"));
57201 this.view = Roo.factory(this.view);
57202 this.view.render && this.view.render(false, '');
57206 this.fireEvent('render', this);
57209 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57211 setRegion : function(region){
57212 this.region = region;
57214 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57216 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57221 * Returns the toolbar for this Panel if one was configured.
57222 * @return {Roo.Toolbar}
57224 getToolbar : function(){
57225 return this.toolbar;
57228 setActiveState : function(active){
57229 this.active = active;
57231 this.fireEvent("deactivate", this);
57233 this.fireEvent("activate", this);
57237 * Updates this panel's element
57238 * @param {String} content The new content
57239 * @param {Boolean} loadScripts (optional) true to look for and process scripts
57241 setContent : function(content, loadScripts){
57242 this.el.update(content, loadScripts);
57245 ignoreResize : function(w, h){
57246 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57249 this.lastSize = {width: w, height: h};
57254 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57255 * @return {Roo.UpdateManager} The UpdateManager
57257 getUpdateManager : function(){
57258 return this.el.getUpdateManager();
57261 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57262 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
57265 url: "your-url.php",
57266 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57267 callback: yourFunction,
57268 scope: yourObject, //(optional scope)
57271 text: "Loading...",
57276 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57277 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
57278 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
57279 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57280 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
57281 * @return {Roo.ContentPanel} this
57284 var um = this.el.getUpdateManager();
57285 um.update.apply(um, arguments);
57291 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
57292 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57293 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
57294 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
57295 * @return {Roo.UpdateManager} The UpdateManager
57297 setUrl : function(url, params, loadOnce){
57298 if(this.refreshDelegate){
57299 this.removeListener("activate", this.refreshDelegate);
57301 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57302 this.on("activate", this.refreshDelegate);
57303 return this.el.getUpdateManager();
57306 _handleRefresh : function(url, params, loadOnce){
57307 if(!loadOnce || !this.loaded){
57308 var updater = this.el.getUpdateManager();
57309 updater.update(url, params, this._setLoaded.createDelegate(this));
57313 _setLoaded : function(){
57314 this.loaded = true;
57318 * Returns this panel's id
57321 getId : function(){
57326 * Returns this panel's element - used by regiosn to add.
57327 * @return {Roo.Element}
57329 getEl : function(){
57330 return this.wrapEl || this.el;
57333 adjustForComponents : function(width, height)
57335 //Roo.log('adjustForComponents ');
57336 if(this.resizeEl != this.el){
57337 width -= this.el.getFrameWidth('lr');
57338 height -= this.el.getFrameWidth('tb');
57341 var te = this.toolbar.getEl();
57342 height -= te.getHeight();
57343 te.setWidth(width);
57346 var te = this.footer.getEl();
57347 //Roo.log("footer:" + te.getHeight());
57349 height -= te.getHeight();
57350 te.setWidth(width);
57354 if(this.adjustments){
57355 width += this.adjustments[0];
57356 height += this.adjustments[1];
57358 return {"width": width, "height": height};
57361 setSize : function(width, height){
57362 if(this.fitToFrame && !this.ignoreResize(width, height)){
57363 if(this.fitContainer && this.resizeEl != this.el){
57364 this.el.setSize(width, height);
57366 var size = this.adjustForComponents(width, height);
57367 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57368 this.fireEvent('resize', this, size.width, size.height);
57373 * Returns this panel's title
57376 getTitle : function(){
57381 * Set this panel's title
57382 * @param {String} title
57384 setTitle : function(title){
57385 this.title = title;
57387 this.region.updatePanelTitle(this, title);
57392 * Returns true is this panel was configured to be closable
57393 * @return {Boolean}
57395 isClosable : function(){
57396 return this.closable;
57399 beforeSlide : function(){
57401 this.resizeEl.clip();
57404 afterSlide : function(){
57406 this.resizeEl.unclip();
57410 * Force a content refresh from the URL specified in the {@link #setUrl} method.
57411 * Will fail silently if the {@link #setUrl} method has not been called.
57412 * This does not activate the panel, just updates its content.
57414 refresh : function(){
57415 if(this.refreshDelegate){
57416 this.loaded = false;
57417 this.refreshDelegate();
57422 * Destroys this panel
57424 destroy : function(){
57425 this.el.removeAllListeners();
57426 var tempEl = document.createElement("span");
57427 tempEl.appendChild(this.el.dom);
57428 tempEl.innerHTML = "";
57434 * form - if the content panel contains a form - this is a reference to it.
57435 * @type {Roo.form.Form}
57439 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57440 * This contains a reference to it.
57446 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57456 * @param {Object} cfg Xtype definition of item to add.
57459 addxtype : function(cfg) {
57461 if (cfg.xtype.match(/^Form$/)) {
57464 //if (this.footer) {
57465 // el = this.footer.container.insertSibling(false, 'before');
57467 el = this.el.createChild();
57470 this.form = new Roo.form.Form(cfg);
57473 if ( this.form.allItems.length) {
57474 this.form.render(el.dom);
57478 // should only have one of theses..
57479 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57480 // views.. should not be just added - used named prop 'view''
57482 cfg.el = this.el.appendChild(document.createElement("div"));
57485 var ret = new Roo.factory(cfg);
57487 ret.render && ret.render(false, ''); // render blank..
57496 * @class Roo.GridPanel
57497 * @extends Roo.ContentPanel
57498 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57500 * Create a new GridPanel.
57501 * @cfg {Roo.grid.Grid} grid The grid for this panel
57503 Roo.GridPanel = function(grid, config){
57505 // universal ctor...
57506 if (typeof(grid.grid) != 'undefined') {
57508 grid = config.grid;
57510 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57511 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57513 this.wrapper.dom.appendChild(grid.getGridEl().dom);
57515 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57518 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57520 // xtype created footer. - not sure if will work as we normally have to render first..
57521 if (this.footer && !this.footer.el && this.footer.xtype) {
57523 this.footer.container = this.grid.getView().getFooterPanel(true);
57524 this.footer.dataSource = this.grid.dataSource;
57525 this.footer = Roo.factory(this.footer, Roo);
57529 grid.monitorWindowResize = false; // turn off autosizing
57530 grid.autoHeight = false;
57531 grid.autoWidth = false;
57533 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57536 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57537 getId : function(){
57538 return this.grid.id;
57542 * Returns the grid for this panel
57543 * @return {Roo.grid.Grid}
57545 getGrid : function(){
57549 setSize : function(width, height){
57550 if(!this.ignoreResize(width, height)){
57551 var grid = this.grid;
57552 var size = this.adjustForComponents(width, height);
57553 grid.getGridEl().setSize(size.width, size.height);
57558 beforeSlide : function(){
57559 this.grid.getView().scroller.clip();
57562 afterSlide : function(){
57563 this.grid.getView().scroller.unclip();
57566 destroy : function(){
57567 this.grid.destroy();
57569 Roo.GridPanel.superclass.destroy.call(this);
57575 * @class Roo.NestedLayoutPanel
57576 * @extends Roo.ContentPanel
57577 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57578 * @cfg Roo.BorderLayout} layout [required] The layout for this panel
57582 * Create a new NestedLayoutPanel.
57585 * @param {Roo.BorderLayout} layout [required] The layout for this panel
57586 * @param {String/Object} config A string to set only the title or a config object
57588 Roo.NestedLayoutPanel = function(layout, config)
57590 // construct with only one argument..
57591 /* FIXME - implement nicer consturctors
57592 if (layout.layout) {
57594 layout = config.layout;
57595 delete config.layout;
57597 if (layout.xtype && !layout.getEl) {
57598 // then layout needs constructing..
57599 layout = Roo.factory(layout, Roo);
57604 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57606 layout.monitorWindowResize = false; // turn off autosizing
57607 this.layout = layout;
57608 this.layout.getEl().addClass("x-layout-nested-layout");
57615 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57617 setSize : function(width, height){
57618 if(!this.ignoreResize(width, height)){
57619 var size = this.adjustForComponents(width, height);
57620 var el = this.layout.getEl();
57621 el.setSize(size.width, size.height);
57622 var touch = el.dom.offsetWidth;
57623 this.layout.layout();
57624 // ie requires a double layout on the first pass
57625 if(Roo.isIE && !this.initialized){
57626 this.initialized = true;
57627 this.layout.layout();
57632 // activate all subpanels if not currently active..
57634 setActiveState : function(active){
57635 this.active = active;
57637 this.fireEvent("deactivate", this);
57641 this.fireEvent("activate", this);
57642 // not sure if this should happen before or after..
57643 if (!this.layout) {
57644 return; // should not happen..
57647 for (var r in this.layout.regions) {
57648 reg = this.layout.getRegion(r);
57649 if (reg.getActivePanel()) {
57650 //reg.showPanel(reg.getActivePanel()); // force it to activate..
57651 reg.setActivePanel(reg.getActivePanel());
57654 if (!reg.panels.length) {
57657 reg.showPanel(reg.getPanel(0));
57666 * Returns the nested BorderLayout for this panel
57667 * @return {Roo.BorderLayout}
57669 getLayout : function(){
57670 return this.layout;
57674 * Adds a xtype elements to the layout of the nested panel
57678 xtype : 'ContentPanel',
57685 xtype : 'NestedLayoutPanel',
57691 items : [ ... list of content panels or nested layout panels.. ]
57695 * @param {Object} cfg Xtype definition of item to add.
57697 addxtype : function(cfg) {
57698 return this.layout.addxtype(cfg);
57703 Roo.ScrollPanel = function(el, config, content){
57704 config = config || {};
57705 config.fitToFrame = true;
57706 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57708 this.el.dom.style.overflow = "hidden";
57709 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57710 this.el.removeClass("x-layout-inactive-content");
57711 this.el.on("mousewheel", this.onWheel, this);
57713 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
57714 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
57715 up.unselectable(); down.unselectable();
57716 up.on("click", this.scrollUp, this);
57717 down.on("click", this.scrollDown, this);
57718 up.addClassOnOver("x-scroller-btn-over");
57719 down.addClassOnOver("x-scroller-btn-over");
57720 up.addClassOnClick("x-scroller-btn-click");
57721 down.addClassOnClick("x-scroller-btn-click");
57722 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57724 this.resizeEl = this.el;
57725 this.el = wrap; this.up = up; this.down = down;
57728 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57730 wheelIncrement : 5,
57731 scrollUp : function(){
57732 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57735 scrollDown : function(){
57736 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57739 afterScroll : function(){
57740 var el = this.resizeEl;
57741 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57742 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57743 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57746 setSize : function(){
57747 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57748 this.afterScroll();
57751 onWheel : function(e){
57752 var d = e.getWheelDelta();
57753 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57754 this.afterScroll();
57758 setContent : function(content, loadScripts){
57759 this.resizeEl.update(content, loadScripts);
57767 * @class Roo.TreePanel
57768 * @extends Roo.ContentPanel
57769 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57770 * Treepanel component
57773 * Create a new TreePanel. - defaults to fit/scoll contents.
57774 * @param {String/Object} config A string to set only the panel's title, or a config object
57776 Roo.TreePanel = function(config){
57777 var el = config.el;
57778 var tree = config.tree;
57779 delete config.tree;
57780 delete config.el; // hopefull!
57782 // wrapper for IE7 strict & safari scroll issue
57784 var treeEl = el.createChild();
57785 config.resizeEl = treeEl;
57789 Roo.TreePanel.superclass.constructor.call(this, el, config);
57792 this.tree = new Roo.tree.TreePanel(treeEl , tree);
57793 //console.log(tree);
57794 this.on('activate', function()
57796 if (this.tree.rendered) {
57799 //console.log('render tree');
57800 this.tree.render();
57802 // this should not be needed.. - it's actually the 'el' that resizes?
57803 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57805 //this.on('resize', function (cp, w, h) {
57806 // this.tree.innerCt.setWidth(w);
57807 // this.tree.innerCt.setHeight(h);
57808 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
57815 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
57819 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57837 * Ext JS Library 1.1.1
57838 * Copyright(c) 2006-2007, Ext JS, LLC.
57840 * Originally Released Under LGPL - original licence link has changed is not relivant.
57843 * <script type="text/javascript">
57848 * @class Roo.ReaderLayout
57849 * @extends Roo.BorderLayout
57850 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
57851 * center region containing two nested regions (a top one for a list view and one for item preview below),
57852 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57853 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57854 * expedites the setup of the overall layout and regions for this common application style.
57857 var reader = new Roo.ReaderLayout();
57858 var CP = Roo.ContentPanel; // shortcut for adding
57860 reader.beginUpdate();
57861 reader.add("north", new CP("north", "North"));
57862 reader.add("west", new CP("west", {title: "West"}));
57863 reader.add("east", new CP("east", {title: "East"}));
57865 reader.regions.listView.add(new CP("listView", "List"));
57866 reader.regions.preview.add(new CP("preview", "Preview"));
57867 reader.endUpdate();
57870 * Create a new ReaderLayout
57871 * @param {Object} config Configuration options
57872 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57873 * document.body if omitted)
57875 Roo.ReaderLayout = function(config, renderTo){
57876 var c = config || {size:{}};
57877 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57878 north: c.north !== false ? Roo.apply({
57882 }, c.north) : false,
57883 west: c.west !== false ? Roo.apply({
57891 margins:{left:5,right:0,bottom:5,top:5},
57892 cmargins:{left:5,right:5,bottom:5,top:5}
57893 }, c.west) : false,
57894 east: c.east !== false ? Roo.apply({
57902 margins:{left:0,right:5,bottom:5,top:5},
57903 cmargins:{left:5,right:5,bottom:5,top:5}
57904 }, c.east) : false,
57905 center: Roo.apply({
57906 tabPosition: 'top',
57910 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57914 this.el.addClass('x-reader');
57916 this.beginUpdate();
57918 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57919 south: c.preview !== false ? Roo.apply({
57926 cmargins:{top:5,left:0, right:0, bottom:0}
57927 }, c.preview) : false,
57928 center: Roo.apply({
57934 this.add('center', new Roo.NestedLayoutPanel(inner,
57935 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57939 this.regions.preview = inner.getRegion('south');
57940 this.regions.listView = inner.getRegion('center');
57943 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57945 * Ext JS Library 1.1.1
57946 * Copyright(c) 2006-2007, Ext JS, LLC.
57948 * Originally Released Under LGPL - original licence link has changed is not relivant.
57951 * <script type="text/javascript">
57955 * @class Roo.grid.Grid
57956 * @extends Roo.util.Observable
57957 * This class represents the primary interface of a component based grid control.
57958 * <br><br>Usage:<pre><code>
57959 var grid = new Roo.grid.Grid("my-container-id", {
57962 selModel: mySelectionModel,
57963 autoSizeColumns: true,
57964 monitorWindowResize: false,
57965 trackMouseOver: true
57970 * <b>Common Problems:</b><br/>
57971 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57972 * element will correct this<br/>
57973 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57974 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57975 * are unpredictable.<br/>
57976 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57977 * grid to calculate dimensions/offsets.<br/>
57979 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57980 * The container MUST have some type of size defined for the grid to fill. The container will be
57981 * automatically set to position relative if it isn't already.
57982 * @param {Object} config A config object that sets properties on this grid.
57984 Roo.grid.Grid = function(container, config){
57985 // initialize the container
57986 this.container = Roo.get(container);
57987 this.container.update("");
57988 this.container.setStyle("overflow", "hidden");
57989 this.container.addClass('x-grid-container');
57991 this.id = this.container.id;
57993 Roo.apply(this, config);
57994 // check and correct shorthanded configs
57996 this.dataSource = this.ds;
58000 this.colModel = this.cm;
58004 this.selModel = this.sm;
58008 if (this.selModel) {
58009 this.selModel = Roo.factory(this.selModel, Roo.grid);
58010 this.sm = this.selModel;
58011 this.sm.xmodule = this.xmodule || false;
58013 if (typeof(this.colModel.config) == 'undefined') {
58014 this.colModel = new Roo.grid.ColumnModel(this.colModel);
58015 this.cm = this.colModel;
58016 this.cm.xmodule = this.xmodule || false;
58018 if (this.dataSource) {
58019 this.dataSource= Roo.factory(this.dataSource, Roo.data);
58020 this.ds = this.dataSource;
58021 this.ds.xmodule = this.xmodule || false;
58028 this.container.setWidth(this.width);
58032 this.container.setHeight(this.height);
58039 * The raw click event for the entire grid.
58040 * @param {Roo.EventObject} e
58045 * The raw dblclick event for the entire grid.
58046 * @param {Roo.EventObject} e
58050 * @event contextmenu
58051 * The raw contextmenu event for the entire grid.
58052 * @param {Roo.EventObject} e
58054 "contextmenu" : true,
58057 * The raw mousedown event for the entire grid.
58058 * @param {Roo.EventObject} e
58060 "mousedown" : true,
58063 * The raw mouseup event for the entire grid.
58064 * @param {Roo.EventObject} e
58069 * The raw mouseover event for the entire grid.
58070 * @param {Roo.EventObject} e
58072 "mouseover" : true,
58075 * The raw mouseout event for the entire grid.
58076 * @param {Roo.EventObject} e
58081 * The raw keypress event for the entire grid.
58082 * @param {Roo.EventObject} e
58087 * The raw keydown event for the entire grid.
58088 * @param {Roo.EventObject} e
58096 * Fires when a cell is clicked
58097 * @param {Grid} this
58098 * @param {Number} rowIndex
58099 * @param {Number} columnIndex
58100 * @param {Roo.EventObject} e
58102 "cellclick" : true,
58104 * @event celldblclick
58105 * Fires when a cell is double clicked
58106 * @param {Grid} this
58107 * @param {Number} rowIndex
58108 * @param {Number} columnIndex
58109 * @param {Roo.EventObject} e
58111 "celldblclick" : true,
58114 * Fires when a row is clicked
58115 * @param {Grid} this
58116 * @param {Number} rowIndex
58117 * @param {Roo.EventObject} e
58121 * @event rowdblclick
58122 * Fires when a row is double clicked
58123 * @param {Grid} this
58124 * @param {Number} rowIndex
58125 * @param {Roo.EventObject} e
58127 "rowdblclick" : true,
58129 * @event headerclick
58130 * Fires when a header is clicked
58131 * @param {Grid} this
58132 * @param {Number} columnIndex
58133 * @param {Roo.EventObject} e
58135 "headerclick" : true,
58137 * @event headerdblclick
58138 * Fires when a header cell is double clicked
58139 * @param {Grid} this
58140 * @param {Number} columnIndex
58141 * @param {Roo.EventObject} e
58143 "headerdblclick" : true,
58145 * @event rowcontextmenu
58146 * Fires when a row is right clicked
58147 * @param {Grid} this
58148 * @param {Number} rowIndex
58149 * @param {Roo.EventObject} e
58151 "rowcontextmenu" : true,
58153 * @event cellcontextmenu
58154 * Fires when a cell is right clicked
58155 * @param {Grid} this
58156 * @param {Number} rowIndex
58157 * @param {Number} cellIndex
58158 * @param {Roo.EventObject} e
58160 "cellcontextmenu" : true,
58162 * @event headercontextmenu
58163 * Fires when a header is right clicked
58164 * @param {Grid} this
58165 * @param {Number} columnIndex
58166 * @param {Roo.EventObject} e
58168 "headercontextmenu" : true,
58170 * @event bodyscroll
58171 * Fires when the body element is scrolled
58172 * @param {Number} scrollLeft
58173 * @param {Number} scrollTop
58175 "bodyscroll" : true,
58177 * @event columnresize
58178 * Fires when the user resizes a column
58179 * @param {Number} columnIndex
58180 * @param {Number} newSize
58182 "columnresize" : true,
58184 * @event columnmove
58185 * Fires when the user moves a column
58186 * @param {Number} oldIndex
58187 * @param {Number} newIndex
58189 "columnmove" : true,
58192 * Fires when row(s) start being dragged
58193 * @param {Grid} this
58194 * @param {Roo.GridDD} dd The drag drop object
58195 * @param {event} e The raw browser event
58197 "startdrag" : true,
58200 * Fires when a drag operation is complete
58201 * @param {Grid} this
58202 * @param {Roo.GridDD} dd The drag drop object
58203 * @param {event} e The raw browser event
58208 * Fires when dragged row(s) are dropped on a valid DD target
58209 * @param {Grid} this
58210 * @param {Roo.GridDD} dd The drag drop object
58211 * @param {String} targetId The target drag drop object
58212 * @param {event} e The raw browser event
58217 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58218 * @param {Grid} this
58219 * @param {Roo.GridDD} dd The drag drop object
58220 * @param {String} targetId The target drag drop object
58221 * @param {event} e The raw browser event
58226 * Fires when the dragged row(s) first cross another DD target while being dragged
58227 * @param {Grid} this
58228 * @param {Roo.GridDD} dd The drag drop object
58229 * @param {String} targetId The target drag drop object
58230 * @param {event} e The raw browser event
58232 "dragenter" : true,
58235 * Fires when the dragged row(s) leave another DD target while being dragged
58236 * @param {Grid} this
58237 * @param {Roo.GridDD} dd The drag drop object
58238 * @param {String} targetId The target drag drop object
58239 * @param {event} e The raw browser event
58244 * Fires when a row is rendered, so you can change add a style to it.
58245 * @param {GridView} gridview The grid view
58246 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
58252 * Fires when the grid is rendered
58253 * @param {Grid} grid
58258 Roo.grid.Grid.superclass.constructor.call(this);
58260 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58263 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58266 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
58269 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58272 * @cfg {Roo.grid.Store} ds The data store for the grid
58275 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58278 * @cfg {String} ddGroup - drag drop group.
58281 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58285 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58287 minColumnWidth : 25,
58290 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58291 * <b>on initial render.</b> It is more efficient to explicitly size the columns
58292 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
58294 autoSizeColumns : false,
58297 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58299 autoSizeHeaders : true,
58302 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58304 monitorWindowResize : true,
58307 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58308 * rows measured to get a columns size. Default is 0 (all rows).
58310 maxRowsToMeasure : 0,
58313 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58315 trackMouseOver : true,
58318 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
58321 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
58325 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58327 enableDragDrop : false,
58330 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58332 enableColumnMove : true,
58335 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58337 enableColumnHide : true,
58340 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58342 enableRowHeightSync : false,
58345 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
58350 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58352 autoHeight : false,
58355 * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
58357 autoExpandColumn : false,
58360 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58363 autoExpandMin : 50,
58366 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58368 autoExpandMax : 1000,
58371 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58376 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58380 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58384 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58386 sortColMenu : false,
58392 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58393 * of a fixed width. Default is false.
58396 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58401 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58402 * %0 is replaced with the number of selected rows.
58404 ddText : "{0} selected row{1}",
58408 * Called once after all setup has been completed and the grid is ready to be rendered.
58409 * @return {Roo.grid.Grid} this
58411 render : function()
58413 var c = this.container;
58414 // try to detect autoHeight/width mode
58415 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58416 this.autoHeight = true;
58418 var view = this.getView();
58421 c.on("click", this.onClick, this);
58422 c.on("dblclick", this.onDblClick, this);
58423 c.on("contextmenu", this.onContextMenu, this);
58424 c.on("keydown", this.onKeyDown, this);
58426 c.on("touchstart", this.onTouchStart, this);
58429 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58431 this.getSelectionModel().init(this);
58436 this.loadMask = new Roo.LoadMask(this.container,
58437 Roo.apply({store:this.dataSource}, this.loadMask));
58441 if (this.toolbar && this.toolbar.xtype) {
58442 this.toolbar.container = this.getView().getHeaderPanel(true);
58443 this.toolbar = new Roo.Toolbar(this.toolbar);
58445 if (this.footer && this.footer.xtype) {
58446 this.footer.dataSource = this.getDataSource();
58447 this.footer.container = this.getView().getFooterPanel(true);
58448 this.footer = Roo.factory(this.footer, Roo);
58450 if (this.dropTarget && this.dropTarget.xtype) {
58451 delete this.dropTarget.xtype;
58452 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58456 this.rendered = true;
58457 this.fireEvent('render', this);
58462 * Reconfigures the grid to use a different Store and Column Model.
58463 * The View will be bound to the new objects and refreshed.
58464 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58465 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58467 reconfigure : function(dataSource, colModel){
58469 this.loadMask.destroy();
58470 this.loadMask = new Roo.LoadMask(this.container,
58471 Roo.apply({store:dataSource}, this.loadMask));
58473 this.view.bind(dataSource, colModel);
58474 this.dataSource = dataSource;
58475 this.colModel = colModel;
58476 this.view.refresh(true);
58480 * Add's a column, default at the end..
58482 * @param {int} position to add (default end)
58483 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
58485 addColumns : function(pos, ar)
58488 for (var i =0;i< ar.length;i++) {
58490 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58491 this.cm.lookup[cfg.id] = cfg;
58495 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58496 pos = this.cm.config.length; //this.cm.config.push(cfg);
58498 pos = Math.max(0,pos);
58501 this.cm.config.splice.apply(this.cm.config, ar);
58505 this.view.generateRules(this.cm);
58506 this.view.refresh(true);
58514 onKeyDown : function(e){
58515 this.fireEvent("keydown", e);
58519 * Destroy this grid.
58520 * @param {Boolean} removeEl True to remove the element
58522 destroy : function(removeEl, keepListeners){
58524 this.loadMask.destroy();
58526 var c = this.container;
58527 c.removeAllListeners();
58528 this.view.destroy();
58529 this.colModel.purgeListeners();
58530 if(!keepListeners){
58531 this.purgeListeners();
58534 if(removeEl === true){
58540 processEvent : function(name, e){
58541 // does this fire select???
58542 //Roo.log('grid:processEvent ' + name);
58544 if (name != 'touchstart' ) {
58545 this.fireEvent(name, e);
58548 var t = e.getTarget();
58550 var header = v.findHeaderIndex(t);
58551 if(header !== false){
58552 var ename = name == 'touchstart' ? 'click' : name;
58554 this.fireEvent("header" + ename, this, header, e);
58556 var row = v.findRowIndex(t);
58557 var cell = v.findCellIndex(t);
58558 if (name == 'touchstart') {
58559 // first touch is always a click.
58560 // hopefull this happens after selection is updated.?
58563 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58564 var cs = this.selModel.getSelectedCell();
58565 if (row == cs[0] && cell == cs[1]){
58569 if (typeof(this.selModel.getSelections) != 'undefined') {
58570 var cs = this.selModel.getSelections();
58571 var ds = this.dataSource;
58572 if (cs.length == 1 && ds.getAt(row) == cs[0]){
58583 this.fireEvent("row" + name, this, row, e);
58584 if(cell !== false){
58585 this.fireEvent("cell" + name, this, row, cell, e);
58592 onClick : function(e){
58593 this.processEvent("click", e);
58596 onTouchStart : function(e){
58597 this.processEvent("touchstart", e);
58601 onContextMenu : function(e, t){
58602 this.processEvent("contextmenu", e);
58606 onDblClick : function(e){
58607 this.processEvent("dblclick", e);
58611 walkCells : function(row, col, step, fn, scope){
58612 var cm = this.colModel, clen = cm.getColumnCount();
58613 var ds = this.dataSource, rlen = ds.getCount(), first = true;
58625 if(fn.call(scope || this, row, col, cm) === true){
58643 if(fn.call(scope || this, row, col, cm) === true){
58655 getSelections : function(){
58656 return this.selModel.getSelections();
58660 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58661 * but if manual update is required this method will initiate it.
58663 autoSize : function(){
58665 this.view.layout();
58666 if(this.view.adjustForScroll){
58667 this.view.adjustForScroll();
58673 * Returns the grid's underlying element.
58674 * @return {Element} The element
58676 getGridEl : function(){
58677 return this.container;
58680 // private for compatibility, overridden by editor grid
58681 stopEditing : function(){},
58684 * Returns the grid's SelectionModel.
58685 * @return {SelectionModel}
58687 getSelectionModel : function(){
58688 if(!this.selModel){
58689 this.selModel = new Roo.grid.RowSelectionModel();
58691 return this.selModel;
58695 * Returns the grid's DataSource.
58696 * @return {DataSource}
58698 getDataSource : function(){
58699 return this.dataSource;
58703 * Returns the grid's ColumnModel.
58704 * @return {ColumnModel}
58706 getColumnModel : function(){
58707 return this.colModel;
58711 * Returns the grid's GridView object.
58712 * @return {GridView}
58714 getView : function(){
58716 this.view = new Roo.grid.GridView(this.viewConfig);
58717 this.relayEvents(this.view, [
58718 "beforerowremoved", "beforerowsinserted",
58719 "beforerefresh", "rowremoved",
58720 "rowsinserted", "rowupdated" ,"refresh"
58726 * Called to get grid's drag proxy text, by default returns this.ddText.
58727 * Override this to put something different in the dragged text.
58730 getDragDropText : function(){
58731 var count = this.selModel.getCount();
58732 return String.format(this.ddText, count, count == 1 ? '' : 's');
58737 * Ext JS Library 1.1.1
58738 * Copyright(c) 2006-2007, Ext JS, LLC.
58740 * Originally Released Under LGPL - original licence link has changed is not relivant.
58743 * <script type="text/javascript">
58746 * @class Roo.grid.AbstractGridView
58747 * @extends Roo.util.Observable
58749 * Abstract base class for grid Views
58752 Roo.grid.AbstractGridView = function(){
58756 "beforerowremoved" : true,
58757 "beforerowsinserted" : true,
58758 "beforerefresh" : true,
58759 "rowremoved" : true,
58760 "rowsinserted" : true,
58761 "rowupdated" : true,
58764 Roo.grid.AbstractGridView.superclass.constructor.call(this);
58767 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58768 rowClass : "x-grid-row",
58769 cellClass : "x-grid-cell",
58770 tdClass : "x-grid-td",
58771 hdClass : "x-grid-hd",
58772 splitClass : "x-grid-hd-split",
58774 init: function(grid){
58776 var cid = this.grid.getGridEl().id;
58777 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58778 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58779 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58780 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58783 getColumnRenderers : function(){
58784 var renderers = [];
58785 var cm = this.grid.colModel;
58786 var colCount = cm.getColumnCount();
58787 for(var i = 0; i < colCount; i++){
58788 renderers[i] = cm.getRenderer(i);
58793 getColumnIds : function(){
58795 var cm = this.grid.colModel;
58796 var colCount = cm.getColumnCount();
58797 for(var i = 0; i < colCount; i++){
58798 ids[i] = cm.getColumnId(i);
58803 getDataIndexes : function(){
58804 if(!this.indexMap){
58805 this.indexMap = this.buildIndexMap();
58807 return this.indexMap.colToData;
58810 getColumnIndexByDataIndex : function(dataIndex){
58811 if(!this.indexMap){
58812 this.indexMap = this.buildIndexMap();
58814 return this.indexMap.dataToCol[dataIndex];
58818 * Set a css style for a column dynamically.
58819 * @param {Number} colIndex The index of the column
58820 * @param {String} name The css property name
58821 * @param {String} value The css value
58823 setCSSStyle : function(colIndex, name, value){
58824 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58825 Roo.util.CSS.updateRule(selector, name, value);
58828 generateRules : function(cm){
58829 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58830 Roo.util.CSS.removeStyleSheet(rulesId);
58831 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58832 var cid = cm.getColumnId(i);
58833 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58834 this.tdSelector, cid, " {\n}\n",
58835 this.hdSelector, cid, " {\n}\n",
58836 this.splitSelector, cid, " {\n}\n");
58838 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58842 * Ext JS Library 1.1.1
58843 * Copyright(c) 2006-2007, Ext JS, LLC.
58845 * Originally Released Under LGPL - original licence link has changed is not relivant.
58848 * <script type="text/javascript">
58852 // This is a support class used internally by the Grid components
58853 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58855 this.view = grid.getView();
58856 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58857 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58859 this.setHandleElId(Roo.id(hd));
58860 this.setOuterHandleElId(Roo.id(hd2));
58862 this.scroll = false;
58864 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58866 getDragData : function(e){
58867 var t = Roo.lib.Event.getTarget(e);
58868 var h = this.view.findHeaderCell(t);
58870 return {ddel: h.firstChild, header:h};
58875 onInitDrag : function(e){
58876 this.view.headersDisabled = true;
58877 var clone = this.dragData.ddel.cloneNode(true);
58878 clone.id = Roo.id();
58879 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58880 this.proxy.update(clone);
58884 afterValidDrop : function(){
58886 setTimeout(function(){
58887 v.headersDisabled = false;
58891 afterInvalidDrop : function(){
58893 setTimeout(function(){
58894 v.headersDisabled = false;
58900 * Ext JS Library 1.1.1
58901 * Copyright(c) 2006-2007, Ext JS, LLC.
58903 * Originally Released Under LGPL - original licence link has changed is not relivant.
58906 * <script type="text/javascript">
58909 // This is a support class used internally by the Grid components
58910 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58912 this.view = grid.getView();
58913 // split the proxies so they don't interfere with mouse events
58914 this.proxyTop = Roo.DomHelper.append(document.body, {
58915 cls:"col-move-top", html:" "
58917 this.proxyBottom = Roo.DomHelper.append(document.body, {
58918 cls:"col-move-bottom", html:" "
58920 this.proxyTop.hide = this.proxyBottom.hide = function(){
58921 this.setLeftTop(-100,-100);
58922 this.setStyle("visibility", "hidden");
58924 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58925 // temporarily disabled
58926 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58927 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58929 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58930 proxyOffsets : [-4, -9],
58931 fly: Roo.Element.fly,
58933 getTargetFromEvent : function(e){
58934 var t = Roo.lib.Event.getTarget(e);
58935 var cindex = this.view.findCellIndex(t);
58936 if(cindex !== false){
58937 return this.view.getHeaderCell(cindex);
58942 nextVisible : function(h){
58943 var v = this.view, cm = this.grid.colModel;
58946 if(!cm.isHidden(v.getCellIndex(h))){
58954 prevVisible : function(h){
58955 var v = this.view, cm = this.grid.colModel;
58958 if(!cm.isHidden(v.getCellIndex(h))){
58966 positionIndicator : function(h, n, e){
58967 var x = Roo.lib.Event.getPageX(e);
58968 var r = Roo.lib.Dom.getRegion(n.firstChild);
58969 var px, pt, py = r.top + this.proxyOffsets[1];
58970 if((r.right - x) <= (r.right-r.left)/2){
58971 px = r.right+this.view.borderWidth;
58977 var oldIndex = this.view.getCellIndex(h);
58978 var newIndex = this.view.getCellIndex(n);
58980 if(this.grid.colModel.isFixed(newIndex)){
58984 var locked = this.grid.colModel.isLocked(newIndex);
58989 if(oldIndex < newIndex){
58992 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58995 px += this.proxyOffsets[0];
58996 this.proxyTop.setLeftTop(px, py);
58997 this.proxyTop.show();
58998 if(!this.bottomOffset){
58999 this.bottomOffset = this.view.mainHd.getHeight();
59001 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59002 this.proxyBottom.show();
59006 onNodeEnter : function(n, dd, e, data){
59007 if(data.header != n){
59008 this.positionIndicator(data.header, n, e);
59012 onNodeOver : function(n, dd, e, data){
59013 var result = false;
59014 if(data.header != n){
59015 result = this.positionIndicator(data.header, n, e);
59018 this.proxyTop.hide();
59019 this.proxyBottom.hide();
59021 return result ? this.dropAllowed : this.dropNotAllowed;
59024 onNodeOut : function(n, dd, e, data){
59025 this.proxyTop.hide();
59026 this.proxyBottom.hide();
59029 onNodeDrop : function(n, dd, e, data){
59030 var h = data.header;
59032 var cm = this.grid.colModel;
59033 var x = Roo.lib.Event.getPageX(e);
59034 var r = Roo.lib.Dom.getRegion(n.firstChild);
59035 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59036 var oldIndex = this.view.getCellIndex(h);
59037 var newIndex = this.view.getCellIndex(n);
59038 var locked = cm.isLocked(newIndex);
59042 if(oldIndex < newIndex){
59045 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59048 cm.setLocked(oldIndex, locked, true);
59049 cm.moveColumn(oldIndex, newIndex);
59050 this.grid.fireEvent("columnmove", oldIndex, newIndex);
59058 * Ext JS Library 1.1.1
59059 * Copyright(c) 2006-2007, Ext JS, LLC.
59061 * Originally Released Under LGPL - original licence link has changed is not relivant.
59064 * <script type="text/javascript">
59068 * @class Roo.grid.GridView
59069 * @extends Roo.util.Observable
59072 * @param {Object} config
59074 Roo.grid.GridView = function(config){
59075 Roo.grid.GridView.superclass.constructor.call(this);
59078 Roo.apply(this, config);
59081 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59083 unselectable : 'unselectable="on"',
59084 unselectableCls : 'x-unselectable',
59087 rowClass : "x-grid-row",
59089 cellClass : "x-grid-col",
59091 tdClass : "x-grid-td",
59093 hdClass : "x-grid-hd",
59095 splitClass : "x-grid-split",
59097 sortClasses : ["sort-asc", "sort-desc"],
59099 enableMoveAnim : false,
59103 dh : Roo.DomHelper,
59105 fly : Roo.Element.fly,
59107 css : Roo.util.CSS,
59113 scrollIncrement : 22,
59115 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59117 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59119 bind : function(ds, cm){
59121 this.ds.un("load", this.onLoad, this);
59122 this.ds.un("datachanged", this.onDataChange, this);
59123 this.ds.un("add", this.onAdd, this);
59124 this.ds.un("remove", this.onRemove, this);
59125 this.ds.un("update", this.onUpdate, this);
59126 this.ds.un("clear", this.onClear, this);
59129 ds.on("load", this.onLoad, this);
59130 ds.on("datachanged", this.onDataChange, this);
59131 ds.on("add", this.onAdd, this);
59132 ds.on("remove", this.onRemove, this);
59133 ds.on("update", this.onUpdate, this);
59134 ds.on("clear", this.onClear, this);
59139 this.cm.un("widthchange", this.onColWidthChange, this);
59140 this.cm.un("headerchange", this.onHeaderChange, this);
59141 this.cm.un("hiddenchange", this.onHiddenChange, this);
59142 this.cm.un("columnmoved", this.onColumnMove, this);
59143 this.cm.un("columnlockchange", this.onColumnLock, this);
59146 this.generateRules(cm);
59147 cm.on("widthchange", this.onColWidthChange, this);
59148 cm.on("headerchange", this.onHeaderChange, this);
59149 cm.on("hiddenchange", this.onHiddenChange, this);
59150 cm.on("columnmoved", this.onColumnMove, this);
59151 cm.on("columnlockchange", this.onColumnLock, this);
59156 init: function(grid){
59157 Roo.grid.GridView.superclass.init.call(this, grid);
59159 this.bind(grid.dataSource, grid.colModel);
59161 grid.on("headerclick", this.handleHeaderClick, this);
59163 if(grid.trackMouseOver){
59164 grid.on("mouseover", this.onRowOver, this);
59165 grid.on("mouseout", this.onRowOut, this);
59167 grid.cancelTextSelection = function(){};
59168 this.gridId = grid.id;
59170 var tpls = this.templates || {};
59173 tpls.master = new Roo.Template(
59174 '<div class="x-grid" hidefocus="true">',
59175 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59176 '<div class="x-grid-topbar"></div>',
59177 '<div class="x-grid-scroller"><div></div></div>',
59178 '<div class="x-grid-locked">',
59179 '<div class="x-grid-header">{lockedHeader}</div>',
59180 '<div class="x-grid-body">{lockedBody}</div>',
59182 '<div class="x-grid-viewport">',
59183 '<div class="x-grid-header">{header}</div>',
59184 '<div class="x-grid-body">{body}</div>',
59186 '<div class="x-grid-bottombar"></div>',
59188 '<div class="x-grid-resize-proxy"> </div>',
59191 tpls.master.disableformats = true;
59195 tpls.header = new Roo.Template(
59196 '<table border="0" cellspacing="0" cellpadding="0">',
59197 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59200 tpls.header.disableformats = true;
59202 tpls.header.compile();
59205 tpls.hcell = new Roo.Template(
59206 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59207 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59210 tpls.hcell.disableFormats = true;
59212 tpls.hcell.compile();
59215 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59216 this.unselectableCls + '" ' + this.unselectable +'> </div>');
59217 tpls.hsplit.disableFormats = true;
59219 tpls.hsplit.compile();
59222 tpls.body = new Roo.Template(
59223 '<table border="0" cellspacing="0" cellpadding="0">',
59224 "<tbody>{rows}</tbody>",
59227 tpls.body.disableFormats = true;
59229 tpls.body.compile();
59232 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59233 tpls.row.disableFormats = true;
59235 tpls.row.compile();
59238 tpls.cell = new Roo.Template(
59239 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59240 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59241 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59244 tpls.cell.disableFormats = true;
59246 tpls.cell.compile();
59248 this.templates = tpls;
59251 // remap these for backwards compat
59252 onColWidthChange : function(){
59253 this.updateColumns.apply(this, arguments);
59255 onHeaderChange : function(){
59256 this.updateHeaders.apply(this, arguments);
59258 onHiddenChange : function(){
59259 this.handleHiddenChange.apply(this, arguments);
59261 onColumnMove : function(){
59262 this.handleColumnMove.apply(this, arguments);
59264 onColumnLock : function(){
59265 this.handleLockChange.apply(this, arguments);
59268 onDataChange : function(){
59270 this.updateHeaderSortState();
59273 onClear : function(){
59277 onUpdate : function(ds, record){
59278 this.refreshRow(record);
59281 refreshRow : function(record){
59282 var ds = this.ds, index;
59283 if(typeof record == 'number'){
59285 record = ds.getAt(index);
59287 index = ds.indexOf(record);
59289 this.insertRows(ds, index, index, true);
59290 this.onRemove(ds, record, index+1, true);
59291 this.syncRowHeights(index, index);
59293 this.fireEvent("rowupdated", this, index, record);
59296 onAdd : function(ds, records, index){
59297 this.insertRows(ds, index, index + (records.length-1));
59300 onRemove : function(ds, record, index, isUpdate){
59301 if(isUpdate !== true){
59302 this.fireEvent("beforerowremoved", this, index, record);
59304 var bt = this.getBodyTable(), lt = this.getLockedTable();
59305 if(bt.rows[index]){
59306 bt.firstChild.removeChild(bt.rows[index]);
59308 if(lt.rows[index]){
59309 lt.firstChild.removeChild(lt.rows[index]);
59311 if(isUpdate !== true){
59312 this.stripeRows(index);
59313 this.syncRowHeights(index, index);
59315 this.fireEvent("rowremoved", this, index, record);
59319 onLoad : function(){
59320 this.scrollToTop();
59324 * Scrolls the grid to the top
59326 scrollToTop : function(){
59328 this.scroller.dom.scrollTop = 0;
59334 * Gets a panel in the header of the grid that can be used for toolbars etc.
59335 * After modifying the contents of this panel a call to grid.autoSize() may be
59336 * required to register any changes in size.
59337 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59338 * @return Roo.Element
59340 getHeaderPanel : function(doShow){
59342 this.headerPanel.show();
59344 return this.headerPanel;
59348 * Gets a panel in the footer of the grid that can be used for toolbars etc.
59349 * After modifying the contents of this panel a call to grid.autoSize() may be
59350 * required to register any changes in size.
59351 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59352 * @return Roo.Element
59354 getFooterPanel : function(doShow){
59356 this.footerPanel.show();
59358 return this.footerPanel;
59361 initElements : function(){
59362 var E = Roo.Element;
59363 var el = this.grid.getGridEl().dom.firstChild;
59364 var cs = el.childNodes;
59366 this.el = new E(el);
59368 this.focusEl = new E(el.firstChild);
59369 this.focusEl.swallowEvent("click", true);
59371 this.headerPanel = new E(cs[1]);
59372 this.headerPanel.enableDisplayMode("block");
59374 this.scroller = new E(cs[2]);
59375 this.scrollSizer = new E(this.scroller.dom.firstChild);
59377 this.lockedWrap = new E(cs[3]);
59378 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59379 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59381 this.mainWrap = new E(cs[4]);
59382 this.mainHd = new E(this.mainWrap.dom.firstChild);
59383 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59385 this.footerPanel = new E(cs[5]);
59386 this.footerPanel.enableDisplayMode("block");
59388 this.resizeProxy = new E(cs[6]);
59390 this.headerSelector = String.format(
59391 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59392 this.lockedHd.id, this.mainHd.id
59395 this.splitterSelector = String.format(
59396 '#{0} div.x-grid-split, #{1} div.x-grid-split',
59397 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59400 idToCssName : function(s)
59402 return s.replace(/[^a-z0-9]+/ig, '-');
59405 getHeaderCell : function(index){
59406 return Roo.DomQuery.select(this.headerSelector)[index];
59409 getHeaderCellMeasure : function(index){
59410 return this.getHeaderCell(index).firstChild;
59413 getHeaderCellText : function(index){
59414 return this.getHeaderCell(index).firstChild.firstChild;
59417 getLockedTable : function(){
59418 return this.lockedBody.dom.firstChild;
59421 getBodyTable : function(){
59422 return this.mainBody.dom.firstChild;
59425 getLockedRow : function(index){
59426 return this.getLockedTable().rows[index];
59429 getRow : function(index){
59430 return this.getBodyTable().rows[index];
59433 getRowComposite : function(index){
59435 this.rowEl = new Roo.CompositeElementLite();
59437 var els = [], lrow, mrow;
59438 if(lrow = this.getLockedRow(index)){
59441 if(mrow = this.getRow(index)){
59444 this.rowEl.elements = els;
59448 * Gets the 'td' of the cell
59450 * @param {Integer} rowIndex row to select
59451 * @param {Integer} colIndex column to select
59455 getCell : function(rowIndex, colIndex){
59456 var locked = this.cm.getLockedCount();
59458 if(colIndex < locked){
59459 source = this.lockedBody.dom.firstChild;
59461 source = this.mainBody.dom.firstChild;
59462 colIndex -= locked;
59464 return source.rows[rowIndex].childNodes[colIndex];
59467 getCellText : function(rowIndex, colIndex){
59468 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59471 getCellBox : function(cell){
59472 var b = this.fly(cell).getBox();
59473 if(Roo.isOpera){ // opera fails to report the Y
59474 b.y = cell.offsetTop + this.mainBody.getY();
59479 getCellIndex : function(cell){
59480 var id = String(cell.className).match(this.cellRE);
59482 return parseInt(id[1], 10);
59487 findHeaderIndex : function(n){
59488 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59489 return r ? this.getCellIndex(r) : false;
59492 findHeaderCell : function(n){
59493 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59494 return r ? r : false;
59497 findRowIndex : function(n){
59501 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59502 return r ? r.rowIndex : false;
59505 findCellIndex : function(node){
59506 var stop = this.el.dom;
59507 while(node && node != stop){
59508 if(this.findRE.test(node.className)){
59509 return this.getCellIndex(node);
59511 node = node.parentNode;
59516 getColumnId : function(index){
59517 return this.cm.getColumnId(index);
59520 getSplitters : function()
59522 if(this.splitterSelector){
59523 return Roo.DomQuery.select(this.splitterSelector);
59529 getSplitter : function(index){
59530 return this.getSplitters()[index];
59533 onRowOver : function(e, t){
59535 if((row = this.findRowIndex(t)) !== false){
59536 this.getRowComposite(row).addClass("x-grid-row-over");
59540 onRowOut : function(e, t){
59542 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59543 this.getRowComposite(row).removeClass("x-grid-row-over");
59547 renderHeaders : function(){
59549 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59550 var cb = [], lb = [], sb = [], lsb = [], p = {};
59551 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59552 p.cellId = "x-grid-hd-0-" + i;
59553 p.splitId = "x-grid-csplit-0-" + i;
59554 p.id = cm.getColumnId(i);
59555 p.value = cm.getColumnHeader(i) || "";
59556 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
59557 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59558 if(!cm.isLocked(i)){
59559 cb[cb.length] = ct.apply(p);
59560 sb[sb.length] = st.apply(p);
59562 lb[lb.length] = ct.apply(p);
59563 lsb[lsb.length] = st.apply(p);
59566 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59567 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59570 updateHeaders : function(){
59571 var html = this.renderHeaders();
59572 this.lockedHd.update(html[0]);
59573 this.mainHd.update(html[1]);
59577 * Focuses the specified row.
59578 * @param {Number} row The row index
59580 focusRow : function(row)
59582 //Roo.log('GridView.focusRow');
59583 var x = this.scroller.dom.scrollLeft;
59584 this.focusCell(row, 0, false);
59585 this.scroller.dom.scrollLeft = x;
59589 * Focuses the specified cell.
59590 * @param {Number} row The row index
59591 * @param {Number} col The column index
59592 * @param {Boolean} hscroll false to disable horizontal scrolling
59594 focusCell : function(row, col, hscroll)
59596 //Roo.log('GridView.focusCell');
59597 var el = this.ensureVisible(row, col, hscroll);
59598 this.focusEl.alignTo(el, "tl-tl");
59600 this.focusEl.focus();
59602 this.focusEl.focus.defer(1, this.focusEl);
59607 * Scrolls the specified cell into view
59608 * @param {Number} row The row index
59609 * @param {Number} col The column index
59610 * @param {Boolean} hscroll false to disable horizontal scrolling
59612 ensureVisible : function(row, col, hscroll)
59614 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59615 //return null; //disable for testing.
59616 if(typeof row != "number"){
59617 row = row.rowIndex;
59619 if(row < 0 && row >= this.ds.getCount()){
59622 col = (col !== undefined ? col : 0);
59623 var cm = this.grid.colModel;
59624 while(cm.isHidden(col)){
59628 var el = this.getCell(row, col);
59632 var c = this.scroller.dom;
59634 var ctop = parseInt(el.offsetTop, 10);
59635 var cleft = parseInt(el.offsetLeft, 10);
59636 var cbot = ctop + el.offsetHeight;
59637 var cright = cleft + el.offsetWidth;
59639 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59640 var stop = parseInt(c.scrollTop, 10);
59641 var sleft = parseInt(c.scrollLeft, 10);
59642 var sbot = stop + ch;
59643 var sright = sleft + c.clientWidth;
59645 Roo.log('GridView.ensureVisible:' +
59647 ' c.clientHeight:' + c.clientHeight +
59648 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59656 c.scrollTop = ctop;
59657 //Roo.log("set scrolltop to ctop DISABLE?");
59658 }else if(cbot > sbot){
59659 //Roo.log("set scrolltop to cbot-ch");
59660 c.scrollTop = cbot-ch;
59663 if(hscroll !== false){
59665 c.scrollLeft = cleft;
59666 }else if(cright > sright){
59667 c.scrollLeft = cright-c.clientWidth;
59674 updateColumns : function(){
59675 this.grid.stopEditing();
59676 var cm = this.grid.colModel, colIds = this.getColumnIds();
59677 //var totalWidth = cm.getTotalWidth();
59679 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59680 //if(cm.isHidden(i)) continue;
59681 var w = cm.getColumnWidth(i);
59682 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59683 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59685 this.updateSplitters();
59688 generateRules : function(cm){
59689 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59690 Roo.util.CSS.removeStyleSheet(rulesId);
59691 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59692 var cid = cm.getColumnId(i);
59694 if(cm.config[i].align){
59695 align = 'text-align:'+cm.config[i].align+';';
59698 if(cm.isHidden(i)){
59699 hidden = 'display:none;';
59701 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59703 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59704 this.hdSelector, cid, " {\n", align, width, "}\n",
59705 this.tdSelector, cid, " {\n",hidden,"\n}\n",
59706 this.splitSelector, cid, " {\n", hidden , "\n}\n");
59708 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59711 updateSplitters : function(){
59712 var cm = this.cm, s = this.getSplitters();
59713 if(s){ // splitters not created yet
59714 var pos = 0, locked = true;
59715 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59716 if(cm.isHidden(i)) {
59719 var w = cm.getColumnWidth(i); // make sure it's a number
59720 if(!cm.isLocked(i) && locked){
59725 s[i].style.left = (pos-this.splitOffset) + "px";
59730 handleHiddenChange : function(colModel, colIndex, hidden){
59732 this.hideColumn(colIndex);
59734 this.unhideColumn(colIndex);
59738 hideColumn : function(colIndex){
59739 var cid = this.getColumnId(colIndex);
59740 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59741 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59743 this.updateHeaders();
59745 this.updateSplitters();
59749 unhideColumn : function(colIndex){
59750 var cid = this.getColumnId(colIndex);
59751 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59752 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59755 this.updateHeaders();
59757 this.updateSplitters();
59761 insertRows : function(dm, firstRow, lastRow, isUpdate){
59762 if(firstRow == 0 && lastRow == dm.getCount()-1){
59766 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59768 var s = this.getScrollState();
59769 var markup = this.renderRows(firstRow, lastRow);
59770 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59771 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59772 this.restoreScroll(s);
59774 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59775 this.syncRowHeights(firstRow, lastRow);
59776 this.stripeRows(firstRow);
59782 bufferRows : function(markup, target, index){
59783 var before = null, trows = target.rows, tbody = target.tBodies[0];
59784 if(index < trows.length){
59785 before = trows[index];
59787 var b = document.createElement("div");
59788 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59789 var rows = b.firstChild.rows;
59790 for(var i = 0, len = rows.length; i < len; i++){
59792 tbody.insertBefore(rows[0], before);
59794 tbody.appendChild(rows[0]);
59801 deleteRows : function(dm, firstRow, lastRow){
59802 if(dm.getRowCount()<1){
59803 this.fireEvent("beforerefresh", this);
59804 this.mainBody.update("");
59805 this.lockedBody.update("");
59806 this.fireEvent("refresh", this);
59808 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59809 var bt = this.getBodyTable();
59810 var tbody = bt.firstChild;
59811 var rows = bt.rows;
59812 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59813 tbody.removeChild(rows[firstRow]);
59815 this.stripeRows(firstRow);
59816 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59820 updateRows : function(dataSource, firstRow, lastRow){
59821 var s = this.getScrollState();
59823 this.restoreScroll(s);
59826 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59830 this.updateHeaderSortState();
59833 getScrollState : function(){
59835 var sb = this.scroller.dom;
59836 return {left: sb.scrollLeft, top: sb.scrollTop};
59839 stripeRows : function(startRow){
59840 if(!this.grid.stripeRows || this.ds.getCount() < 1){
59843 startRow = startRow || 0;
59844 var rows = this.getBodyTable().rows;
59845 var lrows = this.getLockedTable().rows;
59846 var cls = ' x-grid-row-alt ';
59847 for(var i = startRow, len = rows.length; i < len; i++){
59848 var row = rows[i], lrow = lrows[i];
59849 var isAlt = ((i+1) % 2 == 0);
59850 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59851 if(isAlt == hasAlt){
59855 row.className += " x-grid-row-alt";
59857 row.className = row.className.replace("x-grid-row-alt", "");
59860 lrow.className = row.className;
59865 restoreScroll : function(state){
59866 //Roo.log('GridView.restoreScroll');
59867 var sb = this.scroller.dom;
59868 sb.scrollLeft = state.left;
59869 sb.scrollTop = state.top;
59873 syncScroll : function(){
59874 //Roo.log('GridView.syncScroll');
59875 var sb = this.scroller.dom;
59876 var sh = this.mainHd.dom;
59877 var bs = this.mainBody.dom;
59878 var lv = this.lockedBody.dom;
59879 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59880 lv.scrollTop = bs.scrollTop = sb.scrollTop;
59883 handleScroll : function(e){
59885 var sb = this.scroller.dom;
59886 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59890 handleWheel : function(e){
59891 var d = e.getWheelDelta();
59892 this.scroller.dom.scrollTop -= d*22;
59893 // set this here to prevent jumpy scrolling on large tables
59894 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59898 renderRows : function(startRow, endRow){
59899 // pull in all the crap needed to render rows
59900 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59901 var colCount = cm.getColumnCount();
59903 if(ds.getCount() < 1){
59907 // build a map for all the columns
59909 for(var i = 0; i < colCount; i++){
59910 var name = cm.getDataIndex(i);
59912 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59913 renderer : cm.getRenderer(i),
59914 id : cm.getColumnId(i),
59915 locked : cm.isLocked(i),
59916 has_editor : cm.isCellEditable(i)
59920 startRow = startRow || 0;
59921 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59923 // records to render
59924 var rs = ds.getRange(startRow, endRow);
59926 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59929 // As much as I hate to duplicate code, this was branched because FireFox really hates
59930 // [].join("") on strings. The performance difference was substantial enough to
59931 // branch this function
59932 doRender : Roo.isGecko ?
59933 function(cs, rs, ds, startRow, colCount, stripe){
59934 var ts = this.templates, ct = ts.cell, rt = ts.row;
59936 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59938 var hasListener = this.grid.hasListener('rowclass');
59940 for(var j = 0, len = rs.length; j < len; j++){
59941 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59942 for(var i = 0; i < colCount; i++){
59944 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59946 p.css = p.attr = "";
59947 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59948 if(p.value == undefined || p.value === "") {
59949 p.value = " ";
59952 p.css += ' x-grid-editable-cell';
59954 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59955 p.css += ' x-grid-dirty-cell';
59957 var markup = ct.apply(p);
59965 if(stripe && ((rowIndex+1) % 2 == 0)){
59966 alt.push("x-grid-row-alt")
59969 alt.push( " x-grid-dirty-row");
59972 if(this.getRowClass){
59973 alt.push(this.getRowClass(r, rowIndex));
59979 rowIndex : rowIndex,
59982 this.grid.fireEvent('rowclass', this, rowcfg);
59983 alt.push(rowcfg.rowClass);
59985 rp.alt = alt.join(" ");
59986 lbuf+= rt.apply(rp);
59988 buf+= rt.apply(rp);
59990 return [lbuf, buf];
59992 function(cs, rs, ds, startRow, colCount, stripe){
59993 var ts = this.templates, ct = ts.cell, rt = ts.row;
59995 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59996 var hasListener = this.grid.hasListener('rowclass');
59999 for(var j = 0, len = rs.length; j < len; j++){
60000 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60001 for(var i = 0; i < colCount; i++){
60003 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60005 p.css = p.attr = "";
60006 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60007 if(p.value == undefined || p.value === "") {
60008 p.value = " ";
60012 p.css += ' x-grid-editable-cell';
60014 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60015 p.css += ' x-grid-dirty-cell'
60018 var markup = ct.apply(p);
60020 cb[cb.length] = markup;
60022 lcb[lcb.length] = markup;
60026 if(stripe && ((rowIndex+1) % 2 == 0)){
60027 alt.push( "x-grid-row-alt");
60030 alt.push(" x-grid-dirty-row");
60033 if(this.getRowClass){
60034 alt.push( this.getRowClass(r, rowIndex));
60040 rowIndex : rowIndex,
60043 this.grid.fireEvent('rowclass', this, rowcfg);
60044 alt.push(rowcfg.rowClass);
60047 rp.alt = alt.join(" ");
60048 rp.cells = lcb.join("");
60049 lbuf[lbuf.length] = rt.apply(rp);
60050 rp.cells = cb.join("");
60051 buf[buf.length] = rt.apply(rp);
60053 return [lbuf.join(""), buf.join("")];
60056 renderBody : function(){
60057 var markup = this.renderRows();
60058 var bt = this.templates.body;
60059 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60063 * Refreshes the grid
60064 * @param {Boolean} headersToo
60066 refresh : function(headersToo){
60067 this.fireEvent("beforerefresh", this);
60068 this.grid.stopEditing();
60069 var result = this.renderBody();
60070 this.lockedBody.update(result[0]);
60071 this.mainBody.update(result[1]);
60072 if(headersToo === true){
60073 this.updateHeaders();
60074 this.updateColumns();
60075 this.updateSplitters();
60076 this.updateHeaderSortState();
60078 this.syncRowHeights();
60080 this.fireEvent("refresh", this);
60083 handleColumnMove : function(cm, oldIndex, newIndex){
60084 this.indexMap = null;
60085 var s = this.getScrollState();
60086 this.refresh(true);
60087 this.restoreScroll(s);
60088 this.afterMove(newIndex);
60091 afterMove : function(colIndex){
60092 if(this.enableMoveAnim && Roo.enableFx){
60093 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60095 // if multisort - fix sortOrder, and reload..
60096 if (this.grid.dataSource.multiSort) {
60097 // the we can call sort again..
60098 var dm = this.grid.dataSource;
60099 var cm = this.grid.colModel;
60101 for(var i = 0; i < cm.config.length; i++ ) {
60103 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60104 continue; // dont' bother, it's not in sort list or being set.
60107 so.push(cm.config[i].dataIndex);
60110 dm.load(dm.lastOptions);
60117 updateCell : function(dm, rowIndex, dataIndex){
60118 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60119 if(typeof colIndex == "undefined"){ // not present in grid
60122 var cm = this.grid.colModel;
60123 var cell = this.getCell(rowIndex, colIndex);
60124 var cellText = this.getCellText(rowIndex, colIndex);
60127 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60128 id : cm.getColumnId(colIndex),
60129 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60131 var renderer = cm.getRenderer(colIndex);
60132 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60133 if(typeof val == "undefined" || val === "") {
60136 cellText.innerHTML = val;
60137 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60138 this.syncRowHeights(rowIndex, rowIndex);
60141 calcColumnWidth : function(colIndex, maxRowsToMeasure){
60143 if(this.grid.autoSizeHeaders){
60144 var h = this.getHeaderCellMeasure(colIndex);
60145 maxWidth = Math.max(maxWidth, h.scrollWidth);
60148 if(this.cm.isLocked(colIndex)){
60149 tb = this.getLockedTable();
60152 tb = this.getBodyTable();
60153 index = colIndex - this.cm.getLockedCount();
60156 var rows = tb.rows;
60157 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60158 for(var i = 0; i < stopIndex; i++){
60159 var cell = rows[i].childNodes[index].firstChild;
60160 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60163 return maxWidth + /*margin for error in IE*/ 5;
60166 * Autofit a column to its content.
60167 * @param {Number} colIndex
60168 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60170 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60171 if(this.cm.isHidden(colIndex)){
60172 return; // can't calc a hidden column
60175 var cid = this.cm.getColumnId(colIndex);
60176 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60177 if(this.grid.autoSizeHeaders){
60178 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60181 var newWidth = this.calcColumnWidth(colIndex);
60182 this.cm.setColumnWidth(colIndex,
60183 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60184 if(!suppressEvent){
60185 this.grid.fireEvent("columnresize", colIndex, newWidth);
60190 * Autofits all columns to their content and then expands to fit any extra space in the grid
60192 autoSizeColumns : function(){
60193 var cm = this.grid.colModel;
60194 var colCount = cm.getColumnCount();
60195 for(var i = 0; i < colCount; i++){
60196 this.autoSizeColumn(i, true, true);
60198 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60201 this.updateColumns();
60207 * Autofits all columns to the grid's width proportionate with their current size
60208 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60210 fitColumns : function(reserveScrollSpace){
60211 var cm = this.grid.colModel;
60212 var colCount = cm.getColumnCount();
60216 for (i = 0; i < colCount; i++){
60217 if(!cm.isHidden(i) && !cm.isFixed(i)){
60218 w = cm.getColumnWidth(i);
60224 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60225 if(reserveScrollSpace){
60228 var frac = (avail - cm.getTotalWidth())/width;
60229 while (cols.length){
60232 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60234 this.updateColumns();
60238 onRowSelect : function(rowIndex){
60239 var row = this.getRowComposite(rowIndex);
60240 row.addClass("x-grid-row-selected");
60243 onRowDeselect : function(rowIndex){
60244 var row = this.getRowComposite(rowIndex);
60245 row.removeClass("x-grid-row-selected");
60248 onCellSelect : function(row, col){
60249 var cell = this.getCell(row, col);
60251 Roo.fly(cell).addClass("x-grid-cell-selected");
60255 onCellDeselect : function(row, col){
60256 var cell = this.getCell(row, col);
60258 Roo.fly(cell).removeClass("x-grid-cell-selected");
60262 updateHeaderSortState : function(){
60264 // sort state can be single { field: xxx, direction : yyy}
60265 // or { xxx=>ASC , yyy : DESC ..... }
60268 if (!this.ds.multiSort) {
60269 var state = this.ds.getSortState();
60273 mstate[state.field] = state.direction;
60274 // FIXME... - this is not used here.. but might be elsewhere..
60275 this.sortState = state;
60278 mstate = this.ds.sortToggle;
60280 //remove existing sort classes..
60282 var sc = this.sortClasses;
60283 var hds = this.el.select(this.headerSelector).removeClass(sc);
60285 for(var f in mstate) {
60287 var sortColumn = this.cm.findColumnIndex(f);
60289 if(sortColumn != -1){
60290 var sortDir = mstate[f];
60291 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60300 handleHeaderClick : function(g, index,e){
60302 Roo.log("header click");
60305 // touch events on header are handled by context
60306 this.handleHdCtx(g,index,e);
60311 if(this.headersDisabled){
60314 var dm = g.dataSource, cm = g.colModel;
60315 if(!cm.isSortable(index)){
60320 if (dm.multiSort) {
60321 // update the sortOrder
60323 for(var i = 0; i < cm.config.length; i++ ) {
60325 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60326 continue; // dont' bother, it's not in sort list or being set.
60329 so.push(cm.config[i].dataIndex);
60335 dm.sort(cm.getDataIndex(index));
60339 destroy : function(){
60341 this.colMenu.removeAll();
60342 Roo.menu.MenuMgr.unregister(this.colMenu);
60343 this.colMenu.getEl().remove();
60344 delete this.colMenu;
60347 this.hmenu.removeAll();
60348 Roo.menu.MenuMgr.unregister(this.hmenu);
60349 this.hmenu.getEl().remove();
60352 if(this.grid.enableColumnMove){
60353 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60355 for(var dd in dds){
60356 if(!dds[dd].config.isTarget && dds[dd].dragElId){
60357 var elid = dds[dd].dragElId;
60359 Roo.get(elid).remove();
60360 } else if(dds[dd].config.isTarget){
60361 dds[dd].proxyTop.remove();
60362 dds[dd].proxyBottom.remove();
60365 if(Roo.dd.DDM.locationCache[dd]){
60366 delete Roo.dd.DDM.locationCache[dd];
60369 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60372 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60373 this.bind(null, null);
60374 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60377 handleLockChange : function(){
60378 this.refresh(true);
60381 onDenyColumnLock : function(){
60385 onDenyColumnHide : function(){
60389 handleHdMenuClick : function(item){
60390 var index = this.hdCtxIndex;
60391 var cm = this.cm, ds = this.ds;
60394 ds.sort(cm.getDataIndex(index), "ASC");
60397 ds.sort(cm.getDataIndex(index), "DESC");
60400 var lc = cm.getLockedCount();
60401 if(cm.getColumnCount(true) <= lc+1){
60402 this.onDenyColumnLock();
60406 cm.setLocked(index, true, true);
60407 cm.moveColumn(index, lc);
60408 this.grid.fireEvent("columnmove", index, lc);
60410 cm.setLocked(index, true);
60414 var lc = cm.getLockedCount();
60415 if((lc-1) != index){
60416 cm.setLocked(index, false, true);
60417 cm.moveColumn(index, lc-1);
60418 this.grid.fireEvent("columnmove", index, lc-1);
60420 cm.setLocked(index, false);
60423 case 'wider': // used to expand cols on touch..
60425 var cw = cm.getColumnWidth(index);
60426 cw += (item.id == 'wider' ? 1 : -1) * 50;
60427 cw = Math.max(0, cw);
60428 cw = Math.min(cw,4000);
60429 cm.setColumnWidth(index, cw);
60433 index = cm.getIndexById(item.id.substr(4));
60435 if(item.checked && cm.getColumnCount(true) <= 1){
60436 this.onDenyColumnHide();
60439 cm.setHidden(index, item.checked);
60445 beforeColMenuShow : function(){
60446 var cm = this.cm, colCount = cm.getColumnCount();
60447 this.colMenu.removeAll();
60450 for(var i = 0; i < colCount; i++){
60452 id: "col-"+cm.getColumnId(i),
60453 text: cm.getColumnHeader(i),
60454 checked: !cm.isHidden(i),
60459 if (this.grid.sortColMenu) {
60460 items.sort(function(a,b) {
60461 if (a.text == b.text) {
60464 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60468 for(var i = 0; i < colCount; i++){
60469 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60473 handleHdCtx : function(g, index, e){
60475 var hd = this.getHeaderCell(index);
60476 this.hdCtxIndex = index;
60477 var ms = this.hmenu.items, cm = this.cm;
60478 ms.get("asc").setDisabled(!cm.isSortable(index));
60479 ms.get("desc").setDisabled(!cm.isSortable(index));
60480 if(this.grid.enableColLock !== false){
60481 ms.get("lock").setDisabled(cm.isLocked(index));
60482 ms.get("unlock").setDisabled(!cm.isLocked(index));
60484 this.hmenu.show(hd, "tl-bl");
60487 handleHdOver : function(e){
60488 var hd = this.findHeaderCell(e.getTarget());
60489 if(hd && !this.headersDisabled){
60490 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60491 this.fly(hd).addClass("x-grid-hd-over");
60496 handleHdOut : function(e){
60497 var hd = this.findHeaderCell(e.getTarget());
60499 this.fly(hd).removeClass("x-grid-hd-over");
60503 handleSplitDblClick : function(e, t){
60504 var i = this.getCellIndex(t);
60505 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60506 this.autoSizeColumn(i, true);
60511 render : function(){
60514 var colCount = cm.getColumnCount();
60516 if(this.grid.monitorWindowResize === true){
60517 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60519 var header = this.renderHeaders();
60520 var body = this.templates.body.apply({rows:""});
60521 var html = this.templates.master.apply({
60524 lockedHeader: header[0],
60528 //this.updateColumns();
60530 this.grid.getGridEl().dom.innerHTML = html;
60532 this.initElements();
60534 // a kludge to fix the random scolling effect in webkit
60535 this.el.on("scroll", function() {
60536 this.el.dom.scrollTop=0; // hopefully not recursive..
60539 this.scroller.on("scroll", this.handleScroll, this);
60540 this.lockedBody.on("mousewheel", this.handleWheel, this);
60541 this.mainBody.on("mousewheel", this.handleWheel, this);
60543 this.mainHd.on("mouseover", this.handleHdOver, this);
60544 this.mainHd.on("mouseout", this.handleHdOut, this);
60545 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60546 {delegate: "."+this.splitClass});
60548 this.lockedHd.on("mouseover", this.handleHdOver, this);
60549 this.lockedHd.on("mouseout", this.handleHdOut, this);
60550 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60551 {delegate: "."+this.splitClass});
60553 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60554 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60557 this.updateSplitters();
60559 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60560 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60561 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60564 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60565 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60567 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60568 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60570 if(this.grid.enableColLock !== false){
60571 this.hmenu.add('-',
60572 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60573 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60577 this.hmenu.add('-',
60578 {id:"wider", text: this.columnsWiderText},
60579 {id:"narrow", text: this.columnsNarrowText }
60585 if(this.grid.enableColumnHide !== false){
60587 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60588 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60589 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60591 this.hmenu.add('-',
60592 {id:"columns", text: this.columnsText, menu: this.colMenu}
60595 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60597 this.grid.on("headercontextmenu", this.handleHdCtx, this);
60600 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60601 this.dd = new Roo.grid.GridDragZone(this.grid, {
60602 ddGroup : this.grid.ddGroup || 'GridDD'
60608 for(var i = 0; i < colCount; i++){
60609 if(cm.isHidden(i)){
60610 this.hideColumn(i);
60612 if(cm.config[i].align){
60613 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60614 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60618 this.updateHeaderSortState();
60620 this.beforeInitialResize();
60623 // two part rendering gives faster view to the user
60624 this.renderPhase2.defer(1, this);
60627 renderPhase2 : function(){
60628 // render the rows now
60630 if(this.grid.autoSizeColumns){
60631 this.autoSizeColumns();
60635 beforeInitialResize : function(){
60639 onColumnSplitterMoved : function(i, w){
60640 this.userResized = true;
60641 var cm = this.grid.colModel;
60642 cm.setColumnWidth(i, w, true);
60643 var cid = cm.getColumnId(i);
60644 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60645 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60646 this.updateSplitters();
60648 this.grid.fireEvent("columnresize", i, w);
60651 syncRowHeights : function(startIndex, endIndex){
60652 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60653 startIndex = startIndex || 0;
60654 var mrows = this.getBodyTable().rows;
60655 var lrows = this.getLockedTable().rows;
60656 var len = mrows.length-1;
60657 endIndex = Math.min(endIndex || len, len);
60658 for(var i = startIndex; i <= endIndex; i++){
60659 var m = mrows[i], l = lrows[i];
60660 var h = Math.max(m.offsetHeight, l.offsetHeight);
60661 m.style.height = l.style.height = h + "px";
60666 layout : function(initialRender, is2ndPass)
60669 var auto = g.autoHeight;
60670 var scrollOffset = 16;
60671 var c = g.getGridEl(), cm = this.cm,
60672 expandCol = g.autoExpandColumn,
60674 //c.beginMeasure();
60676 if(!c.dom.offsetWidth){ // display:none?
60678 this.lockedWrap.show();
60679 this.mainWrap.show();
60684 var hasLock = this.cm.isLocked(0);
60686 var tbh = this.headerPanel.getHeight();
60687 var bbh = this.footerPanel.getHeight();
60690 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60691 var newHeight = ch + c.getBorderWidth("tb");
60693 newHeight = Math.min(g.maxHeight, newHeight);
60695 c.setHeight(newHeight);
60699 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60702 var s = this.scroller;
60704 var csize = c.getSize(true);
60706 this.el.setSize(csize.width, csize.height);
60708 this.headerPanel.setWidth(csize.width);
60709 this.footerPanel.setWidth(csize.width);
60711 var hdHeight = this.mainHd.getHeight();
60712 var vw = csize.width;
60713 var vh = csize.height - (tbh + bbh);
60717 var bt = this.getBodyTable();
60719 if(cm.getLockedCount() == cm.config.length){
60720 bt = this.getLockedTable();
60723 var ltWidth = hasLock ?
60724 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60726 var scrollHeight = bt.offsetHeight;
60727 var scrollWidth = ltWidth + bt.offsetWidth;
60728 var vscroll = false, hscroll = false;
60730 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60732 var lw = this.lockedWrap, mw = this.mainWrap;
60733 var lb = this.lockedBody, mb = this.mainBody;
60735 setTimeout(function(){
60736 var t = s.dom.offsetTop;
60737 var w = s.dom.clientWidth,
60738 h = s.dom.clientHeight;
60741 lw.setSize(ltWidth, h);
60743 mw.setLeftTop(ltWidth, t);
60744 mw.setSize(w-ltWidth, h);
60746 lb.setHeight(h-hdHeight);
60747 mb.setHeight(h-hdHeight);
60749 if(is2ndPass !== true && !gv.userResized && expandCol){
60750 // high speed resize without full column calculation
60752 var ci = cm.getIndexById(expandCol);
60754 ci = cm.findColumnIndex(expandCol);
60756 ci = Math.max(0, ci); // make sure it's got at least the first col.
60757 var expandId = cm.getColumnId(ci);
60758 var tw = cm.getTotalWidth(false);
60759 var currentWidth = cm.getColumnWidth(ci);
60760 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60761 if(currentWidth != cw){
60762 cm.setColumnWidth(ci, cw, true);
60763 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60764 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60765 gv.updateSplitters();
60766 gv.layout(false, true);
60778 onWindowResize : function(){
60779 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60785 appendFooter : function(parentEl){
60789 sortAscText : "Sort Ascending",
60790 sortDescText : "Sort Descending",
60791 lockText : "Lock Column",
60792 unlockText : "Unlock Column",
60793 columnsText : "Columns",
60795 columnsWiderText : "Wider",
60796 columnsNarrowText : "Thinner"
60800 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60801 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60802 this.proxy.el.addClass('x-grid3-col-dd');
60805 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60806 handleMouseDown : function(e){
60810 callHandleMouseDown : function(e){
60811 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60816 * Ext JS Library 1.1.1
60817 * Copyright(c) 2006-2007, Ext JS, LLC.
60819 * Originally Released Under LGPL - original licence link has changed is not relivant.
60822 * <script type="text/javascript">
60825 * @extends Roo.dd.DDProxy
60826 * @class Roo.grid.SplitDragZone
60827 * Support for Column Header resizing
60829 * @param {Object} config
60832 // This is a support class used internally by the Grid components
60833 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60835 this.view = grid.getView();
60836 this.proxy = this.view.resizeProxy;
60837 Roo.grid.SplitDragZone.superclass.constructor.call(
60840 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60842 dragElId : Roo.id(this.proxy.dom),
60847 this.setHandleElId(Roo.id(hd));
60848 if (hd2 !== false) {
60849 this.setOuterHandleElId(Roo.id(hd2));
60852 this.scroll = false;
60854 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60855 fly: Roo.Element.fly,
60857 b4StartDrag : function(x, y){
60858 this.view.headersDisabled = true;
60859 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60860 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60862 this.proxy.setHeight(h);
60864 // for old system colWidth really stored the actual width?
60865 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60866 // which in reality did not work.. - it worked only for fixed sizes
60867 // for resizable we need to use actual sizes.
60868 var w = this.cm.getColumnWidth(this.cellIndex);
60869 if (!this.view.mainWrap) {
60871 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60876 // this was w-this.grid.minColumnWidth;
60877 // doesnt really make sense? - w = thie curren width or the rendered one?
60878 var minw = Math.max(w-this.grid.minColumnWidth, 0);
60879 this.resetConstraints();
60880 this.setXConstraint(minw, 1000);
60881 this.setYConstraint(0, 0);
60882 this.minX = x - minw;
60883 this.maxX = x + 1000;
60885 if (!this.view.mainWrap) { // this is Bootstrap code..
60886 this.getDragEl().style.display='block';
60889 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60893 handleMouseDown : function(e){
60894 ev = Roo.EventObject.setEvent(e);
60895 var t = this.fly(ev.getTarget());
60896 if(t.hasClass("x-grid-split")){
60897 this.cellIndex = this.view.getCellIndex(t.dom);
60898 this.split = t.dom;
60899 this.cm = this.grid.colModel;
60900 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60901 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60906 endDrag : function(e){
60907 this.view.headersDisabled = false;
60908 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60909 var diff = endX - this.startPos;
60911 var w = this.cm.getColumnWidth(this.cellIndex);
60912 if (!this.view.mainWrap) {
60915 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60918 autoOffset : function(){
60919 this.setDelta(0,0);
60923 * Ext JS Library 1.1.1
60924 * Copyright(c) 2006-2007, Ext JS, LLC.
60926 * Originally Released Under LGPL - original licence link has changed is not relivant.
60929 * <script type="text/javascript">
60933 // This is a support class used internally by the Grid components
60934 Roo.grid.GridDragZone = function(grid, config){
60935 this.view = grid.getView();
60936 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60937 if(this.view.lockedBody){
60938 this.setHandleElId(Roo.id(this.view.mainBody.dom));
60939 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60941 this.scroll = false;
60943 this.ddel = document.createElement('div');
60944 this.ddel.className = 'x-grid-dd-wrap';
60947 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60948 ddGroup : "GridDD",
60950 getDragData : function(e){
60951 var t = Roo.lib.Event.getTarget(e);
60952 var rowIndex = this.view.findRowIndex(t);
60953 var sm = this.grid.selModel;
60955 //Roo.log(rowIndex);
60957 if (sm.getSelectedCell) {
60958 // cell selection..
60959 if (!sm.getSelectedCell()) {
60962 if (rowIndex != sm.getSelectedCell()[0]) {
60967 if (sm.getSelections && sm.getSelections().length < 1) {
60972 // before it used to all dragging of unseleted... - now we dont do that.
60973 if(rowIndex !== false){
60978 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60980 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60983 if (e.hasModifier()){
60984 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60987 Roo.log("getDragData");
60992 rowIndex: rowIndex,
60993 selections: sm.getSelections ? sm.getSelections() : (
60994 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61001 onInitDrag : function(e){
61002 var data = this.dragData;
61003 this.ddel.innerHTML = this.grid.getDragDropText();
61004 this.proxy.update(this.ddel);
61005 // fire start drag?
61008 afterRepair : function(){
61009 this.dragging = false;
61012 getRepairXY : function(e, data){
61016 onEndDrag : function(data, e){
61020 onValidDrop : function(dd, e, id){
61025 beforeInvalidDrop : function(e, id){
61030 * Ext JS Library 1.1.1
61031 * Copyright(c) 2006-2007, Ext JS, LLC.
61033 * Originally Released Under LGPL - original licence link has changed is not relivant.
61036 * <script type="text/javascript">
61041 * @class Roo.grid.ColumnModel
61042 * @extends Roo.util.Observable
61043 * This is the default implementation of a ColumnModel used by the Grid. It defines
61044 * the columns in the grid.
61047 var colModel = new Roo.grid.ColumnModel([
61048 {header: "Ticker", width: 60, sortable: true, locked: true},
61049 {header: "Company Name", width: 150, sortable: true},
61050 {header: "Market Cap.", width: 100, sortable: true},
61051 {header: "$ Sales", width: 100, sortable: true, renderer: money},
61052 {header: "Employees", width: 100, sortable: true, resizable: false}
61057 * The config options listed for this class are options which may appear in each
61058 * individual column definition.
61059 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61061 * @param {Object} config An Array of column config objects. See this class's
61062 * config objects for details.
61064 Roo.grid.ColumnModel = function(config){
61066 * The config passed into the constructor
61068 this.config = []; //config;
61071 // if no id, create one
61072 // if the column does not have a dataIndex mapping,
61073 // map it to the order it is in the config
61074 for(var i = 0, len = config.length; i < len; i++){
61075 this.addColumn(config[i]);
61080 * The width of columns which have no width specified (defaults to 100)
61083 this.defaultWidth = 100;
61086 * Default sortable of columns which have no sortable specified (defaults to false)
61089 this.defaultSortable = false;
61093 * @event widthchange
61094 * Fires when the width of a column changes.
61095 * @param {ColumnModel} this
61096 * @param {Number} columnIndex The column index
61097 * @param {Number} newWidth The new width
61099 "widthchange": true,
61101 * @event headerchange
61102 * Fires when the text of a header changes.
61103 * @param {ColumnModel} this
61104 * @param {Number} columnIndex The column index
61105 * @param {Number} newText The new header text
61107 "headerchange": true,
61109 * @event hiddenchange
61110 * Fires when a column is hidden or "unhidden".
61111 * @param {ColumnModel} this
61112 * @param {Number} columnIndex The column index
61113 * @param {Boolean} hidden true if hidden, false otherwise
61115 "hiddenchange": true,
61117 * @event columnmoved
61118 * Fires when a column is moved.
61119 * @param {ColumnModel} this
61120 * @param {Number} oldIndex
61121 * @param {Number} newIndex
61123 "columnmoved" : true,
61125 * @event columlockchange
61126 * Fires when a column's locked state is changed
61127 * @param {ColumnModel} this
61128 * @param {Number} colIndex
61129 * @param {Boolean} locked true if locked
61131 "columnlockchange" : true
61133 Roo.grid.ColumnModel.superclass.constructor.call(this);
61135 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61137 * @cfg {String} header The header text to display in the Grid view.
61140 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61143 * @cfg {String} smHeader Header at Bootsrap Small width
61146 * @cfg {String} mdHeader Header at Bootsrap Medium width
61149 * @cfg {String} lgHeader Header at Bootsrap Large width
61152 * @cfg {String} xlHeader Header at Bootsrap extra Large width
61155 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61156 * {@link Roo.data.Record} definition from which to draw the column's value. If not
61157 * specified, the column's index is used as an index into the Record's data Array.
61160 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61161 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61164 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61165 * Defaults to the value of the {@link #defaultSortable} property.
61166 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61169 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
61172 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
61175 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61178 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61181 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61182 * given the cell's data value. See {@link #setRenderer}. If not specified, the
61183 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61184 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61187 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
61190 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
61193 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
61196 * @cfg {String} cursor (Optional)
61199 * @cfg {String} tooltip (Optional)
61202 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61205 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61208 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61211 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61214 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61217 * Returns the id of the column at the specified index.
61218 * @param {Number} index The column index
61219 * @return {String} the id
61221 getColumnId : function(index){
61222 return this.config[index].id;
61226 * Returns the column for a specified id.
61227 * @param {String} id The column id
61228 * @return {Object} the column
61230 getColumnById : function(id){
61231 return this.lookup[id];
61236 * Returns the column Object for a specified dataIndex.
61237 * @param {String} dataIndex The column dataIndex
61238 * @return {Object|Boolean} the column or false if not found
61240 getColumnByDataIndex: function(dataIndex){
61241 var index = this.findColumnIndex(dataIndex);
61242 return index > -1 ? this.config[index] : false;
61246 * Returns the index for a specified column id.
61247 * @param {String} id The column id
61248 * @return {Number} the index, or -1 if not found
61250 getIndexById : function(id){
61251 for(var i = 0, len = this.config.length; i < len; i++){
61252 if(this.config[i].id == id){
61260 * Returns the index for a specified column dataIndex.
61261 * @param {String} dataIndex The column dataIndex
61262 * @return {Number} the index, or -1 if not found
61265 findColumnIndex : function(dataIndex){
61266 for(var i = 0, len = this.config.length; i < len; i++){
61267 if(this.config[i].dataIndex == dataIndex){
61275 moveColumn : function(oldIndex, newIndex){
61276 var c = this.config[oldIndex];
61277 this.config.splice(oldIndex, 1);
61278 this.config.splice(newIndex, 0, c);
61279 this.dataMap = null;
61280 this.fireEvent("columnmoved", this, oldIndex, newIndex);
61283 isLocked : function(colIndex){
61284 return this.config[colIndex].locked === true;
61287 setLocked : function(colIndex, value, suppressEvent){
61288 if(this.isLocked(colIndex) == value){
61291 this.config[colIndex].locked = value;
61292 if(!suppressEvent){
61293 this.fireEvent("columnlockchange", this, colIndex, value);
61297 getTotalLockedWidth : function(){
61298 var totalWidth = 0;
61299 for(var i = 0; i < this.config.length; i++){
61300 if(this.isLocked(i) && !this.isHidden(i)){
61301 this.totalWidth += this.getColumnWidth(i);
61307 getLockedCount : function(){
61308 for(var i = 0, len = this.config.length; i < len; i++){
61309 if(!this.isLocked(i)){
61314 return this.config.length;
61318 * Returns the number of columns.
61321 getColumnCount : function(visibleOnly){
61322 if(visibleOnly === true){
61324 for(var i = 0, len = this.config.length; i < len; i++){
61325 if(!this.isHidden(i)){
61331 return this.config.length;
61335 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61336 * @param {Function} fn
61337 * @param {Object} scope (optional)
61338 * @return {Array} result
61340 getColumnsBy : function(fn, scope){
61342 for(var i = 0, len = this.config.length; i < len; i++){
61343 var c = this.config[i];
61344 if(fn.call(scope||this, c, i) === true){
61352 * Returns true if the specified column is sortable.
61353 * @param {Number} col The column index
61354 * @return {Boolean}
61356 isSortable : function(col){
61357 if(typeof this.config[col].sortable == "undefined"){
61358 return this.defaultSortable;
61360 return this.config[col].sortable;
61364 * Returns the rendering (formatting) function defined for the column.
61365 * @param {Number} col The column index.
61366 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61368 getRenderer : function(col){
61369 if(!this.config[col].renderer){
61370 return Roo.grid.ColumnModel.defaultRenderer;
61372 return this.config[col].renderer;
61376 * Sets the rendering (formatting) function for a column.
61377 * @param {Number} col The column index
61378 * @param {Function} fn The function to use to process the cell's raw data
61379 * to return HTML markup for the grid view. The render function is called with
61380 * the following parameters:<ul>
61381 * <li>Data value.</li>
61382 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61383 * <li>css A CSS style string to apply to the table cell.</li>
61384 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61385 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61386 * <li>Row index</li>
61387 * <li>Column index</li>
61388 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61390 setRenderer : function(col, fn){
61391 this.config[col].renderer = fn;
61395 * Returns the width for the specified column.
61396 * @param {Number} col The column index
61397 * @param (optional) {String} gridSize bootstrap width size.
61400 getColumnWidth : function(col, gridSize)
61402 var cfg = this.config[col];
61404 if (typeof(gridSize) == 'undefined') {
61405 return cfg.width * 1 || this.defaultWidth;
61407 if (gridSize === false) { // if we set it..
61408 return cfg.width || false;
61410 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61412 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61413 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61416 return cfg[ sizes[i] ];
61423 * Sets the width for a column.
61424 * @param {Number} col The column index
61425 * @param {Number} width The new width
61427 setColumnWidth : function(col, width, suppressEvent){
61428 this.config[col].width = width;
61429 this.totalWidth = null;
61430 if(!suppressEvent){
61431 this.fireEvent("widthchange", this, col, width);
61436 * Returns the total width of all columns.
61437 * @param {Boolean} includeHidden True to include hidden column widths
61440 getTotalWidth : function(includeHidden){
61441 if(!this.totalWidth){
61442 this.totalWidth = 0;
61443 for(var i = 0, len = this.config.length; i < len; i++){
61444 if(includeHidden || !this.isHidden(i)){
61445 this.totalWidth += this.getColumnWidth(i);
61449 return this.totalWidth;
61453 * Returns the header for the specified column.
61454 * @param {Number} col The column index
61457 getColumnHeader : function(col){
61458 return this.config[col].header;
61462 * Sets the header for a column.
61463 * @param {Number} col The column index
61464 * @param {String} header The new header
61466 setColumnHeader : function(col, header){
61467 this.config[col].header = header;
61468 this.fireEvent("headerchange", this, col, header);
61472 * Returns the tooltip for the specified column.
61473 * @param {Number} col The column index
61476 getColumnTooltip : function(col){
61477 return this.config[col].tooltip;
61480 * Sets the tooltip for a column.
61481 * @param {Number} col The column index
61482 * @param {String} tooltip The new tooltip
61484 setColumnTooltip : function(col, tooltip){
61485 this.config[col].tooltip = tooltip;
61489 * Returns the dataIndex for the specified column.
61490 * @param {Number} col The column index
61493 getDataIndex : function(col){
61494 return this.config[col].dataIndex;
61498 * Sets the dataIndex for a column.
61499 * @param {Number} col The column index
61500 * @param {Number} dataIndex The new dataIndex
61502 setDataIndex : function(col, dataIndex){
61503 this.config[col].dataIndex = dataIndex;
61509 * Returns true if the cell is editable.
61510 * @param {Number} colIndex The column index
61511 * @param {Number} rowIndex The row index - this is nto actually used..?
61512 * @return {Boolean}
61514 isCellEditable : function(colIndex, rowIndex){
61515 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61519 * Returns the editor defined for the cell/column.
61520 * return false or null to disable editing.
61521 * @param {Number} colIndex The column index
61522 * @param {Number} rowIndex The row index
61525 getCellEditor : function(colIndex, rowIndex){
61526 return this.config[colIndex].editor;
61530 * Sets if a column is editable.
61531 * @param {Number} col The column index
61532 * @param {Boolean} editable True if the column is editable
61534 setEditable : function(col, editable){
61535 this.config[col].editable = editable;
61540 * Returns true if the column is hidden.
61541 * @param {Number} colIndex The column index
61542 * @return {Boolean}
61544 isHidden : function(colIndex){
61545 return this.config[colIndex].hidden;
61550 * Returns true if the column width cannot be changed
61552 isFixed : function(colIndex){
61553 return this.config[colIndex].fixed;
61557 * Returns true if the column can be resized
61558 * @return {Boolean}
61560 isResizable : function(colIndex){
61561 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61564 * Sets if a column is hidden.
61565 * @param {Number} colIndex The column index
61566 * @param {Boolean} hidden True if the column is hidden
61568 setHidden : function(colIndex, hidden){
61569 this.config[colIndex].hidden = hidden;
61570 this.totalWidth = null;
61571 this.fireEvent("hiddenchange", this, colIndex, hidden);
61575 * Sets the editor for a column.
61576 * @param {Number} col The column index
61577 * @param {Object} editor The editor object
61579 setEditor : function(col, editor){
61580 this.config[col].editor = editor;
61583 * Add a column (experimental...) - defaults to adding to the end..
61584 * @param {Object} config
61586 addColumn : function(c)
61589 var i = this.config.length;
61590 this.config[i] = c;
61592 if(typeof c.dataIndex == "undefined"){
61595 if(typeof c.renderer == "string"){
61596 c.renderer = Roo.util.Format[c.renderer];
61598 if(typeof c.id == "undefined"){
61601 if(c.editor && c.editor.xtype){
61602 c.editor = Roo.factory(c.editor, Roo.grid);
61604 if(c.editor && c.editor.isFormField){
61605 c.editor = new Roo.grid.GridEditor(c.editor);
61607 this.lookup[c.id] = c;
61612 Roo.grid.ColumnModel.defaultRenderer = function(value)
61614 if(typeof value == "object") {
61617 if(typeof value == "string" && value.length < 1){
61621 return String.format("{0}", value);
61624 // Alias for backwards compatibility
61625 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61628 * Ext JS Library 1.1.1
61629 * Copyright(c) 2006-2007, Ext JS, LLC.
61631 * Originally Released Under LGPL - original licence link has changed is not relivant.
61634 * <script type="text/javascript">
61638 * @class Roo.grid.AbstractSelectionModel
61639 * @extends Roo.util.Observable
61641 * Abstract base class for grid SelectionModels. It provides the interface that should be
61642 * implemented by descendant classes. This class should not be directly instantiated.
61645 Roo.grid.AbstractSelectionModel = function(){
61646 this.locked = false;
61647 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61650 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
61651 /** @ignore Called by the grid automatically. Do not call directly. */
61652 init : function(grid){
61658 * Locks the selections.
61661 this.locked = true;
61665 * Unlocks the selections.
61667 unlock : function(){
61668 this.locked = false;
61672 * Returns true if the selections are locked.
61673 * @return {Boolean}
61675 isLocked : function(){
61676 return this.locked;
61680 * Ext JS Library 1.1.1
61681 * Copyright(c) 2006-2007, Ext JS, LLC.
61683 * Originally Released Under LGPL - original licence link has changed is not relivant.
61686 * <script type="text/javascript">
61689 * @extends Roo.grid.AbstractSelectionModel
61690 * @class Roo.grid.RowSelectionModel
61691 * The default SelectionModel used by {@link Roo.grid.Grid}.
61692 * It supports multiple selections and keyboard selection/navigation.
61694 * @param {Object} config
61696 Roo.grid.RowSelectionModel = function(config){
61697 Roo.apply(this, config);
61698 this.selections = new Roo.util.MixedCollection(false, function(o){
61703 this.lastActive = false;
61707 * @event selectionchange
61708 * Fires when the selection changes
61709 * @param {SelectionModel} this
61711 "selectionchange" : true,
61713 * @event afterselectionchange
61714 * Fires after the selection changes (eg. by key press or clicking)
61715 * @param {SelectionModel} this
61717 "afterselectionchange" : true,
61719 * @event beforerowselect
61720 * Fires when a row is selected being selected, return false to cancel.
61721 * @param {SelectionModel} this
61722 * @param {Number} rowIndex The selected index
61723 * @param {Boolean} keepExisting False if other selections will be cleared
61725 "beforerowselect" : true,
61728 * Fires when a row is selected.
61729 * @param {SelectionModel} this
61730 * @param {Number} rowIndex The selected index
61731 * @param {Roo.data.Record} r The record
61733 "rowselect" : true,
61735 * @event rowdeselect
61736 * Fires when a row is deselected.
61737 * @param {SelectionModel} this
61738 * @param {Number} rowIndex The selected index
61740 "rowdeselect" : true
61742 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61743 this.locked = false;
61746 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
61748 * @cfg {Boolean} singleSelect
61749 * True to allow selection of only one row at a time (defaults to false)
61751 singleSelect : false,
61754 initEvents : function(){
61756 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61757 this.grid.on("mousedown", this.handleMouseDown, this);
61758 }else{ // allow click to work like normal
61759 this.grid.on("rowclick", this.handleDragableRowClick, this);
61761 // bootstrap does not have a view..
61762 var view = this.grid.view ? this.grid.view : this.grid;
61763 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61764 "up" : function(e){
61766 this.selectPrevious(e.shiftKey);
61767 }else if(this.last !== false && this.lastActive !== false){
61768 var last = this.last;
61769 this.selectRange(this.last, this.lastActive-1);
61770 view.focusRow(this.lastActive);
61771 if(last !== false){
61775 this.selectFirstRow();
61777 this.fireEvent("afterselectionchange", this);
61779 "down" : function(e){
61781 this.selectNext(e.shiftKey);
61782 }else if(this.last !== false && this.lastActive !== false){
61783 var last = this.last;
61784 this.selectRange(this.last, this.lastActive+1);
61785 view.focusRow(this.lastActive);
61786 if(last !== false){
61790 this.selectFirstRow();
61792 this.fireEvent("afterselectionchange", this);
61798 view.on("refresh", this.onRefresh, this);
61799 view.on("rowupdated", this.onRowUpdated, this);
61800 view.on("rowremoved", this.onRemove, this);
61804 onRefresh : function(){
61805 var ds = this.grid.ds, i, v = this.grid.view;
61806 var s = this.selections;
61807 s.each(function(r){
61808 if((i = ds.indexOfId(r.id)) != -1){
61810 s.add(ds.getAt(i)); // updating the selection relate data
61818 onRemove : function(v, index, r){
61819 this.selections.remove(r);
61823 onRowUpdated : function(v, index, r){
61824 if(this.isSelected(r)){
61825 v.onRowSelect(index);
61831 * @param {Array} records The records to select
61832 * @param {Boolean} keepExisting (optional) True to keep existing selections
61834 selectRecords : function(records, keepExisting){
61836 this.clearSelections();
61838 var ds = this.grid.ds;
61839 for(var i = 0, len = records.length; i < len; i++){
61840 this.selectRow(ds.indexOf(records[i]), true);
61845 * Gets the number of selected rows.
61848 getCount : function(){
61849 return this.selections.length;
61853 * Selects the first row in the grid.
61855 selectFirstRow : function(){
61860 * Select the last row.
61861 * @param {Boolean} keepExisting (optional) True to keep existing selections
61863 selectLastRow : function(keepExisting){
61864 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61868 * Selects the row immediately following the last selected row.
61869 * @param {Boolean} keepExisting (optional) True to keep existing selections
61871 selectNext : function(keepExisting){
61872 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61873 this.selectRow(this.last+1, keepExisting);
61874 var view = this.grid.view ? this.grid.view : this.grid;
61875 view.focusRow(this.last);
61880 * Selects the row that precedes the last selected row.
61881 * @param {Boolean} keepExisting (optional) True to keep existing selections
61883 selectPrevious : function(keepExisting){
61885 this.selectRow(this.last-1, keepExisting);
61886 var view = this.grid.view ? this.grid.view : this.grid;
61887 view.focusRow(this.last);
61892 * Returns the selected records
61893 * @return {Array} Array of selected records
61895 getSelections : function(){
61896 return [].concat(this.selections.items);
61900 * Returns the first selected record.
61903 getSelected : function(){
61904 return this.selections.itemAt(0);
61909 * Clears all selections.
61911 clearSelections : function(fast){
61916 var ds = this.grid.ds;
61917 var s = this.selections;
61918 s.each(function(r){
61919 this.deselectRow(ds.indexOfId(r.id));
61923 this.selections.clear();
61930 * Selects all rows.
61932 selectAll : function(){
61936 this.selections.clear();
61937 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61938 this.selectRow(i, true);
61943 * Returns True if there is a selection.
61944 * @return {Boolean}
61946 hasSelection : function(){
61947 return this.selections.length > 0;
61951 * Returns True if the specified row is selected.
61952 * @param {Number/Record} record The record or index of the record to check
61953 * @return {Boolean}
61955 isSelected : function(index){
61956 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61957 return (r && this.selections.key(r.id) ? true : false);
61961 * Returns True if the specified record id is selected.
61962 * @param {String} id The id of record to check
61963 * @return {Boolean}
61965 isIdSelected : function(id){
61966 return (this.selections.key(id) ? true : false);
61970 handleMouseDown : function(e, t)
61972 var view = this.grid.view ? this.grid.view : this.grid;
61974 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61977 if(e.shiftKey && this.last !== false){
61978 var last = this.last;
61979 this.selectRange(last, rowIndex, e.ctrlKey);
61980 this.last = last; // reset the last
61981 view.focusRow(rowIndex);
61983 var isSelected = this.isSelected(rowIndex);
61984 if(e.button !== 0 && isSelected){
61985 view.focusRow(rowIndex);
61986 }else if(e.ctrlKey && isSelected){
61987 this.deselectRow(rowIndex);
61988 }else if(!isSelected){
61989 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61990 view.focusRow(rowIndex);
61993 this.fireEvent("afterselectionchange", this);
61996 handleDragableRowClick : function(grid, rowIndex, e)
61998 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61999 this.selectRow(rowIndex, false);
62000 var view = this.grid.view ? this.grid.view : this.grid;
62001 view.focusRow(rowIndex);
62002 this.fireEvent("afterselectionchange", this);
62007 * Selects multiple rows.
62008 * @param {Array} rows Array of the indexes of the row to select
62009 * @param {Boolean} keepExisting (optional) True to keep existing selections
62011 selectRows : function(rows, keepExisting){
62013 this.clearSelections();
62015 for(var i = 0, len = rows.length; i < len; i++){
62016 this.selectRow(rows[i], true);
62021 * Selects a range of rows. All rows in between startRow and endRow are also selected.
62022 * @param {Number} startRow The index of the first row in the range
62023 * @param {Number} endRow The index of the last row in the range
62024 * @param {Boolean} keepExisting (optional) True to retain existing selections
62026 selectRange : function(startRow, endRow, keepExisting){
62031 this.clearSelections();
62033 if(startRow <= endRow){
62034 for(var i = startRow; i <= endRow; i++){
62035 this.selectRow(i, true);
62038 for(var i = startRow; i >= endRow; i--){
62039 this.selectRow(i, true);
62045 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62046 * @param {Number} startRow The index of the first row in the range
62047 * @param {Number} endRow The index of the last row in the range
62049 deselectRange : function(startRow, endRow, preventViewNotify){
62053 for(var i = startRow; i <= endRow; i++){
62054 this.deselectRow(i, preventViewNotify);
62060 * @param {Number} row The index of the row to select
62061 * @param {Boolean} keepExisting (optional) True to keep existing selections
62063 selectRow : function(index, keepExisting, preventViewNotify){
62064 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62067 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62068 if(!keepExisting || this.singleSelect){
62069 this.clearSelections();
62071 var r = this.grid.ds.getAt(index);
62072 this.selections.add(r);
62073 this.last = this.lastActive = index;
62074 if(!preventViewNotify){
62075 var view = this.grid.view ? this.grid.view : this.grid;
62076 view.onRowSelect(index);
62078 this.fireEvent("rowselect", this, index, r);
62079 this.fireEvent("selectionchange", this);
62085 * @param {Number} row The index of the row to deselect
62087 deselectRow : function(index, preventViewNotify){
62091 if(this.last == index){
62094 if(this.lastActive == index){
62095 this.lastActive = false;
62097 var r = this.grid.ds.getAt(index);
62098 this.selections.remove(r);
62099 if(!preventViewNotify){
62100 var view = this.grid.view ? this.grid.view : this.grid;
62101 view.onRowDeselect(index);
62103 this.fireEvent("rowdeselect", this, index);
62104 this.fireEvent("selectionchange", this);
62108 restoreLast : function(){
62110 this.last = this._last;
62115 acceptsNav : function(row, col, cm){
62116 return !cm.isHidden(col) && cm.isCellEditable(col, row);
62120 onEditorKey : function(field, e){
62121 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62126 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62128 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62130 }else if(k == e.ENTER && !e.ctrlKey){
62134 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62136 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62138 }else if(k == e.ESC){
62142 g.startEditing(newCell[0], newCell[1]);
62147 * Ext JS Library 1.1.1
62148 * Copyright(c) 2006-2007, Ext JS, LLC.
62150 * Originally Released Under LGPL - original licence link has changed is not relivant.
62153 * <script type="text/javascript">
62156 * @class Roo.grid.CellSelectionModel
62157 * @extends Roo.grid.AbstractSelectionModel
62158 * This class provides the basic implementation for cell selection in a grid.
62160 * @param {Object} config The object containing the configuration of this model.
62161 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62163 Roo.grid.CellSelectionModel = function(config){
62164 Roo.apply(this, config);
62166 this.selection = null;
62170 * @event beforerowselect
62171 * Fires before a cell is selected.
62172 * @param {SelectionModel} this
62173 * @param {Number} rowIndex The selected row index
62174 * @param {Number} colIndex The selected cell index
62176 "beforecellselect" : true,
62178 * @event cellselect
62179 * Fires when a cell is selected.
62180 * @param {SelectionModel} this
62181 * @param {Number} rowIndex The selected row index
62182 * @param {Number} colIndex The selected cell index
62184 "cellselect" : true,
62186 * @event selectionchange
62187 * Fires when the active selection changes.
62188 * @param {SelectionModel} this
62189 * @param {Object} selection null for no selection or an object (o) with two properties
62191 <li>o.record: the record object for the row the selection is in</li>
62192 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62195 "selectionchange" : true,
62198 * Fires when the tab (or enter) was pressed on the last editable cell
62199 * You can use this to trigger add new row.
62200 * @param {SelectionModel} this
62204 * @event beforeeditnext
62205 * Fires before the next editable sell is made active
62206 * You can use this to skip to another cell or fire the tabend
62207 * if you set cell to false
62208 * @param {Object} eventdata object : { cell : [ row, col ] }
62210 "beforeeditnext" : true
62212 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62215 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
62217 enter_is_tab: false,
62220 initEvents : function(){
62221 this.grid.on("mousedown", this.handleMouseDown, this);
62222 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62223 var view = this.grid.view;
62224 view.on("refresh", this.onViewChange, this);
62225 view.on("rowupdated", this.onRowUpdated, this);
62226 view.on("beforerowremoved", this.clearSelections, this);
62227 view.on("beforerowsinserted", this.clearSelections, this);
62228 if(this.grid.isEditor){
62229 this.grid.on("beforeedit", this.beforeEdit, this);
62234 beforeEdit : function(e){
62235 this.select(e.row, e.column, false, true, e.record);
62239 onRowUpdated : function(v, index, r){
62240 if(this.selection && this.selection.record == r){
62241 v.onCellSelect(index, this.selection.cell[1]);
62246 onViewChange : function(){
62247 this.clearSelections(true);
62251 * Returns the currently selected cell,.
62252 * @return {Array} The selected cell (row, column) or null if none selected.
62254 getSelectedCell : function(){
62255 return this.selection ? this.selection.cell : null;
62259 * Clears all selections.
62260 * @param {Boolean} true to prevent the gridview from being notified about the change.
62262 clearSelections : function(preventNotify){
62263 var s = this.selection;
62265 if(preventNotify !== true){
62266 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62268 this.selection = null;
62269 this.fireEvent("selectionchange", this, null);
62274 * Returns true if there is a selection.
62275 * @return {Boolean}
62277 hasSelection : function(){
62278 return this.selection ? true : false;
62282 handleMouseDown : function(e, t){
62283 var v = this.grid.getView();
62284 if(this.isLocked()){
62287 var row = v.findRowIndex(t);
62288 var cell = v.findCellIndex(t);
62289 if(row !== false && cell !== false){
62290 this.select(row, cell);
62296 * @param {Number} rowIndex
62297 * @param {Number} collIndex
62299 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62300 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62301 this.clearSelections();
62302 r = r || this.grid.dataSource.getAt(rowIndex);
62305 cell : [rowIndex, colIndex]
62307 if(!preventViewNotify){
62308 var v = this.grid.getView();
62309 v.onCellSelect(rowIndex, colIndex);
62310 if(preventFocus !== true){
62311 v.focusCell(rowIndex, colIndex);
62314 this.fireEvent("cellselect", this, rowIndex, colIndex);
62315 this.fireEvent("selectionchange", this, this.selection);
62320 isSelectable : function(rowIndex, colIndex, cm){
62321 return !cm.isHidden(colIndex);
62325 handleKeyDown : function(e){
62326 //Roo.log('Cell Sel Model handleKeyDown');
62327 if(!e.isNavKeyPress()){
62330 var g = this.grid, s = this.selection;
62333 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
62335 this.select(cell[0], cell[1]);
62340 var walk = function(row, col, step){
62341 return g.walkCells(row, col, step, sm.isSelectable, sm);
62343 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62350 // handled by onEditorKey
62351 if (g.isEditor && g.editing) {
62355 newCell = walk(r, c-1, -1);
62357 newCell = walk(r, c+1, 1);
62362 newCell = walk(r+1, c, 1);
62366 newCell = walk(r-1, c, -1);
62370 newCell = walk(r, c+1, 1);
62374 newCell = walk(r, c-1, -1);
62379 if(g.isEditor && !g.editing){
62380 g.startEditing(r, c);
62389 this.select(newCell[0], newCell[1]);
62395 acceptsNav : function(row, col, cm){
62396 return !cm.isHidden(col) && cm.isCellEditable(col, row);
62400 * @param {Number} field (not used) - as it's normally used as a listener
62401 * @param {Number} e - event - fake it by using
62403 * var e = Roo.EventObjectImpl.prototype;
62404 * e.keyCode = e.TAB
62408 onEditorKey : function(field, e){
62410 var k = e.getKey(),
62413 ed = g.activeEditor,
62415 ///Roo.log('onEditorKey' + k);
62418 if (this.enter_is_tab && k == e.ENTER) {
62424 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62426 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62432 } else if(k == e.ENTER && !e.ctrlKey){
62435 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62437 } else if(k == e.ESC){
62442 var ecall = { cell : newCell, forward : forward };
62443 this.fireEvent('beforeeditnext', ecall );
62444 newCell = ecall.cell;
62445 forward = ecall.forward;
62449 //Roo.log('next cell after edit');
62450 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62451 } else if (forward) {
62452 // tabbed past last
62453 this.fireEvent.defer(100, this, ['tabend',this]);
62458 * Ext JS Library 1.1.1
62459 * Copyright(c) 2006-2007, Ext JS, LLC.
62461 * Originally Released Under LGPL - original licence link has changed is not relivant.
62464 * <script type="text/javascript">
62468 * @class Roo.grid.EditorGrid
62469 * @extends Roo.grid.Grid
62470 * Class for creating and editable grid.
62471 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62472 * The container MUST have some type of size defined for the grid to fill. The container will be
62473 * automatically set to position relative if it isn't already.
62474 * @param {Object} dataSource The data model to bind to
62475 * @param {Object} colModel The column model with info about this grid's columns
62477 Roo.grid.EditorGrid = function(container, config){
62478 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62479 this.getGridEl().addClass("xedit-grid");
62481 if(!this.selModel){
62482 this.selModel = new Roo.grid.CellSelectionModel();
62485 this.activeEditor = null;
62489 * @event beforeedit
62490 * Fires before cell editing is triggered. The edit event object has the following properties <br />
62491 * <ul style="padding:5px;padding-left:16px;">
62492 * <li>grid - This grid</li>
62493 * <li>record - The record being edited</li>
62494 * <li>field - The field name being edited</li>
62495 * <li>value - The value for the field being edited.</li>
62496 * <li>row - The grid row index</li>
62497 * <li>column - The grid column index</li>
62498 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62500 * @param {Object} e An edit event (see above for description)
62502 "beforeedit" : true,
62505 * Fires after a cell is edited. <br />
62506 * <ul style="padding:5px;padding-left:16px;">
62507 * <li>grid - This grid</li>
62508 * <li>record - The record being edited</li>
62509 * <li>field - The field name being edited</li>
62510 * <li>value - The value being set</li>
62511 * <li>originalValue - The original value for the field, before the edit.</li>
62512 * <li>row - The grid row index</li>
62513 * <li>column - The grid column index</li>
62515 * @param {Object} e An edit event (see above for description)
62517 "afteredit" : true,
62519 * @event validateedit
62520 * Fires after a cell is edited, but before the value is set in the record.
62521 * You can use this to modify the value being set in the field, Return false
62522 * to cancel the change. The edit event object has the following properties <br />
62523 * <ul style="padding:5px;padding-left:16px;">
62524 * <li>editor - This editor</li>
62525 * <li>grid - This grid</li>
62526 * <li>record - The record being edited</li>
62527 * <li>field - The field name being edited</li>
62528 * <li>value - The value being set</li>
62529 * <li>originalValue - The original value for the field, before the edit.</li>
62530 * <li>row - The grid row index</li>
62531 * <li>column - The grid column index</li>
62532 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62534 * @param {Object} e An edit event (see above for description)
62536 "validateedit" : true
62538 this.on("bodyscroll", this.stopEditing, this);
62539 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
62542 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62544 * @cfg {Number} clicksToEdit
62545 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62552 trackMouseOver: false, // causes very odd FF errors
62554 onCellDblClick : function(g, row, col){
62555 this.startEditing(row, col);
62558 onEditComplete : function(ed, value, startValue){
62559 this.editing = false;
62560 this.activeEditor = null;
62561 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62563 var field = this.colModel.getDataIndex(ed.col);
62568 originalValue: startValue,
62575 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62578 if(String(value) !== String(startValue)){
62580 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62581 r.set(field, e.value);
62582 // if we are dealing with a combo box..
62583 // then we also set the 'name' colum to be the displayField
62584 if (ed.field.displayField && ed.field.name) {
62585 r.set(ed.field.name, ed.field.el.dom.value);
62588 delete e.cancel; //?? why!!!
62589 this.fireEvent("afteredit", e);
62592 this.fireEvent("afteredit", e); // always fire it!
62594 this.view.focusCell(ed.row, ed.col);
62598 * Starts editing the specified for the specified row/column
62599 * @param {Number} rowIndex
62600 * @param {Number} colIndex
62602 startEditing : function(row, col){
62603 this.stopEditing();
62604 if(this.colModel.isCellEditable(col, row)){
62605 this.view.ensureVisible(row, col, true);
62607 var r = this.dataSource.getAt(row);
62608 var field = this.colModel.getDataIndex(col);
62609 var cell = Roo.get(this.view.getCell(row,col));
62614 value: r.data[field],
62619 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62620 this.editing = true;
62621 var ed = this.colModel.getCellEditor(col, row);
62627 ed.render(ed.parentEl || document.body);
62633 (function(){ // complex but required for focus issues in safari, ie and opera
62637 ed.on("complete", this.onEditComplete, this, {single: true});
62638 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
62639 this.activeEditor = ed;
62640 var v = r.data[field];
62641 ed.startEdit(this.view.getCell(row, col), v);
62642 // combo's with 'displayField and name set
62643 if (ed.field.displayField && ed.field.name) {
62644 ed.field.el.dom.value = r.data[ed.field.name];
62648 }).defer(50, this);
62654 * Stops any active editing
62656 stopEditing : function(){
62657 if(this.activeEditor){
62658 this.activeEditor.completeEdit();
62660 this.activeEditor = null;
62664 * Called to get grid's drag proxy text, by default returns this.ddText.
62667 getDragDropText : function(){
62668 var count = this.selModel.getSelectedCell() ? 1 : 0;
62669 return String.format(this.ddText, count, count == 1 ? '' : 's');
62674 * Ext JS Library 1.1.1
62675 * Copyright(c) 2006-2007, Ext JS, LLC.
62677 * Originally Released Under LGPL - original licence link has changed is not relivant.
62680 * <script type="text/javascript">
62683 // private - not really -- you end up using it !
62684 // This is a support class used internally by the Grid components
62687 * @class Roo.grid.GridEditor
62688 * @extends Roo.Editor
62689 * Class for creating and editable grid elements.
62690 * @param {Object} config any settings (must include field)
62692 Roo.grid.GridEditor = function(field, config){
62693 if (!config && field.field) {
62695 field = Roo.factory(config.field, Roo.form);
62697 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62698 field.monitorTab = false;
62701 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62704 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62707 alignment: "tl-tl",
62710 cls: "x-small-editor x-grid-editor",
62715 * Ext JS Library 1.1.1
62716 * Copyright(c) 2006-2007, Ext JS, LLC.
62718 * Originally Released Under LGPL - original licence link has changed is not relivant.
62721 * <script type="text/javascript">
62726 Roo.grid.PropertyRecord = Roo.data.Record.create([
62727 {name:'name',type:'string'}, 'value'
62731 Roo.grid.PropertyStore = function(grid, source){
62733 this.store = new Roo.data.Store({
62734 recordType : Roo.grid.PropertyRecord
62736 this.store.on('update', this.onUpdate, this);
62738 this.setSource(source);
62740 Roo.grid.PropertyStore.superclass.constructor.call(this);
62745 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62746 setSource : function(o){
62748 this.store.removeAll();
62751 if(this.isEditableValue(o[k])){
62752 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62755 this.store.loadRecords({records: data}, {}, true);
62758 onUpdate : function(ds, record, type){
62759 if(type == Roo.data.Record.EDIT){
62760 var v = record.data['value'];
62761 var oldValue = record.modified['value'];
62762 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62763 this.source[record.id] = v;
62765 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62772 getProperty : function(row){
62773 return this.store.getAt(row);
62776 isEditableValue: function(val){
62777 if(val && val instanceof Date){
62779 }else if(typeof val == 'object' || typeof val == 'function'){
62785 setValue : function(prop, value){
62786 this.source[prop] = value;
62787 this.store.getById(prop).set('value', value);
62790 getSource : function(){
62791 return this.source;
62795 Roo.grid.PropertyColumnModel = function(grid, store){
62798 g.PropertyColumnModel.superclass.constructor.call(this, [
62799 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62800 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62802 this.store = store;
62803 this.bselect = Roo.DomHelper.append(document.body, {
62804 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62805 {tag: 'option', value: 'true', html: 'true'},
62806 {tag: 'option', value: 'false', html: 'false'}
62809 Roo.id(this.bselect);
62812 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62813 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62814 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62815 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62816 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62818 this.renderCellDelegate = this.renderCell.createDelegate(this);
62819 this.renderPropDelegate = this.renderProp.createDelegate(this);
62822 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62826 valueText : 'Value',
62828 dateFormat : 'm/j/Y',
62831 renderDate : function(dateVal){
62832 return dateVal.dateFormat(this.dateFormat);
62835 renderBool : function(bVal){
62836 return bVal ? 'true' : 'false';
62839 isCellEditable : function(colIndex, rowIndex){
62840 return colIndex == 1;
62843 getRenderer : function(col){
62845 this.renderCellDelegate : this.renderPropDelegate;
62848 renderProp : function(v){
62849 return this.getPropertyName(v);
62852 renderCell : function(val){
62854 if(val instanceof Date){
62855 rv = this.renderDate(val);
62856 }else if(typeof val == 'boolean'){
62857 rv = this.renderBool(val);
62859 return Roo.util.Format.htmlEncode(rv);
62862 getPropertyName : function(name){
62863 var pn = this.grid.propertyNames;
62864 return pn && pn[name] ? pn[name] : name;
62867 getCellEditor : function(colIndex, rowIndex){
62868 var p = this.store.getProperty(rowIndex);
62869 var n = p.data['name'], val = p.data['value'];
62871 if(typeof(this.grid.customEditors[n]) == 'string'){
62872 return this.editors[this.grid.customEditors[n]];
62874 if(typeof(this.grid.customEditors[n]) != 'undefined'){
62875 return this.grid.customEditors[n];
62877 if(val instanceof Date){
62878 return this.editors['date'];
62879 }else if(typeof val == 'number'){
62880 return this.editors['number'];
62881 }else if(typeof val == 'boolean'){
62882 return this.editors['boolean'];
62884 return this.editors['string'];
62890 * @class Roo.grid.PropertyGrid
62891 * @extends Roo.grid.EditorGrid
62892 * This class represents the interface of a component based property grid control.
62893 * <br><br>Usage:<pre><code>
62894 var grid = new Roo.grid.PropertyGrid("my-container-id", {
62902 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62903 * The container MUST have some type of size defined for the grid to fill. The container will be
62904 * automatically set to position relative if it isn't already.
62905 * @param {Object} config A config object that sets properties on this grid.
62907 Roo.grid.PropertyGrid = function(container, config){
62908 config = config || {};
62909 var store = new Roo.grid.PropertyStore(this);
62910 this.store = store;
62911 var cm = new Roo.grid.PropertyColumnModel(this, store);
62912 store.store.sort('name', 'ASC');
62913 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62916 enableColLock:false,
62917 enableColumnMove:false,
62919 trackMouseOver: false,
62922 this.getGridEl().addClass('x-props-grid');
62923 this.lastEditRow = null;
62924 this.on('columnresize', this.onColumnResize, this);
62927 * @event beforepropertychange
62928 * Fires before a property changes (return false to stop?)
62929 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62930 * @param {String} id Record Id
62931 * @param {String} newval New Value
62932 * @param {String} oldval Old Value
62934 "beforepropertychange": true,
62936 * @event propertychange
62937 * Fires after a property changes
62938 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62939 * @param {String} id Record Id
62940 * @param {String} newval New Value
62941 * @param {String} oldval Old Value
62943 "propertychange": true
62945 this.customEditors = this.customEditors || {};
62947 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62950 * @cfg {Object} customEditors map of colnames=> custom editors.
62951 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62952 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62953 * false disables editing of the field.
62957 * @cfg {Object} propertyNames map of property Names to their displayed value
62960 render : function(){
62961 Roo.grid.PropertyGrid.superclass.render.call(this);
62962 this.autoSize.defer(100, this);
62965 autoSize : function(){
62966 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62968 this.view.fitColumns();
62972 onColumnResize : function(){
62973 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62977 * Sets the data for the Grid
62978 * accepts a Key => Value object of all the elements avaiable.
62979 * @param {Object} data to appear in grid.
62981 setSource : function(source){
62982 this.store.setSource(source);
62986 * Gets all the data from the grid.
62987 * @return {Object} data data stored in grid
62989 getSource : function(){
62990 return this.store.getSource();
62999 * @class Roo.grid.Calendar
63000 * @extends Roo.grid.Grid
63001 * This class extends the Grid to provide a calendar widget
63002 * <br><br>Usage:<pre><code>
63003 var grid = new Roo.grid.Calendar("my-container-id", {
63006 selModel: mySelectionModel,
63007 autoSizeColumns: true,
63008 monitorWindowResize: false,
63009 trackMouseOver: true
63010 eventstore : real data store..
63016 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63017 * The container MUST have some type of size defined for the grid to fill. The container will be
63018 * automatically set to position relative if it isn't already.
63019 * @param {Object} config A config object that sets properties on this grid.
63021 Roo.grid.Calendar = function(container, config){
63022 // initialize the container
63023 this.container = Roo.get(container);
63024 this.container.update("");
63025 this.container.setStyle("overflow", "hidden");
63026 this.container.addClass('x-grid-container');
63028 this.id = this.container.id;
63030 Roo.apply(this, config);
63031 // check and correct shorthanded configs
63035 for (var r = 0;r < 6;r++) {
63038 for (var c =0;c < 7;c++) {
63042 if (this.eventStore) {
63043 this.eventStore= Roo.factory(this.eventStore, Roo.data);
63044 this.eventStore.on('load',this.onLoad, this);
63045 this.eventStore.on('beforeload',this.clearEvents, this);
63049 this.dataSource = new Roo.data.Store({
63050 proxy: new Roo.data.MemoryProxy(rows),
63051 reader: new Roo.data.ArrayReader({}, [
63052 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63055 this.dataSource.load();
63056 this.ds = this.dataSource;
63057 this.ds.xmodule = this.xmodule || false;
63060 var cellRender = function(v,x,r)
63062 return String.format(
63063 '<div class="fc-day fc-widget-content"><div>' +
63064 '<div class="fc-event-container"></div>' +
63065 '<div class="fc-day-number">{0}</div>'+
63067 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63068 '</div></div>', v);
63073 this.colModel = new Roo.grid.ColumnModel( [
63075 xtype: 'ColumnModel',
63077 dataIndex : 'weekday0',
63079 renderer : cellRender
63082 xtype: 'ColumnModel',
63084 dataIndex : 'weekday1',
63086 renderer : cellRender
63089 xtype: 'ColumnModel',
63091 dataIndex : 'weekday2',
63092 header : 'Tuesday',
63093 renderer : cellRender
63096 xtype: 'ColumnModel',
63098 dataIndex : 'weekday3',
63099 header : 'Wednesday',
63100 renderer : cellRender
63103 xtype: 'ColumnModel',
63105 dataIndex : 'weekday4',
63106 header : 'Thursday',
63107 renderer : cellRender
63110 xtype: 'ColumnModel',
63112 dataIndex : 'weekday5',
63114 renderer : cellRender
63117 xtype: 'ColumnModel',
63119 dataIndex : 'weekday6',
63120 header : 'Saturday',
63121 renderer : cellRender
63124 this.cm = this.colModel;
63125 this.cm.xmodule = this.xmodule || false;
63129 //this.selModel = new Roo.grid.CellSelectionModel();
63130 //this.sm = this.selModel;
63131 //this.selModel.init(this);
63135 this.container.setWidth(this.width);
63139 this.container.setHeight(this.height);
63146 * The raw click event for the entire grid.
63147 * @param {Roo.EventObject} e
63152 * The raw dblclick event for the entire grid.
63153 * @param {Roo.EventObject} e
63157 * @event contextmenu
63158 * The raw contextmenu event for the entire grid.
63159 * @param {Roo.EventObject} e
63161 "contextmenu" : true,
63164 * The raw mousedown event for the entire grid.
63165 * @param {Roo.EventObject} e
63167 "mousedown" : true,
63170 * The raw mouseup event for the entire grid.
63171 * @param {Roo.EventObject} e
63176 * The raw mouseover event for the entire grid.
63177 * @param {Roo.EventObject} e
63179 "mouseover" : true,
63182 * The raw mouseout event for the entire grid.
63183 * @param {Roo.EventObject} e
63188 * The raw keypress event for the entire grid.
63189 * @param {Roo.EventObject} e
63194 * The raw keydown event for the entire grid.
63195 * @param {Roo.EventObject} e
63203 * Fires when a cell is clicked
63204 * @param {Grid} this
63205 * @param {Number} rowIndex
63206 * @param {Number} columnIndex
63207 * @param {Roo.EventObject} e
63209 "cellclick" : true,
63211 * @event celldblclick
63212 * Fires when a cell is double clicked
63213 * @param {Grid} this
63214 * @param {Number} rowIndex
63215 * @param {Number} columnIndex
63216 * @param {Roo.EventObject} e
63218 "celldblclick" : true,
63221 * Fires when a row is clicked
63222 * @param {Grid} this
63223 * @param {Number} rowIndex
63224 * @param {Roo.EventObject} e
63228 * @event rowdblclick
63229 * Fires when a row is double clicked
63230 * @param {Grid} this
63231 * @param {Number} rowIndex
63232 * @param {Roo.EventObject} e
63234 "rowdblclick" : true,
63236 * @event headerclick
63237 * Fires when a header is clicked
63238 * @param {Grid} this
63239 * @param {Number} columnIndex
63240 * @param {Roo.EventObject} e
63242 "headerclick" : true,
63244 * @event headerdblclick
63245 * Fires when a header cell is double clicked
63246 * @param {Grid} this
63247 * @param {Number} columnIndex
63248 * @param {Roo.EventObject} e
63250 "headerdblclick" : true,
63252 * @event rowcontextmenu
63253 * Fires when a row is right clicked
63254 * @param {Grid} this
63255 * @param {Number} rowIndex
63256 * @param {Roo.EventObject} e
63258 "rowcontextmenu" : true,
63260 * @event cellcontextmenu
63261 * Fires when a cell is right clicked
63262 * @param {Grid} this
63263 * @param {Number} rowIndex
63264 * @param {Number} cellIndex
63265 * @param {Roo.EventObject} e
63267 "cellcontextmenu" : true,
63269 * @event headercontextmenu
63270 * Fires when a header is right clicked
63271 * @param {Grid} this
63272 * @param {Number} columnIndex
63273 * @param {Roo.EventObject} e
63275 "headercontextmenu" : true,
63277 * @event bodyscroll
63278 * Fires when the body element is scrolled
63279 * @param {Number} scrollLeft
63280 * @param {Number} scrollTop
63282 "bodyscroll" : true,
63284 * @event columnresize
63285 * Fires when the user resizes a column
63286 * @param {Number} columnIndex
63287 * @param {Number} newSize
63289 "columnresize" : true,
63291 * @event columnmove
63292 * Fires when the user moves a column
63293 * @param {Number} oldIndex
63294 * @param {Number} newIndex
63296 "columnmove" : true,
63299 * Fires when row(s) start being dragged
63300 * @param {Grid} this
63301 * @param {Roo.GridDD} dd The drag drop object
63302 * @param {event} e The raw browser event
63304 "startdrag" : true,
63307 * Fires when a drag operation is complete
63308 * @param {Grid} this
63309 * @param {Roo.GridDD} dd The drag drop object
63310 * @param {event} e The raw browser event
63315 * Fires when dragged row(s) are dropped on a valid DD target
63316 * @param {Grid} this
63317 * @param {Roo.GridDD} dd The drag drop object
63318 * @param {String} targetId The target drag drop object
63319 * @param {event} e The raw browser event
63324 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63325 * @param {Grid} this
63326 * @param {Roo.GridDD} dd The drag drop object
63327 * @param {String} targetId The target drag drop object
63328 * @param {event} e The raw browser event
63333 * Fires when the dragged row(s) first cross another DD target while being dragged
63334 * @param {Grid} this
63335 * @param {Roo.GridDD} dd The drag drop object
63336 * @param {String} targetId The target drag drop object
63337 * @param {event} e The raw browser event
63339 "dragenter" : true,
63342 * Fires when the dragged row(s) leave another DD target while being dragged
63343 * @param {Grid} this
63344 * @param {Roo.GridDD} dd The drag drop object
63345 * @param {String} targetId The target drag drop object
63346 * @param {event} e The raw browser event
63351 * Fires when a row is rendered, so you can change add a style to it.
63352 * @param {GridView} gridview The grid view
63353 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
63359 * Fires when the grid is rendered
63360 * @param {Grid} grid
63365 * Fires when a date is selected
63366 * @param {DatePicker} this
63367 * @param {Date} date The selected date
63371 * @event monthchange
63372 * Fires when the displayed month changes
63373 * @param {DatePicker} this
63374 * @param {Date} date The selected month
63376 'monthchange': true,
63378 * @event evententer
63379 * Fires when mouse over an event
63380 * @param {Calendar} this
63381 * @param {event} Event
63383 'evententer': true,
63385 * @event eventleave
63386 * Fires when the mouse leaves an
63387 * @param {Calendar} this
63390 'eventleave': true,
63392 * @event eventclick
63393 * Fires when the mouse click an
63394 * @param {Calendar} this
63397 'eventclick': true,
63399 * @event eventrender
63400 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63401 * @param {Calendar} this
63402 * @param {data} data to be modified
63404 'eventrender': true
63408 Roo.grid.Grid.superclass.constructor.call(this);
63409 this.on('render', function() {
63410 this.view.el.addClass('x-grid-cal');
63412 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63416 if (!Roo.grid.Calendar.style) {
63417 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63420 '.x-grid-cal .x-grid-col' : {
63421 height: 'auto !important',
63422 'vertical-align': 'top'
63424 '.x-grid-cal .fc-event-hori' : {
63435 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63437 * @cfg {Store} eventStore The store that loads events.
63442 activeDate : false,
63445 monitorWindowResize : false,
63448 resizeColumns : function() {
63449 var col = (this.view.el.getWidth() / 7) - 3;
63450 // loop through cols, and setWidth
63451 for(var i =0 ; i < 7 ; i++){
63452 this.cm.setColumnWidth(i, col);
63455 setDate :function(date) {
63457 Roo.log('setDate?');
63459 this.resizeColumns();
63460 var vd = this.activeDate;
63461 this.activeDate = date;
63462 // if(vd && this.el){
63463 // var t = date.getTime();
63464 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63465 // Roo.log('using add remove');
63467 // this.fireEvent('monthchange', this, date);
63469 // this.cells.removeClass("fc-state-highlight");
63470 // this.cells.each(function(c){
63471 // if(c.dateValue == t){
63472 // c.addClass("fc-state-highlight");
63473 // setTimeout(function(){
63474 // try{c.dom.firstChild.focus();}catch(e){}
63484 var days = date.getDaysInMonth();
63486 var firstOfMonth = date.getFirstDateOfMonth();
63487 var startingPos = firstOfMonth.getDay()-this.startDay;
63489 if(startingPos < this.startDay){
63493 var pm = date.add(Date.MONTH, -1);
63494 var prevStart = pm.getDaysInMonth()-startingPos;
63498 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63500 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63501 //this.cells.addClassOnOver('fc-state-hover');
63503 var cells = this.cells.elements;
63504 var textEls = this.textNodes;
63506 //Roo.each(cells, function(cell){
63507 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63510 days += startingPos;
63512 // convert everything to numbers so it's fast
63513 var day = 86400000;
63514 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63517 //Roo.log(prevStart);
63519 var today = new Date().clearTime().getTime();
63520 var sel = date.clearTime().getTime();
63521 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63522 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63523 var ddMatch = this.disabledDatesRE;
63524 var ddText = this.disabledDatesText;
63525 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63526 var ddaysText = this.disabledDaysText;
63527 var format = this.format;
63529 var setCellClass = function(cal, cell){
63531 //Roo.log('set Cell Class');
63533 var t = d.getTime();
63538 cell.dateValue = t;
63540 cell.className += " fc-today";
63541 cell.className += " fc-state-highlight";
63542 cell.title = cal.todayText;
63545 // disable highlight in other month..
63546 cell.className += " fc-state-highlight";
63551 //cell.className = " fc-state-disabled";
63552 cell.title = cal.minText;
63556 //cell.className = " fc-state-disabled";
63557 cell.title = cal.maxText;
63561 if(ddays.indexOf(d.getDay()) != -1){
63562 // cell.title = ddaysText;
63563 // cell.className = " fc-state-disabled";
63566 if(ddMatch && format){
63567 var fvalue = d.dateFormat(format);
63568 if(ddMatch.test(fvalue)){
63569 cell.title = ddText.replace("%0", fvalue);
63570 cell.className = " fc-state-disabled";
63574 if (!cell.initialClassName) {
63575 cell.initialClassName = cell.dom.className;
63578 cell.dom.className = cell.initialClassName + ' ' + cell.className;
63583 for(; i < startingPos; i++) {
63584 cells[i].dayName = (++prevStart);
63585 Roo.log(textEls[i]);
63586 d.setDate(d.getDate()+1);
63588 //cells[i].className = "fc-past fc-other-month";
63589 setCellClass(this, cells[i]);
63594 for(; i < days; i++){
63595 intDay = i - startingPos + 1;
63596 cells[i].dayName = (intDay);
63597 d.setDate(d.getDate()+1);
63599 cells[i].className = ''; // "x-date-active";
63600 setCellClass(this, cells[i]);
63604 for(; i < 42; i++) {
63605 //textEls[i].innerHTML = (++extraDays);
63607 d.setDate(d.getDate()+1);
63608 cells[i].dayName = (++extraDays);
63609 cells[i].className = "fc-future fc-other-month";
63610 setCellClass(this, cells[i]);
63613 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63615 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63617 // this will cause all the cells to mis
63620 for (var r = 0;r < 6;r++) {
63621 for (var c =0;c < 7;c++) {
63622 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63626 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63627 for(i=0;i<cells.length;i++) {
63629 this.cells.elements[i].dayName = cells[i].dayName ;
63630 this.cells.elements[i].className = cells[i].className;
63631 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63632 this.cells.elements[i].title = cells[i].title ;
63633 this.cells.elements[i].dateValue = cells[i].dateValue ;
63639 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63640 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63642 ////if(totalRows != 6){
63643 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63644 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63647 this.fireEvent('monthchange', this, date);
63652 * Returns the grid's SelectionModel.
63653 * @return {SelectionModel}
63655 getSelectionModel : function(){
63656 if(!this.selModel){
63657 this.selModel = new Roo.grid.CellSelectionModel();
63659 return this.selModel;
63663 this.eventStore.load()
63669 findCell : function(dt) {
63670 dt = dt.clearTime().getTime();
63672 this.cells.each(function(c){
63673 //Roo.log("check " +c.dateValue + '?=' + dt);
63674 if(c.dateValue == dt){
63684 findCells : function(rec) {
63685 var s = rec.data.start_dt.clone().clearTime().getTime();
63687 var e= rec.data.end_dt.clone().clearTime().getTime();
63690 this.cells.each(function(c){
63691 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63693 if(c.dateValue > e){
63696 if(c.dateValue < s){
63705 findBestRow: function(cells)
63709 for (var i =0 ; i < cells.length;i++) {
63710 ret = Math.max(cells[i].rows || 0,ret);
63717 addItem : function(rec)
63719 // look for vertical location slot in
63720 var cells = this.findCells(rec);
63722 rec.row = this.findBestRow(cells);
63724 // work out the location.
63728 for(var i =0; i < cells.length; i++) {
63736 if (crow.start.getY() == cells[i].getY()) {
63738 crow.end = cells[i];
63754 for (var i = 0; i < cells.length;i++) {
63755 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63762 clearEvents: function() {
63764 if (!this.eventStore.getCount()) {
63767 // reset number of rows in cells.
63768 Roo.each(this.cells.elements, function(c){
63772 this.eventStore.each(function(e) {
63773 this.clearEvent(e);
63778 clearEvent : function(ev)
63781 Roo.each(ev.els, function(el) {
63782 el.un('mouseenter' ,this.onEventEnter, this);
63783 el.un('mouseleave' ,this.onEventLeave, this);
63791 renderEvent : function(ev,ctr) {
63793 ctr = this.view.el.select('.fc-event-container',true).first();
63797 this.clearEvent(ev);
63803 var cells = ev.cells;
63804 var rows = ev.rows;
63805 this.fireEvent('eventrender', this, ev);
63807 for(var i =0; i < rows.length; i++) {
63811 cls += ' fc-event-start';
63813 if ((i+1) == rows.length) {
63814 cls += ' fc-event-end';
63817 //Roo.log(ev.data);
63818 // how many rows should it span..
63819 var cg = this.eventTmpl.append(ctr,Roo.apply({
63822 }, ev.data) , true);
63825 cg.on('mouseenter' ,this.onEventEnter, this, ev);
63826 cg.on('mouseleave' ,this.onEventLeave, this, ev);
63827 cg.on('click', this.onEventClick, this, ev);
63831 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63832 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63835 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
63836 cg.setWidth(ebox.right - sbox.x -2);
63840 renderEvents: function()
63842 // first make sure there is enough space..
63844 if (!this.eventTmpl) {
63845 this.eventTmpl = new Roo.Template(
63846 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
63847 '<div class="fc-event-inner">' +
63848 '<span class="fc-event-time">{time}</span>' +
63849 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63851 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
63859 this.cells.each(function(c) {
63860 //Roo.log(c.select('.fc-day-content div',true).first());
63861 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63864 var ctr = this.view.el.select('.fc-event-container',true).first();
63867 this.eventStore.each(function(ev){
63869 this.renderEvent(ev);
63873 this.view.layout();
63877 onEventEnter: function (e, el,event,d) {
63878 this.fireEvent('evententer', this, el, event);
63881 onEventLeave: function (e, el,event,d) {
63882 this.fireEvent('eventleave', this, el, event);
63885 onEventClick: function (e, el,event,d) {
63886 this.fireEvent('eventclick', this, el, event);
63889 onMonthChange: function () {
63893 onLoad: function () {
63895 //Roo.log('calendar onload');
63897 if(this.eventStore.getCount() > 0){
63901 this.eventStore.each(function(d){
63906 if (typeof(add.end_dt) == 'undefined') {
63907 Roo.log("Missing End time in calendar data: ");
63911 if (typeof(add.start_dt) == 'undefined') {
63912 Roo.log("Missing Start time in calendar data: ");
63916 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63917 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63918 add.id = add.id || d.id;
63919 add.title = add.title || '??';
63927 this.renderEvents();
63937 render : function ()
63941 if (!this.view.el.hasClass('course-timesheet')) {
63942 this.view.el.addClass('course-timesheet');
63944 if (this.tsStyle) {
63949 Roo.log(_this.grid.view.el.getWidth());
63952 this.tsStyle = Roo.util.CSS.createStyleSheet({
63953 '.course-timesheet .x-grid-row' : {
63956 '.x-grid-row td' : {
63957 'vertical-align' : 0
63959 '.course-edit-link' : {
63961 'text-overflow' : 'ellipsis',
63962 'overflow' : 'hidden',
63963 'white-space' : 'nowrap',
63964 'cursor' : 'pointer'
63969 '.de-act-sup-link' : {
63970 'color' : 'purple',
63971 'text-decoration' : 'line-through'
63975 'text-decoration' : 'line-through'
63977 '.course-timesheet .course-highlight' : {
63978 'border-top-style': 'dashed !important',
63979 'border-bottom-bottom': 'dashed !important'
63981 '.course-timesheet .course-item' : {
63982 'font-family' : 'tahoma, arial, helvetica',
63983 'font-size' : '11px',
63984 'overflow' : 'hidden',
63985 'padding-left' : '10px',
63986 'padding-right' : '10px',
63987 'padding-top' : '10px'
63995 monitorWindowResize : false,
63996 cellrenderer : function(v,x,r)
64001 xtype: 'CellSelectionModel',
64008 beforeload : function (_self, options)
64010 options.params = options.params || {};
64011 options.params._month = _this.monthField.getValue();
64012 options.params.limit = 9999;
64013 options.params['sort'] = 'when_dt';
64014 options.params['dir'] = 'ASC';
64015 this.proxy.loadResponse = this.loadResponse;
64017 //this.addColumns();
64019 load : function (_self, records, options)
64021 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64022 // if you click on the translation.. you can edit it...
64023 var el = Roo.get(this);
64024 var id = el.dom.getAttribute('data-id');
64025 var d = el.dom.getAttribute('data-date');
64026 var t = el.dom.getAttribute('data-time');
64027 //var id = this.child('span').dom.textContent;
64030 Pman.Dialog.CourseCalendar.show({
64034 productitem_active : id ? 1 : 0
64036 _this.grid.ds.load({});
64041 _this.panel.fireEvent('resize', [ '', '' ]);
64044 loadResponse : function(o, success, response){
64045 // this is overridden on before load..
64047 Roo.log("our code?");
64048 //Roo.log(success);
64049 //Roo.log(response)
64050 delete this.activeRequest;
64052 this.fireEvent("loadexception", this, o, response);
64053 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64058 result = o.reader.read(response);
64060 Roo.log("load exception?");
64061 this.fireEvent("loadexception", this, o, response, e);
64062 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64065 Roo.log("ready...");
64066 // loop through result.records;
64067 // and set this.tdate[date] = [] << array of records..
64069 Roo.each(result.records, function(r){
64071 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64072 _this.tdata[r.data.when_dt.format('j')] = [];
64074 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64077 //Roo.log(_this.tdata);
64079 result.records = [];
64080 result.totalRecords = 6;
64082 // let's generate some duumy records for the rows.
64083 //var st = _this.dateField.getValue();
64085 // work out monday..
64086 //st = st.add(Date.DAY, -1 * st.format('w'));
64088 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64090 var firstOfMonth = date.getFirstDayOfMonth();
64091 var days = date.getDaysInMonth();
64093 var firstAdded = false;
64094 for (var i = 0; i < result.totalRecords ; i++) {
64095 //var d= st.add(Date.DAY, i);
64098 for(var w = 0 ; w < 7 ; w++){
64099 if(!firstAdded && firstOfMonth != w){
64106 var dd = (d > 0 && d < 10) ? "0"+d : d;
64107 row['weekday'+w] = String.format(
64108 '<span style="font-size: 16px;"><b>{0}</b></span>'+
64109 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64111 date.format('Y-m-')+dd
64114 if(typeof(_this.tdata[d]) != 'undefined'){
64115 Roo.each(_this.tdata[d], function(r){
64119 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64120 if(r.parent_id*1>0){
64121 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64124 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64125 deactive = 'de-act-link';
64128 row['weekday'+w] += String.format(
64129 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64131 r.product_id_name, //1
64132 r.when_dt.format('h:ia'), //2
64142 // only do this if something added..
64144 result.records.push(_this.grid.dataSource.reader.newRow(row));
64148 // push it twice. (second one with an hour..
64152 this.fireEvent("load", this, o, o.request.arg);
64153 o.request.callback.call(o.request.scope, result, o.request.arg, true);
64155 sortInfo : {field: 'when_dt', direction : 'ASC' },
64157 xtype: 'HttpProxy',
64160 url : baseURL + '/Roo/Shop_course.php'
64163 xtype: 'JsonReader',
64180 'name': 'parent_id',
64184 'name': 'product_id',
64188 'name': 'productitem_id',
64206 click : function (_self, e)
64208 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64209 sd.setMonth(sd.getMonth()-1);
64210 _this.monthField.setValue(sd.format('Y-m-d'));
64211 _this.grid.ds.load({});
64217 xtype: 'Separator',
64221 xtype: 'MonthField',
64224 render : function (_self)
64226 _this.monthField = _self;
64227 // _this.monthField.set today
64229 select : function (combo, date)
64231 _this.grid.ds.load({});
64234 value : (function() { return new Date(); })()
64237 xtype: 'Separator',
64243 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64253 click : function (_self, e)
64255 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64256 sd.setMonth(sd.getMonth()+1);
64257 _this.monthField.setValue(sd.format('Y-m-d'));
64258 _this.grid.ds.load({});
64271 * Ext JS Library 1.1.1
64272 * Copyright(c) 2006-2007, Ext JS, LLC.
64274 * Originally Released Under LGPL - original licence link has changed is not relivant.
64277 * <script type="text/javascript">
64281 * @class Roo.LoadMask
64282 * A simple utility class for generically masking elements while loading data. If the element being masked has
64283 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64284 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
64285 * element's UpdateManager load indicator and will be destroyed after the initial load.
64287 * Create a new LoadMask
64288 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64289 * @param {Object} config The config object
64291 Roo.LoadMask = function(el, config){
64292 this.el = Roo.get(el);
64293 Roo.apply(this, config);
64295 this.store.on('beforeload', this.onBeforeLoad, this);
64296 this.store.on('load', this.onLoad, this);
64297 this.store.on('loadexception', this.onLoadException, this);
64298 this.removeMask = false;
64300 var um = this.el.getUpdateManager();
64301 um.showLoadIndicator = false; // disable the default indicator
64302 um.on('beforeupdate', this.onBeforeLoad, this);
64303 um.on('update', this.onLoad, this);
64304 um.on('failure', this.onLoad, this);
64305 this.removeMask = true;
64309 Roo.LoadMask.prototype = {
64311 * @cfg {Boolean} removeMask
64312 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64313 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
64315 removeMask : false,
64317 * @cfg {String} msg
64318 * The text to display in a centered loading message box (defaults to 'Loading...')
64320 msg : 'Loading...',
64322 * @cfg {String} msgCls
64323 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64325 msgCls : 'x-mask-loading',
64328 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64334 * Disables the mask to prevent it from being displayed
64336 disable : function(){
64337 this.disabled = true;
64341 * Enables the mask so that it can be displayed
64343 enable : function(){
64344 this.disabled = false;
64347 onLoadException : function()
64349 Roo.log(arguments);
64351 if (typeof(arguments[3]) != 'undefined') {
64352 Roo.MessageBox.alert("Error loading",arguments[3]);
64356 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64357 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64364 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64367 onLoad : function()
64369 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64373 onBeforeLoad : function(){
64374 if(!this.disabled){
64375 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64380 destroy : function(){
64382 this.store.un('beforeload', this.onBeforeLoad, this);
64383 this.store.un('load', this.onLoad, this);
64384 this.store.un('loadexception', this.onLoadException, this);
64386 var um = this.el.getUpdateManager();
64387 um.un('beforeupdate', this.onBeforeLoad, this);
64388 um.un('update', this.onLoad, this);
64389 um.un('failure', this.onLoad, this);
64394 * Ext JS Library 1.1.1
64395 * Copyright(c) 2006-2007, Ext JS, LLC.
64397 * Originally Released Under LGPL - original licence link has changed is not relivant.
64400 * <script type="text/javascript">
64405 * @class Roo.XTemplate
64406 * @extends Roo.Template
64407 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64409 var t = new Roo.XTemplate(
64410 '<select name="{name}">',
64411 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
64415 // then append, applying the master template values
64418 * Supported features:
64423 {a_variable} - output encoded.
64424 {a_variable.format:("Y-m-d")} - call a method on the variable
64425 {a_variable:raw} - unencoded output
64426 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64427 {a_variable:this.method_on_template(...)} - call a method on the template object.
64432 <tpl for="a_variable or condition.."></tpl>
64433 <tpl if="a_variable or condition"></tpl>
64434 <tpl exec="some javascript"></tpl>
64435 <tpl name="named_template"></tpl> (experimental)
64437 <tpl for="."></tpl> - just iterate the property..
64438 <tpl for=".."></tpl> - iterates with the parent (probably the template)
64442 Roo.XTemplate = function()
64444 Roo.XTemplate.superclass.constructor.apply(this, arguments);
64451 Roo.extend(Roo.XTemplate, Roo.Template, {
64454 * The various sub templates
64459 * basic tag replacing syntax
64462 * // you can fake an object call by doing this
64466 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64469 * compile the template
64471 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64474 compile: function()
64478 s = ['<tpl>', s, '</tpl>'].join('');
64480 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64481 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64482 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
64483 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64484 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
64489 while(true == !!(m = s.match(re))){
64490 var forMatch = m[0].match(nameRe),
64491 ifMatch = m[0].match(ifRe),
64492 execMatch = m[0].match(execRe),
64493 namedMatch = m[0].match(namedRe),
64498 name = forMatch && forMatch[1] ? forMatch[1] : '';
64501 // if - puts fn into test..
64502 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64504 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64509 // exec - calls a function... returns empty if true is returned.
64510 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64512 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64520 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64521 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64522 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64525 var uid = namedMatch ? namedMatch[1] : id;
64529 id: namedMatch ? namedMatch[1] : id,
64536 s = s.replace(m[0], '');
64538 s = s.replace(m[0], '{xtpl'+ id + '}');
64543 for(var i = tpls.length-1; i >= 0; --i){
64544 this.compileTpl(tpls[i]);
64545 this.tpls[tpls[i].id] = tpls[i];
64547 this.master = tpls[tpls.length-1];
64551 * same as applyTemplate, except it's done to one of the subTemplates
64552 * when using named templates, you can do:
64554 * var str = pl.applySubTemplate('your-name', values);
64557 * @param {Number} id of the template
64558 * @param {Object} values to apply to template
64559 * @param {Object} parent (normaly the instance of this object)
64561 applySubTemplate : function(id, values, parent)
64565 var t = this.tpls[id];
64569 if(t.test && !t.test.call(this, values, parent)){
64573 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64574 Roo.log(e.toString());
64580 if(t.exec && t.exec.call(this, values, parent)){
64584 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64585 Roo.log(e.toString());
64590 var vs = t.target ? t.target.call(this, values, parent) : values;
64591 parent = t.target ? values : parent;
64592 if(t.target && vs instanceof Array){
64594 for(var i = 0, len = vs.length; i < len; i++){
64595 buf[buf.length] = t.compiled.call(this, vs[i], parent);
64597 return buf.join('');
64599 return t.compiled.call(this, vs, parent);
64601 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64602 Roo.log(e.toString());
64603 Roo.log(t.compiled);
64608 compileTpl : function(tpl)
64610 var fm = Roo.util.Format;
64611 var useF = this.disableFormats !== true;
64612 var sep = Roo.isGecko ? "+" : ",";
64613 var undef = function(str) {
64614 Roo.log("Property not found :" + str);
64618 var fn = function(m, name, format, args)
64620 //Roo.log(arguments);
64621 args = args ? args.replace(/\\'/g,"'") : args;
64622 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64623 if (typeof(format) == 'undefined') {
64624 format= 'htmlEncode';
64626 if (format == 'raw' ) {
64630 if(name.substr(0, 4) == 'xtpl'){
64631 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64634 // build an array of options to determine if value is undefined..
64636 // basically get 'xxxx.yyyy' then do
64637 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64638 // (function () { Roo.log("Property not found"); return ''; })() :
64643 Roo.each(name.split('.'), function(st) {
64644 lookfor += (lookfor.length ? '.': '') + st;
64645 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
64648 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64651 if(format && useF){
64653 args = args ? ',' + args : "";
64655 if(format.substr(0, 5) != "this."){
64656 format = "fm." + format + '(';
64658 format = 'this.call("'+ format.substr(5) + '", ';
64662 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
64666 // called with xxyx.yuu:(test,test)
64668 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
64670 // raw.. - :raw modifier..
64671 return "'"+ sep + udef_st + name + ")"+sep+"'";
64675 // branched to use + in gecko and [].join() in others
64677 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
64678 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64681 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
64682 body.push(tpl.body.replace(/(\r\n|\n)/g,
64683 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64684 body.push("'].join('');};};");
64685 body = body.join('');
64688 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64690 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
64696 applyTemplate : function(values){
64697 return this.master.compiled.call(this, values, {});
64698 //var s = this.subs;
64701 apply : function(){
64702 return this.applyTemplate.apply(this, arguments);
64707 Roo.XTemplate.from = function(el){
64708 el = Roo.getDom(el);
64709 return new Roo.XTemplate(el.value || el.innerHTML);