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)
47472 var nodeRange = node.ownerDocument.createRange();
47474 nodeRange.selectNode(node);
47476 nodeRange.selectNodeContents(node);
47478 //nodeRange.collapse(true);
47479 var s = this.win.getSelection();
47480 s.removeAllRanges();
47481 s.addRange(nodeRange);
47484 getSelectedNode: function()
47486 // this may only work on Gecko!!!
47488 // should we cache this!!!!
47493 var range = this.createRange(this.getSelection()).cloneRange();
47496 var parent = range.parentElement();
47498 var testRange = range.duplicate();
47499 testRange.moveToElementText(parent);
47500 if (testRange.inRange(range)) {
47503 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47506 parent = parent.parentElement;
47511 // is ancestor a text element.
47512 var ac = range.commonAncestorContainer;
47513 if (ac.nodeType == 3) {
47514 ac = ac.parentNode;
47517 var ar = ac.childNodes;
47520 var other_nodes = [];
47521 var has_other_nodes = false;
47522 for (var i=0;i<ar.length;i++) {
47523 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
47526 // fullly contained node.
47528 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47533 // probably selected..
47534 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47535 other_nodes.push(ar[i]);
47539 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
47544 has_other_nodes = true;
47546 if (!nodes.length && other_nodes.length) {
47547 nodes= other_nodes;
47549 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47555 createRange: function(sel)
47557 // this has strange effects when using with
47558 // top toolbar - not sure if it's a great idea.
47559 //this.editor.contentWindow.focus();
47560 if (typeof sel != "undefined") {
47562 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47564 return this.doc.createRange();
47567 return this.doc.createRange();
47570 getParentElement: function()
47573 this.assignDocWin();
47574 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47576 var range = this.createRange(sel);
47579 var p = range.commonAncestorContainer;
47580 while (p.nodeType == 3) { // text node
47591 * Range intersection.. the hard stuff...
47595 * [ -- selected range --- ]
47599 * if end is before start or hits it. fail.
47600 * if start is after end or hits it fail.
47602 * if either hits (but other is outside. - then it's not
47608 // @see http://www.thismuchiknow.co.uk/?p=64.
47609 rangeIntersectsNode : function(range, node)
47611 var nodeRange = node.ownerDocument.createRange();
47613 nodeRange.selectNode(node);
47615 nodeRange.selectNodeContents(node);
47618 var rangeStartRange = range.cloneRange();
47619 rangeStartRange.collapse(true);
47621 var rangeEndRange = range.cloneRange();
47622 rangeEndRange.collapse(false);
47624 var nodeStartRange = nodeRange.cloneRange();
47625 nodeStartRange.collapse(true);
47627 var nodeEndRange = nodeRange.cloneRange();
47628 nodeEndRange.collapse(false);
47630 return rangeStartRange.compareBoundaryPoints(
47631 Range.START_TO_START, nodeEndRange) == -1 &&
47632 rangeEndRange.compareBoundaryPoints(
47633 Range.START_TO_START, nodeStartRange) == 1;
47637 rangeCompareNode : function(range, node)
47639 var nodeRange = node.ownerDocument.createRange();
47641 nodeRange.selectNode(node);
47643 nodeRange.selectNodeContents(node);
47647 range.collapse(true);
47649 nodeRange.collapse(true);
47651 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47652 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
47654 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47656 var nodeIsBefore = ss == 1;
47657 var nodeIsAfter = ee == -1;
47659 if (nodeIsBefore && nodeIsAfter) {
47662 if (!nodeIsBefore && nodeIsAfter) {
47663 return 1; //right trailed.
47666 if (nodeIsBefore && !nodeIsAfter) {
47667 return 2; // left trailed.
47673 cleanWordChars : function(input) {// change the chars to hex code
47676 [ 8211, "–" ],
47677 [ 8212, "—" ],
47685 var output = input;
47686 Roo.each(swapCodes, function(sw) {
47687 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47689 output = output.replace(swapper, sw[1]);
47699 cleanUpChild : function (node)
47702 new Roo.htmleditor.FilterComment({node : node});
47703 new Roo.htmleditor.FilterAttributes({
47705 attrib_black : this.ablack,
47706 attrib_clean : this.aclean,
47707 style_white : this.cwhite,
47708 style_black : this.cblack
47710 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47711 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47717 * Clean up MS wordisms...
47718 * @deprecated - use filter directly
47720 cleanWord : function(node)
47722 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47729 * @deprecated - use filters
47731 cleanTableWidths : function(node)
47733 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47740 applyBlacklists : function()
47742 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
47743 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
47745 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
47746 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
47747 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
47751 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47752 if (b.indexOf(tag) > -1) {
47755 this.white.push(tag);
47759 Roo.each(w, function(tag) {
47760 if (b.indexOf(tag) > -1) {
47763 if (this.white.indexOf(tag) > -1) {
47766 this.white.push(tag);
47771 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47772 if (w.indexOf(tag) > -1) {
47775 this.black.push(tag);
47779 Roo.each(b, function(tag) {
47780 if (w.indexOf(tag) > -1) {
47783 if (this.black.indexOf(tag) > -1) {
47786 this.black.push(tag);
47791 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
47792 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
47796 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47797 if (b.indexOf(tag) > -1) {
47800 this.cwhite.push(tag);
47804 Roo.each(w, function(tag) {
47805 if (b.indexOf(tag) > -1) {
47808 if (this.cwhite.indexOf(tag) > -1) {
47811 this.cwhite.push(tag);
47816 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47817 if (w.indexOf(tag) > -1) {
47820 this.cblack.push(tag);
47824 Roo.each(b, function(tag) {
47825 if (w.indexOf(tag) > -1) {
47828 if (this.cblack.indexOf(tag) > -1) {
47831 this.cblack.push(tag);
47836 setStylesheets : function(stylesheets)
47838 if(typeof(stylesheets) == 'string'){
47839 Roo.get(this.iframe.contentDocument.head).createChild({
47841 rel : 'stylesheet',
47850 Roo.each(stylesheets, function(s) {
47855 Roo.get(_this.iframe.contentDocument.head).createChild({
47857 rel : 'stylesheet',
47866 removeStylesheets : function()
47870 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47875 setStyle : function(style)
47877 Roo.get(this.iframe.contentDocument.head).createChild({
47886 // hide stuff that is not compatible
47900 * @event specialkey
47904 * @cfg {String} fieldClass @hide
47907 * @cfg {String} focusClass @hide
47910 * @cfg {String} autoCreate @hide
47913 * @cfg {String} inputType @hide
47916 * @cfg {String} invalidClass @hide
47919 * @cfg {String} invalidText @hide
47922 * @cfg {String} msgFx @hide
47925 * @cfg {String} validateOnBlur @hide
47929 Roo.HtmlEditorCore.white = [
47930 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47932 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
47933 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
47934 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
47935 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
47936 'TABLE', 'UL', 'XMP',
47938 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
47941 'DIR', 'MENU', 'OL', 'UL', 'DL',
47947 Roo.HtmlEditorCore.black = [
47948 // 'embed', 'object', // enable - backend responsiblity to clean thiese
47950 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
47951 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
47952 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
47953 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
47954 //'FONT' // CLEAN LATER..
47955 'COLGROUP', 'COL' // messy tables.
47958 Roo.HtmlEditorCore.clean = [ // ?? needed???
47959 'SCRIPT', 'STYLE', 'TITLE', 'XML'
47961 Roo.HtmlEditorCore.tag_remove = [
47966 Roo.HtmlEditorCore.ablack = [
47970 Roo.HtmlEditorCore.aclean = [
47971 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
47975 Roo.HtmlEditorCore.pwhite= [
47976 'http', 'https', 'mailto'
47979 // white listed style attributes.
47980 Roo.HtmlEditorCore.cwhite= [
47981 // 'text-align', /// default is to allow most things..
47987 // black listed style attributes.
47988 Roo.HtmlEditorCore.cblack= [
47989 // 'font-size' -- this can be set by the project
47995 //<script type="text/javascript">
47998 * Ext JS Library 1.1.1
47999 * Copyright(c) 2006-2007, Ext JS, LLC.
48005 Roo.form.HtmlEditor = function(config){
48009 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48011 if (!this.toolbars) {
48012 this.toolbars = [];
48014 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48020 * @class Roo.form.HtmlEditor
48021 * @extends Roo.form.Field
48022 * Provides a lightweight HTML Editor component.
48024 * This has been tested on Fireforx / Chrome.. IE may not be so great..
48026 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48027 * supported by this editor.</b><br/><br/>
48028 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48029 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48031 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48033 * @cfg {Boolean} clearUp
48037 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48042 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
48047 * @cfg {Number} height (in pixels)
48051 * @cfg {Number} width (in pixels)
48056 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
48059 stylesheets: false,
48063 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48068 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48074 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48079 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48084 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48086 allowComments: false,
48088 * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48097 // private properties
48098 validationEvent : false,
48100 initialized : false,
48103 onFocus : Roo.emptyFn,
48105 hideMode:'offsets',
48107 actionMode : 'container', // defaults to hiding it...
48109 defaultAutoCreate : { // modified by initCompnoent..
48111 style:"width:500px;height:300px;",
48112 autocomplete: "new-password"
48116 initComponent : function(){
48119 * @event initialize
48120 * Fires when the editor is fully initialized (including the iframe)
48121 * @param {HtmlEditor} this
48126 * Fires when the editor is first receives the focus. Any insertion must wait
48127 * until after this event.
48128 * @param {HtmlEditor} this
48132 * @event beforesync
48133 * Fires before the textarea is updated with content from the editor iframe. Return false
48134 * to cancel the sync.
48135 * @param {HtmlEditor} this
48136 * @param {String} html
48140 * @event beforepush
48141 * Fires before the iframe editor is updated with content from the textarea. Return false
48142 * to cancel the push.
48143 * @param {HtmlEditor} this
48144 * @param {String} html
48149 * Fires when the textarea is updated with content from the editor iframe.
48150 * @param {HtmlEditor} this
48151 * @param {String} html
48156 * Fires when the iframe editor is updated with content from the textarea.
48157 * @param {HtmlEditor} this
48158 * @param {String} html
48162 * @event editmodechange
48163 * Fires when the editor switches edit modes
48164 * @param {HtmlEditor} this
48165 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48167 editmodechange: true,
48169 * @event editorevent
48170 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48171 * @param {HtmlEditor} this
48175 * @event firstfocus
48176 * Fires when on first focus - needed by toolbars..
48177 * @param {HtmlEditor} this
48182 * Auto save the htmlEditor value as a file into Events
48183 * @param {HtmlEditor} this
48187 * @event savedpreview
48188 * preview the saved version of htmlEditor
48189 * @param {HtmlEditor} this
48191 savedpreview: true,
48194 * @event stylesheetsclick
48195 * Fires when press the Sytlesheets button
48196 * @param {Roo.HtmlEditorCore} this
48198 stylesheetsclick: true,
48201 * Fires when press user pastes into the editor
48202 * @param {Roo.HtmlEditorCore} this
48206 this.defaultAutoCreate = {
48208 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48209 autocomplete: "new-password"
48214 * Protected method that will not generally be called directly. It
48215 * is called when the editor creates its toolbar. Override this method if you need to
48216 * add custom toolbar buttons.
48217 * @param {HtmlEditor} editor
48219 createToolbar : function(editor){
48220 Roo.log("create toolbars");
48221 if (!editor.toolbars || !editor.toolbars.length) {
48222 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48225 for (var i =0 ; i < editor.toolbars.length;i++) {
48226 editor.toolbars[i] = Roo.factory(
48227 typeof(editor.toolbars[i]) == 'string' ?
48228 { xtype: editor.toolbars[i]} : editor.toolbars[i],
48229 Roo.form.HtmlEditor);
48230 editor.toolbars[i].init(editor);
48238 onRender : function(ct, position)
48241 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48243 this.wrap = this.el.wrap({
48244 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48247 this.editorcore.onRender(ct, position);
48249 if (this.resizable) {
48250 this.resizeEl = new Roo.Resizable(this.wrap, {
48254 minHeight : this.height,
48255 height: this.height,
48256 handles : this.resizable,
48259 resize : function(r, w, h) {
48260 _t.onResize(w,h); // -something
48266 this.createToolbar(this);
48270 this.setSize(this.wrap.getSize());
48272 if (this.resizeEl) {
48273 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48274 // should trigger onReize..
48277 this.keyNav = new Roo.KeyNav(this.el, {
48279 "tab" : function(e){
48280 e.preventDefault();
48282 var value = this.getValue();
48284 var start = this.el.dom.selectionStart;
48285 var end = this.el.dom.selectionEnd;
48289 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48290 this.el.dom.setSelectionRange(end + 1, end + 1);
48294 var f = value.substring(0, start).split("\t");
48296 if(f.pop().length != 0){
48300 this.setValue(f.join("\t") + value.substring(end));
48301 this.el.dom.setSelectionRange(start - 1, start - 1);
48305 "home" : function(e){
48306 e.preventDefault();
48308 var curr = this.el.dom.selectionStart;
48309 var lines = this.getValue().split("\n");
48316 this.el.dom.setSelectionRange(0, 0);
48322 for (var i = 0; i < lines.length;i++) {
48323 pos += lines[i].length;
48333 pos -= lines[i].length;
48339 this.el.dom.setSelectionRange(pos, pos);
48343 this.el.dom.selectionStart = pos;
48344 this.el.dom.selectionEnd = curr;
48347 "end" : function(e){
48348 e.preventDefault();
48350 var curr = this.el.dom.selectionStart;
48351 var lines = this.getValue().split("\n");
48358 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48364 for (var i = 0; i < lines.length;i++) {
48366 pos += lines[i].length;
48380 this.el.dom.setSelectionRange(pos, pos);
48384 this.el.dom.selectionStart = curr;
48385 this.el.dom.selectionEnd = pos;
48390 doRelay : function(foo, bar, hname){
48391 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48397 // if(this.autosave && this.w){
48398 // this.autoSaveFn = setInterval(this.autosave, 1000);
48403 onResize : function(w, h)
48405 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48410 if(typeof w == 'number'){
48411 var aw = w - this.wrap.getFrameWidth('lr');
48412 this.el.setWidth(this.adjustWidth('textarea', aw));
48415 if(typeof h == 'number'){
48417 for (var i =0; i < this.toolbars.length;i++) {
48418 // fixme - ask toolbars for heights?
48419 tbh += this.toolbars[i].tb.el.getHeight();
48420 if (this.toolbars[i].footer) {
48421 tbh += this.toolbars[i].footer.el.getHeight();
48428 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48429 ah -= 5; // knock a few pixes off for look..
48431 this.el.setHeight(this.adjustWidth('textarea', ah));
48435 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48436 this.editorcore.onResize(ew,eh);
48441 * Toggles the editor between standard and source edit mode.
48442 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48444 toggleSourceEdit : function(sourceEditMode)
48446 this.editorcore.toggleSourceEdit(sourceEditMode);
48448 if(this.editorcore.sourceEditMode){
48449 Roo.log('editor - showing textarea');
48452 // Roo.log(this.syncValue());
48453 this.editorcore.syncValue();
48454 this.el.removeClass('x-hidden');
48455 this.el.dom.removeAttribute('tabIndex');
48457 this.el.dom.scrollTop = 0;
48460 for (var i = 0; i < this.toolbars.length; i++) {
48461 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48462 this.toolbars[i].tb.hide();
48463 this.toolbars[i].footer.hide();
48468 Roo.log('editor - hiding textarea');
48470 // Roo.log(this.pushValue());
48471 this.editorcore.pushValue();
48473 this.el.addClass('x-hidden');
48474 this.el.dom.setAttribute('tabIndex', -1);
48476 for (var i = 0; i < this.toolbars.length; i++) {
48477 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48478 this.toolbars[i].tb.show();
48479 this.toolbars[i].footer.show();
48483 //this.deferFocus();
48486 this.setSize(this.wrap.getSize());
48487 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48489 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48492 // private (for BoxComponent)
48493 adjustSize : Roo.BoxComponent.prototype.adjustSize,
48495 // private (for BoxComponent)
48496 getResizeEl : function(){
48500 // private (for BoxComponent)
48501 getPositionEl : function(){
48506 initEvents : function(){
48507 this.originalValue = this.getValue();
48511 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48514 markInvalid : Roo.emptyFn,
48516 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48519 clearInvalid : Roo.emptyFn,
48521 setValue : function(v){
48522 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48523 this.editorcore.pushValue();
48528 deferFocus : function(){
48529 this.focus.defer(10, this);
48533 focus : function(){
48534 this.editorcore.focus();
48540 onDestroy : function(){
48546 for (var i =0; i < this.toolbars.length;i++) {
48547 // fixme - ask toolbars for heights?
48548 this.toolbars[i].onDestroy();
48551 this.wrap.dom.innerHTML = '';
48552 this.wrap.remove();
48557 onFirstFocus : function(){
48558 //Roo.log("onFirstFocus");
48559 this.editorcore.onFirstFocus();
48560 for (var i =0; i < this.toolbars.length;i++) {
48561 this.toolbars[i].onFirstFocus();
48567 syncValue : function()
48569 this.editorcore.syncValue();
48572 pushValue : function()
48574 this.editorcore.pushValue();
48577 setStylesheets : function(stylesheets)
48579 this.editorcore.setStylesheets(stylesheets);
48582 removeStylesheets : function()
48584 this.editorcore.removeStylesheets();
48588 // hide stuff that is not compatible
48602 * @event specialkey
48606 * @cfg {String} fieldClass @hide
48609 * @cfg {String} focusClass @hide
48612 * @cfg {String} autoCreate @hide
48615 * @cfg {String} inputType @hide
48618 * @cfg {String} invalidClass @hide
48621 * @cfg {String} invalidText @hide
48624 * @cfg {String} msgFx @hide
48627 * @cfg {String} validateOnBlur @hide
48631 // <script type="text/javascript">
48634 * Ext JS Library 1.1.1
48635 * Copyright(c) 2006-2007, Ext JS, LLC.
48641 * @class Roo.form.HtmlEditorToolbar1
48646 new Roo.form.HtmlEditor({
48649 new Roo.form.HtmlEditorToolbar1({
48650 disable : { fonts: 1 , format: 1, ..., ... , ...],
48656 * @cfg {Object} disable List of elements to disable..
48657 * @cfg {Array} btns List of additional buttons.
48661 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48664 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48667 Roo.apply(this, config);
48669 // default disabled, based on 'good practice'..
48670 this.disable = this.disable || {};
48671 Roo.applyIf(this.disable, {
48674 specialElements : true
48678 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48679 // dont call parent... till later.
48682 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
48689 editorcore : false,
48691 * @cfg {Object} disable List of toolbar elements to disable
48698 * @cfg {String} createLinkText The default text for the create link prompt
48700 createLinkText : 'Please enter the URL for the link:',
48702 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48704 defaultLinkValue : 'http:/'+'/',
48708 * @cfg {Array} fontFamilies An array of available font families
48726 // "á" , ?? a acute?
48731 "°" // , // degrees
48733 // "é" , // e ecute
48734 // "ú" , // u ecute?
48737 specialElements : [
48739 text: "Insert Table",
48742 ihtml : '<table><tr><td>Cell</td></tr></table>'
48746 text: "Insert Image",
48749 ihtml : '<img src="about:blank"/>'
48758 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
48759 "input:submit", "input:button", "select", "textarea", "label" ],
48762 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
48764 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48773 * @cfg {String} defaultFont default font to use.
48775 defaultFont: 'tahoma',
48777 fontSelect : false,
48780 formatCombo : false,
48782 init : function(editor)
48784 this.editor = editor;
48785 this.editorcore = editor.editorcore ? editor.editorcore : editor;
48786 var editorcore = this.editorcore;
48790 var fid = editorcore.frameId;
48792 function btn(id, toggle, handler){
48793 var xid = fid + '-'+ id ;
48797 cls : 'x-btn-icon x-edit-'+id,
48798 enableToggle:toggle !== false,
48799 scope: _t, // was editor...
48800 handler:handler||_t.relayBtnCmd,
48801 clickEvent:'mousedown',
48802 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48809 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48811 // stop form submits
48812 tb.el.on('click', function(e){
48813 e.preventDefault(); // what does this do?
48816 if(!this.disable.font) { // && !Roo.isSafari){
48817 /* why no safari for fonts
48818 editor.fontSelect = tb.el.createChild({
48821 cls:'x-font-select',
48822 html: this.createFontOptions()
48825 editor.fontSelect.on('change', function(){
48826 var font = editor.fontSelect.dom.value;
48827 editor.relayCmd('fontname', font);
48828 editor.deferFocus();
48832 editor.fontSelect.dom,
48838 if(!this.disable.formats){
48839 this.formatCombo = new Roo.form.ComboBox({
48840 store: new Roo.data.SimpleStore({
48843 data : this.formats // from states.js
48847 //autoCreate : {tag: "div", size: "20"},
48848 displayField:'tag',
48852 triggerAction: 'all',
48853 emptyText:'Add tag',
48854 selectOnFocus:true,
48857 'select': function(c, r, i) {
48858 editorcore.insertTag(r.get('tag'));
48864 tb.addField(this.formatCombo);
48868 if(!this.disable.format){
48873 btn('strikethrough')
48876 if(!this.disable.fontSize){
48881 btn('increasefontsize', false, editorcore.adjustFont),
48882 btn('decreasefontsize', false, editorcore.adjustFont)
48887 if(!this.disable.colors){
48890 id:editorcore.frameId +'-forecolor',
48891 cls:'x-btn-icon x-edit-forecolor',
48892 clickEvent:'mousedown',
48893 tooltip: this.buttonTips['forecolor'] || undefined,
48895 menu : new Roo.menu.ColorMenu({
48896 allowReselect: true,
48897 focus: Roo.emptyFn,
48900 selectHandler: function(cp, color){
48901 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48902 editor.deferFocus();
48905 clickEvent:'mousedown'
48908 id:editorcore.frameId +'backcolor',
48909 cls:'x-btn-icon x-edit-backcolor',
48910 clickEvent:'mousedown',
48911 tooltip: this.buttonTips['backcolor'] || undefined,
48913 menu : new Roo.menu.ColorMenu({
48914 focus: Roo.emptyFn,
48917 allowReselect: true,
48918 selectHandler: function(cp, color){
48920 editorcore.execCmd('useCSS', false);
48921 editorcore.execCmd('hilitecolor', color);
48922 editorcore.execCmd('useCSS', true);
48923 editor.deferFocus();
48925 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
48926 Roo.isSafari || Roo.isIE ? '#'+color : color);
48927 editor.deferFocus();
48931 clickEvent:'mousedown'
48936 // now add all the items...
48939 if(!this.disable.alignments){
48942 btn('justifyleft'),
48943 btn('justifycenter'),
48944 btn('justifyright')
48948 //if(!Roo.isSafari){
48949 if(!this.disable.links){
48952 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
48956 if(!this.disable.lists){
48959 btn('insertorderedlist'),
48960 btn('insertunorderedlist')
48963 if(!this.disable.sourceEdit){
48966 btn('sourceedit', true, function(btn){
48967 this.toggleSourceEdit(btn.pressed);
48974 // special menu.. - needs to be tidied up..
48975 if (!this.disable.special) {
48978 cls: 'x-edit-none',
48984 for (var i =0; i < this.specialChars.length; i++) {
48985 smenu.menu.items.push({
48987 html: this.specialChars[i],
48988 handler: function(a,b) {
48989 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48990 //editor.insertAtCursor(a.html);
49004 if (!this.disable.cleanStyles) {
49006 cls: 'x-btn-icon x-btn-clear',
49012 for (var i =0; i < this.cleanStyles.length; i++) {
49013 cmenu.menu.items.push({
49014 actiontype : this.cleanStyles[i],
49015 html: 'Remove ' + this.cleanStyles[i],
49016 handler: function(a,b) {
49019 var c = Roo.get(editorcore.doc.body);
49020 c.select('[style]').each(function(s) {
49021 s.dom.style.removeProperty(a.actiontype);
49023 editorcore.syncValue();
49028 cmenu.menu.items.push({
49029 actiontype : 'tablewidths',
49030 html: 'Remove Table Widths',
49031 handler: function(a,b) {
49032 editorcore.cleanTableWidths();
49033 editorcore.syncValue();
49037 cmenu.menu.items.push({
49038 actiontype : 'word',
49039 html: 'Remove MS Word Formating',
49040 handler: function(a,b) {
49041 editorcore.cleanWord();
49042 editorcore.syncValue();
49047 cmenu.menu.items.push({
49048 actiontype : 'all',
49049 html: 'Remove All Styles',
49050 handler: function(a,b) {
49052 var c = Roo.get(editorcore.doc.body);
49053 c.select('[style]').each(function(s) {
49054 s.dom.removeAttribute('style');
49056 editorcore.syncValue();
49061 cmenu.menu.items.push({
49062 actiontype : 'all',
49063 html: 'Remove All CSS Classes',
49064 handler: function(a,b) {
49066 var c = Roo.get(editorcore.doc.body);
49067 c.select('[class]').each(function(s) {
49068 s.dom.removeAttribute('class');
49070 editorcore.cleanWord();
49071 editorcore.syncValue();
49076 cmenu.menu.items.push({
49077 actiontype : 'tidy',
49078 html: 'Tidy HTML Source',
49079 handler: function(a,b) {
49080 new Roo.htmleditor.Tidy(editorcore.doc.body);
49081 editorcore.syncValue();
49090 if (!this.disable.specialElements) {
49093 cls: 'x-edit-none',
49098 for (var i =0; i < this.specialElements.length; i++) {
49099 semenu.menu.items.push(
49101 handler: function(a,b) {
49102 editor.insertAtCursor(this.ihtml);
49104 }, this.specialElements[i])
49116 for(var i =0; i< this.btns.length;i++) {
49117 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49118 b.cls = 'x-edit-none';
49120 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49121 b.cls += ' x-init-enable';
49124 b.scope = editorcore;
49132 // disable everything...
49134 this.tb.items.each(function(item){
49137 item.id != editorcore.frameId+ '-sourceedit' &&
49138 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49144 this.rendered = true;
49146 // the all the btns;
49147 editor.on('editorevent', this.updateToolbar, this);
49148 // other toolbars need to implement this..
49149 //editor.on('editmodechange', this.updateToolbar, this);
49153 relayBtnCmd : function(btn) {
49154 this.editorcore.relayCmd(btn.cmd);
49156 // private used internally
49157 createLink : function(){
49158 Roo.log("create link?");
49159 var url = prompt(this.createLinkText, this.defaultLinkValue);
49160 if(url && url != 'http:/'+'/'){
49161 this.editorcore.relayCmd('createlink', url);
49167 * Protected method that will not generally be called directly. It triggers
49168 * a toolbar update by reading the markup state of the current selection in the editor.
49170 updateToolbar: function(){
49172 if(!this.editorcore.activated){
49173 this.editor.onFirstFocus();
49177 var btns = this.tb.items.map,
49178 doc = this.editorcore.doc,
49179 frameId = this.editorcore.frameId;
49181 if(!this.disable.font && !Roo.isSafari){
49183 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49184 if(name != this.fontSelect.dom.value){
49185 this.fontSelect.dom.value = name;
49189 if(!this.disable.format){
49190 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49191 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49192 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49193 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49195 if(!this.disable.alignments){
49196 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49197 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49198 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49200 if(!Roo.isSafari && !this.disable.lists){
49201 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49202 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49205 var ans = this.editorcore.getAllAncestors();
49206 if (this.formatCombo) {
49209 var store = this.formatCombo.store;
49210 this.formatCombo.setValue("");
49211 for (var i =0; i < ans.length;i++) {
49212 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49214 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49222 // hides menus... - so this cant be on a menu...
49223 Roo.menu.MenuMgr.hideAll();
49225 //this.editorsyncValue();
49229 createFontOptions : function(){
49230 var buf = [], fs = this.fontFamilies, ff, lc;
49234 for(var i = 0, len = fs.length; i< len; i++){
49236 lc = ff.toLowerCase();
49238 '<option value="',lc,'" style="font-family:',ff,';"',
49239 (this.defaultFont == lc ? ' selected="true">' : '>'),
49244 return buf.join('');
49247 toggleSourceEdit : function(sourceEditMode){
49249 Roo.log("toolbar toogle");
49250 if(sourceEditMode === undefined){
49251 sourceEditMode = !this.sourceEditMode;
49253 this.sourceEditMode = sourceEditMode === true;
49254 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49255 // just toggle the button?
49256 if(btn.pressed !== this.sourceEditMode){
49257 btn.toggle(this.sourceEditMode);
49261 if(sourceEditMode){
49262 Roo.log("disabling buttons");
49263 this.tb.items.each(function(item){
49264 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49270 Roo.log("enabling buttons");
49271 if(this.editorcore.initialized){
49272 this.tb.items.each(function(item){
49278 Roo.log("calling toggole on editor");
49279 // tell the editor that it's been pressed..
49280 this.editor.toggleSourceEdit(sourceEditMode);
49284 * Object collection of toolbar tooltips for the buttons in the editor. The key
49285 * is the command id associated with that button and the value is a valid QuickTips object.
49290 title: 'Bold (Ctrl+B)',
49291 text: 'Make the selected text bold.',
49292 cls: 'x-html-editor-tip'
49295 title: 'Italic (Ctrl+I)',
49296 text: 'Make the selected text italic.',
49297 cls: 'x-html-editor-tip'
49305 title: 'Bold (Ctrl+B)',
49306 text: 'Make the selected text bold.',
49307 cls: 'x-html-editor-tip'
49310 title: 'Italic (Ctrl+I)',
49311 text: 'Make the selected text italic.',
49312 cls: 'x-html-editor-tip'
49315 title: 'Underline (Ctrl+U)',
49316 text: 'Underline the selected text.',
49317 cls: 'x-html-editor-tip'
49320 title: 'Strikethrough',
49321 text: 'Strikethrough the selected text.',
49322 cls: 'x-html-editor-tip'
49324 increasefontsize : {
49325 title: 'Grow Text',
49326 text: 'Increase the font size.',
49327 cls: 'x-html-editor-tip'
49329 decreasefontsize : {
49330 title: 'Shrink Text',
49331 text: 'Decrease the font size.',
49332 cls: 'x-html-editor-tip'
49335 title: 'Text Highlight Color',
49336 text: 'Change the background color of the selected text.',
49337 cls: 'x-html-editor-tip'
49340 title: 'Font Color',
49341 text: 'Change the color of the selected text.',
49342 cls: 'x-html-editor-tip'
49345 title: 'Align Text Left',
49346 text: 'Align text to the left.',
49347 cls: 'x-html-editor-tip'
49350 title: 'Center Text',
49351 text: 'Center text in the editor.',
49352 cls: 'x-html-editor-tip'
49355 title: 'Align Text Right',
49356 text: 'Align text to the right.',
49357 cls: 'x-html-editor-tip'
49359 insertunorderedlist : {
49360 title: 'Bullet List',
49361 text: 'Start a bulleted list.',
49362 cls: 'x-html-editor-tip'
49364 insertorderedlist : {
49365 title: 'Numbered List',
49366 text: 'Start a numbered list.',
49367 cls: 'x-html-editor-tip'
49370 title: 'Hyperlink',
49371 text: 'Make the selected text a hyperlink.',
49372 cls: 'x-html-editor-tip'
49375 title: 'Source Edit',
49376 text: 'Switch to source editing mode.',
49377 cls: 'x-html-editor-tip'
49381 onDestroy : function(){
49384 this.tb.items.each(function(item){
49386 item.menu.removeAll();
49388 item.menu.el.destroy();
49396 onFirstFocus: function() {
49397 this.tb.items.each(function(item){
49406 // <script type="text/javascript">
49409 * Ext JS Library 1.1.1
49410 * Copyright(c) 2006-2007, Ext JS, LLC.
49417 * @class Roo.form.HtmlEditor.ToolbarContext
49422 new Roo.form.HtmlEditor({
49425 { xtype: 'ToolbarStandard', styles : {} }
49426 { xtype: 'ToolbarContext', disable : {} }
49432 * @config : {Object} disable List of elements to disable.. (not done yet.)
49433 * @config : {Object} styles Map of styles available.
49437 Roo.form.HtmlEditor.ToolbarContext = function(config)
49440 Roo.apply(this, config);
49441 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49442 // dont call parent... till later.
49443 this.styles = this.styles || {};
49448 Roo.form.HtmlEditor.ToolbarContext.types = {
49463 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49489 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49560 name : 'selectoptions',
49566 // should we really allow this??
49567 // should this just be
49584 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49585 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49587 Roo.form.HtmlEditor.ToolbarContext.options = {
49589 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49590 [ 'Courier New', 'Courier New'],
49591 [ 'Tahoma', 'Tahoma'],
49592 [ 'Times New Roman,serif', 'Times'],
49593 [ 'Verdana','Verdana' ]
49597 // fixme - these need to be configurable..
49600 //Roo.form.HtmlEditor.ToolbarContext.types
49603 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
49610 editorcore : false,
49612 * @cfg {Object} disable List of toolbar elements to disable
49617 * @cfg {Object} styles List of styles
49618 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
49620 * These must be defined in the page, so they get rendered correctly..
49631 init : function(editor)
49633 this.editor = editor;
49634 this.editorcore = editor.editorcore ? editor.editorcore : editor;
49635 var editorcore = this.editorcore;
49637 var fid = editorcore.frameId;
49639 function btn(id, toggle, handler){
49640 var xid = fid + '-'+ id ;
49644 cls : 'x-btn-icon x-edit-'+id,
49645 enableToggle:toggle !== false,
49646 scope: editorcore, // was editor...
49647 handler:handler||editorcore.relayBtnCmd,
49648 clickEvent:'mousedown',
49649 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49653 // create a new element.
49654 var wdiv = editor.wrap.createChild({
49656 }, editor.wrap.dom.firstChild.nextSibling, true);
49658 // can we do this more than once??
49660 // stop form submits
49663 // disable everything...
49664 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49665 this.toolbars = {};
49667 for (var i in ty) {
49669 this.toolbars[i] = this.buildToolbar(ty[i],i);
49671 this.tb = this.toolbars.BODY;
49673 this.buildFooter();
49674 this.footer.show();
49675 editor.on('hide', function( ) { this.footer.hide() }, this);
49676 editor.on('show', function( ) { this.footer.show() }, this);
49679 this.rendered = true;
49681 // the all the btns;
49682 editor.on('editorevent', this.updateToolbar, this);
49683 // other toolbars need to implement this..
49684 //editor.on('editmodechange', this.updateToolbar, this);
49690 * Protected method that will not generally be called directly. It triggers
49691 * a toolbar update by reading the markup state of the current selection in the editor.
49693 * Note you can force an update by calling on('editorevent', scope, false)
49695 updateToolbar: function(editor ,ev, sel)
49699 ev.stopEvent(); // se if we can stop this looping with mutiple events.
49703 // capture mouse up - this is handy for selecting images..
49704 // perhaps should go somewhere else...
49705 if(!this.editorcore.activated){
49706 this.editor.onFirstFocus();
49709 //Roo.log(ev ? ev.target : 'NOTARGET');
49712 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49713 // selectNode - might want to handle IE?
49718 (ev.type == 'mouseup' || ev.type == 'click' ) &&
49719 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49720 // they have click on an image...
49721 // let's see if we can change the selection...
49724 // this triggers looping?
49725 //this.editorcore.selectNode(sel);
49730 //var updateFooter = sel ? false : true;
49733 var ans = this.editorcore.getAllAncestors();
49736 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49739 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
49740 sel = sel ? sel : this.editorcore.doc.body;
49741 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49745 var tn = sel.tagName.toUpperCase();
49746 var lastSel = this.tb.selectedNode;
49747 this.tb.selectedNode = sel;
49748 var left_label = tn;
49750 // ok see if we are editing a block?
49751 var sel_el = Roo.get(sel);
49753 // you are not actually selecting the block.
49754 if (sel && sel.hasAttribute('data-block')) {
49756 } else if (sel && !sel.hasAttribute('contenteditable')) {
49757 db = sel_el.findParent('[data-block]');
49758 var cepar = sel_el.findParent('[contenteditable=true]');
49759 if (db && cepar && cepar.tagName != 'BODY') {
49760 db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49766 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49768 block = Roo.htmleditor.Block.factory(db);
49770 tn = 'BLOCK.' + db.getAttribute('data-block');
49772 //this.editorcore.selectNode(db);
49773 if (typeof(this.toolbars[tn]) == 'undefined') {
49774 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
49776 this.toolbars[tn].selectedNode = db;
49777 left_label = block.friendly_name;
49778 ans = this.editorcore.getAllAncestors();
49786 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49787 return; // no change?
49793 ///console.log("show: " + tn);
49794 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49798 this.tb.items.first().el.innerHTML = left_label + ': ';
49801 // update attributes
49804 this.tb.fields.each(function(e) {
49805 e.setValue(block[e.name]);
49809 } else if (this.tb.fields && this.tb.selectedNode) {
49810 this.tb.fields.each( function(e) {
49812 e.setValue(this.tb.selectedNode.style[e.stylename]);
49815 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49817 this.updateToolbarStyles(this.tb.selectedNode);
49822 Roo.menu.MenuMgr.hideAll();
49827 // update the footer
49829 this.updateFooter(ans);
49833 updateToolbarStyles : function(sel)
49835 var hasStyles = false;
49836 for(var i in this.styles) {
49842 if (hasStyles && this.tb.hasStyles) {
49843 var st = this.tb.fields.item(0);
49845 st.store.removeAll();
49846 var cn = sel.className.split(/\s+/);
49849 if (this.styles['*']) {
49851 Roo.each(this.styles['*'], function(v) {
49852 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
49855 if (this.styles[tn]) {
49856 Roo.each(this.styles[tn], function(v) {
49857 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
49861 st.store.loadData(avs);
49868 updateFooter : function(ans)
49871 if (ans === false) {
49872 this.footDisp.dom.innerHTML = '';
49876 this.footerEls = ans.reverse();
49877 Roo.each(this.footerEls, function(a,i) {
49878 if (!a) { return; }
49879 html += html.length ? ' > ' : '';
49881 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49886 var sz = this.footDisp.up('td').getSize();
49887 this.footDisp.dom.style.width = (sz.width -10) + 'px';
49888 this.footDisp.dom.style.marginLeft = '5px';
49890 this.footDisp.dom.style.overflow = 'hidden';
49892 this.footDisp.dom.innerHTML = html;
49899 onDestroy : function(){
49902 this.tb.items.each(function(item){
49904 item.menu.removeAll();
49906 item.menu.el.destroy();
49914 onFirstFocus: function() {
49915 // need to do this for all the toolbars..
49916 this.tb.items.each(function(item){
49920 buildToolbar: function(tlist, nm, friendly_name, block)
49922 var editor = this.editor;
49923 var editorcore = this.editorcore;
49924 // create a new element.
49925 var wdiv = editor.wrap.createChild({
49927 }, editor.wrap.dom.firstChild.nextSibling, true);
49930 var tb = new Roo.Toolbar(wdiv);
49931 ///this.tb = tb; // << this sets the active toolbar..
49932 if (tlist === false && block) {
49933 tlist = block.contextMenu(this);
49936 tb.hasStyles = false;
49939 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
49941 var styles = Array.from(this.styles);
49945 if (styles && styles.length) {
49946 tb.hasStyles = true;
49947 // this needs a multi-select checkbox...
49948 tb.addField( new Roo.form.ComboBox({
49949 store: new Roo.data.SimpleStore({
49951 fields: ['val', 'selected'],
49954 name : '-roo-edit-className',
49955 attrname : 'className',
49956 displayField: 'val',
49960 triggerAction: 'all',
49961 emptyText:'Select Style',
49962 selectOnFocus:true,
49965 'select': function(c, r, i) {
49966 // initial support only for on class per el..
49967 tb.selectedNode.className = r ? r.get('val') : '';
49968 editorcore.syncValue();
49975 var tbc = Roo.form.HtmlEditor.ToolbarContext;
49978 for (var i = 0; i < tlist.length; i++) {
49980 // newer versions will use xtype cfg to create menus.
49981 if (typeof(tlist[i].xtype) != 'undefined') {
49983 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49989 var item = tlist[i];
49990 tb.add(item.title + ": ");
49993 //optname == used so you can configure the options available..
49994 var opts = item.opts ? item.opts : false;
49995 if (item.optname) { // use the b
49996 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50001 // opts == pulldown..
50002 tb.addField( new Roo.form.ComboBox({
50003 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50005 fields: ['val', 'display'],
50008 name : '-roo-edit-' + tlist[i].name,
50010 attrname : tlist[i].name,
50011 stylename : item.style ? item.style : false,
50013 displayField: item.displayField ? item.displayField : 'val',
50014 valueField : 'val',
50016 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
50018 triggerAction: 'all',
50019 emptyText:'Select',
50020 selectOnFocus:true,
50021 width: item.width ? item.width : 130,
50023 'select': function(c, r, i) {
50024 if (tb.selectedNode.hasAttribute('data-block')) {
50025 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50026 b[c.attrname] = r.get('val');
50027 b.updateElement(tb.selectedNode);
50028 editorcore.syncValue();
50033 tb.selectedNode.style[c.stylename] = r.get('val');
50034 editorcore.syncValue();
50038 tb.selectedNode.removeAttribute(c.attrname);
50039 editorcore.syncValue();
50042 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50043 editorcore.syncValue();
50052 tb.addField( new Roo.form.TextField({
50055 //allowBlank:false,
50061 tb.addField( new Roo.form.TextField({
50062 name: '-roo-edit-' + tlist[i].name,
50063 attrname : tlist[i].name,
50069 'change' : function(f, nv, ov) {
50071 if (tb.selectedNode.hasAttribute('data-block')) {
50072 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50073 b[f.attrname] = nv;
50074 b.updateElement(tb.selectedNode);
50075 editorcore.syncValue();
50079 tb.selectedNode.setAttribute(f.attrname, nv);
50080 editorcore.syncValue();
50093 text: 'Stylesheets',
50096 click : function ()
50098 _this.editor.fireEvent('stylesheetsclick', _this.editor);
50106 text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50109 click : function ()
50111 var sn = tb.selectedNode;
50113 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50119 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50120 if (sn.hasAttribute('data-block')) {
50121 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
50122 sn.parentNode.removeChild(sn);
50124 } else if (sn && sn.tagName != 'BODY') {
50125 // remove and keep parents.
50126 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50131 var range = editorcore.createRange();
50133 range.setStart(stn,0);
50134 range.setEnd(stn,0);
50135 var selection = editorcore.getSelection();
50136 selection.removeAllRanges();
50137 selection.addRange(range);
50140 //_this.updateToolbar(null, null, pn);
50141 _this.updateToolbar(null, null, null);
50142 _this.updateFooter(false);
50153 tb.el.on('click', function(e){
50154 e.preventDefault(); // what does this do?
50156 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50159 // dont need to disable them... as they will get hidden
50164 buildFooter : function()
50167 var fel = this.editor.wrap.createChild();
50168 this.footer = new Roo.Toolbar(fel);
50169 // toolbar has scrolly on left / right?
50170 var footDisp= new Roo.Toolbar.Fill();
50176 handler : function() {
50177 _t.footDisp.scrollTo('left',0,true)
50181 this.footer.add( footDisp );
50186 handler : function() {
50188 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50192 var fel = Roo.get(footDisp.el);
50193 fel.addClass('x-editor-context');
50194 this.footDispWrap = fel;
50195 this.footDispWrap.overflow = 'hidden';
50197 this.footDisp = fel.createChild();
50198 this.footDispWrap.on('click', this.onContextClick, this)
50202 // when the footer contect changes
50203 onContextClick : function (ev,dom)
50205 ev.preventDefault();
50206 var cn = dom.className;
50208 if (!cn.match(/x-ed-loc-/)) {
50211 var n = cn.split('-').pop();
50212 var ans = this.footerEls;
50216 var range = this.editorcore.createRange();
50217 range.selectNodeContents(sel);
50218 var selection = this.editorcore.getSelection();
50219 selection.removeAllRanges();
50220 selection.addRange(range);
50224 this.updateToolbar(null, null, sel);
50241 * Ext JS Library 1.1.1
50242 * Copyright(c) 2006-2007, Ext JS, LLC.
50244 * Originally Released Under LGPL - original licence link has changed is not relivant.
50247 * <script type="text/javascript">
50251 * @class Roo.form.BasicForm
50252 * @extends Roo.util.Observable
50253 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50255 * @param {String/HTMLElement/Roo.Element} el The form element or its id
50256 * @param {Object} config Configuration options
50258 Roo.form.BasicForm = function(el, config){
50259 this.allItems = [];
50260 this.childForms = [];
50261 Roo.apply(this, config);
50263 * The Roo.form.Field items in this form.
50264 * @type MixedCollection
50268 this.items = new Roo.util.MixedCollection(false, function(o){
50269 return o.id || (o.id = Roo.id());
50273 * @event beforeaction
50274 * Fires before any action is performed. Return false to cancel the action.
50275 * @param {Form} this
50276 * @param {Action} action The action to be performed
50278 beforeaction: true,
50280 * @event actionfailed
50281 * Fires when an action fails.
50282 * @param {Form} this
50283 * @param {Action} action The action that failed
50285 actionfailed : true,
50287 * @event actioncomplete
50288 * Fires when an action is completed.
50289 * @param {Form} this
50290 * @param {Action} action The action that completed
50292 actioncomplete : true
50297 Roo.form.BasicForm.superclass.constructor.call(this);
50299 Roo.form.BasicForm.popover.apply();
50302 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50304 * @cfg {String} method
50305 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50308 * @cfg {DataReader} reader
50309 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50310 * This is optional as there is built-in support for processing JSON.
50313 * @cfg {DataReader} errorReader
50314 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50315 * This is completely optional as there is built-in support for processing JSON.
50318 * @cfg {String} url
50319 * The URL to use for form actions if one isn't supplied in the action options.
50322 * @cfg {Boolean} fileUpload
50323 * Set to true if this form is a file upload.
50327 * @cfg {Object} baseParams
50328 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50333 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50338 activeAction : null,
50341 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50342 * or setValues() data instead of when the form was first created.
50344 trackResetOnLoad : false,
50348 * childForms - used for multi-tab forms
50351 childForms : false,
50354 * allItems - full list of fields.
50360 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50361 * element by passing it or its id or mask the form itself by passing in true.
50364 waitMsgTarget : false,
50369 disableMask : false,
50372 * @cfg {Boolean} errorMask (true|false) default false
50377 * @cfg {Number} maskOffset Default 100
50382 initEl : function(el){
50383 this.el = Roo.get(el);
50384 this.id = this.el.id || Roo.id();
50385 this.el.on('submit', this.onSubmit, this);
50386 this.el.addClass('x-form');
50390 onSubmit : function(e){
50395 * Returns true if client-side validation on the form is successful.
50398 isValid : function(){
50400 var target = false;
50401 this.items.each(function(f){
50408 if(!target && f.el.isVisible(true)){
50413 if(this.errorMask && !valid){
50414 Roo.form.BasicForm.popover.mask(this, target);
50420 * Returns array of invalid form fields.
50424 invalidFields : function()
50427 this.items.each(function(f){
50440 * DEPRICATED Returns true if any fields in this form have changed since their original load.
50443 isDirty : function(){
50445 this.items.each(function(f){
50455 * Returns true if any fields in this form have changed since their original load. (New version)
50459 hasChanged : function()
50462 this.items.each(function(f){
50463 if(f.hasChanged()){
50472 * Resets all hasChanged to 'false' -
50473 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50474 * So hasChanged storage is only to be used for this purpose
50477 resetHasChanged : function()
50479 this.items.each(function(f){
50480 f.resetHasChanged();
50487 * Performs a predefined action (submit or load) or custom actions you define on this form.
50488 * @param {String} actionName The name of the action type
50489 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
50490 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50491 * accept other config options):
50493 Property Type Description
50494 ---------------- --------------- ----------------------------------------------------------------------------------
50495 url String The url for the action (defaults to the form's url)
50496 method String The form method to use (defaults to the form's method, or POST if not defined)
50497 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
50498 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
50499 validate the form on the client (defaults to false)
50501 * @return {BasicForm} this
50503 doAction : function(action, options){
50504 if(typeof action == 'string'){
50505 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50507 if(this.fireEvent('beforeaction', this, action) !== false){
50508 this.beforeAction(action);
50509 action.run.defer(100, action);
50515 * Shortcut to do a submit action.
50516 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50517 * @return {BasicForm} this
50519 submit : function(options){
50520 this.doAction('submit', options);
50525 * Shortcut to do a load action.
50526 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50527 * @return {BasicForm} this
50529 load : function(options){
50530 this.doAction('load', options);
50535 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50536 * @param {Record} record The record to edit
50537 * @return {BasicForm} this
50539 updateRecord : function(record){
50540 record.beginEdit();
50541 var fs = record.fields;
50542 fs.each(function(f){
50543 var field = this.findField(f.name);
50545 record.set(f.name, field.getValue());
50553 * Loads an Roo.data.Record into this form.
50554 * @param {Record} record The record to load
50555 * @return {BasicForm} this
50557 loadRecord : function(record){
50558 this.setValues(record.data);
50563 beforeAction : function(action){
50564 var o = action.options;
50566 if(!this.disableMask) {
50567 if(this.waitMsgTarget === true){
50568 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50569 }else if(this.waitMsgTarget){
50570 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50571 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50573 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50581 afterAction : function(action, success){
50582 this.activeAction = null;
50583 var o = action.options;
50585 if(!this.disableMask) {
50586 if(this.waitMsgTarget === true){
50588 }else if(this.waitMsgTarget){
50589 this.waitMsgTarget.unmask();
50591 Roo.MessageBox.updateProgress(1);
50592 Roo.MessageBox.hide();
50600 Roo.callback(o.success, o.scope, [this, action]);
50601 this.fireEvent('actioncomplete', this, action);
50605 // failure condition..
50606 // we have a scenario where updates need confirming.
50607 // eg. if a locking scenario exists..
50608 // we look for { errors : { needs_confirm : true }} in the response.
50610 (typeof(action.result) != 'undefined') &&
50611 (typeof(action.result.errors) != 'undefined') &&
50612 (typeof(action.result.errors.needs_confirm) != 'undefined')
50615 Roo.MessageBox.confirm(
50616 "Change requires confirmation",
50617 action.result.errorMsg,
50622 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
50632 Roo.callback(o.failure, o.scope, [this, action]);
50633 // show an error message if no failed handler is set..
50634 if (!this.hasListener('actionfailed')) {
50635 Roo.MessageBox.alert("Error",
50636 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50637 action.result.errorMsg :
50638 "Saving Failed, please check your entries or try again"
50642 this.fireEvent('actionfailed', this, action);
50648 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50649 * @param {String} id The value to search for
50652 findField : function(id){
50653 var field = this.items.get(id);
50655 this.items.each(function(f){
50656 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50662 return field || null;
50666 * Add a secondary form to this one,
50667 * Used to provide tabbed forms. One form is primary, with hidden values
50668 * which mirror the elements from the other forms.
50670 * @param {Roo.form.Form} form to add.
50673 addForm : function(form)
50676 if (this.childForms.indexOf(form) > -1) {
50680 this.childForms.push(form);
50682 Roo.each(form.allItems, function (fe) {
50684 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50685 if (this.findField(n)) { // already added..
50688 var add = new Roo.form.Hidden({
50691 add.render(this.el);
50698 * Mark fields in this form invalid in bulk.
50699 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50700 * @return {BasicForm} this
50702 markInvalid : function(errors){
50703 if(errors instanceof Array){
50704 for(var i = 0, len = errors.length; i < len; i++){
50705 var fieldError = errors[i];
50706 var f = this.findField(fieldError.id);
50708 f.markInvalid(fieldError.msg);
50714 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50715 field.markInvalid(errors[id]);
50719 Roo.each(this.childForms || [], function (f) {
50720 f.markInvalid(errors);
50727 * Set values for fields in this form in bulk.
50728 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50729 * @return {BasicForm} this
50731 setValues : function(values){
50732 if(values instanceof Array){ // array of objects
50733 for(var i = 0, len = values.length; i < len; i++){
50735 var f = this.findField(v.id);
50737 f.setValue(v.value);
50738 if(this.trackResetOnLoad){
50739 f.originalValue = f.getValue();
50743 }else{ // object hash
50746 if(typeof values[id] != 'function' && (field = this.findField(id))){
50748 if (field.setFromData &&
50749 field.valueField &&
50750 field.displayField &&
50751 // combos' with local stores can
50752 // be queried via setValue()
50753 // to set their value..
50754 (field.store && !field.store.isLocal)
50758 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50759 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50760 field.setFromData(sd);
50763 field.setValue(values[id]);
50767 if(this.trackResetOnLoad){
50768 field.originalValue = field.getValue();
50773 this.resetHasChanged();
50776 Roo.each(this.childForms || [], function (f) {
50777 f.setValues(values);
50778 f.resetHasChanged();
50785 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50786 * they are returned as an array.
50787 * @param {Boolean} asString
50790 getValues : function(asString)
50792 if (this.childForms) {
50793 // copy values from the child forms
50794 Roo.each(this.childForms, function (f) {
50795 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50800 if (typeof(FormData) != 'undefined' && asString !== true) {
50801 // this relies on a 'recent' version of chrome apparently...
50803 var fd = (new FormData(this.el.dom)).entries();
50805 var ent = fd.next();
50806 while (!ent.done) {
50807 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50818 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50819 if(asString === true){
50822 return Roo.urlDecode(fs);
50826 * Returns the fields in this form as an object with key/value pairs.
50827 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50830 getFieldValues : function(with_hidden)
50832 if (this.childForms) {
50833 // copy values from the child forms
50834 // should this call getFieldValues - probably not as we do not currently copy
50835 // hidden fields when we generate..
50836 Roo.each(this.childForms, function (f) {
50837 this.setValues(f.getFieldValues());
50842 this.items.each(function(f){
50845 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50846 // if a subform contains a copy of them.
50847 // if you have subforms with the same editable data, you will need to copy the data back
50851 if (!f.getName()) {
50854 var v = f.getValue();
50855 if (f.inputType =='radio') {
50856 if (typeof(ret[f.getName()]) == 'undefined') {
50857 ret[f.getName()] = ''; // empty..
50860 if (!f.el.dom.checked) {
50864 v = f.el.dom.value;
50868 // not sure if this supported any more..
50869 if ((typeof(v) == 'object') && f.getRawValue) {
50870 v = f.getRawValue() ; // dates..
50872 // combo boxes where name != hiddenName...
50873 if (f.name != f.getName()) {
50874 ret[f.name] = f.getRawValue();
50876 ret[f.getName()] = v;
50883 * Clears all invalid messages in this form.
50884 * @return {BasicForm} this
50886 clearInvalid : function(){
50887 this.items.each(function(f){
50891 Roo.each(this.childForms || [], function (f) {
50900 * Resets this form.
50901 * @return {BasicForm} this
50903 reset : function(){
50904 this.items.each(function(f){
50908 Roo.each(this.childForms || [], function (f) {
50911 this.resetHasChanged();
50917 * Add Roo.form components to this form.
50918 * @param {Field} field1
50919 * @param {Field} field2 (optional)
50920 * @param {Field} etc (optional)
50921 * @return {BasicForm} this
50924 this.items.addAll(Array.prototype.slice.call(arguments, 0));
50930 * Removes a field from the items collection (does NOT remove its markup).
50931 * @param {Field} field
50932 * @return {BasicForm} this
50934 remove : function(field){
50935 this.items.remove(field);
50940 * Looks at the fields in this form, checks them for an id attribute,
50941 * and calls applyTo on the existing dom element with that id.
50942 * @return {BasicForm} this
50944 render : function(){
50945 this.items.each(function(f){
50946 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50954 * Calls {@link Ext#apply} for all fields in this form with the passed object.
50955 * @param {Object} values
50956 * @return {BasicForm} this
50958 applyToFields : function(o){
50959 this.items.each(function(f){
50966 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50967 * @param {Object} values
50968 * @return {BasicForm} this
50970 applyIfToFields : function(o){
50971 this.items.each(function(f){
50979 Roo.BasicForm = Roo.form.BasicForm;
50981 Roo.apply(Roo.form.BasicForm, {
50995 intervalID : false,
51001 if(this.isApplied){
51006 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51007 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51008 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51009 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51012 this.maskEl.top.enableDisplayMode("block");
51013 this.maskEl.left.enableDisplayMode("block");
51014 this.maskEl.bottom.enableDisplayMode("block");
51015 this.maskEl.right.enableDisplayMode("block");
51017 Roo.get(document.body).on('click', function(){
51021 Roo.get(document.body).on('touchstart', function(){
51025 this.isApplied = true
51028 mask : function(form, target)
51032 this.target = target;
51034 if(!this.form.errorMask || !target.el){
51038 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51040 var ot = this.target.el.calcOffsetsTo(scrollable);
51042 var scrollTo = ot[1] - this.form.maskOffset;
51044 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51046 scrollable.scrollTo('top', scrollTo);
51048 var el = this.target.wrap || this.target.el;
51050 var box = el.getBox();
51052 this.maskEl.top.setStyle('position', 'absolute');
51053 this.maskEl.top.setStyle('z-index', 10000);
51054 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51055 this.maskEl.top.setLeft(0);
51056 this.maskEl.top.setTop(0);
51057 this.maskEl.top.show();
51059 this.maskEl.left.setStyle('position', 'absolute');
51060 this.maskEl.left.setStyle('z-index', 10000);
51061 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51062 this.maskEl.left.setLeft(0);
51063 this.maskEl.left.setTop(box.y - this.padding);
51064 this.maskEl.left.show();
51066 this.maskEl.bottom.setStyle('position', 'absolute');
51067 this.maskEl.bottom.setStyle('z-index', 10000);
51068 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51069 this.maskEl.bottom.setLeft(0);
51070 this.maskEl.bottom.setTop(box.bottom + this.padding);
51071 this.maskEl.bottom.show();
51073 this.maskEl.right.setStyle('position', 'absolute');
51074 this.maskEl.right.setStyle('z-index', 10000);
51075 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51076 this.maskEl.right.setLeft(box.right + this.padding);
51077 this.maskEl.right.setTop(box.y - this.padding);
51078 this.maskEl.right.show();
51080 this.intervalID = window.setInterval(function() {
51081 Roo.form.BasicForm.popover.unmask();
51084 window.onwheel = function(){ return false;};
51086 (function(){ this.isMasked = true; }).defer(500, this);
51090 unmask : function()
51092 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51096 this.maskEl.top.setStyle('position', 'absolute');
51097 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51098 this.maskEl.top.hide();
51100 this.maskEl.left.setStyle('position', 'absolute');
51101 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51102 this.maskEl.left.hide();
51104 this.maskEl.bottom.setStyle('position', 'absolute');
51105 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51106 this.maskEl.bottom.hide();
51108 this.maskEl.right.setStyle('position', 'absolute');
51109 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51110 this.maskEl.right.hide();
51112 window.onwheel = function(){ return true;};
51114 if(this.intervalID){
51115 window.clearInterval(this.intervalID);
51116 this.intervalID = false;
51119 this.isMasked = false;
51127 * Ext JS Library 1.1.1
51128 * Copyright(c) 2006-2007, Ext JS, LLC.
51130 * Originally Released Under LGPL - original licence link has changed is not relivant.
51133 * <script type="text/javascript">
51137 * @class Roo.form.Form
51138 * @extends Roo.form.BasicForm
51139 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51140 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51142 * @param {Object} config Configuration options
51144 Roo.form.Form = function(config){
51146 if (config.items) {
51147 xitems = config.items;
51148 delete config.items;
51152 Roo.form.Form.superclass.constructor.call(this, null, config);
51153 this.url = this.url || this.action;
51155 this.root = new Roo.form.Layout(Roo.applyIf({
51159 this.active = this.root;
51161 * Array of all the buttons that have been added to this form via {@link addButton}
51165 this.allItems = [];
51168 * @event clientvalidation
51169 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51170 * @param {Form} this
51171 * @param {Boolean} valid true if the form has passed client-side validation
51173 clientvalidation: true,
51176 * Fires when the form is rendered
51177 * @param {Roo.form.Form} form
51182 if (this.progressUrl) {
51183 // push a hidden field onto the list of fields..
51187 name : 'UPLOAD_IDENTIFIER'
51192 Roo.each(xitems, this.addxtype, this);
51196 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51198 * @cfg {Roo.Button} buttons[] buttons at bottom of form
51202 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51205 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51208 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51210 buttonAlign:'center',
51213 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51218 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51219 * This property cascades to child containers if not set.
51224 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51225 * fires a looping event with that state. This is required to bind buttons to the valid
51226 * state using the config value formBind:true on the button.
51228 monitorValid : false,
51231 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51236 * @cfg {String} progressUrl - Url to return progress data
51239 progressUrl : false,
51241 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51242 * sending a formdata with extra parameters - eg uploaded elements.
51248 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51249 * fields are added and the column is closed. If no fields are passed the column remains open
51250 * until end() is called.
51251 * @param {Object} config The config to pass to the column
51252 * @param {Field} field1 (optional)
51253 * @param {Field} field2 (optional)
51254 * @param {Field} etc (optional)
51255 * @return Column The column container object
51257 column : function(c){
51258 var col = new Roo.form.Column(c);
51260 if(arguments.length > 1){ // duplicate code required because of Opera
51261 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51268 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51269 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51270 * until end() is called.
51271 * @param {Object} config The config to pass to the fieldset
51272 * @param {Field} field1 (optional)
51273 * @param {Field} field2 (optional)
51274 * @param {Field} etc (optional)
51275 * @return FieldSet The fieldset container object
51277 fieldset : function(c){
51278 var fs = new Roo.form.FieldSet(c);
51280 if(arguments.length > 1){ // duplicate code required because of Opera
51281 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51288 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51289 * fields are added and the container is closed. If no fields are passed the container remains open
51290 * until end() is called.
51291 * @param {Object} config The config to pass to the Layout
51292 * @param {Field} field1 (optional)
51293 * @param {Field} field2 (optional)
51294 * @param {Field} etc (optional)
51295 * @return Layout The container object
51297 container : function(c){
51298 var l = new Roo.form.Layout(c);
51300 if(arguments.length > 1){ // duplicate code required because of Opera
51301 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51308 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51309 * @param {Object} container A Roo.form.Layout or subclass of Layout
51310 * @return {Form} this
51312 start : function(c){
51313 // cascade label info
51314 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51315 this.active.stack.push(c);
51316 c.ownerCt = this.active;
51322 * Closes the current open container
51323 * @return {Form} this
51326 if(this.active == this.root){
51329 this.active = this.active.ownerCt;
51334 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
51335 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51336 * as the label of the field.
51337 * @param {Field} field1
51338 * @param {Field} field2 (optional)
51339 * @param {Field} etc. (optional)
51340 * @return {Form} this
51343 this.active.stack.push.apply(this.active.stack, arguments);
51344 this.allItems.push.apply(this.allItems,arguments);
51346 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51347 if(a[i].isFormField){
51352 Roo.form.Form.superclass.add.apply(this, r);
51362 * Find any element that has been added to a form, using it's ID or name
51363 * This can include framesets, columns etc. along with regular fields..
51364 * @param {String} id - id or name to find.
51366 * @return {Element} e - or false if nothing found.
51368 findbyId : function(id)
51374 Roo.each(this.allItems, function(f){
51375 if (f.id == id || f.name == id ){
51386 * Render this form into the passed container. This should only be called once!
51387 * @param {String/HTMLElement/Element} container The element this component should be rendered into
51388 * @return {Form} this
51390 render : function(ct)
51396 var o = this.autoCreate || {
51398 method : this.method || 'POST',
51399 id : this.id || Roo.id()
51401 this.initEl(ct.createChild(o));
51403 this.root.render(this.el);
51407 this.items.each(function(f){
51408 f.render('x-form-el-'+f.id);
51411 if(this.buttons.length > 0){
51412 // tables are required to maintain order and for correct IE layout
51413 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51414 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51415 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51417 var tr = tb.getElementsByTagName('tr')[0];
51418 for(var i = 0, len = this.buttons.length; i < len; i++) {
51419 var b = this.buttons[i];
51420 var td = document.createElement('td');
51421 td.className = 'x-form-btn-td';
51422 b.render(tr.appendChild(td));
51425 if(this.monitorValid){ // initialize after render
51426 this.startMonitoring();
51428 this.fireEvent('rendered', this);
51433 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51434 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51435 * object or a valid Roo.DomHelper element config
51436 * @param {Function} handler The function called when the button is clicked
51437 * @param {Object} scope (optional) The scope of the handler function
51438 * @return {Roo.Button}
51440 addButton : function(config, handler, scope){
51444 minWidth: this.minButtonWidth,
51447 if(typeof config == "string"){
51450 Roo.apply(bc, config);
51452 var btn = new Roo.Button(null, bc);
51453 this.buttons.push(btn);
51458 * Adds a series of form elements (using the xtype property as the factory method.
51459 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51460 * @param {Object} config
51463 addxtype : function()
51465 var ar = Array.prototype.slice.call(arguments, 0);
51467 for(var i = 0; i < ar.length; i++) {
51469 continue; // skip -- if this happends something invalid got sent, we
51470 // should ignore it, as basically that interface element will not show up
51471 // and that should be pretty obvious!!
51474 if (Roo.form[ar[i].xtype]) {
51476 var fe = Roo.factory(ar[i], Roo.form);
51482 fe.store.form = this;
51487 this.allItems.push(fe);
51488 if (fe.items && fe.addxtype) {
51489 fe.addxtype.apply(fe, fe.items);
51499 // console.log('adding ' + ar[i].xtype);
51501 if (ar[i].xtype == 'Button') {
51502 //console.log('adding button');
51503 //console.log(ar[i]);
51504 this.addButton(ar[i]);
51505 this.allItems.push(fe);
51509 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51510 alert('end is not supported on xtype any more, use items');
51512 // //console.log('adding end');
51520 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51521 * option "monitorValid"
51523 startMonitoring : function(){
51526 Roo.TaskMgr.start({
51527 run : this.bindHandler,
51528 interval : this.monitorPoll || 200,
51535 * Stops monitoring of the valid state of this form
51537 stopMonitoring : function(){
51538 this.bound = false;
51542 bindHandler : function(){
51544 return false; // stops binding
51547 this.items.each(function(f){
51548 if(!f.isValid(true)){
51553 for(var i = 0, len = this.buttons.length; i < len; i++){
51554 var btn = this.buttons[i];
51555 if(btn.formBind === true && btn.disabled === valid){
51556 btn.setDisabled(!valid);
51559 this.fireEvent('clientvalidation', this, valid);
51573 Roo.Form = Roo.form.Form;
51576 * Ext JS Library 1.1.1
51577 * Copyright(c) 2006-2007, Ext JS, LLC.
51579 * Originally Released Under LGPL - original licence link has changed is not relivant.
51582 * <script type="text/javascript">
51585 // as we use this in bootstrap.
51586 Roo.namespace('Roo.form');
51588 * @class Roo.form.Action
51589 * Internal Class used to handle form actions
51591 * @param {Roo.form.BasicForm} el The form element or its id
51592 * @param {Object} config Configuration options
51597 // define the action interface
51598 Roo.form.Action = function(form, options){
51600 this.options = options || {};
51603 * Client Validation Failed
51606 Roo.form.Action.CLIENT_INVALID = 'client';
51608 * Server Validation Failed
51611 Roo.form.Action.SERVER_INVALID = 'server';
51613 * Connect to Server Failed
51616 Roo.form.Action.CONNECT_FAILURE = 'connect';
51618 * Reading Data from Server Failed
51621 Roo.form.Action.LOAD_FAILURE = 'load';
51623 Roo.form.Action.prototype = {
51625 failureType : undefined,
51626 response : undefined,
51627 result : undefined,
51629 // interface method
51630 run : function(options){
51634 // interface method
51635 success : function(response){
51639 // interface method
51640 handleResponse : function(response){
51644 // default connection failure
51645 failure : function(response){
51647 this.response = response;
51648 this.failureType = Roo.form.Action.CONNECT_FAILURE;
51649 this.form.afterAction(this, false);
51652 processResponse : function(response){
51653 this.response = response;
51654 if(!response.responseText){
51657 this.result = this.handleResponse(response);
51658 return this.result;
51661 // utility functions used internally
51662 getUrl : function(appendParams){
51663 var url = this.options.url || this.form.url || this.form.el.dom.action;
51665 var p = this.getParams();
51667 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51673 getMethod : function(){
51674 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51677 getParams : function(){
51678 var bp = this.form.baseParams;
51679 var p = this.options.params;
51681 if(typeof p == "object"){
51682 p = Roo.urlEncode(Roo.applyIf(p, bp));
51683 }else if(typeof p == 'string' && bp){
51684 p += '&' + Roo.urlEncode(bp);
51687 p = Roo.urlEncode(bp);
51692 createCallback : function(){
51694 success: this.success,
51695 failure: this.failure,
51697 timeout: (this.form.timeout*1000),
51698 upload: this.form.fileUpload ? this.success : undefined
51703 Roo.form.Action.Submit = function(form, options){
51704 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51707 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51710 haveProgress : false,
51711 uploadComplete : false,
51713 // uploadProgress indicator.
51714 uploadProgress : function()
51716 if (!this.form.progressUrl) {
51720 if (!this.haveProgress) {
51721 Roo.MessageBox.progress("Uploading", "Uploading");
51723 if (this.uploadComplete) {
51724 Roo.MessageBox.hide();
51728 this.haveProgress = true;
51730 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51732 var c = new Roo.data.Connection();
51734 url : this.form.progressUrl,
51739 success : function(req){
51740 //console.log(data);
51744 rdata = Roo.decode(req.responseText)
51746 Roo.log("Invalid data from server..");
51750 if (!rdata || !rdata.success) {
51752 Roo.MessageBox.alert(Roo.encode(rdata));
51755 var data = rdata.data;
51757 if (this.uploadComplete) {
51758 Roo.MessageBox.hide();
51763 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51764 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51767 this.uploadProgress.defer(2000,this);
51770 failure: function(data) {
51771 Roo.log('progress url failed ');
51782 // run get Values on the form, so it syncs any secondary forms.
51783 this.form.getValues();
51785 var o = this.options;
51786 var method = this.getMethod();
51787 var isPost = method == 'POST';
51788 if(o.clientValidation === false || this.form.isValid()){
51790 if (this.form.progressUrl) {
51791 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51792 (new Date() * 1) + '' + Math.random());
51797 Roo.Ajax.request(Roo.apply(this.createCallback(), {
51798 form:this.form.el.dom,
51799 url:this.getUrl(!isPost),
51801 params:isPost ? this.getParams() : null,
51802 isUpload: this.form.fileUpload,
51803 formData : this.form.formData
51806 this.uploadProgress();
51808 }else if (o.clientValidation !== false){ // client validation failed
51809 this.failureType = Roo.form.Action.CLIENT_INVALID;
51810 this.form.afterAction(this, false);
51814 success : function(response)
51816 this.uploadComplete= true;
51817 if (this.haveProgress) {
51818 Roo.MessageBox.hide();
51822 var result = this.processResponse(response);
51823 if(result === true || result.success){
51824 this.form.afterAction(this, true);
51828 this.form.markInvalid(result.errors);
51829 this.failureType = Roo.form.Action.SERVER_INVALID;
51831 this.form.afterAction(this, false);
51833 failure : function(response)
51835 this.uploadComplete= true;
51836 if (this.haveProgress) {
51837 Roo.MessageBox.hide();
51840 this.response = response;
51841 this.failureType = Roo.form.Action.CONNECT_FAILURE;
51842 this.form.afterAction(this, false);
51845 handleResponse : function(response){
51846 if(this.form.errorReader){
51847 var rs = this.form.errorReader.read(response);
51850 for(var i = 0, len = rs.records.length; i < len; i++) {
51851 var r = rs.records[i];
51852 errors[i] = r.data;
51855 if(errors.length < 1){
51859 success : rs.success,
51865 ret = Roo.decode(response.responseText);
51869 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51879 Roo.form.Action.Load = function(form, options){
51880 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51881 this.reader = this.form.reader;
51884 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51889 Roo.Ajax.request(Roo.apply(
51890 this.createCallback(), {
51891 method:this.getMethod(),
51892 url:this.getUrl(false),
51893 params:this.getParams()
51897 success : function(response){
51899 var result = this.processResponse(response);
51900 if(result === true || !result.success || !result.data){
51901 this.failureType = Roo.form.Action.LOAD_FAILURE;
51902 this.form.afterAction(this, false);
51905 this.form.clearInvalid();
51906 this.form.setValues(result.data);
51907 this.form.afterAction(this, true);
51910 handleResponse : function(response){
51911 if(this.form.reader){
51912 var rs = this.form.reader.read(response);
51913 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51915 success : rs.success,
51919 return Roo.decode(response.responseText);
51923 Roo.form.Action.ACTION_TYPES = {
51924 'load' : Roo.form.Action.Load,
51925 'submit' : Roo.form.Action.Submit
51928 * Ext JS Library 1.1.1
51929 * Copyright(c) 2006-2007, Ext JS, LLC.
51931 * Originally Released Under LGPL - original licence link has changed is not relivant.
51934 * <script type="text/javascript">
51938 * @class Roo.form.Layout
51939 * @extends Roo.Component
51940 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51941 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51943 * @param {Object} config Configuration options
51945 Roo.form.Layout = function(config){
51947 if (config.items) {
51948 xitems = config.items;
51949 delete config.items;
51951 Roo.form.Layout.superclass.constructor.call(this, config);
51953 Roo.each(xitems, this.addxtype, this);
51957 Roo.extend(Roo.form.Layout, Roo.Component, {
51959 * @cfg {String/Object} autoCreate
51960 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51963 * @cfg {String/Object/Function} style
51964 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51965 * a function which returns such a specification.
51968 * @cfg {String} labelAlign
51969 * Valid values are "left," "top" and "right" (defaults to "left")
51972 * @cfg {Number} labelWidth
51973 * Fixed width in pixels of all field labels (defaults to undefined)
51976 * @cfg {Boolean} clear
51977 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51981 * @cfg {String} labelSeparator
51982 * The separator to use after field labels (defaults to ':')
51984 labelSeparator : ':',
51986 * @cfg {Boolean} hideLabels
51987 * True to suppress the display of field labels in this layout (defaults to false)
51989 hideLabels : false,
51992 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51997 onRender : function(ct, position){
51998 if(this.el){ // from markup
51999 this.el = Roo.get(this.el);
52000 }else { // generate
52001 var cfg = this.getAutoCreate();
52002 this.el = ct.createChild(cfg, position);
52005 this.el.applyStyles(this.style);
52007 if(this.labelAlign){
52008 this.el.addClass('x-form-label-'+this.labelAlign);
52010 if(this.hideLabels){
52011 this.labelStyle = "display:none";
52012 this.elementStyle = "padding-left:0;";
52014 if(typeof this.labelWidth == 'number'){
52015 this.labelStyle = "width:"+this.labelWidth+"px;";
52016 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52018 if(this.labelAlign == 'top'){
52019 this.labelStyle = "width:auto;";
52020 this.elementStyle = "padding-left:0;";
52023 var stack = this.stack;
52024 var slen = stack.length;
52026 if(!this.fieldTpl){
52027 var t = new Roo.Template(
52028 '<div class="x-form-item {5}">',
52029 '<label for="{0}" style="{2}">{1}{4}</label>',
52030 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52032 '</div><div class="x-form-clear-left"></div>'
52034 t.disableFormats = true;
52036 Roo.form.Layout.prototype.fieldTpl = t;
52038 for(var i = 0; i < slen; i++) {
52039 if(stack[i].isFormField){
52040 this.renderField(stack[i]);
52042 this.renderComponent(stack[i]);
52047 this.el.createChild({cls:'x-form-clear'});
52052 renderField : function(f){
52053 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52056 f.labelStyle||this.labelStyle||'', //2
52057 this.elementStyle||'', //3
52058 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52059 f.itemCls||this.itemCls||'' //5
52060 ], true).getPrevSibling());
52064 renderComponent : function(c){
52065 c.render(c.isLayout ? this.el : this.el.createChild());
52068 * Adds a object form elements (using the xtype property as the factory method.)
52069 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
52070 * @param {Object} config
52072 addxtype : function(o)
52074 // create the lement.
52075 o.form = this.form;
52076 var fe = Roo.factory(o, Roo.form);
52077 this.form.allItems.push(fe);
52078 this.stack.push(fe);
52080 if (fe.isFormField) {
52081 this.form.items.add(fe);
52089 * @class Roo.form.Column
52090 * @extends Roo.form.Layout
52091 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52092 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52094 * @param {Object} config Configuration options
52096 Roo.form.Column = function(config){
52097 Roo.form.Column.superclass.constructor.call(this, config);
52100 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52102 * @cfg {Number/String} width
52103 * The fixed width of the column in pixels or CSS value (defaults to "auto")
52106 * @cfg {String/Object} autoCreate
52107 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52111 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52114 onRender : function(ct, position){
52115 Roo.form.Column.superclass.onRender.call(this, ct, position);
52117 this.el.setWidth(this.width);
52124 * @class Roo.form.Row
52125 * @extends Roo.form.Layout
52126 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52127 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52129 * @param {Object} config Configuration options
52133 Roo.form.Row = function(config){
52134 Roo.form.Row.superclass.constructor.call(this, config);
52137 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52139 * @cfg {Number/String} width
52140 * The fixed width of the column in pixels or CSS value (defaults to "auto")
52143 * @cfg {Number/String} height
52144 * The fixed height of the column in pixels or CSS value (defaults to "auto")
52146 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52150 onRender : function(ct, position){
52151 //console.log('row render');
52153 var t = new Roo.Template(
52154 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52155 '<label for="{0}" style="{2}">{1}{4}</label>',
52156 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52160 t.disableFormats = true;
52162 Roo.form.Layout.prototype.rowTpl = t;
52164 this.fieldTpl = this.rowTpl;
52166 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52167 var labelWidth = 100;
52169 if ((this.labelAlign != 'top')) {
52170 if (typeof this.labelWidth == 'number') {
52171 labelWidth = this.labelWidth
52173 this.padWidth = 20 + labelWidth;
52177 Roo.form.Column.superclass.onRender.call(this, ct, position);
52179 this.el.setWidth(this.width);
52182 this.el.setHeight(this.height);
52187 renderField : function(f){
52188 f.fieldEl = this.fieldTpl.append(this.el, [
52189 f.id, f.fieldLabel,
52190 f.labelStyle||this.labelStyle||'',
52191 this.elementStyle||'',
52192 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52193 f.itemCls||this.itemCls||'',
52194 f.width ? f.width + this.padWidth : 160 + this.padWidth
52201 * @class Roo.form.FieldSet
52202 * @extends Roo.form.Layout
52203 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52204 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52206 * @param {Object} config Configuration options
52208 Roo.form.FieldSet = function(config){
52209 Roo.form.FieldSet.superclass.constructor.call(this, config);
52212 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52214 * @cfg {String} legend
52215 * The text to display as the legend for the FieldSet (defaults to '')
52218 * @cfg {String/Object} autoCreate
52219 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52223 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52226 onRender : function(ct, position){
52227 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52229 this.setLegend(this.legend);
52234 setLegend : function(text){
52236 this.el.child('legend').update(text);
52241 * Ext JS Library 1.1.1
52242 * Copyright(c) 2006-2007, Ext JS, LLC.
52244 * Originally Released Under LGPL - original licence link has changed is not relivant.
52247 * <script type="text/javascript">
52250 * @class Roo.form.VTypes
52251 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52254 Roo.form.VTypes = function(){
52255 // closure these in so they are only created once.
52256 var alpha = /^[a-zA-Z_]+$/;
52257 var alphanum = /^[a-zA-Z0-9_]+$/;
52258 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52259 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52261 // All these messages and functions are configurable
52264 * The function used to validate email addresses
52265 * @param {String} value The email address
52267 'email' : function(v){
52268 return email.test(v);
52271 * The error text to display when the email validation function returns false
52274 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52276 * The keystroke filter mask to be applied on email input
52279 'emailMask' : /[a-z0-9_\.\-@]/i,
52282 * The function used to validate URLs
52283 * @param {String} value The URL
52285 'url' : function(v){
52286 return url.test(v);
52289 * The error text to display when the url validation function returns false
52292 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52295 * The function used to validate alpha values
52296 * @param {String} value The value
52298 'alpha' : function(v){
52299 return alpha.test(v);
52302 * The error text to display when the alpha validation function returns false
52305 'alphaText' : 'This field should only contain letters and _',
52307 * The keystroke filter mask to be applied on alpha input
52310 'alphaMask' : /[a-z_]/i,
52313 * The function used to validate alphanumeric values
52314 * @param {String} value The value
52316 'alphanum' : function(v){
52317 return alphanum.test(v);
52320 * The error text to display when the alphanumeric validation function returns false
52323 'alphanumText' : 'This field should only contain letters, numbers and _',
52325 * The keystroke filter mask to be applied on alphanumeric input
52328 'alphanumMask' : /[a-z0-9_]/i
52330 }();//<script type="text/javascript">
52333 * @class Roo.form.FCKeditor
52334 * @extends Roo.form.TextArea
52335 * Wrapper around the FCKEditor http://www.fckeditor.net
52337 * Creates a new FCKeditor
52338 * @param {Object} config Configuration options
52340 Roo.form.FCKeditor = function(config){
52341 Roo.form.FCKeditor.superclass.constructor.call(this, config);
52344 * @event editorinit
52345 * Fired when the editor is initialized - you can add extra handlers here..
52346 * @param {FCKeditor} this
52347 * @param {Object} the FCK object.
52354 Roo.form.FCKeditor.editors = { };
52355 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52357 //defaultAutoCreate : {
52358 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
52362 * @cfg {Object} fck options - see fck manual for details.
52367 * @cfg {Object} fck toolbar set (Basic or Default)
52369 toolbarSet : 'Basic',
52371 * @cfg {Object} fck BasePath
52373 basePath : '/fckeditor/',
52381 onRender : function(ct, position)
52384 this.defaultAutoCreate = {
52386 style:"width:300px;height:60px;",
52387 autocomplete: "new-password"
52390 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52393 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52394 if(this.preventScrollbars){
52395 this.el.setStyle("overflow", "hidden");
52397 this.el.setHeight(this.growMin);
52400 //console.log('onrender' + this.getId() );
52401 Roo.form.FCKeditor.editors[this.getId()] = this;
52404 this.replaceTextarea() ;
52408 getEditor : function() {
52409 return this.fckEditor;
52412 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
52413 * @param {Mixed} value The value to set
52417 setValue : function(value)
52419 //console.log('setValue: ' + value);
52421 if(typeof(value) == 'undefined') { // not sure why this is happending...
52424 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52426 //if(!this.el || !this.getEditor()) {
52427 // this.value = value;
52428 //this.setValue.defer(100,this,[value]);
52432 if(!this.getEditor()) {
52436 this.getEditor().SetData(value);
52443 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
52444 * @return {Mixed} value The field value
52446 getValue : function()
52449 if (this.frame && this.frame.dom.style.display == 'none') {
52450 return Roo.form.FCKeditor.superclass.getValue.call(this);
52453 if(!this.el || !this.getEditor()) {
52455 // this.getValue.defer(100,this);
52460 var value=this.getEditor().GetData();
52461 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52462 return Roo.form.FCKeditor.superclass.getValue.call(this);
52468 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
52469 * @return {Mixed} value The field value
52471 getRawValue : function()
52473 if (this.frame && this.frame.dom.style.display == 'none') {
52474 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52477 if(!this.el || !this.getEditor()) {
52478 //this.getRawValue.defer(100,this);
52485 var value=this.getEditor().GetData();
52486 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52487 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52491 setSize : function(w,h) {
52495 //if (this.frame && this.frame.dom.style.display == 'none') {
52496 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52499 //if(!this.el || !this.getEditor()) {
52500 // this.setSize.defer(100,this, [w,h]);
52506 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52508 this.frame.dom.setAttribute('width', w);
52509 this.frame.dom.setAttribute('height', h);
52510 this.frame.setSize(w,h);
52514 toggleSourceEdit : function(value) {
52518 this.el.dom.style.display = value ? '' : 'none';
52519 this.frame.dom.style.display = value ? 'none' : '';
52524 focus: function(tag)
52526 if (this.frame.dom.style.display == 'none') {
52527 return Roo.form.FCKeditor.superclass.focus.call(this);
52529 if(!this.el || !this.getEditor()) {
52530 this.focus.defer(100,this, [tag]);
52537 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52538 this.getEditor().Focus();
52540 if (!this.getEditor().Selection.GetSelection()) {
52541 this.focus.defer(100,this, [tag]);
52546 var r = this.getEditor().EditorDocument.createRange();
52547 r.setStart(tgs[0],0);
52548 r.setEnd(tgs[0],0);
52549 this.getEditor().Selection.GetSelection().removeAllRanges();
52550 this.getEditor().Selection.GetSelection().addRange(r);
52551 this.getEditor().Focus();
52558 replaceTextarea : function()
52560 if ( document.getElementById( this.getId() + '___Frame' ) ) {
52563 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52565 // We must check the elements firstly using the Id and then the name.
52566 var oTextarea = document.getElementById( this.getId() );
52568 var colElementsByName = document.getElementsByName( this.getId() ) ;
52570 oTextarea.style.display = 'none' ;
52572 if ( oTextarea.tabIndex ) {
52573 this.TabIndex = oTextarea.tabIndex ;
52576 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52577 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52578 this.frame = Roo.get(this.getId() + '___Frame')
52581 _getConfigHtml : function()
52585 for ( var o in this.fckconfig ) {
52586 sConfig += sConfig.length > 0 ? '&' : '';
52587 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52590 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52594 _getIFrameHtml : function()
52596 var sFile = 'fckeditor.html' ;
52597 /* no idea what this is about..
52600 if ( (/fcksource=true/i).test( window.top.location.search ) )
52601 sFile = 'fckeditor.original.html' ;
52606 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52607 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
52610 var html = '<iframe id="' + this.getId() +
52611 '___Frame" src="' + sLink +
52612 '" width="' + this.width +
52613 '" height="' + this.height + '"' +
52614 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
52615 ' frameborder="0" scrolling="no"></iframe>' ;
52620 _insertHtmlBefore : function( html, element )
52622 if ( element.insertAdjacentHTML ) {
52624 element.insertAdjacentHTML( 'beforeBegin', html ) ;
52626 var oRange = document.createRange() ;
52627 oRange.setStartBefore( element ) ;
52628 var oFragment = oRange.createContextualFragment( html );
52629 element.parentNode.insertBefore( oFragment, element ) ;
52642 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52644 function FCKeditor_OnComplete(editorInstance){
52645 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52646 f.fckEditor = editorInstance;
52647 //console.log("loaded");
52648 f.fireEvent('editorinit', f, editorInstance);
52668 //<script type="text/javascript">
52670 * @class Roo.form.GridField
52671 * @extends Roo.form.Field
52672 * Embed a grid (or editable grid into a form)
52675 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52677 * xgrid.store = Roo.data.Store
52678 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52679 * xgrid.store.reader = Roo.data.JsonReader
52683 * Creates a new GridField
52684 * @param {Object} config Configuration options
52686 Roo.form.GridField = function(config){
52687 Roo.form.GridField.superclass.constructor.call(this, config);
52691 Roo.extend(Roo.form.GridField, Roo.form.Field, {
52693 * @cfg {Number} width - used to restrict width of grid..
52697 * @cfg {Number} height - used to restrict height of grid..
52701 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52707 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52708 * {tag: "input", type: "checkbox", autocomplete: "off"})
52710 // defaultAutoCreate : { tag: 'div' },
52711 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52713 * @cfg {String} addTitle Text to include for adding a title.
52717 onResize : function(){
52718 Roo.form.Field.superclass.onResize.apply(this, arguments);
52721 initEvents : function(){
52722 // Roo.form.Checkbox.superclass.initEvents.call(this);
52723 // has no events...
52728 getResizeEl : function(){
52732 getPositionEl : function(){
52737 onRender : function(ct, position){
52739 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52740 var style = this.style;
52743 Roo.form.GridField.superclass.onRender.call(this, ct, position);
52744 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52745 this.viewEl = this.wrap.createChild({ tag: 'div' });
52747 this.viewEl.applyStyles(style);
52750 this.viewEl.setWidth(this.width);
52753 this.viewEl.setHeight(this.height);
52755 //if(this.inputValue !== undefined){
52756 //this.setValue(this.value);
52759 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52762 this.grid.render();
52763 this.grid.getDataSource().on('remove', this.refreshValue, this);
52764 this.grid.getDataSource().on('update', this.refreshValue, this);
52765 this.grid.on('afteredit', this.refreshValue, this);
52771 * Sets the value of the item.
52772 * @param {String} either an object or a string..
52774 setValue : function(v){
52776 v = v || []; // empty set..
52777 // this does not seem smart - it really only affects memoryproxy grids..
52778 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52779 var ds = this.grid.getDataSource();
52780 // assumes a json reader..
52782 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
52783 ds.loadData( data);
52785 // clear selection so it does not get stale.
52786 if (this.grid.sm) {
52787 this.grid.sm.clearSelections();
52790 Roo.form.GridField.superclass.setValue.call(this, v);
52791 this.refreshValue();
52792 // should load data in the grid really....
52796 refreshValue: function() {
52798 this.grid.getDataSource().each(function(r) {
52801 this.el.dom.value = Roo.encode(val);
52809 * Ext JS Library 1.1.1
52810 * Copyright(c) 2006-2007, Ext JS, LLC.
52812 * Originally Released Under LGPL - original licence link has changed is not relivant.
52815 * <script type="text/javascript">
52818 * @class Roo.form.DisplayField
52819 * @extends Roo.form.Field
52820 * A generic Field to display non-editable data.
52821 * @cfg {Boolean} closable (true|false) default false
52823 * Creates a new Display Field item.
52824 * @param {Object} config Configuration options
52826 Roo.form.DisplayField = function(config){
52827 Roo.form.DisplayField.superclass.constructor.call(this, config);
52832 * Fires after the click the close btn
52833 * @param {Roo.form.DisplayField} this
52839 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
52840 inputType: 'hidden',
52846 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52848 focusClass : undefined,
52850 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52852 fieldClass: 'x-form-field',
52855 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52857 valueRenderer: undefined,
52861 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52862 * {tag: "input", type: "checkbox", autocomplete: "off"})
52865 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52869 onResize : function(){
52870 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52874 initEvents : function(){
52875 // Roo.form.Checkbox.superclass.initEvents.call(this);
52876 // has no events...
52879 this.closeEl.on('click', this.onClose, this);
52885 getResizeEl : function(){
52889 getPositionEl : function(){
52894 onRender : function(ct, position){
52896 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52897 //if(this.inputValue !== undefined){
52898 this.wrap = this.el.wrap();
52900 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52903 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52906 if (this.bodyStyle) {
52907 this.viewEl.applyStyles(this.bodyStyle);
52909 //this.viewEl.setStyle('padding', '2px');
52911 this.setValue(this.value);
52916 initValue : Roo.emptyFn,
52921 onClick : function(){
52926 * Sets the checked state of the checkbox.
52927 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52929 setValue : function(v){
52931 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
52932 // this might be called before we have a dom element..
52933 if (!this.viewEl) {
52936 this.viewEl.dom.innerHTML = html;
52937 Roo.form.DisplayField.superclass.setValue.call(this, v);
52941 onClose : function(e)
52943 e.preventDefault();
52945 this.fireEvent('close', this);
52954 * @class Roo.form.DayPicker
52955 * @extends Roo.form.Field
52956 * A Day picker show [M] [T] [W] ....
52958 * Creates a new Day Picker
52959 * @param {Object} config Configuration options
52961 Roo.form.DayPicker= function(config){
52962 Roo.form.DayPicker.superclass.constructor.call(this, config);
52966 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
52968 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52970 focusClass : undefined,
52972 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52974 fieldClass: "x-form-field",
52977 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52978 * {tag: "input", type: "checkbox", autocomplete: "off"})
52980 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52983 actionMode : 'viewEl',
52987 inputType : 'hidden',
52990 inputElement: false, // real input element?
52991 basedOn: false, // ????
52993 isFormField: true, // not sure where this is needed!!!!
52995 onResize : function(){
52996 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52997 if(!this.boxLabel){
52998 this.el.alignTo(this.wrap, 'c-c');
53002 initEvents : function(){
53003 Roo.form.Checkbox.superclass.initEvents.call(this);
53004 this.el.on("click", this.onClick, this);
53005 this.el.on("change", this.onClick, this);
53009 getResizeEl : function(){
53013 getPositionEl : function(){
53019 onRender : function(ct, position){
53020 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53022 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53024 var r1 = '<table><tr>';
53025 var r2 = '<tr class="x-form-daypick-icons">';
53026 for (var i=0; i < 7; i++) {
53027 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53028 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
53031 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53032 viewEl.select('img').on('click', this.onClick, this);
53033 this.viewEl = viewEl;
53036 // this will not work on Chrome!!!
53037 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
53038 this.el.on('propertychange', this.setFromHidden, this); //ie
53046 initValue : Roo.emptyFn,
53049 * Returns the checked state of the checkbox.
53050 * @return {Boolean} True if checked, else false
53052 getValue : function(){
53053 return this.el.dom.value;
53058 onClick : function(e){
53059 //this.setChecked(!this.checked);
53060 Roo.get(e.target).toggleClass('x-menu-item-checked');
53061 this.refreshValue();
53062 //if(this.el.dom.checked != this.checked){
53063 // this.setValue(this.el.dom.checked);
53068 refreshValue : function()
53071 this.viewEl.select('img',true).each(function(e,i,n) {
53072 val += e.is(".x-menu-item-checked") ? String(n) : '';
53074 this.setValue(val, true);
53078 * Sets the checked state of the checkbox.
53079 * On is always based on a string comparison between inputValue and the param.
53080 * @param {Boolean/String} value - the value to set
53081 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53083 setValue : function(v,suppressEvent){
53084 if (!this.el.dom) {
53087 var old = this.el.dom.value ;
53088 this.el.dom.value = v;
53089 if (suppressEvent) {
53093 // update display..
53094 this.viewEl.select('img',true).each(function(e,i,n) {
53096 var on = e.is(".x-menu-item-checked");
53097 var newv = v.indexOf(String(n)) > -1;
53099 e.toggleClass('x-menu-item-checked');
53105 this.fireEvent('change', this, v, old);
53110 // handle setting of hidden value by some other method!!?!?
53111 setFromHidden: function()
53116 //console.log("SET FROM HIDDEN");
53117 //alert('setFrom hidden');
53118 this.setValue(this.el.dom.value);
53121 onDestroy : function()
53124 Roo.get(this.viewEl).remove();
53127 Roo.form.DayPicker.superclass.onDestroy.call(this);
53131 * RooJS Library 1.1.1
53132 * Copyright(c) 2008-2011 Alan Knowles
53139 * @class Roo.form.ComboCheck
53140 * @extends Roo.form.ComboBox
53141 * A combobox for multiple select items.
53143 * FIXME - could do with a reset button..
53146 * Create a new ComboCheck
53147 * @param {Object} config Configuration options
53149 Roo.form.ComboCheck = function(config){
53150 Roo.form.ComboCheck.superclass.constructor.call(this, config);
53151 // should verify some data...
53153 // hiddenName = required..
53154 // displayField = required
53155 // valudField == required
53156 var req= [ 'hiddenName', 'displayField', 'valueField' ];
53158 Roo.each(req, function(e) {
53159 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53160 throw "Roo.form.ComboCheck : missing value for: " + e;
53167 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53172 selectedClass: 'x-menu-item-checked',
53175 onRender : function(ct, position){
53181 var cls = 'x-combo-list';
53184 this.tpl = new Roo.Template({
53185 html : '<div class="'+cls+'-item x-menu-check-item">' +
53186 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
53187 '<span>{' + this.displayField + '}</span>' +
53194 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53195 this.view.singleSelect = false;
53196 this.view.multiSelect = true;
53197 this.view.toggleSelect = true;
53198 this.pageTb.add(new Roo.Toolbar.Fill(), {
53201 handler: function()
53208 onViewOver : function(e, t){
53214 onViewClick : function(doFocus,index){
53218 select: function () {
53219 //Roo.log("SELECT CALLED");
53222 selectByValue : function(xv, scrollIntoView){
53223 var ar = this.getValueArray();
53226 Roo.each(ar, function(v) {
53227 if(v === undefined || v === null){
53230 var r = this.findRecord(this.valueField, v);
53232 sels.push(this.store.indexOf(r))
53236 this.view.select(sels);
53242 onSelect : function(record, index){
53243 // Roo.log("onselect Called");
53244 // this is only called by the clear button now..
53245 this.view.clearSelections();
53246 this.setValue('[]');
53247 if (this.value != this.valueBefore) {
53248 this.fireEvent('change', this, this.value, this.valueBefore);
53249 this.valueBefore = this.value;
53252 getValueArray : function()
53257 //Roo.log(this.value);
53258 if (typeof(this.value) == 'undefined') {
53261 var ar = Roo.decode(this.value);
53262 return ar instanceof Array ? ar : []; //?? valid?
53265 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
53270 expand : function ()
53273 Roo.form.ComboCheck.superclass.expand.call(this);
53274 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53275 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53280 collapse : function(){
53281 Roo.form.ComboCheck.superclass.collapse.call(this);
53282 var sl = this.view.getSelectedIndexes();
53283 var st = this.store;
53287 Roo.each(sl, function(i) {
53289 nv.push(r.get(this.valueField));
53291 this.setValue(Roo.encode(nv));
53292 if (this.value != this.valueBefore) {
53294 this.fireEvent('change', this, this.value, this.valueBefore);
53295 this.valueBefore = this.value;
53300 setValue : function(v){
53304 var vals = this.getValueArray();
53306 Roo.each(vals, function(k) {
53307 var r = this.findRecord(this.valueField, k);
53309 tv.push(r.data[this.displayField]);
53310 }else if(this.valueNotFoundText !== undefined){
53311 tv.push( this.valueNotFoundText );
53316 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53317 this.hiddenField.value = v;
53323 * Ext JS Library 1.1.1
53324 * Copyright(c) 2006-2007, Ext JS, LLC.
53326 * Originally Released Under LGPL - original licence link has changed is not relivant.
53329 * <script type="text/javascript">
53333 * @class Roo.form.Signature
53334 * @extends Roo.form.Field
53338 * @param {Object} config Configuration options
53341 Roo.form.Signature = function(config){
53342 Roo.form.Signature.superclass.constructor.call(this, config);
53344 this.addEvents({// not in used??
53347 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53348 * @param {Roo.form.Signature} combo This combo box
53353 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53354 * @param {Roo.form.ComboBox} combo This combo box
53355 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53361 Roo.extend(Roo.form.Signature, Roo.form.Field, {
53363 * @cfg {Object} labels Label to use when rendering a form.
53367 * confirm : "Confirm"
53372 confirm : "Confirm"
53375 * @cfg {Number} width The signature panel width (defaults to 300)
53379 * @cfg {Number} height The signature panel height (defaults to 100)
53383 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53385 allowBlank : false,
53388 // {Object} signPanel The signature SVG panel element (defaults to {})
53390 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53391 isMouseDown : false,
53392 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53393 isConfirmed : false,
53394 // {String} signatureTmp SVG mapping string (defaults to empty string)
53398 defaultAutoCreate : { // modified by initCompnoent..
53404 onRender : function(ct, position){
53406 Roo.form.Signature.superclass.onRender.call(this, ct, position);
53408 this.wrap = this.el.wrap({
53409 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53412 this.createToolbar(this);
53413 this.signPanel = this.wrap.createChild({
53415 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53419 this.svgID = Roo.id();
53420 this.svgEl = this.signPanel.createChild({
53421 xmlns : 'http://www.w3.org/2000/svg',
53423 id : this.svgID + "-svg",
53425 height: this.height,
53426 viewBox: '0 0 '+this.width+' '+this.height,
53430 id: this.svgID + "-svg-r",
53432 height: this.height,
53437 id: this.svgID + "-svg-l",
53439 y1: (this.height*0.8), // start set the line in 80% of height
53440 x2: this.width, // end
53441 y2: (this.height*0.8), // end set the line in 80% of height
53443 'stroke-width': "1",
53444 'stroke-dasharray': "3",
53445 'shape-rendering': "crispEdges",
53446 'pointer-events': "none"
53450 id: this.svgID + "-svg-p",
53452 'stroke-width': "3",
53454 'pointer-events': 'none'
53459 this.svgBox = this.svgEl.dom.getScreenCTM();
53461 createSVG : function(){
53462 var svg = this.signPanel;
53463 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53466 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53467 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53468 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53469 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53470 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53471 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53472 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53475 isTouchEvent : function(e){
53476 return e.type.match(/^touch/);
53478 getCoords : function (e) {
53479 var pt = this.svgEl.dom.createSVGPoint();
53482 if (this.isTouchEvent(e)) {
53483 pt.x = e.targetTouches[0].clientX;
53484 pt.y = e.targetTouches[0].clientY;
53486 var a = this.svgEl.dom.getScreenCTM();
53487 var b = a.inverse();
53488 var mx = pt.matrixTransform(b);
53489 return mx.x + ',' + mx.y;
53491 //mouse event headler
53492 down : function (e) {
53493 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53494 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53496 this.isMouseDown = true;
53498 e.preventDefault();
53500 move : function (e) {
53501 if (this.isMouseDown) {
53502 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53503 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53506 e.preventDefault();
53508 up : function (e) {
53509 this.isMouseDown = false;
53510 var sp = this.signatureTmp.split(' ');
53513 if(!sp[sp.length-2].match(/^L/)){
53517 this.signatureTmp = sp.join(" ");
53520 if(this.getValue() != this.signatureTmp){
53521 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53522 this.isConfirmed = false;
53524 e.preventDefault();
53528 * Protected method that will not generally be called directly. It
53529 * is called when the editor creates its toolbar. Override this method if you need to
53530 * add custom toolbar buttons.
53531 * @param {HtmlEditor} editor
53533 createToolbar : function(editor){
53534 function btn(id, toggle, handler){
53535 var xid = fid + '-'+ id ;
53539 cls : 'x-btn-icon x-edit-'+id,
53540 enableToggle:toggle !== false,
53541 scope: editor, // was editor...
53542 handler:handler||editor.relayBtnCmd,
53543 clickEvent:'mousedown',
53544 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53550 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53554 cls : ' x-signature-btn x-signature-'+id,
53555 scope: editor, // was editor...
53556 handler: this.reset,
53557 clickEvent:'mousedown',
53558 text: this.labels.clear
53565 cls : ' x-signature-btn x-signature-'+id,
53566 scope: editor, // was editor...
53567 handler: this.confirmHandler,
53568 clickEvent:'mousedown',
53569 text: this.labels.confirm
53576 * when user is clicked confirm then show this image.....
53578 * @return {String} Image Data URI
53580 getImageDataURI : function(){
53581 var svg = this.svgEl.dom.parentNode.innerHTML;
53582 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53587 * @return {Boolean} this.isConfirmed
53589 getConfirmed : function(){
53590 return this.isConfirmed;
53594 * @return {Number} this.width
53596 getWidth : function(){
53601 * @return {Number} this.height
53603 getHeight : function(){
53604 return this.height;
53607 getSignature : function(){
53608 return this.signatureTmp;
53611 reset : function(){
53612 this.signatureTmp = '';
53613 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53614 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53615 this.isConfirmed = false;
53616 Roo.form.Signature.superclass.reset.call(this);
53618 setSignature : function(s){
53619 this.signatureTmp = s;
53620 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53621 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53623 this.isConfirmed = false;
53624 Roo.form.Signature.superclass.reset.call(this);
53627 // Roo.log(this.signPanel.dom.contentWindow.up())
53630 setConfirmed : function(){
53634 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53637 confirmHandler : function(){
53638 if(!this.getSignature()){
53642 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53643 this.setValue(this.getSignature());
53644 this.isConfirmed = true;
53646 this.fireEvent('confirm', this);
53649 // Subclasses should provide the validation implementation by overriding this
53650 validateValue : function(value){
53651 if(this.allowBlank){
53655 if(this.isConfirmed){
53662 * Ext JS Library 1.1.1
53663 * Copyright(c) 2006-2007, Ext JS, LLC.
53665 * Originally Released Under LGPL - original licence link has changed is not relivant.
53668 * <script type="text/javascript">
53673 * @class Roo.form.ComboBox
53674 * @extends Roo.form.TriggerField
53675 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53677 * Create a new ComboBox.
53678 * @param {Object} config Configuration options
53680 Roo.form.Select = function(config){
53681 Roo.form.Select.superclass.constructor.call(this, config);
53685 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53687 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53690 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53691 * rendering into an Roo.Editor, defaults to false)
53694 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53695 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53698 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53701 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53702 * the dropdown list (defaults to undefined, with no header element)
53706 * @cfg {String/Roo.Template} tpl The template to use to render the output
53710 defaultAutoCreate : {tag: "select" },
53712 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53714 listWidth: undefined,
53716 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53717 * mode = 'remote' or 'text' if mode = 'local')
53719 displayField: undefined,
53721 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53722 * mode = 'remote' or 'value' if mode = 'local').
53723 * Note: use of a valueField requires the user make a selection
53724 * in order for a value to be mapped.
53726 valueField: undefined,
53730 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53731 * field's data value (defaults to the underlying DOM element's name)
53733 hiddenName: undefined,
53735 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53739 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53741 selectedClass: 'x-combo-selected',
53743 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
53744 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53745 * which displays a downward arrow icon).
53747 triggerClass : 'x-form-arrow-trigger',
53749 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53753 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53754 * anchor positions (defaults to 'tl-bl')
53756 listAlign: 'tl-bl?',
53758 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53762 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
53763 * query specified by the allQuery config option (defaults to 'query')
53765 triggerAction: 'query',
53767 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53768 * (defaults to 4, does not apply if editable = false)
53772 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53773 * delay (typeAheadDelay) if it matches a known value (defaults to false)
53777 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53778 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53782 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53783 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
53787 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
53788 * when editable = true (defaults to false)
53790 selectOnFocus:false,
53792 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53794 queryParam: 'query',
53796 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
53797 * when mode = 'remote' (defaults to 'Loading...')
53799 loadingText: 'Loading...',
53801 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53805 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53809 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53810 * traditional select (defaults to true)
53814 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53818 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53822 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53823 * listWidth has a higher value)
53827 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53828 * allow the user to set arbitrary text into the field (defaults to false)
53830 forceSelection:false,
53832 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53833 * if typeAhead = true (defaults to 250)
53835 typeAheadDelay : 250,
53837 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53838 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53840 valueNotFoundText : undefined,
53843 * @cfg {String} defaultValue The value displayed after loading the store.
53848 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53850 blockFocus : false,
53853 * @cfg {Boolean} disableClear Disable showing of clear button.
53855 disableClear : false,
53857 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
53859 alwaysQuery : false,
53865 // element that contains real text value.. (when hidden is used..)
53868 onRender : function(ct, position){
53869 Roo.form.Field.prototype.onRender.call(this, ct, position);
53872 this.store.on('beforeload', this.onBeforeLoad, this);
53873 this.store.on('load', this.onLoad, this);
53874 this.store.on('loadexception', this.onLoadException, this);
53875 this.store.load({});
53883 initEvents : function(){
53884 //Roo.form.ComboBox.superclass.initEvents.call(this);
53888 onDestroy : function(){
53891 this.store.un('beforeload', this.onBeforeLoad, this);
53892 this.store.un('load', this.onLoad, this);
53893 this.store.un('loadexception', this.onLoadException, this);
53895 //Roo.form.ComboBox.superclass.onDestroy.call(this);
53899 fireKey : function(e){
53900 if(e.isNavKeyPress() && !this.list.isVisible()){
53901 this.fireEvent("specialkey", this, e);
53906 onResize: function(w, h){
53914 * Allow or prevent the user from directly editing the field text. If false is passed,
53915 * the user will only be able to select from the items defined in the dropdown list. This method
53916 * is the runtime equivalent of setting the 'editable' config option at config time.
53917 * @param {Boolean} value True to allow the user to directly edit the field text
53919 setEditable : function(value){
53924 onBeforeLoad : function(){
53926 Roo.log("Select before load");
53929 this.innerList.update(this.loadingText ?
53930 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53931 //this.restrictHeight();
53932 this.selectedIndex = -1;
53936 onLoad : function(){
53939 var dom = this.el.dom;
53940 dom.innerHTML = '';
53941 var od = dom.ownerDocument;
53943 if (this.emptyText) {
53944 var op = od.createElement('option');
53945 op.setAttribute('value', '');
53946 op.innerHTML = String.format('{0}', this.emptyText);
53947 dom.appendChild(op);
53949 if(this.store.getCount() > 0){
53951 var vf = this.valueField;
53952 var df = this.displayField;
53953 this.store.data.each(function(r) {
53954 // which colmsn to use... testing - cdoe / title..
53955 var op = od.createElement('option');
53956 op.setAttribute('value', r.data[vf]);
53957 op.innerHTML = String.format('{0}', r.data[df]);
53958 dom.appendChild(op);
53960 if (typeof(this.defaultValue != 'undefined')) {
53961 this.setValue(this.defaultValue);
53966 //this.onEmptyResults();
53971 onLoadException : function()
53973 dom.innerHTML = '';
53975 Roo.log("Select on load exception");
53979 Roo.log(this.store.reader.jsonData);
53980 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53981 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53987 onTypeAhead : function(){
53992 onSelect : function(record, index){
53993 Roo.log('on select?');
53995 if(this.fireEvent('beforeselect', this, record, index) !== false){
53996 this.setFromData(index > -1 ? record.data : false);
53998 this.fireEvent('select', this, record, index);
54003 * Returns the currently selected field value or empty string if no value is set.
54004 * @return {String} value The selected value
54006 getValue : function(){
54007 var dom = this.el.dom;
54008 this.value = dom.options[dom.selectedIndex].value;
54014 * Clears any text/value currently set in the field
54016 clearValue : function(){
54018 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54023 * Sets the specified value into the field. If the value finds a match, the corresponding record text
54024 * will be displayed in the field. If the value does not match the data value of an existing item,
54025 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54026 * Otherwise the field will be blank (although the value will still be set).
54027 * @param {String} value The value to match
54029 setValue : function(v){
54030 var d = this.el.dom;
54031 for (var i =0; i < d.options.length;i++) {
54032 if (v == d.options[i].value) {
54033 d.selectedIndex = i;
54041 * @property {Object} the last set data for the element
54046 * Sets the value of the field based on a object which is related to the record format for the store.
54047 * @param {Object} value the value to set as. or false on reset?
54049 setFromData : function(o){
54050 Roo.log('setfrom data?');
54056 reset : function(){
54060 findRecord : function(prop, value){
54065 if(this.store.getCount() > 0){
54066 this.store.each(function(r){
54067 if(r.data[prop] == value){
54077 getName: function()
54079 // returns hidden if it's set..
54080 if (!this.rendered) {return ''};
54081 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
54089 onEmptyResults : function(){
54090 Roo.log('empty results');
54095 * Returns true if the dropdown list is expanded, else false.
54097 isExpanded : function(){
54102 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54103 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54104 * @param {String} value The data value of the item to select
54105 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54106 * selected item if it is not currently in view (defaults to true)
54107 * @return {Boolean} True if the value matched an item in the list, else false
54109 selectByValue : function(v, scrollIntoView){
54110 Roo.log('select By Value');
54113 if(v !== undefined && v !== null){
54114 var r = this.findRecord(this.valueField || this.displayField, v);
54116 this.select(this.store.indexOf(r), scrollIntoView);
54124 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54125 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54126 * @param {Number} index The zero-based index of the list item to select
54127 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54128 * selected item if it is not currently in view (defaults to true)
54130 select : function(index, scrollIntoView){
54131 Roo.log('select ');
54134 this.selectedIndex = index;
54135 this.view.select(index);
54136 if(scrollIntoView !== false){
54137 var el = this.view.getNode(index);
54139 this.innerList.scrollChildIntoView(el, false);
54147 validateBlur : function(){
54154 initQuery : function(){
54155 this.doQuery(this.getRawValue());
54159 doForce : function(){
54160 if(this.el.dom.value.length > 0){
54161 this.el.dom.value =
54162 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54168 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
54169 * query allowing the query action to be canceled if needed.
54170 * @param {String} query The SQL query to execute
54171 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54172 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
54173 * saved in the current store (defaults to false)
54175 doQuery : function(q, forceAll){
54177 Roo.log('doQuery?');
54178 if(q === undefined || q === null){
54183 forceAll: forceAll,
54187 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54191 forceAll = qe.forceAll;
54192 if(forceAll === true || (q.length >= this.minChars)){
54193 if(this.lastQuery != q || this.alwaysQuery){
54194 this.lastQuery = q;
54195 if(this.mode == 'local'){
54196 this.selectedIndex = -1;
54198 this.store.clearFilter();
54200 this.store.filter(this.displayField, q);
54204 this.store.baseParams[this.queryParam] = q;
54206 params: this.getParams(q)
54211 this.selectedIndex = -1;
54218 getParams : function(q){
54220 //p[this.queryParam] = q;
54223 p.limit = this.pageSize;
54229 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54231 collapse : function(){
54236 collapseIf : function(e){
54241 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54243 expand : function(){
54251 * @cfg {Boolean} grow
54255 * @cfg {Number} growMin
54259 * @cfg {Number} growMax
54267 setWidth : function()
54271 getResizeEl : function(){
54274 });//<script type="text/javasscript">
54278 * @class Roo.DDView
54279 * A DnD enabled version of Roo.View.
54280 * @param {Element/String} container The Element in which to create the View.
54281 * @param {String} tpl The template string used to create the markup for each element of the View
54282 * @param {Object} config The configuration properties. These include all the config options of
54283 * {@link Roo.View} plus some specific to this class.<br>
54285 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54286 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54288 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54289 .x-view-drag-insert-above {
54290 border-top:1px dotted #3366cc;
54292 .x-view-drag-insert-below {
54293 border-bottom:1px dotted #3366cc;
54299 Roo.DDView = function(container, tpl, config) {
54300 Roo.DDView.superclass.constructor.apply(this, arguments);
54301 this.getEl().setStyle("outline", "0px none");
54302 this.getEl().unselectable();
54303 if (this.dragGroup) {
54304 this.setDraggable(this.dragGroup.split(","));
54306 if (this.dropGroup) {
54307 this.setDroppable(this.dropGroup.split(","));
54309 if (this.deletable) {
54310 this.setDeletable();
54312 this.isDirtyFlag = false;
54318 Roo.extend(Roo.DDView, Roo.View, {
54319 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54320 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54321 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54322 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54326 reset: Roo.emptyFn,
54328 clearInvalid: Roo.form.Field.prototype.clearInvalid,
54330 validate: function() {
54334 destroy: function() {
54335 this.purgeListeners();
54336 this.getEl.removeAllListeners();
54337 this.getEl().remove();
54338 if (this.dragZone) {
54339 if (this.dragZone.destroy) {
54340 this.dragZone.destroy();
54343 if (this.dropZone) {
54344 if (this.dropZone.destroy) {
54345 this.dropZone.destroy();
54350 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54351 getName: function() {
54355 /** Loads the View from a JSON string representing the Records to put into the Store. */
54356 setValue: function(v) {
54358 throw "DDView.setValue(). DDView must be constructed with a valid Store";
54361 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54362 this.store.proxy = new Roo.data.MemoryProxy(data);
54366 /** @return {String} a parenthesised list of the ids of the Records in the View. */
54367 getValue: function() {
54369 this.store.each(function(rec) {
54370 result += rec.id + ',';
54372 return result.substr(0, result.length - 1) + ')';
54375 getIds: function() {
54376 var i = 0, result = new Array(this.store.getCount());
54377 this.store.each(function(rec) {
54378 result[i++] = rec.id;
54383 isDirty: function() {
54384 return this.isDirtyFlag;
54388 * Part of the Roo.dd.DropZone interface. If no target node is found, the
54389 * whole Element becomes the target, and this causes the drop gesture to append.
54391 getTargetFromEvent : function(e) {
54392 var target = e.getTarget();
54393 while ((target !== null) && (target.parentNode != this.el.dom)) {
54394 target = target.parentNode;
54397 target = this.el.dom.lastChild || this.el.dom;
54403 * Create the drag data which consists of an object which has the property "ddel" as
54404 * the drag proxy element.
54406 getDragData : function(e) {
54407 var target = this.findItemFromChild(e.getTarget());
54409 this.handleSelection(e);
54410 var selNodes = this.getSelectedNodes();
54413 copy: this.copy || (this.allowCopy && e.ctrlKey),
54417 var selectedIndices = this.getSelectedIndexes();
54418 for (var i = 0; i < selectedIndices.length; i++) {
54419 dragData.records.push(this.store.getAt(selectedIndices[i]));
54421 if (selNodes.length == 1) {
54422 dragData.ddel = target.cloneNode(true); // the div element
54424 var div = document.createElement('div'); // create the multi element drag "ghost"
54425 div.className = 'multi-proxy';
54426 for (var i = 0, len = selNodes.length; i < len; i++) {
54427 div.appendChild(selNodes[i].cloneNode(true));
54429 dragData.ddel = div;
54431 //console.log(dragData)
54432 //console.log(dragData.ddel.innerHTML)
54435 //console.log('nodragData')
54439 /** Specify to which ddGroup items in this DDView may be dragged. */
54440 setDraggable: function(ddGroup) {
54441 if (ddGroup instanceof Array) {
54442 Roo.each(ddGroup, this.setDraggable, this);
54445 if (this.dragZone) {
54446 this.dragZone.addToGroup(ddGroup);
54448 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54449 containerScroll: true,
54453 // Draggability implies selection. DragZone's mousedown selects the element.
54454 if (!this.multiSelect) { this.singleSelect = true; }
54456 // Wire the DragZone's handlers up to methods in *this*
54457 this.dragZone.getDragData = this.getDragData.createDelegate(this);
54461 /** Specify from which ddGroup this DDView accepts drops. */
54462 setDroppable: function(ddGroup) {
54463 if (ddGroup instanceof Array) {
54464 Roo.each(ddGroup, this.setDroppable, this);
54467 if (this.dropZone) {
54468 this.dropZone.addToGroup(ddGroup);
54470 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54471 containerScroll: true,
54475 // Wire the DropZone's handlers up to methods in *this*
54476 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54477 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54478 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54479 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54480 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54484 /** Decide whether to drop above or below a View node. */
54485 getDropPoint : function(e, n, dd){
54486 if (n == this.el.dom) { return "above"; }
54487 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54488 var c = t + (b - t) / 2;
54489 var y = Roo.lib.Event.getPageY(e);
54497 onNodeEnter : function(n, dd, e, data){
54501 onNodeOver : function(n, dd, e, data){
54502 var pt = this.getDropPoint(e, n, dd);
54503 // set the insert point style on the target node
54504 var dragElClass = this.dropNotAllowed;
54507 if (pt == "above"){
54508 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54509 targetElClass = "x-view-drag-insert-above";
54511 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54512 targetElClass = "x-view-drag-insert-below";
54514 if (this.lastInsertClass != targetElClass){
54515 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54516 this.lastInsertClass = targetElClass;
54519 return dragElClass;
54522 onNodeOut : function(n, dd, e, data){
54523 this.removeDropIndicators(n);
54526 onNodeDrop : function(n, dd, e, data){
54527 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54530 var pt = this.getDropPoint(e, n, dd);
54531 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54532 if (pt == "below") { insertAt++; }
54533 for (var i = 0; i < data.records.length; i++) {
54534 var r = data.records[i];
54535 var dup = this.store.getById(r.id);
54536 if (dup && (dd != this.dragZone)) {
54537 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54540 this.store.insert(insertAt++, r.copy());
54542 data.source.isDirtyFlag = true;
54544 this.store.insert(insertAt++, r);
54546 this.isDirtyFlag = true;
54549 this.dragZone.cachedTarget = null;
54553 removeDropIndicators : function(n){
54555 Roo.fly(n).removeClass([
54556 "x-view-drag-insert-above",
54557 "x-view-drag-insert-below"]);
54558 this.lastInsertClass = "_noclass";
54563 * Utility method. Add a delete option to the DDView's context menu.
54564 * @param {String} imageUrl The URL of the "delete" icon image.
54566 setDeletable: function(imageUrl) {
54567 if (!this.singleSelect && !this.multiSelect) {
54568 this.singleSelect = true;
54570 var c = this.getContextMenu();
54571 this.contextMenu.on("itemclick", function(item) {
54574 this.remove(this.getSelectedIndexes());
54578 this.contextMenu.add({
54585 /** Return the context menu for this DDView. */
54586 getContextMenu: function() {
54587 if (!this.contextMenu) {
54588 // Create the View's context menu
54589 this.contextMenu = new Roo.menu.Menu({
54590 id: this.id + "-contextmenu"
54592 this.el.on("contextmenu", this.showContextMenu, this);
54594 return this.contextMenu;
54597 disableContextMenu: function() {
54598 if (this.contextMenu) {
54599 this.el.un("contextmenu", this.showContextMenu, this);
54603 showContextMenu: function(e, item) {
54604 item = this.findItemFromChild(e.getTarget());
54607 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54608 this.contextMenu.showAt(e.getXY());
54613 * Remove {@link Roo.data.Record}s at the specified indices.
54614 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54616 remove: function(selectedIndices) {
54617 selectedIndices = [].concat(selectedIndices);
54618 for (var i = 0; i < selectedIndices.length; i++) {
54619 var rec = this.store.getAt(selectedIndices[i]);
54620 this.store.remove(rec);
54625 * Double click fires the event, but also, if this is draggable, and there is only one other
54626 * related DropZone, it transfers the selected node.
54628 onDblClick : function(e){
54629 var item = this.findItemFromChild(e.getTarget());
54631 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54634 if (this.dragGroup) {
54635 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54636 while (targets.indexOf(this.dropZone) > -1) {
54637 targets.remove(this.dropZone);
54639 if (targets.length == 1) {
54640 this.dragZone.cachedTarget = null;
54641 var el = Roo.get(targets[0].getEl());
54642 var box = el.getBox(true);
54643 targets[0].onNodeDrop(el.dom, {
54645 xy: [box.x, box.y + box.height - 1]
54646 }, null, this.getDragData(e));
54652 handleSelection: function(e) {
54653 this.dragZone.cachedTarget = null;
54654 var item = this.findItemFromChild(e.getTarget());
54656 this.clearSelections(true);
54659 if (item && (this.multiSelect || this.singleSelect)){
54660 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54661 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54662 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54663 this.unselect(item);
54665 this.select(item, this.multiSelect && e.ctrlKey);
54666 this.lastSelection = item;
54671 onItemClick : function(item, index, e){
54672 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54678 unselect : function(nodeInfo, suppressEvent){
54679 var node = this.getNode(nodeInfo);
54680 if(node && this.isSelected(node)){
54681 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54682 Roo.fly(node).removeClass(this.selectedClass);
54683 this.selections.remove(node);
54684 if(!suppressEvent){
54685 this.fireEvent("selectionchange", this, this.selections);
54693 * Ext JS Library 1.1.1
54694 * Copyright(c) 2006-2007, Ext JS, LLC.
54696 * Originally Released Under LGPL - original licence link has changed is not relivant.
54699 * <script type="text/javascript">
54703 * @class Roo.LayoutManager
54704 * @extends Roo.util.Observable
54705 * Base class for layout managers.
54707 Roo.LayoutManager = function(container, config){
54708 Roo.LayoutManager.superclass.constructor.call(this);
54709 this.el = Roo.get(container);
54710 // ie scrollbar fix
54711 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54712 document.body.scroll = "no";
54713 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54714 this.el.position('relative');
54716 this.id = this.el.id;
54717 this.el.addClass("x-layout-container");
54718 /** false to disable window resize monitoring @type Boolean */
54719 this.monitorWindowResize = true;
54724 * Fires when a layout is performed.
54725 * @param {Roo.LayoutManager} this
54729 * @event regionresized
54730 * Fires when the user resizes a region.
54731 * @param {Roo.LayoutRegion} region The resized region
54732 * @param {Number} newSize The new size (width for east/west, height for north/south)
54734 "regionresized" : true,
54736 * @event regioncollapsed
54737 * Fires when a region is collapsed.
54738 * @param {Roo.LayoutRegion} region The collapsed region
54740 "regioncollapsed" : true,
54742 * @event regionexpanded
54743 * Fires when a region is expanded.
54744 * @param {Roo.LayoutRegion} region The expanded region
54746 "regionexpanded" : true
54748 this.updating = false;
54749 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54752 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54754 * Returns true if this layout is currently being updated
54755 * @return {Boolean}
54757 isUpdating : function(){
54758 return this.updating;
54762 * Suspend the LayoutManager from doing auto-layouts while
54763 * making multiple add or remove calls
54765 beginUpdate : function(){
54766 this.updating = true;
54770 * Restore auto-layouts and optionally disable the manager from performing a layout
54771 * @param {Boolean} noLayout true to disable a layout update
54773 endUpdate : function(noLayout){
54774 this.updating = false;
54780 layout: function(){
54784 onRegionResized : function(region, newSize){
54785 this.fireEvent("regionresized", region, newSize);
54789 onRegionCollapsed : function(region){
54790 this.fireEvent("regioncollapsed", region);
54793 onRegionExpanded : function(region){
54794 this.fireEvent("regionexpanded", region);
54798 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54799 * performs box-model adjustments.
54800 * @return {Object} The size as an object {width: (the width), height: (the height)}
54802 getViewSize : function(){
54804 if(this.el.dom != document.body){
54805 size = this.el.getSize();
54807 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54809 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54810 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54815 * Returns the Element this layout is bound to.
54816 * @return {Roo.Element}
54818 getEl : function(){
54823 * Returns the specified region.
54824 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54825 * @return {Roo.LayoutRegion}
54827 getRegion : function(target){
54828 return this.regions[target.toLowerCase()];
54831 onWindowResize : function(){
54832 if(this.monitorWindowResize){
54838 * Ext JS Library 1.1.1
54839 * Copyright(c) 2006-2007, Ext JS, LLC.
54841 * Originally Released Under LGPL - original licence link has changed is not relivant.
54844 * <script type="text/javascript">
54847 * @class Roo.BorderLayout
54848 * @extends Roo.LayoutManager
54849 * @children Roo.ContentPanel
54850 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54851 * please see: <br><br>
54852 * <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>
54853 * <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>
54856 var layout = new Roo.BorderLayout(document.body, {
54890 preferredTabWidth: 150
54895 var CP = Roo.ContentPanel;
54897 layout.beginUpdate();
54898 layout.add("north", new CP("north", "North"));
54899 layout.add("south", new CP("south", {title: "South", closable: true}));
54900 layout.add("west", new CP("west", {title: "West"}));
54901 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54902 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54903 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54904 layout.getRegion("center").showPanel("center1");
54905 layout.endUpdate();
54908 <b>The container the layout is rendered into can be either the body element or any other element.
54909 If it is not the body element, the container needs to either be an absolute positioned element,
54910 or you will need to add "position:relative" to the css of the container. You will also need to specify
54911 the container size if it is not the body element.</b>
54914 * Create a new BorderLayout
54915 * @param {String/HTMLElement/Element} container The container this layout is bound to
54916 * @param {Object} config Configuration options
54918 Roo.BorderLayout = function(container, config){
54919 config = config || {};
54920 Roo.BorderLayout.superclass.constructor.call(this, container, config);
54921 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54922 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54923 var target = this.factory.validRegions[i];
54924 if(config[target]){
54925 this.addRegion(target, config[target]);
54930 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54933 * @cfg {Roo.LayoutRegion} east
54936 * @cfg {Roo.LayoutRegion} west
54939 * @cfg {Roo.LayoutRegion} north
54942 * @cfg {Roo.LayoutRegion} south
54945 * @cfg {Roo.LayoutRegion} center
54948 * Creates and adds a new region if it doesn't already exist.
54949 * @param {String} target The target region key (north, south, east, west or center).
54950 * @param {Object} config The regions config object
54951 * @return {BorderLayoutRegion} The new region
54953 addRegion : function(target, config){
54954 if(!this.regions[target]){
54955 var r = this.factory.create(target, this, config);
54956 this.bindRegion(target, r);
54958 return this.regions[target];
54962 bindRegion : function(name, r){
54963 this.regions[name] = r;
54964 r.on("visibilitychange", this.layout, this);
54965 r.on("paneladded", this.layout, this);
54966 r.on("panelremoved", this.layout, this);
54967 r.on("invalidated", this.layout, this);
54968 r.on("resized", this.onRegionResized, this);
54969 r.on("collapsed", this.onRegionCollapsed, this);
54970 r.on("expanded", this.onRegionExpanded, this);
54974 * Performs a layout update.
54976 layout : function(){
54977 if(this.updating) {
54980 var size = this.getViewSize();
54981 var w = size.width;
54982 var h = size.height;
54987 //var x = 0, y = 0;
54989 var rs = this.regions;
54990 var north = rs["north"];
54991 var south = rs["south"];
54992 var west = rs["west"];
54993 var east = rs["east"];
54994 var center = rs["center"];
54995 //if(this.hideOnLayout){ // not supported anymore
54996 //c.el.setStyle("display", "none");
54998 if(north && north.isVisible()){
54999 var b = north.getBox();
55000 var m = north.getMargins();
55001 b.width = w - (m.left+m.right);
55004 centerY = b.height + b.y + m.bottom;
55005 centerH -= centerY;
55006 north.updateBox(this.safeBox(b));
55008 if(south && south.isVisible()){
55009 var b = south.getBox();
55010 var m = south.getMargins();
55011 b.width = w - (m.left+m.right);
55013 var totalHeight = (b.height + m.top + m.bottom);
55014 b.y = h - totalHeight + m.top;
55015 centerH -= totalHeight;
55016 south.updateBox(this.safeBox(b));
55018 if(west && west.isVisible()){
55019 var b = west.getBox();
55020 var m = west.getMargins();
55021 b.height = centerH - (m.top+m.bottom);
55023 b.y = centerY + m.top;
55024 var totalWidth = (b.width + m.left + m.right);
55025 centerX += totalWidth;
55026 centerW -= totalWidth;
55027 west.updateBox(this.safeBox(b));
55029 if(east && east.isVisible()){
55030 var b = east.getBox();
55031 var m = east.getMargins();
55032 b.height = centerH - (m.top+m.bottom);
55033 var totalWidth = (b.width + m.left + m.right);
55034 b.x = w - totalWidth + m.left;
55035 b.y = centerY + m.top;
55036 centerW -= totalWidth;
55037 east.updateBox(this.safeBox(b));
55040 var m = center.getMargins();
55042 x: centerX + m.left,
55043 y: centerY + m.top,
55044 width: centerW - (m.left+m.right),
55045 height: centerH - (m.top+m.bottom)
55047 //if(this.hideOnLayout){
55048 //center.el.setStyle("display", "block");
55050 center.updateBox(this.safeBox(centerBox));
55053 this.fireEvent("layout", this);
55057 safeBox : function(box){
55058 box.width = Math.max(0, box.width);
55059 box.height = Math.max(0, box.height);
55064 * Adds a ContentPanel (or subclass) to this layout.
55065 * @param {String} target The target region key (north, south, east, west or center).
55066 * @param {Roo.ContentPanel} panel The panel to add
55067 * @return {Roo.ContentPanel} The added panel
55069 add : function(target, panel){
55071 target = target.toLowerCase();
55072 return this.regions[target].add(panel);
55076 * Remove a ContentPanel (or subclass) to this layout.
55077 * @param {String} target The target region key (north, south, east, west or center).
55078 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55079 * @return {Roo.ContentPanel} The removed panel
55081 remove : function(target, panel){
55082 target = target.toLowerCase();
55083 return this.regions[target].remove(panel);
55087 * Searches all regions for a panel with the specified id
55088 * @param {String} panelId
55089 * @return {Roo.ContentPanel} The panel or null if it wasn't found
55091 findPanel : function(panelId){
55092 var rs = this.regions;
55093 for(var target in rs){
55094 if(typeof rs[target] != "function"){
55095 var p = rs[target].getPanel(panelId);
55105 * Searches all regions for a panel with the specified id and activates (shows) it.
55106 * @param {String/ContentPanel} panelId The panels id or the panel itself
55107 * @return {Roo.ContentPanel} The shown panel or null
55109 showPanel : function(panelId) {
55110 var rs = this.regions;
55111 for(var target in rs){
55112 var r = rs[target];
55113 if(typeof r != "function"){
55114 if(r.hasPanel(panelId)){
55115 return r.showPanel(panelId);
55123 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55124 * @param {Roo.state.Provider} provider (optional) An alternate state provider
55126 restoreState : function(provider){
55128 provider = Roo.state.Manager;
55130 var sm = new Roo.LayoutStateManager();
55131 sm.init(this, provider);
55135 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
55136 * object should contain properties for each region to add ContentPanels to, and each property's value should be
55137 * a valid ContentPanel config object. Example:
55139 // Create the main layout
55140 var layout = new Roo.BorderLayout('main-ct', {
55151 // Create and add multiple ContentPanels at once via configs
55154 id: 'source-files',
55156 title:'Ext Source Files',
55169 * @param {Object} regions An object containing ContentPanel configs by region name
55171 batchAdd : function(regions){
55172 this.beginUpdate();
55173 for(var rname in regions){
55174 var lr = this.regions[rname];
55176 this.addTypedPanels(lr, regions[rname]);
55183 addTypedPanels : function(lr, ps){
55184 if(typeof ps == 'string'){
55185 lr.add(new Roo.ContentPanel(ps));
55187 else if(ps instanceof Array){
55188 for(var i =0, len = ps.length; i < len; i++){
55189 this.addTypedPanels(lr, ps[i]);
55192 else if(!ps.events){ // raw config?
55194 delete ps.el; // prevent conflict
55195 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55197 else { // panel object assumed!
55202 * Adds a xtype elements to the layout.
55206 xtype : 'ContentPanel',
55213 xtype : 'NestedLayoutPanel',
55219 items : [ ... list of content panels or nested layout panels.. ]
55223 * @param {Object} cfg Xtype definition of item to add.
55225 addxtype : function(cfg)
55227 // basically accepts a pannel...
55228 // can accept a layout region..!?!?
55229 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55231 if (!cfg.xtype.match(/Panel$/)) {
55236 if (typeof(cfg.region) == 'undefined') {
55237 Roo.log("Failed to add Panel, region was not set");
55241 var region = cfg.region;
55247 xitems = cfg.items;
55254 case 'ContentPanel': // ContentPanel (el, cfg)
55255 case 'ScrollPanel': // ContentPanel (el, cfg)
55257 if(cfg.autoCreate) {
55258 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55260 var el = this.el.createChild();
55261 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55264 this.add(region, ret);
55268 case 'TreePanel': // our new panel!
55269 cfg.el = this.el.createChild();
55270 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55271 this.add(region, ret);
55274 case 'NestedLayoutPanel':
55275 // create a new Layout (which is a Border Layout...
55276 var el = this.el.createChild();
55277 var clayout = cfg.layout;
55279 clayout.items = clayout.items || [];
55280 // replace this exitems with the clayout ones..
55281 xitems = clayout.items;
55284 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55285 cfg.background = false;
55287 var layout = new Roo.BorderLayout(el, clayout);
55289 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55290 //console.log('adding nested layout panel ' + cfg.toSource());
55291 this.add(region, ret);
55292 nb = {}; /// find first...
55297 // needs grid and region
55299 //var el = this.getRegion(region).el.createChild();
55300 var el = this.el.createChild();
55301 // create the grid first...
55303 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55305 if (region == 'center' && this.active ) {
55306 cfg.background = false;
55308 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55310 this.add(region, ret);
55311 if (cfg.background) {
55312 ret.on('activate', function(gp) {
55313 if (!gp.grid.rendered) {
55328 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55330 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55331 this.add(region, ret);
55334 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55338 // GridPanel (grid, cfg)
55341 this.beginUpdate();
55345 Roo.each(xitems, function(i) {
55346 region = nb && i.region ? i.region : false;
55348 var add = ret.addxtype(i);
55351 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55352 if (!i.background) {
55353 abn[region] = nb[region] ;
55360 // make the last non-background panel active..
55361 //if (nb) { Roo.log(abn); }
55364 for(var r in abn) {
55365 region = this.getRegion(r);
55367 // tried using nb[r], but it does not work..
55369 region.showPanel(abn[r]);
55380 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55381 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
55382 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55383 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
55386 var CP = Roo.ContentPanel;
55388 var layout = Roo.BorderLayout.create({
55392 panels: [new CP("north", "North")]
55401 panels: [new CP("west", {title: "West"})]
55410 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55419 panels: [new CP("south", {title: "South", closable: true})]
55426 preferredTabWidth: 150,
55428 new CP("center1", {title: "Close Me", closable: true}),
55429 new CP("center2", {title: "Center Panel", closable: false})
55434 layout.getRegion("center").showPanel("center1");
55439 Roo.BorderLayout.create = function(config, targetEl){
55440 var layout = new Roo.BorderLayout(targetEl || document.body, config);
55441 layout.beginUpdate();
55442 var regions = Roo.BorderLayout.RegionFactory.validRegions;
55443 for(var j = 0, jlen = regions.length; j < jlen; j++){
55444 var lr = regions[j];
55445 if(layout.regions[lr] && config[lr].panels){
55446 var r = layout.regions[lr];
55447 var ps = config[lr].panels;
55448 layout.addTypedPanels(r, ps);
55451 layout.endUpdate();
55456 Roo.BorderLayout.RegionFactory = {
55458 validRegions : ["north","south","east","west","center"],
55461 create : function(target, mgr, config){
55462 target = target.toLowerCase();
55463 if(config.lightweight || config.basic){
55464 return new Roo.BasicLayoutRegion(mgr, config, target);
55468 return new Roo.NorthLayoutRegion(mgr, config);
55470 return new Roo.SouthLayoutRegion(mgr, config);
55472 return new Roo.EastLayoutRegion(mgr, config);
55474 return new Roo.WestLayoutRegion(mgr, config);
55476 return new Roo.CenterLayoutRegion(mgr, config);
55478 throw 'Layout region "'+target+'" not supported.';
55482 * Ext JS Library 1.1.1
55483 * Copyright(c) 2006-2007, Ext JS, LLC.
55485 * Originally Released Under LGPL - original licence link has changed is not relivant.
55488 * <script type="text/javascript">
55492 * @class Roo.BasicLayoutRegion
55493 * @extends Roo.util.Observable
55494 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55495 * and does not have a titlebar, tabs or any other features. All it does is size and position
55496 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55498 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55500 this.position = pos;
55503 * @scope Roo.BasicLayoutRegion
55507 * @event beforeremove
55508 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55509 * @param {Roo.LayoutRegion} this
55510 * @param {Roo.ContentPanel} panel The panel
55511 * @param {Object} e The cancel event object
55513 "beforeremove" : true,
55515 * @event invalidated
55516 * Fires when the layout for this region is changed.
55517 * @param {Roo.LayoutRegion} this
55519 "invalidated" : true,
55521 * @event visibilitychange
55522 * Fires when this region is shown or hidden
55523 * @param {Roo.LayoutRegion} this
55524 * @param {Boolean} visibility true or false
55526 "visibilitychange" : true,
55528 * @event paneladded
55529 * Fires when a panel is added.
55530 * @param {Roo.LayoutRegion} this
55531 * @param {Roo.ContentPanel} panel The panel
55533 "paneladded" : true,
55535 * @event panelremoved
55536 * Fires when a panel is removed.
55537 * @param {Roo.LayoutRegion} this
55538 * @param {Roo.ContentPanel} panel The panel
55540 "panelremoved" : true,
55542 * @event beforecollapse
55543 * Fires when this region before collapse.
55544 * @param {Roo.LayoutRegion} this
55546 "beforecollapse" : true,
55549 * Fires when this region is collapsed.
55550 * @param {Roo.LayoutRegion} this
55552 "collapsed" : true,
55555 * Fires when this region is expanded.
55556 * @param {Roo.LayoutRegion} this
55561 * Fires when this region is slid into view.
55562 * @param {Roo.LayoutRegion} this
55564 "slideshow" : true,
55567 * Fires when this region slides out of view.
55568 * @param {Roo.LayoutRegion} this
55570 "slidehide" : true,
55572 * @event panelactivated
55573 * Fires when a panel is activated.
55574 * @param {Roo.LayoutRegion} this
55575 * @param {Roo.ContentPanel} panel The activated panel
55577 "panelactivated" : true,
55580 * Fires when the user resizes this region.
55581 * @param {Roo.LayoutRegion} this
55582 * @param {Number} newSize The new size (width for east/west, height for north/south)
55586 /** A collection of panels in this region. @type Roo.util.MixedCollection */
55587 this.panels = new Roo.util.MixedCollection();
55588 this.panels.getKey = this.getPanelId.createDelegate(this);
55590 this.activePanel = null;
55591 // ensure listeners are added...
55593 if (config.listeners || config.events) {
55594 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55595 listeners : config.listeners || {},
55596 events : config.events || {}
55600 if(skipConfig !== true){
55601 this.applyConfig(config);
55605 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55606 getPanelId : function(p){
55610 applyConfig : function(config){
55611 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55612 this.config = config;
55617 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
55618 * the width, for horizontal (north, south) the height.
55619 * @param {Number} newSize The new width or height
55621 resizeTo : function(newSize){
55622 var el = this.el ? this.el :
55623 (this.activePanel ? this.activePanel.getEl() : null);
55625 switch(this.position){
55628 el.setWidth(newSize);
55629 this.fireEvent("resized", this, newSize);
55633 el.setHeight(newSize);
55634 this.fireEvent("resized", this, newSize);
55640 getBox : function(){
55641 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55644 getMargins : function(){
55645 return this.margins;
55648 updateBox : function(box){
55650 var el = this.activePanel.getEl();
55651 el.dom.style.left = box.x + "px";
55652 el.dom.style.top = box.y + "px";
55653 this.activePanel.setSize(box.width, box.height);
55657 * Returns the container element for this region.
55658 * @return {Roo.Element}
55660 getEl : function(){
55661 return this.activePanel;
55665 * Returns true if this region is currently visible.
55666 * @return {Boolean}
55668 isVisible : function(){
55669 return this.activePanel ? true : false;
55672 setActivePanel : function(panel){
55673 panel = this.getPanel(panel);
55674 if(this.activePanel && this.activePanel != panel){
55675 this.activePanel.setActiveState(false);
55676 this.activePanel.getEl().setLeftTop(-10000,-10000);
55678 this.activePanel = panel;
55679 panel.setActiveState(true);
55681 panel.setSize(this.box.width, this.box.height);
55683 this.fireEvent("panelactivated", this, panel);
55684 this.fireEvent("invalidated");
55688 * Show the specified panel.
55689 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55690 * @return {Roo.ContentPanel} The shown panel or null
55692 showPanel : function(panel){
55693 if(panel = this.getPanel(panel)){
55694 this.setActivePanel(panel);
55700 * Get the active panel for this region.
55701 * @return {Roo.ContentPanel} The active panel or null
55703 getActivePanel : function(){
55704 return this.activePanel;
55708 * Add the passed ContentPanel(s)
55709 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55710 * @return {Roo.ContentPanel} The panel added (if only one was added)
55712 add : function(panel){
55713 if(arguments.length > 1){
55714 for(var i = 0, len = arguments.length; i < len; i++) {
55715 this.add(arguments[i]);
55719 if(this.hasPanel(panel)){
55720 this.showPanel(panel);
55723 var el = panel.getEl();
55724 if(el.dom.parentNode != this.mgr.el.dom){
55725 this.mgr.el.dom.appendChild(el.dom);
55727 if(panel.setRegion){
55728 panel.setRegion(this);
55730 this.panels.add(panel);
55731 el.setStyle("position", "absolute");
55732 if(!panel.background){
55733 this.setActivePanel(panel);
55734 if(this.config.initialSize && this.panels.getCount()==1){
55735 this.resizeTo(this.config.initialSize);
55738 this.fireEvent("paneladded", this, panel);
55743 * Returns true if the panel is in this region.
55744 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55745 * @return {Boolean}
55747 hasPanel : function(panel){
55748 if(typeof panel == "object"){ // must be panel obj
55749 panel = panel.getId();
55751 return this.getPanel(panel) ? true : false;
55755 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55756 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55757 * @param {Boolean} preservePanel Overrides the config preservePanel option
55758 * @return {Roo.ContentPanel} The panel that was removed
55760 remove : function(panel, preservePanel){
55761 panel = this.getPanel(panel);
55766 this.fireEvent("beforeremove", this, panel, e);
55767 if(e.cancel === true){
55770 var panelId = panel.getId();
55771 this.panels.removeKey(panelId);
55776 * Returns the panel specified or null if it's not in this region.
55777 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55778 * @return {Roo.ContentPanel}
55780 getPanel : function(id){
55781 if(typeof id == "object"){ // must be panel obj
55784 return this.panels.get(id);
55788 * Returns this regions position (north/south/east/west/center).
55791 getPosition: function(){
55792 return this.position;
55796 * Ext JS Library 1.1.1
55797 * Copyright(c) 2006-2007, Ext JS, LLC.
55799 * Originally Released Under LGPL - original licence link has changed is not relivant.
55802 * <script type="text/javascript">
55806 * @class Roo.LayoutRegion
55807 * @extends Roo.BasicLayoutRegion
55808 * This class represents a region in a layout manager.
55809 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
55810 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
55811 * @cfg {Boolean} floatable False to disable floating (defaults to true)
55812 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55813 * @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})
55814 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
55815 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
55816 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
55817 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
55818 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
55819 * @cfg {String} title The title for the region (overrides panel titles)
55820 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
55821 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55822 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
55823 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55824 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
55825 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55826 * the space available, similar to FireFox 1.5 tabs (defaults to false)
55827 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
55828 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
55829 * @cfg {Boolean} showPin True to show a pin button
55830 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
55831 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
55832 * @cfg {Boolean} disableTabTips True to disable tab tooltips
55833 * @cfg {Number} width For East/West panels
55834 * @cfg {Number} height For North/South panels
55835 * @cfg {Boolean} split To show the splitter
55836 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
55838 Roo.LayoutRegion = function(mgr, config, pos){
55839 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55840 var dh = Roo.DomHelper;
55841 /** This region's container element
55842 * @type Roo.Element */
55843 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55844 /** This region's title element
55845 * @type Roo.Element */
55847 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55848 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
55849 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55851 this.titleEl.enableDisplayMode();
55852 /** This region's title text element
55853 * @type HTMLElement */
55854 this.titleTextEl = this.titleEl.dom.firstChild;
55855 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55856 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55857 this.closeBtn.enableDisplayMode();
55858 this.closeBtn.on("click", this.closeClicked, this);
55859 this.closeBtn.hide();
55861 this.createBody(config);
55862 this.visible = true;
55863 this.collapsed = false;
55865 if(config.hideWhenEmpty){
55867 this.on("paneladded", this.validateVisibility, this);
55868 this.on("panelremoved", this.validateVisibility, this);
55870 this.applyConfig(config);
55873 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55875 createBody : function(){
55876 /** This region's body element
55877 * @type Roo.Element */
55878 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55881 applyConfig : function(c){
55882 if(c.collapsible && this.position != "center" && !this.collapsedEl){
55883 var dh = Roo.DomHelper;
55884 if(c.titlebar !== false){
55885 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55886 this.collapseBtn.on("click", this.collapse, this);
55887 this.collapseBtn.enableDisplayMode();
55889 if(c.showPin === true || this.showPin){
55890 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55891 this.stickBtn.enableDisplayMode();
55892 this.stickBtn.on("click", this.expand, this);
55893 this.stickBtn.hide();
55896 /** This region's collapsed element
55897 * @type Roo.Element */
55898 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55899 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55901 if(c.floatable !== false){
55902 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55903 this.collapsedEl.on("click", this.collapseClick, this);
55906 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55907 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55908 id: "message", unselectable: "on", style:{"float":"left"}});
55909 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55911 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55912 this.expandBtn.on("click", this.expand, this);
55914 if(this.collapseBtn){
55915 this.collapseBtn.setVisible(c.collapsible == true);
55917 this.cmargins = c.cmargins || this.cmargins ||
55918 (this.position == "west" || this.position == "east" ?
55919 {top: 0, left: 2, right:2, bottom: 0} :
55920 {top: 2, left: 0, right:0, bottom: 2});
55921 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55922 this.bottomTabs = c.tabPosition != "top";
55923 this.autoScroll = c.autoScroll || false;
55924 if(this.autoScroll){
55925 this.bodyEl.setStyle("overflow", "auto");
55927 this.bodyEl.setStyle("overflow", "hidden");
55929 //if(c.titlebar !== false){
55930 if((!c.titlebar && !c.title) || c.titlebar === false){
55931 this.titleEl.hide();
55933 this.titleEl.show();
55935 this.titleTextEl.innerHTML = c.title;
55939 this.duration = c.duration || .30;
55940 this.slideDuration = c.slideDuration || .45;
55943 this.collapse(true);
55950 * Returns true if this region is currently visible.
55951 * @return {Boolean}
55953 isVisible : function(){
55954 return this.visible;
55958 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55959 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
55961 setCollapsedTitle : function(title){
55962 title = title || " ";
55963 if(this.collapsedTitleTextEl){
55964 this.collapsedTitleTextEl.innerHTML = title;
55968 getBox : function(){
55970 if(!this.collapsed){
55971 b = this.el.getBox(false, true);
55973 b = this.collapsedEl.getBox(false, true);
55978 getMargins : function(){
55979 return this.collapsed ? this.cmargins : this.margins;
55982 highlight : function(){
55983 this.el.addClass("x-layout-panel-dragover");
55986 unhighlight : function(){
55987 this.el.removeClass("x-layout-panel-dragover");
55990 updateBox : function(box){
55992 if(!this.collapsed){
55993 this.el.dom.style.left = box.x + "px";
55994 this.el.dom.style.top = box.y + "px";
55995 this.updateBody(box.width, box.height);
55997 this.collapsedEl.dom.style.left = box.x + "px";
55998 this.collapsedEl.dom.style.top = box.y + "px";
55999 this.collapsedEl.setSize(box.width, box.height);
56002 this.tabs.autoSizeTabs();
56006 updateBody : function(w, h){
56008 this.el.setWidth(w);
56009 w -= this.el.getBorderWidth("rl");
56010 if(this.config.adjustments){
56011 w += this.config.adjustments[0];
56015 this.el.setHeight(h);
56016 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56017 h -= this.el.getBorderWidth("tb");
56018 if(this.config.adjustments){
56019 h += this.config.adjustments[1];
56021 this.bodyEl.setHeight(h);
56023 h = this.tabs.syncHeight(h);
56026 if(this.panelSize){
56027 w = w !== null ? w : this.panelSize.width;
56028 h = h !== null ? h : this.panelSize.height;
56030 if(this.activePanel){
56031 var el = this.activePanel.getEl();
56032 w = w !== null ? w : el.getWidth();
56033 h = h !== null ? h : el.getHeight();
56034 this.panelSize = {width: w, height: h};
56035 this.activePanel.setSize(w, h);
56037 if(Roo.isIE && this.tabs){
56038 this.tabs.el.repaint();
56043 * Returns the container element for this region.
56044 * @return {Roo.Element}
56046 getEl : function(){
56051 * Hides this region.
56054 if(!this.collapsed){
56055 this.el.dom.style.left = "-2000px";
56058 this.collapsedEl.dom.style.left = "-2000px";
56059 this.collapsedEl.hide();
56061 this.visible = false;
56062 this.fireEvent("visibilitychange", this, false);
56066 * Shows this region if it was previously hidden.
56069 if(!this.collapsed){
56072 this.collapsedEl.show();
56074 this.visible = true;
56075 this.fireEvent("visibilitychange", this, true);
56078 closeClicked : function(){
56079 if(this.activePanel){
56080 this.remove(this.activePanel);
56084 collapseClick : function(e){
56086 e.stopPropagation();
56089 e.stopPropagation();
56095 * Collapses this region.
56096 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56098 collapse : function(skipAnim, skipCheck){
56099 if(this.collapsed) {
56103 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56105 this.collapsed = true;
56107 this.split.el.hide();
56109 if(this.config.animate && skipAnim !== true){
56110 this.fireEvent("invalidated", this);
56111 this.animateCollapse();
56113 this.el.setLocation(-20000,-20000);
56115 this.collapsedEl.show();
56116 this.fireEvent("collapsed", this);
56117 this.fireEvent("invalidated", this);
56123 animateCollapse : function(){
56128 * Expands this region if it was previously collapsed.
56129 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56130 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56132 expand : function(e, skipAnim){
56134 e.stopPropagation();
56136 if(!this.collapsed || this.el.hasActiveFx()) {
56140 this.afterSlideIn();
56143 this.collapsed = false;
56144 if(this.config.animate && skipAnim !== true){
56145 this.animateExpand();
56149 this.split.el.show();
56151 this.collapsedEl.setLocation(-2000,-2000);
56152 this.collapsedEl.hide();
56153 this.fireEvent("invalidated", this);
56154 this.fireEvent("expanded", this);
56158 animateExpand : function(){
56162 initTabs : function()
56164 this.bodyEl.setStyle("overflow", "hidden");
56165 var ts = new Roo.TabPanel(
56168 tabPosition: this.bottomTabs ? 'bottom' : 'top',
56169 disableTooltips: this.config.disableTabTips,
56170 toolbar : this.config.toolbar
56173 if(this.config.hideTabs){
56174 ts.stripWrap.setDisplayed(false);
56177 ts.resizeTabs = this.config.resizeTabs === true;
56178 ts.minTabWidth = this.config.minTabWidth || 40;
56179 ts.maxTabWidth = this.config.maxTabWidth || 250;
56180 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56181 ts.monitorResize = false;
56182 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56183 ts.bodyEl.addClass('x-layout-tabs-body');
56184 this.panels.each(this.initPanelAsTab, this);
56187 initPanelAsTab : function(panel){
56188 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56189 this.config.closeOnTab && panel.isClosable());
56190 if(panel.tabTip !== undefined){
56191 ti.setTooltip(panel.tabTip);
56193 ti.on("activate", function(){
56194 this.setActivePanel(panel);
56196 if(this.config.closeOnTab){
56197 ti.on("beforeclose", function(t, e){
56199 this.remove(panel);
56205 updatePanelTitle : function(panel, title){
56206 if(this.activePanel == panel){
56207 this.updateTitle(title);
56210 var ti = this.tabs.getTab(panel.getEl().id);
56212 if(panel.tabTip !== undefined){
56213 ti.setTooltip(panel.tabTip);
56218 updateTitle : function(title){
56219 if(this.titleTextEl && !this.config.title){
56220 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
56224 setActivePanel : function(panel){
56225 panel = this.getPanel(panel);
56226 if(this.activePanel && this.activePanel != panel){
56227 this.activePanel.setActiveState(false);
56229 this.activePanel = panel;
56230 panel.setActiveState(true);
56231 if(this.panelSize){
56232 panel.setSize(this.panelSize.width, this.panelSize.height);
56235 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56237 this.updateTitle(panel.getTitle());
56239 this.fireEvent("invalidated", this);
56241 this.fireEvent("panelactivated", this, panel);
56245 * Shows the specified panel.
56246 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56247 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56249 showPanel : function(panel)
56251 panel = this.getPanel(panel);
56254 var tab = this.tabs.getTab(panel.getEl().id);
56255 if(tab.isHidden()){
56256 this.tabs.unhideTab(tab.id);
56260 this.setActivePanel(panel);
56267 * Get the active panel for this region.
56268 * @return {Roo.ContentPanel} The active panel or null
56270 getActivePanel : function(){
56271 return this.activePanel;
56274 validateVisibility : function(){
56275 if(this.panels.getCount() < 1){
56276 this.updateTitle(" ");
56277 this.closeBtn.hide();
56280 if(!this.isVisible()){
56287 * Adds the passed ContentPanel(s) to this region.
56288 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56289 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56291 add : function(panel){
56292 if(arguments.length > 1){
56293 for(var i = 0, len = arguments.length; i < len; i++) {
56294 this.add(arguments[i]);
56298 if(this.hasPanel(panel)){
56299 this.showPanel(panel);
56302 panel.setRegion(this);
56303 this.panels.add(panel);
56304 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56305 this.bodyEl.dom.appendChild(panel.getEl().dom);
56306 if(panel.background !== true){
56307 this.setActivePanel(panel);
56309 this.fireEvent("paneladded", this, panel);
56315 this.initPanelAsTab(panel);
56317 if(panel.background !== true){
56318 this.tabs.activate(panel.getEl().id);
56320 this.fireEvent("paneladded", this, panel);
56325 * Hides the tab for the specified panel.
56326 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56328 hidePanel : function(panel){
56329 if(this.tabs && (panel = this.getPanel(panel))){
56330 this.tabs.hideTab(panel.getEl().id);
56335 * Unhides the tab for a previously hidden panel.
56336 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56338 unhidePanel : function(panel){
56339 if(this.tabs && (panel = this.getPanel(panel))){
56340 this.tabs.unhideTab(panel.getEl().id);
56344 clearPanels : function(){
56345 while(this.panels.getCount() > 0){
56346 this.remove(this.panels.first());
56351 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56352 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56353 * @param {Boolean} preservePanel Overrides the config preservePanel option
56354 * @return {Roo.ContentPanel} The panel that was removed
56356 remove : function(panel, preservePanel){
56357 panel = this.getPanel(panel);
56362 this.fireEvent("beforeremove", this, panel, e);
56363 if(e.cancel === true){
56366 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56367 var panelId = panel.getId();
56368 this.panels.removeKey(panelId);
56370 document.body.appendChild(panel.getEl().dom);
56373 this.tabs.removeTab(panel.getEl().id);
56374 }else if (!preservePanel){
56375 this.bodyEl.dom.removeChild(panel.getEl().dom);
56377 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56378 var p = this.panels.first();
56379 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56380 tempEl.appendChild(p.getEl().dom);
56381 this.bodyEl.update("");
56382 this.bodyEl.dom.appendChild(p.getEl().dom);
56384 this.updateTitle(p.getTitle());
56386 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56387 this.setActivePanel(p);
56389 panel.setRegion(null);
56390 if(this.activePanel == panel){
56391 this.activePanel = null;
56393 if(this.config.autoDestroy !== false && preservePanel !== true){
56394 try{panel.destroy();}catch(e){}
56396 this.fireEvent("panelremoved", this, panel);
56401 * Returns the TabPanel component used by this region
56402 * @return {Roo.TabPanel}
56404 getTabs : function(){
56408 createTool : function(parentEl, className){
56409 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56410 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
56411 btn.addClassOnOver("x-layout-tools-button-over");
56416 * Ext JS Library 1.1.1
56417 * Copyright(c) 2006-2007, Ext JS, LLC.
56419 * Originally Released Under LGPL - original licence link has changed is not relivant.
56422 * <script type="text/javascript">
56428 * @class Roo.SplitLayoutRegion
56429 * @extends Roo.LayoutRegion
56430 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56432 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56433 this.cursor = cursor;
56434 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56437 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56438 splitTip : "Drag to resize.",
56439 collapsibleSplitTip : "Drag to resize. Double click to hide.",
56440 useSplitTips : false,
56442 applyConfig : function(config){
56443 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56446 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
56447 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
56448 /** The SplitBar for this region
56449 * @type Roo.SplitBar */
56450 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56451 this.split.on("moved", this.onSplitMove, this);
56452 this.split.useShim = config.useShim === true;
56453 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56454 if(this.useSplitTips){
56455 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56457 if(config.collapsible){
56458 this.split.el.on("dblclick", this.collapse, this);
56461 if(typeof config.minSize != "undefined"){
56462 this.split.minSize = config.minSize;
56464 if(typeof config.maxSize != "undefined"){
56465 this.split.maxSize = config.maxSize;
56467 if(config.hideWhenEmpty || config.hidden || config.collapsed){
56468 this.hideSplitter();
56473 getHMaxSize : function(){
56474 var cmax = this.config.maxSize || 10000;
56475 var center = this.mgr.getRegion("center");
56476 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56479 getVMaxSize : function(){
56480 var cmax = this.config.maxSize || 10000;
56481 var center = this.mgr.getRegion("center");
56482 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56485 onSplitMove : function(split, newSize){
56486 this.fireEvent("resized", this, newSize);
56490 * Returns the {@link Roo.SplitBar} for this region.
56491 * @return {Roo.SplitBar}
56493 getSplitBar : function(){
56498 this.hideSplitter();
56499 Roo.SplitLayoutRegion.superclass.hide.call(this);
56502 hideSplitter : function(){
56504 this.split.el.setLocation(-2000,-2000);
56505 this.split.el.hide();
56511 this.split.el.show();
56513 Roo.SplitLayoutRegion.superclass.show.call(this);
56516 beforeSlide: function(){
56517 if(Roo.isGecko){// firefox overflow auto bug workaround
56518 this.bodyEl.clip();
56520 this.tabs.bodyEl.clip();
56522 if(this.activePanel){
56523 this.activePanel.getEl().clip();
56525 if(this.activePanel.beforeSlide){
56526 this.activePanel.beforeSlide();
56532 afterSlide : function(){
56533 if(Roo.isGecko){// firefox overflow auto bug workaround
56534 this.bodyEl.unclip();
56536 this.tabs.bodyEl.unclip();
56538 if(this.activePanel){
56539 this.activePanel.getEl().unclip();
56540 if(this.activePanel.afterSlide){
56541 this.activePanel.afterSlide();
56547 initAutoHide : function(){
56548 if(this.autoHide !== false){
56549 if(!this.autoHideHd){
56550 var st = new Roo.util.DelayedTask(this.slideIn, this);
56551 this.autoHideHd = {
56552 "mouseout": function(e){
56553 if(!e.within(this.el, true)){
56557 "mouseover" : function(e){
56563 this.el.on(this.autoHideHd);
56567 clearAutoHide : function(){
56568 if(this.autoHide !== false){
56569 this.el.un("mouseout", this.autoHideHd.mouseout);
56570 this.el.un("mouseover", this.autoHideHd.mouseover);
56574 clearMonitor : function(){
56575 Roo.get(document).un("click", this.slideInIf, this);
56578 // these names are backwards but not changed for compat
56579 slideOut : function(){
56580 if(this.isSlid || this.el.hasActiveFx()){
56583 this.isSlid = true;
56584 if(this.collapseBtn){
56585 this.collapseBtn.hide();
56587 this.closeBtnState = this.closeBtn.getStyle('display');
56588 this.closeBtn.hide();
56590 this.stickBtn.show();
56593 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56594 this.beforeSlide();
56595 this.el.setStyle("z-index", 10001);
56596 this.el.slideIn(this.getSlideAnchor(), {
56597 callback: function(){
56599 this.initAutoHide();
56600 Roo.get(document).on("click", this.slideInIf, this);
56601 this.fireEvent("slideshow", this);
56608 afterSlideIn : function(){
56609 this.clearAutoHide();
56610 this.isSlid = false;
56611 this.clearMonitor();
56612 this.el.setStyle("z-index", "");
56613 if(this.collapseBtn){
56614 this.collapseBtn.show();
56616 this.closeBtn.setStyle('display', this.closeBtnState);
56618 this.stickBtn.hide();
56620 this.fireEvent("slidehide", this);
56623 slideIn : function(cb){
56624 if(!this.isSlid || this.el.hasActiveFx()){
56628 this.isSlid = false;
56629 this.beforeSlide();
56630 this.el.slideOut(this.getSlideAnchor(), {
56631 callback: function(){
56632 this.el.setLeftTop(-10000, -10000);
56634 this.afterSlideIn();
56642 slideInIf : function(e){
56643 if(!e.within(this.el)){
56648 animateCollapse : function(){
56649 this.beforeSlide();
56650 this.el.setStyle("z-index", 20000);
56651 var anchor = this.getSlideAnchor();
56652 this.el.slideOut(anchor, {
56653 callback : function(){
56654 this.el.setStyle("z-index", "");
56655 this.collapsedEl.slideIn(anchor, {duration:.3});
56657 this.el.setLocation(-10000,-10000);
56659 this.fireEvent("collapsed", this);
56666 animateExpand : function(){
56667 this.beforeSlide();
56668 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56669 this.el.setStyle("z-index", 20000);
56670 this.collapsedEl.hide({
56673 this.el.slideIn(this.getSlideAnchor(), {
56674 callback : function(){
56675 this.el.setStyle("z-index", "");
56678 this.split.el.show();
56680 this.fireEvent("invalidated", this);
56681 this.fireEvent("expanded", this);
56709 getAnchor : function(){
56710 return this.anchors[this.position];
56713 getCollapseAnchor : function(){
56714 return this.canchors[this.position];
56717 getSlideAnchor : function(){
56718 return this.sanchors[this.position];
56721 getAlignAdj : function(){
56722 var cm = this.cmargins;
56723 switch(this.position){
56739 getExpandAdj : function(){
56740 var c = this.collapsedEl, cm = this.cmargins;
56741 switch(this.position){
56743 return [-(cm.right+c.getWidth()+cm.left), 0];
56746 return [cm.right+c.getWidth()+cm.left, 0];
56749 return [0, -(cm.top+cm.bottom+c.getHeight())];
56752 return [0, cm.top+cm.bottom+c.getHeight()];
56758 * Ext JS Library 1.1.1
56759 * Copyright(c) 2006-2007, Ext JS, LLC.
56761 * Originally Released Under LGPL - original licence link has changed is not relivant.
56764 * <script type="text/javascript">
56767 * These classes are private internal classes
56769 Roo.CenterLayoutRegion = function(mgr, config){
56770 Roo.LayoutRegion.call(this, mgr, config, "center");
56771 this.visible = true;
56772 this.minWidth = config.minWidth || 20;
56773 this.minHeight = config.minHeight || 20;
56776 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56778 // center panel can't be hidden
56782 // center panel can't be hidden
56785 getMinWidth: function(){
56786 return this.minWidth;
56789 getMinHeight: function(){
56790 return this.minHeight;
56795 Roo.NorthLayoutRegion = function(mgr, config){
56796 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56798 this.split.placement = Roo.SplitBar.TOP;
56799 this.split.orientation = Roo.SplitBar.VERTICAL;
56800 this.split.el.addClass("x-layout-split-v");
56802 var size = config.initialSize || config.height;
56803 if(typeof size != "undefined"){
56804 this.el.setHeight(size);
56807 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56808 orientation: Roo.SplitBar.VERTICAL,
56809 getBox : function(){
56810 if(this.collapsed){
56811 return this.collapsedEl.getBox();
56813 var box = this.el.getBox();
56815 box.height += this.split.el.getHeight();
56820 updateBox : function(box){
56821 if(this.split && !this.collapsed){
56822 box.height -= this.split.el.getHeight();
56823 this.split.el.setLeft(box.x);
56824 this.split.el.setTop(box.y+box.height);
56825 this.split.el.setWidth(box.width);
56827 if(this.collapsed){
56828 this.updateBody(box.width, null);
56830 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56834 Roo.SouthLayoutRegion = function(mgr, config){
56835 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56837 this.split.placement = Roo.SplitBar.BOTTOM;
56838 this.split.orientation = Roo.SplitBar.VERTICAL;
56839 this.split.el.addClass("x-layout-split-v");
56841 var size = config.initialSize || config.height;
56842 if(typeof size != "undefined"){
56843 this.el.setHeight(size);
56846 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56847 orientation: Roo.SplitBar.VERTICAL,
56848 getBox : function(){
56849 if(this.collapsed){
56850 return this.collapsedEl.getBox();
56852 var box = this.el.getBox();
56854 var sh = this.split.el.getHeight();
56861 updateBox : function(box){
56862 if(this.split && !this.collapsed){
56863 var sh = this.split.el.getHeight();
56866 this.split.el.setLeft(box.x);
56867 this.split.el.setTop(box.y-sh);
56868 this.split.el.setWidth(box.width);
56870 if(this.collapsed){
56871 this.updateBody(box.width, null);
56873 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56877 Roo.EastLayoutRegion = function(mgr, config){
56878 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56880 this.split.placement = Roo.SplitBar.RIGHT;
56881 this.split.orientation = Roo.SplitBar.HORIZONTAL;
56882 this.split.el.addClass("x-layout-split-h");
56884 var size = config.initialSize || config.width;
56885 if(typeof size != "undefined"){
56886 this.el.setWidth(size);
56889 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56890 orientation: Roo.SplitBar.HORIZONTAL,
56891 getBox : function(){
56892 if(this.collapsed){
56893 return this.collapsedEl.getBox();
56895 var box = this.el.getBox();
56897 var sw = this.split.el.getWidth();
56904 updateBox : function(box){
56905 if(this.split && !this.collapsed){
56906 var sw = this.split.el.getWidth();
56908 this.split.el.setLeft(box.x);
56909 this.split.el.setTop(box.y);
56910 this.split.el.setHeight(box.height);
56913 if(this.collapsed){
56914 this.updateBody(null, box.height);
56916 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56920 Roo.WestLayoutRegion = function(mgr, config){
56921 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56923 this.split.placement = Roo.SplitBar.LEFT;
56924 this.split.orientation = Roo.SplitBar.HORIZONTAL;
56925 this.split.el.addClass("x-layout-split-h");
56927 var size = config.initialSize || config.width;
56928 if(typeof size != "undefined"){
56929 this.el.setWidth(size);
56932 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56933 orientation: Roo.SplitBar.HORIZONTAL,
56934 getBox : function(){
56935 if(this.collapsed){
56936 return this.collapsedEl.getBox();
56938 var box = this.el.getBox();
56940 box.width += this.split.el.getWidth();
56945 updateBox : function(box){
56946 if(this.split && !this.collapsed){
56947 var sw = this.split.el.getWidth();
56949 this.split.el.setLeft(box.x+box.width);
56950 this.split.el.setTop(box.y);
56951 this.split.el.setHeight(box.height);
56953 if(this.collapsed){
56954 this.updateBody(null, box.height);
56956 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56961 * Ext JS Library 1.1.1
56962 * Copyright(c) 2006-2007, Ext JS, LLC.
56964 * Originally Released Under LGPL - original licence link has changed is not relivant.
56967 * <script type="text/javascript">
56972 * Private internal class for reading and applying state
56974 Roo.LayoutStateManager = function(layout){
56975 // default empty state
56984 Roo.LayoutStateManager.prototype = {
56985 init : function(layout, provider){
56986 this.provider = provider;
56987 var state = provider.get(layout.id+"-layout-state");
56989 var wasUpdating = layout.isUpdating();
56991 layout.beginUpdate();
56993 for(var key in state){
56994 if(typeof state[key] != "function"){
56995 var rstate = state[key];
56996 var r = layout.getRegion(key);
56999 r.resizeTo(rstate.size);
57001 if(rstate.collapsed == true){
57004 r.expand(null, true);
57010 layout.endUpdate();
57012 this.state = state;
57014 this.layout = layout;
57015 layout.on("regionresized", this.onRegionResized, this);
57016 layout.on("regioncollapsed", this.onRegionCollapsed, this);
57017 layout.on("regionexpanded", this.onRegionExpanded, this);
57020 storeState : function(){
57021 this.provider.set(this.layout.id+"-layout-state", this.state);
57024 onRegionResized : function(region, newSize){
57025 this.state[region.getPosition()].size = newSize;
57029 onRegionCollapsed : function(region){
57030 this.state[region.getPosition()].collapsed = true;
57034 onRegionExpanded : function(region){
57035 this.state[region.getPosition()].collapsed = false;
57040 * Ext JS Library 1.1.1
57041 * Copyright(c) 2006-2007, Ext JS, LLC.
57043 * Originally Released Under LGPL - original licence link has changed is not relivant.
57046 * <script type="text/javascript">
57049 * @class Roo.ContentPanel
57050 * @extends Roo.util.Observable
57051 * @children Roo.form.Form Roo.JsonView Roo.View
57052 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57053 * A basic ContentPanel element.
57054 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
57055 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
57056 * @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
57057 * @cfg {Boolean} closable True if the panel can be closed/removed
57058 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
57059 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57060 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
57061 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
57062 * @cfg {String} title The title for this panel
57063 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57064 * @cfg {String} url Calls {@link #setUrl} with this value
57065 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57066 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
57067 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
57068 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
57069 * @cfg {String} style Extra style to add to the content panel
57070 * @cfg {Roo.menu.Menu} menu popup menu
57073 * Create a new ContentPanel.
57074 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57075 * @param {String/Object} config A string to set only the title or a config object
57076 * @param {String} content (optional) Set the HTML content for this panel
57077 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57079 Roo.ContentPanel = function(el, config, content){
57083 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57087 if (config && config.parentLayout) {
57088 el = config.parentLayout.el.createChild();
57091 if(el.autoCreate){ // xtype is available if this is called from factory
57095 this.el = Roo.get(el);
57096 if(!this.el && config && config.autoCreate){
57097 if(typeof config.autoCreate == "object"){
57098 if(!config.autoCreate.id){
57099 config.autoCreate.id = config.id||el;
57101 this.el = Roo.DomHelper.append(document.body,
57102 config.autoCreate, true);
57104 this.el = Roo.DomHelper.append(document.body,
57105 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57110 this.closable = false;
57111 this.loaded = false;
57112 this.active = false;
57113 if(typeof config == "string"){
57114 this.title = config;
57116 Roo.apply(this, config);
57119 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57120 this.wrapEl = this.el.wrap();
57121 this.toolbar.container = this.el.insertSibling(false, 'before');
57122 this.toolbar = new Roo.Toolbar(this.toolbar);
57125 // xtype created footer. - not sure if will work as we normally have to render first..
57126 if (this.footer && !this.footer.el && this.footer.xtype) {
57127 if (!this.wrapEl) {
57128 this.wrapEl = this.el.wrap();
57131 this.footer.container = this.wrapEl.createChild();
57133 this.footer = Roo.factory(this.footer, Roo);
57138 this.resizeEl = Roo.get(this.resizeEl, true);
57140 this.resizeEl = this.el;
57142 // handle view.xtype
57150 * Fires when this panel is activated.
57151 * @param {Roo.ContentPanel} this
57155 * @event deactivate
57156 * Fires when this panel is activated.
57157 * @param {Roo.ContentPanel} this
57159 "deactivate" : true,
57163 * Fires when this panel is resized if fitToFrame is true.
57164 * @param {Roo.ContentPanel} this
57165 * @param {Number} width The width after any component adjustments
57166 * @param {Number} height The height after any component adjustments
57172 * Fires when this tab is created
57173 * @param {Roo.ContentPanel} this
57183 if(this.autoScroll){
57184 this.resizeEl.setStyle("overflow", "auto");
57186 // fix randome scrolling
57187 this.el.on('scroll', function() {
57188 Roo.log('fix random scolling');
57189 this.scrollTo('top',0);
57192 content = content || this.content;
57194 this.setContent(content);
57196 if(config && config.url){
57197 this.setUrl(this.url, this.params, this.loadOnce);
57202 Roo.ContentPanel.superclass.constructor.call(this);
57204 if (this.view && typeof(this.view.xtype) != 'undefined') {
57205 this.view.el = this.el.appendChild(document.createElement("div"));
57206 this.view = Roo.factory(this.view);
57207 this.view.render && this.view.render(false, '');
57211 this.fireEvent('render', this);
57214 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57216 setRegion : function(region){
57217 this.region = region;
57219 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57221 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57226 * Returns the toolbar for this Panel if one was configured.
57227 * @return {Roo.Toolbar}
57229 getToolbar : function(){
57230 return this.toolbar;
57233 setActiveState : function(active){
57234 this.active = active;
57236 this.fireEvent("deactivate", this);
57238 this.fireEvent("activate", this);
57242 * Updates this panel's element
57243 * @param {String} content The new content
57244 * @param {Boolean} loadScripts (optional) true to look for and process scripts
57246 setContent : function(content, loadScripts){
57247 this.el.update(content, loadScripts);
57250 ignoreResize : function(w, h){
57251 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57254 this.lastSize = {width: w, height: h};
57259 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57260 * @return {Roo.UpdateManager} The UpdateManager
57262 getUpdateManager : function(){
57263 return this.el.getUpdateManager();
57266 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57267 * @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:
57270 url: "your-url.php",
57271 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57272 callback: yourFunction,
57273 scope: yourObject, //(optional scope)
57276 text: "Loading...",
57281 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57282 * 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.
57283 * @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}
57284 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57285 * @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.
57286 * @return {Roo.ContentPanel} this
57289 var um = this.el.getUpdateManager();
57290 um.update.apply(um, arguments);
57296 * 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.
57297 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57298 * @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)
57299 * @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)
57300 * @return {Roo.UpdateManager} The UpdateManager
57302 setUrl : function(url, params, loadOnce){
57303 if(this.refreshDelegate){
57304 this.removeListener("activate", this.refreshDelegate);
57306 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57307 this.on("activate", this.refreshDelegate);
57308 return this.el.getUpdateManager();
57311 _handleRefresh : function(url, params, loadOnce){
57312 if(!loadOnce || !this.loaded){
57313 var updater = this.el.getUpdateManager();
57314 updater.update(url, params, this._setLoaded.createDelegate(this));
57318 _setLoaded : function(){
57319 this.loaded = true;
57323 * Returns this panel's id
57326 getId : function(){
57331 * Returns this panel's element - used by regiosn to add.
57332 * @return {Roo.Element}
57334 getEl : function(){
57335 return this.wrapEl || this.el;
57338 adjustForComponents : function(width, height)
57340 //Roo.log('adjustForComponents ');
57341 if(this.resizeEl != this.el){
57342 width -= this.el.getFrameWidth('lr');
57343 height -= this.el.getFrameWidth('tb');
57346 var te = this.toolbar.getEl();
57347 height -= te.getHeight();
57348 te.setWidth(width);
57351 var te = this.footer.getEl();
57352 //Roo.log("footer:" + te.getHeight());
57354 height -= te.getHeight();
57355 te.setWidth(width);
57359 if(this.adjustments){
57360 width += this.adjustments[0];
57361 height += this.adjustments[1];
57363 return {"width": width, "height": height};
57366 setSize : function(width, height){
57367 if(this.fitToFrame && !this.ignoreResize(width, height)){
57368 if(this.fitContainer && this.resizeEl != this.el){
57369 this.el.setSize(width, height);
57371 var size = this.adjustForComponents(width, height);
57372 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57373 this.fireEvent('resize', this, size.width, size.height);
57378 * Returns this panel's title
57381 getTitle : function(){
57386 * Set this panel's title
57387 * @param {String} title
57389 setTitle : function(title){
57390 this.title = title;
57392 this.region.updatePanelTitle(this, title);
57397 * Returns true is this panel was configured to be closable
57398 * @return {Boolean}
57400 isClosable : function(){
57401 return this.closable;
57404 beforeSlide : function(){
57406 this.resizeEl.clip();
57409 afterSlide : function(){
57411 this.resizeEl.unclip();
57415 * Force a content refresh from the URL specified in the {@link #setUrl} method.
57416 * Will fail silently if the {@link #setUrl} method has not been called.
57417 * This does not activate the panel, just updates its content.
57419 refresh : function(){
57420 if(this.refreshDelegate){
57421 this.loaded = false;
57422 this.refreshDelegate();
57427 * Destroys this panel
57429 destroy : function(){
57430 this.el.removeAllListeners();
57431 var tempEl = document.createElement("span");
57432 tempEl.appendChild(this.el.dom);
57433 tempEl.innerHTML = "";
57439 * form - if the content panel contains a form - this is a reference to it.
57440 * @type {Roo.form.Form}
57444 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57445 * This contains a reference to it.
57451 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57461 * @param {Object} cfg Xtype definition of item to add.
57464 addxtype : function(cfg) {
57466 if (cfg.xtype.match(/^Form$/)) {
57469 //if (this.footer) {
57470 // el = this.footer.container.insertSibling(false, 'before');
57472 el = this.el.createChild();
57475 this.form = new Roo.form.Form(cfg);
57478 if ( this.form.allItems.length) {
57479 this.form.render(el.dom);
57483 // should only have one of theses..
57484 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57485 // views.. should not be just added - used named prop 'view''
57487 cfg.el = this.el.appendChild(document.createElement("div"));
57490 var ret = new Roo.factory(cfg);
57492 ret.render && ret.render(false, ''); // render blank..
57501 * @class Roo.GridPanel
57502 * @extends Roo.ContentPanel
57503 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57505 * Create a new GridPanel.
57506 * @cfg {Roo.grid.Grid} grid The grid for this panel
57508 Roo.GridPanel = function(grid, config){
57510 // universal ctor...
57511 if (typeof(grid.grid) != 'undefined') {
57513 grid = config.grid;
57515 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57516 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57518 this.wrapper.dom.appendChild(grid.getGridEl().dom);
57520 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57523 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57525 // xtype created footer. - not sure if will work as we normally have to render first..
57526 if (this.footer && !this.footer.el && this.footer.xtype) {
57528 this.footer.container = this.grid.getView().getFooterPanel(true);
57529 this.footer.dataSource = this.grid.dataSource;
57530 this.footer = Roo.factory(this.footer, Roo);
57534 grid.monitorWindowResize = false; // turn off autosizing
57535 grid.autoHeight = false;
57536 grid.autoWidth = false;
57538 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57541 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57542 getId : function(){
57543 return this.grid.id;
57547 * Returns the grid for this panel
57548 * @return {Roo.grid.Grid}
57550 getGrid : function(){
57554 setSize : function(width, height){
57555 if(!this.ignoreResize(width, height)){
57556 var grid = this.grid;
57557 var size = this.adjustForComponents(width, height);
57558 grid.getGridEl().setSize(size.width, size.height);
57563 beforeSlide : function(){
57564 this.grid.getView().scroller.clip();
57567 afterSlide : function(){
57568 this.grid.getView().scroller.unclip();
57571 destroy : function(){
57572 this.grid.destroy();
57574 Roo.GridPanel.superclass.destroy.call(this);
57580 * @class Roo.NestedLayoutPanel
57581 * @extends Roo.ContentPanel
57582 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57583 * @cfg Roo.BorderLayout} layout [required] The layout for this panel
57587 * Create a new NestedLayoutPanel.
57590 * @param {Roo.BorderLayout} layout [required] The layout for this panel
57591 * @param {String/Object} config A string to set only the title or a config object
57593 Roo.NestedLayoutPanel = function(layout, config)
57595 // construct with only one argument..
57596 /* FIXME - implement nicer consturctors
57597 if (layout.layout) {
57599 layout = config.layout;
57600 delete config.layout;
57602 if (layout.xtype && !layout.getEl) {
57603 // then layout needs constructing..
57604 layout = Roo.factory(layout, Roo);
57609 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57611 layout.monitorWindowResize = false; // turn off autosizing
57612 this.layout = layout;
57613 this.layout.getEl().addClass("x-layout-nested-layout");
57620 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57622 setSize : function(width, height){
57623 if(!this.ignoreResize(width, height)){
57624 var size = this.adjustForComponents(width, height);
57625 var el = this.layout.getEl();
57626 el.setSize(size.width, size.height);
57627 var touch = el.dom.offsetWidth;
57628 this.layout.layout();
57629 // ie requires a double layout on the first pass
57630 if(Roo.isIE && !this.initialized){
57631 this.initialized = true;
57632 this.layout.layout();
57637 // activate all subpanels if not currently active..
57639 setActiveState : function(active){
57640 this.active = active;
57642 this.fireEvent("deactivate", this);
57646 this.fireEvent("activate", this);
57647 // not sure if this should happen before or after..
57648 if (!this.layout) {
57649 return; // should not happen..
57652 for (var r in this.layout.regions) {
57653 reg = this.layout.getRegion(r);
57654 if (reg.getActivePanel()) {
57655 //reg.showPanel(reg.getActivePanel()); // force it to activate..
57656 reg.setActivePanel(reg.getActivePanel());
57659 if (!reg.panels.length) {
57662 reg.showPanel(reg.getPanel(0));
57671 * Returns the nested BorderLayout for this panel
57672 * @return {Roo.BorderLayout}
57674 getLayout : function(){
57675 return this.layout;
57679 * Adds a xtype elements to the layout of the nested panel
57683 xtype : 'ContentPanel',
57690 xtype : 'NestedLayoutPanel',
57696 items : [ ... list of content panels or nested layout panels.. ]
57700 * @param {Object} cfg Xtype definition of item to add.
57702 addxtype : function(cfg) {
57703 return this.layout.addxtype(cfg);
57708 Roo.ScrollPanel = function(el, config, content){
57709 config = config || {};
57710 config.fitToFrame = true;
57711 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57713 this.el.dom.style.overflow = "hidden";
57714 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57715 this.el.removeClass("x-layout-inactive-content");
57716 this.el.on("mousewheel", this.onWheel, this);
57718 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
57719 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
57720 up.unselectable(); down.unselectable();
57721 up.on("click", this.scrollUp, this);
57722 down.on("click", this.scrollDown, this);
57723 up.addClassOnOver("x-scroller-btn-over");
57724 down.addClassOnOver("x-scroller-btn-over");
57725 up.addClassOnClick("x-scroller-btn-click");
57726 down.addClassOnClick("x-scroller-btn-click");
57727 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57729 this.resizeEl = this.el;
57730 this.el = wrap; this.up = up; this.down = down;
57733 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57735 wheelIncrement : 5,
57736 scrollUp : function(){
57737 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57740 scrollDown : function(){
57741 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57744 afterScroll : function(){
57745 var el = this.resizeEl;
57746 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57747 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57748 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57751 setSize : function(){
57752 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57753 this.afterScroll();
57756 onWheel : function(e){
57757 var d = e.getWheelDelta();
57758 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57759 this.afterScroll();
57763 setContent : function(content, loadScripts){
57764 this.resizeEl.update(content, loadScripts);
57772 * @class Roo.TreePanel
57773 * @extends Roo.ContentPanel
57774 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57775 * Treepanel component
57778 * Create a new TreePanel. - defaults to fit/scoll contents.
57779 * @param {String/Object} config A string to set only the panel's title, or a config object
57781 Roo.TreePanel = function(config){
57782 var el = config.el;
57783 var tree = config.tree;
57784 delete config.tree;
57785 delete config.el; // hopefull!
57787 // wrapper for IE7 strict & safari scroll issue
57789 var treeEl = el.createChild();
57790 config.resizeEl = treeEl;
57794 Roo.TreePanel.superclass.constructor.call(this, el, config);
57797 this.tree = new Roo.tree.TreePanel(treeEl , tree);
57798 //console.log(tree);
57799 this.on('activate', function()
57801 if (this.tree.rendered) {
57804 //console.log('render tree');
57805 this.tree.render();
57807 // this should not be needed.. - it's actually the 'el' that resizes?
57808 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57810 //this.on('resize', function (cp, w, h) {
57811 // this.tree.innerCt.setWidth(w);
57812 // this.tree.innerCt.setHeight(h);
57813 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
57820 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
57824 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57842 * Ext JS Library 1.1.1
57843 * Copyright(c) 2006-2007, Ext JS, LLC.
57845 * Originally Released Under LGPL - original licence link has changed is not relivant.
57848 * <script type="text/javascript">
57853 * @class Roo.ReaderLayout
57854 * @extends Roo.BorderLayout
57855 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
57856 * center region containing two nested regions (a top one for a list view and one for item preview below),
57857 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57858 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57859 * expedites the setup of the overall layout and regions for this common application style.
57862 var reader = new Roo.ReaderLayout();
57863 var CP = Roo.ContentPanel; // shortcut for adding
57865 reader.beginUpdate();
57866 reader.add("north", new CP("north", "North"));
57867 reader.add("west", new CP("west", {title: "West"}));
57868 reader.add("east", new CP("east", {title: "East"}));
57870 reader.regions.listView.add(new CP("listView", "List"));
57871 reader.regions.preview.add(new CP("preview", "Preview"));
57872 reader.endUpdate();
57875 * Create a new ReaderLayout
57876 * @param {Object} config Configuration options
57877 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57878 * document.body if omitted)
57880 Roo.ReaderLayout = function(config, renderTo){
57881 var c = config || {size:{}};
57882 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57883 north: c.north !== false ? Roo.apply({
57887 }, c.north) : false,
57888 west: c.west !== false ? Roo.apply({
57896 margins:{left:5,right:0,bottom:5,top:5},
57897 cmargins:{left:5,right:5,bottom:5,top:5}
57898 }, c.west) : false,
57899 east: c.east !== false ? Roo.apply({
57907 margins:{left:0,right:5,bottom:5,top:5},
57908 cmargins:{left:5,right:5,bottom:5,top:5}
57909 }, c.east) : false,
57910 center: Roo.apply({
57911 tabPosition: 'top',
57915 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57919 this.el.addClass('x-reader');
57921 this.beginUpdate();
57923 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57924 south: c.preview !== false ? Roo.apply({
57931 cmargins:{top:5,left:0, right:0, bottom:0}
57932 }, c.preview) : false,
57933 center: Roo.apply({
57939 this.add('center', new Roo.NestedLayoutPanel(inner,
57940 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57944 this.regions.preview = inner.getRegion('south');
57945 this.regions.listView = inner.getRegion('center');
57948 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57950 * Ext JS Library 1.1.1
57951 * Copyright(c) 2006-2007, Ext JS, LLC.
57953 * Originally Released Under LGPL - original licence link has changed is not relivant.
57956 * <script type="text/javascript">
57960 * @class Roo.grid.Grid
57961 * @extends Roo.util.Observable
57962 * This class represents the primary interface of a component based grid control.
57963 * <br><br>Usage:<pre><code>
57964 var grid = new Roo.grid.Grid("my-container-id", {
57967 selModel: mySelectionModel,
57968 autoSizeColumns: true,
57969 monitorWindowResize: false,
57970 trackMouseOver: true
57975 * <b>Common Problems:</b><br/>
57976 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57977 * element will correct this<br/>
57978 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57979 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57980 * are unpredictable.<br/>
57981 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57982 * grid to calculate dimensions/offsets.<br/>
57984 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57985 * The container MUST have some type of size defined for the grid to fill. The container will be
57986 * automatically set to position relative if it isn't already.
57987 * @param {Object} config A config object that sets properties on this grid.
57989 Roo.grid.Grid = function(container, config){
57990 // initialize the container
57991 this.container = Roo.get(container);
57992 this.container.update("");
57993 this.container.setStyle("overflow", "hidden");
57994 this.container.addClass('x-grid-container');
57996 this.id = this.container.id;
57998 Roo.apply(this, config);
57999 // check and correct shorthanded configs
58001 this.dataSource = this.ds;
58005 this.colModel = this.cm;
58009 this.selModel = this.sm;
58013 if (this.selModel) {
58014 this.selModel = Roo.factory(this.selModel, Roo.grid);
58015 this.sm = this.selModel;
58016 this.sm.xmodule = this.xmodule || false;
58018 if (typeof(this.colModel.config) == 'undefined') {
58019 this.colModel = new Roo.grid.ColumnModel(this.colModel);
58020 this.cm = this.colModel;
58021 this.cm.xmodule = this.xmodule || false;
58023 if (this.dataSource) {
58024 this.dataSource= Roo.factory(this.dataSource, Roo.data);
58025 this.ds = this.dataSource;
58026 this.ds.xmodule = this.xmodule || false;
58033 this.container.setWidth(this.width);
58037 this.container.setHeight(this.height);
58044 * The raw click event for the entire grid.
58045 * @param {Roo.EventObject} e
58050 * The raw dblclick event for the entire grid.
58051 * @param {Roo.EventObject} e
58055 * @event contextmenu
58056 * The raw contextmenu event for the entire grid.
58057 * @param {Roo.EventObject} e
58059 "contextmenu" : true,
58062 * The raw mousedown event for the entire grid.
58063 * @param {Roo.EventObject} e
58065 "mousedown" : true,
58068 * The raw mouseup event for the entire grid.
58069 * @param {Roo.EventObject} e
58074 * The raw mouseover event for the entire grid.
58075 * @param {Roo.EventObject} e
58077 "mouseover" : true,
58080 * The raw mouseout event for the entire grid.
58081 * @param {Roo.EventObject} e
58086 * The raw keypress event for the entire grid.
58087 * @param {Roo.EventObject} e
58092 * The raw keydown event for the entire grid.
58093 * @param {Roo.EventObject} e
58101 * Fires when a cell is clicked
58102 * @param {Grid} this
58103 * @param {Number} rowIndex
58104 * @param {Number} columnIndex
58105 * @param {Roo.EventObject} e
58107 "cellclick" : true,
58109 * @event celldblclick
58110 * Fires when a cell is double clicked
58111 * @param {Grid} this
58112 * @param {Number} rowIndex
58113 * @param {Number} columnIndex
58114 * @param {Roo.EventObject} e
58116 "celldblclick" : true,
58119 * Fires when a row is clicked
58120 * @param {Grid} this
58121 * @param {Number} rowIndex
58122 * @param {Roo.EventObject} e
58126 * @event rowdblclick
58127 * Fires when a row is double clicked
58128 * @param {Grid} this
58129 * @param {Number} rowIndex
58130 * @param {Roo.EventObject} e
58132 "rowdblclick" : true,
58134 * @event headerclick
58135 * Fires when a header is clicked
58136 * @param {Grid} this
58137 * @param {Number} columnIndex
58138 * @param {Roo.EventObject} e
58140 "headerclick" : true,
58142 * @event headerdblclick
58143 * Fires when a header cell is double clicked
58144 * @param {Grid} this
58145 * @param {Number} columnIndex
58146 * @param {Roo.EventObject} e
58148 "headerdblclick" : true,
58150 * @event rowcontextmenu
58151 * Fires when a row is right clicked
58152 * @param {Grid} this
58153 * @param {Number} rowIndex
58154 * @param {Roo.EventObject} e
58156 "rowcontextmenu" : true,
58158 * @event cellcontextmenu
58159 * Fires when a cell is right clicked
58160 * @param {Grid} this
58161 * @param {Number} rowIndex
58162 * @param {Number} cellIndex
58163 * @param {Roo.EventObject} e
58165 "cellcontextmenu" : true,
58167 * @event headercontextmenu
58168 * Fires when a header is right clicked
58169 * @param {Grid} this
58170 * @param {Number} columnIndex
58171 * @param {Roo.EventObject} e
58173 "headercontextmenu" : true,
58175 * @event bodyscroll
58176 * Fires when the body element is scrolled
58177 * @param {Number} scrollLeft
58178 * @param {Number} scrollTop
58180 "bodyscroll" : true,
58182 * @event columnresize
58183 * Fires when the user resizes a column
58184 * @param {Number} columnIndex
58185 * @param {Number} newSize
58187 "columnresize" : true,
58189 * @event columnmove
58190 * Fires when the user moves a column
58191 * @param {Number} oldIndex
58192 * @param {Number} newIndex
58194 "columnmove" : true,
58197 * Fires when row(s) start being dragged
58198 * @param {Grid} this
58199 * @param {Roo.GridDD} dd The drag drop object
58200 * @param {event} e The raw browser event
58202 "startdrag" : true,
58205 * Fires when a drag operation is complete
58206 * @param {Grid} this
58207 * @param {Roo.GridDD} dd The drag drop object
58208 * @param {event} e The raw browser event
58213 * Fires when dragged row(s) are dropped on a valid DD target
58214 * @param {Grid} this
58215 * @param {Roo.GridDD} dd The drag drop object
58216 * @param {String} targetId The target drag drop object
58217 * @param {event} e The raw browser event
58222 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58223 * @param {Grid} this
58224 * @param {Roo.GridDD} dd The drag drop object
58225 * @param {String} targetId The target drag drop object
58226 * @param {event} e The raw browser event
58231 * Fires when the dragged row(s) first cross another DD target while being dragged
58232 * @param {Grid} this
58233 * @param {Roo.GridDD} dd The drag drop object
58234 * @param {String} targetId The target drag drop object
58235 * @param {event} e The raw browser event
58237 "dragenter" : true,
58240 * Fires when the dragged row(s) leave another DD target while being dragged
58241 * @param {Grid} this
58242 * @param {Roo.GridDD} dd The drag drop object
58243 * @param {String} targetId The target drag drop object
58244 * @param {event} e The raw browser event
58249 * Fires when a row is rendered, so you can change add a style to it.
58250 * @param {GridView} gridview The grid view
58251 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
58257 * Fires when the grid is rendered
58258 * @param {Grid} grid
58263 Roo.grid.Grid.superclass.constructor.call(this);
58265 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58268 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58271 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
58274 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58277 * @cfg {Roo.grid.Store} ds The data store for the grid
58280 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58283 * @cfg {String} ddGroup - drag drop group.
58286 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58290 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58292 minColumnWidth : 25,
58295 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58296 * <b>on initial render.</b> It is more efficient to explicitly size the columns
58297 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
58299 autoSizeColumns : false,
58302 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58304 autoSizeHeaders : true,
58307 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58309 monitorWindowResize : true,
58312 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58313 * rows measured to get a columns size. Default is 0 (all rows).
58315 maxRowsToMeasure : 0,
58318 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58320 trackMouseOver : true,
58323 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
58326 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
58330 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58332 enableDragDrop : false,
58335 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58337 enableColumnMove : true,
58340 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58342 enableColumnHide : true,
58345 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58347 enableRowHeightSync : false,
58350 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
58355 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58357 autoHeight : false,
58360 * @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.
58362 autoExpandColumn : false,
58365 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58368 autoExpandMin : 50,
58371 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58373 autoExpandMax : 1000,
58376 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58381 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58385 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58389 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58391 sortColMenu : false,
58397 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58398 * of a fixed width. Default is false.
58401 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58406 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58407 * %0 is replaced with the number of selected rows.
58409 ddText : "{0} selected row{1}",
58413 * Called once after all setup has been completed and the grid is ready to be rendered.
58414 * @return {Roo.grid.Grid} this
58416 render : function()
58418 var c = this.container;
58419 // try to detect autoHeight/width mode
58420 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58421 this.autoHeight = true;
58423 var view = this.getView();
58426 c.on("click", this.onClick, this);
58427 c.on("dblclick", this.onDblClick, this);
58428 c.on("contextmenu", this.onContextMenu, this);
58429 c.on("keydown", this.onKeyDown, this);
58431 c.on("touchstart", this.onTouchStart, this);
58434 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58436 this.getSelectionModel().init(this);
58441 this.loadMask = new Roo.LoadMask(this.container,
58442 Roo.apply({store:this.dataSource}, this.loadMask));
58446 if (this.toolbar && this.toolbar.xtype) {
58447 this.toolbar.container = this.getView().getHeaderPanel(true);
58448 this.toolbar = new Roo.Toolbar(this.toolbar);
58450 if (this.footer && this.footer.xtype) {
58451 this.footer.dataSource = this.getDataSource();
58452 this.footer.container = this.getView().getFooterPanel(true);
58453 this.footer = Roo.factory(this.footer, Roo);
58455 if (this.dropTarget && this.dropTarget.xtype) {
58456 delete this.dropTarget.xtype;
58457 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58461 this.rendered = true;
58462 this.fireEvent('render', this);
58467 * Reconfigures the grid to use a different Store and Column Model.
58468 * The View will be bound to the new objects and refreshed.
58469 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58470 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58472 reconfigure : function(dataSource, colModel){
58474 this.loadMask.destroy();
58475 this.loadMask = new Roo.LoadMask(this.container,
58476 Roo.apply({store:dataSource}, this.loadMask));
58478 this.view.bind(dataSource, colModel);
58479 this.dataSource = dataSource;
58480 this.colModel = colModel;
58481 this.view.refresh(true);
58485 * Add's a column, default at the end..
58487 * @param {int} position to add (default end)
58488 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
58490 addColumns : function(pos, ar)
58493 for (var i =0;i< ar.length;i++) {
58495 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58496 this.cm.lookup[cfg.id] = cfg;
58500 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58501 pos = this.cm.config.length; //this.cm.config.push(cfg);
58503 pos = Math.max(0,pos);
58506 this.cm.config.splice.apply(this.cm.config, ar);
58510 this.view.generateRules(this.cm);
58511 this.view.refresh(true);
58519 onKeyDown : function(e){
58520 this.fireEvent("keydown", e);
58524 * Destroy this grid.
58525 * @param {Boolean} removeEl True to remove the element
58527 destroy : function(removeEl, keepListeners){
58529 this.loadMask.destroy();
58531 var c = this.container;
58532 c.removeAllListeners();
58533 this.view.destroy();
58534 this.colModel.purgeListeners();
58535 if(!keepListeners){
58536 this.purgeListeners();
58539 if(removeEl === true){
58545 processEvent : function(name, e){
58546 // does this fire select???
58547 //Roo.log('grid:processEvent ' + name);
58549 if (name != 'touchstart' ) {
58550 this.fireEvent(name, e);
58553 var t = e.getTarget();
58555 var header = v.findHeaderIndex(t);
58556 if(header !== false){
58557 var ename = name == 'touchstart' ? 'click' : name;
58559 this.fireEvent("header" + ename, this, header, e);
58561 var row = v.findRowIndex(t);
58562 var cell = v.findCellIndex(t);
58563 if (name == 'touchstart') {
58564 // first touch is always a click.
58565 // hopefull this happens after selection is updated.?
58568 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58569 var cs = this.selModel.getSelectedCell();
58570 if (row == cs[0] && cell == cs[1]){
58574 if (typeof(this.selModel.getSelections) != 'undefined') {
58575 var cs = this.selModel.getSelections();
58576 var ds = this.dataSource;
58577 if (cs.length == 1 && ds.getAt(row) == cs[0]){
58588 this.fireEvent("row" + name, this, row, e);
58589 if(cell !== false){
58590 this.fireEvent("cell" + name, this, row, cell, e);
58597 onClick : function(e){
58598 this.processEvent("click", e);
58601 onTouchStart : function(e){
58602 this.processEvent("touchstart", e);
58606 onContextMenu : function(e, t){
58607 this.processEvent("contextmenu", e);
58611 onDblClick : function(e){
58612 this.processEvent("dblclick", e);
58616 walkCells : function(row, col, step, fn, scope){
58617 var cm = this.colModel, clen = cm.getColumnCount();
58618 var ds = this.dataSource, rlen = ds.getCount(), first = true;
58630 if(fn.call(scope || this, row, col, cm) === true){
58648 if(fn.call(scope || this, row, col, cm) === true){
58660 getSelections : function(){
58661 return this.selModel.getSelections();
58665 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58666 * but if manual update is required this method will initiate it.
58668 autoSize : function(){
58670 this.view.layout();
58671 if(this.view.adjustForScroll){
58672 this.view.adjustForScroll();
58678 * Returns the grid's underlying element.
58679 * @return {Element} The element
58681 getGridEl : function(){
58682 return this.container;
58685 // private for compatibility, overridden by editor grid
58686 stopEditing : function(){},
58689 * Returns the grid's SelectionModel.
58690 * @return {SelectionModel}
58692 getSelectionModel : function(){
58693 if(!this.selModel){
58694 this.selModel = new Roo.grid.RowSelectionModel();
58696 return this.selModel;
58700 * Returns the grid's DataSource.
58701 * @return {DataSource}
58703 getDataSource : function(){
58704 return this.dataSource;
58708 * Returns the grid's ColumnModel.
58709 * @return {ColumnModel}
58711 getColumnModel : function(){
58712 return this.colModel;
58716 * Returns the grid's GridView object.
58717 * @return {GridView}
58719 getView : function(){
58721 this.view = new Roo.grid.GridView(this.viewConfig);
58722 this.relayEvents(this.view, [
58723 "beforerowremoved", "beforerowsinserted",
58724 "beforerefresh", "rowremoved",
58725 "rowsinserted", "rowupdated" ,"refresh"
58731 * Called to get grid's drag proxy text, by default returns this.ddText.
58732 * Override this to put something different in the dragged text.
58735 getDragDropText : function(){
58736 var count = this.selModel.getCount();
58737 return String.format(this.ddText, count, count == 1 ? '' : 's');
58742 * Ext JS Library 1.1.1
58743 * Copyright(c) 2006-2007, Ext JS, LLC.
58745 * Originally Released Under LGPL - original licence link has changed is not relivant.
58748 * <script type="text/javascript">
58751 * @class Roo.grid.AbstractGridView
58752 * @extends Roo.util.Observable
58754 * Abstract base class for grid Views
58757 Roo.grid.AbstractGridView = function(){
58761 "beforerowremoved" : true,
58762 "beforerowsinserted" : true,
58763 "beforerefresh" : true,
58764 "rowremoved" : true,
58765 "rowsinserted" : true,
58766 "rowupdated" : true,
58769 Roo.grid.AbstractGridView.superclass.constructor.call(this);
58772 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58773 rowClass : "x-grid-row",
58774 cellClass : "x-grid-cell",
58775 tdClass : "x-grid-td",
58776 hdClass : "x-grid-hd",
58777 splitClass : "x-grid-hd-split",
58779 init: function(grid){
58781 var cid = this.grid.getGridEl().id;
58782 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58783 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58784 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58785 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58788 getColumnRenderers : function(){
58789 var renderers = [];
58790 var cm = this.grid.colModel;
58791 var colCount = cm.getColumnCount();
58792 for(var i = 0; i < colCount; i++){
58793 renderers[i] = cm.getRenderer(i);
58798 getColumnIds : function(){
58800 var cm = this.grid.colModel;
58801 var colCount = cm.getColumnCount();
58802 for(var i = 0; i < colCount; i++){
58803 ids[i] = cm.getColumnId(i);
58808 getDataIndexes : function(){
58809 if(!this.indexMap){
58810 this.indexMap = this.buildIndexMap();
58812 return this.indexMap.colToData;
58815 getColumnIndexByDataIndex : function(dataIndex){
58816 if(!this.indexMap){
58817 this.indexMap = this.buildIndexMap();
58819 return this.indexMap.dataToCol[dataIndex];
58823 * Set a css style for a column dynamically.
58824 * @param {Number} colIndex The index of the column
58825 * @param {String} name The css property name
58826 * @param {String} value The css value
58828 setCSSStyle : function(colIndex, name, value){
58829 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58830 Roo.util.CSS.updateRule(selector, name, value);
58833 generateRules : function(cm){
58834 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58835 Roo.util.CSS.removeStyleSheet(rulesId);
58836 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58837 var cid = cm.getColumnId(i);
58838 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58839 this.tdSelector, cid, " {\n}\n",
58840 this.hdSelector, cid, " {\n}\n",
58841 this.splitSelector, cid, " {\n}\n");
58843 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58847 * Ext JS Library 1.1.1
58848 * Copyright(c) 2006-2007, Ext JS, LLC.
58850 * Originally Released Under LGPL - original licence link has changed is not relivant.
58853 * <script type="text/javascript">
58857 // This is a support class used internally by the Grid components
58858 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58860 this.view = grid.getView();
58861 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58862 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58864 this.setHandleElId(Roo.id(hd));
58865 this.setOuterHandleElId(Roo.id(hd2));
58867 this.scroll = false;
58869 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58871 getDragData : function(e){
58872 var t = Roo.lib.Event.getTarget(e);
58873 var h = this.view.findHeaderCell(t);
58875 return {ddel: h.firstChild, header:h};
58880 onInitDrag : function(e){
58881 this.view.headersDisabled = true;
58882 var clone = this.dragData.ddel.cloneNode(true);
58883 clone.id = Roo.id();
58884 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58885 this.proxy.update(clone);
58889 afterValidDrop : function(){
58891 setTimeout(function(){
58892 v.headersDisabled = false;
58896 afterInvalidDrop : function(){
58898 setTimeout(function(){
58899 v.headersDisabled = false;
58905 * Ext JS Library 1.1.1
58906 * Copyright(c) 2006-2007, Ext JS, LLC.
58908 * Originally Released Under LGPL - original licence link has changed is not relivant.
58911 * <script type="text/javascript">
58914 // This is a support class used internally by the Grid components
58915 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58917 this.view = grid.getView();
58918 // split the proxies so they don't interfere with mouse events
58919 this.proxyTop = Roo.DomHelper.append(document.body, {
58920 cls:"col-move-top", html:" "
58922 this.proxyBottom = Roo.DomHelper.append(document.body, {
58923 cls:"col-move-bottom", html:" "
58925 this.proxyTop.hide = this.proxyBottom.hide = function(){
58926 this.setLeftTop(-100,-100);
58927 this.setStyle("visibility", "hidden");
58929 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58930 // temporarily disabled
58931 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58932 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58934 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58935 proxyOffsets : [-4, -9],
58936 fly: Roo.Element.fly,
58938 getTargetFromEvent : function(e){
58939 var t = Roo.lib.Event.getTarget(e);
58940 var cindex = this.view.findCellIndex(t);
58941 if(cindex !== false){
58942 return this.view.getHeaderCell(cindex);
58947 nextVisible : function(h){
58948 var v = this.view, cm = this.grid.colModel;
58951 if(!cm.isHidden(v.getCellIndex(h))){
58959 prevVisible : function(h){
58960 var v = this.view, cm = this.grid.colModel;
58963 if(!cm.isHidden(v.getCellIndex(h))){
58971 positionIndicator : function(h, n, e){
58972 var x = Roo.lib.Event.getPageX(e);
58973 var r = Roo.lib.Dom.getRegion(n.firstChild);
58974 var px, pt, py = r.top + this.proxyOffsets[1];
58975 if((r.right - x) <= (r.right-r.left)/2){
58976 px = r.right+this.view.borderWidth;
58982 var oldIndex = this.view.getCellIndex(h);
58983 var newIndex = this.view.getCellIndex(n);
58985 if(this.grid.colModel.isFixed(newIndex)){
58989 var locked = this.grid.colModel.isLocked(newIndex);
58994 if(oldIndex < newIndex){
58997 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59000 px += this.proxyOffsets[0];
59001 this.proxyTop.setLeftTop(px, py);
59002 this.proxyTop.show();
59003 if(!this.bottomOffset){
59004 this.bottomOffset = this.view.mainHd.getHeight();
59006 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59007 this.proxyBottom.show();
59011 onNodeEnter : function(n, dd, e, data){
59012 if(data.header != n){
59013 this.positionIndicator(data.header, n, e);
59017 onNodeOver : function(n, dd, e, data){
59018 var result = false;
59019 if(data.header != n){
59020 result = this.positionIndicator(data.header, n, e);
59023 this.proxyTop.hide();
59024 this.proxyBottom.hide();
59026 return result ? this.dropAllowed : this.dropNotAllowed;
59029 onNodeOut : function(n, dd, e, data){
59030 this.proxyTop.hide();
59031 this.proxyBottom.hide();
59034 onNodeDrop : function(n, dd, e, data){
59035 var h = data.header;
59037 var cm = this.grid.colModel;
59038 var x = Roo.lib.Event.getPageX(e);
59039 var r = Roo.lib.Dom.getRegion(n.firstChild);
59040 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59041 var oldIndex = this.view.getCellIndex(h);
59042 var newIndex = this.view.getCellIndex(n);
59043 var locked = cm.isLocked(newIndex);
59047 if(oldIndex < newIndex){
59050 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59053 cm.setLocked(oldIndex, locked, true);
59054 cm.moveColumn(oldIndex, newIndex);
59055 this.grid.fireEvent("columnmove", oldIndex, newIndex);
59063 * Ext JS Library 1.1.1
59064 * Copyright(c) 2006-2007, Ext JS, LLC.
59066 * Originally Released Under LGPL - original licence link has changed is not relivant.
59069 * <script type="text/javascript">
59073 * @class Roo.grid.GridView
59074 * @extends Roo.util.Observable
59077 * @param {Object} config
59079 Roo.grid.GridView = function(config){
59080 Roo.grid.GridView.superclass.constructor.call(this);
59083 Roo.apply(this, config);
59086 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59088 unselectable : 'unselectable="on"',
59089 unselectableCls : 'x-unselectable',
59092 rowClass : "x-grid-row",
59094 cellClass : "x-grid-col",
59096 tdClass : "x-grid-td",
59098 hdClass : "x-grid-hd",
59100 splitClass : "x-grid-split",
59102 sortClasses : ["sort-asc", "sort-desc"],
59104 enableMoveAnim : false,
59108 dh : Roo.DomHelper,
59110 fly : Roo.Element.fly,
59112 css : Roo.util.CSS,
59118 scrollIncrement : 22,
59120 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59122 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59124 bind : function(ds, cm){
59126 this.ds.un("load", this.onLoad, this);
59127 this.ds.un("datachanged", this.onDataChange, this);
59128 this.ds.un("add", this.onAdd, this);
59129 this.ds.un("remove", this.onRemove, this);
59130 this.ds.un("update", this.onUpdate, this);
59131 this.ds.un("clear", this.onClear, this);
59134 ds.on("load", this.onLoad, this);
59135 ds.on("datachanged", this.onDataChange, this);
59136 ds.on("add", this.onAdd, this);
59137 ds.on("remove", this.onRemove, this);
59138 ds.on("update", this.onUpdate, this);
59139 ds.on("clear", this.onClear, this);
59144 this.cm.un("widthchange", this.onColWidthChange, this);
59145 this.cm.un("headerchange", this.onHeaderChange, this);
59146 this.cm.un("hiddenchange", this.onHiddenChange, this);
59147 this.cm.un("columnmoved", this.onColumnMove, this);
59148 this.cm.un("columnlockchange", this.onColumnLock, this);
59151 this.generateRules(cm);
59152 cm.on("widthchange", this.onColWidthChange, this);
59153 cm.on("headerchange", this.onHeaderChange, this);
59154 cm.on("hiddenchange", this.onHiddenChange, this);
59155 cm.on("columnmoved", this.onColumnMove, this);
59156 cm.on("columnlockchange", this.onColumnLock, this);
59161 init: function(grid){
59162 Roo.grid.GridView.superclass.init.call(this, grid);
59164 this.bind(grid.dataSource, grid.colModel);
59166 grid.on("headerclick", this.handleHeaderClick, this);
59168 if(grid.trackMouseOver){
59169 grid.on("mouseover", this.onRowOver, this);
59170 grid.on("mouseout", this.onRowOut, this);
59172 grid.cancelTextSelection = function(){};
59173 this.gridId = grid.id;
59175 var tpls = this.templates || {};
59178 tpls.master = new Roo.Template(
59179 '<div class="x-grid" hidefocus="true">',
59180 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59181 '<div class="x-grid-topbar"></div>',
59182 '<div class="x-grid-scroller"><div></div></div>',
59183 '<div class="x-grid-locked">',
59184 '<div class="x-grid-header">{lockedHeader}</div>',
59185 '<div class="x-grid-body">{lockedBody}</div>',
59187 '<div class="x-grid-viewport">',
59188 '<div class="x-grid-header">{header}</div>',
59189 '<div class="x-grid-body">{body}</div>',
59191 '<div class="x-grid-bottombar"></div>',
59193 '<div class="x-grid-resize-proxy"> </div>',
59196 tpls.master.disableformats = true;
59200 tpls.header = new Roo.Template(
59201 '<table border="0" cellspacing="0" cellpadding="0">',
59202 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59205 tpls.header.disableformats = true;
59207 tpls.header.compile();
59210 tpls.hcell = new Roo.Template(
59211 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59212 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59215 tpls.hcell.disableFormats = true;
59217 tpls.hcell.compile();
59220 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59221 this.unselectableCls + '" ' + this.unselectable +'> </div>');
59222 tpls.hsplit.disableFormats = true;
59224 tpls.hsplit.compile();
59227 tpls.body = new Roo.Template(
59228 '<table border="0" cellspacing="0" cellpadding="0">',
59229 "<tbody>{rows}</tbody>",
59232 tpls.body.disableFormats = true;
59234 tpls.body.compile();
59237 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59238 tpls.row.disableFormats = true;
59240 tpls.row.compile();
59243 tpls.cell = new Roo.Template(
59244 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59245 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59246 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59249 tpls.cell.disableFormats = true;
59251 tpls.cell.compile();
59253 this.templates = tpls;
59256 // remap these for backwards compat
59257 onColWidthChange : function(){
59258 this.updateColumns.apply(this, arguments);
59260 onHeaderChange : function(){
59261 this.updateHeaders.apply(this, arguments);
59263 onHiddenChange : function(){
59264 this.handleHiddenChange.apply(this, arguments);
59266 onColumnMove : function(){
59267 this.handleColumnMove.apply(this, arguments);
59269 onColumnLock : function(){
59270 this.handleLockChange.apply(this, arguments);
59273 onDataChange : function(){
59275 this.updateHeaderSortState();
59278 onClear : function(){
59282 onUpdate : function(ds, record){
59283 this.refreshRow(record);
59286 refreshRow : function(record){
59287 var ds = this.ds, index;
59288 if(typeof record == 'number'){
59290 record = ds.getAt(index);
59292 index = ds.indexOf(record);
59294 this.insertRows(ds, index, index, true);
59295 this.onRemove(ds, record, index+1, true);
59296 this.syncRowHeights(index, index);
59298 this.fireEvent("rowupdated", this, index, record);
59301 onAdd : function(ds, records, index){
59302 this.insertRows(ds, index, index + (records.length-1));
59305 onRemove : function(ds, record, index, isUpdate){
59306 if(isUpdate !== true){
59307 this.fireEvent("beforerowremoved", this, index, record);
59309 var bt = this.getBodyTable(), lt = this.getLockedTable();
59310 if(bt.rows[index]){
59311 bt.firstChild.removeChild(bt.rows[index]);
59313 if(lt.rows[index]){
59314 lt.firstChild.removeChild(lt.rows[index]);
59316 if(isUpdate !== true){
59317 this.stripeRows(index);
59318 this.syncRowHeights(index, index);
59320 this.fireEvent("rowremoved", this, index, record);
59324 onLoad : function(){
59325 this.scrollToTop();
59329 * Scrolls the grid to the top
59331 scrollToTop : function(){
59333 this.scroller.dom.scrollTop = 0;
59339 * Gets a panel in the header of the grid that can be used for toolbars etc.
59340 * After modifying the contents of this panel a call to grid.autoSize() may be
59341 * required to register any changes in size.
59342 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59343 * @return Roo.Element
59345 getHeaderPanel : function(doShow){
59347 this.headerPanel.show();
59349 return this.headerPanel;
59353 * Gets a panel in the footer of the grid that can be used for toolbars etc.
59354 * After modifying the contents of this panel a call to grid.autoSize() may be
59355 * required to register any changes in size.
59356 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59357 * @return Roo.Element
59359 getFooterPanel : function(doShow){
59361 this.footerPanel.show();
59363 return this.footerPanel;
59366 initElements : function(){
59367 var E = Roo.Element;
59368 var el = this.grid.getGridEl().dom.firstChild;
59369 var cs = el.childNodes;
59371 this.el = new E(el);
59373 this.focusEl = new E(el.firstChild);
59374 this.focusEl.swallowEvent("click", true);
59376 this.headerPanel = new E(cs[1]);
59377 this.headerPanel.enableDisplayMode("block");
59379 this.scroller = new E(cs[2]);
59380 this.scrollSizer = new E(this.scroller.dom.firstChild);
59382 this.lockedWrap = new E(cs[3]);
59383 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59384 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59386 this.mainWrap = new E(cs[4]);
59387 this.mainHd = new E(this.mainWrap.dom.firstChild);
59388 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59390 this.footerPanel = new E(cs[5]);
59391 this.footerPanel.enableDisplayMode("block");
59393 this.resizeProxy = new E(cs[6]);
59395 this.headerSelector = String.format(
59396 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59397 this.lockedHd.id, this.mainHd.id
59400 this.splitterSelector = String.format(
59401 '#{0} div.x-grid-split, #{1} div.x-grid-split',
59402 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59405 idToCssName : function(s)
59407 return s.replace(/[^a-z0-9]+/ig, '-');
59410 getHeaderCell : function(index){
59411 return Roo.DomQuery.select(this.headerSelector)[index];
59414 getHeaderCellMeasure : function(index){
59415 return this.getHeaderCell(index).firstChild;
59418 getHeaderCellText : function(index){
59419 return this.getHeaderCell(index).firstChild.firstChild;
59422 getLockedTable : function(){
59423 return this.lockedBody.dom.firstChild;
59426 getBodyTable : function(){
59427 return this.mainBody.dom.firstChild;
59430 getLockedRow : function(index){
59431 return this.getLockedTable().rows[index];
59434 getRow : function(index){
59435 return this.getBodyTable().rows[index];
59438 getRowComposite : function(index){
59440 this.rowEl = new Roo.CompositeElementLite();
59442 var els = [], lrow, mrow;
59443 if(lrow = this.getLockedRow(index)){
59446 if(mrow = this.getRow(index)){
59449 this.rowEl.elements = els;
59453 * Gets the 'td' of the cell
59455 * @param {Integer} rowIndex row to select
59456 * @param {Integer} colIndex column to select
59460 getCell : function(rowIndex, colIndex){
59461 var locked = this.cm.getLockedCount();
59463 if(colIndex < locked){
59464 source = this.lockedBody.dom.firstChild;
59466 source = this.mainBody.dom.firstChild;
59467 colIndex -= locked;
59469 return source.rows[rowIndex].childNodes[colIndex];
59472 getCellText : function(rowIndex, colIndex){
59473 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59476 getCellBox : function(cell){
59477 var b = this.fly(cell).getBox();
59478 if(Roo.isOpera){ // opera fails to report the Y
59479 b.y = cell.offsetTop + this.mainBody.getY();
59484 getCellIndex : function(cell){
59485 var id = String(cell.className).match(this.cellRE);
59487 return parseInt(id[1], 10);
59492 findHeaderIndex : function(n){
59493 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59494 return r ? this.getCellIndex(r) : false;
59497 findHeaderCell : function(n){
59498 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59499 return r ? r : false;
59502 findRowIndex : function(n){
59506 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59507 return r ? r.rowIndex : false;
59510 findCellIndex : function(node){
59511 var stop = this.el.dom;
59512 while(node && node != stop){
59513 if(this.findRE.test(node.className)){
59514 return this.getCellIndex(node);
59516 node = node.parentNode;
59521 getColumnId : function(index){
59522 return this.cm.getColumnId(index);
59525 getSplitters : function()
59527 if(this.splitterSelector){
59528 return Roo.DomQuery.select(this.splitterSelector);
59534 getSplitter : function(index){
59535 return this.getSplitters()[index];
59538 onRowOver : function(e, t){
59540 if((row = this.findRowIndex(t)) !== false){
59541 this.getRowComposite(row).addClass("x-grid-row-over");
59545 onRowOut : function(e, t){
59547 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59548 this.getRowComposite(row).removeClass("x-grid-row-over");
59552 renderHeaders : function(){
59554 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59555 var cb = [], lb = [], sb = [], lsb = [], p = {};
59556 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59557 p.cellId = "x-grid-hd-0-" + i;
59558 p.splitId = "x-grid-csplit-0-" + i;
59559 p.id = cm.getColumnId(i);
59560 p.value = cm.getColumnHeader(i) || "";
59561 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
59562 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59563 if(!cm.isLocked(i)){
59564 cb[cb.length] = ct.apply(p);
59565 sb[sb.length] = st.apply(p);
59567 lb[lb.length] = ct.apply(p);
59568 lsb[lsb.length] = st.apply(p);
59571 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59572 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59575 updateHeaders : function(){
59576 var html = this.renderHeaders();
59577 this.lockedHd.update(html[0]);
59578 this.mainHd.update(html[1]);
59582 * Focuses the specified row.
59583 * @param {Number} row The row index
59585 focusRow : function(row)
59587 //Roo.log('GridView.focusRow');
59588 var x = this.scroller.dom.scrollLeft;
59589 this.focusCell(row, 0, false);
59590 this.scroller.dom.scrollLeft = x;
59594 * Focuses the specified cell.
59595 * @param {Number} row The row index
59596 * @param {Number} col The column index
59597 * @param {Boolean} hscroll false to disable horizontal scrolling
59599 focusCell : function(row, col, hscroll)
59601 //Roo.log('GridView.focusCell');
59602 var el = this.ensureVisible(row, col, hscroll);
59603 this.focusEl.alignTo(el, "tl-tl");
59605 this.focusEl.focus();
59607 this.focusEl.focus.defer(1, this.focusEl);
59612 * Scrolls the specified cell into view
59613 * @param {Number} row The row index
59614 * @param {Number} col The column index
59615 * @param {Boolean} hscroll false to disable horizontal scrolling
59617 ensureVisible : function(row, col, hscroll)
59619 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59620 //return null; //disable for testing.
59621 if(typeof row != "number"){
59622 row = row.rowIndex;
59624 if(row < 0 && row >= this.ds.getCount()){
59627 col = (col !== undefined ? col : 0);
59628 var cm = this.grid.colModel;
59629 while(cm.isHidden(col)){
59633 var el = this.getCell(row, col);
59637 var c = this.scroller.dom;
59639 var ctop = parseInt(el.offsetTop, 10);
59640 var cleft = parseInt(el.offsetLeft, 10);
59641 var cbot = ctop + el.offsetHeight;
59642 var cright = cleft + el.offsetWidth;
59644 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59645 var stop = parseInt(c.scrollTop, 10);
59646 var sleft = parseInt(c.scrollLeft, 10);
59647 var sbot = stop + ch;
59648 var sright = sleft + c.clientWidth;
59650 Roo.log('GridView.ensureVisible:' +
59652 ' c.clientHeight:' + c.clientHeight +
59653 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59661 c.scrollTop = ctop;
59662 //Roo.log("set scrolltop to ctop DISABLE?");
59663 }else if(cbot > sbot){
59664 //Roo.log("set scrolltop to cbot-ch");
59665 c.scrollTop = cbot-ch;
59668 if(hscroll !== false){
59670 c.scrollLeft = cleft;
59671 }else if(cright > sright){
59672 c.scrollLeft = cright-c.clientWidth;
59679 updateColumns : function(){
59680 this.grid.stopEditing();
59681 var cm = this.grid.colModel, colIds = this.getColumnIds();
59682 //var totalWidth = cm.getTotalWidth();
59684 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59685 //if(cm.isHidden(i)) continue;
59686 var w = cm.getColumnWidth(i);
59687 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59688 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59690 this.updateSplitters();
59693 generateRules : function(cm){
59694 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59695 Roo.util.CSS.removeStyleSheet(rulesId);
59696 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59697 var cid = cm.getColumnId(i);
59699 if(cm.config[i].align){
59700 align = 'text-align:'+cm.config[i].align+';';
59703 if(cm.isHidden(i)){
59704 hidden = 'display:none;';
59706 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59708 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59709 this.hdSelector, cid, " {\n", align, width, "}\n",
59710 this.tdSelector, cid, " {\n",hidden,"\n}\n",
59711 this.splitSelector, cid, " {\n", hidden , "\n}\n");
59713 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59716 updateSplitters : function(){
59717 var cm = this.cm, s = this.getSplitters();
59718 if(s){ // splitters not created yet
59719 var pos = 0, locked = true;
59720 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59721 if(cm.isHidden(i)) {
59724 var w = cm.getColumnWidth(i); // make sure it's a number
59725 if(!cm.isLocked(i) && locked){
59730 s[i].style.left = (pos-this.splitOffset) + "px";
59735 handleHiddenChange : function(colModel, colIndex, hidden){
59737 this.hideColumn(colIndex);
59739 this.unhideColumn(colIndex);
59743 hideColumn : function(colIndex){
59744 var cid = this.getColumnId(colIndex);
59745 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59746 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59748 this.updateHeaders();
59750 this.updateSplitters();
59754 unhideColumn : function(colIndex){
59755 var cid = this.getColumnId(colIndex);
59756 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59757 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59760 this.updateHeaders();
59762 this.updateSplitters();
59766 insertRows : function(dm, firstRow, lastRow, isUpdate){
59767 if(firstRow == 0 && lastRow == dm.getCount()-1){
59771 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59773 var s = this.getScrollState();
59774 var markup = this.renderRows(firstRow, lastRow);
59775 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59776 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59777 this.restoreScroll(s);
59779 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59780 this.syncRowHeights(firstRow, lastRow);
59781 this.stripeRows(firstRow);
59787 bufferRows : function(markup, target, index){
59788 var before = null, trows = target.rows, tbody = target.tBodies[0];
59789 if(index < trows.length){
59790 before = trows[index];
59792 var b = document.createElement("div");
59793 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59794 var rows = b.firstChild.rows;
59795 for(var i = 0, len = rows.length; i < len; i++){
59797 tbody.insertBefore(rows[0], before);
59799 tbody.appendChild(rows[0]);
59806 deleteRows : function(dm, firstRow, lastRow){
59807 if(dm.getRowCount()<1){
59808 this.fireEvent("beforerefresh", this);
59809 this.mainBody.update("");
59810 this.lockedBody.update("");
59811 this.fireEvent("refresh", this);
59813 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59814 var bt = this.getBodyTable();
59815 var tbody = bt.firstChild;
59816 var rows = bt.rows;
59817 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59818 tbody.removeChild(rows[firstRow]);
59820 this.stripeRows(firstRow);
59821 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59825 updateRows : function(dataSource, firstRow, lastRow){
59826 var s = this.getScrollState();
59828 this.restoreScroll(s);
59831 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59835 this.updateHeaderSortState();
59838 getScrollState : function(){
59840 var sb = this.scroller.dom;
59841 return {left: sb.scrollLeft, top: sb.scrollTop};
59844 stripeRows : function(startRow){
59845 if(!this.grid.stripeRows || this.ds.getCount() < 1){
59848 startRow = startRow || 0;
59849 var rows = this.getBodyTable().rows;
59850 var lrows = this.getLockedTable().rows;
59851 var cls = ' x-grid-row-alt ';
59852 for(var i = startRow, len = rows.length; i < len; i++){
59853 var row = rows[i], lrow = lrows[i];
59854 var isAlt = ((i+1) % 2 == 0);
59855 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59856 if(isAlt == hasAlt){
59860 row.className += " x-grid-row-alt";
59862 row.className = row.className.replace("x-grid-row-alt", "");
59865 lrow.className = row.className;
59870 restoreScroll : function(state){
59871 //Roo.log('GridView.restoreScroll');
59872 var sb = this.scroller.dom;
59873 sb.scrollLeft = state.left;
59874 sb.scrollTop = state.top;
59878 syncScroll : function(){
59879 //Roo.log('GridView.syncScroll');
59880 var sb = this.scroller.dom;
59881 var sh = this.mainHd.dom;
59882 var bs = this.mainBody.dom;
59883 var lv = this.lockedBody.dom;
59884 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59885 lv.scrollTop = bs.scrollTop = sb.scrollTop;
59888 handleScroll : function(e){
59890 var sb = this.scroller.dom;
59891 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59895 handleWheel : function(e){
59896 var d = e.getWheelDelta();
59897 this.scroller.dom.scrollTop -= d*22;
59898 // set this here to prevent jumpy scrolling on large tables
59899 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59903 renderRows : function(startRow, endRow){
59904 // pull in all the crap needed to render rows
59905 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59906 var colCount = cm.getColumnCount();
59908 if(ds.getCount() < 1){
59912 // build a map for all the columns
59914 for(var i = 0; i < colCount; i++){
59915 var name = cm.getDataIndex(i);
59917 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59918 renderer : cm.getRenderer(i),
59919 id : cm.getColumnId(i),
59920 locked : cm.isLocked(i),
59921 has_editor : cm.isCellEditable(i)
59925 startRow = startRow || 0;
59926 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59928 // records to render
59929 var rs = ds.getRange(startRow, endRow);
59931 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59934 // As much as I hate to duplicate code, this was branched because FireFox really hates
59935 // [].join("") on strings. The performance difference was substantial enough to
59936 // branch this function
59937 doRender : Roo.isGecko ?
59938 function(cs, rs, ds, startRow, colCount, stripe){
59939 var ts = this.templates, ct = ts.cell, rt = ts.row;
59941 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59943 var hasListener = this.grid.hasListener('rowclass');
59945 for(var j = 0, len = rs.length; j < len; j++){
59946 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59947 for(var i = 0; i < colCount; i++){
59949 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59951 p.css = p.attr = "";
59952 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59953 if(p.value == undefined || p.value === "") {
59954 p.value = " ";
59957 p.css += ' x-grid-editable-cell';
59959 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59960 p.css += ' x-grid-dirty-cell';
59962 var markup = ct.apply(p);
59970 if(stripe && ((rowIndex+1) % 2 == 0)){
59971 alt.push("x-grid-row-alt")
59974 alt.push( " x-grid-dirty-row");
59977 if(this.getRowClass){
59978 alt.push(this.getRowClass(r, rowIndex));
59984 rowIndex : rowIndex,
59987 this.grid.fireEvent('rowclass', this, rowcfg);
59988 alt.push(rowcfg.rowClass);
59990 rp.alt = alt.join(" ");
59991 lbuf+= rt.apply(rp);
59993 buf+= rt.apply(rp);
59995 return [lbuf, buf];
59997 function(cs, rs, ds, startRow, colCount, stripe){
59998 var ts = this.templates, ct = ts.cell, rt = ts.row;
60000 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60001 var hasListener = this.grid.hasListener('rowclass');
60004 for(var j = 0, len = rs.length; j < len; j++){
60005 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60006 for(var i = 0; i < colCount; i++){
60008 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60010 p.css = p.attr = "";
60011 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60012 if(p.value == undefined || p.value === "") {
60013 p.value = " ";
60017 p.css += ' x-grid-editable-cell';
60019 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60020 p.css += ' x-grid-dirty-cell'
60023 var markup = ct.apply(p);
60025 cb[cb.length] = markup;
60027 lcb[lcb.length] = markup;
60031 if(stripe && ((rowIndex+1) % 2 == 0)){
60032 alt.push( "x-grid-row-alt");
60035 alt.push(" x-grid-dirty-row");
60038 if(this.getRowClass){
60039 alt.push( this.getRowClass(r, rowIndex));
60045 rowIndex : rowIndex,
60048 this.grid.fireEvent('rowclass', this, rowcfg);
60049 alt.push(rowcfg.rowClass);
60052 rp.alt = alt.join(" ");
60053 rp.cells = lcb.join("");
60054 lbuf[lbuf.length] = rt.apply(rp);
60055 rp.cells = cb.join("");
60056 buf[buf.length] = rt.apply(rp);
60058 return [lbuf.join(""), buf.join("")];
60061 renderBody : function(){
60062 var markup = this.renderRows();
60063 var bt = this.templates.body;
60064 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60068 * Refreshes the grid
60069 * @param {Boolean} headersToo
60071 refresh : function(headersToo){
60072 this.fireEvent("beforerefresh", this);
60073 this.grid.stopEditing();
60074 var result = this.renderBody();
60075 this.lockedBody.update(result[0]);
60076 this.mainBody.update(result[1]);
60077 if(headersToo === true){
60078 this.updateHeaders();
60079 this.updateColumns();
60080 this.updateSplitters();
60081 this.updateHeaderSortState();
60083 this.syncRowHeights();
60085 this.fireEvent("refresh", this);
60088 handleColumnMove : function(cm, oldIndex, newIndex){
60089 this.indexMap = null;
60090 var s = this.getScrollState();
60091 this.refresh(true);
60092 this.restoreScroll(s);
60093 this.afterMove(newIndex);
60096 afterMove : function(colIndex){
60097 if(this.enableMoveAnim && Roo.enableFx){
60098 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60100 // if multisort - fix sortOrder, and reload..
60101 if (this.grid.dataSource.multiSort) {
60102 // the we can call sort again..
60103 var dm = this.grid.dataSource;
60104 var cm = this.grid.colModel;
60106 for(var i = 0; i < cm.config.length; i++ ) {
60108 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60109 continue; // dont' bother, it's not in sort list or being set.
60112 so.push(cm.config[i].dataIndex);
60115 dm.load(dm.lastOptions);
60122 updateCell : function(dm, rowIndex, dataIndex){
60123 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60124 if(typeof colIndex == "undefined"){ // not present in grid
60127 var cm = this.grid.colModel;
60128 var cell = this.getCell(rowIndex, colIndex);
60129 var cellText = this.getCellText(rowIndex, colIndex);
60132 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60133 id : cm.getColumnId(colIndex),
60134 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60136 var renderer = cm.getRenderer(colIndex);
60137 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60138 if(typeof val == "undefined" || val === "") {
60141 cellText.innerHTML = val;
60142 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60143 this.syncRowHeights(rowIndex, rowIndex);
60146 calcColumnWidth : function(colIndex, maxRowsToMeasure){
60148 if(this.grid.autoSizeHeaders){
60149 var h = this.getHeaderCellMeasure(colIndex);
60150 maxWidth = Math.max(maxWidth, h.scrollWidth);
60153 if(this.cm.isLocked(colIndex)){
60154 tb = this.getLockedTable();
60157 tb = this.getBodyTable();
60158 index = colIndex - this.cm.getLockedCount();
60161 var rows = tb.rows;
60162 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60163 for(var i = 0; i < stopIndex; i++){
60164 var cell = rows[i].childNodes[index].firstChild;
60165 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60168 return maxWidth + /*margin for error in IE*/ 5;
60171 * Autofit a column to its content.
60172 * @param {Number} colIndex
60173 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60175 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60176 if(this.cm.isHidden(colIndex)){
60177 return; // can't calc a hidden column
60180 var cid = this.cm.getColumnId(colIndex);
60181 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60182 if(this.grid.autoSizeHeaders){
60183 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60186 var newWidth = this.calcColumnWidth(colIndex);
60187 this.cm.setColumnWidth(colIndex,
60188 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60189 if(!suppressEvent){
60190 this.grid.fireEvent("columnresize", colIndex, newWidth);
60195 * Autofits all columns to their content and then expands to fit any extra space in the grid
60197 autoSizeColumns : function(){
60198 var cm = this.grid.colModel;
60199 var colCount = cm.getColumnCount();
60200 for(var i = 0; i < colCount; i++){
60201 this.autoSizeColumn(i, true, true);
60203 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60206 this.updateColumns();
60212 * Autofits all columns to the grid's width proportionate with their current size
60213 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60215 fitColumns : function(reserveScrollSpace){
60216 var cm = this.grid.colModel;
60217 var colCount = cm.getColumnCount();
60221 for (i = 0; i < colCount; i++){
60222 if(!cm.isHidden(i) && !cm.isFixed(i)){
60223 w = cm.getColumnWidth(i);
60229 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60230 if(reserveScrollSpace){
60233 var frac = (avail - cm.getTotalWidth())/width;
60234 while (cols.length){
60237 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60239 this.updateColumns();
60243 onRowSelect : function(rowIndex){
60244 var row = this.getRowComposite(rowIndex);
60245 row.addClass("x-grid-row-selected");
60248 onRowDeselect : function(rowIndex){
60249 var row = this.getRowComposite(rowIndex);
60250 row.removeClass("x-grid-row-selected");
60253 onCellSelect : function(row, col){
60254 var cell = this.getCell(row, col);
60256 Roo.fly(cell).addClass("x-grid-cell-selected");
60260 onCellDeselect : function(row, col){
60261 var cell = this.getCell(row, col);
60263 Roo.fly(cell).removeClass("x-grid-cell-selected");
60267 updateHeaderSortState : function(){
60269 // sort state can be single { field: xxx, direction : yyy}
60270 // or { xxx=>ASC , yyy : DESC ..... }
60273 if (!this.ds.multiSort) {
60274 var state = this.ds.getSortState();
60278 mstate[state.field] = state.direction;
60279 // FIXME... - this is not used here.. but might be elsewhere..
60280 this.sortState = state;
60283 mstate = this.ds.sortToggle;
60285 //remove existing sort classes..
60287 var sc = this.sortClasses;
60288 var hds = this.el.select(this.headerSelector).removeClass(sc);
60290 for(var f in mstate) {
60292 var sortColumn = this.cm.findColumnIndex(f);
60294 if(sortColumn != -1){
60295 var sortDir = mstate[f];
60296 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60305 handleHeaderClick : function(g, index,e){
60307 Roo.log("header click");
60310 // touch events on header are handled by context
60311 this.handleHdCtx(g,index,e);
60316 if(this.headersDisabled){
60319 var dm = g.dataSource, cm = g.colModel;
60320 if(!cm.isSortable(index)){
60325 if (dm.multiSort) {
60326 // update the sortOrder
60328 for(var i = 0; i < cm.config.length; i++ ) {
60330 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60331 continue; // dont' bother, it's not in sort list or being set.
60334 so.push(cm.config[i].dataIndex);
60340 dm.sort(cm.getDataIndex(index));
60344 destroy : function(){
60346 this.colMenu.removeAll();
60347 Roo.menu.MenuMgr.unregister(this.colMenu);
60348 this.colMenu.getEl().remove();
60349 delete this.colMenu;
60352 this.hmenu.removeAll();
60353 Roo.menu.MenuMgr.unregister(this.hmenu);
60354 this.hmenu.getEl().remove();
60357 if(this.grid.enableColumnMove){
60358 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60360 for(var dd in dds){
60361 if(!dds[dd].config.isTarget && dds[dd].dragElId){
60362 var elid = dds[dd].dragElId;
60364 Roo.get(elid).remove();
60365 } else if(dds[dd].config.isTarget){
60366 dds[dd].proxyTop.remove();
60367 dds[dd].proxyBottom.remove();
60370 if(Roo.dd.DDM.locationCache[dd]){
60371 delete Roo.dd.DDM.locationCache[dd];
60374 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60377 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60378 this.bind(null, null);
60379 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60382 handleLockChange : function(){
60383 this.refresh(true);
60386 onDenyColumnLock : function(){
60390 onDenyColumnHide : function(){
60394 handleHdMenuClick : function(item){
60395 var index = this.hdCtxIndex;
60396 var cm = this.cm, ds = this.ds;
60399 ds.sort(cm.getDataIndex(index), "ASC");
60402 ds.sort(cm.getDataIndex(index), "DESC");
60405 var lc = cm.getLockedCount();
60406 if(cm.getColumnCount(true) <= lc+1){
60407 this.onDenyColumnLock();
60411 cm.setLocked(index, true, true);
60412 cm.moveColumn(index, lc);
60413 this.grid.fireEvent("columnmove", index, lc);
60415 cm.setLocked(index, true);
60419 var lc = cm.getLockedCount();
60420 if((lc-1) != index){
60421 cm.setLocked(index, false, true);
60422 cm.moveColumn(index, lc-1);
60423 this.grid.fireEvent("columnmove", index, lc-1);
60425 cm.setLocked(index, false);
60428 case 'wider': // used to expand cols on touch..
60430 var cw = cm.getColumnWidth(index);
60431 cw += (item.id == 'wider' ? 1 : -1) * 50;
60432 cw = Math.max(0, cw);
60433 cw = Math.min(cw,4000);
60434 cm.setColumnWidth(index, cw);
60438 index = cm.getIndexById(item.id.substr(4));
60440 if(item.checked && cm.getColumnCount(true) <= 1){
60441 this.onDenyColumnHide();
60444 cm.setHidden(index, item.checked);
60450 beforeColMenuShow : function(){
60451 var cm = this.cm, colCount = cm.getColumnCount();
60452 this.colMenu.removeAll();
60455 for(var i = 0; i < colCount; i++){
60457 id: "col-"+cm.getColumnId(i),
60458 text: cm.getColumnHeader(i),
60459 checked: !cm.isHidden(i),
60464 if (this.grid.sortColMenu) {
60465 items.sort(function(a,b) {
60466 if (a.text == b.text) {
60469 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60473 for(var i = 0; i < colCount; i++){
60474 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60478 handleHdCtx : function(g, index, e){
60480 var hd = this.getHeaderCell(index);
60481 this.hdCtxIndex = index;
60482 var ms = this.hmenu.items, cm = this.cm;
60483 ms.get("asc").setDisabled(!cm.isSortable(index));
60484 ms.get("desc").setDisabled(!cm.isSortable(index));
60485 if(this.grid.enableColLock !== false){
60486 ms.get("lock").setDisabled(cm.isLocked(index));
60487 ms.get("unlock").setDisabled(!cm.isLocked(index));
60489 this.hmenu.show(hd, "tl-bl");
60492 handleHdOver : function(e){
60493 var hd = this.findHeaderCell(e.getTarget());
60494 if(hd && !this.headersDisabled){
60495 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60496 this.fly(hd).addClass("x-grid-hd-over");
60501 handleHdOut : function(e){
60502 var hd = this.findHeaderCell(e.getTarget());
60504 this.fly(hd).removeClass("x-grid-hd-over");
60508 handleSplitDblClick : function(e, t){
60509 var i = this.getCellIndex(t);
60510 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60511 this.autoSizeColumn(i, true);
60516 render : function(){
60519 var colCount = cm.getColumnCount();
60521 if(this.grid.monitorWindowResize === true){
60522 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60524 var header = this.renderHeaders();
60525 var body = this.templates.body.apply({rows:""});
60526 var html = this.templates.master.apply({
60529 lockedHeader: header[0],
60533 //this.updateColumns();
60535 this.grid.getGridEl().dom.innerHTML = html;
60537 this.initElements();
60539 // a kludge to fix the random scolling effect in webkit
60540 this.el.on("scroll", function() {
60541 this.el.dom.scrollTop=0; // hopefully not recursive..
60544 this.scroller.on("scroll", this.handleScroll, this);
60545 this.lockedBody.on("mousewheel", this.handleWheel, this);
60546 this.mainBody.on("mousewheel", this.handleWheel, this);
60548 this.mainHd.on("mouseover", this.handleHdOver, this);
60549 this.mainHd.on("mouseout", this.handleHdOut, this);
60550 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60551 {delegate: "."+this.splitClass});
60553 this.lockedHd.on("mouseover", this.handleHdOver, this);
60554 this.lockedHd.on("mouseout", this.handleHdOut, this);
60555 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60556 {delegate: "."+this.splitClass});
60558 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60559 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60562 this.updateSplitters();
60564 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60565 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60566 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60569 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60570 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60572 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60573 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60575 if(this.grid.enableColLock !== false){
60576 this.hmenu.add('-',
60577 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60578 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60582 this.hmenu.add('-',
60583 {id:"wider", text: this.columnsWiderText},
60584 {id:"narrow", text: this.columnsNarrowText }
60590 if(this.grid.enableColumnHide !== false){
60592 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60593 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60594 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60596 this.hmenu.add('-',
60597 {id:"columns", text: this.columnsText, menu: this.colMenu}
60600 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60602 this.grid.on("headercontextmenu", this.handleHdCtx, this);
60605 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60606 this.dd = new Roo.grid.GridDragZone(this.grid, {
60607 ddGroup : this.grid.ddGroup || 'GridDD'
60613 for(var i = 0; i < colCount; i++){
60614 if(cm.isHidden(i)){
60615 this.hideColumn(i);
60617 if(cm.config[i].align){
60618 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60619 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60623 this.updateHeaderSortState();
60625 this.beforeInitialResize();
60628 // two part rendering gives faster view to the user
60629 this.renderPhase2.defer(1, this);
60632 renderPhase2 : function(){
60633 // render the rows now
60635 if(this.grid.autoSizeColumns){
60636 this.autoSizeColumns();
60640 beforeInitialResize : function(){
60644 onColumnSplitterMoved : function(i, w){
60645 this.userResized = true;
60646 var cm = this.grid.colModel;
60647 cm.setColumnWidth(i, w, true);
60648 var cid = cm.getColumnId(i);
60649 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60650 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60651 this.updateSplitters();
60653 this.grid.fireEvent("columnresize", i, w);
60656 syncRowHeights : function(startIndex, endIndex){
60657 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60658 startIndex = startIndex || 0;
60659 var mrows = this.getBodyTable().rows;
60660 var lrows = this.getLockedTable().rows;
60661 var len = mrows.length-1;
60662 endIndex = Math.min(endIndex || len, len);
60663 for(var i = startIndex; i <= endIndex; i++){
60664 var m = mrows[i], l = lrows[i];
60665 var h = Math.max(m.offsetHeight, l.offsetHeight);
60666 m.style.height = l.style.height = h + "px";
60671 layout : function(initialRender, is2ndPass)
60674 var auto = g.autoHeight;
60675 var scrollOffset = 16;
60676 var c = g.getGridEl(), cm = this.cm,
60677 expandCol = g.autoExpandColumn,
60679 //c.beginMeasure();
60681 if(!c.dom.offsetWidth){ // display:none?
60683 this.lockedWrap.show();
60684 this.mainWrap.show();
60689 var hasLock = this.cm.isLocked(0);
60691 var tbh = this.headerPanel.getHeight();
60692 var bbh = this.footerPanel.getHeight();
60695 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60696 var newHeight = ch + c.getBorderWidth("tb");
60698 newHeight = Math.min(g.maxHeight, newHeight);
60700 c.setHeight(newHeight);
60704 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60707 var s = this.scroller;
60709 var csize = c.getSize(true);
60711 this.el.setSize(csize.width, csize.height);
60713 this.headerPanel.setWidth(csize.width);
60714 this.footerPanel.setWidth(csize.width);
60716 var hdHeight = this.mainHd.getHeight();
60717 var vw = csize.width;
60718 var vh = csize.height - (tbh + bbh);
60722 var bt = this.getBodyTable();
60724 if(cm.getLockedCount() == cm.config.length){
60725 bt = this.getLockedTable();
60728 var ltWidth = hasLock ?
60729 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60731 var scrollHeight = bt.offsetHeight;
60732 var scrollWidth = ltWidth + bt.offsetWidth;
60733 var vscroll = false, hscroll = false;
60735 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60737 var lw = this.lockedWrap, mw = this.mainWrap;
60738 var lb = this.lockedBody, mb = this.mainBody;
60740 setTimeout(function(){
60741 var t = s.dom.offsetTop;
60742 var w = s.dom.clientWidth,
60743 h = s.dom.clientHeight;
60746 lw.setSize(ltWidth, h);
60748 mw.setLeftTop(ltWidth, t);
60749 mw.setSize(w-ltWidth, h);
60751 lb.setHeight(h-hdHeight);
60752 mb.setHeight(h-hdHeight);
60754 if(is2ndPass !== true && !gv.userResized && expandCol){
60755 // high speed resize without full column calculation
60757 var ci = cm.getIndexById(expandCol);
60759 ci = cm.findColumnIndex(expandCol);
60761 ci = Math.max(0, ci); // make sure it's got at least the first col.
60762 var expandId = cm.getColumnId(ci);
60763 var tw = cm.getTotalWidth(false);
60764 var currentWidth = cm.getColumnWidth(ci);
60765 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60766 if(currentWidth != cw){
60767 cm.setColumnWidth(ci, cw, true);
60768 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60769 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60770 gv.updateSplitters();
60771 gv.layout(false, true);
60783 onWindowResize : function(){
60784 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60790 appendFooter : function(parentEl){
60794 sortAscText : "Sort Ascending",
60795 sortDescText : "Sort Descending",
60796 lockText : "Lock Column",
60797 unlockText : "Unlock Column",
60798 columnsText : "Columns",
60800 columnsWiderText : "Wider",
60801 columnsNarrowText : "Thinner"
60805 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60806 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60807 this.proxy.el.addClass('x-grid3-col-dd');
60810 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60811 handleMouseDown : function(e){
60815 callHandleMouseDown : function(e){
60816 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60821 * Ext JS Library 1.1.1
60822 * Copyright(c) 2006-2007, Ext JS, LLC.
60824 * Originally Released Under LGPL - original licence link has changed is not relivant.
60827 * <script type="text/javascript">
60830 * @extends Roo.dd.DDProxy
60831 * @class Roo.grid.SplitDragZone
60832 * Support for Column Header resizing
60834 * @param {Object} config
60837 // This is a support class used internally by the Grid components
60838 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60840 this.view = grid.getView();
60841 this.proxy = this.view.resizeProxy;
60842 Roo.grid.SplitDragZone.superclass.constructor.call(
60845 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60847 dragElId : Roo.id(this.proxy.dom),
60852 this.setHandleElId(Roo.id(hd));
60853 if (hd2 !== false) {
60854 this.setOuterHandleElId(Roo.id(hd2));
60857 this.scroll = false;
60859 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60860 fly: Roo.Element.fly,
60862 b4StartDrag : function(x, y){
60863 this.view.headersDisabled = true;
60864 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60865 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60867 this.proxy.setHeight(h);
60869 // for old system colWidth really stored the actual width?
60870 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60871 // which in reality did not work.. - it worked only for fixed sizes
60872 // for resizable we need to use actual sizes.
60873 var w = this.cm.getColumnWidth(this.cellIndex);
60874 if (!this.view.mainWrap) {
60876 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60881 // this was w-this.grid.minColumnWidth;
60882 // doesnt really make sense? - w = thie curren width or the rendered one?
60883 var minw = Math.max(w-this.grid.minColumnWidth, 0);
60884 this.resetConstraints();
60885 this.setXConstraint(minw, 1000);
60886 this.setYConstraint(0, 0);
60887 this.minX = x - minw;
60888 this.maxX = x + 1000;
60890 if (!this.view.mainWrap) { // this is Bootstrap code..
60891 this.getDragEl().style.display='block';
60894 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60898 handleMouseDown : function(e){
60899 ev = Roo.EventObject.setEvent(e);
60900 var t = this.fly(ev.getTarget());
60901 if(t.hasClass("x-grid-split")){
60902 this.cellIndex = this.view.getCellIndex(t.dom);
60903 this.split = t.dom;
60904 this.cm = this.grid.colModel;
60905 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60906 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60911 endDrag : function(e){
60912 this.view.headersDisabled = false;
60913 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60914 var diff = endX - this.startPos;
60916 var w = this.cm.getColumnWidth(this.cellIndex);
60917 if (!this.view.mainWrap) {
60920 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60923 autoOffset : function(){
60924 this.setDelta(0,0);
60928 * Ext JS Library 1.1.1
60929 * Copyright(c) 2006-2007, Ext JS, LLC.
60931 * Originally Released Under LGPL - original licence link has changed is not relivant.
60934 * <script type="text/javascript">
60938 // This is a support class used internally by the Grid components
60939 Roo.grid.GridDragZone = function(grid, config){
60940 this.view = grid.getView();
60941 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60942 if(this.view.lockedBody){
60943 this.setHandleElId(Roo.id(this.view.mainBody.dom));
60944 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60946 this.scroll = false;
60948 this.ddel = document.createElement('div');
60949 this.ddel.className = 'x-grid-dd-wrap';
60952 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60953 ddGroup : "GridDD",
60955 getDragData : function(e){
60956 var t = Roo.lib.Event.getTarget(e);
60957 var rowIndex = this.view.findRowIndex(t);
60958 var sm = this.grid.selModel;
60960 //Roo.log(rowIndex);
60962 if (sm.getSelectedCell) {
60963 // cell selection..
60964 if (!sm.getSelectedCell()) {
60967 if (rowIndex != sm.getSelectedCell()[0]) {
60972 if (sm.getSelections && sm.getSelections().length < 1) {
60977 // before it used to all dragging of unseleted... - now we dont do that.
60978 if(rowIndex !== false){
60983 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60985 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60988 if (e.hasModifier()){
60989 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60992 Roo.log("getDragData");
60997 rowIndex: rowIndex,
60998 selections: sm.getSelections ? sm.getSelections() : (
60999 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61006 onInitDrag : function(e){
61007 var data = this.dragData;
61008 this.ddel.innerHTML = this.grid.getDragDropText();
61009 this.proxy.update(this.ddel);
61010 // fire start drag?
61013 afterRepair : function(){
61014 this.dragging = false;
61017 getRepairXY : function(e, data){
61021 onEndDrag : function(data, e){
61025 onValidDrop : function(dd, e, id){
61030 beforeInvalidDrop : function(e, id){
61035 * Ext JS Library 1.1.1
61036 * Copyright(c) 2006-2007, Ext JS, LLC.
61038 * Originally Released Under LGPL - original licence link has changed is not relivant.
61041 * <script type="text/javascript">
61046 * @class Roo.grid.ColumnModel
61047 * @extends Roo.util.Observable
61048 * This is the default implementation of a ColumnModel used by the Grid. It defines
61049 * the columns in the grid.
61052 var colModel = new Roo.grid.ColumnModel([
61053 {header: "Ticker", width: 60, sortable: true, locked: true},
61054 {header: "Company Name", width: 150, sortable: true},
61055 {header: "Market Cap.", width: 100, sortable: true},
61056 {header: "$ Sales", width: 100, sortable: true, renderer: money},
61057 {header: "Employees", width: 100, sortable: true, resizable: false}
61062 * The config options listed for this class are options which may appear in each
61063 * individual column definition.
61064 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61066 * @param {Object} config An Array of column config objects. See this class's
61067 * config objects for details.
61069 Roo.grid.ColumnModel = function(config){
61071 * The config passed into the constructor
61073 this.config = []; //config;
61076 // if no id, create one
61077 // if the column does not have a dataIndex mapping,
61078 // map it to the order it is in the config
61079 for(var i = 0, len = config.length; i < len; i++){
61080 this.addColumn(config[i]);
61085 * The width of columns which have no width specified (defaults to 100)
61088 this.defaultWidth = 100;
61091 * Default sortable of columns which have no sortable specified (defaults to false)
61094 this.defaultSortable = false;
61098 * @event widthchange
61099 * Fires when the width of a column changes.
61100 * @param {ColumnModel} this
61101 * @param {Number} columnIndex The column index
61102 * @param {Number} newWidth The new width
61104 "widthchange": true,
61106 * @event headerchange
61107 * Fires when the text of a header changes.
61108 * @param {ColumnModel} this
61109 * @param {Number} columnIndex The column index
61110 * @param {Number} newText The new header text
61112 "headerchange": true,
61114 * @event hiddenchange
61115 * Fires when a column is hidden or "unhidden".
61116 * @param {ColumnModel} this
61117 * @param {Number} columnIndex The column index
61118 * @param {Boolean} hidden true if hidden, false otherwise
61120 "hiddenchange": true,
61122 * @event columnmoved
61123 * Fires when a column is moved.
61124 * @param {ColumnModel} this
61125 * @param {Number} oldIndex
61126 * @param {Number} newIndex
61128 "columnmoved" : true,
61130 * @event columlockchange
61131 * Fires when a column's locked state is changed
61132 * @param {ColumnModel} this
61133 * @param {Number} colIndex
61134 * @param {Boolean} locked true if locked
61136 "columnlockchange" : true
61138 Roo.grid.ColumnModel.superclass.constructor.call(this);
61140 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61142 * @cfg {String} header The header text to display in the Grid view.
61145 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61148 * @cfg {String} smHeader Header at Bootsrap Small width
61151 * @cfg {String} mdHeader Header at Bootsrap Medium width
61154 * @cfg {String} lgHeader Header at Bootsrap Large width
61157 * @cfg {String} xlHeader Header at Bootsrap extra Large width
61160 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61161 * {@link Roo.data.Record} definition from which to draw the column's value. If not
61162 * specified, the column's index is used as an index into the Record's data Array.
61165 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61166 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61169 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61170 * Defaults to the value of the {@link #defaultSortable} property.
61171 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61174 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
61177 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
61180 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61183 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61186 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61187 * given the cell's data value. See {@link #setRenderer}. If not specified, the
61188 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61189 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61192 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
61195 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
61198 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
61201 * @cfg {String} cursor (Optional)
61204 * @cfg {String} tooltip (Optional)
61207 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61210 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61213 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61216 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61219 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61222 * Returns the id of the column at the specified index.
61223 * @param {Number} index The column index
61224 * @return {String} the id
61226 getColumnId : function(index){
61227 return this.config[index].id;
61231 * Returns the column for a specified id.
61232 * @param {String} id The column id
61233 * @return {Object} the column
61235 getColumnById : function(id){
61236 return this.lookup[id];
61241 * Returns the column Object for a specified dataIndex.
61242 * @param {String} dataIndex The column dataIndex
61243 * @return {Object|Boolean} the column or false if not found
61245 getColumnByDataIndex: function(dataIndex){
61246 var index = this.findColumnIndex(dataIndex);
61247 return index > -1 ? this.config[index] : false;
61251 * Returns the index for a specified column id.
61252 * @param {String} id The column id
61253 * @return {Number} the index, or -1 if not found
61255 getIndexById : function(id){
61256 for(var i = 0, len = this.config.length; i < len; i++){
61257 if(this.config[i].id == id){
61265 * Returns the index for a specified column dataIndex.
61266 * @param {String} dataIndex The column dataIndex
61267 * @return {Number} the index, or -1 if not found
61270 findColumnIndex : function(dataIndex){
61271 for(var i = 0, len = this.config.length; i < len; i++){
61272 if(this.config[i].dataIndex == dataIndex){
61280 moveColumn : function(oldIndex, newIndex){
61281 var c = this.config[oldIndex];
61282 this.config.splice(oldIndex, 1);
61283 this.config.splice(newIndex, 0, c);
61284 this.dataMap = null;
61285 this.fireEvent("columnmoved", this, oldIndex, newIndex);
61288 isLocked : function(colIndex){
61289 return this.config[colIndex].locked === true;
61292 setLocked : function(colIndex, value, suppressEvent){
61293 if(this.isLocked(colIndex) == value){
61296 this.config[colIndex].locked = value;
61297 if(!suppressEvent){
61298 this.fireEvent("columnlockchange", this, colIndex, value);
61302 getTotalLockedWidth : function(){
61303 var totalWidth = 0;
61304 for(var i = 0; i < this.config.length; i++){
61305 if(this.isLocked(i) && !this.isHidden(i)){
61306 this.totalWidth += this.getColumnWidth(i);
61312 getLockedCount : function(){
61313 for(var i = 0, len = this.config.length; i < len; i++){
61314 if(!this.isLocked(i)){
61319 return this.config.length;
61323 * Returns the number of columns.
61326 getColumnCount : function(visibleOnly){
61327 if(visibleOnly === true){
61329 for(var i = 0, len = this.config.length; i < len; i++){
61330 if(!this.isHidden(i)){
61336 return this.config.length;
61340 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61341 * @param {Function} fn
61342 * @param {Object} scope (optional)
61343 * @return {Array} result
61345 getColumnsBy : function(fn, scope){
61347 for(var i = 0, len = this.config.length; i < len; i++){
61348 var c = this.config[i];
61349 if(fn.call(scope||this, c, i) === true){
61357 * Returns true if the specified column is sortable.
61358 * @param {Number} col The column index
61359 * @return {Boolean}
61361 isSortable : function(col){
61362 if(typeof this.config[col].sortable == "undefined"){
61363 return this.defaultSortable;
61365 return this.config[col].sortable;
61369 * Returns the rendering (formatting) function defined for the column.
61370 * @param {Number} col The column index.
61371 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61373 getRenderer : function(col){
61374 if(!this.config[col].renderer){
61375 return Roo.grid.ColumnModel.defaultRenderer;
61377 return this.config[col].renderer;
61381 * Sets the rendering (formatting) function for a column.
61382 * @param {Number} col The column index
61383 * @param {Function} fn The function to use to process the cell's raw data
61384 * to return HTML markup for the grid view. The render function is called with
61385 * the following parameters:<ul>
61386 * <li>Data value.</li>
61387 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61388 * <li>css A CSS style string to apply to the table cell.</li>
61389 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61390 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61391 * <li>Row index</li>
61392 * <li>Column index</li>
61393 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61395 setRenderer : function(col, fn){
61396 this.config[col].renderer = fn;
61400 * Returns the width for the specified column.
61401 * @param {Number} col The column index
61402 * @param (optional) {String} gridSize bootstrap width size.
61405 getColumnWidth : function(col, gridSize)
61407 var cfg = this.config[col];
61409 if (typeof(gridSize) == 'undefined') {
61410 return cfg.width * 1 || this.defaultWidth;
61412 if (gridSize === false) { // if we set it..
61413 return cfg.width || false;
61415 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61417 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61418 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61421 return cfg[ sizes[i] ];
61428 * Sets the width for a column.
61429 * @param {Number} col The column index
61430 * @param {Number} width The new width
61432 setColumnWidth : function(col, width, suppressEvent){
61433 this.config[col].width = width;
61434 this.totalWidth = null;
61435 if(!suppressEvent){
61436 this.fireEvent("widthchange", this, col, width);
61441 * Returns the total width of all columns.
61442 * @param {Boolean} includeHidden True to include hidden column widths
61445 getTotalWidth : function(includeHidden){
61446 if(!this.totalWidth){
61447 this.totalWidth = 0;
61448 for(var i = 0, len = this.config.length; i < len; i++){
61449 if(includeHidden || !this.isHidden(i)){
61450 this.totalWidth += this.getColumnWidth(i);
61454 return this.totalWidth;
61458 * Returns the header for the specified column.
61459 * @param {Number} col The column index
61462 getColumnHeader : function(col){
61463 return this.config[col].header;
61467 * Sets the header for a column.
61468 * @param {Number} col The column index
61469 * @param {String} header The new header
61471 setColumnHeader : function(col, header){
61472 this.config[col].header = header;
61473 this.fireEvent("headerchange", this, col, header);
61477 * Returns the tooltip for the specified column.
61478 * @param {Number} col The column index
61481 getColumnTooltip : function(col){
61482 return this.config[col].tooltip;
61485 * Sets the tooltip for a column.
61486 * @param {Number} col The column index
61487 * @param {String} tooltip The new tooltip
61489 setColumnTooltip : function(col, tooltip){
61490 this.config[col].tooltip = tooltip;
61494 * Returns the dataIndex for the specified column.
61495 * @param {Number} col The column index
61498 getDataIndex : function(col){
61499 return this.config[col].dataIndex;
61503 * Sets the dataIndex for a column.
61504 * @param {Number} col The column index
61505 * @param {Number} dataIndex The new dataIndex
61507 setDataIndex : function(col, dataIndex){
61508 this.config[col].dataIndex = dataIndex;
61514 * Returns true if the cell is editable.
61515 * @param {Number} colIndex The column index
61516 * @param {Number} rowIndex The row index - this is nto actually used..?
61517 * @return {Boolean}
61519 isCellEditable : function(colIndex, rowIndex){
61520 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61524 * Returns the editor defined for the cell/column.
61525 * return false or null to disable editing.
61526 * @param {Number} colIndex The column index
61527 * @param {Number} rowIndex The row index
61530 getCellEditor : function(colIndex, rowIndex){
61531 return this.config[colIndex].editor;
61535 * Sets if a column is editable.
61536 * @param {Number} col The column index
61537 * @param {Boolean} editable True if the column is editable
61539 setEditable : function(col, editable){
61540 this.config[col].editable = editable;
61545 * Returns true if the column is hidden.
61546 * @param {Number} colIndex The column index
61547 * @return {Boolean}
61549 isHidden : function(colIndex){
61550 return this.config[colIndex].hidden;
61555 * Returns true if the column width cannot be changed
61557 isFixed : function(colIndex){
61558 return this.config[colIndex].fixed;
61562 * Returns true if the column can be resized
61563 * @return {Boolean}
61565 isResizable : function(colIndex){
61566 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61569 * Sets if a column is hidden.
61570 * @param {Number} colIndex The column index
61571 * @param {Boolean} hidden True if the column is hidden
61573 setHidden : function(colIndex, hidden){
61574 this.config[colIndex].hidden = hidden;
61575 this.totalWidth = null;
61576 this.fireEvent("hiddenchange", this, colIndex, hidden);
61580 * Sets the editor for a column.
61581 * @param {Number} col The column index
61582 * @param {Object} editor The editor object
61584 setEditor : function(col, editor){
61585 this.config[col].editor = editor;
61588 * Add a column (experimental...) - defaults to adding to the end..
61589 * @param {Object} config
61591 addColumn : function(c)
61594 var i = this.config.length;
61595 this.config[i] = c;
61597 if(typeof c.dataIndex == "undefined"){
61600 if(typeof c.renderer == "string"){
61601 c.renderer = Roo.util.Format[c.renderer];
61603 if(typeof c.id == "undefined"){
61606 if(c.editor && c.editor.xtype){
61607 c.editor = Roo.factory(c.editor, Roo.grid);
61609 if(c.editor && c.editor.isFormField){
61610 c.editor = new Roo.grid.GridEditor(c.editor);
61612 this.lookup[c.id] = c;
61617 Roo.grid.ColumnModel.defaultRenderer = function(value)
61619 if(typeof value == "object") {
61622 if(typeof value == "string" && value.length < 1){
61626 return String.format("{0}", value);
61629 // Alias for backwards compatibility
61630 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61633 * Ext JS Library 1.1.1
61634 * Copyright(c) 2006-2007, Ext JS, LLC.
61636 * Originally Released Under LGPL - original licence link has changed is not relivant.
61639 * <script type="text/javascript">
61643 * @class Roo.grid.AbstractSelectionModel
61644 * @extends Roo.util.Observable
61646 * Abstract base class for grid SelectionModels. It provides the interface that should be
61647 * implemented by descendant classes. This class should not be directly instantiated.
61650 Roo.grid.AbstractSelectionModel = function(){
61651 this.locked = false;
61652 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61655 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
61656 /** @ignore Called by the grid automatically. Do not call directly. */
61657 init : function(grid){
61663 * Locks the selections.
61666 this.locked = true;
61670 * Unlocks the selections.
61672 unlock : function(){
61673 this.locked = false;
61677 * Returns true if the selections are locked.
61678 * @return {Boolean}
61680 isLocked : function(){
61681 return this.locked;
61685 * Ext JS Library 1.1.1
61686 * Copyright(c) 2006-2007, Ext JS, LLC.
61688 * Originally Released Under LGPL - original licence link has changed is not relivant.
61691 * <script type="text/javascript">
61694 * @extends Roo.grid.AbstractSelectionModel
61695 * @class Roo.grid.RowSelectionModel
61696 * The default SelectionModel used by {@link Roo.grid.Grid}.
61697 * It supports multiple selections and keyboard selection/navigation.
61699 * @param {Object} config
61701 Roo.grid.RowSelectionModel = function(config){
61702 Roo.apply(this, config);
61703 this.selections = new Roo.util.MixedCollection(false, function(o){
61708 this.lastActive = false;
61712 * @event selectionchange
61713 * Fires when the selection changes
61714 * @param {SelectionModel} this
61716 "selectionchange" : true,
61718 * @event afterselectionchange
61719 * Fires after the selection changes (eg. by key press or clicking)
61720 * @param {SelectionModel} this
61722 "afterselectionchange" : true,
61724 * @event beforerowselect
61725 * Fires when a row is selected being selected, return false to cancel.
61726 * @param {SelectionModel} this
61727 * @param {Number} rowIndex The selected index
61728 * @param {Boolean} keepExisting False if other selections will be cleared
61730 "beforerowselect" : true,
61733 * Fires when a row is selected.
61734 * @param {SelectionModel} this
61735 * @param {Number} rowIndex The selected index
61736 * @param {Roo.data.Record} r The record
61738 "rowselect" : true,
61740 * @event rowdeselect
61741 * Fires when a row is deselected.
61742 * @param {SelectionModel} this
61743 * @param {Number} rowIndex The selected index
61745 "rowdeselect" : true
61747 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61748 this.locked = false;
61751 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
61753 * @cfg {Boolean} singleSelect
61754 * True to allow selection of only one row at a time (defaults to false)
61756 singleSelect : false,
61759 initEvents : function(){
61761 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61762 this.grid.on("mousedown", this.handleMouseDown, this);
61763 }else{ // allow click to work like normal
61764 this.grid.on("rowclick", this.handleDragableRowClick, this);
61766 // bootstrap does not have a view..
61767 var view = this.grid.view ? this.grid.view : this.grid;
61768 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61769 "up" : function(e){
61771 this.selectPrevious(e.shiftKey);
61772 }else if(this.last !== false && this.lastActive !== false){
61773 var last = this.last;
61774 this.selectRange(this.last, this.lastActive-1);
61775 view.focusRow(this.lastActive);
61776 if(last !== false){
61780 this.selectFirstRow();
61782 this.fireEvent("afterselectionchange", this);
61784 "down" : function(e){
61786 this.selectNext(e.shiftKey);
61787 }else if(this.last !== false && this.lastActive !== false){
61788 var last = this.last;
61789 this.selectRange(this.last, this.lastActive+1);
61790 view.focusRow(this.lastActive);
61791 if(last !== false){
61795 this.selectFirstRow();
61797 this.fireEvent("afterselectionchange", this);
61803 view.on("refresh", this.onRefresh, this);
61804 view.on("rowupdated", this.onRowUpdated, this);
61805 view.on("rowremoved", this.onRemove, this);
61809 onRefresh : function(){
61810 var ds = this.grid.ds, i, v = this.grid.view;
61811 var s = this.selections;
61812 s.each(function(r){
61813 if((i = ds.indexOfId(r.id)) != -1){
61815 s.add(ds.getAt(i)); // updating the selection relate data
61823 onRemove : function(v, index, r){
61824 this.selections.remove(r);
61828 onRowUpdated : function(v, index, r){
61829 if(this.isSelected(r)){
61830 v.onRowSelect(index);
61836 * @param {Array} records The records to select
61837 * @param {Boolean} keepExisting (optional) True to keep existing selections
61839 selectRecords : function(records, keepExisting){
61841 this.clearSelections();
61843 var ds = this.grid.ds;
61844 for(var i = 0, len = records.length; i < len; i++){
61845 this.selectRow(ds.indexOf(records[i]), true);
61850 * Gets the number of selected rows.
61853 getCount : function(){
61854 return this.selections.length;
61858 * Selects the first row in the grid.
61860 selectFirstRow : function(){
61865 * Select the last row.
61866 * @param {Boolean} keepExisting (optional) True to keep existing selections
61868 selectLastRow : function(keepExisting){
61869 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61873 * Selects the row immediately following the last selected row.
61874 * @param {Boolean} keepExisting (optional) True to keep existing selections
61876 selectNext : function(keepExisting){
61877 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61878 this.selectRow(this.last+1, keepExisting);
61879 var view = this.grid.view ? this.grid.view : this.grid;
61880 view.focusRow(this.last);
61885 * Selects the row that precedes the last selected row.
61886 * @param {Boolean} keepExisting (optional) True to keep existing selections
61888 selectPrevious : function(keepExisting){
61890 this.selectRow(this.last-1, keepExisting);
61891 var view = this.grid.view ? this.grid.view : this.grid;
61892 view.focusRow(this.last);
61897 * Returns the selected records
61898 * @return {Array} Array of selected records
61900 getSelections : function(){
61901 return [].concat(this.selections.items);
61905 * Returns the first selected record.
61908 getSelected : function(){
61909 return this.selections.itemAt(0);
61914 * Clears all selections.
61916 clearSelections : function(fast){
61921 var ds = this.grid.ds;
61922 var s = this.selections;
61923 s.each(function(r){
61924 this.deselectRow(ds.indexOfId(r.id));
61928 this.selections.clear();
61935 * Selects all rows.
61937 selectAll : function(){
61941 this.selections.clear();
61942 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61943 this.selectRow(i, true);
61948 * Returns True if there is a selection.
61949 * @return {Boolean}
61951 hasSelection : function(){
61952 return this.selections.length > 0;
61956 * Returns True if the specified row is selected.
61957 * @param {Number/Record} record The record or index of the record to check
61958 * @return {Boolean}
61960 isSelected : function(index){
61961 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61962 return (r && this.selections.key(r.id) ? true : false);
61966 * Returns True if the specified record id is selected.
61967 * @param {String} id The id of record to check
61968 * @return {Boolean}
61970 isIdSelected : function(id){
61971 return (this.selections.key(id) ? true : false);
61975 handleMouseDown : function(e, t)
61977 var view = this.grid.view ? this.grid.view : this.grid;
61979 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61982 if(e.shiftKey && this.last !== false){
61983 var last = this.last;
61984 this.selectRange(last, rowIndex, e.ctrlKey);
61985 this.last = last; // reset the last
61986 view.focusRow(rowIndex);
61988 var isSelected = this.isSelected(rowIndex);
61989 if(e.button !== 0 && isSelected){
61990 view.focusRow(rowIndex);
61991 }else if(e.ctrlKey && isSelected){
61992 this.deselectRow(rowIndex);
61993 }else if(!isSelected){
61994 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61995 view.focusRow(rowIndex);
61998 this.fireEvent("afterselectionchange", this);
62001 handleDragableRowClick : function(grid, rowIndex, e)
62003 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62004 this.selectRow(rowIndex, false);
62005 var view = this.grid.view ? this.grid.view : this.grid;
62006 view.focusRow(rowIndex);
62007 this.fireEvent("afterselectionchange", this);
62012 * Selects multiple rows.
62013 * @param {Array} rows Array of the indexes of the row to select
62014 * @param {Boolean} keepExisting (optional) True to keep existing selections
62016 selectRows : function(rows, keepExisting){
62018 this.clearSelections();
62020 for(var i = 0, len = rows.length; i < len; i++){
62021 this.selectRow(rows[i], true);
62026 * Selects a range of rows. All rows in between startRow and endRow are also selected.
62027 * @param {Number} startRow The index of the first row in the range
62028 * @param {Number} endRow The index of the last row in the range
62029 * @param {Boolean} keepExisting (optional) True to retain existing selections
62031 selectRange : function(startRow, endRow, keepExisting){
62036 this.clearSelections();
62038 if(startRow <= endRow){
62039 for(var i = startRow; i <= endRow; i++){
62040 this.selectRow(i, true);
62043 for(var i = startRow; i >= endRow; i--){
62044 this.selectRow(i, true);
62050 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62051 * @param {Number} startRow The index of the first row in the range
62052 * @param {Number} endRow The index of the last row in the range
62054 deselectRange : function(startRow, endRow, preventViewNotify){
62058 for(var i = startRow; i <= endRow; i++){
62059 this.deselectRow(i, preventViewNotify);
62065 * @param {Number} row The index of the row to select
62066 * @param {Boolean} keepExisting (optional) True to keep existing selections
62068 selectRow : function(index, keepExisting, preventViewNotify){
62069 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62072 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62073 if(!keepExisting || this.singleSelect){
62074 this.clearSelections();
62076 var r = this.grid.ds.getAt(index);
62077 this.selections.add(r);
62078 this.last = this.lastActive = index;
62079 if(!preventViewNotify){
62080 var view = this.grid.view ? this.grid.view : this.grid;
62081 view.onRowSelect(index);
62083 this.fireEvent("rowselect", this, index, r);
62084 this.fireEvent("selectionchange", this);
62090 * @param {Number} row The index of the row to deselect
62092 deselectRow : function(index, preventViewNotify){
62096 if(this.last == index){
62099 if(this.lastActive == index){
62100 this.lastActive = false;
62102 var r = this.grid.ds.getAt(index);
62103 this.selections.remove(r);
62104 if(!preventViewNotify){
62105 var view = this.grid.view ? this.grid.view : this.grid;
62106 view.onRowDeselect(index);
62108 this.fireEvent("rowdeselect", this, index);
62109 this.fireEvent("selectionchange", this);
62113 restoreLast : function(){
62115 this.last = this._last;
62120 acceptsNav : function(row, col, cm){
62121 return !cm.isHidden(col) && cm.isCellEditable(col, row);
62125 onEditorKey : function(field, e){
62126 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62131 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62133 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62135 }else if(k == e.ENTER && !e.ctrlKey){
62139 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62141 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62143 }else if(k == e.ESC){
62147 g.startEditing(newCell[0], newCell[1]);
62152 * Ext JS Library 1.1.1
62153 * Copyright(c) 2006-2007, Ext JS, LLC.
62155 * Originally Released Under LGPL - original licence link has changed is not relivant.
62158 * <script type="text/javascript">
62161 * @class Roo.grid.CellSelectionModel
62162 * @extends Roo.grid.AbstractSelectionModel
62163 * This class provides the basic implementation for cell selection in a grid.
62165 * @param {Object} config The object containing the configuration of this model.
62166 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62168 Roo.grid.CellSelectionModel = function(config){
62169 Roo.apply(this, config);
62171 this.selection = null;
62175 * @event beforerowselect
62176 * Fires before a cell is selected.
62177 * @param {SelectionModel} this
62178 * @param {Number} rowIndex The selected row index
62179 * @param {Number} colIndex The selected cell index
62181 "beforecellselect" : true,
62183 * @event cellselect
62184 * Fires when a cell is selected.
62185 * @param {SelectionModel} this
62186 * @param {Number} rowIndex The selected row index
62187 * @param {Number} colIndex The selected cell index
62189 "cellselect" : true,
62191 * @event selectionchange
62192 * Fires when the active selection changes.
62193 * @param {SelectionModel} this
62194 * @param {Object} selection null for no selection or an object (o) with two properties
62196 <li>o.record: the record object for the row the selection is in</li>
62197 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62200 "selectionchange" : true,
62203 * Fires when the tab (or enter) was pressed on the last editable cell
62204 * You can use this to trigger add new row.
62205 * @param {SelectionModel} this
62209 * @event beforeeditnext
62210 * Fires before the next editable sell is made active
62211 * You can use this to skip to another cell or fire the tabend
62212 * if you set cell to false
62213 * @param {Object} eventdata object : { cell : [ row, col ] }
62215 "beforeeditnext" : true
62217 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62220 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
62222 enter_is_tab: false,
62225 initEvents : function(){
62226 this.grid.on("mousedown", this.handleMouseDown, this);
62227 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62228 var view = this.grid.view;
62229 view.on("refresh", this.onViewChange, this);
62230 view.on("rowupdated", this.onRowUpdated, this);
62231 view.on("beforerowremoved", this.clearSelections, this);
62232 view.on("beforerowsinserted", this.clearSelections, this);
62233 if(this.grid.isEditor){
62234 this.grid.on("beforeedit", this.beforeEdit, this);
62239 beforeEdit : function(e){
62240 this.select(e.row, e.column, false, true, e.record);
62244 onRowUpdated : function(v, index, r){
62245 if(this.selection && this.selection.record == r){
62246 v.onCellSelect(index, this.selection.cell[1]);
62251 onViewChange : function(){
62252 this.clearSelections(true);
62256 * Returns the currently selected cell,.
62257 * @return {Array} The selected cell (row, column) or null if none selected.
62259 getSelectedCell : function(){
62260 return this.selection ? this.selection.cell : null;
62264 * Clears all selections.
62265 * @param {Boolean} true to prevent the gridview from being notified about the change.
62267 clearSelections : function(preventNotify){
62268 var s = this.selection;
62270 if(preventNotify !== true){
62271 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62273 this.selection = null;
62274 this.fireEvent("selectionchange", this, null);
62279 * Returns true if there is a selection.
62280 * @return {Boolean}
62282 hasSelection : function(){
62283 return this.selection ? true : false;
62287 handleMouseDown : function(e, t){
62288 var v = this.grid.getView();
62289 if(this.isLocked()){
62292 var row = v.findRowIndex(t);
62293 var cell = v.findCellIndex(t);
62294 if(row !== false && cell !== false){
62295 this.select(row, cell);
62301 * @param {Number} rowIndex
62302 * @param {Number} collIndex
62304 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62305 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62306 this.clearSelections();
62307 r = r || this.grid.dataSource.getAt(rowIndex);
62310 cell : [rowIndex, colIndex]
62312 if(!preventViewNotify){
62313 var v = this.grid.getView();
62314 v.onCellSelect(rowIndex, colIndex);
62315 if(preventFocus !== true){
62316 v.focusCell(rowIndex, colIndex);
62319 this.fireEvent("cellselect", this, rowIndex, colIndex);
62320 this.fireEvent("selectionchange", this, this.selection);
62325 isSelectable : function(rowIndex, colIndex, cm){
62326 return !cm.isHidden(colIndex);
62330 handleKeyDown : function(e){
62331 //Roo.log('Cell Sel Model handleKeyDown');
62332 if(!e.isNavKeyPress()){
62335 var g = this.grid, s = this.selection;
62338 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
62340 this.select(cell[0], cell[1]);
62345 var walk = function(row, col, step){
62346 return g.walkCells(row, col, step, sm.isSelectable, sm);
62348 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62355 // handled by onEditorKey
62356 if (g.isEditor && g.editing) {
62360 newCell = walk(r, c-1, -1);
62362 newCell = walk(r, c+1, 1);
62367 newCell = walk(r+1, c, 1);
62371 newCell = walk(r-1, c, -1);
62375 newCell = walk(r, c+1, 1);
62379 newCell = walk(r, c-1, -1);
62384 if(g.isEditor && !g.editing){
62385 g.startEditing(r, c);
62394 this.select(newCell[0], newCell[1]);
62400 acceptsNav : function(row, col, cm){
62401 return !cm.isHidden(col) && cm.isCellEditable(col, row);
62405 * @param {Number} field (not used) - as it's normally used as a listener
62406 * @param {Number} e - event - fake it by using
62408 * var e = Roo.EventObjectImpl.prototype;
62409 * e.keyCode = e.TAB
62413 onEditorKey : function(field, e){
62415 var k = e.getKey(),
62418 ed = g.activeEditor,
62420 ///Roo.log('onEditorKey' + k);
62423 if (this.enter_is_tab && k == e.ENTER) {
62429 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62431 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62437 } else if(k == e.ENTER && !e.ctrlKey){
62440 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62442 } else if(k == e.ESC){
62447 var ecall = { cell : newCell, forward : forward };
62448 this.fireEvent('beforeeditnext', ecall );
62449 newCell = ecall.cell;
62450 forward = ecall.forward;
62454 //Roo.log('next cell after edit');
62455 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62456 } else if (forward) {
62457 // tabbed past last
62458 this.fireEvent.defer(100, this, ['tabend',this]);
62463 * Ext JS Library 1.1.1
62464 * Copyright(c) 2006-2007, Ext JS, LLC.
62466 * Originally Released Under LGPL - original licence link has changed is not relivant.
62469 * <script type="text/javascript">
62473 * @class Roo.grid.EditorGrid
62474 * @extends Roo.grid.Grid
62475 * Class for creating and editable grid.
62476 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62477 * The container MUST have some type of size defined for the grid to fill. The container will be
62478 * automatically set to position relative if it isn't already.
62479 * @param {Object} dataSource The data model to bind to
62480 * @param {Object} colModel The column model with info about this grid's columns
62482 Roo.grid.EditorGrid = function(container, config){
62483 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62484 this.getGridEl().addClass("xedit-grid");
62486 if(!this.selModel){
62487 this.selModel = new Roo.grid.CellSelectionModel();
62490 this.activeEditor = null;
62494 * @event beforeedit
62495 * Fires before cell editing is triggered. The edit event object has the following properties <br />
62496 * <ul style="padding:5px;padding-left:16px;">
62497 * <li>grid - This grid</li>
62498 * <li>record - The record being edited</li>
62499 * <li>field - The field name being edited</li>
62500 * <li>value - The value for the field being edited.</li>
62501 * <li>row - The grid row index</li>
62502 * <li>column - The grid column index</li>
62503 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62505 * @param {Object} e An edit event (see above for description)
62507 "beforeedit" : true,
62510 * Fires after a cell is edited. <br />
62511 * <ul style="padding:5px;padding-left:16px;">
62512 * <li>grid - This grid</li>
62513 * <li>record - The record being edited</li>
62514 * <li>field - The field name being edited</li>
62515 * <li>value - The value being set</li>
62516 * <li>originalValue - The original value for the field, before the edit.</li>
62517 * <li>row - The grid row index</li>
62518 * <li>column - The grid column index</li>
62520 * @param {Object} e An edit event (see above for description)
62522 "afteredit" : true,
62524 * @event validateedit
62525 * Fires after a cell is edited, but before the value is set in the record.
62526 * You can use this to modify the value being set in the field, Return false
62527 * to cancel the change. The edit event object has the following properties <br />
62528 * <ul style="padding:5px;padding-left:16px;">
62529 * <li>editor - This editor</li>
62530 * <li>grid - This grid</li>
62531 * <li>record - The record being edited</li>
62532 * <li>field - The field name being edited</li>
62533 * <li>value - The value being set</li>
62534 * <li>originalValue - The original value for the field, before the edit.</li>
62535 * <li>row - The grid row index</li>
62536 * <li>column - The grid column index</li>
62537 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62539 * @param {Object} e An edit event (see above for description)
62541 "validateedit" : true
62543 this.on("bodyscroll", this.stopEditing, this);
62544 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
62547 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62549 * @cfg {Number} clicksToEdit
62550 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62557 trackMouseOver: false, // causes very odd FF errors
62559 onCellDblClick : function(g, row, col){
62560 this.startEditing(row, col);
62563 onEditComplete : function(ed, value, startValue){
62564 this.editing = false;
62565 this.activeEditor = null;
62566 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62568 var field = this.colModel.getDataIndex(ed.col);
62573 originalValue: startValue,
62580 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62583 if(String(value) !== String(startValue)){
62585 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62586 r.set(field, e.value);
62587 // if we are dealing with a combo box..
62588 // then we also set the 'name' colum to be the displayField
62589 if (ed.field.displayField && ed.field.name) {
62590 r.set(ed.field.name, ed.field.el.dom.value);
62593 delete e.cancel; //?? why!!!
62594 this.fireEvent("afteredit", e);
62597 this.fireEvent("afteredit", e); // always fire it!
62599 this.view.focusCell(ed.row, ed.col);
62603 * Starts editing the specified for the specified row/column
62604 * @param {Number} rowIndex
62605 * @param {Number} colIndex
62607 startEditing : function(row, col){
62608 this.stopEditing();
62609 if(this.colModel.isCellEditable(col, row)){
62610 this.view.ensureVisible(row, col, true);
62612 var r = this.dataSource.getAt(row);
62613 var field = this.colModel.getDataIndex(col);
62614 var cell = Roo.get(this.view.getCell(row,col));
62619 value: r.data[field],
62624 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62625 this.editing = true;
62626 var ed = this.colModel.getCellEditor(col, row);
62632 ed.render(ed.parentEl || document.body);
62638 (function(){ // complex but required for focus issues in safari, ie and opera
62642 ed.on("complete", this.onEditComplete, this, {single: true});
62643 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
62644 this.activeEditor = ed;
62645 var v = r.data[field];
62646 ed.startEdit(this.view.getCell(row, col), v);
62647 // combo's with 'displayField and name set
62648 if (ed.field.displayField && ed.field.name) {
62649 ed.field.el.dom.value = r.data[ed.field.name];
62653 }).defer(50, this);
62659 * Stops any active editing
62661 stopEditing : function(){
62662 if(this.activeEditor){
62663 this.activeEditor.completeEdit();
62665 this.activeEditor = null;
62669 * Called to get grid's drag proxy text, by default returns this.ddText.
62672 getDragDropText : function(){
62673 var count = this.selModel.getSelectedCell() ? 1 : 0;
62674 return String.format(this.ddText, count, count == 1 ? '' : 's');
62679 * Ext JS Library 1.1.1
62680 * Copyright(c) 2006-2007, Ext JS, LLC.
62682 * Originally Released Under LGPL - original licence link has changed is not relivant.
62685 * <script type="text/javascript">
62688 // private - not really -- you end up using it !
62689 // This is a support class used internally by the Grid components
62692 * @class Roo.grid.GridEditor
62693 * @extends Roo.Editor
62694 * Class for creating and editable grid elements.
62695 * @param {Object} config any settings (must include field)
62697 Roo.grid.GridEditor = function(field, config){
62698 if (!config && field.field) {
62700 field = Roo.factory(config.field, Roo.form);
62702 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62703 field.monitorTab = false;
62706 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62709 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62712 alignment: "tl-tl",
62715 cls: "x-small-editor x-grid-editor",
62720 * Ext JS Library 1.1.1
62721 * Copyright(c) 2006-2007, Ext JS, LLC.
62723 * Originally Released Under LGPL - original licence link has changed is not relivant.
62726 * <script type="text/javascript">
62731 Roo.grid.PropertyRecord = Roo.data.Record.create([
62732 {name:'name',type:'string'}, 'value'
62736 Roo.grid.PropertyStore = function(grid, source){
62738 this.store = new Roo.data.Store({
62739 recordType : Roo.grid.PropertyRecord
62741 this.store.on('update', this.onUpdate, this);
62743 this.setSource(source);
62745 Roo.grid.PropertyStore.superclass.constructor.call(this);
62750 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62751 setSource : function(o){
62753 this.store.removeAll();
62756 if(this.isEditableValue(o[k])){
62757 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62760 this.store.loadRecords({records: data}, {}, true);
62763 onUpdate : function(ds, record, type){
62764 if(type == Roo.data.Record.EDIT){
62765 var v = record.data['value'];
62766 var oldValue = record.modified['value'];
62767 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62768 this.source[record.id] = v;
62770 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62777 getProperty : function(row){
62778 return this.store.getAt(row);
62781 isEditableValue: function(val){
62782 if(val && val instanceof Date){
62784 }else if(typeof val == 'object' || typeof val == 'function'){
62790 setValue : function(prop, value){
62791 this.source[prop] = value;
62792 this.store.getById(prop).set('value', value);
62795 getSource : function(){
62796 return this.source;
62800 Roo.grid.PropertyColumnModel = function(grid, store){
62803 g.PropertyColumnModel.superclass.constructor.call(this, [
62804 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62805 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62807 this.store = store;
62808 this.bselect = Roo.DomHelper.append(document.body, {
62809 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62810 {tag: 'option', value: 'true', html: 'true'},
62811 {tag: 'option', value: 'false', html: 'false'}
62814 Roo.id(this.bselect);
62817 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62818 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62819 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62820 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62821 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62823 this.renderCellDelegate = this.renderCell.createDelegate(this);
62824 this.renderPropDelegate = this.renderProp.createDelegate(this);
62827 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62831 valueText : 'Value',
62833 dateFormat : 'm/j/Y',
62836 renderDate : function(dateVal){
62837 return dateVal.dateFormat(this.dateFormat);
62840 renderBool : function(bVal){
62841 return bVal ? 'true' : 'false';
62844 isCellEditable : function(colIndex, rowIndex){
62845 return colIndex == 1;
62848 getRenderer : function(col){
62850 this.renderCellDelegate : this.renderPropDelegate;
62853 renderProp : function(v){
62854 return this.getPropertyName(v);
62857 renderCell : function(val){
62859 if(val instanceof Date){
62860 rv = this.renderDate(val);
62861 }else if(typeof val == 'boolean'){
62862 rv = this.renderBool(val);
62864 return Roo.util.Format.htmlEncode(rv);
62867 getPropertyName : function(name){
62868 var pn = this.grid.propertyNames;
62869 return pn && pn[name] ? pn[name] : name;
62872 getCellEditor : function(colIndex, rowIndex){
62873 var p = this.store.getProperty(rowIndex);
62874 var n = p.data['name'], val = p.data['value'];
62876 if(typeof(this.grid.customEditors[n]) == 'string'){
62877 return this.editors[this.grid.customEditors[n]];
62879 if(typeof(this.grid.customEditors[n]) != 'undefined'){
62880 return this.grid.customEditors[n];
62882 if(val instanceof Date){
62883 return this.editors['date'];
62884 }else if(typeof val == 'number'){
62885 return this.editors['number'];
62886 }else if(typeof val == 'boolean'){
62887 return this.editors['boolean'];
62889 return this.editors['string'];
62895 * @class Roo.grid.PropertyGrid
62896 * @extends Roo.grid.EditorGrid
62897 * This class represents the interface of a component based property grid control.
62898 * <br><br>Usage:<pre><code>
62899 var grid = new Roo.grid.PropertyGrid("my-container-id", {
62907 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62908 * The container MUST have some type of size defined for the grid to fill. The container will be
62909 * automatically set to position relative if it isn't already.
62910 * @param {Object} config A config object that sets properties on this grid.
62912 Roo.grid.PropertyGrid = function(container, config){
62913 config = config || {};
62914 var store = new Roo.grid.PropertyStore(this);
62915 this.store = store;
62916 var cm = new Roo.grid.PropertyColumnModel(this, store);
62917 store.store.sort('name', 'ASC');
62918 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62921 enableColLock:false,
62922 enableColumnMove:false,
62924 trackMouseOver: false,
62927 this.getGridEl().addClass('x-props-grid');
62928 this.lastEditRow = null;
62929 this.on('columnresize', this.onColumnResize, this);
62932 * @event beforepropertychange
62933 * Fires before a property changes (return false to stop?)
62934 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62935 * @param {String} id Record Id
62936 * @param {String} newval New Value
62937 * @param {String} oldval Old Value
62939 "beforepropertychange": true,
62941 * @event propertychange
62942 * Fires after a property changes
62943 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62944 * @param {String} id Record Id
62945 * @param {String} newval New Value
62946 * @param {String} oldval Old Value
62948 "propertychange": true
62950 this.customEditors = this.customEditors || {};
62952 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62955 * @cfg {Object} customEditors map of colnames=> custom editors.
62956 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62957 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62958 * false disables editing of the field.
62962 * @cfg {Object} propertyNames map of property Names to their displayed value
62965 render : function(){
62966 Roo.grid.PropertyGrid.superclass.render.call(this);
62967 this.autoSize.defer(100, this);
62970 autoSize : function(){
62971 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62973 this.view.fitColumns();
62977 onColumnResize : function(){
62978 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62982 * Sets the data for the Grid
62983 * accepts a Key => Value object of all the elements avaiable.
62984 * @param {Object} data to appear in grid.
62986 setSource : function(source){
62987 this.store.setSource(source);
62991 * Gets all the data from the grid.
62992 * @return {Object} data data stored in grid
62994 getSource : function(){
62995 return this.store.getSource();
63004 * @class Roo.grid.Calendar
63005 * @extends Roo.grid.Grid
63006 * This class extends the Grid to provide a calendar widget
63007 * <br><br>Usage:<pre><code>
63008 var grid = new Roo.grid.Calendar("my-container-id", {
63011 selModel: mySelectionModel,
63012 autoSizeColumns: true,
63013 monitorWindowResize: false,
63014 trackMouseOver: true
63015 eventstore : real data store..
63021 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63022 * The container MUST have some type of size defined for the grid to fill. The container will be
63023 * automatically set to position relative if it isn't already.
63024 * @param {Object} config A config object that sets properties on this grid.
63026 Roo.grid.Calendar = function(container, config){
63027 // initialize the container
63028 this.container = Roo.get(container);
63029 this.container.update("");
63030 this.container.setStyle("overflow", "hidden");
63031 this.container.addClass('x-grid-container');
63033 this.id = this.container.id;
63035 Roo.apply(this, config);
63036 // check and correct shorthanded configs
63040 for (var r = 0;r < 6;r++) {
63043 for (var c =0;c < 7;c++) {
63047 if (this.eventStore) {
63048 this.eventStore= Roo.factory(this.eventStore, Roo.data);
63049 this.eventStore.on('load',this.onLoad, this);
63050 this.eventStore.on('beforeload',this.clearEvents, this);
63054 this.dataSource = new Roo.data.Store({
63055 proxy: new Roo.data.MemoryProxy(rows),
63056 reader: new Roo.data.ArrayReader({}, [
63057 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63060 this.dataSource.load();
63061 this.ds = this.dataSource;
63062 this.ds.xmodule = this.xmodule || false;
63065 var cellRender = function(v,x,r)
63067 return String.format(
63068 '<div class="fc-day fc-widget-content"><div>' +
63069 '<div class="fc-event-container"></div>' +
63070 '<div class="fc-day-number">{0}</div>'+
63072 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63073 '</div></div>', v);
63078 this.colModel = new Roo.grid.ColumnModel( [
63080 xtype: 'ColumnModel',
63082 dataIndex : 'weekday0',
63084 renderer : cellRender
63087 xtype: 'ColumnModel',
63089 dataIndex : 'weekday1',
63091 renderer : cellRender
63094 xtype: 'ColumnModel',
63096 dataIndex : 'weekday2',
63097 header : 'Tuesday',
63098 renderer : cellRender
63101 xtype: 'ColumnModel',
63103 dataIndex : 'weekday3',
63104 header : 'Wednesday',
63105 renderer : cellRender
63108 xtype: 'ColumnModel',
63110 dataIndex : 'weekday4',
63111 header : 'Thursday',
63112 renderer : cellRender
63115 xtype: 'ColumnModel',
63117 dataIndex : 'weekday5',
63119 renderer : cellRender
63122 xtype: 'ColumnModel',
63124 dataIndex : 'weekday6',
63125 header : 'Saturday',
63126 renderer : cellRender
63129 this.cm = this.colModel;
63130 this.cm.xmodule = this.xmodule || false;
63134 //this.selModel = new Roo.grid.CellSelectionModel();
63135 //this.sm = this.selModel;
63136 //this.selModel.init(this);
63140 this.container.setWidth(this.width);
63144 this.container.setHeight(this.height);
63151 * The raw click event for the entire grid.
63152 * @param {Roo.EventObject} e
63157 * The raw dblclick event for the entire grid.
63158 * @param {Roo.EventObject} e
63162 * @event contextmenu
63163 * The raw contextmenu event for the entire grid.
63164 * @param {Roo.EventObject} e
63166 "contextmenu" : true,
63169 * The raw mousedown event for the entire grid.
63170 * @param {Roo.EventObject} e
63172 "mousedown" : true,
63175 * The raw mouseup event for the entire grid.
63176 * @param {Roo.EventObject} e
63181 * The raw mouseover event for the entire grid.
63182 * @param {Roo.EventObject} e
63184 "mouseover" : true,
63187 * The raw mouseout event for the entire grid.
63188 * @param {Roo.EventObject} e
63193 * The raw keypress event for the entire grid.
63194 * @param {Roo.EventObject} e
63199 * The raw keydown event for the entire grid.
63200 * @param {Roo.EventObject} e
63208 * Fires when a cell is clicked
63209 * @param {Grid} this
63210 * @param {Number} rowIndex
63211 * @param {Number} columnIndex
63212 * @param {Roo.EventObject} e
63214 "cellclick" : true,
63216 * @event celldblclick
63217 * Fires when a cell is double clicked
63218 * @param {Grid} this
63219 * @param {Number} rowIndex
63220 * @param {Number} columnIndex
63221 * @param {Roo.EventObject} e
63223 "celldblclick" : true,
63226 * Fires when a row is clicked
63227 * @param {Grid} this
63228 * @param {Number} rowIndex
63229 * @param {Roo.EventObject} e
63233 * @event rowdblclick
63234 * Fires when a row is double clicked
63235 * @param {Grid} this
63236 * @param {Number} rowIndex
63237 * @param {Roo.EventObject} e
63239 "rowdblclick" : true,
63241 * @event headerclick
63242 * Fires when a header is clicked
63243 * @param {Grid} this
63244 * @param {Number} columnIndex
63245 * @param {Roo.EventObject} e
63247 "headerclick" : true,
63249 * @event headerdblclick
63250 * Fires when a header cell is double clicked
63251 * @param {Grid} this
63252 * @param {Number} columnIndex
63253 * @param {Roo.EventObject} e
63255 "headerdblclick" : true,
63257 * @event rowcontextmenu
63258 * Fires when a row is right clicked
63259 * @param {Grid} this
63260 * @param {Number} rowIndex
63261 * @param {Roo.EventObject} e
63263 "rowcontextmenu" : true,
63265 * @event cellcontextmenu
63266 * Fires when a cell is right clicked
63267 * @param {Grid} this
63268 * @param {Number} rowIndex
63269 * @param {Number} cellIndex
63270 * @param {Roo.EventObject} e
63272 "cellcontextmenu" : true,
63274 * @event headercontextmenu
63275 * Fires when a header is right clicked
63276 * @param {Grid} this
63277 * @param {Number} columnIndex
63278 * @param {Roo.EventObject} e
63280 "headercontextmenu" : true,
63282 * @event bodyscroll
63283 * Fires when the body element is scrolled
63284 * @param {Number} scrollLeft
63285 * @param {Number} scrollTop
63287 "bodyscroll" : true,
63289 * @event columnresize
63290 * Fires when the user resizes a column
63291 * @param {Number} columnIndex
63292 * @param {Number} newSize
63294 "columnresize" : true,
63296 * @event columnmove
63297 * Fires when the user moves a column
63298 * @param {Number} oldIndex
63299 * @param {Number} newIndex
63301 "columnmove" : true,
63304 * Fires when row(s) start being dragged
63305 * @param {Grid} this
63306 * @param {Roo.GridDD} dd The drag drop object
63307 * @param {event} e The raw browser event
63309 "startdrag" : true,
63312 * Fires when a drag operation is complete
63313 * @param {Grid} this
63314 * @param {Roo.GridDD} dd The drag drop object
63315 * @param {event} e The raw browser event
63320 * Fires when dragged row(s) are dropped on a valid DD target
63321 * @param {Grid} this
63322 * @param {Roo.GridDD} dd The drag drop object
63323 * @param {String} targetId The target drag drop object
63324 * @param {event} e The raw browser event
63329 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63330 * @param {Grid} this
63331 * @param {Roo.GridDD} dd The drag drop object
63332 * @param {String} targetId The target drag drop object
63333 * @param {event} e The raw browser event
63338 * Fires when the dragged row(s) first cross another DD target while being dragged
63339 * @param {Grid} this
63340 * @param {Roo.GridDD} dd The drag drop object
63341 * @param {String} targetId The target drag drop object
63342 * @param {event} e The raw browser event
63344 "dragenter" : true,
63347 * Fires when the dragged row(s) leave another DD target while being dragged
63348 * @param {Grid} this
63349 * @param {Roo.GridDD} dd The drag drop object
63350 * @param {String} targetId The target drag drop object
63351 * @param {event} e The raw browser event
63356 * Fires when a row is rendered, so you can change add a style to it.
63357 * @param {GridView} gridview The grid view
63358 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
63364 * Fires when the grid is rendered
63365 * @param {Grid} grid
63370 * Fires when a date is selected
63371 * @param {DatePicker} this
63372 * @param {Date} date The selected date
63376 * @event monthchange
63377 * Fires when the displayed month changes
63378 * @param {DatePicker} this
63379 * @param {Date} date The selected month
63381 'monthchange': true,
63383 * @event evententer
63384 * Fires when mouse over an event
63385 * @param {Calendar} this
63386 * @param {event} Event
63388 'evententer': true,
63390 * @event eventleave
63391 * Fires when the mouse leaves an
63392 * @param {Calendar} this
63395 'eventleave': true,
63397 * @event eventclick
63398 * Fires when the mouse click an
63399 * @param {Calendar} this
63402 'eventclick': true,
63404 * @event eventrender
63405 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63406 * @param {Calendar} this
63407 * @param {data} data to be modified
63409 'eventrender': true
63413 Roo.grid.Grid.superclass.constructor.call(this);
63414 this.on('render', function() {
63415 this.view.el.addClass('x-grid-cal');
63417 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63421 if (!Roo.grid.Calendar.style) {
63422 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63425 '.x-grid-cal .x-grid-col' : {
63426 height: 'auto !important',
63427 'vertical-align': 'top'
63429 '.x-grid-cal .fc-event-hori' : {
63440 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63442 * @cfg {Store} eventStore The store that loads events.
63447 activeDate : false,
63450 monitorWindowResize : false,
63453 resizeColumns : function() {
63454 var col = (this.view.el.getWidth() / 7) - 3;
63455 // loop through cols, and setWidth
63456 for(var i =0 ; i < 7 ; i++){
63457 this.cm.setColumnWidth(i, col);
63460 setDate :function(date) {
63462 Roo.log('setDate?');
63464 this.resizeColumns();
63465 var vd = this.activeDate;
63466 this.activeDate = date;
63467 // if(vd && this.el){
63468 // var t = date.getTime();
63469 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63470 // Roo.log('using add remove');
63472 // this.fireEvent('monthchange', this, date);
63474 // this.cells.removeClass("fc-state-highlight");
63475 // this.cells.each(function(c){
63476 // if(c.dateValue == t){
63477 // c.addClass("fc-state-highlight");
63478 // setTimeout(function(){
63479 // try{c.dom.firstChild.focus();}catch(e){}
63489 var days = date.getDaysInMonth();
63491 var firstOfMonth = date.getFirstDateOfMonth();
63492 var startingPos = firstOfMonth.getDay()-this.startDay;
63494 if(startingPos < this.startDay){
63498 var pm = date.add(Date.MONTH, -1);
63499 var prevStart = pm.getDaysInMonth()-startingPos;
63503 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63505 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63506 //this.cells.addClassOnOver('fc-state-hover');
63508 var cells = this.cells.elements;
63509 var textEls = this.textNodes;
63511 //Roo.each(cells, function(cell){
63512 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63515 days += startingPos;
63517 // convert everything to numbers so it's fast
63518 var day = 86400000;
63519 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63522 //Roo.log(prevStart);
63524 var today = new Date().clearTime().getTime();
63525 var sel = date.clearTime().getTime();
63526 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63527 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63528 var ddMatch = this.disabledDatesRE;
63529 var ddText = this.disabledDatesText;
63530 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63531 var ddaysText = this.disabledDaysText;
63532 var format = this.format;
63534 var setCellClass = function(cal, cell){
63536 //Roo.log('set Cell Class');
63538 var t = d.getTime();
63543 cell.dateValue = t;
63545 cell.className += " fc-today";
63546 cell.className += " fc-state-highlight";
63547 cell.title = cal.todayText;
63550 // disable highlight in other month..
63551 cell.className += " fc-state-highlight";
63556 //cell.className = " fc-state-disabled";
63557 cell.title = cal.minText;
63561 //cell.className = " fc-state-disabled";
63562 cell.title = cal.maxText;
63566 if(ddays.indexOf(d.getDay()) != -1){
63567 // cell.title = ddaysText;
63568 // cell.className = " fc-state-disabled";
63571 if(ddMatch && format){
63572 var fvalue = d.dateFormat(format);
63573 if(ddMatch.test(fvalue)){
63574 cell.title = ddText.replace("%0", fvalue);
63575 cell.className = " fc-state-disabled";
63579 if (!cell.initialClassName) {
63580 cell.initialClassName = cell.dom.className;
63583 cell.dom.className = cell.initialClassName + ' ' + cell.className;
63588 for(; i < startingPos; i++) {
63589 cells[i].dayName = (++prevStart);
63590 Roo.log(textEls[i]);
63591 d.setDate(d.getDate()+1);
63593 //cells[i].className = "fc-past fc-other-month";
63594 setCellClass(this, cells[i]);
63599 for(; i < days; i++){
63600 intDay = i - startingPos + 1;
63601 cells[i].dayName = (intDay);
63602 d.setDate(d.getDate()+1);
63604 cells[i].className = ''; // "x-date-active";
63605 setCellClass(this, cells[i]);
63609 for(; i < 42; i++) {
63610 //textEls[i].innerHTML = (++extraDays);
63612 d.setDate(d.getDate()+1);
63613 cells[i].dayName = (++extraDays);
63614 cells[i].className = "fc-future fc-other-month";
63615 setCellClass(this, cells[i]);
63618 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63620 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63622 // this will cause all the cells to mis
63625 for (var r = 0;r < 6;r++) {
63626 for (var c =0;c < 7;c++) {
63627 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63631 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63632 for(i=0;i<cells.length;i++) {
63634 this.cells.elements[i].dayName = cells[i].dayName ;
63635 this.cells.elements[i].className = cells[i].className;
63636 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63637 this.cells.elements[i].title = cells[i].title ;
63638 this.cells.elements[i].dateValue = cells[i].dateValue ;
63644 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63645 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63647 ////if(totalRows != 6){
63648 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63649 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63652 this.fireEvent('monthchange', this, date);
63657 * Returns the grid's SelectionModel.
63658 * @return {SelectionModel}
63660 getSelectionModel : function(){
63661 if(!this.selModel){
63662 this.selModel = new Roo.grid.CellSelectionModel();
63664 return this.selModel;
63668 this.eventStore.load()
63674 findCell : function(dt) {
63675 dt = dt.clearTime().getTime();
63677 this.cells.each(function(c){
63678 //Roo.log("check " +c.dateValue + '?=' + dt);
63679 if(c.dateValue == dt){
63689 findCells : function(rec) {
63690 var s = rec.data.start_dt.clone().clearTime().getTime();
63692 var e= rec.data.end_dt.clone().clearTime().getTime();
63695 this.cells.each(function(c){
63696 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63698 if(c.dateValue > e){
63701 if(c.dateValue < s){
63710 findBestRow: function(cells)
63714 for (var i =0 ; i < cells.length;i++) {
63715 ret = Math.max(cells[i].rows || 0,ret);
63722 addItem : function(rec)
63724 // look for vertical location slot in
63725 var cells = this.findCells(rec);
63727 rec.row = this.findBestRow(cells);
63729 // work out the location.
63733 for(var i =0; i < cells.length; i++) {
63741 if (crow.start.getY() == cells[i].getY()) {
63743 crow.end = cells[i];
63759 for (var i = 0; i < cells.length;i++) {
63760 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63767 clearEvents: function() {
63769 if (!this.eventStore.getCount()) {
63772 // reset number of rows in cells.
63773 Roo.each(this.cells.elements, function(c){
63777 this.eventStore.each(function(e) {
63778 this.clearEvent(e);
63783 clearEvent : function(ev)
63786 Roo.each(ev.els, function(el) {
63787 el.un('mouseenter' ,this.onEventEnter, this);
63788 el.un('mouseleave' ,this.onEventLeave, this);
63796 renderEvent : function(ev,ctr) {
63798 ctr = this.view.el.select('.fc-event-container',true).first();
63802 this.clearEvent(ev);
63808 var cells = ev.cells;
63809 var rows = ev.rows;
63810 this.fireEvent('eventrender', this, ev);
63812 for(var i =0; i < rows.length; i++) {
63816 cls += ' fc-event-start';
63818 if ((i+1) == rows.length) {
63819 cls += ' fc-event-end';
63822 //Roo.log(ev.data);
63823 // how many rows should it span..
63824 var cg = this.eventTmpl.append(ctr,Roo.apply({
63827 }, ev.data) , true);
63830 cg.on('mouseenter' ,this.onEventEnter, this, ev);
63831 cg.on('mouseleave' ,this.onEventLeave, this, ev);
63832 cg.on('click', this.onEventClick, this, ev);
63836 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63837 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63840 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
63841 cg.setWidth(ebox.right - sbox.x -2);
63845 renderEvents: function()
63847 // first make sure there is enough space..
63849 if (!this.eventTmpl) {
63850 this.eventTmpl = new Roo.Template(
63851 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
63852 '<div class="fc-event-inner">' +
63853 '<span class="fc-event-time">{time}</span>' +
63854 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63856 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
63864 this.cells.each(function(c) {
63865 //Roo.log(c.select('.fc-day-content div',true).first());
63866 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63869 var ctr = this.view.el.select('.fc-event-container',true).first();
63872 this.eventStore.each(function(ev){
63874 this.renderEvent(ev);
63878 this.view.layout();
63882 onEventEnter: function (e, el,event,d) {
63883 this.fireEvent('evententer', this, el, event);
63886 onEventLeave: function (e, el,event,d) {
63887 this.fireEvent('eventleave', this, el, event);
63890 onEventClick: function (e, el,event,d) {
63891 this.fireEvent('eventclick', this, el, event);
63894 onMonthChange: function () {
63898 onLoad: function () {
63900 //Roo.log('calendar onload');
63902 if(this.eventStore.getCount() > 0){
63906 this.eventStore.each(function(d){
63911 if (typeof(add.end_dt) == 'undefined') {
63912 Roo.log("Missing End time in calendar data: ");
63916 if (typeof(add.start_dt) == 'undefined') {
63917 Roo.log("Missing Start time in calendar data: ");
63921 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63922 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63923 add.id = add.id || d.id;
63924 add.title = add.title || '??';
63932 this.renderEvents();
63942 render : function ()
63946 if (!this.view.el.hasClass('course-timesheet')) {
63947 this.view.el.addClass('course-timesheet');
63949 if (this.tsStyle) {
63954 Roo.log(_this.grid.view.el.getWidth());
63957 this.tsStyle = Roo.util.CSS.createStyleSheet({
63958 '.course-timesheet .x-grid-row' : {
63961 '.x-grid-row td' : {
63962 'vertical-align' : 0
63964 '.course-edit-link' : {
63966 'text-overflow' : 'ellipsis',
63967 'overflow' : 'hidden',
63968 'white-space' : 'nowrap',
63969 'cursor' : 'pointer'
63974 '.de-act-sup-link' : {
63975 'color' : 'purple',
63976 'text-decoration' : 'line-through'
63980 'text-decoration' : 'line-through'
63982 '.course-timesheet .course-highlight' : {
63983 'border-top-style': 'dashed !important',
63984 'border-bottom-bottom': 'dashed !important'
63986 '.course-timesheet .course-item' : {
63987 'font-family' : 'tahoma, arial, helvetica',
63988 'font-size' : '11px',
63989 'overflow' : 'hidden',
63990 'padding-left' : '10px',
63991 'padding-right' : '10px',
63992 'padding-top' : '10px'
64000 monitorWindowResize : false,
64001 cellrenderer : function(v,x,r)
64006 xtype: 'CellSelectionModel',
64013 beforeload : function (_self, options)
64015 options.params = options.params || {};
64016 options.params._month = _this.monthField.getValue();
64017 options.params.limit = 9999;
64018 options.params['sort'] = 'when_dt';
64019 options.params['dir'] = 'ASC';
64020 this.proxy.loadResponse = this.loadResponse;
64022 //this.addColumns();
64024 load : function (_self, records, options)
64026 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64027 // if you click on the translation.. you can edit it...
64028 var el = Roo.get(this);
64029 var id = el.dom.getAttribute('data-id');
64030 var d = el.dom.getAttribute('data-date');
64031 var t = el.dom.getAttribute('data-time');
64032 //var id = this.child('span').dom.textContent;
64035 Pman.Dialog.CourseCalendar.show({
64039 productitem_active : id ? 1 : 0
64041 _this.grid.ds.load({});
64046 _this.panel.fireEvent('resize', [ '', '' ]);
64049 loadResponse : function(o, success, response){
64050 // this is overridden on before load..
64052 Roo.log("our code?");
64053 //Roo.log(success);
64054 //Roo.log(response)
64055 delete this.activeRequest;
64057 this.fireEvent("loadexception", this, o, response);
64058 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64063 result = o.reader.read(response);
64065 Roo.log("load exception?");
64066 this.fireEvent("loadexception", this, o, response, e);
64067 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64070 Roo.log("ready...");
64071 // loop through result.records;
64072 // and set this.tdate[date] = [] << array of records..
64074 Roo.each(result.records, function(r){
64076 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64077 _this.tdata[r.data.when_dt.format('j')] = [];
64079 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64082 //Roo.log(_this.tdata);
64084 result.records = [];
64085 result.totalRecords = 6;
64087 // let's generate some duumy records for the rows.
64088 //var st = _this.dateField.getValue();
64090 // work out monday..
64091 //st = st.add(Date.DAY, -1 * st.format('w'));
64093 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64095 var firstOfMonth = date.getFirstDayOfMonth();
64096 var days = date.getDaysInMonth();
64098 var firstAdded = false;
64099 for (var i = 0; i < result.totalRecords ; i++) {
64100 //var d= st.add(Date.DAY, i);
64103 for(var w = 0 ; w < 7 ; w++){
64104 if(!firstAdded && firstOfMonth != w){
64111 var dd = (d > 0 && d < 10) ? "0"+d : d;
64112 row['weekday'+w] = String.format(
64113 '<span style="font-size: 16px;"><b>{0}</b></span>'+
64114 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64116 date.format('Y-m-')+dd
64119 if(typeof(_this.tdata[d]) != 'undefined'){
64120 Roo.each(_this.tdata[d], function(r){
64124 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64125 if(r.parent_id*1>0){
64126 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64129 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64130 deactive = 'de-act-link';
64133 row['weekday'+w] += String.format(
64134 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64136 r.product_id_name, //1
64137 r.when_dt.format('h:ia'), //2
64147 // only do this if something added..
64149 result.records.push(_this.grid.dataSource.reader.newRow(row));
64153 // push it twice. (second one with an hour..
64157 this.fireEvent("load", this, o, o.request.arg);
64158 o.request.callback.call(o.request.scope, result, o.request.arg, true);
64160 sortInfo : {field: 'when_dt', direction : 'ASC' },
64162 xtype: 'HttpProxy',
64165 url : baseURL + '/Roo/Shop_course.php'
64168 xtype: 'JsonReader',
64185 'name': 'parent_id',
64189 'name': 'product_id',
64193 'name': 'productitem_id',
64211 click : function (_self, e)
64213 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64214 sd.setMonth(sd.getMonth()-1);
64215 _this.monthField.setValue(sd.format('Y-m-d'));
64216 _this.grid.ds.load({});
64222 xtype: 'Separator',
64226 xtype: 'MonthField',
64229 render : function (_self)
64231 _this.monthField = _self;
64232 // _this.monthField.set today
64234 select : function (combo, date)
64236 _this.grid.ds.load({});
64239 value : (function() { return new Date(); })()
64242 xtype: 'Separator',
64248 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64258 click : function (_self, e)
64260 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64261 sd.setMonth(sd.getMonth()+1);
64262 _this.monthField.setValue(sd.format('Y-m-d'));
64263 _this.grid.ds.load({});
64276 * Ext JS Library 1.1.1
64277 * Copyright(c) 2006-2007, Ext JS, LLC.
64279 * Originally Released Under LGPL - original licence link has changed is not relivant.
64282 * <script type="text/javascript">
64286 * @class Roo.LoadMask
64287 * A simple utility class for generically masking elements while loading data. If the element being masked has
64288 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64289 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
64290 * element's UpdateManager load indicator and will be destroyed after the initial load.
64292 * Create a new LoadMask
64293 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64294 * @param {Object} config The config object
64296 Roo.LoadMask = function(el, config){
64297 this.el = Roo.get(el);
64298 Roo.apply(this, config);
64300 this.store.on('beforeload', this.onBeforeLoad, this);
64301 this.store.on('load', this.onLoad, this);
64302 this.store.on('loadexception', this.onLoadException, this);
64303 this.removeMask = false;
64305 var um = this.el.getUpdateManager();
64306 um.showLoadIndicator = false; // disable the default indicator
64307 um.on('beforeupdate', this.onBeforeLoad, this);
64308 um.on('update', this.onLoad, this);
64309 um.on('failure', this.onLoad, this);
64310 this.removeMask = true;
64314 Roo.LoadMask.prototype = {
64316 * @cfg {Boolean} removeMask
64317 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64318 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
64320 removeMask : false,
64322 * @cfg {String} msg
64323 * The text to display in a centered loading message box (defaults to 'Loading...')
64325 msg : 'Loading...',
64327 * @cfg {String} msgCls
64328 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64330 msgCls : 'x-mask-loading',
64333 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64339 * Disables the mask to prevent it from being displayed
64341 disable : function(){
64342 this.disabled = true;
64346 * Enables the mask so that it can be displayed
64348 enable : function(){
64349 this.disabled = false;
64352 onLoadException : function()
64354 Roo.log(arguments);
64356 if (typeof(arguments[3]) != 'undefined') {
64357 Roo.MessageBox.alert("Error loading",arguments[3]);
64361 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64362 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64369 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64372 onLoad : function()
64374 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64378 onBeforeLoad : function(){
64379 if(!this.disabled){
64380 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64385 destroy : function(){
64387 this.store.un('beforeload', this.onBeforeLoad, this);
64388 this.store.un('load', this.onLoad, this);
64389 this.store.un('loadexception', this.onLoadException, this);
64391 var um = this.el.getUpdateManager();
64392 um.un('beforeupdate', this.onBeforeLoad, this);
64393 um.un('update', this.onLoad, this);
64394 um.un('failure', this.onLoad, this);
64399 * Ext JS Library 1.1.1
64400 * Copyright(c) 2006-2007, Ext JS, LLC.
64402 * Originally Released Under LGPL - original licence link has changed is not relivant.
64405 * <script type="text/javascript">
64410 * @class Roo.XTemplate
64411 * @extends Roo.Template
64412 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64414 var t = new Roo.XTemplate(
64415 '<select name="{name}">',
64416 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
64420 // then append, applying the master template values
64423 * Supported features:
64428 {a_variable} - output encoded.
64429 {a_variable.format:("Y-m-d")} - call a method on the variable
64430 {a_variable:raw} - unencoded output
64431 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64432 {a_variable:this.method_on_template(...)} - call a method on the template object.
64437 <tpl for="a_variable or condition.."></tpl>
64438 <tpl if="a_variable or condition"></tpl>
64439 <tpl exec="some javascript"></tpl>
64440 <tpl name="named_template"></tpl> (experimental)
64442 <tpl for="."></tpl> - just iterate the property..
64443 <tpl for=".."></tpl> - iterates with the parent (probably the template)
64447 Roo.XTemplate = function()
64449 Roo.XTemplate.superclass.constructor.apply(this, arguments);
64456 Roo.extend(Roo.XTemplate, Roo.Template, {
64459 * The various sub templates
64464 * basic tag replacing syntax
64467 * // you can fake an object call by doing this
64471 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64474 * compile the template
64476 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64479 compile: function()
64483 s = ['<tpl>', s, '</tpl>'].join('');
64485 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64486 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64487 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
64488 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64489 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
64494 while(true == !!(m = s.match(re))){
64495 var forMatch = m[0].match(nameRe),
64496 ifMatch = m[0].match(ifRe),
64497 execMatch = m[0].match(execRe),
64498 namedMatch = m[0].match(namedRe),
64503 name = forMatch && forMatch[1] ? forMatch[1] : '';
64506 // if - puts fn into test..
64507 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64509 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64514 // exec - calls a function... returns empty if true is returned.
64515 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64517 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64525 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64526 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64527 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64530 var uid = namedMatch ? namedMatch[1] : id;
64534 id: namedMatch ? namedMatch[1] : id,
64541 s = s.replace(m[0], '');
64543 s = s.replace(m[0], '{xtpl'+ id + '}');
64548 for(var i = tpls.length-1; i >= 0; --i){
64549 this.compileTpl(tpls[i]);
64550 this.tpls[tpls[i].id] = tpls[i];
64552 this.master = tpls[tpls.length-1];
64556 * same as applyTemplate, except it's done to one of the subTemplates
64557 * when using named templates, you can do:
64559 * var str = pl.applySubTemplate('your-name', values);
64562 * @param {Number} id of the template
64563 * @param {Object} values to apply to template
64564 * @param {Object} parent (normaly the instance of this object)
64566 applySubTemplate : function(id, values, parent)
64570 var t = this.tpls[id];
64574 if(t.test && !t.test.call(this, values, parent)){
64578 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64579 Roo.log(e.toString());
64585 if(t.exec && t.exec.call(this, values, parent)){
64589 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64590 Roo.log(e.toString());
64595 var vs = t.target ? t.target.call(this, values, parent) : values;
64596 parent = t.target ? values : parent;
64597 if(t.target && vs instanceof Array){
64599 for(var i = 0, len = vs.length; i < len; i++){
64600 buf[buf.length] = t.compiled.call(this, vs[i], parent);
64602 return buf.join('');
64604 return t.compiled.call(this, vs, parent);
64606 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64607 Roo.log(e.toString());
64608 Roo.log(t.compiled);
64613 compileTpl : function(tpl)
64615 var fm = Roo.util.Format;
64616 var useF = this.disableFormats !== true;
64617 var sep = Roo.isGecko ? "+" : ",";
64618 var undef = function(str) {
64619 Roo.log("Property not found :" + str);
64623 var fn = function(m, name, format, args)
64625 //Roo.log(arguments);
64626 args = args ? args.replace(/\\'/g,"'") : args;
64627 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64628 if (typeof(format) == 'undefined') {
64629 format= 'htmlEncode';
64631 if (format == 'raw' ) {
64635 if(name.substr(0, 4) == 'xtpl'){
64636 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64639 // build an array of options to determine if value is undefined..
64641 // basically get 'xxxx.yyyy' then do
64642 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64643 // (function () { Roo.log("Property not found"); return ''; })() :
64648 Roo.each(name.split('.'), function(st) {
64649 lookfor += (lookfor.length ? '.': '') + st;
64650 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
64653 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64656 if(format && useF){
64658 args = args ? ',' + args : "";
64660 if(format.substr(0, 5) != "this."){
64661 format = "fm." + format + '(';
64663 format = 'this.call("'+ format.substr(5) + '", ';
64667 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
64671 // called with xxyx.yuu:(test,test)
64673 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
64675 // raw.. - :raw modifier..
64676 return "'"+ sep + udef_st + name + ")"+sep+"'";
64680 // branched to use + in gecko and [].join() in others
64682 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
64683 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64686 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
64687 body.push(tpl.body.replace(/(\r\n|\n)/g,
64688 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64689 body.push("'].join('');};};");
64690 body = body.join('');
64693 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64695 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
64701 applyTemplate : function(values){
64702 return this.master.compiled.call(this, values, {});
64703 //var s = this.subs;
64706 apply : function(){
64707 return this.applyTemplate.apply(this, arguments);
64712 Roo.XTemplate.from = function(el){
64713 el = Roo.getDom(el);
64714 return new Roo.XTemplate(el.value || el.innerHTML);